目录
1. 通用定时器简介(F1为例)
2. 通用定时器框图
①时钟源
②控制器
③时基单元
④输入捕获
⑤捕获/比较(公共)
⑥输出比较
3.时钟源配置
3.1 计数器时钟源寄存器设置方法
3.2 外部时钟模式1
3.3 外部时钟模式2
3.4 定时器级联
4. 通用定时器中断实验
1. 通用定时器简介(F1为例)
F1系列通用定时器有4个,TIM2/TIM3/TIM4/TIM5
主要特性:
16位递增、递减、中心对齐计数器(计数值:0~65535);
16位预分频器(分频系数:1~65536);
可用于触发DAC、ADC;
在更新事件、触发事件、输入捕获、输出比较时,会产生中断/DMA请求;
4个独立通道,可用于:输入捕获、输出比较、输出PWM、单脉冲模式;
使用外部信号控制定时器且可实现多个定时器级联的同步电路(用一个定时器的溢出事件作为下一个定时器的时钟源驱动计数器计数);
支持编码器和霍尔传感器电路等(用于电机)。
2. 通用定时器框图
定时器的核心是③时基单元,不管是通用定时器还是高级定时器,都是在时基单元的基础上拓展出来的
将通用定时器分为六部分来一一介绍:
①时钟源
通用定时器的时钟源有4类
第一类 内部时钟(CK_INT),来自外设总线APB提供的时钟。
第二类 内部触发输入(ITR0~ITR1)。
第三类 外部时钟模式2(TIMx_ETR),来自IO口复用为TIMx_ETR引脚。
第四类 外部时钟模式1,来自定时器通道TIMx_CH1、TIMx_CH2。
为什么说不来自通道3和通道4,因为①时钟源框图里没有对应的TI3FP和TI4FP信号!
TI1F_ED是双边沿检测信号,而TI1FP1和TI2FP2都是单边沿。
我们可以在《数据手册》或《原理图》中找到管脚定义,各个功能对应的IO口;
总结:
1. 内部时钟(CK_INT),来自外设总线APB提供的时钟
2. 外部时钟模式1:外部输入引脚(TIx),来自定时器通道1或者通道2引脚的信号
3. 外部时钟模式2:外部触发输入(ETR),来自可以复用为TIMx_ETR的IO引脚
4. 内部触发输入(ITRx),用于与芯片内部其它通用/高级定时器级联
②控制器
TRGO触发信号可以触发DAC、ADC以及到其他寄存器。
什么叫触发到其他寄存器呢?
触发输出信号会连接另一寄存器的内部触发输入信号(ITR0~ITR1),从而实现级联的功能;
③时基单元
原理与基本定时器相同,不在赘述,可以参考基本定时器部分帖子!
④输入捕获
应用流程:比如外部信号通过通道1复用的IO口进入通道1产生TI1信号,经过输入滤波器和边沿检测器产生TI1FP1和TI1FP2,通过相关配置选择映射到IC1或IC2(也可理解为输入捕获通道1或输入捕获通道2),经过预分频器,假设边沿检测器配置为上升沿检测,当捕获到上升沿时,会产生捕获事件U并把计数器的值转移到捕获/比较寄存器里,也会产生捕获中断CC1I(需要我们自己开启)。其他通道类似。
(通用IO,就是GPIO外设输入出输出功能。复用就是作为其他外设的的输入或者输出使用)
为什么要有输入滤波器和边沿检测器呢?
答:当外部来一个脉冲信号,这个信号不一定是稳定的,可能会有毛刺,而这些毛刺的频率一定会比脉冲高电平的信号要高,因此控制输入滤波器就可以把高频的毛刺信号过滤掉,来保证脉冲信号的稳定;
边沿检测器的作用是检测信号是上升沿还是下降沿。
⑤捕获/比较(公共)
属于公共部分。
⑥输出比较
应用流程:我们会往捕获/比较寄存器里写入比较值,计数器正常计数,当CNT计数器的计数值=捕获/比较寄存器的比较值(影子)时,会产生比较事件、输出参考信号(OC1REF,高电平有效)、比较中断(前提要配置),来进行输出控制(8种模式)产生OC1信号通过通道1输出给对应的IO口。其他通道类似。
图下面的ETRF信号是控制输出参考信号OC1REF的,会把OC1REF强制清零。
总结:输入捕获和输出比较是分时复用的,当输入捕获模块工作时,输出比较模块失效,当输出比较模块工作时,输入捕获模块失效。
3.时钟源配置
3.1 计数器时钟源寄存器设置方法
计数器时钟选择类型 | 设置方法 |
内部时钟(CK_INT) | 设置TIMx_SMCR的SMS=000,ECE保持为0 |
外部时钟模式1:外部输入引脚(TIx) | 设置TIMx_SMCR的SMS=111,ECE保持为0 |
外部时钟模式2:外部触发输入(ETR) | 设置TIMx_SMCR的ECE=1 |
内部触发输入(ITRx) | 设置可参考STM32F10xxx参考手册_V10(中文版).pdf 14.3.15节 |
从模式控制寄存器(TIMx_SMCR):
3.2 外部时钟模式1
接下来介绍“输入捕获滤波”、“边沿检测方式选择”两部分寄存器配置,以及从模式控制寄存器(TIMx_SMCR) 。
1. 捕获/ 比较模式寄存器 1(TIMx_CCMR1)的输入捕获模式:
当IC1F[3:0]=0000,无滤波,以 采样,而 = = (为来自APB总线的时钟)。
控制寄存器 1(TIMx_CR1):
当IC1F[3:0]=0011,采样频率 = ,N=8。
怎么理解N=8?
“数字滤波器由一个事件计数器组成,它记录到N个事件后会产生一个输出的跳变”,意思是记录到的N个信号全都相同,信号才更新,否则不以第一次采样到的电平为准;举例说明:第一次采样6次低电平,2次高电平,不满足8次采样都相同的条件,因此以第一次采样到的低电平为准输出低电平,第二次采样到的都是高电平,那么电平跳变为高。
目的是把有毛刺的部分过滤掉!
2. 捕获/ 比较使能寄存器(TIMx_CCER):
3. 从模式控制寄存器(TIMx_SMCR):
3.3 外部时钟模式2
接下来介绍“外部触发极性、外部触发预分频、滤波器”的寄存器配置。
从模式控制寄存器(TIMx_SMCR):
3.4 定时器级联
同样也是介绍相关寄存器功能:
控制寄存器 2(TIMx_CR2):
从模式控制寄存器(TIMx_SMCR):
由上表可知,当TIM2作为从定时器,TS = 000 时,对应的主模式定时器为TIM1。
4. 通用定时器中断实验
tim.c代码
#include "./BSP/TIMER/gtim.h"
#include "./BSP/LED/led.h"
TIM_HandleTypeDef g_timx_handle; /* 定时器x句柄 */
/**
* @brief 通用定时器TIMX定时中断初始化函数
* @note
* 通用定时器的时钟来自APB1,当PPRE1 ≥ 2分频的时候
* 通用定时器的时钟为APB1时钟的2倍, 而APB1为36M, 所以定时器时钟 = 72Mhz
* 定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
* Ft=定时器工作频率,单位:Mhz
*
* @param arr: 自动重装值。
* @param psc: 时钟预分频数
* @retval 无
*/
void gtim_timx_int_init(uint16_t arr, uint16_t psc)
{
GTIM_TIMX_INT_CLK_ENABLE(); /* 使能TIMx时钟 */
g_timx_handle.Instance = GTIM_TIMX_INT; /* 通用定时器x */
g_timx_handle.Init.Prescaler = psc; /* 预分频系数 */
g_timx_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
g_timx_handle.Init.Period = arr; /* 自动装载值 */
HAL_TIM_Base_Init(&g_timx_handle);
HAL_NVIC_SetPriority(GTIM_TIMX_INT_IRQn, 1, 3); /* 设置中断优先级,抢占优先级1,子优先级3 */
HAL_NVIC_EnableIRQ(GTIM_TIMX_INT_IRQn); /* 开启ITMx中断 */
HAL_TIM_Base_Start_IT(&g_timx_handle); /* 使能定时器x和定时器x更新中断 */
}
/**
* @brief 定时器中断服务函数
* @param 无
* @retval 无
*/
void GTIM_TIMX_INT_IRQHandler(void)
{
/* 以下代码没有使用定时器HAL库共用处理函数来处理,而是直接通过判断中断标志位的方式 */
if(__HAL_TIM_GET_FLAG(&g_timx_handle, TIM_FLAG_UPDATE) != RESET)
{
LED1_TOGGLE();
__HAL_TIM_CLEAR_IT(&g_timx_handle, TIM_IT_UPDATE); /* 清除定时器溢出中断标志位 */
}
}
与基本定时器不同的是,通用定时器的中断直接通过判断中断标志位的方式来实现,本质上相同!
我们进入到“基本定时器代码”里的定时器中断公共处理函数当中,找到更新中断,发现函数作用也是先判断标志位,后执行定时器更新中断回调函数(弱函数),相当于层层嵌套,本质就是对中断标志位进行判断-----是否发生更新中断!
main.c代码:
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
led_init(); /* 初始化LED */
gtim_timx_int_init(5000 - 1, 7200 - 1); /* 10Khz的计数频率,计数5K次为500ms */
while (1)
{
LED0_TOGGLE();
delay_ms(200);
}
}
通用定时器中断实验的实现效果和基本定时器中断实验的实验效果相同! 主要是学习思路!
例程代码请在正点原子官网获得!
本篇完。
本人博客仅代表个人见解方便记录成长笔记。
若有不足,请指出,感谢您的阅读!