这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 行业应用 » 汽车电子 » 【S32K146】 Rt-thread PWM 配置使用

共1条 1/1 1 跳转至

【S32K146】 Rt-thread PWM 配置使用

高工
2025-06-24 08:37:57     打赏

【简介】          

PWM(Pulse Width Modulation , 脉冲宽度调制) ,在 Rt-thread 把PWM 设备抽象为PWM 设备驱动框架,设备驱动代码为components\drivers\misc\rt_drv_pwm.c ,应用层通过PWM设备驱动层的 rt_pwm_enable,rt_pwm_disable ,rt_pwm_set ,rt_pwm_set_period ,rt_pwm_set_pulse ,rt_pwm_set_dead_time ,rt_pwm_set_phase ,rt_pwm_get 接口调用底层提供的服务接口,rt_device_pwm_register 接口可以将PWM 设备注册到系统中。

rt-thread pwm 设备device 结构体,编写PWM设备驱动基于该结构体派生出新的PWM设备结构体。

struct rt_device_pwm;
struct rt_pwm_ops
{
    rt_err_t (*control)(struct rt_device_pwm *device, int cmd, void *arg);
};

struct rt_device_pwm
{
    struct rt_device parent;
    const struct rt_pwm_ops *ops;
};

PWM 设备的配置通过以下结构体进行配置

struct rt_pwm_configuration
{
    rt_uint32_t channel; /* 0 ~ n or 0 ~ -n, which depends on specific MCU requirements */
    rt_uint32_t period;  /* unit:ns 1ns~4.29s:1Ghz~0.23hz */
    rt_uint32_t pulse;   /* unit:ns (pulse<=period) */
    rt_uint32_t dead_time;  /* unit:ns */
    rt_uint32_t phase;  /*unit: degree, 0~360, which is the phase of pwm output, */
    /*
     * RT_TRUE  : The channel of pwm is complememtary.
     * RT_FALSE : The channel of pwm is nomal.
    */
    rt_bool_t  complementary;
};

PWM 设备驱动的启用在env 的menuconfig  下开启

image.png

生成工程后,pwm 驱动框架的已经被加入工程

image.png

PWM 驱动适配底层和芯片的接口我们在Kconfig 中添加开启PWM驱动的配置开关

    menuconfig BSP_USING_PWM
        bool "Enable PWM"
        default n
        select RT_USING_PWM

并在SConscript 文件中加入编译选项的配置,我们在drv_pwm 文件中适配PWM驱动

# add pwm driver
if GetDepend('BSP_USING_PWM'):
    src += ['drv_pwm.c']

在menuconfig 中开启PWM 驱动

image.png

我们添加如下代码添加PWM设备

/** *****************************************************************************************************
 * \file     drv_pwm.c                                                                            *
 *******************************************************************************************************/
/********************************************************************************************************
 *                                      Include header files                                            *
 *******************************************************************************************************/
#include <rtthread.h>
#include <rtdevice.h>
#include "board.h"
/********************************************************************************************************
 *                                 Private Macro definition                                             *
 *******************************************************************************************************/
#define DBG_ENABLE
#define DBG_TAG                         "drv.pwm"
#define DBG_LEVEL                       DBG_LOG
#define DBG_COLOR
#include <rtdbg.h>

/********************************************************************************************************
 *                                    Private Type Declarations                                         *
 *******************************************************************************************************/
struct s32k14x_pwm
{
    struct rt_device_pwm pwm_device;
    rt_uint8_t instance;
    ftm_user_config_t * user_cfg;
    ftm_pwm_param_t * pwm_cfg;
    ftm_state_t state;
    char *name;
};

/********************************************************************************************************
 *                                  Private Variable Definitions                                        *
 *******************************************************************************************************/
static struct s32k14x_pwm  ftm0_pwm5 = 
{
    .instance = INST_FLEXTIMER_PWM_1,
    .user_cfg = &flexTimer_pwm_1_InitConfig,
    .pwm_cfg = &flexTimer_pwm_1_PwmConfig,
    .name = "pwm0"
};

static FTM_Type * const g_pwm_ftmBase[FTM_INSTANCE_COUNT] = FTM_BASE_PTRS;
static rt_err_t drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg);
static struct rt_pwm_ops drv_ops =
{
    drv_pwm_control
};
/********************************************************************************************************
 *                                  Private Function Declarations                                       *
 *******************************************************************************************************/
static rt_err_t drv_pwm_enable(rt_uint32_t instance,struct rt_pwm_configuration *configuration, rt_bool_t enable)
{
    status_t ret;
    if (!configuration->complementary)
    {
        ret = FTM_DRV_ControlChannelOutput(instance, configuration->channel,enable);
    }
    
    return  ret == STATUS_SUCCESS ? RT_EOK : RT_ERROR;
}

static rt_err_t drv_pwm_set_period(rt_uint32_t instance, struct rt_pwm_configuration *configuration)
{
    status_t ret;
    /* Convert nanosecond to frequency and duty cycle. 1s = 1 * 1000 * 1000 * 1000 ns */
    ret = FTM_DRV_UpdatePwmPeriod(instance,FTM_PWM_UPDATE_IN_DUTY_CYCLE,1000000000/configuration->period,true);
    
    return  ret == STATUS_SUCCESS ? RT_EOK : RT_ERROR;
}


static rt_err_t drv_pwm_set_pulse(rt_uint32_t instanc, ftm_state_t *state,struct rt_pwm_configuration *configuration)
{
    status_t ret;
    uint16_t firstEdge;
    uint16_t duty;

    duty = (configuration->pulse*100)/(1000000000/state->ftmSourceClockFrequency);

    firstEdge = (uint16_t)(327.68f*((float)duty));
    ret = FTM_DRV_UpdatePwmChannel(INST_FLEXTIMER_PWM_1,configuration->channel,FTM_PWM_UPDATE_IN_DUTY_CYCLE,firstEdge,0U,true);   
    
    return  ret == STATUS_SUCCESS ? RT_EOK : RT_ERROR;

}


static rt_err_t drv_pwm_set(rt_uint32_t instance, ftm_state_t *state ,struct rt_pwm_configuration *configuration)
{
    status_t ret = STATUS_SUCCESS;
    uint16_t firstEdge;
    uint16_t duty;

    /* Convert nanosecond to frequency and duty cycle. 1s = 1 * 1000 * 1000 * 1000 ns */
    ret = FTM_DRV_UpdatePwmPeriod(instance,FTM_PWM_UPDATE_IN_DUTY_CYCLE,1000000000/configuration->period,true);
    if(ret != STATUS_SUCCESS)
        goto  __exit;

    duty = (configuration->pulse*100)/(configuration->period);

    firstEdge = (uint16_t)(327.68f*((float)duty));
    ret = FTM_DRV_UpdatePwmChannel(INST_FLEXTIMER_PWM_1,configuration->channel,FTM_PWM_UPDATE_IN_DUTY_CYCLE,firstEdge,0U,true);

__exit:     
    return  ret == STATUS_SUCCESS ? RT_EOK : RT_ERROR;
}

static rt_err_t drv_pwm_get(rt_uint32_t instance,ftm_state_t *state, struct rt_pwm_configuration *configuration)
{
    uint32_t mod = g_pwm_ftmBase[instance]->MOD + 1u;
    uint32_t cval = g_pwm_ftmBase[instance]->CONTROLS[configuration->channel].CnV;
    configuration->period = 1000000000/(state->ftmSourceClockFrequency/state->ftmPeriod);
    configuration->pulse = configuration->period*cval/mod;
    return RT_EOK;
}

 static rt_err_t drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
{
    struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg;
    rt_uint32_t instance = ((struct s32k14x_pwm *)(device->parent.user_data))->instance;
    switch(cmd)
    {
    case PWM_CMD_ENABLE:
      return drv_pwm_enable(instance,configuration,RT_TRUE);
    case PWM_CMD_DISABLE:
      return drv_pwm_enable(instance,configuration,RT_FALSE);
    case PWM_CMD_SET:
        return drv_pwm_set(instance,&((struct s32k14x_pwm *)(device->parent.user_data))->state,configuration);
    case PWM_CMD_SET_PERIOD:
      return drv_pwm_set_period(instance,configuration);
    case PWM_CMD_SET_PULSE:
        return drv_pwm_set_pulse(instance,&((struct s32k14x_pwm *)(device->parent.user_data))->state,configuration);
    case PWM_CMD_GET:
        return drv_pwm_get(instance,&((struct s32k14x_pwm *)(device->parent.user_data))->state,configuration);
    default:
        return -RT_EINVAL;
    }
}

/********************************************************************************************************
 *                                  Global Function Declarations                                        *
 *******************************************************************************************************/
static int s32k14x_pwm_init(void)
{
    status_t ret;
    int result = RT_EOK;
    /* config pwm timer */
    ret = FTM_DRV_Init(ftm0_pwm5.instance, ftm0_pwm5.user_cfg, &ftm0_pwm5.state);
    if(ret != STATUS_SUCCESS)
    {
        LOG_E("Ftm0 init failed %x.",ret);
        result = -RT_ERROR;
        goto __exit;
    }
    else
    {
        LOG_I("Ftm0 init OK.");
    }

    ret = FTM_DRV_InitPwm(ftm0_pwm5.instance, ftm0_pwm5.pwm_cfg);
    if(ret != STATUS_SUCCESS)
    {
        LOG_E("Ftm0 pwm init failed %x.",ret);
        result = -RT_ERROR;
        goto __exit;
    }
    else
    {
        LOG_I("Ftm0 pwm init OK.");
    }
    
    if(rt_device_pwm_register(&ftm0_pwm5.pwm_device,ftm0_pwm5.name,&drv_ops,&ftm0_pwm5) == RT_EOK)
    {
        LOG_D("%s register success", ftm0_pwm5.name);
    }
    else
    {
        LOG_E("%s register failed", ftm0_pwm5.name);
        result = -RT_ERROR;
    }

__exit:
    return result;
}
INIT_DEVICE_EXPORT(s32k14x_pwm_init);

以上代码会注册设备到系统中,并会添加pwm 命令对应代码如下

#ifdef RT_USING_FINSH
#include <stdlib.h>
#include <string.h>
#include <finsh.h>

