STM32的PWM以及DAC功能的实现
摘要:在之前学习的STM32中,已经学会了通过给GPIO端口赋值高低电平来控制LED的亮灭。但在日常生活中,并不是所有的信号都是恒定的值,而是一个连续变化的信号,那么该如何实现呢?
一、 PWM
1.1 原理
脉冲宽度调制(PWM),是英文“ Pulse width modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽度的控制,PWM原理如图1411所示:
图14.1.1就是一个简单的PWM原理示意图。图中,我们假定定时器工作在向上计数PWM模式,且当CNI<CCRx
时,输出0
,当CNI>=CCRx
时输出1
.那么就可以得到如上的PWM示意图:当CNT值小于cCRx的时候,IO输出低电平(0),当CNT值大于等于CCRx的时候IO输出高电平(1),当CNT达到ARR值的时候,重新归零,然后重新向上计数,依次循环改变CCRⅹ的值,就可以改变PWM输出的占空比,改变ARR的值,就可以改变PWM输出的频率,这就是PWM输出的原理。
要使STM32的通用定时器TMx产生PWM输出,除了上一章介绍的寄存器外,我们还会用到3个寄存器,来控制PWM的。这三个寄存器分别是:捕获/比较模式寄存器( TIMX CCMR/2)、捕获/比较使能寄存器( TIMX CCER)捕获/比较寄存器( TIMX CCR1~4)。
详细资料请参考学习《STM32F1开发指南》第十四章
1.2 PWM输出
具体工程参考正点原子 PWM输出实验
详细资料请参考学习《STM32F1开发指南》第十四章
部分相关代码如下:
- main.c
int main(void)
{
u16 led0pwmval=0;
u8 dir=1;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init(); //LED端口初始化
TIM3_PWM_Init(899,0); //不分频。PWM频率=72000000/900=80Khz
while(1)
{
delay_ms(10);
if(dir)led0pwmval++;
else led0pwmval--;
if(led0pwmval>300)dir=0;
if(led0pwmval==0)dir=1;
TIM_SetCompare2(TIM3,led0pwmval);
}
}
- TIM3_PWM_Init函数
void TIM3_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5
//设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
//初始化TIM3 Channel2 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC2
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIM3
}
- 实验下载效果显示
通过实验观察到DS0不停的由暗变到亮,然后又从亮变到暗,时间约为3秒钟。
- 通过PB5观察PWM波形
通过PWM的占空比的不断变化,使得输入LED灯由亮变暗,由暗变亮的效果。
二、 DAC
2.1 简介
DAC为数字模拟转换模块,故名思议,它的作用就是把输入的数字编码,转换成对应的模拟电压输出,它的功能与ADC相反。在常见的数字信号系统中,大部分传感器信号被化成电压信号,而ADC把电压模拟信号转换成易于计算机存储、处理的数字编码,由计算机处理完成后,再由DAC输岀电压模拟信号,该电压模拟信号常常用来驱动某些执行器件,使人类易于感知。如音频信号的采集及还原就是这样一个过程。
STM32具有片上DAC外设,它的分辨率可配置为8位或12位的数字输入信号,具有两个DAC输出通道,这两个通道互不影响,每个通道都可以使用DMA功能,都具有出错检测能力,可外部触发。
2.2 功能框图
整个DAC模块围绕框图下方的“数字至模拟转换器ⅹ”展开,它的左边分别是参考电源的引脚:VDDA、VSSA及Vrer+,其中SIM32的DAC规定了它的参考电压Vnef+输入范围为2.4——3.3V。“数字至模拟转换器κ”的输入为DAC的数据寄存器“DORx”的数字编码,经过它转换得的模拟信号由图中右侧的“ DAC OUTX”输出。而数据寄存器“DORx”又受“控制逻辑”支配,它可以控制数据寄存器加入一些伪噪声信号或配置产生三角波信号。
图中的左上角为DAC的触发源,DAC根据触发源的信号来进行DAC转换,其作用就相当于DAC转换器的开关,它可以配置的触发源为外部中断源触发、定时器触发或软件控制触发。如本章实验中需要控制正弦波的频率,就需要定时器定时触发DAC进行数据转换。
具体参考《零死角玩转STM32-F103指南者》第32章
2.3 DAC 初始化结构体简介
在ST的标准库中,把控制DAC相关的各种配置封装到了结构体 DAC InitTypeDef中,它主要包含了 DAC CR控制寄存器的各寄存器位的配置。
- DAC_InitTypeDef 结构体
typedef struct {
/*DAC 触发方式 */
uint32_t DAC_Trigger;
/*是否自动输出噪声或三角波 */
uint32_t DAC_WaveGeneration;
/*选择噪声生成器的低通滤波或三角波的幅值 */
uint32_t DAC_LFSRUnmask_TriangleAmplitude;
/*选择是否使能输出缓冲器 */
uint32_t DAC_OutputBuffer;
} DAC_InitTypeDef;
DAC_Trigger
本成员用于配置DAC 的触发模式,当DAC 产生相应的触发事件时,才会把DHRx 寄存器的值转移到DORx 寄存器中进行转换。DAC_WaveGeneration
本成员用于设置是否使用DAC 输出伪噪声或三角波
(DAC_WaveGeneration_None/Noise/Triangle),使用伪噪声和三角波输出时,DAC都会把LFSR 寄存器的值叠加到DHRx 数值上,产生伪噪声和三角波,若希望产生自定义的输出时,直接配置为DAC_WaveGeneration_None 即可。DAC_LFSRUnmask_TriangleAmplitude
本成员通过控制DAC_CR 的MAMP2 位设置LFSR 寄存器位的数据,即当使用伪噪声或三角波输出时要叠加到DHRx 的值,非噪声或三角波输出模式下,本配置无效。DAC_OutputBuffer
本结构体成员用于控制是否使能DAC 的输出缓冲
(DAC_OutputBuffer_Enable/Disable),使能了DAC 的输出缓冲后可以减小输出阻抗,适合直接驱动一些外部负载。
三、 实验
硬件:STM32F1精英版
工程:基于野火-DAC输出正弦波
进行相关调试
3.1 任务要求
1)输出一个周期112khz的正弦波(循环)。此波形驱动作用至蜂鸣器或喇叭,会呈现一个“滴…”的单音;
2)将一段数字音频歌曲数据转换为模拟音频波形输出(循环)。
3.2 工程
① 112kH正弦波输出
根据野火的实验教程进行实操便可以直接生成默认的112kHz的循环正弦波,如果要生成自定义频率的正弦波参考下图计算方法:
如果设置的频率为2kHz,则设置如下:
②数字音频歌曲数据转换为模拟音频波形输出
step 1:
step 2:
用UltraEdit打开导出的十六进制文件并进行提取加工波形数据
使用notepad++
、EXCEL等文本编辑软件在十六进制之前添加0x
下面是一个简易的python转换脚本(这里转换的文件末尾会多一个,直接手动去掉即可)
import re
path=r'"C:\Users\lt\Desktop\新建文本文档 (2).txt"' #文件路径
t=re.compile(r'\d\d')
file=open(path)
txt=file.read()
print(file.read())
file.close()
test=t.findall(txt)
file=open(path,'w')
j=0
for i in test:
i=''.join(['0x',str(i),', '])
if j == 16:
j = 0
file.write('\n') #换行
file.write(i)
j = j + 1
file.close()
显示效果:
step 3:
替换之前产生正弦波的数组
step 4:输出波形
四、 总结
现实生活中处处存在着模拟信号,但计算机存储转发处理的都是数字信号,这里就要进行数字信号和模拟信号的互相转化。模拟信号的初步学习,对信号采集以及处理输出有着重要作用,此次实操不仅在基础知识上进行了拓展学习,还对之后的系统设计以及信号采集处理做好了基础。
五、 参考资料
1.STM32F103使用TIM DMA DAC实现播放WAV音乐
2.《STM32F1开发指南》
3.《零死角玩转STM32-F103指南者》