用定时器生成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); }}
嵌入式物联网需要学的东西真的非常多,千万不要学错了路线和内容,导致工资要不上去!
无偿分享大家一个资料包,差不多150多G。里面学习内容、面经、项目都比较新也比较全!某鱼上买估计至少要好几十。
点击这里找小助理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_TimebaseInitTypeDef TIM_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); // 初始化tim3TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除TIM3溢出中断标志TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); // 使能TIM3的溢出更新中断TIM_Cmd(TIM3,ENABLE); // 使能TIM3}
- 配置中断优先级
void nvic() //配置中断优先级{ NVIC_InitTypeDef NVIC_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电源。
#include "stm32f10x.h"#include "math.h"#include "stdio.h"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时钟下,约延时usvoid delay_nms(u32); //72M时钟下,约延时msvoid 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 %drn", 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); // 将PC14 PC15置为低电平 } 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 //typedef struct { u16 GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; GPIOMode_TypeDef GPIO_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_TimebaseInitTypeDef TIM_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_TimebaseInitTypeDef TIM_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_InitTypeDef NVIC_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);}
来源:STM32嵌入式开发
本文转载自“STM32嵌入式开发”,如有侵权,请联系删除原文链接:STM32呼吸灯的PWM原理与代码实现
版权声明:本文来源网络,免费传达知识,版权归原作者所有。如涉及作品版权问题,请联系我进行删除。
,