当前位置:   article > 正文

STM32(六):定时器PWM呼吸灯 (标准库函数)_stm32呼吸灯标准库

stm32呼吸灯标准库

前言

上一篇文章已经介绍了如何用STM32单片机中的TIMER定时器来控制LED灯的交替闪烁,实现了点灯的第五种方式。这篇文章我们来介绍一下如何用STM32单片机中的定时器的PWM波来实现LED的“呼吸”。

一、实验原理

关于定时器这边就不多加赘述,详细请看上一篇文章,链接放在下面了

STM32(五):TIMER定时器 (标准库函数)

这边我们介绍一下PWM

1.PWM的介绍

PWM的全称是脉冲宽度调制(Pulse-width modulation),是指在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效的地获得所需要的模拟参量,常应用于电机控速等领域。在STM32微控制器的应用中,PWM技术也非常重要,因为它能够提供精确的信号控制,从而达到对设备行为的精确管理。

  • 频率:一秒钟内从高电平时间在到低电平时间,再从低电平跳到高电平的瞬间次数,也就是一秒钟内有多少个PWM的周期,f = 1/Ts。
  • 周期:一秒钟内从高电平时间在到低电平时间,Ts= 1/f。
  • PWM信号产生:用于控制电机速度、灯光亮度等。
  • 占空比:一个周期内高电平时间和总时间的比值,Ton/Ts。
  • 分辨率:占空比变化步距。

2.PWM参数计算公式

PWM主要参数计算公式如下所示:

  • PWM频率 Freq=CK_PSC/[(PSC+1) (ARR+1)], CK_PSC=72M
  • PWM占空比=CCR/(ARR+1)
  • PWM分辨率=1/(ARR+1)

3.PWM工作原理

以下是PWM输入模式时序工作原理图,以下对其符号进行简单的解释说明

  1. T1:PWM信号的波形,显示了信号的高电平和低电平状态。
  2. TIMx_CNT :定时器的计数器(Counter),它的值会在定时器时钟的脉冲下从0开始增加,直到达到自动重载寄存器(ARR)的值后归零,循环开始新的周期。
  3. TIMx_CCR1TIMx_CCR2 :捕捉/比较寄存器(Capture/Compare Register),用于设置PWM的高电平宽度。它们的值决定了在计数器达到这些值时输出比较匹配事件,影响PWM输出信号的占空比。
  • TIMx_CCR1 设置为 0004,代表在计数器计数到4时,第一个通道的PWM输出从高电平跳变到低电平。
  • TIMx_CCR2 设置为 0002,代表在计数器计数到2时,第二个通道的PWM输出从高电平跳变到低电平。

 接下来简单介绍一下PWM信号的一个典型的边缘对齐模式,

  • ARR = 8:设置了定时器的计数上限,决定了PWM周期的长度,计数器会从0计数到8,然后重置为0,开始新的周期。
  • CCRx = 4:决定了PWM信号从高电平变为低电平的切换点,当计数器计数到4时,输出信号从高电平切换到低电平。
  • 红线(有效电平):PWM输出从高电平切换到低电平的时刻,即在计数器值等于CCR值时,输出发生变化。
  • 蓝线(无效电平):PWM输出在一个完整的周期结束时仍然是低电平。
  • CCxIF:捕捉/比较中断标志,当定时器的计数器值与CCR寄存器的预设值相匹配时,该标志被设置。

  • OCxREF:输出比较引用信号,通常与PWM的实际输出相对应,反映了PWM信号的状态更改。

二、实验步骤

1.引脚设置

这边在h文件的宏定义中定义即可

  1. static void GENERAL_TIMx_GPIO_Config(void)
  2. {
  3. GPIO_InitTypeDef GPIO_InitStructure;
  4. /* 使能定时器始终:设置TIM3CLK 为 72MHZ */
  5. GENERAL_TIM_APBxClock_FUN (GENERAL_TIM_CLK, ENABLE);
  6. /* 使能定时器通道引脚GPIO时钟 */
  7. GENERAL_TIM_GPIO_APBxClock_FUN(GENERAL_TIM_GPIO_CLK1|GENERAL_TIM_GPIO_CLK1, ENABLE);
  8. /* 配置定时器通道3输出引脚模式:复用推挽输出模式 */
  9. GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_PIN1;
  10. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  11. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  12. GPIO_Init(GENERAL_TIM_PORT1, &GPIO_InitStructure);
  13. }

