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

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

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

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


4 F; P) U- r  a, m- r) S1 H

PWM原理介绍

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

" l) ^7 u- _( T8 K6 _0 |

" `" ?  F2 ]/ j
微信图片_20230402183103.png

' U/ k4 k6 j. h9 s
PWM 原理示意图
& n" w2 B7 J' W5 l- Z9 a
; M9 h5 X" }7 ^$ g. R/ D' v0 C

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


- w& l9 ~6 M+ d- T) |

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


% \( Q) l$ ?9 ?

微信图片_20230402183100.png


/ F8 a+ ?' p0 S: ^' @

cubemx配置PWM

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

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

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

' T4 |, _: g% c6 U3 S: ^) b8 d6 H

微信图片_20230402183056.png

7 N2 W& ~3 {$ i

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

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


, }0 x7 [$ J7 Q7 ^

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

fclk = 72000000/500/72=2KHZ

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


$ q; S: _/ W' E- I1 {5 C: [

微信图片_20230402183052.png


7 g9 U$ X  ?! V7 X

PB9是被复用的


- ]1 K. q7 M. \& c

微信图片_20230402183049.png


; H: A, d# y5 x, L3 b6 X

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


- |4 j$ z7 P) I) Z% p. y

PWM代码解析

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

  1. void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)3 @* K( W! S3 u' O
  2. {
    , ^' e; \% x  a' Y0 A) J

  3. ) v. v& k  s* J
  4.   GPIO_InitTypeDef GPIO_InitStruct = {0};
    3 E! H: I* C6 A, b; f1 Y; _
  5.   if(timHandle->Instance==TIM4)
    ' j! i! f+ w! G8 `" ~( u* `9 K
  6.   {; d2 G. @4 H. E  a. z" n4 u  ~
  7.   /* USER CODE BEGIN TIM4_MspPostInit 0 */, c0 G2 i- [  l  x' c7 C
  8. # i9 T  E8 S; @5 ~  J! C! H
  9.   /* USER CODE END TIM4_MspPostInit 0 */
    " N5 O5 ?& c; Y# ]3 k
  10. 7 K/ i( [3 J5 \/ l; e
  11.     __HAL_RCC_GPIOB_CLK_ENABLE();" B& f1 S- I; S
  12.     /**TIM4 GPIO Configuration/ ]8 t& D  _9 x( T: \
  13.     PB9     ------> TIM4_CH4
    6 Y0 l3 s1 W8 m+ _1 o! L4 J. }; V
  14.     */
    / x+ s* ^) c8 R
  15.     GPIO_InitStruct.Pin = GPIO_PIN_9;
    2 h! M8 U; R4 Q1 I% g* B/ V
  16.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;1 L( ^9 L1 S) B! @
  17.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;- J# B3 Z; d, ]- Y
  18.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);4 y/ s) t" t% u" N1 X
  19. ' v% I8 N1 P" c
  20.   /* USER CODE BEGIN TIM4_MspPostInit 1 */
    2 \+ e6 ]3 R9 q

  21. - I5 U1 N$ T. m
  22.   /* USER CODE END TIM4_MspPostInit 1 */
    - U7 m  ~3 c! j) I) t5 d# z  C
  23.   }6 [. w# Q/ {& H
  24. 8 f8 H3 T9 q0 {7 h9 b) i* g7 H
  25. }
复制代码
+ R) O: z. l% M: I. n

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

Pulse = .pulse/period

0 }# B* R2 r/ s) E

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


