RT-Thread Studio学习(十五)PWM测量
RT-Thread Studio学习(十五)PWM测量
- 一、简介
- 二、新建RT-Thread项目并使用外部时钟
- 三、启用PWM输入捕获功能
- 四、测试
一、简介
本文将基于STM32F407VET芯片介绍如何在RT-Thread Studio开发环境下使用定时器的PWM输入模式进行脉宽和周期测量。硬件及开发环境如下:
- OS WIN10
- STM32F407VET6
- STM32CubeMX v6.10.0
- STM32Cube MCU Package for STM32F4 Series v1.28.0
- RT-Thread Studio v2.2.7
- RT-Thread Source Code v5.0.2
- STM32F4 chip support packages v0.2.3
二、新建RT-Thread项目并使用外部时钟
打开RT-Thread Studio软件新建基于芯片的项目,并使用外部时钟系统,具体参见《RT-Thread Studio学习(一)使用外部时钟系统》。
三、启用PWM输入捕获功能
-
打开PWM驱动框架
在RT-Thread Setting中借助图形化配置工具打开组件中的HWTIMER的驱动框架,如下图所示:

-
定义ADC相关的宏
将TIM2配置为PWM输入模式,在board.h文件中使能宏定义:
#define BSP_USING_TIM
#ifdef BSP_USING_TIM
#define BSP_USING_TIM2
#endif
- 复制TIM初始化函数
双击RT-Thread Studio工程中的cubemx.ioc文件,使能TIM2。设置计数器预分频为84,即计数频率为1MHz。使用通道1用于捕获上升沿,使用通道2用于捕获下降沿。具体如下图:

使能TIM2的全局中断:

再重新生成STM32CubeMX代码,将.\cubemx\Src\tim.c中的函数HAL_TIM_Base_MspInit和HAL_TIM_MspPostInit复制到board.c的末尾。
再次复制函数HAL_TIM_Base_MspInit到board.c的末尾,并将其函数名改成HAL_TIM_PWM_MspInit。
在Application文件夹中添加头文件pwm_input.h,代码如下:
#ifndef APPLICATIONS_PWM_INPUT_H_
#define APPLICATIONS_PWM_INPUT_H_#include <rtthread.h>
#include <board.h>extern uint16_t PWM_RisingCount;
extern uint16_t PWM_FallingCount;
extern float duty;void MX_TIM2_Init(void);
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);#endif /* APPLICATIONS_PWM_INPUT_H_ */
在Application文件夹中添加源文件pwm_input.c,代码如下:
#include <rtthread.h>
#include <board.h>uint16_t PWM_RisingCount;
uint16_t PWM_FallingCount;
float duty = -1;uint32_t uiDutyCycle;
uint32_t uiCycle;
uint32_t uiFrequency;TIM_HandleTypeDef htim2;/* TIM2 init function */
void MX_TIM2_Init(void)
{/* USER CODE BEGIN TIM2_Init 0 *//* USER CODE END TIM2_Init 0 */TIM_ClockConfigTypeDef sClockSourceConfig = {0};TIM_SlaveConfigTypeDef sSlaveConfig = {0};TIM_IC_InitTypeDef sConfigIC = {0};TIM_MasterConfigTypeDef sMasterConfig = {0};/* USER CODE BEGIN TIM2_Init 1 *//* USER CODE END TIM2_Init 1 */htim2.Instance = TIM2;htim2.Init.Prescaler = 84-1;htim2.Init.CounterMode = TIM_COUNTERMODE_UP;htim2.Init.Period = 4294967295;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();}if (HAL_TIM_IC_Init(&htim2) != HAL_OK){Error_Handler();}sSlaveConfig.SlaveMode = TIM_SLAVEMODE_RESET;sSlaveConfig.InputTrigger = TIM_TS_TI2FP2;sSlaveConfig.TriggerPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;sSlaveConfig.TriggerPrescaler = TIM_ICPSC_DIV1;sSlaveConfig.TriggerFilter = 0;if (HAL_TIM_SlaveConfigSynchro(&htim2, &sSlaveConfig) != HAL_OK){Error_Handler();}sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI;sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;sConfigIC.ICFilter = 0;if (HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1) != HAL_OK){Error_Handler();}sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;if (HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_2) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK){Error_Handler();}/* USER CODE BEGIN TIM2_Init 2 */HAL_TIM_Base_Start(&htim2);HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1); /* 使能定时器2通道1的PWM输入捕获 */HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2); /* 使能定时器2通道2的PWM输入捕获 *//* USER CODE END TIM2_Init 2 */}void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)//注意这里为HAL_TIM_ACTIVE_CHANNEL_1而不是TIM_CHANNEL_1{PWM_RisingCount = HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1);duty = 100.00 - (float)PWM_RisingCount / PWM_FallingCount*100.00;}if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2){PWM_FallingCount = HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_2);}
}
函数MX_TIM2Init来自.\cubemx\Src\tim.c。
修改.\drivers\include\config\tim_config.h文件,在tim_config.h中添加代码:
#ifdef BSP_USING_TIM2
#ifndef TIM2_CONFIG
#define TIM2_CONFIG \{ \.tim_handle.Instance = TIM2, \.tim_irqn = TIM2_IRQn, \.name = "timer2", \}
#endif /* TIM2_CONFIG */
#endif /* BSP_USING_TIM2 */
- 定义
.\cubemx\Inc\stm32f4xx_hal_conf.h中的相关宏
#define HAL_TIM_MODULE_ENABLED
四、测试
修改main.c的代码为:
#include <rtthread.h>
#include "stm32f4xx.h"
#include <rtdevice.h>#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>#include "pwm_input.h"// PD12 ------> TIM4_CH1
// PA6 ------> TIM13_CH1
// PA7 ------> TIM14_CH1
// PA1 ------> TIM2_CH2#define PWM4_DEV_NAME "pwm4" /* PWM设备名称 */
#define PWM13_DEV_NAME "pwm13" /* PWM设备名称 */
#define PWM14_DEV_NAME "pwm14" /* PWM设备名称 */
#define PWM_DEV_CHANNEL 1 /* PWM通道 */
struct rt_device_pwm *pwm_dev; /* PWM设备句柄 */rt_uint32_t channel[4], period[4], pulse[4];int pwm_init(void)
{for (int i=0; i<2; i++){period[i] = 1000000; /* 周期为1ms,单位为纳秒ns */pulse[i] = 500000; /* PWM脉冲宽度值,单位为纳秒ns */}/* 初始化设备PWM4 */pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM4_DEV_NAME);if (pwm_dev == RT_NULL){rt_kprintf("pwm sample run failed! can't find %s device!\n", PWM4_DEV_NAME);return RT_ERROR;}rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period[0], pulse[0]);rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);/* 初始化设备PWM13 */pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM13_DEV_NAME);if (pwm_dev == RT_NULL){rt_kprintf("pwm sample run failed! can't find %s device!\n", PWM13_DEV_NAME);return RT_ERROR;}rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period[0], pulse[0]);rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);/* 初始化设备PWM14 */pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM14_DEV_NAME);if (pwm_dev == RT_NULL){rt_kprintf("pwm sample run failed! can't find %s device!\n", PWM14_DEV_NAME);return RT_ERROR;}rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period[1], pulse[1]);rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);return 0;
}// 第一个参数为命令,第二个参数为 PWM 设备名称,第 3 个参数为 PWM 通道,
// 第 4 个参数为周期(单位纳秒),第 5 个参数为脉冲宽度(单位纳秒)
static int pwm_set(int argc, char *argv[])
{if(argc!=5){rt_kprintf("Usage: pwm_set <device name> <channel> <period> <pulse>\n");rt_kprintf("Example: pwm_set pwm13 1 100000 50000\n");return RT_ERROR;}rt_uint32_t period, pulse;char pwmdevname[RT_NAME_MAX];rt_strncpy(pwmdevname, argv[1], RT_NAME_MAX);if((!strcmp(argv[1], "pwm4")) || (!strcmp(argv[1], "pwm13")) || (!strcmp(argv[1], "pwm14"))){period = atoi(argv[3]); /* PWM period, ns */pulse = atoi(argv[4]); /* PWM pulse, ns */}else{rt_kprintf("pwm sample run failed! can't find %s device!\n", pwmdevname);return RT_ERROR;}pwm_dev = (struct rt_device_pwm *)rt_device_find(pwmdevname);if (pwm_dev == RT_NULL){rt_kprintf("pwm sample run failed! can't find %s device!\n", pwmdevname);return RT_ERROR;}rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);rt_kprintf("pwm_set %s channel:%d period:%dns pulse:%dns\n", pwmdevname, PWM_DEV_CHANNEL, period, pulse);return 0;
}int main(void)
{int count = 1;LOG_D("Hello RT-Thread! 2024.1.17");LOG_D("System CLock information");LOG_D("SYSCLK_Frequency = %d", HAL_RCC_GetSysClockFreq());LOG_D("HCLK_Frequency = %d", HAL_RCC_GetHCLKFreq());LOG_D("PCLK1_Frequency = %d", HAL_RCC_GetPCLK1Freq());LOG_D("PCLK2_Frequency = %d", HAL_RCC_GetPCLK2Freq());LOG_D("SysTick->LOAD = %d", SysTick->LOAD);LOG_D("Current tick = %d", rt_tick_get());pwm_init();MX_TIM2_Init();while (count++){if(count%60 == 0) LOG_D("Hello RT-Thread! %d", rt_tick_get());if(count%2 == 0){rt_kprintf("PWM_Duty = %d \r\n", (int)duty);rt_kprintf("PWM_FallingCount = %d, PWM_RisingCount = %d \r\n", PWM_FallingCount, PWM_RisingCount);}rt_thread_mdelay(1000);}return RT_EOK;
}/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(pwm_set, set pwm4 period/pulse. Usage: pwm_set pwm4 1 10000 5000);
在工程中,还使能TIM4、TIM13和TIM14为PWM输出。操作参见链接: RT-Thread Studio学习(三)PWM
将PWM输入引脚PA1和PWM输出引脚PD12短接,运行结果如下:

用逻辑分析仪查看3个PWM的输出引脚PA6、PA7和PD12:
