設備樹 Overlay(Device Tree Overlays, DTO),它在嵌入式Linux系統(尤其是基于ARM的設備,比如樹莓派、NanoPi等)中非常常見。它主要用于動態修改設備樹,以適配硬件的變化或擴展外設支持。
1. 設備樹 (Device Tree) 基礎
- 設備樹是Linux內核用來描述硬件的一個數據結構(通常是
.dts
源文件,編譯成.dtb
二進制文件)。 - 它告訴內核“硬件是什么”,“有什么設備”,以及“設備如何連接”的信息。
- 這樣,內核就不需要硬編碼特定硬件,增強了移植性。
2. 設備樹 Overlay(DTO)是什么?
- Overlay 就是對設備樹的“增量補丁”,它定義了一段額外的設備樹節點,能夠在運行時或者啟動時“疊加”到主設備樹上。
- 目的:實現設備樹的動態擴展,比如給板子添加新的外設(GPIO擴展板、攝像頭模塊、觸摸屏等),而不用修改主設備樹文件。
- Overlay通常是一個獨立的
.dtbo
文件(編譯后的Overlay),由主設備樹加載時或內核模塊加載時合并。
3. 使用場景
典型場景:
-
外設模塊動態添加
比如你的主板沒有集成攝像頭,需要外接攝像頭模塊,就可以用Overlay來描述攝像頭設備節點和相關GPIO、中斷配置。 -
GPIO復用和引腳配置
一塊板子多個設備共用引腳,Overlay能配置引腳復用,打開或關閉某些設備接口。 -
設備樹不方便修改的環境
有些固件或內核只能加載固定的主設備樹,Overlay給了動態配置的能力。
4. Overlay 如何使用?
-
制作Overlay文件
- 編寫
.dts
文件,繼承主設備樹的內容,只寫你想“覆蓋/新增”的部分。 - 使用設備樹編譯器(
dtc
)編譯成.dtbo
文件。
- 編寫
-
加載Overlay
- 在啟動時加載(很多板子在bootloader配置文件中支持Overlay加載,比如
config.txt
里的dtoverlay=
) - 運行時加載(通過
configfs
接口,動態插入Overlay)
- 在啟動時加載(很多板子在bootloader配置文件中支持Overlay加載,比如
舉例(樹莓派):
# /boot/config.txt
dtoverlay=your-overlay-name
5. Overlay 和 Initramfs、systemd 對比
特性 | 設備樹 Overlay | initramfs | systemd |
---|---|---|---|
作用 | 動態修改硬件描述,加載新硬件節點 | 早期用戶空間臨時根文件系統加載 | Linux啟動初始化系統及服務管理 |
關注層面 | 硬件抽象層 | 系統啟動早期階段用戶空間環境 | 系統服務管理及并發啟動 |
運行時機 | 啟動階段或運行時內核設備樹修改 | 內核啟動時掛載,用于系統啟動 | 內核啟動后啟動管理服務 |
主要功能 | 硬件描述擴展、配置引腳、設備綁定 | 提供最初的文件系統環境 | 啟動服務、管理依賴、日志、資源等 |
適用場景 | 硬件變動、模塊化硬件設計 | 內核啟動時根文件系統不可用場景 | 系統服務啟動和管理 |
是否直接涉及硬件 | 是 | 否 | 否 |
動態修改硬件描述能力 | 支持,動態加載/卸載Overlay | 不支持 | 不支持 |
6. 簡單總結
- 設備樹 Overlay:專注于硬件描述和配置,特別適合動態加載硬件節點。
- initramfs:內核啟動的第一個用戶空間,負責掛載根文件系統和初始化環境。
- systemd:現代Linux系統的初始化和服務管理器,負責啟動和維護系統服務。
7. 你實際使用設備樹Overlay時要注意
- 設備樹版本兼容性
- Overlay文件的正確編譯
- 確保Overlay描述的硬件節點匹配實際硬件
- 確保bootloader或內核支持Overlay機制
- 運行時加載Overlay需內核開啟
CONFIG_OF_OVERLAY
支持
很好,你正在使用的是 NanoPi NEO Core 這類基于 Allwinner H3 SoC 的板子,跑的是 Armbian,這類系統通常是 Debian 或 Ubuntu 的輕量定制版本,廣泛用于嵌入式設備和 DIY 項目。
? 背景確認
你的平臺:
- 硬件:NanoPi NEO Core(基于 Allwinner H3)
- 系統:Armbian(可能基于 Debian/Ubuntu)
- 用途:可能用于音頻服務(你提到 Navidrome)或其他嵌入式用途
- 需求:動態控制硬件(如 I2C、SPI、UART、GPIO、LED、外接模塊等)
📌 在 Armbian 上使用設備樹 Overlay(DTO)
Armbian 提供 標準化支持 DTO 的機制,適用于大多數基于 mainline kernel 的 Allwinner 平臺。
📘 Armbian Overlay 使用方法
1. 查看當前啟用的 Overlays
armbian-config
進入 → System
→ Hardware
→ 你可以通過界面啟用常見的設備樹 Overlays(I2C, SPI, UART, PWM, etc)
或手動查看:
cat /boot/armbianEnv.txt
你可能會看到類似:
overlays=spi-spidev i2c0 uart1
param_spidev_spi_bus=0
2. 啟用已有 Overlay
編輯 /boot/armbianEnv.txt
:
sudo nano /boot/armbianEnv.txt
添加或修改:
overlays=spi-spidev i2c0 uart1
保存后重啟:
sudo reboot
你可以用 dmesg | grep spi
或 ls /dev/spidev*
來確認是否生效。
3. Overlay 文件存放位置
Armbian 默認加載 /boot/dtb/overlay/
中的 .dtbo
文件。你也可以:
ls /boot/dtb/allwinner/overlay/
4. 自定義 Overlay 示例
舉個例子:你要啟用一個額外的 LED 燈連接到 GPIO PH7
創建文件:my-led-overlay.dts
/dts-v1/;
/plugin/;&{/} {fragment@0 {target-path = "/";__overlay__ {my_led: gpio-leds {compatible = "gpio-leds";status_led {label = "status_led";gpios = <&pio 7 7 GPIO_ACTIVE_HIGH>; // PH7 = port 7, pin 7default-state = "on";};};};};
};
然后編譯:
dtc -@ -I dts -O dtb -o my-led-overlay.dtbo my-led-overlay.dts
拷貝到 overlay 文件夾:
sudo cp my-led-overlay.dtbo /boot/dtb/allwinner/overlay/
修改 /boot/armbianEnv.txt
添加:
user_overlays=my-led-overlay
保存并重啟:
sudo reboot
🧠 Overlay 與 systemd/initramfs 區別(再次歸納)
項目 | 設備樹 Overlay (DTO) | initramfs | systemd |
---|---|---|---|
含義 | 動態疊加修改設備樹(硬件配置) | 臨時根文件系統(啟動用) | 啟動初始化系統管理器 |
使用時機 | 啟動時或運行時加載 | 啟動時使用 | 內核啟動后立即運行 |
作用層面 | 硬件層(GPIO/I2C/UART等) | 用戶空間臨時運行環境 | 管理服務、守護進程、掛載等 |
是否與硬件直接相關 | ? 是 | ? 否 | ? 否 |
🧪 常用場景舉例(在你設備上)
目標 | 使用方式 |
---|---|
啟用 SPI | overlays=spi-spidev |
啟用 UART | overlays=uart1 uart2 |
自定義 LED GPIO | 自寫 DTO + user_overlays |
音頻模塊支持(I2S DAC) | DTO + 手動加載驅動 |
外接 OLED 屏(I2C) | overlays=i2c0 + dto 添加地址和驅動綁定 |
🔧 小貼士
- 用
dtc -I fs /proc/device-tree
可以查看當前設備樹的實際內容 - 確保
CONFIG_OF_OVERLAY=y
在內核中開啟(Armbian默認是開的) - 你可以使用
fdtput
工具直接修改.dtb
文件(高級用法)
如果你遇到的問題是:
- DTO加載無效 ? 看
/boot/armbianEnv.txt
里拼寫是否正確 - 驅動不生效 ? 用
dmesg
、lsmod
、modinfo
等調試 - 不知道引腳編號 ? 查找對應的 sunxi pin map(Allwinner特有)
你想在 NanoPi NEO Core(Armbian) 上通過 I2C 接 OLED 顯示屏(SSD1306 控制器),下面是完整詳解,包括:
- ? 硬件連接
- ? 內核模塊/驅動配置
- ? 設備樹 Overlay 編寫與使用
- ? 軟件層顯示內容(Python 示例)
? 1. 硬件接線(基于 I2C)
OLED Label | NanoPi NEO Pin | Notes |
---|---|---|
VCC | 3.3V / 5V | OLED 供電 |
GND | GND | 地線 |
SDA | I2C0_SDA (Pin 3, GPIO PA11) | 數據線 |
SCL | I2C0_SCL (Pin 5, GPIO PA12) | 時鐘線 |
確認你接在了 I2C0(默認引出)的 SDA/SCL 上。
? 2. 啟用 I2C0 接口(Armbian Overlay)
編輯 /boot/armbianEnv.txt
:
overlays=i2c0
保存后重啟:
sudo reboot
確認 /dev/i2c-0
是否出現:
ls /dev/i2c*
還可以確認 OLED 是否連上(地址通常為 0x3c
):
sudo apt install -y i2c-tools
sudo i2cdetect -y 0
看到類似:
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- 3c
...
? 3. 添加設備樹 Overlay(掛載 SSD1306)
Armbian 自帶的 overlay 目錄通常 沒有直接包含 SSD1306 的 overlay。所以我們需要自定義:
?? 創建文件 ssd1306-i2c.dts
/dts-v1/;
/plugin/;/ {compatible = "allwinner,sun8i-h3";fragment@0 {target-path = "/";__overlay__ {i2c@01c2ac00 {status = "okay";ssd1306: ssd1306@3c {compatible = "solomon,ssd1306fb-i2c";reg = <0x3c>;pinctrl-names = "default";reset-gpios = <0>; /* if no reset pin */width = <128>;height = <64>;page-offset = <0>;seg-remap;com-invdir;contrast = <127>;};};};};
};
說明:
- 如果你接的是 128x32 OLED,請將
height = <32>
。- 若有
reset
引腳可設置為<&pio X Y GPIO_ACTIVE_LOW>
,比如<&pio 0 6 0>
是 PA6。
🛠? 編譯 & 安裝
dtc -@ -I dts -O dtb -o ssd1306-i2c.dtbo ssd1306-i2c.dts
sudo cp ssd1306-i2c.dtbo /boot/dtb/allwinner/overlay/
修改 /boot/armbianEnv.txt
添加:
user_overlays=ssd1306-i2c
然后重啟:
sudo reboot
? 4. 驅動檢查
確認驅動模塊是否加載:
lsmod | grep ssd1306
如果沒有:
sudo modprobe ssd1306
也可以添加到 /etc/modules
里讓其開機加載:
echo ssd1306 | sudo tee -a /etc/modules
? 5. 顯示內容(Python)
安裝 Python 庫
sudo apt install python3-pip
pip3 install luma.oled
示例 Python 腳本
from luma.core.interface.serial import i2c
from luma.oled.device import ssd1306
from PIL import ImageDraw, ImageFont, Imageserial = i2c(port=0, address=0x3C)
device = ssd1306(serial)draw = ImageDraw.Draw(Image.new("1", device.size))
font = ImageFont.load_default()
draw.text((0, 0), "Hello NanoPi!", font=font, fill=255)device.display(draw.im)
? 6. 常見問題排查
問題 | 原因 |
---|---|
/dev/i2c-0 不存在 | overlay 沒啟用 i2c0 |
i2cdetect 無響應或空 | 線路錯誤 / 接線問題 |
OLED 無反應 | 地址錯誤(0x3c vs 0x3d),接錯線,驅動未加載 |
無法加載驅動 | 沒有開啟內核模塊 CONFIG_FB_SSD1306 |
Python 報錯 | 未裝 luma.oled 或設備沒有正確掛載 |
? 小結
你只需要:
- 確保 I2C 通了 →
overlays=i2c0
- 加載自定義 Overlay →
ssd1306@3c
- 加載驅動 →
ssd1306fb
(內核驅動或 Python 用戶空間庫) - 選定顯示方式 → 控制臺 framebuffer 或 Python 圖形庫
很好!你想通過 framebuffer 控制 SSD1306
OLED(I2C 接口),這樣你可以直接使用命令行工具、C 程序或 Shell 腳本將圖像、文字顯示到屏幕上,而不需要 Python 庫。
🧩 目標實現:
讓 SSD1306 顯示屏出現在 /dev/fb1
,通過 cat
、fbi
、dd
等 framebuffer 工具控制它顯示內容。
? 1. 檢查內核模塊
確保內核已包含以下模塊(Armbian 通常自帶):
lsmod | grep ssd1306
如果沒有,手動加載:
sudo modprobe ssd1306
sudo modprobe ssd1306fb
?? 如果提示模塊不存在,可能你需要啟用模塊支持并重新編譯內核(我可指導你做)。
? 2. 設備樹 Overlay(帶 framebuffer 綁定)
使用下面的 .dts
文件創建 overlay,掛載 SSD1306,并啟用 framebuffer 輸出。
?? 文件:ssd1306-fb-i2c.dts
/dts-v1/;
/plugin/;/ {compatible = "allwinner,sun8i-h3";fragment@0 {target = <&i2c0>;__overlay__ {status = "okay";ssd1306fb: ssd1306fb@3c {compatible = "solomon,ssd1306fb-i2c";reg = <0x3c>;width = <128>;height = <64>;page-offset = <0>;seg-remap;com-invdir;contrast = <127>;rotate = <0>;};};};
};
? 3. 編譯并啟用 overlay
dtc -@ -I dts -O dtb -o ssd1306-fb-i2c.dtbo ssd1306-fb-i2c.dts
sudo cp ssd1306-fb-i2c.dtbo /boot/dtb/overlay/
編輯 /boot/armbianEnv.txt
,添加:
user_overlays=ssd1306-fb-i2c
保存后重啟:
sudo reboot
? 4. 驗證 framebuffer
重啟后檢查:
ls /dev/fb*
你應該看到 /dev/fb1
(主 framebuffer 通常是 fb0)
確認信息:
cat /sys/class/graphics/fb1/name
應返回:ssd1306fb
查看分辨率:
cat /sys/class/graphics/fb1/virtual_size
? 5. 使用 framebuffer 工具顯示內容
🖼? 顯示圖片(需安裝 fbi 工具)
sudo apt install fbi
sudo fbi -d /dev/fb1 -T 1 --noverbose -a your-image.png
注意:
- 圖片必須為 單色位圖(1bit bmp/png),大小 128×64。
- 你也可以用
imagemagick
轉圖:
convert input.png -resize 128x64\! -monochrome out.bmp
🧪 輸出簡單圖像(命令行寫 framebuffer)
sudo bash -c 'dd if=/dev/zero of=/dev/fb1 bs=1024 count=1'
這會清空屏幕。
你可以寫一個程序像素級控制 /dev/fb1
,例如寫字符、畫圖。
? 6. 示例:C 程序寫 framebuffer
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main() {int fb = open("/dev/fb1", O_RDWR);if (fb < 0) return 1;// 簡單填充圖案char buffer[1024]; // 128 x 64 = 1024 bits = 128 bytes x 8 linesmemset(buffer, 0xAA, sizeof(buffer)); // 交錯黑白條紋write(fb, buffer, sizeof(buffer));close(fb);return 0;
}
? 小結
步驟 | 動作 |
---|---|
1?? | 確保 OLED 連到 I2C(地址 0x3c) |
2?? | 使用 ssd1306fb-i2c overlay |
3?? | 檢查 /dev/fb1 是否出現 |
4?? | 用 fbi 、dd 、程序操作 framebuffer 顯示內容 |