# M. p3 Q" M) G6 ~3 s' Z: z. k

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

  1. /**
    ' n& T( ^; @3 D& T9 Z3 {
  2.   * @brief  TIM Time base Configuration Structure definition& @) S: N  [4 a& {- `
  3.   */
      E( C! e' U6 K4 i* i
  4. typedef struct
    ; g& ]; ~. I+ P* e
  5. {* T5 g9 T+ h7 X# n
  6.   uint32_t Prescaler;         /*!< Specifies the prescaler value used to divide the TIM clock.( T+ h+ h+ T+ b" {6 k
  7.                                    This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */- u  l! O8 _; P) [! @

  8. & u! i* u3 }- R# B6 Y
  9.   uint32_t CounterMode;       /*!< Specifies the counter mode.% ^3 E& W5 [# T% N* |9 t
  10.                                    This parameter can be a value of @ref TIM_Counter_Mode */) W- l8 [  K/ V" Z0 `+ Z

  11. / \, \% z5 i4 U: x. q5 s3 ?
  12.   uint32_t Period;            /*!< Specifies the period value to be loaded into the active* `; M& A6 |5 r9 p* Q# A' N
  13.                                    Auto-Reload Register at the next update event.# k' W7 i) t% m: b% |
  14.                                    This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF.  */3 n& {  Q2 I, F
  15. + ~6 Q' t6 o8 Q2 o
  16.   uint32_t ClockDivision;     /*!< Specifies the clock division.
    * X9 C7 R6 M% V, P
  17.                                    This parameter can be a value of @ref TIM_ClockDivision */' g5 A; j- ]; @' i3 X1 \
  18. & i" s0 q- I5 m( {: n
  19.   uint32_t RepetitionCounter;  /*!< Specifies the repetition counter value. Each time the RCR downcounter
    ) X: j2 ^% @% }7 l3 I
  20.                                     reaches zero, an update event is generated and counting restarts( X: h. q" `0 F; d( g* V/ n8 Z
  21.                                     from the RCR value (N).
    9 u: [- I. y+ _/ S' w3 C4 }
  22.                                     This means in PWM mode that (N+1) corresponds to:! k5 p1 l" B; ]4 M! ]" |
  23.                                         - the number of PWM periods in edge-aligned mode0 A( i0 h) Z' p# e! p
  24.                                         - the number of half PWM period in center-aligned mode( S! W8 U* e% S2 J
  25.                                      GP timers: this parameter must be a number between Min_Data = 0x00 and% b9 H5 D2 c- A/ [
  26.                                      Max_Data = 0xFF.
    3 o1 q7 Q+ a. [! @0 s8 X
  27.                                      Advanced timers: this parameter must be a number between Min_Data = 0x0000 and
    1 V7 J5 v9 l7 s! F9 ~
  28.                                      Max_Data = 0xFFFF. */0 U; w/ V( D7 N) ?% a
  29. 6 }: n2 N0 _1 u6 |9 Y" L
  30.   uint32_t AutoReloadPreload;  /*!< Specifies the auto-reload preload.2 T) g: F) E9 Y- I' ?& p- g
  31.                                    This parameter can be a value of @ref TIM_AutoReloadPreload */
    / I1 ~2 |0 w6 O+ I9 O" d
  32. } TIM_Base_InitTypeDef;
