这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 软件与操作系统 » rtthread pwm框架分析

共5条 1/1 1 跳转至

rtthread pwm框架分析

助工
2024-10-30 20:52:34     打赏

PWM算是一个很基础的功能了,在LED灯控制上,为了保证高负荷时pwm灯不会出现明显闪烁感,一般都会直接采用硬件pwm的方式实现呼吸灯之类的效果。而在电机控制上,通过多路PWM信号的组合,可以做出精确的控制效果。RTT也支持这些功能,且瑞萨RA系列已经适配了这套框架,因此我们也需要查看RTT的PWM框架部分,以更加清晰的认识RTT PWM这块的设计。

PWM框架

代码位置

\components\drivers\misc\rt_drv_pwm.c

       说来也挺奇怪的,pwm框架的源码命名方式有些无厘头,乍一看名字,还以为是某颗IC的pwm驱动,个人认为最好是和adc,dac一样,直接命名为pwm.c更合适,但实际上,这是PWM的实现框架层所在的位置。

对接驱动层的接口

PWM驱动注册入口

#ifdef RT_USING_DEVICE_OPS
static const struct rt_device_ops pwm_device_ops =
{
    RT_NULL,
    RT_NULL,
    RT_NULL,
    _pwm_read,
    _pwm_write,
    _pwm_control
};
#endif /* RT_USING_DEVICE_OPS */

rt_err_t rt_device_pwm_register(struct rt_device_pwm *device, const char *name, const struct rt_pwm_ops *ops, const void *user_data)
{
    rt_err_t result = RT_EOK;

    rt_memset(device, 0, sizeof(struct rt_device_pwm));

#ifdef RT_USING_DEVICE_OPS
    device->parent.ops = &pwm_device_ops;
#else
    device->parent.init = RT_NULL;
    device->parent.open = RT_NULL;
    device->parent.close = RT_NULL;
    device->parent.read  = _pwm_read;
    device->parent.write = _pwm_write;
    device->parent.control = _pwm_control;
#endif /* RT_USING_DEVICE_OPS */

    device->parent.type         = RT_Device_Class_PWM;
    device->ops                 = ops;
    device->parent.user_data    = (void *)user_data;

    result = rt_device_register(&device->parent, name, RT_DEVICE_FLAG_RDWR);

    return result;
}

   PWM模块,对驱动的要求是注册时,需要提供一个struct rt_device_pwm *的设备句柄,并构造一个struct rt_pwm_ops *的控制接口集。而对应用暴露的接口,仅仅留了read,write,control三个接口。个人理解是,由于这些接口并不是直接暴露给应用去调用的(虽然应用也可以通过read,write,control去调用),且PWM只有所有参数配置完后才能使能,因此不需要实现init,open的函数,而close函数,其实也可以通过control函数间接实现,因此也可以省略。

PWM读接口

static rt_ssize_t _pwm_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    rt_err_t result = RT_EOK;
    struct rt_device_pwm *pwm = (struct rt_device_pwm *)dev;
    rt_uint32_t *pulse = (rt_uint32_t *)buffer;
    struct rt_pwm_configuration configuration = {0};

    configuration.channel = (pos > 0) ? (pos) : (-pos);

    if (pwm->ops->control)
    {
        result = pwm->ops->control(pwm, PWM_CMD_GET,  &configuration);
        if (result != RT_EOK)
        {
            return 0;
        }

        *pulse = configuration.pulse;
    }

    return size;
}

     有点神奇的是,pwm_read接口,居然仅仅是读pwm的脉宽信息,而不是读取pwm的当前配置。

PWM写接口

static rt_ssize_t _pwm_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    rt_err_t result = RT_EOK;
    struct rt_device_pwm *pwm = (struct rt_device_pwm *)dev;
    rt_uint32_t *pulse = (rt_uint32_t *)buffer;
    struct rt_pwm_configuration configuration = {0};

    configuration.channel = (pos > 0) ? (pos) : (-pos);

    if (pwm->ops->control)
    {
        result = pwm->ops->control(pwm, PWM_CMD_GET, &configuration);
        if (result != RT_EOK)
        {
            return 0;
        }

        configuration.pulse = *pulse;

        result = pwm->ops->control(pwm, PWM_CMD_SET, &configuration);
        if (result != RT_EOK)
        {
            return 0;
        }
    }

    return size;
}

       看到write接口,我大致猜到了为何read接口读取的是脉宽信息了,因为在设置时,也不需要动到周期信息,因此没必要在读的时候读取周期信息。另外,可能RTT PWM框架也有单独封装一个读取所有配置的接口。

