溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么進(jìn)行rk3288 GPIO的使用

發(fā)布時(shí)間:2021-11-06 15:23:43 來源:億速云 閱讀:170 作者:柒染 欄目:建站服務(wù)器

這篇文章將為大家詳細(xì)講解有關(guān)怎么進(jìn)行rk3288 GPIO的使用,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

GPIO 使用 

簡(jiǎn)介 

GPIO, 全稱 General-Purpose Input/Output(通用輸入輸出),是一種軟件運(yùn)行期間能夠動(dòng)態(tài)配置和控制的通用引腳。 RK3288 有 9 組 GPIO bank: GPIO0,GPIO1, …, GPIO8。每組又以 A0~A7, B0~B7, C0~C7, D0~D7 作為編號(hào)區(qū)分(不是所有 bank 都有全部編號(hào),例如 GPIO5 就只有 B0~B7, C0~C3)。

每個(gè) GPIO 口除了通用輸入輸出功能外,還可能有其它復(fù)用功能,例如 GPIO5_B4,可以復(fù)用成以下功能之一:

  • spi0_clk

  • ts0_data4

  • uart4exp_ctsn

每個(gè) GPIO 口的驅(qū)動(dòng)電流、上下拉和重置后的初始狀態(tài)都不盡相同,詳細(xì)情況請(qǐng)參考 《RK3288 規(guī)格書》中的 “RK3288 function IO description” 一章。

RK3288 的 GPIO 驅(qū)動(dòng)是在以下 pinctrl 文件中實(shí)現(xiàn)的:

kernel/drivers/pinctrl/pinctrl-rockchip.c

其核心是填充 GPIO bank 的方法和參數(shù),并調(diào)用 gpiochip_add 注冊(cè)到內(nèi)核中。

使用 

sunychip-rk3288開發(fā)板有兩個(gè)電源 LED 燈是 GPIO 口控制的,分別是: 怎么進(jìn)行rk3288 GPIO的使用

從電路圖上看,GPIO 口輸出低電平時(shí)燈亮,高電平時(shí)燈滅。 另外,擴(kuò)展槽上引出了幾個(gè)空閑的 GPIO 口,分別是:

怎么進(jìn)行rk3288 GPIO的使用

這幾個(gè) GPIO 口可以自定義作輸入、輸出使用。

輸入輸出 

下面以電源 LED 燈的驅(qū)動(dòng)為例,講述如何在內(nèi)核編寫代碼控制 GPIO 口的輸出。 首先需要在 dts (Device Tree) 文件 firefly-rk3288.dts 中增加驅(qū)動(dòng)的資源描述:

firefly-led{
    compatible = "firefly,led";
    led-work = <&gpio8 GPIO_A2 GPIO_ACTIVE_LOW>;
    led-power = <&gpio8 GPIO_A1 GPIO_ACTIVE_LOW>;
    status = "okay";};

這里定義了兩顆 LED 燈的 GPIO 設(shè)置:

led-work  GPIO8_A2  GPIO_ACTIVE_LOWled-power GPIO8_A1  GPIO_ACTIVE_LOW

GPIO_ACTIVE_LOW 表示低電平有效(燈亮),如果是高電平有效,需要替換為 GPIO_ACTIVE_HIGH 。 之后在驅(qū)動(dòng)程序中加入對(duì) GPIO 口的申請(qǐng)和控制則可:

#ifdef  CONFIG_OF
#include <linux/of.h>
#include <linux/of_gpio.h>
#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 配置編號(hào)和標(biāo)志,gpio_is_valid 判斷該 GPIO 編號(hào)是否有效,gpio_request 則申請(qǐng)占用該 GPIO。如果初始化過程出錯(cuò),需要調(diào)用 gpio_free 來釋放之前申請(qǐng)過且成功的 GPIO 。 調(diào)用 gpio_direction_output 就可以設(shè)置輸出高還是低電平,因?yàn)槭?GPIO_ACTIVE_LOW ,如果要燈亮,需要寫入 0 。 實(shí)際中如果要讀出 GPIO,需要先設(shè)置成輸入模式,然后再讀取值:

int val;gpio_direction_input(your_gpio);val = gpio_get_value(your_gpio);

下面是常用的 GPIO API 定義:

#include <linux/gpio.h>#include <linux/of_gpio.h> 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ù)用,在運(yùn)行時(shí)又如何切換功能呢?以 I2C4 為例作簡(jiǎn)單的介紹。 查規(guī)格表可知,I2C4_SDA 與 I2C4_SCL 的功能定義如下: 怎么進(jìn)行rk3288 GPIO的使用

在 /kernel/arch/arm/boot/dts/rk3288.dtsi 里有:

i2c4: i2c@ff160000 {
    compatible = "rockchip,rk30-i2c";
    reg = <0xff160000 0x1000>;
    interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
    #address-cells = <1>;
    #size-cells = <0>;
    pinctrl-names = "default", "gpio";
    pinctrl-0 = <&i2c4_sda &i2c4_scl>;
    pinctrl-1 = <&i2c4_gpio>;
    gpios = <&gpio7 GPIO_C1 GPIO_ACTIVE_LOW>, <&gpio7 GPIO_C2 GPIO_ACTIVE_LOW>;
    clocks = <&clk_gates6 15>;
    rockchip,check-idle = <1>;
    status = "disabled";};
  • 此處,跟復(fù)用控制相關(guān)的是 pinctrl- 開頭的屬性:

    • pinctrl-names 定義了狀態(tài)名稱列表: default (i2c 功能) 和 gpio 兩種狀態(tài)。

    • pinctrl-0 定義了狀態(tài) 0 (即 default)時(shí)需要設(shè)置的 pinctrl: i2c4_sda 和 i2c4_scl

    • pinctrl-1 定義了狀態(tài) 1 (即 gpio)時(shí)需要設(shè)置的 pinctrl: i2c4_gpio

這些 pinctrl 在 /kernel/arch/arm/boot/dts/rk3288-pinctrl.dtsi 中定義:

/ { 
    pinctrl: pinctrl@ff770000 {
        compatible = "rockchip,rk3288-pinctrl";
        ...     
        gpio7_i2c4 {
            i2c4_sda:i2c4-sda {
                rockchip,pins = <I2C4TP_SDA>;
                rockchip,pull = <VALUE_PULL_DISABLE>;
                rockchip,drive = <VALUE_DRV_DEFAULT>;
                //rockchip,tristate = <VALUE_TRI_DEFAULT>;
            };
            i2c4_scl:i2c4-scl {
                rockchip,pins = <I2C4TP_SCL>;
                rockchip,pull = <VALUE_PULL_DISABLE>;
                rockchip,drive = <VALUE_DRV_DEFAULT>;
                //rockchip,tristate = <VALUE_TRI_DEFAULT>;
            };
            i2c4_gpio: i2c4-gpio {
                rockchip,pins = <FUNC_TO_GPIO(I2C4TP_SDA)>, <FUNC_TO_GPIO(I2C4TP_SCL)>;
                rockchip,drive = <VALUE_DRV_DEFAULT>;
            };
        };
        ...
    }
  }

I2C4TP_SDA, I2C4TP_SCL 的定義在 /kernel/arch/arm/boot/dts/include/dt-bindings/pinctrl/rockchip-rk3288.h 中:

#define GPIO7_C1 0x7c10#define I2C4TP_SDA 0x7c11 #define GPIO7_C2 0x7c20#define I2C4TP_SCL 0x7c21

FUN_TO_GPIO 的定義在 /kernel/arch/arm/boot/dts/include/dt-bindings/pinctrl/rockchip.h 中:

#define FUNC_TO_GPIO(m)     ((m) & 0xfff0)

也就是說 FUNC_TO_GPIO(I2C4TP_SDA) == GPIO7_C1, FUNC_TO_GPIO(I2C4TP_SCL) == GPIO7_C2 。 像 0x7c11 這樣的值是有編碼規(guī)則的:

7 c1 1
| |  `- func
| `---- offset
`------ bank
0x7c11 就表示 GPIO7_C1 func1, 即 i2c4tp_sda 。

在復(fù)用時(shí),如果選擇了 “default” (即 i2c 功能),系統(tǒng)會(huì)應(yīng)用 i2c4_sda 和 i2c4_scl 這兩個(gè) pinctrl,最終得將 GPIO7_C1 和 GPIO7_C2 兩個(gè)針腳切換成對(duì)應(yīng)的 i2c 功能;而如果選擇了 “gpio” ,系統(tǒng)會(huì)應(yīng)用 i2c4_gpio 這個(gè) pinctrl,將 GPIO7_C1 和 GPIO7_C2 兩個(gè)針腳還原為 GPIO 功能。

我們看看 i2c 的驅(qū)動(dòng)程序 /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è)備樹中 i2c4 結(jié)點(diǎn)的 gpios 屬于所定義的兩個(gè) gpio:

gpios = <&gpio7 GPIO_C1 GPIO_ACTIVE_LOW>, <&gpio7 GPIO_C2 GPIO_ACTIVE_LOW>;

然后是調(diào)用 devm_gpio_request 來申請(qǐng) 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 <linux/pinctrl/consumer.h> struct device {
    //...
    #ifdef 
    CONFIG_PINCTRLstruct 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);

關(guān)于怎么進(jìn)行rk3288 GPIO的使用就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI