RT-Thread 中配置 PWM 输出详细过程

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、结果

在这里插入图片描述

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值