北屋教程网

专注编程知识分享,从入门到精通的编程学习平台

STM32 HAL库开发学习13. 定时器及基本定时器使用


一、定时器概述

1. 定时器简介

实现定时器的方式一般有软件、硬件两种方式,本文主要针对STM32的硬件定时器。

定时器的核心是一个计数器,该计数器会按照一定的时钟频率进行加 1 或减 1 操作。当计数器的值达到预设的阈值(即自动重装载值)时,会触发特定的事件,如产生中断、更新输出信号等。同时,计数器可以根据配置自动重新开始计数,形成周期性的定时操作。

2. 定时器常见类型

3. STM32定时器特性表

F1系列

定时器类型

定时器编号

位宽

计数模式

主要功能

总线

高级控制定时器

TIM1、TIM8

16 位

向上、向下、向上/向下(中央对齐)

PWM 输出(带死区插入)、4个输入捕获、互补输出、刹车输入、编码器接口、触发 DAC/ADC

APB2

通用定时器

TIM2、TIM3、TIM4、TIM5

16 位(TIM2、TIM5 为 32 位)

向上、向下、向上/向下(中央对齐)

PWM 输出、4个输入捕获、输出比较、编码器接口、触发 DAC/ADC

APB1

基本定时器

TIM6、TIM7

16 位

向上

定时、触发 DAC/ADC

APB1

H7系列

定时器类型

定时器

计数器位数

计数模式

预分频系数

产生 DMA 请示

捕获/比较通道

互补输出

高级定时器

TIM1、TIM8

16 位

向上

1 - 65536

支持

4 个

通用定时器

TIM2、TIM5

32 位

向上、向下、中央对齐

1 - 65536

支持

4 个

通用定时器

TIM3、TIM4

16 位

向上、向下、中央对齐

1 - 65536

支持

4 个

通用定时器

TIM12

16 位

向上

1 - 65536

不支持

2 个

通用定时器

TIM13、TIM14

16 位

向上

1 - 65536

不支持

1 个

通用定时器

TIM15

16 位

向上

1 - 65536

支持

2 个

基本定时器

TIM16、TIM17

16 位

向上

1 - 65536

支持

1 个

基本定时器

TIM6、TIM7

16 位

向上

1 - 65536

支持

1 个

二、基本定时器

1. 基本定时器功能简介

基本定时器有TIM6/TIM7,其主要特性有:

  • 基本定时器使用 16 位的计数器,计数模式为向上计数,即从 0 开始计数,逐步递增,直到达到自动重载寄存器(ARR)中设置的值,然后计数器会重新归零并开始下一轮计数。
  • 可编程预分频器可以对定时器的时钟源进行分频,预分频系数范围通常为 1 - 65536。通过设置预分频寄存器(PSC)的值,可以调整定时器的计数频率,从而实现不同的定时精度。
  • 自动重载寄存器(ARR)用于存储计数器的上限值。当计数器的值达到 ARR 中设置的值时,会产生更新事件,计数器会重新归零。
  • 计数器溢出(达到 ARR 值)时会产生更新事件。更新事件可以用于触发一些操作,例如触发 DAC(数模转换器)或 ADC(模数转换器)的转换。
  • 基本定时器可以产生 DMA(直接内存访问)请求。当定时器产生更新事件时,可以触发 DMA 传输,将数据从一个内存位置传输到另一个内存位置,而无需 CPU 的干预,提高系统效率。

2. 时钟树


在STM32F1中,TIM1和TIM8是挂载在APB2总线上,(最大稳定频率72MHz)
而TIM2~5挂载在APB1总线上(最大频率36MHz)。

并不是挂载在APB1上,定时器频率最大就是36MHz,还要取决于预分频系数,如下图所示的 TIM1 Timer x1,2 Multiplier。

3. STM32 定时器计数模式

计数器模式

溢出条件

向上

CNT==ARR

向下

CNT==0

中心对齐

CNT == ARR-1 、 CNT==1

4. STM32 定时器相关寄存器

(1)TIMx_CR1 设置ARR寄存器是否有缓冲、关闭/开启计数器

位7:ARPE: 自动重载预装载使能(Auto-reload preload enable)

  • 0: TIMx_ARR寄存器没有缓冲,默认值,赋值后立即将ARR的值加载到影子寄存器
  • 1: TIMx_ARR寄存器有缓冲,在更新事件时自动将ARR的值加载到影子寄存器

