众所周知,linux开发非常方便,因为有很多开源的库可以通过简单的安装就能使用,而实时操作系统像Free RTOS大多只提供了系统内核的功能,但是RT-Thread在提供了一套通用设备的框架前提下,往上移植了很多的常用组件功能,比如控制台,文件系统,网络协议栈等,通过简单的配置即可应用开发,给用户提供了非常不错的解决方案参考。
FinSH 是 RT-Thread 的命令行组件,提供一套供用户在命令行调用的操作接口,主要用于调试或查看系统信息。
FinSH 支持两种输入模式,分别是传统命令行模式和 C 语言解释器模式。
RT-Thread shell commands:
version - show RT-Thread version information
list_thread - list thread
list_sem - list semaphore in system
list_event - list event in system
list_mutex - list mutex in system
list_mailbox - list mail box in system
list_msgqueue - list message queue in system
list_timer - list timer in system
list_device - list device in system
exit - return to RT-Thread shell mode.
help - RT-Thread shell help.
ps - List threads in the system.
time - Execute command with time.
free - Show the memory usage in the system.
这里列出输入常用命令后返回的字段信息,方便开发者理解返回的信息内容。
除了 FinSH 自带的命令,FinSH 还也提供了多个宏接口来导出自定义命令,导出的命令可以直接在 FinSH 中执行。
DFS 是 RT-Thread 提供的虚拟文件系统组件,全称为 Device File System,即设备虚拟文件系统,文件系统的名称使用类似 UNIX 文件、文件夹的风格,目录结构如下图所示:
RT-Thread DFS 组件的主要功能特点有:
为应用程序提供统一的 POSIX 文件和目录操作接口:read、write、poll/select 等。
支持多种类型的文件系统,如 FatFS、RomFS、DevFS 等,并提供普通文件、设备文件、网络文件描述符的管理。
支持多种类型的存储设备,如 SD Card、SPI Flash、Nand Flash 等。
DFS 的层次架构如下图所示,主要分为 POSIX 接口层、虚拟文件系统层和设备抽象层。
文件系统的初始化过程一般分为以下几个步骤:
初始化 DFS 组件。
初始化具体类型的文件系统。
在存储器上创建块设备。
格式化块设备。
挂载块设备到 DFS 目录中。
当文件系统不再使用,可以将它卸载。
// 注册文件系统
int dfs_register(const struct dfs_filesystem_ops *ops);
// 格式化文件系统
int dfs_mkfs(const char * fs_name, const char * device_name);
// 挂载文件系统
int dfs_mount(const char *device_name,
const char *path,
const char *filesystemtype,
unsigned long rwflag,
const void *data);
// 卸载文件系统
int dfs_unmount(const char *specialfile);
对文件进行操作的相关函数,对文件的操作一般都要基于文件描述符 fd,如下图所示:
目录管理经常使用的函数,对目录的操作一般都基于目录地址,如下图所示:
netdev(network interface device),即网络接口设备,又称网卡。每一个用于网络连接的设备都可以注册成网卡,为了适配更多的种类的网卡,避免系统中对单一网卡的依赖,RT-Thread 系统提供了 netdev 组件用于网卡管理和控制。
网卡的初始化和注册建立在协议簇类型上,所以每种网卡对应唯一的协议簇类型。 Socket 套接字描述符的创建建立在 netdev 网卡基础上,所以每个创建的 Socket 对应唯一的网卡。协议簇、网卡和 socket 之间关系如下图所示:
网卡结构体对象参数介绍:
/* 网卡结构体对象 */
struct netdev
{
rt_slist_t list; /* 网卡列表 */
char name[RT_NAME_MAX]; /* 网卡名称 */
ip_addr_t ip_addr; /* IP 地址 */
ip_addr_t netmask; /* 子网掩码地址 */
ip_addr_t gw; /* 网关地址 */
ip_addr_t dns_servers[NETDEV_DNS_SERVERS_NUM]; /* DNS 服务器地址 */
uint8_t hwaddr_len; /* 硬件地址长度 */
uint8_t hwaddr[NETDEV_HWADDR_MAX_LEN]; /* 硬件地址 */
uint16_t flags; /* 网卡状态位 */
uint16_t mtu; /* 网卡最大传输单元 */
const struct netdev_ops *ops; /* 网卡操作回调函数 */
netdev_callback_fn status_callback; /* 网卡状态改变回调 */
netdev_callback_fn addr_callback; /* 网卡地址改变回调 */
#ifdef RT_USING_SAL
void *sal_user_data; /* 网卡中协议簇相关参数数据 */
#endif /* RT_USING_SAL */
void *user_data; /* 预留用户数据 */
};
// 注册网卡
int netdev_register(struct netdev *netdev, const char *name, void *user_data);
// 注销网卡
int netdev_unregister(struct netdev *netdev);
// 通过状态获取第一个匹配的网卡对象
struct netdev *netdev_get_first_by_flags(uint16_t flags);
// 获取第一个指定协议簇类型的网卡对象
struct netdev *netdev_get_by_family(int family);
// 通过 IP 地址获取网卡对象
struct netdev *netdev_get_by_ipaddr(ip_addr_t *ip_addr);
// 通过名称获取网卡对象
struct netdev *netdev_get_by_name(const char *name);
// 设置默认网卡
void netdev_set_default(struct netdev *netdev);
// 设置网卡 up/down 状态
int netdev_set_up(struct netdev *netdev);
int netdev_set_down(struct netdev *netdev);
// 设置网卡 DHCP 功能状态
int netdev_dhcp_enabled(struct netdev *netdev, rt_bool_t is_enabled);
// 设置网卡地址信息
/* 设置网卡 IP 地址 */
int netdev_set_ipaddr(struct netdev *netdev, const ip_addr_t *ipaddr);
/* 设置网卡网关地址 */
int netdev_set_gw(struct netdev *netdev, const ip_addr_t *gw);
/* 设置网卡子网掩码地址 */
int netdev_set_netmask(struct netdev *netdev, const ip_addr_t *netmask);
// 设置网卡 DNS 服务器地址
int netdev_set_dns_server(struct netdev *netdev, uint8_t dns_num, const ip_addr_t *dns_server);
// 设置网卡回调函数
typedef void (*netdev_callback_fn )(struct netdev *netdev, enum netdev_cb_type type);
void netdev_set_status_callback(struct netdev *netdev, netdev_callback_fn status_callback);
// 设置网卡地址信息改变时调用的回调函数
void netdev_set_addr_callback(struct netdev *netdev, netdev_callback_fn addr_callback);
// 获取网卡信息
#define netdev_is_up(netdev)
#define netdev_is_link_up(netdev)
#define netdev_is_internet_up(netdev)
#define netdev_is_dhcp_enable(netdev)
为了适配更多的网络协议栈类型,避免系统对单一网络协议栈的依赖,RT-Thread 系统提供了一套 SAL(套接字抽象层)组件,该组件完成对不同网络协议栈或网络实现接口的抽象并对上层提供一组标准的 BSD Socket API。
RT-Thread 的 网络框架结构如下所示:
使用流程如下:
配置开启任意网络协议栈支持(如 lwIP 协议栈);
配置开启 MbedTLS 软件包(目前只支持 MbedTLS 类型加密方式);
配置开启 SAL_TLS 功能支持
为了方便用户使用 AT 命令,简单的适配不同的 AT 模块, RT-Thread 提供了 AT 组件用于 AT 设备的连接和数据通讯。AT 组件的实现包括客户端的和服务器两部分。
AT 组件资源占用:
AT Client 功能:4.6K ROM 和 2.0K RAM;
AT Server 功能:4.0K ROM 和 2.5K RAM;
AT CLI 功能: 1.5K ROM ,几乎没有使用 RAM。
ulog 是一个非常简洁、易用的 C/C++ 日志组件,第一个字母 u 代表 μ,即微型的意思。它能做到最低ROM<1K, RAM<0.2K的资源占用。ulog 不仅有小巧体积,同样也有非常全面的功能,其设计理念参考的是另外一款 C/C++ 开源日志库:EasyLogger(简称 elog),并在功能和性能等方面做了非常多的改进。主要特性如下:
日志输出的后端多样化,可支持例如:串口、网络,文件、闪存等后端形式。
日志输出被设计为线程安全的方式,并支持异步输出模式。
日志系统高可靠,在中断 ISR 、Hardfault 等复杂环境下依旧可用。
日志支持运行期 / 编译期设置输出级别。
日志内容支持按关键词及标签方式进行全局过滤。
API 和日志格式可兼容 linux syslog。
支持以 hex 格式 dump 调试数据到日志中。
兼容 rtdbg (RTT 早期的日志头文件)及 EasyLogger 的日志输出 API。
ulog 日志组件架构图:
utest(unit test)是 RT-Thread 开发的单元测试框架。设计 utest 的初衷是方便 RT-Thread 开发者使用统一的框架接口编写测试程序,实现单元测试、覆盖测试以及集成测试的目的。
在传统桌面操作系统中,用户空间和内核空间是分开的,应用程序运行在用户空间,内核以及内核模块则运行于内核空间,其中内核模块可以动态加载与删除以扩展内核功能。dlmodule
则是 RT-Thread 下,在内核空间对外提供的动态模块加载机制的软件组件。
POSIX Threads 简称 Pthreads,POSIX 是 “Portable Operating System Interface”(可移植操作系统接口) 的缩写,POSIX 是 IEEE Computer Society 为了提高不同操作系统的兼容性和应用程序的可移植性而制定的一套标准。
RT-Thread 的 PM 组件采用分层设计思想,分离架构和芯片相关的部分,提取公共部分作为核心。在对上层提供通用的接口同时,也让底层驱动对组件的适配变得更加简单。
RT-Thread提供了比较完整的外设组件,给用户很宽泛的选择空间去考虑实际应用需求,减少了用户对于设备底层开发的移植工作量,吧更多的时间放在应用实现和稳定性测试上。
此内容由EEWORLD论坛网友ID.LODA原创,如需转载或用于商业用途需征得作者同意并注明出处
今天楼主发了好几篇这样的帖子啊。真是太好了。一直看好RT系统。希望楼主坚持下去,把这个系列发完。
然后版主、管理员一定要给加精!写的太好了