2012年7月3日 星期二

GPIO 整理(一)

  • 什麼是GPIO?
GPIO全名是General Purpose Input/Output。他是一個有彈性的軟體可控制的數位信號。在許多chip例如CPU或是PMU(Power Management Unit)等等的裝置上都提供了GPIO的設計。每一個GPIO用一個bit(0/1) 來表示他的狀態。GPIO可以拿來做什麼事呢?
  1. Output Value
      輸出的值是可寫的(high=1, low=0)。例如果們可以利用 GPIO來控制LED的亮/暗。
  2. Input Value
      輸入的值是可讀的(1/0)。當然我們也可以藉由讀取GPIO來知道現在例如 LED的亮/暗
  3. Used as IRQ signal
      可以把GPIO拿來當IRQ的信號,例如利用GPIO來判斷 SDcard的"插入/拔出"的動作或是按鍵的"按/放"
  4. Alternative Use
      或甚至,我們可以利用GPIO來實做成SPI等等應用
  • 使用GPIO
在Driver裡面如果要使用GPIO可以簡單分成五個步驟:
  1. Include Header
      在Driver裡面include linux/gpio.h
      #include <linux/gpio.h>
  2. Check Valid(Optional)
      每一個chip上面的GPIO數量是有限制的(而且GPIO號碼一定>=0),為了防止指定的GPIO號碼是錯的,可以先使用gpio_is_valid來檢查。
      int gpio_is_valid(int number);//return true if valid.
  3. 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);
  4. 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);
  5. 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));

  • Reference:

沒有留言: