背景
让单片机生成各种波可以说是非常常见的情况,从基础的PWM到SVPWM,SIN。可以说DAC也是非常重要的一环。
假设我需要一个500K的sin函数怎么办呢?于是就需要用到一个东西就是定时器触发DAC,我们也可以用DMA将大量的数据搬到DAC的寄存器里面,实现非常流畅的效果。
好了,废话少说下面我们开始吧
配置流程
DAC
首先我们看一下那些定时器可以触发DAC
大家看到TRGO就蒙了,这啥玩意?怎么听都没听过?
别怕,其实这个事件是专门用于STM32用于触发内部东西的一个事件,
大家也不要慌,这个事件配置起来非常简单,比PWM配置的还简单,这里我就以TIM4的TRGO举例了。
DMA
这里大家可以看一看,没有什么特别重要的东西
可以看到的是DAC全都在DMA1上面而ADC全都在DMA2上,也就是说我们可以做到ADC一边读,DAC一边写,而我经过实践也确实证明了这个方案可行。
寄存器
TIM4
TIM4->CR2
主要是第4位
这里将模式写成2——更新模式
对就这么简单,配置好模式,设置好分频和重装值就能触发DAC了
DAC
DAC->CR
主要是第4位
配置好相关事件,看好注意事项。
第12位
第0,1位
第3位
DMA
DMA1_Stream5 ->CR
第6-14位
相信用到这个的盆友都非常了解DMA了,我就不过多解释了。我配置的是存储器到外设模式,禁止循环模式,外设指针固定,存储器指针递增,一次传输半个字节。
这里注意一下,DAC一共有四个数据寄存器分别是12位的两个,8位的两个。大家注意指定好放在哪个寄存器里面,否则输出电压和理论值是不一样的。
程序
TIM4
void tim4_pwm(unsigned int arr4,unsigned int psc4)
{
RCC->APB1ENR|=1<<2; //TIM4时钟使能
TIM4->ARR=arr4; //设定计数器自动重装值
TIM4->PSC=psc4; //设定计数器分频
TIM4->CR2|=2<<4; //触发TRGO
TIM4->CR1|=0x01; //使能定时器4
}
DAC
void init_alladc(void)
{
RCC->APB1ENR|=1<<29;//开启DAC时钟
RCC->AHB1ENR|=1<<0;//开启GPIOA时钟
}
void init_dac1(void)
{
GPIOA->MODER|=3<<8;//PA4模拟输入
GPIOA->PUPDR|=2<<8;//PA4下拉模式
DAC->CR|=1<<0;//使能DAC1通道
DAC->CR|=(1<<1);//使能DAC1输出通道缓冲器
DAC->CR|=1<<2;//DAC1通道触发使能
DAC->CR|=5<<3;//TIM4 TRGO事件触发DAC1
DAC->CR|=1<<12;//DAC1通道DMA使能
}
DMA
//初始化DMA1 组5 通道7
//DAC1
void init_DMA1_S5C7(unsigned short DACWEI)
{
DMA1_Stream5 ->CR = 0;//禁止数据流 ,才能写寄存器
//外设地址寄存器
//将所需寄存器的地址放入PAR寄存器
DMA1_Stream5 ->PAR = (unsigned int)(&DAC->DHR12R1);
//数据流地址寄存器
//M1AR仅在双通道模式下有用
//将数据所在地址给M0AR寄存器
DMA1_Stream5 ->M0AR = (unsigned int)(&(FMSin));
DMA1_Stream5 ->NDTR = DACWEI; // 一次传输数量
DMA1_Stream5 ->FCR = 0x21; //FIFO所有配置失效
DMA1_Stream5 ->CR |= 1<< 6; //储存器到外设模式
//循环模式:
//当NDTR寄存器减到0时自动重装
//单次模式(普通模式):
//NDTR减到0后停止DMA
DMA1_Stream5 ->CR |= 1<< 8; //循环模式
DMA1_Stream5 ->CR |= 0<< 9; //外设非增量模式
DMA1_Stream5 ->CR |= 1<<10; //存储器增量模式,指针增加,可用于传输数组
DMA1_Stream5 ->CR |= 1<<11; //外设数据长度:16位
DMA1_Stream5 ->CR |= 1<<13; //存储器数据长度:16位
DMA1_Stream5 ->CR |= 1<<16; //中等优先级
//突发传输
//DMA占用CPU总线时间,此时CPU无法工作
//一个节拍:传输多少次32位变量
//应用场景:从ram里读出字节
DMA1_Stream5 ->CR |= 0<<21; //外设突发单次传输
DMA1_Stream5 ->CR |= 0<<23; //存储器突发单次传输
DMA1_Stream5 ->CR |= 7<<25; //通道7
DMA1_Stream5 ->CR |= 1<<0; //使能数据流
}
结尾
还是那句话,程序上不可能无脑搬的,希望大家能汇总各家所长,理解后写出属于自己风格的驱动程序,若还有不懂的,可以评论区留言,祝愿大家学有所成。