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

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

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

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

4 q9 i$ B7 X1 E1 B6 x3 E

PWM原理介绍

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


. L7 ?+ |0 s+ Y; H0 `3 q5 `. G

; ^9 U' v: o  b* \& D
微信图片_20230402183103.png

# n' \* w. k. I3 f: |7 @& r
PWM 原理示意图
: {. o0 j9 c7 \

: N) M- f9 _+ \6 |

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


0 {: G6 b- x; U

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


4 _8 b. k/ a1 }# X2 f6 {- U

微信图片_20230402183100.png

. _/ ?7 C- ~$ a! @

cubemx配置PWM

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

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

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

4 X) k% f' ^, Q4 I& a4 x# k8 g

微信图片_20230402183056.png


; Q& H8 h4 w6 |* g! K

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

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

$ F0 Y2 ]5 u7 ^0 A, M7 Z

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

fclk = 72000000/500/72=2KHZ

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

1 S. b! J4 P$ Y5 ]

微信图片_20230402183052.png


' b  R% i8 A& ~! L

PB9是被复用的

; t$ P3 b6 E* a( `& i5 M

微信图片_20230402183049.png


: n! i& M5 y" v- m

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

( {9 C" k/ H& k

PWM代码解析

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

  1. void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
    0 Q3 I: e3 G2 T5 U0 O4 ~# J
  2. {
    6 E# Y9 i: i- F; q* l9 m$ T1 e

  3. % @: D6 \- \9 B
  4.   GPIO_InitTypeDef GPIO_InitStruct = {0};% Q* m6 B1 ?6 Z  b
  5.   if(timHandle->Instance==TIM4)" C( j# {$ Y: `- n! E9 v/ z& K% f
  6.   {
    & E! V! v4 R* m
  7.   /* USER CODE BEGIN TIM4_MspPostInit 0 */8 y& A/ a+ z$ e3 y
  8. . f8 J8 f* j' N/ ^2 p/ p. T
  9.   /* USER CODE END TIM4_MspPostInit 0 */8 F. a, g: F8 r" W

  10. , \7 |, @, a2 w! t
  11.     __HAL_RCC_GPIOB_CLK_ENABLE();
    ( d/ }4 I) Q; e% s
  12.     /**TIM4 GPIO Configuration( Z: v/ S! `. M
  13.     PB9     ------> TIM4_CH4
    ! y: e# H+ F! k" M( P4 a0 y4 {, G
  14.     */
    ( |8 {9 `2 f$ a" |+ }, M/ z
  15.     GPIO_InitStruct.Pin = GPIO_PIN_9;
    0 G$ H4 ?! k, y6 I
  16.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    : X& {% i6 c% g# y% {- D
  17.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;# |; n- H5 R7 w$ U2 B( j# d. r
  18.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);) Z4 F) Y5 W, X; Q" z
  19. 7 m% C4 M: O  x2 I' V# c
  20.   /* USER CODE BEGIN TIM4_MspPostInit 1 */
    / j- m, ^7 c5 Q& w5 t

  21. , O8 a" |, ]! t3 u/ l
  22.   /* USER CODE END TIM4_MspPostInit 1 */1 r3 a9 a- X0 ^( k
  23.   }
    ) J. L5 F- t* T2 o
  24. ) ]8 ~+ ~4 g2 Z$ F, a; {! X
  25. }
复制代码
) O8 M% f8 i9 ^  X; ~8 f( D

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

Pulse = .pulse/period


# K1 l. Q+ P/ p

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


" W7 j% ]. |0 @% q; i

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

  1. /**0 `4 M3 Q/ X( {* N0 M: m
  2.   * @brief  TIM Time base Configuration Structure definition
    - m" d& H( d9 _9 D: F( u
  3.   */0 c' f7 V% }& z, R0 [" r+ w
  4. typedef struct: v$ o) d  Y* C2 {9 y3 T( A
  5. {6 Y* p8 N& y0 Q0 b
  6.   uint32_t Prescaler;         /*!< Specifies the prescaler value used to divide the TIM clock.
    0 V' I" s7 x8 E3 f( Y; ~
  7.                                    This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
    * O0 `' K( ]3 T- `  |) G
  8. ! t) J8 i5 c# {5 M' f
  9.   uint32_t CounterMode;       /*!< Specifies the counter mode.
    8 p6 z$ q. ~& E, {7 u3 [+ m- v" \
  10.                                    This parameter can be a value of @ref TIM_Counter_Mode */2 U8 a. \; h1 {4 j) z
  11. 2 A. ~( G+ y9 D) {1 J: ]7 ?4 V& p& `
  12.   uint32_t Period;            /*!< Specifies the period value to be loaded into the active+ R/ d9 Q" n; {! r0 h# N, w
  13.                                    Auto-Reload Register at the next update event.3 `! n! E" X* {5 @
  14.                                    This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF.  */
    2 w4 r1 M) b' ]8 j1 E
  15. $ F, u9 j& W2 `  w* N* ]& n' E4 g+ F
  16.   uint32_t ClockDivision;     /*!< Specifies the clock division.7 N( B/ m5 F# r) A8 n& D1 r
  17.                                    This parameter can be a value of @ref TIM_ClockDivision */# G' ~6 ]% A5 I4 \7 L' Q2 y$ M3 R

  18. ) [& V" f# V4 @" Y& t9 X
  19.   uint32_t RepetitionCounter;  /*!< Specifies the repetition counter value. Each time the RCR downcounter% P; h& ~; s. S# ^3 Q" I( p
  20.                                     reaches zero, an update event is generated and counting restarts2 e. _9 x9 {5 H1 g/ j1 J; n
  21.                                     from the RCR value (N).
    4 O- R% k+ c/ e" v! W& c- m
  22.                                     This means in PWM mode that (N+1) corresponds to:
    1 V. J9 `. T( R3 J1 \1 K, X
  23.                                         - the number of PWM periods in edge-aligned mode' d6 l( z# w+ v: b4 n' m
  24.                                         - the number of half PWM period in center-aligned mode* T  ?* D6 n6 s' r4 Q
  25.                                      GP timers: this parameter must be a number between Min_Data = 0x00 and
    + P8 }9 P3 n: ?; U$ i! J
  26.                                      Max_Data = 0xFF.% H  l2 Z) {7 `: i) ]- q
  27.                                      Advanced timers: this parameter must be a number between Min_Data = 0x0000 and$ F4 _  i* i, O2 \7 p  w; l
  28.                                      Max_Data = 0xFFFF. */( H, N; Y7 t8 A" A% _+ P
  29. 4 p1 }5 M+ ]) F$ F" p2 F  L/ G. G
  30.   uint32_t AutoReloadPreload;  /*!< Specifies the auto-reload preload.- y! G: c7 z  F( @1 N
  31.                                    This parameter can be a value of @ref TIM_AutoReloadPreload */
    , c% i/ f4 @, w# w
  32. } TIM_Base_InitTypeDef;
复制代码
  1. /**
    ) o8 u( a* M- h% w$ w- A
  2.   * @brief  TIM One Pulse Mode Configuration Structure definition7 M8 i; B7 H% V6 o# I" j2 o3 F, n
  3.   */3 @  W) s' V5 z/ N! k
  4. typedef struct
    3 f+ o: w" D- X' `1 b
  5. {
    , b+ j2 H& _  u! ?0 R6 ^% P
  6.   uint32_t OCMode;        /*!< Specifies the TIM mode.- {3 r3 ~: f0 W9 C
  7.                                This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes */
    ( e) D5 s; T( h% h) _3 Z, z1 a

  8. 2 v; E7 a6 ?/ \' L/ @, Y/ R
  9.   uint32_t Pulse;         /*!< Specifies the pulse value to be loaded into the Capture Compare Register.
    / f; H& B, u6 P/ N: Z) ~' j( b8 a3 Y
  10.                                This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
    9 f$ r! q% \, ~% u0 b! L. y
  11. 4 y6 a4 b3 `9 A, z
  12.   uint32_t OCPolarity;    /*!< Specifies the output polarity.
    & m, k: k$ K: I& c
  13.                                This parameter can be a value of @ref TIM_Output_Compare_Polarity */
    ; K1 H' H- `  B5 P# N, U8 w
  14. ; P- g' z1 D8 E  ]9 @/ y
  15.   uint32_t OCNPolarity;   /*!< Specifies the complementary output polarity.
    * y3 f& m9 r% h
  16.                                This parameter can be a value of @ref TIM_Output_Compare_N_Polarity
    3 i- m0 p( c+ Q+ o+ ?- {/ f
  17.                                @note This parameter is valid only for timer instances supporting break feature. */% T6 o% R! [6 h# J7 [# J

  18. ! m- T7 M9 Q. y$ |
  19.   uint32_t OCIdleState;   /*!< Specifies the TIM Output Compare pin state during Idle state.: A$ s+ P8 q0 z; J. \- o" T  |8 Z; s  j
  20.                                This parameter can be a value of @ref TIM_Output_Compare_Idle_State
    3 n/ K+ r9 B  _+ p
  21.                                @note This parameter is valid only for timer instances supporting break feature. */0 H  r+ f- R5 |" H& w

  22. 3 |& G# j* N+ m8 l% y( g
  23.   uint32_t OCNIdleState;  /*!< Specifies the TIM Output Compare pin state during Idle state., X, g- ^1 b) |0 c2 @4 @
  24.                                This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State. b# ^0 c" e5 `2 B; ^3 G1 s
  25.                                @note This parameter is valid only for timer instances supporting break feature. */
    4 a, m: ]( L4 |' n5 h* `, q7 w

  26. 8 ~/ W, m  F( p, A
  27.   uint32_t ICPolarity;    /*!< Specifies the active edge of the input signal.0 U9 G' l) |. h; Q% o- b
  28.                                This parameter can be a value of @ref TIM_Input_Capture_Polarity */! a, {% a) \- g$ [: |" a, a

  29. / P0 l5 G3 Z4 ~7 S/ X6 E
  30.   uint32_t ICSelection;   /*!< Specifies the input.
    . ]$ ^. v2 N7 |0 [% {- _: m
  31.                               This parameter can be a value of @ref TIM_Input_Capture_Selection */) `8 A7 J, _/ q: ?4 f- j
  32. - X: _( x5 J2 c% L9 R
  33.   uint32_t ICFilter;      /*!< Specifies the input capture filter.8 H7 d" _9 p7 [. O
  34.                               This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */0 p% }1 \  o) t
  35. } TIM_OnePulse_InitTypeDef;
复制代码

; K: J$ F# G2 i8 ]7 m

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


. t' C2 ^( D8 e/ p: n  H3 Y

微信图片_20230402183046.png

& Z1 S3 m' ~5 W2 R1 k& a

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

! @$ R: s2 G. A# K: J& j* h6 A

微信图片_20230402183042.png


$ l: L! s+ |" A" X% e* C% o

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

利用到的是TIM4,通道4

7 G1 C% N. Z) c( q3 N

微信图片_20230402183039.png


" |/ Z2 T, u( p- E; e

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

5 _8 k0 T" @( [5 q4 O* i

微信图片_20230402183036.png

9 \- Z0 g2 T  s: e4 {

代码编写

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

  1. typedef struct{& j) W% Q3 ^: H9 x6 X6 u
  2. uint16_t LedpwmVal;//占空比调整参数3 H1 A6 o* k3 D* l5 N# w
  3. uint8_t LedpwmVal_Dir:1;//调整方向,1-递增,0递减. I  ?% c3 r% H' N4 u
  4. }peripheral;
复制代码


+ m9 P$ ~' z& P- V: a3 w% J2 b

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


9 Q$ ?/ w8 n7 M2 q% O
% Y& X5 _' q  d+ m) A3 f% @4 t4 ^& q
微信图片_20230402183033.png
! v$ A4 \8 j6 E! `3 M& F1 P% h5 i
7 \1 ?9 J' h* q2 f: x, l
  1. while (1)7 n) v6 B# G, V$ \& u
  2.   {
    # C# B. S, ]% D# U' d. r
  3.     /* USER CODE END WHILE */
    ! h1 `" B* y% _  R0 r. |- z
  4. 5 s: @- ?& T" ?& x+ ]# I
  5.     /* USER CODE BEGIN 3 */
    ' v" R, ]6 B$ H5 l" v- P
  6.   HAL_Delay(10);//加延时,否则变化不明显,看不到效果8 c0 X9 ]" L, F- ~; v; F% h
  7.   
    , f& J: X! L! o
  8.   if(PWM.LedpwmVal_Dir)' a3 c! M" G( p2 b
  9.    PWM.LedpwmVal++;: Q3 t, n) p! R
  10.   else
    ' y+ N- m, r) R" W& E8 Y
  11.    PWM.LedpwmVal--;8 h, y& G$ ~% e1 @* c
  12.   ! B1 M' T! Q8 }* `% v( w
  13.   if(PWM.LedpwmVal>breath_UP)
    ) u2 h5 V7 l0 j5 F7 R
  14.    PWM.LedpwmVal_Dir=Breath_DOWN;//切换为PWM值递减状态+ L1 w' h% I; r/ ?2 y, K6 `6 h
  15.   
    ! v! X  P% e1 _0 U# x
  16.   if(PWM.LedpwmVal==Breath_DOWN), b( X; N1 f# z& a4 M) T
  17.    PWM.LedpwmVal_Dir=1;//切换为PWM值递增状态8 C% m8 I$ S) a4 I- I  _$ z
  18.   
    0 q. h) l1 b1 x1 e! [
  19. // TIM4->CCR4 = PWM.LedpwmVal;5 h1 M, S# a' `4 G
  20.   __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_4,PWM.LedpwmVal);//设置占空比参数4 [" `$ r. O- Q" D
  21.   }
复制代码
8 f: Z# Y6 m) l+ j& G2 _0 L9 X* i2 b; K

5 c' E' ^# m, F) B, L

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


; r/ W! S4 m! s% g

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

递增效果:


& o5 J2 G5 o7 P. U/ }% a

微信图片_20230402183030.png


  F4 @+ y, D5 {0 {$ A

递减效果:


1 O# t! E8 `0 W! ], F7 h& }

微信图片_20230402183026.png 5 f/ Q/ ~& U4 M) X: C5 t( S
转载自:Embedded小飞哥
" y- E3 L/ T) F; x6 |& y如有侵权请联系删除
. i# b' }; X& P; C2 q1 V8 n9 E
2 F, T) f/ |, v' @/ |
; q  ~+ Q" D% i1 ~9 G
赞 收藏 评论0 发布时间:2023-4-2 18:31

举报

0个回答

所属标签

相似技术帖

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