2.时钟中断

时钟中断函数void NVIC_Config_PWM(void),这边是对中断来源以及优先级的配置,前面在Systick中有所介绍,可以看一下之前的连接:STM32(四):Systick (标准库函数)-CSDN博客

  1. static void NVIC_Config_PWM(void)
  2. {
  3. NVIC_InitTypeDef NVIC_InitStructure;
  4. /* 选择中断优先级配置组为2个抢占式优先级和2个子优先级,可以参考misc.h文件了解相关设置 */
  5. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  6. /* 配置TIM3_IRQ中断为中断源 */
  7. NVIC_InitStructure.NVIC_IRQChannel = GENERAL_TIMx_IRQn;
  8. /* 设置抢占式优先级为0 */
  9. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  10. /* 设置子优先级为3 */
  11. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
  12. /* 使能外部中断通道 */
  13. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  14. NVIC_Init(&NVIC_InitStructure);
  15. }

但到这里我们的中断配置还没结束!!重点!!易踩坑!!

我们还需要在stm32f10x_it.c的文件里面加上新的中断服务函数 GENERAL_TIMx_IRQHandler (),记得加上头文件,例如:

  1. #include "bsp/GeneralTIM/bsp_GeneralTIM.h"
  2. extern uint8_t indexWave[];
  3. void GENERAL_TIMx_IRQHandler(void)
  4. {
  5. static uint8_t pwm_index = 0; /* 用于PWM查表 */
  6. static uint8_t period_cnt = 0; /* 用于计算周期数 */
  7. /* 定时器更新中断 */
  8. if (TIM_GetITStatus(GENERAL_TIMx, TIM_IT_Update) != RESET)
  9. {
  10. period_cnt++;
  11. /* 若输出的周期数大于20,输出下一种脉冲宽的PWM波 */
  12. if(period_cnt >= 20)
  13. {
  14. /* 根据PWM表修改定时器的比较寄存器值 */
  15. GENERAL_TIMx->GENERAL_TIM_CCRx = indexWave[pwm_index];
  16. /* 标志PWM表的下一个元素 */
  17. pwm_index++;
  18. /* 若PWM脉冲表已经输出完成一遍,重置PWM查表标志 */
  19. if( pwm_index >= 40)
  20. {
  21. pwm_index=0;
  22. }
  23. /* 重置周期计数标志 */
  24. period_cnt=0;
  25. }
  26. /* 必须要清除中断标志位 */
  27. TIM_ClearITPendingBit (GENERAL_TIMx, TIM_IT_Update);
  28. }
  29. }

3.定时器配置

定时周期和预分频需要根据实际需要进行调整,而这边TIM_Pulse=0是因为我们给了一个脉冲配置的数组indexWave[],若不用,调整这个大小会改变“呼吸”的速率。

  1. static void GENERAL_TIMx_Configuration(void)
  2. {
  3. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  4. TIM_OCInitTypeDef TIM_OCInitStructure;
  5. /* 当定时器从0计数到255,即为256次,为一个定时周期 */
  6. TIM_TimeBaseStructure.TIM_Period = 255;
  7. /* 设置预分频:不预分频,即为72MHz,输出脉冲频率:72MHz/(1999+1)/(255+1) */
  8. TIM_TimeBaseStructure.TIM_Prescaler = 1999;
  9. /* 设置时钟分频系数:不分频(这里用不到) */
  10. TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ;
  11. /* 向上计数模式 */
  12. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  13. TIM_TimeBaseInit(GENERAL_TIMx, &TIM_TimeBaseStructure);
  14. /* 模式配置:PWM模式1 */
  15. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  16. /* 输出状态设置:使能输出 */
  17. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  18. /* 设置跳变值,当计数器计数到这个值时,电平发生跳变 */
  19. TIM_OCInitStructure.TIM_Pulse = 0;
  20. /* 当定时器计数值小于CCR1_Val时为低电平 */
  21. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  22. /* 初始化定时器通道1输出PWM */
  23. GENERAL_TIM_OCxInit(GENERAL_TIMx, &TIM_OCInitStructure);
  24. /* 定时器比较输出通道1预装载配置:使能预装载 */
  25. GENERAL_TIM_OCxPreloadConfig(GENERAL_TIMx, TIM_OCPreload_Enable);
  26. /* 使能定时器重载寄存器ARR */
  27. TIM_ARRPreloadConfig(GENERAL_TIMx, ENABLE);
  28. /* 使能定时器 */
  29. TIM_Cmd(GENERAL_TIMx, ENABLE);
  30. /* 配置NVIC */
  31. NVIC_Config_PWM();
  32. /* 定时器更新中断 */
  33. TIM_ITConfig(GENERAL_TIMx, TIM_IT_Update, ENABLE);
  34. }

