PWM模式也叫脉冲宽度调制模式,它可以产生一个频率和占空比可调的方波。由TIMx_ARR寄存器确定频率、由TIMx_CCRx寄存器确定占空比的信号。在硬件电路中,PWM波产生通常是由一个三角波和参考值送入比较器中,然后比较器输出的就是PWM波。* N( ^& X s% n# p; V1 @% p: L
' v% L0 b# p, k, h' B
0 J/ \. T$ V1 K! L' `6 w
6 s# B& b& @! x4 e/ K9 sV1是三角波发生器,幅度为5V,频率为1K,V2是直流源,电压为3V,将这两个波形送入到比较器中,然后比较器输出的就是PWM波。输出波形如下:7 R' T6 E3 I4 e( ?
, U5 p4 |% \" f& u; C8 R: b
5 n3 T; e% l4 O
2 U* K# d( u5 n( D2 H9 U当V1的电压值大于V2时,比较器输出高电平。当V1电压值小于V2时,比较器输出的就是低电平。改变V2的值,就可以改变占空比。
" j' F, x8 o+ X. |$ e/ d" R$ `9 I( s
* J' V# w0 E$ a! V: I
. a4 I; o4 B/ s- G; ?
+ g' G7 ?, Z6 t3 w8 r H 在单片机中寄存器ARR的值就相当于V1的值,CCR的值就相当于V2的值。当ARR和CCR寄存器的值设置好之后,计时器在计数的过程中计数器值就会实时和这两个值比较,当计数器值小于CCR时,输出低电平,当计数器值大于CCR值时,输出高电平。当计数器的值等于ARR时,计数器就会清零。0 ^: S% ^* @: k' [7 V! C8 G/ J
: q! v2 w1 k; _3 J2 u 在设置PWM计数模式的时候,一般有三种模式,向上计数模式、向下计数模式、中央对齐模式。- v/ }2 ?1 d- C
/ O: K, @; J8 ~7 l9 w6 |; h# \
向上计数模式:指的是计数器从0开始递增计数,当计数值等于ARR时,计数器清0,然后从0开始重新递增计数。" d5 `* S% s# j0 P& D
3 h7 l d% K: C2 y. t) Z& I& p
2 M5 B8 t: Z% I9 S! Q6 `: H# J- {3 p) r& t& n( F
向下计数模式:指的是计数器从ARR开始递减计数,当计数值等于0时,计数器值重新设置为ARR,然后从ARR开始重新递减计数。
} w2 ]) p. Z. C6 U; u3 [& j+ T" s Z" [; A& P1 y% A
) s; s2 ]% R6 R& m5 R8 q9 t4 T+ X* p& q: z' `9 ]: d) S* u: `' |
中央对齐模式:指的是计数器0开始递增计数,当计数值等于ARR时,计时器开始递减计数。当计数器值减到0时,又开始递增计数。; k. v' b* d9 ^! {- }
1 h3 x2 l" l2 { {8 y
7 Q& v( A7 a3 p% n& b; L v6 }
3 i- V# Y; d( b4 M& J9 o 一般情况下用的比较多的就是向上计数模式,因为这种模式比较简单,理解起来也更容易一点。下面就通过代码来实现PWM的输出功能。
, k l: F$ K/ D6 X9 o0 ]# l E
5 i0 C8 i) b7 c, t% H& L8 w( e' T- void TIM3_PWM_Init(u16 arr, u16 psc)
: Q) x" P8 e3 | C) X! b - {
, X/ W( s# n* s! I1 W! ` - GPIO_InitTypeDef GPIO_InitStructure;
" w4 r2 T) h8 ^2 V6 j/ f - TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
7 @ [6 n3 O. p, O. [ - TIM_OCInitTypeDef TIM_OCInitStructure;$ ?* j3 l8 ^% H# l8 V
- 1 c/ i7 |- g( D
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟$ j$ r3 s! p- X' {; s/ |' i
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO和AFIO复用功能模块时钟( Z% i, E8 ?% A! P# u3 A
- ( ]( q3 M' d" f) `! R
- GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2(PA7)--->PB5 TIM3_CH1(PA6)-->PB4- T- ~/ o0 ] v* ]# i1 G8 p+ {
- - U6 [: ?! l, A* ~6 Y6 c/ F; }! Y
- //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5
# s$ C! y" L7 D1 _ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;0 l X+ _( N- e& |& d- n( B7 W
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;$ o: R5 P5 C5 F/ A; }
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;4 }# B8 O% J6 F
- GPIO_Init(GPIOB, &GPIO_InitStructure);% q z9 y% x$ }& X' q' a
- //初始化TIM34 j, T. h' {0 Q+ |$ @( T
- TIM_TimeBaseInitStructure.TIM_Period = arr;, m( }8 e+ O* }2 P& z
- TIM_TimeBaseInitStructure.TIM_Prescaler = psc;! O* y9 S6 C- y/ K
- TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
d. q2 K4 N9 {' H9 F5 v- B* P+ h - TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;& Q( O- ^2 P* }# A4 v
- TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
: y' a9 |$ s m - //初始化TIM3_CH2 PWM 模式
. v. S2 \" w/ l) Q& N4 x! v - TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;5 _0 S# A; K+ C& b! z
- TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
' ]' d0 M$ E: d0 Z6 M5 s' R) t/ J - TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;7 d/ l9 T2 c2 Q' ], q0 \
- TIM_OC2Init(TIM3, &TIM_OCInitStructure);
5 b8 A$ B; T8 ` - //使能TIM3在CCR2上的预装载寄存器4 i9 J1 w! O4 O" |1 S
- TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);+ Y! {8 L) L" M4 j
- //使能TIM31 Q& V( [3 n/ ]( G2 T, Z
- TIM_Cmd(TIM3, ENABLE);9 |1 E( G4 ^! o) U0 O
- }& |/ |* q3 j8 C6 S
- void TIM1_PWM_Init(u16 arr, u16 psc)+ I4 X4 y9 z+ ]$ w2 h4 `; G1 g
- {- }" q! [( E' A- w9 H* u" f
- GPIO_InitTypeDef GPIO_InitStructure;4 \' W; i% k0 o; T- G( Y- Z# Q N
- TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;" G( ^1 w% M2 L. M& E; {
- TIM_OCInitTypeDef TIM_OCInitSturcture;- Z" m" _) O2 i! N7 m& ]
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE);
% z" P1 k- U2 L/ F' a - / R# `& \' j" F
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;2 m& v/ C3 u" @8 T& U- W7 v& m+ v/ C+ l
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
2 w( |: w( W8 `- d/ W1 g B# {$ y! s - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;$ _, q" u, g+ L% H
- GPIO_Init(GPIOA, &GPIO_InitStructure);
' H1 Z+ o0 E( U& Y1 A; z
& D2 e; q( p+ v2 Z8 Z A a- TIM_TimeBaseInitStructure.TIM_Period = arr;
$ U. v r) A/ p+ {) y! \ - TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
8 f: `) W: F! o( o `* x - TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
, h+ ]& E3 }) S7 W2 ] - TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;+ o7 \6 _* F3 j' _# j
- TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0x00;( d2 v6 t& z9 ^+ A
- TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);
8 K& {) F0 O5 I) r5 h; b - 1 b+ H% T4 p9 Q! _1 E3 R% @ j
- TIM_OCInitSturcture.TIM_OCMode = TIM_OCMode_PWM2;
9 ~; Q. U( N; l* h' o9 o5 K: T8 j - TIM_OCInitSturcture.TIM_OutputState = TIM_OutputState_Enable;
3 f/ ?; Z! O [: w8 N - TIM_OCInitSturcture.TIM_OCPolarity = TIM_OCPolarity_High;1 ~3 ]5 _. f4 ]5 l `
- TIM_OC1Init(TIM1, &TIM_OCInitSturcture);
3 E t$ P9 ^2 T& {, { - 4 S& _; Z) z: O2 A
- # ^1 y3 L1 X+ h* A$ d: [; y
- TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);+ @; m- P0 R! C; w& |1 A, V3 w9 Z
- - m' B7 X+ y- y
- TIM_Cmd(TIM1, ENABLE); //使能计数器
4 B: `0 p1 I& [# v. k) S! Z6 }' S - TIM_CtrlPWMOutputs(TIM1,ENABLE); //主输出使能/ u% N3 X( |. D: ^% d+ j
- }
复制代码
M: H( P" A- K4 v" b6 t 这里通过两个定时器输出2路PWM波。使用的计数模式都为向上计数模式。PWM模式为PWM2模式。
9 [3 h3 e7 @5 b) P, E6 _8 b7 e
; D! b4 V g# h2 A( p5 n2 ^
" S' ?7 T: l' {6 x% D 接下来在主程序中设置PWM的频率和占空比。: i6 C# u; A# D
" e# u0 L# y. w' z! g# O
- int main(void)
s2 D y- t( }, w' b - { l2 s/ b# {# ~6 C- A: ~
- u16 led_pwm_val=0;: Z( ^, J& Q6 u* o, p; x
- u8 dir=1;) @7 f1 Y8 B% h; e
- delay_init(); //延时函数初始化
# f# C# g5 N) _6 ]3 }& O+ x) ~: P3 d - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
4 y3 y' g5 y* c% O, h - LED_Init();" J9 b/ {$ o8 W
- TIM3_PWM_Init(899,0); //不分频。PWM频率=72000000/900=80Khz
6 R, j; a3 F2 F - TIM1_PWM_Init(899,0);) z3 J. Q* [) k9 N6 ?
- while(1)
- z0 G7 b3 m& H; Z! @4 h - {
~; m8 w3 p- o! f' J) j - delay_ms(10);2 B/ f2 _9 L3 V- ?; |* k
- if(dir) led_pwm_val++;& |' L. @0 ~& q9 A2 m
- else led_pwm_val--;
3 J6 F0 U3 A' k2 N) ^' L - ' e! L7 K2 X5 X9 n8 @
- if(led_pwm_val>300) dir=0;
* \6 L7 A. ^9 e$ ] - if(led_pwm_val==0) dir=1;: i* A v8 z0 e& ]* X4 r+ I
- TIM_SetCompare2(TIM3,led_pwm_val);/ i) S. V& H; l
- TIM_SetCompare1(TIM1,led_pwm_val);2 q" v3 a9 L7 x3 S8 s
- }
; g6 J, y) E% `: [0 }9 z - }
复制代码 7 L1 M# v8 M' x* W8 j+ Q( b0 J
在主函数中向初始化函数中传递两个参数,第一个参数是设置ARR值,第二个参数是设置时钟的分频系数。这里设置的ARR值为900,系统会自动给设置的值加1(因为ARR的值至少为1),所以设置值为899时,系统真正写入ARR寄存器的值是899+1,然后时钟分频系数设置为0,也就是默认时钟频率72MHz不分频。这样PWM的频率就为系统时钟除以自动装载的值。设置的PWM频率为 72MHz/900=80K。
( d& U6 h3 r. Z! D
7 O1 C4 v) I8 O+ J8 D( [1 O 要改变占空比时通过TIM_SetCompare()函数来设置,这个函数内部设置的就是CCR寄存器的值。
/ V7 j. B3 |$ s8 g1 j6 M代码中设置的是CCR的值从0增加到300,然后又减到0。将PWM的输出端口接上一个LED灯,就可看到这个LED灯从亮到灭慢慢变化,实现了类似于呼吸灯的效果。) f! N5 Q' R4 K
2 g/ N% T! U4 N9 y# u9 N
|