STM32 HAL 库开发之通用定时器中断

发布于:2025-04-15 ⋅ 阅读:(38) ⋅ 点赞:(0)

一、引言

在嵌入式系统开发中,定时器是一种非常重要的外设,它可以用于生成精确的时间延迟、定时执行任务、产生 PWM 信号等。STM32F407 是一款高性能的 32 位微控制器,它提供了多个通用定时器,这些定时器具有丰富的功能和灵活的配置选项。HAL(Hardware Abstraction Layer)库是 ST 公司为 STM32 系列微控制器提供的一种硬件抽象层库,它可以简化开发过程,提高开发效率。本文将详细介绍如何使用 STM32F407 的 HAL 库来配置和使用通用定时器中断。

二、STM32F407 通用定时器概述

2.1 通用定时器特性

STM32F407 的通用定时器(TIM2 - TIM5、TIM9 - TIM14)具有以下特性:

  • 16 位或 32 位向上、向下、向上 / 向下自动重载计数器。
  • 16 位可编程预分频器,用于对计数器时钟频率进行分频。
  • 4 个独立的通道,可用于输入捕获、输出比较、PWM 生成和单脉冲模式输出。
  • 可使用外部信号(ETR)控制定时器。
  • 支持定时器级联功能,可实现更长的定时时间。
  • 支持触发输入,可用于启动定时器、复位定时器等。

2.2 定时器时钟源

通用定时器的时钟源可以有以下几种选择:

  • 内部时钟源(CK_INT):这是最常用的时钟源,它来自于 APB 总线时钟。
  • 外部时钟源 1(ETR):通过外部触发输入引脚 ETR 提供时钟信号。
  • 外部时钟源 2(TIx):通过定时器的输入捕获通道 TIx 提供时钟信号。
  • 内部触发输入(ITR):用于定时器级联时,一个定时器的输出可以作为另一个定时器的时钟源。

三、开发环境搭建

3.1 硬件准备

  • STM32F407 开发板。
  • USB 线,用于连接开发板和计算机。

3.2 软件准备

  • Keil MDK - ARM:一款常用的嵌入式开发工具,用于编写、编译和调试 STM32 程序。
  • STM32CubeMX:ST 公司提供的图形化配置工具,用于生成 STM32 的初始化代码。

四、编写定时器中断服务函数

4.1 打开生成的工程

使用 Keil MDK - ARM 打开 STM32CubeMX 生成的工程。

4.2 编写定时器中断服务函数

stm32f4xx_it.c文件中,可以找到定时器中断服务函数的声明:

void TIM2_IRQHandler(void)
{
  /* USER CODE BEGIN TIM2_IRQn 0 */

  /* USER CODE END TIM2_IRQn 0 */
  HAL_TIM_IRQHandler(&htim2);
  /* USER CODE BEGIN TIM2_IRQn 1 */

  /* USER CODE END TIM2_IRQn 1 */
}

/* USER CODE BEGIN TIM2_IRQn 1 *//* USER CODE END TIM2_IRQn 1 */之间编写定时器中断处理代码。例如,我们可以在中断处理函数中翻转一个 LED 的状态:

#include "main.h"
extern TIM_HandleTypeDef htim2;
extern GPIO_TypeDef* LED_GPIO_Port;
extern uint16_t LED_Pin;

void TIM2_IRQHandler(void)
{
  /* USER CODE BEGIN TIM2_IRQn 0 */

  /* USER CODE END TIM2_IRQn 0 */
  HAL_TIM_IRQHandler(&htim2);
  /* USER CODE BEGIN TIM2_IRQn 1 */
  HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
  /* USER CODE END TIM2_IRQn 1 */
}

4.3 初始化 LED 引脚

main.c文件中,初始化 LED 引脚:

/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim2;
GPIO_InitTypeDef GPIO_InitStruct = {0};

/* USER CODE BEGIN PV */
#define LED_GPIO_Port GPIOA
#define LED_Pin GPIO_PIN_5
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM2_Init(void);

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM2_Init();
  /* USER CODE BEGIN 2 */
  HAL_TIM_Base_Start_IT(&htim2); // 启动定时器并使能中断
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : LED_Pin */
  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);
}

static void MX_TIM2_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 8399;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 9999;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

五、代码解释

5.1 定时器初始化

MX_TIM2_Init函数中,对定时器 TIM2 进行初始化配置:

  • htim2.Instance = TIM2;:指定使用的定时器为 TIM2。
  • htim2.Init.Prescaler = 8399;:设置预分频器的值为 8399,将系统时钟频率分频为 168000000 / (8399 + 1) = 20000Hz。
  • htim2.Init.Period = 9999;:设置自动重载值为 9999,定时器的计数周期为 (9999 + 1) 个时钟周期。
  • htim2.Init.CounterMode = TIM_COUNTERMODE_UP;:设置计数器为向上计数模式。
  • HAL_TIM_Base_Init(&htim2);:初始化定时器的基本参数。

5.2 启动定时器并使能中断

main函数中,使用HAL_TIM_Base_Start_IT(&htim2);启动定时器并使能定时器中断。

5.3 定时器中断服务函数

TIM2_IRQHandler函数中,调用HAL_TIM_IRQHandler(&htim2);处理定时器中断,然后在中断处理函数中翻转 LED 的状态。

六、调试与测试

6.1 编译和下载程序

在 Keil MDK - ARM 中编译程序,确保没有编译错误。然后通过 JTAG 或 SWD 接口将程序下载到 STM32F407 开发板上。

6.2 观察实验现象

下载完成后,开发板上的 LED 将以 0.5 秒的间隔闪烁,说明定时器中断功能正常工作。

七、总结

本文详细介绍了如何使用 STM32F407 的 HAL 库来配置和使用通用定时器中断。通过 STM32CubeMX 工具可以方便地进行定时器的初始化配置,然后在中断服务函数中编写相应的处理代码。定时器中断在嵌入式系统开发中有着广泛的应用,例如定时采样、定时控制等。掌握定时器中断的使用方法,可以提高嵌入式系统的实时性和稳定性。