AVR32を使う

Atmel製のマイコン「AVR32」を使ってみた感想です。



index


32bitAVRとは?

Atmelからは,32bitマイコンが約2種類出されています.それぞれの特徴を挙げます.

AVR32

32bit版AVRマイコンです.AVRという名が付いていますが,中身は全くの別物です.国内での使用例は非常に少ないです.が,秋月で3種類のデバイスが発売されています.

SAM

ARMベースのマイコンです.AVR32との違いは,ARMベースか否かという点です.Arduino DueとArduino Zeroのメインチップに使われています.チップ単体の購入は国内ではできないのでDigi-keyに頼ることになります.

今回は,前者「AVR32」を使ってみた感想を述べたいと思います.因みにXmegaシリーズはまた別物です.

AVR32を使う利点・欠点

利点
  • 高性能 …… 32bitマイコンですから.ROMやRAMの容量も大きいので,メモリが足りなくなるといった心配はカラー液晶とか使わない限りありません.8bitAVRに比べ高いクロック周波数でも動作するものが多いです.例えば秋月で販売されてるAT32UC3L064はFlashメモリは64Kbyte・SRAMは16Kbyte・クロックは最大50MHzで動作します.因みに,AVR32とSAMシリーズのどちらが高性能かは,一概には比較できないので分かりません.
  • 高機能 …… 秋月で販売されてるAT32UC3L064を例に上げると,ピン数が48ピン,うちGPIOが36ピン(すべてのピンで外部割り込み機能あり),SPIが5つ・TWI(I2C)が2つ・USARTが4つ,ADコンバーターが8つ,アナログ比較器が8つ,タイマーが6つ,PWM出力が35ピンあります.
  • 安い …… 秋月で販売されてるAT32UC3L064は600円位で手に入ります.Mega644pをDigi-keyで買った場合と殆ど変わりません.
  • Atmel studioが使える …… 多くのマイコンは開発環境が有料だったりしますが,ご存知の通りAtmel studioは完全無料で全機能使える有能なIDEです.操作手順はAVRの時と変わりません.WinAVRにもコンパイラは入っているので適切なMakefileを作ればWinAVRでも使用できます.
  • Atmel Software Framework(略:ASF)がある …… Atmelのマイコン(tinyシリーズ除く)を簡単に使うためのライブラリです.これを使えば煩わしいレジスタ設定することなく簡単にマイコンを弄れます.欠点も参照.
欠点
  • 国内での使用例が非常に少ない …… データシートを含め資料は基本英語です.まぁ英語が読めなくてもAVRのデータシートを日本語でも読んだことがある人ならフィーリングでイケます.Googleで検索すると中国語やロシア語もヒットします.
  • Dipパッケージが無い …… 変換基板を使いましょう.32bitマイコンは基本的にDipパッケージはありません.秋月には唯一のDipパッケージのARMマイコンが売ってますが,開発環境が無料だと制限があったりします.
  • 書き込みが用意じゃない …… 本来であれば5000円位するライター(秋月で売ってるw)を用いて書き込みをしますが,AT32UC3L064の場合後述の方法で書き込みができます.
  • 高機能なので色々面倒くさい …… 起動したらまずクロック源を変えたりしなければいけないし,レジスタの数も8bitAVRに比べて比べ物にならない程多いので,データシート読むだけで一苦労です.
  • AVR(大嘘) …… 8bitAVRとはGPIOの操作方法から何から全て異なります.
  • delay関数すら用意されていない …… ASFを使わなければ,delay関数すら用意されていません.また,割り込みを使うとなると自分でAssemblyファイルを書く必要があります.
  • Atmel Software Framework(略:ASF)がクソ …… 見ればわかりますが,どうやら評価ボードでの使用を前提としているらしく,かえって使いづらかったりします.また,C言語での使用に限定されているので,C++を使うことができません.また,コンパイラのオプションを修正しなければ動作しないという致命的欠陥を抱えています.
  • 3.3V動作 …… 3.3V動作だぞ気をつけろ!

入手方法

秋月で売ってます.変換基板も同時に買いましょう.秋月にも売ってますが,ブレッドボードを使って実験してみたい人は,Aitendoに売ってる変換基板がお勧めです.UC3L064の場合,TQFP 48ピン(ピッチ0.5)です.

AVR32の配線

しつこいようですが3.3V動作だぞ気をつけろ! レギュレーターでも挟めば挿し間違え発煙事故も起きないでしょう.

5Vの物と通信する際は,レベル変換回路・IC・モジュールが必要になります.モジュールは秋月で何個か売っています.aitendoのカラー液晶の多くは3.3Vで動くので(バックライトは勿論違うけど)問題なく動きます.お勧めです.Xbeeなどは元から3.3V動作なので電圧変換不要で動きます.

電源周りの接続は下記の回路図を参考にしてください.

VDDIOやGNDはどれか1ピンだけ配線すれば恐らく大丈夫です.データシートも一応確認して下さい.パスコン・デカップリングコンデンサも忘れずに.

開発環境

コンパイラ

AVR32で使うコンパイラは,avr32-gccC言語)・avr32-g++(C++)です.WinAVRにも入っていますが,Makefileの作成を白紙状態からやらねばならないので辛いです.大人しくAtmel Studioを使ってください.

AVR32に限らず,AVRでもC++用のコンパイラが用意されているので,C++を使うことができます.C++の標準ライブラリは使えなかったりしますが,ClassやNamespaceを使うことができるので,大規模なプログラミングにはもってこいです.

ASFを使わない場合

プロジェクトの作成
  1. Atmel Studioを起動する.
  2. スタートページにあるNew Project...か,メニューバーのファイル→新規作成→プロジェクトをクリックする.
  3. C言語でプログラミングする場合は「C Executable Project」を,C++の場合は「C Executable Project」を選択する. あとは,名前・場所・ソリューション名を設定してOKを押す.名前は半角英数にして下さい.
  4. 「Device Family:」を“AVR32,UC3 32bit”にして,デバイス(ここではAT32UC3L064)を選択し,OKを押す.

これでプロジェクトの作成は完了です.プログラムを打ち込んで下さい.コンパイルはAVRの時と同じようにビルドすれば成されます.ASFの場合はコンパイラオプションの変更は多分不要です.

ASFを使う場合

Atmel Software Framework(通称:ASF)を使う場合,C++は使えません.また,コンパイラオプションの変更などを正しく行わないと,動作しません.

プロジェクトの作成 ~ASFを使う場合~
  1. Atmel Studioを起動する.
  2. スタートページにあるNew Project...か,メニューバーのファイル→新規作成→プロジェクトをクリックする.
  3. 「インストールされたテンプレート」の“UserBoards”を選択.次に,使用するデバイスのシリーズ名が書いてある物(ここでは”User Board - UC3 L0/L3U/L4U“)を選択. あとは,名前・場所・ソリューション名を設定してOKを押す.名前は半角英数にして下さい.
  4. バイス(ここではAT32UC3L064)を選択し,OKを押す.

次に,スタートアップファイル(.Sファイル等)をコンパイル対象から外します.

  1. ソリューションエクスプローラーの「src/asf/avr32/utils/startup」フォルダ以下の3つのファイルを選択し,右クリックで「プロパティ」を選択.
  2. 「Build Action」を“NONE”に設定する.

あるいはいっその事

  1. ソリューションエクスプローラーの「src/asf/avr32/utils/startup」フォルダ以下の3つのファイルを削除する.

んでも良いと思います.

コンパイラオプションの変更 ~ASFを使う場合~

コンパイルをする前に,最適化を有効にします.これをしないと,プログラムメモリを浪費してしまいます.最適化を有効にするためには,ソリューション構成を変更します.

Debugが出来る環境が整っていないので,いっその事「Debug」を削除してしまいたいと思います.

  1. メニューバーから,「ビルド>構成マネージャー」の順に選択する.
  2. 「アクティブソリューション構成」から“<編集...>”をクリック.
  3. “Debug”を選択して「削除」をクリック.

これで,ソリューション構成が「Release」のみになりました.

次に,コンパイルオプションを変更します.この作業をしないとプログラムが動作しません.

  1. メニューバーから,「プロジェクト>(プロジェクト名)のプロパティ」の順に選択する.
  2. 左端の「Toolcahin」をクリック.
  3. 「AVR32/GNU C Compiler」以下の「Optimization」を選択.「Other optimization flags:」に-fdata-sectionsとあるが,削除して空にする.
  4. 「AVR32/GNU C Compiler」以下の「Miscellaneous」を選択.「Other flags:」を全て削除して空にする.
  5. 「AVR32/GNU Linker」以下の「General」を選択.「Do not use standard start files(-nostartfiles)」のチェックを外す.
  6. 「AVR32/GNU Linker」以下の「Miscellaneous」を選択.「Linker flags:」に-Wl,--relax -Wl,-e,_trampolineとあるが,全て削除して空にする.
  7. 「AVR32/GNU Assembler」以下の「General」を選択.「Assembler flags:」に-mrelaxとあるが,削除して空にする.
  8. 「AVR32/GNU Preprocessing Assembler」以下の「General」を選択.
    「Assembler flags:」に-DBOARD=USER_BOARD -mrelaxとあるが,-mrelaxのみを削除する.

あとはいつも通りビルドをすれば終わりです.スタートアップファイルをコンパイル対象から外していないとエラーが出るので気をつけてください.

プログラム

基本のプログラム

#include<avr32/io.h>
int main(void){
    //ここに処理を記述します.
    for(;;){
    }
}

AVRとの違いは,インクルードファイル名位でしょうか.

GPIOを操作する

例を出したほうが早いでしょう.例えばPA09(GPIO number:9)を出力にしてHを出力するプログラムは

AVR32_GPIO.port[0].gpers = 1 << 9;
AVR32_GPIO.port[0].oders = 1 << 9;
AVR32_GPIO.port[0].ovrs = 1 << 9;

となります.は?と思うかもしれませんが,解説していきます.

“GPIO port”と“GPIO pin”

データシートの「3.2 Peripheral Multiplexing on I/O lines」にある表の「GPIO」と書いてある列には,番号が書いてあります.これが“GPIO number”です. “GPIO port”と“GPIO pin”は,以下の数式で計算できます.

(GPIO port) = (GPIO number) / 32 ;
(GPIO pin) = (GPIO number) % 32 ;

要するに,“GPIO port”は“GPIO number”を32で割った商,“GPIO pin”は“GPIO number”を32で割った余りです.

運良くUC3L064では,ピン名と“GPIO port”及び“GPIO pin”がうまく対応(PB09であれば,GPIO port:1 , GPIO pin:9)していますが,データシート曰く,そうとは限らないようです.

AVR32の主要GPIO関連レジスタ

以下のレジスタは,AVRで云うところのDDRX,PORTX,PINXに対応します.

RegisterName詳細
GPIO Enable RegisterGPER1を書き込んだビットと対応するピンのGPIOが有効になる
Peripheral Mux Register 0,1,2PMR0,1,2GPERが1の時は関係ない(後述)
Output Driver Enable RegisterODER1を書き込むと対応するピンが出力,0だと入力(Hi-Z出力)
Output Value RegisterOVR1を書き込むと対応するピンからHが,0だとLが出力される
Pin Value RegisterPVR読み込みのみ.Hだと1に,Lだと0に対応するビットがなる
Pull-up Enable RegisterPUER1を書き込むと対応するピンのプルアップが有効になる

AVR32でのGPIOのレジスタの設定方法

GPIO各種レジスタは,

AVR32_GPIO.port[(GPIO port)].(小文字レジスタ名) = 値;

という記法で記述します.各レジスタの第n bitは,“GPIO pin”に対応しています.例えば,PA08(GPIO number:8, ∴GPIO port:0,GPIO pin:8)のGPIOを有効にしたいときは,いわゆる“ビット操作関数”を利用して

AVR32_GPIO.port[0].gper |= 1 << 8;

と書けます.

GPIOのレジスタへのアクセスタイプ

AVR32では,GPIOの各レジスタに4つのアクセスタイプがあります.

Access Type詳細レジスタ
Read/Write普通に読み込み/書き込みができる.元のまま(例:GPER)
Set1を書き込んだビットのみ1に変化する.読み込み不可.末尾にSが付く(例:GPERS)
Cleaar1を書き込んだビットのみ0に変化する.読み込み不可.末尾にCが付く(例:GPERC)
Toggle1を書き込んだ所のみ0なら1に,1なら0に変化する.末尾にTが付く(例:GPERT)

先程,PA08のGPIOを有効にするプログラムを“ビット操作関数”を用いて記述しましたが,AVR32の場合,GPIOに限って言うとその方法は効率が悪いです*1.本来であれば,Access Type“Set”を利用して

AVR32_GPIO.port[0].gpers = 1 << 8;

とすべきです.要するに,

AVR32_GPIO.port[(GPIO port)].(小文字レジスタ名) |= 値;
                AVR32_GPIO.port[(GPIO port)].(小文字レジスタ名)s = 値;
AVR32_GPIO.port[(GPIO port)].(小文字レジスタ名) &= ~(値);
                AVR32_GPIO.port[(GPIO port)].(小文字レジスタ名)c = 値;

に対応すると記憶しておけばよいでしょう.

《例の解説》

AVR32_GPIO.port[0].gpers = 1 << 9;  //PA09のGPIOを有効にする
AVR32_GPIO.port[0].oders = 1 << 9;  //PA09を出力にする
AVR32_GPIO.port[0].ovrs = 1 << 9;   //PA09からHを出力にする

delay関数

ありません.Good-bye...

という訳にはにはいきませんので,解決策を提示します.

Atmel Software Framework(ASF)を使う

ASFではdelay関数が用意されています.関数名は \begin{center} _delay_ms(); ではなく delay_ms();
_delay_us(); ではなく delay_us(); \end{center} なので注意してください.

ASFから引っ張ってくる

ASFのdelay関数のコードを一部改変すれば,何処でも(C++でも)つかえます.もっとも,ライセンス的にセーフなのかアウトなのかわかりませんが.関数名は同じく頭部に_が付きません.


当然のことですが,delay関数を使うにはAVRの時同様F_CPUの定義が必要です.

EEPROM

ありません.Good-bye...

という訳にはいきませんが,AVR32ではEEPROMの代わりにFlash Memory上の“User Page”が使えます.

詳しくは後ほど触れます.

プログラムメモリ上の定数

多分頭にconstを付ければ終わりです.

const uint16_t kpc = 771; //kpcという名前で771を保存

プログラムの書き込み

本来であれば5000円位するライター(秋月で売ってる)を用いて書き込みをしますが,AT32UC3L064の場合,「aWire」というデバック機能を使ってUART経由で書き込むことができます.

aWireとは

AVR32シリーズのさらに一部のデバイスに搭載されている,デバッグ機能*2のことです.秋月で売ってる3つのデバイスのうち,AT32UC3L064には搭載されていますが,他の2つには搭載されていません.

aWireを使えば,UART経由でAVR32のメモリやレジスタを操作できます.これを上手いこと利用して,フラッシュメモリを書き換えてやればプログラムの書き込みができるという訳です.

aWireでプログラム書き込む流れ

細かいaWireの仕様はデータシートの「Programming and Debugging」の章を読んでください.

  1. BaudRateを1000にして「0X55」を送信 …… AVR32がaWireデバッグモードに入る.以後BaudRateは速めて良い.
  2. コマンド「2_PIN_MODE」を送信 …… 返答が「aWire DATAOUT」ピンから来る.1-pin Modeで動かすなら不要.
  3. コマンド「AYA」を送信 …… AVR32と通信できているかどうかの確認.
  4. コマンド「CHIP_ERASE」を送信 …… AVR32をEraseするコマンド.
  5. コマンド「STATUS_REQUEST」を送信 …… Eraseが終了しているか確認する.
  6. コマンド「MEMORY_WRITE」で「Flash Command Register(FCMD)」の「CMD」を「Clear Page Buffer」に設定 …… Page Bufferを空っぽにする.詳細はデータシートの「Flash Controller(FLASHCDW)」の章を参照.ちなみに「MEMORY_WRITE」コマンドは必ずDWORDモードで発行すること.
  7. コマンド「MEMORY_WRITE」でFlashメモリの該当箇所に256バイト(1ページ)ずつプログラムを書き込む …… この作業はFlashメモリにプログラムを書き込んでいるように見えて,実際はPage Bufferに書き込んでいるに過ぎない.
  8. コマンド「MEMORY_WRITE」で「Flash Command Register(FCMD)」の「CMD」を「Write Page」・「PAGEN」に書き込むページ番号を設定する …… Page Bufferの内容をFladhメモリに書き込む.
  9. コマンド「MEMORY_READ」で「Flash Status Register(FSR)」の「Flash Ready Status(FRDY)」ビットが1になっているか確認 …… 書き込みが完了しているかどうか確認.

書き込むページが複数ある場合(殆どの場合そうだが)は,6~9の手順を繰り返します.

以上の操作を簡単に行えるよう,筆者がプログラムを自作したので,以後はそれを使ってプログラムを書き込む方法について述べたいと思います.

具体的な方法

用意するもの
  • USBシリアル変換モジュール …… ピンヘッダがある物は,必ず「3.3V」側にセットしてください.
  • 書き込み用ソフトウェア(aWireWriter.exe) …… .Net Framework 4.0以上が必須です.
配線図

さっき載せたのと同じですが再掲.VDDIOやGNDはどれか1ピンだけ配線すれば恐らく大丈夫です.データシートも一応確認して下さい.パスコン・デカップリングコンデンサも忘れずに.

aWireWriter.exeの仕様

コンソールプログラムです.GUIはありません.コンソールプログラムですが .Net Framework 4.0以上が無いと動きません.

内部では,主にHEXファイルの解析・最適化,シリアルポートの操作をしています.

以下に,コマンドラインオプションを羅列します.

