数模转换DAC

Tutorial: 电子电路 Category: 嵌入式 Published: 2026-10-08 01:26:37 Views: 0 Likes: 0 Comments: 0

157.10数模转换DAC

/* 以下仅摘录 CubeIDE/16DAC/Core/Src/main.c 中本节使用的原始代码,未补写完整程序。 */
uint16_t DAC_Value=0;                                                        /* 定义 16 位无符号变量,初始数字码为 0。 */

MX_DAC_Init();                                                               /* 调用工程原有的 DAC 初始化函数。 */

while (1)                                                                    /* 进入工程原有的无限循环,持续更新输出电压。 */
{                                                                            /* 一轮 DAC 输出处理开始。 */
	  DAC_Value++;                                                            /* 原代码先把数字码加 1。 */
	  DAC_Value=DAC_Value%2048;                                               /* 原代码将结果限制在 0~2047。 */
	  HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, DAC_Value);      /* 把数字码写入 DAC 通道 1 的 12 位右对齐数据保持寄存器。 */
	  HAL_DAC_Start(&hdac,DAC_CHANNEL_1);                                     /* 使能 DAC 通道 1,并按本工程配置产生软件触发。 */
	  HAL_Delay(1);                                                           /* 延时 1 ms 后再更新下一个数字码。 */
}                                                                            /* 本轮结束,返回循环顶部继续执行。 */

一、数模转换 DAC

DAC 是 Digital-to-Analog Converter(数字到模拟转换器) 的缩写,中文应为“数模转换器”。它把程序给出的离散数字码转换为对应的模拟电压;ADC(Analog-to-Digital Converter,模数转换器)则把模拟电压转换为数字结果,两者的数据方向相反。

本节目标是会完成一个最小 DAC 输出实验:STM32F103ZET6 的 DAC 通道 1 从 PA4 输出逐级升高的电压,达到设定上限后立即回到低值,再重新上升。

1.1 数字信号转模拟信号

12 位 DAC 能表示的数字码为:

0~2^12-1 = 0~4095

一共有 2^12 = 4096 个码。若把参考电压记为 VREF+、DAC 数据输出寄存器中的码记为 D,按照 STM32F1 参考手册 RM0008 给出的理想关系:

VOUT = VREF+ × D / 4095

因此在 VREF+ = 3.3 V 的理想条件下:

数字码 D理想输出电压含义
00 V最低码
2047约 1.6496 V配套最终程序的最高码
2048约 1.6504 V接近半量程
40953.3 V满量程码

公式分母是最大码 4095,而数字码总数是 4096,两者不能混淆;后文使用取余运算时,除数决定的是“结果有多少种”,因此若要得到 0~4095,除数应写成 4096。

这里的“理想”很重要。配套代码启用了片内输出缓冲器。现行 STM32F103xC/xD/xE 数据手册 DS5792 Rev 13 给出的缓冲开启保证输出摆幅为 0.2 V~VDDA - 0.2 V,并不保证真正到达供电两端;实际电压还会受到 VREF+、负载、芯片偏移/增益误差和万用表误差影响。因此,“写 0 就必然精确为 0.000 V、写 4095 就必然精确为 3.300 V”并不严谨。

1.1.1 实验演示
  • 正确输出引脚是 PA4(DAC_OUT1),不是转录误识别出的 PE4。配套 16DAC.ioc、dac.c 和 STM32F103ZET6 引脚表三者均指向 PA4。
  • 万用表黑表笔(COM)接开发板 GND,红表笔(V/Ω)接 PA4,并选择直流电压档。开发板和测量仪器必须共地,否则电压没有共同参考点。
  • 程序每次增加数字码,电压逐级上升;计数回卷时电压从最高码突然跳回 0,所以波形是上升锯齿波,不是平滑往返的三角波。
  • DAC 实际输出由许多离散台阶组成,并非数学意义上完全连续。12 位分辨率下每相邻两码的理想电压间隔约为 VREF+ / 4095;VREF+ = 3.3 V 时约为 0.806 mV。
  • 普通万用表刷新较慢,无法显示每个 1 ms 台阶,只会呈现不断变化的采样/平均读数。若要观察锯齿形状,应使用示波器。

硬件信号链可以这样理解:

DAC_Value → 数据保持寄存器 → 软件触发 → 12 位 DAC 电阻网络 → 输出缓冲器 → PA4 → 万用表 → GND

