当前位置:   article > 正文

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

rt_device_write

目录

摘要

注册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示例与分析

示例

  1. #include <rtthread.h>
  2. #include <rtdevice.h>
  3. #include <board.h>
  4. #include "drv_gpio.h"
  5. #define LED0_PIN    GET_PIN(C, 0)
  6. int main()
  7. {
  8. struct rt_device_pin_mode mypinMode;                
  9. struct rt_device_pin_value value1;
  10. struct rt_device_pin_value value0;
  11. value1.pin=LED0_PIN;
  12. value1.value=PIN_HIGH;
  13. value0.pin=LED0_PIN;
  14. value0.value=PIN_LOW;
  15. mypinMode.pin =LED0_PIN;
  16. mypinMode.mode = PIN_MODE_OUTPUT
  17. rt_device_t pin= RT_NULL;/* pin设备句柄 */
  18. pin = rt_device_find("pin");
  19. rt_device_open(pin, RT_DEVICE_OFLAG_RDWR);
  20. rt_device_control(pin,0,&mypinMode);//等价与rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
  21. while(1)
  22. {
  23. rt_device_write(pin,0,&value1,sizeof(value1));
  24. rt_thread_mdelay(1000);
  25. rt_device_write(pin,0,&value0,sizeof(value0));
  26. rt_thread_mdelay(1000);                
  27. }
  28. return 0;
  29. }

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

分析

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

  1. rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg)
  2. {
  3.     /* parameter check */
  4.     RT_ASSERT(dev != RT_NULL);
  5.     RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
  6.     /* call device_write interface */
  7.     if (dev->control != RT_NULL)
  8.     {
  9.         return dev->control(dev, cmd, arg);
  10.     }
  11.     return -RT_ENOSYS;
  12. }

又,rt_device的指针rt_device_tcontrol等成员

  1. struct rt_device
  2. {
  3.     struct rt_object          parent;                   /**< inherit from rt_object */
  4. #ifdef RT_USING_DM
  5.     struct rt_driver *drv;
  6.     void *dtb_node;
  7. #endif
  8.     enum rt_device_class_type type;                     /**< device type */
  9.     rt_uint16_t               flag;                     /**< device flag */
  10.     rt_uint16_t               open_flag;                /**< device open flag */
  11.     rt_uint8_t                ref_count;                /**< reference count */
  12.     rt_uint8_t                device_id;                /**< 0 - 255 */
  13.     /* device call back */
  14.     rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);
  15.     rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);
  16. #ifdef RT_USING_DEVICE_OPS
  17.     const struct rt_device_ops *ops;
  18. #else
  19.     /* common device interface */
  20.     rt_err_t  (*init)   (rt_device_t dev);
  21.     rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);
  22.     rt_err_t  (*close)  (rt_device_t dev);
  23.     rt_ssize_t (*read)  (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
  24.     rt_ssize_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
  25.     rt_err_t  (*control)(rt_device_t dev, int cmd, void *args);
  26. #endif /* RT_USING_DEVICE_OPS */
  27. #ifdef RT_USING_POSIX_DEVIO
  28.     const struct dfs_file_ops *fops;
  29.     struct rt_wqueue wait_queue;
  30. #endif /* RT_USING_POSIX_DEVIO */
  31.     void                     *user_data;                /**< device private data */
  32. };

图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 上手指南

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/67418
推荐阅读
相关标签
  

闽ICP备14008679号