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

【经验分享】STM32单片机一个定时器输出不同频率PWM波

[复制链接]
STMCU小助手 发布时间:2022-3-18 22:13
  在使用STM32单片机输出PWM波形的时候,通常可以直接使用定时器提供的PWM模式。可以通过自动重装载寄存器(TIMx_ARR)来设置定时器的输出频率,然后通过捕获/ 比较寄存器 1(TIMx_CCRx)来设置占空比。一个定时器只有一个自动重装载寄存器(TIMx_ARR),但是有4个通道的捕获/ 比较寄存器 1(TIMx_CCR1、TIMx_CCR2、TIMx_CCR3、TIMx_CCR4)。所以使用一个定时器输出PWM波形的时候,频率是统一调整的,4个通道的频率是相同的,但是占空比每个通道可以独立设置。比较寄存器TIMx_CCR1、TIMx_CCR2、TIMx_CCR3、TIMx_CCR4分别设置4个通道的占空比。3 ?" q  `& T) }# L. Q/ b. @

) S, s1 Q( \6 Z& H/ ~0 Q  如果使用中央对齐模式计数的话,那么定时器的计数波形如下:& Y$ s% k3 r. J: v
`(OQAIT)Z0`{LP[(Y]~$K9P.png
! t$ c! _" Z" ]; @6 c* `4 r5 y- R
, X; m7 v$ k2 S  三角波就是计数器计数的方式,从0开始逐渐增大到装载值ARR,然后在减小到0。蓝色的波形就是比较寄存器的值CCR,计数器在计数的时候,每次都会将计数值和比较寄存器的值CCR进行比较,当计数器值小于CCR的值时,输出低电平。当计数器的值大于CCR的值时,输出高电平。
/ ?  N7 Z+ U9 P1 L7 V: P3 H4 E% Q0 y/ }+ z" r: f( m: ]  Q
K$Q`{652@TWPP)9KKT@LD`P.png , Y7 h9 a( N& L& o# T4 P: j

) j$ c/ a' }: F8 `0 V8 b  当需要改变占空比的时候,只需要改变比较寄存器CCR的值就行了。6 C) O. V' S( J% h+ {; j7 [

2 H+ g! R7 B4 D& |$ l! D KM13}J___0`@7A@AJ@XG5WB.png , j: a; p+ \( I! L$ ~7 t. C* [5 J# `