PWM控制接口

static rt_err_t _pwm_control(rt_device_t dev, int cmd, void *args)
{
    rt_err_t result = RT_EOK;
    struct rt_device_pwm *pwm = (struct rt_device_pwm *)dev;
    struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)args;

    switch (cmd)
    {
    case PWMN_CMD_ENABLE:
        configuration->complementary = RT_TRUE;
        break;
    case PWMN_CMD_DISABLE:
        configuration->complementary = RT_FALSE;
        break;
    default:
        if (pwm->ops->control)
            result = pwm->ops->control(pwm, cmd, args);
        break;
    }

    return result;
}

    单纯的看control接口,可以发现这个设置有些怪怪的,PWMN_CMD_ENABLE 和 PWMN_CMD_DISABLE 仅仅是设置了传入数据的参数,那为何要把这个放到传入数据里面来,直接在上层改configuration->complementary的值不就好了吗?

     而如果去掉这个接口,会发现,这个control就是调用了驱动的control接口,而control接口需要实现什么功能,未知,只能通过后面的进一步分析才能弄明白。

对接应用层接口

PWM使能入口

rt_err_t rt_pwm_enable(struct rt_device_pwm *device, int channel)
{
    rt_err_t result = RT_EOK;
    struct rt_pwm_configuration configuration = {0};

    if (!device)
    {
        return -RT_EIO;
    }

    /* Make it is positive num forever */
    configuration.channel = (channel > 0) ? (channel) : (-channel);

    /* If channel is a positive number (0 ~ n), it means using normal output pin.
     * If channel is a negative number (0 ~ -n), it means using complementary output pin. */
    if (channel > 0)
    {
        result = rt_device_control(&device->parent, PWMN_CMD_DISABLE, &configuration);
    }
    else
    {
        result = rt_device_control(&device->parent, PWMN_CMD_ENABLE, &configuration);
    }

    result = rt_device_control(&device->parent, PWM_CMD_ENABLE, &configuration);

    return result;
}

    PWM打开接口,最大的亮点是备注,说明通道为正的时候是正常的pwm输出,而如果通道是负数,则认为是对应正数通道的互补输出。另外,驱动需要实现PWM_CMD_ENABLE命令,参数为struct rt_pwm_configuration中的complementary变量。

PWM关闭入口

rt_err_t rt_pwm_disable(struct rt_device_pwm *device, int channel)
{
    rt_err_t result = RT_EOK;
    struct rt_pwm_configuration configuration = {0};

    if (!device)
    {
        return -RT_EIO;
    }

    /* Make it is positive num forever */
    configuration.channel = (channel > 0) ? (channel) : (-channel);

    /* If channel is a positive number (0 ~ n), it means using normal output pin.
     * If channel is a negative number (0 ~ -n), it means using complementary output pin. */
    if (channel > 0)
    {
        result = rt_device_control(&device->parent, PWMN_CMD_DISABLE, &configuration);
    }
    else
    {
        result = rt_device_control(&device->parent, PWMN_CMD_ENABLE, &configuration);
    }

    result = rt_device_control(&device->parent, PWM_CMD_DISABLE, &configuration);

    return result;
}

    PWM关闭接口,与打开接口相对应,此接口需要驱动适配PWM_CMD_DISABLE功能,具体功能为关闭对应pwm通路功能。

PWM设置配置入口

rt_err_t rt_pwm_set(struct rt_device_pwm *device, int channel, rt_uint32_t period, rt_uint32_t pulse)
{
    rt_err_t result = RT_EOK;
    struct rt_pwm_configuration configuration = {0};

    if (!device)
    {
        return -RT_EIO;
    }

    configuration.channel = (channel > 0) ? (channel) : (-channel);
    configuration.period = period;
    configuration.pulse = pulse;
    result = rt_device_control(&device->parent, PWM_CMD_SET, &configuration);

    return result;
}

   这个函数基本上可以认为是一次性设置对应pwm所有参数的接口(不包括使能关闭),而这个数据需要驱动自行维护。

