嵌入式接口之TIM定时器与NVIC的STM32模板库函数的一些解释

文章目录

  • 前言
  • 定时基本方法
  • STM32的时钟
  • STM的定时器TIM
    • 通用定时器的结构
      • 时基单元
          • 例子讲解
            • TIM_TimeBaseInit
            • TIM_TimeBaseInitTypeDef
            • TIM_Cmd
            • TIM _ITConfig
            • 核心代码
      • 输出比较单元
          • 例子讲解
      • 输入捕获单元
          • 例子讲解

前言

下面将讲解一下STM32中的定时器TIM的一些基本操作。这同样也是考试的重点内容之一,比较光自己看一遍印象还是不深刻,写篇博客就会逼自己去弄得更明白一点。

定时基本方法

1.软件延时:CPU执行一段“空”程序实现延时
优点:不需要添加额外的硬件设备,简单易实现
缺点:占用CPU时间,精度受中断及CPU主频时钟影响

2.硬件延时电路:采用附加的数字电路(如555单稳态芯片)产生特定的延时。
优点:不占用CPU时间
缺点:改变延时时间需要调整电路参数(电阻、电容值),通用性灵活性差,时间精度受电路参数影响,稳定性差

3.可编程定时器(计数器)
以专用芯片(如Intel8253)或MCU中集成的定时器接口设备(如STM32中的TIM1~8 )来进行定时。
优点:定时器初始化后就独立于CPU工作,不占用CPU时间,时间长短不限,使用灵活精度高。
缺点:增加硬件成本

STM32的时钟

51单片机采用石英晶体和振荡电路构成的时钟模块,来为系统提供时钟脉冲。此类时钟电路结构简单,输出时钟的频率取决于石英晶体的谐振频率,且只有单一频率时钟输出。受加工工艺等影响,高频石英车成本较高,对于需要频率为72MHz等处理器,常用锁相环技术把低频时钟信号升到一个较高频率。
STM32片内除ARM Cortx - M3内核,还有众多用途各异的外设,既有需要高速时钟的外设,也有需要低速时钟的外设,因此还需要分频器将时钟频率降到一个合适的频率供外设使用。
嵌入式接口之TIM定时器与NVIC的STM32模板库函数的一些解释_第1张图片

STM的定时器TIM

通用定时器的结构

嵌入式接口之TIM定时器与NVIC的STM32模板库函数的一些解释_第2张图片
从图中可以看出STM32的通用定时器分为时基单元、输出比较单元和输入捕获单元三大部分。

时基单元

嵌入式接口之TIM定时器与NVIC的STM32模板库函数的一些解释_第3张图片
时基单元简单来讲就是可以实现计数的功能,以向上计数为例,定时器初始化完成后,被选做计数器时钟源的时钟CS_PSK经过预分频器分频后作为计数脉冲施加到计数器上,计数器的值随着CK_CNT开始从0递增,当计数到自动重加载值(ARR的值)之后,产生计数器向上溢出更新事件,并且开始新一轮的计数。

例子讲解

问题:利用定时器接口TIM3,实现1s定时,利用TIM3定时器中断,在中断服务程序让LED0~1闪烁一次。
下面将讲解STM32模板库中对于的核心代码

TIM_TimeBaseInit

用于初始化对应的定时器外设
嵌入式接口之TIM定时器与NVIC的STM32模板库函数的一些解释_第4张图片

TIM_TimeBaseInitTypeDef

嵌入式接口之TIM定时器与NVIC的STM32模板库函数的一些解释_第5张图片

其中各个参数的一般取值和解释如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
嵌入式接口之TIM定时器与NVIC的STM32模板库函数的一些解释_第6张图片

TIM_Cmd

用于使能TIMx
嵌入式接口之TIM定时器与NVIC的STM32模板库函数的一些解释_第7张图片

TIM _ITConfig

使能或使能TIM的中断。
嵌入式接口之TIM定时器与NVIC的STM32模板库函数的一些解释_第8张图片

核心代码
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
//使能TIM3的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
//计数器计数10000次就发生更新事件(因为从0开始需要-1)
TIM_TimeBaseStructure.TIM_Period = 10000-1;
//分频器设置7200(因为从0开始需要-1),那么系统时钟经过分频器就变为10KHz
//再通过计数器计数就是10K / 10K = 1Hz,正好就是1s一次
TIM_TimeBaseStructure.TIM_Prescaler = 7200-1;
//设置TIM3控制寄存器CR1的CKD[2:0] = 00,使tDTS = tCK_INT
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
//向上计数模式
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
//初始化TIM3
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
//允许TIM3更新中断
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
//设置TIM3的控制器CR1的CEN位,开启定时器TIM3
TIM_Cmd(TIM3, ENABLE);

由于是使用TIM中断实现,那么中断离不开NVIC,因此还需要配置NVIC以及对于的中断服务程序,这里就不一一列举出来了。

输出比较单元

嵌入式接口之TIM定时器与NVIC的STM32模板库函数的一些解释_第9张图片
输出比较功能由一个捕获/比较寄存器CCR和计数器CNT以及相应的输出部分组成。
在运行的过程中,如果CNT==CCR1那么执行以下操作:

设置通道输出值:输出比较模式OC1M[2:0] (捕获/比较模式寄存器TIMx_CCMR1中)决定中间信号OC1REF值高有效,OC1REF和输出有效极
性选择位CC1P(捕获/比较使能寄存器TIMx_CCER)决定OC1输出值。

例子讲解

利用TIM3的输出比较功能,产生10Hz的方波,用来控制LED0闪烁。

