STM32产生PWM信号
首先我们必须肯定ST公司的实力,也承认STM32的确是一款非常不错的Cortex-M3核单片机,但是,手册实在是让人觉得无法理解,尤其是其中的TIM模块,没有条理可言,看了两天几乎还是不知所云,让人很是郁闷。同时配套的固件库的说明也很难和手册上的寄存器对应起来,研究起来非常费劲!功能强大倒是真的,但至少也应该配套一个让人看的明白的说明吧~~5 v6 O! _2 w0 d! Z 两天时间研究了STM32定时器的最最基础的部分,把定时器最基础的两个功能实现了,余下的功能有待继续学习。 首先有一点需要注意:FWLib固件库目前的最新版应该是V2.0.x,V1.0.x版本固件库中,TIM1模块被独立出来,调用的函数与其他定时器不同;在V2.0系列版本中,取消了TIM1.h,所有的TIM模块统一调用TIM.h即可。网络上流传的各种代码有许多是基于v1版本的固件库,在移植到v2版本固件库时,需要做些修改。本文的所有程序都是基于V2.0固件库。( y& r, A q5 d7 [: N! L' `5 R' K 以下是定时器向上溢出示例代码: C语言: TIM1模块产生向上溢出事件+ _. _; _2 S X //Step1.时钟设置:启动TIM1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);3 s4 b! z c& [& v2 q. Z //Step2.中断NVIC设置:允许中断,设置优先级3 J. V0 ^0 Q' O _ NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQChannel; //更新事件' R2 o; D% T) Q+ t. X5 s NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级0 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级12 \& ]- }! W/ N2 s$ A NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //允许中断 NVIC_Init(&NVIC_InitStructure); //写入设置( x+ S* ]1 Z3 i //Step3.TIM1模块设置 void TIM_Configuration(void) {# n# S: h d/ \! i TIM_TimeBaseInitTypeDef TIM_BaseInitStructure; TIM_OCInitTypeDef TIM_OCInitStructure;7 V6 C0 M1 k+ G //TIM1 使用内部时钟 //TIM_InternalClockConfig(TIM1); //TIM1基本设置 //设置预分频器分频系数71,即APB2=72M, TIM1_CLK=72/72=1MHz //TIM_Period(TIM1_ARR)=1000,计数器向上计数到1000后产生更新事件,计数值归零: _$ Q' {/ O, u& G7 s3 | //向上计数模式1 T/ Y ~4 i2 |$ V+ ? //TIM_RepetitionCounter(TIM1_RCR)=0,每次向上溢出都产生更新事件. F6 D5 ]0 f/ {. R7 M2 H+ e/ b- Y$ `9 J* L TIM_BaseInitStructure.TIM_Period = 1000; TIM_BaseInitStructure.TIM_Prescaler = 71;1 b# B, P j. N, m# z( q5 b3 ^ TIM_BaseInitStructure.TIM_ClockDivision = 0; TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;' |! ~' A8 ?( U TIM_BaseInitStructure.TIM_RepetitionCounter = 0;. v9 ~$ N/ Q d9 S8 I TIM_TimeBaseInit(TIM1, &TIM_BaseInitStructure); //清中断,以免一启用中断后立即产生中断7 d" X3 M) b( r1 a* S TIM_ClearFlag(TIM1, TIM_FLAG_Update); //使能TIM1中断源 TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); //TIM1总开关:开启+ Q7 O9 f% f0 t% J8 u) a TIM_Cmd(TIM1, ENABLE); } //Step4.中断服务子程序:! x" M' y2 S/ K void TIM1_UP_IRQHandler(void)$ r7 q+ y0 z1 f# \1 r" u* O {/ O' k* r/ a: d4 R GPIOC->ODR ^= (1<<4); //闪灯7 b1 l8 L( p2 O; X/ o+ j5 G TIM_ClearITPendingBit(TIM1, TIM_FLAG_Update); //清中断 } 下面是输出比较功能实现TIM1_CH1管脚输出指定频率的脉冲: C语言: TIM1模块实现输出比较,自动翻转并触发中断5 l7 E0 x+ m: f6 F5 ~ //Step1.启动TIM1,同时还要注意给相应功能管脚启动时钟5 p* s0 i1 T. x0 r RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);/ A. \* X) P0 ], l RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //Step2. PA.8口设置为TIM1的OC1输出口4 ?$ B& z$ f% g1 G8 b. o7 { GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;! m( ~; N& B9 w( p0 C8 \9 ] GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;2 n& z3 x# m9 X$ C* C6 b4 Y# ] GPIO_Init(GPIOA, &GPIO_InitStructure);$ [6 X# O2 H$ j2 @: r1 Z //Step3.使能TIM1的输出比较匹配中断$ i; e+ i% r) e, F" Y NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQChannel;/ D1 o+ a* u1 P NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;. i# i c$ \- [6 M* u" w+ m9 H NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;/ J. k, f7 [1 |6 ^+ i4 ^ NVIC_Init(&NVIC_InitStructure); //Step4. TIM模块设置' C4 q; m* u. D. [. K void TIM_Configuration(void) { TIM_TimeBaseInitTypeDefTIM_BaseInitStructure; TIM_OCInitTypeDefTIM_OCInitStructure; //TIM1基本计数器设置 TIM_BaseInitStructure.TIM_Period =0xffff; //这里必须是65535+ ~! k! i9 q8 r" p7 h TIM_BaseInitStructure.TIM_Prescaler= 71; //预分频71,即72分频,得1M TIM_BaseInitStructure.TIM_ClockDivision = 0; //时钟分屏; x/ v* m m$ }0 j( h TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数模式为往上计算 TIM_BaseInitStructure.TIM_RepetitionCounter = 0; //重复的次数为0,也即是只计算一次 TIM_TimeBaseInit(TIM1, &TIM_BaseInitStructure); //写入配置 //TIM1_OC1模块设置 TIM_OCStructInit(&TIM_OCInitStructure);9 R6 G. b* Q( y2 \ TIM_OCInitStructure.TIM_OCMode =TIM_OCMode_Toggle; //管脚输出模式:翻转 TIM_OCInitStructure.TIM_Pulse =2000; //翻转周期:2000个脉冲 TIM_OCInitStructure.TIM_OutputState= TIM_OutputState_Enable; //使能TIM1_CH1通道 TIM_OCInitStructure.TIM_OCPolarity =TIM_OCPolarity_High; //输出为正逻辑 TIM_OC1Init(TIM1,&TIM_OCInitStructure); //写入配置! V. x0 v: }/ A+ g! g/ G& p //清中断' _1 \) D9 Y N+ H9 s9 s0 t TIM_ClearFlag(TIM1, TIM_FLAG_CC1); //TIM1中断源设置,开启相应通道的捕捉比较中断 TIM_ITConfig(TIM1, TIM_IT_CC1,ENABLE); C9 A1 _ c8 X //TIM1开启4 B. t" T9 j2 h8 j+ I% b5 g) u TIM_Cmd(TIM1, ENABLE); //通道输出使能: K C! L# y8 I/ Z. A, E8 V% v TIM_CtrlPWMOutputs(TIM1, ENABLE);! A5 u+ {" O/ _7 m5 D6 } } Step5.中断服务子程序 g1 T" ^# h* C* Q. i void TIM1_CC_IRQHandler(void) { u16 capture;* z L# C+ Y1 r( A$ I1 G$ {8 f if(TIM_GetITStatus(TIM1, TIM_IT_CC1)== SET)6 b& @4 L5 E2 A* t0 g c {5 b8 V0 r3 j1 J TIM_ClearITPendingBit(TIM1,TIM_IT_CC1 ); capture = TIM_GetCapture1(TIM1); TIM_SetCompare1(TIM1, capture +2000); //这里解释下:$ f3 ~9 y' Z" q h! h //将TIM1_CCR1的值增加2000,使得下一个TIM事件也需要2000个脉冲, x4 }. m) F! q //另一种方式是清零脉冲计数器 //TIM_SetCounter(TIM2,0x0000);5 J3 v) G* }' [# V5 ]( k; b3 e" n0 P } } 关于TIM的操作,要注意的是STM32处理器因为低功耗的需要,各模块需要分别独立开启时钟,所以,一定不要忘记给用到的模块和管脚使能时钟,因为这个原因,浪费了我好多时间阿~~! 九九的STM32笔记(二)TIM模块产生PWM$ W4 f+ U* b, t: O9 o: c% z 这个是STM32的PWM输出模式,STM32的TIM1模块是增强型的定时器模块,天生就是为电机控制而生,可以产生3组6路PWM,同时每组2路PWM为互补,并可以带有死区,可以用来驱动H桥。 下面的代码,是利用TIM1模块的1、2通道产生一共4路PWM的代码例子,类似代码也可以参考ST的固件库中相应example C语言: TIM1模块产生PWM,带死区/ C' N. Q. l7 ^# p //Step1.开启TIM和相应端口时钟 //启动GPIO RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | \ RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD,\ ENABLE);* X( _+ \, U% j z, ` //启动AFIO RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //启动TIM1/ \# |, B8 d4 [' s! p( y RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //Step2. GPIO做相应设置,为AF输出 //PA.8/9口设置为TIM1的OC1输出口 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;$ X! m# }- M) O GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;2 d! R W* F- Q2 ^1 i5 i- m" R% F7 A7 q GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;4 c+ U- E7 _. V& {' V GPIO_Init(GPIOA, &GPIO_InitStructure); //PB.13/14口设置为TIM1_CH1N和TIM1_CH2N输出口" P4 w. k, h2 h8 Q: y$ _ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;7 X. t- M- l1 j3 V- q7 } GPIO_Init(GPIOB, &GPIO_InitStructure);# W d3 R. V( x //Step3. TIM模块初始化- H2 n( o( B$ w- F% T7 Q void TIM_Configuration(void)5 P a- b% [0 i { TIM_TimeBaseInitTypeDefTIM_BaseInitStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_BDTRInitTypeDefTIM_BDTRInitStructure; //TIM1基本计数器设置(设置PWM频率) //频率=TIM1_CLK/(ARR+1) TIM_BaseInitStructure.TIM_Period =1000-1; TIM_BaseInitStructure.TIM_Prescaler= 72-1;+ d" v9 K7 B$ g4 T) T& k5 e6 Y z! P TIM_BaseInitStructure.TIM_ClockDivision= 0; TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;: H5 ~2 ?) S( A: W8 Y1 H. |% Q) C& Z TIM_BaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM1,&TIM_BaseInitStructure);) G0 ~6 A0 [( u2 \ //启用ARR的影子寄存器(直到产生更新事件才更改设置) TIM_ARRPreloadConfig(TIM1, ENABLE);( A- k( d% f5 @' x; t% S& ^ / K: r3 w% G5 C" u: ]4 [ //TIM1_OC1模块设置(设置1通道占空比)# q. P/ t( y% c2 G' ~ TIM_OCInitStructure.TIM_OCMode =TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState= TIM_OutputState_Enable;/ `1 [" x: [2 n# K TIM_OCInitStructure.TIM_OutputNState= TIM_OutputNState_Enable;; i& }0 L' a2 n0 B TIM_OCInitStructure.TIM_OCPolarity =TIM_OCPolarity_High;4 }$ y3 I; b1 s% R2 w7 W TIM_OCInitStructure.TIM_OCNPolarity= TIM_OCNPolarity_High; TIM_OCInitStructure.TIM_Pulse = 120;- b% Y# P( A+ v$ N0 _8 G4 @8 q8 s TIM_OC1Init(TIM1,&TIM_OCInitStructure); //启用CCR1寄存器的影子寄存器(直到产生更新事件才更改设置) TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable);6 g; h. U7 I& R$ {! V( P //TIM2_OC2模块设置(设置2通道占空比) TIM_OCInitStructure.TIM_OutputState= TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OutputNState= TIM_OutputNState_Enable; TIM_OCInitStructure.TIM_Pulse = 680; TIM_OC2Init(TIM1, &TIM_OCInitStructure);' S2 e7 Y: p1 A( T" r: n4 L //启用CCR2寄存器的影子寄存器(直到产生更新事件才更改设置)& x7 L( s+ b1 Q1 x9 T TIM_OC2PreloadConfig(TIM1,TIM_OCPreload_Enable);9 N6 u( g- U4 y9 J1 f //死区设置" }5 l8 U e: C TIM_BDTRInitStructure.TIM_OSSRState= TIM_OSSRState_Enable; TIM_BDTRInitStructure.TIM_OSSIState= TIM_OSSIState_Enable;" I: @$ x: k, C0 ~% T TIM_BDTRInitStructure.TIM_LOCKLevel= TIM_LOCKLevel_OFF;1 f8 [6 H0 [ t/ F: G TIM_BDTRInitStructure.TIM_DeadTime =0x90; //这里调整死区大小0-0xff TIM_BDTRInitStructure.TIM_Break =TIM_Break_Disable;- @# f0 v- g2 D) ~ TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;0 n- k3 ~9 H# o; ]: Z1 `$ d8 m TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;% x; t" y' a& Q/ g* \( [1 i TIM_BDTRConfig(TIM1,&TIM_BDTRInitStructure); //TIM1开启) r. @' ~: R: C3 X* a+ U TIM_Cmd(TIM1, ENABLE);0 `0 [9 n4 c2 e# x6 v+ N //TIM1_OC通道输出PWM(一定要加)! L+ g+ O9 z, c& y TIM_CtrlPWMOutputs(TIM1, ENABLE); }$ D+ N8 o/ p: m( v# e# W% Z 其实,PWM模块还可以有很多花样可以玩,比方在异常时(如CPU时钟有问题),可以紧急关闭输出,以免发生电路烧毁等严重事故。 死区,简单解释:. e* J' U$ E A' _; Y C: Y. ^7 w 通常,大功率电机、变频器等,末端都是由大功率管、IGBT等元件组成的H桥或3相桥。 每个桥的上半桥和下半桥是是绝对不能同时导通的,但高速的PWM驱动信号在达到功率元件的控制极时,往往会由于各种各样的原因产生延迟的效果,造成某个半桥元件在应该关断时没有关断,造成功率元件烧毁。' ], N, L' v# b 死区就是在上半桥关断后,延迟一段时间再打开下半桥或在下半桥关断后,延迟一段时间再打开上半桥,从而避免功率元件烧毁。这段延迟时间就是死区。(就是上、下半桥的元件都是关断的)死区时间控制在通常的低端单片机所配备的PWM中是没有的。 死区设置详解:& c& \2 U% x1 X: \8 B: W) `( g X- P, @' [+ c6 D% z // 设置刹车特性,死区时间,锁电平,OSSI,OSSR状态和AOE(自动输出使能& K. i# G: C. I4 @- g /* Automatic Output enable, Break, deadtime and lock configuration*/ // TIM1_OSSRState设置在运行模式下非工作状态选项5 N4 P/ y4 c0 G2 N/ _/ C // TIM1_OSSRState_Enable,使能TIM1 OSSR状态 // TIM1_OSSRState_Disable,失能TIM1 OSSR状态' C1 @4 q) F* O- r TIM_BDTRInitStructure.TIM_OSSRState =TIM_OSSRState_Disable;; N$ B/ `3 U) u% J8 D! q# w /*6 e/ m, k( s& G4 Y5 |" n7 C$ [ TIM1_OSSIState设置在运行模式下非工作状态选项。该参数取值见下表。9 D( U) u7 y8 L+ B, x% j TIM1_OSSIState_Enable 使能TIM1 OSSI状态. |. ]9 I- W' H% G) r TIM1_OSSIState_Disable 失能TIM1 OSSI状态, O5 K: u2 q6 P N/ ~ */ TIM_BDTRInitStructure.TIM_OSSIState =TIM_OSSIState_Disable;5 s: v2 L9 M' I) W3 A$ r /*% f- {1 U/ P# g) h: A% x* n TIM1_LOCKLevel设置了锁电平参数。该参数取值见下表。 TIM1_LOCKLevel_OFF 不锁任何位% f" E) x' X% f8 M" k6 o H9 \% A TIM1_LOCKLevel_1 使用锁电平1 B+ ]) z+ O5 b+ ?4 \ TIM1_LOCKLevel_2 使用锁电平2. V% ^9 e6 g1 A3 z8 k& Q TIM1_LOCKLevel_3 使用锁电平3( a- m7 e0 f2 F- h$ Z/ U */7 ^$ [6 G0 E9 X, i+ K8 n1 j& H+ R TIM_BDTRInitStructure.TIM_LOCKLevel =TIM_LOCKLevel_1;$ ` l- _# W% v# i) G // TIM1_DeadTIM1指定了输出打开和关闭状态之间的延时。 w9 \4 \4 z% j; A1 `1 ] k2 E TIM_BDTRInitStructure.TIM_DeadTime = 5;( D: l+ c* @% Y& _% N /* TIM1_Break使能或者失能TIM1刹车输入。该参数取值见下表。1 ?, ^) R6 l6 ?" i7 H o TIM1_Break_Enable 使能TIM1刹车输入0 O2 v+ S: n9 ?% l1 n, r TIM1_Break_Disable 失能TIM1刹车输入 [, z# G0 I: H, v( } */ TIM_BDTRInitStructure.TIM_Break =TIM_Break_Disable;/ K9 b% i% ^0 w% |/ D" C, e /* TIM1_BreakPolarity设置TIM1刹车输入管脚极性。该参数取值见下表。8 T4 m7 m) J4 d c: T, {7 e7 m TIM1_BreakPolarity_Low TIM1刹车输入管脚极性低5 T) i B4 D5 K4 M1 d4 g: d TIM1_BreakPolarity_High TIM1刹车输入管脚极性高 */( E* d/ _0 N2 I TIM_BDTRInitStructure.TIM_BreakPolarity= TIM_BreakPolarity_High; /*: _$ C5 ~* d9 i8 |0 O4 W: Y" T) E TIM1_AutomaticOutput使能或者失能自动输出功能,该参数取值见下表。' k+ m3 g# H+ v6 v2 a. l TIM1_AutomaticOutput_Enable 自动输出功能使能* I3 I: O- u9 v TIM1_AutomaticOutput_Disable 自动输出功能失能4 g0 n" p8 e1 ]0 p */8 U7 |4 N0 d3 X TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Disable; TIM_BDTRConfig(TIM1,&TIM_BDTRInitStructure); // 这条语句可以不执行 // 这条语句可以不执行/ e7 b9 \6 T" L TIM_SelectOutputTrigger(TIM1,TIM_TRGOSource_Update); /* Master Mode selection */ // 这条语句可以不执行+ w- h* l, e3 T TIM_ARRPreloadConfig(TIM1,ENABLE); // 使能TIMx在ARR上的预装载寄存器5 v9 N! v$ O5 o9 ^ TIM_ITConfig(TIM1,TIM_IT_Update, ENABLE); TIM_ITConfig(TIM1,TIM_IT_CC4, ENABLE); TIM_Cmd(TIM1, DISABLE);' B( f {& F1 }: }: C 6 m7 @( [' E: G) B& o, p* _ 5 [ f+ Y0 D, [3 l7 n2 M6 N' g . b3 Y4 c4 K; [ |
微信公众号
手机版