4.PWM信号初始化

  1. void GENERAL_TIMx_PWM_Init(void)
  2. {
  3. GENERAL_TIMx_GPIO_Config();
  4. GENERAL_TIMx_Configuration();
  5. }

三、实操代码

程序分为3个文件:bsp_GeneralTIM.c、bsp_GeneralTIM.h、main.c

1.bsp_GeneralTIM.c

  1. #include "bsp/GeneralTIM/bsp_GeneralTIM.h"
  2. uint8_t indexWave[] = {1,1,2,2,3,4,6,8,10,14,19,25,33,44,59,80,107,143,191,255,
  3. 255,191,143,107,80,59,44,33,25,19,14,10,8,6,4,3,2,2,1,1};
  4. /**
  5. * 函数功能: 配置TIMx复用输出PWM时用到的I/O
  6. */
  7. static void GENERAL_TIMx_GPIO_Config(void)
  8. {
  9. GPIO_InitTypeDef GPIO_InitStructure;
  10. /* 使能定时器始终:设置TIM3CLK 为 72MHZ */
  11. GENERAL_TIM_APBxClock_FUN (GENERAL_TIM_CLK, ENABLE);
  12. /* 使能定时器通道引脚GPIO时钟 */
  13. GENERAL_TIM_GPIO_APBxClock_FUN(GENERAL_TIM_GPIO_CLK1|GENERAL_TIM_GPIO_CLK1, ENABLE);
  14. /* 配置定时器通道3输出引脚模式:复用推挽输出模式 */
  15. GPIO_InitStructure.GPIO_Pin = GENERAL_TIM_PIN1;
  16. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  17. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  18. GPIO_Init(GENERAL_TIM_PORT1, &GPIO_InitStructure);
  19. }
  20. /**
  21. * 函数功能: 配置嵌套向量中断控制器NVIC
  22. */
  23. static void NVIC_Config_PWM(void)
  24. {
  25. NVIC_InitTypeDef NVIC_InitStructure;
  26. /* 选择中断优先级配置组为2个抢占式优先级和2个子优先级,可以参考misc.h文件了解相关设置 */
  27. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  28. /* 配置TIM3_IRQ中断为中断源 */
  29. NVIC_InitStructure.NVIC_IRQChannel = GENERAL_TIMx_IRQn;
  30. /* 设置抢占式优先级为0 */
  31. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  32. /* 设置子优先级为3 */
  33. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
  34. /* 使能外部中断通道 */
  35. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  36. NVIC_Init(&NVIC_InitStructure);
  37. }
  38. /**
  39. * 函数功能: 配置TIMx输出的PWM信号的模式,如周期、极性、占空比
  40. */
  41. static void GENERAL_TIMx_Configuration(void)
  42. {
  43. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  44. TIM_OCInitTypeDef TIM_OCInitStructure;
  45. /* 定时器基本参数始终 */
  46. /* 当定时器从0计数到255,即为256次,为一个定时周期 */
  47. TIM_TimeBaseStructure.TIM_Period = 255;
  48. /* 设置预分频:不预分频,即为72MHz,输出脉冲频率:72MHz/(1999+1)/(255+1) */
  49. TIM_TimeBaseStructure.TIM_Prescaler = 1999;
  50. /* 设置时钟分频系数:不分频(这里用不到) */
  51. TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ;
  52. /* 向上计数模式 */
  53. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  54. TIM_TimeBaseInit(GENERAL_TIMx, &TIM_TimeBaseStructure);
  55. /* 定时器输出通道1模式配置 */
  56. /* 模式配置:PWM模式1 */
  57. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  58. /* 输出状态设置:使能输出 */
  59. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  60. /* 设置跳变值,当计数器计数到这个值时,电平发生跳变 */
  61. TIM_OCInitStructure.TIM_Pulse = 0;
  62. /* 当定时器计数值小于CCR1_Val时为低电平 */
  63. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  64. /* 初始化定时器通道1输出PWM */
  65. GENERAL_TIM_OCxInit(GENERAL_TIMx, &TIM_OCInitStructure);
  66. /* 定时器比较输出通道1预装载配置:使能预装载 */
  67. GENERAL_TIM_OCxPreloadConfig(GENERAL_TIMx, TIM_OCPreload_Enable);
  68. /* 使能定时器重载寄存器ARR */
  69. TIM_ARRPreloadConfig(GENERAL_TIMx, ENABLE);
  70. /* 使能定时器 */
  71. TIM_Cmd(GENERAL_TIMx, ENABLE);
  72. /* 配置NVIC */
  73. NVIC_Config_PWM();
  74. /* 定时器更新中断 */
  75. TIM_ITConfig(GENERAL_TIMx, TIM_IT_Update, ENABLE);
  76. }
  77. /**
  78. * 函数功能: TIMx输出PWM信号初始化
  79. * 说 明:只要调用这个函数TIMx的通道就会有PWM信号输出
  80. */
  81. void GENERAL_TIMx_PWM_Init(void)
  82. {
  83. GENERAL_TIMx_GPIO_Config();
  84. GENERAL_TIMx_Configuration();
  85. }

2.bsp_GeneralTIM.h

  1. #ifndef __GENERAL_TIM_H__
  2. #define __GENERAL_TIM_H__
  3. /* 包含头文件 ----------------------------------------------------------------*/
  4. #include <stm32f10x.h>
  5. /* 类型定义 ------------------------------------------------------------------*/
  6. /* 宏定义 --------------------------------------------------------------------*/
  7. #define GENERAL_TIMx TIM3
  8. #define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
  9. #define GENERAL_TIM_CLK RCC_APB1Periph_TIM3
  10. #define GENERAL_TIM_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
  11. #define GENERAL_TIM_GPIO_CLK1 RCC_APB2Periph_GPIOA
  12. #define GENERAL_TIM_PORT1 GPIOA
  13. #define GENERAL_TIM_PIN1 GPIO_Pin_11
  14. #define GENERAL_TIM_OCxInit TIM_OC3Init
  15. #define GENERAL_TIM_OCxPreloadConfig TIM_OC3PreloadConfig
  16. #define GENERAL_TIM_CCRx CCR3
  17. #define GENERAL_TIMx_IRQn TIM3_IRQn //中断
  18. #define GENERAL_TIMx_IRQHandler TIM3_IRQHandler
  19. /* 扩展变量 ------------------------------------------------------------------*/
  20. /* 函数声明 ------------------------------------------------------------------*/
  21. void GENERAL_TIMx_PWM_Init(void);
  22. #endif /* __GENERAL_TIM_H__ */

3.main.c

  1. /* 包含头文件 ----------------------------------------------------------------*/
  2. #include "stm32f10x.h"
  3. #include "bsp/led/bsp_led.h"
  4. #include "bsp/key/bsp_key.h"
  5. #include "bsp/delay/delay.h"
  6. #include "bsp/systick/bsp_SysTick.h"
  7. #include "bsp/GeneralTIM/bsp_GeneralTIM.h"
  8. /* 函数体 --------------------------------------------------------------------*/
  9. /**
  10. * 函数功能: 主函数.
  11. */
  12. int main(void)
  13. {
  14. /* 初始化定时器PWM输出 */
  15. GENERAL_TIMx_PWM_Init();
  16. while (1)
  17. {
  18. //空
  19. }
  20. }

四、实验效果

呼吸灯

结束语

本文以STM32VET6为例讲解了用STM32单片机中用定时器的PWM波来实现LED的“呼吸”,并指出其中的易坑点。希望对大家有所帮助!如果还有什么问题,欢迎评论区留言,谢谢!

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

闽ICP备14008679号