本帖最后由 正点原子运营 于 2024-4-9 15:16 编辑
1)实验平台:正点原子 M144Z-M3 STM32F103最小系统板
2) 章节摘自【正点原子】M144Z-M3最小系统板使用指南——STM32F103版
6)正点原子STM32技术交流QQ群:725095144
本章将介绍使用STM32F103通用定时器对输入脉冲的个数进行计数。通过本章的学习,读者将学习到通用定时器从模式的使用。 本章分为如下几个小节: 20.1 硬件设计 20.2 程序设计 20.3 下载验证
20.1 硬件设计 20.1.1 例程功能 1. 串口输出WKUP按键被按下的次数,按下KEY0可清零计数值 2. LED0闪烁,提示程序正在运行
20.1.2 硬件资源 1. LED LED0 - PB5 2. 按键 WKUP - PA0 KEY0 - PE4 3. TIM2 CH1 - PA0 4. USART1 USART1_TX - PA9 USART1_RX - PA10
20.1.3 原理图 本章实验使用的TIM2为STM32F103的片上资源,因此没有对应的连接原理图。
20.2 程序设计 20.2.1 HAL库的TIM驱动 本章实验将使用WKUP按键按下时产生的上升沿作为TIM2计数的触发源,因此除了像第十九章实验配置定时器输入捕获外,该需要配置通用定时器的从模式,具体的步骤如下: ①:配置定时器从模式 在HAL库中对应的驱动函数如下: ①:配置定时器从模式 该函数用于配置定时器从模式,其函数原型如下所示: - HAL_StatusTypeDef HAL_TIM_SlaveConfigSynchro(
- TIM_HandleTypeDef*htim,
- TIM_SlaveConfigTypeDef*sSlaveConfig);
复制代码该函数的形参描述,如下表所示: 表20.2.1.1 函数HAL_TIM_SlaveConfigSynchro()形参描述 该函数的返回值描述,如下表所示: 表20.2.1.2 函数HAL_TIM_SlaveConfigSynchro()返回值描述 该函数使用TIM_SlaveConfigTypeDef类型结构体指针传入了定时器从模式的配置参数,该结构体的定义如下所示: - typedef struct
- {
- uint32_t SlaveMode; /* 从模式 */
- uint32_t InputTrigger; /* 触发源 */
- uint32_t TriggerPolarity; /* 触发极性 */
- uint32_t TriggerPrescaler; /* 触发输入分频系数 */
- uint32_t TriggerFilter; /* 触发输入滤波 */
- } TIM_SlaveConfigTypeDef;
复制代码该函数的使用示例,如下所示: - #include "stm32f1xx_hal.h"
- void example_fun(void)
- {
- TIM_SlaveConfigTypeDeftim_slave_config_struct = {0};
-
- /* 配置定时器从模式 */
- tim_slave_config_struct.SlaveMode =TIM_SLAVEMODE_EXTERNAL1;
- tim_slave_config_struct.InputTrigger = TIM_TS_TI1FP1;
- tim_slave_config_struct.TriggerPolarity =TIM_TRIGGERPOLARITY_RISING;
- tim_slave_config_struct.TriggerPrescaler =TIM_TRIGGERPRESCALER_DIV1;
- tim_slave_config_struct.TriggerFilter = 0;
- HAL_TIM_SlaveConfigSynchro(&tim_handle, &tim_slave_config_struct);
- }
复制代码20.2.2通用定时器驱动 本章实验的通用定时器驱动主要负责向应用层提供通用定时器的初始化、获取和清零计数值等函数,并实现通用定时器的中断回调函数。本章实验中,通用定时器的驱动代码包括gtim.c和gtim.h两个文件。 通用定时器驱动中,对TIM、GPIO的相关宏定义,如下所示: - #define GTIM_TIMX_CNT TIM2
- #define GTIM_TIMX_CNT_IRQn TIM2_IRQn
- #define GTIM_TIMX_CNT_IRQHandler TIM2_IRQHandler
- #define GTIM_TIMX_CNT_CLK_ENABLE() \
- do { \
- __HAL_RCC_TIM2_CLK_ENABLE(); \
- }while (0)
- #define GTIM_TIMX_CNT_CHY TIM_CHANNEL_1
- #define GTIM_TIMX_CNT_CHY_GPIO_PORT GPIOA
- #define GTIM_TIMX_CNT_CHY_GPIO_PIN GPIO_PIN_0
- #define GTIM_TIMX_CNT_CHY_GPIO_CLK_ENABLE() \
- do { \
- __HAL_RCC_GPIOA_CLK_ENABLE(); \
- }while (0)
复制代码通用定时器驱动中TIM2的初始化函数,如下所示: - /**
- *@brief 初始化通用定时器脉冲计数
- *@param psc: 预分频系数
- *@retval 无
- */
- voidgtim_timx_cnt_chy_init(uint16_t psc)
- {
- TIM_SlaveConfigTypeDef tim_slave_config_struct = {0};
-
- /* 初始化定时器输入捕获 */
- g_timx_cnt_handle.Instance = GTIM_TIMX_CNT;
- g_timx_cnt_handle.Init.Prescaler = psc;
- g_timx_cnt_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
- g_timx_cnt_handle.Init.Period = 0xFFFF;
- HAL_TIM_IC_Init(&g_timx_cnt_handle);
-
- /* 配置定时器从模式 */
- tim_slave_config_struct.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;
- tim_slave_config_struct.InputTrigger = TIM_TS_TI1FP1;
- tim_slave_config_struct.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;
- tim_slave_config_struct.TriggerPrescaler = TIM_TRIGGERPRESCALER_DIV1;
- tim_slave_config_struct.TriggerFilter = 0;
- HAL_TIM_SlaveConfigSynchro(&g_timx_cnt_handle, &tim_slave_config_struct);
-
- /* 开始定时器输入捕获 */
- __HAL_TIM_ENABLE_IT(&g_timx_cnt_handle, TIM_IT_UPDATE);
- HAL_TIM_IC_Start(&g_timx_cnt_handle, GTIM_TIMX_CNT_CHY);
- }
复制代码从TIM2的初始化代码中可以看到,初始化函数中初始化了TIM2的输入捕获并配置了TIM2的从模式,最后开始TIM2的中断模式输入捕获。 通用定时器驱动中,获取和清零计数值的函数,如下所示: - /**
- *@brief 获取通用定时器脉冲计数值
- *@param 无
- *@retval 脉冲计数值
- */
- uint32_t gtim_timx_cnt_chy_get_count(void)
- {
- uint32_t total;
-
- /* 计算总脉冲计数值 */
- total =g_timx_chy_cnt_ofcnt * 0xFFFF;
- total +=__HAL_TIM_GET_COUNTER(&g_timx_cnt_handle);
-
- return total;
- }
- /**
- *@brief 重启通用定时器脉冲计数
- *@param 无
- *@retval 无
- */
- voidgtim_timx_cnt_chy_restart(void)
- {
- __HAL_TIM_DISABLE(&g_timx_cnt_handle);
- g_timx_chy_cnt_ofcnt = 0;
- __HAL_TIM_SET_COUNTER(&g_timx_cnt_handle, 0);
- __HAL_TIM_ENABLE(&g_timx_cnt_handle);
- }
复制代码从上面的代码中可以看到,获取计数值时,就是对定时器当前的计数值和溢出次数进行计算得出的,清零计数值就是清零计数值和溢出次数。定时器的溢出次数是在TIM2的更新中断中累加的。 通用定时器驱动中,TIM2的超时回调函数,如下所示: - /**
- *@brief HAL库定时器超时中断回调函数
- *@param 无
- *@retval 无
- */
- voidHAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
- {
- /* 脉冲计数定时器 */
- if (htim->Instance == GTIM_TIMX_CNT)
- {
- /* 更新脉冲计数定时器溢出次数 */
- g_timx_chy_cnt_ofcnt++;
- }
- }
复制代码TIM2的超时回调函数就是用于在TIM2发生更新中断时,统计TIM2的溢出次数。
20.2.3 实验应用代码 本章实验的应用代码,如下所示: - int main(void)
- {
- uint8_t key;
- uint32_t count;
- uint32_t count_prev = 0;
- uint8_t t = 0;
-
- HAL_Init(); /* 初始化HAL库 */
- sys_stm32_clock_init(RCC_PLL_MUL9); /* 配置时钟,72MHz */
- delay_init(72); /* 初始化延时 */
- usart_init(115200); /* 初始化串口 */
- led_init(); /* 初始化LED */
- key_init(); /* 初始化按键 */
- gtim_timx_cnt_chy_init(0); /* 初始化通用定时器脉冲计数 */
- gtim_timx_cnt_chy_restart(); /* 重启通用定时器脉冲计数 */
-
- while (1)
- {
- key = key_scan(0);
- if (key == KEY0_PRES)
- {
- /* 重启通用定时器脉冲计数 */
- gtim_timx_cnt_chy_restart();
- }
- /* 获取通用定时器脉冲计数值 */
- count = gtim_timx_cnt_chy_get_count();
- /* 脉冲计数值有更新 */
- if (count_prev != count)
- {
- count_prev = count;
- printf("Cnt: %d\r\n", count);
- }
- if (++t == 20)
- {
- t = 0;
- LED0_TOGGLE();
- }
- delay_ms(10);
- }
- }
复制代码从上面的代码中可以看到,TIM2的预分频系数被配置为0,因此每个脉冲都会触发TIM2的计数值加1。完成相关初始化后,便不断地扫描按键和获取TIM2的计数值,若KEY0按键被按下,则会清零TIM2的计数值;若WKUP按键被按下,则会触发TIM2的计数值加1,同时会将新的计数值通过串口输出至串口调试助手。
20.3 下载验证 在完成编译和烧录操作后,连续按下WKUP按键,模拟产生多个脉冲,可以通过串口调试助手观察到WKUP按键被按下的次数,同时按下KEY0按键,可清零统计值。 |