STM32F103 basic定时器的介绍和应用
目录
概述
1 定时器的重要参数
1.1 核心概念
1.2 使用步骤 (以产生固定周期中断为例)
2 使用STM32Cube工具配置参数
2.1 配置定时器参数
2.2 配置项目参数
3 定时器的使用
3.1 实现功能代码
3.2 测试
概述
STM32F103 的基础定时器(TIM6 和 TIM7)是相对简单的定时器,主要用于产生精确的时间基准、触发 DAC 转换或驱动软件定时器。它们没有输入捕获或输出比较通道,结构简单,是理解 STM32 定时器工作原理的良好起点。
1 定时器的重要参数
1.1 核心概念
时钟源 (Clock Source): 定时器的核心是一个计数器,它需要一个时钟信号来驱动计数。基础定时器的时钟源固定来自 APB1 总线时钟 (
PCLK1
)。预分频器 (Prescaler - PSC): 用于将输入的时钟频率进行分频,降低计数器的计数速度。
CK_CNT = PCLK1 / (PSC + 1)
。自动重装载寄存器 (Auto-Reload Register - ARR): 定义了计数器计数的上限(周期)。计数器从 0 开始计数,达到 ARR 值时,产生更新事件 (Update Event - UE),然后通常复位回 0(或根据配置),重新开始计数。
计数器 (Counter - CNT): 实际的计数值寄存器,在时钟驱动下递增(或递减)。
更新事件 (Update Event - UE): 当计数器达到 ARR 值(溢出)时发生。这是定时器最基本的“定时完成”信号。
更新中断 (Update Interrupt - UI): 如果使能了中断,当更新事件发生时,会触发中断请求。
1.2 使用步骤 (以产生固定周期中断为例)
1) 使能定时器时钟
-
定时器挂载在 APB1 总线下。
-
在 RCC 寄存器中使能 TIM6 (或 TIM7) 的时钟。
// 标准外设库 (Standard Peripheral Library - SPL)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);// HAL 库
__HAL_RCC_TIM6_CLK_ENABLE(); // 通常写在 HAL_TIM_Base_Init() 内部
2) 配置时基单元 (Time Base Unit)
这是最核心的配置,设置 PSC 和 ARR 以确定定时器的溢出周期。
计算公式:
定时周期 T = (ARR + 1) * (PSC + 1) / TIMx_CLK
TIMx_CLK
: 通常是PCLK1
(APB1 时钟频率)。注意 STM32F103 的 APB1 最大频率为 36MHz。
PSC
: 预分频器值 (0 到 65535)。
ARR
: 自动重装载值 (0 到 65535)。目标: 例如,系统时钟
SYSCLK = 72MHz
,APB1
预分频器为/2
,则PCLK1 = 36MHz
(TIMx_CLK = PCLK1 = 36MHz
)。要产生一个 1ms (0.001s) 的周期中断:
T = 0.001 = (ARR + 1) * (PSC + 1) / 36000000
(ARR + 1) * (PSC + 1) = 36000
可以选取
PSC = 35999
(即分频 36000 倍),则ARR + 1 = 1
->ARR = 0
。也可以选取
PSC = 35
(分频 36 倍),则ARR + 1 = 1000
->ARR = 999
。通常让
ARR
大一些,PSC
小一些,分辨率更高(计数器步进时间更小)。但两种组合都能达到 1ms 周期。
参考代码如下:
// SPL 配置时基结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 999; // ARR 值 (0 - 65535)
TIM_TimeBaseStructure.TIM_Prescaler = 35; // PSC 值 (0 - 65535)
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // 基础定时器通常忽略或设为0
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 基础定时器只能向上计数
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure); // 应用配置// HAL 配置时基结构体
TIM_HandleTypeDef htim6;
htim6.Instance = TIM6;
htim6.Init.Prescaler = 35; // PSC 值
htim6.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数
htim6.Init.Period = 999; // ARR 值
htim6.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // 通常忽略
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; // ARR是否缓冲 (可选)
if (HAL_TIM_Base_Init(&htim6) != HAL_OK) {// 初始化错误处理
}
3) 使能更新中断 (如果需要中断):
-
配置定时器中断源(更新事件中断)。
-
配置 NVIC (嵌套向量中断控制器) 以允许定时器中断请求到达 CPU。
// SPL
TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE); // 使能 TIM6 更新中断// 配置 NVIC (需先定义 NVIC 初始化结构体)
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn; // TIM6 全局中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);// HAL
// HAL_TIM_Base_Init() 内部通常会调用 HAL_NVIC_SetPriority() 和 HAL_NVIC_EnableIRQ()
// 但通常需要在主程序中显式调用中断使能函数
HAL_NVIC_SetPriority(TIM6_IRQn, 0, 0); // 设置优先级
HAL_NVIC_EnableIRQ(TIM6_IRQn); // 使能 TIM6 中断通道
4) 使能定时器
-
启动计数器开始计数。
// SPL
TIM_Cmd(TIM6, ENABLE); // 启动 TIM6// HAL (使用中断模式启动)
HAL_TIM_Base_Start_IT(&htim6);
5) 编写中断服务程序 (ISR - 如果使用了中断):
-
在中断服务程序中,必须检查中断标志位,并在处理完成后清除该标志位。
-
这里是执行定时任务的代码位置(例如,翻转 LED、更新变量、读取传感器等)。
// SPL
void TIM6_IRQHandler(void) { // TIM6 中断服务函数名是固定的if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) { // 检查更新中断标志// 用户代码 - 在这里执行你的定时任务// 例如: GPIO_ToggleBits(GPIOA, GPIO_Pin_0); // 翻转 PA0 引脚状态TIM_ClearITPendingBit(TIM6, TIM_IT_Update); // 清除更新中断标志位!非常重要!}
}// HAL
void TIM6_IRQHandler(void) {HAL_TIM_IRQHandler(&htim6); // HAL 库的通用中断处理函数
}// HAL 回调函数 (在 HAL_TIM_IRQHandler 内部调用)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {if (htim->Instance == TIM6) { // 检查是哪个定时器触发的回调// 用户代码 - 在这里执行你的定时任务// 例如: HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0); // 翻转 PA0}
}
6)不使用中断的查询方式:
如果你不需要中断,只想在主循环中查询定时器是否溢出:
// SPL
TIM_Cmd(TIM6, ENABLE); // 启动定时器
while (1) {if (TIM_GetFlagStatus(TIM6, TIM_FLAG_Update) != RESET) { // 查询更新标志// 用户代码 - 执行定时任务TIM_ClearFlag(TIM6, TIM_FLAG_Update); // 清除更新标志}// ... 其他代码 ...
}// HAL
HAL_TIM_Base_Start(&htim6); // 启动定时器 (不使用中断)
while (1) {if (__HAL_TIM_GET_FLAG(&htim6, TIM_FLAG_UPDATE) != RESET) { // 查询标志if (__HAL_TIM_GET_IT_SOURCE(&htim6, TIM_IT_UPDATE) != RESET) { // 检查中断源使能 (可选)// 用户代码__HAL_TIM_CLEAR_FLAG(&htim6, TIM_FLAG_UPDATE); // 清除标志}}// ... 其他代码 ...
}
2 使用STM32Cube工具配置参数
2.1 配置定时器参数
1) 在Clcok configuration 中配置时钟,具体参数如下:
2) 定时器相关的配置,选择Timer-6,配置参数,产生1ms的定时中断
3) 配置中断函数,使能中断,并设置中的函数的优先级
2.2 配置项目参数
在Project Setting卡片上配置开发工具和堆栈参数
完成以上参数配置后,可点击GNERATE CODE生成代码
项目结构如下:
3 定时器的使用
3.1 实现功能代码
1) 启动定时器
源代码如下:
HAL_TIM_Base_Start_IT(&htim6);
2) 重写回调函数,并在该函数中调用相应的功能
源代码如下:
/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{/* USER CODE BEGIN Callback 0 */static int count = 0;/* USER CODE END Callback 0 */if (htim->Instance == TIM6) {if( count %150 == 0){}count++;}/* USER CODE BEGIN Callback 1 */if (htim->Instance == TIM7) {}/* USER CODE END Callback 1 */
}
/* USER CODE END 4 */
3.2 测试
完成以上代码后,可编译代码并下载到板卡中运行,运行情况如下: