今天在網路上搜尋 MicroPython 指針時鐘資料時找到下面這篇 :
範例程式使用了 GFX 這個模組來繪圖, 查詢網路發現原來這也是 Adafruit 的作品, GFX 旨在為各種尺寸的 OLED 及 LCD 營幕提供一個通用繪圖函式庫, 其原始碼使用 C 語言寫成, 參考 :
GFX 的 MicroPython 驅動程式 gfx.py 可從 GitHub 下載 :
GFX 運作原理參考下面這兩篇 :
以下測試使用 128*64 解析度的 SSD1306 OLED 顯示器, 搭配燒錄 MicroPython v1.23.0 韌體的 LOLIN D32 開發板 (ESP32). 使用的 Adafruit SSD1306 驅動程式可從 GitHub 下載 :
首先將下載的 ssd1306.py 與 gfx.py 上傳到開發板 :
MicroPython v1.23.0 on 2024-06-02; Generic ESP32 module with ESP32
Type "help()" for more information.
用 dir() 檢視 gfx 模組內容 :
>>> import gfx
>>> dir(gfx)
['__class__', '__name__', '__dict__', '__file__', 'GFX']
其中 GFX 類別就是用來建立繪圖物件的類別, 為了方便通常直接從 gfx 匯入 GFX 即可 :
>>> from gfx import GFX
先匯入模組 :
>>> from machine import I2C, Pin
>>> import ssd1306
建立 SSD1306_I2C 物件 :
>>> i2c=I2C(0, scl=Pin(22), sda=Pin(21)) # ESP32 I2C
>>> oled=ssd1306.SSD1306_I2C(128, 64, i2c) # 0.96 吋解析度 128*64
這樣就可以呼叫 GFX 類別的建構式 GFX() 來建立 GFX 繪圖物件了, 呼叫時必須傳入螢幕寬度, 高度, 以及繪製像素的函式, 參數結構如下 :
GFX(width, height, pixel_function)
因為 GFX 是一個通用的繪圖函式庫, 不是專用於特定螢幕 (可用於 SSD1306 或 ILI9341 等螢幕), 因此必須傳入螢幕尺寸與繪製像素的函式, 以 SSD1306 而言, 這些參數都可在 SSD1306_I2C 物件中取得, 檢視 oled 物件 :
>>> dir(oled)
['__class__', '__init__', '__module__', '__qualname__', '__dict__', 'addr', 'buffer', 'fill', 'framebuf', 'invert', 'pixel', 'scroll', 'text', 'width', 'height', 'external_vcc', 'pages', 'poweron', 'init_display', 'write_cmd', 'show', 'poweroff', 'contrast', 'write_framebuf', 'i2c', 'temp']
其中 width 與 height 屬性就是螢幕尺寸; 而 pixel() 就是像素繪製函式 :
>>> oled.width
128
>>> oled.height
64
>>> oled.pixel
<bound_method>
只要將此三個參數傳入 GFX() 即可建立 GFX 物件 :
>>> gfx=GFX(oled.width, oled.height, oled.pixel)
>>> type(gfx)
<class 'GFX'>
檢視 GFX 物件內容 :
>>> dir(gfx)
['__class__', '__init__', '__module__', '__qualname__', '__dict__', 'fill_rect', 'hline', 'line', 'rect', 'vline', 'width', 'height', '_pixel', '_slow_hline', '_slow_vline', 'circle', 'fill_circle', 'triangle', 'fill_triangle']
其中 width 與 height 為傳入隻螢幕尺寸 :
>>> gfx.width
128
>>> gfx.height
64
GFX 物件提供許多繪圖函式, 摘要說明如下表 :
GFX 物件的方法 | 說明 |
line(x0, y0, x1, y1, color) | 在座標 (x0, y0) 與 (x1, y1) 間畫一直線, color=0 (暗)/1 (亮) |
hline(x, y, w, color) | 從座標 (x, y) 開始向右畫一段長 w 的水平線, color=0 (暗)/1 (亮) |
vline(x, y, h, color) | 從座標 (x, y) 開始向下畫一段高 h 的水平線, color=0 (暗)/1 (亮) |
rect(x, y, w, h, color) | 從座標 (x, y) 開始繪製寬 w 高 h 的矩形, color=0 (暗)/1 (亮) |
fill_rect(x, y, w, h, color) | 從座標 (x, y) 開始繪製寬 w 高 h 的填滿矩形, color=0 (暗)/1 (亮) |
triangle(x0, y0, x1, y1, x2, y2, color) | 連接座標 (x0, y0), (x1, y1), (x2, y2) 三點繪製三角形, color=0 (暗)/1 (亮) |
fill_triangle(x0, y0, x1, y1, x2, y2, color) | 連接座標 (x0, y0), (x1, y1), (x2, y2) 三點繪製填滿三角形, color=0 (暗)/1 (亮) |
circle(x0, y0, r, color) | 以 (x0, y0) 為圓心, r 為半徑繪製圓形, color=0 (暗)/1 (亮) |
fill_circle(x0, y0, r, color) | 以 (x0, y0) 為圓心, r 為半徑繪製填滿圓形, color=0 (暗)/1 (亮) |
這些繪圖方法都是利用呼叫 GFX() 時傳入的第三參數, 即顯示器的像素繪製函式 oled.pixel() 將圖描出來的, 不同的螢幕有各自的像素繪製函式, 因此 GFX 是一個可用於各型螢幕的通用函式庫.
1. 繪製直線 :
GFX 物件的 line(), hline(), vline() 可用來繪製直線, 先清除螢幕 :
>>> oled.fill(0) # 填滿暗點
>>> oled.show()
呼叫 line() 方法繪製直線, 先畫一條左上角到右下角的對角線 :
>>> gfx.line(0, 0, gfx.width-1, gfx.height-1, 1) # 從左上角到右下角的對角線
>>> oled.show()
再畫一條右上角到左下角的對角線 :
>>> gfx.line(gfx.width-1, 0, 0, gfx.height-1, 1) # 從右上角到左下角的對角線
>>> oled.show()
結果如下 :
接下來測試 vline() 與 hline(), 先清螢幕 :
>>> oled.fill(0) # 填滿暗點
>>> oled.show()
先畫一條起點為 (0, 31) 寬度為 128 的水平線 (即左邊中央往右) :
>>> gfx.hline(0, 31, 128, 1)
>>> oled.show()
然後畫一條起點為 (63, 0) 高度為 63 的垂直線 (即最上面中央往下) :
>>> gfx.vline(63, 0, 64, 1)
>>> oled.show()
結果如下 :
當然這也可以用 line() 方法來畫, 只是要傳入直線兩端座標較麻煩 :
>>> gfx.line(0, 31, 127, 31, 1)
>>> gfx.line(63, 0, 63, 63, 1)
>>> oled.show()
結果與上面是一樣的.
2. 繪製矩形 :
GFX 物件的 rect() 與 fill_rect() 可用來繪製矩形, 先清除螢幕 :
>>> oled.fill(0) # 填滿暗點
>>> oled.show()
繪製一個沿螢幕邊框的方框矩形 :
>>> gfx.rect(0, 0, 128, 64, 1)
>>> oled.show()
結果是一個有 1px 寬度的方框 :
如果要讓框邊加粗 1 個 px, 可以在此矩形內畫一個內縮 1px 的矩形, 這時左上角座標要用 (1, 1), 尺寸因兩邊各縮 1px 變成 128-2=126 與 64-2=62 :
>>> gfx.rect(1, 1, 126, 62, 1)
>>> oled.show()
結果如下 :
下面測試 fill_rect() 效果, 先清除螢幕 :
>>> oled.fill(0) # 填滿暗點
>>> oled.show()
然後在螢幕四周畫一個 1px 邊框 :
>>> gfx.rect(0, 0, 128, 64, 1)
>>> oled.show()
接著往內縮一個 px 用 fill_rect() 畫一個填滿內部的矩形, 因邊框佔據 1px, 內縮 1px, 故左上角座標是 (2, 2), 尺寸要減 4 :
>>> gfx.fill_rect(2, 2, 124, 60, 1)
>>> oled.show()
結果如下 :
由於填滿效果會不斷繪製畫素, 故照相時會因掃描頻率較低產生 aliasing 現象, 實際顯示效果是白邊框內有一黑框, 裡面才是白色填滿.
3. 繪製三角形 :
GFX 物件的 triangle() 與 fill_triangle() 繪製三角形, 先清除螢幕 :
>>> oled.fill(0) # 填滿暗點
>>> oled.show()
用 triangle() 在螢幕繪製一個等腰三角形 :
>>> gfx.triangle(0, 0, 127, 0, 63, 64, 1)
>>> oled.show()
結果如下 :
用 fill_triangle() 在螢幕繪製一個填滿的等腰三角形 :
>>> oled.fill(0) # 填滿暗點清除螢幕
>>> oled.show()
>>> gfx.fill_triangle(0, 0, 127, 0, 63, 64, 1)
>>> oled.show()
結果如下 :
4. 繪製圓形 :
GFX 物件的 triangle() 與 fill_triangle() 繪製三角形, 先清除螢幕 :
>>> oled.fill(0) # 填滿暗點清除螢幕
>>> oled.show()
呼叫 circle() 在螢幕左半邊畫一個圓形, 圓心為 (31, 31); 再呼叫 fill_circle() 在右半邊畫一個填滿的圓形, 圓心為 (96, 31), 半徑都是 31 :
>>> gfx.circle(31, 31, 31, 1)
>>> gfx.fill_circle(96, 31, 31, 1)
>>> oled.show()
結果如下 :