第一章 STM32学习笔记之新建标准库
第二章 STM32学习笔记之GPIO及点灯实例
第N章 STM32学习笔记之OLED屏幕
第N章 STM32学习笔记之EXTI外部中断
第N章 STM32学习笔记之对射式红外传感器计次
第N章 STM32学习笔记之TIM定时中断
目录
系列文章目录
1. TIM定时中断
1.1. 简介
1.2. 定时器类型
1.2.1. 基本定时器
1.2.2. 通用定时器
1.2.3. 高级定时器
1.3. 定时中断基本结构
1.4. 时序图
1.4.1. 预分频器时序
1.4.2. 计数器时序
1.4.3. 计数器无预装时序
1.4.4. 计数器有预装时序
2. 定时器内部时钟配置
2.1. 模块化软件开发
2.2. 初始化函数
2.2.1. RCC时钟开启
2.2.2. 选择时钟源
2.2.3. 配置时基单元
2.2.4. 使能中断
2.2.5. 配置NVIC
2.2.6. 启动定时器
2.3. 中断函数编写
2.4. 声明初始化函数
2.5. main函数处理
3. 定时器外部时钟配置
3.1. 区别
3.1.1. 时钟源
3.1.2. 添加GPIO模块
3.1.3. 时基单元
3.1.4. 功能函数实现
总结
前言:
笔记:跟着B站教学视频做的学习笔记
TIM(Timer)定时器
根据定时器的类型,分为了高级定时器、通用定时器和基本定时器
基本定时器可以完成定时中断和主模式触发DAC的功能,但只能选择内部时钟,即系统频率72MHz
通用定时器拥有基本定时器全部功能,还能外接时钟,即它的时钟源可以选择内部的72MHz时钟,还可以选择外部时钟,外部时钟常用的是TIMx_ETR
即选择时钟后,经过分频,计数器计数,到了重装载值时触发中断,中断信号经过中断输出控制到NVIC申请中断
计数器计数频率:CK_CNT = CK_PSC /(PSC+1)
计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC /(PSC+1) / (ARR + 1)
无预装时,当重装载值发生改变,计数寄存器会直接更新,如果计数到了就直接变,如果计数已经超过了,它会默认先计数到FFFF再重新回到0重新开始
有预装时,会多了一个影子寄存器,当自动加载寄存器发生改变时,它先等计数器计数到位了,影子寄存器才发生改变,计数器重新开始计数,而不是立即改变
根据定时器基本结构图,得出配置定时器外部时钟主要有以下步骤:
本次实操是基于新建好标准库工程之后
新增Timer.c和Timer.h文件到System组里,具体操作见《STM32模块化软件开》链接
根据STM32的系统结构得出TIM定时器是挂在APB1总线上的,开启时钟后,定时器的基准时钟和整个外设的工作时钟都会同时开启,
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
}
对于定时中断,选择内部的时钟源,
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InternalClockConfig(TIM2);
}
根据公式CK_PSC /(PSC+1) / (ARR + 1),为了配1秒的中断(1Hz),所以ARR选择为(10000-1),即72000000 /(PSC+1) / (10000 - 1 + 1)
根据公式CK_PSC /(PSC+1) / (ARR + 1),为了配1秒的中断(1Hz),所以PSC选择为(7200-1),即72000000 /(7200 - 1+1) / (10000 - 1 + 1)= 1Hz
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct) ;
}
使能中断,就是允许中断更新中断输出到NVIC中
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct) ;
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
}
在NVIC中打开定时器中断的通道,并分配一个优先级,详细步骤见《STM32学习笔记之EXTI应用实例》链接
因为NVIC是内核外设,它的库函数被ST发配到misc.h(杂项)文件里,在这里找它的库函数
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct) ;
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStruct);
}
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct) ;
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStruct);
TIM_Cmd(TIM2,ENABLE);
}
在STM32中,中断通道的名字都是固定的,每个中断通道都对应一个中断函数,可以在启动文件里查看,里面以“ IRQHandler ”结尾的字符串就是中断函数的名字
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
在.h文件里声明刚刚编写的传感器初始化函数,这样在别的文件里就能调用该函数了,中断函数不用声明
声明红外传感器.h文件和调用初始化函数
通过OLED屏显示时间:功能函数如下
外部时钟采用红外传感器计数来模拟时钟,因为使用的是TIM2_CH1_ETR这个外部时钟,对应的引脚是PA0,所以要将红外传感器的AO口接到PA0引脚上
把2.2.2里的时钟源"TIM_InternalClockConfig函数"换成TIM_ETRClockMode2Config函数"
开启时钟并且初始化GPIO
将周期改成10,分频改成1,即触发一次加一次,加到10后触发中断
计数函数:
uint16_t Timer_GetCounter(void)
{
return TIM_GetCounter(TIM2);
}
主函数:
END
本章节记录了STM的定时器相关操作,无论是内部时钟或者外部时钟都有样例讲解