RT-Thread 组件

FinSH控制台

在计算机发展的早期,图形系统出现之前,没有鼠标,甚至没有键盘。

那时候人们如何与计算机交互呢?

最早期的计算时使用打孔的纸条向计算机输入命令,编写程序。

后来随着计算机的不断发展,显示器、键盘成为计算机的标准配置,但此时的操作系统还不支持图形界面,计算机先驱们开发了一种软件,它接受用户输入的命令,解释之后,传递给操作系统,并将操作系统的执行结果返回给用户。这个程序像一层外壳包括在操作系统的外面,所以被称为shell。

嵌入式设备通常需要将开发板与PC机连接起来通讯,常见连接方式包括:串口、USB、以太网、Wi-Fi等。

一个灵活的shell也应该支持在多种连接方式上工作。

有了shell,就像在开发者和计算机之间架起了一座沟通的桥梁,开发者能很方便的获取系统的运行情况,并通过命令控制系统的运行。

特别是在调试阶段,有了shell,开发者除了能更快的定位到问题之外,也能利用shell调用测试函数,改变测试函数的参数,减少代码的烧录次数,缩短项目的开发时间。

FinSH是RTT的命令行组件。

FinSH简介

FinSH是RTT的命令行组件,提供一套供用户在命令行调用的操作接口,主要用于调试或查看系统信息。

它可以使用串口/以太网/USB等与PC机进行通信。

用户在控制终端输入命令,控制终端通过串口将命令传给设备里的FinSH,FinSH会读取设备输入命令,解析并自动扫描内部函数表,寻找对应函数名,执行函数后输出回应,回应通过原路返回,将结果显示在控制终端上。

当使用串口连接设备与控制终端时,FinSH命令的执行流程,如图所示:

FinSH支持权限验证功能,系统在启动后会进行权限验证,只有权限验证通过,才会开启FinSH功能,提升系统输入的安全性。

FinSH支持自动补全、查看历史命令等功能,通过键盘上的按键可以很方便的使用这些功能,FinSH支持的按键如下表所示:

FinSH支持命令行模式,与传统shell执行方式一致。

显示设备状态

使用list device命令来显示系统中所有的设备信息,包括设备名称、设备类型和设备被打开次数。

显示动态内存状态

free

包括:总内存大小、已使用的内存大小、最大分配内存、可用内存大小

自定义FinSH命令

除了FinSH自带的命令,FinSH还提供了多个宏接口来导出自定义命令,导出的命令可以直接在FinSH中执行。

自定义的msh命令,可以在msh模式下呗运行,将一个命令导出到msh模式可以使用如下宏接口:

c 复制代码
MSH_CMD_EXPORT(name ,desc);
  • name:要导出的命令
  • desc:导出命令的描述

FinSH移植

FinSH完全采用ANSI C编写,具备极好的移植性;内存占用少,如果不使用前面章节中介绍的函数方式动态地向FinSH添加符号,FinSH将不会动态申请内存。

FinSH源码位于components/finsh目录下。
FinSH线程

每次的命令执行都是在FinSH线程(即tshell线程)的上下文中完成的。

FinSH的输出

输出依赖于rt_kprintf()输出。

rt_console_set_device(RT_CONSOLE_DEVICE_NAME);这个函数设置了FinSH的打印输出设备。

FAL组件

Flash抽象层,是对Flash及基于Flash的分区进行管理、操作的抽象层,对上层统一了Flash及分区操作的API(框架图如下所示):

  • 支持静态可配置的分区表,并可关联多个Flash设备;
  • 分区表支持自动状态。避免在多固件项目,分区表被多次定义的问题。
  • 代码简洁,对操作系统无依赖,可运行于裸机平台,比如对资源有一定要得BootLoader。
  • 统一的操作接口。保证了文件系统、OTA、NVM等对Flash有一定依赖的组件,底层Flash驱动的可重用性。
  • 自带基于FinSH/MSH的测试命令,可以通过shell按字节寻址的方式操作Flash或分区。

打开FAL

在组件中打开使用:

每个功能的配置说明如下:

  • 开启调试日志输出(默认开启);
  • 分区表是否在fal_cfg.h中定义(默认开启)。

FAL API

c 复制代码
const struct fal_flash_dev *fal_flash_device_find(const char *name)
  • name:Flash设备名称
  • return:如果查找成功,将返回Flash设备对象,查找失败返回NULL

使用FAL

使用 FAL 的基本步骤如下所示:

  1. 打开 FAL:从 Env 中打开 fal 软件包并下载到工程。
  2. FAL移植:定义flash设备、定义flash设备表、定义flash分区表。
  3. 调用fal_init()初始化该库:移植完成后,可在应用层调用。

定义flash设备

在定义 Flash 设备表前,需要先定义 Flash 设备。可以是片内 flash, 也可以是片外基于 SFUD 的 spi flash:

定义具体的flash设备对象,用户需要根据自己的flash情况分别实现init、read、write、erase这些操作函数:

c 复制代码
static int init(void);
static int read(long offset, uint8_t *buf, size_t size);
static int write(long offset, const uint8_t *buf, size_t size);
static int erase(long offset, size_t size)

用户需要根据自己的Flash情况分别实现这些操作函数。

在文件最底部定义了具体的Flash设备对象,比如示例定义了stm32f2片上flash:stm32f2_onchip_flash

c 复制代码
const struct fal_flash_dev stm32f2_onchip_flash = 
{
	.name = "stm32_onchip",
	.addr = 0x08000000,
	.len = 1024*1024,
	.blk_size = 2*1024
	.ops = {init, read, write, erase},
	.write_gran = 64
}

ulog日志

日志的定义:日志是将软件运行的状态、过程等信息,输出到不同的介质中(例如:文件、控制台、显示屏等),并进行显示和保存。为软件调试、维护过程中的问题追溯、性能分析、系统监控、故障预警等功能,提供参考依据。可以说,日志的使用,几乎占用的软件生命周期的至少80%的时间。

日志的重要性:对于操作系统而言,由于其软件的复杂度非常大,单步调试在一些场景下并不适合,所以日志组件在操作系统上几乎都是标配。完善的日志系统也能让操作系统的调试事半功倍。

ulog的起源:RTT一直缺少小巧、实用的日志组件,而ulog的诞生补全了这块的短板。它将作为RTT的基础组件被开源出来,让我们的开发者也能用上简洁易用的日志系统,提高开发效率。

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架构

下图为ulog日志组件架构图:

  • 前端:该层作为离应用层最近的一层,给用户提供了syslog及LOG_X两类API接口,方便用户在不同的场景中使用。
  • 核心:中间核心层的主要工作是将上层传递过来的日志,按照不同的配置要求进行格式化与过滤然后生成日志帧,最终通过不同的输出模块,输出到最底层的后端设备上。
  • 后端:接收到核心层发来的日志帧后,将日志输出到已经注册的日志后端设备上,例如:文件、控制台、日志服务器等。

日志级别

日志级别代表了日志的重要性,在ulog中由高到低,有如下几个日志级别:

  • LOG_LVL_ASSERT(断言):发生无法处理、致命性的错误,以至于系统无法继续运行的断言日志
  • LOG_LVL_ERROR(错误):发生严重的、不可修复的错误时输出的日志属于错误级别日志
  • LOG_LVL_WARNING(警告):出现一些不太重要的、具有可修复性的错误时,会输出这些警告日志
  • LOG_LVL_INFO(信息):给本模块上层使用人员查看的重要提示信息日志,例如:初始化成功,当前工作状态等。该级别日志一般在量产时依旧保留。
  • LOG_LVL_DBG(调试):给本模块开发人员查看的调试日志,该级别日志一般在量产时关闭。

在ulog中日志级别还有如下分类:

  • 静态级别与动态级别:按照日志是否可以在运行阶段修改进行分类。可在运行阶段修改的称为动态级别,只能在编译阶段修改的称为静态级别。比静态级别低的日志(这里特指使用 LOG_X API 的日志)将不会被编译到 ROM 中,最终也不会输出、显示出来。而动态级别可以管控的是高于或等于静态级别的日志。在 ulog 运行时,比动态级别低的日志会被过滤掉。
  • 全局级别与模块级别:按照作用域进行的分类。在ulog中每个文件也可以设定独立的日志级别。全局级别作用域大于模块级别,也就是模块级别只能管控那些高于或等于全局级别的模块日志。
c 复制代码
int ulog_init();

在使用ulog前必须调用该函数完成ulog初始化。

日志输出API

c 复制代码
#define LOG_E(...)                           ulog_e(LOG_TAG, __VA_ARGS__)
#define LOG_W(...)                           ulog_w(LOG_TAG, __VA_ARGS__)
#define LOG_I(...)                           ulog_i(LOG_TAG, __VA_ARGS__)
#define LOG_D(...)                           ulog_d(LOG_TAG, __VA_ARGS__)
#define LOG_RAW(...)                         ulog_raw(__VA_ARGS__)
#define LOG_HEX(name, width, buf, size)      ulog_hex(name, width, buf, size)

宏LOG_X(...):X 对应的是不同级别的第一个字母大写。参数 ... 为日志内容,格式与 printf 一致。这种方式是首选,一方面因为其 API 格式简单,入参只有一个即日志信息,再者还支持按模块静态日志级别过滤。

相关推荐
西岸行者10 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意10 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码10 天前
嵌入式学习路线
学习
科技前瞻观察10 天前
腾讯控股下的销售易,如何重塑中国CRM格局?
microsoft
毛小茛10 天前
计算机系统概论——校验码
学习
babe小鑫10 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms10 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下10 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。10 天前
2026.2.25监控学习
学习
im_AMBER10 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode