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 格式简单,入参只有一个即日志信息,再者还支持按模块静态日志级别过滤。

相关推荐
@小博的博客1 小时前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
南宫生2 小时前
贪心算法习题其四【力扣】【算法学习day.21】
学习·算法·leetcode·链表·贪心算法
懒惰才能让科技进步3 小时前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝
love_and_hope3 小时前
Pytorch学习--神经网络--搭建小实战(手撕CIFAR 10 model structure)和 Sequential 的使用
人工智能·pytorch·python·深度学习·学习
Chef_Chen3 小时前
从0开始学习机器学习--Day14--如何优化神经网络的代价函数
神经网络·学习·机器学习
芊寻(嵌入式)3 小时前
C转C++学习笔记--基础知识摘录总结
开发语言·c++·笔记·学习
hong1616884 小时前
跨模态对齐与跨领域学习
学习
阿伟来咯~4 小时前
记录学习react的一些内容
javascript·学习·react.js
Suckerbin5 小时前
Hms?: 1渗透测试
学习·安全·网络安全
水豚AI课代表5 小时前
分析报告、调研报告、工作方案等的提示词
大数据·人工智能·学习·chatgpt·aigc