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

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

[复制链接]
攻城狮Melo 发布时间:2023-4-2 18:31
摘要

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


4 u$ f% t+ d4 B' g; H+ N$ L- c

PWM原理介绍

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


# l) [8 }# b( h

: Z& r/ [/ F! ~+ d6 u
微信图片_20230402183103.png

! ?. F' Y! m; a- k# p6 u
PWM 原理示意图

( g$ E* I. H* S( c1 ]0 U4 |, [+ J; `; d0 I8 L* i. _8 D8 O; F: y

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


$ \- [4 A" P/ d$ s' i0 \% ]) t# o

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


' _, P8 A/ @; Q+ u: m4 x

微信图片_20230402183100.png


- i! T5 S4 t+ E. r- n/ T) o5 a

cubemx配置PWM

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

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

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

4 e2 m% ~6 w) S+ t, s

微信图片_20230402183056.png

8 |. C  X; D1 N" _, E2 O9 r

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

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

. `2 i3 W; C& p

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

fclk = 72000000/500/72=2KHZ

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


1 H2 ^0 [0 g% K

微信图片_20230402183052.png

: l3 a- A! I" m; G( |

PB9是被复用的

3 K# y+ I0 b$ i4 j! Z

微信图片_20230402183049.png


2 M  b* G  f( R" M* y/ }6 d

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

( d5 A' O6 K6 o. x

PWM代码解析

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

  1. void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
    0 w- r& |& A5 a+ Y
  2. {
    5 x# g6 n. P% [) x
  3. * ~+ J' S! i' ]7 X
  4.   GPIO_InitTypeDef GPIO_InitStruct = {0};
    ) f% [. }9 R8 b- \2 s$ e/ I
  5.   if(timHandle->Instance==TIM4)5 Y# f7 r4 }+ ]0 H2 A0 o7 G
  6.   {4 F) D$ y" ^# _! Y: l3 b, F' b
  7.   /* USER CODE BEGIN TIM4_MspPostInit 0 */
    . G" I, A8 O( A8 Q+ v- u

  8. ! E2 V% g5 c/ j5 q0 C0 ^, @
  9.   /* USER CODE END TIM4_MspPostInit 0 */
    ' n) z& h6 c7 }# u, x1 T

  10. $ q& A& Y2 F5 E& b* F1 I
  11.     __HAL_RCC_GPIOB_CLK_ENABLE();: J; S" e' R7 T. G0 f
  12.     /**TIM4 GPIO Configuration: ~5 s+ ]& K; Z" ~9 @, f2 v6 g  y4 R
  13.     PB9     ------> TIM4_CH4$ r3 @; Y- t- W) w  P
  14.     */8 {7 u! l, x0 T
  15.     GPIO_InitStruct.Pin = GPIO_PIN_9;3 m+ i8 c" _  G& r. G% Z7 G+ j
  16.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    . F0 E& w: r  W# Y, m9 T- f
  17.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;1 a/ p, t/ Q: y6 t- {+ v7 C
  18.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);& Y+ z5 `4 x: K/ M$ ?7 q/ F

  19. 2 Z; W. Q$ h# M, M; `4 i$ c
  20.   /* USER CODE BEGIN TIM4_MspPostInit 1 */! @3 v/ r# w! o: a5 n' f6 b: l0 z
  21. ; L; Z6 x6 v" i; F
  22.   /* USER CODE END TIM4_MspPostInit 1 */
    6 \6 r, d) j3 K$ J$ D
  23.   }
    5 w3 _! q" F8 J: c: s5 m
  24. : u$ D$ q! ]$ S, y& E6 E6 r, f. {
  25. }
复制代码

# ~1 K/ m3 L( m' p3 `( v8 Y$ j, s8 G/ A

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

Pulse = .pulse/period

- W4 f- R% B0 `- `6 _

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

: Q8 t8 R% [9 |0 j$ j

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

  1. /**1 f% k+ ^; }: ]
  2.   * @brief  TIM Time base Configuration Structure definition* C' X6 t: `- l: Y3 r
  3.   */
    ) {* U+ U+ {1 v" E+ s  C
  4. typedef struct: P0 T, m! |" z
  5. {
    9 m- Q  m2 A! `& P* c0 M% V
  6.   uint32_t Prescaler;         /*!< Specifies the prescaler value used to divide the TIM clock.5 {7 z9 u2 ?( u; e3 Y( ^
  7.                                    This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
    7 Y' ~8 ^' T- W/ G/ R7 l) X

  8. % G2 X4 Y: e+ E9 V* v
  9.   uint32_t CounterMode;       /*!< Specifies the counter mode.
    0 w: S+ g8 p' O( _& j
  10.                                    This parameter can be a value of @ref TIM_Counter_Mode */, }! v7 }; \4 q

  11. # x& M/ p9 t, q9 ]5 D
  12.   uint32_t Period;            /*!< Specifies the period value to be loaded into the active/ C, `# j2 w: o9 z6 _4 [; c, n
  13.                                    Auto-Reload Register at the next update event.
    $ X3 w1 X9 {3 n
  14.                                    This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF.  */
    8 c: D9 g$ @+ [1 L; b% _- Y! s

  15. ) b; Y# p% ^% m* ^* D9 d
  16.   uint32_t ClockDivision;     /*!< Specifies the clock division.7 ]9 U5 u' w) }: F
  17.                                    This parameter can be a value of @ref TIM_ClockDivision */
    . l+ h# B  m) ]* z1 k

  18. 9 o+ b* g" d& ^
  19.   uint32_t RepetitionCounter;  /*!< Specifies the repetition counter value. Each time the RCR downcounter& T/ z4 C6 \* o* ?1 _7 _# @1 g8 u& `
  20.                                     reaches zero, an update event is generated and counting restarts/ o* U  o, N) G) b/ p& I* F
  21.                                     from the RCR value (N).
    " g. ?# H& a* P  `+ S& q, d
  22.                                     This means in PWM mode that (N+1) corresponds to:: `( F' O# P* @7 o4 y, \
  23.                                         - the number of PWM periods in edge-aligned mode% y1 U* O' h3 z7 |; ^. T* b
  24.                                         - the number of half PWM period in center-aligned mode
    * N! w3 R* b7 H
  25.                                      GP timers: this parameter must be a number between Min_Data = 0x00 and, G' H) w3 ]0 I8 R+ F
  26.                                      Max_Data = 0xFF.. s, {( }* U3 r- S$ g* A
  27.                                      Advanced timers: this parameter must be a number between Min_Data = 0x0000 and5 i, l- I1 M) E1 c7 P5 _3 g
  28.                                      Max_Data = 0xFFFF. */* O- d$ z3 A; X1 t
  29. 5 `6 W% c( ^, l1 u, Z) A; {
  30.   uint32_t AutoReloadPreload;  /*!< Specifies the auto-reload preload./ c. f) D0 p$ {
  31.                                    This parameter can be a value of @ref TIM_AutoReloadPreload */
    : Z0 @9 y" U  H1 c7 }
  32. } TIM_Base_InitTypeDef;
复制代码
  1. /**5 N1 w1 C; ~" a, @
  2.   * @brief  TIM One Pulse Mode Configuration Structure definition' @1 h9 L7 `# u5 Z
  3.   */
    0 N. Q6 A) f  Z* o  k9 F
  4. typedef struct
    4 X: T9 b' Y8 n  Y9 B0 J% v% R
  5. {
    ! R0 K- A1 j: }2 C: Z7 v. m
  6.   uint32_t OCMode;        /*!< Specifies the TIM mode.# b. F+ w2 G. T" W
  7.                                This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes */
    3 f  _2 }# T; C# z. r

  8. & [) H  n$ N/ Q9 M( H
  9.   uint32_t Pulse;         /*!< Specifies the pulse value to be loaded into the Capture Compare Register.  z1 l# [( Y, Q. u* \
  10.                                This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */) p  w  Y/ e9 S1 O# R

  11.   @1 g8 i2 M4 N
  12.   uint32_t OCPolarity;    /*!< Specifies the output polarity., _; ^3 H" W- u
  13.                                This parameter can be a value of @ref TIM_Output_Compare_Polarity */
    / @0 r+ ?: V# Y9 s: L
  14. ; j# w6 ~% C+ B& ]; |) t+ y3 e2 B
  15.   uint32_t OCNPolarity;   /*!< Specifies the complementary output polarity.
    / |# Q; i8 v* Q& s! i4 t
  16.                                This parameter can be a value of @ref TIM_Output_Compare_N_Polarity, O; M9 B4 S! @
  17.                                @note This parameter is valid only for timer instances supporting break feature. */( o: Z( Q: E" [2 Z- h9 v9 k  X
  18. / o/ y4 r$ q% J. d/ `' @( a* `
  19.   uint32_t OCIdleState;   /*!< Specifies the TIM Output Compare pin state during Idle state.: a4 D, }' p# c, W
  20.                                This parameter can be a value of @ref TIM_Output_Compare_Idle_State$ F3 i& x* @# J: ]4 V  S% p
  21.                                @note This parameter is valid only for timer instances supporting break feature. */
    ! n  {9 e, e, y" q' o
  22. / b: H) S$ {5 T5 p
  23.   uint32_t OCNIdleState;  /*!< Specifies the TIM Output Compare pin state during Idle state.. Z! C' a) z( `
  24.                                This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State
    . ]" A; u: N& H! }
  25.                                @note This parameter is valid only for timer instances supporting break feature. */
    / c* x) Q4 [- f
  26. ! z3 @/ A, A0 X2 W- o6 u" l' V
  27.   uint32_t ICPolarity;    /*!< Specifies the active edge of the input signal.) s; f5 F* ~; |8 N
  28.                                This parameter can be a value of @ref TIM_Input_Capture_Polarity */' B2 Y+ j$ Z8 Y' k8 K' j" A& U, k

  29. 9 q- a( l  |0 `: v- K$ e
  30.   uint32_t ICSelection;   /*!< Specifies the input.- \2 l& L: f8 G5 T
  31.                               This parameter can be a value of @ref TIM_Input_Capture_Selection */
    ; z* B7 m! U! ?' ?) k
  32. 1 q  G$ o( c7 |, x
  33.   uint32_t ICFilter;      /*!< Specifies the input capture filter.
    " [9 d  e7 t* A' p1 j5 r  i
  34.                               This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */, \1 c: ^2 n1 F0 `# m# i4 o  u
  35. } TIM_OnePulse_InitTypeDef;
复制代码
9 s% X6 z" ?6 ~5 R

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

* w3 k9 n; h9 z' d( k

微信图片_20230402183046.png


3 Y1 y7 |* J1 j

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

  J1 a& j; ~+ N3 n, A' x  J

微信图片_20230402183042.png


' S) P; {" g7 J2 o- {( x

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

利用到的是TIM4,通道4

6 h0 _( @' t7 |$ w2 J

微信图片_20230402183039.png

5 ]) s, ~1 K  Z  o; u

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


  z7 m3 u' f) o3 f- y8 B! ~

微信图片_20230402183036.png


+ g, q/ n3 q* v! O& K2 [1 B

代码编写

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

  1. typedef struct{
    6 H7 X  ^7 U' j+ A" n" Y
  2. uint16_t LedpwmVal;//占空比调整参数
    * q) x/ Q2 Q  {$ _
  3. uint8_t LedpwmVal_Dir:1;//调整方向,1-递增,0递减
    % y, V3 y6 N5 H0 t" }
  4. }peripheral;
复制代码


2 Y# M4 |+ ^8 b; h* l

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


+ p0 M4 r; F% N

* z( t' t3 a0 L$ A/ ~0 x
微信图片_20230402183033.png
6 R! s6 I+ N$ D/ l0 c
7 X6 @4 C. U- m# o
  1. while (1): i  H6 ^5 @" _0 q  ?6 J
  2.   {" [4 J/ R' p/ v, W$ D' t
  3.     /* USER CODE END WHILE */2 B9 ?- W2 \& k" U$ @
  4. 1 [' h5 ~" H/ P+ A" N; I, s
  5.     /* USER CODE BEGIN 3 */3 I( c" Z' l  @% h
  6.   HAL_Delay(10);//加延时,否则变化不明显,看不到效果
    2 \) b* V, y% n& ^* v; g
  7.   
      V& [* x) C3 m/ U) `( i$ L2 `
  8.   if(PWM.LedpwmVal_Dir)' D1 j* R9 m: }- t
  9.    PWM.LedpwmVal++;
    1 ], K/ V, L* i7 Y( o6 l. D9 |4 |) _
  10.   else6 b6 D- g& P" c# j. D$ N) d7 N
  11.    PWM.LedpwmVal--;8 y6 e9 H- Y/ u- t) Q0 n
  12.   ) ]8 B. _  X! @) G' x
  13.   if(PWM.LedpwmVal>breath_UP)
    # F( M/ d: U9 ~, |* L! E: f2 z4 k& k
  14.    PWM.LedpwmVal_Dir=Breath_DOWN;//切换为PWM值递减状态# f5 b* l6 F- F' [! h' Z
  15.   
    % G' d" |9 I$ d. O
  16.   if(PWM.LedpwmVal==Breath_DOWN)
    + ?+ `  F+ t  Y# ~) n/ p3 c: V
  17.    PWM.LedpwmVal_Dir=1;//切换为PWM值递增状态; J! I- r1 f+ ?- ]% Y
  18.   
    ; F& c6 ^# ~$ `; ?* a" m8 R
  19. // TIM4->CCR4 = PWM.LedpwmVal;
    7 k( o4 m4 Y. }) t& N
  20.   __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_4,PWM.LedpwmVal);//设置占空比参数/ H' ?0 R1 S4 Y: y) J  s
  21.   }
复制代码
4 o# L+ H  G7 Y) Y1 i# b. r4 t/ d
* l: G$ F1 T5 Y2 I

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

- {  q/ @4 T( U5 I  |% O3 z: P+ U

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

递增效果:

1 ^" l; Z! k, P5 _

微信图片_20230402183030.png

4 a& B) x& j8 B8 I, N$ m+ T

递减效果:

5 p7 S% A% n' ?! b9 i" e5 ~

微信图片_20230402183026.png
, k) S4 g- i) _4 d转载自:Embedded小飞哥
5 t- O& u' w" }0 c2 `9 D, x如有侵权请联系删除0 @8 j+ {2 u6 u
5 y) ]* V% Q. U- ?) J/ M

( U; ~8 x7 l; p
赞 收藏 评论0 发布时间:2023-4-2 18:31

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版