Arduino M0 Pro を Eclipse と C 言語で開発しよう

投稿者: | 2017年9月12日

Next step is, developing Arduino M0 software by C language on Eclipse IDE.

先日は、Arduino Sketch のソフトを GDB でデバッグする方法を説明しました。今回は、Sketch ではなくて普通の C 言語で Arduino M0 Pro のソフトを設計する方法を学びましょう。

毎度のことですが、最初は前振りです。お急ぎの方は、「最初に GCC、OpenOCD のインストール」からお読みください。

なぜ C 言語?  なぜ GDB ?

Sketch で簡単お気楽プログラミングできるのが Arduino の利点なのに、なぜわざわざそんな苦行を?  という意見もあるかと思いますが、私は次のような理由で「生の」C 言語開発も必要だと思っています。

  • ARM Cortex-M プロセッサ(マイコン)の動きを勉強したい
    Arduino 開発環境では、マイコンをリセットしてからどのような仕組でプログラムが動作しているのか、レジスタとはなんなのか、割込の仕組はどうなっているのか、などが隠されていてよく分かりません。本当にマイコンの動作を理解しようとすると、この辺の知識は非常に重要です。
  • ハードウェアベンダー(ここでは旧 Atmel)が提供するライブラリを使ってソフトウェア開発をしたい
    マイコンのベンダー会社の多くは、自社で開発した様々なライブラリを提供しています。たとえば旧 Atmel 社(現 Microchip)は ASF と呼ばれるライブラリを提供しており、これを使って USB や A/D コンバータ、UART(USART)のソフトウェアを「比較的」容易に設計することができます。Arduino Sketch では、ASF のようなライブラリを併用する方法を積極的には提供していないので、場合によっては Sketch と ASF を共存させることが難しい場合もあります。このような理由から、C 言語と ASF だけを使ってソフトウェア開発をしたい場合があるでしょう。ASF は、有名な Keil MDK や IAR IDE だけでなく、GCC による開発にも対応しているので、高価な開発ツールを購入することが難しい日曜プログラマも、C 言語開発を勉強しておく価値はあるはずです。
  • プログラムを小さなメモリに納めなくてはいけない
    今回使っている SAM D21 マイコンは比較的ふんだんなメモリを利用できますが、ARM Cortex-M マイコンの中には、NXP LPC810M のように、フラッシュメモリ 4Kバイト、RAM 1Kバイト、といったように非常に搭載メモリが少ないものもあります。このようなマイコンでは Arduino Sketch での開発が困難です。(おそらく、LPC810M 用の Arduino ボードマネージャのサポートは無いはず。) そんなときには、Eclipse などを使った GCC 開発が役立ちます。(もちろん、Keil MDK の無償評価ライセンスでも LPC810M の開発はできますが。)

最初に GCC、OpenOCD のインストール

これから GNU MCU Eclipse をインストールしますが、その中に GCC(ツールチェイン)や OpenOCD は付いてきませんので、予めインストールしておきます。Mac OS X の場合は、前回の記事を御参考いただき、GCC、GDB、OpenOCD をインストールしておいてください。次のセクション以降は、/usr/local/bin の下にツールチェインが入っていることを前提としています。(Homebrew でインストールすると、そのようになるはずです。)

GNU MCU Eclipse のインストール

GCC や make コマンドをガリガリ使ってプログラミングしても良いのですが、今回は Eclipse ベースの無償 IDE(統合開発環境)を使ってみましょう。

実際、Atmel ASF ライブラリをダウンロードすると、GCC と make(Makefile)を使うことを前提としたサンプルコードが付いてきます。Eclipse で Makefile ベースの開発をすることもできるはずです。

GNU MCU Eclipse は、以前 GNU ARM と呼ばれていましたが、ARM 以外の組込系プロセッサをサポートするようになったことから名称が変わったようです。

以下の説明では、GNU MCU Eclipse IDE for C/C++ Developers Neon.3 20170711 バージョンを使うことにします。バージョンの違いにより、メニュー構成などが異なることがあると思いますが、その際は他の場所を探してみてください。(本家サイトの説明通りにメニューを探しても見つからないことがあります。)

なお、詳細な説明はこちらを御覧ください。以下、本家情報はこのように「引用」形式で説明します。https://gnu-mcu-eclipse.github.io/plugins/install/

ダウンロードが終わったらインストールしてください。ここまでお読みの方には余計かも知れませんが、ターミナルで