分析:让TIM3基本定时器工作在0.5s定时状态(20Hz ),再利用TIM3_CH2通道的输出比较功能,在“相等则翻转模”式下,每次间隔0.5s,出现一次翻转,产生10Hz的方波输出。

这里需要调用TIM对应的输出比较模式,因此也需要使用对应的接口驱动函数。但是由于我手上的函数库可能有点老了,这块代码好像对不上,就不详细将其中的函数和参数了。直接讲核心代码好了

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
//使能TIM3的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
//计数器计数500次就发生更新事件(因为从0开始需要-1)
TIM_TimeBaseStructure.TIM_Period = 500-1;
//分频器设置7200(因为从0开始需要-1),那么系统时钟经过分频器就变为10KHz
//再通过计数器计数就是10K / 500 = 20Hz
TIM_TimeBaseStructure.TIM_Prescaler = 7200-1;
//设置TIM3控制寄存器CR1的CKD[2:0] = 00,使tDTS = tCK_INT
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
//向上计数模式
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
//初始化TIM3
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

TIM_OCInitTypeDef TIM_OCInitStructure;
//初始化TIM3_CH2输出翻转模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
//比较输出使能
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
//输出极性,TIM输出比较极性高
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
//设置比较输出寄存器TIM3_CCR2的值,每计数达到200次信号翻转一次,定时器输出20Hz,那么方波就为10Hz,(0-499都可以,因为计数器范围是0-499)
TIM_OCInitStructure.TIM_Pulse = 200;
//初始化TIM3_OC2
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
//使能TIM3在CCR2上的预加载寄存器
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
//使能TIM3
TIM_Cmd(TIM3, ENABLE);

输入捕获单元

输入捕获是指通过对引脚输入信号电平变化的检测,在发现电平变化时,保存计数器的值到输入捕获寄存器中,同时设置输入捕获状态标志位;如果允许中断,则向CPU发出中断请求,进而实现对事件发生间隔时间的测量以及对实时事件的响应。
用途:雷达测速、超声波测距、发动机转速以及车速的测量等。

嵌入式接口之TIM定时器与NVIC的STM32模板库函数的一些解释_第10张图片
输入捕获功能由捕获信号输入部分(数字滤波、边沿检测和预分频器),捕获/比较寄存器TIMx_CCRx以及计数器CNT组成。

输入滤波器对TI1输入信号采样滤波得到信号TI1F,经带极性选择的边沿检测器后获得有效沿信号TI1FP1,进而得到输入捕获信号IC1,此信号再经分频得到IC1PS。当一个有效的IC1PS来到后,即触发捕获事件,计数器当前值被锁存到捕获/比较寄存器TIMx_CCR1中,同时通道1捕获/比较标志CC1IF(TIMx_SR寄存器)被置1。

例子讲解

利用TIM2的输入捕获功能,测量任意PWM信号的频率和占空比。

核心代码如下:

	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	TIM_ICInitTypeDef TIM_ICInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	//定义时钟频率并设置ARR到最大值
	u32 CK_INT_FREQ = 72000000;
	//定义内部时钟源CK_INT频率为72MHz
	u32 CK_CNT_FREQ = 1000 * 1000;
	//设置预分频比PSC的值
	u16 PrescalerValue = (u16)(CK_INT_FREQ/CK_CNT_FREQ-1);
	// ARR计数初值65535,测量范围:PWM周期<65536 * 1us = 0.06536s
	//PWM频率 > 16.258Hz
	u16 ARRValue = 65535;
	//使能时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    //设置定时器复位模式并选定复位触发信号
	TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);
	//将TIM2复位从模式(SMS=100),每次TRIG上跳沿引起TIM2_CNT复位。
	//选择TI1FP作为触发信号TRIG
	TIM_SelectInputTrigger(TIM2, TIM_TS_TI1FP1);
	
	//配置TIM2定时器
	TIM_TimeBaseStructure.TIM_Period = ARRValue;
	TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
	TIM_TimeBaseStructure.TIM_ClockDivision = 0;
	//向上计数模式
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

	//设置TIM2的捕获通道IC1
	TIM_ICInitStructure.TIM_Channel=TIM_Channel_1;
	//TIM2_TI1输入信号不滤波
	TIM_ICInitStructure.TIM_ICFilter=0x0;
	//IC1选择TI1的边缘极性信号TI1FP1(CC1S=01)
	TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;
	//上升沿触发捕获
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
	//IC1信号不分配得到IC1PS
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPSC_DIV1;
	//初始化TIM2的IC1
	TIM_PWMIConfig(TIM2, &TIM_ICInitStructure);
	//允许IC1捕获中断
	TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE);
	//使能IC1输入捕获中断
	TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
	开启TIM2
	TIM_Cmd(TIM2, ENABLE);
	
	//配置NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);


//-------------------------------------------------------------------
//NVIC中断服务程序
	float PWMDutyResult=0;
	float PWMFreqResult=0;
	u16 IC1Value=0;
	u16 IC2Value=0;
	void TIM2_IRQHandler(void)   //中断服务程序
{	
	IC1Value=TIM_GetCapture1(TIM2);
	IC2Value=TIM_GetCapture2(TIM2);
	if(IC1Value != 0 && IC2Value != 0)
	{
	PWMDutyResult = (float)(IC2Value+1) * 100 / (IC1Value + 1);
	PWMFreqResult = (float)(1000000 / (IC1Value + 1));
	
	//清空中断标志位
	TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
	}
}

你可能感兴趣的:(嵌入式接口,stm32,单片机,arm)