用定时器生成PWM波
PWM全称是Pulse Width Modulation,通过控制高频信号的占空比,眼睛当成低通滤波器,可以控制亮暗。再循环更改PWM的阈值,就弄出了呼吸的效果,相关文章推荐:STM32中PWM的配置与应用详解。
这里采用一个比较简单的方法生成PWM波:设置定时器中断然后根据阈值判断置高和置低。
void TIM3_IRQHandler(void)
{
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
if(counter==255)
counter = 0;
else
counter+=1;
if(mode == 0){
if(counter < pwm)
GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);
else
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);
}
if(mode == 1)
{
if(counter < pwm)
GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);
else
GPIO_ResetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);
}
if(mode ==2){
if(counter < pwm)
GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0);
else
GPIO_ResetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0);
}
}
程序流程
-
开启外设时钟(GPIO和TIM)
void RCC_Configuration(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4|RCC_APB1Periph_TIM3, ENABLE);
}
-
配置GPIO
-
配置时钟, 使能中断(计数阈值,预分频,时钟分频,计数模式)
void tim3() //配置TIM3为基本定时器模式 ,约10us触发一次,触发频率约100kHz
{
TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;//定义格式为TIM_TimeBaseInitTypeDef的结构体的名字为TIM_TimeBaseStructure
TIM_TimeBaseStructure. TIM_Period =9; //配置计数阈值为9,超过时,自动清零,并触发中断
TIM_TimeBaseStructure.TIM_Prescaler=71;//时钟预分频值,除以多少
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频倍数
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//计数方式为向上计数
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // 初始化tim3
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除TIM3溢出中断标志
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); // 使能TIM3的溢出更新中断
TIM_Cmd(TIM3,ENABLE); // 使能TIM3
}
-
配置中断优先级
void nvic() //配置中断优先级
{
NVIC_InitTypeDefNVIC_InitStructure;////命名一优先级变量
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 将优先级分组方式配置为group1,有2个抢占(打断)优先级,8个响应优先级
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //该中断为TIM4溢出更新中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//打断优先级为1,在该组中为较低的,0优先级最高
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 响应优先级0,打断优先级一样时,0最高
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 设置使能
NVIC_Init(&NVIC_InitStructure);//初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //要用同一个Group
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 溢出更新中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;// 打断优先级为1,与上一个相同,不希望中断相互打断对方
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 响应优先级1,低于上一个,当两个中断同时来时,上一个先执行
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
-
写中断服务函数
代码实现
为了方便按键检测,除了TIM3配置PWM波之外,TIM4用来检测是否有输入。由于使用开漏输出,这里使用5V电源。
u8 counter=0;
int pwm=100;
int flag=0;
int mode =0;
int velocity =0;
int turning=1;
void RCC_Configuration(void); //时钟初始化,开启外设时钟
void GPIO_Configuration(void); //IO口初始化,配置其功能
void tim3(void); //定时器tim4初始化配置
void tim4(void); //定时器tim4初始化配置
void nvic(void); //中断优先级等配置
void exti(void); //外部中断配置
void delay_nus(u32); //72M时钟下,约延时us
void delay_nms(u32); //72M时钟下,约延时ms
void breathing(int velocity){
switch(velocity){
case 0:
if(flag)
pwm +=1;
if(pwm>240) flag=0;
if(flag == 0){
pwm -=1;
if(pwm<10) flag=1;
}
break;
case 1:
if(flag)
pwm +=2;
if(pwm>240) flag=0;
if(flag == 0){
pwm -=2;
if(pwm<10) flag=1;
}
break;
case 2:
if(flag)
pwm +=3;
if(pwm>240) flag=0;
if(flag == 0){
pwm -=3;
if(pwm<10) flag=1;
}
break;
}
}
void assert_failed(uint8_t* file, uint32_t line)
{
printf("Wrong parameters value: file %s on line %d ", file, line);
while(1);
}
void TIM4_IRQHandler(void) //TIM4的溢出更新中断响应函数 ,读取按键输入值,根据输入控制pwm波占空比
{
u8 key_in1=0x01,key_in2=0x01;
TIM_ClearITPendingBit(TIM4,TIM_IT_Update);//清空TIM4溢出中断响应函数标志位
key_in1= GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_12); // 读PC12的状态
key_in2=GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13);//读PC13的状态
if(key_in1&&key_in2)turning=1;
breathing(velocity);
if(key_in1==0 && turning){
turning =0;
velocity = (velocity + 1) % 3;
}//调速度
if(key_in2==0 && turning){
turning =0;
mode = (mode + 1) % 3;
}//调颜色
}
void TIM3_IRQHandler(void) // //TIM3的溢出更新中断响应函数,产生pwm波
{
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);////清空TIM3溢出中断响应函数标志位
if(counter==255) //counter 从0到255累加循环计数,每进一次中断,counter加一
counter = 0;
else
counter+=1;
if(mode == 0){
if(counter < pwm) //当counter值小于pwm值时,将IO口设为高;当counter值大于等于pwm时,将IO口置低
GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1); //将PC14 PC15置为高电平
else
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1); // 将PC14 PC15置为低电平
}
if(mode == 1)
{
if(counter < pwm) //当counter值小于pwm值时,将IO口设为高;当counter值大于等于pwm时,将IO口置低
GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2); //将PC14 PC15置为高电平
else
GPIO_ResetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);//将PC14PC15置为低电平
}
if(mode ==2){
if(counter < pwm) //当counter值小于pwm值时,将IO口设为高;当counter值大于等于pwm时,将IO口置低
GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0); //将PC14 PC15置为高电平
else
GPIO_ResetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0); // 将PC14 PC15置为低电平
}
}
int main(void)
{
RCC_Configuration();
GPIO_Configuration();
tim4();
tim3();
nvic();
while(1)
{
}
}
void delay_nus(u32 n) //72M时钟下,约延时us
{
u8 i;
while(n--)
{
i=7;
while(i--);
}
}
void delay_nms(u32 n) //72M时钟下,约延时ms
{
while(n--)
delay_nus(1000);
}
void RCC_Configuration(void) //使用任何一个外设时,务必开启其相应的时钟
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE); //使能APB2控制外设的时钟,包括GPIOC, 功能复用时钟AFIO等,
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4|RCC_APB1Periph_TIM3, ENABLE); //使能APB1控制外设的时钟,定时器tim3、4,其他外设详见手册
}
void GPIO_Configuration(void) //使用某io口输入输出时,请务必对其初始化配置
{
GPIO_InitTypeDef GPIO_InitStructure; //定义格式为GPIO_InitTypeDef的结构体的名字为GPIO_InitStructure
//typedefstruct{u16GPIO_Pin;GPIOSpeed_TypeDefGPIO_Speed;GPIOMode_TypeDefGPIO_Mode;}GPIO_InitTypeDef;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //配置IO口的工作模式为上拉输入(该io口内部外接电阻到电源)
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //配置IO口最高的输出速率为50M
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13; //配置被选中的管脚,|表示同时被选中
GPIO_Init(GPIOC,&GPIO_InitStructure);//初始化GPIOC的相应IO口为上述配置,用于按键检测
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //配置IO口工作模式为 推挽输出(有较强的输出能力)
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //配置IO口最高的输出速率为50M
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2; //配置被选的管脚,|表示同时被选中
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA的相应IO口为上述配置
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //失能STM32 JTAG烧写功能,只能用SWD模式烧写,解放出PA15和PB中部分IO口
}
void tim4() //配置TIM4为基本定时器模式,约10ms触发一次,触发频率约100Hz
{
TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;//定义格式为TIM_TimeBaseInitTypeDef的结构体的名字为TIM_TimeBaseStructure
TIM_TimeBaseStructure. TIM_Period =9999; // 配置计数阈值为9999,超过时,自动清零,并触发中断
TIM_TimeBaseStructure.TIM_Prescaler=71;//时钟预分频值,除以多少
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频倍数
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//计数方式为向上计数
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); // 初始化tim4
TIM_ClearITPendingBit(TIM4,TIM_IT_Update); //清除TIM4溢出中断标志
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); // 使能TIM4的溢出更新中断
TIM_Cmd(TIM4,ENABLE); // 使能TIM4
}
void tim3() //配置TIM3为基本定时器模式 ,约10us触发一次,触发频率约100kHz
{
TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;//定义格式为TIM_TimeBaseInitTypeDef的结构体的名字为TIM_TimeBaseStructure
TIM_TimeBaseStructure. TIM_Period =9; //配置计数阈值为9,超过时,自动清零,并触发中断
TIM_TimeBaseStructure.TIM_Prescaler=71;//时钟预分频值,除以多少
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频倍数
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//计数方式为向上计数
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // 初始化tim3
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除TIM3溢出中断标志
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); // 使能TIM3的溢出更新中断
TIM_Cmd(TIM3,ENABLE); // 使能TIM3
}
void nvic() //配置中断优先级
{
NVIC_InitTypeDefNVIC_InitStructure;////命名一优先级变量
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 将优先级分组方式配置为group1,有2个抢占(打断)优先级,8个响应优先级
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //该中断为TIM4溢出更新中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//打断优先级为1,在该组中为较低的,0优先级最高
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 响应优先级0,打断优先级一样时,0最高
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 设置使能
NVIC_Init(&NVIC_InitStructure);//初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //要用同一个Group
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 溢出更新中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;// 打断优先级为1,与上一个相同,不希望中断相互打断对方
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 响应优先级1,低于上一个,当两个中断同时来时,上一个先执行
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。
举报投诉
-
PWM
+关注
关注
114文章
5186浏览量
213878 -
STM32
+关注
关注
2270文章
10899浏览量
355886 -
定时器
+关注
关注
23文章
3247浏览量
114764
原文标题:STM32呼吸灯的PWM原理与代码实现
文章出处:【微信号:单片机与嵌入式,微信公众号:单片机与嵌入式】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
STM32.定时器.PWM波
定时器的通道与路是什么关系???定时器可以通过不同的通道实现不同的模式输出,那么图片所说的PWM 4路输出是什么意思?如果每一路代表不同的模式,是4种不同的模式、通过不同端口输出吗?还是说相同的模式下也以4路? 仅使用一种通道产
发表于 07-17 10:01
定时器输出PWM实验
定时器输出PWM 实验
一. 实验目的利用定时器控制产生占空比可变的PWM 波。二. 实验设备及器件IBM PC 机 一台DP-51PRO
发表于 09-22 10:49
•7420次阅读
51单片机定时器实现PWM波
51单片机是可以实现PWM波输出的,原理其实都是一样的。说白了,PWM波就是让某一个引脚输出周期性连续高低电平变化的信号。那么如何用51单片机实现周期性的高低电平呢?答案就是
发表于 11-12 10:36
•13次下载
STM32CubeMX_定时器中断_PWM
文章目录前言STM32CubeMX新建工程基本定时器配置生成代码定时器中断PWM配置工程代码前言STM32CubeMX_环境搭建_GPIO_外部中断上节整理的是GPIO和外部中断, 这
发表于 12-05 13:51
•13次下载
用定时器生成PWM波的原理和方法
PWM全称是Pulse Width Modulation,通过控制高频信号的占空比,眼睛当成低通滤波器,可以控制亮暗。再循环更改pwm的阈值,就弄出了呼吸的效果。
评论