STM32 DAC 实战:用 DMA 把定时器表变成波形,自制信号发生器

想用 STM32 出个正弦波、三角波、或者可调频的方波,最常用的笨办法是在主循环里一步步算好值、写进 DAC 数据寄存器。CPU 被这事儿占满不说,频率稍微高点波形就锯齿得没法看。

正确的玩法是让 DAC 自己按节奏出数:定时器当节拍器,每隔固定时间触发一次 DAC 转换,DMA 把一张预先算好的波形表自动搬过去。CPU 只管生成那张表,之后彻底脱手。这篇文章把 DAC + 定时器触发 + DMA 波形发生讲清楚,以及那些让波形失真、频率算错的坑。

DAC 到底在干什么

DAC(数模转换器)把一个数字量变成模拟电压。STM32 多数型号带 1~2 路 DAC,分辨率 12 位,输出 0 到 VREF 之间的电压。最基础用法是 HAL_DAC_SetValue 写一个值,输出立刻变。

但手动写值的瓶颈在 CPU:你想出 1kHz 正弦波,一个周期至少几十个点,每秒就得写几万次,主循环啥也别干了。解法是用定时器触发DMA

  • 定时器按你设定的频率产生触发事件(比如溢出)
  • 每次触发让 DAC 转换下一个数据
  • DMA 自动从波形表取下一个值送进 DAC 寄存器,取完一圈自动回绕

整个过程 CPU 不参与数据搬运,只偶尔更新一下波形表或频率参数。

波形表和定时器触发

先在内存里放一张正弦波表,比如 256 个点,值是 12 位范围内按正弦算好的:

c 复制代码
#define N 256
uint16_t sine[N];
for (int i = 0; i < N; i++) {
    // 12位 DAC,中点 2048,幅度 2047
    sine[i] = (uint16_t)(2048 + 2047 * sin(2 * PI * i / N));
}

配置定时器触发 DAC。以 TIM6 作触发源,它的溢出频率决定每个点间隔,进而决定输出波形频率:

text 复制代码
波形频率 = 定时器触发频率 / 表长 N
         = (TIM_CLK / (PSC+1) / (ARR+1)) / N

比如表长 256,想出 1kHz 波形,触发频率就得是 256kHz。反过来用这个公式去算 PSC 和 ARR。

启动:

c 复制代码
HAL_TIM_Base_Start(&htim6);                       // 定时器开始产触发
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1,
                  (uint32_t *)sine, N, DAC_ALIGN_12B_R);   // DMA 自动送表

DAC_ALIGN_12B_R 是右对齐 12 位格式,波形表就按这个对齐准备。DMA 在 Circular 模式下自动回绕,波形就持续不断地出。

输出缓冲和幅度

DAC 内部有输出缓冲放大器,开启后驱动能力强、带负载稳,但会吃掉一点电压余量(到不了轨)。关闭缓冲直接输出,摆幅更满,但源阻抗高,后面不接运放就带不动负载。

想要任意幅度和偏移,单靠 DAC 不够。常见做法是 DAC 出 0~3.3V 的波形,再经过一级运放做缩放和电平平移。比如要 ±5V 输出,DAC 先出 0~3.3V 中点 1.65V,运放做 2 倍增益、减去偏置,得到 ±5V。

十个坑,波形丑基本都在这

  1. 触发源没配对。DAC 的触发源要在 CubeMX 里选成对应定时器(比如 TIM6 TRGO),没选或选错,DAC 不转,输出一直是上次的值。

  2. 波形表对齐格式错 。DMA 送的是 DAC_ALIGN_12B_R(右对齐)还是 12B_L(左对齐),要和表的数据格式一致。左对齐的表用右对齐送进去,波形直接被砍半或移位。

  3. 表长 N 和频率公式搞反。以为改 N 就能调频,其实 N 是分辨率、ARR 才是频率主旋钮。频率 = 触发频率 / N,调频动定时器,调波形平滑度动 N。

  4. DMA 没开 Circular 模式。Normal 模式送完一圈就停,波形只出一次。波形发生器必须 Circular,让它自动回绕。

  5. 定时器触发频率太高超 DAC 建立时间。DAC 转换和建立需要时间,触发太快,前一个值还没稳下一个又来了,波形台阶化、失真。查手册建立时间,别逼太狠。

  6. 输出缓冲没开带不动负载。直接驱动长线或低阻负载,电压被拉垮、波形变形。轻载可以关缓冲换满摆幅,重载必须开缓冲或后级加运放。

  7. 波形表没算对幅度和偏置。算出来的值超 0~4095 被截断,正弦顶部削平;没加直流偏置,纯交流表送进去负半周全丢。表值夹在范围内,交流波要加中点偏置。

  8. 两路 DAC 抢同一个触发源。部分型号两路 DAC 共用一个定时器触发,想两路不同频率做不到,得错开或用其他触发。需要独立波形时确认型号是否支持分触发。

  9. 更新波形表时和 DMA 读冲突 。正 Circular 搬运时你改表,可能半新半旧拼成一个怪波形。更新前先 HAL_DAC_Stop_DMA 或双缓冲切换,别边读边写。

  10. VREF 不稳波形带电源噪声。DAC 参考电压取自 VREF 脚,电源纹波直接叠到输出。参考源加旁路、用独立基准,波形才干净,尤其小信号时明显。

把这件事收一下

DAC 出波形,核心是「定时器定节拍、DMA 搬数据、CPU 只备表」。这三件套搭起来,正弦、三角、任意波形都是同一张表的区别,频率交给定时器算。坑大多在触发源配对、对齐格式、还有缓冲和参考电压这些模拟侧的细节,数字部分反而省心。调好了,一个 STM32 就是台可编程信号源。

DAC 出波靠的是「定时器打拍、DMA 搬表、CPU 备料」。频率动定时器、平滑动表长,对齐格式和参考电压卡死,波形才干净不乱。

相关推荐
zcmodeltech19 分钟前
反应装置模型控制系统设计与实现:多设备协同联动方案
java·网络·数据库·stm32·嵌入式硬件·能源·制造
深圳市恒锐丰科技杨生21 分钟前
CL4054HL 36V 耐压 600mA 单节锂电池线性充电芯片|SOT23‑5 小型便携充电方案
嵌入式硬件·硬件工程
ALINX技术博客34 分钟前
【期刊共读】如何应对 FPGA 短缺常态?
嵌入式硬件·fpga开发·fpga
一条破秋裤36 分钟前
STM32 学习笔记:STM32 简介与学习路线
笔记·stm32·学习
快秃头的码农1 小时前
esp32-s3 espidf实现st7789-tn+电阻屏
嵌入式硬件
电子工匠1 小时前
Altium + Quilter 搭建指南
嵌入式硬件·ai·pcb工艺
HUI-4741 小时前
EG1185|SOT23‑6 高压非隔离恒压恒流控制器,极简外围高压辅助与 LED 驱动方案
嵌入式硬件·硬件工程
louis.D.1 小时前
CMake + VS Code + J-Link
单片机
恒锐丰科技林技术员1 小时前
SS6952T 单通道 H 桥电机驱动芯片:大电流电机一体化解决方案
经验分享·嵌入式硬件·硬件工程