位0:CEN:计数器使能(Counter enable)

  • 0:关闭计数器
  • 1: 使能计数器

(2)TIMx_DIER

位8: UDE:更新DMA请求使能(Update DMA request enable)

  • 0:关闭DMA请求
  • 1: 使能DMA请求

位0:UIE:更新中断使能(Update interrupt enable)

  • 0:关闭更新中断
  • 1: 使能更新中断

(3)TIMx_SR 状态寄存器

位0: UIF:更新中断标志(Update interrupt flag)

  • 0:无更新中断
  • 1: 有更新中断

在中断服务函数中,需要手动清除中断标志位,否则会一直触发中断。

(4)TIMx_CNT 计数器寄存器

  • 位宽为16位,用于存储计数器的值。

(5)TIMx_PSC 预分频器寄存器

  • 位宽为16位,用于设置预分频器的值。
  • 计数器的时钟频率 = 定时器时钟频率 / (预分频器值 + 1)。

(6)TIMx_ARR 自动重装载寄存器

  • 位宽为16位,用于设置计数器的上限值。
  • 当计数器的值达到 ARR 中设置的值时,会产生更新事件,计数器会重新归零。
  • 如果自动重装载值为0,则计数器停止 。

5. 定时器溢出时间计算方法

定时器溢出时间 T o u t = (自动重装载值 A R R + 1 ) * (预分频系数 P S C + 1 ) / 定时器时钟频率 F t 定时器溢出时间T_{out} = (自动重装载值ARR + 1) * (预分频系数PSC + 1) / 定时器时钟频率F_t定时器溢出时间Tout=(自动重装载值ARR+1)*(预分频系数PSC+1)/定时器时钟频率Ft

三、定时器配置步骤

1. 配置定时器基础工作参数

HAL_TIM_Base_Init(&htim);

2. 定时器基础MSP初始化

HAL_TIM_Base_MspInit(&htim); 配置NVIC,CLOCK等, GPIO配置

3. 使能更新中断并启动计数器

HAL_TIM_Base_Start_IT(&htim);

4. 设置优先级,使能中断

HAL_NVIC_SetPriority(TIMx_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIMx_IRQn);

5. 编写中断服务函数

void TIMx_IRQHandler(void)

6. 清除中断标志位

__HAL_TIM_CLEAR_IT(&htim, TIM_IT_UPDATE);

另外有一个定时器更新中断回调函数,在中断服务函数中会自动调用,该函数用户可以自行重写:
void
HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);

四、定时器配置实例

本示例使用定时器6,实现500ms定时器更新中断,控制GPIO口翻转。本项目在之前项目基础上增加配置,省略了GPIO等配置说明 。
时钟频率设置如下:

1. STM32CubeMX配置 启用TIM6


超时时间=(499+1)*(7999+1)/8MHz = 500ms。

2. 启动 TIM6全局中断

3. 生成的部分代码

main.c里TIM6初始化


/**
  * @brief TIM6 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM6_Init(void)
{

  /* USER CODE BEGIN TIM6_Init 0 */

  /* USER CODE END TIM6_Init 0 */

  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM6_Init 1 */

  /* USER CODE END TIM6_Init 1 */
  htim6.Instance = TIM6;
  htim6.Init.Prescaler = 7999;
  htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim6.Init.Period = 499;
  htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM6_Init 2 */

  /* USER CODE END TIM6_Init 2 */

}


stm32f1xx_it.c 中断

/**
  * @brief This function handles TIM6 global interrupt.
  */
void TIM6_IRQHandler(void)
{
  /* USER CODE BEGIN TIM6_IRQn 0 */

  /* USER CODE END TIM6_IRQn 0 */
  HAL_TIM_IRQHandler(&htim6);
  /* USER CODE BEGIN TIM6_IRQn 1 */

  /* USER CODE END TIM6_IRQn 1 */
}


4. 手工添加代码

(1)TIM6初始化的代码里,添加启动中断

/* 在TIM6初始化函数末尾添加 */
/* USER CODE BEGIN TIM6_Init 2 */
HAL_TIM_Base_Start_IT(&htim6);  // 启动定时器中断
/* USER CODE END TIM6_Init 2 */


(2)实现中断回调函数

/* 定时器6中断回调 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM6)
    {
        // 这里添加中断处理代码
        HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_0);
    }
}
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言