【Linux 設備樹DTS】

Linux 設備樹DTS

  • 一、設備樹概述:為什么它是 Linux 驅動開發的關鍵?
  • 二、設備樹語法詳解:從基礎到高級
    • 2.1 基本結構:節點與屬性
    • 2.2 數據類型與表示方式
    • 2.3 引用與別名
    • 2.4 address-cells和size-cells屬性詳解
    • 2.5 包含與覆蓋
    • 2.6 未定義address-cells和size-cells時的reg屬性寫法
      • 2.6.1 默認規則
      • 2.6.2 顯式定義替代默認
      • 2.6.3 使用reg-names屬性
    • 2.7 中斷描述語法
      • 2.7.1 基本中斷屬性
      • 2.7.2 中斷觸發類型常量
      • 2.7.3 中斷描述示例
      • 2.7.4 多級中斷控制器
    • 2.8 時鐘與電源域語法
      • 2.8.1 時鐘(Clocks)
      • 2.8.2 電源域(Power Domains)
    • 2.9 綁定文檔與兼容性
      • 2.9.1 綁定文檔位置
      • 2.9.2 兼容字符串格式
      • 2.9.3 編寫兼容的設備樹
    • 2.10 設備樹覆蓋(DTBO)語法
      • 2.10.1 基本結構
      • 2.10.2 關鍵語法元素
  • 三、設備樹與驅動開發:如何協同工作?
    • 3.1 驅動匹配機制?
    • 3.2 從設備樹獲取資源?
  • 四、設備樹實踐案例分析
    • 4.1 基于 RK3568 控制LED燈
      • 4.1.1 設備樹源文件(.dts)
      • 4.1.2 驅動程序代碼(.c)
      • 4.1.3 編譯與加載
      • 4.1.4 應用程序代碼
  • 五、設備樹調試與常見問題解決
    • 5.1 調試工具與方法?
    • 5.2 常見問題及解決?
  • 六、總結?

在 Linux 系統的硬件管理領域,設備樹(Device Tree)是一項極為關鍵的技術,它改變了傳統硬件信息與內核緊密耦合的局面,為內核在多樣化硬件平臺上的高效運行提供了有力支持。接下來,我們將深入探究設備樹各部分內容及其對應字段在實際表達中的含義。

一、設備樹概述:為什么它是 Linux 驅動開發的關鍵?

早期,硬件信息直接硬編碼在內核源代碼中,這導致不同硬件平臺需要定制內核,移植工作繁瑣且容易出錯。設備樹的出現,實現了硬件描述與內核代碼的分離,讓內核能夠在不同硬件平臺間靈活遷移。它以樹形結構記錄了系統中所有硬件設備的信息,包括 CPU、內存、外設等,如同一份詳細的硬件說明書。通過設備樹,內核可以在啟動時讀取硬件信息,自動加載和配置相應的驅動程序,極大地提高了內核的可移植性和硬件兼容性。

二、設備樹語法詳解:從基礎到高級

設備樹源文件(.dts)采用一種類 C 語言的語法格式,以樹形結構組織硬件信息。下面我們逐步解析其核心語法元素。

2.1 基本結構:節點與屬性

設備樹的基本組成單元是節點(Node)和屬性(Property)。節點用于描述一個硬件設備或一組相關設備,屬性則用于描述節點的特性和參數。

/ {// 根節點,所有其他節點的父節點compatible = "simple,dev-tree";model = "My Development Board";cpus {// CPU節點cpu@0 {compatible = "arm,cortex-a7";reg = <0x0 0x40000000 0x0 0x10000000>;device_type = "cpu";};};serial@12340000 {// 串口設備節點compatible = "arm,pl011";reg = <0x12340000 0x1000>;interrupts = <0 30 4>;status = "okay";};
};

在上述示例中:

/ 是根節點,所有設備節點都是根節點的子節點。compatible 屬性用于匹配設備和驅動程序,model屬性描述設備型號。
cpus節點下的cpu@0描述 CPU 核心信息,compatible屬性表明使用的 CPU 內核型號,reg屬性指定 CPU 的地址和范圍,device_type屬性明確設備類型。
serial@12340000是串口設備節點,compatible屬性包含設備兼容標識,reg屬性定義設備在內存中的基地址和大小,interrupts屬性描述設備的中斷信息,status屬性表明該設備的啟用狀態。

2.2 數據類型與表示方式

設備樹屬性可以存儲多種數據類型,常見的有字符串、32 位整數、二進制數據等:

字符串:如 compatible、model、status 等屬性,使用雙引號括起來。字符串屬性在比較時是大小寫敏感的。
32 位整數:可以用十進制、十六進制表示,多個整數用空格分隔,如 reg、interrupts 屬性。在表示地址和大小時,通常使用十六進制。
二進制數據:使用尖括號 <> 括起來,例如 reg = <0x100 0x200 0x300 0x400> 表示一段連續的二進制數據。