复制代码
  1. /**- P1 C. H6 h6 f# c7 |
  2.   * @brief  TIM One Pulse Mode Configuration Structure definition, n6 Q" V; J$ R2 @
  3.   */; L, z+ W5 J- z3 b9 s* A
  4. typedef struct; P3 u) M9 g! S0 ?! ?  D
  5. {
    9 g, @) K/ v; b0 y/ u4 {
  6.   uint32_t OCMode;        /*!< Specifies the TIM mode.0 l/ m$ T/ A: ]9 ?9 Y" j& y5 B
  7.                                This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes */
    - l( {! x1 {0 i3 r) i! W6 {8 j
  8.   g2 d% |: H% Y7 p5 [) a
  9.   uint32_t Pulse;         /*!< Specifies the pulse value to be loaded into the Capture Compare Register.
    , l: O. c+ J: f5 V  O9 i
  10.                                This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
    9 K* z  d3 n5 q+ @7 G: R

  11. ' Z( U! b$ i2 V: s. [/ S
  12.   uint32_t OCPolarity;    /*!< Specifies the output polarity.
    ) ~9 I- k' f+ X
  13.                                This parameter can be a value of @ref TIM_Output_Compare_Polarity */8 P% k( }6 P' M

  14. * W8 N' C9 F4 g" t) W- Y
  15.   uint32_t OCNPolarity;   /*!< Specifies the complementary output polarity.- @# l3 C. N% N. P$ B  d
  16.                                This parameter can be a value of @ref TIM_Output_Compare_N_Polarity% Q' V; _6 d5 `8 _. c
  17.                                @note This parameter is valid only for timer instances supporting break feature. */$ S. x3 g  x8 P
  18. ; s9 {. B/ p3 E8 H) g+ j
  19.   uint32_t OCIdleState;   /*!< Specifies the TIM Output Compare pin state during Idle state.
    : T- A* G1 ?0 @* q
  20.                                This parameter can be a value of @ref TIM_Output_Compare_Idle_State
    8 b% [7 k& O, W+ z9 s: r8 |
  21.                                @note This parameter is valid only for timer instances supporting break feature. */
    , i1 J  N3 U; f& ^

  22. 6 ?/ f+ N6 X3 `& }/ A
  23.   uint32_t OCNIdleState;  /*!< Specifies the TIM Output Compare pin state during Idle state.
    ( P% |" p3 P- L  T
  24.                                This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State* e+ W1 H& H# ?4 k, l& L
  25.                                @note This parameter is valid only for timer instances supporting break feature. */2 B( A) y5 a, `7 a8 k

  26. / n1 T) J4 x+ B3 Y8 q6 a- {; s1 o
  27.   uint32_t ICPolarity;    /*!< Specifies the active edge of the input signal./ o/ X3 F9 }' P0 t7 f, d
  28.                                This parameter can be a value of @ref TIM_Input_Capture_Polarity */9 D2 {' X, d* J2 q4 n/ W8 n' r7 [
  29. 8 X$ Q% r& n+ d- v
  30.   uint32_t ICSelection;   /*!< Specifies the input.
    ' ]; X/ X* k& Z# q  i" w
  31.                               This parameter can be a value of @ref TIM_Input_Capture_Selection */
    3 F# b9 d2 Y, f& P5 i
  32. 9 }( m6 M  w8 T! t. [5 E
  33.   uint32_t ICFilter;      /*!< Specifies the input capture filter.; m0 {+ Z# ~9 ~
  34.                               This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */
    9 I- k7 |6 x/ i5 J; c5 b( U2 L
  35. } TIM_OnePulse_InitTypeDef;
复制代码
6 a) y# d- G  _1 n1 Y

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

/ L! M1 a$ U# s/ R. ]7 l

微信图片_20230402183046.png


, K, Q( q0 k7 w3 f7 L! M

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

; z( g$ m- u5 y: o

微信图片_20230402183042.png

1 b9 a/ K1 X# B4 o

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

利用到的是TIM4,通道4


7 B3 D% l7 @, I) Q

微信图片_20230402183039.png

+ E, j9 m' I- W

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

  Y. k- P4 d, w7 E4 a2 ]; ~( {

微信图片_20230402183036.png


! i) `6 n5 U: ^* H, a( p

代码编写

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

  1. typedef struct{
    ' Q" y8 P: b+ E- c
  2. uint16_t LedpwmVal;//占空比调整参数
    : ^) V- L: ?2 g# {8 V& U2 [: a
  3. uint8_t LedpwmVal_Dir:1;//调整方向,1-递增,0递减
    ! m; P% b: V1 `- s6 r
  4. }peripheral;
复制代码

- e. v$ H, B! i- ?

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

; W8 y- o7 q9 q

. X; H( }; y2 d6 _% {- t
微信图片_20230402183033.png
1 J" @1 q' P# I2 v- T
! ]* ], O2 Q' v8 F8 [6 A
  1. while (1): |1 k1 D' k7 x8 D+ z
  2.   {
    # Y  H9 v3 _& v$ R$ k
  3.     /* USER CODE END WHILE */
    8 q$ C0 k5 M/ t) Q
  4. 7 A& C4 e1 c) m  I* U* n
  5.     /* USER CODE BEGIN 3 */! e5 N& E& n- {2 n" M9 C% z/ \  `
  6.   HAL_Delay(10);//加延时,否则变化不明显,看不到效果% _- a& {& D) I5 s
  7.   
    ) L1 a: J$ [0 t5 p$ H4 ~( K! A5 E
  8.   if(PWM.LedpwmVal_Dir)
    5 w& d8 Q/ M% `6 m
  9.    PWM.LedpwmVal++;
    " `. v/ {1 @( ~  D
  10.   else% ]7 @& w$ V- @# j- N( e. l
  11.    PWM.LedpwmVal--;
    % N. }1 `1 H0 J, s# @) h7 }7 t  U
  12.   
    - m; Q/ F! ^9 \: G7 k, @
  13.   if(PWM.LedpwmVal>breath_UP)
    7 N1 j+ h  l* P$ \
  14.    PWM.LedpwmVal_Dir=Breath_DOWN;//切换为PWM值递减状态7 p9 R1 g5 J( Y" r# u
  15.   
    6 \8 Z# H$ x* o4 i
  16.   if(PWM.LedpwmVal==Breath_DOWN); {9 p( R* h9 T! q! Q
  17.    PWM.LedpwmVal_Dir=1;//切换为PWM值递增状态  C6 W; }5 c. p9 F' c0 B
  18.   
    $ r# K- k+ J/ ~& I
  19. // TIM4->CCR4 = PWM.LedpwmVal;
    & K* [/ K: `6 p3 z' y) @5 X
  20.   __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_4,PWM.LedpwmVal);//设置占空比参数
    . }+ p9 g: T3 p
  21.   }
复制代码
( n% ^' Z  T2 P
; w$ F4 o* }# S9 l% b

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

/ ?/ O: e" J  j0 @

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

递增效果:

6 T' z! i0 G) \4 S5 E/ X; }

微信图片_20230402183030.png

$ J% T- d9 u1 b# n+ M- E

递减效果:

" Q2 |; X8 U& p0 M

微信图片_20230402183026.png
0 Y+ E* B/ M6 o$ D9 E. J7 \转载自:Embedded小飞哥$ J* d* u$ Q* o% B
如有侵权请联系删除" z% |1 s! t" t. r) k
6 R: w0 b" M1 P4 F9 @7 R% r

% f, r/ R2 m5 z; D" k8 ?
收藏 评论0 发布时间:2023-4-2 18:31

举报

0个回答

所属标签

相似技术帖

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