当前位置:   article > 正文

STM32使用定时器的捕获比较功能输出PWM波,并且实现呼吸灯的效果bjtu

STM32使用定时器的捕获比较功能输出PWM波,并且实现呼吸灯的效果bjtu

一、原理

1、PWM(Pulse Width Modulation)脉冲宽度调制

一种利用脉冲宽度,即占空比实现对模拟信号进行仿真的技术,即是对模拟信号电平进行数字表示的方法

2、占空比(Duty Cycle)

指在一个周期内,高电平时间占整个信号周期的百分比,即高电平时间与周期的比值

占空比=Tp/T

其中Tp指一个周期内高电平时间,T表示的是周期

3、​​​​​定时器输出比较功能与实现(PWM)

        (1)PWM的工作原理

        *STM32中每个定时器有4个输入/输出通道:TIMx_CH1-TIMx_CH4

        *每个通道对应1个捕获/比较寄存器TIMx_CRRx,将寄存器值和计数器值相比较,通过比较结果输出高低电平,从而得到PWM信号

        *脉冲宽度调制模式可以产生一个由TIMx_ARR寄存器确定频率由TIM_CRRx寄存器确定占空比的信号

        (2)原理流程及示意图

        *在PWM的一个周期内,定时器从0开始 向上计数,在0-t1时间段,定时器计数器 TIMx_CNT值小于TIMx_CCRx值,输出 低电平;

        *在t1-t2时间段,定时器计数器 TIMx_CNT值大于TIMx_CCRx值,输出 高电平;

        *当定时器计数器的值TIMx_CNT达到 ARR时,定时器溢出,重新从0开始向上 计数,如此循环。

        (3)​​​​​​​实际输出电平

实际输出的电平是由输出极性和电平是否有效共同决定的。

举例:如果此时电平为有效,且配置为输出高级性,那么实际输出的电平是高;如果此时电平为有效,且配置为输出低级性,那么实际输出的电平是低。

STM32中PWM共有两种输出模式:PWM1和PWM2

假设此时配置为PWM模式2,输出高级性,如图所示,当TIMx_CNT<TIMx_CCRx的时候,是无效电平,那么输出为0,即低电平;当TIMx_CNT>TIMx_CCRx的时候,是有效电平,那么输出为1,即高电平。

初始状态下:

PWM1 Low  ——高

PWM2 High ——高

二、实验案例1

内容及要求

1)利用TIM2的通道2输出PWM信号,控制LED发光管H40亮灭;

2)无按键操作时,将PWM周期、高电平时间交替显示在4LED数码管上(单位

ms),交替显示频率1Hz左右;

3)按键操作更改PWM周期或占空比,并将更改结果实时显示在4LED数码管上:

利用KEY1(减少)和KEY2(增加)调整PWM周期; KEY3(减少)和KEY4(增加)

调整PWM占空比(0-100%);PWM最小周期为100ms,最大周期为1000ms

程序流程图如下:

部分代码演示(仅供参考):

1、定时器配置

  1. #include "timer.h"
  2. #include "stm32f10x.h"
  3. void TIM2_PWM_Init(u16 arr, u32 psc)
  4. {
  5. GPIO_InitTypeDef GPIO_InitStructure;
  6. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  7. TIM_OCInitTypeDef TIM_OCInitStructure;
  8. // 配置PWM输出通道,开启TIM2时钟-----------------------------------------------------------------------------
  9. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能定时器TIM2的时钟
  10. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); // 使能GPIOA和AFIO时钟
  11. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; // GPIO引脚设置PA1
  12. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出模式
  13. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  14. GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA
  15. // TIM2初始化------------------------------------------------------------------------------------------------
  16. TIM_TimeBaseStructure.TIM_Period = arr; // 设置自动重载值
  17. TIM_TimeBaseStructure.TIM_Prescaler = psc; // 设置用来作为TIM2时钟频率的预分频值
  18. TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 设置时钟分割
  19. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // TIM向上计数模式
  20. TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // 初始化TIM2;
  21. // 设置TIM2_CH2的PWM模式,使能TIM2的CH2输出--------------------------------------------------------------------
  22. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; // 设置定时器模式:PWM1模式
  23. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 比较输出使能
  24. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // 设置输出极性
  25. TIM_OCInitStructure.TIM_Pulse=0; // 设置占空比,由于main函数里会重新初始化,这里取0
  26. TIM_OC2Init(TIM2, &TIM_OCInitStructure); // 初始化TIM2 OC2
  27. TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); // 使能TIM2在CCR2上的预装载寄存器
  28. TIM_ARRPreloadConfig(TIM2,ENABLE);
  29. TIM_Cmd(TIM2, ENABLE); // 使能TIM2
  30. }

2、按键配置

  1. #include "stm32f10x.h"
  2. #include "key.h"
  3. #include "sys.h"
  4. #include "delay.h"
  5. //按键初始化函数
  6. void KEY_Init(void) //IO初始化
  7. {
  8. GPIO_InitTypeDef GPIO_InitStructure;
  9. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE);//使能PORTA,PORTC时钟
  10. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; //KEY2 KEY1 KEY4对应引脚
  11. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
  12. GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIOC1,2,3
  13. //初始化 WK_UP-->GPIOA.0 下拉输入
  14. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//KEY3对应引脚
  15. //GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉
  16. GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0
  17. }
  18. //按键处理函数
  19. //返回按键值
  20. //mode:0,不支持连续按;1,支持连续按;
  21. //0,没有任何按键按下
  22. //1,KEY0按下
  23. //2,KEY1按下
  24. //3,KEY2按下
  25. //4,WKUP按下 WK_UP
  26. //注意此函数有响应优先级,KEY0>KEY1>KEY2>WK_UP!!
  27. u8 KEY_Scan(u8 mode)
  28. {
  29. static u8 key_up=1;//按键按松开标志
  30. if(mode)key_up=1; //支持连按
  31. if(key_up&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0))
  32. {
  33. delay_ms(10);//去抖动
  34. key_up=0;
  35. if(KEY1==0)return 1;
  36. else if(KEY2==0)return 2;
  37. else if(KEY3==0)return 3;
  38. else if(KEY4==0)return 4;
  39. }else if(KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1)key_up=1;
  40. return 0;// 无按键按下
  41. }

3、主程序设计

  1. #include "stm32f10x.h"
  2. #include "timer.h"
  3. #include "delay.h"
  4. #include "key.h"
  5. #include "exti.h"
  6. #include "74HC595_LED.h"
  7. u16 ccr=5000; // 设置高电平时间为5000(预分频后为500ms)
  8. u16 arr=10000; // 设置定时器设置自动重载值(即周期)为10000(预分频后为1s)
  9. float pulse=0.5; // 设置占空比
  10. uint8_t u8DispBuf[5]={0,0x3f,0x06,0x4f,0x66};
  11. //------------------------------------------------------------------------------------------
  12. // 主函数设计-------------------------------------------------------------------------------
  13. int main(void)
  14. {
  15. Init74HC595(); // 数码管初始化
  16. delay_init(); // delay函数初始化
  17. KEY_Init(); // 按键初始化
  18. EXTIX_Init(); // 外部中断初始化
  19. TIM2_PWM_Init(pulse*arr,7200-1); // 定时器输出PWM波初始化
  20. TIM_SetAutoreload(TIM2,arr); //更新定时器初始周期为1000ms
  21. while(1)
  22. {
  23. int i=0;
  24. TIM_SetCompare2(TIM2,ccr); // 配置高电平时间
  25. ccr=pulse*arr; //更新ccr的值
  26. TIM_SetCompare2(TIM2,ccr); // 配置高电平时间
  27. for(i = 0;i <100;i++) // 数码管显示arr/10的值,即显示周期,显示0.5s
  28. {
  29. u8DispBuf[4]=(arr/10)%10;
  30. u8DispBuf[3]=(arr/100)%10;
  31. u8DispBuf[2]=(arr/1000)%10;
  32. u8DispBuf[1]=(arr/10000)%10;
  33. DispUpdate();
  34. }
  35. for(i = 0;i <100;i++) // 数码管显示ccr/10的值,即显示高电平时长,显示0.5s
  36. {
  37. u8DispBuf[4]=(ccr/10)%10;
  38. u8DispBuf[3]=(ccr/100)%10;
  39. u8DispBuf[2]=(ccr/1000)%10;
  40. u8DispBuf[1]=(ccr/10000)%10;
  41. DispUpdate();
  42. }
  43. }
  44. }
  45. // 按键实现外部中断来调整周期和占空比--------------------------------------------------------------------
  46. // 外部中断1服务程序
  47. void EXTI1_IRQHandler(void)
  48. {
  49. delay_ms(10); // 消抖
  50. if(KEY2==0 && arr<10000)
  51. {
  52. arr+=1000;
  53. TIM_SetAutoreload(TIM2,arr); // 重装arr
  54. }
  55. EXTI_ClearITPendingBit(EXTI_Line1); //清除LINE1上的中断标志位
  56. }
  57. //外部中断2服务程序
  58. void EXTI2_IRQHandler(void)
  59. {
  60. delay_ms(10); // 消抖
  61. if(KEY1==0 && arr>1000)
  62. {
  63. arr-=1000; // 周期减少500(即减少50ms)
  64. TIM_SetAutoreload(TIM2,arr); // 重装arr
  65. }
  66. EXTI_ClearITPendingBit(EXTI_Line2); //清除LINE2上的中断标志位
  67. }
  68. // 外部中断0服务程序
  69. void EXTI0_IRQHandler(void)
  70. {
  71. delay_ms(10); // 消抖
  72. if(KEY3==0 && pulse>0)
  73. {
  74. pulse-=0.2; //占空比-10%
  75. // TIM_SetCompare2(TIM2,ccr); 此处不用重设高电平时间,在main函数里会进行重设
  76. }
  77. EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位
  78. }
  79. //外部中断3服务程序
  80. void EXTI3_IRQHandler(void)
  81. {
  82. delay_ms(10); // 消抖
  83. if(KEY4==0 && pulse<1)
  84. {
  85. pulse+=0.2; // 占空比+10%
  86. // TIM_SetCompare2(TIM2,ccr); 此处不用重设高电平时间,在main函数里会进行重设
  87. }
  88. EXTI_ClearITPendingBit(EXTI_Line3); //清除LINE3上的中断标志位
  89. }

三、呼吸灯

实现原理

        1. 视觉暂留

人眼在观察景物时,光信号传入大脑神经,需经过一段短暂的时间,光的作用结束后,视觉形象并不立即消失,这种残留的视觉称“后像”,视觉的这一现象则被称为“视觉暂留”。

        2.呼吸灯原理

利用人眼的视觉暂留效应,将定时器的定时周期设定为20ms,通过PWM调制,改变占空比控制一个周期内高低电平的持续时间比例,并将PWM波输出给LED灯。

在这个定时周期下可以体现人眼的视觉暂留效果,此时人眼无法分辨高低电平的变换,因此人眼看见的LED灯始终处于亮/灭的两种状态。但可以通过改变高低电平持续时间的比例,即改变占空比,调节LED灯的亮暗程度。

当占空比低时,高电平持续时间短,在视觉暂留下体现出灯的亮度较低,反之则较高。通过持续循环改变占空比,即可以实现呼吸灯的效果。  

代码演示(仅供参考):

1、定时器配置(同上)

2、呼吸灯主函数代码

  1. #include "stm32f10x.h"
  2. #include "timer.h"
  3. #include "delay.h"
  4. // 主函数设计-------------------------------------------------------------------------------
  5. int main(void)
  6. {
  7. u8 pwd=1; //用于切换渐变状态
  8. int CCR=0; //比较寄存器值CRR
  9. delay_init(); // delay函数初始化
  10. TIM2_PWM_Init(200-1,7200-1); // 定时器输出PWM波初始化,一个定时周期为20ms
  11. while(1)
  12. {
  13. if(pwd) //状态1:递减
  14. {
  15. CCR++;
  16. TIM_SetCompare2(TIM2,CCR); // 配置高电平时间
  17. delay_ms(10); //使LED灯亮一段时间,让实验结果更加明显
  18. if(CCR>200)pwd=0;
  19. }
  20. else //状态2:递增
  21. {
  22. CCR--;
  23. TIM_SetCompare2(TIM2,CCR); // 配置高电平时间
  24. delay_ms(10);
  25. if(CCR==0)pwd=1;
  26. }
  27. }
  28. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/562180
推荐阅读
相关标签
  

闽ICP备14008679号