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

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

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

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

/ x2 d# Q/ ?6 k1 ]* K  I- F5 Q

PWM原理介绍

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


1 w) a# d2 _: _& d  Y6 {0 i0 w8 A

! [4 `' t) H0 ^2 k0 O. u
微信图片_20230402183103.png

2 F/ @% e: p  _3 x2 w) H" e
PWM 原理示意图

* _' D& K- g" t5 w4 }
6 M' |  u) f) m. c* R

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


; q  B6 A+ o4 C3 O) Z. S4 Z( h

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

9 H- _9 s3 I! @1 R6 U* v, d5 q6 W7 \

微信图片_20230402183100.png


6 p; }! y$ V# U4 ]/ }- ~

cubemx配置PWM

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

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

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


1 g: y8 T3 I: ?. e8 [

微信图片_20230402183056.png


& g* k4 O1 p- {) l" d5 i: S

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

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


0 F! {9 m# N" U6 \4 p( a+ I

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

fclk = 72000000/500/72=2KHZ

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


3 c- `4 u, ^0 l0 d6 o8 G

微信图片_20230402183052.png

' i* J5 X; [0 V. M/ S  x- j

PB9是被复用的

1 O+ U( q* ?: E' f. F3 r

微信图片_20230402183049.png


6 m1 X  Y1 U5 c" C+ R3 V! x

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


1 e, b& S- n0 M8 i

PWM代码解析

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

  1. void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle): A. z& J, q0 @: J3 K" ~8 I
  2. {
    ; q; k/ l+ ?# ~+ i" r
  3. & ^5 v8 C- b) t% Y+ j
  4.   GPIO_InitTypeDef GPIO_InitStruct = {0};2 h0 n8 q/ K' ^9 \
  5.   if(timHandle->Instance==TIM4)0 L$ b! }& k2 K3 o5 |
  6.   {
    ) Y, S  S) l4 D7 y
  7.   /* USER CODE BEGIN TIM4_MspPostInit 0 */' P% u; F6 @7 @/ o9 W  V: U$ c" d
  8. 1 A) D5 T; V0 [0 o- }% [9 B
  9.   /* USER CODE END TIM4_MspPostInit 0 */% M% }- {" K1 ~, g
  10. 5 k: S6 j; Y" V
  11.     __HAL_RCC_GPIOB_CLK_ENABLE();. x: K9 G8 u1 F) P1 {) g$ \$ ^
  12.     /**TIM4 GPIO Configuration
    % F3 ?6 D9 z3 E/ C+ ?3 I& N3 q
  13.     PB9     ------> TIM4_CH48 C1 }0 o5 x8 S4 c6 w) y* c' l
  14.     */0 [3 H9 B, ^7 {
  15.     GPIO_InitStruct.Pin = GPIO_PIN_9;( y2 ?/ Y0 f+ c
  16.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;# U. Y5 E( x2 ~
  17.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;4 s" h" O+ N6 B. V8 I. W
  18.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);/ h' E2 T: L) V& G5 |; p: B: X

  19. " D4 |4 a0 ^0 Q. a
  20.   /* USER CODE BEGIN TIM4_MspPostInit 1 */$ s& E5 X8 W& V! ^: E+ b
  21. 3 T% R4 L, l$ w$ h: N2 @7 S& L4 |
  22.   /* USER CODE END TIM4_MspPostInit 1 */( H2 O/ R. I: K: G3 X9 W+ K1 h! d
  23.   }
    ' u! d2 V( [! z" j6 T

  24.   a& D: k8 g( J# O! k" G# k& w2 h
  25. }
复制代码

* s. Z" x; q& X+ f

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

Pulse = .pulse/period

5 c- q, E. l* K7 M% R

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


( v# R) c: X0 h/ M* H3 i0 t

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

  1. /**
    ) k, ~/ H7 M7 O
  2.   * @brief  TIM Time base Configuration Structure definition
    " q, D; B( l( v$ J2 E1 C4 f
  3.   */& t( g8 d+ \! {& B/ B! q6 [
  4. typedef struct: c- W% L2 R! b9 d, c) Z! k
  5. {" ~4 t( l! X5 ?9 F% K; Q
  6.   uint32_t Prescaler;         /*!< Specifies the prescaler value used to divide the TIM clock.
    $ M8 ~: j: {( K% b/ ?; v3 b
  7.                                    This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */- U. y4 W) s' Q0 T* W# G$ a$ ]

  8. 2 \; |1 @0 H8 A3 f+ y$ k; g
  9.   uint32_t CounterMode;       /*!< Specifies the counter mode.! e, L: F7 [* d5 _
  10.                                    This parameter can be a value of @ref TIM_Counter_Mode */, i5 M; V2 p0 @& K  a# }- A( O

  11. ! v3 r5 G% i/ L- D5 G
  12.   uint32_t Period;            /*!< Specifies the period value to be loaded into the active
    5 [" D: I* k+ h9 i; g- o
  13.                                    Auto-Reload Register at the next update event.2 `  f% y& S+ }% @
  14.                                    This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF.  */
    5 K! a9 e, O# D; X: {5 B3 ?0 J

  15. ) H9 t3 f1 g8 b- @5 s" w
  16.   uint32_t ClockDivision;     /*!< Specifies the clock division.
    " N: h& {) Y3 L
  17.                                    This parameter can be a value of @ref TIM_ClockDivision */5 \0 [1 b$ u% S6 |
  18. # u/ v- M3 E4 |4 ]4 N8 B2 C3 F
  19.   uint32_t RepetitionCounter;  /*!< Specifies the repetition counter value. Each time the RCR downcounter
    - h3 y, d' c% u* z
  20.                                     reaches zero, an update event is generated and counting restarts- g/ E8 X" i$ \) j$ N' K
  21.                                     from the RCR value (N).
    2 A5 _* }; W* C3 ^$ e: t" \
  22.                                     This means in PWM mode that (N+1) corresponds to:
    ! L4 l) m/ ]+ J* Y+ H% M9 `. T" @! N
  23.                                         - the number of PWM periods in edge-aligned mode2 N$ `. O+ P6 C. f! H; m6 j
  24.                                         - the number of half PWM period in center-aligned mode
    9 ?' ~6 n3 i, x4 J* H5 t' G- |- Z
  25.                                      GP timers: this parameter must be a number between Min_Data = 0x00 and
    , X! j" O; I% S8 \" C* Z
  26.                                      Max_Data = 0xFF.
    5 @. m9 R' e$ ^7 ]8 Q3 M
  27.                                      Advanced timers: this parameter must be a number between Min_Data = 0x0000 and
    0 f: G; F8 e# D1 C
  28.                                      Max_Data = 0xFFFF. */
    ' x% G7 v/ x# [% g; x" |9 m5 V
  29. 8 j6 @/ f) n  n1 }
  30.   uint32_t AutoReloadPreload;  /*!< Specifies the auto-reload preload.
    7 I5 }0 n; N' R5 p
  31.                                    This parameter can be a value of @ref TIM_AutoReloadPreload */
    - x. z3 M4 G3 r$ [
  32. } TIM_Base_InitTypeDef;
复制代码
  1. /**& L; H$ F0 ?! }
  2.   * @brief  TIM One Pulse Mode Configuration Structure definition
    ' C9 p2 ^5 X3 _( D6 J' Q
  3.   */& p' G5 I2 `2 V3 ?) t& o
  4. typedef struct
    ( {! [5 V+ Q" w5 t
  5. {
    8 n# S' O& k7 ~: }. _+ u% `
  6.   uint32_t OCMode;        /*!< Specifies the TIM mode.
    / M. [: X- }. ^6 C9 x7 K, t( n
  7.                                This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes */3 |: Q# }+ ^3 p! C

  8. 1 M- Z, o9 U; Q$ F5 w
  9.   uint32_t Pulse;         /*!< Specifies the pulse value to be loaded into the Capture Compare Register.
    / e9 u- O6 P& ^4 ^+ x
  10.                                This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
      ]; A2 A- M5 X
  11. $ g7 p( S* ^9 p
  12.   uint32_t OCPolarity;    /*!< Specifies the output polarity.5 }; |, V& V+ r: O- {
  13.                                This parameter can be a value of @ref TIM_Output_Compare_Polarity */
    6 ^+ o) h7 C6 G& H6 q" E

  14. 0 U9 p/ I, M+ R
  15.   uint32_t OCNPolarity;   /*!< Specifies the complementary output polarity.. y- n( n0 W' x% s  A* Y- n" \8 {9 }* q
  16.                                This parameter can be a value of @ref TIM_Output_Compare_N_Polarity
    ' ~- l: G5 y" \) f; u9 {1 L% y
  17.                                @note This parameter is valid only for timer instances supporting break feature. */
    & v0 h7 i* w% K# e
  18. % [' T7 T% d/ i6 n. [8 o% W$ }' E1 k
  19.   uint32_t OCIdleState;   /*!< Specifies the TIM Output Compare pin state during Idle state.
    ) m3 j- E  b# f$ B/ m  a) T
  20.                                This parameter can be a value of @ref TIM_Output_Compare_Idle_State. W$ q" R# i7 a: A. {
  21.                                @note This parameter is valid only for timer instances supporting break feature. */
    , Z; a, m1 ~$ R9 N4 ~, l, u

  22. # I6 H- G( l! ?; |0 u4 W& j
  23.   uint32_t OCNIdleState;  /*!< Specifies the TIM Output Compare pin state during Idle state.  z4 I# J- l+ v2 {+ O: D
  24.                                This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State% y# [7 E8 ^7 K
  25.                                @note This parameter is valid only for timer instances supporting break feature. */1 X. m- W* b% _. ]/ V% M5 U" v
  26. 8 i: p. M3 h8 L* }
  27.   uint32_t ICPolarity;    /*!< Specifies the active edge of the input signal.* a% @2 `, k6 `8 q/ {( n: `
  28.                                This parameter can be a value of @ref TIM_Input_Capture_Polarity */
    / x4 C: L) ?) V: l: Z

  29. ; X' Z0 A. N1 e. q9 K7 f* r; ?
  30.   uint32_t ICSelection;   /*!< Specifies the input.
    & P1 D. J" ~# n: c& W
  31.                               This parameter can be a value of @ref TIM_Input_Capture_Selection */4 ^: S: W7 L. C* y- W# M- z

  32. , h3 T7 [3 z1 [8 K/ I9 n0 `
  33.   uint32_t ICFilter;      /*!< Specifies the input capture filter.8 ^8 w6 s/ s- y1 q% W
  34.                               This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */7 o: x* Q1 x/ A' v7 P3 f: A
  35. } TIM_OnePulse_InitTypeDef;
复制代码
7 y% \7 o9 f( T! e/ \. o

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

/ _0 p4 R5 ^3 G8 w

微信图片_20230402183046.png


. h6 b0 A7 c: z( ~$ z7 Y

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

9 A4 X' G7 x9 Q7 k  Q- J8 {8 S

微信图片_20230402183042.png


/ ^$ F; a; N) P) J" T7 W1 ?

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

利用到的是TIM4,通道4

1 Z4 r( u/ A5 [$ p& _% b* i

微信图片_20230402183039.png


0 M+ [1 E' P5 R( N& `

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

8 \% Y( S4 V; B; Y  b

微信图片_20230402183036.png


7 B$ `7 Q0 ]. W( h& ]- W- B

代码编写

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

  1. typedef struct{
    & j) Q& c' Z+ t/ ?
  2. uint16_t LedpwmVal;//占空比调整参数' v# K& ~7 n7 D  M
  3. uint8_t LedpwmVal_Dir:1;//调整方向,1-递增,0递减4 I: M' v8 N  b7 s
  4. }peripheral;
复制代码

. X2 F6 X  {1 u+ h

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


  n- _: K3 X, _0 n

7 p5 ]/ f* a) @: V$ [+ n% L* [7 g
微信图片_20230402183033.png
8 I5 \/ N! t6 ~- ^+ f

1 a* K5 u5 v+ E+ X9 w1 I; b" k7 }9 u
  1. while (1)8 Q8 t) B2 L) K* ~+ u
  2.   {! i7 J' w" ~$ a& `9 `) g
  3.     /* USER CODE END WHILE */
    5 n% J1 ]( e: Q/ |2 x) z

  4. / e7 M: b0 V$ s
  5.     /* USER CODE BEGIN 3 */
    1 F' e; T, N' A: y
  6.   HAL_Delay(10);//加延时,否则变化不明显,看不到效果2 i$ j! [' p; H  H) ?7 m
  7.   # E( B, _# f. G% ^9 T
  8.   if(PWM.LedpwmVal_Dir); d1 i7 K8 K$ C
  9.    PWM.LedpwmVal++;1 F8 _7 e4 q6 G  O( u! q& @
  10.   else
    + M/ y) ]: X' I8 ?
  11.    PWM.LedpwmVal--;  r4 s% ~1 \" l* K% p
  12.   
    + p% [$ n- I% {0 `
  13.   if(PWM.LedpwmVal>breath_UP)
    8 }: Y+ R" b6 `. ~, y; s* j$ U
  14.    PWM.LedpwmVal_Dir=Breath_DOWN;//切换为PWM值递减状态; J* y1 K) |9 l
  15.   9 g4 k9 s/ o& g$ u' g3 s4 g% t
  16.   if(PWM.LedpwmVal==Breath_DOWN)' i8 w$ C5 I1 U# e
  17.    PWM.LedpwmVal_Dir=1;//切换为PWM值递增状态9 f; Z5 n  c  m4 o, |( j
  18.   
    ; R" B+ I9 Y" @- S
  19. // TIM4->CCR4 = PWM.LedpwmVal;) y  p* q1 [6 B  n7 S/ o
  20.   __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_4,PWM.LedpwmVal);//设置占空比参数
    : b$ e9 H" Y3 ?) p# h6 M
  21.   }
复制代码
2 N8 ~* `' q! ]0 ]' a
/ Z% b4 ]5 e  B8 W: @3 A

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

# f2 i8 N4 u9 I1 h) j3 O

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

递增效果:


& ]) i' n" X! W3 F7 _4 [$ _

微信图片_20230402183030.png

( S; S8 {; c" z/ c* t

递减效果:

. d: \- x- Q* O/ r2 H6 r. {( ^

微信图片_20230402183026.png 3 ^9 R4 E+ W% g- R4 ~6 w: R8 Y
转载自:Embedded小飞哥2 p, O9 i! a2 [0 H. s* k
如有侵权请联系删除
! s+ I0 _# A- E3 [; j7 E6 ]  q" Q$ k; |! Y4 K

% l3 ~9 \, P' x
收藏 评论0 发布时间:2023-4-2 18:31

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版