- Arduino UNO
- Arduino Leonardo
- Arduino NANO
- Arduino Pro mini
- Arduino Pro micro
- ESP8266 GPIO
- NodeMCU 1.0 (ESP8266)
- WeMos D1 mini (ESP8266)
- NodeOLED (ESP8266)
- ESP-01 (ESP8266)
- ESP32 DevBoard (ESP32)
- Node ESP-32S (ESP32)
- Ai-Thinker ESP12K (ESP32-S2)
- Mediatek Linkit 7697 Nano
- Mediatek Linkit 7697 Nano Board
- Mediatek Linkit 7697 Bit
- Microbit
2022年2月13日 星期日
常用微控制板接腳圖 (Pin Layout)
2022年2月11日 星期五
自訂 Linux 的 Bash Shell 命令提示字串 Prompt
如何在 Linux 或 Unix 操作系統下更改 shell 提示的顏色?如何在 Linux、macOS 或類 Unix 系統上自定義和著色我的 Bash 提示符 PS1?
我們可以更改 shell 提示符的顏色,或者在命令提示符下讓工作變得更輕鬆。BASH shell 是 Linux 和 Apple OS X 下的預設的設置。現在讓我們看看在 Linux 或 Unix 系統上使用 bash 時如何更改 shell 提示的顏色。
基本設定方法
Linux 的" Bash Shell 命令提示字串可以透過 PS1 這個環境變數來設定,通常他都是寫在 ~/.bashrc 或是 ~/.bash_profile 這些 Bash 的設定檔中,通常預設的設定會類似這樣:
PS1="\u@\h \W$ "在 PS1 的設定中,若以反斜線加上一個特定字母,都有一些特殊意義:
- \u:表使用者的帳號名稱。
- \h:主機名稱。
- \W:目前的工作目錄名稱。
顯示出來的結果會像這樣:
通常剛裝好的 Linux 系統,命令提示字串大概就是像這樣,沒有什麼特別,但是其實它的功能很強大,可以有很多的變化,以下是一些常用的功能介紹。
顯示時間
在 PS1 環境變數中,您可以使用 $(linux_command) 的方式,直接執行任何的 Linux 指令,下面是一個執行 $(date) 來顯示目前時間的例子。
PS1="\u@\h [$(date +%k:%M:%S)]> "結果會像這樣:
另一種方式是直接使用 \t,則可以顯示 hh:mm:ss 格式的時間,例如:
PS1="[\t] \u@$(pwd)> "而 \@ 則是可以顯示 12 小時制的時間:
PS1="[\@] \u@$(pwd)> "
任意指令輸出
如果您對於 shell 的程式設計很熟悉的話,可以在 PS1 插入任何的指令輸出或是變數:
kernel_version=$(uname -r) PS1="\!|\h|$kernel_version|$? > "
文字顏色
命令提示字串也可以使用彩色的文字:
PS1="\e[0;34m\u@\h \w\e[0m > "這裡文字的色彩是靠 ANSI escape code 來指定的,\e[0;34m 是顏色指定的開始控制碼,結束是 \e[0m,而在放這中間的所有文字都會是有顏色的,而文字的顏色則是由開始控制碼中的數值來決定:
0 與 1:0 代表正常亮度,1 代表高亮度。 30 與 37:30 + x 所得到的數值可指定前景顏色(x 值與顏色的對應請參考下面的對應表)。 40 與 47:40 + x 所得到的數值可指定背景顏色(x 值與顏色的對應請參考下面的對應表)。 多個數值之間以分號(;)隔開,像這裡的 \e[0;34m 就是指定正常亮度(0),顏色為藍色(34 = 30 + 4),結果會像這樣:
這是使用高亮度(1)的狀況:
PS1="\e[1;34m\u@\h \w\e[0m > "加上背景的顏色:
PS1="\e[0;34;47m\u@\h \w\e[0m > "使用多種顏色:
PS1='\e[1;37m\t\e[0m\n\e[1;32m\u\e[1;33m@\e[1;32m\h\e[33m:\e[1;36m$(pwd)\e[1;33m\$\e[0m '
PS1 特殊字元
以下這些是在 PS1 中可以使用的特殊字元,您可以用運這些設計自己的命令提示字串。
- \a:ASCII bell 字元(07)。
- \d:格式為 Weekday Month Date 的日期(例如 Tue May 26)。
- \D{format}:將 format 傳給 strftime(3),然後將輸出的結果放進命令提示字串中,如果 format 是空字串,就會使用目前語系的預設的格式,其中的大括號不可以省略。
- \e:ASCII 跳脫字元(escape character,033)。
- \h:機器的簡短主機名稱(hostname),只顯示到第一個句點之前。
- \H:機器的完整主機名稱(hostname)。
- \j:目前的 shell 所掌控的 jobs 數量。
- \l:the basename of the shell’s terminal device name。
- \n:換行。
- \r:carriage return。
- \s:the name of the shell, the basename of $0 (the portion following the final slash)。
- \t:現在時間,24 小時制(HH:MM:SS)。
- \T:現在時間,12 小時制(HH:MM:SS)。
- \@:現在時間,12 小時制(HH:MM AM/PM)。
- \A:現在時間,24 小時制(HH:MM)。
- \u:目前使用者的使用者名稱(username)。
- \v:目前的 bash shell 版本(如 2.00)。
- \V:目前的 bash shell 詳細版本(如 2.00.0)。
- \w:目前的工作目錄完整路徑,若在 $HOME 中,則以 ~ 顯示。
- \W:目前的工作目錄名稱,若在 $HOME 中,則以 ~ 顯示。
- \!:目前指令的歷史紀錄編號。
- \#:目前指令的編號。
- \$:如果是 root 管理者,則顯示 #,否則顯示 $。
- \n:以八進位表示字元,例如 33。the character corresponding to the octal number nnn。
- \\:反斜線。
- [ 與 ]:當 PS1 參雜一些無法顯示的字元時,就要用這兩個特殊字元包起來,這樣顯示才會正常,例如所有控制顏色或是格式的控制碼,都要加上這兩個特殊字元,這樣可以避免 bash 在計算提示字元長度時出錯。
只要善用這些特殊字元,其實就可以讓自己的命令提示字串有許多的變化。
2022年2月4日 星期五
讓 U8G2 OLED 函式庫在 Arduino 中活用
快速連結
- u8g2 建構子 (Constrator)
- u8g2 在各類顯示器上的測試結果
- u8g2 函式庫載點 (GitHub)
- u8g2 支援字型列表
- u8g2 基本函式庫
- u8g2 繪圖函式庫
- u8g2 顯示配置函式庫
- u8g2 繪圖緩衝區函式庫
什麼是 u8g2?
大家也知道,OLED 規格相當多,常見的就有 SSD1306 和 SH1106,LCD 更不用說了,如果為了這些不同的顯示器去找對映的函式庫,有時還真的蠻累人的,而且還有 ESP8266 或 ESP32 支援的問題。
u8g2是嵌入式設備的單色圖形庫,一句話簡單明瞭。
還好有 u8g2 函式庫,它算是 u8glib 的新版本,它對顯示器的支援超強大的!舉凡常見的 OLED,像是 SSD1306 和 SH1106,或是其它各種 TFT LCD,都在它的支援範圍,真的是學一招就無敵了!
u8g2 支援單色 OLED 和 LCD,包括以下控制器:
SSD1305,SSD1306,SSD1309,SSD1322,SSD1325,SSD1327,SSD1329,SSD1606,SSD1607,SH1106,SH1107,SH1108,SH1122,T6963,RA8835,LC7981,PCD8544,PCF8812,HX1230 ,UC1601,UC1604,UC1608,UC1610,UC1611,UC1701,ST7565,ST7567,ST7588,ST75256,NT7534,IST3020,ST7920,LD7032,KS0108,SED1520,SBN1661,IL3820,MAX7219。
有關完整列表,請參見連結。
安裝、測試 u8g2 函式庫
先安裝 u8g2 函式庫,在程式庫管理員就能找到。
下載完畢,測試一下庫是否安裝成功:
#include <U8g2lib.h> void setup() { // put your setup code here, to run once: } void loop() { // put your main code here, to run repeatedly: }若編譯成功,證明已經加載了 u8g2 函式庫。這裡是 u8g2 官方提供的各種顯示器的測試圖與宣告語法,我挑選幾種手邊有的顯示器,看看 u8g2 的顯示效果與宣告語法:
- ESP32 and SSD1306 OLED
-
U8G2_SSD1306_128X64_NONAME_1_SW_I2C u8g2(U8G2_R2, /* clock=*/ 16, /* data=*/ 17, /* reset=*/ U8X8_PIN_NONE); // ESP32 Thing, pure SW emulated I2C U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R2, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ 16, /* data=*/ 17); // ESP32 Thing, HW I2C with pin remapping
- MAX7219 32x8 LED Matrix
-
The MAX7219 is a LED driver for one 8x8 LED matrix. Multiple 8x8 blocks can be connected together. Here added support for a 32x8 LED matrix.
U8x8 API is not supported, but the U8x8 fonts are also available for U8g2. The MAX7219 does not require a dc input signal, it has to be set to U8X8_PIN_NONE. The LOAD input line of the MAX7219 has to be connected as cs.
U8G2_MAX7219_32X8_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 11, /* data=*/ 12, /* cs=*/ 10, /* dc=*/ U8X8_PIN_NONE, /* reset=*/ U8X8_PIN_NONE);
- WEMOS D1 (ESP8266) OLED Shield
-
The 64x48 OLED Shield for the WEMOS ESP8266 boards. It features a SSD1306 driver and works with no special modification using the I2C bus (hardware or software):
U8G2_SSD1306_64X48_ER_F_HW_I2C u8g2(U8G2_R0); // hardware U8G2_SSD1306_64X48_ER_F_SW_I2C u8g2(U8G2_R0, D1, D2); // software
SW I2C and HW I2C with pin-remapping will work:
u8g2 庫函數
u8g2 庫函數可以分爲四大類:
- 基本函數
- 繪製相關函數
- 顯示配置相關函數
- 緩存相關函數
- 基本函數
u8g2.begin() —— 建構 u8g2
/** * 初始化 u8g2 庫 * @Note 關聯方法 initDisplay clearDisplay setPowerSave */ bool U8G2::begin(void) bool begin(void) { /* note: call to u8x8_utf8_init is not required here, this is done in the setup procedures before */ initDisplay(); //初始化顯示器 clearDisplay(); // 重置清屏 setPowerSave(0); //喚醒屏幕 return 1; }u8g2.beginSimple() —— 建構 u8g2
/** * 初始化 u8g2 庫 * @Note 關聯方法 initDisplay clearDisplay setPowerSave */ bool U8G2::beginSimple(void) bool beginSimple(void) { /* does not clear the display and does not wake up the display */ /* user is responsible for calling clearDisplay() and setPowerSave(0) */ initDisplay(); //初始化顯示器 }註:
可以看到和 begin() 函數的區別,需要用戶自行控制初始化過程,給了一定的自由度,不過建議還是直接用 begin 函數吧。u8g2.clearDisplay() —— 清除屏幕內容
/** * 清除屏幕內容 */ void U8G2::clearDisplay(void)註:
① 這個方法不需要我們單獨調用,會在 begin 函數主動調用一次,我們主要理解即可。
② 不要在 firstPage 和 nextPage 函數之間調用該方法。u8g2.setPowerSave() —— 是否開啓省電模式
/** * 清除顯示緩衝區 * @param is_enable * 1 表示啓用顯示器的省電模式,屏幕上看不到任何東西 * 0 表示禁用省電模式 */ void U8G2::setPowerSave(uint8_t is_enable)註:
① 不管是啓用還是禁用,顯示器需要的內存消耗是不會變的,說到底就是爲了關閉屏幕,做到省電。
② 所以這裏就可以理解爲什麼初始化需要 setPowerSave(0); 。u8g2.clear() —— 清除操作
/** * 清除屏幕顯示,清除緩衝區,光標回到左上角原點位置(0,0) * @Note 關聯方法 home clearDisplay clearBuffer */ void U8G2::clear(void) void clear(void) { home(); //回到原點 clearDisplay(); //清除屏幕上的顯示 clearBuffer(); //清除緩衝區 }u8g2.clearBuffer() —— 清除緩衝區
/** * 清除內存中數據緩衝區 */ void U8G2::clearBuffer(void)
註:
一般這個函數是與 sendBuffer 函數配對使用,通常用法如下:void loop(void) { u8g2.clearBuffer(); // ... write something to the buffer u8g2.sendBuffer(); delay(1000); }u8g2.disableUTF8Print() —— 禁用 UTF8 字型顯示
/** * 禁用 Arduino 平臺下輸出 UTF8 字符集,默認是開啓 */ void U8G2::disableUTF8Print(void)u8g2.enableUTF8Print() —— 啟用 UTF8 字型顯示
/** * 開啟 Arduino 平臺下輸出 UTF8 字符集 */ void U8G2::enableUTF8Print(void)註:
我們的中文字符就是 UTF8。常見範例如下:void setup(void) { u8g2.begin(); u8g2.enableUTF8Print(); // enable UTF8 support for the Arduino print() function } void loop(void) { u8g2.setFont(u8g2_font_unifont_t_chinese2); u8g2.firstPage(); do { u8g2.setCursor(0, 40); u8g2.print("你好世界"); } while ( u8g2.nextPage() ); delay(1000); }u8g2.home() —— 重置顯示游標的位置
/** * 重置顯示游標的位置,回到原點(0,0) * @Note 關聯方法 print clear */ void U8G2::home(void)- 繪製相關函數
u8g2.drawBox() —— 畫實心方形
/** * 畫實心方形,左上角座標爲 (x,y), 寬度爲 w,高度爲 h * @param x 左上角的 x 座標 * @param y 左上角的 y 座標 * @param w 方形的寬度 * @param h 方形的高度 * @Note 關聯方法 drawFrame setDrawColor */ void U8G2::drawBox(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h)註:
如果支援繪製顏色(也就是不是單色顯示器),那麼由 setDrawColor 設置,請看下例:u8g2.drawBox(3,7,25,15);u8g2.drawCircle() —— 畫空心圓
/** * 畫空心圓,圓心座標爲 (x0,y0), 半徑爲 rad * @param x0 圓點的 x 座標 * @param y0 圓點的 y 座標 * @param rad 圓形的半徑 * @param opt 圓形選項 * U8G_DRAW_ALL 整個圓 * U8G2_DRAW_UPPER_RIGHT 右上部分的圓弧 * U8G2_DRAW_UPPER_LEFT 左上部分的圓弧 * U8G2_DRAW_LOWER_LEFT 左下部分的圓弧 * U8G2_DRAW_LOWER_RIGHT 右下部分的圓弧 * 選項可以通過 | 操作符來組合 * @Note 關聯方法 drawDisc setDrawColor */ void U8G2::drawCircle(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t opt = U8G2_DRAW_ALL)註:
① 如果支持繪製顏色(也就是不是單色顯示器),那麼由 setDrawColor 設置。
② 直徑等於 2 × rad + 1,範例如下:u8g2.drawCircle(20, 25, 10, U8G2_DRAW_ALL);u8g2.drawDisc() —— 畫實心圓
/** * 畫空心圓,圓心座標爲 (x0,y0), 半徑爲 rad * @param x0 圓點的 x 座標 * @param y0 圓點的 y 座標 * @param rad 圓形的半徑 * @param opt 圓形選項 * U8G_DRAW_ALL 整個圓 * U8G2_DRAW_UPPER_RIGHT 右上部分的圓弧 * U8G2_DRAW_UPPER_LEFT 左上部分的圓弧 * U8G2_DRAW_LOWER_LEFT 左下部分的圓弧 * U8G2_DRAW_LOWER_RIGHT 右下部分的圓弧 * 選項可以通過 | 操作符來組合 * @Note 關聯方法 drawDisc setDrawColor */ void U8G2::drawDisc(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t opt = U8G2_DRAW_ALL)u8g2.drawEllipse() —— 畫空心橢圓
/** * 畫空心圓,圓心座標爲 (x0,y0), 半徑爲 rad * @param x0 圓點的 x 座標 * @param y0 圓點的 y 座標 * @param rx 橢圓形水平 x 方向的半徑 * @param ry 橢圓形豎直 y 方向的半徑 * @param opt 圓形選項 * U8G_DRAW_ALL 整個圓 * U8G2_DRAW_UPPER_RIGHT 右上部分的圓弧 * U8G2_DRAW_UPPER_LEFT 左上部分的圓弧 * U8G2_DRAW_LOWER_LEFT 左下部分的圓弧 * U8G2_DRAW_LOWER_RIGHT 右下部分的圓弧 * 選項可以通過 | 操作符來組合 * @Note 關聯方法 drawDisc setDrawColor */ void U8G2::drawEllipse()(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rx, u8g2_uint_t ry, uint8_t opt = U8G2_DRAW_ALL)註:
① rx × ry 在 8 位模式的 u8g2 必須小於 512(目前暫沒有理解為什麼)。
② 範例如下:u8g2.drawEllipse(20, 25, 15, 10, U8G2_DRAW_ALL);u8g2.drawFrame() —— 畫空心方形
/** * 畫實心方形,左上角座標爲 (x,y), 寬度爲 w,高度爲 h * @param x 左上角的 x 座標 * @param y 左上角的 y 座標 * @param w 方形的寬度 * @param h 方形的高度 * @Note 關聯方法 setDrawColor */ void U8G2::drawFrame(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h)註:
如果支援繪製顏色(也就是不是單色顯示器),那麼由 setDrawColor 設置,請看下例:u8g2.drawFrame(3,7,25,15);u8g2.drawGlyph() —— 繪製字體字集的符號
/** * 繪製字體字集裏面定義的符號 * @param x 左上角的 x 座標 * @param y 左上角的 y 座標 * @param encoding 字符的 unicode 值 * @Note 關聯方法 setFont */ void U8G2::drawGlyph(u8g2_uint_t x, u8g2_uint_t y, uint16_t encoding)註:
① u8g2 支持 16 位以內的 unicode 字符集,也就是說 encoding 的範圍爲 0-65535,drawGlyph 方法只能
繪製存在於所使用的字體字集中的 unicode 值。
② 這個繪製方法依賴於當前的字體模式和繪製顏色。u8g2.drawHLine() —— 繪製水平線
/** * 繪製水平線 * @param x 左上角的 x 座標 * @param y 左上角的 y 座標 * @param w 水平線的長度 * @Note 關聯方法 setDrawColor */ void U8G2::drawHLine(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w)註:
如果支持繪製顏色(也就是不是單色顯示器),那麼由 setDrawColor 設置。u8g2.drawLine() —— 兩點之間繪製線
/** * 繪製線,從座標 (x0,y0) 到 (x1,y1) * @param x0 端點 0 的 x 座標 * @param y0 端點 0 的 y 座標 * @param x1 端點 1 的 x 座標 * @param y1 端點 1 的 y 座標 * @Note 關聯方法 setDrawColor */ void U8G2::drawLine(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t x1, u8g2_uint_t y1)註:
如果支持繪製顏色(也就是不是單色顯示器),那麼由 setDrawColor 設置。請見範例:u8g2.drawLine(20, 5, 5, 32);u8g2.drawPixel() —— 繪製像素點
/** * 繪製像素點,座標 (x,y) * @param x 像素點的 x 座標 * @param y 像素點的 y 座標 * @Note 關聯方法 setDrawColor */ void U8G2::drawPixel(u8g2_uint_t x, u8g2_uint_t y)註:
① 如果支持繪製顏色(也就是不是單色顯示器),那麼由 setDrawColor 設置。
② 你會發現很多繪製方法的底層都是調用drawPixel,畢竟像素屬於最小顆粒度。
③ 我們可以利用這個繪製方法自定義自己的圖形顯示。u8g2.drawRBox() —— 繪製圓角實心方形
/** * 繪製圓角實心方形,左上角座標爲 (x,y), 寬度爲 w,高度爲 h,圓角半徑爲 r * @param x 左上角的 x 座標 * @param y 左上角的 y 座標 * @param w 方形的寬度 * @param h 方形的高度 * @param r 圓角半徑 */ void U8G2::drawRBox(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h, u8g2_uint_t r)註:
① 如果支持繪製顏色(也就是不是單色顯示器),那麼由 setDrawColor 設置。
② 要求,w >= 2 × (r+1) 並且 h >= 2 × (r+1),這是顯而易見的限制。u8g2.drawRFrame() —— 繪製圓角空心方形
/** * 繪製圓角實心方形,左上角座標爲 (x,y), 寬度爲 w,高度爲 h,圓角半徑爲 r * @param x 左上角的 x 座標 * @param y 左上角的 y 座標 * @param w 方形的寬度 * @param h 方形的高度 * @param r 圓角半徑 */ void U8G2::drawRFrame(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h, u8g2_uint_t r)註:
① 如果支持繪製顏色(也就是不是單色顯示器),那麼由 setDrawColor 設置。
② 要求,w >= 2 × (r+1) 並且 h >= 2 × (r+1),這是顯而易見的限制。請見以下範例:u8g2.drawRFrame(20,15,30,22,7);u8g2.drawStr() —— 繪製字符串
/** * 繪製字符串 * @param x 左上角的 x 座標 * @param y 左上角的 y 座標 * @param s 繪製字符串內容 * @return 字符串的長度 */ u8g2_uint_t U8g2::drawStr(u8g2_uint_t x, u8g2_uint_t y, const char *s)註:
① 需要先設置字體,調用 setFont 方法。
② 這個方法不能繪製 encoding 超過 256 的,超過 256 需要用 drawUTF8 或者 drawGlyph;說白了就是一般
用來顯示英文字符。
③ x,y 屬於字符串左下角的座標。請見範例:u8g2.setFont(u8g2_font_ncenB14_tr); u8g2.drawStr(0,15,"Hello World!");u8g2.drawTriangle() —— 繪製實心三角形
/** * 繪製實心三角形,定點座標分別爲 (x0,y0), (x1,y1), (x2,y2) */ void U8G2::drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2)請見範例:
u8g2.drawTriangle(20,5, 27,50, 5,32);u8g2.drawUTF8() —— 繪製UTF8編碼的字符
/** * 繪製UTF8編碼的字符串 * @param x 字符串在屏幕上的左下角 x 座標 * @param y 字符串在屏幕上的左下角 y 座標 * @param s 需要繪製的 UTF-8 編碼字符串 * @return 返回字符串的長度 */ u8g2_uint_t U8g2::drawUTF8(u8g2_uint_t x, u8g2_uint_t y, const char *s)註:
① 使用該方法,有兩個前提。首先是你的編譯器需要支持 UTF-8 編碼,對於絕大部分 Arduino 板子已經支持。
② 其次,顯示的字符串需要存爲 “UTF-8” 編碼,Arduino IDE 上默認支援。
③ 該方法需要依賴於 fontMode(setFont)以及 drawing Color,也就是說如果你傳進來的字符串編碼必須
在 font 定義裏面。請見範例:u8g2.setFont(u8g2_font_unifont_t_symbols); u8g2.drawUTF8(5, 20, "Snowman: ☃");u8g2.drawVLine() —— 繪製豎直線
/** * 繪製豎直線 * @param x 左上角座標 x * @param y 左上角座標 y * @param h 高度 */ void U8G2::drawVLine(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t h)u8g2.drawXBM()/drawXBMP() —— 繪製圖像
/** * 繪製圖像 * @param x 左上角座標 x * @param y 左上角座標 y * @param w 圖形寬度 * @param h 圖形高度 * @param bitmap 圖形內容 * @Note 關聯方法 setBitmapMode */ void U8G2::drawXBM(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h, const uint8_t *bitmap) void U8G2::drawXBMP(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h, const uint8_t *bitmap)註:
drawXBM 和 drawXBMP 區別在於 XBMP 支持 PROGMEM。u8g2.firstPage() / nextPage() —— 繪製命令
/** * 繪製圖像 */ void U8G2::firstPage(void) uint8_t U8G2::nextPage(void)註:
① firstPage 方法會把當前頁碼位置變成 0。
② 修改內容處於 firstPage 和 nextPage 之間,每次都是重新渲染所有內容。
③ 該方法消耗的 ram 空間,比 sendBuffer 消耗的 ram 空間要少。請見範例:u8g2.firstPage(); do { /* all graphics commands have to appear within the loop body. */ u8g2.setFont(u8g2_font_ncenB14_tr); u8g2.drawStr(0,20,"Hello World!"); } while ( u8g2.nextPage() );程式碼解析:
void u8g2_FirstPage(u8g2_t *u8g2) { if ( u8g2->is_auto_page_clear ) { // 清除緩衝區 u8g2_ClearBuffer(u8g2); } // 設置當前緩衝區的 Tile Row 一個 Tile 等於 8 個像素點的高度 u8g2_SetBufferCurrTileRow(u8g2, 0); } uint8_t u8g2_NextPage(u8g2_t *u8g2) { uint8_t row; u8g2_send_buffer(u8g2); row = u8g2->tile_curr_row; row += u8g2->tile_buf_height; if ( row >= u8g2_GetU8x8(u8g2)->display_info->tile_height ) { // 如果 row 已經到達最後一行,觸發 refreshDisplay 調用,表示整個頁面已經刷完了 u8x8_RefreshDisplay( u8g2_GetU8x8(u8g2) ); return 0; } if ( u8g2->is_auto_page_clear ) { // 清除緩衝區 u8g2_ClearBuffer(u8g2); } // 不斷更新 TileRow 這是非常關鍵的一步 u8g2_SetBufferCurrTileRow(u8g2, row); return 1; }u8g2.print() —— 繪製內容
/** * 繪製內容 * @Note 關聯方法 setFont setCursor enableUTF8Print */ void U8G2::print(...)請見範例:
u8g2.setFont(u8g2_font_ncenB14_tr); u8g2.setCursor(0, 15); u8g2.print("Hello World!");u8g2.sendBuffer() —— 繪製緩衝區的內容
/** * 繪製緩衝區的內容 * @Note 關聯方法 clearBuffer */ void U8G2::sendBuffer(void)註:
① sendBuffer 的 RAM 佔用空間大,需要結合構造器的 buffer 選項(請繼續往下看,先有個概念)使用。
② 不管是 fistPage、nextPage 還是 sendBuffer,都涉及到一個叫做 current page position 的概念。
③ 程式碼解析:void u8g2_SendBuffer(u8g2_t *u8g2) { u8g2_send_buffer(u8g2); u8x8_RefreshDisplay( u8g2_GetU8x8(u8g2) ); } static void u8g2_send_tile_row(u8g2_t *u8g2, uint8_t src_tile_row, uint8_t dest_tile_row) { uint8_t *ptr; uint16_t offset; uint8_t w; w = u8g2_GetU8x8(u8g2)->display_info->tile_width; offset = src_tile_row; ptr = u8g2->tile_buf_ptr; offset *= w; offset *= 8; ptr += offset; u8x8_DrawTile(u8g2_GetU8x8(u8g2), 0, dest_tile_row, w, ptr); } /* write the buffer to the display RAM. For most displays, this will make the content visible to the user. Some displays (like the SSD1606) require a u8x8_RefreshDisplay() */ static void u8g2_send_buffer(u8g2_t *u8g2) U8X8_NOINLINE; static void u8g2_send_buffer(u8g2_t *u8g2) { uint8_t src_row; uint8_t src_max; uint8_t dest_row; uint8_t dest_max; src_row = 0; src_max = u8g2->tile_buf_height; dest_row = u8g2->tile_curr_row; dest_max = u8g2_GetU8x8(u8g2)->display_info->tile_height; do { u8g2_send_tile_row(u8g2, src_row, dest_row); src_row++; dest_row++; } while ( src_row < src_max && dest_row < dest_max ); }請見範例:
void loop(void) { u8g2.clearBuffer(); // ... write something to the buffer u8g2.sendBuffer(); delay(1000); }- 顯示配置相關函數
u8g2.getAscent() —— 獲取基準線以上的高度
/** * 獲取基準線以上的高度 * @return 返回高度值 * @Note 關聯方法 setFont getDescent setFontRefHeightAll */ int8_t U8G2::getAscent(void)註:
① 跟字體有關(setFont)。
② 請看範例,下面例子,ascent 是 18:u8g2.getDescent() —— 獲取基準線以下的高度
/** * 獲取基準線以上的高度 * @return 返回高度值 * @Note 關聯方法 setFont getDescent setFontRefHeightAll */ int8_t U8G2::getDescent(void)註:
① 跟字體有關(setFont)。
② 請看範例,下面例子,descent 是 -5:u8g2.getDisplayHeight() —— 獲取顯示器的高度
/** * 獲取顯示器的高度 * @return 返回高度值 */ u8g2_uint_t getDisplayHeight(void)u8g2.getDisplayWidth() —— 獲取顯示器的寬度
/** * 獲取顯示器的寬度 * @return 返回寬度值 */ u8g2_uint_t getDisplayWidth(void)u8g2.getMaxCharHeight() —— 獲取當前字體裏的最大字符的高度
/** * 獲取當前字體裏的最大字符的高度 * @return 返回高度值 * @Note 關聯方法 setFont */ u8g2_uint_t getMaxCharHeight(void)註:
每一個字符在 font 字集中都是一個位圖,位圖有高度和寬度。u8g2.getMaxCharWidth() —— 獲取當前字體裏的最大字符的寬度
/** * 獲取當前字體裏的最大字符的寬度 * @return 返回寬度值 * @Note 關聯方法 setFont */ u8g2_uint_t getMaxCharWidth(void)註:
每一個字符在 font 字集中都是一個位圖,位圖有高度和寬度。u8g2.getStrWidth() —— 獲取字符串的像素寬度
/** * 獲取字符串的像素寬度 * @param s 繪製字符串 * @return 返回字符串的像素寬度值 * @Note 關聯方法 setFont drawStr */ u8g2_uint_t U8G2::getStrWidth(const char *s)註:
像素寬度和當前 font 字體有關。u8g2.getUTF8Width() —— 獲取 UTF-8 字符串的像素寬度
/** * 獲取 UTF-8 字符串的像素寬度 * @param s 繪製字符串 * @return 返回字符串的像素寬度值 * @Note 關聯方法 setFont drawStr */ u8g2_uint_t U8G2::getUTF8Width(const char *s)註:
像素寬度和當前 font 字體有關。u8g2.setAutoPageClear() —— 設置自動清除緩衝區
/** * 是否自動清除緩衝區 * @param mode 0 表示關閉 * 1 表示開啓,默認是開啓 */ void U8G2::setAutoPageClear(uint8_t mode)註:
① 用於 firstPage 和 nextPage(看上面的程式碼解析)。
② 建議方法保持默認就好,如果用戶禁止了,那麼需要自己維護緩衝區的狀態或者手動調用 clearBuffer。u8g2.setBitmapMode() —— 設置位圖模式
/** * 設置位圖模式(定義 drawXBM 方法是否繪製背景顏色) * @param is_transparent * 0 繪製背景顏色,不透明,默認是該值 * 1 不繪製背景顏色,透明 * @Note 關聯方法 drawXBM */ void U8G2::setBitmapMode(uint8_t is_transparent)請看範例一:
u8g2.setDrawColor(1); u8g2.setBitmapMode(0); u8g2.drawXBM(4,3, u8g2_logo_97x51_width, u8g2_logo_97x51_height, u8g2_logo_97x51_bits); u8g2.drawXBM(12,11, u8g2_logo_97x51_width, u8g2_logo_97x51_height, u8g2_logo_97x51_bits);請看範例二:
u8g2.setDrawColor(1); u8g2.setBitmapMode(1); u8g2.drawXBM(4,3, u8g2_logo_97x51_width, u8g2_logo_97x51_height, u8g2_logo_97x51_bits); u8g2.drawXBM(12,11, u8g2_logo_97x51_width, u8g2_logo_97x51_height, u8g2_logo_97x51_bits);u8g2.setBusClock() —— 設置 I2C、SPI 頻率 (clock speed in HZ)
/** * 設置總線時鐘 (I2C、SPI) * @param mode clock_speed 總線時鐘頻率(Hz) * @Note 關聯方法 begin */ void U8G2::setBusClock(uint32_t clock_speed);註:
① 僅僅 Arduino 平臺支援。
② 必須在 u8g2.begin() 或者 u8g2.initDisplay() 之前呼叫。u8g2.setClipWindow() —— 設置顯示窗口大小
/** * 設置顯示窗口,窗口範圍從左上角 (x0,y0) 到右下角 (x1,y1) * 也就是我們繪製的內容只能在規範範圍內顯示 * @param x0 左上角 x 座標 * @param y0 左上角 y 座標 * @param x1 右上角 x 座標 * @param y1 右上角 y 座標 * @Note 關聯方法 begin */ void U8G2::setClipWindow(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t x1, u8g2_uint_t y1 );註:
可以通過 setMaxClipWindow 去掉該限制。void U8G2::setMaxClipWindow(void)請看範例:
u8g2.setClipWindow(10, 10, 85, 30); u8g2.setDrawColor(1); u8g2.drawStr(3, 32, "U8g2");u8g2.setCursor() —— 設置繪製游標位置
/** * 設置繪製游標位置 (x,y) * @Note 關聯方法 print */ void U8G2::setCursor(u8g2_uint_t x, u8g2_uint_t y)請看範例:
u8g2.setFont(u8g2_font_ncenB14_tr); u8g2.setCursor(0, 15); u8g2.print("Hello World!");u8g2.setDisplayRotation() —— 設置顯示器的旋轉角度
/** * 設置顯示器的旋轉角度 * @param u8g2_cb 旋轉選項 * U8G2_R0 不做旋轉 水平 * U8G2_R1 旋轉 90 度 * U8G2_R2 旋轉 180 度 * U8G2_R3 旋轉 270 度 * U8G2_MIRROR 不做旋轉 水平,顯示內容是鏡像的,暫時不理解 */ void setDisplayRotation(const u8g2_cb_t *u8g2_cb)u8g2.setDrawColor() —— 設置繪製顏色
/** * 設置繪製顏色(暫時還沒有具體去了解用法) */ void U8G2::setDrawColor(uint8_t color)u8g2.setFont() —— 設置使用字體
/** * 設置字體集(字體集用於字符串繪製方法或者 glyph 繪製方法) * @param font 具體的字體集 * @Note 關聯方法 drawUTF8 drawStr drawGlyph print */ void U8G2::setFont(const uint8_t *font)Font 會根據像素點高度做了很多區分,具體 font 請參考:連結。
如果我們需要用到中文字符,可以在上面的連結裏面搜索一下 chinese,你就會發現很多中文 font,比如:(以下字型均支援 UTF-8 或者 GB2312 編碼)
u8g2_font_wqy15_t_chinese1
u8g2_font_wqy15_t_chinese2
u8g2_font_wqy15_t_chinese3
u8g2_font_wqy12_t_gb2312
u8g2_font_wqy12_t_gb2312a
......註:
中文字符集消耗記憶體大,請謹慎使用,可以用在 Arduino 101 等 RAM 空間比較大的板子上。我們看看 Font 的命名規則:
<prefix> '_font_' <name> '_' <purpose> <char set>例如:
u8g2.setFont( u8g2_font_5x7_tr );
u8ge.setFont( u8g2_font_pressstart2p_8u );其中:
- prefix 基本上都是 u8g2
- name 一般會掛鉤上字符像素使用量,比如 5X7
- purpose
t:Transparent font, Do not use a background color.
h:All glyphs have common height (所有的圖形有通用的高度).
m:All glyphs have common height and width (monospace).
8:All glyphs fit into a 8x8 pixel box.- char set
f:The font includes up to 256 glyphs.
r:Only glyphs on the range of the ASCII codes 32 to 127 are
included in the font.
u:Only glyphs on the range of the ASCII codes 32 to 95 (uppercase
hars) are included in the font.
n:Only numbers and extra glyphs for writing date and time strings
are included in the font.
…:Other custom character list.
註:
u8g2 庫提供的 font 非常多,各位也暫時消化不了太多。如果我們使用中文的話,就去看看中文 font 就好。u8g2.setFontDirection() —— 設置字體方向
/** * 定義字符串繪製或者圖形繪製的方向 * @param dir 方向 * @param 關聯方法 drawStr */ void U8G2::setFontDirection(uint8_t dir)dir 的參數說明
Argument String Rotation Description 0 0 degree Left to right 1 90 degree Top to down 2 180 degree Right to left 3 270 degree Down to top 請看範例:
u8g2.setFont(u8g2_font_ncenB14_tfå); u8g2.setFontDirection(0); u8g2.drawStr(15, 20, "Abc"); u8g2.setFontDirection(1); u8g2.drawStr(15, 20, "Abc");- 緩存相關函數
緩存相關函數,一般不會去操作,瞭解即可。
u8g2.getBufferPtr() —— 獲取緩存空間的地址
/** * 獲取緩存空間的地址 * @return 返回緩存空間起始地址 * @Note 關聯方法 getBufferTileHeight, getBufferTileWidth, clearBuffer */ uint8_t *U8G2::getBufferPtr(void)註:
緩存大小等於 8 × u8g2.getBufferTileHeight() × u8g2.getBufferTileWidth()。u8g2.getBufferTileHeight() —— 獲取緩衝區的 Tile 高度
/** * 獲取緩衝區的 Tile 高度 * @return 返回高度值 */ uint8_t U8G2::getBufferTileHeight(void)註:
一個 tile 等於 8 個像素點。u8g2.getBufferTileWidth() —— 獲取緩衝區的 Tile 寬度
/** * 獲取緩衝區的 Tile 寬度 * @return 返回寬度值 */ uint8_t U8G2::getBufferTileWidth(void)註:
一個 tile 等於 8 個像素點。u8g2.getBufferCurrTileRow() —— 獲取緩衝區的當前 Tile row
/** * 獲取緩衝區的當前 Tile row 行數 * @return 返回當前的 tilerow */ uint8_t U8G2::getBufferCurrTileRow(void)註:
這個方法跟我們上面說到的 page position 相關。u8g2.setBufferCurrTileRow() —— 設置緩衝區的當前 Tile row
/** * 設置緩衝區的當前 Tile row * @param 當前的 Tile row */ uint8_t U8G2::setBufferCurrTileRow(uint8_t row)註:
在 firstPage/nextPage 循環時,由於底層調用了 setBufferCurrTileRow,所以儘量不要自己手動調用該方法。
請看範例:u8g2.setBufferCurrTileRow(0); // let y=0 be the topmost row of the buffer u8g2.clearBuffer(); u8g2.setFont(u8g2_font_helvB08_tr); u8g2.drawStr(2, 8, "abcdefg"); u8g2.setBufferCurrTileRow(2); // write the buffer to tile row 2 (y=16) on the display u8g2.sendBuffer(); u8g2.setBufferCurrTileRow(4); // write the same buffer to tile row 4 (y=32) on the display u8g2.sendBuffer();
如何運用 u8g2 函式庫
前面介紹到 u8g2 適配了絕大部分的 OLED,那麼我們如何構建具體的 OLED 驅動呢?可分爲以下幾個順序步驟:
- 區分顯示器:
首先,你需要知道 OLED 顯示器的控制器型號以及屏幕大小。舉個例子,我手上有一塊 SSD1306 128×64 的 OLED,那麼它的控制器就是 SSD1306,螢幕大小是 128×64。
其次,你所選擇的 OLED 必須在 u8g2 庫所支持的 OLED 列表中,具體可參考:這個網址。
- 確認物理連線介面:
圖像信息是通過物理連接線傳送給 OLED 顯示器。通常,我們的連謝種類包括:
- 3SPI,3-wire SPI:串行外圍接口,依靠三個控制信號,Clock、Data、CS
- 4SPI, 4-Wire SPI:跟 3SPI 一樣,只是多了一條數據命令線,經常叫做 D/C
- I2C, IIC or TWI:SCL、SDA
- 8080:A 8-Bit bus which requires 8 data lines
- 6800:Another 8-Bit bus, but with a different protocol
具體的 OLED 使用怎樣的連線,我們需要查閱設備手冊。比如,SSD1306 就是 I2C。
- 確認連線引腳:
知道了物理連線模式之後,我們一般都是把 OLED 連接到 Arduino Board 的輸出引腳,也就是軟件模擬通訊協議。當然,如果有現成的物理連線介面那就更好了。
如:Arduino 的 A4 即為 I2C 的 SDA、A5 即為 I2C 的 SCL- u8g2 初始化:
經歷以上三步之後,我們就可以開始初始化出具體的 OLED 驅動了。比如,IIC SSD1306 128X64 的 OLED,就可以用以下初始化建構子(Constrator):
U8G2_SSD1306_128X64_NONAME_1_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE); // All Boards without Reset of the Display當然 SSD1306 還有其他的建構子,如:
OLED : SSD1306 / 128×64 U8G2_SSD1306_128X64_NONAME_1_4W_SW_SPI(rotation, clock, data, cs, dc [, reset])
page buffer size : 128 bytesU8G2_SSD1306_128X64_NONAME_2_4W_SW_SPI(rotation, clock, data, cs, dc [, reset])
page buffer size : 256 bytesU8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI(rotation, clock, data, cs, dc [, reset])
page buffer size : 1024 bytesU8G2_SSD1306_128X64_NONAME_1_4W_HW_SPI(rotation, cs, dc [, reset])
page buffer size : 128 bytes看到這裡,我們需要重點講述一下建構子(Constrator)的規則。建構子的名字由以下幾方面組合而成,它們之間使用 "_" 連接起來。
No Description Example 1 Prefix U8G2 2 Display Controller SSD1306 3 Display Name 128X64_NONAME 4 Buffer Size 1:保持一頁的緩衝區,用於 firstPage/nextPage 的 PageMode
2:保持兩頁的緩衝區,用於 firstPage/nextPage 的 PageMode
F:獲取整個屏幕的緩衝區,RAM 消耗大,一般用在 RAM 空間
比較大的 arduino 板子5 Communication 4W_SW_SPI:
4-wire (clock, data, cs and dc) software emulated SPI
4W_HW_SPI:
4-wire (clock, data, cs and dc) hardware SPI (based on Arduino SPI library)
2ND_4W_HW_SPI:
If supported, second 4-wire hardware SPI (based on Arduino SPI library)
3W_SW_SPI:
3-wire (clock, data and cs) software emulated SPI
SW_I2C:
Software emulated I2C/TWI
HW_I2C:
Hardware I2C based on the Arduino Wire library
2ND_HW_I2C:
If supported, use second hardware I2C (Arduino Wire lib)
6800:
8-bit parallel interface, 6800 protocol
8080:
8-bit parallel interface, 8080 protocol
建構子參數的第一個項目為 Rotation,其意義是指定整個螢幕是否要在旋轉的狀態下顯示內容,其詳細定義是:
Rotation / Morror Descriptiomn U8G2_R0 No rotation, landscape U8G2_R1 90 degree clockwise rotation U8G2_R2 180 degree clockwise rotation U8G2_R3 270 degree clockwise rotation U8G2_MIRROR No rotation, landscape, display content is mirrored (v2.6.x) 所以,一個完整的例子爲:
#include <Arduino.h> #include <U8g2lib.h> #include <SPI.h> #include <Wire.h> U8G2_ST7920_128X64_1_SW_SPI u8g2(U8G2_R0, 13, 11, 10, 8); void setup(void) { u8g2.begin(); } void loop(void) { u8g2.firstPage(); do { u8g2.setFont(u8g2_font_ncenB14_tr); u8g2.drawStr(0,24,"Hello World!"); } while ( u8g2.nextPage() ); }- u8g2 繪製模式:
u8g2 支援三種繪製模式:
- Full screen buffer mode(全屏緩存模式)
特點:繪製速度快、所有的繪製方法都可以使用、需要大量的 RAM 空間。
建構子:必須帶有 F,比如:
U8G2_ST7920_128X64_F_SW_SPI(rotation, clock, data, cs [, reset])
用法:
- 清除緩衝區 u8g2.clearBuffer()
- 呼叫繪製方法
- 發送緩衝區的內容到顯示器 u8g2.sendBuffer()
範例程式:
void setup(void) { u8g2.begin(); } void loop(void) { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_ncenB14_tr); u8g2.drawStr(0,20,"Hello World!"); u8g2.sendBuffer(); }- Page mode(分頁模式:This is the U8glib picture loop)
特點:繪製速度慢、所有的繪製方法都可以使用、需要少量的 RAM 空間。
建構子:必須帶有 1 或 2,比如:
U8G2_ST7920_128X64_1_SW_SPI(rotation, clock, data, cs [, reset])
用法:
- 呼叫 u8g2.firstPage()
- 開始一個 do while 循環
- 在循環內部呼叫繪製方法
- 不斷判斷 u8g2.nextPage()
範例程式:
void setup(void) { u8g2.begin(); } void loop(void) { u8g2.firstPage(); do { u8g2.setFont(u8g2_font_ncenB14_tr); u8g2.drawStr(0,24,"Hello World!"); } while ( u8g2.nextPage() ); }- U8x8 character only mode(僅僅支援普通字符)
特點:繪製速度快、並不是對所有的顯示器都有效、圖形繪製不可用、不需要 RAM 空間。
建構子:使用 U8X8 建構子,比如:
U8X8_ST7565_EA_DOGM128_4W_SW_SPI(clock, data, cs, dc [, reset])
範例程式:
void setup(void) { u8x8.begin(); } void loop(void) { u8x8.setFont(u8x8_font_chroma48medium8_r); u8x8.drawString(0,1,"Hello World!"); }