本帖最后由 正点原子运营 于 2024-4-12 17:55 编辑
1)实验平台:正点原子 M144Z-M3 STM32F103最小系统板
2) 章节摘自【正点原子】M144Z-M3最小系统板使用指南——STM32F103版
6)正点原子STM32技术交流QQ群:725095144
本章将介绍使用STM32F103的高级定时器检测输入PWM的占空比和周期。通过本章的学习,读者将学习到高级定时器PWM输入模式的使用。 本章分为如下几个小节: 24.1 硬件设计 24.2 程序设计 24.3 下载验证
24.1 硬件设计 24.1.1 例程功能 1. PC6引脚输出频率为10KHz的PWM,串口输出PC6引脚输入PWM的周期和高电平脉宽 2. LED0闪烁,提示程序正在运行
24.1.2 硬件资源 1. LED LED0 - PB5 2. TIM8 CH1 - PC6 3. TIM3 CH1 - PB4 4. USART1 USART1_TX - PA9 USART1_RX - PA10
24.1.3 原理图 本章实验使用的TIM8为STM32F103的片上资源,因此没有对应的连接原理图。
24.2 程序设计 24.2.1 HAL库的TIM驱动 本章实验将使用TIM8的通道1(PC6引脚)在PWM输入模式下捕获TIM3从通道1(PB4)引脚输出的PWM(有关通用定时器输出PWM的相关内容,请见第十八章“通用定时器PWM输出实验”),将分别捕获输入PWM信号的上升沿和下降沿,以此来计算输入PWM信号的占空比和周期,要是实现以上功能都依赖于高级定时器的PWM输入模式,其具体的配置步骤如下: ①:初始化定时器输入捕获 ②:配置定时器从模式 ③:配置定时器输入捕获通道 ④:开始定时器输入捕获 在HAL库中对应的驱动函数如下: ①:初始化定时器输入捕获 请见第19.2.1小节中初始化定时器输入捕获的相关内容。 ②:配置定时器从模式 请见第20.2.1小节中配置定时器从模式的相关内容。 ③:配置定时器输入捕获通道 请见第19.2.1小节中配置定时器输入捕获通道的相关内容。 ④:开始定时器输入捕获 请见第19.2.1小节中开始定时器输入捕获的相关内容。
24.2.2 高级定时器驱动 本章实验的高级定时器驱动主要负责向应用层提供高级定时器的初始化函数,并实现高级定时器的中断服务函数。本章实验中,高级定时器的驱动代码包括atim.c和atim.h两个文件。 高级定时器驱动中,对TIM、GPIO相关的宏定义,如下所示: - #define ATIM_TIMX_PWMIN TIM8
- #define ATIM_TIMX_PWMIN_IRQn TIM8_CC_IRQn
- #define ATIM_TIMX_PWMIN_IRQHandler TIM8_CC_IRQHandler
- #define ATIM_TIMX_PWMIN_CLK_ENABLE() \
- do { \
- __HAL_RCC_TIM8_CLK_ENABLE(); } \
- while (0)
- #define ATIM_TIMX_PWMIN_CHY TIM_CHANNEL_1
- #define ATIM_TIMX_PWMIN_CHY_GPIO_PORT GPIOC
- #define ATIM_TIMX_PWMIN_CHY_GPIO_PIN GPIO_PIN_6
- #define ATIM_TIMX_PWMIN_CHY_GPIO_CLK_ENABLE() \
- do { \
- __HAL_RCC_GPIOC_CLK_ENABLE(); \
- }while (0)
复制代码高级定时器驱动中TIM8的初始化函数,如下所示: - /**
- *@brief 初始化高级定时器PWM输入
- *@param psc: 预分频系数
- *@retval 无
- */
- voidatim_timx_pwmin_chy_init(uint16_t psc)
- {
- GPIO_InitTypeDef gpio_init_struct;
- TIM_SlaveConfigTypeDef tim_slave_config_struct = {0};
- TIM_IC_InitTypeDef tim_ic_init_struct = {0};
-
- /* 使能相关时钟 */
- ATIM_TIMX_PWMIN_CLK_ENABLE();
- ATIM_TIMX_PWMIN_CHY_GPIO_CLK_ENABLE();
-
- /* 配置PWM输入引脚 */
- gpio_init_struct.Pin = ATIM_TIMX_PWMIN_CHY_GPIO_PIN;
- gpio_init_struct.Mode = GPIO_MODE_AF_PP;
- gpio_init_struct.Pull = GPIO_PULLDOWN;
- gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
- HAL_GPIO_Init(ATIM_TIMX_PWMIN_CHY_GPIO_PORT, &gpio_init_struct);
-
- /* 初始化定时器输入捕获 */
- g_timx_pwmin_handle.Instance = ATIM_TIMX_PWMIN;
- g_timx_pwmin_handle.Init.Prescaler = psc;
- g_timx_pwmin_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
- g_timx_pwmin_handle.Init.Period = 0xFFFF;
- HAL_TIM_IC_Init(&g_timx_pwmin_handle);
-
- /* 配置定时器从模式 */
- tim_slave_config_struct.SlaveMode = TIM_SLAVEMODE_RESET;
- tim_slave_config_struct.InputTrigger = TIM_TS_TI1FP1;
- tim_slave_config_struct.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;
- tim_slave_config_struct.TriggerFilter = 0;
- HAL_TIM_SlaveConfigSynchro(&g_timx_pwmin_handle, &tim_slave_config_struct);
-
- /* 配置定时器输入捕获通道 */
- tim_ic_init_struct.ICPolarity = TIM_ICPOLARITY_RISING;
- tim_ic_init_struct.ICSelection = TIM_ICSELECTION_DIRECTTI;
- tim_ic_init_struct.ICPrescaler = TIM_ICPSC_DIV1;
- tim_ic_init_struct.ICFilter = 0;
- HAL_TIM_IC_ConfigChannel( &g_timx_pwmin_handle,
- &tim_ic_init_struct,
- TIM_CHANNEL_1);
-
- /* 配置定时器输入捕获通道 */
- tim_ic_init_struct.ICPolarity = TIM_ICPOLARITY_FALLING;
- tim_ic_init_struct.ICSelection = TIM_ICSELECTION_INDIRECTTI;
- tim_ic_init_struct.ICPrescaler = TIM_ICPSC_DIV1;
- tim_ic_init_struct.ICFilter = 0;
- HAL_TIM_IC_ConfigChannel( &g_timx_pwmin_handle,
- &tim_ic_init_struct,
- TIM_CHANNEL_2);
-
- /* 配置中断优先级并使能中断 */
- HAL_NVIC_SetPriority(ATIM_TIMX_PWMIN_IRQn, 0, 0);
- HAL_NVIC_EnableIRQ( ATIM_TIMX_PWMIN_IRQn );
-
- /* 开始定时器输入捕获 */
- HAL_TIM_IC_Start_IT(&g_timx_pwmin_handle, TIM_CHANNEL_1);
- HAL_TIM_IC_Start_IT(&g_timx_pwmin_handle, TIM_CHANNEL_2);
- }
复制代码从上面的代码中可以看出,在配置定时器输入捕获通道时,配置了IC1捕获上升沿,IC2捕获下降沿,并同时开启了捕获比较1和2的中断,这么一来就可以在中断回调函数中获取通道1和通道2的捕获比较寄存器值来计算输入PWM信号的周期和占空比了。 高级定时器驱动中,TIM8的中断回调函数,如下所示: - /**
- *@brief PWM输入高级定时器中断回调函数
- *@param 无
- *@retval 无
- */
- voidATIM_TIMX_PWMIN_IRQHandler(void)
- {
- /* 输入捕获通道1捕获到上升沿 */
- if (__HAL_TIM_GET_FLAG(&g_timx_pwmin_handle, TIM_FLAG_CC1) != RESET)
- {
- /* 获取捕获到PWM的周期 */
- g_timx_chy_pwmin_cval =
- HAL_TIM_ReadCapturedValue(&g_timx_pwmin_handle, TIM_CHANNEL_1) + 1;
- g_timx_chy_pwmin_sta = 1;
-
- __HAL_TIM_CLEAR_FLAG(&g_timx_pwmin_handle, TIM_FLAG_CC1);
- }
-
- /* 输入捕获通道2捕获到下降沿 */
- if (__HAL_TIM_GET_FLAG(&g_timx_pwmin_handle, TIM_FLAG_CC2) != RESET)
- {
- /* 获取捕获到PWM的高电平脉宽 */
- g_timx_chy_pwmin_hval =
- HAL_TIM_ReadCapturedValue(&g_timx_pwmin_handle, TIM_CHANNEL_2) + 1;
-
- __HAL_TIM_CLEAR_FLAG(&g_timx_pwmin_handle, TIM_FLAG_CC2);
- }
- }
复制代码从上面的代码中可以看出,在捕获比较通道1中断中获取通道1的捕获比较寄存器值就是TIM8在输入PWM信号的两个上升沿之间的计数值,通过该值可以计算出输入PWM信号的周期;在捕获比较通道2中断中获取通道2的捕获比较寄存器值就是TIM8在输入PWM信号的上升沿和下降沿之间的计数值,通过该值可以计算出输入PWM信号的高电平占空比。
24.2.3 实验应用代码 本章实验的应用代码,如下所示: - int main(void)
- {
- uint8_t t = 0;
-
- HAL_Init(); /* 初始化HAL库 */
- sys_stm32_clock_init(RCC_PLL_MUL9); /* 配置时钟,72MHz */
- delay_init(72); /* 初始化延时 */
- usart_init(115200); /* 初始化串口 */
- led_init(); /* 初始化LED */
- gtim_timx_pwm_chy_init(100 - 1, 72 - 1); /* 初始化通用定时器PWM */
- atim_timx_pwmin_chy_init(72 - 1); /* 初始化高级定时器PWM输入 */
-
- while (1)
- {
- /* 捕获成功 */
- if (g_timx_chy_pwmin_sta == 1)
- {
- g_timx_chy_pwmin_sta = 0;
-
- printf("高电平时间:%d us\r\n", g_timx_chy_pwmin_hval);
- printf("PWM周期:%d us\r\n", g_timx_chy_pwmin_cval);
- printf("PWM频率:%d Hz\r\n", 1000000 / g_timx_chy_pwmin_cval);
- printf("\r\n");
- }
-
- if (++t == 20)
- {
- t = 0;
- LED0_TOGGLE();
- }
-
- delay_ms(10);
- }
- }
复制代码从上面的代码中可以看出,会配置通用定时器输出一个频率为100Hz、占空比为50%的PWM,该PWM信号用于作为本实验中高级定时器的PWM输入信号。随后会初始化高级定时器TIM8,TIM8的预分频计数器值配置为(72-1),并且TIM8的时钟频率为72MHz,因此TIM8的计数频率为1MHz,即1个计数值对应1微秒,方便后面的时间计算。 初始化完成后,就等待高级定时器中断函数中的捕获成功标志为真,捕获成功后,便将捕获到的PWM输入信号的周期和占空比输出值串口调试助手。
24.3 下载验证 在完成编译和烧录操作后,将TIM3通道1输出的PWM信号接入TIM8的PWM输入引脚,即将PB4引脚与PC6引脚进行短接,随后便可在串口调试助手上看到,捕获到PWM输入信号的高电平时间为5000微秒、PWM周期为10000微秒,即PWM的占空比为50%,并且也能看到PWM输入信号的频率为100Hz。 |