厦门外贸建站,网页制作站点,做网站用什么域名好,网站预约挂号怎么做定时器分类
STM32F1中除了互联型产品#xff08;STM32F103C8T6为64KB Flash 中容量产品#xff09;#xff0c;其余有8个定时器。
可以8个定时器分为高级#xff0c;通用#xff0c;基本三种。
高级定时器有两个#xff0c;分别是TIM1和TIM8。
通用定时器有四个…定时器分类
STM32F1中除了互联型产品STM32F103C8T6为64KB Flash 中容量产品其余有8个定时器。
可以8个定时器分为高级通用基本三种。
高级定时器有两个分别是TIM1和TIM8。
通用定时器有四个是TIMxx2~4
基本定时器有两个是TIMxx6~7
功能上高级通用基本
不过高级定时器一般也用不着如果只是普通的计时的话基本定时器也就够用了但是基本定时器没有输出比较没法实现PWM脉冲宽度调制因此我们主要使用的是通用定时器并且STM32F103中通用定时器的资源也是最多的。
下图截自《ARM Cortex-M3嵌入式原理及应用基于STM32F103微控制器》的第150页。 通用定时器 下图截自《STM32F10xxx参考手册中文》第253页 不太严谨地介绍一下定时器的工作流程。时钟源每传来nn预分频器的值次脉冲都会使得计数器工作一次。如果选择的是向上计数模式那么计数器每工作一次都会使得计数器的值1直到值等于了自动重装载寄存器的值那么触发一次中断并且计数器清零。
如果选择的是向下计数模式那么计数器每工作一次都会使得计数器的值-1直到值等于0那么触发一次中断并且计数器的值变为自动重装载寄存器的值。
如果选择向上向下计数模式那么计数器工作一次会使得计数器的值1或者-1计数器的值不会被重置会一直在0和自动重装载寄存器的值之间上下跳动。
一般情况下这三种工作模式对于计数的效果都是一样的但是当我们需要利用到计数器里的值的时候就有差别了例如PWM通常我们使用的是向上计数模式这也比较符合我们的直觉。 上述的介绍中提到的是以下三个寄存器。 可以看出它们都是只有16位也就是说可以设置的最大数值为2^16-165535。
并且从工作流程中也可以得知定时器溢出的频率公式为
时钟源频率/(自动重装载寄存器的值1)/(预分频器的值1)CK_INT/(ARR1)/(PSC1)
PWM脉冲宽度调制
STM32F103中每个通用定时器都可以输出4路PWM而每个高级定时器都可以输出7路PWM所以理论上STM32最多是可以同时产生30路PWM。
PWM简单来说就是一段有高电平也有低电平的脉冲信号。
我们一般点亮一个LED灯就是把LED灯的短脚接地然后通过GPIO口从LED灯的长脚传入高电平我们假设此刻LED的亮度为100。如果我们改用PWM来代替原本的高电平并且PWM传入的脉冲信号中高低电平各占50%那么此刻LED的亮度就为50%了因为人眼大概只能接受每秒25帧左右的图像因此只要PWM够快那么我们就看不出LED实际上是亮灭亮灭的由于人眼的残留影像我们看到的只是LED灯变得暗了些。
之所以把PWM和定时器放在一起是因为定时器的硬件中就含有捕获比较寄存器。
我们可以给这个寄存器设置一个阈值当定时器的计数器的值小于这个阈值的时候我们就输出高电平反之输出低电平具体看选择的PWM输出模式。
综上我们可以得知PWM输出的频率等于定时器的溢出中断频率。
固件库函数
定时器相关的函数还是非常多的接下来就以计数1ms为目的来说明相关函数。
首先需要打开相关定时器的外设时钟不过不属于定时器库函数里的就不介绍。
下一步选择时钟源默认是使用内部时钟源72MHz所以也可以不指定。
然后初始化时基单元配置自动重装载寄存器预分频器时钟分频计数模式
接着是中断使能以及配置NVCI相关的中断优先级
最后是使能计数器就可以开始计数了。
TIM_InternalClockConfig 上面三个函数是常用的用来指定时钟源的函数。
第一个也是默认的使用的是内部时钟作为时钟源。
第二个可以将其他定时器的溢出信号作为时钟源也就是两个定时器联动这样可以将最大的计时数平方一次也就是可以记更久的时间不过我们也是可以使用软件来完成这种效果的。
第三个可以将时钟源接到外部的时钟。 TIM_TimeBaseInit 初始化时基单元参数一指定定时器资源参数二传入一个TIM_TimeBaseInitTyepDef类型的结构体变量用于初始化配置。 TIM_Prescale预分频器的值0~65535。
TIM_CounteMode计数器模式一般使用向上计数TIM_CounterMode_Up
TIM_Period自动重装载计数器的值0~65535。
TIM_ClockDivision时钟分频TIM_CKD_DIV1。
TIM_RepetitionCounter重复计数器的值只有高级定时器才用的到基本定时器和通用定时器随便给个0就行。
TIM_ITConfig 中断使能参数一选择定时器资源参数二选择中断源参数三给个ENABLE。 中断源一般就指定第一个TIM_IT_Update
这样每计数一轮都会触发一次中断。
TIM_Cmd 使能计数器。
使能之后计数器就开始运作了每次记满一轮之后就会进入一次中断我们可以从“startup_stm32f10x_md.s”中找到对应的中断函数。 由于可进入这个中断函数的中断源有多个参考上面中断使能的参数表因为我们需要查询中断标志位是否是我们所规定的TIM_IT_Update触发的是的话我们还需要手动把标志位清除其他中断源也是一样。
TIM_GetITStatus 查询中断标志位参数二给的一样的TIM_IT_Update。
TIM_ClearITPendingBit 清除中断标志位参数二给的一样的TIM_IT_Update。
上面的函数就是用来计数用的接下来就是如何输出PWM的了。
把上面计数的流程走一遍然后把中断相关的都删掉再加上配置输出比较单元以及初始化GPIO口的函数即可。
TIM_OC1Init 配置通道1的输出比较单元。这里需要注意的是不同定时器资源的不同通道对应的GPIO输出口是不一样的TIM2的通道1对应的是GPIOA的0号引脚具体可以查询引脚定义表。并且需要配置为复用推挽输出模式GPIO_Mode_AF_PP。 参数一指定定时器资源参数二传入TIM_OCInitTypeDef类型的参数。 一共有八个成员变量但是我们只需要用到我圈起来的4个其余为高级定时器使用的。
TIM_OCMode设置输出比较的模式一般我们选择TIM_OCMode_PWM1在这个模式下当计数器的值小于阈值时输出设置的极性下面会设置反之输出相反的极性。
TIM_OCPolarity输出比较的极性这个根据需求来设置设置为高电平的参数为TIM_OCPolarity_High。
TIM_OutputState设置输出使能TIM_OutputState_Enable。
TIM_Pulse设置阈值0~65535。因此我们定时器的自动重装载寄存器的值最好不要超过这个阈值并且由于容易计算PWM的占空比一般我会把自动重装载寄存器的值设为10的倍数。
以上就足够我们输出PWM了如果我们需要实现呼吸灯的效果那么还需要动态地调整PWM的阈值。
TIM_SetCompare1 设置通道1的输出比较阈值。
参数一指定定时器资源参数二重新设置阈值大小。
通用定时器计数1ms代码
#include stm32f10x.h // Device header
#include OLED.huint32_t count0;int main(void){OLED_Init();RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //打开TIM2的外设时钟TIM_InternalClockConfig(TIM2); //选择内部时钟(72MHz)作为时钟源TIM_TimeBaseInitTypeDef itd;itd.TIM_ClockDivisionTIM_CKD_DIV1; //时钟1分频itd.TIM_CounterModeTIM_CounterMode_Up; //向上计数模式itd.TIM_Period1000-1; //设置自动重装器的值itd.TIM_Prescaler72-1; //设置预分频器的值//TIM2的计数器溢出频率(每秒中断次数)为//(72MHz/1(1分频)/(1000-11)(自动重装器的值1)/(72-11)(预分频器的值1))1000(ms级,即1ms溢出一次)itd.TIM_RepetitionCounter0; //重复计数器的值,但是仅高级定时器有效TIM_TimeBaseInit(TIM2,itd);TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //开启定时器中断,TIM为中断源//配置中断优先级NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //抢占优先级和响应优先级各占两位NVIC_InitTypeDef itd1;itd1.NVIC_IRQChannelTIM2_IRQn; //指定TIM2的中断通道itd1.NVIC_IRQChannelCmdENABLE;itd1.NVIC_IRQChannelPreemptionPriority2; //抢占优先级为2itd1.NVIC_IRQChannelSubPriority2; //响应优先级为2NVIC_Init(itd1);TIM_Cmd(TIM2,ENABLE); //使能定时器while(1){OLED_ShowNum(2,1,count,6);}
}void TIM2_IRQHandler(void){if(TIM_GetITStatus(TIM2,TIM_FLAG_Update)){ //判断是否是TIM中断源引发的中断count; TIM_ClearITPendingBit(TIM2,TIM_FLAG_Update); //清除标志位}
}
效果 通用定时器使用PWM实现LED呼吸灯
#include stm32f10x.h // Device header
#include Delay.hint main(void){RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //打开TIM2的外设时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef gitd;gitd.GPIO_ModeGPIO_Mode_AF_PP; //配置为复用推挽输出gitd.GPIO_PinGPIO_Pin_0;gitd.GPIO_SpeedGPIO_Speed_2MHz;GPIO_Init(GPIOA,gitd);TIM_InternalClockConfig(TIM2); //选择内部时钟(72MHz)作为时钟源TIM_TimeBaseInitTypeDef itd;itd.TIM_ClockDivisionTIM_CKD_DIV1; //时钟1分频itd.TIM_CounterModeTIM_CounterMode_Up; //向上计数模式itd.TIM_Period100-1; //设置自动重装器的值itd.TIM_Prescaler72-1; //设置预分频器的值itd.TIM_RepetitionCounter0; //重复计数器的值,但是仅高级定时器有效TIM_TimeBaseInit(TIM2,itd);TIM_OCInitTypeDef itd1;itd1.TIM_OCMode TIM_OCMode_PWM1; //比较输出模式为PWM1itd1.TIM_OCPolarity TIM_OCPolarity_High; //输出极性为高电平itd1.TIM_OutputStateTIM_OutputState_Enable; //使能itd1.TIM_Pulse0; //初始化输出比较的阈值 TIM_OC1Init(TIM2,itd1);TIM_Cmd(TIM2,ENABLE); //使能定时器while(1){for(int i0;i100;i){ TIM_SetCompare1(TIM2,i);Delay_ms(10);}for(int i100;i0;--i){TIM_SetCompare1(TIM2,i);Delay_ms(10);}}
}效果 参考
《STM32F10xxx参考手册中文》
《STM32F103xx固件函数库用户手册》
b站江科大自化协
《STM32库开发实战指南基于STM32F103》
《ARM Cortex-M3嵌入式原理及应用基于STM32F103微控制器》