コマンド内容
xxx.hexxxx.hexを書き込む.必須オプション.
-skip「Eraseしますか?」などの確認をスキップして,問答無用で実行する.
-skip-verify確認のスキップに加えてVerifyをスキップする.
-com=n-skipコマンド指定時は必須.シリアルポート番号をnに指定する.
-dispHEXファイルの中身を表示する.

例: >aWireWriter.exe c:\Users\...\main.hex -skip-verify -com=3

Atmel Studioで使う場合は,引数を$(TargetDir)\$(TargetName).hexにして外部ツールに登録すると書き込みが楽です.

割り込みや周辺機能(ペリフェラル)を使う

GPIOも周辺機能なんですが,まぁそこんところは目をつぶって下さい.

割り込みを使う

ASFを使わない限り,AVRのようには簡単に行かず,なんとアセンブる必要があります!!!

ASFを使わない場合

AVRの場合,割り込みベクタはプログラムメモリの先頭に固定的に置かれていました.しかし,AVR32の場合はプログラムメモリ上のEVBA(Exception Vector Base Address)付近に置かなければなりません.

AVR32には例外処理(Exception Processing)というものがあります.これは,AVR32に深刻なエラー発生した時に例外が発行されるというものです.例外が発行されると,EVBA+Offset値にJumpし,そこに書かれている処理が実行されます.普通はそこに無限ループrjmp $を書きます.なお,このOffset値は例外の原因(Exception Cause)によって決まっています*3

割り込み(Interrupt)も同様の仕組みで起こります.割り込みが起こると,予め指定したOffset値(AutoVector値という)に基づいてEVBA|AutoVector値にJumpします.普通はそこにrjmp 呼び出す関数(ISR)名を書きます.

割り込みを起こすにはEVBAは適切な値でなければなりません.割り込み時のJump先はEVBA|AutoVector値であり,加算ではなくORを取られるので,EVBAの下9ビット位は0でないと困ります.また,AutoVector値の最下位1ビット(第0ビット)は0である必要があります.

例外処理(Exception Processing)はStatus RegisterのEM(Exception Mask)ビットが0の時行われますが,たとえEMビットが1でも,いくつかの重大エラー*4発生時は行われてしまいます.それゆえ,EVBA直後には例外ベクタ(Exception Vector)が置かれるべきであり*5,割り込みベクタはその後に置かれるべきです.


割り込み源となるペリフェラルごとに,GroupとLineが割り当てられています(データシート参照).GroupごとにInterrupt Priority Registersというのがあり,そこで割り込み優先度とAutoVector値を設定できます.

AVRとは違い,Group(ペリフェラル)単位では呼び出すISR(Interrupt Service Routine)は変えられますが,Lineごとや割り込み要因ごと(例えばUSART送信・受信など)にはISRを変えられません.

故に,ISRではまず,どのLineから割り込みが起こったかを調べ,次に細かな割り込み要因を調べあげる必要があります*6


以下にその手順を示します.

  • アセンブリ(.S)ファイルを用意.書き方はサンプルのexception.Sから察して.
  • C言語側では初期設定として以下を行なうこと
    • System RegisterであるEVBAレジスタEVBAアドレスを書き込む.
    • 割り込みを行いたいペリフェラルの所属するGroupのInterrupt ControllerのIPRレジスタに,割り込み優先度とAutoVector値を設定.
  • ISR内では必要に応じて以下を行なうこと
    • IRRレジスタ(Interrupt Request Registers)で,どのLineから割り込みが起こったかを調べる.
    • ペリフェラルのそれっぽいレジスタ(USARTであればChannel Status Register)で割り込み要因を調べる.
ASF・ASFから引っ張ってきたものを使う場合

メイン関数側は以下のようになります

INTC_init_interrupts();   //割り込みの初期化(EVBA Addressの登録等)
INTC_register_interrupt(&割り込み時に実行する関数(ISR)名,割り込み要因,割り込みレベル);
cpu_irq_enable();   //割り込みの有効化

割り込みが起こったら実行する関数(ISR)はメイン関数より手前で以下のように書きます.

__attribute__((interrupt)) static void 関数名(void){
    //実行内容をここに書く.
    //割り込みフラグを何らかの手段で0にする必要があります.
    その方法は割り込み源により異なります.
}

C++を使っている場合は,

extern "C" __attribute__((interrupt)) void 関数名(void){

としてください.

周辺機能(ペリフェラル)を使う

レジスタの設定

先ほど,GPIOレジスタの設定については触れましたが,如何せんまだレジスタ一般については触れていません.

レジスタ一般に,以下の表現で一般的に操作できます

AVR32_(ペリフェラル名).(小文字のレジスタ名)
例: AVR32_USART0.mr

ペリフェラル名は,avr32-gcc特有の表記だったりしてデータシートを読んでみてもわからないことがありますが,Atmel Studioそ使っていれば,それらはIntelliSense機能のおかげで予測変換が出るので,ある程度カンで書けます.あるいは,avr32/io.hを辿っていけばやがて定義に出会えます.

一般的なレジスタには,GPIOのようにAccess Typeはありません.おとなしく“ビット操作関数”を用いて記述して下さい.

レジスタ値の設定ですが,AVRの時のように0b01110010;とやってしまうと,AVR32のレジスタは32ビットなために0b01000100010000010111101010011111;となり,脳筋にも程があります.別に16進数で0x44417A9F;としたり,10進数で1145141919;としても良いですが,メンテナンス性も最悪ですし良いことはありません.

例えばこのようにするのが望ましいです. USARTのMode Register(Name:MR)で,Stop Bitを2に変えようとした時,こう書きたくなります

AVR32_USART0.mr |= 0b10 << 12;

AVR32/io.hでは,MRレジスタのNBSTOP関連に関する定数が下記の通り宣言されています.

#define AVR32_USART_MR_NBSTOP                                        12
#define AVR32_USART_MR_NBSTOP_1                              0x00000000
#define AVR32_USART_MR_NBSTOP_1_5                            0x00000001
#define AVR32_USART_MR_NBSTOP_2                              0x00000002
#define AVR32_USART_MR_NBSTOP_MASK                           0x00003000
#define AVR32_USART_MR_NBSTOP_OFFSET                                 12
#define AVR32_USART_MR_NBSTOP_SIZE                                    2

AVR32_USART_MR_NBSTOP = AVR32_USART_MR_NBSTOP_OFFSETはNBSTOPフィールドの最下位ビットが第何ビットかを表し,AVR32_USART_MR_NBSTOP_MASKはNBSTOPフィールドの部分(第12~13ビット)だけ1になっています.

これを用いれば,先ほどのプログラムは

AVR32_USART0.mr |= AVR32_USART_MR_NBSTOP_2 << AVR32_USART_MR_NBSTOP_OFFSET;

と書けます.この方が断然読みやすいのが分かるのではないでしょうか?

他のレジスタ・フィールドの定数も同様の形で定義されています.定数名はIntelliSense機能の予測変換から推測できます.あるいは,avr32/io.hを辿っていけばやがて定義に出会えます.


なお, 先ほどのプログラムは,こうやっても書けます.

avr32_usart_mr_t ahhhhh = AVR32_USART0.MR;
ahhhhh.usart_mode.nbstop = AVR32_USART_MR_NBSTOP_2;
AVR32_USART0.MR = ahhhhh;

これは,各レジスタ

typedef struct avr32_usart_t {
    union {
        unsigned long                  cr        ;//0x0000
        avr32_usart_cr_t               CR        ;
    };
    ...

というように,構造体と共有体とビットフィールドを駆使して定義されている恩恵です.なお

AVR32_USART0.MR.usart_mode.nbstop = AVR32_USART_MR_NBSTOP_2;

として直接代入してしまうと,MRレジスタへのアクセスがWord単位では無くByte・Halfword単位で行われてしまう危険性があるため避けたほうが良いようです*7.AVR32のレジスタ・メモリは基本的に32ビット(1Word)単位で読み書きされる必要があります.8ビット(1Byte)・16ビット(1Halfword)単位での読み書きは不審な挙動を示します. *8

Peripheral Multiplexing

AVR32の各ピンは,GPIOとしてだけでなく,各ペリフェラルのSignal(USARTであればRXDやTXD,タイマーであればPWM出力)としての役割も果たします.ここまではAVRと同じですが,AVRの場合はあるペリフェラルの機能を有効にした時点で該当するピンには自動的にSignalとしての役割が付与されますが(Mega88pであれば,USARTの送信機能を有効にしたらPD1がTXDとして機能する),AVR32の場合は,Peripheral Mux RegisterでピンをどのペリフェラルのSignalにするか設定する必要があります.

RegisterName詳細
GPIO Enable RegisterGPER0を書き込むと対応するピンのGPIOが無効になり,そのピンの役割はPMR0,1,2で決められる.
Peripheral Mux Register 0,1,2PMR0,1,2PMR0,1,2の組み合わせによって,対応するピンの役割がFunction A~Hのどれになるか決まる.

以上2つのレジスタは,ともにGPIO関連レジスタであるので,例外的に4つのAccess Typeが使えます.

PMR0,1,2の組み合わせとそれによって決まるFunctionの対応関係は以下の表の通りです.

PMR2PMR1PMR0Function
000A
001B
010C
011D
100E
101F
110G
111H

例えば,PA08(GPIO port:0,GPIO pin:8)をFunction Fとして機能させたければ,

(GPERの第8ビット) = 0;  //GPIO無効化
(PMR2の第8ビット) = 1;
(PMR1の第8ビット) = 0;
(PMR0の第8ビット) = 1;  //Function Fを選択

とすれば良いので,プログラムはこうなります.

AVR32_GPIO.port[0].gperc = 1 << 8;  //PA08のGPIOを無効にする
AVR32_GPIO.port[0].pmr2s = 1 << 8;
AVR32_GPIO.port[0].pmr1c = 1 << 8;
AVR32_GPIO.port[0].pmr0s = 1 << 8;  //PA08はFunction Fとして機能

なお,それぞれのピンのFunction A~Fが何なのかは,データシートの「3.2 Peripheral Multiplexing on I/O lines」にある表に載っています*9

AVR32のクロック

AVR32のクロック源はヒューズビットで変更するのではなく,すべてプログラム上で変更します.AVR32の起動時は,115kHzのSystem RC Oscillatorによって動いています.まともなスピードで動かしたい場合,クロック源の変更は避けて通れません.

UC3L064の最高クロック周波数は50MHzであることに留意して,読み進めてください.

AVR32のMain Clock源
  • System RC oscillator(RCSYS) …… 115kHz内蔵発振器.起動後,Reset後は変更しない限りこれがMain Clockになる.
  • Oscillator0(OSC0) …… 外部クリスタル発信器.16MHzまで対応.
  • DFLL …… 40~150MHzの周波数のクロックを自由に?生成できる装置.
  • 120MHz RC oscillator(RC120M) …… 120MHz内蔵発振器.Main Clockに使うには最低4分周が必要.実質30MHzになる.

どのClock源を使うかは,Power ManagerのMCCTRLレジスタが決めます.

DFLLを起動する

最高のパフォーマンスを発揮したいのであれば,DFLLを使うのが良いと考えられるのでそれを使うことを考えます.

データシート読んで理解できる猛者は多くはないと思うので*10ざっくり説明すると,DFLLにはOpen Loop ModeとClosed Loop Modeがあり,Open Loop Modeでは周波数がFINEとCOARSEという2つの値と温度で決まります.周波数とFINE値・COARSE値の関係はASFのscif_dfll0_openloop_start関数の中身から察してください.

Closed Loop Modeで周波数を固定してOpen Loop Modeに切り換えるのが正当な方法らしいですが,めんどうなのでいきなりOpen Loop Modeで96MHzを作ってしまいたいと思います.

96MHzは,ASFのExample Project(PM Example2)によるとCOARSE = 150 , FINE = 65で生成できるようです.

具体的なプログラムはサンプルを参照してください.

Main Clock源と分周比を切り替える

先ほどDFLLで生成した96MHzをMain Clock源にして,マイコンに供給することを考えます.

AVR32では,システム部及びペリフェラルがCPU・HSB(High Speed Bus)・PBA(Peripheral Block A)・PBB(Peripheral Block B)というブロックに分かれています.それぞれのブロックが分周器を持ち(CPUとHSBは同一),Main Clockを分周したものをClockとして各々のブロックは動作します.それぞれレジスタで個々に分周比を設定できます*11

96MHzを書くブロックのClockとして使うには,48MHzに落とす,すなわち2分周する必要があります.故に,Main Clock源をDFLLにする前にCPU・PBA・PBBの分周器の分周比を2に設定する必要があります.その後,Main Clock源をDFLLに設定します.

具体的なプログラムはサンプルを参照してください.

User Pageを使う

EEPROMがない代わりにUser Pageを使おうという話です.

User Pageの特徴
  • Flashメモリの一部 …… Flashメモリの一部なので,Flashメモリにアクセスするのと同じようにアクセスできます.当然,電源を切ってもResetしてもデータは消えません.
  • Eraseされない …… 通常のErase操作ではEraseされません.特別なコマンドを実行しなければEraseされません.
  • 256Byte …… Address:0x80800000から始まり,1ページ分(256Byte,64Word)あります.
  • 書き換えはいっぺんにする …… Flashメモリの一部なので書き換えは1ページ(要するにUser Page全体)ごとに行います.
  • User Page先頭2Word(8Byte)は変更してはならない …… はじめの2Word(8Byte)はBoot設定が書き込まれているので,初期値(すべて1,Erase状態)のままにしておく必要があります.起動しなくなりますよ.
特定のアドレスへの読み書き

まず,AVR32一般について

*((volatile uint32_t*)アドレス) = 値;

とすると,指定したアドレスに値を代入できます.uint32_tとしているのは,一般にAVR32ではレジスタ・メモリの読み書きは32ビット単位で行われるので,32ビット変数として取り扱うと便利だからです.

なお,先ほど触れた通り,AVR32のレジスタ・メモリは基本的に32ビット(1Word)単位で読み書きされる必要があります.8ビット(1Byte)・16ビット(1Halfword)単位での読み書きは不審な挙動を示します*12

User Pageの先頭アドレスは,AVR32_FLASHCDW_USER_PAGE_ADDRESSと定義されているので,例えば

*((volatile uint32_t*)AVR32_FLASHCDW_USER_PAGE_ADDRESS)

にアクセスすることでUser Pageの先頭1Wordへの読み書きができます.

User Pageへの書き込み

User Page(というよりFlashメモリ全般)への値の転送先,即ち

*((volatile uint32_t*)UserPage内の任意のアドレス) = 値;

で変化するのは,Page Bufferと呼ばれるものの中身です.User Pageへの書き込みは,Page Bufferへの値の転送が終わったあとに,Write User Pageコマンドを発行することでなされなければなりません.

Page Bufferは使う前にClear Page Bufferコマンドを発行してClear(全ビット1に)する必要があります.また,Page Bufferへの値の転送は「第2nWord →第2n+1Word」の順で2Wordづつなされる必要があります.第(偶数)Wordから2Wordづつ書き込んでくれればいいのでかならずしも先頭AVR32_FLASHCDW_USER_PAGE_ADDRESSからなされる必要は無さそうです.

以下にその手順を示します.

  1. Flash Command Register(FCMD)の「CMD」を「Clear Page Buffer」にして,Page Bufferを空っぽにする.
  2. 「第2nWord →第2n+1Word」の順で2Word単位で値を転送する.この時点ではUser Pageに値を書き込んだように見えてPage Bufferに書き込んだにすぎません.
  3. Flash Command Register(FCMD)の「CMD」を「Write User Page」にして,Page Bufferの内容をUser Pageに書き込む.
  4. Flash Status Register(FSR)の「Flash Ready Status(FRDY)」ビットが1になっていれば,書き込みが完了している.なってなければ書き込みが完了していないので,なるまで待つ.

詳細はデータシートの「Flash Controller(FLASHCDW)」の章を参照してください.

User Pageからの読み込み
変数 = *((volatile uint32_t*)UserPage内の任意のアドレス);

とすることで,値の読み込みが簡単にできます.書き込みの際は「第2nWord →第2n+1Word」の順で2Wordづつなされる必要がありましたが,読み込みの際はどうやら気にしないで1Word単位で好きな場所へアクセスできるようです.

Flashコントローラーに関するバグ

データシート末尾のErrataに,Flashコントローラーに関するバグが書いてあります. Flashメモリ関連を弄る前には,

AVR32_HMATRIXB.mcfg[1] = AVR32_HMATRIXB_MCFG1_ULBT_INFINITE <<
AVR32_HMATRIXB_MCFG1_ULBT_OFFSET;
AVR32_HMATRIXB.scfg[0] = 255 << AVR32_HMATRIXB_SCFG0_SLOT_CYCLE_OFFSET;

を書かなければならないようです.

このように,困ったときはデータシート末尾のErrataに解決策が書いてあるかも知れません.

*1:一度レジスタの内容を読み取ってからORをとってレジスタに再度書き込んでいるため.これだけで3クロック以上はかかる.

*2:JTAG等がよく知られている.

*3:例えばIllegal Opcodeは0X20といった具合で

*4:NMI, Unrecoverable Exception, TLB Multiple Hit, Bus Error

*5:最低限さっき挙げた重大エラーはハンドルすべきである.

*6:もっとも,Lineを一つしか持たないGroupもあるので,その場合は調べるまでもない.また,割り込みが起こる要因が1つしか考え得ない(例えばUSARTの受信割り込みだけを有効にしている)場合とかも調べるのはバカバカしい

*7:ここではWord=32ビット=4Byte,Halfword=16ビット=2Byteです.AVRの場合,Word=16ビットと表記されることが多かったりするので注意してください.

*8:もっとも,CompileオプションOptimizeの「Force double-word alignment」にチェックを入れれば問題なさそうだけど……まぁわかりません.

*9:PA08のFunction Fは何もありませんでした……ごめんなさい……

*10:ワイが理解できなかっただけか?

*11:CPUとPBA・PBBの分周比があまりにもギャップがあったりすると問題が起こるようです.

*12:C/C++でプログラムしているときは大してきにしなくても良いかもしれない.