さて、KEIL MDKでビルドできるようにはなったけど、試用版だと作成できるプログラムに32kBの制限がある。また、scatterファイルも使えないみたいな記述もある。なにより、ダウンロードするのに登録が必要なのが嫌だ(したけど)。
他の開発環境としては、IAR Embedded Workbench と言うのもあるが、こちらも登録が必要&機能制限版だ。
他にないかと思って調べていた所、マイコン風雲録: arm-none-eabi-gcc / 2012年メモと言うページを発見。GCC ARM Embedded in Launchpadと言うものがあるようだ。
これは、本家ARMの人が開発に携わっていて、登録不要でダウンロードできる。さらに、WindowsのみでなくLinux用やMac用も存在する。
早速、上記ページからMac用の gcc-arm-none-eabi-4_8-2014q1-20140314-mac.tar.bz2 をダウンロードし、$HOMEに展開、$HOME/gcc-arm-none-eabi-4_8-2014q1/bin に PATH を通す。
前回MDKで利用したSysTickタイマを使ったLED点滅プログラムを試そうと思ったが、includeしているmcu.hと、それがincludeしている各種ヘッダがないため、そのままだとビルドできない。
そこで、まずは予習。fm3のデータシートと、ペリフェラルマニュアル、前回のサンプルプログラムと、それがincludeしているヘッダファイルを読む。

メモリマップ

メモリマップは、データシートに載っている。
知っておかないといけないことは、以下くらいか。
  • flash ROM は、1Mbyteで、0x0000_0000〜0x000f_ffff
  • SRAMは64kbyteの領域が2つあり、0x1fff_0000〜0x0x1fff_ffffと0x2000_0000〜0x2000_ffff
  • ペリフェラルは0x4000_0000〜0x43ff_ffffに割り当てられている

GPIO

GPIOを使うために押さえないといけない情報は、Interfaceのページからダウンロードできる「拡張コネクタピン配置図」と、チップのペリフェラルマニュアルにある。
ペリフェラルマニュアルのレジスタの説明を見ると、IO関連のレジスタとしてPFR, PCR, DDR, PDIR, PDOR, PZRがあることがわかる。(他に、E***もあるが、当面無視)
PFR
ポートをGPIOで使用する(0)か、周辺機器として使用する(1)か
PCR
内蔵プルアップ抵抗を接続する(1)か、切断する(0)か
DDR
入力端子で使用する(0)か、出力端子で使用する(1)か
PDIR
現在のレベル状態を読出し
PDOR
出力レベル設定。1: high, 0: low
PZR
オープンドレイン(1)
雑誌の付属基板の場合、IOポートのPF3(ポートは、16進二桁で表す。PF3は0xF3のポートと言う意味)に負論理で繋がっているらしい。
ここで、負論理とかオープンドレインとか知らない用語が出てくる。
オープンドレインについて調べたところ、RA4の罠がわかりやすかった。このページの最後の図を見ると、意味が理解できると思う。
とにかく、基板上のLEDは、「ポートF3をGPIOで使う設定にし、オープンドレインにし、入出力を出力にし」た上で、出力をlowにすれば点灯し、highにすれば消灯するのだ。

ここまでの前提知識を入れた上でSysTickタイマのサンプルプログラムを見ると、FM3_GPIO->*F_f.P3 にアクセスして上記の内容を実現していることがわかる。
FM3_GPIOの正体は、mb9b610t.h で宣言されている構造体のポインタであり、0x40033000を指している。
この構造体は、レジスタ毎に PFR*_fやPZR*_f のような構造体を持っており、その中に P0〜PFのようなメンバ(ビットフィールド)を持っている。*の部分と、P?の部分でポートを特定するのだ。
さらにこのヘッダを読み込むと、bFM3_GPIO_PFR0_P0 のような define があることに気づく。
コメントには、「Peripheral Bit Band Alias declaration」とある。
上記構造体がビットフィールドでメンバを持っているため、ビット毎にunsigned intでアクセスできるようにしたアドレスエイリアスがあるようだ。
ここまでわかれば、アプリケーションは書けそうだ。後はコンパイル&リンクの方法だ。
まず読むのは、gccのreadme.txt
ここを読むと、アーキテクチャ毎のコマンドラインオプション、libcであるnewlibとnewlib-nano、gcc-arm-none-eabi-*/share/gcc-arm-none-eabi/samplesにサンプルがあることがわかる。
早速サンプルディレクトリを見ると、startupコード、リンカスクリプト、Makefile等のサンプルを見ることができる。
サンプルプログラムのコード自身は、無限ループだったり、printfだったりしてあまり参考にならない。

まずはstartupコード。gcc-arm-none-eabi-4_8-2014q1/share/gcc-arm-none-eabi/samples/startup/startup_ARMCM3.Sを使う。
試しに、MDK用のstartupコード startup_mb9bf61x.s と比較すると、アセンブラの文法が違う!
最初は、MDKのサンプルのstartupコードをそのまま使おうと思ったのだが、アセンブラが文句を言うので諦めた。
startupコードを眺めると、stackセクション、heapセクションに続き、isr_vectorの定義が続く。MDKのと比べると、stack size は0x200と0xc00でgccの方が大きく、heap size は両方共0、isr_vectorはgccの方はSysTick_Handlerで終わっているが、MDKの方はその後にいろいろある。
isr_vectorと言うのは、大雑把に言うとアドレス0x0から配置されるアドレステーブルで、割り込みがあったときにそこに書いてあるアドレスの割り込みハンドラを実行するためのもの。
続いて、Reset_Handler。MDKのものは、SystemInitを呼んで、__mainを呼ぶと言うものだが、gccのものは初期値付データのROMからRAMへのコピーと、BSSの0クリアがあったあとSystemInitの呼出、_startの呼び出しとなる。
__mainも、_startも、ユーザが書くコードではなく、libcに含まれるCのスタートアップルーチンと思われる。
その後は、isr_vectorに並べた割り込みハンドラとして、何もしないDefault_Handlerがweakで定義される。
知識がないので推測だが、ユーザがXXX_Handlerを書いてリンクするとそちらが採用され、書かないとデフォルトハンドラがリンクされるのだと思う。

続いて、リンカスクリプト。gcc-arm-none-eabi-4_8-2014q1/share/gcc-arm-none-eabi/samples/ldscripts/gcc.ld を採用する。
中を見ると、MEMORYと言うセクションにFLASH, RAMと言う定義があり、開始アドレスとサイズが書いてあるので、データシートから読み取った値に変更する。(rename and modify for target · cd4fcca · false-git/fm3)
後は、サンプルのMakefileを参考にMakefileを書く。(fm3/sample1/Makefile at master · false-git/fm3)
そうそう、gccでビルドしてできるのはaxfファイルなので、USB DIRECT Programmerで焼けるように hex 形式に変換してあげないといけない。
調べてみたところ、objcopyを使うと変換できるらしいので、Makefileに書いておいた。
一応 Makefile の解説をしておく。
  • NAME はアプリケーションの名前。この名前の.cがあることを想定していて、この名前の.axf, .hexができる
  • ARCH_FLAGS は readmeに書いてあったアーキテクチャ毎のオプション
  • STARTUP は、startupコードの.o
  • CFLAGS/CXXFLAGS/ASFLAGSは説明は不要か
  • USE_NANO は、newlib-nanoを使うときに定義する。使わない場合は空にする。ちなみに、nanoだとバイナリが2kくらい。nanoじゃないと10kくらいになる
  • USE_SEMIHOSTとUSE_NOHOSTはsemihostかそうじゃないかでどちらを使うか決める。JTAGを持っていないので、NOHOSTの方を使っている(参考: ARM Information Center)
  • GCは良くわからない。サンプルからそのまま
  • MAPはmapファイルの出力を指示する
  • LDSCRIPTSは、リンカスクリプトの指定
  • LFLAGSはリンク時のオプション
と、言うわけでビルドできるようになった一式は、githubに上げてある。(fm3/sample1 at master · false-git/fm3)
あ、sample1.cの説明をしていなかった。
これは単に、基板上のLEDを点灯させるだけのプログラムである。
MDKのサンプルだと、SysTick_Config()とか呼んで、SysTick_Handlerで点滅させていたのだけれど、gccでこの辺が使えるかわからなかったので、点灯させるだけで無限ループに入るようにしてある。
SystemInit()は、startupコードを__NO_SYSTEM_INITを定義せずにビルドすると呼ばれるので、空の関数を入れてある。
次回は、MDKのサンプルが動くように、CMSISをgccで動かす話をしようと思う。

カテゴリ

トラックバック(0)

このブログ記事を参照しているブログ一覧: fm3マイコンで遊ぶ(2)

このブログ記事に対するトラックバックURL: https://www.wizard-limit.net/cgi-bin/mt/mt-tb.cgi/3161

コメントする

このブログ記事について

このページは、falseが2014年5月28日 11:06に書いたブログ記事です。

ひとつ前のブログ記事は「fm3マイコンで遊ぶ(1)」です。

次のブログ記事は「fm3マイコンで遊ぶ(3)」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

広告

Powered by Movable Type 6.1.1