STM32
直播中

石胜厚

7年用户 1059经验值
私信 关注
[问答]

PWM是如何去控制单色LED呼吸灯的

PWM是什么?PWM生成方法是什么?
PWM是如何去控制单色LED呼吸灯的?



回帖(1)

徐振方

2021-11-15 14:31:57
  一.PWM简介
  PWM是 Pulse Width Modulation 的缩写,中文意思就是脉冲宽度调 制,简称脉宽调制。 PWM是一种对模拟信号电平进行数字编码 的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个 具体模拟信号的电平进行编码。PWM 信号仍然是数字的,因为在给定的 任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压 或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去 的。通的时候即是直流供电被加到负载上的时候,断的时候即是供电被 断开的时候。只要带宽足够,任何模拟值都可以使用 PWM 进行编码。
  
  PWM生成方法
  计算法
  根据拟合波形的频率、幅值和半周期脉冲数,准确计算 PWM 波各脉冲宽度和间隔,据此控制开关器件的通断,就可得到所需 PWM 波形;
  调制法
  拟合波形作调制信号,进行调制得到期望的 PWM 波;该方法一般采用等腰三角波为载波,其任一点水平宽度和高度成线性关系且左右对称。载波(等腰三角波)与平缓变化的调制信号波(即要拟合的波形)相交,在载波与信号波的交点控制器件通断,就得宽度正比于信号波幅值的脉冲,符合 PWM 的要求。相对于计算法,其处理过程计算简单。
  二、PWM控制单色LED—单色呼吸灯
  硬件说明
  本文使用的是野火STM32F103指南者开发板
  本开发板中设计的 RGB 灯控制引脚是经过仔细选择的,因为本实验的软件将使用STM32 的定时器控制输出 PWM 脉冲,然而并不是任意 GPIO都具有 STM32 定时器的输出通道功能,所以在设计硬件时,需要根据《STM32 中文数据手册》中的说明,选择具有定时器输出通道功能的引脚来控制 RGB 灯。
  
  本实验中的 RGB灯使用阴极分别连接到了 PB5、PB0及 PB1,它们分别是定时器 TIM3的通道 2、3、4,其中 PB5 用于定时器输出通道时,需要使用重定义功能。
  代码分析
  这里用的例子是野火官方例程,可以去官网下载,在里面找到TIM—单色呼吸灯就可以了。
  硬件相关宏定义文件bsp_breathing.h
  #ifndef __PWM_BREATHING_H
  #define __PWM_BREATHING_H
  #include “stm32f10x.h”
  /*PWM表中的点数*/
  extern uint16_t POINT_NUM ;
  //控制输出波形的频率
  extern __IO uint16_t period_class ;
  #define RED_LIGHT 1
  #define GREEN_LIGHT 2
  #define BLUE_LIGHT 3
  /*要使用什么颜色的呼吸灯,可选RED_LIGHT、GREEN_LIGHT、BLUE_LIGHT*/
  #define LIGHT_COLOR RED_LIGHT
  /********************定时器通道**************************/
  #if LIGHT_COLOR == RED_LIGHT
  /************红灯***************/
  #define BRE_TIMx TIM3
  #define BRE_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
  #define BRE_TIM_CLK RCC_APB1Periph_TIM3
  #define BRE_TIM_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
  #define BRE_TIM_GPIO_CLK (RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO)
  //红灯的引脚需要重映射
  #define BRE_GPIO_REMAP_FUN() GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);
  #define BRE_TIM_LED_PORT GPIOB
  #define BRE_TIM_LED_PIN GPIO_Pin_5
  #define BRE_TIM_OCxInit TIM_OC2Init //通道选择,1~4
  #define BRE_TIM_OCxPreloadConfig TIM_OC2PreloadConfig
  #define BRE_CCRx CCR2
  #define BRE_TIMx_IRQn TIM3_IRQn //中断
  #define BRE_TIMx_IRQHandler TIM3_IRQHandler
  #elif LIGHT_COLOR == GREEN_LIGHT
  /************绿灯***************/
  #define BRE_TIMx TIM3
  #define BRE_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
  #define BRE_TIM_CLK RCC_APB1Periph_TIM3
  #define BRE_TIM_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
  #define BRE_TIM_GPIO_CLK (RCC_APB2Periph_GPIOB)
  //绿灯不需要重映射
  #define BRE_GPIO_REMAP_FUN()
  #define BRE_TIM_LED_PORT GPIOB
  #define BRE_TIM_LED_PIN GPIO_Pin_0
  #define BRE_TIM_OCxInit TIM_OC3Init //通道选择,1~4
  #define BRE_TIM_OCxPreloadConfig TIM_OC3PreloadConfig
  #define BRE_CCRx CCR3
  #define BRE_TIMx_IRQn TIM3_IRQn //中断
  #define BRE_TIMx_IRQHandler TIM3_IRQHandler
  #elif LIGHT_COLOR == BLUE_LIGHT
  /************蓝灯***************/
  #define BRE_TIMx TIM3
  #define BRE_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
  #define BRE_TIM_CLK RCC_APB1Periph_TIM3
  #define BRE_TIM_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
  #define BRE_TIM_GPIO_CLK (RCC_APB2Periph_GPIOB)
  //蓝灯不需要重映射
  #define BRE_GPIO_REMAP_FUN()
  #define BRE_TIM_LED_PORT GPIOB
  #define BRE_TIM_LED_PIN GPIO_Pin_1
  #define BRE_TIM_OCxInit TIM_OC4Init //通道选择,1~4
  #define BRE_TIM_OCxPreloadConfig TIM_OC4PreloadConfig
  #define BRE_CCRx CCR4
  #define BRE_TIMx_IRQn TIM3_IRQn //中断
  #define BRE_TIMx_IRQHandler TIM3_IRQHandler
  #endif
  void TIMx_Breathing_Init (void);
  #endif /* __PWM_BREATHING_H */
  为方便切换LED 灯 的颜色,它定义了三组宏 , 通 过 修 改 代 码 中 的 “#defineLIGHT_COLOR RED_LIGHT”语句,可以切换使用红、绿、蓝种颜色的呼吸灯。在每组宏定义中,与全彩 LED 灯实验中的类似,定义了定时器编号、定时器时钟使能库函数、引脚重映射操作、GPIO 端口和引脚号、通道对应的比较寄存器名以及中断通道和中断服务函数名。
  初始化GPIO口,代码:
  
  本实验直接使用定时器输出通道的脉冲信号控制 LED 灯,此处代码把 ==GPIO ==相关的引脚配置成了复用推挽输出模式。其中由于红灯使用的引脚需要用到第二功能,本代码使用宏 BRE_GPIO_REMAP_FUN ()进行了该引脚的功能重定义操作。
  这是PWM表:
  
  PWM 表记录了呼吸特性曲线,在本实验中,PWM 表的数据将会被赋
  值到定时器的 CCRx 比较寄存器,从而控制输出占空比呈呼吸特性曲线变化的 PWM 波。
  。定时器 PWM 配置
  static void NVIC_Config_PWM(void)
  {
  NVIC_InitTypeDef NVIC_InitStructure;
  /* Configure one bit for preemption priority */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  /* 配置TIM3_IRQ中断为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = BRE_TIMx_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  }
  /**
  * @brief 配置TIM输出的PWM信号的模式,如周期、极性
  * @param 无
  * @retval 无
  */
  static void TIMx_Mode_Config(void)
  {
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  TIM_OCInitTypeDef TIM_OCInitStructure;
  /* 设置TIM3CLK 时钟 */
  BRE_TIM_APBxClock_FUN ( BRE_TIM_CLK, ENABLE );
  /* 基本定时器配置 ,配合PWM表点数、中断服务函数中的period_cnt循环次数设置*/
  ************************************************************/
  /* 基本定时器配置 */
  TIM_TimeBaseStructure.TIM_Period = (1024-1);; //当定时器从0计数到 TIM_Period+1 ,为一个定时周期
  TIM_TimeBaseStructure.TIM_Prescaler = (200-1); //设置预分频
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ; //设置时钟分频系数:不分频(这里用不到)
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
  TIM_TimeBaseInit(BRE_TIMx, &TIM_TimeBaseStructure);
  /* PWM模式配置 */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //配置为PWM模式1
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能输出
  TIM_OCInitStructure.TIM_Pulse = 0; //设置初始PWM脉冲宽度为0
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //当定时器计数值小于CCR1_Val时为低电平
  BRE_TIM_OCxInit ( BRE_TIMx, &TIM_OCInitStructure ); //使能通道
  BRE_TIM_OCxPreloadConfig ( BRE_TIMx, TIM_OCPreload_Enable ); //使能预装载
  TIM_ARRPreloadConfig(BRE_TIMx, ENABLE); //使能TIM重载寄存器ARR
  /* TIM3 enable counter */
  TIM_Cmd(BRE_TIMx, ENABLE); //使能定时器
  TIM_ITConfig(BRE_TIMx, TIM_IT_Update, ENABLE); //使能update中断
  NVIC_Config_PWM(); //配置中断优先级
  }
  #python计算脚本 count.py
  #PWM点数
  POINT_NUM = 110
  #周期倍数
  PERIOD_CLASS = 10
  #定时器定时周期
  TIMER_TIM_Period = 2**10
  #定时器分频
  TIMER_TIM_Prescaler = 200
  #STM32系统时钟频率和周期
  f_pclk = 72000000
  t_pclk = 1/f_pclk
  #定时器update事件周期
  t_timer = t_pclk*TIMER_TIM_Prescaler*TIMER_TIM_Period
  #每个PWM点的时间
  T_Point = t_timer * PERIOD_CLASS
  #整个呼吸周期
  T_Up_Down_Cycle = T_Point * POINT_NUM
  print (“呼吸周期:”,T_Up_Down_Cycle)
  #运行结果:
  呼吸周期:3.12888
  代码中初始化了控制 RGB 灯用的定时器,
  它被配置为向上计数,PWM 通道输出也被配置成当计数器 CNT 的值小于输出比较寄存器CCRx的值时,PWM通道输出低电平,点亮 LED 灯。在函数的最后还使能了定时器中断,每当定时器的一个计数周期完成时,产生中断,配合中断服务函数,即可切换 CCRx 比较
  寄存器的值。
  代码中的 TIM_Period 和 TIM_Prescaler 是关键配置。其中 TIMPeriod 被配置为(1024-1),它控制控制定时器的定时周期,定时器的计数寄存器 CNT 从 0 开始,每个时钟会对计数器加 1,计数至 1023 时完成一次计数,产生中断,也就是说一共 1024 个计数周期,与 PWM 表元素中的最大值相同。若定时器的输出比较寄存器 CCRx被赋值为 PWM表中的元素,即可改变输出对应占空比的 PWM波,控制 LED灯。
  main.c
  
  main 函数中直接调用了 TIMx_Breathing_Init 函数,而该函数内部又直接调用了GPIO和PWM配置函数:TIMx_GPIO_Config和TIMx_Mode_Config。初始化完成后,定时器开始工作,然后它会在中断服务函数中切换 PWM 数据,控制 LED 灯显示呼吸效果。
  进行验证
  示波器查看波形
  这里因为我们点亮的是PB5所控制的LED灯,所以我们的测试点也是PB5。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分