专业汽车网站东营网站建设入门
news/
2025/9/22 22:39:27/
文章来源:
专业汽车网站,东营网站建设入门,云建站的步骤,企业seo推广I/O设备模型
绝大部分的嵌入式系统都包括一些I/O#xff08;Input/Outut#xff0c;输入/输出#xff09;设备#xff0c;例如仪器上的数据显示屏、工业设备上的串口通信、数据采集设备上用于保存数据的Flash或SD卡#xff0c;以及网络设备的以太网接口等。
I/O设备模型…I/O设备模型
绝大部分的嵌入式系统都包括一些I/OInput/Outut输入/输出设备例如仪器上的数据显示屏、工业设备上的串口通信、数据采集设备上用于保存数据的Flash或SD卡以及网络设备的以太网接口等。
I/O设备模型框架
RT-Thread提供了一套简单的I/O设备模型框架如图所示它位于硬件和应用程序之间共分成三层从下到下分别是I/O设备管理层、设备驱动框架层、设备驱动层。 应用程序通过I/O设备管理接口获得正确的设备驱动然后通过这个设备驱动与底层I/O硬件设备进行数据交互。
I/O设备管理层实现了对设备驱动程序的封装。应用程序通过图中的I/O设备管理层提供的标准接口访问底层设备设备驱动程序的升级、更替不会对上层应用产生影响。这种方式使得设备的硬件操作相关的代码能够独立于应用程序而存在双方只需关注各自的功能实现从而降低了代码的耦合型、复杂性提高了系统的可靠性。
设备驱动框架层是对同类硬件设备驱动的抽象将不同厂家的同类硬件设备驱动中相同的部分抽取出来将不同部分留出接口由驱动程序实现。
设备驱动层是一组驱使硬件设备工作的程序实现访问硬件设备的功能。 它负责创建和注册I/O设备对于操作逻辑简单的设备可以不经过设备驱动框架层直接将设备注册到I/O设备管理器中。 设备驱动根据设备模型定义创建出具备硬件访问能力的设备实例将该设备通过rt_device_register()接口注册到I/O设备管理器中。应用程序通过rt_device_find()接口查找到设备然后使用I/O设备管理接口来访问硬件。
对于另一些设备如看门狗等则会将创建的设备实例先注册到对应的设备驱动框架中再由设备驱动框架向I/O设备管理器进行注册主要有以下几点
看门狗设备驱动程序根据看门狗设备模型定义创建出具备硬件访问能力的看门狗设备实例并将该看门狗设备通过rt_hw_watchdog_register()接口注册到看门狗设备驱动框架中。看门狗设备驱动框架通过rt_device_register()接口将看门狗注册到I/O设备管理器中。应用程序通过I/O设备管理接口来访问看门狗设备硬件。 I/O设备模型
RT-Thread的设备模型是建立在内核对象模型基础之上的设备被认为是一类对象被纳入对象管理器的范畴。每个设备对象都是由基对象派生而来每个具体设备都可以继承其父类对象的属性并派生出其私有属性。 struct rt_object
{char name[RT_NAME_MAX];rt_uint8_t type;rt_uint8_t flag;rt_list_t list;
};
typedef struct rt_object *rt_object_t;struct rt_device
{struct rt_obejct parent;enum rt_device_class_type type;rt_uint16_t flag;rt_uint16_t open_flag;rt_uint8_t ref_count;rt_uint8_t device_id;/* 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);/* 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_size_t (*read) (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);rt_size_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);void *user_data;
}I/O设备类型
enum rt_device_class_type
{RT_Device_Class_Char 0, /** character device */RT_Device_Class_Block, /** block device */RT_Device_Class_NetIf, /** net interface */RT_Device_Class_MTD, /** memory device */RT_Device_Class_CAN, /** CAN device */RT_Device_Class_RTC, /** RTC device */RT_Device_Class_Sound, /** Sound device */RT_Device_Class_Graphic, /** Graphic device */RT_Device_Class_I2CBUS, /** I2C bus device */RT_Device_Class_USBDevice, /** USB slave device */RT_Device_Class_USBHost, /** USB host bus */RT_Device_Class_USBOTG, /** USB OTG bus */RT_Device_Class_SPIBUS, /** SPI bus device */RT_Device_Class_SPIDevice, /** SPI device */RT_Device_Class_SDIO, /** SDIO bus device */RT_Device_Class_PM, /** PM pseudo device */RT_Device_Class_Pipe, /** Pipe device */RT_Device_Class_Portal, /** Portal device */RT_Device_Class_Timer, /** Timer device */RT_Device_Class_Miscellaneous, /** Miscellaneous device */RT_Device_Class_Sensor, /** Sensor device */RT_Device_Class_Touch, /** Touch device */RT_Device_Class_PHY, /** PHY device */RT_Device_Class_Security, /** Security device */RT_Device_Class_Unknown /** unknown device */
};其中字符设备、块设备是常用的设备类型它们的分类依据是设备数据与系统之间的传输处理方式。 字符模式设备允许非结构的数据传输即通常数据传输采用串行的形式每次一个字节。字符设备通常是一些简单设备如串口、按键。
块设备每次传输一个数据块例如每次传输512个字节数据。这个数据块是硬件强制性的数据块可能使用某类数据接口或某些强制性的传输协议否则就可能发生错误。 因此有时块设备驱动程序对读或写操作必须执行附加的工作。 当系统服务于一个具有大量数据的写操作时设备驱动程序必须首先将数据分为多个包每个包采用设备指定的数据尺寸。 而在实际过程中最后一部分数据尺寸有可能小于正常的设备块尺寸。 如上图中每个块使用单独的写请求写入到设备中头3个直接进行写操作。 但最后一个数据块尺寸小于设备块尺寸设备驱动程序必须使用不同于前3个块的方式处理最后的数据块。 通常情况下设备驱动程序需要首先执行相对应的设备块的读操作然后把写入数据覆盖到读出数据上然后再把这个“合成”的数据块作为一整个块写回到设备中。
例如块4驱动程序需要先把块4所对应的设备块读出来然后将需要写入的数据覆盖至从设备块读出的数据上使其合并成一个新的块最后再写回到块设备中。
创建和注册I/O设备
驱动层负责创建设备实例并注册到I/O设备管理器中可以通过静态声明的方式创建设备实例也可以用下面的接口进行动态创建
rt_device_t rt_device_create(int type, int attach_size)
{int size;rt_device_t device;size RT_ALIGN(sizeof(struct rt_device), RT_ALIGN_SIZE);attach_size RT_ALIGN(attach_size, RT_ALIGN_SIZE);size attach_size;device (rt_device_t)rt_malloc(size);if(device){rt_memset(device, 0x0, sizeof(struct rt_device));device-type (enum rt_device_class_type)type;}return device;
}调用该接口时系统会从动态堆内存中分配一个设备控制块大小为struct rt_device和attach_size的和设备的类型由参数type设定。 设备被创建后需要实现它访问硬件的操作方法。
struct rt_device_ops
{/* 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_size_t (*read) (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);rt_size_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);
};init初始化设备。设备初始化完成后设备控制块的flag会被置成已激活状态RT_DEVICE_FLAG_ACTIVATED。如果设备控制块中的flag标志已经设置成激活状态那么再运行初始化接口就会立刻返回而不会重新进行初始化。
rt_err_t rt_device_init(rt_device_t dev)
{rt_err_t result RT_EOK;if(dev-init ! RT_NULL){result dev-init(dev);if (result ! RT_EOK){RT_DEBUG_LOG(RT_DEBUG_DEVICE, (To initialize device:%s failed. The error code is %d\n,dev-parent.name, result));}else{dev-flag | RT_DEVICE_FLAG_ACTIVATED;}}
}open。打开设备。有些设备并不是系统一启动就已经打开开始运行或者设备需要进行数据收发但如果上层应用还未准备好设备也不应该默认已经使能并开始接收数据。所以建议在写底层驱动程序时在调用open接口时才使能设备。
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag)
{rt_err_t result RT_EOK;RT_ASSERT(dev ! RT_NULL);RT_ASSERT(rt_object_get_type(dev-parent) RT_Object_Class_Device);if(!(dev-flag RT_DEVICE_FLAG_ACTIVATED)){if(device_init ! RT_NULL){result device_init(dev);if (result ! RT_EOK){RT_DEBUG_LOG(RT_DEBUG_DEVICE, (To initialize device:%s failed. The error code is %d\n,dev-parent.name, result));return result;}}dev-flag | RT_DEVICE_FLAG_ACTIVATED;}/* 如果设备是一个独立的设备并且已经打开 */if ((dev-flag RT_DEVICE_FLAG_STANDALONE) (dev-open_flag RT_DEVICE_OFLAG_OPEN)){return -RT_EBUSY;}/* call device_open interface */if (device_open ! RT_NULL){result device_open(dev, oflag);}else{dev-open_flag (oflag RT_DEVICE_OFLAG_MASK);}/* set open flag */if (result RT_EOK || result -RT_ENOSYS){dev-open_flag | RT_DEVICE_OFLAG_OPEN;dev-ref_count;/* dont let bad things happen silently. If you are bitten by this assert,* please set the ref_count to a bigger type. */RT_ASSERT(dev-ref_count ! 0);}return result;
}close关闭设备在打开设备时设备控制块会维护一个打开计数在打开设备时进行1操作在关闭设备时进行-1操作当计数器变为0时才会进行真正的关闭操作。
rt_err_t rt_device_close(rt_device_t dev)
{rt_err_t result RT_EOK;/* parameter check */RT_ASSERT(dev ! RT_NULL);RT_ASSERT(rt_object_get_type(dev-parent) RT_Object_Class_Device);if(dev-ref_count 0)return -RT_ERROR;dev-ref_count--;if(dev-ref_count ! 0)return RT_EOK;if(dev-close ! RT_NULL)result dev-close(dev);/* set open flag */if (result RT_EOK || result -RT_ENOSYS)dev-open_flag RT_DEVICE_OFLAG_CLOSE;return result;
}read。从设备读取数据。参数pos是读取数据的偏移量但是有些设备并不一定需要指定偏移量例如串口设备驱动程序应忽略这个参数。而对于块设备来说pos以及size都是以块设备的数据块大小为单位的。 例如块设备的数据块大小是512而参数中pos10size2那么驱动程序应该返回设备中第10个块从第0个块作为起始共计2个块的数据。这个接口返回的类型是rt_size_t即读到的字节数或块数目。正常情况下应该返回参数中size的数值如果返回零需要设置对应的errno值。
rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, 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_read interface */if(device_read ! RT_NULL)return dev-read(dev, pos, buffer, size);rt_set_errno(-RT_ENOSYS);return 0;
}write向设备写入数据。参数pos是写入数据的偏移量。与读操作类似对于块设备来说pos以及size都是以块设备的数据块大小为单位的。这个接口返回的类型是rt_size_t即真实写入数据的字节数或块数目。正常情况下应该会返回参数中 size 的数值如果返回零请设置对应的 errno 值。
rt_size_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 (device_write ! RT_NULL){return device_write(dev, pos, buffer, size);}/* set error code */rt_set_errno(-RT_ENOSYS);return 0;
}
RTM_EXPORT(rt_device_write);control根据cmd命令控制设备。命令往往是由底层各类设备驱动自定义实现。例如参数RT_DEVICE_CTRL_BLK_GETGEOME意思是获取块设备的大小信息。
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 (device_control ! RT_NULL){return device_control(dev, cmd, arg);}return -RT_ENOSYS;
}
RTM_EXPORT(rt_device_control);当一个动态创建的设备不再需要使用时可以通过如下函数来销毁。
void rt_device_destroy(rt_device_t dev)
{rt_object_detach((dev-parent));rt_free(dev);
}设备被创建后需要注册到I/O设备管理器中应用程序才能够访问。
rt_err_t rt_device_register(rt_device_t dev, const char *name, rt_uint16_t flags)
{if (dev RT_NULL)return -RT_ERROR;if (rt_device_find(name) ! RT_NULL) //应当避免重复注册已经注册的设备已经注册相同名字的设备。return -RT_ERROR;rt_object_init((dev-parent), RT_Object_Class_Device, name);dev-flag flags;dev-ref_count 0;dev-open_flag 0;return RT_EOK;
}flags参数支持下列参数
#define RT_DEVICE_FLAG_RDONLY 0x001 /* 只读 */
#define RT_DEVICE_FLAG_WRONLY 0x002 /* 只写 */
#define RT_DEVICE_FLAG_RDWR 0x003 /* 读写 */
#define RT_DEVICE_FLAG_REMOVABLE 0x004 /* 可移除 */
#define RT_DEVICE_FLAG_STANDALONE 0x008 /* 独立 */
#define RT_DEVICE_FLAG_SUSPENDED 0x020 /* 挂起 */
#define RT_DEVICE_FLAG_STREAM 0x040 /* 流模式 */
#define RT_DEVICE_FLAG_INT_RX 0x100 /* 中断接收 */
#define RT_DEVICE_FLAG_DMA_RX 0x200 /* DMA 接收 */
#define RT_DEVICE_FLAG_INT_TX 0x400 /* 中断发送 */
#define RT_DEVICE_FLAG_DMA_TX 0x800 /* DMA 发送 */设备流模式RT_DEVICE_FLAG_STREAM参数用于向串口终端输出字符串当输出字符是\n时自动在前面补一个“\r”。
当设备注销后设备将从设备管理器中移除但不会释放设备控制块占用的内存。
看门狗设备注册示例
struct rt_device_ops
{/* 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_size_t (*read) (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);rt_size_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);
};const static struct rt_device_ops wdt_ops
{rt_watchdog_init,rt_watchdog_open,rt_watchdog_close,RT_NULL,RT_NULL,rt_watchdog_control
};rt_err_t rt_hw_watchdog_register(struct rt_watchdog_device *wtd,const char *name,rt_uint32_t flag,void *data)
{struct rt_device *device;RT_ASSERT(wtd ! RT_NULL);device (wtd-parent);device-type RT_Device_Class_Miscellaneous;device-rx_indicate RT_NULL;device-tx_complete RT_NULL;device-ops wdt_ops;device-user_data data;/* register a character device */return rt_device_register(device, name, flag);
}访问I/O设备
应用程序通过I/O设备管理接口来访问硬件设备当设备驱动程序实现后应用程序就可以访问该硬件。 设备驱动框架层在components/drivers里面查找。 设备驱动层在libraries/HAL_Drivers
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/910615.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!