RT-Thread 中配置 PWM 输出详细过程
强烈建议,在配置PWM输出前,先将PWM对应管脚配置成普通GPIO,并高低切换输出,用示波器或万用表检测输出,以验证电路板的没问题!
官方教程:
/** if you want to use pwm you can use the following instructions.
STEP 1, open pwm driver framework support in the RT-Thread Settings file
STEP 2, define macro related to the pwm
such as #define BSP_USING_PWM1
STEP 3, copy your pwm timer init function from stm32xxxx_hal_msp.c generated by stm32cubemx to the end if board.c file
such as void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base) and
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* htim)
STEP 4, modify your stm32xxxx_hal_config.h file to support pwm peripherals. define macro related to the peripherals
such as #define HAL_TIM_MODULE_ENABLED
*/
1、配置 RT-Thread Setting
这个没啥好说的,用rt thread studio就配置,用mdk5就用env中的menuconfig配置
2、board.h中给出定义
这个地方要加入,BSP_USING_PWM1_CH1,这类通道宏,这样才能通过drv_pwm.c文件中pwm_get_channel()函数打开通道。
当然工程中如果没有drv_pwm.h和drv_pwm.c,可以从rt thread源文件中拷贝出来。
#define BSP_USING_PWM1
#define BSP_USING_PWM1_CH1
#define BSP_USING_PWM1_CH2
#define BSP_USING_PWM1_CH3
#define BSP_USING_PWM3
#define BSP_USING_PWM3_CH1
#define BSP_USING_PWM3_CH2
#define BSP_USING_PWM3_CH3
#define BSP_USING_PWM4
#define BSP_USING_PWM4_CH1
#define BSP_USING_PWM4_CH2
#define BSP_USING_PWM4_CH3
pwm_get_channel()函数原型如下
static void pwm_get_channel(void)
{
#ifdef BSP_USING_PWM1_CH1
stm32_pwm_obj[PWM1_INDEX].channel |= 1 << 0;
#endif
#ifdef BSP_USING_PWM1_CH2
stm32_pwm_obj[PWM1_INDEX].channel |= 1 << 1;
#endif
#ifdef BSP_USING_PWM1_CH3
stm32_pwm_obj[PWM1_INDEX].channel |= 1 << 2;
#endif
....................
}
3、加入cubemx生成的配置函数
在stm32裸机配置外设中,无非三个步骤,1、配置管脚,2、使能时钟(管脚时钟和外设时钟),3、配置外设
rt thread通过使用rt device的方法,帮你封装了第三步,配置外设,而前两个步骤就需要自己配置
官方给出需要拷贝void HAL_TIM_Base_MspInit(TIM_HandleTypeDef htim_base) 和void HAL_TIM_MspPostInit(TIM_HandleTypeDef htim)这两个函数到board.c中
void HAL_TIM_MspPostInit(TIM_HandleTypeDef htim);这个函数是配置对应管脚,相信大家在配置如IIC,ADC都遇到类似函数
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef htim_base);这个函数是配置时钟的函数。
在配置完成后,generate code,生成对应工程,但可能是由于cubemx版本问题,
在time.c文件中只找到配置管脚void HAL_TIM_MspPostInit(TIM_HandleTypeDef htim); 函数,
而配置时钟的void HAL_TIM_Base_MspInit(TIM_HandleTypeDef htim_base) ;没有找到
/****************************************************************************
名称:HAL_TIM_MspPostInit
功能:配置tim管脚
参数:
作者:cubeMx
日期:2021年12月15日
**************************************************************************/
void HAL_TIM_MspPostInit(TIM_HandleTypeDef timHandle)
{
rt_kprintf("tim_gpio_init\r\n");
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(timHandle->Instance==TIM1)
{
/ USER CODE BEGIN TIM1_MspPostInit 0 /
/ USER CODE END TIM1_MspPostInit 0 */
__HAL_RCC_GPIOE_CLK_ENABLE();
/**TIM1 GPIO Configuration
PE9 ------> TIM1_CH1
PE11 ------> TIM1_CH2
PE13 ------> TIM1_CH3
/
GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_11|GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/ USER CODE BEGIN TIM1_MspPostInit 1 /
/ USER CODE END TIM1_MspPostInit 1 /
}
else if(timHandle->Instance==TIM3)
{
/ USER CODE BEGIN TIM3_MspPostInit 0 /
/ USER CODE END TIM3_MspPostInit 0 */
__HAL_RCC_GPIOC_CLK_ENABLE();
/**TIM3 GPIO Configuration
PC6 ------> TIM3_CH1
PC7 ------> TIM3_CH2
PC8 ------> TIM3_CH3
/
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/ USER CODE BEGIN TIM3_MspPostInit 1 /
/ USER CODE END TIM3_MspPostInit 1 /
}
else if(timHandle->Instance==TIM4)
{
/ USER CODE BEGIN TIM4_MspPostInit 0 /
/ USER CODE END TIM4_MspPostInit 0 */
__HAL_RCC_GPIOD_CLK_ENABLE();
/**TIM4 GPIO Configuration
PD12 ------> TIM4_CH1
PD13 ------> TIM4_CH2
PD14 ------> TIM4_CH3
/
GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM4;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/ USER CODE BEGIN TIM4_MspPostInit 1 /
/ USER CODE END TIM4_MspPostInit 1 */
}
}
虽然没有找到 void HAL_TIM_Base_MspInit(TIM_HandleTypeDef htim_base) ;,
但是找到了同样功能的 void HAL_TIM_OC_MspInit(TIM_HandleTypeDef tim_ocHandle);
作用都是将TIM的时钟使能,将这个函数复制到board.c中,并在drv_pwm.c中调用,调用位置可以放在
static rt_err_t stm32_hw_pwm_init(struct stm32_pwm *device)中的靠前部分(测试过,靠后可能失效)
其实,知道原理的话,这个问题好解,比如可以自己将__HAL_RCC_TIM1_CLK_ENABLE();放到函数static int stm32_pwm_init(void)中调用,效果也是一样的,都是使能时钟罢了。
void HAL_TIM_OC_MspInit(TIM_HandleTypeDef* tim_ocHandle)
{
if(tim_ocHandle->Instance==TIM1)
{
/* USER CODE BEGIN TIM1_MspInit 0 /
/ USER CODE END TIM1_MspInit 0 /
/ TIM1 clock enable /
__HAL_RCC_TIM1_CLK_ENABLE();
/ USER CODE BEGIN TIM1_MspInit 1 /
/ USER CODE END TIM1_MspInit 1 */
}
else if(tim_ocHandle->Instance==TIM3)
{
/* USER CODE BEGIN TIM3_MspInit 0 */
/* USER CODE END TIM3_MspInit 0 */
/* TIM3 clock enable */
__HAL_RCC_TIM3_CLK_ENABLE();
/* USER CODE BEGIN TIM3_MspInit 1 */
/* USER CODE END TIM3_MspInit 1 */
}
else if(tim_ocHandle->Instance==TIM4)
{
/* USER CODE BEGIN TIM4_MspInit 0 */
/* USER CODE END TIM4_MspInit 0 */
/* TIM4 clock enable */
__HAL_RCC_TIM4_CLK_ENABLE();
/* USER CODE BEGIN TIM4_MspInit 1 */
/* USER CODE END TIM4_MspInit 1 */
}
}
调用位置:
static rt_err_t stm32_hw_pwm_init(struct stm32_pwm *device)
{
rt_err_t result = RT_EOK;
TIM_HandleTypeDef *tim = RT_NULL;
TIM_OC_InitTypeDef oc_config = {0};
TIM_MasterConfigTypeDef master_config = {0};
TIM_ClockConfigTypeDef clock_config = {0};
RT_ASSERT(device != RT_NULL);
tim = (TIM_HandleTypeDef )&device->tim_handle;
/ configure the timer to pwm mode */
tim->Init.Prescaler = 0;
tim->Init.CounterMode = TIM_COUNTERMODE_UP;
tim->Init.Period = 0;
tim->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
#if defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32L4)
tim->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
#endif
//时钟配置,调用位置在此处,尽量放在前面,经过测试,放在后面就失效了
HAL_TIM_OC_MspInit(tim);
if (HAL_TIM_PWM_Init(tim) != HAL_OK)
{
LOG_E("%s pwm init failed", device->name);
result = -RT_ERROR;
goto __exit;
}
省略.....
}
4、在stm32f4xx_hal_conf.h中打开宏:
#define HAL_TIM_MODULE_ENABLED
5、放个简单测试代码
#include <rtthread.h>
#include <rtdevice.h>
//PWM使能管脚
#define PWM_PITCH_EN 78 //PE14
#define PWM_ROLL_EN 38 //PC6
#define PWM_YAW_EN 60 //PD12
#define PWM_DEV_NAME "pwm1" /* PWM设备名称 /
#define PWM_DEV_CHANNEL 1 / PWM通道 */
struct rt_device_pwm pwm_dev; / PWM设备句柄 /
static int pwm()
{
rt_uint32_t period, pulse;
period = 10000; / 周期为0.5ms,单位为纳秒ns / //500000 8s
pulse = 0; / PWM脉冲宽度值,单位为纳秒ns /
/ 查找设备 */
pwm_dev = (struct rt_device_pwm )rt_device_find(PWM_DEV_NAME);
if (pwm_dev == RT_NULL)
{
rt_kprintf("pwm sample run failed! can't find %s device!\n",PWM_DEV_NAME);
return RT_ERROR;
}
/ 使能设备 /
rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
pulse = period/2;
/ 设置PWM周期和脉冲宽度 */
rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
return 0;
}
INIT_APP_EXPORT(pwm);
6、结果
原作者:修理工小刘