昆仑山

注册

 

发新话题 回复该主题

STM32通用定时器使用详解 [复制链接]

1#
1.通用定时器基本介绍

通用定时器包括TIM2、TIM3、TIM4和TIM5

STM32通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成。

每个定时器都是完全独立的,没有互相共享任何资源。它们可以一起同步操作。

定时器可以进行定时器基本定时,输出4路PWM,输入捕获

本文详细介绍这三个功能并且利用定时器3并且示例代码使用

2.开发环境

开发平台:keil5单片机:STM32FZET6

#3.基本定时功能##3.1定时器时钟来源分析STM32部分时钟树:3.1.1首先我们我们的系统时钟(SYSCLK72MHz)经过AHB分频器给APB1外设,但是APB1外设最大的只能到36Mhz,所以必须要系统时钟的二分频。下面又规定了如果APB1预分频系数为1则频率不变,否则频率X2至定时器27**,**所以定时器27的时钟频率为还是72MHz3.1.2分配给我们定时器的时钟是72MHz,我们可以根据自己的需求再设置定时器的分频,设置它的定时值

/**初始化定时器的时候指定我们分频系数psc,这里是将我们的系统时钟(72MHz)进行分频*然后指定重装载值arr,这个重装载值的意思就是当我们的定时器的计数值达到这个arr时,定时器就会重新装载其他值.例如当我们设置定时器为向上计数时,定时器计数的值等于arr之后就会被清0重新计数*定时器计数的值被重装载一次被就是一个更新(Update)*计算Update时间公式Tout=((arr+1)*(psc+1))/Tclk公式推导详解clk是定时器时钟源,在这里就是72Mhz我们将分配的时钟进行分频,指定分频值为psc,就将我们的Tclk分了psc+1,我们定时器的最终频率就是Tclk/(psc+1)MHz这里的频率的意思就是1s中记Tclk/(psc+1)M个数(1M=10的6次方),每记一个数的时间为(psc+1)/Tclk,很好理解频率的倒数是周期,这里每一个数的周期就是(psc+1)/Tclk秒然后我们从0记到arr就是(arr+1)*(psc+1)/Tclk举例:比如我们设置arr=,psc=我们将72MHz(1M等于10的6次方)分成了(+1)等于Hz就是一秒钟记录数,每记录一个数就是1/秒我们这里记录个数进入定时器更新(+1)*(1/)=1s,也就是1s进入一次更新Update*///简单进行定时器初始化,设置预装载值和分频系数voidMY_TIM3_Init(u16arr,u16psc){//初始化结构体TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;//1.分配时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//2.初始化定时器相关配置TIM_TimeBaseStructure.TIM_Period=arr;TIM_TimeBaseStructure.TIM_Prescaler=psc;/*在这里说一下这个TIM_ClockDivision是设置与进行输入捕获相关的分频设置的这个值不会影响定时器的时钟频率,我们一般设置为TIM_CKD_DIV1,也就是不分频*/TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//向上计数TIM_TimeBaseInit(TIM3,TIM_TimeBaseStructure);//3.打开定时器TIM_Cmd(TIM3,ENABLE);}/******************主函数********************///在主函数中我们可以调用初始化intmain(){//定时器初始化MY_TIM3_Init(,);while(1){//检测更新标志位if(TIM_GetFlagStatus(TIM3,TIM_IT_Update)){//清除标志位TIM_ClearFlag(TIM3,TIM_IT_Update);//....(每隔一秒执行任务)}}}

#4.定时器输出PWM#4.1基本介绍**4.1.1****PWM**是脉冲宽度调制,我们是通过改变**脉冲的宽度**来达到改变**输出电压**的效果,本质上就是调节**占空比**实现的,STM32除了**基本定时器(TIM6,TIM7)不能输出PWM**以外,其它的定时器都具有输出PWM,其中**高级定时器(TIM1和TIM8)**还能输出**7**路PWM,**基本定时器(TIM2,TIM3,TIM4,TIM5)**也可以输出**4**路PWM输出**PWM**是很有用的,比如我们可以通过**控制电机**来玩小车,或者通过输出PWM改变LED的亮度,制造**呼吸灯**等等

4.1.2我们通用定时器能输出PWM的IO口是固定的,虽然我们可以通过重映射可以改变引脚,具体是哪一些IO口我们要通过查阅STM32的参考手册

这里涉及到一个重映射的概念,重映射就是管脚的外设功能映射到另一个管脚,但是不是可以随便映射的,具体对应关系参考手册上的管脚说明。这样优点是可以优化电路设计;扩展功能,减少外设芯片资源