结合本课程开发板原理图,接线可以进一步落实到实际器件:

  • 主控 U5 的 40 脚就是 PA4,该网络没有连接板载 LED、按键、蜂鸣器等负载,而是直接引到双排针 H5 的 27 脚。红表笔应接这个 PA4 引出点。
  • PE4 接的是 KEY0 按键电路。因此视频转录中的“PE4”是识别错误;如果把表笔接到 PE4,测到的是按键网络电平,而不是 DAC 输出。
  • VREF+ 决定 DAC 换算数字码时使用的参考电压。原理图中 VREF+ 经 R30(0 Ω)接 3.3 V,模拟电源 VDDA 经 R31(0 Ω)接 3.3 V;VREF- 经 R29(0 Ω)接 GND,VSSA 也接 GND,所以本板可按标称 VREF+ ≈ 3.3 V 理解。0 Ω 电阻在这里相当于便于布线、调试或改版的导线,不是分压电阻。
  • 万用表测的是“PA4 相对 GND 的电势差”。黑表笔接 GND 是为了与 MCU 的 VSSA/VREF- 使用同一个零电位基准;只接红表笔或双方不共地,读数就没有可靠参考。

PA4 是模拟信号输出,不是电源输出。数据手册在输出缓冲开启时给出的负载条件为电阻负载不小于 5 kΩ、电容负载不大于 50 pF;高输入阻抗万用表适合本实验,但不能用 PA4 直接带电机、继电器、扬声器等大负载。需要驱动较重负载时,应增加合适的运算放大器或功率级。

1.1.2 新建工程
  • 在 STM32CubeIDE 中新建 STM32 工程,目标器件选择 STM32F103ZET6;配套工程名为 16DAC。
  • 按课程一贯配置选择外部高速时钟并保留 SWD 调试接口。配套 .ioc 最终配置为 HSE 经 PLL 得到 72 MHz 系统时钟。
  • 工程生成后应先确认芯片型号、封装和引脚图一致,再编写业务代码。不同 STM32F103 子型号不一定都带 DAC,不能只看到“STM32F103”就假定外设完全相同。
1.1.3 配置 DAC
  • 在 CubeMX/CubeIDE 的 Pinout & Configuration 页面启用 DAC_OUT1,工具会把它映射到固定引脚 PA4。
  • 通道触发方式选择 Software trigger。这与代码中 sConfig.DAC_Trigger = DAC_TRIGGER_SOFTWARE 一致。
  • 配套工程启用了 DAC 输出缓冲器,即 DAC_OUTPUTBUFFER_ENABLE。缓冲器用于降低输出阻抗,但会牺牲靠近 0 V 和 VDDA 两端的输出摆幅。
  • 本实验由 CPU 每次写值并软件触发,不使用 DMA;DMA 配置页保持为空即可。
  • 视频中打开的是 DAC 的 GPIO Settings 页,并没有给 PA4 自定义用户标签;配套 .ioc 里也只有 PA4.Signal=COMP_DAC1_group。原稿所谓“在 GPIO 处填写 p4”并不是实际操作步骤。
  • 保存 .ioc 后生成的 dac.c 会配置 DAC 时钟、GPIOA 时钟以及 PA4 模拟模式。PA4 的模拟初始化位于 HAL_DAC_MspInit(),不应误以为 MX_GPIO_Init() 中必须再次出现 PA4。
1.1.4 代码初始化

初始化与输出过程分为四层:

  1. MX_DAC_Init() 先让 hdac.Instance 指向片内 DAC,再调用 HAL_DAC_Init()。
  2. HAL_DAC_MspInit() 打开 DAC/GPIOA 时钟,并把 PA4 配成模拟模式。
  3. HAL_DAC_ConfigChannel() 为通道 1 选择软件触发和输出缓冲器。
  4. 主循环用 HAL_DAC_SetValue() 写入数字码,再用 HAL_DAC_Start() 使能通道并发出软件触发。

HAL_DAC_SetValue() 的四个参数依次表示:DAC 句柄、通道、数据对齐方式和数字码。HAL_DAC_Start() 的第二个参数必须与前面写值和配置的通道一致;本节始终使用 DAC_CHANNEL_1。

这里把 HAL_DAC_Start() 放在循环中并不只是反复“启动一次外设”。配套 HAL 在检测到 DAC_TRIGGER_SOFTWARE 后,会在该函数内部设置 SWTRIG1;硬件随后把数据保持寄存器中的新值传到数据输出寄存器。因此本例每次先 SetValue,再 Start,才能对每个新码产生一次软件触发。若改成关闭触发或定时器触发,调用组织方式会不同,不能机械照搬本例顺序。

配套程序没有检查 HAL_DAC_SetValue() 和 HAL_DAC_Start() 的返回值,课堂实验通常仍能运行,但在更严谨的工程中应判断返回状态并记录错误,避免外设未正确配置时静默失败。

1.1.5 对齐方式

STM32F1 DAC 提供三种数据保持寄存器写法:

HAL 参数有效数据写入含义
DAC_ALIGN_12B_R12 位,右对齐数字码直接使用 0x000~0xFFF,本节采用此方式
DAC_ALIGN_12B_L12 位,左对齐12 位数据位于 16 位字的 [15:4],写入值形如 0x0000~0xFFF0
DAC_ALIGN_8B_R8 位,右对齐使用 0x00~0xFF,只有 256 个码

“左/右对齐”描述的是有效数据在寄存器写入宽度中的位置,不是电压方向。配套 HAL 的 HAL_DAC_SetValue() 会根据 Alignment 选择相应的保持寄存器地址,但不会替业务代码自动把左对齐数据移位;若改用 DAC_ALIGN_12B_L,应按当前 HAL 接口要求提供已经左移 4 位的值。

1.1.6 定义变量
  • uint8_t 是 8 位无符号整数,范围为 0~255,共有 256 个可能值;原稿所说“最大值为 256”不正确。
  • 12 位 DAC 码需要表达 0~4095,因此 uint16_t 已足够;它的范围为 0~65535。uint32_t 也能容纳,但会多占用存储空间。
  • 配套工程定义 uint16_t DAC_Value = 0U;。主循环先执行 DAC_Value++,所以第一次送给 DAC 的值是 1,而不是 0;当取余结果回卷时才会写入 0。
  • 变量类型决定它能保存多大的数,但不会自动把数值限制为 12 位。传给 12 位右对齐 DAC 时,业务代码仍应主动保证数值不超过 4095。
1.1.7 取余实现电压输出功能

取余运算的结果范围是 0~除数-1,所以边界必须写准确:

表达式实际结果范围约对应的理想最高电压(VREF+ = 3.3 V)
DAC_Value % 40950~4094约 3.2992 V,永远不会得到 4095
DAC_Value % 40960~40953.3 V,可遍历全部 4096 个 12 位码
DAC_Value % 20480~2047约 1.6496 V,是配套源码的最终设置

因此,如果目标是完整遍历 0~4095,取余数应写成 % 4096,或在到达 4095 后显式回零。课程口述中的“对 4095 取余即可得到 0~4095”存在边界错误,不能照抄。

配套 main.c 最终保存的是 % 2048 和 HAL_Delay(1):输出码从 0~2047 循环,理想上限约为半量程。忽略函数执行开销,一轮锯齿约需 2048 × 1 ms = 2.048 s。若改为 % 4096,同样延时下一轮约需 4.096 s;实际周期会略长,因为 HAL 调用和循环本身也要耗时。

HAL_Delay() 只用于降低变化速度,便于万用表观察,它不是精密采样时基。若要得到频率稳定、CPU 占用低的周期波形,应使用定时器触发 DAC,并根据需要配合 DMA;本节没有实现该方案,不能把它误写成现有功能。

二、容易混淆的关键点

  • DAC 是数模转换,ADC 是模数转换。 “Digital to Analogy”是转录错误,正确英文为 Digital-to-Analog Converter。
  • 本工程是 PA4,不是 PE4。 PA4 对应 DAC_OUT1;PA5 对应 DAC_OUT2。当前只启用通道 1。
  • 4095 是最大码,4096 是码的总数。 取余遍历全部 12 位码时应除以 4096。
  • 数字码不是直接等于电压。 它还要结合 VREF+、DAC 非线性、偏移误差、输出缓冲和负载条件换算。
  • 锯齿并不是真正连续斜坡。 它由离散电压台阶组成,回卷处会突然跳变。
  • DAC 引脚不能当电源脚。 它适合提供参考电压、控制电压或经后级放大的波形,不适合直接驱动大电流负载。

三、参考依据

  • ST 官方 RM0008 Rev 21 参考手册:第 12 章,核对 DAC 数据格式、转换公式、软件触发和寄存器传递过程。
  • ST 官方 DS5792 Rev 13 数据手册:引脚定义及 5.3.20 节 DAC 电气规格,核对 PA4/PA5、输出摆幅、负载和误差条件。
  • 本地 STM32F103ZET6_V5.0 开发板原理图:核对 PA4/PE4 的板级连接、H5 排针位置及 VREF+、VDDA、VREF-、VSSA 的供电关系。
  • 本地配套工程 16DAC:核对目标器件、PA4、软件触发、输出缓冲以及最终 % 2048 代码。