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 备料」。频率动定时器、平滑动表长,对齐格式和参考电压卡死,波形才干净不乱。

相关推荐
Jason_zhao_MR3 小时前
Linux与实时控制如何兼得
linux·嵌入式硬件·机器人·工业控制
BSD_CGQ4 小时前
FSR传感器STM32 DMA高速采样工程实现(高精度、低CPU占用)
stm32·单片机·嵌入式硬件·压力传感器·源头工厂·柔性薄膜压力传感器·可变电阻压力传感器
芯岭技术郦7 小时前
32 位 ARM® Cortex®-M0+ 内核MCU普冉MS32C001-C
单片机·嵌入式硬件
骑着毛驴数星星7 小时前
STM32 CAN波特率与采样点计算,基于HAL库
stm32·单片机·嵌入式硬件
zhangrelay8 小时前
Arduino-MEGA-ESP单片机理论课笔记02-硬件-2026
笔记·单片机·嵌入式硬件
艾芯微科技8 小时前
钰泰 ETA5055V330DD1E|SOT-223 3.3V LDO,低压差、大电流线性稳压选型分享
网络·单片机·嵌入式硬件·集成测试·51单片机
新晨单片机设计9 小时前
S023A-基于STM32单片机智能插座【Proteus仿真+Keil程序+原理图】
stm32·单片机·proteus·单片机智能插座
威嵌神州10 小时前
恩智浦 T1022 性能怎么样?QorIQ 架构中端工业嵌入式优势解析
嵌入式硬件
weixin_6906547410 小时前
龙迅#LT9611EX 现MIPI转HDMI1.4功能主推型号,分辨率高达4K30HZ,IC内置I2C无需外部12C控制。
嵌入式硬件·音视频·信号处理
EatFans10 小时前
全栈自造 Status Deck(四):硬件终于到了,从点亮屏幕到跑通第一个 Demo
单片机·物联网