contents
- むとうたけし@K*BUG <mutoh@610t.org>
- GPIOってなあに?
- FreeBSDでGPIOを使う
- ユーザランドから使うGPIO
- GPIOポートモード一覧
- GPIOモードの設定
- GPIO出力
- GPIO入力
- 応用: MZTX-PI-EXTをFreeBSDで使う
- MZTX-PI-EXT用Linuxアプリケーション
- FreeBSDのGPIO経由で簡単な描画ができるところまで実装する
- デモ1: 文字列、円を描画する
- デモ2: loadavgを表示する
- おわりに
FreeBSDでRaspberry PiのGPIOを使う
むとうたけし@K*BUG <mutoh@610t.org>
この文章は、 FreeBSD Advent Calendar 2016の17日目として書かれたものです。 2015年03月07日(土)の関西*BSDユーザ会研究会2015年第1回研究会の発表FreeBSDでRaspberry PiのGPIOを使ってみたが元になっています。
GPIOってなあに?
GPIOは、General Purpose Input/Outputの略で、汎用入出力という意味です。 GPIOでは、コンピュータへのデジタルの入出力を比較的簡単に扱うことができます。 デジタル入出力以外にも、Pull-upやPull-downモードにすることができる場合もあります。
Raspberry Piでは、GPIOのためにBroadcom BCM2835の機能を使っています。
FreeBSDでGPIOを使う
FreeBSDでRaspberry PiのGPIOを使うためには、 WORKING WITH GPIO ON RASPBERRY PI WITH FREEBSDが参考になります。
FreeBSDの起動時には、以下のdmesgのようにGPIOデバイスが認識されます。
mutoh@raspberry-pi:~ % dmesg (snip) gpio0: <BCM2708/2835 GPIO controller> mem 0x200000-0x2000af irq 57,59,58,60 on simplebus0 gpio0: read-only pins: 46,47,48,49,50,51,52,53. gpio0: reserved pins: 48,49,50,51,52,53. gpioc0: <GPIO controller> on gpio0 gpiobus0: <OFW GPIO bus> on gpio0 gpioled0: <GPIO led> at pin(s) 16 on gpiobus0 iichb0: <BCM2708/2835 BSC controller> mem 0x205000-0x20501f irq 61 on simplebus0 iicbus0: <OFW I2C bus> on iichb0 iic0: <I2C generic I/O> on iicbus0 iichb1: <BCM2708/2835 BSC controller> mem 0x804000-0x80401f irq 61 on simplebus0 iicbus1: <OFW I2C bus> on iichb1 iic1: <I2C generic I/O> on iicbus1 spi0: <BCM2708/2835 SPI controller> mem 0x204000-0x20401f irq 62 on simplebus0 spibus0: <OFW SPI bus> on spi0 (snip)
GPIOの制御用デバイスは/dev/gpioc0になります。
ユーザランドから使うGPIO
コマンドラインからGPIOを使うためには、gpioctlが利用できます。
gpioctlでは、GPIOポートを指定し、モード設定や実際の入出力が可能です。
以下、順に見ていきます。
GPIOポートモード一覧
以下のようにして、GPIOポートのモードを確認できます。
mutoh@raspberry-pi:~ % sudo gpioctl -lv (snip) pin 23: 0 pin 23<IN>, caps:<IN,OUT,PU,PD> pin 24: 0 pin 24<IN>, caps:<IN,OUT,PU,PD> pin 25: 0 pin 25<IN>, caps:<IN,OUT,PU,PD> (snip) pin 45: 0 pin 45<>, caps:<IN,OUT,PU,PD>
GPIOモードの設定
GPIOモードの設定は、-cオプションを与えて指定します。
mutoh@raspberry-pi:~ % sudo gpioctl -c 25 OUT 0/OUT mutoh@raspberry-pi:~ % sudo gpioctl -lv | egrep 25 pin 25: 0 pin 25<OUT>, caps:<IN,OUT,PU,PD> mutoh@raspberry-pi:~ % sudo gpioctl -c 23 IN 0/IN mutoh@raspberry-pi:~ % sudo gpioctl -lv | egrep 23 pin 23: 0 pin 23<IN>, caps:<IN,OUT,PU,PD>
GPIO出力
出力は、GPIOポート番号の後ろに、出力したい0 or 1を与えます。
mutoh@raspberry-pi:~ % sudo gpioctl 25 0 0/25 1/0 mutoh@raspberry-pi:~ % sudo gpioctl 25 1 0/25 1/1
GPIO入力
入力は、入力したいポート番号を指定します。
mutoh@raspberry-pi:~ % sudo gpioctl 23 0/23 0 mutoh@raspberry-pi:~ % sudo gpioctl 23 0/23 1
応用: MZTX-PI-EXTをFreeBSDで使う
MZTX-PI-EXTは、初代Raspberry Pi Type Bで使える320x240のLCDモニタ+タッチパネルのデバイスです。 2000円ぐらいで入手可能でした。 基板の形状の問題で、2 B+や3 Bなどでは利用できません。
MZTX-PI-EXTのピン配置は、以下のようになっています。
ピン番号 | 意味 | 備考 |
8 | SPICS | |
25 | SPIRS | |
23 | SPIRST | |
11 | SPISCL | |
10 | SPISCI | |
18 | LCDPWM |
MZTX-PI-EXT用Linuxアプリケーション
Rasbianなどには、以下のようなドライバが用意されています。
LCD表示には、 https://s3.amazonaws.com/tontec/24usingmanual.zip (Github) が利用できます。 このドライバは、 /dev/memを経由してGPIOにアクセスします。 デモプログラムでは、フレームバッファを読み込んで、液晶に表示する様になっています。 主なプログラムファイルは、以下の通りです。
- bcm2835.c: BCM2708/2835 GPIO controller用ライブラリ
- mztx06a.c: アプリケーション本体
タッチパネルは、Xから使えるLinux用kernelモジュールがあるようですが、実際に試したことはありません。
FreeBSDのGPIO経由で簡単な描画ができるところまで実装する
では、FreeBSDからMZTX-PI-EXTを利用するためのプログラムを記述していきます。
ここで紹介したプログラムは、以下のURLから入手可能です。
ここでは、最低限動作させることを目標にプログラムを作成することとし、以下のLinuxのドライバのソースコードで使えるものは再利用します。
bcm2835_new.cとして6つの関数を実装します。
- int bcm2835_init()
- void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode)
- void bcm2835_gpio_write(uint8_t pin,uint8_t val)
- void bcm2835_gpio_clr(uint8_t pin)
- void bcm2835_gpio_set(uint8_t pin)
- void bcm2835_delay(unsigned int millis)
GPIOモードを設定するGPIOSETCONFIGを bcm2835_gpio_fsel() で、GPIO出力を行うGPIOSETを bcm2835_gpio_write() で、それぞれioctl(2)を実行しています。
デモプログラムmztx06a.cには、以下の変更を行います。
- SPIを使わないようにする(#undef BCM2708SPI)
- LCD_WR_Data()のinlineを消す (LLVMのみ?)
デモ1: 文字列、円を描画する
ひとつめのデモでは、オリジナルのライブラリで提供されている関数を利用して、文字列を描画したり、円を描画したりしてみます。
先程、bcm2835_new.cとして実装した関数を使って、MZTX-PI-EXTの初期化などを行います。
実際に描画する関数は、文字列が DisplayString("String",x,y) で、円が draw_circle(x,y,radius,color) です。 描画10回ごとに、画面をランダムな色でクリアします。
int main (void) { int c; (snip) printf("bcm2835 init now\n"); if (!bcm2835_init()) { printf("bcm2835 init error\n"); return 1; } bcm2835_gpio_fsel(SPICS, BCM2835_GPIO_FSEL_OUTP) ; bcm2835_gpio_fsel(SPIRS, BCM2835_GPIO_FSEL_OUTP) ; bcm2835_gpio_fsel(SPIRST, BCM2835_GPIO_FSEL_OUTP) ; bcm2835_gpio_fsel(LCDPWM, BCM2835_GPIO_FSEL_OUTP) ; #ifdef BCM2708SPI bcm2835_spi_begin(); bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); bcm2835_spi_setDataMode(BCM2835_SPI_MODE3); bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_2); #else bcm2835_gpio_fsel(SPISCL, BCM2835_GPIO_FSEL_OUTP) ; bcm2835_gpio_fsel(SPISCI, BCM2835_GPIO_FSEL_OUTP) ; #endif LCD_PWM_CLR; (snip) LCD_Init(); LCD_clear(RGB565(130,130,150)); srandom(0); for (;;) { int x,y,radius,r,g,b; x=myrand(200); y=myrand(200); radius=myrand(200); r=myrand(255); g=myrand(255); b=myrand(255); printf("%d %d %d RGB(%d %d %d)\n",x,y,radius,r,g,b); draw_circle(x,y,radius,RGB565(r,g,b)); DisplayString("K*BUG FreeBSD",myrand(30),myrand(15)); if ((c++ % 10)==0) LCD_clear(RGB565(r,g,b)); } (snip) }
動作の様子は、以下のようになります。 静止画ではわからないですが、実際には描画はかなり遅いです。
デモ2: loadavgを表示する
二つ目のデモとして、loadavgをLCDに表示してみます。 このデモは、OSC2016 Kyotoで展示を行いました。
loadavgを取得するためにC言語でloadavg.cを作成しました。 getloadavg(3)を使ってloadavgを取得し、その値に応じた垂直線を描画します。 GPIO経由での描画はかなり遅いのとMZTX-PI-EXT側の現在描画されてる情報を取得するための方法が無いため、xload(1)の様に右端まで描画した時にスクロールするのは難しいので、右端まで行くとまた左端から描画するようにしました。 これだけでは、更新されている場所がわかりづらいので、更新している場所に黒で垂直線を引いた後で、それを白で消去しています。
実際の動作の様子は以下の通りです。
おわりに
Raspberry Piには、多くのGPIOが用意されており、デジタルの入出力で十分な場合には、比較的簡単に電子工作などに利用できます。
LEDを点滅するLチカや、スイッチの値を読み取ることなどの簡単な実装から、今回デモで行ったようなLCDへの描画まで色々な応用が考えられます。
是非、皆さんもFreeBSD + Raspberry Pi + GPIOで遊んでみませんか?