第8章 定时器中断实验
第八章 定时器中断实验
1. 硬件设计
本实验用到的硬件资源有:
-
指示灯DS0和DS1
-
定时器TIM3
本章将通过 TIM3 的中断来控制 DS1 的亮灭, DS1 是直接连接到 PF10 上的,这个前面已经有介绍了。而 TIM3 属于 STM32F4 的内部资源,只需要软件设置即可正常工作。
2. 软件设计
2.1 编程大纲
-
定时器TIM3初始化
-
配置中断和编写中断函数(控制DS1翻转)
-
主函数测试,配置定时器发生中断为500ms
2.2 代码分析
2.2.1 定时器宏定义
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
#define TIMx TIM3
#define TIM_IRQn TIM3_IRQn
void TIM_Config(uint16_t arr, uint16_t psc);
#endif /* __TIMER_H */
2.2.2 定时器TIM3初始化
TIM_HandleTypeDef TIM3_Handler; // 声明一个定时器句柄
// 前置知识
/*
arr:自动重装载值,计数器到达该值时,计数器自动重装载,计数器重新计数。
psc:预分频值,用来设置时钟源的频率,以此来控制计数器的频率。
定时器溢出时间计算公式:
Time= ((arr+1)*(psc+1))/fclk us ,这里的fclk是定时器工作频率,单位为Hz。
我们这个工程使用的是定时器3,定时器挂在APB1上,时钟位HCLK/2
*/
void TIM_Config(uint16_t arr, uint16_t psc)
{
TIM3_Handler.Instance = TIM3;
TIM3_Handler.Init.Prescaler = psc;
TIM3_Handler.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数模式
TIM3_Handler.Init.Period = arr;
TIM3_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // 时钟分频系数为1
HAL_TIM_Base_Init(&TIM3_Handler);
HAL_TIM_Base_Start_IT(&TIM3_Handler);
}
// 此函数会被HAL_TIM_Base_Init()调用,用来初始化定时器
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM3)
{
__HAL_RCC_TIM3_CLK_ENABLE();
HAL_NVIC_SetPriority(TIM_IRQn, 1, 3);
HAL_NVIC_EnableIRQ(TIM_IRQn); // 开启定时器中断
}
}
2.2.3 中断函数及其服务函数
// 中断服务函数
void TIM3_IRQHandler(void)
{
HAL_TIM_IRQHandler(&TIM3_Handler); // 调用HAL库的中断处理函数
}
// 回调函数,中断服务函数会调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM3)
{
LED1 = !LED1;
}
}
2.2.4 主函数测试
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "timer.h"
#include "led.h"
int main(void)
{
HAL_Init();
Stm32_Clock_Init(336,8,2,7);
delay_init(168);
uart_init(115200);
LED_Init();
// 定时器初始化,设置重装值(arr)为5000-1,分频系数(psc)为8400-1
// 这个工程中我们的定时器的工作频率为84MHz/8400=10KHz
// 周期计算Time = ((arr+1)*(psc+1))/fclk = 5000*(8400)/84000000 = 500ms
TIM_Config(5000-1,8400-1);
while(1)
{
LED0 =! LED0;
delay_ms(200);
}
}
// LED0每200ms翻转一次
// LED1通过定时器中断每隔500ms翻转一次
3. 小结
本章还算简单,就用了一个定时器的中断功能,定时器配置为向上计数,500ms后产生中断LED1反转,我们简单回顾一下:
通过 TIM3 中断,每 500 毫秒切换一次 DS1 的状态(亮和灭)。
#include "stm32f4xx_hal.h"
// 定义 LED 引脚
#define LED_PIN GPIO_PIN_0
#define LED_GPIO_PORT GPIOA
// TIM3 中断处理函数
void TIM3_IRQHandler(void)
{
// 检查 TIM3 更新中断标志
if (__HAL_TIM_GET_FLAG(&htim3, TIM_FLAG_UPDATE) != RESET)
{
// 清除更新中断标志
__HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_UPDATE);
// 切换 LED 状态
HAL_GPIO_TogglePin(LED_GPIO_PORT, LED_PIN);
}
}
// 定时器和 GPIO 初始化函数
void Timer_Config(void)
{
TIM_HandleTypeDef htim3;
// 启用 TIM3 时钟
__HAL_RCC_TIM3_CLK_ENABLE();
// 配置 TIM3
htim3.Instance = TIM3;
htim3.Init.Prescaler = 8399; // 84MHz / (8399 + 1) = 10kHz
htim3.Init.Period = 5000 - 1; // 10kHz / 5000 = 2 Hz (500ms)
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
// 初始化 TIM3
HAL_TIM_Base_Init(&htim3);
// 启用 TIM3 更新中断
HAL_TIM_Base_Start_IT(&htim3);
}
void GPIO_Config(void)
{
// 启用 GPIOA 时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置 LED 引脚
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct);
}
int main(void)
{
// 初始化 HAL 库
HAL_Init();
// 配置 GPIO
GPIO_Config();
// 配置 TIM3
Timer_Config();
while (1)
{
// 主循环可以做其他事情
}
}
2024.10.2 第一次修订,后期不再维护
2025.1.13 优化了一下代码
本文作者:hazy1k
本文链接:https://www.cnblogs.com/hazy1k/p/18444487
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步