static int pwm(int argc, char **argv)
{
    rt_err_t result = -RT_ERROR;
    char *result_str;
    static struct rt_device_pwm *pwm_device = RT_NULL;
    struct rt_pwm_configuration cfg = {0};

    if(argc > 1)
    {
        if(!strcmp(argv[1], "probe"))
        {
            if(argc == 3)
            {
                pwm_device = (struct rt_device_pwm *)rt_device_find(argv[2]);
                result_str = (pwm_device == RT_NULL) ? "failure" : "success";
                rt_kprintf("probe %s %s\n", argv[2], result_str);
            }
            else
            {
                rt_kprintf("pwm probe <device name>                  - probe pwm by name\n");
            }
        }
        else
        {
            if(pwm_device == RT_NULL)
            {
                rt_kprintf("Please using 'pwm probe <device name>' first.\n");
                return -RT_ERROR;
            }
            if(!strcmp(argv[1], "enable"))
            {
                if(argc == 3)
                {
                    result = rt_pwm_enable(pwm_device, atoi(argv[2]));
                    result_str = (result == RT_EOK) ? "success" : "failure";
                    rt_kprintf("%s channel %d is enabled %s \n", pwm_device->parent.parent.name, atoi(argv[2]), result_str);
                }
                else
                {
                    rt_kprintf("pwm enable <channel>                     - enable pwm channel\n");
                    rt_kprintf("    e.g. MSH >pwm enable  1              - PWM_CH1  nomal\n");
                    rt_kprintf("    e.g. MSH >pwm enable -1              - PWM_CH1N complememtary\n");
                }
            }
            else if(!strcmp(argv[1], "disable"))
            {
                if(argc == 3)
                {
                    result = rt_pwm_disable(pwm_device, atoi(argv[2]));
                }
                else
                {
                    rt_kprintf("pwm disable <channel>                    - disable pwm channel\n");
                }
            }
            else if(!strcmp(argv[1], "get"))
            {
                cfg.channel = atoi(argv[2]);
                result = rt_pwm_get(pwm_device, &cfg);
                if(result == RT_EOK)
                {
                    rt_kprintf("Info of device [%s] channel [%d]:\n",pwm_device, atoi(argv[2]));
                    rt_kprintf("period      : %d\n", cfg.period);
                    rt_kprintf("pulse       : %d\n", cfg.pulse);
                    rt_kprintf("Duty cycle  : %d%%\n",(int)(((double)(cfg.pulse)/(cfg.period)) * 100));
                }
                else
                {
                    rt_kprintf("Get info of device: [%s] error.\n", pwm_device);
                }
            }
            else if (!strcmp(argv[1], "set"))
            {
                if(argc == 5)
                {
                    result = rt_pwm_set(pwm_device, atoi(argv[2]), atoi(argv[3]), atoi(argv[4]));
                    rt_kprintf("pwm info set on %s at channel %d\n",pwm_device,(rt_base_t)atoi(argv[2]));
                }
                else
                {
                    rt_kprintf("Set info of device: [%s] error\n", pwm_device);
                    rt_kprintf("Usage: pwm set <channel> <period> <pulse>\n");
                }
            }
            else if(!strcmp(argv[1], "phase"))
            {
                if(argc == 4)
                {
                    result = rt_pwm_set_phase(pwm_device, atoi(argv[2]),atoi(argv[3]));
                    result_str = (result == RT_EOK) ? "success" : "failure";
                    rt_kprintf("%s phase is set %d \n", pwm_device->parent.parent.name, (rt_base_t)atoi(argv[3]));
                }
            }
            else if(!strcmp(argv[1], "dead_time"))
            {
                if(argc == 4)
                {
                    result = rt_pwm_set_dead_time(pwm_device, atoi(argv[2]),atoi(argv[3]));
                    result_str = (result == RT_EOK) ? "success" : "failure";
                    rt_kprintf("%s dead_time is set %d \n", pwm_device->parent.parent.name, (rt_base_t)atoi(argv[3]));
                }
            }
            else
            {
                rt_kprintf("Usage: \n");
                rt_kprintf("pwm probe      <device name>                - probe pwm by name\n");
                rt_kprintf("pwm enable     <channel>                    - enable pwm channel\n");
                rt_kprintf("pwm disable    <channel>                    - disable pwm channel\n");
                rt_kprintf("pwm get        <channel>                    - get pwm channel info\n");
                rt_kprintf("pwm set        <channel> <period> <pulse>   - set pwm channel info\n");
                rt_kprintf("pwm phase      <channel> <phase>            - set pwm phase\n");
                rt_kprintf("pwm dead_time  <channel> <dead_time>        - set pwm dead time\n");
                result = -RT_ERROR;
            }
        }
    }
    else
    {
        rt_kprintf("Usage: \n");
        rt_kprintf("pwm probe      <device name>               - probe pwm by name\n");
        rt_kprintf("pwm enable     <channel>                   - enable pwm channel\n");
        rt_kprintf("pwm disable    <channel>                   - disable pwm channel\n");
        rt_kprintf("pwm get        <channel>                   - get pwm channel info\n");
        rt_kprintf("pwm set        <channel> <period> <pulse>  - set pwm channel info\n");
        rt_kprintf("pwm phase      <channel> <phase>           - set pwm phase\n");
        rt_kprintf("pwm dead_time  <channel> <dead_time>       - set pwm dead time\n");
        result = -RT_ERROR;
    }

    return RT_EOK;
}
MSH_CMD_EXPORT(pwm, pwm [option]);

#endif /* RT_USING_FINSH */

image.png

我们就可以使用pwm 命令来配置PWM 外设

image.png

【功能验证】

使用上述的pwm 命令来验证pwm 功能,首先使用pwm probe 命令来查找pwm0 设备

image.png

使用pwm set 命令来配置周期和脉冲宽度时长对应单位为ns

image.png

使用pwm get 来获取当前pwm 的信息

image.png

逻辑分析仪抓取波形和配置的参数保持一致

image.png







共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]