Linux驅動開發09:【裝置樹】nanopi的LED驅動
介紹
這節通過在nanopi的裝置樹中新增LED子節點,來實現一個LED驅動。由於linux核心支援LED驅動框架並且有通用的LED驅動,因此這裡只需按照驅動要求新增裝置樹節點就可以了,不用我們自己重寫LED驅動。這一節先在裝置樹中新增一個LED節點,重新編譯裝置樹測試效果,然後對核心中drivers/leds/leds-gpio.c
進行分析,看該LED驅動是如何實現的。
新增裝置樹節點
我們在sun8i-h3-nanopi-neo-air.dts中新增如下內容:
/ {
...
mygpio-leds {
compatible = "gpio-leds" ; /* 才能被識別為Led */
pinctrl-names = "default";
pinctrl-0 = <&test_pin>;
test {
label = "nanopi:red:test";
gpios = <&pio 6 11 GPIO_ACTIVE_HIGH>; /* PG11 */
default-state = "on";
};
};
}
&pio {
...
test_pin: [email protected]0 {
pins = "PG11";
function = "gpio_out";
};
}
該節點分為兩部分,第一部分是mygpio-leds子節點,第二部分是[email protected]子節點。第一個節點是根節點下的一個子節點,根據上一節的分析,該節點將被系統解析為一個platform_device。第二個節點是pio的子節點,該節點表明了對應的GPIO引腳編號和該GPIO的模式,這是pinctrl相關的內容,對於pinctrl不同的平臺實現不同,這裡nanopi的pinctrl是用字串來表示引腳號和模式(似乎nanopi的pinctrl只支援設定pin的模式,並不能直接指定該引腳的輸出值)。
再看mygpio-leds節點,其中的compatible = "gpio-leds"
,用於與platform_drivers匹配,pinctrl-0 = <&test_pin>
引用了[email protected]
子節點,表明要設定PG11為輸出模式,緊接著是一個子節點test,其中label = "nanopi:red:test"
表示該LED名稱,gpios = <&pio 6 11 GPIO_ACTIVE_HIGH>
引用了pio標號,該標號定義在sun8i-h3-h5.dtsi檔案中:
pio: [email protected]01c20800 {
/* compatible is in per SoC .dtsi file */
reg = <0x01c20800 0x400>;
interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_BUS_PIO>, <&osc24M>, <&osc32k>;
clock-names = "apb", "hosc", "losc";
gpio-controller;
#gpio-cells = <3>;
interrupt-controller;
#interrupt-cells = <3>;
...
}
注意其中的#gpio-cells = <3>
屬性,它表明引用該節點時需要三個引數,這在前面有提到,這三個引數的分別表示該GPIO的bank、編號和初始化後預設輸出模式。這裡bank=6,按照資料手冊上的定義,0表示A,1表示B…6就表示G,編號為11,也就是PG11,初始化為高電平。
測試
我們先進行測試,重新編譯寫好的裝置樹,然後拷貝到SD卡中,啟動nanopi,進入/sys/class/leds
目錄可以看到我們定義的”nanopi:red:test”檔案
進入該資料夾的device/of_node
目錄可以看到我們定義的屬性
最後退回到/sys/class/leds/nanopi:red:test
目錄,對brightness
檔案進行操作,可控制PG11輸出高電平和低電平,如果在PG11上接一個LED可以看到該LED的亮滅
LED驅動的實現分析
核心中實現LED驅動的檔案是drivers/leds/leds-gpio.c
,這是一個platform驅動,下面對其進行分析,先看platform_driver的定義。
static const struct of_device_id of_gpio_leds_match[] = {
{ .compatible = "gpio-leds", },
{},
};
static struct platform_driver gpio_led_driver = {
.probe = gpio_led_probe,
.shutdown = gpio_led_shutdown,
.driver = {
.name = "leds-gpio",
.of_match_table = of_gpio_leds_match,
},
};
module_platform_driver(gpio_led_driver);
我們定義的mygpio-leds中,compatible = "gpio-leds"
,因此可以執行gpio_led_probe
函式
struct gpio_leds_priv {
int num_leds;
struct gpio_led_data leds[]; // 不定長陣列,使用時才分配長度
};
static inline int sizeof_gpio_leds_priv(int num_leds)
{
return sizeof(struct gpio_leds_priv) +
(sizeof(struct gpio_led_data) * num_leds);
}
drivers --- leds --- leds-gpio.c --- gpio_led_probe( --- struct gpio_led_platform_data *pdata
| struct platform_device *pdev) |- pdata = dev_get_platdata(&pdev->dev)
| |- if (pdata && pdata->num_leds) {
| | ...
| | } else {
| | priv = gpio_leds_create(pdev)
| | }
| |- platform_set_drvdata(pdev, priv)
|- gpio_leds_create( --- int count
| struct platform_device *pdev) |- struct gpio_leds_priv *priv
| |- struct fwnode_handle *child
| |- count = device_get_child_node_count(dev)
| |- priv = devm_kzalloc(dev,
| | sizeof_gpio_leds_priv(count), GFP_KERNEL)
| |- device_for_each_child_node(dev, child) {
| | struct gpio_led_data *led_dat;
| | struct gpio_led led = {};
| | struct device_node *np;
| | np = to_of_node(child);
| | led_dat = &priv->leds[priv->num_leds];
| |* fwnode_property_read_string(child,
| | "label", &led.name);
| |* led.gpiod = devm_fwnode_get_gpiod_from_child(
| | dev, NULL, child, GPIOD_ASIS, led.name);
| | // 獲取gpio_desc
| |* create_gpio_led(&led, led_dat, dev, NULL);
| | // 註冊到LED驅動框架
| | priv->num_leds++;
| |- }
|- create_gpio_led( --- led_dat->gpiod = template->gpiod;
const struct gpio_led *template, |- if (!led_dat->gpiod) { // 這裡不是空
struct gpio_led_data *led_dat, | ...
struct device *parent, | }
gpio_blink_set_t blink_set) |- led_dat->cdev.name = template->name
|- 對led_dat的一系列賦值
|* gpiod_direction_output(led_dat->gpiod, state);
|* devm_led_classdev_register(parent, &led_dat->cdev);
由於我們解析裝置樹為platform_device時並沒有設定platform_data,因此執行gpio_leds_create
函式對裝置樹節點進行解析。首先獲取子節點的數目,對gpio_leds_priv進行動態初始化,遍歷子節點(這裡只有一個子節點),獲取每個子節點的label屬性,然後通過lable屬性獲取gpio_desc,然後呼叫create_gpio_led對LED驅動進行註冊,其中對led_dat->cdev進行了設定,然後利用LED驅動框架將其註冊到系統
linux的LED框架這裡不展開了,該框架與應用層的介面是sysfs,通過讀寫sysfs的檔案,來控制LED的亮滅。
相關推薦
Linux驅動開發09:【裝置樹】nanopi的LED驅動
介紹 這節通過在nanopi的裝置樹中新增LED子節點,來實現一個LED驅動。由於linux核心支援LED驅動框架並且有通用的LED驅動,因此這裡只需按照驅動要求新增裝置樹節點就可以了,不用我們自己重寫LED驅動。這一節先在裝置樹中新增一個LED節點,重新編譯
Linux驅動開發08:【裝置樹】MPU6050驅動和i2c驅動
介紹 上一節在nanopi裝置樹的I2C節點下增加了一個MPU6050的子節點,並在sysfs中檢視到了該節點已經被正確解析,這一節我們來修改之前的MPU6050驅動,使之能夠匹配到我們的裝置樹節點,然後再分析裝置樹節點是如何載入到i2c總線上的。 MP
Linux驅動開發11:【裝置樹】nanopi的PWM驅動
介紹 前兩節利用裝置樹實現了nanopi的LED驅動和按鍵驅動,這一節來實現nonapi的PWM驅動。PWM驅動在核心中也有相應的實現,因此這裡只是按照要求新增裝置樹檔案即可。這一節和之前一樣,首先修改裝置樹檔案進行測試,然後分析核心相應的軟體實現。 新增裝置樹節點 因為在s
Linux驅動開發10:【裝置樹】nanopi的按鍵驅動
介紹 這一節在nanopi上實現按鍵驅動,和LED驅動一樣,通用的按鍵驅動在linux核心中已經實現好,我們只需要按照要求寫好裝置樹即可,不用我們自己實現按鍵驅動。這一節中首先修改裝置樹並測試按鍵驅動,然後分析drivers/input/keyboard/gpio_keys.c檔案,
Linux驅動開發04:塊裝置驅動和網路裝置驅動
介紹 因為塊裝置驅動和網路裝置驅動實際中用得較少,所以只給出驅動模板,我也沒有具體測試,等到實際用到是再研究吧,溜了溜了。 塊裝置驅動模板 struct xxx_dev { int size; struct request_q
ARM+Linux嵌入式開發05:【uboot-2017移植】重定位
概述 上一節初始化好了串列埠和LED,我們可以用它們進行除錯;也設定好了時鐘和DRAM,為uboot的重定位做好準備。之前所做的所有工作都是在BL1中,也就是uboot的前16KB,而大部分uboot的程式碼還在SD卡中沒有載入記憶體,沒有載入記憶體的原因是之前我們使用的是內部SRA
Exynos4412 中斷驅動開發(三)—— 裝置樹中中斷節點的建立
https://www.cnblogs.com/tureno/articles/6403946.html 轉載於 : http://blog.csdn.net/zqixiao_09/article/details/50916212 題目: Exynos4412 中斷驅動
Linux核心移植 part2:uboot裝置樹--生成過程分析
本文從裝置樹軟體控制相關程式碼進行分析,進而理清裝置樹相關的知識。 先放一個裝置樹在記憶體中的結構圖: 分析來源為$(tree)/lib/fdtdec_test.c 一、資料結構 1.1 檔案頭 每個dtb都包含如下結構的檔案頭,用來表示裝
Linux核心移植 part2:uboot 裝置樹--基本概念和原始碼介紹
arm uboot的裝置樹原始檔位於arch/arm/dts/目錄下,網路上有很多介紹Linux裝置樹概念的文章,這裡以dts相關的API為切入點,如果都懂了,裝置樹的東西就迎刃而解了。本篇文章首先記錄一些基本知識,下一篇進行原始碼分析。 一、裝置樹檔案基
【TINY4412】LINUX移植筆記:(23)裝置樹LCD觸控式螢幕驅動
【TINY4412】LINUX移植筆記:(23)裝置樹 LCD觸控式螢幕驅動 宿主機 : 虛擬機器 Ubuntu 16.04 LTS / X64 目標板[底板]: Tiny4412SDK - 1506 目標板[核心板]
【TINY4412】LINUX移植筆記:(22)裝置樹LCD按鍵驅動
【TINY4412】LINUX移植筆記:(22)裝置樹 LCD按鍵驅動 宿主機 : 虛擬機器 Ubuntu 16.04 LTS / X64 目標板[底板]: Tiny4412SDK - 1506 目標板[核心板]:
嵌入式Linux裝置驅動開發之:按鍵驅動程式例項
11.6 按鍵驅動程式例項 11.6.1 按鍵工作原理 高電平和低電平相接怎麼會變成低電平呢 就像你把電源正極的負極相連一樣會把電壓拉低。大電流會從高電平引腳流向低電平引腳,把高電平引腳拉低。 LED和蜂鳴器是最簡單的GPIO的應用,都不需要任何外部
04-Linux裝置樹系列-GPIO驅動實踐
1. 前言 GPIO驅動開發可能算是Linux核心裝置驅動開發中最為簡單、最常見的一個方向,對於開發板的按鍵、LED、蜂鳴器、電源控制等模組,可能都是使用GPIO實現的。Linux核心的GPIO子系統在核心不斷的演進過程中進行了多次的重構,本文的第二
linux驅動由淺入深系列:塊裝置驅動之三(塊裝置驅動結構分析,以mmc為例)
linux驅動由淺入深系列:塊裝置驅動之一(高通eMMC分割槽例項)前一篇文章介紹了塊裝置驅動在linux框架張的位置關係,本文來分析一下驅動本身。塊裝置驅動的模型還是基本基於字元裝置驅動的,可以簡單理解為塊裝置僅僅增加了操作緩衝區,對使用者操作請求進行佇列重排。因此只在有了
Linux ALSA音效卡驅動之三:PCM裝置的建立
1. PCM是什麼 PCM是英文Pulse-code modulation的縮寫,中文譯名是脈衝編碼調製。我們知道在現實生活中,人耳聽到的聲音是模擬訊號,PCM就是要把聲音從模擬轉換成數字訊號的一種技術,他的原理簡單地說就是利用一個固定的頻率對模擬訊號進行取
linux驅動開發-檔案系統與裝置檔案
目錄 1.Linux檔案系統操作 Linux檔案建立,開啟,關閉函式 #檔案許可權最終由mode&umask決定 int creat (const char *filename,mode_t mod
Linux ALSA音效卡驅動之五:移動裝置中的ALSA
1. ASoC的由來 ASoC--ALSA System on Chip ,是建立在標準ALSA驅動層上,為了更好地支援嵌入式處理器和移動裝置中的音訊Codec的一套軟體體系。在ASoc出現之前,核心對於SoC中的音訊已經有部分的支援,不過會有一些侷限性:
ZYNQ Linux驅動開發——第一個字元裝置驅動
硬體平臺:XCZ7020 CLG484-1 完全適配Zedboard 開發環境:Widows下Vivado 2016.2 、 SDK2016.2 、 Linux機器:debin 目的:操作板載的LED燈LD9,受PS部分的MIO7控制 linux裝置驅
Linux ALSA音效卡驅動之五:移動裝置中的ALSA(ASoC)
1. ASoC的由來 ASoC--ALSA System on Chip ,是建立在標準ALSA驅動層上,為了更好地支援嵌入式處理器和移動裝置中的音訊Codec的一套軟體體系。在ASoc出現之前,核心對於SoC中的音訊已經有部分的支援,不過會有一些侷限性: C
Linux ALSA音效卡驅動之四:Control裝置的建立
Control介面 Control介面主要讓使用者空間的應用程式(alsa-lib)可以訪問和控制音訊codec晶片中的多路開關,滑動控制元件等。對於Mixer(混音)來說,Control介面顯得尤為重要,從ALSA 0.9.x版本開始,所有的mixer工作都是通過co