前言
正常情况下,使用 printf 向串口打印调试信息,在串口调试工具界面只能看到一种颜色的字符,且使用不同的串口调试工具,字符默认的颜色都不一样。
如果你在 VSCode 上使用过 ESP-IDF 开发 ESP32 应用程序,你会发现其终端上输出的调试信息是五颜六色的,十分花哨。 而且不同类型的调试信息使用不同的颜色区分,能够快速地定位到程序中的问题,非常便捷实用。
那么我们使用 printf 函数通过串口能否输出彩色的调试信息呢? 答案是肯定的,只是需要一丢丢小技巧罢了。
ANSI 转义序列
在介绍如何打印彩色调试信息之前,我们先来了解一些背景知识 —— ANSI 转义序列。
我们将信息打印到终端上显示时,可以控制信息显示成粗体、斜体、下划线等形式,也可以控制字符显示不同的颜色。
例如执行如下代码,会在终端上显示红色 ---testing---:
printf("\\033[31m---testing---\\n");
为什么有些字符会被当做信息显示,有些字符会被当做命令处理? 其实这一切都归功于转义字符。
在C语言中,用反斜杠 "" 作为转义字符,正常情况下,"" 和 "n" 都是普通的字符,但是将这两个字符组合起来变成 "\\n" 的字符序列后,就表示换行符了。
在终端中也有类似的用法,只不过终端中的转义字符不是反斜杠 "" ,而是 "ESC"。 终端使用 "ESC" 作为转义字符,而 "ESC" 在 ASCII 表中的序号是 27,也就是 0x1b,因此终端中与 0x1b 组合起来的字符序列就会被转义成命令,不同的字符组合表示不同的终端命令,这些转义规则被规范化以后,就叫做 ANSI 转义序列(ANSI Escape Sequences), 主要用来控制终端上光标位置、颜色及其他选项。
ANSI 转义序列起始标志
ANSI 转义序列起始标志由两个字节组成,第一个字节是终端转义字符 "ESC" 也就是 0x1b,第二个字节的取值范围是0x40–0x5F(即 ASCII:@ A – Z [ \\ ] ^ _),也就是说 ANSI 转义序列的起始标志共有如下几种:
- x1b@(ESC@)
- x1bA(扫描)
- x1b-(电调-)
- x1bZ(ESCZ)
- x1b[(ESC[)
- x1b\\(ESC\\)
- x1b](ESC])
- x1b^(ESC^)
- x1b_(ESC_)
上述那么多种起始标志,常见的起始标志就是 " x1b[ " ,其他的起始标志现在都很少用到了。
控制符起始标志 CSI
当 0x1b 与 "[" 组合时,就叫做控制符起始标志(Control Sequence Introducer),简称为 CSI ,其基本格式如下:
x1b[<code><tail>
code: 转义序列的具体内容,多个内容之间使用分号 ";" 隔开
tail : 转义序列结束标志,不同 tail 表示不同功能的序列
以 CSI 开头的转义序列有很多,不同的序列表示不同的终端指令,大致可以分为如下几类:
- 字符渲染指令(SGR)
- 光标移动指令
- 清屏指令
- 终端控制指令
字符渲染指令
字符渲指令(Select Graphic Rendition),简称为 SGR 。主要用于设置字符显示效果,其基本格式如下:
CSI编号
CSI:控制符起始标志 " 0x1b[ "
n :取值范围 0 ~ 107
m :转义序列结束标志,字母 m 作为结尾,表明是字符渲染序列
示例:
x1b[3m
n 的取值范围是 0 ~ 107,主要分为如下两类功能:
- 字符样式控制
- 字符颜色控制
n 的取值及说明:
n | 功能 | 示例 |
---|---|---|
0 | 重置/正常 | x1b[0m 或 x1b[m |
1 | 粗体或增加强度 | x1b[1m |
2 | 弱化(降低强度) | x1b[2m |
3 | 斜体 | x1b[3m |
4 | 下划线 | x1b[4m |
5 | 缓慢闪烁 | ...... |
6 | 快速闪烁 | ...... |
7 | 反显 | ...... |
8 | 隐藏 | ...... |
9 | 划除 | ...... |
10 | 主要(默认)字体 | ...... |
11–19 | 替代字体 | ...... |
20 | 尖角体 | ...... |
21 | 关闭粗体或双下划线 | ...... |
22 | 正常颜色或强度 | ...... |
23 | 非斜体、非尖角体 | ...... |
24 | 关闭下划线 | ...... |
25 | 关闭闪烁 | ...... |
27 | 关闭反显 | ...... |
28 | 关闭隐藏 | ...... |
29 | 关闭划除 | ...... |
30–37 | 设置前景色 | ...... |
38 | 设置前景色 | ...... |
39 | 默认前景色 | ...... |
40–47 | 设置背景色 | ...... |
48 | 设置背景色 | ...... |
49 | 默认背景色 | ...... |
51 | Framed | ...... |
52 | Encircled | ...... |
53 | 上划线 | ...... |
54 | Not framed or encircled | ...... |
55 | 关闭上划线 | ...... |
60 | 表意文字下划线或右边线 | ...... |
61 | 表意文字双下划线或双右边线 | ...... |
62 | 表意文字上划线或左边线 | ...... |
63 | 表意文字双上划线或双左边线 | ...... |
64 | 表意文字着重标志 | ...... |
65 | 表意文字属性关闭 | ...... |
90–97 | 设置明亮的前景色 | ...... |
100–107 | 设置明亮的背景色 | ...... |
有了上述这些背景知识,就足够搞懂串口输出彩色调试信息的原理了,关于 ANSI 转义序列的更多内容,感兴趣的小伙伴可以自行查阅。
串口输出彩色调试信息
在 《单片机 printf 重定向串口输出调试信息》这篇文章中介绍了如何使用 printf 通过串口输出调试信息,那么现在就在此基础上,再说说如何将输出的调试信息变成彩色的。
通过串口输出彩色调试信息有两个必要条件:
- printf 参数列表中添加字符颜色控制相关的 ANSI转义序列
- 接收串口数据的串口调试工具支持 ANSI 转义序列解析
回顾一下上面讲到的字符渲指令(SGR),其基本格式如下:
CSI n m
CSI:控制符起始标志 " 0x1b[ "
n :取值范围 0 ~ 107
m :转义序列结束标志,字母 m 作为结尾,表明是字符渲染序列
当我们想要输出不同颜色的字符时,将 n 替换成对应的数字即可,常见的颜色如下:
n | 功能 | 示例 |
---|---|---|
30 | 黑色 | x1b[30m |
31 | 红色 | x1b[31m |
32 | 绿色 | x1b[32m |
33 | 黄色 | x1b[33m |
34 | 蓝色 | x1b[34m |
35 | 品红 | x1b[35m |
36 | 青色 | x1b[36m |
使用 printf 打印 "Hello World" 代码如下:
printf("Hello World\\n");
打印不同的 "Hello World" 代码如下:
printf("\\x1b[31m" "Hello World\\n" "\\x1b[0m");
printf("\\x1b[32m" "Hello World\\n" "\\x1b[0m");
printf("\\x1b[33m" "Hello World\\n" "\\x1b[0m");
printf("\\x1b[34m" "Hello World\\n" "\\x1b[0m");
printf("\\x1b[35m" "Hello World\\n" "\\x1b[0m");
printf("\\x1b[36m" "Hello World\\n" "\\x1b[0m");
注意事项
如果使用的串口调试工具不支持 ANSI 转义序列解析,那么接收到的串口数据会有部分乱码,而且只会使用工具默认的颜色显示信息:
支持 ANSI 转义序列解析的串口调试工具才能正常显示彩色字符,推荐使用 MobaXterm 。
-
调试
+关注
关注
7文章
578浏览量
33923 -
串口
+关注
关注
14文章
1551浏览量
76421 -
函数
+关注
关注
3文章
4327浏览量
62569 -
命令
+关注
关注
5文章
683浏览量
22011 -
Printf
+关注
关注
0文章
83浏览量
13648
发布评论请先 登录
相关推荐
评论