2013年04月28日

Raspberry PiでI/O拡張&Arduinoシールドを使おう〜firmata+Ruby編

Raspberry Piでいろいろと制御を楽しむ時に、よりたくさんのI/Oを叩きたいとか、サーボを手軽に動かしたくなる場合があるかもしれません。また、Arduinoシールドを既に持っている場合、これを活用したいという場合もあるでしょう。
Raspberry PiはそれほどはI/Oが充実していませんしね〜。
以下は回路図のピンヘッダのところ。
RPi_PIN_sch.png
回路図にありますように、SPI、I2Cが出てるので、ポートエキスパンダを接続するという手は使えます。
ポートエキスパンダは、例えばこんなのとか。
http://akizukidenshi.com/catalog/g/gI-03708/

とはいえPWMやりたい、とかAD値を取りたいとか、なんてことを手軽な工作で実現するには手間がかかりそうです。


本記事では、上記を解決するアプローチの1つとして、firmataのスケッチを書き込んだArduinoマイコンボードをRaspberry PiのUSBポートに接続し、Raspberry Pi側からArduinoを手足のように使って制御を行なってみる内容になっています。
そんなわけで、ArduinoシールドもRaspberry Piから制御できるようになります。いろいろお持ちの方は資産活用できるかもしれませんね

firmataは、元々PC用のJavaベースの開発環境Processingで、PCに接続したArduinoマイコンボードを制御してフィジカルコンピューティングするものです(と理解してます)。

そこで、firmataのプロトコル http://firmata.org/wiki/Protocol をRaspberryPi側に喋らせる事で、Raspberry Piで手軽にArduinoマイコンボードを制御できるようにする準備をしていきたいと思います。
今回、Raspberry Pi側で使用する言語はRubyにしました。これでRaspberry Pi上のスクリプト言語で、お手軽にスケッチ相当のコードを書いて、フィジカルコンピューティング出来ますね!(たぶん)

IMG_20130428_030933resized.jpg
さて、今回はArduinoマイコンボードとして最もスタンダードなものの1つである、Arduino Unoを使用しました。
最近スイッチサイエンスさんではこのボード、永久保証されてるようで話題になりました。
【永久保証付き】Arduino Uno
スイッチサイエンス
売り上げランキング: 3,309

Arduinoマイコンボード側の準備

では、これに、Standard Firmataのスケッチを焼きこんでおきましょう。サンプルに用意されています。
ss_firmata.png

Raspberry Pi側の準備
Raspberry Pi側のディストリビューションはRaspbian Wheezyを使用します。その他のLinuxディストリビューションでも多分実施可能かとは思います。Arduno Unoを認識させる為には、USB CDC ACMデバイスを認識できるようにする必要がありますが、Raspbian Wheezyでは標準で認識するようになってます。

●Rubyのセットアップ
今回はRubyとRubyでシリアルポートを制御するruby-serialportを使用します。また、ruby-serialportをセットアップするのにRubygemを使用しますので、以下のようにセットアップするとOKです。

$ sudo apt-get install ruby
$ sudo apt-get install ruby-dev
$ sudo apt-get install rubygems
$ sudo gem install ruby-serialport

ruby-devは、gemでモジュールをインストールする際に必要になる関係でセットアップしています。

では、ライブラリを準備します。

patcoll氏のruby-firmataをベースに、現在のfirmataで使用できるように改造し、改変や機能追加したものを私のgithubで公開しています。

当初そのまま使えるかなと思ってたのですが、ruby-firmataの開発が2年ほど止まっているようで、その間のfirmataの拡張などもあったためでしょうか、そのままでは動作させられませんでした。
改変版は以下にあります。
https://github.com/yishii/ruby-firmata
これを取得します。
下記のようにgitで取得出来ますし、gitを使わない場合、「zip_icon_github.png」アイコンをクリックするとZip形式でダウンロード出来ます。
git clone https://github.com/yishii/ruby-firmata
それではサンプルコードを用意します。以下のコードは、上述のリポジトリ内にあります、test.rbとなってます。
runメソッドにクラスを渡すとsetupとloopを呼び出すArduino IDEライクなスタイルです。直接Arduinoクラスメソッド呼び出しでの制御もできますので、お好みで。

digital_readやanalog_readメソッドの値は、firmataではレポートが定期的(または変化があった時)にArduino側から送られてきますので、それをパースした結果を返します。本ライブラリでいえば、Arduino::parseを呼び出した内部でArduinoからのレポートを解釈し、内部で記録します。

コードを見ていただくとおわかりになるかと思いますが、内容としては、デジタルピン2番をリードしてデジタルピン13番のHigh/Lowを制御するのと、アナログピン0番をリードしてデジタルピン9番に接続したサーボモーターを制御させています。

require './lib/arduino'

class Test
  def initialize(arduino)
    @arduino = arduino
  end

  def setup
    @arduino.set_report_interval(100) # アナログ値のレポート間隔 = 100[ms] 短かくし過ぎた場合通信量が大きくなるので注意
    @arduino.pin_mode(Arduino::DIGITAL2 , Arduino::INPUT)
    @arduino.pin_mode(Arduino::DIGITAL3 , Arduino::INPUT)
    @arduino.pin_mode(Arduino::DIGITAL13, Arduino::OUTPUT)
    @arduino.pin_mode(Arduino::DIGITAL9 , Arduino::SERVO)
    @arduino.pin_mode(Arduino::ANALOG0  , Arduino::ANALOG)
    @arduino.report # デジタル・アナログのレポート開始
  end

  def loop
    @arduino.parse # レポートを受信して解釈する

    # デジタルピン2番のLOW/HIGHでデジタルピン13番を制御
    if (@arduino.digital_read(Arduino::DIGITAL2) == 0)
      @arduino.digital_write(Arduino::DIGITAL13,0)
    else
      @arduino.digital_write(Arduino::DIGITAL13,1)
    end

    # アナログピン0番の入力値を用いてデジタルピン9番のサーボを回転させる
    val = @arduino.analog_read(Arduino::ANALOG0)
    @arduino.servo_write(Arduino::DIGITAL9,val >> 2)
   end

end

arduino = Arduino.new("/dev/ttyACM0")
arduino.run(Test)



/dev/ttyACM0は、Arduino Unoのシリアルポートのデバイススペシャルファイルです。これを介してRaspberry PiからArduino Unoにアクセスします。

テストに使用したのは、ジョイスティックシールド+サーボモーター+LEDです。
IMG_20130428_030941resized.jpg
IMG_20130428_030852resized.jpg
使用してるところの動画撮りましたのでどーぞ!


firmataを使用すれば、手軽にRaspberry PiでI/O制御の拡張と、既存Arduinoシールド資産が活用できるので、よかったら是非どうぞー!

ちなみに、今回使用したライブラリはArduino::parseメソッドを呼び出して1バイトずつデータを解釈させるのですが、できれば受信は別スレッドで行わせたほうが扱いやすいように思います。その内また更新して本ブログに掲載させていただこうかなーと思います。
Raspberry Piユーザーガイド
Eben Upton Gareth Halfacree
インプレスジャパン
売り上げランキング: 1,952
Raspberry Pi Type B 512MB
Raspberry Pi Type B 512MB
posted with amazlet at 13.04.28
RS Components Ltd
売り上げランキング: 1,915
posted by いしいっち at 04:16| Comment(0) | TrackBack(0) | Raspberry Pi | このブログの読者になる | 更新情報をチェックする

2013年04月17日

Raspberry PiでSPIシリアルで有機ELにフレームバッファを表示する実験してみた

Raspberry Piでちょこちょこ遊んでおります石井です。

先日、本ブログにRaspberry PiのI2Cを使用する実験で簡易電圧電流計を作ってみたというのを書かせて頂きました。
http://projectc3.seesaa.net/article/350916175.html

こんどはSPIシリアルを試そうと思いまして、何か制御対象物ないかなーとマイ積み基板BOXを漁っていたところ、出てきたのがaitendoで1年前に買ってた数百円程度で買える有機ELモジュールです。このモジュールはバスインターフェース(6800タイプ or 8080タイプ)と、SPIに対応してるということなんですね。で、取り出しました。

今回使用したモジュールの、aitendoのサイトでの商品情報へのリンクはこちら。
http://www.aitendo.com/product/2099 ai_oled1.gif
http://www.aitendo.com/product/2484 ai_oled2.gif

上記リンクの内、上のはSPIシリアル専用の回路になっていて、下のはバスのピンも全部出されてます。とはいえ今回はSPIで使用するのでどちらでも適用可能です。僕は、下のタイプを手持ちしていたのでそれを使ってます。

