本文将基于STM32F407VET芯片介绍如何在RT-Thread Studio开发环境下使用定时器的PWM输入模式进行脉宽和周期测量。硬件及开发环境如下:
打开RT-Thread Studio软件新建基于芯片的项目,并使用外部时钟系统,具体参见《RT-Thread Studio学习(一)使用外部时钟系统》。
打开PWM驱动框架
在RT-Thread Setting
中借助图形化配置工具打开组件中的HWTIMER的驱动框架,如下图所示:
定义ADC相关的宏
将TIM2配置为PWM输入模式,在board.h
文件中使能宏定义:
#define BSP_USING_TIM
#ifdef BSP_USING_TIM
#define BSP_USING_TIM2
#endif
再重新生成STM32CubeMX代码,将.\cubemx\Src\adc.c中的函数HAL_DAC_MspInit(DAC_HandleTypeDef* dacHandle)
复制到board.c的末尾。
在Application文件夹中添加头文件pwm_input.h
,代码如下:
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-01-18 Administrator the first version
*/
#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
,代码如下:
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-01-18 Administrator the first version
*/
#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 = (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
。
从STM32CubeMX项目生成后的工程文件.\cubemx\Src\tim.c
中复制函数void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
和void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
到board.c
的末尾。
另外,再复制函数HAL_TIM_Base_MspInit
到board.c
的末尾,并将其函数名改成HAL_TIM_PWM_MspInit
。
修改.\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: