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

共9条 1/1 1 跳转至

rtthread输入采集设备框架解析

助工
2024-10-26 23:08:41     打赏

背景

   输入采集设备,就是以硬件的方式采集输入PWM信号的周期和占空比的。在RTT里面是比较晚(2019年提交)才被加进去,这类设备的启用,一般都需要PWM模块参与。根据目前遇到的方案上来看,新塘半导体、ST、NXP都有硬件层面支持输入采集设备的实现。而在RTT目前开源的代码上来看,输入采集设备只有新塘半导体有适配。

框架解析

源码路径components/drivers/misc/rt_inputcapture.c

注册入口

#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops inputcapture_ops =
{
    rt_inputcapture_init,
    rt_inputcapture_open,
    rt_inputcapture_close,
    rt_inputcapture_read,
    RT_NULL,
    rt_inputcapture_control
};
#endif

rt_err_t rt_device_inputcapture_register(struct rt_inputcapture_device *inputcapture, const char *name, void *user_data)
{
    struct rt_device *device;

    RT_ASSERT(inputcapture != RT_NULL);
    RT_ASSERT(inputcapture->ops != RT_NULL);
    RT_ASSERT(inputcapture->ops->get_pulsewidth != RT_NULL);

    device = &(inputcapture->parent);

    device->type        = RT_Device_Class_Miscellaneous;
    device->rx_indicate = RT_NULL;
    device->tx_complete = RT_NULL;
    inputcapture->ringbuff = RT_NULL;

#ifdef RT_USING_DEVICE_OPS
    device->ops         = &inputcapture_ops;
#else
    device->init        = rt_inputcapture_init;
    device->open        = rt_inputcapture_open;
    device->close       = rt_inputcapture_close;
    device->read        = rt_inputcapture_read;
    device->write       = RT_NULL;
    device->control     = rt_inputcapture_control;
#endif
    device->user_data   = user_data;

    return rt_device_register(device, name, RT_DEVICE_FLAG_RDONLY | RT_DEVICE_FLAG_STANDALONE);
}

   整体来说,注册入口和脉冲编码器类似,唯一的区别是多了个ringbuffer,这个ringbuffer的作用,可以在后面的解析中看到。

初始化入口

static rt_err_t rt_inputcapture_init(struct rt_device *dev)
{
    rt_err_t ret;
    struct rt_inputcapture_device *inputcapture;

    RT_ASSERT(dev != RT_NULL);

    ret = RT_EOK;
    inputcapture = (struct rt_inputcapture_device *)dev;
    inputcapture->watermark = RT_INPUT_CAPTURE_RB_SIZE / 2;
    if (inputcapture->ops->init)
    {
        ret = inputcapture->ops->init(inputcapture);
    }

    return ret;
}

   这里,有个水位控制的标记,从目前的代码上看,看不出有什么功能,但在后续的代码分析中,会发现此标记是用来确保已经采集到的数据达到判定标准时上报的。

打开入口

static rt_err_t rt_inputcapture_open(struct rt_device *dev, rt_uint16_t oflag)
{
    rt_err_t ret;
    struct rt_inputcapture_device *inputcapture;

    RT_ASSERT(dev != RT_NULL);

    ret = RT_EOK;
    inputcapture = (struct rt_inputcapture_device *)dev;
    if (inputcapture->ringbuff == RT_NULL)
    {
        inputcapture->ringbuff = rt_ringbuffer_create(sizeof(struct rt_inputcapture_data) * RT_INPUT_CAPTURE_RB_SIZE);
    }
    if (inputcapture->ops->open)
    {
        ret = inputcapture->ops->open(inputcapture);
    }

    return ret;
}

   打开入口可以看到一个很明显的操作,在注册入口中出现ringbuffer在打开入口中被初始化,初始化的大小为水位标记的2倍。但ringbuffer的作用还是需要后续继续看。

控制入口

static rt_err_t rt_inputcapture_control(struct rt_device *dev, int cmd, void *args)
{
    rt_err_t result;
    struct rt_inputcapture_device *inputcapture;

    RT_ASSERT(dev != RT_NULL);

    result = RT_EOK;
    inputcapture = (struct rt_inputcapture_device *)dev;
    switch (cmd)
    {
    case INPUTCAPTURE_CMD_CLEAR_BUF:
        if (inputcapture->ringbuff)
        {
            rt_ringbuffer_reset(inputcapture->ringbuff);
        }
        break;
    case INPUTCAPTURE_CMD_SET_WATERMARK:
        inputcapture->watermark = *(rt_size_t *)args;
        break;
    default:
        result = -RT_ENOSYS;
        break;
    }

    return result;
}

   控制入口预留了两个操作,一个是清空ringbuffer,在后面的分析中,可以知道这个ringbuffer其实就是输入采集的高低电平的持续时间的buffer,因此可以认为是清除已采集的数据。另一个接口是设置水位,不过个人并不觉得这个实现安全,因为在后面的分析中会发现,这个标记的作用是采集到的数据量够了,就通知应用读取数据,而数据存储于ringbuffer中,ringbuffer是由大小限制的,而这个交由应用设置的接口,并没有做任何数据溢出的判定,若设置下来的数据不符合要求,明显会导致整个系统运行不稳定。

读入口

static rt_ssize_t rt_inputcapture_read(struct rt_device *dev,
                                 rt_off_t          pos,
                                 void             *buffer,
                                 rt_size_t         size)
{
    rt_size_t receive_size;
    struct rt_inputcapture_device *inputcapture;

    RT_ASSERT(dev != RT_NULL);

    inputcapture = (struct rt_inputcapture_device *)dev;
    receive_size = rt_ringbuffer_get(inputcapture->ringbuff, (rt_uint8_t *)buffer, sizeof(struct rt_inputcapture_data) * size);

    return receive_size / sizeof(struct rt_inputcapture_data);
}

   从实现上来看,读入口就是我上面提到的,从ringbuffer中读取数据,读取完毕后将实际读取的数量上报至应用端。

关闭入口

static rt_err_t rt_inputcapture_close(struct rt_device *dev)
{
    rt_err_t ret;
    struct rt_inputcapture_device *inputcapture;

    RT_ASSERT(dev != RT_NULL);

    ret = -RT_ERROR;
    inputcapture = (struct rt_inputcapture_device *)dev;

    if (inputcapture->ops->close)
    {
        ret = inputcapture->ops->close(inputcapture);
    }

    if (ret != RT_EOK)
    {
        return ret;
    }

    if (inputcapture->ringbuff)
    {
        rt_ringbuffer_destroy(inputcapture->ringbuff);
        inputcapture->ringbuff = RT_NULL;
    }
    return ret;
}

   关闭入口,其实大差不差,就是关闭设备并销毁资源。在输入采集设备框架里,唯一的资源便是ringbuffer,因此销毁资源也就是销毁这一部分。

需要驱动实现的中断处理入口

   前面的实现,其实会发现,往ringbuffer塞数据的位置并没有体现,另外,通知应用端数据来了的接口也没体现,而这部分的实现,都是在中断处理函数中实现的。

