RTT I/O设备模型

news/2024/5/19 18:42:39

I/O设备模型

绝大部分的嵌入式系统都包括一些I/O(Input/Output,输入/输出)设备,例如仪器上的数据显示屏、工业设备上的串口通信、数据采集设备上用于保存数据的Flash或SD卡,以及网络设备的以太网接口等,都是嵌入式系统中容易找到的I/O设备例子。

I/O设备模型框架

RTT提供了一套简单的I/O设备模型框架,如图所示,它位于硬件和应用程序之间,共分成三层,从上到下分别是I/O设备管理层,设备驱动框架层,设备驱动层。

在这里插入图片描述
应用程序通过I/O设备管理接口获得正确的设备驱动,然后通过这个设备驱动与底层I/O硬件设备进行数据交互。

I/O设备管理层实现了对设备驱动程序的封装。
应用程序通过图中的I/O设备管理层提供的标准接口访问底层设备,设备驱动程序的升级、更替不会对上层应用产生影响。
这种方式使得设备的硬件操作相关的代码能够独立于应用程序而存在,双方只需关注各自的功能实现,从而降低了代码的耦合性、复杂性,提高了系统的可靠性。

设备驱动框架层是对同类硬件设备驱动的抽象,将不同厂家的同类硬件设备驱动中相同的部分抽取出来,将不同部分留出接口,由驱动程序实现。

设备驱动层是一组驱使硬件设备工作的程序,实现访问硬件设备的功能。它负责创建和注册I/O设备,对于操作逻辑简单的设备,可以不经过设备驱动框架层,直接将设备注册到I/O设备管理器中。

  1. 设备驱动根据设备模型定义,创建出具备硬件访问能力的设备实例,将该设备通过rt_device_register()接口注册到I/O设备管理器中。
  2. 应用程序通过rt_device_find()接口查找到设备,然后使用I/O设备管理接口来访问硬件。

在这里插入图片描述

对于另一些设备,如看门狗等,则会将创建的设备实例先注册到对应的设备驱动框架中,再由设备驱动向I/O设备管理器进行注册,主要有以下几点:

  1. 看门狗设备驱动程序根据看门狗设备模型定义,创建出具备硬件访问能力的看门狗设备实例,并将该看门狗设备通过rt_hw_watchdog_register()接口注册到看门狗设备驱动框架中。
  2. 看门狗设备驱动框架
  3. 应用程序通过I/O设备管理接口来访问看门狗设备硬件。

在这里插入图片描述

I/O设备模型

RTT的设备模型是建立在内核对象模型基础之上的,设备被认为是一类对象,被纳入对象管理器的范畴。每个设备对象都是由基对象派生而来,每个具体设备都可以继承其父类对象的属性,并派生其私有属性。

struct rt_object
{if RT_NAME_MAX > 0char        name[RT_NAME_MAX];                       /**< dynamic name of kernel object */
#elseconst char *name;                                    /**< static name of kernel object */
#endif /* RT_NAME_MAX > 0 */rt_uint8_t  type;                                    /**< type of kernel object */rt_uint8_t  flag;                                    /**< flag of kernel object */#ifdef RT_USING_MODULEvoid      * module_id;                               /**< id of application module */
#endif /* RT_USING_MODULE */#ifdef RT_USING_SMARTrt_atomic_t lwp_ref_count;                           /**< ref count for lwp */
#endif /* RT_USING_SMART */rt_list_t   list;                                    /**< list node of kernel object */
};
typedef struct rt_object *rt_object_t;
struct rt_device
{struct rt_object parent;enum rt_device_class_type 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);/* 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);void *user_data;
}

I/O设备类型

RT_Device_Class_Char             /* 字符设备       */
RT_Device_Class_Block            /* 块设备         */
RT_Device_Class_NetIf            /* 网络接口设备    */
RT_Device_Class_MTD              /* 内存设备       */
RT_Device_Class_RTC              /* RTC 设备        */
RT_Device_Class_Sound            /* 声音设备        */
RT_Device_Class_Graphic          /* 图形设备        */
RT_Device_Class_I2CBUS           /* I2C 总线设备     */
RT_Device_Class_USBDevice        /* USB device 设备  */
RT_Device_Class_USBHost          /* USB host 设备   */
RT_Device_Class_SPIBUS           /* SPI 总线设备     */
RT_Device_Class_SPIDevice        /* SPI 设备        */
RT_Device_Class_SDIO             /* SDIO 设备       */
RT_Device_Class_Miscellaneous    /* 杂类设备        */

其中字符设备、块设备是常用的设备类型,它们的分类依据是设备数据与系统之间的传输处理方式。

创建和注册I/O设备

驱动层负责创建设备实例,并注册到I/O设备管理器中。