$ cd /var/tmp
$ zcat ~/Downloads/20170711-2049-gnumcueclipse-4.1.1-neon-3-macosx.cocoa.x86_64.tar.gz | tar xf -

などと展開ののち、Finder で Eclipse.app をアプリケーションフォルダに移動(インストール)してください。

GNU MCU Eclipse の基本的な設定

インストールした Eclipse を起動すると、Eclipse Launcher というダイアログが現れ、「Select a directory …」と要求してきます。Eclipse を御存知の方にはお馴染みの表示ですが、ワークスペースを置きたいディレクトリを選んで OK します。分からない方はデフォルトのままで良いでしょう。

続いて Welcome タブが表示されます。いろいろと興味深そうなメニューが表示されますが、今回は Welcome タブをクローズしてしまいます。(新しいワークスペースを作ると、また表示されますので御安心を。)

Pack のインストール

ARM プロセッサでは、いろいろな会社から様々な統合開発環境が提供(販売?)されています。上に述べた Keil MDK(uVision)や IAR などが有名だと思います。これら異なる開発環境をデバイスメーカーが全てサポートするのは大変なためか、最近は CMSIS-Pack というフレームワークが用意され、共通の枠組みでランタイムライブラリやスタートアップルーチンを利用できるようになっています。(私はこの分野に長くないので、聞きかじりですスミマセン。)

GNU MCU Eclipse でも、この Pack という枠組を利用できます。最初に Packs manager をセットアップしておきましょう。

本家の説明は、こちらです。

Welcome タブをクローズした後に次のような画面が表示されていると思いますので、上のメニューにある「荷積み」のようなアイコンをクリックします。

Packs のウィンドウが開いたら、続いて Update アイコンをクリックします。(ああ、GUI の説明って面倒。)

しばらく時間がかかりますが、昼ご飯でも食べながら待ちましょう。途中で Read error というのが出ることがありますが、そのときは Ignore して大丈夫です。(どこかのデバイスメーカーのファイルがネットからダウンロードできない、という意味だと思います。)

そうしたら、今回の Arduino M0 Pro に搭載されているマイコン Atmel SAMD21G18A を探します。

Arduino M0 Pro 基板上のデバイスの捺印をよーく見た人は、SAMD21G18A-U と印字されているのを見つけたと思います。この「U」は、Atmel 社では Package Grade といって動作可能温度範囲などを示しているものです。「U」は、摂氏 -40〜85度で動作保証している製品です。今回のようなプログラミングには特に関係ないので、無視して大丈夫です。

画面の左の「枠」(ペイン?)に Devices というタブがあるのでそれを選び、Atmel という部分を開きます。中央の Packs というペインから SAMD21_DFP を探し、すぐ左の三角をクリックしてツリーを開きます。最新版の DFP(私が見たときには 1.2.0 が最新)を右クリックし、Install しましょう。Eclipse の画面右下の小さなプログレスバーが表示されますので、完了まで待ちましょう。

さて、Pack をインストールすると何が嬉しいのでしょうか。GNU MCU Eclipse では Pack を完全にサポートできていないようですが、ここの説明を見ると、Benefits という部分に何が嬉しいのか説明されています。現状では、

できること

  • デバッグセッション時に適切な設定を選んでくれたり、
  • ペリフェラル(USART とかタイマとか)のレジスタアドレスを覚えてくれたり

できないこと

  • ビルド時に適切なコアアーキテクチャ(Cortex-M0+)を選んだり、
  • リンカスクリプトにメモリサイズを反映したり、
  • CMSIS のヘッダファイルやソースファイルを自動的に配置したり

などと説明されています。今後のバージョンアップに期待です。(ユーザーが増えることが大事かも知れません。そんな訳で、これを書かせて頂いています。 🙂 )

という訳で、CMSIS のヘッダやソースファイルを自動的に設定してくれたりはしないのですが、この辺りは厄介なので、今回は説明しません(できません)。本当はこれらをしないと、デバイスの正しい初期設定ができないので、詳しい方は Keil MDK を参考にしたりして、ヘッダやソースファイルをコピーしてください。

…だけでは寂しいので、とりあえずメモリサイズだけでも調べておきましょう。上記 DFP 1.2.0 をクリックすると画面右に Outline というペインが表示されますが、そのツリーを開いていくと、以下のように情報が表示されます。SAMD21G18A のフラッシュ ROM サイズは 0x40000(256K)バイトであることが分かります。これをリンカスクリプトに反映すれば良い訳ですね。(あとで説明します。)

