STM32学习记录-05 -1-TIM定时中断

1 TIM简介

TIM(Timer)定时器

定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断(如在STM32中主频为72MHz,计72个数即1MHz也就是1us,计72000个数即1KHz也就是1ms)

16位计数器、预分频器(对计数器的时钟进行分频)、自动重装寄存器(计数的目标值,计多少个时钟申请中断)的时基单元,在72MHz计数时钟下可以实现最大59.65s(中断频率为72M/65536/65536的倒数)的定时

不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能

根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型

2 定时器类型

STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4

3 基本定时器

拥有定时中断、主模式触发DAC的功能

(1)定时中断:下方的计数器、预分频器和自动重装载寄存器为时基单元。预分频器之前连接的是基准计数时钟的输入,即直接通过控制器连接到内部时钟RCC的TIMxCLK,频率一般为系统主频72MHz。时基单元中的预分频器可以对72MHz的计数时钟进行预分频,若预分频器写0,则不分频,此时输出频率=输入频率=72MHz;若写1,就为2分频,输出频率=输入频率/2=36MHz,;若写n,则为n+1分频,输出频率=输入频率/(n+1),n最大值为65535。计数器(16位)可对预分频后的计数时钟进行计数,计数时钟每来一个上升沿计数值就加1,里面的值可以从0一直到65535,然后归0重新计数。自动重装寄存器(16位)存的是写入的计数目标,运行过程中计数值不断自增,自动重装值为固定的目标,当计数值等于自动重装值,计时时间到,产生中断信号,并清零计数器开始下一次的计数计时。向上的UI代表产生的中断信号,计数值等于自动重装值产生的中断叫更新中断,会通往NVIC,配置好NVIC的定时器通道就能得到CPU响应。向下的U代表会产生更新事件,不会触发中断但可以触发内部其他电路的工作。

(2)主模式触发DAC:使用DAC时会用DAC输出一段波形,需要每隔一段时间触发一次DAC输出下一个电压点。使用这个主模式可以将定时器的更新事件映射到触发输出TRGO(Trigger Out)的位置,然后TRGO直接接到DAC的触发转换引脚。

4 通用定时器

中间核心部分为时基单元,和基本定时器一样。但计数器的计数模式不止向上计数(计数器从0开始向上自增到重装值,清零同时申请中断),还有向下计数模式(从重装值开始向下自减到0之后,回到重装值同时申请中断)和中央对齐模式(从0开始先向上自增计到重装值申请中断,然后再向下自减到0申请中断)。

左上部分:内外时钟源选择和主从触发模式。内外时钟源选择,可选择内部72MHz时钟也可选择外部时钟,有TIMx_ETR引脚的外部时钟(可在左上角的TIM2的ETR引脚即PA0接一个外部方波时钟,然后配置内部极性选择、边沿检测和预分频器电路,再配置输入滤波电路,最后滤波后的信号兵分两路,上一路(外部时钟模式2)ETRF进入触发控制器就可以选择作为时基单元的时钟,下一路(外部时钟模式1)TRGI主要用作触发输入,可以触发定时器的从模式。)外部时钟模式1连接了ETR引脚信号和ITR信号,ITR信号来自其他定时器,主模式的TRGO可以通向其他定时器,此时就接到了其他定时器的ITR引脚上,实现定时器级联的功能;还可以选择TI1F_ED连接了输入捕获单元的CH1引脚,从CH1引脚获得时钟,后缀加ED(Edge)就是边沿的意思,也就是通过这一路的时钟上升下降沿都有效;还可以通过TI1FP1和TI2FP2获得,TI1FP1连接到CH1引脚的时钟,TI2FP2连接到CH2引脚的时钟.编码器接口可以读取正交编码器输出的波形。

右上部分(TRGO):定时器的主模式输出,把内部事件映射到TRGO引脚,用于触发其他定时器、DAC或ADC。

右下部分(输出比较电路):共四个通道,分别对应CH1到CH4的引脚,可以用于输出PWM波形驱动电机

