AVR Libc Home Page | ![]() |
AVR Libc Development Pages | |||
Main Page | User Manual | FAQ | Library Reference | Additional Documentation | Example Projects |
C言語は可搬性のある言語としてデザインされています。移植には2通りのパターンがあります。
ひとつは異なるプラットホームへの移植(OSやプロセッサが異なる)、もう一つは異なるコンパイラへの移植があります。
異なるコンパイラへの移植は、そのアプリケーションが埋め込みシステムの場合はしばしば困難になります。
たとえば、標準のC言語では、奇妙なことに割り込みサービスルーチンの宣言・定義の方法が標準化されていません。
コンパイラが異なれば、レジスタの扱い方も異なり、しばしば標準的な言語の構造に合わない方法で扱われています。
この章では、IARコンパイラで組み上げられたアプリケーションをGNUツールチェーン(AVR
GCC)に移植するための、いくつかの方法・指針を提供します。
これは全てを網羅したリストではないことをご承知おき下さい。
IOヘッダファイルはそれぞれ個別のプロセッサ固有のレジスタ名・ビット名などを保有しています。
IARは各プロセッサ毎に固有のヘッダファイルを持っていて、レジスタがコード内で使用される場合は必ずインクルードされていなければなりません。
たとえば、こんな風に。
#include <iom169.h>
AVR GCC もまた個別のプロセッサ固有のヘッダファイルを持っています。しかし、実際のプロセッサ名指定は、コンパイラへのコマンドラインフラグでなされます。
(-mmcu=processor
フラグオプションによる)
これは通常Makefile内で指定されます。これにより、1つのヘッダファイルで多くのプロセッサタイプに対応できます。
※ソースの#inlcude指定を書き換えず、makefile内のMCU指定の書き換えだけで対応可能
#include <avr/io.h>
コンパイラは、プロセッサタイプを知ることが出来ます。
そして、プロセッサ名から、上記の単一ヘッダファイル(avr/io.h??)を通して、正しい個別IOヘッダファイルを引き出しインクルードします。
これにより、1つの汎用ヘッダファイルを指定するだけでよく、異なるプロセッサ用にアプリケーションを作り直すときでも、インクルードファイルを交換することなく、(※makefile内のMCU指定を変更するだけで)すませることができます。
AVRツールチェーンは、AVRデータシートに記された正しいレジスタやビット名に従うように努力しています。
IARのIOヘッダファイルとAVR GCCのIOヘッダファイルとの間には、いくらかの不一致があります。
上で述べたように、標準C言語は妙なことに標準的な方法で割り込みサービスルーチン(ISR)の宣言や定義ができません。
という訳で、各コンパイラはそれぞれ独自の特別な方法でISRを取り扱います。
IAR はISRを以下のように宣言します:
#pragma vector=TIMER0_OVF_vect __interrupt void MotorPWMBottom() { // code }
AVR GCC では、ISRを以下のように宣言します:
ISR(PCINT1_vect) { //code }
AVR GCC はISR宣言のためにISRマクロを使います。このマクロは以下のヘッダファイルを要求します:
#include <avr/interrupt.h>
種々の割込ベクタは<avr/io.h>
と共に組み込まれる個別のプロセッサIOヘッダに記されています。
IARは以下のような、いくつかの固有ルーチン ( Intrinsic Routines ) を持っています。
__enable_interrupts()
__disable_interrupts()
__watchdog_reset()
これらの固有な関数はそれぞれ特異的なAVRオペコード (SEI, CLI, WDRなど)に翻訳するものです。
AVRGCCにも同等のマクロはありますが、AVRGCCではこれらは単一のインクルードファイルには入っていません。.
AVR GCC は、 sei()
がIARの__enable_interrupts()
に該当し、cli()
が __disable_interrupts()
に該当します。これらは <avr/interrupts.h>
に含まれます。
AVR GCC は、 wdt_reset()
が __watchdog_reset()
に該当します。これらはAVRGCCのWatchdog Timer
APIを利用可能にするものですが、こちらは <avr/wdt.h>
に含まれています。
C言語はメモリ空間が分断された(命令コードとデータ)ハーバードアーキテクチャプロセッサ用には作られていません。
これにより、データをプログラムメモリ(フラッシュメモリ)に置くためには、標準的でない方法を採る必要があります。
IAR では、プログラムメモリ上に変数をおく非標準キーワードを用います。
__flash int mydata[] = ....
AVR GCC では同じ事をするために変数アトリビュートを用います。
int mydata[] __attribute__((progmem))
avr-libc は、変数アトリビュートをより簡便につけるために、以下のようなマクロを用意しています。
#include <avr/pgmspace.h>
.
.
.
int mydata[] PROGMEM = ....
<avr/pgmspace.h>
を要します。フラッシュデータを読み出すためには、マクロ
pgm_read_*
() を使います。
これは <avr/pgmspace.h>
内で定義されています。全てのプログラムメモリハンドリングマクロはここで定義されています。
プログラムメモリ内に変数を置くのに、IARとAVRGCCで共通の方法(コード)を使う事もできます。
以下のような宣言を持つマクロを用意すればいいです。
#if defined(__ICCAVR__) // IAR C Compiler #define FLASH_DECLARE(x) __flash x #endif #if defined(__GNUC__) // GNU Compiler #define FLASH_DECLARE(x) x __attribute__((__progmem__)) #endif
この短いコードは、IARコンパイラ・GCCコンパイラどちらが使われているかを判定し、それに合わせてプログラムメモリ利用マクロ
FLASH_DECLARE(x)を定義します。
こんな感じで使えます。
FLASH_DECLARE(int mydata[] = ...);
帰り先がないmain()の宣言で、IARでは以下のようにします。
__C_task void main(void) { // code }
AVRGCCで同様の事をするには、以下のようになります。
void main(void) __attribute__((noreturn)); void main(void) { //... }
AVRGCCでは、main()に対するプロトタイプが要求されるので、type
"noreturn"関数アトリビュートを宣言することができます。
こうして、main()の宣言は普通通りに行えます。main()の返す型は現在の所はvoidであることに注意してください。
IAR コンパイラは、コンパイラオプションによりr15以降の汎用レジスタをロックして以下のキーワード文法でコンパイラに(変数として)使用させることができます。
__regvar __no_init volatile unsigned int filteredTimeSinceCommutation @14;
このコードは、r14をロックして、変数名 "filteredTimeSinceCommutation"
専用に使用させることができます。
これにより、コンパイラはこのレジスタを勝手に破棄しないことになります。
※通常は、コンパイラはメモリ内容を使用するときだけレジスタに読み込み、使用し終わったら必要に応じてSRAMメモリに戻した後レジスタを他の用途に使う
※この場合は、SRAMメモリに戻すことなく、このレジスタをずっと変数
"filteredTimeSinceCommutation"
として扱う。
AVR GCCでは同じ事をこのように行います。
register unsigned char counter asm("r3");
通常はr2〜r15がこの用途に使用できます。
レジスタのロックはAVRGCCでは推奨されていません。
コンパイラが使えるレジスタを減らし、コード効率を悪化させるからです。
リスク自分持ちの上ご使用下さい。