RT-Thread操作系统的I/O 设备模型框架接口示例与驱动分析

目录

摘要

注册I/O设备程序调用流程:

I/O 设备模型框架接口操作IO示例与分析

示例

分析

直接调用硬件层API

示例

总结

参考文献


摘要

        在RT-Thread实时操作系统环境下使用STM32F1XX的GPIO资源有两种方式——I/O 设备模型框架接口和调用硬件API。本文分别给出这两种方式的示例程序,并分析它们的实现机制、比较了它们的异同之处。

注册I/O设备程序调用流程:

int $Sub$$main(void);

rtthread_startup();

rt_hw_board_init();

rt_hw_pin_init();

rt_device_pin_register("pin", &_stm32_pin_ops, RT_NULL);

rt_device_register(&_hw_pin.parent, name, RT_DEVICE_FLAG_RDWR);

        下面代码为PIN设备的注册示例。如图1所示,先调用rt_device_pin_register()接口后,

图1

        设备通过 rt_device_register() 接口被注册到 I/O 设备管理器中,如图2所示。设备管理器中就会存在名为“pin”的设备。

图2

I/O 设备模型框架接口操作IO示例与分析

示例

#include <rtthread.h>

#include <rtdevice.h>

#include <board.h>

#include "drv_gpio.h"

#define LED0_PIN    GET_PIN(C, 0)

int main()

{

struct rt_device_pin_mode mypinMode;                

struct rt_device_pin_value value1;

struct rt_device_pin_value value0;

value1.pin=LED0_PIN;

value1.value=PIN_HIGH;

value0.pin=LED0_PIN;

value0.value=PIN_LOW;

mypinMode.pin =LED0_PIN;

mypinMode.mode = PIN_MODE_OUTPUT; 

rt_device_t pin= RT_NULL;/* pin设备句柄 */

pin = rt_device_find("pin");

rt_device_open(pin, RT_DEVICE_OFLAG_RDWR);

rt_device_control(pin,0,&mypinMode);//等价与rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);

while(1)

{

rt_device_write(pin,0,&value1,sizeof(value1));

rt_thread_mdelay(1000);

rt_device_write(pin,0,&value0,sizeof(value0));

rt_thread_mdelay(1000);                

}

return 0;

}

        使用RTT的I/O 设备模型框架接口完全屏蔽底层硬件,具有代码移植性好的显著特点。

分析

        查找设备rt_device_find、开启设备rt_device_open操作必不可少。下面分析rt_device_control的实现细节,函数原型为

rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg)

{

    /* parameter check */

    RT_ASSERT(dev != RT_NULL);

    RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);

    /* call device_write interface */

    if (dev->control != RT_NULL)

    {

        return dev->control(dev, cmd, arg);

    }

    return -RT_ENOSYS;

}

又,rt_device的指针rt_device_tcontrol等成员

struct rt_device

{

    struct rt_object          parent;                   /**< inherit from rt_object */

#ifdef RT_USING_DM

    struct rt_driver *drv;

    void *dtb_node;

#endif

    enum rt_device_class_type type;                     /**< device type */

    rt_uint16_t               flag;                     /**< device flag */

    rt_uint16_t               open_flag;                /**< device open flag */

    rt_uint8_t                ref_count;                /**< reference count */

    rt_uint8_t                device_id;                /**< 0 - 255 */


    /* device call back */

    rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);

    rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);



#ifdef RT_USING_DEVICE_OPS

    const struct rt_device_ops *ops;

#else

    /* common device interface */

    rt_err_t  (*init)   (rt_device_t dev);

    rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);

    rt_err_t  (*close)  (rt_device_t dev);

    rt_ssize_t (*read)  (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);

    rt_ssize_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);

    rt_err_t  (*control)(rt_device_t dev, int cmd, void *args);

#endif /* RT_USING_DEVICE_OPS */



#ifdef RT_USING_POSIX_DEVIO

    const struct dfs_file_ops *fops;

    struct rt_wqueue wait_queue;

#endif /* RT_USING_POSIX_DEVIO */

    void                     *user_data;                /**< device private data */

};

图3

        因为作者已经定义了_hw_pin是struct rt_device_pin

{

    struct rt_device parent;

    const struct rt_pin_ops *ops;

}类型。

此处的_hw_pin.parent对应的是结构体rt_device。由图3可知,那么_hw_pin.parent.control函数指针指向的是函数_pin_control.它在pin.c中实现如下:

static rt_err_t _pin_control(rt_device_t dev, int cmd, void *args)

{

    struct rt_device_pin_mode *mode;

    struct rt_device_pin *pin = (struct rt_device_pin *)dev;

    /* check parameters */

    RT_ASSERT(pin != RT_NULL);

    mode = (struct rt_device_pin_mode *)args;

    if (mode == RT_NULL)

        return -RT_ERROR;

    pin->ops->pin_mode(dev, (rt_base_t)mode->pin, (rt_base_t)mode->mode);

    return 0;

}

        用户传入参数rt_device_control(pin,0,&mypinMode);先传参给“dev->control(dev, cmd, arg);”再传参给“_pin_control(rt_device_t dev, int cmd, void *args)”。由“mode = (struct rt_device_pin_mode *)args;”我们才将mypinMode定义为struct rt_device_pin_mode类型,包含mode->pinmode->mode两个成员,以符合调用函数接口类型匹配。另,参数cmd在PIN控制函数中没有用到,可以给0值。

        在pin.h中,声明struct rt_pin_ops,如下图4所示。

图4

        在drv_gpio.c中,定义_stm32_pin_ops,如下图5所示。

图5

        最后由“pin->ops->pin_mode(dev, (rt_base_t)mode->pin, (rt_base_t)mode->mode);”调用用户函数stm32_pin_mode()函数实现引脚配置。

        接下来在分析函数rt_device_write(pin,0,&value1,sizeof(value1))的实现机制。

在device.c中函数原型实现如下:

rt_ssize_t rt_device_write(rt_device_t dev,

                          rt_off_t    pos,

                          const void *buffer,

                          rt_size_t   size)

{

    /* parameter check */

    RT_ASSERT(dev != RT_NULL);

    RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);

    if (dev->ref_count == 0)

    {

        rt_set_errno(-RT_ERROR);

        return 0;

    }

    /* call device_write interface */

    if (dev->write != RT_NULL)

    {

        return dev->write(dev, pos, buffer, size);

    }

    /* set error code */

    rt_set_errno(-RT_ENOSYS);

    return 0;

}

同理,用户传入参数rt_device_write(pin,0,&value1,sizeof(value1));先传参给“dev->write(dev, pos, buffer, size);”再传参给“_pin_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)”。它在pin.c中实现如下:

static rt_ssize_t _pin_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)

{

    struct rt_device_pin_value *value;

    struct rt_device_pin *pin = (struct rt_device_pin *)dev;

    /* check parameters */

    RT_ASSERT(pin != RT_NULL);

    value = (struct rt_device_pin_value *)buffer;

    if (value == RT_NULL || size != sizeof(*value))

        return 0;

    pin->ops->pin_write(dev, (rt_base_t)value->pin, (rt_base_t)value->value);

    return size;

}

        由“value = (struct rt_device_pin_value *)buffer;”我才将value1和value0定义为struct rt_device_pin_value类型,包含value->pin和value->value两个成员,以符合调用函数接口类型匹配。另,参数pos在PIN控制函数中没有用到,可以给0值。最后由“pin->ops->pin_mode(dev, (rt_base_t)mode->pin, (rt_base_t)mode->mode);”调用用户函数stm32_pin_write()函数实现引脚操作。注意参数size虽然在_pin_write()中没有具体用途而是直接“return size;”。但是在rt_device_write()中的“return dev->write(dev, pos, buffer, size);”返回值是来自_pin_write(),rt_device_write()的返回值(表征错误类型,RTT在rtdef.h中定义rt_ssize_t类型)不正确时,程序不会正常运行。因此必须传入sizeof(value0)或者sizeof(value1),而不能随便传入一个值。

        上例子中只操作一只引脚,为这一只引脚分配了引脚模式和引脚值,即一只管脚就被视为一个设备。若使用多个引脚,那么应该为每个引脚一一分配设备句柄及分配引脚模式和引脚值。

直接调用硬件层API

示例

#include <rtthread.h>

#include <rtdevice.h>

#include <board.h>

#include "drv_gpio.h"

#define LED0_PIN    GET_PIN(C, 0)

int main()

{

    rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);

    while (1)

    {

        rt_pin_write(LED0_PIN, PIN_HIGH);

        rt_thread_mdelay(500);

        rt_pin_write(LED0_PIN, PIN_LOW);

        rt_thread_mdelay(500);

    }

return 0;

}

        此方法和直接使用标准库较为类似,没有体现操作系统面向设备对象的编程思想。在官方例程中多以这种方式出现读者眼中。

总结

        针对在RTT环境下操作STM32的IO不同实现方式的分析比较,本质上调用函数没有区别。但是在编程思想上,它们明显不同。具体使用哪种方式,取决于您的项目复杂度和开发人员关系。假设驱动和使用驱动的应用开发人员为同一人,他完全可以直接调用封装好的接口,诸如rt_pin_mode()、rt_pin_write()、rt_pin_read()等等。

图6

        若使用驱动的应用开发者和驱动开发者不是同一人,应用开发者不会再花时间去学习了解驱动的接口时如何使用的,他只会使用RTT的标准接口。例如图6所示的rt_device_find()、rt_device_open()、rt_device_control()、rt_device_read/write()等。

参考文献

I/O 设备模型

STM32F103 德飞莱开发板 BSP 说明

Keil 模拟器 STM32F103 上手指南

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值