文章目录
一、TIM定时器
1.1定时器
- TIM(Timer)定时器
- 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
- 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时
- 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
- 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
1.2定时器类型
== STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4==
1.1.1 高级定时器
1.1.2通用定时器
1.1.3基本定时器
二、定时中断基本结构
预分频器时器
预分频器作用:通过分频降低定时器时钟频率,公式为 CK_CNT = CK_PSC / (PSC + 1)(PSC 是预分频器寄存器值)。
- 时序关键节点:
CK_PSC:输入时钟(高频);
CNT_EN:计数器使能信号;
CK_CNT:分频后的计数器时钟(低频,驱动CNT计数);
更新事件(UEV):预分频器参数变更后,触发定时器更新,使新分频值生效。
流程:修改TIMx_PSC寄存器值→预分频缓冲器暂存→更新事件触发时,新值加载到实际计数器,改变CK_CNT频率。
计时器时序
计数器无预装时序
计数器有预装时序
RCC时钟树
三、定时器定时中断
3.1 接线图
3.2代码
main.c
#include "stm32f10x.h" // 包含STM32F10x系列设备头文件,访问寄存器和外设
#include "Delay.h" // 延时功能(未使用,可忽略)
#include "OLED.h" // OLED显示驱动,实现屏幕初始化和显示
#include "CountSensor.h" // 计数传感器(未使用,可忽略)
#include "Timer.h" // TIM2定时器初始化(内部时钟,1秒中断一次)
uint16_t Num = 0; // 记录定时器中断次数(每秒+1)
int main(void)
{
OLED_Init(); // 初始化OLED(配置通信,如I2C/SPI)
Timer_Init(); // 初始化TIM2(内部时钟,周期1秒)
OLED_ShowString(1, 1, "Num:"); // 第1行显示"Num:",标识计数
while(1) // 主循环,持续更新显示
{
OLED_ShowNum(1, 5, Num, 5); // 显示中断次数(右对齐,5位)
OLED_ShowNum(2, 5, TIM_GetCounter(TIM2), 5); // 显示TIM2当前计数值(0~9999循环)
}
}
// TIM2更新中断服务函数(每秒触发一次)
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update)) { // 检查中断标志
Num++; // 计数+1(每秒更新)
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除标志
}
}
Timer.
#include "stm32f10x.h" // 包含STM32F10x系列设备头文件,访问寄存器和外设功能
extern uint16_t Num; // 外部声明计数变量(主程序中定义)
// 定时器TIM2初始化(内部时钟,1秒中断一次)
void Timer_Init(void)
{
// 1. 使能TIM2时钟(APB1总线)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// 2. 配置内部时钟源
TIM_InternalClockConfig(TIM2);
// 3. 时基单元配置(72MHz系统时钟下,1秒周期)
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频(无分频)
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数
TIM_TimeBaseInitStruct.TIM_Period = 9999; // 自动重装值(0~9999,共10000次计数)
TIM_TimeBaseInitStruct.TIM_Prescaler = 7199; // 预分频(7200分频,72MHz/7200=10kHz)
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0; // 重复计数(通用定时器不用)
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct); // 初始化时基
// 4. 清除初始更新标志(避免误触发)
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
// 5. 使能更新中断
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
// 6. NVIC配置(中断优先级)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 分组:2位抢占,2位子优先级
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn; // TIM2中断通道
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; // 使能中断
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2; // 抢占优先级2
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; // 子优先级1
NVIC_Init(&NVIC_InitStruct); // 初始化NVIC
// 7. 启动定时器
TIM_Cmd(TIM2, ENABLE);
}
/*
// TIM2中断服务函数(需取消注释以更新Num)
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) { // 检查更新中断标志
Num++; // 计数变量递增(每秒+1)
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除中断标志
}
}
*/
3.3效果:
Num 变化:每秒递增 1(如:0 → 1 → 2 → …)
CNT 变化:每秒内从 0 递增到 9999,然后归零,Num+1。
四、定时器外部中断
4.1接线图
4.2代码
Timer.c
#include "stm32f10x.h" // 包含STM32F10x系列外设寄存器定义
extern uint16_t Num; // 外部声明的计数变量(需在主程序中定义)
/**
* @brief 定时器TIM2初始化(ETR外部时钟模式)
* @note 存在3处关键问题:
* 1. PA0被配置为上拉输入,与ETR复用功能冲突;
* 2. 中断服务函数被注释,无法响应溢出中断;
* 3. 时基参数过于激进(周期9、预分频0),计数过快。
*/
void Timer_Init(void)
{
// 1. 使能时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能TIM2时钟(APB1总线)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟(APB2总线)
// 2. 配置PA0(冲突点:ETR模式下应自动复用,手动配置输入模式会冲突)
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入(与ETR功能冲突,建议移除)
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 输入模式下速度设置无效,仅语法要求
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 配置PA0引脚
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA
// 3. 配置ETR外部时钟模式2
// 参数:不分频、上升沿触发(NonInverted对应上升沿)、无滤波
TIM_ETRClockMode2Config(TIM2,
TIM_ExtTRGPSC_OFF, // ETR预分频器关闭
TIM_ExtTRGPolarity_NonInverted, // 极性:非反相(上升沿触发)
0x00); // 滤波系数0(无滤波)
// 4. 配置时基单元(参数问题:周期9 + 预分频0 → 每10个脉冲触发一次中断)
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频(不影响计数频率)
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数
TIM_TimeBaseInitStruct.TIM_Period = 10 - 1; // 自动重装值:计数到9时溢出
TIM_TimeBaseInitStruct.TIM_Prescaler = 1 - 1; // 预分频值:0(不分频)
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0; // 重复计数器(通用定时器无效)
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct); // 初始化时基
// 5. 清除更新标志(避免初始化时误触发中断)
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
// 6. 使能更新中断
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 允许TIM2的更新中断
// 7. 配置NVIC中断优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 分组:2位抢占优先级 + 2位子优先级
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn; // 中断通道:TIM2全局中断
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; // 使能中断通道
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2; // 抢占优先级:2
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; // 子优先级:1
NVIC_Init(&NVIC_InitStruct); // 初始化NVIC
// 8. 启动定时器
TIM_Cmd(TIM2, ENABLE); // 启动TIM2计数器
}
/**
* @brief 获取TIM2当前计数值
* @retval TIM2的计数器值(0 ~ TIM_Period)
*/
uint16_t Timer_GetCounter(void)
{
return TIM_GetCounter(TIM2); // 读取TIM2的CNT寄存器值
}
/*
// 【关键缺失】TIM2中断服务函数(当前被注释,导致Num无法递增)
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) { // 检查更新中断标志
Num++; // 计数变量递增
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除中断标志(必须操作)
}
}
*/
main.c
#include "stm32f10x.h" // 包含STM32F10x系列设备的寄存器定义和外设库函数
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"
#include "Timer.h" // 定时器驱动头文件(包含TIM2初始化逻辑)
uint16_t Num = 0; // 全局计数变量,记录定时器中断次数
int main(void)
{
OLED_Init(); // 初始化OLED显示屏(配置通信接口,如I2C/SPI)
Timer_Init(); // 初始化TIM2定时器(ETR外部时钟模式,需PA0输入外部信号)
OLED_ShowString(1, 1, "Num:"); // 在OLED第1行第1列显示字符串"Num:",标识计数结果
OLED_ShowString(2, 1, "CNT:"); // 在OLED第2行第1列显示字符串"CNT:",标识定时器当前计数值
while(1) // 主循环,持续更新显示内容
{
OLED_ShowNum(1, 5, Num, 5); // 在OLED第1行第5列显示Num(右对齐,占5位)
OLED_ShowNum(2, 5, Timer_GetCounter(), 5); // 在OLED第2行第5列显示TIM2当前计数值(右对齐,占5位)
}
}
void TIM2_IRQHandler(void)// TIM2定时器中断服务函数(处理更新中断,每TIM_Period+1个脉冲触发一次)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) // 检查TIM2的更新中断标志是否置位
{
Num++; // 计数变量递增(记录中断次数)
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除中断标志,避免重复触发
}
}
- 定时器配置(Timer.c):
使能 TIM2 和 GPIOA 时钟(PA0 为 ETR 引脚)。
配置 ETR 外部时钟模式(上升沿触发,不分频)。
时基参数:TIM_Period(自动重载值)和TIM_Prescaler(预分频值,需根据外部信号频率调整)。
使能更新中断,配置 NVIC 优先级。
- 中断处理:
每次 TIM2 计数溢出(达到TIM_Period)时,Num加 1,用于统计中断次数。
OLED 实时显示Num(总计数)和TIM2->CNT(当前计数值,0~TIM_Period循环)。
4.3效果:
- 当 PA0 接入外部脉冲信号(如方波)时,TIM2 计数器CNT随脉冲上升沿递增。
- 每计数到 9(即收到 10 个脉冲)时,触发更新中断,Num加 1。
- 计数器每触发一次CNT+1,加到9时,Num+1.