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

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

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

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


  Z! {' N/ v. O& w

PWM原理介绍

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

) l* C8 o% G) n8 S7 i
9 A) l: i- r' T) N- |" c+ g2 y3 e
微信图片_20230402183103.png
! Q, y, {) I7 o* S" _8 G7 o5 i
PWM 原理示意图

: O* v. M: p5 E/ ~8 J. ]; o& p4 |, E" G# J5 Q

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

2 y5 `  z8 R/ A" Q9 E# T9 N. s( m

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


( P& h7 }, H* `- ?4 [! k4 h

微信图片_20230402183100.png

0 g: D' L. F) [; F1 r, C/ G; \

cubemx配置PWM

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

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

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

2 L1 p) ^1 ^9 h0 |* ^! }5 V1 Q

微信图片_20230402183056.png

5 g8 {9 Y! @4 u( A) Z! A; @

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

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


* V( D2 K/ X( C/ V. K

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

fclk = 72000000/500/72=2KHZ

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

# b5 |: V5 ]1 e/ `! G

微信图片_20230402183052.png

$ k7 N& N/ ^9 _  S

PB9是被复用的

3 ~5 L. `$ g/ I! G

微信图片_20230402183049.png

: B  A; L4 Y/ P

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

" j$ e$ Z2 i- Y$ e2 Z' Y

PWM代码解析

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

  1. void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle); p- q% U3 x  s
  2. {
    2 U) X5 O4 z9 ?& k: C

  3. ' y0 J; k7 T0 X4 |+ [- c1 r
  4.   GPIO_InitTypeDef GPIO_InitStruct = {0};
    % Y8 c  c$ ?1 g2 v
  5.   if(timHandle->Instance==TIM4)6 E" n( v7 T) J8 g9 p. R- j! O
  6.   {
    7 d. ^9 t) J$ B2 i3 H9 P
  7.   /* USER CODE BEGIN TIM4_MspPostInit 0 */
    7 V) E9 R! W8 n7 N: W- q7 I

  8. + S$ [/ y4 e' G4 K9 R: n1 e
  9.   /* USER CODE END TIM4_MspPostInit 0 */
    - R' J, u% k; m) k) L5 v
  10. 1 H* w3 ^& o4 M* K  k
  11.     __HAL_RCC_GPIOB_CLK_ENABLE();
    % o/ h+ L/ v' S* ]9 Q2 D( k
  12.     /**TIM4 GPIO Configuration* {$ Y7 @, ^9 U+ G8 R% b
  13.     PB9     ------> TIM4_CH42 b0 G. M# C1 [2 S" X) i, H3 h, W
  14.     */
      P. q7 y% H) ~6 E8 \- ~* X
  15.     GPIO_InitStruct.Pin = GPIO_PIN_9;/ e/ ^1 y8 f9 _6 P8 j
  16.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;0 y0 B3 _/ h& t- Q* V0 x* V3 s9 w/ f
  17.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;. S, N3 O- W- K# W
  18.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);# e, @; n9 V2 T: P! M

  19. ) l& l3 P* G2 h3 m
  20.   /* USER CODE BEGIN TIM4_MspPostInit 1 */
    . G* a5 t. e( t$ ], @6 h
  21. 7 m8 l/ N2 i! ~1 a2 W5 ^% h4 r
  22.   /* USER CODE END TIM4_MspPostInit 1 */' S, v4 y+ \/ s4 r( v2 F6 r
  23.   }
    ' o# A4 k% T/ E  V
  24.   o3 S3 @2 `5 \7 ?$ w
  25. }
复制代码

9 f  Q5 b: F4 K- b4 k3 b* o9 O

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

Pulse = .pulse/period


% W' m8 c) |: u% H, R

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


& I; n- H) M" M+ T# p0 H; o$ A# S9 o

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

  1. /**2 |+ c+ _2 H4 ^& N# A
  2.   * @brief  TIM Time base Configuration Structure definition
    ' \* j  }: j( t; Q- J
  3.   */
    : X3 ]- ?' k% j& b# r2 @/ e
  4. typedef struct* R9 y8 P, \5 G8 Q! `7 ]( Z! `
  5. {6 x4 K/ z$ O5 ^! k" i6 d
  6.   uint32_t Prescaler;         /*!< Specifies the prescaler value used to divide the TIM clock." @4 c3 `  Y2 }* O, C- V
  7.                                    This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */+ H# d+ B3 |6 R) K8 q3 C

  8. 7 H7 }! u4 \% j6 b9 r3 U
  9.   uint32_t CounterMode;       /*!< Specifies the counter mode.
      @+ i+ Q" a! A: ~+ L
  10.                                    This parameter can be a value of @ref TIM_Counter_Mode */8 ^5 i* `, d1 C+ j3 U
  11. 5 \& X) u9 H7 J. n* m/ ]0 e1 K
  12.   uint32_t Period;            /*!< Specifies the period value to be loaded into the active
      s/ ]/ X$ f! t6 i
  13.                                    Auto-Reload Register at the next update event.
    * p; S# g5 Z" U4 j6 I2 _
  14.                                    This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF.  */
    4 ?" {( P& S' u6 _- q' s4 q
  15. - R( h6 O3 D4 Z3 _" o" b
  16.   uint32_t ClockDivision;     /*!< Specifies the clock division.
    3 d) O# p) D' S4 }- n8 q
  17.                                    This parameter can be a value of @ref TIM_ClockDivision */
    1 h+ }' e& q5 G; q3 f

  18. 1 S# a5 G, Q4 w
  19.   uint32_t RepetitionCounter;  /*!< Specifies the repetition counter value. Each time the RCR downcounter( g$ C& K, L& y
  20.                                     reaches zero, an update event is generated and counting restarts0 s- t7 x# e6 `4 ~8 G# k
  21.                                     from the RCR value (N).
    * O7 K. _7 e8 ^7 t6 C, H' q
  22.                                     This means in PWM mode that (N+1) corresponds to:! @" h5 l' p7 S- i" k
  23.                                         - the number of PWM periods in edge-aligned mode
    9 R7 H( E& ]3 H- L$ H
  24.                                         - the number of half PWM period in center-aligned mode8 {+ i( _2 D1 ]( i0 ?7 b6 n$ Z/ Q
  25.                                      GP timers: this parameter must be a number between Min_Data = 0x00 and1 j2 I. N. _/ Y+ \
  26.                                      Max_Data = 0xFF.$ ^* V6 e! o3 u
  27.                                      Advanced timers: this parameter must be a number between Min_Data = 0x0000 and
    $ G0 D; C, u4 ~" C
  28.                                      Max_Data = 0xFFFF. */" y- {: [' W) `( J" W/ x& x

  29. # ^! G# l' N0 Y: r+ u
  30.   uint32_t AutoReloadPreload;  /*!< Specifies the auto-reload preload.
      H  a! p" e4 u2 C- y
  31.                                    This parameter can be a value of @ref TIM_AutoReloadPreload */1 B4 ?# W2 F9 Z" m, {, M
  32. } TIM_Base_InitTypeDef;
复制代码
  1. /**  S$ A" Z+ s% P/ N' N& |! z9 n3 j+ P7 d9 d
  2.   * @brief  TIM One Pulse Mode Configuration Structure definition
    4 D6 e) b  Y( X. X! _; L8 j" |
  3.   */
    . j) Y. Q' e/ R: s& ]9 w- N
  4. typedef struct. J9 X2 h6 ~/ s! ^* s
  5. {
    $ R5 y6 w2 ?4 N( s% q5 H
  6.   uint32_t OCMode;        /*!< Specifies the TIM mode.
    2 `* V# y8 j1 h$ B$ }
  7.                                This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes */
    , u% d& K6 d. }9 m+ ]8 o6 b

  8. 2 r4 M  r  ^# O7 M# z4 T. F
  9.   uint32_t Pulse;         /*!< Specifies the pulse value to be loaded into the Capture Compare Register.
    6 Z7 y  I1 X$ H. ]
  10.                                This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */) z& W, B( P( V, O. M; D
  11. & S1 y9 w( }+ v, T- w
  12.   uint32_t OCPolarity;    /*!< Specifies the output polarity.
    & I6 f7 `% A; ~/ V! H, B. x
  13.                                This parameter can be a value of @ref TIM_Output_Compare_Polarity */# _" z! c4 V! s( T4 y

  14. % s" s3 d- b8 f  E) s: h
  15.   uint32_t OCNPolarity;   /*!< Specifies the complementary output polarity.* [4 w. @$ t1 V5 i, |0 @, U. ^
  16.                                This parameter can be a value of @ref TIM_Output_Compare_N_Polarity  J/ }( r: f- B5 |! G
  17.                                @note This parameter is valid only for timer instances supporting break feature. */2 i5 {4 n% l+ [6 o) V6 t
  18. * k8 j5 u/ t8 N5 W
  19.   uint32_t OCIdleState;   /*!< Specifies the TIM Output Compare pin state during Idle state.
    6 P. @- N; ~, Q
  20.                                This parameter can be a value of @ref TIM_Output_Compare_Idle_State  I: ^  [. M# Y
  21.                                @note This parameter is valid only for timer instances supporting break feature. */# N4 z$ H; O! l' h' N. W- ?

  22. 1 \  p5 S3 V7 W" [' x2 {* R
  23.   uint32_t OCNIdleState;  /*!< Specifies the TIM Output Compare pin state during Idle state.* H& A/ v  R1 ?/ O
  24.                                This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State
    8 `8 N2 B: I; `/ p- ^  H
  25.                                @note This parameter is valid only for timer instances supporting break feature. */
    ! i2 E. p8 ^5 A
  26. * N4 b8 W8 Q/ q, Z
  27.   uint32_t ICPolarity;    /*!< Specifies the active edge of the input signal.
    ! l8 r6 p- x, f9 U
  28.                                This parameter can be a value of @ref TIM_Input_Capture_Polarity */+ @+ D0 w* X5 V  \9 Z$ G" C

  29.   L5 O' Z" i5 q  \& v" p
  30.   uint32_t ICSelection;   /*!< Specifies the input.
    , w- t* f7 i  w# P
  31.                               This parameter can be a value of @ref TIM_Input_Capture_Selection */
    0 R) ~* G1 E; U; n+ T
  32. + M! j; I7 u) G4 C0 k
  33.   uint32_t ICFilter;      /*!< Specifies the input capture filter.
    $ t2 x7 e' u0 T6 i4 c& n: I+ T
  34.                               This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */4 h1 O, I. `, K; A; j0 i- C
  35. } TIM_OnePulse_InitTypeDef;
复制代码
8 v( Z* V0 i: c9 R

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

& C$ }- i8 B" _/ }5 H2 x

微信图片_20230402183046.png

+ N' @& y" _  h

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

3 p- ?. [5 z- E1 }# k

微信图片_20230402183042.png

* b  o+ n" }7 R4 Q

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

利用到的是TIM4,通道4

% |+ Z! P$ x# f! K$ @* Y( x1 [) {4 J

微信图片_20230402183039.png


$ f4 Y6 ^& O" R1 r6 y+ f1 }% ~

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

% F1 M: g0 d6 }3 i6 q3 @$ E8 ?7 U

微信图片_20230402183036.png


' B( q) J5 {# n. C

代码编写

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

  1. typedef struct{+ \; J1 P# ?) `+ j0 H3 X
  2. uint16_t LedpwmVal;//占空比调整参数8 `2 z$ M% w3 m+ L
  3. uint8_t LedpwmVal_Dir:1;//调整方向,1-递增,0递减
    2 F9 K4 r5 a2 v5 Q+ f" s
  4. }peripheral;
复制代码

$ S" y+ r& k4 J" N0 Q- U8 u

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

7 {' w8 ~+ K2 c+ z2 O; U5 I" L

0 A# x0 B# T. V- s5 Y& p. W) ]0 [
微信图片_20230402183033.png

4 m, E* M2 M' t* U" E+ g1 g6 D9 J+ X9 t7 F3 Q, A9 C6 q
  1. while (1)
      H& u" n# k$ U' ~+ A
  2.   {
    8 J3 E4 Q2 Q" L/ Z/ h) ]. E5 ^
  3.     /* USER CODE END WHILE */
    5 l0 H  h3 d0 u+ Q8 s

  4. , T9 J$ P0 d8 b
  5.     /* USER CODE BEGIN 3 */
    6 q8 W- G) l+ `! s
  6.   HAL_Delay(10);//加延时,否则变化不明显,看不到效果
    3 y; [6 y' q) `7 @/ H) F. Z* Q0 g
  7.   : E9 ^) H, E6 l
  8.   if(PWM.LedpwmVal_Dir)% v; b/ u2 h6 N
  9.    PWM.LedpwmVal++;9 `0 @$ s. G5 J7 _
  10.   else
    * r, J! S, M1 p  @
  11.    PWM.LedpwmVal--;
    2 |# Q' T& C" i7 U/ h( w+ C
  12.   8 M: q3 a* o" u( o6 e. S
  13.   if(PWM.LedpwmVal>breath_UP)3 A; Q% Y. L1 h, Q, N( o2 [
  14.    PWM.LedpwmVal_Dir=Breath_DOWN;//切换为PWM值递减状态3 G3 |" v1 }" V" ~! x- c# x
  15.   6 @' }& V% @' @3 O  ]
  16.   if(PWM.LedpwmVal==Breath_DOWN)
    + [6 }# Q- ^9 R/ F, n! J
  17.    PWM.LedpwmVal_Dir=1;//切换为PWM值递增状态+ J! Z4 W4 C1 ~  A
  18.   9 h$ K! Z- }' X( x$ O, P- [) [
  19. // TIM4->CCR4 = PWM.LedpwmVal;
    3 c7 x0 \# r* ]6 a0 i, Q5 |
  20.   __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_4,PWM.LedpwmVal);//设置占空比参数* ]; K3 ^$ y9 Z$ c
  21.   }
复制代码
! r6 A/ }& f% G+ i: h" ?" j# I
$ {9 k4 k/ t; w4 ~* S- H9 y; l

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


: i6 N/ l% W9 D  E  l% _  q% L

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

递增效果:

! s1 g' N$ H1 i

微信图片_20230402183030.png

( [" X* b  k& p6 z4 b! L+ {

递减效果:


8 `: }* I6 c, W3 J; E, _5 `( R

微信图片_20230402183026.png , o6 i5 q& i4 e. u  X' E
转载自:Embedded小飞哥% R( g0 ?+ |- O7 I
如有侵权请联系删除
' `) v8 l8 Z4 B! [% c& Q9 W: T; F$ Q* ]
( G8 H* f9 {; I
赞 收藏 评论0 发布时间:2023-4-2 18:31

举报

0个回答

所属标签

相似技术帖

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