目次
- ごあいさつ
- 必要なもの
- Raspberry Piとハードウェアの準備
- 各種ツールのインストール
- プロジェクトを作成する
- Raspberry Piで実行する
- JTAGを使ってデバッグする
ごあいさつ
こんにちは、yamanekkoです。この記事はmruby Advent Calendar 2013の23日目のエントリになります。
本記事では、Raspberry Pi で、あえてLinuxはもちろんRTOS等も使わずに(いわゆるbare metal環境で)mrubyでLチカをしてみます。JTAGとEclipseを使用することにより、GDBを視覚的に使ってデバッグしながら開発することができます。
私たちはふだんはMac OS Xを使用しています。なのでスライド等で公開している情報はMacに偏りがちなのですが、今回はRubyConf 2013で発表したネタをWindowsで試してみました。
あまりWindowsに詳しくない者がとりあえず動かしてみましたというご紹介ですので、Windows使いの方にブラッシュアップしていただけるとありがたいです。
ところで、なぜRaspberry Pi なのか疑問に思われている方もいるかもしれません。ここではRaspberry Piを「やたらリッチなマイコンボード」として使っているわけですが、そもそもRaspberry PiはLinuxマシンであり、bare metalで使っている人は少なさそうで、日本語の情報はほとんど見つかりませんでした(ひょっとするとこのエントリが日本語では一番詳しいものになるかもしれません)。
Raspberry Piを使ってみた理由は大きく2つあります。
- Raspberry Piはすでに持っている人が多そう
- わざわざ新しいボードを購入しなくても、手元にあるもので気軽に試してみていただきたかったからです。
- つぶしが効きやすい
- 新たに購入したとしても、Raspberry Piであれば飽きたり挫折したとしても単なるLinuxサーバやメディアサーバ等、他の用途に使えそうです。
安価なマイコンボードで試してみたい方にはSTM32F4 Discoveryをお勧めします(STM32F4 Discoveryで動かす話は https://github.com/yamanekko/mruby-on-stm32f4discovery/wiki に少し書いています)。
必要なもの
ハードウェア
- Raspberry Pi
- ここでは512KBのType Bを使っていますが、TypeAでも問題ないはずです。
- なお、以下のように本記事ではデバッグ用にJTAGを利用しているのですが、JTAGが正式にサポートされたのはrevision 2.0からです(参考: http://www.raspberrypi.org/archives/1929 )。以前のrev 1.0のボードをお使いの方はごめんなさい。
- LEDをつけたブレッドボード
- Raspberry Pi自体にもLEDがついていますが、いっぱいあった方が楽しそうなのでブレッドボードにLEDを置いてみます。
- JTAG
- JTAGはデバッグ時に必要になります。とりあえずサンプルプログラムをダウンロード&コンパイルして動かしてみたいだけならなくても大丈夫です。
- 今回使用したものはolimexのARM-USB-TINY-Hです。他の製品でも自作でもお好きなものをどうぞ。
開発用ソフトウェア(各種ツール)
- Ruby
- 当然のように必要です。artonさんのmsi版やCygwin版などもありますが、今回はRubyInstaller版を使っています。
- GNUツールチェーン(YAGARTO)
- ビルドとデバッグにはGNUツールチェーンを使います。Windows向けのGCCもいろいろありますが、今回はYAGARTOを使っています(GNU Tools for ARM Embedded Processorsでもできるかもしれませんが、OS X版でいろいろハマった上に「今どきARM11とかサポートしないっすよ(超訳)」みたいな説明が書かれていたのでスルーします)。
- Eclipse CDT(Pleiades All in One 日本語エディション)
- CのIDEです。CRubyでRailsの開発をするならエディタでも特に苦労はありませんが、私たちのような素人がクロス開発環境でリモートデバッガで頑張るならIDEを使ってみるのがおすすめです(「Emacs or Vimでぜんぜん大丈夫では?」というみなさんにはそもそもこんな解説記事は不要だと思うのでお好きな環境でどうぞ)。Pleiades All in One 64bit版は後述の通りMinGW関連のツールも一緒にインストールしてくれるので便利です。
- OpenOCD
- これを使うとARMでJTAGデバッグできるようになります。ついでにクロス開発環境でビルドしたアプリの転送もできて便利です。
- TeraTerm
- OpenOCDとのコマンドインターフェースとして使います。ターミナルソフトなら何でもいいです。
Raspberry Piとハードウェアの準備
まずはハードの方をさらっと説明します。
ブレッドボード
まず、IOポートにつなぐLEDをつないでいきます。手軽に使えるということでブレッドボードを使ってみます。
配線はこんな感じです。fritzing.orgは便利ですね。なお、保護ダイオードとかは面倒なのでさぼっていますので、接続の向きにはご注意ください。
JTAG接続
最近のRaspberry Piはハンダ付け不要でJTAG接続できるようになっています。が、JTAGコネクタのような気の利いたものはついてなくて、ピンにそれぞれつなぐ必要があります。
ここではリボンケーブル等は使わず、直接必要な線だけつないでいきます。
ARM-USB-TINY-Hの20ピンのコネクタとRaspiberry Piとのつなぎ方は以下のようになります。
ハードの準備は以上です。
各種ツールのインストール
続いて、ソフトウェアの方をインストールしていきます。
Rubyのインストール
RubyInstallerは以下のサイトからダウンロードできます。
この辺からrubyinstaller-2.0.0-p353.exeというファイルがダウンロードされます。ダブルクリックでインストーラが開始されます。
YAGARTOのインストール
YAGARTOのサイトに行くと「YAGARTO終了のお知らせ」みたいな表示になっていて愕然とするのですが、YAGARTO自体はsourceforgeの方からダウンロードできます。
こちらのダウンロードをクリックして、「yagarto-bu-2.23.1_gcc-4.7.2-c-c++_nl-1.20.0_gdb-7.5.1_eabi_20121222.exe」のファイルをダウンロードして、インストールします。
※インストール先ディレクトリは日本語や半角スペースがない場所がよいでしょう。
この記事では「C:\yagarto」にしています。以下の設定をコピーして使いたい場合は合わせてください。
Pleiades All in One 日本語エディション C/C++(Eclipse CDT)のインストール
前述の通り、MinGWが同梱されているので64bit版をインストールします。ここではEclipse 4.3 Keplerの「C/C++」の「Full edition」を選択します。別途MinGWをインストールするのであれば32bit版でも大丈夫だと思います。
C/C++ GDB Hardware Debugging プラグインのインストール
さらに、リモートでデバッグするためのプラグインをEclipseに追加します。
Help -> Install New Software...
より表示された画面でKepler - http://download.eclipse.org/releases/kepler
を選択します。
しばらく待つと一覧が表示されますので、その中からMobile and Device Development -> C/C++ GDB Hardware Debugging
を選択し、インストールします。
(参考)mrbgemsのテンプレート生成プラグイン
ちなみにyamanekko.github.ioでひっそり公開しているプラグインもあります(EclipseからインストールするときのURLは http://yamanekko.github.io/mruby-tools-EclipsePlugin/ になります)。
今のところ、ウィザード形式のmrbgemsのテンプレートが作成できる機能が実装済みなのですが、誌面と時間の都合で今回は省略します。
C(mruby)のファイルの中に埋め込まれたRubyスクリプトをmrbcでCのコード片に変換する機能もありますが、こちらはプラグインには追加されていません。
プロジェクトを作成する
それではいよいよビルドするためのプロジェクトを作ります。
今回はプロジェクトを2つ使用します。1つはmrubyそのものをビルドして、libmruby.aを作るためのプロジェクト、もう1つはそのlibmruby.aをリンクして、Raspberry Pi用のバイナリ(kernle.img)を作るためのプロジェクトです。前者のプロジェクトをmrubyプロジェクト、後者のプロジェクトをraspiプロジェクトと呼ぶことにします。
mrubyプロジェクトを作成する
先にmrubyプロジェクトを作成します。
Githubのmrubyレポジトリを取得する
今回は使用するmrbgemsを追加している関係で、github上のyamanekkoアカウントでクローンしたレポジトリのrubyconfブランチ( https://github.com/yamanekko/mruby/tree/rubyconf )からプロジェクトを作成しますが、同様の手順で本家サイトからクローンを作成することもできます。もちろんそこにご自身の作成したgemを追加しても構いません。
- まずパースペクティブをGitリポジトリー・エクスプローラに変更します。
- ここで「Gitリポジトリーの複製」を選択してください。ロケーションには
https://github.com/yamanekko/mruby.git
を入力します。 - 「ブランチ選択」画面で「rubyconf」を選択します。
- 「ローカル宛先」画面で初期ブランチが
rubyconf
になっていることを確認します。「ディレクトリー」には保存先を指定し、「完了」ボタンをクリックします。 - mrubyのブランチが作成されます。「C/C++パースペクティブ」に変更します。
上記のmrubyレポジトリのrubyconfブランチですが、基本的にはbuild_config.rbが変更になっているのと、いくつかのmrbgemsが追加されています。Raspberry Piで使うのは以下の2つのmrbgemsです。
- mruby-rs-led ( https://github.com/yamanekko/mruby/tree/rubyconf/mrbgems/mruby-rs-led )
- Raspberry Pi用のLedクラスです。Led.new、Led#on、Led#off、Led#toggleメソッドの他、色別のLEDの定数(値はGPIOのポート番号になります)が定義されています。mruby-stm-ledという同梱のmrbgemsにも同様のクラスが定義されています。
- mruby-rs-timer ( https://github.com/yamanekko/mruby/tree/rubyconf/mrbgems/mruby-rs-timer )
- RsTimerクラスです。RsTimer.new、RsTimer#check、RsTimer#enable、RsTimer#intervalメソッドが定義されています。
Eclipseのプロジェクトを作成する
続いてプロジェクトを作成します。
- 「ファイル」メニューから「「Makefile Project with Existing Code」を選択し、「次へ」ボタンを押します。
- 「Exist Code Location」に先ほど保存したリポジトリを指定し、「Toolchain for Indexer Settings」で「MinGW」を選択し、「完了」ボタンをクリックします。
- 作成されたプロジェクトを右クリックしてプロパティ画面を表示します。
- 「C/C++ビルド」タブで「自動的にMakefileを作成」にチェックが入っていないことを確認します。ビルドディレクトリは「${workspace_loc:/mruby}」にします。
- 「ツール・チェーン・エディタ」で現在のツールチェーンが「MinGW GCC」、現在のビルダーが「Gnu Make ビルダー」になっていることを確認します。こうすると、MinGWの
make.exe
を使用します。
ビルドの設定情報を自分の環境に合わせる
mrubyをビルドするための設定を変更するため、おなじみbuild_config.rb
を編集します。
クロスビルドについてはすでに本advent calanderの20日目の記事、「mrubyとAndroid.mk 2013年冬ver.」にてmonamour555さんが熱く愚痴って語っていますが、ここで使うbuild_config.rb
はざっくり以下のような構成になっています。
MRuby::Build.new do |conf|
#...(Windows用のmrbcをビルドするための設定)
end
MRuby::CrossBuild.new('rs') do |conf|
#... (Raspberry Pi用のlibmruby.aをビルドするための設定)
end
MRuby::CrossBuild.new('STM32F4') do |conf|
#... (STM32F4 Discovery用のlibmruby.aをビルドするための設定)
end
このうち下の方は今回は使用しないので、MRuby::CrossBuild.new('STM32F4') do |conf| ...
以下の行をすべて削除します(こちらはSTM32F4 Discovery用の*.hファイル等が必要になるため、残しておくとエラーになります)。
加えて、以下の2箇所をyagartoをインストールしたパスに合わせて変更してください。
conf.cc do |cc|
- cc.command='/opt/gnuarm/bin/arm-none-eabi-gcc'
+ cc.command='C:\yagarto\bin\arm-none-eabi-gcc.exe'
cc.include_paths = ["#{MRUBY_ROOT}/include/"]
cc.flags << "#{CFLAGS}"
end
conf.archiver do |archiver|
- archiver.command = '/opt/gnuarm/bin/arm-none-eabi-ar'
+ archiver.command = 'C:\yagarto\bin\arm-none-eabi-ar.exe'
archiver.archive_options = 'rcs %{outfile} %{objs}'
end
また、YAGARTOではどうもVFPを使うように設定できない(VFPを使うようになっているlibcやlibmが同梱されてない)ようだったので、とりあえずsoftfpに変更しています(ちなみにMac OS Xでは自力でGNUツールチェインをビルドしたりしました。また、MacPorts等でインストールされるarm-none-eabi-XXXではVFPを使ったバイナリがビルドできるようでした)。FPUを使わないため数値演算は遅くなりますが、気にしないことにします。
- CFLAGS = "-Wall -nostartfiles -ffreestanding -g -mcpu=arm1176jzf-s -mtune=arm1176jzf-s -mhard-float -ggdb -g -O0"
+ CFLAGS = "-Wall -nostartfiles -ffreestanding -g -mcpu=arm1176jzf-s -mtune=arm1176jzf-s -mfloat-abi=softfp -ggdb -g -O0"
mrubyプロジェクトをビルドする
それではビルドしてlibmruby.aを作ってみましょう。
Eclipseのメニューより「プロジェクト」→「プロジェクトのビルド」を選択し、ビルドします。
完了するとmruby/build/の下にrs
というフォルダとその中身が作成されます。libmruby.aはrs/lib/libmruby.a
にできるので、これを使用します。
ここまでで、libmruby.aができました。以下では、このlibmruby.aを利用するRaspberry Pi用のバイナリをビルドします。
なお、ビルドがうまくいかないときは、Makefileの方も変更した方がいいかもしれません。
mrubyで詳しいビルドのログを表示するを参考にしてください。
raspiプロジェクトを作成する
先ほども説明した通り、もう一つのプロジェクトであるraspiプロジェクトを作ります。
こちらについては、以下のページの「RaspberryPiプロジェクトの作成」以降を参照してください。
(Eclipseが英語メニュー表記になっているので、日本語メニューに読み替えてください)
- Eclipseのメニューより「ファイル」→「インポート」を選択
- 「選択」画面で「Git」→「Gitからプロジェクト」を選択
- 「リポジトリー・ソースの選択」画面で「URL」を選択
- 「ソースGitリポジトリー」画面のロケーションのURIに
https://github.com/yamanekko/raspberrypi.git
を入力 - 「ブランチ選択」画面で「master」を選択
- 「ローカル宛先」画面で宛先のディレクトリーを指定(デフォルト表示ではユーザのgitフォルダになっていますが、先ほど作成したmrubyの保存先に合わせるのがよいでしょう)
- 「プロジェクトをインポートに使用するウィザードを選択」画面で「既存プロジェクトのインポート」を選択
- 「プロジェクトのインポート」画面はデフォルトのままで「完了」ボタンをクリック
- 作成されたプロジェクトを選択し、右クリックでプロパティーを選択し、プロパティー画面を表示
- プロパティ画面の「C/C++ビルド」→「ツール・チェーン・エディター」で現在のツールチェーンを「MinGW」に変更
- ※ここを変更すると、つられて現在のビルダーも変わります。現在のビルダーは「Gnu Make ビルダー」に戻してください。
- もう一度「C/C++ビルド」に戻り、Makefile生成の「自動的にMakefileを作成する」にチェックが入っていたら外す
Makefileの修正
YAGARTOの環境に合わせてMakefileを修正します。
まず、GNUツールチェインのパスを変更します。
- ARMGNU = /opt/gnuarm/bin/arm-none-eabi
+ ARMGNU = C:\yagarto\bin\arm-none-eabi
また、先ほど同様、FPUの設定をVFPを使わない設定に変更します。
- AOPS = --warn --fatal-warnings -mcpu=arm1176jzf-s -march=armv6 -mfpu=vfp
+ AOPS = --warn --fatal-warnings -mcpu=arm1176jzf-s -march=armv6
COPS = -Wall -nostartfiles -ffreestanding
- CFLAGS = -mcpu=arm1176jzf-s -mtune=arm1176jzf-s -mhard-float -mfpu=vfp -O0 -ggdb -g
+ CFLAGS = -mcpu=arm1176jzf-s -mtune=arm1176jzf-s -mfloat-abi=softfp -O0 -ggdb -g
さらに、LIBのパスもYAGARTOのインストールパスに合わせて変更します。
- LIB = -L /opt/gnuarm/arm-none-eabi/lib/fpu/ -L/opt/gnuarm/lib/gcc/arm-none-eabi/4.8.1/fpu/
+ LIB = -L C:\yagarto\lib\gcc\arm-none-eabi\4.7.2 -L C:\yagarto\arm-none-eabi\lib
Rubyスクリプトについて
ところで、ここで使っているRubyスクリプトの説明をしていませんでした。
本記事で使っているraspiプロジェクトには、demo2.c
というファイルがあります(https://github.com/yamanekko/raspberrypi/blob/master/demo2.c )。これがいわゆるmainになるわけですが、この中にRubyスクリプトが(Cのコメントの形で)埋め込まれています。/* <ruby->
で始まって<-ruby> */
で終わる部分がRubyスクリプトです。
実際にはこの部分をCに変換したdemo.c
を使います。変換済みのdemo.c
はすでにレポジトリに含まれているので、このままであれば特に変換する必要はないのですが、demo2.c
のRubyスクリプトを変更し、自分で変換することもできます。この埋め込みRubyスクリプトの変換には実行ファイル形式になっているexecmrbc-rs
を使っていたのですが、これはOS Xの実行ファイルなので、WIndowsで変換するにはRubyで書かれたexecmrbc.rb
をお使い下さい(https://github.com/yamanekko/execmrbc )。
変換には以下のように実行します。
$ ruby execmrbc.rb 【変換元Cファイル名】 【変換先Cファイル名】 【mrbcのパス】
raspiプロジェクトをビルドする
Eclipseのメニューより「プロジェクト」→「プロジェクトのビルド」を選択し、ビルドします。
コンソールに「Build Finished」が表示されたら完了です。おつかれさまでした!
Raspberry Piで実行する
それでは先ほど作ったバイナリをRaspberry Piで実行してみます。
Raspberry Piで実行するには2通りのやり方があります。
- SDカードにバイナリをコピーして実行する
- JTAG経由でバイナリを転送して実行する
前者の方が簡単ではありますが、開発中にいちいちビルドするたびSDカードにPCにSDカードを挿入&バイナリをコピー&Raspberry PiにSDカードを挿入という流れをやっていると大変なのと、うまく実行できない時に何もできない(何が起こっているのか調べようがない)ので、最終的には後者の方がお勧めです。が、説明の都合上、前者のSDカードにコピーする方から紹介します。
SDカードにバイナリをコピーする
Raspberry Piのブート用SDカードを作るには、最低限ファイルが3つ必要になります。
- bootcode.bin
- start.elf
- kernel.img
最後のkernel.imgが先ほどビルドしたバイナリファイルで、上の2つを実行した後呼び出される形になります。最初の2つはgithubのRaspberry Pi公式レポジトリから取得します。
ちなみにこのbootcode.binとstart.elfはオープンソースではありません。ライセンスにはご注意ください ( https://github.com/raspberrypi/firmware/blob/master/boot/LICENCE.broadcom )
ビルドして作成された「kernel.img」と、「bootcode.bin」「start.elf」をFATでフォーマットしたSDカードにコピーします。Raspberry PiでLinuxを動かすときにはフォーマットに苦労しますが、Linuxを使わないならFATだけでいけるので扱いはラクです。
コピーをしたら、SDカードをPCから抜き取り、Raspberry Piに差込みます。
最初に用意したブレッドボードとRaspberry Piを正しく配線し、電源を入れるとLEDが点滅するはずです。
JTAGを使ってデバッグする
先ほどからたびたび説明していますが、JTAGを使ったリモートデバッグについても説明します。
そろそろ別エントリで説明すればよかったような気になってきましたが、ここまで来たらまさに乗りかかった船という気もします。頑張って読んでみてください。
必要なソフトのインストール
ARM-USB-TINY-Hドライバのインストール
ARM-USB-TINY-Hを使用するには、ドライバのインストールが必要になります。
ARM-USB-TINY-HをUSBで接続して、デバイスマネージャの「ほかのデバイス」に「Olimex OpenOCD JTAG ARM-USB-TINY-H」が警告アイコンつきで2つ表示される場合、以下の作業をします。正しくインストールできると「libusb-win32 devices」の下に「Olimex OpenOCD ARM-USB-TINY-H」と表示されます。
-
https://www.olimex.com/Products/ARM/JTAG/ARM-USB-TINY-H/ の中ほどのSOFTWAREの一番上に「OLIMEX ARM DEVELOPMENT PACKAGE V1.1 NEW」があるので、そのリンク先からzipをダウンロードする
- わかりにくければ以下のリンクから直接落とすことができます。 https://www.olimex.com/Products/ARM/JTAG/_resources/OpenOCD_OnlinePackage_v1.1.zip
- 解凍すると「OpenOCD_OnlinePackage_v1.1」というフォルダが作成されます。
- 展開したフォルダの中の
OpenOCD_OnlinePackage_v1.1\DRIVERS\olimex-libusb-1.2.2.0\driver.inf
というファイルを適用する- デバイスドライバーマネージャの「Olimex OpenOCD JTAG ARM-USB-TINY-H」を右クリックし、「ドライバーソフトウェアの更新」を選択する
- 「ドライバーソフトウェアを手動で検索してインストールします」を選択し、上記ファイルを指定する
OpenOCDのインストール
http://www.freddiechopin.info/pl/download/category/4-openocd よりOpenOCD0.7.0をダウンロードし、解凍してください。
以下の例では、解凍してできた「openocd-0.7.0」を「openocd」とリネームし、 c:\openocd に置いています。
Eclipseの設定追加
Eclipseの設定を追加します。
OpenOCD起動設定
- メニューの「実行」→「外部ツール」→「外部ツールの構成」を選択
- 新規ボタンをクリック
- 「openocd」など、わかりやすい名前をつける
- 「メイン」タブの「ロケーション」に
C:\openocd\bin\openocd-0.7.0.exe
と入力(OpenOCDをインストールしたパスに合わせて変更する) - 「引数」に
-f C:/openocd/scripts/interface/olimex-arm-usb-tiny-h.cfg -f ${workspace_loc:/raspi}/raspi.cfg
を指定 - 「共通」タブのお気に入りのメニューに表示で「外部ツール」を選択
- 「適用」をクリックし、閉じる
デバッグ設定
- 「GDB Hardware Debugging」を選択肢、新規ボタンをクリック
- 「メイン」タブの「C/C++アプリケーション」に「demo.elf」(実行したい.elfの名前)、プロジェクトに「raspi」を指定
- 「デバッガー」タブで「GDB Command」に「C:\yagarto\bin\arm-none-eabi-gdb.exe」を指定(YAGARTOをインストールしたパスに合わせて変更してください)
- 「Use remote target」にチェックを入れ、JTAG Deviceは「Generid TCP/IP」を選択、ホスト名は「localhost」ポート番号は「3333」を指定
- 「共通」タブのお気に入りのメニューに表示で「デバッグ」を選択
- 「適用」をクリックしてから閉じてください。
SDカードの作成
JTAGデバッグ用のSDカードを作ります。
SDカードには、先ほど作ったkernel.imgの代わりに、JTAG用のkernel.imgをコピーします。
-
https://github.com/dwelch67/raspberrypi/tree/master/armjtag にある
armjtag.bin
をダウンロードし、kernel.img
にリネームする - この
kernel.img」
と、先ほどの https://github.com/raspberrypi/firmware/tree/master/boot にあるbootcode.bin
、start.elf
の3ファイルををSDカードにコピーする - SDカードをRaspberry Piに差込み、電源を入れる
こうすると、Raspberry PiのGPIOピン経由でJTAGが使える状態になります。
実行手順
ようやくここまで辿り着きました。実行してみましょう。
- 外部ツールの構成で作成したopenocdを選択
- 起動したらtelnetで接続
- ここではTeraTermを使っています。ホスト名は
localhost
、ポート番号は4444
と入力し、TCP/IPで接続します。
- ここではTeraTermを使っています。ホスト名は
- 接続できたら
halt
を入力 - demo.cの適当なところにブレークポイントを作成
- Eclipseにて先ほど作成したデバッグを実行
これで、WindowsのEclipse上からRaspberry Piの実行をデバッグできるようになります。
以上です。最後までお読みいただきありがとうございました。
参考情報
- dwelch67/raspberrypi on github https://github.com/dwelch67/raspberrypi Raspberry Piをbere metalで使うための情報の本拠地的な場所。最後はここを見ることになっている。
- Raspberry Pi ・ View forum - Bare metal http://www.raspberrypi.org/phpBB3/viewforum.php?f=72 Raspberry PiをBare metal情報がいっぱいあるけれど、結局は上記サイトを見ましょう、で終わってることも多い。