打印
[其他ST产品]

基于Cubemx与HAL库之PWM呼吸灯经验分享

[复制链接]
1820|39
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
jcky001|  楼主 | 2023-4-3 10:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
摘要

本章还是点灯,但是小飞哥带大家换一种点灯方式,利用PWM功能实现“呼吸灯”,什么是呼吸灯?顾名思义,像人呼吸一样的灯...简而言之就是,吸气...呼气...实现灯光渐亮渐灭的效果。


PWM原理介绍

脉冲宽度调制(PWM),是英文“Pulse Width Modulation” 的缩写,简称脉宽调制,是利用 微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽 度的控制,如下图(摘自正点原子手册)




PWM 原理示意图


上图就是一个简单的PWM 原理示意图。图中,我们假定定时器工作在向上计数PWM模式,且当 CNT<CCRx时,输出0,当CNT>=CCRx时输出1。那么就可以得到如上的PWM示意图:当CNT值小于CCRx的时候,IO输出低电平(0),当CNT值大于等于CCRx的时候,IO输出高电平(1),当CNT达到ARR值的时候,重新归零,然后重新向上计数,依次循环。改变CCRx的值,就可以改变PWM输出的占空比,改变 ARR的值,就可以改变PWM输出的频率,这就是PWM输出的原理。




使用特权

评论回复
沙发
jcky001|  楼主 | 2023-4-3 10:13 | 只看该作者

STM32的定时器几乎都能够产生PWM波,高级定时器 TIM1和TIM8可以同时产生多达7路的PWM输出。而通用定时器也能同时产生多达4路的PWM输出,如下图定时器4,可以产生4路PWM波。本文,咱们只使用一路PWM,学懂原理之后,随便怎么搞~




cubemx配置PWM

本次使用的是TIM4的通道4来产生PWM波,也即是PB9。硬件连接比较简单,LED正极连接PB9,负极连接GND即可。

关于时钟等配置,就不多做介绍了

选择TIM4->勾选inter clock->通道4->PWM output CH4->PB9




定时器参数配置,主要分为两部分,一部分是定时器的基本配置,分频系数、周期,这个配置不是固定的,把握频率的计算方式即可:

fclk= (Fcore/(Prescaler+1)/(Period+1)


使用特权

评论回复
板凳
jcky001|  楼主 | 2023-4-3 10:14 | 只看该作者

按照我的配置,计算有得到:

fclk = 72000000/500/72=2KHZ

这个频率是直接影响到LED灯光的闪烁频率的,如果设置的太低了,会有明显的闪烁感。




PB9是被复用的




配置是比较简单的,直接生成代码即可


PWM代码解析

上面知道,PB9是被复用为PWM通道功能的,代码如下,自从有了cubemx,代码初始化似乎变得简单了呢...

  • void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
  • {
  •   GPIO_InitTypeDef GPIO_InitStruct = {0};
  •   if(timHandle->Instance==TIM4)
  •   {
  •   /* USER CODE BEGIN TIM4_MspPostInit 0 */
  •   /* USER CODE END TIM4_MspPostInit 0 */
  •     __HAL_RCC_GPIOB_CLK_ENABLE();
  •     /**TIM4 GPIO Configuration
  •     PB9     ------> TIM4_CH4
  •     */
  •     GPIO_InitStruct.Pin = GPIO_PIN_9;
  •     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  •     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  •     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  •   /* USER CODE BEGIN TIM4_MspPostInit 1 */
  •   /* USER CODE END TIM4_MspPostInit 1 */
  •   }
  • }

复制代码


上面我们对定时器及PWM参数的配置代码如下,其中有一个参数.pulse,这个参数是决定PWM的占空比的,计算如下:

Pulse = .pulse/period


计算得出的就是我们波形,高电平(由于是配置的模式1)所占整个周期的比例,占得比例越高,相应的电压会越高,对应的LED灯会越亮


关于定时器的参数是被封装在一个结构体里面的:

  • /**
  •   * @brief  TIM Time base Configuration Structure definition
  •   */
  • typedef struct
  • {
  •   uint32_t Prescaler;         /*!< Specifies the prescaler value used to divide the TIM clock.
  •                                    This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
  •   uint32_t CounterMode;       /*!< Specifies the counter mode.
  •                                    This parameter can be a value of @ref TIM_Counter_Mode */
  •   uint32_t Period;            /*!< Specifies the period value to be loaded into the active
  •                                    Auto-Reload Register at the next update event.
  •                                    This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF.  */
  •   uint32_t ClockDivision;     /*!< Specifies the clock division.
  •                                    This parameter can be a value of @ref TIM_ClockDivision */
  •   uint32_t RepetitionCounter;  /*!< Specifies the repetition counter value. Each time the RCR downcounter
  •                                     reaches zero, an update event is generated and counting restarts
  •                                     from the RCR value (N).
  •                                     This means in PWM mode that (N+1) corresponds to:
  •                                         - the number of PWM periods in edge-aligned mode
  •                                         - the number of half PWM period in center-aligned mode
  •                                      GP timers: this parameter must be a number between Min_Data = 0x00 and
  •                                      Max_Data = 0xFF.
  •                                      Advanced timers: this parameter must be a number between Min_Data = 0x0000 and
  •                                      Max_Data = 0xFFFF. */
  •   uint32_t AutoReloadPreload;  /*!< Specifies the auto-reload preload.
  •                                    This parameter can be a value of @ref TIM_AutoReloadPreload */
  • } TIM_Base_InitTypeDef;

复制代码

  • /**
  •   * @brief  TIM One Pulse Mode Configuration Structure definition
  •   */
  • typedef struct
  • {
  •   uint32_t OCMode;        /*!< Specifies the TIM mode.
  •                                This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes */
  •   uint32_t Pulse;         /*!< Specifies the pulse value to be loaded into the Capture Compare Register.
  •                                This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
  •   uint32_t OCPolarity;    /*!< Specifies the output polarity.
  •                                This parameter can be a value of @ref TIM_Output_Compare_Polarity */
  •   uint32_t OCNPolarity;   /*!< Specifies the complementary output polarity.
  •                                This parameter can be a value of @ref TIM_Output_Compare_N_Polarity
  •                                @NOTE This parameter is valid only for timer instances supporting break feature. */
  •   uint32_t OCIdleState;   /*!< Specifies the TIM Output Compare pin state during Idle state.
  •                                This parameter can be a value of @ref TIM_Output_Compare_Idle_State
  •                                @NOTE This parameter is valid only for timer instances supporting break feature. */
  •   uint32_t OCNIdleState;  /*!< Specifies the TIM Output Compare pin state during Idle state.
  •                                This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State
  •                                @note This parameter is valid only for timer instances supporting break feature. */
  •   uint32_t ICPolarity;    /*!< Specifies the active edge of the input signal.
  •                                This parameter can be a value of @ref TIM_Input_Capture_Polarity */
  •   uint32_t ICSelection;   /*!< Specifies the input.
  •                               This parameter can be a value of @ref TIM_Input_Capture_Selection */
  •   uint32_t ICFilter;      /*!< Specifies the input capture filter.
  •                               This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */
  • } TIM_OnePulse_InitTypeDef;

复制代码


同样的,PWM的配置也是被封装在一个结构体里面,所以说,用了cubemx是方便了,其实对初学者学习是不太好的,知道了怎么用功能,但是背后的代码结构可能了解的会很少...




使用特权

评论回复
地板
jcky001|  楼主 | 2023-4-3 10:16 | 只看该作者

关于PWM的函数还是非常多的,此次功能比较简单,仅仅用到了红框中的几个函数,主要看看start函数,其他的初始化过程中自动调用了,不用关心。




start函数需要传两个参数,一个是定时器,另一个是对应的PWM通道号,本次

利用到的是TIM4,通道4




启动定时器之后,我们需要更改的参数是PWM的占空间比,HAL库以宏定义的方式给了我们定义,看了这个代码之后,你有没有一种感觉,每天满世界找优秀的代码,找优美的宏定义写法,这不就挺好的...所以,库函数本身就是一个宝藏学习资料,大家不要忽略了




代码编写

看了上面的函数介绍之后,其实写起来就很简单了,结构体能够让代码变得整洁一些,先来定义一个呼吸灯相关的结构体:

  • typedef struct{
  • uint16_t LedpwmVal;//占空比调整参数
  • uint8_t LedpwmVal_Dir:1;//调整方向,1-递增,0递减
  • }peripheral;

复制代码


初始化结构体成员为0,1,启动PWM波,接下来编写呼吸灯效果代码,代码也是非常的简单,延时是不能省略的,否则效果是眼睛分辨不出来的...代码也是非常的简单





  • while (1)
  •   {
  •     /* USER CODE END WHILE */
  •     /* USER CODE BEGIN 3 */
  •   HAL_Delay(10);//加延时,否则变化不明显,看不到效果
  •   if(PWM.LedpwmVal_Dir)
  •    PWM.LedpwmVal++;
  •   else
  •    PWM.LedpwmVal--;
  •   if(PWM.LedpwmVal>breath_UP)
  •    PWM.LedpwmVal_Dir=Breath_DOWN;//切换为PWM值递减状态
  •   if(PWM.LedpwmVal==Breath_DOWN)
  •    PWM.LedpwmVal_Dir=1;//切换为PWM值递增状态
  • // TIM4->CCR4 = PWM.LedpwmVal;
  •   __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_4,PWM.LedpwmVal);//设置占空比参数
  •   }

