赞
踩
目录
在RT-Thread实时操作系统环境下使用STM32F1XX的GPIO资源有两种方式——I/O 设备模型框架接口和调用硬件API。本文分别给出这两种方式的示例程序,并分析它们的实现机制、比较了它们的异同之处。
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
- #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_t有control等成员
- 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->pin和mode->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),而不能随便传入一个值。
上例子中只操作一只引脚,为这一只引脚分配了引脚模式和引脚值,即一只管脚就被视为一个设备。若使用多个引脚,那么应该为每个引脚一一分配设备句柄及分配引脚模式和引脚值。
#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()等。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。