PWM设置周期入口

rt_err_t rt_pwm_set_period(struct rt_device_pwm *device, int channel, rt_uint32_t period)
{
    rt_err_t result = RT_EOK;
    struct rt_pwm_configuration configuration = {0};

    if (!device)
    {
        return -RT_EIO;
    }

    configuration.channel = (channel > 0) ? (channel) : (-channel);
    configuration.period = period;
    result = rt_device_control(&device->parent, PWM_CMD_SET_PERIOD, &configuration);

    return result;
}

     由于驱动自行维护pwm的信息,因此可以单独设置pwm周期。

PWM设置脉宽入口

rt_err_t rt_pwm_set_pulse(struct rt_device_pwm *device, int channel, rt_uint32_t pulse)
{
    rt_err_t result = RT_EOK;
    struct rt_pwm_configuration configuration = {0};

    if (!device)
    {
        return -RT_EIO;
    }

    configuration.channel = (channel > 0) ? (channel) : (-channel);
    configuration.pulse = pulse;
    result = rt_device_control(&device->parent, PWM_CMD_SET_PULSE, &configuration);

    return result;
}

     由于驱动自行维护pwm的信息,因此可以单独设置pwm脉宽。

PWM设置死区时间入口

rt_err_t rt_pwm_set_dead_time(struct rt_device_pwm *device, int channel, rt_uint32_t dead_time)
{
    rt_err_t result = RT_EOK;
    struct rt_pwm_configuration configuration = {0};

    if (!device)
    {
        return -RT_EIO;
    }

    configuration.channel = (channel > 0) ? (channel) : (-channel);
    configuration.dead_time = dead_time;
    result = rt_device_control(&device->parent, PWM_CMD_SET_DEAD_TIME, &configuration);

    return result;
}

    死区时间在互补模式时有效,主要是为了防止互补模式的两个通道同时导通。

PWM设置相位入口

rt_err_t rt_pwm_set_phase(struct rt_device_pwm *device, int channel, rt_uint32_t phase)
{
    rt_err_t result = RT_EOK;
    struct rt_pwm_configuration configuration = {0};

    if (!device)
    {
        return -RT_EIO;
    }

    configuration.channel = (channel > 0) ? (channel) : (-channel);
    configuration.phase = phase;
    result = rt_device_control(&device->parent, PWM_CMD_SET_PHASE, &configuration);

    return result;
}

    个人理解所谓的相位就是PWM高电平出现在整个周期里的位置。在互补模式时才有意义,单通道时,相位没有任何作用。

PWM获取配置入口

static rt_err_t rt_pwm_get(struct rt_device_pwm *device, struct rt_pwm_configuration *cfg)
{
    rt_err_t result = RT_EOK;

    if (!device)
    {
        return -RT_EIO;
    }

    result = rt_device_control(&device->parent, PWM_CMD_GET, cfg);

    return result;
}

    如果应用不维护脉宽,周期,相位,死区时间等信息的话,此接口就非常有必要了。

总结

   平时用RTT的PWM,用的比较多的是单路PWM输出,而且PWM官方提供的PWM操作指引,也仅仅提到了单路PWM输出模式。而实际上,RTT的PWM框架是支持PWM互补模式的,这就需要我们通过查看PWM驱动文件的方式来查看。

   另外,由于官方提供的文档并未说明支持互补输出,死区控制之类的功能,不少芯片即使支持这些功能,也不一定有适配这样的功能,在使用PWM模块时需注意。






关键词: rtthread     设备驱动     PWM    

专家
2024-10-31 00:00:20     打赏
2楼

感谢分享


专家
2024-10-31 00:02:20     打赏
3楼

感谢分享


专家
2024-10-31 00:30:45     打赏
4楼

感谢分享


高工
2024-11-24 22:32:34     打赏
5楼

感谢楼主分享


共5条 1/1 1 跳转至

回复

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