/**定时器3,可产生四路的PWM输出,四个通道分别对应的引脚情况如下TIM3_CH1,TIM3_CH2,TIM3_CH3,TIM3_CH4没有重映像的对应情况A6,PA7,PB0,PB1部分重映像B4,PB5,PB0,PB1完全重映像C6,PC7,PC8,PC9当我们的IO口不仅仅是做普通的输入输出使用的时候,作为别的外设(AD,串口,定时器等)的特定功能引脚,就需要开启外设.这里我们还需要开启APB2外设上的复用时钟AFIO,同时IO口采用的是复用输出!我们这里是没有使用重映射功能.*///宏定义//判断当前是处于哪一种模式,以便于我们初始化IO口#defineNO_REAMP0#definePART_REAMP1#defineFULL_REAMP2//---这里是需要制定的参数//指定这里的当前的模式,我们给她默认指定是没有重映射#defineCURRENT_MODENO_REAMP//*************根据当前模式初始化IO口函数voidMY_TIM3_GPIO_Init(void){GPIO_InitTypeDefGPIO_InitStructure;//1.开启AFIO时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//2.根据当前的重映像的模式配置时钟和初始化相关引脚switch(CURRENT_MODE){//2.1如果没有重映射caseNO_REAMP:{//时钟分配RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA

RCC_APB2Periph_GPIOB,ENABLE);//初始化IO口GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6

GPIO_Pin_7;GPIO_Init(GPIOA,GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0

GPIO_Pin_1;GPIO_Init(GPIOB,GPIO_InitStructure);break;}//2.2部分重映射casePART_REAMP:{//时钟分配RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//初始化IO口GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0

GPIO_Pin_1

GPIO_Pin_4

GPIO_Pin_5;GPIO_Init(GPIOB,GPIO_InitStructure);break;}//2.3全映射caseFULL_REAMP:{//时钟分配RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//初始化IO口GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6

GPIO_Pin_7

GPIO_Pin_8

GPIO_Pin_9;GPIO_Init(GPIOB,GPIO_InitStructure);break;}default:break;}}//*****************定时器PWM输出初始化函数voidMY_TIM3_PWM_Init(u16arr,u16psc){//初始化结构体TIM_OCInitTypeDefTIM_OCInitstrcuture;//1.初始化定时器和相关的IO口MY_TIM3_Init(arr,psc);MY_TIM3_GPIO_Init();//2.初始化PWM的模式/**选择PWM模式WM1模式:向上计数时,当我们当前的计数值小于我们的设置阈值为有效电平,否则为无效电平,向下计数时与向上计数时相反PWM2模式:与PWM1模式向上向下计数时完全相反*/TIM_OCInitstrcuture.TIM_OCMode=TIM_OCMode_PWM1;TIM_OCInitstrcuture.TIM_OutputState=TIM_OutputState_Enable;TIM_OCInitstrcuture.TIM_OCPolarity=TIM_OCPolarity_High;//输出电平为高,也就是有效电平为高TIM_OC1Init(TIM3,TIM_OCInitstrcuture);//这里是设置利用通道1输出//这里只初始化通道1,我们可以根据自己需求初始化其它通道//TIM_OC2Init(TIM3,TIM_OCInitstrcuture);//TIM_OC3Init(TIM3,TIM_OCInitstrcuture);//TIM_OC4Init(TIM3,TIM_OCInitstrcuture);TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable);//使能预装载寄存器}//*********************主函数调用intmain(){//因为我们单片机引脚输出电压3.3V左右,我们设置预装载值为MY_TIM3_PWM_Init(,0);//我们初始化的时候选择的是PWM1模式,当计数值小于我们的设定值时为有效电平,这里是高电平//所以对于的1通道(PA6)电压是大概就是3.3*(/)=1V左右,我们可以用万用表测量TIM_SetCompare1(TIM3,);while(1);}

#5.定时器输入捕获##5.1基本介绍

上面介绍了定时器的四路通道可以输出PWM,同样的我们也可以捕获该定时器这四路通道上的边沿状态(上升沿,下降沿)

由此可见基本定时器也不能进行输入捕获,没有思路通道

我们可以通过输入捕获的来测量高电平脉宽时间,首先捕获到高电平,记录下改时间,然后切换为捕获低电平,得到时间##5.2开发步骤###输入捕获(捕获边沿信号,上升沿和下降沿)首先我们需要以一定的频率检测电平的跳变,然后对部分跳变(也就是部分输入的波形)进行过滤------这就是定时器里面的滤波器的任务

