目录
用DMA的方式将位于存储器(数组)中的数据传递给DAC的数据输出寄存器。放到存储器(数组)中的数据,可以是一段波形数据(正弦波、锯齿波等),譬如一个周期的数据,利用DMA周期性地将该数据传递到DAC,就可以实现周期性信号的输出了。
一、建立新工程
1.项目依赖的硬件
本文项目依赖的硬件开发板请参考本文作者的类似文章,比如:细说MCU用定时器控制单路DAC模块设计和输出锯齿波的实现方法-CSDN博客 https://wenchm.blog.csdn.net/article/details/140611180http:// https://wenchm.blog.csdn.net/article/details/140611180
2.配置DAC
配置DAC1,将OUT1模式(OUT1mode)选择为Connected to external pin only,也就是将DAC1的输出通道1连接到外部引脚,该引脚为PA4。因为要用定时器来触发DAC,所以将其中的Trigger选择为Timer 3 Trigger Out event。
3.配置DMA
在DMA Request条目下选择其中的DAC1_CH1。DMA请求设置中的模式(Mode)选择为Circular,让DMA循环工作;二是数据宽度(Data Width),对应外设(Peripheral),即DAC模块的那一个(左侧),要改为字(Word)。1Word有32位。虽然DAC只有12位,占用半个字就够了,但DAC的数据输出寄存器却是32位的,所以这个参数一定要设置为Word。
4.配置TIM3
在TIM3模式和配置界面中,先将模式(Mode)的时钟源(Clock Source)选择为内部时钟(Internal Clock);在定时器设置(Counter Settings)区,将预分频因子(Prescaler)设置为169,计数器周期(Counter Period)设置为9。由于配置了用TIM3来触发DAC,所以在定时器的触发事件选择(Trig-ger Event Selection TRGO)列表框中要选择Update Event。
由于本例不会用到定时器的中断,所以不用配置定时器NVIC。
如果系统时钟频率为170 MHz,则在上面设置的定时器参数下,定时器的事件更新频率将为(170×10^6)/((169+1)×(9+1))=0.1(MHz),即100 kHz。
5.选择时钟源和Debug
将高速时钟(HSE)设置为Crystal/Ceramic Resonator,使用片外时钟晶体作为HSE的时钟源。最后,在SYS中将Debug设置为Serial Wire。由于没有使用中断,所以不用配置NVIC。
6.配置系统时钟
在Clock Configuration中将系统时钟(SYSCLK)频率配置为170 MHz。
二、代码修改
1. 启动定时器和DMA
由于使用了定时器和DAC(带DMA的DAC),所以需要在初始化时启动定时器和DAC。启动定时器使用库函数HAL_TIM_Base_Start(),启动带DMA的DAC,使用库函数HAL_DAC_Start _DMA()。对这两个函数的调用,可以放到while(1)之前的注释对中:
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start(&htim3);
HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t*)SineWaveData,DAC_BUFFER_SIZE,DAC_ALIGN_12B_R);
/* USER CODE END 2 */
HAL_TIM_Base_Start()函数只有一个参数,就是定时器句柄。
HAL_DAC_Start_DMA()函数有5个参数:前面两个参数与函数HAL_DAC_Start()的参数相同,一个是DAC句柄,一个是DAC通道,这里用的是通道1,即DAC_CHANNEL_1;第三个参数是指定波形数据在存储器中的地址,用的是数组,名为SineWaveData;第四个参数用于指定数据长度,用了变量DAC_BUFFER_SIZE;最后一个参数是数据格式,用的是12位右对齐的方式(DAC_ALIGN_12B_R)。
变量DAC_CHANNEL_1和DAC_ALIGN_12B_R在HAL库中都有定义。数组SineWaveData和数据长度DAC_BUFFER_SIZE则需要来在代码中定义。这里的数据长度就是数组的长度。
将数据长度变量定义在main.h文件中:
/* USER CODE BEGIN Private defines */
#define DAC_BUFFER_SIZE (uint16_t) 50
/* USER CODE END Private defines */
2.定义输出波形数据
将数组定义在main.c中,定义为全局变量:
/* USER CODE BEGIN PV */
uint16_t SineWaveData[DAC_BUFFER_SIZE]={2047,2304,2557,2801,3034,3251,3449,3625,3776,3900,3994,4058,4090,4090,4058,3994,3900,3776,3625,3449,3251,3034,2801,2557,2304,2048,1791,1538,1294,1061,844,646,470,319,195,101,37,5,5,37,101,195,319,470,646,844,1061,1294,1538,1791};
/* USER CODE END PV */
这里给出的SineWaveData,长度为DAC_BUFFER_SIZE(在前面已将其定义为50)。这个数组中的数据,实际是一个周期的正弦信号数据;也就是说,将一个周期的正弦波形,用50个数据点来表示。
3.通过MATLAB产生这个波形数据的方法
打开MATLAB软件中,新建编辑器窗口,输入产生一个周期正弦波形的数据的程序.m,50个点/周期。
A=4096/2-1; %信号幅值
N=50; %一个周期内的数据点数
Ph=0; %初始相位
SineData = ceil(A*sin(Ph: 2*pi/N: 2*pi*(1-1/N)+Ph)+A);
Fid = fopen('SineWaveData.txt','w');
fprintf(Fid,'%d,',SineData);
fclose(Fid);
第一行中的A是指定正弦函数的幅值,由于给12位DAC数据寄存器传递的数值范围是0~4095,所以需要将波形零点抬高至最大值的一半。这里给定幅度最大值为2047。
第四句中ceil函数为取整函数;Ph:2×pi/N:2*pi*(1-1/N)+Ph是指在Ph到2*pi(1-1/N)+Ph之间分成N份,也就是步长为1/N;语句后的“+A”是指将零点抬升到A,即2047。第五、六两句是将数据存入文件SineWaveData.txt中,第七句是关闭文件。在fprintf()函数中用了'%d,',表示以十进制格式存储数据,数据之间加“,”。
如果要修改数据点数,将“N=50”中的“50”改为需要的值即可。如果要改变初始相位修改Ph的值(注意,初始相位用的是弧度,譬如90°时,Ph=pi/2)。
三、查看结果
下载到硬件中,并将程序运行起来。
由于是通过定时器的更新事件(Update Event)来触发DAC的,定时器事件的更新频率已经被设定为100 kHz,所以数组SineWaveData中的50个数将以此频率顺次取出,赋值DAC的数据输出寄存器。DMA传递50个数据需要时间为50/(100×10³)=0.5(ms)。这50个数据刚好为一个正弦周期,所以DAC产生的正弦波的频率将为2 kHz。
通过示波器测量PA4引脚上的电压,将会得到一个正弦波,频率为2 kHz(100 kHz/50)与前面的分析一致。