接続はこんなイメージ。
oled_overview.png

試したところ、とりあえずSPIからの制御自体はできたので、表示データを用意すべく昔の資産を掘り起こして簡易的な描画ライブラリを考えましたが、LinuxのコンソールやXの描画に使用されているフレームバッファ(/dev/fb0)をそのまま展開してしまえば、と思い、今回はそうしてみました。


動作させた状態の動画を作りました。良かったらどーーぞー!

従って、以下の様にフレームバッファをSPI経由で有機LEDモジュールに周期的に領域コピーするような構成にします。

oled_block.png

結線は、以下のようにしてみました。有機ELモジュールのRS端子(D/C#端子)は、SPIで有機ELモジュールに対して通信している内容がコマンドなのか、データなのかを通知する為の信号です。
コマンドをやりとりする場合はLowにし、それ以外の時はHighに制御する為、Raspberry PiのGPIO_GEN0ピン(汎用入出力端子)を使用しています。

oled_connection.png

ディストリビューションはRaspbian "wheezy"を使用します。デフォルトではSPIシリアルはローダブルモジュールになっており、自動でロードされません。
以下のサイトに書いてあるような方法を参考にSPIのデバドラが叩けるようにしました。
http://www.pwv.co.jp/~take/TakeWiki/index.php?raspberrypi%2FSPI%E3%82%92%E8%A9%A6%E3%81%99

本記事のソースコードはgithubにて公開させていただいております。
https://github.com/yishii/RaspberryPi_SPI_OLED

Raspberry Pi環境でセルフビルドもできますし、PC上で、Ubuntu+Sourcery g++によるクロス環境でもビルド出来ました。src/Makefileに記載してますので、ご覧になってください。

有機ELモジュールはSPIモードで使用するため、変換基板ピンヘッダコネクタ部の3番、4番(BS1、BS2)は共にGNDに落としておきます。以下のように変換基板にすすメッキ線で配線しました。

oled_jumper.jpg

実際に結線したイメージはこちら。
P1130476resized.jpg

フレームバッファを展開するプログラムを作成し、実行すると、テキストもグラフィックも結構クッキリで、いい感じ。

oled_text.jpg
oled_bitmap.jpg
ss.jpg
それでは、コードを見ていきましょう。SPIの初期化です。有機ELコントローラの仕様書を参照すると、SPIのクロックは最高4[MHz]の規定ですが、高速に画面更新してみようということで、おためしで8[MHz]に設定しています。
一見問題なく使えているように見えますが、メーカーの仕様を超えた使い方であるので、商品等に組み込むソフトの制御などでは当然NGでしょうし、想定外の不具合の発生やデバイス寿命短縮などになるかもしれません。
void oled_main(void)
{
    int fd;
    int result;
    uint8_t mode = SPI_MODE_0;
    uint8_t bits = 8;
    //uint32_t speed = 4000000;
    uint32_t speed = 8000000; // on the spec,SCLK max freq=4[MHz]
    uint16_t delay;

    fd = open("/dev/spidev0.0",O_RDWR);
    if(fd == -1){
	printf("Device open error\n");
	return;
    }

    /*
      set SPI read/write mode
    */
    result = ioctl(fd,SPI_IOC_WR_MODE,&mode);
    if(result < 0){
	printf("error(L%d)\n",__LINE__);
	return;
    }
    result = ioctl(fd,SPI_IOC_RD_MODE,&mode);
    if(result < 0){
	printf("error(L%d)\n",__LINE__);
	return;
    }

    result = ioctl(fd,SPI_IOC_WR_BITS_PER_WORD,&bits);
    if(result < 0){
	printf("error(L%d)\n",__LINE__);
	return;
    }
    result = ioctl(fd,SPI_IOC_RD_BITS_PER_WORD,&bits);
    if(result < 0){
	printf("error(L%d)\n",__LINE__);
	return;
    }

    result = ioctl(fd,SPI_IOC_WR_MAX_SPEED_HZ,&speed);
    if(result < 0){
	printf("error(L%d)\n",__LINE__);
	return;
    }
    result = ioctl(fd,SPI_IOC_RD_MAX_SPEED_HZ,&speed);
    if(result < 0){
	printf("error(L%d)\n",__LINE__);
	return;
    }

フレームバッファは、以下のように/dev/fb0のデバイスをマップしてアクセスしています。

bool fb_init(void)
{
    int fb_fd;
    struct fb_var_screeninfo vinfo;

    fb_fd = open("/dev/fb0",O_RDWR);
    if(fb_fd < 0){
	printf("framebuffer open error\n");
	return(false);
    }

    if(ioctl(fb_fd,FBIOGET_VSCREENINFO,&vinfo)){
	printf("screen information retrieve error\n");
	return(false);
    }
    printf("%dx%d,%dbpp\n",vinfo.xres,vinfo.yres,vinfo.bits_per_pixel);

    screen_size_x = vinfo.xres;
    screen_size_y = vinfo.yres;
    screen_bpp = vinfo.bits_per_pixel;
  
    if(vinfo.bits_per_pixel != 16){
	printf("This color mode is not supported.\nOnly 16bpp is supported currently\n");
	return(false);
    }

    fb_mem = (volatile unsigned char*)mmap(
	NULL,
	(vinfo.xres*vinfo.yres*vinfo.bits_per_pixel) >> 3,
	PROT_READ|PROT_WRITE,
	MAP_SHARED,
	fb_fd,
	0);

    if(fb_mem == NULL){
	printf("framebuffer mapping error\n");
	return(false);
    }

    return(true);
}

シリアル通信でイメージデータと制御コマンドとを切り分けるのにRSという信号を使用しますが、これはGPIO17番を使用します。実装のgpiolib.cご覧ください。

このように結構いい感じにカラーのイメージ展開が出来ました!

Raspberry Pi Model B (512MB)本体のみ
kalron
売り上げランキング: 16,048

Raspberry Piユーザーガイド
Eben Upton Gareth Halfacree
インプレスジャパン
売り上げランキング: 1,492
posted by いしいっち at 23:13| Comment(0) | TrackBack(0) | 電子工作 | このブログの読者になる | 更新情報をチェックする

2013年04月07日

FPGAボードDE0とKYANITEを使ってHDMIモニターに画像を表示して遊んでみた

4月になり、ここ関西エリアの気候もどんどん暖かく、過ごしやすくなって来ました。今はまさに、桜満開といった感じですが、今週末は雨風きつくて、多少散ってしまうかもしれませんね。

さて、先日、NT京都という、ニコニコ技術部展示会イベントが京都市は西院にあります、春日幼稚園・春日神社境内で開催されてまして、今回僕は初めて見学させていただきました。
(去年もほぼ同じ時期にあったんですよね。都合が付かなくて参加できなかったのですが)

きっと、普通の神社なので会場としてはそれほど広くないんでしょーと勝手に予想してたのですがそれは間違いで、かなり広い面積に幅広い展示物が並んでまして、Make: meeting(Makers faire)っぽい感じ
もしました。すごく楽しかったです。



さて、このイベントではクリエイターの方々の作品も買うことが出来るようで、僕は長船氏(@s_osafune)作のDE0及びDE0-nanoボード用の拡張基板 KYANITE(カイヤナイト、でいいんですよね)を買ってみました。イベントでは、完成品もありましたが、自分で作ってみたいと思ってキット品を購入しました。

DE0及びDE0-nanoは、ALTERAのCycloneIII(-nanoはCycloneIV) FPGAと周辺回路を搭載したリーズナブルな(1万円前後?)ボードです。台湾Terasic社の製品です。(写真はDE0の端子側)
IMG_20130407_125837.jpg

僕はDE0は購入したのが1年4ヶ月くらい前で、作ったのはDE0ラジコンだけです。
http://projectc3.seesaa.net/article/248752693.html
この記事が2012年1月27日で、それくらいの時期からずーーーーっと戸棚に入ったままになってました。(反省。。。)
(今年は積み基板削減を目指しています!。笑)
※僕はFPGAでは上記ラジコンを作ったことがある程度ですので、記事中に間違ったことがあれば是非ご指摘下さいマセ。



さてさて、KYANITEは、DE0ボードに、HDMI出力、アナログ音声出力、USBホストポート、マイクロSDカードスロット、LANを提供してくれるボードです。
KYANITEボード自体は受動部品で構成され、制御は全てFPGA側に作りこむということのようで、HDMIやDVI出力の無い、本DE0(-nano)ハードに長船氏の手によりHDMI出力回路をFPGA上に作りこまれ、デモプロジェクトとして公開されています。
凄いですね〜!!

鈴木氏(@kskue)のブログで、DE0-nanoとKYANITEとの組み合わせで動作させる記事を書かれています。
[FPGA][DE0] DE0/DE0-nano拡張基板で.bmpファイルをHDMI出力 http://d.hatena.ne.jp/ksksue/20130403/1364984675

さて、KYANITE基板に部品を実装してみました。

IMG_20130406_205822resized.jpg
部品のほとんどは1608(ミリ表記)のチップ部品なので、チップ部品慣れしてない方は予めいらない基板で練習してからやると良いでしょう。

こんな感じで完成しました。LANポートはWIZnetのもの(別売)を搭載させるのですが、手持ちが無いので今は実装していません。
IMG_20130407_122704resized.jpgIMG_20130407_122757resized.jpg

デモプロジェクト関連は長船氏が公開されています。URLは鈴木氏のブログ記事にも記載ありますが、引用すると、以下です。
DE0用デモプロジェクト https://t.co/uz4mRicSzm
DE0-nano用デモプロジェクト https://t.co/JcZD4q8mBc

まずはQuartusIIにプロジェクトを読み込みます。僕は、QuartusII 32bit Version 12.1 Build 243 01/31/2013 SJ Web Editionを使用しました。そしたら、Fitter (Place & Route)で「Internal Error: Sub-system: VPR20KMAIN」なんてのを吐くので、Google先生に お伺いして、以下のALTERAのサポートページにあるような、quartus.iniをプロジェクトのトップディレクトリに置いたら回避出来ました。
http://www.altera.co.jp/support/kdb/solutions/rd12222009_898.html

NiosII側ではSDカードにbmpファイルを置いて展開させたいと思います。サンプルのbmp_testは、RGB555のベタのイメージをSDカードの/de0/test_vga.binに用意し、それを展開するようになってるようです。
一方、DE0-nanoのデモプロジェクトにはBMP形式をオープンしてビットマップに貼り付けるloadbmp関数というのがあったので、そのまま貰ってきて利用し、DE0側のbmp_test側で使用するようにしました。
以下はbmp_testのmain関数内のファイルをメモリーに読み出す箇所で、★部のようにしました。
#if 0
		if((fp = fopen("mmcfs:/de0/test_vga.bin","rb"))!= NULL) {
			fread(pFrameBuffer, 1, 1024*480*2, fp);
			fclose(fp);

			alt_dcache_flush(pFrameBuffer, 1024*480*2);
		} else {
			printf("[!] File not found.\n");
		}
#else // ★★以下が追加部分
		loadbmp("mmcfs:/de0/sample.bmp",pFrameBuffer);
#endif
SDカードは、本デモプロジェクトではKYANITE側ではなく、DE0側のフルサイズのSDカードスロットを使用します。

春なのに非常に秋らしい写真を表示してみました。笑 IMG_20130407_104549resized.jpg

ビットマップを表示する以外にも、本デモプロジェクトには、長船氏によるプリミティブな描画ライブラリ関数がたっぷり入っていて(nd_egl.h参照)、綺麗なGUIをFPGAからいろいろ描画することも出来そうです。
また是非試してみたいです。


というわけでこのKYANITEボードを使えば楽しくDE0と共に遊べそうです。Androidとも繋いでみたりしたいな。。と思いますが、また次回・・・


一年前に鈴木氏に教えてもらった本の紹介です。
QuartusIIでNiosIIを使用したプロジェクトをいじるのには、以下の本「FPGA ボードで学ぶ組込みシステム開発入門」が役立つと思います。
もしQuartusII初めて使うなら是非見てみてはいかがでしょうか!
FPGA ボードで学ぶ組込みシステム開発入門 〜Altera編〜
小林 優
技術評論社
売り上げランキング: 39,623

WIZ820io
WIZ820io
posted with amazlet at 13.04.07
スイッチサイエンス
売り上げランキング: 101,278

Terasic USB Blaster(1-TB1) ALTERA USB Blaster互換品
HuMANDATA LTD.
売り上げランキング: 41,593
タグ:DE0 FPGA KYANITE
posted by いしいっち at 13:33| Comment(0) | TrackBack(0) | 電子工作 | このブログの読者になる | 更新情報をチェックする