PWM模式也叫脉冲宽度调制模式,它可以产生一个频率和占空比可调的方波。由TIMx_ARR寄存器确定频率、由TIMx_CCRx寄存器确定占空比的信号。在硬件电路中,PWM波产生通常是由一个三角波和参考值送入比较器中,然后比较器输出的就是PWM波。1 n2 x+ c: s! O
% Y1 V# o/ ]' V2 H: K$ u2 m
- s- Z' J$ n F v2 B/ L6 L
m) |4 m0 Z' i/ t! M1 c6 ^
V1是三角波发生器,幅度为5V,频率为1K,V2是直流源,电压为3V,将这两个波形送入到比较器中,然后比较器输出的就是PWM波。输出波形如下:& q, u3 L4 h9 V1 N1 F5 a7 {
& S+ Q4 `& Y5 F6 ?/ k. [/ R" h3 f9 j
) N) W/ `) U; I; C: `3 h
* Y, b, f7 q5 d: w4 L当V1的电压值大于V2时,比较器输出高电平。当V1电压值小于V2时,比较器输出的就是低电平。改变V2的值,就可以改变占空比。
9 y6 q# ]( R. h" B" s
' X2 C' [# }8 @) k- f, `- Y3 x
3 i% B! ^, U4 ^1 D/ E
8 p1 Y) B, u. [5 ~$ h 在单片机中寄存器ARR的值就相当于V1的值,CCR的值就相当于V2的值。当ARR和CCR寄存器的值设置好之后,计时器在计数的过程中计数器值就会实时和这两个值比较,当计数器值小于CCR时,输出低电平,当计数器值大于CCR值时,输出高电平。当计数器的值等于ARR时,计数器就会清零。
/ i' A; N# X% o2 `7 H
: p/ j7 ?( f) K2 j. r 在设置PWM计数模式的时候,一般有三种模式,向上计数模式、向下计数模式、中央对齐模式。 l- I, W( r o2 |3 D$ i5 r5 K1 i
' l$ i3 ?2 ]6 Q1 B 向上计数模式:指的是计数器从0开始递增计数,当计数值等于ARR时,计数器清0,然后从0开始重新递增计数。) \1 @) [4 M, B$ Y) x- B ?
' F. k% L8 c' N1 b# K$ f: \
! r( n4 U% K. S/ |) H1 ]" K& o: `/ g& q' _" |
向下计数模式:指的是计数器从ARR开始递减计数,当计数值等于0时,计数器值重新设置为ARR,然后从ARR开始重新递减计数。6 y& c+ N o; `1 Z8 ?0 j& m: R& n# K
! V; |3 u' m7 Y
+ u# w7 z9 \" A# n( ^0 S& ^8 L
3 t% @& Q0 c6 `9 t. N 中央对齐模式:指的是计数器0开始递增计数,当计数值等于ARR时,计时器开始递减计数。当计数器值减到0时,又开始递增计数。1 i& `9 C4 O/ A
' `8 r Z. z' `
% ?' E* r0 M, }( J1 h$ t0 B- Q
& p6 v2 c, G% u t9 @8 `* Q; Z
一般情况下用的比较多的就是向上计数模式,因为这种模式比较简单,理解起来也更容易一点。下面就通过代码来实现PWM的输出功能。" c W3 a. n% F+ S% c
C+ c8 \2 x* B% T: x9 h+ C L5 J- void TIM3_PWM_Init(u16 arr, u16 psc)1 y& z) R! V% Q' @; U( B8 |2 q6 c
- {
% i/ Z2 o" d* \9 x* `' }5 W4 S6 W$ s - GPIO_InitTypeDef GPIO_InitStructure;
4 @* O$ S! K C' q - TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;* e9 B" s. Q! [2 O2 ^
- TIM_OCInitTypeDef TIM_OCInitStructure;
; O% A/ w2 B: d6 O1 s
9 @6 |5 N( d$ B9 }- O+ {- d- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟, U4 \0 T; l5 f' c
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO和AFIO复用功能模块时钟# J9 V) A& D0 K& p8 B: \4 V
4 [1 @7 s0 `( w- w3 Z- GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2(PA7)--->PB5 TIM3_CH1(PA6)-->PB41 \1 s+ w7 ~/ i% G+ e
" H( T! A8 ?# m/ B$ e1 K- //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5' n4 O7 {: P& g3 F, [
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;0 |4 |$ I% i# ]
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
5 ^( l: T% x% |5 a3 o ` - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;* X7 d8 x2 P, K( K! U. y
- GPIO_Init(GPIOB, &GPIO_InitStructure);
( }7 f8 t) S/ C1 p" L5 ] - //初始化TIM3
4 ^% u' o1 P' s! g9 s - TIM_TimeBaseInitStructure.TIM_Period = arr;
* n) W; b9 W7 Y( q9 k! z1 x - TIM_TimeBaseInitStructure.TIM_Prescaler = psc;; L% g7 m( n! @0 J
- TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
5 X# [$ T! ~ l - TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;) Z- Q# h4 h! v% j
- TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); }0 o5 L+ }" N' k5 C n) H+ q
- //初始化TIM3_CH2 PWM 模式
l* r# h, x$ R* [ - TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
, Z0 d. {8 t: G+ ?3 O; e - TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;% N; X6 I/ p) `% M- V
- TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;" _/ c; h( L# [) [% Z, P) S
- TIM_OC2Init(TIM3, &TIM_OCInitStructure);
) C/ H+ ?: a1 G+ Y* _- p0 a' w# I% Y - //使能TIM3在CCR2上的预装载寄存器1 R2 b# `* r0 I& B, M* s% h u
- TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
9 _% v& c; j- k; G5 ^( @ - //使能TIM3. r; X8 [. M1 j9 r7 ~
- TIM_Cmd(TIM3, ENABLE);
; D V3 A' F, g - }! Y' S% t* [! h, M1 m
- void TIM1_PWM_Init(u16 arr, u16 psc)6 z" p$ r5 K6 c! L+ E8 o
- {- T1 V; R! k- |6 |; F
- GPIO_InitTypeDef GPIO_InitStructure;4 e# g5 B j5 s2 F
- TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
& E: Z, H* G" \ - TIM_OCInitTypeDef TIM_OCInitSturcture;
8 s3 R2 S* i; g2 R - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE);1 A" |0 Z! L7 B' J5 ?+ ?0 F
- 8 S, I9 m& ^6 `9 m L! x% A
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
. Q4 ?/ \0 K, T: T4 c4 w5 ?5 h - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
) ^" g5 v0 ?' `0 C2 m8 n - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;- |0 Z, W9 C3 z2 y
- GPIO_Init(GPIOA, &GPIO_InitStructure);( z% z& P: I5 |7 [' B# l, q
, |. } [; \4 M- TIM_TimeBaseInitStructure.TIM_Period = arr;
, N% W; c: o5 F: o4 b3 y$ S - TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
4 K# [9 o' p; C* T$ V4 W - TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
- F$ V# W# B+ u1 Y) H2 f2 z - TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;! p4 n6 g6 b5 [0 r$ ^# V: w
- TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0x00;2 t, v# n/ F; ~$ ~7 y* ]% ]+ `
- TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);
/ r& L( x7 I) R: S0 ]' z' w6 B - / Q$ }( D; @3 G# e8 v
- TIM_OCInitSturcture.TIM_OCMode = TIM_OCMode_PWM2;
8 C. g' q' @/ M - TIM_OCInitSturcture.TIM_OutputState = TIM_OutputState_Enable;
" V2 a8 H& t. G. J; g6 c5 `5 ] - TIM_OCInitSturcture.TIM_OCPolarity = TIM_OCPolarity_High;- P% g7 l5 o- k M3 e
- TIM_OC1Init(TIM1, &TIM_OCInitSturcture);
6 ^0 r8 @: ^: @( T3 r: a: c
+ h* y' q$ _) y-
' E, E- M4 g! f- P0 @ - TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);( g% ~7 T0 M" x) P6 y- _. \; g( u
) W5 N% j0 {) B; \" o: H- TIM_Cmd(TIM1, ENABLE); //使能计数器
) S C5 n1 D) W2 L' | - TIM_CtrlPWMOutputs(TIM1,ENABLE); //主输出使能
: L, ]$ a& H# P) H7 w. k# x - }
复制代码
5 s! p Q" {+ D0 h+ S/ R- f 这里通过两个定时器输出2路PWM波。使用的计数模式都为向上计数模式。PWM模式为PWM2模式。
6 O% ^5 N6 u& l" t* Q! x" n, C
8 ~9 e) z- k+ H" m
3 g3 d+ q/ [6 ~4 C, i" |! G
' S/ B& \( v4 o3 r5 B9 o; o. j9 ~ 接下来在主程序中设置PWM的频率和占空比。2 j) D9 o) R9 i( y2 L: ]
6 P# m3 l+ a3 L' D) X- int main(void)
{( Z+ V6 x$ W e; p$ ~+ h% c - {
0 A+ e+ B0 `- Z% ]5 r - u16 led_pwm_val=0;4 Z* f1 U) m6 k- p! i/ G5 Y
- u8 dir=1;
- Q+ d2 G* x' ?" H - delay_init(); //延时函数初始化: P8 A7 G8 r5 Y: o
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);7 |9 `& |/ e. T' L8 |& [; q; ?
- LED_Init();
; ^6 h$ b% U& T t - TIM3_PWM_Init(899,0); //不分频。PWM频率=72000000/900=80Khz' P; @9 }& W# |* T
- TIM1_PWM_Init(899,0);0 n# b: f+ @$ o: I) r" z- i# r$ D
- while(1)
1 K; A" Y3 ?$ h- t( e - {
% N3 t6 `+ u, {+ q+ V1 h - delay_ms(10);
$ G5 l- e+ t$ X5 A! j6 s - if(dir) led_pwm_val++;: v& j7 P6 z7 C2 ]; p
- else led_pwm_val--;4 }: B0 b- w& j- \+ u
- 5 i% w3 @" c. D
- if(led_pwm_val>300) dir=0;, B9 a9 D8 _/ x" i) t4 G6 f
- if(led_pwm_val==0) dir=1;
5 S2 c$ l6 R1 O3 r, B; B - TIM_SetCompare2(TIM3,led_pwm_val);5 l! k7 u( e0 L3 Q
- TIM_SetCompare1(TIM1,led_pwm_val);% V, q2 C7 q) X' I$ P: A
- }! G& p- o7 t' z' d# k5 f# A( b
- }
复制代码
* M9 {% P# c% v" R0 q3 i 在主函数中向初始化函数中传递两个参数,第一个参数是设置ARR值,第二个参数是设置时钟的分频系数。这里设置的ARR值为900,系统会自动给设置的值加1(因为ARR的值至少为1),所以设置值为899时,系统真正写入ARR寄存器的值是899+1,然后时钟分频系数设置为0,也就是默认时钟频率72MHz不分频。这样PWM的频率就为系统时钟除以自动装载的值。设置的PWM频率为 72MHz/900=80K。
1 c0 p/ a* C) e* e; F5 j
0 ~) M. |6 e0 P% r 要改变占空比时通过TIM_SetCompare()函数来设置,这个函数内部设置的就是CCR寄存器的值。
, u" j' ?+ \: k代码中设置的是CCR的值从0增加到300,然后又减到0。将PWM的输出端口接上一个LED灯,就可看到这个LED灯从亮到灭慢慢变化,实现了类似于呼吸灯的效果。/ I' z. b: z1 h/ R
' V3 y, j/ o+ u; W7 o
|