一、通用定时器
道 STM32F407 有 10 个通用 定时器(TIM2~TIM5 和 TIM9~TIM14)。这些定时器彼此完全独立,不共享任何资源。
1.1、与基本定时器的比较
通用定时器的架构图如下,从架构图就可以发现其结构比基本定时器复杂很多。
通用定时器的计数模式有三种:递增计数模式、递减计数模式和中心对齐模式;
STM32F4 定时器 TIM2/TIM3/TIM4/TIM5/ TIM6/TIM7/ TIM12/ TIM13/ TIM14 挂载在 APB1 总线
APB2 总线上挂载的通用定时器 TIM9/TIM10/TIM11,以及高级定时器 TIM1 和 TIM8
④部分是输入捕获,一般应用是要和第⑤部分一起完成测量功能。 TIMx_CH1~ TIMx_CH4 表示定时器的 4 个通道,这 4 个通道都是可以独立工作的。IO 端口通 过复用功能与这些通道相连。配置好 IO 端口的复用功能后,将需要测量的信号输入到相应的 IO 端口,输入捕获部分可以对输入的信号的上升沿,下降沿或者双边沿进行捕获,常见的测量 有:测量输入信号的脉冲宽度、测量 PWM 输入信号的频率和占空比等。
第⑥部分是输出比较,一般应用是要和第⑤部分一起完成定时器输出功能。 TIMx_CH1~ TIMx_CH4 表示定时器的 4 个通道,这 4 个通道都是可以独立工作的。IO 端口通 过复用功能与这些通道相连。
二、通用定时器的中断实战
通用定时器的中断和基本定时器的中断基本没有任何区别,就是在配置的时候可以选择递增、递减或者中心对齐模式。还有就是要注意不同定时器在APB1还是APB2总线上
现在配置通用定时器3,用中断实现每500ms实现GPIOF_PIN_10上的LED状态反转。
#include "stm32f4xx.h"
#include "core_cm4.h"
#include "stm32f4xx_hal.h"
#include "stdio.h"
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
TIM_HandleTypeDef g_timx_handle;
/* ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓通用定时器初始化↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
void gtim_tim3_int_init(uint16_t arr, uint16_t psc)
{
__HAL_RCC_TIM3_CLK_ENABLE(); /* 使能TIM3时钟 */
g_timx_handle.Instance = TIM3; /* 通用定时器3 */
g_timx_handle.Init.Prescaler = psc; /* 预分频系数 */
g_timx_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
g_timx_handle.Init.Period = arr; /* 自动装载值 */
HAL_TIM_Base_Init(&g_timx_handle);
HAL_NVIC_SetPriority(TIM3_IRQn, 1, 3); /* 设置中断优先级,抢占优先级1,子优先级3 */
HAL_NVIC_EnableIRQ(TIM3_IRQn); /* 开启ITMx中断 */
HAL_TIM_Base_Start_IT(&g_timx_handle); /* 使能定时器x和定时器x更新中断 */
}
/* ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ LED的配置↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
void led_init(void) /* 对LED串口进行初始化*/
{
GPIO_InitTypeDef gpio_init_struct; /* 定义结构体 */
gpio_init_struct.Pin = GPIO_PIN_10; /* LED引脚 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(GPIOF, &gpio_init_struct); /* 初始化LED引脚 */
}
/* ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 中断函数↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
void TIM3_IRQHandler(void)
{
/* 以下代码没有使用定时器HAL库共用处理函数来处理,而是直接通过判断中断标志位的方式 */
if(__HAL_TIM_GET_FLAG(&g_timx_handle, TIM_FLAG_UPDATE) != RESET)
{
HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_10);
__HAL_TIM_CLEAR_IT(&g_timx_handle, TIM_IT_UPDATE); /* 清除定时器溢出中断标志位 */
}
}
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
led_init(); /* 初始化LED */
gtim_tim3_int_init(4999, 8399); /* 84 000 000 / 84 00 = 10 000 10Khz的计数频率,计数5K
次为500ms */
__HAL_RCC_GPIOF_CLK_ENABLE(); /* F端口使能*/
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_10, GPIO_PIN_SET); /* 开启LED */
while(1)
{
delay_ms(200);
}
}
注:delay_ms(200);直接用的正点原子的代码,这里只是用来表示一下。
这里对定时器进行配置的时候没有使用HAL自带的通用函数,直接在 gtim_timx_int_init(uint16_t arr, uint16_t psc)中完成了定时器参数的配置和中断使能等操作。此外在定时器中断的函数里面也没有调用HAL提供的统一函数,所以需要手动将中断的标志位进行重置,利用函数__HAL_TIM_CLEAR_IT(&g_timx_handle, TIM_IT_UPDATE);