news 2026/4/23 15:48:15

STM32 DAC寄存器级原理与DMA波形发生器实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32 DAC寄存器级原理与DMA波形发生器实现

1. STM32 DAC结构原理与寄存器级控制机制

DAC(Digital-to-Analog Converter)在STM32F103系列微控制器中并非简单的“写入即输出”外设,而是一个具有明确数据流路径、时序约束和配置逻辑的模拟前端模块。其核心价值在于将数字域的离散值精确映射为连续的模拟电压信号,广泛应用于波形发生、传感器校准、音频信号生成等场景。理解其内部结构框图与数据搬运机制,是实现稳定、可控模拟输出的前提。本节将基于STM32F103参考手册(RM0008)与数据手册(DS5383)的官方定义,系统性地剖析DAC的数据通路、触发机制、对齐方式与时序特性,摒弃“配置完寄存器就能出波形”的表层认知,深入到硬件行为的本质。

1.1 DAC核心数据通路:DHR → DOR → 模拟输出

DAC的数据流遵循一条严格定义的单向路径:用户写入数据 → 数据保持寄存器(DHR)→ 数据输出寄存器(DOR)→ 内部数模转换器 → 引脚模拟输出。这一路径的设计并非冗余,而是服务于关键的工程需求:确保输出电压的稳定性和抗干扰能力。

  • DHR(Data Holding Register):这是用户唯一可直接写入的寄存器。它本质上是一个缓冲区,用于暂存待转换的数字值。DHR本身不驱动任何模拟电路,其作用纯粹是“持有”数据,等待一个确定的时机将其传递给下一级。DHR有三种形式:DHR12L(左对齐12位)、DHR12R(右对齐12位)和DHR8R(8位右对齐)。选择哪种形式,取决于应用所需的分辨率与数据准备方式。
  • DOR(Data Output Register):这是一个只读寄存器,用户无法直接写入。它的唯一来源是DHR。DOR的值被直接连接到DAC的核心——内部R-2R电阻网络或权电流型转换器。因此,DOR的值决定了当前时刻DAC引脚的实际模拟输出电压。VOUT= VREF+× (DOR / 4096)(12位模式下),其中VREF+是DAC的参考电压。
  • 数据搬运的必要性:为何不能直接写DOR?答案在于同步与时序控制。如果允许软件直接修改DOR,那么在DOR值正在被模拟电路采样并转换的过程中,一次意外的写操作会导致输出电压产生毛刺或非预期的跳变。DHR作为中间缓冲,配合严格的触发机制,确保了DOR的更新发生在模拟转换周期的安全窗口内,从而保障了输出波形的纯净度。

1.2 触发机制:软件触发与硬件触发的时序差异

DHR中的数据何时被转移到DOR,由DAC的触发源决定。STM32F103提供了两种触发方式:软件触发(Software Trigger)和硬件触发(Hardware Trigger),它们在时序上存在本质区别,直接影响系统的实时性与波形精度。

  • 软件触发(TNX = 0)
  • 当DAC控制寄存器(DAC_CR)中的TENx位(Trigger Enable)被清零(TENx = 0)时,DAC进入软件触发模式。
  • 在此模式下,用户需要手动置位SWTRIGx位(Software Trigger)来发起一次数据转移。
  • 关键时序参数:APB1时钟周期(tAPB1。从SWTRIGx位被置位的那一刻起,经过恰好1个APB1总线时钟周期后,DHR的内容被锁存到DOR中。这个延迟是确定且固定的,由硬件逻辑保证。
  • 工程意义:软件触发模式简单、可控,适用于对实时性要求不高、但需要精确控制每个点输出时机的场景,例如手动步进生成一个静态的直流电平,或在主循环中按需更新一个缓慢变化的参考电压。

  • 硬件触发(TNX = 1)

  • TENx位被置位(TENx = 1)时,DAC启用硬件触发。
  • 触发源由TSELx[2:0]位(Trigger Select)选择,可选的源包括:定时器2/3/4/5/6/7的更新事件(TRGO)、外部中断线9/15(EXTI Line 9/15)等。这使得DAC的输出可以与系统中其他外设(如PWM、ADC采样)实现严格的同步。
  • 关键时序参数:3个APB1时钟周期。当选定的硬件触发信号到达时,并非立即转移数据,而是需要等待3个APB1总线时钟周期后,DHR的内容才被锁存到DOR。这个额外的延迟是为了确保触发信号在内部时钟域间完成可靠的同步,避免亚稳态问题。
  • 工程意义:硬件触发是生成高精度、高频率波形(如正弦波、三角波)的基石。例如,将DAC与TIM6的更新事件绑定,TIM6以固定频率溢出,每次溢出都自动触发DAC更新一个新点,从而构成一个由硬件精准计时的波形发生器。此时,波形的频率完全由TIM6的计数频率决定,而非软件执行时间,极大地提升了稳定性和精度。

1.3 输出建立时间与最大转换速率

DOR的值被更新后,模拟输出引脚并不会瞬间达到目标电压。从DOR值稳定到VOUT稳定在最终值的±1 LSB范围内,需要一段被称为“建立时间(Settling Time)”的物理过程。这段延时由DAC内部的模拟电路(运算放大器、开关、电阻网络)的带宽和负载能力共同决定。

  • 典型建立时间:根据STM32F103数据手册,当电源电压为3.3V,且输出端接一个标准的10kΩ负载时,DAC的典型建立时间为3 µs,最大不超过4 µs
  • 最大转换速率(Maximum Conversion Rate):这是衡量DAC性能的关键指标,它定义了DAC能够无失真地输出的最高波形频率。计算公式为:f_max ≈ 1 / (t_settle)。代入典型值3 µs,得到f_max ≈ 333 kHz;代入最大值4 µs,则f_max ≈ 250 kHz。这意味着,即使你的软件或硬件触发频率远高于此,DAC的模拟输出也无法跟上,波形将出现严重的失真、幅度衰减和相位滞后。
  • 工程启示:在设计波形发生器时,必须将此物理极限作为硬性约束。例如,若要生成一个100 kHz的正弦波,一个周期需要10 µs,理论上你有10 µs的时间来更新100个点,即每100 ns更新一个点。但这远远超出了DAC的250 kHz能力。实际可行的方案是,用100个点来描述一个周期,那么DAC的更新频率只需100 kHz,这完全在其能力范围之内。因此,“DAC能输出多快的波形”,其瓶颈不在数字逻辑,而在于模拟电路的物理响应速度。

2. 数据对齐与格式配置:12位与8位模式的实践选择

DAC的分辨率并非一成不变,它由用户通过配置DHR寄存器的类型和DAC_CR寄存器中的相关位来动态设定。STM32F103支持8位和12位两种分辨率模式,而12位模式又细分为左对齐与右对齐两种数据布局方式。这种灵活性带来了便利,但也要求开发者深刻理解不同模式下的数据准备方法,否则极易导致输出电平错误。

2.1 12位右对齐模式(DHR12R)

这是最直观、最常用的模式,也是本实验所采用的配置。其数据布局遵循“低位对齐”原则。

  • 寄存器映射:DHR12R寄存器的低12位(bit[11:0])用于存放12位的DAC数据,高4位(bit[15:12])为保留位,写入时应为0。
  • 数据准备:假设你需要输出一个代表3.3V * (2048/4096) = 1.65V的电平,对应的12位数字值就是2048(0x0800)。你只需将0x0800直接写入DHR12R寄存器即可。硬件会自动将其放置在寄存器的最低12位上。
  • 优势:编程模型简单,与常见的12位ADC读取结果(通常也是右对齐)保持一致,便于在ADC采样-处理-DAC输出的闭环系统中直接传递数据。

2.2 12位左对齐模式(DHR12L)

此模式将12位数据“左移”4位,放置在寄存器的高12位(bit[15:4])上。

  • 寄存器映射:DHR12L寄存器的高12位(bit[15:4])存放数据,低4位(bit[3:0])为保留位。
  • 数据准备:同样输出1.65V(2048),你需要将2048左移4位,即2048 << 4 = 32768(0x8000),然后写入DHR12L。
  • 应用场景:左对齐模式的主要优势在于与某些特定的DMA传输或硬件加速器兼容。例如,当DMA从内存搬运一个16位数据块到DAC时,若数据在内存中是以16位字形式存储,且高位包含了有效数据,左对齐模式可以避免在CPU端进行额外的位移操作,提高传输效率。但在纯软件控制的简单应用中,其优势并不明显。

2.3 8位右对齐模式(DHR8R)

此模式仅使用8位分辨率,牺牲精度换取更简单的数据接口。

  • 寄存器映射:DHR8R寄存器的低8位(bit[7:0])存放数据,高8位为保留位。
  • 数据准备:输出1.65V,对应8位值为128(0x80),直接写入DHR8R。
  • 精度损失:8位模式的最大分辨率为256级,而12位为4096级,精度相差16倍。对于需要精细电压控制的应用(如高精度传感器偏置),8位模式往往不够。但对于LED亮度调节、蜂鸣器音调控制等对绝对精度要求不高的场合,8位模式已绰绰有余,且数据准备更为轻量。

2.4 配置流程与寄存器操作

所有对齐模式的选择,均通过向DAC控制寄存器(DAC_CR)的相应位写入来完成。具体步骤如下:

  1. 使能DAC通道:设置EN1(通道1使能)或EN2(通道2使能)位为1。
  2. 选择触发方式:清零TEN1/TEN2位(软件触发)或置位(硬件触发)。
  3. 选择数据对齐与位宽
    • 对于通道1:配置DAC_CRMAMP1[3:0]位(波形发生器掩码,此处不涉及)和WAVE1[1:0]位(波形发生器,此处不涉及),关键在于写入DHRx寄存器时,选择正确的地址。DHR12R、DHR12L、DHR8R分别对应不同的内存地址,向哪个地址写入,就决定了采用哪种对齐方式。
    • 对于通道2:同理,操作DHR2寄存器的不同地址。
  4. 写入数据:将准备好的数值写入选定的DHRx寄存器。
  5. 触发更新:若为软件触发,置位SWTRIG1/SWTRIG2;若为硬件触发,启动相应的触发源(如启动TIM6)。

3. DMA集成:自动化数据搬运与双通道协同

DAC与DMA(Direct Memory Access)的结合,是构建高性能、低CPU占用率波形发生器的核心技术。它将数据搬运这一繁重任务从CPU手中剥离,交由专用的DMA控制器完成,使CPU得以专注于更复杂的算法处理。

3.1 DAC的DMA请求机制

DAC的DMA功能与其触发机制深度耦合。只有在硬件触发模式下(TENx = 1),DAC才会在每次触发事件发生时,向DMA控制器发出一个DMA请求(DMA Request)。这个请求信号是DAC与DMA之间的握手信令。

  • 请求源:DAC通道1的请求信号名为DAC_CH1,通道2的为DAC_CH2。它们分别连接到DMA1的通道3和通道4。
  • 触发条件:当硬件触发信号(如TIM6的更新事件)到达,并经过3个APB1周期的内部同步后,DAC不仅将DHR的值转移到DOR,同时还会向DMA控制器发出一个请求。这个请求告诉DMA:“我已经准备好接收新数据了,请把下一个值搬过来”。

3.2 DMA数据流:内存 → DHR

DMA控制器收到请求后,会自动执行一次数据传输。其传输方向是从内存(Memory)到外设(Peripheral),即从用户预先准备好的数据缓冲区(Buffer)中,读取一个数据,并将其写入DAC的DHR寄存器。

  • 配置要点
  • 外设地址:必须设置为DAC的DHR1或DHR2寄存器的基地址(例如,&DAC->DHR12R1)。
  • 内存地址:指向用户定义的uint16_t waveform_buffer[]数组的首地址。
  • 数据宽度:由于DHR寄存器是16位宽,DMA传输单元应配置为DMA_MemoryDataSize_HalfWord(16位)。
  • 传输数量:设置为缓冲区的长度(buffer_size)。
  • 循环模式:必须启用DMA_Mode_Circular。这是实现无限循环波形的关键。当DMA传输完缓冲区的最后一个数据后,它会自动回到第一个数据,开始下一轮循环,从而源源不断地为DAC提供新数据。

3.3 双通道DMA协同策略

STM32F103拥有两个独立的DAC通道(DAC1和DAC2),它们可以各自独立工作,也可以协同工作以生成差分信号或I/Q信号。在DMA模式下,双通道的协同需要精心设计,以避免资源冲突。

  • 独立DMA通道:最直接的方式是为每个DAC通道分配一个独立的DMA通道。DAC1使用DMA1_Channel3,DAC2使用DMA1_Channel4。这种方式逻辑清晰,互不干扰,适用于两个通道输出完全独立波形的场景。
  • 共享DMA通道(高级技巧):当两个通道需要输出高度同步的波形(如一个正弦波和一个余弦波),且希望用同一个DMA请求源来驱动时,可以采用共享策略。其核心思想是:只使能一个DAC通道的DMA请求(例如DMAEN1),而禁用另一个(DMAEN2 = 0。然后,在DMA的传输完成中断(TC Interrupt)中,由软件手动更新另一个通道的DHR寄存器。这样,DMA硬件负责一个通道的自动更新,而CPU在中断中负责另一个通道的更新。由于中断服务程序(ISR)的执行时间极短且可预测,两个通道的更新时刻几乎完全同步,实现了“伪硬件同步”。这种方法节省了一个DMA通道资源,但增加了少量的CPU开销。

4. 实验实现:基于软件触发的DAC波形发生器

本实验的目标是构建一个最基础、最可控的DAC波形发生器,为后续引入硬件触发和DMA打下坚实基础。我们将采用通道1、12位右对齐、软件触发的配置,通过主循环依次写入预定义的波形数据点,观察其输出效果。

4.1 硬件连接与初始化

  • 硬件连接:DAC通道1的输出引脚为PA4。将其连接至示波器探头,即可观测模拟输出波形。
  • GPIO初始化:PA4需要配置为模拟输入模式(GPIO_MODE_ANALOG)。这是DAC输出的必要条件,因为DAC的输出驱动器会直接接管该引脚的模拟功能,普通推挽或开漏模式将导致不可预测的行为。
  • DAC初始化
    ```c
    // 1. 使能DAC和GPIOA时钟
    RCC->APB1ENR |= RCC_APB1ENR_DACEN;
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;

// 2. 配置PA4为模拟输入
GPIOA->CRL &= ~(GPIO_CRL_MODE4 | GPIO_CRL_CNF4);
GPIOA->CRL |= GPIO_CRL_CNF4_1; // CNF4[1:0] = 10 => Analog input

// 3. 复位DAC并配置控制寄存器
DAC->CR = 0x00000000; // 清零,确保初始状态
DAC->CR |= DAC_CR_EN1; // 使能通道1
// TEN1 = 0, WAVE1 = 0, MAMP1 = 0, BOFF1 = 0 (默认)
// 此时DAC处于软件触发、12位右对齐模式
```

4.2 波形数据准备与主循环

我们以生成一个简单的16点正弦波为例。首先,利用数学工具(如Python或Excel)计算出0°到360°之间16个等间隔角度的正弦值,并将其缩放到0-4095的12位整数范围内。

// 16-point sine wave table (0 to 2*PI) const uint16_t sine_table[16] = { 2048, 2529, 2982, 3389, 3733, 3999, 4176, 4256, 4236, 4116, 3900, 3596, 3216, 2776, 2296, 1800 }; int main(void) { // ... 初始化代码 ... uint8_t index = 0; while(1) { // 将当前点的数据写入DHR12R1寄存器 DAC->DHR12R1 = sine_table[index]; // 发起软件触发,将DHR12R1的值转移到DOR1 DAC->SWTRIGR |= DAC_SWTRIGR_SWTRIG1; // 更新索引,循环播放 index = (index + 1) % 16; // 添加一个简单的延时,控制波形频率 // 注意:此处延时是粗略的,实际应用中应使用SysTick或定时器 for(volatile uint32_t i = 0; i < 10000; i++); } }

4.3 输出波形分析与调试要点

  • 预期波形:在示波器上,你将看到一个由16个阶梯组成的近似正弦波。每个阶梯的宽度由主循环中的延时决定,高度由sine_table中的值决定。
  • 关键调试点
  • 无输出或恒定电平:首先检查PA4的GPIO模式是否为ANALOG;其次检查DAC->CR中的EN1位是否已置位;最后检查DAC->DHR12R1是否被正确写入,以及SWTRIG1是否被触发。
  • 波形失真严重:检查延时是否过短,导致DAC来不及建立。根据前文所述,DAC建立时间约为3-4 µs,因此每个点的驻留时间(即延时)必须远大于此。若延时仅为几个微秒,则输出将是混乱的毛刺。
  • 波形频率不稳定:主循环中的延时受编译器优化等级影响极大。在实际项目中,绝不可依赖空循环延时。应使用SysTick定时器产生精确的毫秒级中断,在中断服务程序中更新DAC数据,这才是工业级的可靠做法。

5. 工程经验与常见陷阱

在多年的STM32 DAC开发实践中,我踩过不少坑,也积累了一些行之有效的经验。这些并非教科书上的理论,而是来自真实项目的血泪教训。

  • “为什么我的DAC输出总是0V或VREF?”:这是最常见的问题。绝大多数情况下,是因为忘记使能DAC的参考电压(VREF+)。在STM32F103中,VREF+引脚(PA0)必须连接一个稳定的参考电压(通常为3.3V),并且该引脚的电源必须是干净的。如果VREF+悬空或接触不良,DAC的输出将无法建立,表现为恒定的0V或VREF(取决于内部电路状态)。务必使用万用表测量PA0引脚的电压。
  • “DMA传输后,波形有明显的抖动”:这通常不是DAC的问题,而是DMA缓冲区未对齐或未启用缓存一致性所致。在使用Keil MDK时,确保你的waveform_buffer数组声明为__attribute__((aligned(4))),以保证其地址是4字节对齐的。此外,如果开启了ICache或DCache,需要在DMA传输前后调用SCB_InvalidateDCache_by_Addr()来刷新数据缓存,否则DMA可能读取到的是缓存中的旧数据。
  • “两个DAC通道的输出电平不一致”:即使写入相同的DHR值,DAC1和DAC2的输出也可能有微小差异(几mV)。这是因为它们是两个独立的模拟电路,存在工艺偏差。如果应用要求高精度匹配(如差分信号),必须在出厂时进行校准,并在软件中引入一个校准系数。不要期望它们天生就完美一致。
  • “我想用DAC输出一个非常缓慢变化的电压,比如每分钟变化1mV”:此时,软件触发是最佳选择,但要注意功耗问题。DAC本身在输出时会消耗电流。如果长时间维持一个非零电平,且没有其他低功耗需求,可以考虑在不需要输出时,通过DAC->CR &= ~DAC_CR_EN1关闭DAC通道,以节省功耗。在需要时再重新使能并写入新值。

DAC的真正力量,在于它如何与系统的其他部分(定时器、DMA、中断)协同工作,而不是孤立地看作一个“写寄存器就出电压”的黑盒子。当你第一次在示波器上看到自己用代码生成的、光滑的正弦波时,那种成就感,正是嵌入式工程师最纯粹的快乐源泉。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/22 16:31:11

VibeVoice实时语音合成原理解析:从文本到波形的技术实现

VibeVoice实时语音合成原理解析&#xff1a;从文本到波形的技术实现 1. 为什么传统TTS总让人感觉“不像真人” 你有没有试过用语音助手读一段长文字&#xff0c;结果等了两三秒才听到第一个字&#xff1f;或者听AI生成的播客&#xff0c;发现两个人对话时声音切换生硬&#x…

作者头像 李华
网站建设 2026/4/23 12:53:59

Qwen3-ForcedAligner-0.6B多场景实践:TTS评估+语言教学+ASR质检三合一

Qwen3-ForcedAligner-0.6B多场景实践&#xff1a;TTS评估语言教学ASR质检三合一 1. 这不是语音识别&#xff0c;但比识别更关键 你有没有遇到过这样的问题&#xff1a; 一段精心录制的课程音频&#xff0c;想给每个单词标上发音起止时间&#xff0c;却要靠耳朵反复听、手动打点…

作者头像 李华
网站建设 2026/4/23 14:32:37

Z-Image-Turbo LoRA Web服务灾备方案:模型/LoRA/历史记录异地备份教程

Z-Image-Turbo LoRA Web服务灾备方案&#xff1a;模型/LoRA/历史记录异地备份教程 1. 引言&#xff1a;为什么你的AI绘画服务需要备份&#xff1f; 想象一下这个场景&#xff1a;你花了好几天时间&#xff0c;精心调试了一个完美的亚洲美女LoRA模型&#xff0c;用它生成了上百…

作者头像 李华
网站建设 2026/4/23 9:54:51

STM32F103 DAC电压调节系统设计与实现

1. DAC数模转换实验&#xff1a;基于STM32F103的电压可调输出系统设计与实现在嵌入式控制系统中&#xff0c;数字信号向模拟信号的转换是连接微控制器逻辑世界与物理执行单元的关键桥梁。DAC&#xff08;Digital-to-Analog Converter&#xff09;作为STM32F103系列MCU内置的重要…

作者头像 李华