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

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

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

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


% D; S' @% P( v# |: A

PWM原理介绍

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

& b$ k' ~3 ?& f8 p+ K8 y1 [6 _
1 O+ `) ~# e0 s; E( j. `
微信图片_20230402183103.png

- R. |0 Z) d6 t
PWM 原理示意图
- C8 h1 a7 L1 k3 j
5 ?! u  I6 u' v1 x: M/ f* {

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

8 H: L1 }5 t) ]% C2 I5 {# h

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

2 \; ~2 h. J( a

微信图片_20230402183100.png

* c# F4 ~+ l7 {1 ^  }! e# _7 l5 K1 g

cubemx配置PWM

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

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

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

0 i" q5 G# v9 Q8 e' S4 h. B

微信图片_20230402183056.png

$ |& P2 ^- A7 k/ X

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

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


$ A8 d( v# W7 t6 Z4 D! s8 }

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

fclk = 72000000/500/72=2KHZ

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

% c, n1 ~  ~; @: R6 d5 _

微信图片_20230402183052.png


0 T0 O- a2 K7 \/ V

PB9是被复用的


: n% v" C* i5 f

微信图片_20230402183049.png

9 i6 V/ T# p; z$ u

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


( r! U6 B- S0 r8 r! o9 |- T+ U9 N

PWM代码解析

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

  1. void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)& M* f7 [6 r) }2 t% U
  2. {5 q% ^# ]' X5 ]4 u$ ?
  3. 6 E  J) d- D; w  A& k
  4.   GPIO_InitTypeDef GPIO_InitStruct = {0};9 ~8 Y) z' A& h
  5.   if(timHandle->Instance==TIM4), X# J8 m8 Z; A" H$ Q
  6.   {% S- f1 |" ?7 N2 R& d# e$ m% z
  7.   /* USER CODE BEGIN TIM4_MspPostInit 0 */
    % g# U& e1 E- p7 s# J8 W6 z; t. \
  8.   ]6 h3 I9 V3 ~5 A
  9.   /* USER CODE END TIM4_MspPostInit 0 */
    # O" t6 l3 ~/ h, [1 y% ^# j

  10. 4 A, m# n- T) d$ i
  11.     __HAL_RCC_GPIOB_CLK_ENABLE();3 K, g- N* b* G+ w& S, ^2 A
  12.     /**TIM4 GPIO Configuration
    0 t- f9 ^1 T& G& O; K6 h- g
  13.     PB9     ------> TIM4_CH4
    ( X6 T% h& A& ]0 o! w( g2 j
  14.     */
    7 q3 k! \  o2 r* I
  15.     GPIO_InitStruct.Pin = GPIO_PIN_9;3 ?9 s/ u( z: }7 S& _' b& e" ?% Z
  16.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;7 L, [8 u8 z* t7 X, c
  17.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;& I! ?) s1 z8 G: ~' }8 F6 ]2 n& C
  18.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);: U/ }& E# [. p* F/ }
  19. , t! X: P2 ~$ |
  20.   /* USER CODE BEGIN TIM4_MspPostInit 1 */
    / ~2 H5 _. n# g: t$ F
  21. ) |+ V( l$ I+ b: P' L5 Y: T/ e3 A+ d
  22.   /* USER CODE END TIM4_MspPostInit 1 */( y% q" H* G# f; {) x+ z" l2 S
  23.   }$ _( f" L* g0 w; e! L& w
  24. 5 G! c7 N( x8 O6 v7 B( U# K
  25. }
复制代码

$ o. [) i/ m& l" g

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

Pulse = .pulse/period

9 K  B# E% m3 E0 O. q5 b4 W

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

8 f9 i6 B) w' Q

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

  1. /**
    0 R& x, \( w" V8 S4 i) M
  2.   * @brief  TIM Time base Configuration Structure definition) ]) @! f' `1 `
  3.   */
    1 a; t' {1 d1 w; e* i
  4. typedef struct
    9 ]4 C, R$ y$ O/ n
  5. {* t  p$ ^' R# `) F2 d; [3 n2 b" I) Z
  6.   uint32_t Prescaler;         /*!< Specifies the prescaler value used to divide the TIM clock.# u4 ~6 @, e% p5 F) ?, w9 ]
  7.                                    This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
    4 n; j* E, X+ z8 [; o) @

  8. * W& U# V8 u3 W% R1 d
  9.   uint32_t CounterMode;       /*!< Specifies the counter mode.
    , M% z' w' |2 [" o6 Q: N
  10.                                    This parameter can be a value of @ref TIM_Counter_Mode */+ I; b/ j7 F  W: s) [- E3 A' e
  11. - L  Q# V2 P1 e- h
  12.   uint32_t Period;            /*!< Specifies the period value to be loaded into the active
    ! R6 F' t  U" V  _! w
  13.                                    Auto-Reload Register at the next update event.
    ' p% r- |% h" R
  14.                                    This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF.  */4 w6 f; _, ~9 p  m

  15. . P! V& g; ?" l3 p) ^4 k) Z8 J
  16.   uint32_t ClockDivision;     /*!< Specifies the clock division.
    " I, \9 U. ~* ~: i, u( i; Z6 X
  17.                                    This parameter can be a value of @ref TIM_ClockDivision */( r6 P  S4 ~; Q8 W4 l

  18. " _8 Z- O+ J2 ]5 d
  19.   uint32_t RepetitionCounter;  /*!< Specifies the repetition counter value. Each time the RCR downcounter
    ! W- I+ A+ S& s% m- R! J& @
  20.                                     reaches zero, an update event is generated and counting restarts
      ]) L% q; U# Z
  21.                                     from the RCR value (N).
    0 P# |" @6 [5 K
  22.                                     This means in PWM mode that (N+1) corresponds to:8 S3 Z6 P8 Z' u4 a! i( n
  23.                                         - the number of PWM periods in edge-aligned mode
    5 Z! w" m* m/ K3 j0 W  r8 k
  24.                                         - the number of half PWM period in center-aligned mode
    7 t% d, @$ N+ J9 Q4 Q3 e( j
  25.                                      GP timers: this parameter must be a number between Min_Data = 0x00 and" Z7 ?' J" `6 ?# i3 i5 E; D5 J) {
  26.                                      Max_Data = 0xFF.
    9 b& p6 k' m! B/ i9 y
  27.                                      Advanced timers: this parameter must be a number between Min_Data = 0x0000 and
    8 @0 W! h% ^8 y" q5 m; k. I% b
  28.                                      Max_Data = 0xFFFF. */
    . L0 W8 L5 l3 [& p8 J; K
  29. , T2 o1 s6 r! R1 S% e! a
  30.   uint32_t AutoReloadPreload;  /*!< Specifies the auto-reload preload.& K! I: o5 H, h" v# Y$ S
  31.                                    This parameter can be a value of @ref TIM_AutoReloadPreload */
    - t' _0 [9 z$ F. L% Z6 }, f
  32. } TIM_Base_InitTypeDef;
复制代码
  1. /**
    ' S1 m" \8 w6 I3 s) H
  2.   * @brief  TIM One Pulse Mode Configuration Structure definition: d& l' Y4 d% F
  3.   */
    % S5 J5 J/ {# k/ N. V1 q) T
  4. typedef struct2 o* i" G" R) X  E$ g- w+ L3 P
  5. {
    " P- r' Q6 D" k. c' Y0 s
  6.   uint32_t OCMode;        /*!< Specifies the TIM mode.6 i& X: ?4 i/ q) u
  7.                                This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes */; C. V% X5 L; @
  8. 3 Y. B$ s" ^0 B! d) t$ k
  9.   uint32_t Pulse;         /*!< Specifies the pulse value to be loaded into the Capture Compare Register.  ?* c6 t- O2 }! K
  10.                                This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */5 P- f8 ]8 r+ L+ D

  11. / k! L0 l# K" h) S0 g& G( n
  12.   uint32_t OCPolarity;    /*!< Specifies the output polarity.
    " ~, e4 a/ m9 {6 l# y& Z8 I
  13.                                This parameter can be a value of @ref TIM_Output_Compare_Polarity */
      @! M. i- L7 `5 G# z

  14. 2 |0 L. P: {; b2 w7 |+ t; ?
  15.   uint32_t OCNPolarity;   /*!< Specifies the complementary output polarity.7 ?  G; `8 B- d
  16.                                This parameter can be a value of @ref TIM_Output_Compare_N_Polarity5 `# i- J4 W5 G% m# Z1 Q
  17.                                @note This parameter is valid only for timer instances supporting break feature. */
      y0 d; p) T, |" M; ?7 S# L. h7 n
  18. # ^. S: R0 e2 Q  a6 `+ q
  19.   uint32_t OCIdleState;   /*!< Specifies the TIM Output Compare pin state during Idle state.- B. m) o2 f3 j# Y  R) l
  20.                                This parameter can be a value of @ref TIM_Output_Compare_Idle_State
    * T" p! i* @. j. o) q
  21.                                @note This parameter is valid only for timer instances supporting break feature. */
    / g( z/ |" ]1 N: S- K, Z/ A0 f/ g
  22. 7 U! L( Y% @* |
  23.   uint32_t OCNIdleState;  /*!< Specifies the TIM Output Compare pin state during Idle state.
    4 J4 I; ?; D- C7 o8 _$ f
  24.                                This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State; `- j7 f' w* c
  25.                                @note This parameter is valid only for timer instances supporting break feature. */) J* ~2 ^9 [: S* _6 E6 k; k  J

  26. ' D  O! B0 E6 |3 V* T2 Z
  27.   uint32_t ICPolarity;    /*!< Specifies the active edge of the input signal.
    5 b5 a! o% T9 W$ ?
  28.                                This parameter can be a value of @ref TIM_Input_Capture_Polarity */2 r0 _) P, K. p! |- k( v

  29. 0 {- @8 T4 K# a! S9 X8 I* L
  30.   uint32_t ICSelection;   /*!< Specifies the input.- C/ f* f  M4 m1 V: x, {8 c! ]+ [
  31.                               This parameter can be a value of @ref TIM_Input_Capture_Selection */" T6 M6 t/ a8 s+ ]8 `' D: D2 w$ V; B
  32. 5 b" m9 i- x6 ~1 m
  33.   uint32_t ICFilter;      /*!< Specifies the input capture filter.4 G  l* W+ V2 G  q
  34.                               This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */
    $ N" L. E; S5 d
  35. } TIM_OnePulse_InitTypeDef;
复制代码

. |- I6 z; Z* B/ p3 N" v

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


# O$ l* J- N7 V8 a' L: X  N

微信图片_20230402183046.png


7 K+ p  [; V) m( r

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

+ u. A- _9 I  e2 W  T1 X

微信图片_20230402183042.png

5 e/ v8 p; F+ h3 N

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

利用到的是TIM4,通道4

5 _7 K5 w% s# v- L

微信图片_20230402183039.png


0 d: Q5 O- ^; J2 o3 f! m& h

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

/ a$ C& U$ O  O7 U6 Q& j: u! s+ U

微信图片_20230402183036.png


: a' g, n/ w/ i

代码编写

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

  1. typedef struct{
    " d0 d" v: D1 m! q! `9 ?9 i$ O
  2. uint16_t LedpwmVal;//占空比调整参数( J! Y7 f$ Y; q  }/ q
  3. uint8_t LedpwmVal_Dir:1;//调整方向,1-递增,0递减+ C: n. G& X( G7 Q0 s
  4. }peripheral;
复制代码


% j: R! d& e4 I$ c5 r

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


2 G; J( z1 U- a  e1 \5 s# S
3 I+ a' q. a# u
微信图片_20230402183033.png

0 C! F8 y' N- Q, f9 v0 y! ^! J5 g5 d5 d1 D
  1. while (1)' y. l; A, ]4 i" k+ Z: ]' b
  2.   {% n* H1 p5 j+ R  L$ ?
  3.     /* USER CODE END WHILE */
    * |5 O( t, T3 e3 [- X

  4. 4 \* N! p. C; x5 X
  5.     /* USER CODE BEGIN 3 */
    : _7 x, u  J+ o/ E0 `1 d
  6.   HAL_Delay(10);//加延时,否则变化不明显,看不到效果8 B# z2 _" U# a
  7.   & d7 a8 F5 H; J* N: M( }& W
  8.   if(PWM.LedpwmVal_Dir)4 z( a2 n  s$ @. g
  9.    PWM.LedpwmVal++;( Y. H& h: R( C
  10.   else
    , q/ M2 Q- Q4 D. Q' d
  11.    PWM.LedpwmVal--;9 n4 [, D  O; x; u" P, ]6 F
  12.   9 z( a4 l% I' t( r+ ^' w* n6 M/ s
  13.   if(PWM.LedpwmVal>breath_UP)$ b3 x; \+ N4 i* M& z
  14.    PWM.LedpwmVal_Dir=Breath_DOWN;//切换为PWM值递减状态8 k0 j# o" S& Q
  15.   
    1 W- L' K9 ?5 j! X9 b3 l
  16.   if(PWM.LedpwmVal==Breath_DOWN)
    8 R- S+ x* E) `& C
  17.    PWM.LedpwmVal_Dir=1;//切换为PWM值递增状态( \* N- u# ^1 u$ K$ X/ \
  18.   * f; H. `) O  K6 H- f, \1 |
  19. // TIM4->CCR4 = PWM.LedpwmVal;
    8 x! \9 x6 N# W& M3 A/ y
  20.   __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_4,PWM.LedpwmVal);//设置占空比参数
    5 Y# Q1 X: Q2 d$ c6 ~3 z
  21.   }
复制代码
9 x! e) Q' a  ?
9 D0 ^: Y" c/ P% e+ M4 q" M

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


! u  R, |6 p9 }/ E

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

递增效果:

1 A4 ^2 G9 g# L3 q' T

微信图片_20230402183030.png


  Y7 F1 C0 M# e- b

递减效果:

( j' [5 ]9 X2 z- a3 e% R* d

微信图片_20230402183026.png
& I# I8 [2 P' t2 W转载自:Embedded小飞哥
( v% ~  X. g' {) T2 u% E如有侵权请联系删除' K8 k5 ]* {+ y( \8 S" W

% F  ?, L0 k0 ]# t  }# o% s  K7 J; t
收藏 评论0 发布时间:2023-4-2 18:31

举报

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