人若无名 便可潜心练剑.|

hazy1k

园龄:7个月粉丝:14关注:0

2024-10-02 10:50阅读: 29评论: 0推荐: 0

第8章 定时器中断实验

第八章 定时器中断实验

1. 硬件设计

本实验用到的硬件资源有:

  • 指示灯DS0和DS1

  • 定时器TIM3

本章将通过 TIM3 的中断来控制 DS1 的亮灭, DS1 是直接连接到 PF10 上的,这个前面已经有介绍了。而 TIM3 属于 STM32F4 的内部资源,只需要软件设置即可正常工作。

2. 软件设计

2.1 编程大纲

  1. 定时器TIM3初始化

  2. 配置中断和编写中断函数(控制DS1翻转)

  3. 主函数测试,配置定时器发生中断为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 中国大陆许可协议进行许可。

posted @   hazy1k  阅读(29)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起