mbed TOP

mbedの中を覗く 

 mbedの中身やARMのCPUの使い方を見るためにmbedのライブラリのソースを見たい。スタートアップ処理も見たい。
 それに実際にコードを書き始めると、ライブラリ関数の動作が細かく分からないと不便なのだ。
 例えばSerilPort.readable()。
 この関数は受信データが有るときに「1」を返すの?それとも受信バッファに溜まってるデータ個数を返すの?受信バッファのサイズは?オーバーフローした場合には受信データを捨てるの?それとも古い受信データを捨てて最新のデータは残す?・・・などなど。
 「やってみりゃ分かる」んだけど、この手の関数が沢山あるんでいちいちやってらんないじゃん。
 mbedのAPI

 自分で作ったライブラリじゃないんで、やっぱり目を通す必要が有る。
 見るだけで理解するのは難しいけど、ステップ実行しちまえばARMのCPUの動作を含めて理解もすすむってもんだ。
 ちゅうことでμVisionの出番なり。


目次

 mbed-srcをステップ実行するための下準備
 LPC1768CPUのメモリマップとスキャッタファイル
 秋月のmbedじゃない素のCPUボードでI/Oをしてみる
 mbedのスタートアップ処理
 物理アドレスに直接アクセスする方法
 気がついたこと2
 UART


mbed-srcをステップ実行するための下準備 2014/02/01

 mbed-srcの入ったプロジェクトを作る。
 大まかな流れとしては・・・
 1:オンラインコンパイラでプロジェクトを生成
 2:これをExportしてローカルに持ってくる
 3:μVision用にちょい手を加える
 4:ステップ実行して中身を見る


 まずは例によってオンラインコンパイラでプロジェクトを生成する。
 適当にNewProgramを生成(ここでは「_MbedTraceSrc」と言うプロジェクト名にした)。

 


 次に非常に大事な事だけど、下図の
mbedフォルダを削除する
 これをしないと以下のエラーが発生する。
 Error: L6235E: More than one section matches selector - cannot all be FIRST/LAST.
 本家mbedのQ&Aには解決策がいまだに出てない 2014/2/1現在
 ま、「Sectionが重複してる」って事でしょう。
 そりゃそうだよね。だってライブラリのソースをコンパイルしてリンクしようとしてるときに、元々の物を残したままなんだもん。
 でもさぁ、なかなか気が付かなくて、NXP fanさんのQ&Aに質問を出して教えてもらったのだ。
 とても素早く親切に図入りで教えてもらいました。有り難うございました。
 で、とにかくこいつをDeleteするのだ。

 


 次にmbed-srcのライブラリのソースをImportする。
 オフィシャルなソースはここにあるのでImportする。

 


 Importするとプロジェクト内にソースが入るので早速コンパイルする。と、エラーが出る。まったくも〜!!
 ちっともオフィシャルじゃない(笑)

 


 で、NXP fanさんに教えてもらった通り、少し古いソースを使う(リビジョンを戻す)。
 現時点(2014/2/1)ではRevison78を選択・・・オンラインコンパイラでリビジョンを戻したりも出来るんだね〜凄い。
 上図赤枠の「Fix it」をクリックするとリビジョンを変えなくてもエラーが修正されるようだ。

 


 で、再度コンパイル。
 おお〜やった〜
 見事に成功であります。

 


 んじゃ次。
 μVisionで使うためにこいつをExportする。
 やり方はいつもと同じ。

 


 Exportしたのをローカルディスクに入れてやる。
 で、uvprojをWクリックしてμVisionを起動。

 


 ソースの数が多いんで、だらだら〜と出てくるけど、これじゃ使いにくいんで、main.cppだけは別のグループにする。
 使いにくくなければそのままでもOKだけど、気分がよろしくないよね。

 


 元のフォルダ名をMbedSrcにリネーム。
 main.cppを入れるフォルダをMySrcと言う名前で生成。

 


 Manage Projectを使ってソースを入れ替える。
 D&Dが使えると便利なんだけど(もしかして使えるのかもだけど)、自分の環境では上手く行かないんで、こつこつと移動した。
  → その後D&Dでソースの移動OKを確認した。

 


 ELFファイルの転送先を指定する(JTAG/SWD使用時は自動で転送されるので不要)。




 ソースがこんな感じで綺麗にまとまった。
 んじゃビルド(コンパイル)してやる。
 お見事です〜行きました。

 


 いよいよステップ実行開始じゃ。
 μVisionをデバッグモードにして、main()先頭からStep one lineで実行する。
 良いじゃ〜ん!
 C++のコードも、アセンブラコードも、レジスタの変化も変数の変化も一目瞭然。
 これぞデバッガ冥利に尽きるってやつですね。
 あ〜気持ち良い(^^;

 


LPC1768のメモリ管理とスキャッタファイル 2014/05/07 officialな説明書P9

 ※スキャッタファイルはHEWで言うところのセクション設定みたいなもの
 この頃やっとメモリ管理を見始めたのでメモ。
 mbedLPC1768のBlinkyをExportし、MDK-ARMに取り込んでビルドして実験。
 使ったCPUボードは「mbedではないただのCPUボード」で、秋月謹製BlueBoard
 こうすればmbedからExportしたプロジェクトが、素のLPC1768CPUでちゃんと動くかも確かめられるし。

 このボードに某国製のやや怪しいデバッグアダプタであるRealViewを挿す。
 怪しいCPUボード+怪しいJTAGアダプタ+怪しい頭の奴の実験なので、結果は間違いなく怪しい(^^;

  怪しものセット。
このRealView、今でも$17で売ってる。しかも送料無料。
しかもそれなりに動くんだよ。



 ところで未だスキャッタファイルの取説を読んでないんだった。間違いは後で修正します・・・多分

  LPC1768CPUのメモリ配置だけど、こんな感じになってるらしい。データシートP23
 ・青枠部分は、CODE用のフラッシュROMエリア
 ・赤枠部分は32K。ZI-dataで示されるRAM1エリア.
  ここ、ちょっと変かも。
   図では0x1000-4000からだけど、スキャッタファイルでは0x1000-0000を指定してるんだよな。不明。
  <解決> 2015/01/18
    図の見方を間違えてた。
    RAMの頭はどのシリーズでも共通の0x1000-0000だが、尻はLPC1764なら0x1000-4000、LPC1768なら0x1000-8000ってこと。
 ・緑枠部分は32K。RW-dataで示されるRAM2エリア。
 
 

2014/05/08
ユーザーズマニュアルP14を見たら別のことが書いてある。
こっちだとスキャッタファイルと整合性があるから、こっちにしておくか・・・雑(爆)
 
 

このメモリマップとスキャッタファイルを見比べる(スキャッタファイルはProject → Options → LinkerでEditボタンを押すと出てくる)。
MDK-ARMではメモリマップの指定にこのスキャッタファイルってのを使うらしい。
未だProject → Options → Targetダイアログでの設定との関係は不明・・・そのうちに分かるっしょ。
<解決> 2015/01/18 ここ

で、スキャッタファイルは上から順に・・・
 1:CODEエリアで、フラッシュROMの開始アドレスとサイズ指定。
 2:DATAエリア1で、SRAMの開始アドレスとサイズ指定。外部変数はここにとられ、リンク時にZI-dataに出てくる。
 3:DATAエリア2で、SRAMの開始アドレスとサイズ指定。特殊命令で指定するとリンク時にRW-dataに出てくる。
白いアンダーラインを引いた文字列は特殊命令(__attribute__)で指定するときのsection名だ。
 
 

MDK-ARM上で外部変数を宣言してリンクすると以下のようにZI-dataが変化する。
このことからZI-dataは外部変数のサイズと予測。
2014/05/10追記
ZIは「ゼロで初期化された」という意味だとさ。ちゅうことはやっぱり外部変数ってことで良いみたい。
 
 

今度はスキャッタファイルを少しいじってやる。 スキャッタファイルの構文
DATAエリア2(IRAM2)のサイズを元の2倍の0x8000にし、section名をIRAM2TOPとしてみた。
 
 

で、ビルドする。
RW-dataに__attribute__指定した変数bのサイズが入り、ZI-dataには外部変数aのサイズが入る。
__attribute__指定は物理メモリへの直接アクセスで、この例ではスキャッタファイルのsection名でアドレスを指定してる。
ふむ〜〜これで合ってるのかなぁ(笑)
 
 

<淡い期待>▲
で、ものは試しと以下のようにIRAM2の指定を変え、外部変数 a[0xf000] とでかく宣言してみた。
もしかしてIRAM1とIRAM2が連結してZI-dataが64Kに広がるかとちょい期待したけど、やっぱり無理だわな。
 



素のLPC1768でI/Oする 2014/05/08

 上で使った秋月のBlueBoardのI/Oを試す。こちらにもあり
 いつも同じだけど以下の手順なのだ。
  1:mbedのオンラインコンパイラでプロジェクトを作る(コンパイルしてエラーの出ないことも確認)。
  2:これをExportしてMDK-ARMのプロジェクトとする。
  3:μVisionでビルドとデバッグを行う。
 まずは簡単なところでprintfで出すUART0のTXDをやってみよっと。

 ※このボードの電源をUSBハブ経由でとると、プログラムの書き込み時にパワー不足する感じだから気を付けて。

   mbedLPC1768のピンとBlueBoardのコネクタの対応を調べる。
 mbed側は回路図なんて見なくてもこの図を見れば一目瞭然。便利なものをホント、有り難うございます。
 で、USBTXがTXD0に割り付けられてる。
 
 
 BlueBoard側は仕方ないから回路図を見る。
 J3の1PINがTXD0らしい。 
 
 
 なんだ、コネクタに書いてあるじゃん。 
 
 

 
   んじゃBlueBoardのTXD0を秋月のUSBシリアル変換ケーブルのRXD(黄色)に挿す。
 GNDもGND(黒)に挿す。
 プログラムはBlinkyに一行「printf("test\n");」を追加するだけ。
 ビルドしたらTeratermで受信すれば良いじゃん。
 やってみるべ。
 
わ〜い、動いた動いた (^o^)/

って、当たり前すぎて素晴らしい。
これだけのことをSHでやろうと思ったら結構大変だよ。
1000ページもあるハードウエアマニュアルを読んで、ピンファンクション設定、クロック設定、メモリ設定、UART設定、割込み設定、PalmICEのマクロを書き、フラッシュROMの属性を設定、ここまで来てからやっとプログラム作りで、リングバッファを作り関数化する。
で、最後にパワーセーブBITを外し忘れて3日は動かず(爆)

mbed 素晴らしいっす。
物忘れのひどい年寄りにも優しいmbedさま。
今日も有り難うございます。

Suga koubouさんのすすめに従ってこの本を注文しました。



mbedのスタートアップ処理 2014/05/09 officialな説明書P15

 mbedLPC1768のmbed-srcに含まれる startup_LPC17xx.s を覗き見してみた

上の方から見ていくか・・・
 
ベクターテーブルに続いてCRP_Keyってのが出てきた。
こりゃなんだ?とググってみたら code read protection だってさ。
ユーザーズマニュアルP630を見たら、なにやらごちゃごちゃと・・・英語で(^^;
SHで言うところのフラッシュプロテクトみたいなもんか?今はまだ知らなくても困らないから良いや。 
   
 
で、その下がリセットハンドラだ。
SystemInit関数は system_LPC17xx.c にある。
ちょっと覗いただけだけど、クロックやらフラッシュのアクセス方法なんかを設定してる。
で、その後でコールしてるのはアンダースコアが2本の__main。これってmain()関数じゃなくて、ここでヒープやスタックのLimitや変数のコピーやゼロクリアなんかをしてるみたいだ・・・詳細は未だ不明。 
   
 
更にその下側には割込みハンドラの実現部(関数)がずらずらと出てくる。
なお[WEAK]がついてるハンドラは横取り可能なのだ(ここに実例あり)。
で、スタートアップのコードは終わり。 
   



 ん?んん
 あれ?RAMの初期化とかゼロクリアの処理が無いぞ。
 普通ならっつうかSHやBCCなら外部変数のクリア処理とか初期値のコピー処理があるんだけど。
 CPUが内部コードで勝手にやってる?まさかね〜
 特に困ってるわけじゃないんで、後で調べてみるです。

2014/05/10追記
 逆アセンブラでステップ実行してたら、それらしいのを発見。
 main()に来る前に__main(アンダースコアは二つ)ってやつをコールしてて、その中でやってるっぽい。
 _scatterload_copyとか_scatterload_zeroinitなんていかにもじゃん!
 でもこのソースはどこにあるんだろ?

  多分これだと思うけど、ソースファイルはどこだ?
 
 
MAPファイルに何か出てないかと見てみたら、居た居た。
スキャッタファイルからコンパイラが生成してるのかも。 RAMサイズの変更指示 スキャッタファイルの構文
あ〜そっか、ラベル名がscatterってんだからスキャッタファイルなんだろきっと。
   



 で、この後でヒープの開始位置やZI(外部変数)の位置なんかを設定してるコードも発見。ソースファイル名は sys.cpp だ。
 で、さらに追いかけてたら、$Super$$Main ってのを発見。
 ググったらこれを発見。こっちにはリンカのマニュアルがあったけど、なんとまぁ分かりにくい書き方だ〜

  ・ $Sub$$main と言う関数があると、本物のmain関数に行く前にこの $Sub$$main 関数が実行される
  ・ そしてこの関数内で $Super$$Main と書くと本物のmain関数が実行される

 つまり「本物のmain()を呼ぶ前にやりたいことをやらせちまうための手段」ってことだよな。ここで実使用してる
 自由度が高くてとても良いけど・・・むずい・・・


 2014/05/11 まとめ。
 retarget.cppの中でmbed.mainをコールしてるけど、その関数内では何もしてない。
 でもきっと何かの時にはここで初期化処理が行われるんじゃないかと想像。

 



 スタートアップで特に変わったことをしてるわけじゃなくて、そこらの組み込み用コンパイラと大差無い。
 ちょっとひっかかったのは、スキャッタファイルから変数の初期化コードを出力してることぐらいかな。
 mbedのプロジェクトを使っても変な小細工もしていないし($Super$$main程度はお愛嬌ってことで)、これで安心して使えます。


気がついたこと2(知らないこと)

 気がついたことやら知らないことを書いていく予定。気が付いたこと1はこっち


<コンパイラの振る舞い1> 2014/05/09

  以下のコードをコンパイルすると動かない(オンラインでもオフラインでも同じ)
   MDK-ARM(μVision)でオフラインコンパイルしステップ実行すると、s[0]に代入したところでハードフォルトで停止する。ちゃんとやっててCPUエライ!
 const宣言してない文字列定数に代入できるかと思ったけどダメみたい。
 組み込みじゃこんな使い方しないからどうでも良いけど。

 Project → Options → C/C++でコンパイラオプションを変更すれば、コンパイル時にエラーを出させることができるみたい。
 
 
 しつこく続き。  2014/05/10
 マジメにCastしてWarningを消し、sにセットする文字列のアドレスを調べてみた。
 constを入れても入れなくても文字列は常にROM上のアドレスになる。
 つまり「文字列は常にROMに置きまっせ」って感じ・・・これが普通なんだっけか?C#ボケか・・・&sとすればconstが効いてるはずだけど。
 ARM社長:組み込み用の男のコンパイラはこれで良いんだよ!・・・はい〜〜(^^;
 
 
intだとこんな感じ。
次は__attribute__の使い方を調べてみよっと。
 
   




<mbedで使うAPIの調べ方> 2014/05/12

 ハンドブック日本語版) から行くってことに今頃気がついた(爆)
 例えばSerialがらみはここの一番上のこれ
 で、そこからExampleの直ぐ下にあるReferrenceをクリックするとメンバーや派生元の説明ページに行く。
 で、例えば readable() の説明はここ
 「1 if there is a character available to read, 0 otherwise」だから「受信データが有れば1、無ければ0」だ・・・いくら英語でも一行なら判読可(^^;

   



<UART> 2014/05/26

 LPC1768のUART。
 CPUマニュアルのP307に概要が書いてある。
 ・自動でボーレートを設定する機能がある(auto-baud)。設定の仕方
 ・送受信用のハードウエアFIFOは16バイト。

 ソースはmbed-src内のserial_api.c(mbed-src\targets\hal\TARGET_NXP\TARGET_LPC176X\serial_api.c)。
 ・1バイト毎に送受信割込みを発生させてる。
 ・割込み設定ではNVIC_SetVectorやNVIC_EnableIRQなどを使ってる。
以下工事中






 
 


mbed TOP

覚え書きTOP