void rt_hw_inputcapture_isr(struct rt_inputcapture_device *inputcapture, rt_bool_t level)
{
    struct rt_inputcapture_data data;
    rt_size_t receive_size;
    if (inputcapture->ops->get_pulsewidth(inputcapture, &data.pulsewidth_us) != RT_EOK)
    {//获取采集到信号的时间,如果未获取正确的数据,则直接退出中断处理
        return;
    }

    data.is_high = level; // 设置采集数据的电平标记
    if (rt_ringbuffer_put(inputcapture->ringbuff, (rt_uint8_t *)&data, sizeof(struct rt_inputcapture_data)) == 0)
    { //将数据存储至rinbuffer中
        LOG_W("inputcapture ringbuffer doesn't have enough space.");
    }

    //统计目前已有的数据量,当数据量超过水位时,通知应用读取数据
    receive_size =  rt_ringbuffer_data_len(inputcapture->ringbuff) / sizeof(struct rt_inputcapture_data);

    if (receive_size >= inputcapture->watermark)
    {
        /* indicate to upper layer application */
        if (inputcapture->parent.rx_indicate != RT_NULL)
            inputcapture->parent.rx_indicate(&inputcapture->parent, receive_size);
    }
}

总结

   至此,我们已经分析完输入采集设备的实现了。通过分析此实现,我们可以得到以下输入采集设备实现框架,后面编写驱动,直接套用此模板实现,便可对接RTT的驱动框架。

#define TIMER_CHANNEL_NUM 4

typedef struct _timer
{
    struct rt_inputcapture_device   parent;
    
    // 驱动内部数据
} capture_t;

// TODO:输入捕获中断处理函数,内部调用接口rt_hw_inputcapture_isr,传递参数为读取到的高低电平标记

static rt_err_t capture_init(struct rt_inputcapture_device *inputcapture)
{
    rt_err_t ret = RT_EOK;

    // TODO:采集设备实现
    // ret = xxxx();

    return -(ret);
}

static rt_err_t capture_open(struct rt_inputcapture_device *inputcapture)
{
    rt_err_t ret = RT_EOK;

    RT_ASSERT(inputcapture != RT_NULL);

    // TODO: 打开设备实现
    // ret = xxxx();
    return ret;
}

static rt_err_t capture_close(struct rt_inputcapture_device *inputcapture)
{
    rt_err_t ret = RT_EOK;


    RT_ASSERT(inputcapture != RT_NULL);

    // TODO: 关闭设备实现
    // ret = xxxx();

    return ret;
}

static rt_err_t capture_get_pulsewidth(struct rt_inputcapture_device *inputcapture, rt_uint32_t *pulsewidth_us)
{
    rt_err_t ret = RT_EOK;

     // TODO: 读取实际采集时间,单位us
    // *pulsewidth_us = xxxx;

    return -(ret);
}

static struct rt_inputcapture_ops capture_ops =
{
    .init   =   capture_init,
    .open   =   capture_open,
    .close  =   capture_close,
    .get_pulsewidth =   capture_get_pulsewidth,
};

static int timer_capture_device_init(void)
{
    char *timer_device_name[TIMER_CHANNEL_NUM] = { "timer0i0", "timer1i0", "timer2i0", "timer3i0"};
    for (int i = 0; i < TIMER_CHANNEL_NUM; i++)
    {
        timer_capture[i] = (capture_t *)rt_malloc(sizeof(capture_t));

        // 驱动内部数据初始化

        rt_device_inputcapture_register(&timer_capture[i]->parent, timer_device_name[i], &timer_capture[i]);
    }

    return 0;
}
INIT_DEVICE_EXPORT(timer_capture_device_init);







关键词: rtthread     输入     采集     设备     框架     解析    

专家
2024-10-26 23:48:27     打赏
2楼

感谢分享


高工
2024-10-27 06:42:06     打赏
3楼

感谢分享


专家
2024-10-27 06:47:48     打赏
4楼

学习一下


专家
2024-10-27 08:46:05     打赏
5楼

感谢楼主分享


专家
2024-10-27 11:32:28     打赏
6楼

若有通用需求,这样的输入设备,会有其他单片机厂商跟进吧。这样的输入设备,简化了好多工作,棒!


专家
2024-10-27 14:27:15     打赏
7楼

学习一下


工程师
2024-10-27 21:48:40     打赏
8楼

666666


高工
2024-10-28 00:03:42     打赏
9楼

学习一下


共9条 1/1 1 跳转至

回复

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