赞
踩
目录
定时器作为STM32的基础外设,配置还是比较复杂的,本章主要从定时器的使用,结合寄存器的配置和常用实例来理解定时器。
STM32的定时器主要分为三种:高级定时器(TIM1/TIM8)、通用定时器(TIM2-TIM5)、基本定时器(TIM6/TIM7),基本功能不多做介绍,到处都有。
看起来比较复杂,可以分为四个部分:1定时器时钟、2时基单元、3捕获输入、4PWM输出。
定时器时钟来源有四种:
①内部时钟(CK_INT)。一般不做其他配置,默认为此时钟。
②外部时钟模式:外部触发输入(ETR)
③内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。
定时器的触发可以是以上两种触发模式,也可以是这种通过一个定时器触发另一个定时器的工作方式。发出触发信号的定时器工作于主模式,接受触发信号而启动的定时器工作于从模式。
定时器之间相互联系的主要通过配置TIMx_SMCR寄存器中的TS比特位【ITR0 ITR1 ITR2 ITR3】来设置。
例如,如果要用TIM4去触发TIM2,需要将TIM2的Internal Triger配置成ITR3
④外部时钟模式:外部输入脚(TIx)
可以参考:(121条消息) 定时器定时中断&定时器外部时钟_tim_etrclockmode2config_tz得像个小孩的博客-CSDN博客
关于时钟来源不做展开讲解,可以在应用实例中体会。
时基单元就是定时器框图的第二部分,它包括三个寄存器:计数器寄存器(TIMx_CNT)、预分频器寄存器(TIMx_PSC)和自动装载寄存器(TIMx_ARR)。对这三个寄存器的介绍如下:
计数器寄存器(TIMx_CNT)
向上计数、向下计数或者中心对齐计数;
预分频器寄存器(TIMx_PSC)
可将时钟频率按1到65535之间的任意值进行分频,可在运行时改变其设置值;
自动装载寄存器(TIMx_ARR)
此处有关介绍信息比较多,也比较简单,不多做介绍。
IC1、2和IC3、4可以分别通过软件设置将其映射到TI1、TI2和TI3、TI4;
4个16位捕捉比较寄存器可以编程用于存放检测到对应的每一次输入捕捉时计数器的值;
当产生一次捕捉,相应的CCxIF标志位被置1;同时如果中断或DMA请求使能,则产生中断或DMA请求。
如果当CCxIF标志位已经为1,当又产生一个捕捉,则捕捉溢出标志位CCxOF将被置1
PWM,英文名Pulse Width Modulation,是脉冲宽度调制缩写,它是通过对一系列脉冲的宽度进行调制,等效出所需要的波形。
在STM32单片机中,可以使用定时器的输出比较功能来产生PWM波:
即PWM模式可以产生一个由TIMx_ARR寄存器确定频率、由TIMx_CCRx寄存器确定占空比的信号。
以上为定时器框架的基本介绍,下面主要通过实例分解来详细学习定时器。
程序实例:STM32F407的TIM1定时800ms,800ms产生一次溢出中断
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = 7999; // 自动重装(TIMx_ARR)
TIM_TimeBaseStructure.TIM_Prescaler =16799; //800ms 预分频(TIMx_PSC)
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 计数模式TIMx->CR1
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); TIMx->CR1
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); //允许更新中断 TIMx->DIER
TIM_SetCounter(TIM1,0); //设置计数初始值
TIM_ClearFlag(TIM1,TIM_IT_Update); //清除更新中断标志
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_TIM10_IRQn; //打开定时器中断的通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM1, ENABLE); //使能计数器 TIMx_CR1 -> CEN
配置中断服务函数: void TIM1_UP_TIM10_IRQHandler(void)
{
if(TIM_GetITStatus(TIM1, TIM_IT_Update) == SET)
{
/*****处理事件********/
TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
}
}
从定时器的框图和配置步骤来学习:
(1)RCC开启时钟; (2)选择时基单元的时钟源,对于定时中断选择内部时钟源TIM_InternalClockConfig(TIM_TypeDef* TIMx);默认可以省略。 (3)配置时基单元。包括预分频(TIMx_PSC)、自动重装(TIMx_ARR)、计数模式(TIMx_CNT)等 (4)配置输出中断控制允许更新中断输出到NVIC (5)配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级 (6)运行控制,配置完成后,需要对计数器进行使能,否则计数器无法运行
计数器更新时,触发中断,最后再写一个定时器中断服务函数。
a、实例:TIM3比较通道3,通道4输出PWM
void PWM_Iint(void)
{ GPIO_InitTypeDef GPIO_InitStructure TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_Init(GPIOB, &GPIO_InitStructure); //先配置IO复用推挽输出
GPIO_PinAFConfig(GPIOB, GPIO_PinSource0, GPIO_AF_TIM3); //PB0映射为TIM3_ch3
GPIO_PinAFConfig(GPIOB, GPIO_PinSource1, GPIO_AF_TIM3); //PB1映射为TIM3_ch4
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = 60000; //10HZ
TIM_TimeBaseStructure.TIM_Prescaler =139;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //设置PWM输出模式
TIM_OCInitStructure.TIM_Pulse =0; //占空比初始值
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC3Init(TIM3, &TIM_OCInitStructure);
TIM_OC4Init(TIM3, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM3, ENABLE);
TIM_Cmd(TIM3, ENABLE); TIM_SetCompare3(TIM3, pwm_cnt); TIM_SetCompare4(TIM3, pwm_cnt); }
b、根据以上实例讲解原理和配置步骤: (1)、根据选择的IO口配置RCC时钟; (2)、 根据IO口选择映射的定时器通道,本实例中映射TIM3_ch3和TIM3_ch4 ; (3)、初始化定时器TIM_TimeBaseInit。配置时基及计数模式,它决定PWM的频率; (4)、初始化输出比较参数TIM_OC3Init。配置TIMx->CR2,TIMx->CCMR2,TIMx->CCR3,TIMx->CCER中输出比较的相关寄存器; (5)、使能预装载定时器TIM_OC3PreloadConfig; (6)、使能自动重装的预装载寄存器允许位 TIM_ARRPreloadConfig; (7)、使能定时器TIM_Cmd; (8)、设置改变比较器的值TIM-CCRx,达到不同占空比的效果。
c、寄存器讲解 根据配置步骤讲解寄存器:(1)、1/2/3步骤的讲解省略,直接学习捕获/比较的输出配置;以通道1为例,从最左边进入的是时钟源,由内部时钟(CNT)或者外部触发时钟(ETRF)输入,进入输出模式控制器,通过OCMR1寄存器的OC1M[2:0]位来配置PWM模式,之后进入一个选择器,由CCER寄存器的CC1P位来设置输出极性,最后由CCER寄存器的CC1E位来使能输出,然后通过OC1来输出PWM波。
(2)、初始化输出比较参数 TIMx_CCMR1的OC1M[2:0]配置输出模式(通道1有详细讲解,所以用通道1寄存器)
TIMx_CCR3(比较器预装载值,决定占空比)
TIMx_CCER中的CC3P和CC3E分别为输出极性和输出使能
TIMx_CCMR2中的预装载使能(TIM_OC3PreloadConfig)
TIMx_CR1中的ARPE自动装载使能(TIM_ARRPreloadConfig)
TIMx-CR1中的CEN 使能定时器TIM_Cmd
脉冲计数我常用的方法主要左右三种:捕获输入、外部引脚输入(TIx--外部时钟模式1)、外部触发输入(ETR--外部时钟模式2)。下面对上述三种方法分别实例学习。
对管脚PA12 (TIM1_ETR)上的脉冲进行计数
GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_TIM1);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_DeInit(TIM1);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
TIM_TimeBaseStructure.TIM_Prescaler =0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
TIM_ITConfig(TIM1,TIM_IT_Update,ENABLE);//使能中断,Update更新中断
TIM_SetCounter(TIM1,0);
TIM_ClearFlag(TIM1,TIM_IT_Update);
TIM_ETRClockMode2Config(TIM1,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0);//SMCR->ETPS外部触发预分频;SMCR->ETP外部触发极性;SMCR->ETF外部触发滤波器;SMCR->ECE使能外部时钟模式2;
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_TIM10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
讲解:
PA12是TIM1_ETR管脚,采用外部时钟源模式2配置,PA12管脚每来一个上升沿脉冲,计数器值加1。
TIM_ETRClockMode2Config(TIM1,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0);
同样的功能,改到PB7,对管脚PB7 (TIM4_CH2)上的脉冲进行计数。如果是PA5(TIM2_CH1_ERT)这样的管脚,既可以配置外部引脚输入(TIx--外部时钟模式1),也可以配置外部触发输入(ETR--外部时钟模式2),具体要根据选择的管脚进行配置。
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_TIM4);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
TIM_DeInit(TIM4);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
TIM_TimeBaseStructure.TIM_Prescaler =0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_ICInitStructure.TIM_Channel=TIM_Channel_2;
TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Falling;//CCER->CC2P
TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;//CCMR1-CC2S方向和输入脚的选择
TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;//输入捕获预分频CCMR1-IC2PSC
TIM_ICInitStructure.TIM_ICFilter=0x00;//输入捕获滤波CCMR1-IC2F
TIM_ICInit(TIM4, &TIM_ICInitStructure);
TIM_SetCounter(TIM4,0);
TIM_ClearFlag(TIM4,TIM_IT_Update);
TIM_ETRClockMode1Config(TIM4,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0); //SMCR->ETPS外部触发预分频;SMCR->ETP外部触发极性;SMCR->ETF外部触发滤波器;SMCR->SMS外部时钟模式1;
TIM_SelectInputTrigger(TIM4, TIM_TS_TI2FP2);//SMCR->TS
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
TIM_DeInit(TIM4);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
TIM_TimeBaseStructure.TIM_Prescaler =0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_ICInitStructure.TIM_Channel=TIM_Channel_2;
TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Falling;
TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter=0x00;
TIM_ICInit(TIM4, &TIM_ICInitStructure);
TIM_SetCounter(TIM4,0);
TIM_ITConfig(TIM4, TIM_IT_CC2, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
然后在捕获中断函数里,计数++即可。达到和上述两种方法一样的效果。这几种方法要根据不同的管脚进行配置。捕获输入脉冲计数的方法,中断开销太大,每来一个脉冲就要进入中断++,不适合高频信号的计数。
设计思想:在上升沿计数器设为0,然后下降沿读出计数器,就能得到相应时间了,但是这样其实有个小问题,就是有可能有溢出。我们如何解决这个问题呢?答案就是记录中断溢出的次数,然后在后面加上这些时间就好了。
GPIO_InTIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM5_ICInitStruct;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);//TIM5时钟使能
GPIO_PinAFConfig(GPIOA,GPIO_PinSource0,GPIO_AF_TIM5);//使能PORTA时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIOA0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA0
TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM5,&TIM_TimeBaseStructure);
TIM5_ICInitStruct.TIM_Channel = TIM_Channel_1;//选择输入端 IC1映射到TI1上
TIM5_ICInitStruct.TIM_ICFilter = 0x00;//上升沿捕获
TIM5_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;//映射到TI1上
TIM5_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;//配置输入分频,不分频
TIM5_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;// 配置输入滤波器 不滤波
TIM_ICInit(TIM5 ,&TIM5_ICInitStruct);
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);
TIM_Cmd(TIM5,ENABLE ); //使能定时器5
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =2; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
u8 TIM5CH1_CAPTURE_STA=0; //输入捕获状态
u32 TIM5CH1_CAPTURE_VAL; //输入捕获值(TIM2/TIM5是32位)
//定时器5中断服务程序
void TIM5_IRQHandler(void)
{
if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获
{
//溢出
if(TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
{
if(TIM5CH1_CAPTURE_STA&0X40)//已经捕获到高电平了
{
if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM5CH1_CAPTURE_STA|=0X80; //标记成功捕获了一次
TIM5CH1_CAPTURE_VAL=0XFFFFFFFF;
}else TIM5CH1_CAPTURE_STA++;
}
}
//未溢出
if(TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)//捕获1发生捕获事件
{
if(TIM5CH1_CAPTURE_STA&0X40) //捕获到一个下降沿
{
TIM5CH1_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽
TIM5CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5);//获取当前的捕获值.
TIM_OC1PolarityConfig(TIM5,TIM_OCPolarity_High); //CC1P=0 设置为上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM5CH1_CAPTURE_STA=0; //清空
TIM5CH1_CAPTURE_VAL=0;
TIM5CH1_CAPTURE_STA|=0X40; //标记捕获到了上升沿
TIM_Cmd(TIM5,DISABLE ); //关闭定时器5
TIM_SetCounter(TIM5,0);
TIM_OC1PolarityConfig(TIM5,TIM_OCPolarity_Low); //CC1P=1 设置为下降沿捕获
TIM_Cmd(TIM5,ENABLE ); //使能定时器5
}
}
}
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位
}
1、 开启 TIM5 时钟,配置 PA0 为复用功能(AF2),并开启下拉电阻。
2、初始化 TIM5,设置 TIM5 的 ARR 和 PSC。 3、设置 TIM5 的输入捕获参数,开启输入捕获。
4、使能捕获和更新中断(设置 TIM5 的 DIER 寄存器)。
5、设置中断优先级,编写中断服务函数。
6、使能定时器(设置 TIM5 的 CR1 寄存器)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。