一、定时中断
Init
cs
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_ClearFlag(TIM2, TIM_IT_Update);
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 = 2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStruct);
TIM_Cmd(TIM2, ENABLE);
定时器时钟与基础配置
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
开启 TIM2 时钟,APB1 外设默认关时钟,不开则后续配置无效。
TIM_InternalClockConfig(TIM2);
指定 TIM2 用内部 72MHz 时钟,默认配置,可省略。
定时器时基核心配置
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
定义时基配置结构体,标准库固定写法。
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
时钟不分频,与定时无关,固定配置。
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
向上计数,从 0 到预设值溢出触发中断,定时中断默认模式。
TIM_TimeBaseInitStruct.TIM_Period = 10000 - 1;
自动重装值 9999,计数器从 0 开始,减 1 才是 10000 次计数。
TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1;
预分频器 7199,减 1 实现 7200 倍分频,72MHz 主频分频为 10kHz 计数时钟。
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
通用定时器无此功能,固定置 0。
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);
将配置写入寄存器,时基配置正式生效。
定时器中断配置
TIM_ClearFlag(TIM2, TIM_IT_Update);
清除初始化产生的中断标志,防止启动后立即触发中断。
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
使能 TIM2 更新中断,允许溢出时发送中断请求。
NVIC 中断控制器配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
配置中断优先级分组 2,工程仅需配置一次。
NVIC_InitTypeDef NVIC_InitStruct;
定义 NVIC 配置结构体,标准库固定写法。
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
指定配置 TIM2 对应的中断通道。
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
使能 TIM2 中断通道,允许 NVIC 处理其请求。
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
设置 TIM2 抢占优先级 2,分组 2 下范围 0-3。
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
设置 TIM2 响应优先级 1,分组 2 下范围 0-3。
NVIC_Init(&NVIC_InitStruct);
写入 NVIC 配置,中断优先级与使能正式生效。
启动定时器
TIM_Cmd(TIM2, ENABLE);
启动 TIM2,计数器开始工作,正式进入 1 秒定时。
核心定时逻辑
72MHz 主频经 7200 倍分频得 10kHz 计数时钟,每 100μs 计数 1 次;计数器从 0 数到 9999 共 10000 次,总计 1 秒,溢出触发更新中断。
中断函数
TIM2的的中断函数是TIM2_IRQHandler(void),因为我们要在显示屏中显示Num,所以可以直接将这个中断函数放在main文件中,如果放在定时器的文件中,那么就extern uint16_t Num;声明在这个文件中有定义这个变量,调用的时候程序就会自行查找。
cs
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
{
Num++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
使用TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)去获取定时中断的标准位
中断结束后要手动复位中断TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
调用
cs
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num = 0;
int main(void)
{
OLED_Init();
Timer_Init();
OLED_ShowString(1,1,"Num:");
while(1)
{
OLED_ShowNum(1,5,Num,5);
OLED_ShowNum(2,5,TIM_GetCounter(TIM2),5);
}
}
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
{
Num++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
使用TIM_GetCounter(TIM2)获取计数值
二、外部时钟
cs
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
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);
GPIO_SetBits(GPIOA, GPIO_Pin_0);
TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x0F);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 10 - 1;
TIM_TimeBaseInitStruct.TIM_Prescaler = 1 - 1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);
TIM_ClearFlag(TIM2, TIM_IT_Update);
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 = 2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStruct);
TIM_Cmd(TIM2, ENABLE);
因为采用的是外部时钟源,所以要先打开GPIO,输入模式可以任意配置。GPIOA0 是 TIM2 的外部触发输入(ETR)复用引脚。
TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x0F);
配置 TIM2 使用外部 ETR 引脚(PA0)的脉冲信号作为计数时钟源
参数含义说明:
TIM_ExtTRGPSC_OFF:外部信号不分频,直接作为定时器时钟TIM_ExtTRGPolarity_NonInverted:外部信号高电平 / 上升沿有效,不反相0x0F:外部信号滤波,有效滤除引脚杂波,让触发更稳定
这个滤波可以不加,直接写0x00,但是上电测试不稳定的话可以加上合适的滤波。
TIM_TimeBaseInitStruct.TIM_Prescaler = 1 - 1;
预分频器设为 0,实际不分频,定时器直接以外部 ETR 输入的原始信号作为计数时钟。
TIM_TimeBaseInitStruct.TIM_Period = 10 - 1;自动重装值设为 9,计数器从 0 数到 9 即溢出触发中断,计数仅 10 次。
cs
uint16_t Timer_Getcounter(void)
{
return TIM_GetCounter(TIM2);
}
封装 TIM_GetCounter (TIM2) 为独立函数,方便工程中任意位置直接调用获取 TIM2 当前计数值,无需重复写原函数,简化代码调用,提升可读性和复用性。
其他的代码和上面基本一致
cs
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num = 0;
int main(void)
{
OLED_Init();
Timer_Init();
OLED_ShowString(1,1,"Num:");
OLED_ShowString(2,1,"CNT:");
while(1)
{
OLED_ShowNum(1,5,Num,5);
OLED_ShowNum(2,5,Timer_Getcounter(),5);
}
}
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
{
Num++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}