DAC,数模转换器,用于输出模拟信号的大哥,输出正弦波就靠它了。
大部分图片来源:正点原子HAL库课程
专栏目录:记录自己的嵌入式学习之路-CSDN博客
目录
1 特性
2 DAC数据格式
- 其中x代表通道1还是通道2。
3 触发方式
共三种:自动触发(也称禁止触发)、软件触发、外部事件触发。
3.1 自动触发
即不使能触发的情况。
特点:经过1个APB1时钟周期,数据保存寄存器的值输出到数据输出寄存器中。DHRx数据加载到DORx后,模拟输出电压将经过时间VSETTLING后可用,即写入数据输出寄存器后,经过一个建立时间后转成电压输出。
3.2 外部事件触发
包括选择了外部中断线触发或者定时器触发的情况,详见TSEL2寄存器。
特点:经过3个APB1时钟周期,数据保存寄存器的值输出到数据输出寄存器中。DHRx数据加载到DORx后,模拟输出电压将经过时间VSETTLING后可用,即写入数据输出寄存器后,经过一个建立时间后转成电压输出。
- 外部事件触发支持DMA
3.3 软件触发
写入寄存器进行软件触发后,其寄存器又会自动清零,下次再写入的时候才会进行下一次的软件触发。
特点:经过1个APB1时钟周期,数据保存寄存器的值输出到数据输出寄存器中。DHRx数据加载到DORx后,模拟输出电压将经过时间VSETTLING后可用,即写入数据输出寄存器后,经过一个建立时间后转成电压输出。
4 DMA
每个DAC通道都有DMA功能,两个DMA通道分别用于处理两个DAC通道的DMA请求。如果DMAENx位置1时,如果发生外部触发(而不是软件触发),就会产生一个DMA请求,然后DAC_DHRx寄存器的数据被转移到DAC_NORx寄存器。
5 输出电压计算
这里存疑,有可能是除以4095或者255。
6 关于输出缓冲
使能了DAC的输出缓冲后可以减小输出阻抗,适合直接驱动一些外部负载,但无法输出0V,因此在例程中并不使用。
7 输出电压配置步骤
7.1 相关HAL库函数
- 注意⚠️:HAL_DAC_SetValue和HAL_DAC_GetValue的数字量都是基于位数决定的,并不是电压值。如12bit就是0-4095,8bit就是0-255,将其与3.3V(即DAC的参考电压)进行线性换算,算出来才是输出的电压。
7.2 关键结构体
- 注意!DAC_ChannelConfTypeDef的DAC_Trigger成员宏通过文档注释中的@ref是找不到的,需要去stm32f1xx_hal_dac_ex.h文件找DACEx_trigger_selection;
8 输出正弦波
8.1 配置步骤
8.2 波形数值计算
- 形参取值范围的拟定原因解析:
- 以一个正弦函数的峰谷之间的范围为例,输出正弦波的前提是这一段范围内所有点都有自己的独立对应的x和y,因此要求(sample/2)<maxval;
- 相邻采样点的x轴间隔的数值由来:
- samples在这里就是相当于周期T,而整段的距离(姑且理解为距离吧)根据三角函数的表达式,它就是2π,所以相邻点的间隔就是2π/sample了。
- 注意⚠️:此处输出的正弦波并不通用,因为这里数值4095就是3.3V,数值0就是0V,是无法像真正的通用正弦一样随意调整幅值、相位、垂直位移的。
8.3 总体思路
- 创建一个数组,用于存放正弦函数的离散点,并使用上述公式计算离散点并对离散点进行合法性校验,如最大值不超过4095、(sample/2)<maxval等;
- 将DAC的触发设置为定时器触发信号触发;
- 初始化DMA用于传输数据,将数组作为源地址,将DAC1->DHR12R1寄存器作为目标地址;
- 调用HAL_TIMEx_MasterConfigSynchronization函数配置定时器的更新事件作为触发输出(TRGO);
- 按以上方法配置后,每发生一次定时器更新事件,DMA就会在数组中取出一个数据点写入至DAC1的DHR12R1寄存器中;
- 最终呈现的正弦波的频率为:
- 频率 = 定时器频率 ÷ 每个正弦波的离散点数目
9 PWM DAC
9.1 应用场景
STM32的DAC只有两路输出通道,有些应用可能需要多路DAC,外扩DAC成本会高不少。在精度要求不高的场合,可以用一种廉价的解决方案实现DAC输出:PWM + RC滤波器。