|
本帖最后由 大碗刚 于 2017-12-27 09:01 编辑 ' }& _ \7 m7 D# f& @% X 转载于whsh304的博客3 u! E# C% q8 g3 P& x& X STM32 普通定时器
STM32 系列的 CPU,有多达 8 个定时器,其中 TIM1 和 TIM8 是能够产生三对 PWM 互补输出的高级定时器,常用于三相电机的驱动,它们的时钟由 APB2的输出产生。其它 6 个为普通定时器,时钟由 APB1 的输出产生。如图1. 当 APB1 的预分频系数为 1 时,这个倍频器不起作用,定时器的时钟频率等于 APB1 的频率;当 APB1 的预分频系数为其它数值(即预分频系数为 2、4、8 或 16)时,这个倍频器起作用,定时器的时钟频率等于 APB1 的频率两倍。 当 AHB=72MHz 时,APB1 的预分频系数必须大于 2,因为APB1 的最大频率只能为 36MHz。如果 APB1 的预分频系数=2,则因为这个倍频器,TIM2~7 仍然能够得到 72MHz 的时钟频率。 一、通用定时器 定时器编程,就是中断的编程。因为使用定时器必定要使用到中断(pwm没用到中断)。 步骤一 系统配置 SystemInit();,包括时钟 RCC 的配置,倍频到 72MHZ。 步骤二 GPIO 的配置,使用函数为 GPIO_Config(); 输出LED对应的引脚以及输出模式 步骤三 嵌套中断控制器的配置 关键点是NVIC_InitStructure.NVIC_IRQChannel = TIMx_IRQChannel; //通道 其它的配置都差不多。 步骤四 定时器的初始化配置,使用 Timer_Config();。OK,关键部分出来了。(来自芯达) 我们来看下实现过程: void Timer_Config(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);! b% I( Q& ^+ \( u5 N; z O TIM_DeInit(TIM2); TIM_TimeBaseStructure.TIM_Period=2000; //自动重装载寄存器的值+ z) g: y9 b( d! Q& b$ u3 V- t TIM_TimeBaseStructure.TIM_Prescaler= (36000 - 1); //时钟预分频数# k9 q0 l+ G8 f1 P TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; //采样分频 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_ClearFlag(TIM2, TIM_FLAG_Update);+ {% Q) M. o3 H3 y7 R" l- o //清除溢出中断标志) i2 k4 ^3 r3 W- Y TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);3 n5 a3 v( {" ?3 g, z TIM_Cmd(TIM2, ENABLE);& j/ R' s* y; k% y /开启时钟 }+ h' [3 f" \- }* {7 @3 @1 V: B 我们每个语句都来解释一下。首先我们想使用定时器,就必须使能定时器的时钟,这就是函 CC_APB1PeriphClockCmd(); , 通 过 它 开 启CC_APB1Periph_TIM2。TIM_DeInit( TIM2); 该函数主要用于复位 TIM2 定时器,使之进入初始状态。之后我们对自动重装载寄存器赋值,TIM_Period 的大小实际上表示的是需要经过 TIM_Period 次计数后才会发生一次更新或中断。接下来需要设置时钟预分频数IM_Prescaler,这里有一个公式,我们举例来说明:例如时钟频率=72MHZ/(时钟预分频+1)。说明当前设置的这个 TIM_Prescaler,直接决定定时器的时钟频率。通俗点说,就是一秒钟能计数多少次。比如算出来的时钟频率是 2000,也就是一秒钟会计数 2000 次,而此时如果 TIM_Period 设置为 4000,即 4000 次计数后就会中断一次。由于时钟频率是一秒钟计数 2000 次,因此只要 2 秒钟,就会中断一次。 步骤五 编写中断服务程序。同样需要注意的,一进入中断服务程序,第一步要做的,就是清除掉中断标志位。由于我们使用的是向上溢出模式,因此使用的函数应该是:TIM_ClearITPendingBit(TIM2 ,TIM_FLAG_Update);。芯达 STM32开发板实现的中断服务程序如下: void TIM2_IRQHandler(void)* X5 f3 |( d, `8 z3 l { if ( TIM_GetITStatus(TIM2 , TIM_IT_Update) != RESET ) { TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update);9 x/ e$ K$ B- t switch(state){% S: ~+ s: k% s/ l. v4 A9 Z i case 0:& Z( o( W) K! o. Y2 Z- j ; o# Q' @! g4 t# u/ x( | GPIO_ResetBits(GPIOB , GPIO_Pin_8);* R# ~8 q& I, Y, ~ GPIO_SetBits(GPIOB, GPIO_Pin_9); GPIO_SetBits(GPIOE, GPIO_Pin_0);8 I; }) D5 b l9 Q% }# T9 B GPIO_SetBits(GPIOE, GPIO_Pin_1); break;; i3 p) K6 G) n ... default: break; } if(++state >= 4){ //state在中断函数中定义为全局变量 state = 0; } } } 二、输出比较模式 1. 选择计数器时钟(内部,外部,预分频器) 2. 将相应的数据写入TIMx_ARR和TIMx_CCRx寄存器中 3. 如果要产生一个中断请求和/或一个DMA请求,设置CCxIE位和/或CCxDE位。 4. 选择输出模式,例如:必须设置OCxM=’011’、OCxPE=’0’、CCxP=’0’和CCxE=’1’,当计数器CNT与CCRx匹配时翻转OCx的输出管脚,CCRx预装载未用,开启OCx输出且高电平有效。! ]3 W# c& ?0 _" T, M! C 5. 设置TIMx_CR1寄存器的CEN位启动计数器: a$ H; |4 `3 @# G TIMx_CCRx寄存器能够在任何时候通过软件进行更新以控制输出波形,条件是未使用预装载寄存器(OCxPE=’0’,否则TIMx_CCRx影子寄存器只能在发生下一次更新事件时被更新)。下图给出了一个例子。入图2. file:///C:\Users\lenovo\AppData\Roaming\Tencent\Users\974670111\QQ\WinTemp\RichOle\JR_$B}_I4UT8XL]KINAK%$1.png9 N3 Q- R( Z/ |) {! ?. a 程序:(库里有提供) int main(void) {# e! Q" |) K/ b8 }! M: e" y8 F X' P: p1 Y* J8 K7 E$ K5 { RCC_Configuration(); NVIC_Configuration(); GPIO_Configuration(); 3 n7 R5 Q+ R8 m3 U: D TIM_TimeBaseStructure.TIM_Period = 65535;! [5 ~; b6 c, F" F( o TIM_TimeBaseStructure.TIM_Prescaler =0; //时钟频率 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); / f; u5 p* ?; M( L TIM_PrescalerConfig(TIM3, 4, TIM_PSCReloadMode_Immediate); // TIM 预分频值即时装入 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing; //TIM_OCMode 选择定时器模式:TIM 输出比较时间模式# R& {# @4 n4 g2 u- k* q6 `9 f. x TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = CCR3_Val; //设置了待装入捕获比较寄存器的脉冲值. C- i/ k$ h- q- P& _! A4 G/ X TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // TIM 输出比较极性高 TIM_OC3Init(TIM3, &TIM_OCInitStructure); //根据 TIM_OCInitStruct 中指定的参数初始化外设 TIMx TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Disable); //失能 TIMx 在 CCR3 上的预装载寄存器 ( c$ M9 O3 y0 e3 X4 {; q) q TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = CCR4_Val; TIM_OC4Init(TIM3, &TIM_OCInitStructure); //根据 TIM_OC4InitStruct 中指定的参数初始化外设 TIM3 TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Disable); //失能 TIMx 在 CCR4 上的预装载寄存器 TIM_ITConfig(TIM3, TIM_IT_CC3 | TIM_IT_CC4 , ENABLE); & P8 p3 \$ c7 x$ I; _- A- K8 M* x TIM_Cmd(TIM3, ENABLE); while (1);1 A4 G2 T" t% w% v0 l } 中断it.c中 void TIM3_IRQHandler(void)2 L1 n( ^8 m# p! B, K% F9 i {* t) N9 f, G2 @5 B if (TIM_GetITStatus(TIM3, TIM_IT_CC3) != RESET)1 m, k* n- U7 R. Y8 J- Z { TIM_ClearITPendingBit(TIM3, TIM_IT_CC3); ( g6 |8 C8 F5 y+ g% W* | GPIO_WriteBit(GPIOB, GPIO_Pin_0, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_0)));//设置或者清除指定的数据端口位:取反5 k+ j6 V! z- T1 [3 X capture = TIM_GetCapture3(TIM3); //获得 TIM3 输入捕获 3 的值 TIM_SetCompare3(TIM3, capture + CCR3_Val); //设置 TIM3 捕获比较 3 寄存器值) R9 L: D/ Z* A% C* Y# q }( h# S3 I7 G4 F b else if (TIM_GetITStatus(TIM3, TIM_IT_CC4) != RESET); w* ^- I4 n- W. ~$ U {6 a1 @8 M7 l) D6 k2 P4 r- G1 J TIM_ClearITPendingBit(TIM3, TIM_IT_CC4); / T' @" V+ b$ D: @6 `$ P% ^ GPIO_WriteBit(GPIOB, GPIO_Pin_1, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_1)));% K: a5 X* t- c7 |2 ?5 Q capture = TIM_GetCapture4(TIM3); TIM_SetCompare4(TIM3, capture + CCR4_Val);. ~3 r2 ~% m- H8 C8 Q } u% ~* B8 o6 A( d } 三、PWM输出 脉冲宽度调制模式可以产生一个由TIMx_ARR寄存器确定频率、由TIMx_CCRx寄存器确定占空比的信号。 在TIMx_CCMRx寄存器中的OCxM位写入’110’(PWM模式1)或’111’(PWM模式2),能够独立地设置每个OCx输出通道产生一路PWM。必须设置TIMx_CCMRx寄存器OCxPE位以使能相应的预装载寄存器,最后还要设置TIMx_CR1寄存器的ARPE位使能自动重装载的预装载寄存器。 OCx的极性可以通过软件在TIMx_CCER寄存器中的CCxP位设置,它可以设置为高电平有效活或低电平有效。TIMx_CCER寄存器中的CCxE位控制OCx输出使能。 在PWM模式(模式1或模式2)下,TIMx_CNT和TIM1_CCRx始终在进行比较,(依据计数器的计数方向)以确定是否符合TIM1_CCRx≤TIM1_CNT或者TIM1_CNT≤TIM1_CCRx。如图3. 程序: int main(void) { 3 ?: V, z3 o5 [2 Q- u4 ?3 P RCC_Configuration(); GPIO_Configuration(); & N; V! G( F2 z. |" h/ v TIM_TimeBaseStructure.TIM_Period = 999; //在下一个更新事件装入活动的自动重装载寄存器周期的值5 e% ^8 W! `" b TIM_TimeBaseStructure.TIM_Prescaler = 0; // 设置了用来作为 TIMx 时钟频率除数的预分频值8 x- ?9 R/ m+ E$ }1 K5 p TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置了时钟分割 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; " S1 Z0 B, a# k% S- m1 y TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //TIM_OCMode TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = CCR3_Val; //设置了待装入捕获比较寄存器的脉冲值 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//TIM_OCPolarity :TIM 输出比较极性高! D8 C9 h; P. b. Z: [ TIM_OC3Init(TIM3, &TIM_OCInitStructure); TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能或者失能 TIM3 在 CCR3 上的预装载寄存器 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; X& i$ R! H0 J# k2 N' G/ e TIM_OCInitStructure.TIM_Pulse = CCR4_Val; TIM_OC4Init(TIM3, &TIM_OCInitStructure); TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); 3 ]4 A3 ~; }6 M- f( }) E* A: ? TIM_Cmd(TIM3, ENABLE); while (1)6 f4 {! o d8 d% E; b) r4 U4 a {} } 它不需要中断程序。输出的是占空比为50%和25%的方波信号。 . z W! p. y+ Z3 \0 `3 |! s |
微信公众号
手机版