一、定时器
通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成。 它适用于多种场合,包括测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)。
1.1 主要特性
通用TIMx (TIM2、TIM3、TIM4和TIM5)定时器功能包括:
- 16位向上、向下、向上/向下自动装载计数器
- 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意数值
- 4个独立通道:
- 输入捕获
- 输出比较
- PWM生成(边缘或中间对齐模式)
- 单脉冲模式输出
1.2 结构框图
如上是定时器基本定时功能的结构框图。从上图不难看出,要实现基本定时功能需要涉及到3个器件;这三个也是定时器实现定时的根本:预分频器(PSC)、自动重装载寄存器(ARR)、计数器(CNT)。
计数器按照定时器本身的工作频率进行计数实现了计时功能;如果想要实现最终的定时功能就需要我们的自动重装载寄存器了;它的功能简而言之就是可以给计数器设定一个目标值,CNT每次计数之后都会和ARR里的值进行比较,当CNT的值等于ARR的值时会出发溢出事件。至此就实现了定时功能;既然计数器+自动重装载寄存器这两个器件就已经实现了定时功能,那预分频器就毫无意义了吗?很显然不是的。预分频器的主要功能是降低CNT的计数频率,从而使我们可以计更长的时间;分频值越大,会导致CNT计数的耗时增加,整体的计时时长也会相应增加。
实际使用过程中,为方便使用,我们常用的分频值如下表分布:
工作主频 | 分频值 | CNT计数频率 | 单次计数耗时 | 最长溢出时间 |
72MHz | 1 | 72MHz | 1/72us | 65535 * 1/72us ≈ 910us |
72 | 1MHz | 1us | 65535 * 1us = 65535us ≈ 65.54ms | |
7200 | 10KHz | 0.1ms | 65535 * 0.1ms = 6553.5ms ≈ 6.55s | |
36000 | 2KHz | 0.5ms | 65535 * 0.5ms = 32767.5ms ≈ 32.77s |
1.3 相关寄存器
1.4 相关配置代码
/******************************************************************************\
函数功能:TIM1基本定时功能初始化配置
形参说明:
返 回 值:
\******************************************************************************/
void TIM1_Init(u16 psc, u16 arr)
{
//1,打开外设时钟
RCC->APB2ENR |= 1<<11; //TIM1外设时钟使能
RCC->APB2RSTR |= 1<<11; //开启TIM1复位
RCC->APB2RSTR &= ~(1<<11);//关闭TIM1复位
//2,配置核心寄存器
TIM1->CNT = 0;
TIM1->PSC = psc - 1; //填写分频值
TIM1->ARR = arr - 1; //重装载值
//3,中断配置
TIM1->DIER |= 1<<0; //允许更新中断
CM3_NVIC_SetPriority(TIM1_UP_IRQn, 2, 2); //设置中断优先级
TIM1->CR1 &= ~(1<<0); //计数器失能
}
//TIM1中断服务函数
u8 OverFlowCnt; //记录定时器溢出次数
void TIM1_UP_IRQHandler(void)
{
if(TIM1->SR & 1<<0)
{
OverFlowCnt++;
TIM1->SR &= ~(1<<0); //清除溢出标志位
}
}
二、外部中断
对于互联型产品,外部中断/事件控制器由20个产生事件/中断请求的边沿检测器组成,对于其它产品,则有19个能产生事件/中断请求的边沿检测器。每个输入线可以独立地配置输入类型(脉冲或挂起)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。每个输入线都可以独立地被屏蔽。挂起寄存器保持着状态线的中断请求。
2.1 主要特性
EXTI控制器的主要特性如下:
- 每个中断/事件都有独立的触发和屏蔽
- 每个中断线都有专用的状态位
- 支持多达20个软件的中断/事件请求
- 检测脉冲宽度低于APB2时钟宽度的外部信号。参见数据手册中电气特性部分的相关参数。
2.2 结构框图
2.3 相关代码配置
/******************************************************************************\
函数功能:外部中断线0初始化配置---以KEY1(PA0)为例
形参说明:
返 回 值:
\******************************************************************************/
void EXTI0_Init(void)
{
//1,开放来自线0上的中断请求
EXTI->IMR |= 1<<0;
//2,设置需要监测边沿(上升沿、下降沿、上升沿和下降沿)
EXTI->RTSR |= 1<<0; //允许输入线0上的上升沿触发
EXTI->FTSR |= 1<<0; //允许输入线0上的下降沿触发
//3,选择EXTI0的信号源
//3.1 打开AFIO的时钟
RCC->APB2ENR |= 1<<0;
//3.2 选择PA0作为EXTI0的信号源
AFIO->EXTICR[0] &= ~(0xF<<0);
//4,设置中断优先级
CM3_NVIC_SetPriority(EXTI0_IRQn, 2, 2);
}
//EXTI0的中断服务函数
extern u8 OverFlowCnt; //定时器溢出次数
void EXTI0_IRQHandler(void)
{
u32 total;
if(EXTI->PR & 1<<0) //PR的第0位的状态
{
//判断到底是触发了什么边沿
if(KEY1) //上升沿
{
TIM1->CNT = 0;
TIM1->CR1 |= 1<<0; //启动计数器
}
else //下降沿
{
TIM1->CR1 &= ~(1<<0); //关闭计数器
total = OverFlowCnt * 65000 + TIM1->CNT; //计算总时长
printf("捕获时长:%dus, %.2fms, %.2fs\n", total, total/1000.0, total/1000000.0);
OverFlowCnt = 0;
}
EXTI->PR |= 1<<0; //清除标志位
}
}
2.4 小结
在使用外部中断时,有一步是要选择外部中断线的信号源;这一步设置的寄存器比较特殊,位于AFIO寄存器中。通过AFIO_EXTICRx配置GPIO线上的外部中断/事件,必须先使能AFIO时钟。
三、效果展示
至此,基本实现了对按键按下时长的捕获,当然实际的测试结果还是会存在一定的误差;实际上,这次实现的功能约等于定时器自带的输入捕获功能。