GPIO 使用
簡介
GPIO, 全稱 General-Purpose Input/Output(通用輸入輸出),是一種軟件運行期間能夠動態(tài)配置和控制的通用引腳。
AIO-3128C 有 4 組 GPIO bank: GPIO0,GPIO1, GPIO2, GPIO3。每組又以 A0~A7, B0~B7, C0~C7, D0~D7 作為編號區(qū)分。
每個 GPIO 口除了通用輸入輸出功能外,還可能有其它復(fù)用功能,例如 GPIO1_C2,可以復(fù)用成以下功能之一:
GPIO1_C2
SDMMC0_D0
UART2_TX
每個 GPIO 口的驅(qū)動電流、上下拉和重置后的初始狀態(tài)都不盡相同,詳細(xì)情況請參考《RK3128 規(guī)格書》中的 “RK3128 function IO description” 一章。
AIO-3128C 的 GPIO 驅(qū)動是在以下 pinctrl 文件中實現(xiàn)的:
kernel/drivers/pinctrl/pinctrl-rockchip.c
其核心是填充 GPIO bank 的方法和參數(shù),并調(diào)用 gpiochip_add 注冊到內(nèi)核中。
使用
開發(fā)板有兩個電源 LED 燈是 GPIO 口控制的,分別是:
從電路圖上看,GPIO 口輸出低電平時燈亮,高電平時燈滅。
輸入輸出
下面以電源 LED 燈的驅(qū)動為例,講述如何在內(nèi)核編寫代碼控制 GPIO 口的輸出。
首先需要在 aio-3128c.dts 中增加驅(qū)動的資源描述:
firefly-led{ compatible = "firefly,led"; led-work = <&gpio1 GPIO_C6 GPIO_ACTIVE_LOW>; led-power = <&gpio1 GPIO_C7 GPIO_ACTIVE_LOW>; status = "okay"; };
這里定義了兩顆 LED 燈的 GPIO 設(shè)置:
led-work GPIO1_C6 GPIO_ACTIVE_LOW led-power GPIO1_C7 GPIO_ACTIVE_LOW
GPIO_ACTIVE_LOW 表示低電平有效(燈亮),如果是高電平有效,需要替換為 GPIO_ACTIVE_HIGH 。 之后在驅(qū)動程序中加入對 GPIO 口的申請和控制則可:
#ifdef CONFIG_OF #include #include #endif static int firefly_led_probe(struct platform_device *pdev) { int ret = -1; int gpio, flag; struct device_node *led_node = pdev->dev.of_node; gpio = of_get_named_gpio_flags(led_node, "led-power", 0, &flag); if (!gpio_is_valid(gpio)){ printk("invalid led-power: %d\n",gpio); return -1; } if (gpio_request(gpio, "led_power")) { printk("gpio %d request failed!\n",gpio); return ret; } led_info.power_gpio = gpio; led_info.power_enable_value = (flag == OF_GPIO_ACTIVE_LOW) ? 0 : 1; gpio_direction_output(led_info.power_gpio, !(led_info.power_enable_value)); ... on_error:gpio_free(gpio); }
of_get_named_gpio_flags 從設(shè)備樹中讀取 led-power 的 GPIO 配置編號和標(biāo)志,gpio_is_valid 判斷該 GPIO 編號是否有效,gpio_request 則申請占用該 GPIO。如果初始化過程出錯,需要調(diào)用 gpio_free 來釋放之前申請過且成功的 GPIO 。
調(diào)用 gpio_direction_output 就可以設(shè)置輸出高還是低電平,因為是 GPIO_ACTIVE_LOW ,如果要燈亮,需要寫入 0 。
實際中如果要讀出 GPIO,需要先設(shè)置成輸入模式,然后再讀取值:
int val;gpio_direction_input(your_gpio);val = gpio_get_value(your_gpio);
下面是常用的 GPIO API 定義:
#include #include enum of_gpio_flags { OF_GPIO_ACTIVE_LOW = 0x1, }; int of_get_named_gpio_flags(struct device_node *np, const char *propname, int index, enum of_gpio_flags *flags); int gpio_is_valid(int gpio); int gpio_request(unsigned gpio, const char *label); void gpio_free(unsigned gpio); int gpio_direction_input(int gpio); int gpio_direction_output(int gpio, int v);
復(fù)用
如何定義 GPIO 有哪些功能可以復(fù)用,在運行時又如何切換功能呢?以 I2C1 為例作簡單的介紹。查規(guī)格表可知,I2C1_SDA 與 I2C1_SCL 的功能定義如下:
在 /kernel/arch/arm/boot/dts/rk312x.dtsi 里有:
i2c1: i2c@20056000 { compatible = "rockchip,rk30-i2c"; reg = <0x20056000 0x1000>; interrupts =
此處,跟復(fù)用控制相關(guān)的是 pinctrl- 開頭的屬性:
pinctrl-names 定義了狀態(tài)名稱列表: default (i2c 功能) 和 gpio 兩種狀態(tài)。
pinctrl-0 定義了狀態(tài) 0 (即 default)時需要設(shè)置的 pinctrl: i2c1_sda 和 i2c1_scl
pinctrl-1 定義了狀態(tài) 1 (即 gpio)時需要設(shè)置的 pinctrl: i2c1_gpio
這些 pinctrl 在 /kernel/arch/arm/boot/dts/rk312x-pinctrl.dtsi 中定義:
/ { pinctrl: pinctrl@20008000 { compatible = "rockchip,rk312x-pinctrl"; ... gpio0_i2c1 { i2c1_sda:i2c1-sda { rockchip,pins =
I2C1_SDA, I2C1_SCL 的定義在 /kernel/arch/arm/boot/dts/include/dt-bindings/pinctrl/rockchip-rk312x.h 中:
#define GPIO0_A3 0x0a30#define I2C1_SDA 0x0a31#define MMC1_CMD 0x0a32 #define GPIO0_A2 0x0a20#define I2C1_SCL 0x0a21
FUN_TO_GPIO 的定義在 /kernel/arch/arm/boot/dts/include/dt-bindings/pinctrl/rockchip.h 中:
#define FUNC_TO_GPIO(m) ((m) & 0xfff0)
也就是說 FUNC_TO_GPIO(I2C1_SDA) == GPIO0_A3, FUNC_TO_GPIO(I2C1_SCL) == GPIO7_A2 。 像 0x0a31 這樣的值是有編碼規(guī)則的:
0 a3 1 | | `- func | `---- offset `------ bank
0x0a31 就表示 GPIO0_A3 func1, 即 I2C1_SDA 。
在復(fù)用時,如果選擇了 “default” (即 i2c 功能),系統(tǒng)會應(yīng)用 i2c1_sda 和 i2c1_scl 這兩個 pinctrl,最終得將 GPIO0_A3 和 GPIO0_A2 兩個針腳切換成對應(yīng)的 i2c 功能;而如果選擇了 “gpio” ,系統(tǒng)會應(yīng)用 i2c1_gpio 這個 pinctrl,將 GPIO0_A3 和 GPIO0_A2 兩個針腳還原為 GPIO 功能。我們看看 i2c 的驅(qū)動程序 /kernel/drivers/i2c/busses/i2c-rockchip.c 是如何切換復(fù)用功能的:
static int rockchip_i2c_probe(struct platform_device *pdev){ struct rockchip_i2c *i2c = NULL; struct resource *res; struct device_node *np = pdev->dev.of_node; int ret; // ... i2c->sda_gpio = of_get_gpio(np, 0); if (!gpio_is_valid(i2c->sda_gpio)) { dev_err(&pdev->dev, "sda gpio is invalid\n"); return -EINVAL; } ret = devm_gpio_request(&pdev->dev, i2c->sda_gpio, dev_name(&i2c->adap.dev)); if (ret) { dev_err(&pdev->dev, "failed to request sda gpio\n"); return ret; } i2c->scl_gpio = of_get_gpio(np, 1); if (!gpio_is_valid(i2c->scl_gpio)) { dev_err(&pdev->dev, "scl gpio is invalid\n"); return -EINVAL; } ret = devm_gpio_request(&pdev->dev, i2c->scl_gpio, dev_name(&i2c->adap.dev)); if (ret) { dev_err(&pdev->dev, "failed to request scl gpio\n"); return ret; } i2c->gpio_state = pinctrl_lookup_state(i2c->dev->pins->p, "gpio"); if (IS_ERR(i2c->gpio_state)) { dev_err(&pdev->dev, "no gpio pinctrl state\n"); return PTR_ERR(i2c->gpio_state); } pinctrl_select_state(i2c->dev->pins->p, i2c->gpio_state); gpio_direction_input(i2c->sda_gpio); gpio_direction_input(i2c->scl_gpio); pinctrl_select_state(i2c->dev->pins->p, i2c->dev->pins->default_state); // ... }
首先是調(diào)用 of_get_gpio 取出設(shè)備樹中 i2c1 結(jié)點的 gpios 屬于所定義的兩個 gpio:
gpios = <&gpio0 GPIO_A3 GPIO_ACTIVE_LOW>, <&gpio0 GPIO_A2 GPIO_ACTIVE_LOW>;
然后是調(diào)用 devm_gpio_request 來申請 gpio,接著是調(diào)用 pinctrl_lookup_state 來查找 “gpio” 狀態(tài),而默認(rèn)狀態(tài) “default” 已經(jīng)由框架保存到 i2c->dev-pins->default_state 中了。最后調(diào)用 pinctrl_select_state 來選擇是 “default” 還是 “gpio” 功能。 下面是常用的復(fù)用 API 定義:
#include struct device { //... #ifdef CONFIG_PINCTRL struct dev_pin_info *pins;#endif//...}; struct dev_pin_info {struct pinctrl *p; struct pinctrl_state *default_state; #ifdef CONFIG_PMstruct pinctrl_state *sleep_state; struct pinctrl_state *idle_state;#endif}; struct pinctrl_state * pinctrl_lookup_state(struct pinctrl *p, const char *name); int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s); //... };
-
Linux
+關(guān)注
關(guān)注
87文章
11329瀏覽量
209975 -
嵌入式主板
+關(guān)注
關(guān)注
7文章
6086瀏覽量
35490 -
Firefly
+關(guān)注
關(guān)注
2文章
538瀏覽量
7075
發(fā)布評論請先 登錄
相關(guān)推薦
評論