(AVR編第4回)AVRとArduinoについて,調べたことをまとめておこう

2012/08/25

あなたは 人目のお客様です.

ええと前回にArduino DuemilanoveでKOZOSが動作した わけだが,実はIPAのセキュリティ・キャンプでAVR(ていうかArduino)を使う機会が あったので勉強がてらKOZOSを移植(いじってみるのが一番の勉強になるのと, 他のOSを移植する必要が出たときの練習にもなるので)してみた,というものだった. (結果的にはセキュリティ・キャンプではKOZOS/Arduinoを利用したので, 移植したのは直接的にとても役に立った)

で,移植の過程でいろいろAVRやArduinoについて調べたりしたのだが, せっかくなのでそのときの記録をここにまとめておこう.とはいっても きちんとまとめるというのではなく,そのときのメモを転載しておくだけだが. (公開用に部分的に修正はしてあるのでまったくそのままではないが,ほとんど そのまま)

AVRとArduinoについてなのだが,ネット上で情報はいろいろあるのだが 果してAVRの情報なのか,それともArduino特有の情報なのか, イマイチ区別がはっきりしなかったり,情報もいろいろ分散していてけっきょく よくわからん,ということが多い.

なのでここでは「AVR一般の情報」と「Arduino特有の情報」をはっきり させた上でまとめておきたいと思う.

ちなみに以下の調査だが,AVRのデータシートとかをろくに読まず,ArduinoのIDEに 付属しているライブラリなどのソースコードをベースにして調査しているので注意. 書籍等も参照しているが,基本はソースコードのありモノベースでの調査になって いる.

※ こういうときの私の調査のクセとして,データシート読むよりもソースコード等 あればそれを先に読んで,最後にデータシートで確認しておく,という手順が 多いです. 理由はデータシート読むよりもソースコード読んじゃったほうが早いしわかり やすいから. まあでも別に,あまりおすすめはしないです.やっぱりちゃんとデータシート 読むべきでしょう.

全般的に

AVRはかなりマイコン寄りのアーキテクチャだが,でもレジスタは32本もあるので コンテキストスイッチのレジスタ退避がたいへん.あと割り込み復帰命令で割り込み 許可フラグが必ず立つ仕様になっていてちょっと扱いづらい.あとソフトウェア 割り込みが無い!

開発ツール群がFreeBSDのportsになっているので,FreeBSD上で,ツール類の ソースコードベースで調べることができた.これはとても嬉しかった. というわけで基本として,FreeBSDのportsでArduinoのIDEとmake環境(arduino-mk)を インストールして,そのソースコードをベースにして調査する,ということをやって いる.

Arduino Duemilanove と Arduino Uno に搭載されているのは「ATmega328P」になる. ATmega328Pの世代は「AVR5」になる.

KOZOS on Arduino について

AVRにはソフトウェア割り込みが無い.

このためKOZOSではシステムコールは 「割り込み禁止にして,システムコールハンドラに直接ジャンプする」 ということでシステムコールを実現している.(kozos.cのkz_syscall()参照)
これはソフトウェア割り込みを疑似的に実現しているような感じで,その先でタスク 切替えが行なわれることになる.

通常の(OS載せるのが前提の,高機能の)プロセッサだとシステムコール発行時に現在の 割り込み有効/無効フラグとかCPUの特権レベルとかを保存しておかなければならない のでソフトウェア割り込みが必要だが,AVRは特権モードが無く,さらにreti命令が 「割り込み有効化して戻り先に戻る」という決めうちの動作なので, 「KOZOSカーネル内部は割り込み禁止,アプリは割り込み有効」という前提で動作する として,「システムコール発行時は(飛び先はカーネルなので)必ず割り込み無効化」 「retiで戻る際は(戻り先はアプリなので)必ず割り込み有効化」という仕様にして ある.

つまり例えばアプリ内で割り込み禁止にすると,それはもしかしたら割り込み発生後に 勝手に解除されてしまうかもしれないわけだ(つまりいつのまにか解除されてる可能性 がある).このへんが,AVRはプリエンプティブOSを載せることをあまり想定して いないんじゃないかなーと思う理由でもある.

あ,でも考えてみたら割り込み禁止にしてるんだから割り込みは発生しないのか.^^;
NMIみたいなものがあったらか,もしくは割り込み禁止区間でシステムコール呼びだし したら,というように話を限定できるわけだ.
割り込み禁止してるのにシステムコールっていうのもちょっと考えにくいし,ということはあまり気にしなくていいことかもしれない.

多重割り込みも注意すれば実現できそうな気がする.

自分用メモの転載

以下は自分用メモを公開用に部分的に修正し,時系列に並べたもの.

AVR全般の話なのか,それともArduino特有の話なのかに注意.

2012/7/27(金) 21:24 ---- AVR全般の話

で、とりあえずAVRのアセンブラとGDBのAVRシミュレータのソース読んでみてわかった こと。

(まとめ)

とりあえずAVRはいかにもマイコン、ってアーキなことが確認できた。 マイコンでセキュリティ、ってあまり考えている人いないしちょう面白そう。 テキスト空間とデータ空間が分離されているので、命令書き換えやシェルコード実行みたいなのは原理的にできない。ただ戻り先アドレス変更とかは可能なので、そのへんがセキュリティ上のテーマになるように思える。

2012/7/29(日) 2:44 ---- AVR全般の話

AVRにKOZOSを移植.
GDBシミュレータがかなり役に立った.
AVRおもしろい.いかにもマイコンって感じで勉強になる.
レジスタが32本もあるけど,おそらくちょっとしたミニマムなシステムなら関数 呼び出し無しでレジスタをSRAMがわりに使うような実装を考えてあるのかなあと 勝手に想像.
各種レジスタやI/OがSRAM上にもマッピングされていてとてもマイコンっぽい.
テキスト領域とデータ領域のアドレス空間が完全分離されていて混乱することあり.
関数のアドレスが,なんか2で割った値で扱われているんだけどこれってそういう ものなのかしら.
デバイスドライバと割り込みまわりを実機に合わせれば,わりと簡単に実機動作 させられるかも.

2012/7/30(月) 21:12 ---- Arduino特有の話

とりあえず開発環境なのだけど,FreeBSDのportsのdevelカテゴリにarduinoとか arduino-mkとかいうものがあるようだ.なんか使えそう.
pkg-descr見るとなんかsketchのビルドができるみたいで期待.

portsのarduinoはフツーにArduinoのIDEだった.
で,arduino-mk使ったらgmakeでsketchをビルドできた.

FreeBSDのCUI環境で開発できるみたいで,俄然やる気が出る.

2012/7/31(火) 0:07 ---- Arduino特有の話

とりあえずArduino環境のソースコード調査.

まずはブートローダー.FreeBSDではソースは以下にあるようだ.

/usr/local/arduino/hardware/arduino/bootloaders/atmega
Makefileを見ると
LDSECTION  = --section-start=.text=0x3800
みたいにしてフラッシュROM終端のほうに配置されているようだ.

ソースファイルを見ると,なんかmain()関数でレジスタ設定して何やらシリアル通信 してapp_start()というのでアプリ起動するみたい.ウォッチドッグタイマを利用した 動作になっている.
app_start()はゼロ番地になっているので,ゼロ番地の命令(アプリ側のリセットベクタ に相当)が実行されてアプリ起動,ということのようだ.リセット要因調べてアプリ 起動したりとかしているみたい.ほかにもさまざまな箇所でアプリ起動している.

sketchで生成された実行形式をreadelf&objdumpで解析.テキストはゼロ番地から配置 されていて,先頭には割り込みベクタらしきものが見える.

AVR はMCUCRとかいうレジスタで割り込みベクタを移動できるみたい.起動時はジャンパ ピンの設定でブートローダーのほうの割り込みベクタが使われ,そのリセットベクタ からブートローダーが起動.アプリはゼロ番地から配置されていて,ブートローダー からアプリを起動し,MCUCRを設定変更してアプリ側の割り込みベクタが利用される ようにする,という構造みたい.

Arduinoのブートローダーは評価高いけど,こうして見てみるとたしかにうまいこと 作られているなあ,というかんじ.

MCUCRの設定変更している箇所がどっかにあるはず.ブートローダー側には見つからず. アプリ側で設定しているのかな?

sketch で生成された実行形式の逆アセンブルのスタートアップを読むがそれっぽい ところは無い.(単にスタックポインタ初期化とデータとBSSの初期化して ctors呼び出して,main()に飛んでるだけ.ctorsに登録されているのはシリアルの 初期化っぽいので関係なさそう)

Arduinoのライブラリのほうを探してみると,以下になんかそれっぽいのがある.

/usr/local/arduino/hardware/arduino/cores/arduino/WInterrupts.c

アプリ側のArduinoのライブラリのattachInterrupt()というのでベクタ設定されると そこで変更される,ということなのかな? つまり起動時はまだ割り込みベクタは ブートローダーのものが使われていて,割り込み設定するとアプリのものが使われる ようになる,ということみたい.

sketch によるビルド時のリンカスクリプトを確認.make時のログではとくに指定 されていないので,おそらくリンカにビルトインのものが使われている.実行されて いるld(/usr/local/avr/bin/ld)に --verbose オプションをつけて実行してビルトイン のスクリプトの内容確認.テキストはゼロ番地から配置されていてあっているみたい.

ということで,gccで開発する際には以下が必要.

2012/8/1(水) 21:49 ---- AVR全般の話

AVRはいままではArduinoをちょっといじったことがあっただけ(sketch書いてLED 光らせてみてそれで終わっていた)だったのだが,キャンプではAVRのフラッシュ 書き換えが必要になるので,ちょっとフラッシュ書き込み方法について調べてみた.

まずフラッシュ書き込みにはいくつか方法があるようだ.

  1. ISPという方法で書き込む安価なライタがある.
  2. 高電圧パラレル方式というので書き込む高価なライタがある. (AVR Dragonはこっち)
  3. ちょっと特別な方法として,Arduinoのブートローダーで書き込むという方法も ある.
まずいきなり3の方法なのだけど,これはあらかじめフラッシュに書き込まれている ブートローダーによってシリアル通信ベースでファイルを転送してあとはブート ローダーの書き込みプログラムがフラッシュ書き換えを行なうというものらしい. なのでまずブートローダーがそもそも入ってなければダメというのと,あとブート ローダー潰してしまったら不可能になる.

ただこれだと単なるシリアル通信でファイル送るだけで書き込めるので非常に簡単で, Arduinoはこれのおかげで専用のライタ機器無しで簡単にフラッシュ書き換えできる ようになっていてとっても評価が高い.

でも3の方法だと,ブートローダー書き潰してしまったらおしまいになる.ブート ローダー自体を書き込むには,1か2の方法が必要になる.つまりなんらかのライタ 機器が必要になる.

1の方法ではフラッシュROMに自由に書き込めるので,ブートローダーを書き込むこと はできる.しかしヒューズビットを書き換えるとやはりダメになるらしい.ここで ヒューズビットについて調査.

AVR はヒューズビットという不揮発領域を持っていて,そこに 「ISP方式で書き換え可能にするか?」というビット情報がある.また他にも外部 クロック使うかとかの情報がある.つまり,通常ならディップスイッチやジャンパピン で設定するような設定を内部の不揮発領域に持っていて,そこの設定情報を見て動作 開始する,というようになっているらしい.

ということでヒューズビットを誤って書き換えてしまうと箱の鍵は箱の中状態になって ISP方式での書き換えは不可能になってしまう.で,2の高電圧パラレル方式というのが 必要になる.これだとヒューズビットの設定も書き換えられるので,これが最終手段に なる.

1の方法はオンボードで書き込みができる.つまりボード上にAVRのチップが載って いるままの状態でライタを接続してツール操作で書き込みができる.たとえばArduino にライタを接続して書き込んだりすることができる.

対して2の方法はオンボードではできず,AVRのチップをボードから抜いてライタ上の コネクタに挿し,ツールを操作して書き込みを行なうという手順になる.つまり めんどい.

ライタとしてArduinoを使う(つまりArduinoでAVRの書き換えを行なう)とか,Arduino 上のシリアルUSB変換チップの機能を利用して書き込みを行なうとかそもそもライタを 自作するとかの方法もあるのだが,基本的には上記1,2,3の方法のいずれかに なる. (ライタとしてArduino を使うなどは,上記1の方法のライタとしてArduinoを使って いる,ということになる.シリアルUSB変換チップの機能を利用する,というのも同じ)

2012/8/1(水) 22:25 ---- AVR全般の話

次に書き込みツールについて.

AVRの書き込みツールは検索するとavrdudeというのがやたら出てくるのだけど, これが何なのかよくわからない.

FreeBSD のarduino-mkのMakefileを追ってみてみると,Arduinoのsketch書き込みにも 使われているみたい.なのでArduino専用の書き込みツール?と思っていたのだが どうもそうではなく,ISP方式の書き込みなどにも使われているみたい.

よくわからんものを使うのはイヤなので,調べてみた.

まずavrdudeはFreeBSDのportsになっていて,arduinoだかarduino-mkだかをパッケージ インストールしたときに一緒にインストールされたようだ.で,man avrdudeしてみる.

なんか読むと各種ライタに対応した書き込みツール,みたいに書かれていて,いろんな ライタの機種名が出てくる.-cというオプションでライタの機種を指定して, -Pオプションでシリアルデバイスのデバイスファイル(/dev/cuaU0とかか?)を指定 して実行すれば,あとは書き込みが行なわれるようだ.

各種ライタを購入するとWindows上で動作する書き込みツールとかも付属してくるの だと思うのだけど,つまりavrdudeというのはそういう書き込みツールに相当する フリーソフトウェア,ということなのかな.

で,Arduinoについてなのだが,man avrdudeには以下のように書かれている.

     The Arduino (which is very similar to the STK500 1.x) is supported via
     its own programmer type specification ``arduino''.
つまりavrdudeはArduinoのブートローダーにも対応していて,-cオプションで 「arduino」を指定すればいいらしい.

arduino-mkのMakefileを追ってみると,Makefileで

AVRDUDE_ARD_PROGRAMMER= arduino
のように定義されていて,make update の際にはこれが-cオプションに渡されてavrdudeが実行されるようになっている.詳しくは以下参照.
/usr/local/arduino/lib/Arduino.mk
問題はデバイスで,arduino-mkのMakefileを見ると以下のように定義されている.
ARDUINO_PORT= /dev/arduino
そしてこれが-Pオプションに渡されているのだが,デバイスファイルってこれで いいのか?という疑問がわく.(続く)

2012/8/1(水) 22:49 ---- AVR全般の話

前回の続き.Arduinoに書き込むためのシリアルデバイス指定について.

もしかして/devに特別なデバイスファイルが必要なのか?と思い「FreeBSD arduino」 とかで検索すると,「uarduno」というのが出てくる.portsになっているようだ.

探したらcomms/uardunoにあった.pkg-descrを見てみると以下のように書いてある.

The Arduino Uno (http://arduino.cc/) is an open source hardware micro-
controller designed primarily for prototyping and experimentation.
Although the devel/arduino port already exists for programming the device,
it will not work properly with the newest Arduino hardware.  Previous
versions of the Arduino used an FTDI USB to Serial interface.  The newest
Arduino (beginning with the Uno) uses an on-board ATMel 8U2 controller
to emulate a USB to Serial interface with its own custom Vendor ID and
Hardware ID.  As a result, NONE of the existing FreeBSD USB to serial
drivers can work with it.  This kernel driver supplies the necessary
kernel support for the Arduino Uno on FreeBSD.
Arduinoのwikipediaの情報と照らしあわせると,以下のようなことがわかる. pkg-messageを見ると「kldload uarduno.ko」とか書いてあるので,uardunoを インストールすると /boot/modules あたりに uarduno.ko がインストールされて, kldloadできるようになるのだろう.

英文を訳すと以下のようなかんじ.

----
devel/arduino のportsがあってデバイスのプログラミング(ここでいう「programming」はフラッシュ書き込みのこと)に使えるのだけど,最新の Arduino(つまりArduino Uno)ではうまくいかない.以前のArduinoはUSB-シリアル変換にFTDIを使っていたのだけど,最新のArduinoではAVRの8U2というチップを別に積んでいて,そいつがUSB-シリアル変換のエミュレートをしている.そしてその8U2のチップは自身のカスタムの(つまり,専用の)ベンダIDとハードウェアIDを持っている.結果として現存のFreeBSDのUSBシリアルドライバで動作するものが無い.このカーネルドライバを使えば,Arduino UnoをFreeBSDから利用できるようになる.
----

で,ぼくが持っているArduinoはUnoではなくてひと世代前の「Arduino Duemilanove」 なので,FTDIのチップを積んでいる.なので上記uardunoを使う必要はなく, おそらく/dev/cuaU0あたりで認識できると思う.

ということでarduino-mkのMakefileでは

ARDUINO_PORT= /dev/cuaU0
のようにしておいてあとは make upload すれば,うまく書き込みできると思われる.

2012/8/1(水) 23:20 ---- AVR全般の話

どうやらFreeBSDでArduinoベースでAVRの開発できそうなメドが立ってきた.
なるべくならArduinoのブートローダーを利用してフラッシュ書き換えするのがラク なので,そーいうポリシーで考えてみた. あとシミュレータについて. ただしブートローダーを上書きしないように注意する必要がある.これを上書きすると ライタが必要になってしまう.しかしライタは持っていないので失敗しないように いろいろ知っておきたい.まあでもこれは前回,ブートローダーの配置アドレスを 調べたので,そこにかぶさらないようにリンカスクリプトの MEMORY指定で制限して やればよい.

さらに気になるのはヒューズビットで,これを間違って上書きするとISPのライタじゃ なくて高電圧パラレル方式のライタが必要になってしまう.チップを抜く必要も出て きてめんどいし,なので失敗しないようにある程度ヒューズビットについて知って おきたい.

ということでヒューズビットについて調べてみる.リンカスクリプトでなにか指定して ないかなーと思って探してみると,/usr/local/avr/lib/ldscripts にリンカスクリプト のサンプルがいっぱいある.

ていうかそもそも

/usr/local/avr/bin/ld --verbose
で標準の(ldにビルトインの)リンカスクリプトが出力されるので見てみると, おもいっきし以下のようになっている.
MEMORY
{
  text   (rx)   : ORIGIN = 0, LENGTH = 8K
  data   (rw!x) : ORIGIN = 0x800060, LENGTH = 0xffa0
  eeprom (rw!x) : ORIGIN = 0x810000, LENGTH = 64K
  fuse      (rw!x) : ORIGIN = 0x820000, LENGTH = 1K
  lock      (rw!x) : ORIGIN = 0x830000, LENGTH = 1K
  signature (rw!x) : ORIGIN = 0x840000, LENGTH = 1K
}

...

  }  > data
  .eeprom  :
  {
    *(.eeprom*)
     __eeprom_end = . ;
  }  > eeprom
  .fuse  :
  {
    KEEP(*(.fuse))
    KEEP(*(.lfuse))
    KEEP(*(.hfuse))
    KEEP(*(.efuse))
  }  > fuse
  .lock  :
  {
    KEEP(*(.lock*))
  }  > lock
  .signature  :
  {
    KEEP(*(.signature*))
  }  > signature
つーことは0x820000にデータを配置するとそれがヒューズビットの値として書き込ま れるようだ.ロックビットは0x830000になっていて,これもやりかたは同じ. プログラム中では.fuseというセクション作ってそこにデータ配置すれば,それが 0x820000に配置されることになる.

つまり,自分のプログラムを書き込むときは,以下に留意すればよい.

まあリンカスクリプトさえきちんと書いておけば,プログラムのサイズが増えて ブートローダー領域やヒューズビット領域にかぶさりそうになってもエラーを出して くるので,ビルド時点で知ることができるので安全なはず. (出力したELF形式をチェックしてから書き込むことも最初は必要だろう)

2012/8/1(水) 23:31 ---- AVR全般の話

