PWM是一种对模拟信号电平进行 数字编码的方法。通过高 分辨率计数器的使用,方波的占空比被调制用来对一个具体 模拟信号的电平进行编码。PWM信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的。通的时候即是直流供电被加到负载上的时候,断的时候即是供电被断开的时候。只要带宽足够,任何模拟值都可以使用PWM进行编码。
脉宽调制(PWM,Pulse Width Modulation)是利用微处理器的数字输出来对模拟威廉希尔官方网站 进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中 。
PWM的一个优点是从处理器到 被控系统信号都是数字形式的,再进行数模转换。可将噪声影响降到最低(可以跟电脑一样)。噪声只有在强到足以将逻辑1改变为逻辑0或将逻辑0改变为逻辑1时,也才能对数字信号产生影响。
1.PWM模式
用户可以使用 PWM 模式产生一个信号,其占空比由 TIMx_CCDATx 寄存器的值决定,其频率由TIMx_AR 寄存器的值决定。 并且取决于 TIMx_CTRL1.CAMSEL 的值, TIM 可以在边沿对齐模式或中央对齐模式下产生 PWM 信号。
用户可以通过设置 TIMx_CCMODx. OCxMD=110 或设置 TIMx_CCMODx.OCxMD=111 来设置 PWM 模式 1 或 PWM 模式 2。 要使能预加载寄存器,用户必须设置相应的 TIMx_CCMODx.OCxPEN。 然后设置 TIMx_CTRL1.ARPEN 自动重装载预加载寄存器。
用户可以通过设置 TIMx_CCEN.CCxP 来设置 OCx 的极性。当 TIM 处于 PWM 模式时, TIMx_CNT 和 TIMx_CCDATx 的值总是相互比较。
只有当更新事件发生时,预加载寄存器才会转移到影子寄存器。 因此,用户必须在计数器开始计数之前通过设置 TIMx_EVTGEN.UDGN 来复位所有寄存器。
1.1 PWM中央对齐模式
如果用户设置 TIMx_CTRL1.CAMSEL 等于 01、 10 或 11, PWM 中央对齐模式将被激活。 比较标志的设置取决于 TIMx_CTRL1.CAMSEL 的值。 设置比较标志的情况有 3 种,仅当计数器向上计数时,仅当计数器向下计数时,或当计数器向上计数和向下计数时。 用户不应通过软件修改 TIMx_CTRL1.DIR,它是由硬件更新的。
中央对齐 PWM 波形示例如下,波形设置为: TIMx_AR=8, PWM 模式 1 ,当计数器向下计数对应TIMx_CTRL1.CAMSEL=01 时设置比较标志。
使用中央对齐模式时用户应注意的事项如下:
计数器向上或向下计数取决于 TIMx_CTRL1.DIR 的值。 注意不要同时更改 DIR 和 CAMSEL 位
用户在中央对齐模式下不要写计数器,否则会导致意想不到的结果。 例如:
如果写入计数器的值为 0 或者是 TIMx_AR 的值,则方向会被更新,但不会产生更新事件
如果写入计数器的值大于自动重载的值,则方向不会更新
为了安全起见,建议用户在启动计数器之前设置 TIMx_EVTGEN.UDGN 以通过软件生成更新,并且在计数器运行时不要写入计数器。
1.2 PWM 边沿对齐模式
边沿对齐模式有两种配置,向上计数和向下计数。
向上计数
用户可以设置 TIMx_CTRL1.DIR=0 使计数器向上计数。
PWM 模式 1 的示例:
当 TIMx_CNT < TIMx_CCDATx 时, OCxREF 为高电平,否则为低电平。 如果 TIMx_CCDATx 中的比较值大于自动重载值,则 OCxREF 将保持为 1。相反,如果比较值为 0,则 OCxREF 将保持为 0。当 TIMx_AR=8 时, PWM 波形如下:
向下计数
用户可以设置 TIMx_CTRL1.DIR=1 使计数器向下计数。
PWM 模式 1 的示例:
当 TIMx_CNT > TIMx_CCDATx 时, OCxREF 为低电平,否则为高电平。 如果 TIMx_CCDATx 中的比较值大于自动重载值,则 OCxREF 将保持为 1。
注:若第n 个PWM 周期CCDATx 影子寄存器>=AR 值,第n+1 个PWM 周期CCDATx 的影子寄存器值是0。在第n+1 个PWM周期的计数器为0 的时刻,虽然计数器 = CCDATx 影子寄存器的值 = 0, OCxREF =‘0’,但不会产生比较事件。
2.通过PWM输出呼吸灯示例
1.根据N32G45帖子介绍可知LED硬件接口:https://bbs.elecfans.com/jishu_2320004_1_1.html
2.接下来我我们以D2、D3为例(D2、D3刚才处于TIM3的通道1和通道2上),实现PWM输出控制LED。
根据参考手册第7章可以看到,我们想要实现该功能,就需要开始TIM3的部分重映射功能。
TIM3通道的部分重映射配置如下:
RCC->APB2PCLKEN|=1<<0;//AFIO AFIO->RMP_CFG&=~(0x3<<10); AFIO->RMP_CFG|=2<<10;//开启部分重映像 AFIO->RMP_CFG&=~(0x7<<24); AFIO->RMP_CFG|=1<<24;//将PB4设置为普通IO
寄存器相关介绍可查看N32用户手册第7.4章节AFIO寄存器配置。
3.配置定时器基本功能和PWM模式。
在完成PWM模式输出时,我们需要先完成定时器的基本功能配置(开定时器时钟、设置预分频系数,设置周期时间)。
定时器相关寄存器可参考N32用户手册12.4章节。下面列举几个常用寄存器。
控制寄存器1(TIM_CTRL1)
本寄存器主要实现定时器的基本功能配置:设置计数模式、开始定时器等。
预分频器(TIM_PSC)
本寄存器设置定时器的工作频率,例如要实现计数器+1的时长为1us,则时钟的PSC=72-1;
注意:定时器的时钟线工作频率为72MHZ,即CK_PSC=72MHZ
重装载寄存器(TIM_AR)
重装载寄存器为实现定时器计数周期。
捕获比较寄存器(TIM_CCDAT1)
当为输出模式时,CCDAT寄存器用于设置占空比;当为输入模式时,CCDAT用于保存捕获到的电平时间;
捕获/比较寄存器(TIM_CCMOD)
捕获比较寄存器用于设置输入捕获模式和输出比较模式的通道参数信息。
捕获/比较使能寄存器(TIM_CCEN)
捕获比较使能寄存器用于启动通道、设置有效电平极性。
2.1 定时配置示例
完成定时器基本功能配置,设置周期时间,设置分频系数,配置通道参数,输出PWM。
/*********************************** ** **函数功能:定时器器PWM输出 **TIM3通道引脚: ** 没有重映射 部分重映射 完全重映射 ** TIM3_CH1 PA6 PB4 PC6 ** TIM3_CH2 PA7 PB5 PC7 **形参: ** chx --要开启的通道(1 --通道1,2--通道2, 3 --表示开启通道1和通道2) ** psc --预分频系数 ** ar --重装载值(即周期时间) ** ccr --占空比 ** 注意:本示例采用部分重映射功能 **作者:IT_阿水 ************************************/ void TIM3_PWM_Out(u8 chx,u16 psc,u16 ar,u16 ccr) { /*1.开时钟*/ RCC->APB2PCLKEN|=1<<3;//PB RCC->APB2PCLKEN|=1<<0;//AFIO AFIO->RMP_CFG&=~(0x3<<10); AFIO->RMP_CFG|=2<<10;//开启部分重映像 AFIO->RMP_CFG&=~(0x7<<24); AFIO->RMP_CFG|=1<<24;//将PB4设置为普通IO /*2.配置GPIO*/ GPIOB->PL_CFG&=0xFF00FFFF; GPIOB->PL_CFG|=0x00BB0000;//通用复用推挽输出模式 /*3.定时器配置*/ RCC->APB1PCLKEN|=1<<1;//TIM3 RCC->APB1PRST|=1<<1;//开启复位时钟 RCC->APB1PRST&=~(1<<1);//取消复位 TIM3->CTRL1|=1<<7; TIM3->PSC=psc-1;//预分频 TIM3->AR=ar;//重装载值 /*输出PWM配置*/ if(chx&0x1) { TIM3->CCMOD1&=~(0x3<<0);//输出 TIM3->CCMOD1|=1<<2;//快速使能 TIM3->CCMOD1|=1<<3;//预加载 TIM3->CCMOD1|=0x6<<4;//PWM0 TIM3->CCDAT1=ccr;//占空比,有效电平时间 TIM3->CCEN|=1<<0;//开启CH1 } if(chx&1<<1)//CH2 { TIM3->CCMOD1&=~(0x3<<8);//输出 TIM3->CCMOD1|=1<<10;//快速使能 TIM3->CCMOD1|=1<<11;//预加载 TIM3->CCMOD1|=0x6<<12;//PWM0 TIM3->CCDAT2=ccr;//占空比,有效电平时间 TIM3->CCEN|=1<<4;//开启CH2 } TIM3->CTRL1|=1<<0;//开启定时器 }
2.2 呼吸灯效果实现
通过调节PWM输出的占空比,实现LED呼吸灯效果。
#include "n32g45x.h" #include #include #include "led.h" #include "key.h" #include "usart.h" #include "delay.h" #include "timer.h" int main() { u8 key_val; int time=0; u8 flag=0; LED_Init(); KEY_Init(); USART_Init(115200); TIM3_PWM_Out(3,72,400,0); printf("串口初始化完成rn"); while(1) { key_val=Key_Scan(); if(key_val) { printf("串口+DMA数据发送测试示例!rn"); } if(usart1_flag) { usart1_rx_buff[usart1_cnt]='�'; printf("%s,%drn",usart1_rx_buff,usart1_cnt); if(strcmp((char *)usart1_rx_buff,"LED1_ON")==0)LED_D1=1; else if(strcmp((char *)usart1_rx_buff,"LED1_OFF")==0)LED_D1=0; else if(strcmp((char *)usart1_rx_buff,"LED2_ON")==0)LED_D2=1; else if(strcmp((char *)usart1_rx_buff,"LED2_OFF")==0)LED_D2=0; else if(strcmp((char *)usart1_rx_buff,"LED3_ON")==0)LED_D3=1; else if(strcmp((char *)usart1_rx_buff,"LED3_OFF")==0)LED_D3=0; usart1_flag=0; } if(time>=400)flag=1; else if(time<=0)flag=0; if(flag)time--; else time++; TIM3->CCDAT1=time; TIM3->CCDAT2=time; Delay_Ms(10); } }
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !