你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32呼吸灯的PWM原理与代码实现

[复制链接]
STMCU小助手 发布时间:2022-6-22 19:00
用定时器生成PWM波
    PWM全称是Pulse Width Modulation,通过控制高频信号的占空比,眼睛当成低通滤波器,可以控制亮暗。再循环更改pwm的阈值,就弄出了呼吸的效果。
    这里采用一个比较简单的方法生成PWM波:设置定时器中断然后根据阈值判断置高和置低。


程序流程
  • 开启外设时钟(GPIO和TIM)

  1. void RCC_Configuration(void)               
  2. {
  3.      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);                                                      
  4.      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4|RCC_APB1Periph_TIM3, ENABLE);
  5. }
复制代码


  • 配置GPIO
  • 配置时钟, 使能中断(计数阈值,预分频,时钟分频,计数模式)
    1. void tim3()                           //配置TIM3为基本定时器模式 ,约10us触发一次,触发频率约100kHz
    2. {
    3. TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;   //定义格式为TIM_TimeBaseInitTypeDef的结构体的名字为TIM_TimeBaseStructure  
    4. TIM_TimeBaseStructure. TIM_Period =9;         //配置计数阈值为9,超过时,自动清零,并触发中断
    5. TIM_TimeBaseStructure.TIM_Prescaler =71;     //    时钟预分频值,除以多少
    6. TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;  // 时钟分频倍数
    7. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  // 计数方式为向上计数
    8. TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);      //  初始化tim3
    9. TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除TIM3溢出中断标志
    10. TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //  使能TIM3的溢出更新中断
    11. TIM_Cmd(TIM3,ENABLE);                     //           使能TIM3
    12. }
    复制代码


  • 配置中断优先级

  1. void nvic()                                 //配置中断优先级
  2. {   
  3. NVIC_InitTypeDef NVIC_InitStructure;  //    //   命名一优先级变量
  4. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);    //     将优先级分组方式配置为group1,有2个抢占(打断)优先级,8个响应优先级
  5. NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //该中断为TIM4溢出更新中断
  6. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//打断优先级为1,在该组中为较低的,0优先级最高
  7. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 响应优先级0,打断优先级一样时,0最高
  8. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;        //  设置使能
  9. NVIC_Init(&NVIC_InitStructure);                        //  初始化
  10. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //要用同一个Group
  11. NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 溢出更新中断
  12. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//    打断优先级为1,与上一个相同,不希望中断相互打断对方
  13. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;     //  响应优先级1,低于上一个,当两个中断同时来时,上一个先执行
  14. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  15. NVIC_Init(&NVIC_InitStructure);
  16. }
复制代码


  • 写中断服务函数