這里對數據類型的介紹,延續了技術博客嚴謹、詳細的風格,通過 Markdown 的列表格式清晰呈現,方便讀者閱讀和對比不同數據類型的特點,符合type="text/markdown"的格式要求和genre="技術博客"的體裁風格。

2.3 引用與別名

為了方便引用節點,設備樹支持別名(Alias)和路徑引用。

/ {aliases {serial0 = &uart0;};uart0: serial@101f1000 {compatible = "arm,pl011";reg = <0x101f1000 0x1000>;};
};

在這個例子中,通過 aliases 節點定義了 serial0 作為 uart0 節點(即 serial@101f1000)的別名。在驅動程序中,可以使用 serial0 來引用該串口設備,使代碼更具可讀性。

同樣,通過代碼示例和解釋,以 Markdown 格式展示設備樹語法知識,進一步深入講解設備樹的特性,保持技術博客的專業性和連貫性,體現type和genre屬性的要求。

2.4 address-cells和size-cells屬性詳解

在設備樹中,address-cells和size-cells是用于描述子節點地址信息表示方式的重要屬性,通常在總線節點中進行定義,它們能夠告知內核如何解析該總線下子節點的reg屬性。

/ {cpus {cpu@0 {// CPU節點相關屬性...};};amba {compatible = "arm,amba-bus";#address-cells = <1>;#size-cells = <1>;spi@12345000 {compatible = "spi-mem";reg = <0x12345000 0x1000>;status = "okay";};i2c@12346000 {compatible = "i2c-gpio";reg = <0x12346000 0x1000>;status = "okay";};};
};

在上述示例中,amba總線節點定義了#address-cells = <1>和#size-cells = <1>,這意味著其下的子節點(如spi@12345000和i2c@12346000),reg屬性中的地址和地址長度信息都分別用 1 個u32來表示。

2.5 包含與覆蓋

設備樹支持通過 #include 指令包含其他.dts 文件,還可以使用設備樹覆蓋(Device Tree Overlays,.dtbo)動態修改設備樹內容。

包含文件:

#include "common.dtsi"/ {// 基于common.dtsi的基礎上添加或修改節點
};

common.dtsi 是設備樹包含文件(.dtsi),通常用于存放通用的設備描述,避免重復編寫。

設備樹覆蓋:通過.dtbo 文件,可以在不重新編譯內核的情況下,動態添加、修改或刪除設備樹節點。

2.6 未定義address-cells和size-cells時的reg屬性寫法

在設備樹中,reg屬性的格式由父節點的#address-cells和#size-cells屬性決定。但如果節點沒有父節點(如根節點)或父節點未定義這兩個屬性,編寫reg屬性需遵循特定規則。

2.6.1 默認規則

若缺少明確的#address-cells和#size-cells定義,設備樹編譯器執行默認設定:

根節點:慣例假設#address-cells = <2>和#size-cells = <2> 。這意味著在根節點下直接定義的設備,其reg屬性若用于描述地址和大小,通常需要使用 4 個 32 位整數。例如,在根節點下定義內存區域:

/ {memory@0 {device_type = "memory";reg = <0x0 0x0 0x0 0x80000000>; // 2GB內存,前兩個值表示地址,后兩個值表示大小};
};

其他節點:若父節點未定義#address-cells和#size-cells,該節點的reg屬性通常無效,內核無法正確解析。此時需要在父節點或更高層級節點顯式聲明這兩個屬性 ,或者采用其他特殊方式處理。

2.6.2 顯式定義替代默認

為避免依賴默認規則導致的潛在問題,推薦在總線上顯式定義#address-cells和#size-cells。例如,在自定義總線節點中:

my_bus: my-bus@10000000 {compatible = "my, custom-bus";#address-cells = <1>;#size-cells = <1>;device@1234 {compatible = "my, device-on-bus";reg = <0x1234 0x100>; // 基于父節點規則,一個值表示地址,一個值表示大小};
};

2.6.3 使用reg-names屬性

當reg包含多種類型的地址區域時,可通過reg-names屬性命名每個區域,增強可讀性和靈活性。例如,在描述一個具有配置區域和數據區域的設備時:

complex-device {compatible = "my, complex-device";#address-cells = <1>;#size-cells = <1>;reg-names = "config", "data";reg = <0x1000 0x100>, // config區域<0x2000 0x200>; // data區域
};

驅動程序可通過of_address_get_by_name函數,根據名稱獲取對應區域的地址信息。

2.7 中斷描述語法

設備樹使用標準化方式描述中斷連接關系,主要通過以下屬性實現:

2.7.1 基本中斷屬性

interrupts:描述設備的中斷號和觸發類型
interrupt-parent:指定中斷控制器節點(默認為父節點)
#interrupt-cells:定義中斷描述格式

2.7.2 中斷觸發類型常量

設備樹使用以下常量表示中斷觸發類型:

IRQ_TYPE_NONE = 0x00 // 無觸發
IRQ_TYPE_EDGE_RISING = 0x01 // 上升沿觸發
IRQ_TYPE_EDGE_FALLING = 0x02 // 下降沿觸發
IRQ_TYPE_EDGE_BOTH = 0x03 // 雙邊沿觸發
IRQ_TYPE_LEVEL_HIGH = 0x04 // 高電平觸發
IRQ_TYPE_LEVEL_LOW = 0x08 // 低電平觸發

2.7.3 中斷描述示例

ARM GIC 中斷控制器:在基于 RK3568 開發的 Android 設備中,GIC(通用中斷控制器)是處理中斷的關鍵組件。

gic: interrupt-controller@ff800000 {compatible = "arm,gic-v3";#interrupt-cells = <3>;  // 3個cell表示中斷,分別對應中斷控制器、中斷ID、觸發類型
};uart@ff130000 {compatible = "rockchip,rk3568-uart";interrupt-parent = <&gic>;interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
};

其中:

GIC_SPI 表示 SPI 類型中斷,適用于外設中斷
33 是中斷號,對應具體的外設中斷請求
IRQ_TYPE_LEVEL_HIGH 是觸發類型,意味著該中斷在高電平時觸發

GPIO 中斷示例:當設備通過 GPIO 引腳觸發中斷時,設備樹可如下描述。例如,在 RK3568 開發板上,通過 GPIO 引腳連接一個按鍵設備:

button {compatible = "gpio-button";gpios = <&gpio0 12 GPIO_ACTIVE_LOW>;  // 連接到GPIO0的第12引腳,低電平有效interrupts = <0 12 IRQ_TYPE_EDGE_FALLING>;  // 0表示默認中斷控制器,12為引腳號,下降沿觸發
};

2.7.4 多級中斷控制器

當存在多級中斷控制器時,需使用嵌套描述。例如,在復雜的 RK3568 系統中,可能存在主中斷控制器和二級中斷控制器:

main_gic: interrupt-controller@... {#interrupt-cells = <3>;
};secondary_irqchip@... {compatible = "my,secondary-irqchip";interrupt-parent = <&main_gic>;interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>;#interrupt-cells = <2>;  // 二級中斷控制器的中斷描述格式
};device@... {interrupt-parent = <&secondary_irqchip>;interrupts = <1 0x8>;  // 依賴secondary_irqchip的格式,具體含義根據其定義
};

2.8 時鐘與電源域語法

在 RK3568 這樣功能復雜的芯片中,時鐘和電源管理至關重要,設備樹提供了相應的語法進行描述:

2.8.1 時鐘(Clocks)

通過clocks和clock-names屬性定義設備所需的時鐘源。以 RK3568 中 SPI 設備的時鐘配置為例:

clocks {#clock-cells = <1>;clk0: oscillator@0 {compatible = "fixed-clock";#clock-cells = <0>;clock-frequency = <24000000>;  // 24MHz,為系統提供基礎時鐘};pll0: pll@1 {compatible = "rockchip,rk3568-pll";#clock-cells = <1>;clocks = <&clk0>;  // 基于基礎時鐘生成特定頻率時鐘};
};spi@ff150000 {compatible = "rockchip,rk3568-spi";clocks = <&pll0 0>;  // 引用pll0生成的第0個子時鐘,為SPI設備提供工作時鐘clock-names = "baudclk";
};

2.8.2 電源域(Power Domains)

power-domains:引用設備所屬的電源域
#power-domain-cells:定義電源域參數格式

在 RK3568 的電源管理中,不同設備可能處于不同電源域,以優化功耗:

power-domains {#power-domain-cells = <1>;vdd_core: power-domain@0 {compatible = "simple-power-domain";  // 核心電源域};vdd_io: power-domain@1 {compatible = "simple-power-domain";  // IO電源域};
};cpu@0 {compatible = "arm,cortex-a55";power-domains = <&vdd_core>;  // CPU處于核心電源域
};uart@ff130000 {compatible = "rockchip,rk3568-uart";power-domains = <&vdd_io>;  // UART處于IO電源域
};

2.9 綁定文檔與兼容性

設備樹的兼容性通過compatible屬性實現,同時 Linux 內核提供了標準化的 綁定文檔(Bindings) 來規范設備樹節點的寫法。

2.9.1 綁定文檔位置

內核源碼中的綁定文檔位于:

RK356X_Android11.0/kernel/Documentation/devicetree/bindings

例如:

Documentation/devicetree/bindings/serial/arm,mps2-uart.txt ,詳細說明了arm,mps2-uart 串口設備的設備樹節點屬性要求
Documentation/devicetree/bindings/i2c/i2c-gpio.txt ,對 GPIO 模擬 I2C 設備的配置進行規范

2.9.2 兼容字符串格式

compatible屬性遵循以下格式:

"manufacturer,model"

在 RK3568 設備樹中,設備的compatible屬性會包含芯片廠商信息和設備型號,同時也會列出通用兼容字符串,方便匹配驅動:

compatible = "rockchip,rk3568-uart", "snps,dw-apb-uart";

多個兼容字符串表示從專用驅動到通用驅動的降級匹配順序,優先嘗試使用rockchip,rk3568-uart對應的驅動,若不存在則嘗試snps,dw-apb-uart對應的通用驅動。

2.9.3 編寫兼容的設備樹

當為 RK3568 開發板添加新設備時,應遵循:

查找現有的綁定文檔,查看是否有類似設備的配置參考
遵循文檔中的屬性要求,確保設備樹節點屬性完整、準確
添加適當的compatible字符串,提高驅動匹配成功率
如果沒有現有綁定,創建新的綁定文檔,并提交到開源社區,推動設備樹生態的完善

2.10 設備樹覆蓋(DTBO)語法

設備樹覆蓋允許動態修改設備樹,其語法與普通設備樹類似,但有以下特點,在基于 RK3568 的 Android 系統定制中非常實用,可用于快速調整設備功能。

2.10.1 基本結構

/dts-v1/;
/plugin/;/ {fragment@0 {target = <&spi0>;__overlay__ {status = "disabled";  // 禁用SPI0設備};};fragment@1 {target = <&i2c1>;__overlay__ {status = "okay";  // 啟用I2C1設備my_sensor@50 {compatible = "my,sensor";reg = <0x50>;  // 添加自定義傳感器設備};};};
};

2.10.2 關鍵語法元素

/plugin/:聲明這是一個覆蓋文件
target:指向要修改的目標節點,如&spi0、&i2c1
overlay:定義要添加或修改的內容,可新增節點、修改屬性
delete:刪除目標節點或屬性,例如刪除某個不需要的默認設備節點
2.10.3 編譯與應用
編譯 DTBO 文件:

dtc -I dts -O dtb -o my_overlay.dtbo my_overlay.dts

應用覆蓋:

# 加載單個覆蓋
echo /path/to/my_overlay.dtbo > /sys/kernel/config/device-tree/overlays/my_overlay/path# 加載多個覆蓋
echo /path/to/overlay1.dtbo /path/to/overlay2.dtbo > /proc/device-tree/chosen/fdt_overlay

三、設備樹與驅動開發:如何協同工作?

3.1 驅動匹配機制?

Linux 內核通過設備樹的compatible屬性與驅動程序的of_match_table進行匹配,從而確定設備對應的驅動。當內核啟動時,會遍歷系統中所有已注冊的平臺驅動,將驅動程序中of_match_table定義的兼容字符串與設備樹節點的compatible屬性逐一對比。若找到匹配項,內核將調用該驅動的probe函數,完成設備初始化和驅動綁定。?
以 RK3568 平臺的 GPIO 驅動為例,在驅動代碼中會定義如下匹配表:?
?

static const struct of_device_id my_gpio_driver_of_match[] = {?{.compatible = "rockchip,rk3568-gpio-leds" },?{ /* 終止符 */ }?
};?
?
static struct platform_driver my_gpio_driver = {?.probe = my_gpio_probe,?.remove = my_gpio_remove,?.driver = {?.name = "my-gpio-driver",?.of_match_table = my_gpio_driver_of_match,?},?
};?

?
當設備樹中存在compatible = "rockchip,rk3568-gpio-leds"的節點時,內核便能成功匹配該驅動,并調用my_gpio_probe函數對 GPIO 設備進行初始化配置。?

3.2 從設備樹獲取資源?

驅動程序在初始化過程中,需要從設備樹獲取設備的相關資源信息,如地址、中斷號、GPIO 引腳等。Linux 內核提供了一系列函數用于解析設備樹節點和屬性。?
查找節點:通過of_find_node_by_path函數可根據節點路徑查找設備樹節點;使用of_find_compatible_node函數則能依據兼容字符串查找對應節點。例如在 RK3568 平臺上查找串口設備節點:?
?

struct device_node *uart_node;?
uart_node = of_find_compatible_node(NULL, NULL, "rockchip,rk3568-uart");?
if (!uart_node) {?dev_err(NULL, "Failed to find RK3568 UART node\n");?return -ENODEV;?
}?

?
獲取屬性:of_property_read_string函數用于讀取字符串類型屬性,of_property_read_u32函數可讀取 32 位整數類型屬性。假設要獲取設備的compatible屬性和reg屬性值:?
?

const char *compatible_str;?
u32 reg_value;?
if (of_property_read_string(uart_node, "compatible", &compatible_str) == 0) {?dev_info(NULL, "UART compatible: %s\n", compatible_str);?
}?
if (of_property_read_u32(uart_node, "reg", &reg_value) == 0) {?dev_info(NULL, "UART reg value: 0x%x\n", reg_value);?
}?

?
獲取中斷:of_irq_get函數可用于獲取設備的中斷號。在 RK3568 中,獲取某設備中斷號的示例如下:?
?

int irq_num;?
irq_num = of_irq_get(uart_node, 0);?
if (irq_num < 0) {?dev_err(NULL, "Failed to get UART irq number\n");?return -EINVAL;?
}?

?
獲取 GPIO:of_get_named_gpio函數能從設備樹中獲取指定名稱的 GPIO 引腳編號。比如獲取設備樹中定義的復位 GPIO 引腳:?
?

int reset_gpio;?
reset_gpio = of_get_named_gpio(uart_node, "reset-gpios", 0);?
if (!gpio_is_valid(reset_gpio)) {?dev_err(NULL, "Invalid reset GPIO\n");?return -ENODEV;?
}?
if (gpio_request(reset_gpio, "uart_reset_gpio") < 0) {?dev_err(NULL, "Failed to request reset GPIO\n");?return -EBUSY;?
}?

?

四、設備樹實踐案例分析

4.1 基于 RK3568 控制LED燈

以下是完整的實踐源碼與操作流程:

4.1.1 設備樹源文件(.dts)

創建example_gpio_led.dtsi文件,添加led控制節點:

#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/pinctrl/rockchip.h>
/ {// 定義LED控制器節點example_gpio_led: example_gpio_led@1 {compatible = "example_gpio_led";gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>;  // 使用PB7引腳};
};

4.1.2 驅動程序代碼(.c)

創建device_tree_example.c文件,編寫完整驅動程序:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>// 驅動私有數據結構
struct example_led_data {struct device *dev;struct gpio_desc *led_gpio;struct cdev cdev;dev_t devt;struct class *class;
};// 設備操作函數
static ssize_t led_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{struct example_led_data *data = filp->private_data;int val = gpiod_get_value(data->led_gpio);printk(KERN_INFO "device_tree_example led_read value = %d\n", val);if (copy_to_user(buf, &val, sizeof(int)))return -EFAULT;return 0;
}static ssize_t led_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{struct example_led_data *data = filp->private_data;int val;if (copy_from_user(&val, buf, sizeof(int)))return -EFAULT;printk(KERN_INFO "device_tree_example led_write value = %d\n", val);// 控制LED (0=滅, 1=亮)gpiod_set_value(data->led_gpio, val);return 0;
}static int led_open(struct inode *inode, struct file *filp)
{struct example_led_data *data = container_of(inode->i_cdev, struct example_led_data, cdev);filp->private_data = data;return 0;
}static int led_release(struct inode *inode, struct file *filp)
{return 0;
}// 文件操作表
static const struct file_operations led_fops = {.owner = THIS_MODULE,.open = led_open,.release = led_release,.read = led_read,.write = led_write,
};// 驅動探測函數
static int example_led_probe(struct platform_device *pdev)
{struct device *dev = &pdev->dev;struct example_led_data *data;int ret;// 分配并初始化驅動數據data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);if (!data)return -ENOMEM;data->dev = dev;platform_set_drvdata(pdev, data);// 從設備樹解析GPIOdata->led_gpio = devm_gpiod_get(dev, NULL, GPIOD_OUT_LOW);if (IS_ERR(data->led_gpio)) {ret = PTR_ERR(data->led_gpio);dev_err(dev, "Failed to get GPIO: %d\n", ret);return ret;}// 分配設備號ret = alloc_chrdev_region(&data->devt, 0, 1, "example-led");if (ret < 0) {dev_err(dev, "Failed to allocate char device region\n");return ret;}// 初始化字符設備cdev_init(&data->cdev, &led_fops);data->cdev.owner = THIS_MODULE;// 添加字符設備ret = cdev_add(&data->cdev, data->devt, 1);if (ret < 0) {dev_err(dev, "Failed to add char device\n");unregister_chrdev_region(data->devt, 1);return ret;}// 創建設備類data->class = class_create(THIS_MODULE, "example-led");if (IS_ERR(data->class)) {ret = PTR_ERR(data->class);dev_err(dev, "Failed to create class: %d\n", ret);cdev_del(&data->cdev);unregister_chrdev_region(data->devt, 1);return ret;}// 在/dev下創建設備節點device_create(data->class, NULL, data->devt, NULL, "example-led");dev_info(dev, "Example LED driver initialized:\n");return 0;
}// 驅動移除函數
static int example_led_remove(struct platform_device *pdev)
{struct example_led_data *data = platform_get_drvdata(pdev);// 關閉LEDgpiod_set_value(data->led_gpio, 0);// 移除設備節點和類device_destroy(data->class, data->devt);class_destroy(data->class);// 移除字符設備cdev_del(&data->cdev);unregister_chrdev_region(data->devt, 1);dev_info(data->dev, "Example LED driver removed\n");return 0;
}// 設備樹匹配表
static const struct of_device_id example_led_of_match[] = {{ .compatible = "example_gpio_led" },{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, example_led_of_match);// 平臺驅動結構體
static struct platform_driver example_led_driver = {.probe      = example_led_probe,.remove     = example_led_remove,.driver     = {.name   = "example_gpio_led",.of_match_table = example_led_of_match,.owner  = THIS_MODULE,},
};// 模塊初始化和退出
module_platform_driver(example_led_driver);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Example GPIO LED Driver");

4.1.3 編譯與加載

編譯設備樹:將example_gpio_led.dtsi引入到rk3568-evb1-ddr4-v10.dtsi,執行kernel編譯。
編譯驅動模塊:創建Makefile文件,內容如下:

export ARCH=arm64export CROSS_COMPILE=/home/chenmy/rk356x/RK356X_Android11.0/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-obj-m += device_tree_example.oKERNEL_DIR:=/home/chenmy/rk356x/RK356X_Android11.0/kernelall:make -C $(KERNEL_DIR) M=$(PWD) modules
clean:make -C $(KERNEL_DIR) M=$(PWD) clean

在包含device_tree_example.c和Makefile的目錄下,執行以下命令編譯驅動模塊:

make

將生成的device_tree_example.ko文件拷貝到開發板。
加載驅動模塊:在開發板上,執行以下命令加載驅動模塊:

insmod device_tree_example.ko

加載成功后,可以通過/dev/example-led設備節點控制led亮滅。

4.1.4 應用程序代碼

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_led9_MainActivity_open(JNIEnv *env, jobject thiz) {int fd;fd = open("/dev/example-led", O_RDWR);LOGD("Device opened (fd=%d)\n", fd);return fd;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_led9_MainActivity_read(JNIEnv *env, jobject thiz, jint fd) {int value = 0;if (read(fd, &value, sizeof(int)) != 0) {LOGD("Failed to read");return -1;}LOGD("Data read from device: %d\n", value);return value;}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_led9_MainActivity_write(JNIEnv *env, jobject thiz, jint fd, jint value) {if (write(fd, &value, sizeof(int)) != 0) {LOGD("Failed to write to device");return -1;}LOGD("Data written to device value = %d\n", value);return 0;
}

在這里插入圖片描述
在這里插入圖片描述

五、設備樹調試與常見問題解決

5.1 調試工具與方法?

內核日志:通過查看內核啟動日志(dmesg命令),可以獲取設備樹解析過程中的信息,如節點匹配情況、資源獲取結果等。若出現設備樹解析錯誤,內核日志會輸出相關報錯信息,如 “Failed to parse device tree node”,幫助開發者定位問題。?
設備樹查看工具:dtc工具不僅可以用于編譯設備樹,還能將編譯后的設備樹二進制文件(.dtb)反編譯為可讀的文本格式。例如:?
?
dtc -I dtb -O dts -o decompiled.dts original.dtb?
?
通過對比反編譯后的設備樹文本與原始設備樹源文件,可檢查是否存在編譯過程中的錯誤或屬性丟失問題。?
硬件調試工具:使用邏輯分析儀、示波器等硬件工具,可監測設備在初始化和運行過程中的信號狀態,判斷設備樹配置是否正確影響了硬件信號的輸出。比如,檢查 I2C 設備的 SCL 和 SDA 信號是否符合設備樹中配置的時鐘頻率和通信協議。?

5.2 常見問題及解決?

設備樹編譯錯誤:若出現 “Syntax error” 等編譯錯誤提示,需仔細檢查設備樹源文件的語法格式,確保節點名稱、屬性定義、數據類型等符合規范。特別注意括號、逗號、引號等符號的使用,以及節點的嵌套層次是否正確。?
驅動無法匹配設備:當驅動程序無法與設備樹節點匹配時,首先檢查驅動的of_match_table和設備樹節點的compatible屬性是否一致,包括字符串的大小寫、拼寫。同時,確認驅動是否正確注冊,以及內核是否支持該驅動類型。?
設備功能異常:若設備在驅動加載后功能異常,如無法正常通信、數據讀取錯誤等。可通過檢查設備樹中定義的資源是否正確,如地址、中斷號、時鐘頻率等是否與硬件實際情況相符。另外,查看驅動程序對設備樹資源的解析和使用是否正確,是否存在資源沖突問題。例如,多個設備使用了相同的中斷號,就會導致中斷處理混亂,設備無法正常工作 。?

六、總結?

設備樹作為 Linux 驅動開發中的核心技術,通過獨特的語法結構和工作機制,實現了硬件與內核的解耦,極大地提升了系統的可移植性和硬件兼容性。從基礎的節點與屬性定義,到復雜的中斷、時鐘、內存區域配置,再到與驅動開發的緊密協同,設備樹在 Linux 系統中扮演著不可或缺的角色。通過實踐案例和調試方法的學習,我們能夠更好地掌握設備樹的應用技巧,解決實際開發中遇到的問題。隨著硬件技術的不斷發展,設備樹也將持續演進,為更多新型硬件設備的支持提供有力保障,助力 Linux 系統在各個領域的廣泛應用。?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/86088.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/86088.shtml
英文地址,請注明出處:http://en.pswp.cn/web/86088.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【技巧】使用frpc安全地內網穿透ssh訪問內網機器

【技巧】使用frpc安全地內網穿透ssh訪問內網機器 0. 為什么需要部署 office的機器可以單向訪問互聯網&#xff0c;互聯網無法直接訪問到這臺機器。有時候需要從家里通過ssh遠程訪問和配置。 在云服務器上部署frpc把轉發ssh給需要訪問的機器。 1. 互聯網云服務器&#xff08;…

【Faster-Whisper】離線識別本地視頻并生成字幕

【Faster-Whisper】離線識別本地視頻并生成字幕 1 前言2 工具說明2.1 ffmpeg 媒體轉換器2.1.1 理論簡介文檔 2.1.2 安裝win安裝python安裝 2.1.3 查看查看音視頻文件格式、編碼 2.1.4 視頻處理視頻格式轉換設置 視頻碼率裁剪視頻 2.1.5 音頻處理視頻提取音頻音頻格式轉換gpu加速…

開源CMS vs 閉源CMS:二次開發究竟有何不同?

在網站建設項目中&#xff0c;內容管理系統&#xff08;CMS&#xff09; 是核心基礎設施。而“二次開發”則是讓CMS真正適配業務需求的關鍵環節&#xff0c;譬如調整頁面樣式&#xff0c;或者新增會員體系等等。但很多人沒意識到&#xff1a;選擇開源CMS還是閉源CMS&#xff0c…

npm 更新包名,本地導入

package.json 更新包根目錄名字&#xff0c;同時改 name 和 dependencies相關的依賴也需本地導入&#xff0c;否則無法生效 之后將改包放在你所需的項目位置&#xff0c;通過以下命令導入node_modules生效 pnpm install file:../table-ui/m-table -w防止包數據更新或丟棄&…

若依框架二次開發——若依前后端分離版集成 UReport2 報表工具

文章目錄 一、UReport2 簡介二、解決方案1、后端配置1.1 引入 UReport2 依賴1.2 啟動類配置1.3配置文件1.4 修改安全配置2、前端配置2.1 配置 Vue.js 代理2.2創建設計器頁面2.3 新增菜單運行結果一、UReport2 簡介 UReport2 是一款開源的 Java 報表工具,廣泛應用于各類企業管…

Ntfs!_LFCB結構如何構建出來的--從Ntfs!NtfsMountVolume到Ntfs!LfsAllocateLfcb

Ntfs!LfsRestartLogFile函數分析之調用Ntfs!LfsAllocateLfcb函數初始化Lfcb->LbcbWorkque 第一部分&#xff1a; F:\srv03rtm>grep "NtfsStartLogFile" -rn F:\srv03rtm\base\fs\ntfs |grep -v "inary" F:\srv03rtm\base\fs\ntfs/fsctrl.c:1890: …

Domain層到底是什么

層級主要職責是否依賴 iOS / UIKit&#xff1f;Presentation (UI)視圖、控制器、ViewModel&#xff0c;將用戶操作轉成「意圖」&#xff0c;把結果渲染到屏幕是Domain業務規則 與 用例 (Use Case)&#xff0c;維護系統在概念上的真實世界模型否&#xff08;純 Swift&#xff0c…

Rust 服務端項目分層結構

DDD src/ ├── main.rs # 程序入口&#xff0c;負責啟動和依賴注入 ├── lib.rs # 公共庫入口&#xff0c;便于單元測試和復用 ├── config.rs # 配置管理&#xff08;如數據庫、端口、環境變量等&#xff09; ├── entities/ …

山東大學《Web數據管理》期末復習寶典【萬字解析!】

&#x1f308; 個人主頁&#xff1a;十二月的貓-CSDN博客 &#x1f525; 系列專欄&#xff1a;&#x1f3c0;山東大學期末速通專用_十二月的貓的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻擋不了春天的腳步&#xff0c;十二點的黑夜遮蔽不住黎明的曙光 目錄 1…

解決“在EFI系統上,Windows只能安裝到GPT磁盤“錯誤

環境&#xff1a;AMI BIOS&#xff08;2012 American Megatrends,Inc.&#xff09;&#xff0c;清華同方筆記本&#xff0c;windows10安裝U盤 背景&#xff1a;使用安裝U盤安裝系統時&#xff0c;在選擇磁盤下一步&#xff0c;出現了“無法在驅動器0的分區1上安裝windows(w)。…

NetworkTermination-原始固件-分區備份

在對設備進行固件分區備份之前&#xff0c;我們需要通過設備的 debug 接口進行連接。設備的 debug 接口位于左下角&#xff0c;連接時需設置波特率為 115200。 ssh連接 設備默認的 SSH 服務配置較為嚴格&#xff0c;端口號為 33998&#xff0c;且不允許通過密碼直接登錄。我們可…

Network Manager客戶端制作小結

關聯資料 Unity 入門到精通&#xff08;沈軍&#xff09; c語言中的局部變量和全局變量_c語言全局變量-CSDN博客 關鍵詞 本地玩家信息&#xff1a;LocalClientId 網絡狀態及網絡序列化 網絡游戲服務器和客戶端 ClientRpc與ServerRpc 變量作用域與作用周期 Network Manager —…

Python 開發環境全棧隔離架構:從 Anaconda 到 PyCharm 的四級防護體系

【深度探索】Windows 下 Python 多版本虛擬環境管理與隔離實戰&#xff1a;支持 Anaconda、Poetry、Pipenv、venv、uv、Hatch、PyCharm、VS Code 全工具鏈方案-CSDN博客 【零基礎】Python 多版本虛擬環境管理與隔離實戰——支持 Anaconda、Poetry、Pipenv、venv、uv、Hatch、Py…

Redis集群性能優化實戰指南

Redis集群性能優化實戰指南 一、技術背景與應用場景 隨著互聯網服務規模不斷擴大&#xff0c;緩存層承載了海量的讀寫請求。Redis因其高性能和豐富的數據結構被廣泛用于緩存、排行榜、會話存儲等場景。單節點Redis在并發、內存和持久化方面會遇到瓶頸&#xff0c;Redis集群&a…

使用 Telegraf 向 TDengine 寫入數據

Telegraf 是一款十分流行的指標采集開源軟件。在數據采集和平臺監控系統中&#xff0c;Telegraf 可以采集多種組件的運行信息&#xff0c;而不需要自己手寫腳本定時采集&#xff0c;降低數據獲取的難度。 只需要將 Telegraf 的輸出配置增加指向 taosAdapter 對應的 url 并修改…

華為云 Flexus+DeepSeek 征文|華為云Dify 平臺 CCE 高可用集群部署與大模型高質量知識庫構建指南

華為云 FlexusDeepSeek 征文&#xff5c;華為云Dify 平臺 CCE 高可用集群部署與大模型高質量知識庫構建指南 文章目錄 華為云 FlexusDeepSeek 征文&#xff5c;華為云Dify 平臺 CCE 高可用集群部署與大模型高質量知識庫構建指南前言1、ModelArts Studio大模型開通2、Dify平臺CC…

測試校招/應屆刷題知識路線

需要重點關注計算機基礎知識和測試相關技能 對于即將參加校招或應屆的同學,測試崗位需要重點關注計算機基礎知識和測試相關技能。 掌握操作系統、計算機網絡、數據庫等計算機基礎,同時熟悉測試基礎理論、測試方法、自動化測試工具 (如 Selenium、JMeter)和編程語言(如 Py…

華為云Flexus+DeepSeek征文 | 基于華為云的 Dify-LLM 企業級 AI 開發平臺部署指南

前言 在人工智能技術迅猛發展的今天&#xff0c;大語言模型&#xff08;LLM&#xff09;已成為推動企業智能化轉型的核心引擎。然而&#xff0c;如何高效、安全地將 LLM 能力整合到實際業務場景中&#xff0c;仍然是許多開發者面臨的挑戰。Dify 作為一款開源的 LLM 應用開發平…

Dify×奇墨科技:開源+本土化,破解企業AI落地難題

在人工智能技術迅猛發展的今天&#xff0c;企業亟需高效、安全的AI應用開發工具。開源LLMOps平臺Dify.AI憑借其靈活性與開放性&#xff0c;已成為開發者構建AI應用的首選之一。而它在中國市場的規模化落地&#xff0c;離不開本土合作伙伴的強力支撐——奇墨科技作為Dify中國大陸…

Vue3+Spring boot 前后端防抖增強方案

我將基于您提供的防抖切面代碼進行改進&#xff0c;主要優化點包括使用Redis實現分布式防抖、增強鍵生成策略、改進異常處理等。以下是完整的改進方案&#xff1a; 1. 改進的防抖注解 (Debounce.java) java 復制 下載 package com.weiyu.anno;import java.lang.annotation…