玩转u8g2 OLED库,一篇就够
1.前言
????最近博主聽到QQ群里面問得比較多的問題:
????“博哥,有玩過OLED嗎?”
????“博哥,有試過在ESP8266上調成功過SSD1306嗎?”
????“博哥,OLED上顯示天氣信息怎么弄?”
????........
????諸如此類的問題,在博主看來,無非都是對OLED庫用法的不熟悉甚至不了解。Arduino OLED庫眾多,博主也曾經介紹過一款 《博哥OLED系列》- 玩轉SSD1306-12864 OLED Adafruit_GFX 和 Adafruit_SSD1306庫。但是,博主今天要介紹的是目前Arduino平臺上使用最廣泛的OLED庫 —— U8G2庫,也是很多初學者容易懵逼的一個第三方庫。目前在github上超過1K star,1800次commit,可以說維護熱度很高,讀者大可放心使用。
????至于這個庫為什么這么火爆,請讀者認真看完本博文自行評價,博主只能說功能真的很好很強大!!!!
????那么,博主首先立個flag,閱讀完本篇博文的最終效果——讀者以后在Arduino平臺上開發OLED項目的時候,可以考慮U8G2庫,并且知道U8G2庫的具體用法,甚至可以能夠當做工具書來翻閱。
????進入正文之前,博主要給讀者灌輸兩個個非常重要的知識點:
- 其一 像素點點陣
- 其二 坐標系
????在坐標系中,左上角是原點,向右是X軸,向下是Y軸。
????希望讀者能仔細理解這兩個知識點。
2.U8g2簡介
2.1 U8g2是什么
????U8g2是嵌入式設備的單色圖形庫,一句話簡單明了。主要應用于嵌入式設備,包括我們常見的單片機;
2.2 U8g2支持的顯示控制器
????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(有關完整列表,請參見 此處)。
????可以說,基本上主流的顯示控制器都支持,比如我們常見的SSD1306 12864,讀者在使用該庫之前請查閱自己的OLED顯示控制器是否處于支持列表中。
2.2 U8g2支持的Arduino主板
????可以說基本上所有Arduino API的主板都得到U8g2的支持。包括:
- Aruino Zero,Uno,Mega,Due,101,MKR Zero以及所有其他Arduino官方主板
- 基于Arduino平臺的STM32
- 基于Arduino平臺的ESP8266和ESP32
- 甚至其他不知名的基于Arduino平臺的開發板
????所以說,讀者完全不用擔心兼容性問題,放心使用。
2.3 U8g2如何在Arduino平臺上安裝
????Arduino庫U8g2可以從Arduino IDE的庫管理器安裝,讀者在庫管理器搜索“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庫。
2.4 U8g2的優勢
????為什么要運用U8g2庫?也就是說U8g2庫能帶給我們什么樣的開發便利。在博主看來,主要考慮幾個方面:
- U8g2庫平臺支持性好,基本上支持絕大部分Arduino開發板,特別也博主比較喜歡的ESP8266;
- U8g2庫顯示控制器支持性好,基本上市面上的OLED都完美支持;
- U8g2庫 API眾多,特別支持了中文,支持了不同字體,這是一個對于開發者倆說不小的福利。
????以下是官方提供的一些U8G2庫測試圖,博主挑選了幾張常見的以及附上U8G2庫的配置(讀者可以先不用理會配置,等看完博文再次閱讀就會了解含義):
- ESP32 and SSD1306 OLED
- MAX7219 32x8 LED Matrix
- SSD1305 128x32
- SSD1306 128x32
- PCD8544 84x48 (Nokia 5110) LCD
- ST7920 128x64 LCD in 8080 parallel mode
????這只是一些常見的顯示器,可以說,已經足夠覆蓋到我們常見的范圍了。
3.U8g2庫詳解
3.1 U8g2庫百度腦圖
老規則,先上一個圖:
方法可以分為四大類:
- 基本函數
- 繪制相關函數
- 顯示配置相關函數
- 緩存相關函數
3.2 U8g2庫函數詳解
3.2.1 基本函數
3.2.1.1 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; }3.2.1.2 u8g2.beginSimple() —— 構造U8G2
函數說明:
/*** 簡單初始化U8g2庫* @Note 關聯方法 beginSimple*/ void U8G2::beginSimple(void);源碼說明:
void 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函數吧。
3.2.1.3 u8g2.initDisplay() —— 初始化顯示控制器
函數說明:
/*** 初始化顯示控制器*/ void U8G2::initDisplay(void)注意點:
- 這個方法不需要我們單獨調用,會在begin函數主動調用一次,我們主要理解即可,會在里面針對具體的OLED進行配置;
3.2.1.4 u8g2.clearDisplay() —— 清除屏幕內容
函數說明:
/*** 清除屏幕*/ void U8G2::clearDisplay(void)注意點:
- 這個方法不需要我們單獨調用,會在begin函數主動調用一次,我們主要理解即可;
- 不要在 firstPage 和 nextPage 函數之間調用該方法;
3.2.1.5 u8g2.setPowerSave() —— 是否開啟省電模式
函數說明:
/*** 清除顯示緩沖區* @param is_enable* 1 表示啟用顯示器的省電模式,屏幕上看不到任何東西* 0 表示禁用省電模式*/ void U8G2::setPowerSave(uint8_t is_enable)注意點:
- 不管是啟用還是禁用,顯示器需要的內存消耗是不會變的,說到底就是為了關閉屏幕,做到省電;
- 所以這里就可以理解為什么初始化需要 setPowerSave(0);
3.2.1.6 u8g2.clear() —— 清除操作
函數說明:
/*** 清除屏幕顯示,清除緩沖區,光標回到左上角原點位置(0,0)* @Note 關聯方法 home clearDisplay clearBuffer*/ void U8G2::clear(void)源碼說明:
void clear(void) { home(); //回到原點clearDisplay(); //清除屏幕上的顯示clearBuffer(); //清除緩沖區 }3.2.1.7 u8g2.clearBuffer() —— 清除緩沖區
函數說明:
/*** 清除內存中數據緩沖區*/ void U8G2::clearBuffer(void)注意點:
- 一般這個函數是與sendBuffer函數配對使用,通常用法如下:
3.2.1.8 u8g2.disableUTF8Print() —— 禁用 UTF8打印
函數說明:
/*** 禁用Arduino平臺下支持輸出UTF8字符集,默認是開啟*/ void U8G2::disableUTF8Print(void)3.2.1.9 u8g2.enableUTF8Print() —— 啟用 UTF8打印
函數說明:
/*** 開啟Arduino平臺下支持輸出UTF8字符集*/ void U8G2::enableUTF8Print(void)注意點:
- 我們的中文字符就是UTF8;
- 常見例子
3.2.1.10 u8g2.home() —— 重置顯示光標的位置
函數說明:
/*** 重置顯示光標的位置,回到原點(0,0)* @Note 關聯方法 print clear*/ void U8G2::home(void)3.2.2 繪制相關函數
3.2.2.1 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);3.2.2.2 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設置;
- 直徑等于2rad + 1;
示例:
u8g2.drawCircle(20, 25, 10, U8G2_DRAW_ALL);3.2.2.3 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 關聯方法 drawCircle setDrawColor*/ void U8G2::drawDisc(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t opt = U8G_DRAW_ALL)注意點:
- 如果支持繪制顏色(也就是不是單色顯示器),那么由setDrawColor設置;
- 直徑等于2rad + 1;
3.2.2.4 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 關聯方法 drawCircle*/ void U8G2::drawEllipse(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rx, u8g2_uint_t ry, uint8_t opt)注意點:
- rx*ry 在8位模式的u8g2必須小于512(博主暫且沒有理解);
示例:
u8g2.drawEllipse(20, 25, 15, 10, U8G2_DRAW_ALL);3.2.2.5 u8g2.drawFilledEllipse() —— 畫實心橢圓
函數說明:
/*** 畫實心橢圓,圓心坐標為(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 關聯方法 drawCircle*/ void U8G2::drawFilledEllipse(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rx, u8g2_uint_t ry, uint8_t opt)注意點:
- rx*ry 在8位模式的u8g2必須小于512(博主暫且沒有理解);
3.2.2.6 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);3.2.2.7 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值;
- 這個繪制方法依賴于當前的字體模式和繪制顏色;
3.2.2.8 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設置;
3.2.2.9 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);3.2.2.10 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,畢竟像素屬于最小顆粒度;
- 我們可以利用這個繪制方法自定義自己的圖形顯示;
3.2.2.11 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),這是顯而易見的限制;
3.2.2.12 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);3.2.2.13 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!");3.2.2.14 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);3.2.2.15 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: ?");3.2.2.16 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)3.2.2.17 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;
3.2.2.18 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; }3.2.2.19 u8g2.print() —— 繪制內容
函數說明:
/*** 繪制內容* @Note 關聯方法 setFont setCursor enableUTF8Print*/ void U8G2::print(...)示例:
u8g2.setFont(u8g2_font_ncenB14_tr); u8g2.setCursor(0, 15); u8g2.print("Hello World!");3.2.2.20 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);3.2.3 顯示配置相關函數
3.2.3.1 u8g2.getAscent() —— 獲取基準線以上的高度
函數說明:
/*** 獲取基準線以上的高度* @return 返回高度值* @Note 關聯方法 setFont getDescent setFontRefHeightAll*/ int8_t U8G2::getAscent(void)注意點:
- 跟字體有關(setFont);
示例:
下面例子,ascent是18
3.2.3.2 u8g2.getDescent() —— 獲取基準線以下的高度
函數說明:
/*** 獲取基準線以下的高度* @return 返回高度值* @Note 關聯方法 setFont setFontRefHeightAll*/ int8_t U8G2::getDescent(void)注意點:
- 跟字體有關(setFont);
示例:
下面例子,descent是-5
3.2.3.3 u8g2.getDisplayHeight() —— 獲取顯示器的高度
函數說明:
/*** 獲取顯示器的高度* @return 返回高度值*/ u8g2_uint_t getDisplayHeight(void)3.2.3.4 u8g2.getDisplayWidth() —— 獲取顯示器的寬度
函數說明:
/*** 獲取顯示器的寬度* @return 返回寬度值*/ u8g2_uint_t getDisplayWidth(void)3.2.3.5 u8g2.getMaxCharHeight() —— 獲取當前字體里的最大字符的高度
函數說明:
/*** 獲取當前字體里的最大字符的高度* @return 返回高度值* @Note 關聯方法 setFont*/ u8g2_uint_t getMaxCharHeight(void)注意點:
- 每一個字符在font字集中都是一個位圖,位圖有高度和寬度;
3.2.3.6 u8g2.getMaxCharWidth() —— 獲取當前字體里的最大字符的寬度
函數說明:
/*** 獲取當前字體里的最大字符的寬度* @return 返回寬度值* @Note 關聯方法 setFont*/ u8g2_uint_t getMaxCharWidth(void)注意點:
- 每一個字符在font字集中都是一個位圖,位圖有高度和寬度;
3.2.3.7 u8g2.getStrWidth() —— 獲取字符串的像素寬度
函數說明:
/*** 獲取字符串的像素寬度* @param s 繪制字符串* @return 返回字符串的像素寬度值* @Note 關聯方法 setFont drawStr*/ u8g2_uint_t U8G2::getStrWidth(const char *s)注意點:
- 像素寬度和當前font字體有關;
3.2.3.8 u8g2.getUTF8Width() —— 獲取UTF-8字符串的像素寬度
函數說明:
/*** 獲取UTF-8字符串的像素寬度* @param s 繪制字符串* @return 返回字符串的像素寬度值* @Note 關聯方法 setFont drawStr*/ u8g2_uint_t U8G2::getUTF8Width(const char *s)注意點:
- 像素寬度和當前font字體有關;
3.2.3.9 u8g2.setAutoPageClear() —— 設置自動清除緩沖區
函數說明:
/*** 是否自動清除緩沖區* @param mode 0 表示關閉* 1 表示開啟,默認是開啟*/ void U8G2::setAutoPageClear(uint8_t mode)注意點:
- 該方法用于 firstPage 和 nextPage(看上面的源碼解析);
- 建議該方法保持默認就好,如果用戶禁止了,那么需要自己維護緩沖區的狀態或者手動調用clearBuffer;
3.2.3.10 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);3.2.3.11 u8g2.setBusClock() —— 設置總線時鐘
函數說明:
/*** 設置總線時鐘(I2C SPI)* @param mode clock_speed 總線時鐘頻率(Hz)* @Note 關聯方法 begin*/ void U8G2::setBusClock(uint32_t clock_speed);注意點:
- 僅僅Arduino平臺支持;
- 必須在u8g2.begin() 或者 u8g2.initDisplay()之前調用;
3.2.3.12 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 去掉該限制
示例:
u8g2.setClipWindow(10, 10, 85, 30); u8g2.setDrawColor(1); u8g2.drawStr(3, 32, "U8g2");3.2.3.13 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!");3.2.3.14 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)3.2.3.15 u8g2.setDrawColor() —— 設置繪制顏色
函數說明:
/*** 設置繪制顏色(暫時還沒有具體去了解用法)*/ void U8G2::setDrawColor(uint8_t color)3.2.3.16 u8g2.setFont() —— 設置字體集
這是一個非常重要的方法,非常重要!!!
函數說明:
/*** 設置字體集(字體集用于字符串繪制方法或者glyph繪制方法)* @param font 具體的字體集* @Note 關聯方法 drawUTF8 drawStr drawGlyph print*/ void U8G2::setFont(const uint8_t *font)Font會根據像素點高度做了很多區分,具體font請參考 wiki。
如果我們需要用到中文字符,可以在wiki里面搜索一下chinese,你就會發現很多中文font,比如:
注意點:
- 中文字符集消耗內存大,請謹慎使用,可以用在Arduino 101等ram空間比較大的板子上;
至于用哪一個,看自己的需求了。
我們看看Font的命名規則:
<prefix> '_' <name> '_' <purpose> <char set>其中:
- 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 chars) 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就好;
示例:Fonts u8g2_font_5x7_tr and u8g2_font_pressstart2p_8u
3.2.3.17 u8g2.setFontDirection() —— 設置字體方向
函數說明:
/*** 定義字符串繪制或者圖形繪制的方向* @param dir 方向* @param 關聯方法 drawStr*/ void U8G2::setFontDirection(uint8_t dir)注意點:
- dir參數
| 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");3.2.4 緩存相關函數
緩存相關函數,一般不會去操作,了解即可;
3.2.4.1 u8g2.getBufferPtr() —— 獲取緩存空間的地址
函數說明
/*** 獲取緩存空間的地址* @return 返回緩存空間起始地址* @Note 關聯方法 getBufferTileHeight, getBufferTileWidth, clearBuffer*/ uint8_t *U8G2::getBufferPtr(void)注意點:
- 緩存大小等于 8 * u8g2.getBufferTileHeight() * u8g2.getBufferTileWidth().
3.2.4.2 u8g2.getBufferTileHeight() —— 獲取緩沖區的Tile高度
函數說明
/*** 獲取緩沖區的Tile高度* @return 返回高度值*/ uint8_t U8G2::getBufferTileHeight(void)注意點:
- 一個tile等于8個像素點.
3.2.4.3 u8g2.getBufferTileWidth() —— 獲取緩沖區的Tile寬度
函數說明
/*** 獲取緩沖區的Tile寬度* @return 返回寬度值*/ uint8_t U8G2::getBufferTileWidth(void)注意點:
- 一個tile等于8個像素點.
3.2.4.4 u8g2.getBufferCurrTileRow() —— 獲取緩沖區的當前Tile row
函數說明
/*** 獲取緩沖區的當前Tile row行數* @return 返回當前的tilerow*/ uint8_t U8G2::getBufferCurrTileRow(void)注意點:
- 這個方法跟我們上面說到的page position相關.
3.2.4.5 u8g2.setBufferCurrTileRow() —— 設置緩沖區的當前Tile row
函數說明
/*** 設置緩沖區的當前Tile row* @param 當前的tilerow*/ void 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();利用好該方法,我們可以實現部分更新;
4.如何運用U8G2庫
????前面博主介紹到U8G2適配了絕大部分的OLED,那么我們如何構建具體的OLED驅動呢?可分為以下幾個順序步驟:
- 區分顯示器
- 選擇物理總線方式
- 區分數字連線
- U8g2初始化
- U8g2繪制模式
4.1 區分顯示器
????首先,你需要知道OLED顯示器的控制器型號以及屏幕大小。舉個例子,博主手上有一塊SSD1306 128X64的OLED,那么它的控制器就是SSD1306,屏幕大小是128X64。
????其次,你所選擇的OLED必須在U8g2庫所支持的OLED列表中,具體可參考 鏈接地址。
4.2 選擇物理總線方式
????圖像信息是通過物理總線方式發給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, chip select and a write strobe signa
- 6800: Another 8-Bit bus, but with a different protocol.
????具體的OLED使用什么物理總線,我們需要查閱各自的數據手冊。比如,博主的SSD1306就是IIC。
4.3 區分數字連線
????知道了物理連線模式之后,我們一般都是把OLED連接到Arduino Board的輸出引腳,也就是軟件模擬具體總線協議。當然,如果有現成的物理總線端口那就更好了。
4.4 U8g2初始化
????經歷以上三步之后,我們就可以開始初始化出具體的OLED驅動了。比如,博主的IIC SSD1306 128X64 的OLED,就可以用以下初始化構造器(Builder設計模式,有空可以去了解一下):
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還有其他構造器(具體可以參考 wiki):
| U8G2_SSD1306_128X64_NONAME_1_4W_SW_SPI(rotation, clock, data, cs, dc [, reset]) | page buffer, size = 128 bytes |
| U8G2_SSD1306_128X64_NONAME_2_4W_SW_SPI(rotation, clock, data, cs, dc [, reset]) | page buffer, size = 256 bytes |
| U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI(rotation, clock, data, cs, dc [, reset]) | full framebuffer, size = 1024 bytes |
| U8G2_SSD1306_128X64_NONAME_1_4W_HW_SPI(rotation, cs, dc [, reset]) | page buffer, size = 128 bytes |
????那么,我們這里就需要重點講述一下構造器的規則。
????構造器的名字包括以下幾方面:
| 1 | Prefix | U8G2 |
| 2 | Display Controller | SSD1306 |
| 3 | Display Name | 128X64_NONAME |
| 4 | Buffer Size | 1, 2 or F (full frame buffer) |
| 5 | Communication | 4W_SW_SPI |
????它們之間使用"_"連接起來。其中:
- BufferSize,緩存大小
| 1 | 保持一頁的緩沖區,用于firstPage/nextPage的PageMode. |
| 2 | 保持兩頁的緩沖區,用于firstPage/nextPage的PageMode.. |
| F | 獲取整個屏幕的緩沖區,ram消耗大,一般用在ram空間比較大的arduino板子. |
- 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 (軟件模擬總線前提下的構造器的第一個參數)
| 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() ); }????那么,我們來看看到底構造器里面做了什么操作?還是以博主使用的SSD1306 128X64為例子:
/*** SSD1306 4線軟件模擬SPI*/ U8G2_SSD1306_128X64_NONAME_1_4W_SW_SPI(.....)????類定義:
/*** SSD1306構造器,繼承U8G2*/ class U8G2_SSD1306_128X64_NONAME_1_4W_SW_SPI : public U8G2 {public: U8G2_SSD1306_128X64_NONAME_1_4W_SW_SPI(const u8g2_cb_t *rotation, uint8_t clock, uint8_t data, uint8_t cs, uint8_t dc, uint8_t reset = U8X8_PIN_NONE) : U8G2() {//配置SSD1306u8g2_Setup_ssd1306_128x64_noname_1(&u8g2, rotation, u8x8_byte_arduino_4wire_sw_spi, u8x8_gpio_and_delay_arduino);//設置通信協議u8x8_SetPin_4Wire_SW_SPI(getU8x8(), clock, data, cs, dc, reset);} };????從上面代碼看出,默認調用了父類U8G2的構造函數,我們看看它里面做了什么:
class U8G2 : public Print {protected:u8g2_t u8g2;u8x8_char_cb cpp_next_cb; /* the cpp interface has its own decoding function for the Arduino print command */public:u8g2_uint_t tx, ty;U8G2(void) { //設置Arduino print函數的解碼方法,這里是ASCII,當然也有UTF-8cpp_next_cb = u8x8_ascii_next; //屏幕初始化home(); }.......- U8G2類構造函數主要是定義好解碼方法以及初始化屏幕(包括重置原點);
????然后我們看看,u8g2_Setup_ssd1306_128x64_noname_1做了什么:
void u8g2_Setup_ssd1306_128x64_noname_1(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb) {uint8_t tile_buf_height;//定義好緩存空間 記住 這里是 1 page modeuint8_t *buf;//配置屏幕u8g2_SetupDisplay(u8g2, u8x8_d_ssd1306_128x64_noname, u8x8_cad_001, byte_cb, gpio_and_delay_cb);//生成buf 這里是128 bytesbuf = u8g2_m_16_8_1(&tile_buf_height);//初始化bufu8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation); }/*============================================*/ /*This procedure is called after setting up the display (u8x8 structure).--> This is the central init procedure for u8g2 object */ void u8g2_SetupBuffer(u8g2_t *u8g2, uint8_t *buf, uint8_t tile_buf_height, u8g2_draw_ll_hvline_cb ll_hvline_cb, const u8g2_cb_t *u8g2_cb) {u8g2->font = NULL;//u8g2->kerning = NULL;//u8g2->get_kerning_cb = u8g2_GetNullKerning;//u8g2->ll_hvline = u8g2_ll_hvline_vertical_top_lsb;u8g2->ll_hvline = ll_hvline_cb;u8g2->tile_buf_ptr = buf;u8g2->tile_buf_height = tile_buf_height;u8g2->tile_curr_row = 0;//頁碼 這是一個很重要的參數u8g2->font_decode.is_transparent = 0; /* issue 443 */u8g2->bitmap_transparency = 0;u8g2->draw_color = 1;u8g2->is_auto_page_clear = 1;//自動清除u8g2->cb = u8g2_cb;u8g2->cb->update_dimension(u8g2); #ifdef U8G2_WITH_CLIP_WINDOW_SUPPORTu8g2_SetMaxClipWindow(u8g2); /* assign a clip window and call the update() procedure */ #elseu8g2->cb->update_page_win(u8g2); #endifu8g2_SetFontPosBaseline(u8g2); /* issue 195 */#ifdef U8G2_WITH_FONT_ROTATION u8g2->font_decode.dir = 0; #endif }可以總結幾點:
- 配置初始化屏幕
- 配置初始化緩沖區
注意點:
- 上面講解涉及到了繪制模式,請參考下一節;
4.5 U8g2繪制模式
????U8g2支持三種繪制模式:
- Full screen buffer mode,全屏緩存模式
- Page mode (This is the U8glib picture loop) 分頁模式
- U8x8, character only mode 僅僅支持普通字符
4.5.1 Full screen buffer mode
特點:
- 繪制速度快
- 所有的繪制方法都可以使用
- 需要大量的ram空間
構造器:
- 構造器必須帶有F,比如:
用法:
示例代碼:
void setup(void) {u8g2.begin(); }void loop(void) {u8g2.clearBuffer();u8g2.setFont(u8g2_font_ncenB14_tr);u8g2.drawStr(0,20,"Hello World!");u8g2.sendBuffer(); }4.5.2 Page mode
特點:
- 繪制速度慢
- 所有的繪制方法都可以使用
- 需要少量的ram空間
構造器:
- 構造器必須帶有“1”或者2,比如:
用法:
示例代碼:
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() ); }4.5.3 U8x8 character mode
特點:
- 繪制速度快
- 并不是對所有的顯示器都有效
- 圖形繪制不可用
- 不需要ram空間
構造器:
- 使用U8X8構造器,比如:
用法:
- 所有繪制命令是直接把數據寫到顯示器
示例代碼:
void setup(void) {u8x8.begin(); }void loop(void) {u8x8.setFont(u8x8_font_chroma48medium8_r);u8x8.drawString(0,1,"Hello World!"); }轉載于:https://www.cnblogs.com/danpianjicainiao/p/11048729.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的玩转u8g2 OLED库,一篇就够的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 快开宝PDA开单器出入库扫码:让批发零售
- 下一篇: Entity Framework-02