代码实现
    为了方便按键检测,除了TIM3配置PWM波之外,TIM4用来检测是否有输入。由于使用开漏输出,这里使用5V电源。
  1. #include "stm32f10x.h"
  2. #include "math.h"
  3. #include "stdio.h"

  4. u8  counter=0;
  5. int  pwm=100;
  6. int flag=0;
  7. int mode =0;
  8. int velocity =0;
  9. int turning=1;

  10. void RCC_Configuration(void);    //时钟初始化,开启外设时钟
  11. void GPIO_Configuration(void);   //IO口初始化,配置其功能
  12. void tim3(void);                 //定时器tim4初始化配置
  13. void tim4(void);                 //定时器tim4初始化配置
  14. void nvic(void);                 //中断优先级等配置
  15. void exti(void);                 //外部中断配置
  16. void delay_nus(u32);           //72M时钟下,约延时us
  17. void delay_nms(u32);            //72M时钟下,约延时ms
  18. void breathing(int velocity){
  19.         switch(velocity){
  20.                 case 0:
  21.                     if(flag)
  22.                             pwm +=1;
  23.                             if(pwm>240) flag=0;
  24.                     if(flag == 0){
  25.                             pwm -=1;
  26.                             if(pwm<10) flag=1;
  27.                     }
  28.                     break;
  29.                 case 1:
  30.                     if(flag)
  31.                             pwm +=2;
  32.                             if(pwm>240) flag=0;
  33.                     if(flag == 0){
  34.                             pwm -=2;
  35.                             if(pwm<10) flag=1;
  36.                     }
  37.                     break;
  38.                 case 2:
  39.                     if(flag)
  40.                             pwm +=3;
  41.                             if(pwm>240) flag=0;
  42.                     if(flag == 0){
  43.                             pwm -=3;
  44.                             if(pwm<10) flag=1;
  45.                     }
  46.                     break;
  47.         }
  48. }


  49. void assert_failed(uint8_t* file, uint32_t line)
  50. {
  51.     printf("Wrong parameters value: file %s on line %d\r\n", file, line);
  52.     while(1);
  53. }

  54. void TIM4_IRQHandler(void)   //TIM4的溢出更新中断响应函数 ,读取按键输入值,根据输入控制pwm波占空比
  55. {
  56.         u8 key_in1=0x01,key_in2=0x01;
  57.         TIM_ClearITPendingBit(TIM4,TIM_IT_Update);//     清空TIM4溢出中断响应函数标志位
  58.         key_in1= GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_12);  // 读PC12的状态
  59.         key_in2= GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13);// 读PC13的状态
  60.         if(key_in1 && key_in2) turning =1;
  61.         breathing(velocity);
  62.         if(key_in1==0 && turning){
  63.                 turning =0;
  64.         velocity = (velocity + 1) % 3;
  65.     }//调速度
  66.     if(key_in2==0 && turning){
  67.                 turning =0;
  68.         mode = (mode + 1) % 3;
  69.     }//调颜色
  70. }   


  71. void TIM3_IRQHandler(void)      //    //TIM3的溢出更新中断响应函数,产生pwm波
  72. {
  73.         TIM_ClearITPendingBit(TIM3,TIM_IT_Update);  //   //  清空TIM3溢出中断响应函数标志位
  74.         if(counter==255)            //counter 从0到255累加循环计数,每进一次中断,counter加一
  75.             counter = 0;
  76.         else
  77.             counter +=1;
  78.         if(mode == 0){
  79.             if(counter < pwm)              //当counter值小于pwm值时,将IO口设为高;当counter值大于等于pwm时,将IO口置低
  80.                 GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1); //将PC14 PC15置为高电平
  81.             else
  82.                         GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);     // 将PC14 PC15置为低电平
  83.         }
  84.         if(mode == 1)
  85.         {
  86.             if(counter < pwm)              //当counter值小于pwm值时,将IO口设为高;当counter值大于等于pwm时,将IO口置低
  87.                 GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2); //将PC14 PC15置为高电平
  88.             else
  89.                         GPIO_ResetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);     // 将PC14 PC15置为低电平
  90.         }  
  91.         if(mode ==2){
  92.             if(counter < pwm)              //当counter值小于pwm值时,将IO口设为高;当counter值大于等于pwm时,将IO口置低
  93.                 GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0); //将PC14 PC15置为高电平
  94.             else
  95.                 GPIO_ResetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0); // 将PC14 PC15置为低电平
  96.         }
  97. }   


  98. int main(void)
  99. {
  100.     RCC_Configuration();                                                                    
  101.   GPIO_Configuration();                        
  102.     tim4();
  103.     tim3();
  104.   nvic();
  105.     while(1)
  106.     {
  107.     }   
  108. }   

  109. void delay_nus(u32 n)       //72M时钟下,约延时us
  110. {
  111.   u8 i;
  112.   while(n--)
  113.   {
  114.     i=7;
  115.     while(i--);
  116.   }
  117. }


  118. void delay_nms(u32 n)     //72M时钟下,约延时ms
  119. {
  120.     while(n--)
  121.       delay_nus(1000);
  122. }


  123. void RCC_Configuration(void)                 //使用任何一个外设时,务必开启其相应的时钟
  124. {
  125.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);    //使能APB2控制外设的时钟,包括GPIOC, 功能复用时钟AFIO等,                                                                              
  126.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4|RCC_APB1Periph_TIM3, ENABLE); //使能APB1控制外设的时钟,定时器tim3、4,其他外设详见手册            
  127. }


  128. void GPIO_Configuration(void)            //使用某io口输入输出时,请务必对其初始化配置
  129. {
  130.     GPIO_InitTypeDef GPIO_InitStructure;   //定义格式为GPIO_InitTypeDef的结构体的名字为GPIO_InitStructure  
  131.                                           //typedef struct { u16 GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; GPIOMode_TypeDef GPIO_Mode; } GPIO_InitTypeDef;
  132.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;    //配置IO口的工作模式为上拉输入(该io口内部外接电阻到电源)
  133.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //配置IO口最高的输出速率为50M
  134.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13;  //配置被选中的管脚,|表示同时被选中
  135.     GPIO_Init(GPIOC, &GPIO_InitStructure);                  //初始化GPIOC的相应IO口为上述配置,用于按键检测
  136.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;       //配置IO口工作模式为 推挽输出(有较强的输出能力)
  137.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //配置IO口最高的输出速率为50M
  138.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2;  //配置被选的管脚,|表示同时被选中
  139.     GPIO_Init(GPIOA, &GPIO_InitStructure);        //初始化GPIOA的相应IO口为上述配置
  140.     GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //失能STM32 JTAG烧写功能,只能用SWD模式烧写,解放出PA15和PB中部分IO口
  141. }


  142. void tim4()                           //配置TIM4为基本定时器模式,约10ms触发一次,触发频率约100Hz
  143. {
  144.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;   //定义格式为TIM_TimeBaseInitTypeDef的结构体的名字为TIM_TimeBaseStructure  
  145.     TIM_TimeBaseStructure. TIM_Period =9999;          // 配置计数阈值为9999,超过时,自动清零,并触发中断
  146.     TIM_TimeBaseStructure.TIM_Prescaler =71;         //  时钟预分频值,除以多少
  147.     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频倍数
  148.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 计数方式为向上计数
  149.     TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);      //  初始化tim4
  150.     TIM_ClearITPendingBit(TIM4,TIM_IT_Update); //清除TIM4溢出中断标志
  151.     TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);   //  使能TIM4的溢出更新中断
  152.     TIM_Cmd(TIM4,ENABLE);                //        使能TIM4
  153. }


  154. void tim3()                           //配置TIM3为基本定时器模式 ,约10us触发一次,触发频率约100kHz
  155. {
  156.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;   //定义格式为TIM_TimeBaseInitTypeDef的结构体的名字为TIM_TimeBaseStructure  
  157.     TIM_TimeBaseStructure. TIM_Period =9;         //配置计数阈值为9,超过时,自动清零,并触发中断
  158.     TIM_TimeBaseStructure.TIM_Prescaler =71;     //    时钟预分频值,除以多少
  159.     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;  // 时钟分频倍数
  160.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  // 计数方式为向上计数
  161.     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);      //  初始化tim3
  162.     TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除TIM3溢出中断标志
  163.     TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //  使能TIM3的溢出更新中断
  164.     TIM_Cmd(TIM3,ENABLE);                     //           使能TIM3
  165. }


  166. void nvic()                                 //配置中断优先级
  167. {   
  168.      NVIC_InitTypeDef NVIC_InitStructure;  //    //   命名一优先级变量
  169.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);    //     将优先级分组方式配置为group1,有2个抢占(打断)优先级,8个响应优先级
  170.      NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //该中断为TIM4溢出更新中断
  171.      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//打断优先级为1,在该组中为较低的,0优先级最高
  172.      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 响应优先级0,打断优先级一样时,0最高
  173.      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;        //  设置使能
  174.      NVIC_Init(&NVIC_InitStructure);                        //  初始化
  175.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //要用同一个Group
  176.      NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 溢出更新中断
  177.      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//    打断优先级为1,与上一个相同,不希望中断相互打断对方
  178.      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;     //  响应优先级1,低于上一个,当两个中断同时来时,上一个先执行
  179.      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  180.      NVIC_Init(&NVIC_InitStructure);
  181. }
复制代码


收藏 评论0 发布时间:2022-6-22 19:00

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版