( z' Z: o8 |" D; J- n  通过改变ARR的值设置输出PWM波形的频率,通过改变CCR的值改变PWM波的占空比。但是用这种方式输出PWM波时,一个定时器的4个通道输出的PWM波频率都是一样的,那么能不能用一个定时器输出4路不同频率的PWM波呢?' L4 i. Z* z; ?: l4 @, B+ N
  通过观察捕获/ 比较模式寄存器 1(TIMx_CCMR1)中的OC1M位时可以发现,除了PWM模式外还有一个翻转模式,在翻转模式下,当计数器CNT的值等于比较寄存器CCR的值时,输出的电平就会翻转。
8 j9 S7 {" d9 Y6 K  ~. j/ @8 d1 B1 u& m$ {# }2 V
QKXSGN@UT28(A4NM`B{G60F.png 3 q9 V0 W' R8 q" c$ A
( K, d+ h. r  q3 f
1 e% R. [  d" f% X2 j. h; M, j9 ~
  那么就可以通过这个功能,在输出PWM波的过程中不停的去改变CCR的值,那么输出的电平就会不停的翻转,这样就可以手动改变PWM波的频率了。
6 E, J9 Q6 A1 w- T2 f5 g. Q
, w! z7 N2 Y5 T8 c [O%Q`[U9HY[NBV]D8(I5SWE.png $ i# O5 z) }' P$ o5 r( ?

( M" }  `3 o4 A) l; K  x1 U5 v& U5 [  比如第一次将CCR的值设置为CCR1,然后使能比较中断,当计数器的值CNT等于CCR1时,就会触发中断,然后在中断中将CCR的值修改为CCR2,。接下来当计数器的值等于CCR2时,又会触发中断,在中断中继续设置下一次CCR的值。这样每触发一次中断后,输出的PWM波形电平就会翻转一次。在计数器从0到ARR计数期间,PWM的波形可以翻转好多次。在PWM模式中,每一个计数周期波形只能翻转一次,但是在翻转模式下,一个计数周期可以翻转好多次,这样就可以改变输出PWM波形的频率了,同样每次设置不同的CCR值时,也可以改变占空比。
4 F( r9 W6 k# s0 I- g- d% r- k
) `; y  v, c) u# S2 D  下面就通过代码来实现。
) R; \% R# x/ g% F! d; Y- \! ]' U5 n: a* j0 L% {
  1. #include "timer2.h"
    ! C  Q) p0 g1 X, o" d
  2. //比较值
    & N" e2 ?  @" K9 M: _" y- o
  3. u16 CCR1_Val = 32768;0 k! e3 x- C* Z/ D  L* h
  4. u16 CCR2_Val = 16384;
    / p7 L  U! H+ v# y5 A
  5. u16 CCR3_Val = 8192;
    2 U- z  L" {3 j/ ^
  6. u16 CCR4_Val = 4096;
    5 B1 V8 p6 P) R3 d; h
  7. $ p9 ]8 v) ]; N2 T, q- i- t
  8. void TIM2_PWM_Init( u16 arr, u16 psc )$ C9 F0 d6 C- }5 W
  9. {
    + Z+ A5 h. ]- X
  10. 4 j- O/ O! ]' O7 ?% Z  @
  11.     TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;+ P, U( m6 n( Y0 l
  12.     NVIC_InitTypeDef NVIC_InitStructure;
    ) z' q5 z: b: a4 W6 g: e) {
  13.     TIM_OCInitTypeDef TIM_OCInitStructure;
    , T- x1 h) s+ F* x" p% I
  14.     GPIO_InitTypeDef GPIO_InitStructure;7 {8 R0 m7 c: D$ ^& @2 J
  15. , {* k! K2 M4 y- `0 ]) Z" Q& I
  16.         //使能时钟7 o1 }% b/ `- g# f. V; I# q4 T4 t
  17.     RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE );
    ' ~3 k0 y# P; O
  18.     RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
    4 \2 T( H+ B- c9 q: t
  19. ; X! |) w! R" Z3 y& g
  20.     //设置IO口# s4 b' I! ]7 j
  21.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
    ; _# D6 i: i8 P8 P
  22.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    - X; a2 M$ i+ w3 \6 Y' N3 k
  23.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;4 T# j3 L4 U3 _$ x$ @* y7 K3 ]7 q
  24.     GPIO_Init( GPIOA, &GPIO_InitStructure );/ x! P1 x  y: I0 c' Y/ E! Q% O
  25.   b4 b: X0 ^; a
  26.     //设置中断优先级% f) L+ v; L. P
  27.     NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;9 z9 k9 S& e6 A4 k$ V( j* R; L7 d. x. c8 [
  28.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;( i4 _% u' ]" t7 E  m/ y4 ^
  29.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;' t. {. ^/ Z" U0 J9 N1 |
  30.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;" V# `8 n5 d* w  M6 l+ o: N
  31.     NVIC_Init( &NVIC_InitStructure );3 A6 X# A; ^- O& L5 ?, j
  32. ! \3 N0 d( l' h' v2 s! i
  33.     //设置定时器基本参数
    $ y8 t9 @& M) ^, V9 J
  34.     TIM_TimeBaseInitStructure.TIM_Period = arr;
    5 `1 _% i2 ~- F% ]6 W) \: M
  35.     TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
    9 `5 Q" N0 `9 V* l. x* q
  36.     TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;                                //只有高级定时器需要设置,其他定时器可以不设置。
    4 n# j/ U5 E$ |  u
  37.     TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;0 f6 f( d* G" `( C# A$ M& O
  38.     TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    - _1 c  S: A( j5 X, I+ p5 f
  39.     TIM_TimeBaseInit( TIM2, &TIM_TimeBaseInitStructure );
    & ?) p% r2 W7 o; X8 A, U: E& q

  40. ' {2 V- p9 j6 Q/ f* c7 E, a
  41.     //设置工作模式
    : A) T# [! I  p4 p! W% n* H
  42.     TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;                                        //设置定时器工作在翻转模式1 b7 x$ F. S7 c3 N) Q3 W
  43.     TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;' _$ D" J* E# b+ r9 l- J: E' q" J( K
  44.     TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    " z5 S" o7 M  E' v2 L
  45. * i4 x. E& d# M; i
  46.     //设置4个通道 比较寄存器值
    + M, l* P% n$ ]( S( a  {6 R& a9 f& ^- i
  47.     TIM_OCInitStructure.TIM_Pulse = CCR1_Val;* R9 s! p1 y! U2 f( {
  48.     TIM_OC1Init( TIM2, &TIM_OCInitStructure );                                                        //TIM2_OC1   PA0
    % ]+ ~# w$ N+ K' |2 N" Z' C/ H

  49. 3 A2 L6 V; n+ S6 f
  50.     TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
    7 j# u- h& r  R( Y/ j* q
  51.     TIM_OC2Init( TIM2, &TIM_OCInitStructure );                                                        //TIM2_OC2   PA1  X( W) B, K* j, F/ Z

  52. # |8 w5 C  L5 M$ x/ m+ j0 f
  53.     TIM_OCInitStructure.TIM_Pulse = CCR3_Val;
    % `0 F' @. `7 C  z3 P! C' [
  54.     TIM_OC3Init( TIM2, &TIM_OCInitStructure );                                                        //TIM2_OC1   PA2  D2 i9 d7 j' i+ S$ [- E

  55. 2 w3 Z2 I8 X. w) `" T! M
  56.     TIM_OCInitStructure.TIM_Pulse = CCR4_Val;
    9 |. M4 F# `' A8 [- m8 O9 `" W
  57.     TIM_OC4Init( TIM2, &TIM_OCInitStructure );                                                        //TIM2_OC1   PA3$ K& f1 f9 O+ |% t. J4 ]7 K
  58. , F/ f# V% b) D2 S* d
  59.     //禁止自动预装载* M/ ~& q' e& w  H3 V1 P
  60.     TIM_OC1PreloadConfig( TIM2, TIM_OCPreload_Disable );
    ; [0 J9 }2 n6 {" v
  61.     TIM_OC2PreloadConfig( TIM2, TIM_OCPreload_Disable );
    9 f; N/ r0 [8 p1 X
  62.     TIM_OC3PreloadConfig( TIM2, TIM_OCPreload_Disable );  M. m, [  X. S
  63.     TIM_OC4PreloadConfig( TIM2, TIM_OCPreload_Disable );
    - Y9 H, u+ I6 V, f4 c1 E
  64. & w# w) c* O; f& ~& ]
  65.     //使能定时器$ K/ ?. H) ^" p( \# r
  66.     TIM_Cmd( TIM2, ENABLE );
    ( K7 g6 y/ q3 r' y- L
  67.     TIM_ITConfig( TIM2, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE );( ~9 ?# r, [3 V! y, F" z
  68. }
复制代码
6 F4 {1 a5 h( U- {% ?
  首先初始化定时器,初始化的两个参数arr和psc分别设置定时器的自动重装载值和分频系数。这里将arr的自动装载值设置为最大0xFFFF,也就是65535,相当于计数器从0开始计数,直到65535才结束。将分频系数psc设置为0,也就是不分频,定时器按照72MHz的频率工作。
. v" \/ G7 W, Y# ~+ B* c; e+ |% k6 m/ f( N; Y* S  v
  将定时器的工作模式设置为翻转模式,然后依次给4个通道设置比较值。这里要将自动重装载功能关闭,否则就不能手动的更改比较值了。最后使能定时器和4个通道的比较中断。2 L4 x. C0 o. G: q

7 W( ^' Z/ e6 `0 P# O1 r  接下来就是最重要的环节了,在中断中改变每个通道的频率和占空比。
  p! L; R" W8 v: e, h
( \9 u' I$ Q5 o, \- Y
  1. //占空比 0 --- 100
    ' S. D$ r5 H8 g# n, H% L
  2. u16 CCR1_dc = 20;
    ' w5 g8 d$ V& B5 Q5 y. O
  3. u16 CCR2_dc = 40;* s, Y* v+ d/ I4 B- ~* b
  4. u16 CCR3_dc = 60;
    5 W* e8 l& y, g) `
  5. u16 CCR4_dc = 80;
    , L  i. ]; e2 h; L$ Q, c: g3 ]

  6. + Y  H' t& u6 E6 j" ?
  7. u32 capture = 0;
    " d; q! s+ f7 T
  8. u8 flag1 = 0, flag2 = 0, flag3 = 0, flag4 = 0;. a" {: Q2 ?: t8 I& b3 ]% p, S
  9. u16 setcap = 0;" R% p5 r# B$ x& Q
  10.   l; h6 }4 l8 d8 w3 V: z( k
  11. void TIM2_IRQHandler( void )
    : l  Z: [; _% k$ h/ ?0 ~9 p; d
  12. {& A8 c5 E- r  t4 T7 L& ]( [
  13.     if( TIM_GetITStatus( TIM2, TIM_IT_CC1 ) != RESET )
    0 O4 u  O. ?" A. m0 e- N( J
  14.     {  D( N2 J1 d* h  O  y( B
  15.         TIM_ClearITPendingBit( TIM2, TIM_IT_CC1 );
    8 P! r+ r' t/ P4 v
  16.         capture = TIM_GetCapture1( TIM2 );2 d" @5 Y- b4 {3 K2 P" Q' [, J
  17.         //设置占空比6 [% W9 C8 P1 w7 f% J, k: h
  18.         if( flag1 == 0 )
    ' K) m& U) Z/ f0 W
  19.         {7 l8 C7 G. q1 h/ T' A* ^
  20.             flag1 = 1;
    ) \- J- Z: F# O- Y7 f1 z. X
  21.             setcap = capture + ( u32 )CCR1_Val * CCR1_dc / 100;
    . ^5 R6 g$ A6 H: w% k) {
  22.         }
    - W% {# \7 F. P/ O& Y6 `6 N' q. v$ k" Z
  23.         else' E* a) o- Z, |3 }; ?3 b
  24.         {
      B; y+ R1 D' @* `7 ]5 w
  25.             flag1 = 0;
    2 y( s0 Y' i1 y8 f2 U0 Q
  26.             setcap = capture + ( u32 )CCR1_Val  * ( 100 - CCR1_dc ) / 100;# I3 u8 \! G0 z5 Y% n$ J* f# k
  27.         }$ k4 g0 u3 Q- h' a' G7 W
  28.         TIM_SetCompare1( TIM2, setcap  );
    * a4 H" O, H+ y7 E& g
  29.     }
    + P& l1 w) S1 C" P

  30. , F6 Y( x( F( X- x  w
  31.     if( TIM_GetITStatus( TIM2, TIM_IT_CC2 ) != RESET )
    + }) O4 ]" f6 K0 x$ T: j  |: e
  32.     {( u1 o  }7 F3 t& n- m4 X6 @: C) B
  33.         TIM_ClearITPendingBit( TIM2, TIM_IT_CC2 );" O7 s) k8 y7 n! m
  34.         capture = TIM_GetCapture2( TIM2 );. P% `. U* q, L" Y
  35.         if( flag2 == 0 )+ h& k4 z$ Z% W+ _1 P3 Y7 H
  36.         {
    9 L" l) c; O* d9 u
  37.             flag2 = 1;
    % t6 e( ^5 v& O& [  X6 O% m8 @
  38.             setcap = capture + CCR2_Val * CCR2_dc / 100;
    , |& t; j: Z) ]! [9 D
  39.         }4 B. O6 i( Q: q- D4 W! y
  40.         else0 [9 R+ k2 N) y1 v! z
  41.         {
    0 ~/ C) V) |/ A) V# ]2 M
  42.             flag2 = 0;/ @/ p1 N" D' G, ]
  43.             setcap = capture + CCR2_Val  * ( 100 - CCR2_dc ) / 100;
    1 a* @  @" J. B1 r
  44.         }. ^; C( \' ?4 P' ^: ?
  45.         TIM_SetCompare2( TIM2, setcap );# G/ t; V& k) d) F+ ?  _
  46.     }
    . z: ^& W" D) ]  o2 J) B8 p" t
  47. 6 J& t2 v. Z6 U( a0 |% G

  48. ' f( A$ j, Z! h- V
  49.     if( TIM_GetITStatus( TIM2, TIM_IT_CC3 ) != RESET ); h8 F, w$ o" B: ~$ H; p
  50.     {
    $ @' m8 ]6 e. X7 ^' y
  51.         TIM_ClearITPendingBit( TIM2, TIM_IT_CC3 );  K' R7 `4 J2 q# s6 n
  52.         capture = TIM_GetCapture3( TIM2 );# h8 j8 A6 b: C0 @* z9 h. x9 F" ^+ }/ u7 h
  53. ' P* ^2 h! _# ?) m
  54.         if( flag3 == 0 ). V& K  |, L3 a% Q1 c0 n7 n
  55.         {
    " N* ~9 [1 \( p, K- ^
  56.             flag3 = 1;
    , C) ?& k# j! `% o
  57.             setcap = capture + CCR3_Val * CCR3_dc / 100;4 ~3 {, [' d8 A
  58.         }6 e6 Z4 C, X: ~4 T9 M
  59.         else
    / Y0 H1 j. K3 J- l% }
  60.         {
    & y$ \$ n6 ^) j5 ]/ [, m
  61.             flag3 = 0;
    3 `% e/ X0 W2 o
  62.             setcap = capture + CCR3_Val  * ( 100 - CCR3_dc ) / 100;
    - V* }$ `/ V/ n3 p# a
  63.         }
    5 [7 W% V+ m+ |
  64.         TIM_SetCompare3( TIM2, setcap );  T7 |7 [6 H. P1 r
  65.     }7 g4 v& \' O" K$ K0 f: M
  66. # s/ \" G* M% }, \( k0 b0 T3 e
  67.     if( TIM_GetITStatus( TIM2, TIM_IT_CC4 ) != RESET )
    / V  }2 d0 X( Q5 `% F. i6 T& `5 @8 d
  68.     {
    4 Q$ }7 h$ o; s" w( H
  69.         TIM_ClearITPendingBit( TIM2, TIM_IT_CC4 );
    ( X  z. A) Y! S! b! e2 b
  70.         capture = TIM_GetCapture4( TIM2 );
    ; u. ~; H' F: D2 h0 O
  71. ' N: P& X) I" `* ]* ~9 ~
  72.         if( flag4 == 0 )& W" ~; N0 K/ L- I6 H. y5 l' O
  73.         {
      R2 K1 K8 t/ N1 D
  74.             flag4 = 1;
    * R* i# j' B. M( U
  75.             setcap = capture + CCR4_Val * CCR4_dc / 100;
    9 Y# B; a0 T% k3 n: ]
  76.         }
    1 A( i5 [' u3 q2 ?
  77.         else
    ' J. R: \/ V2 U
  78.         {
    ) ^( O/ h1 b3 t. g$ n
  79.             flag4 = 0;% p( ~6 f- N4 U7 [5 C# u2 S
  80.             setcap = capture + CCR4_Val  * ( 100 - CCR4_dc ) / 100;. T2 A/ Q. n% w2 e
  81.         }
    6 o8 T+ b$ K1 N" M! ?% y  A& f" H
  82.         TIM_SetCompare4( TIM2, setcap );; n1 `' t4 ?  i8 X
  83.     }! _: X" ?+ h$ Q& S1 K" [( L  e% N
  84. : M5 I  i5 F: {6 D* K
  85. }
复制代码
) C; ]9 N% [; E) }% H, `
  进入中断后,首先读取当前比较寄存器的值,因为比较寄存器的值是会一直累加的,直到比较寄存器的值等于ARR的时,才会清零。所以每次中断的时候,需要将现在的比较值读出来,然后加上一个值,作为下一次的比较值。读出当前的比较值存放在capture中,接下来需要在加一个值,作为下一次的比较值。因为要改变占空比,所以增加的值有两种情况,分别是高电平持续时间值和低电平持续时间值。为了区分高低电平,这里使用的一个标志位来判断,标志位的值只有0和1两种情况,将0作为高电平的时间,将1作为低电平的时间。CCRx_Val 中存放整个周期的计数值,当标志位为0时,CCRx_Val 乘以高电平的百分比,计算出高电平的累加时间。当标志位为1时,CCRx_Val 乘以低电平的百分比,计算出低电平的累加时间。CCRx_dc 中存放占空比的值,范围是0到100,表示占空比从0%到100%。 当需要改变周期时,就修改每个通道的 CCRx_Val 值,当需要改变占空比时,就修改每个通道的 CCRx_dc 的值。
8 h, `+ p6 Y3 j3 {( `/ B3 F+ K: l5 a9 V9 y6 {
  1. int main( void )
    0 ?- f) ^; @; }" V* U
  2. {; H1 V* T6 W1 T, R( ~" I
  3.     u16 i = 0;  _: N% [$ H5 \7 Q) S, Y
  4.     delay_init();
    ( `- p4 J" a( }0 v1 ~
  5.     NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );7 f9 }# R7 B6 v8 c+ o
  6.     TIM2_PWM_Init( 65535, 0 );
    0 f& I0 N% d2 ]- c4 ~. @! Q# Y7 _) }
  7.     while( 1 )$ i" y; Z# {1 @# }  k7 X
  8.     {
    5 a( f: L0 c* q: e, K' v) n  r
  9.     }
    + V6 ~* O% ^# _6 f
  10. }
复制代码
' y0 A! x' l; N5 \3 F
  接下来在主函数中初始化定时器,定时器2的4个通道输出波形如下:
& C. K/ l1 c  ~# ~" y6 ~6 L
1 |2 ?2 Y% R* @) V* g 38$@}E}78QD8CQX(RTY%7[L.png $ m9 W" C: q. U0 C& m/ f- J' r5 `
6 P: _9 [) @/ b$ P' q( O! x  r& ^
  通过波形可以看到,定时器2的四个通道频率和占空比都是不一样的。说明通过定时器中的比较模式,是可以实现同一个定时器输出不同频率PWM波的。
+ e4 O- H! @' J' n7 u- F* N5 I
. n+ U& G: @; t* }2 \$ H. |; A- Q( D1 o  ~

; K8 T4 |3 G3 w/ [) E7 S2 _
赞 收藏 评论0 发布时间:2022-3-18 22:13

举报

0个回答

所属标签

相似技术帖

官网相关资源

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