最近在做一个用STM32+ADS1256开发光纤电流传感器解调仪的工作。疫情原因,经常需要在家工作。板子虽然额外置办了一套,但实验设备没法搬回家,又苦于没有信号函数发生器,调试起来有隔靴搔痒、闭门造车之感。人脑不是电脑,百密难免一疏。
突然想到,何不用STM32闲置的DAC开发一个信号发生器来模拟传感器信号呢?自己动手,丰衣足食。做过才知道,原来这么简单!
基本原理
基于STM32的DAC可产生任意周期性波形。原理很简单:
第一步,创建一个数组,把生成波形所需的离散数据存入其中。对于正弦波这类周期性信号,一个周期的数据即可。生成波形数组的过程有点类似于ADC,包括抽样和量化两步(取整)。抽样间隔越小,将来生成的波形会越平滑。抽样后的数值需要取整。对于12位的DAC,波形数组中元素的值须在0-4095之间。信号的幅值与波形数组的值成正比,4095对应参考电压3.3V。为便于控制幅值,可以设置一个系数与数组相乘。
第二步,在定时器TIMx的触发下用DMA将数据逐个发送到DAC。DAC会再下一次更新前保持当前电平。信号的周期/频率由TIMx控制。
运行中如需修改信号的幅值或频率,可通过USART向STM32发送指令,修改系数或TIMx的参数即可。具体方法可参考这篇文章。
参数配置
根据上述原理,除了时钟等常规配置外,还需要配置4个外设:DAC,DMA,TIM,NVIC。具体参数设置图示如下:
DAC参数设置
切换到DMA Settings标签,为两个通道Add DMA并设置参数。参数如下:
DMA参数设置
特别注意:STM32的12位DAC不Half Word,上图中外设数据宽度一定要选Word。存储器的数据宽度根据波形数组的数据类型来选择,12位DAC用16位无符号数组绰绰有余,所以一般选Half Word即可。
由于系统时钟频率为72MHz,Prescaler设为72时,计数器计数间隔为1μs。波形数组准备用500个点,对于50Hz的工频信号,每个周期20ms,需要40μs发送一个数。所以计数器周期设为39(注意:计数器从0开始计数)。当然也可以Prescaler设小些,时间分辨率更高,计数值设大些。设置完上图参数后,切换到NVIC标签,开启TIM2 global interrupt。
代码编写
在main主函数中,添加如下代码:
uint16_t NUM_POINTs = 500U;
u8 SIG_AMP[2] = {2U, 8U};
uint16_t sin1[NUM_POINTs], sin2[NUM_POINTs];
double_t pi = 3.14159265358979;
for (u16 i = 0U; i<NUM_POINTs; i++) {
sin1[i] = (u16) ((1.0+sin(2.0*pi*(dt)i/NUM_POINTs))*4095.0/2.0/SIG_AMP[0]);
sin2[i] = (u16) ((1.0+sin(pi+2.0*pi*(dt)i/NUM_POINTs))*4095.0/2.0/SIG_AMP[1]);
}
括号中的“1.0+”是为了避免负值,SIG_AMP是为了控制信号幅度。通过上位机修改它的值,即可改变幅度。注意:要确保最大值不超过4095,超过的会被截断为4095!
最后一步,启动定时器TIM2和DAC的两个DMA通道。
HAL_TIM_Base_Start(&htim2);
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)sin1, 500U, DAC_ALIGN_12B_R);
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_2, (uint32_t *)sin2, 500U, DAC_ALIGN_12B_R);
while (1){
}
效果验证
查一下芯片DAC的两个通道对应的引脚(PA4和PA5),用杜邦线把信号接到需要的地方就信了。下图是用ADS1256采集到的实时波形,我用DAC的输出用来模拟传感器的信号。
实测效果图
哇哦,简直不要太完美!顺便炫耀一下我做的采集软件(用MATLAB开发的),特别是我的圆形大按钮,界面右下角那个!
总结
用DAC产生波形的原理和方法还是很简单的,尤其是基于CubeIDE来配置,只需自己编写不到10行代码,即可实现上述功能。依据上述方法,可以产生所需的任意其他波形。
发现一个有趣的现象:实测动画图中,您觉得波形是向哪边移动的呢?