Raspberry Pi の Drive Strength を変更して少し大きめの電流を流す


## 参考にしたWebページ
[1] http://ja.scribd.com/doc/101830961/GPIO-Pads-Control2
[2] http://www.thebox.myzen.co.uk/Raspberry/Understanding_Outputs.html
[3] http://raspberrypi.stackexchange.com/questions/9772/how-do-i-change-the-drive-strength-on-gpio


## GPIOポートのDrive Strengthとは
Raspberry PiのGPIOは全体で最大50mAの出力が可能であるということは多くのサイトで見つかります。
例えば以下のページでもGPIOの電流は最大で50mAであることが書かれています。

https://www.raspberrypi.org/documentation/hardware/computemodule/cm-designguide.md

それでは、GPIOのピン1つからはどれだけの電流を流すことができるでしょうか？
それを決めるのは Drive Strength になります。
以下の図はGPIO出力部分の図です（[1]から引用）。

![gpio.png](https://qiita-image-store.s3.amazonaws.com/0/99572/a5f394b1-20c0-5d14-7373-44ffac88ab3a.png "gpio.png")

図の通り、GPIOの出力は4つの出力バッファが並列につながった形をとります。
4つのうち、3つはトライステートで制御出来ます。
一方、残り１つは常に出力として使用するため、ピン1つの Drive Strength は2mA〜16mAとなります。

ここでの注意点は Drive Strength は流せる最大の電流量と言う訳ではないことです。
設定した Drive Strength 以上の電流を流そうとすると、GPIOの出力電圧の大きさを保証することができなくなります。

ここらへんの詳しい話は参考[2]に書いているのですが、簡単に言うとDrive Strength が小さいほど出力インピーダンスが高いため、負荷に大きな電流を流そうとすると出力電圧が下がってくるということです。

そのため、Drive Strength が2mAに設定されているにもかかわらず、GPIOに抵抗とLEDを直接つなげて10mA流そうと設計しても実際は10mA流すことが出来ません。

## Drive Strengthの変更
Drive Strength の設定を行うためのレジスタは[1]に示されています。

[1]によると、Drive Strength はデフォルトでは8mAに設定されていると書かれていたのですが、Raspbianで立ち上げたRaspberry Piでは2mAに設定されていました。
これはおそらくOS側で2mAに設定するようにされているんだと思います。

Drive Strengthはピンごとに設定することは出来ず、GPIOのパッドごとに設定することになります。

以下はPAD1(GPIO28-45)の Drive Strength を16mAに設定したプログラムの例です。
このようにして Drive Strength を変更することが出来ます。

```C:rpi_gpio_drive_strength.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

//  レジスタブロックの物理アドレス
#define BCM2708_PERI_BASE        0x20000000
#define GPIO_BASE                (BCM2708_PERI_BASE + 0x200000) /* GPIO   */
#define PADS_BASE                (BCM2708_PERI_BASE + 0x100000) /* PADS   */
#define BLOCK_SIZE    4096

//  ピン機能（BCM2835）
#define GPIO_INPUT    0x0       //  入力
#define GPIO_OUTPUT   0x1       //  出力
#define GPIO_ALT0     0x4
#define GPIO_ALT1     0x5
#define GPIO_ALT2     0x6
#define GPIO_ALT3     0x7
#define GPIO_ALT4     0x3
#define GPIO_ALT5     0x2

#define PADS0   *(pads+11)
#define PADS1   *(pads+12)
#define PADS2   *(pads+13)

// I/O access - Reqires maping.
volatile unsigned *pads = NULL;

void gpio_init(void);

int main(void)
{
    gpio_init();
    PADS1 = (0xFFFFFFF8 & PADS1) | 0x7 | 0x5a000000;
    
    return 0;
}

void gpio_init(void)
{
    //  既に初期化済なら何もしない
    if (pads) return;

    //  ここから GPIO 初期化
    int fd;
    void *pad_map;

    //  /dev/mem（物理メモリデバイス）を開く（sudo が必要）
    fd = open("/dev/mem", O_RDWR | O_SYNC);

    if (fd == -1) {
        printf("error: cannot open /dev/mem (gpio_setup)\n");
        exit(-1);
    }

    //  mmapで物理メモリを仮想メモリに対応づける
    pad_map = mmap(NULL, BLOCK_SIZE,
                    PROT_READ | PROT_WRITE, MAP_SHARED,
                    fd, PADS_BASE );

    if ((int) pad_map == -1) {
        printf("error: cannot map /dev/mem on the memory (gpio_setup)\n");
        exit(-1);
    }

    //  mmap 後は不要な fd をクローズ
    close(fd);
    
    pads = (unsigned int *) pad_map;
}
```

## 結論
Drive Strength を変更することで10mA前後の少し大きめの電流を直接流すことが出来るようになります。
しかし、理想的にはRaspberry Piから大きな電流を駆動するのではなく、外部に出力ドライバを接続して負荷を駆動するようにしたほうがよいのだと思います。
Drive Strength を変更して負荷に大きな電流を流すのは最終手段として使用すべきでしょう。