書き込みのシーケンスについて調べてみる.

AVR の書き込みとかでネットで調べると,ロックビットを開けて閉じるとかそんな シーケンスがいろいろ出てくる.Arduinoのフラッシュ書き込みは make upload で できるように思うのだけど,こういう操作って必要なのかしら? 必要ならどこで 行なわれているのかしら?

/usr/local/arduino/lib/Arduino.mk を詳しく見てみると,以下のようになっていた.

Arduinoのブートローダーを使ったフラッシュ書き換えの場合はこっちのはず.

他にmake isploadというのがあって,こちらだとavrdudeが何度か実行され, 実行パラメータにlockとかfuseとか出てくるので,まさにロックビットの操作とかが 行なわれているようだ.

ということはISPライタでのフラッシュ書き込み時にはロックビットの操作などが必要 で,arduino.mkを使えば make ispload でこのへんもきちんと順番に処理してくれる ようだ.

しかしArduinoのブートローダーを使うぶんには make upload を実行すればいいので, そういった操作は不要でavrdudeを1回実行してフラッシュ書き換えすればいいだけ, ということのようだ.まあ単にバイナリをシリアル経由で送り込めば,あとはブート ローダーのほうでよろしくやってくれるのだろう.ブートローダーのソースももう 一度詳しく追いかけといたほうがいいかもしれない.

2012/8/6(月) 19:59 ---- Arduino特有の話

本日より作業再開.
とりあえずFreeBSDのarduino-mk環境で手持ちのArduino Duemilanove向けにsketchを ビルドして書き込んで動作させてみる. で,あっさり書き込みできて動作した.あっさり動作して喜ぶんでなく拍子抜けして しまうところが,ちょっとスレてしまっているということなのだろうか.

ひとまずFreeBSD上で完全にCUIベースで開発できる実績がとれたので,この点はとても 嬉しかったりする.

2012/8/6(月) 20:45 ---- AVR全般の話

本日より勉強再開.

とりあえずAVRはテキストとデータでアドレス空間が分かれている.テキストは フラッシュROM上でデータは内蔵RAM上にあるようでいかにも組込みプロセッサという 感じなのだが,気になるのはアクセス方法だ.

sketchのバイナリをreadelf&逆アセンブルして見てみると,rodataなどに配置される 文字列リテラルとかはどうなるのだろうか?

ためしに文字列リテラル使うようなsketch作成してビルドしてreadelfと逆アセンブル とELFのバイナリダンプを見比べてみたところ,文字列リテラル自体はフラッシュ上に 書き込まれているが扱うときはデータ領域にあるものとして扱われていて,で, スタートアップ中の __do_copy_data というデータ領域コピー処理で,フラッシュから RAMにコピーされているようだ.

なおRAMに配置されるデータは,readelf上は 0x00800000以降のアドレスになっている のだけど,実際にアクセスする際には0x00000000を先頭としたアドレスになるようだ. テキストとアドレスがかぶらないようにリンク上は後ろのほうのアドレスにしておく, ということなのだろう.(GDBのAVRシミュレータでも,そういうふうに丸め込んである)

__do_copy_dataではZとXというインデックスレジスタでコピーもととコピー先を指して いるみたい.r26, r27 がXレジスタ,r30, r31がZレジスタになっているようだ.

で,フラッシュからの値のロードは「lpm」という命令が使われていて,RAMへの値の ストアは「st」という命令が使われている.フラッシュに値を書き込むことは無いの で,おそらくフラッシュから値を読むための特別な命令が用意されているのだろう.

lpm命令の実装をGDBのAVRシミュレータのソースで確認.gdb-7.3.1/sim/avr/interp.c を見てみると,やはりフラッシュから値を読んでいる.RAMからの読み込みはst命令に 対してldという命令があるみたい.

ということは通常は文字列リテラルもRAMから読み込まれるので,lpm命令はおそらく コンパイラは使わない.なのでスタートアップのデータコピー処理はアセンブラで lpm命令を使って書いて,いったん全部RAMにコピーしとかなきゃならない.うーん せっかくフラッシュがあるのに無駄で残念.ATmega328PはRAMが2KBしか無いのだけれど 足りるかなあ.

2012/8/6(月) 21:07 ---- Arduino特有の話

シリアル通信の方法についてちょっと知りたいのでArduinoブートローダーのソース コードを調査.
/usr/local/arduino/hardware/arduino/bootloaders
あとライブラリのシリアル通信部分を調査.
/usr/local/arduino/hardware/arduino/cores/arduino/HardwareSerial.cpp
パッと見どちらもUDRっていうレジスタでシリアル1文字受信しているみたい.このへん参考にすればよさそうだ.

2012/8/6(月) 23:24 ---- AVR全般の話

Arduinoのソースを参考にして,シリアル送受信と割り込みについて調査.

Arduino Duemilanove向けのブートローダーはおそらく以下.

/usr/local/arduino/hardware/arduino/bootloaders/atmega/ATmegaBOOT_168.c
ブートローダーのMakefileに atmega328 というターゲットがあり,ここで
MCU_TARGET = atmega328p
のように定義されているので,このターゲットが Duemilanove向けのブートローダーが作成できるように思える. (atmega328_pro8 というターゲットもあるのだがこっちはクロックが8MHzに設定 されているみたいなので違うみたい.(Duemilanoveのクロックは16MHz))

■ シリアル初期化

ブートローダーのmain()の先頭部分の defined(__AVR_ATmega328P__) の部分を参照

DOUBLE_SPEED は atmega328_pro8 で定義されていて,クロックが半分の8MHzのときに シリアルのボーレートを同じにするために×2するための定義のように思うので, おそらく未定義として扱ってよい.

#ifdef DOUBLE_SPEED
        UCSR0A = (1<<U2X0); //Double speed mode USART0
        UBRR0L = (uint8_t)(F_CPU/(BAUD_RATE*8L)-1);
        UBRR0H = (F_CPU/(BAUD_RATE*8L)-1) >> 8;
#else
        UBRR0L = (uint8_t)(F_CPU/(BAUD_RATE*16L)-1);
        UBRR0H = (F_CPU/(BAUD_RATE*16L)-1) >> 8;
#endif

        UCSR0B = (1<<RXEN0) | (1<<TXEN0);
        UCSR0C = (1<<UCSZ00) | (1<<UCSZ01);

        /* Enable internal pull-up resistor on pin D0 (RX), in order
        to supress line noise that prevents the bootloader from
        timing out (DAM: 20070509) */
        DDRD &= ~_BV(PIND0);
        PORTD |= _BV(PIND0);
_BV()は以下で定義
/usr/local/avr/include/avr/sfr_defs.h
#define _BV(bit) (1 << (bit))

■ シリアル1文字リード

ブートローダーの getch() の defined(__AVR_ATmega328P__) の部分を参照

        while (!(UCSR0A & _BV(RXC0))){
                ;
        }
        return UDR0;

■ シリアル1文字ライト

ブートローダーの putch() の defined(__AVR_ATmega328P__) の部分を参照

        while (!(UCSR0A & _BV(UDRE0)));
        UDR0 = ch;

■ シリアル割り込み

「AVRマイコン・プログラミング入門」参照

■ 割込みベクタ移動

割込みベクタはブートローダー起動時はブートローダー領域にある (つまりゼロ番地でなく,後ろのほうのアドレスにある)ので,ゼロ番地に移動する 必要がある.

割り込みベクタはMCUCRのIVSELビットがセットされていると,ブートローダー領域の ベクタになるらしい.なのでMCUCRのIVSELをクリアすれば割り込みベクタがゼロ番地に 移動するようだ.

手順としては,以下のようにする必要があるようだ. (IVSELの変更前に,IVCEを1にする必要があるみたい)

(参照)

/usr/local/arduino/hardware/arduino/firmwares/arduino-usbdfu/Arduino-usbdfu.c
(割込みベクタをブートローダー領域に移動)

        MCUCR = (1 << IVCE);
        MCUCR = (1 << IVSEL);

(割込みベクタをゼロ番地に移動)

        MCUCR = (1 << IVCE);
        MCUCR = 0;

■ 各種パラメータ,ポート定義

各種パラメータはブートローダーのMakefileの atmega328 というターゲットの ところで定義されている.
以下,抜粋.

F_CPU=$(AVR_FREQ)
BAUD_RATE=57600
AVR_FREQ = 16000000L

各種ポートの定義は以下にある.

/usr/local/avr/include/avr/iom328p.h

以下,抜粋.

#define UCSR0A _SFR_MEM8(0xC0)
#define UCSR0B _SFR_MEM8(0xC1)
#define UCSR0C _SFR_MEM8(0xC2)
#define UBRR0L _SFR_MEM8(0xC4)
#define UBRR0H _SFR_MEM8(0xC5)

#define TXEN0 3
#define RXEN0 4
#define TXCIE0 6
#define RXCIE0 7

#define UCSZ00 1
#define UCSZ01 2

#define DDRD _SFR_IO8(0x0A)
#define PORTD _SFR_IO8(0x0B)
#define PIND0 0

#define UDRE0 5
#define TXC0 6
#define RXC0 7
#define UDR0 _SFR_MEM8(0xC6)
以下は /usr/local/avr/include/avr/sfr_defs.h より抜粋

アセンブラ向け定義

#define _SFR_MEM8(mem_addr) (mem_addr)
#define _SFR_IO8(io_addr) ((io_addr) + __SFR_OFFSET)

C言語向け定義

#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
#define _SFR_MEM8(mem_addr) _MMIO_BYTE(mem_addr)
#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)

#    define __SFR_OFFSET 0x20

#define MCUCR _SFR_IO8(0x35)
#define IVCE 0
#define IVSEL 1

2012/8/9(木) 20:01 ---- Arduino特有の話

KOZOS/Arduinoが動作した.シリアル送信の割り込み番号が間違っていたのをなおしたら さっくり動いた.

あとなんかAVRはR1がゼロだっつう前提でコンパイラはコードを吐くみたい.memcpy()や memset()の逆アセンブル見るとそんな感じになっていて気になっていたのだが, ネットで調べたらやっぱしそういうもののようだ.なので割り込み処理の先頭でR1を ゼロクリアする処理を追加した(たぶん不要なのだけどいちおう).

2012/8/13(月) 0:32 ---- ATROSについての話

いちおうATROSについてお勉強.

とりあえずこのへんを見てみた.

http://akiba.geocities.jp/atmel_avr_atros/20070708.html
PIC向けのPICROSというOSのAVR移植版らしい.

ソースコード見てみたが構造はちょう簡単.main()の内部で各タスクのTCBを優先度順に 見て,実行可能状態ならば登録されている関数を実行するだけ.以前にARM向けにKOZOS をタスクレス構造で移植したときのやりかたに似ている.

タイマかけるとその間実行されなくなるが,ビジーループで減算されててゼロになると また実行可能状態になるという単純な構造.

タスクのコンテキスト保存領域というものが無く,ノンプリエンプティブな仕様に なっているようだ.
タスクのmain()関数は名前こそled_main()だったりするが毎回抜けて戻ってしまう (つまりスケジューリングされるたびにled_main()が呼ばれる).このへんもタスクレス KOZOSと同じ.

構造は単純だしすぐに読みきれる分量なので,質問あってもなんとか対応できそうだ.

気になるのは割り込み許可/禁止のsei()/cli()という関数や割り込み有効化/無効化 のsbi()/cbi()といった関数なのだけど,ソースコード内部では定義されていないので, どうもなんらかの統合開発環境みたいなのでのビルドが前提なのかなあ,という気が する.スタートアップも無く,main()関数でいきなり始まっている.ていうか アセンブラファイルが一切無い.

なのでおそらくWinAVRとかAVR Studio とかそういう環境が必要なのかもしれない.

あ,でもFreeBSDの/usr/local/include/avr以下を見てみたら,sei()とかあるようだ. arduinoのIDEかarduino-mkをインストールしたときに,おそらくライブラリも入ったのだと思われる.ちょい調整すればFreeBSDのarduino環境でもビルドできるかも.


メールは kozos(アットマーク)kozos.jp まで