GPIO全名是General Purpose Input/Output。他是一個有彈性的軟體可控制的數位信號。在許多chip例如CPU或是PMU(Power Management Unit)等等的裝置上都提供了GPIO的設計。每一個GPIO用一個bit(0/1) 來表示他的狀態。GPIO可以拿來做什麼事呢?
- Output Value
輸出的值是可寫的(high=1, low=0)。例如果們可以利用 GPIO來控制LED的亮/暗。
- Input Value
輸入的值是可讀的(1/0)。當然我們也可以藉由讀取GPIO來知道現在例如 LED的亮/暗
- Used as IRQ signal
可以把GPIO拿來當IRQ的信號,例如利用GPIO來判斷 SDcard的"插入/拔出"的動作或是按鍵的"按/放"
- Alternative Use
或甚至,我們可以利用GPIO來實做成SPI等等應用
在Driver裡面如果要使用GPIO可以簡單分成五個步驟:
- Include Header
在Driver裡面include linux/gpio.h
#include <linux/gpio.h>
- Check Valid(Optional)
每一個chip上面的GPIO數量是有限制的(而且GPIO號碼一定>=0),為了防止指定的GPIO號碼是錯的,可以先使用gpio_is_valid來檢查。
int gpio_is_valid(int number);//return true if valid.
- Request GPIO(Optional, but recommended)
gpio_request 可以檢查GPIO number是否超出範圍或<0 及 指定的GPIO是否正在使用。有時候別的地方也控制著同一個GPIO 可能會發生程式預期外的結果
/**
*return -EINVAL if GPIO is not valid(same as gpio_is_valid)
*return -EBUSY if GPIO is already used
*return 0 if GPIO if fine to use
*因此我們只要檢查回傳質是否>=0就可以判斷是否可用。
*第一個參數是填入GPIO的號碼、第二個參數是想要對這個GPIO的命名。
*$ cat /sys/kernel/debug/gpio就可以看到
*/
int gpio_request(unsigned gpio, const char *label);
- Write/Read Value
//return 1 or 0
int gpio_direction_input(unsigned gpio);
/**
*gpio_direction_output會作許多檢查,例如該GPIO是否可以設high?low?
*也會呼叫gpio_is_valid來檢查是否可用等等
*第一個參數是GPIO號碼
*第二個參數是要寫入的值(0/1)
*/
int gpio_direction_output(unsigned gpio, int value);
- Free GPIO
當使用完GPIO之後利用gpio_free來釋放GPIO 。
int gpio_free(unsigned gpio);
將上述串起來以對CARD_DETECT_GPIO寫入1為例:
#include <linux/gpio.h>
...
int ret = gpio_request(CARD_DETECT_GPIO, "CARD_DETECT");
if (ret<0)
pr_err("%s: gpio_request failed for gpio %d\n",
__func__, CARD_DETECT_GPIO);
else
gpio_direction_output(CARD_DETECT_GPIO, 1);
gpio_free(CARD_DETECT_GPIO);
/**
*當我們對某個GPIO設定direction_output之後
*我們可以直接透過gpio_set_value去改變output的值
*/
void gpio_set_value(unsigned gpio, int value)
/**
*某些裝置像omap允許設置debounce time(不是所有裝置都有支援)。
*/
int gpio_set_debounce(unsigned gpio, unsigned debounce)
/**
*對於外部的GPIO controller裝置可能是透過SOC的I2C或其他bus來連接,要
*控制裝置上的GPIO就必須透過I2C/其他bus來傳送命令。如果這個裝置是sleepable
*這個時候就必須使用_cansleep了。
*/
int gpio_get_value_cansleep(unsigned gpio);
void gpio_set_value_cansleep(unsigned gpio, int value);
/**
*gpio_request_*簡化了gpio_request和gpio_direction_output/input。
*透過一個function就可以完成 request及設定direction及初始值。
*第一個參數和第三個參數同gpio_request, 第二個參數如下:
* GPIOF_DIR_IN - to configure direction as input
* GPIOF_OUT_INIT_LOW - configured as output, initial level LOW
* GPIOF_OUT_INIT_HIGH - configured as output, initial level HIGH
*/
//request/set_direction one GPIO in a single call
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
/**
*如果我們要同時request多個GPIO的話,gpiolib也定義了一個GPIO的struct
*可讓我們建成一個array丟進gpio_request_array。Struct的組成如下
*/
struct gpio {
unsigned gpio;
unsigned long flags;
const char *label;
};
//request/set_direction multi-GPIO in a single call
int gpio_request_array(struct gpio *array, size_t num);
//free multi-GPIO in a single call
void gpio_free_array(struct gpio *array, size_t num);
下面的範例示範了如何使用gpio_request_one及 同時設定多組GPIO
static struct gpio leds_gpios[] = {
{ 32, GPIOF_OUT_INIT_HIGH, "Power LED" }, /* default to ON */
{ 33, GPIOF_OUT_INIT_LOW, "Green LED" }, /* default to OFF */
{ 34, GPIOF_OUT_INIT_LOW, "Red LED" }, /* default to OFF */
{ 35, GPIOF_OUT_INIT_LOW, "Blue LED" }, /* default to OFF */
{ ... },
};
//request/set_direction in a single call
err = gpio_request_one(31, GPIOF_IN, "Reset Button");
if (err)
...
//request/set_direction multi-GPIO in a single call
err = gpio_request_array(leds_gpios, ARRAY_SIZE(leds_gpios));
if (err)
...
gpio_free_array(leds_gpios, ARRAY_SIZE(leds_gpios));