GNU MCU Eclipse で Hello World

次に、簡単なプロジェクトを作成し、プログラムをビルドしてデバイス上で動かしてみましょう。

Eclipse の経験をお持ちの方は御存知と思いますが、Eclipse には perspective という機能があり、プログラミングやデバッグのための適切な画面レイアウトやメニュー構成に簡単に(一発で)切り替えることができます。画面右上を見ると次のようになっていると思いますので、「荷積み」アイコン左の「C という文字が書かれた」アイコンをクリックしましょう。これで、C/C++ perspective に切り替わります。

続いて、メニューから New → C Project を選びます。まず Project name というところに適当な名前をつけます。ここでは myfirst としましょう。

次に、Project type から Hello World ARM Cortex-M C/C++ Project を選び、右の Toolchains から ARM Cross GCC を選び、Next > をクリックします。

C Project というダイアログが表示されるので、Processor core には Cortex-M0+ を設定します。Clock (Hz) ですが、これはデバイスの初期設定をしないと意味がないので、とりあえず 8000000 のままにしておきます。前述の CMSIS 関連のソースファイルをちゃんと適用してハードウェアクロックの設定ができるようになったら、また考えましょう。

Flash size と RAM size も、今回の Hello World ではあまり意味がないのですが、先ほどせっかく調べたので設定しておきましょう。それぞれ、256 と 32 ですね。

次の Use system calls と Trace output が難関です。GNU MCU Eclipse のサイトを調べても詳細な説明が見つかりませんでした。(探し方が悪いかも。) Use system calls は、POSIX 風の open() とか write() という API を使うかどうか、ということだと思います。今回はそこまで要求しないので、Freestanding (no POSIX system calls) としておきましょう。

Trace output のほうは、いわゆる printf() デバッグで、デバッガの画面にメッセージを表示することのできる機能の設定です。通常、マイコンでは UART や USB CDC 等を使わないと printf() すらできないものですが、ARM では Semihosting という機能があって、それらのデバッグメッセージをデバッガに送ることができるようになっています。ARM Cortex-M4 などでは SWO という機能を使って、デバッガ用のピンから直接メッセージを送ることができるようですが、Cortex-M0+ にはその機能がないので、マイコンのコアとデバッガが、ブレークポイント機能などを駆使してメッセージをやりとりします。これは便利な機能なので、試してみましょう。

というわけで、Trace output では Semihosting DEBUG channel を選びます。STDOUT stream というのも面白い機能ですが、今回は扱いません。

詳しくはこの辺に説明があります。

ここまで設定したら、次のようなダイアログ画面になっているはずです。

Next > をクリックします。いろいろとフォルダ名の指定をするダイアログが出ますが、今回はそのままで、再度 Next > します。Select Configurations もそのまま Next > です。

次が問題です。ここでは、ソースコードのビルドに使うツールチェインを設定します。上の Toochain name はそのまま(GNU Tools for ARM Embedded Processors (arm-none-eabi-gcc))で良いのですが、Toolchain path の設定が必要です。Mac OS X で Homebrew を使ってインストールした場合は、このテキストボックスに /usr/local/bin と入力してください。

そして、Finish ボタンを押します。画面左の Project Explorer というペインに myfirst というツリーが表示され、画面中央に main.c ソースコードが表示されていれば OK です。それではビルドしてみましょう。

Project Explorer の myfirst を右クリックし、Build Project を選びます。

なおここで、これは GNU MCU Eclipse のバグだと思うのですが、Build Project がグレイアウトされてクリックできないことがあります。その場合は、main.c ソースコードのどこかを一度クリックして、再度試すとうまくいくようです。不可解な挙動です。

プログレスバーのダイアログが閉じたら、画面下のペインから Console というタブを探してクリックし、選択します。ペインの末尾に、

Invoking: GNU ARM Cross Print Size
arm-none-eabi-size --format=berkeley "myfirst.elf"
   text   data     bss     dec     hex filename
   3829     160     416   4405   1135 myfirst.elf
Finished building: myfirst.siz

12:01:34 Build Finished (took 2s.273ms)

のような表示があれば、正しくビルドされています。

プログラムの実行

それでは実際に Arduino M0 Pro 上でプログラムを実行してみましょう。

重要な注意: この後でプログラムを Arduino M0 Pro に書き込むと、Arduino Sketch のためのブートローダーが上書き(消去)されてしまいます。そのため、あとで Arduino IDE でプログラムを動かすときは、まず USB ケーブルで Programming ポートに接続したのち、Arduino IDE のメニューで

  1. ツール → ボード → Arduino M0 Pro (Programming Port) を選ぶ
  2. ツール → 書込装置 → Atmel EDBG
  3. ツール → ブートローダを書き込む

としてブートローダを書き戻してください。

最初に、GNU MCU Eclipse のグローバル設定をしておきます。メニューで Eclipse → 環境設定… を選びます。(最初のほうで述べたように、この辺のメニュー設定はバージョンによって違うようなので、「頑張って」Preferences ダイアログが表示されるように探してください。)

Preferences ダイアログが表示されたら、左側の一覧から MCU → Global OpenOCD Path を選びます。Folder というボックスに /usr/local/bin と入れます。(これは、HomeBrew で OpenOCD をインストールした場合です。違う場合は、適切なパスを調べて入力してください。) そして OK をクリックします。

つづいて、Arduino M0 Pro の Programming ポートに USB ケーブルでパソコン(私の場合は Mac)を繋ぎます。GNU MCU Eclipse のメニューから、Run → Debug Configurations… を選びます。

Debug Configurations のダイアログで、左に見える GDB OpenOCD Debugging をダブルクリックします。すると、myfirst プロジェクトのための設定枠(ペイン)が表示されます。

Main タブでは、Project に myfirst と入力されていること、また C/C++ Application で Debug/myfirst.elf が入力されていることを確認してください。

次に Debugger タブを開きます。Start OpenOCD locally にチェックが入り、Actual executable の欄に /usr/local/bin/openocd と入力されていることを確認してください。(デフォルトでは、ここに先ほどのグローバル設定が反映されます。)

次に、Config options という欄を探してください。ここでは、前回説明した OpenOCD の設定ファイルを指定しなくていけません。ここが分かりにくい点の一つです。例えば arduino_zero.cfg という設定ファイルを作り、プロジェクト myfirst のフォルダ直下に置きます。簡単な方法としては、Finder を使って、.cfg ファイルを Project Explorer 下の myfirst プロジェクトにドラッグ & ドロップします。(一度、ダイアログ下の Apply ボタンと Close ボタンを押してからコピーし、再度、このダイアログを開き直すのが良いでしょう。) そして、Config options のテキストボックスに、

-f arduino_zero.cfg

と入力します。「-f」を忘れないようにしましょう。

あとは、デフォルトの設定のままで良いと思います。Debug Configurations ダイアログ下部の Apply ボタンを押し、さらに Debug ボタンを押しましょう。(以後は、ウィンドウ上部にある「虫」アイコンの下向き三角をクリックし、myfirst Debug という項目を選べば  OK です。この名前は、先ほど Debug Configurations ダイアログで GDB OpenOCD Debugging をダブルクリックした際につけられた名前です。変更することも可能です。)

ここで、Confirm Perspective Switch というダイアログが表示されるかも知れません。これは、勝手に perspective (画面レイアウト)を切り替えてしまうけど良い?  ということなので、Yes をクリックします。Remember my decision にチェックを入れておいても良いでしょう。

するとデバッガが立ち上がります。

重要な注意(その 2): なおここで、OpenOCD から次のようなエラーが出て停止してしまうことがあります。

Error: SAMD: NVM lock error

これは、「マイコン内のフラッシュメモリが保護されているので、プログラムをロードできません」という意味のようです。技術的には、Atmel マイコンのフューズ ROM(EEPROM?)にある BOOTPROT という設定が効いているようです。BOOTPROT を 0x7 に設定すれば直るはずですが、この設定はちょっと厄介です。

一番簡単な方法は、上で説明した方法で Arduino Sketch のブートローダーを書き戻すやり方です。買ってから一度もブートローダー領域を消して(いじって)いない Arduino M0 Pro でこのエラーが出た場合も、一度この方法でブートローダー領域を再設定すると直ることがあるようです。なおこの際、最新版の Arudino IDE を使ったほうが良いかも知れません。

下記のような、画面上部の「Resume」アイコン(マウスカーソルを合わせると、Resume (F8) と表示されます)をクリックするとプログラムが動きます。

Eclipse 画面下方の、Console というタブ(選択されていなければクリックして開きます)下のウィンドウに、

Open On-Chip Debugger 0.10.0
(略)
Hello ARM World!
System clock: 8000000 Hz
Second 1
Second 2
Second 3
(略)