指定输入滤波器时钟频率,首先是系统时钟分给定时器72Mhz,我们首先初始化定时器的时候指定了TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;没有分频,输入给滤波器的时钟频率还是72MHz,TIM_ClockDivision也可以指定为2分频或者4分频

波形过滤(TIM_ICFilter),这里有一个指定过滤器的参数(参考芯片手册),例如我们设置参数为(二进制),采样频率(fsampling)为滤波器频率/2=36Mhz,N=8.当检测到一个上升沿的时候,再以fsampling频率连续8次检测到高电平才确认是一个有效的上升沿,这样可以滤除那些高电平脉宽低于8个采样周期的脉冲信号,从而达到滤高频波的效果。

配置输入分频(TIM_ICPrescaler),如果我们设置不分频,一个边沿(上升沿或者下降沿)就触发一次捕获,二分频就是两次边沿触发捕获,这里这个分频可以为1,2,4,8

//定时器输入捕获初始化voidMY_TIM3_Cap_Init(u16arr,u16psc){//初始化结构体TIM_ICInitTypeDefTIM_ICInitStructure;//1.初始化定时器和相关的IO口MY_TIM3_Init(arr,psc);//这里的IO口根据自己需求改成输入,我这改成下拉输入,具体代码就不展现了MY_TIM3_GPIO_Init();//2.初始化定时器输入捕获TIM_ICInitStructure.TIM_Channel=TIM_Channel_1;//设置输入捕获的通道//不使用过滤器,假设我们想使用,例如上述举例使用//我们就给TIM_ICFilter=0x05,(0000),根据上表可以知道这个值范围(0x00~0x0F)TIM_ICInitStructure.TIM_ICFilter=0x00;TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//上升沿捕获TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;//配置输入分频,这里不分频,1次检测到边沿信号就发生捕获/*这里说一下定时器通道可以进行交叉捕获,通道1捕获通道2引脚上的边沿信号,通道2捕获通道1引脚,通道3可以捕获通道4对应引脚,...但是只能相邻一对可以相互捕获,例如通道2不能捕获通道3引脚边沿信号TIM_ICSelection_DirectTI表示直接捕获,通道1对应通道1引脚,通道2对应通道2引脚TIM_ICSelection_IndirectTI表示进行交叉捕获*/TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;//映射捕获对应通道的引脚TIM_ICInit(TIM3,TIM_ICInitStructure);}//****************主函数intmain(){//初始化输入捕获MY_TIM3_Cap_Init(0,0);while(1){//检测是否捕获到上升沿if(TIM_GetFlagStatus(TIM3,TIM_IT_CC1)){TIM_ClearFlag(TIM3,TIM_IT_CC1);//捕获到上升沿之后的任务...//一般测量高电平脉宽,我们可以先捕获上升沿再捕获下降沿//TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);修改为下降沿捕获}}}

#6.定时器中断1.谈及到中断,我们就必须涉及到NVIC,具体关于NVIC请参考我的另外一篇,这里是直接使用,我们使能定时器3中断并且配置完抢占优先级和响应优先级之后,再在主函数中使能其更新中断和输入捕获中断

//使能更新中断和输入捕获通道1的中断TIM_ITConfig(TIM3,TIM_IT_Update

TIM_IT_CC1,ENABLE);

2.我们使用中断的一个主要目的就是能够及时处理信息,不用在主函数的while循环里面等待

//定时器3的中断处理函数voidTIM3_IRQHandler(void){//1.判断是什么中断//1.1定时器更新中断if(TIM_GetITStatus(TIM3,TIM_IT_Update)){//...处理定时器更新之后任务}//1.2如果是定时器通道1的捕获中断elseif(TIM_GetITStatus(TIM3,TIM_IT_CC1)){//处理输入捕获之后的任务//TIM_OC1PolarityConfig(TIM3,TIM_ICPolarity_Falling);更改为下降沿捕获}//2.最后将中断标志位都清理掉TIM_ClearITPendingBit(TIM3,TIM_IT_Update

TIM_IT_CC1);}

By鱼芯工作室

最后

若觉得文章不错,转发分享,也是我们继续更新的动力。

资源大放送!包括但不限于:STM32、51单片机、Arduino、MSP/MSP、PCB设计、C语言、项目分享、等等!在
分享 转发
TOP
发新话题 回复该主题