焦點訊息:STM32呼吸燈的PWM原理與代碼實現

2023-06-28 17:10:55 來源:ARM與嵌入式

定時器生成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_TimeBaseStructureTIM_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_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電源

#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 %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);}
審核編輯:湯梓紅

標簽:

上一篇:天天快看:如何理解運輸工具保險合同?保險人的給付義務是什么?
下一篇:最后一頁