のような表示が出ていれば、正しく動作しています。ちなみに、どうしてマイコンから printf() の結果がここに表示されるの!? というのが、先ほど説明した Semihosting という機能です。ソースコードで main() 関数の中を見ると、

trace_puts("Hello ARM World!");

とか

trace_printf ("Second %d\n", seconds);

といった関数呼び出しが見つかると思います。これが、それです。便利ですね。

一つ注意ですが、ARM Cortex-M0+ では、ブレークポイントの機能を使ってマイコンのコアとデバッガが通信することでこれを実現しています。つまり、一瞬ですがブレークポイントでソフトが停止していることになりますので、リアルタイム制約の強いソフト(割込を使っているなど)では、割込のタイミングを逃すなど、正しく動作しないこともあるので注意が必要です。

ところで…(重要なオマケ)

前回、 Arduino Sketch のコードを GDB でデバッグしました。これを、Eclipse の画面上でできたら便利だと思いませんか?   ちょっと試してみましょう。

ここでは前回に試した、Blink サンプルをビジュアルにデバッグしてみましょう。前回のディレクトリ ~/arduino_build_344897 は、まだ残っていますでしょうか。 🙂  以下、残っていることを仮定して進めます。

まず最初に、上のほうで説明した方法で Arduino のブートローダーを書き戻しておきます。ブートローダーがないと Blink サンプルコードは動きません。

次に GNU MCU Eclipse を起動します。もし既に起動している場合は、そしてデバッグセッションが継続中の場合は、Run → Terminate でデバッグセッションを停止します。

まず、メニューから Run → Debug Configurations… を開きます。GDB OpenOCD Debugging をダブルクリックします。Name というテキストボックスには、Arduino Blink と入れましょう。次に、Main タブの下で Project の欄は空のまま、C/C++ Application のテキストボックスを設定します。Browse… ボタンをクリックし、~/arduino_build_344897  の下にある Blink.ino.elf を選びます。

次に Debugger タブを選びます。Start OpenOCD locally にチェックが入っていることを確認したら、最初に、Executable の欄に /usr/local/bin/openocd と入力しましょう。Eclipse プロジェクトの場合は自動的に変数 openocd_path や openocd_executable を見てくれるのですが、今回は明示的に指定する必要があります。同様に、GDB Client Setup では、Executable の下に /usr/local/bin/arm-none-eabi-gdb と入力します。

最後に、Config options に -f arduino_zero.cfg と入力されていることを確認しましょう。このファイルは、今回は ~/arduino_build_344897 に下に置いておく必要があります。最後に Apply ボタンを押して、まずは Close しましょう。

ここで大事な設定

このままデバッグを始めると、例えば loop() のように Blink.ino で定義したファイルにジャンプした瞬間に、Eclipse は Arduino IDE を開こうとします。これは困るので設定を変更します。Eclipse → 環境設定… で Preferences ダイアログを開きます。

そうしたら、左のツリーから General → Editors → File Associations を開きます。File types という画面で、Add… をクリックして *.ino を追加します。次に、その下の Associated editors 隣の Add… ボタンを押し、C/C++ Editor (default) を選択します。次のような画面になれば、ちゃんと設定できています。最後に OK ボタンを押して確定しましょう。

そうしたらデバッガを立ち上げましょう。メニューの Run → Debug Configurations… を開き、左のリストから先ほどの GDB OpenOCD Debugging → Arduino Blink を選択し、Debug ボタンを押しましょう。

main() 関数に飛んでブレークしましたでしょうか。次のような画面になるはずです。

ここで簡単に loop() 関数内にブレークポイントを張れれば良いのですが、簡単な方法が見つかりませんでした。そこで、main.cpp のファイルの中で 51行目の loop() という関数呼び出し部分を右クリックで選択し、Run to Line という項目を選びます。そうすると、loop() 関数に入る直前でブレークします。ここでメニューから Run → Step Into とすれば、loop() 関数内で停止するはずです。おそらく最適化の都合で delay() 関数呼び出しのところで停まりますが、あとは digitalWrite() 呼び出し等の適当な行の先頭(行番号付近)をダブルクリックすると、ブレークポイントが設定されます。画面右上の Breakpoints というタブを選択すると、現在有効なブレークポイントが表示されます。

最後に、メニューから Run → Resume とすると、次のブレークポイントまで実行されるはずです。あとは前回と同じです。これで、「ビジュアルにデバッグ」できますね!

次回は、時間があれば Semihosting についてもう少し詳しく調べてみたいと思います。今日はここまで。

おしまい。