rt_device_t rt_device_create(int type, int attach_size);
  • type:设备类型,可取前面小节列出的设备类型值。
  • attach_size:用户数据大小。

创建成功,返回设备句柄。
返回RT_NULL,表示动态内存分配失败。

调用该接口时,系统会从动态堆内存中分配一个设备控制块,大小为 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标志已经设置成激活状态。那么再次运行初始化接口时会立刻返回,而不会重新进行初始化。
  • open:打开设备。有些设备并不是系统一启动就已经打开开始运行,或者设备需要进行数据收发,但如果上层应用还未准备好,设备也不应默认已经使能并开始接收数据。建议在写底层驱动程序时,在调用open接口时才使能设备
  • close:关闭设备。在打开设备时,设备控制块会维护一个打开计数,在打开设备时进行+1操作,在关闭设备时进行-1操作,当计数器变为0时,才会进行真正的关闭操作。
  • read:从设备读取数据。参数pos是读取数据的偏移量,但是有些设备并不一定需要指定偏移量,例如串口设备,设备驱动应忽略这个参数。而对于块设备来说,pos 以及 size 都是以块设备的数据块大小为单位的。例如块设备的数据块大小是 512,而参数中 pos = 10, size = 2,那么驱动应该返回设备中第 10 个块 (从第 0 个块做为起始),共计 2 个块的数据。这个接口返回的类似是rt_size_t,即读到的字节数或块数目。正常情况下应该会返回参数中size的数值,如果返回零请设置对应的errno值。
  • write:向设备写入数据。参数 pos 是写入数据的偏移量。与读操作类似,对于块设备来说,pos 以及 size 都是以块设备的数据块大小为单位的。这个接口返回的类型是 rt_size_t,即真实写入数据的字节数或块数目。正常情况下应该会返回参数中 size 的数值,如果返回零请设置对应的 errno 值。
  • control:根据cmd命令控制设备。命令往往由底层设备驱动自定义实现。例如参数RT_DEVICE_CTRL_BLK_GETGEOME,意思是获取块设备的大小信息。

设备被创建后,需要注册到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_pin
{struct rt_device parent;const struct rt_pin_ops *ops;
}
struct rt_pin_ops
{void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_uint8_t mode);void (*pin_write)(struct rt_device *device, rt_base_t pin, rt_uint8_t value);rt_int8_t  (*pin_read)(struct rt_device *device, rt_base_t pin);rt_err_t (*pin_attach_irq)(struct rt_device *device, rt_base_t pin,rt_uint8_t mode, void (*hdr)(void *args), void *args);rt_err_t (*pin_detach_irq)(struct rt_device *device, rt_base_t pin);rt_err_t (*pin_irq_enable)(struct rt_device *device, rt_base_t pin, rt_uint8_t enabled);rt_base_t (*pin_get)(const char *name);
};

int rt_device_pin_register(const char *name, const struct rt_pin_ops *ops, void *user_data)
{_hw_pin.parent.type         = RT_Device_Class_Pin;_hw_pin.parent.rx_indicate  = RT_NULL;_hw_pin.parent.tx_complete  = RT_NULL;#ifdef RT_USING_DEVICE_OPS_hw_pin.parent.ops          = &pin_ops;
#else_hw_pin.parent.init         = RT_NULL;_hw_pin.parent.open         = RT_NULL;_hw_pin.parent.close        = RT_NULL;_hw_pin.parent.read         = _pin_read;_hw_pin.parent.write        = _pin_write;_hw_pin.parent.control      = _pin_control;
#endif_hw_pin.ops                 = ops;_hw_pin.parent.user_data    = user_data;/* register a character device */rt_device_register(&_hw_pin.parent, name, RT_DEVICE_FLAG_RDWR);return 0;
}

访问I/O设备

应用程序通过 I/O 设备管理接口来访问硬件设备,当设备驱动实现后,应用程序就可以访问该硬件。I/O 设备管理接口与 I/O 设备的操作方法的映射关系下图所示:在这里插入图片描述

  1. 查找设备:rt_device_find(const char*name);
  2. 初始化设备:rt_device_init(rt_device_t dev);
  3. 打开设备:rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
    如果上层应用需要设置设备的接收回调函数,则必须以RT_DEVICE_FLAG_INT_RX 或者 RT_DEVICE_FLAG_DMA_RX 的方式打开设备,否则不会回调函数。

数据收发回调

当硬件设备收到数据时,可以通过如下函数回调另一个函数来设置数据接收指示,通知上层应用线程有数据到达:

rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size));

该函数的回调函数由调用者提供。
当硬件设备接收到数据时,会回调这个函数并把收到的数据长度放在size参数中传递给上层应用。

设备访问示例

#include <rtthread.h>
#include <rtdevice.h>#define IWDG_DEVICE_NAME "wdt"static rt_device_t wdg_dev;static void idle_hook(void)
{/*在空闲线程的回调函数里喂狗*/rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_KEEPALIVE, NULL);rt_kprintf("feed dog\n");
}int main(void)
{rt_err_t res = RT_EOK;rt_uint32_t timeout = 10;wdg_dev = rt_device_find(IWDG_DEVICE_NAME);if(!wdg_dev){rt_kprintf("find %s failed]n", IWDG_DEVICE_NAME);return RT_ERROR;}* 初始化设备 */res = rt_device_init(wdg_dev);if (res != RT_EOK){rt_kprintf("initialize %s failed!\n", IWDG_DEVICE_NAME);return res;}/* 设置看门狗溢出时间 */res = rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, &timeout);if (res != RT_EOK){rt_kprintf("set %s timeout failed!\n", IWDG_DEVICE_NAME);return res;}/* 设置空闲线程回调函数 */rt_thread_idle_sethook(idle_hook);return res;
}

在这里插入图片描述
图中各类里的c文件是各类对应的管理接口所在,比如设备基类rt_device的管理接口在device.c中。

开发者只需开发驱动层即可,设备驱动框架层和I/O设备管理层,RTT已经写好了,无需改动,除非发现BUG或增加新的类别。


http://www.mrgr.cn/p/61135462

相关文章

TCP的特性(4)

TCP特性 拥塞控制(可靠性机制)延迟应答(效率机制)捎带应答(效率机制)面向字节流(粘包问题)TCP异常机制(心跳包)小结 拥塞控制(可靠性机制) 虽然TCP引入了滑动窗口,能够高效可靠的传输大量数据,但是在开始阶段就发送大量数据,可能引起一系列问题. TCP引入了慢启动机制,先发少量的…

Vue3+.NET6前后端分离式管理后台实战(十七)

1&#xff0c;Vue3.NET6前后端分离式管理后台实战(十七)已经在微信公众号更新&#xff0c;有兴趣的扫码关注一起交流学习。

Learning GitHub Actions Automation and Integration of CI/CD with GitHub【7】

CHAPTER 7 Managing Data Within Workflows 今天,很少有人用一个工作或项目来完成一套完整的工作。考虑一个典型的CI/CD管道。 你通常会有一个做建筑的工作,一个做包装的工作,多个做测试的工作,等等。 但即使这些都是单独的作业,它们仍然需要能够在它们之间传递数据和文件…

Learning GitHub Actions Automation and Integration of CI/CD with GitHub【8】

CHAPTER 8:Managing Workflow Execution 根据定义,GitHub操作工作流更多的是声明性的,而不是命令式的。 这意味着,您不是编写定义如何完成任务的编程逻辑,而是主要通过声明要使用的triggers、jobs、steps和runners来创建工作流。 并且,对于每个步骤,您将定义运行哪些操作…

Learning GitHub Actions Automation and Integration of CI/CD with GitHub【9】

CHAPTER 9:Actions and Security 正如前面几章所看到的,动作提供了令人印象深刻的自动信息水平。 它们还提供了直接在GitHub中完成任务的方法,否则这是不可能的。 然而,这些同样的功能也可能意味着必须事先考虑和计划的安全风险。 否则,您将打开存储库到多个攻击面和漏洞。…

知识图谱在提升大语言模型性能中的应用:减少幻觉与增强推理的综述

幻觉现象指的是模型在生成文本时可能会产生一些听起来合理但实际上并不准确或相关的输出&#xff0c;这主要是由于模型在训练数据中存在知识盲区所致。 为了解决这一问题&#xff0c;研究人员采取了多种策略&#xff0c;其中包括利用知识图谱作为外部信息源。知识图谱通过将信息…

Learning GitHub Actions Automation and Integration of CI/CD with GitHub【4】

CHAPTER 4 : Working with Workflows 我相信您现在已经收集到了,工作流是使用GitHub操作的核心。 我已经介绍了一些理解工作流的基本知识。 但是,您还需要能够轻松地创建、运行和监控它们的成功/失败。 本章将重点介绍这些活动。 首先,我将调查GitHub为从启动程序创建工作流…

Learning GitHub Actions Automation and Integration of CI/CD with GitHub【5】

CHAPTER 5:Runners 无论使用GitHub操作实现什么功能,都必须有一个地方来执行该功能 -- 一个具有足够资源来处理作业的虚拟或物理系统,以及一个配置为在分派作业时与操作控制平面交互的系统。 在操作术语中,在工作流中执行作业的系统被称为运行器。 在高级,跑步器系统有两种…

快速入门!学习鸿蒙App开发的终极指南!

鸿蒙&#xff08;HarmonyOS&#xff09;是华为推出的一款分布式操作系统&#xff0c;旨在为不同设备提供统一的操作体验。鸿蒙App开发可以让应用程序在多个设备上实现流畅运行。本文将介绍鸿蒙App开发的终极指南&#xff0c;帮助您快速入门。 开发环境搭建 鸿蒙App开发过程需要…

Learning GitHub Actions Automation and Integration of CI/CD with GitHub【1】

Foreword 连续集成/连续交付的基本概念(CI/CD)现在已经存在了几十年,因为马丁福勒和马修Foem- mel的作品首次推广CI在2000年9月的开创性的文章,和杰兹谦虚和戴夫法利写了CD在2010年的书连续交付:可靠的软件发布通过构建、测试和部署自动化(艾迪森-韦斯利专业)。然而,CI…

Learning GitHub Actions Automation and Integration of CI/CD with GitHub【2】

CHAPTER 2 : How Does Actions Work❓ 在第一章中,您高度了解了GitHub操作的整体框架和价值。 在本章中,我们将深入研究组成GitHub操作的部分,以及它们如何一起工作,意味着什么启动它们,当它们运行时发生什么,等等。 提醒一下,在GitHub操作的世界中,操作可以参考以下内…

数据结构十一:数组相关经典面试题

本篇博客详细介绍分析数组/顺序表常见的面试题&#xff0c;对于前面所学知识进行一个巩固&#xff0c;同时介绍一些力扣刷题中的一些概念&#xff1a;如&#xff1a;输出型参数等&#xff0c;在刷题中培养自己的编程思维&#xff0c;掌握常见的编程套路&#xff0c;形成题感&am…

智慧农场系统 搭建重点,会用到哪些三方服务?

智慧农场小游戏的搭建重点主要集中在游戏设计、用户体验、数据安全和稳定性等方面。为了实现这些目标&#xff0c;可能会用到以下第三方服务&#xff1a; 游戏引擎和开发工具&#xff1a;使用成熟的游戏引擎和开发工具可以极大地简化开发流程&#xff0c;提高开发效率。例如&a…

M2 Mac mini跑Llama3

前言 在4-19左右&#xff0c;Meta 宣布正式推出下一代开源大语言模型 Llama 3&#xff1b;共包括 80 亿和 700 亿参数两种版本&#xff0c;号称 “是 Llama 2 的重大飞跃”&#xff0c;并为这些规模的 LLM 确立了新的标准。实际上笔者早就体验过&#xff0c;只不过自己电脑没什…

【华为】路由策略小实验

【华为】软考中级-路由策略实验 实验需求拓扑配置AR1AR2需求1需求2 AR3 检验 实验需求 1、让 R3 可以学到R1的 192.168.10.0/24和192.168.20.0/24的 路由&#xff0c;不能学到192.168.30.0/24。 2、让 R1可以学到 R3 的 172.16.20.0/24和172.16.30.0/24的路由&#xff0c;不能…

Android手写自己的路由SDK

实现自己的路由框架 ​ 在较大型的Android app中常会用到组件化技术&#xff0c;针对不同的业务/基础功能对模块进行划分&#xff0c;从上到下为壳工程、业务模块、基础模块。其中业务模块依赖基础模块&#xff0c;壳工程依赖业务模块。同级的横向模块&#xff08;比如多个业务…

iMazing下载安装不了怎么办?

iMazing是一款可用于iPhone、iPad等ios移动设备管理软件&#xff0c;但需要注意的是&#xff0c;iMazing只能安装在Windows与Mac系统中&#xff0c;不能安装在iOS移动设备上。iOS移动设备可以通过USB线或Wi-Fi连接Windows或Mac系统上的iMazing软件。 iMazing的安装失败&#x…

手撕spring框架(3)

手撕spring框架&#xff08;3&#xff09; 相关系列 手撕spring框架&#xff08;1&#xff09; 手撕spring框架&#xff08;2&#xff09; 手撕spring框架&#xff08;4&#xff09; InitializingBean 接口详解 什么是 InitializingBean 接口&#xff1f; InitializingBean 接…

【深度学习】序列模型

深度学习&#xff08;Deep Learning&#xff09;是机器学习的一个分支领域&#xff1a;它是从数据中学习表示的一种新方法&#xff0c;强调从连续的层中进行学习&#xff0c;这些层对应于越来越有意义的表示。 1. 为什么选择序列模型&#xff1f; 循环神经网络&#xff08;RNN…

OpenHarmony实战开发-请求自绘制内容绘制帧率

对于基于XComponent进行Native开发的业务&#xff0c;可以请求独立的绘制帧率进行内容开发&#xff0c;如游戏、自绘制UI框架对接等场景。 接口说明 开发步骤 说明&#xff1a; 本范例是通过Drawing在Native侧实现图形的绘制&#xff0c;并将其呈现在NativeWindow上 1.定义Ark…