I/O设备指的是嵌入式系统中的一些Input输入/Output输出设备,输入输出设备是嵌入式系统重要的组成部分。输入和输出设备可以看做是计算机系统和外界进行沟通的桥梁,因此在计算机组成原理中输入输出设备是重要的组成部分。
计算机组成原理中的5大组成部分:
- 控制器(Control):控制和协调计算机系统中各外设的工作和内存的访问,对程序、数据、地址进行控制访问
- 运算器(Datapath):对数据进行算数运算和逻辑运算
- 存储器(Memory):存储程序、程序运行时的临时数据等(RAM+ROM)
- 输入设备(Input System):将外部的字符、控制指令、或者采集到的原始数据输入到计算机系统中进行处理的设备,常见的有鼠标、键盘等
- 输出设备(Output System):将计算机系统中的计算结果、各种字符、控制信号等对外输出的设备,常见的有显示器、打印机等
- I/O设备模型框架
RT-Thread程序可以分为三层,即上图中三种颜色框出的部分,红色部分为应用程序层,绿色部分为驱动层,蓝色为硬件层。
【应用程序层】应用层只需关心业务逻辑,而不需要关心底层硬件
【驱动层】给应用程序层提供接口使其可以间接的使用硬件,从而搭建起了应用层和硬件层之间的桥梁,驱动层未实现的硬件驱动应用层无法调用
【硬件层】具体的硬件,可配置其内部的寄存器实现硬件功能的定制
- 应用程序通过 I/O 设备管理接口获得正确的设备驱动,然后通过这个设备驱动与底层 I/O 硬件设备进行数据(或控制)交互。
- I/O 设备管理层实现了对设备驱动程序的封装。应用程序通过 I/O 设备层提供的标准接口访问底层设备,设备驱动程序的升级、更替不会对上层应用产生影响。这种方式使得设备的硬件操作相关的代码能够独立于应用程序而存在,双方只需关注各自的功能实现,从而降低了代码的耦合性、复杂性,提高了系统的可靠性。
- 设备驱动框架层是对同类硬件设备驱动的抽象,将不同厂家的同类硬件设备驱动中相同的部分抽取出来,将不同部分留出接口,由驱动程序实现。
- 设备驱动层是一组驱使硬件设备工作的程序,实现访问硬件设备的功能。它负责创建和注册 I/O 设备,对于操作逻辑简单的设备,可以不经过设备驱动框架层,直接将设备注册到 I/O 设备管理器中
应用程序层只负责用户业务逻辑的实现,通过对驱动层统一接口的调用实现对硬件层的实际操作。因此当硬件层有所改动时,只需要更改驱动层的具体实现(驱动层修改时需保证给应用程序层提供的调用接口不能变),驱动程序的升级更改不会对上层应用程序层产生影响。
程序分层的原因及好处:
不对程序进行分层也可以实现功能,实际上在小型的项目中往往是不分驱动层、应用层的,如写一个芯片的驱动程序这些可能都是放在一个.c文件中完成的。但是当项目变大后,可能使用到的芯片有几十种,那么针对这几十种芯片写驱动程序的话重复工作很多,代码也很难复用,提供给用户调用的接口也不同。因此就会考虑是否可以将这些芯片对接到统一的框架中,通过框架给用户提供统一接口,这样不管是什么芯片对于应用户程序来说没区别。应用层通过驱动层实现对硬件层设备的访问,驱动层程序的升级更改并不会影响应用层大程序。以此可以实现驱动层程序和应用层程序的低耦合,可独立进行开发。可以这样去理解,【硬件层】是一堆原材料,【驱动层】是一个工匠,他可以将这些原材料打造成各种各样的工具,【应用层】是人,他只能使用工匠打造好的工具而不能直接去使用原材料。
RT-Thread中的I/O设备模型框架处于应用程序层和硬件层之间,总共有三层组成,即I/O设备管理层、设备驱动框架层(简单的设备可以不需要这一层)、设备驱动层。
- I/O设备模型
RT-Thread中的设备都是基于基类rt_object之上再添加自身特有的参数所构成的,设备是一类对象,因此被纳入对象管理器进行统一的管理。每种设备都是由基类派生而来,因此每种设备都可以继承父类对象的属性,并派生出自身的私有属性。
设备对象定义的结构体如下所示:
struct rt_device
{struct rt_object 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; /* 设 备 ID,0 - 255 *//* 数 据 收 发 回 调 函 数 */rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);const struct rt_device_ops *ops; /* 设 备 操 作 方 法 *//* 设 备 的 私 有 数 据 */void *user_data;
};
typedef struct rt_device *rt_device_t;
重点需要注意数据收发回调函数和设备操作方法。
1、设备的创建
可以调用rt_device_t rt_device_create(int type, int attach_size); 创建设备,设备创建后需要实现其访问硬件的操作方法,即需要实现ops设备操作方法
/*** operations set for device object*/
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);
};
ops中各个函数的说明如下,可以根据设备具体功能选择实现全部或部分接口
2、设备的注册
设备被创建后,还需要将其注册到I/O设备管理器中,这样才能进行统一管理,应用程序才能够访问的到,可以使用rt_err_t rt_device_register(rt_device_t dev, const char* name, rt_uint8_t flags); 进行设备的注册。注册就相当于是在系统中上户口一样,只有注册了才能找到相关信息。
3、设备的访问