用 Zephyr Display API 驅(qū)動 LCD/OLED 顯示屏)
MicroPython Zephyr 端口zephyr.Display類完全指南復(fù)用 Zephyr Display API 驅(qū)動 LCD/OLED 顯示屏【免費下載鏈接】micropythonMicroPython - a lean and efficient Python implementation for microcontrollers and constrained systems項目地址: https://gitcode.com/gh_mirrors/mi/micropython導(dǎo)讀zephyr.Display是 MicroPython Zephyr 端口ports/zephyr為上層 Python 代碼提供的顯示屏訪問類它通過封裝 Zephyr RTOS 原生 Display 驅(qū)動接口讓開發(fā)者可以用 MicroPython 直接驅(qū)動由 Zephyr devicetree 描述zephyr,display/zephyr,displayschosen 節(jié)點的 LCD、OLED、電子墨水屏等顯示設(shè)備。讀完本文你將掌握Display對象的構(gòu)造方式、像素格式與朝向管理、write()/rgb()等核心方法、capabilities()返回的能力元組解析以及如何借助as_framebuf()把顯示屏無縫接入 MicroPython 的framebuf繪圖體系。本文以 docs/library/zephyr.Display.rst 為骨架并結(jié)合 ports/zephyr/zephyr_display.c、ports/zephyr/modzephyr.c 等源碼展開說明。一、模塊背景與啟用條件1.1 什么是zephyr.Display在 Zephyr 生態(tài)中顯示設(shè)備的底層能力由 Zephyr 的 Display API 提供。MicroPython 的 Zephyr 端口并沒有為每種屏幕重新實現(xiàn)一套驅(qū)動而是把 Zephyr 已有的驅(qū)動基礎(chǔ)設(shè)施直接暴露給 Python顯示設(shè)備在 devicetree 中通過 chosen 節(jié)點指定zephyr,display單個顯示節(jié)點或zephyr,displays帶displays屬性的顯示列表節(jié)點zephyr.Display類通過一個復(fù)刻 Zephyr 的 API 來訪問這些顯示設(shè)備文檔原話即write()、clear()、blanking()、orientation()等方法的語義與 Zephyr C API 一一對應(yīng)。1.2 編譯期開關(guān)Display類并非在所有固件中都存在它依賴兩個編譯開關(guān)見 ports/zephyr/zephyr_display.c開關(guān)作用CONFIG_DISPLAY啟用 Zephyr 顯示子系統(tǒng)Display類僅在定義該 Kconfig 時編譯并注冊到zephyr模塊見 modzephyr.cMICROPY_PY_FRAMEBUF啟用 MicroPython 的framebuf模塊只有開啟后as_framebuf()方法才可用見 zephyr_display.c也就是說import zephyr后能否使用zephyr.Display取決于構(gòu)建固件時板級配置如ports/zephyr/boards/下的.conf文件是否打開了CONFIG_DISPLAY。1.3 設(shè)備從哪里來源碼中設(shè)備的發(fā)現(xiàn)順序zephyr_display.c優(yōu)先枚舉 compatible 為zephyr,displays的節(jié)點中的displays屬性得到設(shè)備數(shù)組zephyr_display_devices[]否則回退到 chosen 節(jié)點zephyr,display指向的單個設(shè)備若兩者都沒有則設(shè)備數(shù)組為空此時用整數(shù)id構(gòu)造會拋出ValueError: Invalid display ID。因此板子上有哪些可用屏幕完全由 devicetree 決定MicroPython 側(cè)只是讀取這份靜態(tài)描述。二、構(gòu)造函數(shù)Display(id)Display(id)獲取一個用于訪問由id標識的顯示設(shè)備的對象。id有兩種形式整數(shù)0、1…按zephyr,displays屬性中的位置索引。越界小于 0 或大于等于Display.display_count會拋ValueError: Invalid display ID源碼字符串如ssd13063c通過節(jié)點標識符node label / devicetree 名字查找設(shè)備底層調(diào)用zephyr_device_find()ports/zephyr/zephyr_device.c內(nèi)部先device_get_binding()若開啟CONFIG_DEVICE_DT_METADATA還會嘗試按 node label 匹配找不到則拋ValueError: device xxx not found。另外如果設(shè)備未就緒device_is_ready()失敗會拋RuntimeError: Display is not readyzephyr_display.c。源碼還額外暴露了一個文檔未列出的類屬性Display.display_count值為DT_ZEPHYR_DISPLAYS_COUNT見 zephyr_display.c可用于遍歷所有屏幕from zephyr import Display print(Display.display_count) # 當前固件可用屏幕數(shù)量 d Display(0) # 按位置取第一個屏幕 print(d) # 輸出形如 Display(ssd13063c)三、方法詳解3.1write(buf, x0, y0, size_xNone, size_yNone)—— 向屏幕寫入像素數(shù)據(jù)Display.write(buf[, x[, y[, size_x[, size_y]]]])把緩沖區(qū)協(xié)議對象bytearray、bytes等按顯示器的當前像素格式寫入屏幕。x、y為寫入?yún)^(qū)域左上角坐標size_x、size_y為寫入?yún)^(qū)域的寬高均可省略源碼默認行為見 zephyr_display.c默認x 0、y 0默認size_x取capabilities.x_resolution屏幕完整一行寬度默認size_y len(buf) / (size_x * 每像素字節(jié)數(shù))即按當前像素格式從緩沖區(qū)長度反推高度。緩沖區(qū)長度必須不小于size_x * size_y * 每像素字節(jié)數(shù)否則拋ValueError: Buffer is shorter than size requires。寫入失敗底層display_write()返回負值拋RuntimeError: Couldnt write to display。# 以 RGB565 屏幕為例填充一整屏128x64 單色屏則除以 8 得每行 16 字節(jié) import zephyr d zephyr.Display(0) caps d.capabilities() w, h caps[0], caps[1] buf bytearray(w * h * 2) # RGB565 每像素 2 字節(jié) d.write(buf) # 整屏寫入 d.write(buf, 10, 10, 32, 32) # 只寫入 10,10 開始的 32x32 區(qū)域3.2rgb(r, g, b)—— 顏色到像素格式的轉(zhuǎn)換Display.rgb(r, g, b)把0..255的 RGB 三通道顏色轉(zhuǎn)換為當前像素格式對應(yīng)的整數(shù)。轉(zhuǎn)換規(guī)則zephyr_display.c當前像素格式轉(zhuǎn)換結(jié)果MONO01任一通道 127 →0xFF否則0x00MONO10任一通道 127 →0x00否則0xFF極性反轉(zhuǎn)RGB_5650xF800、0x07E0、0x001F掩碼組合字節(jié)序交換MP_BSWAP16RGB_565X同上但不交換字節(jié)序L_8(r g b) / 3灰度L_4若支持灰度右移 4 位若當前格式無法映射到 framebuf 像素格式拋ValueError: Not a framebuf pixel format。# 在 RGB565 屏幕上把紅色轉(zhuǎn)換為 2 字節(jié)像素值 red d.rgb(255, 0, 0)3.3capabilities()—— 查詢屏幕能力Display.capabilities()返回一個 7 元組格式為(X Size, Y Size, Supported PFs, Current PF, Current Orientation, Misc Characteristics, Current PF as framebuf format)各元素含義對應(yīng) zephyr_display.c索引含義說明0X 分辨率整數(shù)如1281Y 分辨率整數(shù)如642支持的像素格式整數(shù)元組每個元素是一個格式位掩碼如1 i3當前像素格式整數(shù)對應(yīng)下方常量4當前朝向整數(shù)對應(yīng)ORIENTATION_*常量5雜項特性字符串元組可含VTILED、MSB_FIRST、EPD、DOUBLE_BUFFER、X_ALIGNMENT_WIDTH等6當前格式對應(yīng)的 framebuf 格式整數(shù)供as_framebuf()/ 手工創(chuàng)建framebuf.FrameBuffer使用x_size, y_size, pfs, cur_pf, orient, misc, fb_fmt d.capabilities() print(f{x_size}x{y_size}, PF{cur_pf:#x}, fb{fb_fmt}, misc{misc})像素格式 → framebuf 格式的映射由zephyr_display_framebuf_current_format_helper()完成zephyr_display.c單色MONO01/MONO10依據(jù)VTILED/MSB_FIRST特性映射為MVLSB(0)、MHMSB(4)或MHLSB(3)RGB_565/RGB_565X→RGB565(1)L_8→GS8(6)L_4→GS4_HMSB(2)。若VTILED與MSB_FIRST同時出現(xiàn)對應(yīng) framebuf 不支持的 VMSB 布局會拋ValueError: Not a framebuf pixel format。3.4format([format])—— 讀取 / 設(shè)置像素格式Display.format([format])不帶參數(shù)返回當前像素格式整數(shù)帶參數(shù)嘗試調(diào)用 Zephyr 的display_set_pixel_format()切換格式失敗拋ValueError: Invalid pixel format成功后再返回新的當前格式zephyr_display.c。注意目標格式必須包含在capabilities()[2]支持列表中否則底層會拒絕。print(hex(d.format())) # 例如 0x1100 (RGB565) d.format(Display.FORMAT_MONO01) # 切換到單色格式若支持3.5blanking(value)—— 屏幕消隱開關(guān)Display.blanking(value)value為真值時調(diào)用display_blanking_on()否則調(diào)用display_blanking_off()失敗拋RuntimeError: Couldnt set blankingzephyr_display.c。適合實現(xiàn)省電或熄屏效果。d.blanking(True) # 熄屏 d.blanking(False) # 亮屏3.6clear()—— 清屏Display.clear()調(diào)用 Zephyr 的display_clear()。若底層驅(qū)動未實現(xiàn)清屏返回-ENOSYS拋RuntimeError: Clearing is not supported by the display其他失敗拋Couldnt clear displayzephyr_display.c。電子墨水屏等設(shè)備通常支持清屏。3.7set_brightness(value)/set_contrast(value)—— 亮度與對比度Display.set_brightness(value) # 0..255 Display.set_contrast(value) # 0..255參數(shù)會被CLAMP限定在0..255區(qū)間。若底層驅(qū)動不支持-ENOSYS分別拋RuntimeError: Setting brightness/contrast is not supported by the displayzephyr_display.c。對帶背光的 LCD 或支持對比度調(diào)節(jié)的 OLED如 SSD1306 的set_contrast非常實用。d.set_brightness(200) # 調(diào)低背光 d.set_contrast(128) # 調(diào)整 OLED 對比度3.8orientation([orientation])—— 讀取 / 設(shè)置屏幕朝向Display.orientation([orientation])不帶參數(shù)返回當前朝向整數(shù)帶參數(shù)先校驗0 arg 270越界拋ValueError: Invalid orientation然后調(diào)用display_set_orientation()失敗時按-ENOSYS拋Setting orientation is not supported by the display否則拋Couldnt set orientation最后返回新的當前朝向zephyr_display.c。建議使用源碼提供的四個類常量而非裸數(shù)字print(d.orientation()) d.orientation(Display.ORIENTATION_90) # 旋轉(zhuǎn) 90 度3.9as_framebuf()—— 無縫接入 framebuf 繪圖Display.as_framebuf()僅當固件啟用了MICROPY_PY_FRAMEBUF時可用。它返回一個framebuf.FrameBuffer實例的增強版一個匿名子類實例除繼承framebuf.FrameBuffer的全部繪圖方法fill、line、rect、text、blit等外還額外帶有show()方法調(diào)用show()即把當前緩沖區(qū)內(nèi)容按當前設(shè)置的配置直接寫入屏幕zephyr_display.c。其實現(xiàn)要點緩沖區(qū)大小按x_resolution * y_resolution * 每像素字節(jié)數(shù)分配并清零幀緩沖格式取自capabilities()[6]當前像素格式對應(yīng)的 framebuf 格式show()內(nèi)部通過display.write(buf)把整個緩沖區(qū)交給顯示器zephyr_display.c。from zephyr import Display d Display(0) fb d.as_framebuf() fb.fill(0) # 清屏寫 0 fb.text(Hello MicroPython, 0, 0, 1) fb.rect(10, 20, 50, 20, 1) fb.show() # 一次性刷新到屏幕配合 extmod/modframebuf.c 中定義的 framebuf 格式常量MVLSB0、RGB5651、GS4_HMSB2、MHLSB3、MHMSB4、GS86你還可以手工構(gòu)造與屏幕匹配的FrameBuffer實現(xiàn)雙緩沖或局部刷新。四、像素格式與朝向常量zephyr.Display類上還暴露了一批常量zephyr_display.c與 Zephyr 的PIXEL_FORMAT_*/DISPLAY_ORIENTATION_*枚舉值一一對應(yīng)朝向常量常量值Display.ORIENTATION_NORMAL0Display.ORIENTATION_901Display.ORIENTATION_1802Display.ORIENTATION_2703像素格式常量部分常量說明Display.FORMAT_RGB_88824 位 RGBDisplay.FORMAT_MONO01/FORMAT_MONO10單色兩種極性Display.FORMAT_ARGB_888832 位 ARGBDisplay.FORMAT_RGB_565/FORMAT_RGB_565X16 位 RGB565字節(jié)序差異Display.FORMAT_L_88 位灰度Display.FORMAT_L_4/FORMAT_I_44 位灰度/索引色Zephyr 4.4Display.FORMAT_AL_88、FORMAT_XRGB_8888、FORMAT_BGR_888、FORMAT_ABGR_8888、FORMAT_RGBA_8888、FORMAT_BGRA_8888視 Zephyr 版本條件編譯注意源碼針對 Zephyr 4.4 做了兼容處理——Zephyr 4.4 將PIXEL_FORMAT_BGR_565更名為PIXEL_FORMAT_RGB_565X若舊頭文件缺失PANEL_PIXEL_FORMAT_RGB_565X定義會用PANEL_PIXEL_FORMAT_BGR_565補齊zephyr_display.c保證跨版本可編譯。五、端到端示例在支持顯示器的板子上點亮屏幕以檢測到屏幕 → 查詢能力 → 繪圖并刷新的完整流程為例from zephyr import Display # 1. 確認固件帶有顯示設(shè)備 if Display.display_count 0: raise SystemExit(No display in devicetree) # 2. 獲取顯示器并查看能力 d Display(0) # 或 Display(ssd13063c) print(d) w, h, _, pf, orient, misc, _ d.capabilities() print(fresolution{w}x{h} pf{pf:#x} orient{orient} misc{misc}) # 3. 清屏、設(shè)置亮度/對比度按設(shè)備能力選擇 d.clear() try: d.set_contrast(180) except RuntimeError as e: print(contrast unsupported:, e) # 4. 用 framebuf 繪圖并刷新固件需啟用 MICROPY_PY_FRAMEBUF fb d.as_framebuf() fb.fill(0) fb.text(Zephyr, 0, 0, 1) fb.hline(0, 12, w, 1) fb.show()若當前像素格式不是 framebuf 可直接映射的格式如RGB_888as_framebuf()會拋ValueError: Not a framebuf pixel format此時可先嘗試d.format(Display.FORMAT_RGB_565)切換或直接用d.write()手工組包。六、異常速查表場景異常整數(shù)id越界ValueError: Invalid display ID字符串id找不到設(shè)備ValueError: device xxx not found設(shè)備未就緒RuntimeError: Display is not readywrite()緩沖區(qū)比所需短ValueError: Buffer is shorter than size requireswrite()底層失敗RuntimeError: Couldnt write to displayrgb()/as_framebuf()格式無法映射ValueError: Not a framebuf pixel formatformat()設(shè)置非法格式ValueError: Invalid pixel formatorientation()參數(shù)越界ValueError: Invalid orientation驅(qū)動未實現(xiàn)某功能返回-ENOSYSRuntimeError: ... not supported by the display七、延伸閱讀類文檔docs/library/zephyr.Display.rstzephyr模塊總覽docs/library/zephyr.rst核心實現(xiàn)ports/zephyr/zephyr_display.c設(shè)備枚舉、方法綁定、framebuf 映射設(shè)備查找輔助ports/zephyr/zephyr_device.c模塊注冊與CONFIG_DISPLAY開關(guān)ports/zephyr/modzephyr.c、ports/zephyr/modzephyr.hframebuf 格式常量與繪圖實現(xiàn)extmod/modframebuf.cZephyr 端口構(gòu)建與板級配置ports/zephyr/README.md、ports/zephyr/boards如 rpi_pico.overlay 展示了 devicetree 覆蓋文件的寫法總而言之zephyr.Display是連接 MicroPython 腳本層與 Zephyr 顯示驅(qū)動棧的薄封裝一方面它完整復(fù)刻了 Zephyr Display API 的語義寫入、格式、朝向、消隱、亮度/對比度、清屏另一方面通過as_framebuf()與 MicroPython 原生framebuf繪圖體系打通使得在 Zephyr 支持的任意帶屏開發(fā)板上用幾行 Python 完成繪圖與刷新成為可能。【免費下載鏈接】micropythonMicroPython - a lean and efficient Python implementation for microcontrollers and constrained systems項目地址: https://gitcode.com/gh_mirrors/mi/micropython創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考