一.TIM简介TIMTimer定时器定时器可以对输入的时钟进行计数并在计数值达到设定值时触发中断16位计数器CNT、预分频器(PSC)、自动重装寄存器ARR的时基单元在72MHz计数时钟下可以实现最大59.65s的定时不仅具备基本的定时中断功能而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型二.通用定时器主要功能通用TIMx (TIM2、TIM3、TIM4和TIM5)定时器功能包括● 16位向上、向下、向上/向下自动装载计数器● 16位可编程(可以实时修改)预分频器计数器时钟频率的分频系数为165536之间的任意数值● 4个独立通道─ 输入捕获─ 输出比较─ PWM生成(边缘或中间对齐模式)─ 单脉冲模式输出● 使用外部信号控制定时器和定时器互连的同步电路● 如下事件发生时产生中断/DMA─ 更新计数器向上溢出/向下溢出计数器初始化(通过软件或者内部/外部触发)─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)─ 输入捕获─ 输出比较● 支持针对定位的增量(正交)编码器和霍尔传感器电路● 触发输入作为外部时钟或者按周期的电流管理三.通用定时器框图四.时基单元可编程通用定时器的主要部分是一个16位计数器和与其相关的自动装载寄存器。这个计数器可以向上计数、向下计数或者向上向下双向计数。此计数器时钟由预分频器分频得到。 计数器、自动装载寄存器和预分频器寄存器可以由软件读写在计数器运行时仍可以读写。时基单元包含● 计数器寄存器(TIMx_CNT)● 预分频器寄存器 (TIMx_PSC)● 自动装载寄存器 (TIMx_ARR)自动装载寄存器是预先装载的写或读自动重装载寄存器将访问预装载寄存器。根据在 TIMx_CR1寄存器中的自动装载预装载使能位(ARPE)的设置预装载寄存器的内容被立即或在 每次的更新事件UEV时传送到影子寄存器。当计数器达到溢出条件(向下计数时的下溢条件)并当 TIMx_CR1寄存器中的UDIS位等于’0’时产生更新事件。更新事件也可以由软件产生。随后会 详细描述每一种配置下更新事件的产生。计数器由预分频器的时钟输出CK_CNT驱动仅当设置了计数器TIMx_CR1寄存器中的计数器使 能位(CEN)时CK_CNT才有效。五.实验1部分1.实验1目标在OLED上实现计时功能2.实验1主要代码2.1 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线路的抢占优先级为2NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; //指定NVIC线路的响应优先级为1NVIC_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);}}*/2.2 main.c#include stm32f10x.h // Device header#include Delay.h#include OLED.h#include Timer.huint16_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更新事件的中断标志位//中断标志位必须清除//否则中断将连续不断地触发导致主程序卡死}}3.实验1硬件部分及现象定时器定时中断六.实验二部分1.实验2目标当挡光片遮住对射式红外传感器CNT到达10清零同时NUM。2.实验2主要代码2.2 Timer.c#include stm32f10x.h // Device header/*** 函 数定时中断初始化* 参 数无* 返 回 值无* 注意事项此函数配置为外部时钟定时器相当于计数器*/void Timer_Init(void){/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA, GPIO_InitStructure); //将PA0引脚初始化为上拉输入/*外部时钟配置*/TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);//选择外部时钟模式2时钟从TIM_ETR引脚输入//注意TIM2的ETR引脚固定为PA0无法随意更改//最后一个滤波器参数加到最大0x0F可滤除时钟信号抖动/*时基单元初始化*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量TIM_TimeBaseInitStructure.TIM_ClockDivision TIM_CKD_DIV1; //时钟分频选择不分频此参数用于配置滤波器时钟不影响时基单元功能TIM_TimeBaseInitStructure.TIM_CounterMode TIM_CounterMode_Up; //计数器模式选择向上计数TIM_TimeBaseInitStructure.TIM_Period 10 - 1; //计数周期即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler 1 - 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线路的抢占优先级为2NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; //指定NVIC线路的响应优先级为1NVIC_Init(NVIC_InitStructure); //将结构体变量交给NVIC_Init配置NVIC外设/*TIM使能*/TIM_Cmd(TIM2, ENABLE); //使能TIM2定时器开始运行}/*** 函 数返回定时器CNT的值* 参 数无* 返 回 值定时器CNT的值范围0~65535*/uint16_t Timer_GetCounter(void){return TIM_GetCounter(TIM2); //返回定时器TIM2的CNT}/* 定时器中断函数可以复制到使用它的地方void TIM2_IRQHandler(void){if (TIM_GetITStatus(TIM2, TIM_IT_Update) SET){TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}}*/2.3 Main.c#include stm32f10x.h // Device header#include Delay.h#include OLED.h#include Timer.huint16_t Num; //定义在定时器中断里自增的变量int main(void){/*模块初始化*/OLED_Init(); //OLED初始化Timer_Init(); //定时中断初始化/*显示静态字符串*/OLED_ShowString(1, 1, Num:); //1行1列显示字符串Num:OLED_ShowString(2, 1, CNT:); //2行1列显示字符串CNT:while (1){OLED_ShowNum(1, 5, Num, 5); //不断刷新显示Num变量OLED_ShowNum(2, 5, Timer_GetCounter(), 5); //不断刷新显示CNT的值}}/*** 函 数TIM2中断函数* 参 数无* 返 回 值无* 注意事项此函数为中断函数无需调用中断触发后自动执行* 函数名为预留的指定名称可以从启动文件复制* 请确保函数名正确不能有任何差异否则中断函数将不能进入*/void TIM2_IRQHandler(void){if (TIM_GetITStatus(TIM2, TIM_IT_Update) SET) //判断是否是TIM2的更新事件触发的中断{Num ; //Num变量自增用于测试定时中断TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIM2更新事件的中断标志位//中断标志位必须清除//否则中断将连续不断地触发导致主程序卡死}}3.实验2硬件部分及现象定时器外部时钟