STM32学习笔记09—PWM输出实验 9.1 PWM简介
0 P Q7 D" v* f( I, W! i7 v 脉冲宽度调制(Pulse Width Modulation)技术,简称PWM调制技术,是一种通过改变脉冲的宽度以及频率来改变输出频率的一种控制方式。采样控制理论中有一个重要结论:冲量相等而形状不同的窄脉冲加在具有惯性的环节上时,其效果基本相同。PWM控制技术就是以该结论为理论基础,对半导体开关器件的导通和关断进行控制,使输出端得到一系列幅值相等而宽度不相等的脉冲,用这些脉冲来代替正弦波或其他所需要的波形。按一定的规则对各脉冲的宽度进行调制,既可改变逆变电路输出电压的大小,也可改变输出频率。- a0 P# M2 G8 z) _7 F2 ^* Q8 n8 s; F
STM32的定时器除了有计数,定时,输入捕获功能以外,还有PWM输出功能,主要工作原理就是通过在定时器内部设置阈值,当CNT计数器的数小于这个阈值的时候输出1个电平,当CNT的数据大于这个阈值的时候输出另一个电平。通过改变这个阈值来改变占空比(因为CNT最大计数值就是65535),通过改变计数频率来改变PWM脉冲频率。
) i1 x1 h8 f% m# q X- Q( J# O0 O) s9.2 相关寄存器- d3 n/ \8 N+ O9 {- y
9.2.1 捕获/比较寄存器:TIMx_CCMRx# V8 G7 u( q/ W8 X' S0 P/ F, ]$ v
# R* L. l9 t) v. k OCxCE:输出比较x清0使能
* Y& ~& i5 l1 C0 i& Q4 l 0:OCxREF不受ETRF输入的影响
8 m+ G2 W2 \+ e 1:一旦检测到ETRF输入高电平,清除OCxREF=0- v) i2 Q% E0 _5 [
OCxPE:输出比较x预装载使能
1 s% i) g0 f+ |; A: z 0:禁止预装载功能,写入的数值立即起作用. C3 p8 m5 o7 M7 T
1:开启预装载功能,TIMx_CCRx的预装载值在更新事件到来时被加载至当前寄存器中
1 h8 o* Q* Q/ f注1:一旦LOCK级别设为3并且CC1S=00则该位不能被修改
# n! _9 y! F. [' g注2:仅在单脉冲模式下,可以在未确认预装载寄存器情况下使用PWM模式,否则其动作不确定3 \) i E/ M$ \2 D
OCxFE:输出比较x快速使能(该位用于加快CC输出对触发输入事件的响应)8 {; k: p6 R( ?" a
0:根据计数器与CCRx的值,当触发器的输入有一个有效沿时,激活CCx输出的最小延时为5个时钟周期
7 [% G( {8 ^2 P3 G } 1:OC被设置为比较电平而与比较结果无关,触发器有效沿和CCx输出间的延时被缩短为3个时钟周期" O. S, }; j5 F( Q! X0 x
注3:只在通道被配置成PWM1或PWM2模式时起作用
! m- I2 d8 S5 a7 pOCxM[2:0]:输出比较x模式! b( u9 a; N( t) D- H2 |/ K8 A: `
% f. w$ \0 d1 g9 a1 A CCxS[1:0]:捕获/比较x选择(用于定义通道x输入还是输出)
) J s6 ?' ? P9 V. c# a 00:输出模式$ ~0 `( l8 E' t# v1 _
01:输入模式,映射在TI1上
, @, r5 {3 Z- d% X 10:输入模式,映射在TI2上
k5 l0 q& n$ B3 g" p6 d1 W 11:输入模式,映射在TRC上,此模式引用于内部触发器输入被选中时
' m% B2 `2 R/ j$ C/ M' |9.2.2 刹车和死区寄存器:TIMx_BDTR3 c( p5 }2 l( P {2 m2 c5 u3 T
注:这个寄存器只有TIM1和TIM8这两个高级定时器才需要控制。
' B' R5 {. w" Z* {
W; ]3 l! e D8 Q7 B- U Bit 15:主输出使能(一旦刹车输入有效,该位被硬件异步清0)
" E% J$ z9 u3 L 0:禁止OC和OCN输出或强制为空闲状态/ C5 f$ w' W4 ~0 T( x
1:如果设置了相应的使能位,则开启OC和OCN输出。+ ~* `- g! D7 e
Bit 14:自动输出使能/ D5 _+ |7 R" E" O: |1 L
0:MOE只能被软件置1
+ e! ^) ?/ J: F4 N5 \ F/ F" n2 M" j 1:MOE能被软件置1或在下一个更新事件被自动置1' Y" G) I2 `7 |2 L4 G: u0 e
注1:一旦LOCK级别设为1,则该位不能被修改
+ D9 e( f7 T0 V9 b8 C/ qBit 13:刹车输入极性 I& C0 R7 C- t. C
0:刹车输入低电平有效
' c, S8 b5 U( _8 d, a* U$ m 1:刹车输入高电平有效( r; r$ x; j! s( e& \
注2:一旦LOCK级别设为1,则该位不能被修改8 w. }/ X. W6 j" `) d" v
注3:任何对该位的写操作都需要一个APB时钟的延迟以后才能起作用* g7 ^ N5 a6 W# F9 [# u. a
Bit 12:刹车功能使能6 ^& Q8 _3 C- g! t
0:禁止刹车输入
# q; ?4 F8 z/ h8 X& ^9 @, _: I 1:开启刹车输入. b% w/ M9 o @' S, H
注4:当设置了LOCK级别1时,该位不能被修改( H- A+ B6 W0 j* Z
注5:任何对该位的写操作都需要一个APB时钟的延迟以后才能起作用 h, e R" ?! f1 N, J: ~0 f+ z+ h
Bit 11:运行模式下“关闭状态”选择(该位用于当MOE=1且通道为互补输出)
7 O$ |+ k, E+ s( S$ w" g* r 0:当定时器不工作时,禁止OC/OCN输出(OC/OCN使能输出信号=0)4 p% }. t% r \+ Q& o2 C% y$ E9 w
1:当定时器不工作时,一旦CCxE或CCxNE为1,先开启OC/OCN并输出无效电平,再置使能输出信号为1
( \* T3 s! Z. p# R& Y 注6:一旦LOCK级别设为2,则该位不能被修改* J( O4 R: I9 I$ s" k, S
Bit 10:空闲模式下“关闭状态”选择(该位用于当MOE=0且通道设为输出时)/ Q7 F" l v7 w* F/ ?
0:当定时器不工作时,禁止OC/OCN输出(OC/OCN使能输出信号=0)
- [+ Y! b1 E9 Z 1:当定时器不工作时,一旦CCxE或CCxNE为1, OC/OCN先输出其空闲电平,然后使能输出信号为1. n2 u# N# o2 X0 e+ J$ o1 M
注7:一旦LOCK级别设为2,则该位不能被修改8 n' \& X; W2 l+ c
Bit 9~Bit 8:锁定设置(该位为防止软件错误而提供写保护)4 W2 L6 }6 s0 G2 ?
00:锁定关闭,寄存器无写保护
% ~, E" k) \, r: u# ~9 u3 `: z% d8 Q" b 01:锁定级别1,不能写入TIMx_BDTR寄存器DTG、 BKE、 BKP、 AOE位和TIMx_CR2寄存器OISx/OISxN位( w3 t$ O( w! b& a. {: N6 f9 x
10:锁定级别2,不能写入锁定级别1中的各位,也不能写入CC极性位! Z9 T3 y1 W; P+ b& e
11:锁定级别3,不能写入锁定级别2中的各位,也不能写入CC控制位
8 u& {" h/ b$ n# ~4 I+ H 注8:在系统复位后,只能写一次LOCK位,一旦写入TIMx_BDTR寄存器,则其内容冻结直至复位
" Z! l9 |/ D o& EBit 7~Bit 0:死区发生器设置(定义了插入互补输出之间的死区持续时间)& z: T* c' X2 S' h
9.3 PWM实验例程2 T; \" s. M9 W) [# w
利用STM32的PA8输出频率1KHz,占空比为30%,高电平有效的脉冲。; @! D, h; k2 V; E
(1)创建基础工程与pwm.c和pwm.h文件,并将pwm.c和pwm.h文件添加进工程。
1 `1 @) v7 h m. s
- D4 c- l/ @2 r3 q# t- [* J (2)pwm.h添加代码如下。3 g9 X8 y3 Y( e; @6 Y) t
9 K& Z% R- c) I7 I, p% l5 F# ]# v9 E (3)pwm.c添加代码如下。
6 V+ V$ v5 m' q/ k3 j5 N: a- b' T5 }0 K$ u/ L
- #include "pwm.h"7 n; G1 t3 x! U/ y% Z/ h9 v3 r; P0 b
- /***************************************************) Z( K2 I. b- h
- Name :PWM_Init
7 R3 n6 X2 ?' d, k3 \. s1 S w6 `1 y - Function :PWM输出初始化
0 c* h2 u- f) R5 Y% [1 y0 o% G - Paramater :
$ o/ S X# t# M* u8 t - psc:预分频系数; L3 O1 v& @6 } p8 f5 S, S4 q
- Return :None
) W' o) W3 I, v' b" E - ***************************************************/
$ J+ @9 l' C! Y - void PWM_Init( u16 psc )
, F+ ]( J3 C: u: `3 X - {3 J, ?2 @9 j# N- l0 C2 k' N( M7 G
- RCC->APB2ENR |= 1<<2 ;& ^( n2 E0 n K. P
- GPIOA->CRH &= 0xFFFFFFF0 ; //清除PA8配置
( `2 Z5 n; I& T% E - GPIOA->CRH |= 0x0000000B ; //PA8复用功能输出. c4 k4 y7 `1 ^! J3 X+ Z
- RCC->APB2ENR |= 1<<11 ; //TIM1时钟开启
9 X2 z, g, \3 @1 t M - TIM1->ARR = 100 ; X0 \+ i# B$ r6 ?
- TIM1->PSC = psc ;
! \7 c7 s. Y2 Z2 G7 n - TIM1->CCMR1 |= 7<<4 ; //CH1 PWM2模式' T8 ` O% a' |/ T( }
- TIM1->CCMR1 |= 1<<3 ; //CH1预装载使能6 _% S) \: W8 O' P4 U) G
- TIM1->CCER |= 1<<1 ; //OC1低电平有效3 T( ~) Q$ A/ e' R6 I' l
- TIM1->CCER |= 1<<0 ; //OC1输出使能
6 u+ I' t( L1 N; N - TIM1->BDTR |= 1<<15 ; //MOE输出使能
: n: l2 k' c3 K: i7 `- m D2 Q" D - TIM1->CR1 |= 1<<7 ; //自动重装载使能
+ l, C' J+ T* K% S+ ~ \2 ~ ?4 d - TIM1->CR1 |= 1<<0 ; //开启定时器1/ j+ I2 d: P9 V- M
- }
* R; [" S: G; u' U% F" o% G# `/ `! W - /***************************************************
) @) N( \0 k+ s' t4 k - Name :PWM_Set \+ U8 t- Q) n" y- e
- Function :PWM设置1 r$ Z$ K4 J- ^
- Paramater :
; {( a. T E' j$ ^( l - pwm_data:占空比
, b2 S, u- M6 T5 b% V; v8 C# _ - Return :None
1 @; [/ j, ^& Z( c+ e1 | - ***************************************************/
9 m9 ~: q! U3 a- r( J2 M0 K8 r - void PWM_Set( u8 pwm_data )
7 E" h" U. W8 {4 o. i" ~ - {
& N1 ~- b- N# D4 H6 ` - TIM1->CCR1 = pwm_data ;
" k6 r3 R/ O& z) `! l7 m5 v; ~ - }
复制代码 ' _; d" N4 Q: I4 g. K
! Q/ l8 |( P! s% y
(4)1.c添加代码如下。
! m% L8 O2 k: a- A
- V8 V) y+ t2 H8 s6 x& p- <font size="3">#include "sys.h"
* L# A" v6 H; U( R - #include "delay.h"& [; v& ^% q- U) ^" }- N8 W/ z
- #include "usart1.h"" Z" w- @6 z4 a" T
- #include "pwm.h"
t3 K2 w+ I n t; i, R
- m6 t0 ~, F7 w6 P8 c& S; |# `. q- int main()) A: ~$ V: K% W! K, Z4 Z" Y. A
- {
& t8 B; A3 w/ Z( c$ c - STM32_Clock_Init( 9 ) ; //STM32时钟初始化
0 c( |. p$ G# s% v0 k9 W8 Q - SysTick_Init( 72 ) ; //SysTick初始化
/ }- b( \ g' u2 y - USART1_Init( 72, 115200 ) ; //初始化串口1波特率115200
2 `6 ?$ H' T& m6 \* L# F. [0 u: O - PWM_Init( 719 ) ; //PWM初始化% ~/ D2 ]% L; T( j* w- z2 U/ r% ]% b' Z
- PWM_Set( 30 ) ; //设置占空比30%; S, _3 V+ g, Q/ P: l. U! u$ x
- while( 1 )" q8 J, G' T# F: f1 U1 b- _# Z. Z
- {
8 y4 g# I: C8 q( o0 N/ ]6 L -
3 Y1 J2 Q+ t3 U - }
$ U! k0 ~3 {3 k( y( E4 x) K - }& t+ o# E& F* H% H) l
- </font>
复制代码
) W. }' O4 w4 @9 p: G
$ |* y9 d. \, r' [. R* i9.4 扩展:PWM实现DAC输出$ f8 {" C4 Y0 L7 n
9.4.1 工作原理
& h I+ O& o8 d: @( ?5 y 由于STM32F1自带2路DAC输出,所以当DAC不够用的时候,为了节约成本,需要利用PWM配合RC滤波器来实现DAC输出,对于PWM脉冲的波形可以列写出脉冲的时域函数表达式
3 X' }& y2 _% Z9 d: y0 a' J
* {+ c: O0 e6 w7 m! ~+ r
9.4.2 电路设计+ U& b) }8 i0 c4 P. q, b z
我们现在设计一个分辨率为8位的PWM信号,其实STM32的分辨率都可以达到16位,甚至32位,但是分辨率越高,速度就越慢,在8位分辨率条件下,我们要求1次谐波对输出电压的影响不要超过1个位的精度,也就是3.3/256=0.01289V。假设VH为3.3V,VL为0V,那么一次谐波的最大值是2*3.3/π=2.1V,这就要求我们的RC滤波电路提供至少-20lg(2.1/0.01289)=-44dB的衰减。- e4 c; `/ I/ J! J! w
STM32的定时器最快的计数频率是72Mhz,8为分辨率的时候,PWM频率为72M/256=281.25Khz。如果是1阶RC滤波,则要求截止频率为1.77Khz,如果为2阶RC滤波,则要求截止频率为22.34Khz。
& t/ d$ Z: C B0 d( i
* ~. n/ s1 W5 l7 i) t/ @# P5 g 上图所示位二阶RC滤波器的电路原理,根据二阶RC滤波器的截止频率计算公式# e9 ?" ]4 w4 |- Z! {) F
3 k" P7 A: F. _
可以得到R28*C37=R29*C38=RC,通过这个公式我们选定的R和C的参数,并得到实际的截止频率为33.8KHz,远超过理论的截止频率,该电路实测精度大约在0.5LSB左右。
# \, i1 W4 d, S5 I/ I! E" C9.4.3 实验例程# k& m, @( g ?! k2 z; [$ b2 G8 z. A
在这里,我们只需要将实验例程中的PWM频率改为22.34KHz即可。通过改变占空比,我们可以用电压表来测量出实际的输出电压。! |$ V# f/ v7 v6 M
PS:PWM实现DAC输出最常见的功能就是语音合成芯片。
# }; @: c5 o$ c4 o0 z; [* P6 X9 l, Z# I# p
7 S7 q3 d6 \. f) q% K0 W6 r0 Q$ {
|