前言
正常情況下,使用 printf 向串口打印調(diào)試信息,在串口調(diào)試工具界面只能看到一種顏色的字符,且使用不同的串口調(diào)試工具,字符默認(rèn)的顏色都不一樣。
如果你在 VSCode 上使用過(guò) ESP-IDF 開發(fā) ESP32 應(yīng)用程序,你會(huì)發(fā)現(xiàn)其終端上輸出的調(diào)試信息是五顏六色的,十分花哨。 而且不同類型的調(diào)試信息使用不同的顏色區(qū)分,能夠快速地定位到程序中的問(wèn)題,非常便捷實(shí)用。
那么我們使用 printf 函數(shù)通過(guò)串口能否輸出彩色的調(diào)試信息呢? 答案是肯定的,只是需要一丟丟小技巧罷了。
ANSI 轉(zhuǎn)義序列
在介紹如何打印彩色調(diào)試信息之前,我們先來(lái)了解一些背景知識(shí) —— ANSI 轉(zhuǎn)義序列。
我們將信息打印到終端上顯示時(shí),可以控制信息顯示成粗體、斜體、下劃線等形式,也可以控制字符顯示不同的顏色。
例如執(zhí)行如下代碼,會(huì)在終端上顯示紅色 ---testing---:
printf("\\033[31m---testing---\\n");
為什么有些字符會(huì)被當(dāng)做信息顯示,有些字符會(huì)被當(dāng)做命令處理? 其實(shí)這一切都?xì)w功于轉(zhuǎn)義字符。
在C語(yǔ)言中,用反斜杠 "" 作為轉(zhuǎn)義字符,正常情況下,"" 和 "n" 都是普通的字符,但是將這兩個(gè)字符組合起來(lái)變成 "\\n" 的字符序列后,就表示換行符了。
在終端中也有類似的用法,只不過(guò)終端中的轉(zhuǎn)義字符不是反斜杠 "" ,而是 "ESC"。 終端使用 "ESC" 作為轉(zhuǎn)義字符,而 "ESC" 在 ASCII 表中的序號(hào)是 27,也就是 0x1b,因此終端中與 0x1b 組合起來(lái)的字符序列就會(huì)被轉(zhuǎn)義成命令,不同的字符組合表示不同的終端命令,這些轉(zhuǎn)義規(guī)則被規(guī)范化以后,就叫做 ANSI 轉(zhuǎn)義序列(ANSI Escape Sequences), 主要用來(lái)控制終端上光標(biāo)位置、顏色及其他選項(xiàng)。
ANSI 轉(zhuǎn)義序列起始標(biāo)志
ANSI 轉(zhuǎn)義序列起始標(biāo)志由兩個(gè)字節(jié)組成,第一個(gè)字節(jié)是終端轉(zhuǎn)義字符 "ESC" 也就是 0x1b,第二個(gè)字節(jié)的取值范圍是0x40–0x5F(即 ASCII:@ A – Z [ \\ ] ^ _),也就是說(shuō) ANSI 轉(zhuǎn)義序列的起始標(biāo)志共有如下幾種:
- x1b@(ESC@)
- x1bA(掃描)
- x1b-(電調(diào)-)
- x1bZ(ESCZ)
- x1b[(ESC[)
- x1b\\(ESC\\)
- x1b](ESC])
- x1b^(ESC^)
- x1b_(ESC_)
上述那么多種起始標(biāo)志,常見的起始標(biāo)志就是 " x1b[ " ,其他的起始標(biāo)志現(xiàn)在都很少用到了。
控制符起始標(biāo)志 CSI
當(dāng) 0x1b 與 "[" 組合時(shí),就叫做控制符起始標(biāo)志(Control Sequence Introducer),簡(jiǎn)稱為 CSI ,其基本格式如下:
x1b[<code><tail>
code: 轉(zhuǎn)義序列的具體內(nèi)容,多個(gè)內(nèi)容之間使用分號(hào) ";" 隔開
tail : 轉(zhuǎn)義序列結(jié)束標(biāo)志,不同 tail 表示不同功能的序列
以 CSI 開頭的轉(zhuǎn)義序列有很多,不同的序列表示不同的終端指令,大致可以分為如下幾類:
- 字符渲染指令(SGR)
- 光標(biāo)移動(dòng)指令
- 清屏指令
- 終端控制指令
字符渲染指令
字符渲指令(Select Graphic Rendition),簡(jiǎn)稱為 SGR 。主要用于設(shè)置字符顯示效果,其基本格式如下:
CSI編號(hào)
CSI:控制符起始標(biāo)志 " 0x1b[ "
n :取值范圍 0 ~ 107
m :轉(zhuǎn)義序列結(jié)束標(biāo)志,字母 m 作為結(jié)尾,表明是字符渲染序列
示例:
x1b[3m
n 的取值范圍是 0 ~ 107,主要分為如下兩類功能:
- 字符樣式控制
- 字符顏色控制
n 的取值及說(shuō)明:
n | 功能 | 示例 |
---|---|---|
0 | 重置/正常 | x1b[0m 或 x1b[m |
1 | 粗體或增加強(qiáng)度 | x1b[1m |
2 | 弱化(降低強(qiáng)度) | x1b[2m |
3 | 斜體 | x1b[3m |
4 | 下劃線 | x1b[4m |
5 | 緩慢閃爍 | ...... |
6 | 快速閃爍 | ...... |
7 | 反顯 | ...... |
8 | 隱藏 | ...... |
9 | 劃除 | ...... |
10 | 主要(默認(rèn))字體 | ...... |
11–19 | 替代字體 | ...... |
20 | 尖角體 | ...... |
21 | 關(guān)閉粗體或雙下劃線 | ...... |
22 | 正常顏色或強(qiáng)度 | ...... |
23 | 非斜體、非尖角體 | ...... |
24 | 關(guān)閉下劃線 | ...... |
25 | 關(guān)閉閃爍 | ...... |
27 | 關(guān)閉反顯 | ...... |
28 | 關(guān)閉隱藏 | ...... |
29 | 關(guān)閉劃除 | ...... |
30–37 | 設(shè)置前景色 | ...... |
38 | 設(shè)置前景色 | ...... |
39 | 默認(rèn)前景色 | ...... |
40–47 | 設(shè)置背景色 | ...... |
48 | 設(shè)置背景色 | ...... |
49 | 默認(rèn)背景色 | ...... |
51 | Framed | ...... |
52 | Encircled | ...... |
53 | 上劃線 | ...... |
54 | Not framed or encircled | ...... |
55 | 關(guān)閉上劃線 | ...... |
60 | 表意文字下劃線或右邊線 | ...... |
61 | 表意文字雙下劃線或雙右邊線 | ...... |
62 | 表意文字上劃線或左邊線 | ...... |
63 | 表意文字雙上劃線或雙左邊線 | ...... |
64 | 表意文字著重標(biāo)志 | ...... |
65 | 表意文字屬性關(guān)閉 | ...... |
90–97 | 設(shè)置明亮的前景色 | ...... |
100–107 | 設(shè)置明亮的背景色 | ...... |
有了上述這些背景知識(shí),就足夠搞懂串口輸出彩色調(diào)試信息的原理了,關(guān)于 ANSI 轉(zhuǎn)義序列的更多內(nèi)容,感興趣的小伙伴可以自行查閱。
串口輸出彩色調(diào)試信息
在 《單片機(jī) printf 重定向串口輸出調(diào)試信息》這篇文章中介紹了如何使用 printf 通過(guò)串口輸出調(diào)試信息,那么現(xiàn)在就在此基礎(chǔ)上,再說(shuō)說(shuō)如何將輸出的調(diào)試信息變成彩色的。
通過(guò)串口輸出彩色調(diào)試信息有兩個(gè)必要條件:
- printf 參數(shù)列表中添加字符顏色控制相關(guān)的 ANSI轉(zhuǎn)義序列
- 接收串口數(shù)據(jù)的串口調(diào)試工具支持 ANSI 轉(zhuǎn)義序列解析
回顧一下上面講到的字符渲指令(SGR),其基本格式如下:
CSI n m
CSI:控制符起始標(biāo)志 " 0x1b[ "
n :取值范圍 0 ~ 107
m :轉(zhuǎn)義序列結(jié)束標(biāo)志,字母 m 作為結(jié)尾,表明是字符渲染序列
當(dāng)我們想要輸出不同顏色的字符時(shí),將 n 替換成對(duì)應(yīng)的數(shù)字即可,常見的顏色如下:
n | 功能 | 示例 |
---|---|---|
30 | 黑色 | x1b[30m |
31 | 紅色 | x1b[31m |
32 | 綠色 | x1b[32m |
33 | 黃色 | x1b[33m |
34 | 藍(lán)色 | 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");
注意事項(xiàng)
如果使用的串口調(diào)試工具不支持 ANSI 轉(zhuǎn)義序列解析,那么接收到的串口數(shù)據(jù)會(huì)有部分亂碼,而且只會(huì)使用工具默認(rèn)的顏色顯示信息:
支持 ANSI 轉(zhuǎn)義序列解析的串口調(diào)試工具才能正常顯示彩色字符,推薦使用 MobaXterm 。
-
調(diào)試
+關(guān)注
關(guān)注
7文章
582瀏覽量
33971 -
串口
+關(guān)注
關(guān)注
14文章
1555瀏覽量
76641 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4337瀏覽量
62730 -
命令
+關(guān)注
關(guān)注
5文章
687瀏覽量
22053 -
Printf
+關(guān)注
關(guān)注
0文章
83瀏覽量
13675
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論