左下部分(输入捕获电路):四个通道,分别对应CH1到CH4的引脚,可以用于测方波频率

中下部分(捕获/比较寄存器):是输入捕获和输出比较电路共用的

5 高级定时器

右边中间部分:上方增加了重复次数计数器,可实现每隔几个技术周期才发生一次更新事件和更新中断,相当于对输出的更新信号又做了一次分频。下方的DTG(Dead Time Denerate)是死区生成电路,右边的输出引脚变为两个互补的输出,可输出一对互补的PWM波形,为驱动三项五刷电机(为防止互补输出的PWM驱动桥臂时在开关切换瞬间由于器件不理想造成短暂的直通现象,使用加上了死区生成电路,在开关切换瞬间产生一定时长的死区让桥臂上下管全部关断防止直通现象)

下方部分(刹车输入功能):给电机驱动提供安全保障。若外部引脚BKIN(Break IN)产生刹车信号或者内部时钟失效产生故障,控制电路就会自动切断电机输出防止意外发生

6 定时中断基本结构

最重要的为时基单元。运行控制可以控制寄存器的一些位(启动停止、向上向下计数等等),操作寄存器就能控制时基单元运行。左边为时基单元提供时钟部分,可以选择RCC提供的内部时钟,可以选ETR引脚提供的外部时钟模式2,还可以选择外部时钟模式1的触发输入当外部时钟(对应有ETR外部时钟、ITRx其他定时时钟、TIx输入捕获通道),最后还有编码器模式。右边为计时时间到产生更新中断后的信号去向(如果是高级定时器还多一个重复计数器),中断信号会在状态寄存器置一个中断位,这个标志位会通过中断输出控制(中断输出允许位)到NVIC申请中断。

7 预分频器时序

计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)

CK_PSC预分频器的输入时钟

CNT_EN计数器使能:高电平计数器正常运行,低电平计数器停止

CK_CNT计数器时钟:既是预分频器的时钟输出,也是计数器的时钟输入

(开始时计数器未使能,计数器时钟不运行,使能后前半段预分频器系数为1,计数器时钟等于预分频器前的时钟,后半段预分频器系数为2,计数器时钟变为预分频器前时钟的一半,在计数器时钟驱动下计数器寄存器也跟随时钟上升沿不断自增,在FC之后计数值变为0,可知ARR自动重装值为FC,同时产生一个更新事件)

下面三行时序:预分频器的缓冲机制。预分频控制寄存器供我们读写用,不直接决定分频系数。缓存寄存器(若把预分频控制寄存器从0变为1,若此时立刻改变时钟分频系数就会导致在一个计数周期内前半段和后半段频率不一样,只有在本周期结束后才会将预分频寄存器的值传递到缓存寄存器里生效)。当预分频值为0时计数器一直为0直接输出原频率,当值为1时计数器才开始010101计数,回到0时输出脉冲,就变为2分频

8 计数器时序

计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)

CK_INT内部时钟72MHz,CNT_EN时钟使能,高电平启动

CK_CNT计数器时钟:分频系数为2,频率为内部时钟/2,计数器在每个上升沿自增到0036时溢出,再来一个上升沿清零,会更新事件UEV,还会置一个更新中断标志位UIF,标志位置1后就会申请中断,在中断响应后需要在中断程序手动清零

9 计数器无预装时序

计数器正在自增计数,突然更改自动加载寄存器(FF改36),计数值目标值则由FF改为36,则在计数器寄存器计数到36时,就直接更新开始下一轮计数

10 计数器有预装时序

计数器正在自增计数,突然更改自动加载寄存器(计数目标F5改36),但自动加载影子寄存器还是F5,所以现在目标还是F5才产生更新事件,要更改的36才被传递到影子寄存器,在下一个周期才有效

11 RCC时钟树

由AHB左边为界分为左右两部分,左边部分为时钟产生电路,右边部分为时钟分配电路,中间的SYSCLK就是系统时钟72MHz。

时钟产生电路:有四个震荡源,分别为内部8MHz高速RC振荡器、外部4-16MHz高速石英晶体振荡器(即晶振,一般接8MHz)、外部32.768MHz低速晶振(一般给RTC提供时钟)、内部40KHz低速RC振荡器(给看门狗提供时钟)。高速晶振给系统提供时钟。在SystemInit函数里ST先启动内部时钟选内部8MHz时钟运行,再启动外部时钟,配置外部时钟进入PLL锁相环进行倍频,8MHz倍频9倍得到72MHz,等锁相环输出稳定后选择锁相环输出为系统时钟。CSS为时钟安全系统,负责切换时钟,监测外部时钟运行状态,一旦外部时钟失效就会切换回内部时钟,保证系统时钟的运行防止程序卡死。

时钟分配电路:系统时钟72MHz进入AHB总线,有个预分频器,在SystemInit里配置的分配系数为1,所以AHB的时钟为72MHz。然后进入APB1总线,配置的分配系数为2,所以时钟为36MHz。(APB1接的通用定时器、基本定时器的内部基准时钟都为72MHz,因为要/2再×2)。APB2的分频系数为1,所以APB2为72MHz(接在APB2的高级定时器内部基准时钟也为72MHz)。在时钟输出都有一个与门进行输出控制,写的是外部时钟使能,也就是在程序中写RCC_APB2/1PeriphClockCmd作用的地方,打开时钟就是在这里写1让左边的时钟能够通过与门输出给外设。

定时器模块初始化

        关于TIM的库函数:

                void TIM_DeInit(TIM_TypeDef* TIMx);//恢复缺省配置

                void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);//时基单元初始化(选择某个定时器,结构体)

                void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);//把结构体变量赋一个默认值

                void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);//使能计数器,即运行控制(选择定时器,使能还是失能)

                void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);//使能中断输出信号(选择定时器,配置哪个中断输出,使能还是失能)

        //时基单元的时钟选择部分(RCC内部时钟\ETR外部时钟\ITRx其他定时器\TIx捕获通道)

                void TIM_InternalClockConfig(TIM_TypeDef* TIMx);//选择内部时钟

                void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);//选择ITRx其他定时器时钟(选择定时器,接入哪个其他的定时器)

                void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource, uint16_t TIM_ICPolarity, uint16_t ICFilter);//选择TIx捕获通道时钟(选择定时器,选择TIx具体某个引脚,极性,滤波器)

                void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);//选择ETR外部时钟输入模式1的时钟(选择定时器,外部触发预分频器,极性,滤波器)

                void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);//选择ETR外部时钟输入模式2的时钟(选择定时器,外部触发预分频器,极性,滤波器)

                void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);//配置ETR引脚(选择时钟,预分频器,极性,滤波器)

                void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);//单独写预分频值(要写入的预分频值,写入的模式)

                void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);//改变计数器的计数模式(选择计数器,选择计数器模式)

                void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);//自动重装器预装功能配置(选择计数器,使能或失能)

                void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);//给计数器写一个值

                void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);//给自动重装器写入一个值

                uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);//获取当前计数器的值

                uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);//获取当前预分频器的值

        //获取标志位和清除标志位

                FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);

                void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);

                ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);

                void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

        (1)RCC开启时钟使定时器基准时钟和整个外设工作时钟同时打开

                RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//开启时钟TIM2

        (2)选择时基单元的时钟源,对于定时中断就选择内部时钟源

                TIM_InternalClockConfig(TIM2);

        (3)配置时基单元(预分频器、自动重装器、计数模式)

                TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;

                TIM_TimeBaseInitStruct.TIM_ClockDivision = ;//指定时钟分频

                TIM_TimeBaseInitStruct.TIM_CounterMode = ;//计数器模式

                        TIM_CounterMode_Up 向上计数,TIM_CounterMode_Down 向下计数

                        TIM_CounterMode_CenterAligned1 中央对齐模式1,2,3

                TIM_TimeBaseInitStruct.TIM_Period = 10000 - 1;//ARR自动重装器的值

                TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1;//PSC预分频器的值

        //计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1),此时计时1s,频率为1Hz

                TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;//重复计数器的值(高级计数器才有)

                TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);

        (4)配置输出中断控制,允许更新中断输出到NVIC

                TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

        (5)配置NVIC,在NVIC中打开定时器中断通道并分配一个优先级

                NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

                NVIC_InitTypeDef NVIC_InitStructure;

                NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;

                NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

                NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;

                NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

                NVIC_Init(&NVIC_InitStructure);

        (6)运行控制,使能计数器

                TIM_Cmd(TIM2, ENABLE);