复制代码



呼吸灯波形实际是怎么样的呢,接下来通过逻辑分析仪来看看波形


为了效果明显,我们选择10ms增加10的比例来看看波形:

递增效果:




递减效果:



使用特权

评论回复
5
MessageRing| | 2023-4-5 22:56 | 只看该作者
频繁通断电会不对寿命有影响啊

使用特权

评论回复
6
51xlf| | 2023-4-13 15:42 | 只看该作者
怎么用STM32配置产生PWM              

使用特权

评论回复
7
claretttt| | 2023-4-13 20:43 | 只看该作者
如何用keil显示stm32的端口的pwm输出波形图

使用特权

评论回复
8
maqianqu| | 2023-4-13 21:03 | 只看该作者
stm32输出pwm频率是如何计算的

使用特权

评论回复
9
averyleigh| | 2023-4-16 22:35 | 只看该作者
pwm输出不为零如何变为零              

使用特权

评论回复
10
AloneKaven| | 2023-4-17 23:36 | 只看该作者
averyleigh 发表于 2023-4-16 22:35
pwm输出不为零如何变为零

在程序中设置PWM输出的占空比为0

使用特权

评论回复
11
gygp| | 2023-4-18 12:20 | 只看该作者
用stm32怎么实现互补PWM的调频率及同时调PWM的占空比

使用特权

评论回复
12
lzmm| | 2023-4-18 13:22 | 只看该作者
如何将stm32的pwm放大               

使用特权

评论回复
13
maqianqu| | 2023-4-18 14:22 | 只看该作者
stm32 spwm的最高频率可以到多少

使用特权

评论回复
14
olivem55arlowe| | 2023-4-18 14:33 | 只看该作者
stm32可以产生几路PWM信号?能有六路吗?

使用特权

评论回复
15
pentruman| | 2023-4-18 14:37 | 只看该作者
怎么测pwm调制声音的频段               

使用特权

评论回复
16
mollylawrence| | 2023-4-18 17:11 | 只看该作者
stm32的pwm有带载能力吗              

使用特权

评论回复
17
adolphcocker| | 2023-4-18 18:14 | 只看该作者
决定STM32微控制器PWM信号输出的周期是哪个寄存器

使用特权

评论回复
18
hearstnorman323| | 2023-4-18 18:59 | 只看该作者
定时器PWM输出              

使用特权

评论回复
19
olivem55arlowe| | 2023-4-18 19:14 | 只看该作者
如何设置stm32中pwm的占空比为20%?

使用特权

评论回复
20
sanfuzi| | 2023-4-18 21:45 | 只看该作者
stm32如何设置pwm周期为20ms

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1506

主题

4531

帖子

6

粉丝