用定時器生成PWM波
PWM全稱是Pulse Width Modulation,通過控制高頻信號的占空比,眼睛當成低通濾波器,可以控制亮暗。再循環更改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);
}
原文標題:STM32呼吸燈的PWM原理與代碼實現
文章出處:【微信公眾號:硬件攻城獅】歡迎添加關注!文章轉載請注明出處。
-
PWM
+關注
關注
114文章
5144瀏覽量
213409 -
定時器
+關注
關注
23文章
3237瀏覽量
114475 -
代碼
+關注
關注
30文章
4748瀏覽量
68357
原文標題:STM32呼吸燈的PWM原理與代碼實現
文章出處:【微信號:mcu168,微信公眾號:硬件攻城獅】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論