程序源码

Timer.c
#include "stm32f10x.h"                  // Device header

/**
  * 函    数:定时中断初始化
  * 参    数:无
  * 返 回 值:无
  */
void Timer_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;				//计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;				//预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			//重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				//将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元	
	
	/*中断输出配置*/
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);						//清除定时器更新标志位
																//TIM_TimeBaseInit函数末尾,手动产生了更新事件
																//若不清除此标志位,则开启中断后,会立刻进入一次中断
																//如果不介意此问题,则不清除此标志位也可
	
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
																//即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅需调用一次
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//指定NVIC线路的抢占优先级为2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

/* 定时器中断函数,可以复制到使用它的地方
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}
*/
main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"

uint16_t Num;			//定义在定时器中断里自增的变量

int main(void)
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	Timer_Init();		//定时中断初始化
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "Num:");			//1行1列显示字符串Num:
	
	while (1)
	{
		OLED_ShowNum(1, 5, Num, 5);			//不断刷新显示Num变量
	}
}

/**
  * 函    数:TIM2中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)		//判断是否是TIM2的更新事件触发的中断
	{
		Num ++;												//Num变量自增,用于测试定时中断
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);			//清除TIM2更新事件的中断标志位
															//中断标志位必须清除
															//否则中断将连续不断地触发,导致主程序卡死
	}
}

  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32F103是一款基于ARM Cortex-M3内核的微控制器,它具有丰富的外设和功能。其中,TIM1是STM32F103系列中的一个定时器模块,可以用于生成定时中断TIM1定时中断的实现步骤如下: 1. 配置TIM1的时钟源和预分频器,确定计数器的时钟频率。 2. 配置TIM1的自动重装载寄存器(ARR),设置定时器的周期。 3. 配置TIM1的计数器(CNT),确定计数器的初始值。 4. 配置TIM1的工作模式,选择定时器的工作方式(例如,向上计数、向下计数等)。 5. 配置TIM1的中断使能,使能定时中断。 6. 编写中断服务函数,处理定时中断事件。 以下是一个简单的示例代码,演示了如何配置和使用TIM1定时中断: ```c #include "stm32f10x.h" void TIM1_IRQHandler(void) { if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET) { // 处理定时中断事件 // ... // 清除中断标志位 TIM_ClearITPendingBit(TIM1, TIM_IT_Update); } } int main(void) { // 启用TIM1的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); // 配置TIM1 TIM_TimeBaseInitTypeDef TIM_InitStruct; TIM_InitStruct.TIM_Prescaler = 7200 - 1; // 设置预分频器,得到10kHz的计数频率 TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式 TIM_InitStruct.TIM_Period = 10000 - 1; // 设置定时器周期为1秒 TIM_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1; TIM_InitStruct.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM1, &TIM_InitStruct); // 使能TIM1更新中断 TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); // 配置中断优先级 NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel = TIM1_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); // 启动TIM1 TIM_Cmd(TIM1, ENABLE); while (1) { // 主循环 // ... } } ``` 在上述示例代码中,我们首先启用了TIM1的时钟,并配置了TIM1的工作模式和定时周期。然后,使能了TIM1的更新中断,并编写了中断服务函数`TIM1_IRQHandler`来处理定时中断事件。最后,在主循环中可以执行其他任务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值