Skip to content

Macro_API

askn37 edited this page Jan 31, 2024 · 14 revisions

Macro_API マクロリファレンス

<api/macro_api.h>

Macro/Micro API導入ヘッダ。 Arduino互換APIに準じた機能別名を提供し、 AVR-LIBCのうち多用する一部も一括して組み込む。 ENABLE_MACRO_APIマクロが未定義なら、 あるいはDISABLE_MACRO_APIマクロが定義済なら、 これらは有効化されない。

依存性:同時にインクルードされる

  • <avr/io.h> <avr/interrupt.h> <avr/pgmspace.h> <avr/sleep.h> <util/atomic.h> <inttypes.h> <stdbool.h> <api/btools.h> <api/delay_busywait.h> <api/macro_digital.h> <api/memspace.h> <api/power.h> <variant.h>

<avr/io.h> <variant.h> はボード定義依存。

  • Macro_APIはマクロによって Arduino標準API の一部を模倣する。 これによって少なくともBlink.inoはエラーなくビルドすることが出来る。
  • Macro_API<Arduino.h>によりインクルードされるため スケッチ.inoでは既定で(強制的に)組み込まれている。 この挙動を抑制するには事前にENABLE_MACRO_APIを 未定義(#undef ENABLE_MACRO_API)とするか、 DISABLE_MACRO_APIを定義する。
  • その他のC言語.c C++言語.cppファイル(場合によってはアセンブラ.Sも) についてはそうではない。明示的にインクルード文を書かなければ組み込まれない。

別名マクロ

以下の別名マクロは事前にDISABLE_ALIAS_<大文字別名>が定義されていると個別に無効化出来る。

別名 原義
delay(_MS) delay_millis(_MS)
delayMicroseconds(_US) delay_micros(_US)
digitalRead(PIN) digitalReadMacro(PIN)
digitalWrite(PIN,OUT) digitalWriteMacro(PIN,OUT)
interrupts() __builtin_avr_sei()
map(_X,_IMIN,_IMAX,_OMIN,_OMAX) map_long(_X,_IMIN,_IMAX,_OMIN,_OMAX)
noInterrupts() __builtin_avr_cli()
nop() __builtin_avr_nop()
openDrainWrite(PIN,OUT) openDrainWriteMacro(PIN,OUT)
pinMode(PIN,DIR) pinModeMacro(PIN,DIR)
wdt_reset() __builtin_avr_wdr()

マクロなので他の同名関数はnamespace classメンバーにも関係なく、こちらで上書きされることに注意。
この場合別名マクロを無効化しないと、意図しないコードが出力あるいはエラーになることだろう。

<api/memspace.h>

これはプログラムメモリ拡張属性等とそれを補うマクロ等を定義している。

#define NIMEM __attribute__ ((section (".noinit")))

この属性を付加した SRAMデータ領域宣言は、スタートアップコードで初期化されないことを保証する。 暗黙のゼロ初期化もされない。

char sram_store[128] NIMEM;

#define ROMEM __attribute__ ((section (".progmem.nvm")))

この属性を付加した データ領域宣言は、初期値を与えてフラッシュメモリ領域に配置される。必然的にconstを併用する。 読込アクセス方法はPROGMEM属性に同じ。 効能もPROGMEM属性に同じだが、リンクプロセス時に最後方へ配置される。

const char message[] ROMEM = "Hello World!";

#define NVMEM __attribute__ ((section (".nvmem")))

この属性を付加した データ領域宣言は、初期値を与えずに フラッシュメモリ領域に配置される。 最終出力HEX/binファイルには含まれない。 読込アクセス方法はPROGMEM属性に同じ。

char storage[] NVMEM;

自己フラッシュメモリ書換領域を事前予約および型宣言するために使用する。
当該機能を使用しない場合は単に無意味なスペースを費やすに過ぎない。

#define PGM_ALIGN alignas(PROGMEM_PAGE_SIZE)

領域宣言に付加するとそれを(ボード選択に依存する)フラッシュメモリページ境界に合わせる。 PROGMEM ROMEM NVMEM と同時に使用できる。

当該オブジェクト先頭はページ境界となり、次のオブジェクトとの間には強制的にパディングが入る。 つまり該当フラッシュメモリページ内には、ただひとつのオブジェクトだけが配置される。

char object1[33] PROGMEM PGM_ALIGN;
/* ここに隙間が空く */
char object2[33] PROGMEM PGM_ALIGN;

#define PAGE_ALIGN(sect)

後続の指定セクションに属す領域宣言を(ボード選択に依存する)フラッシュメモリページ境界に合わせる。 セクション名は ".text" ".progmem" ".nvmem" が有効。

PGM_ALIGNとの違いは直後のオブジェクトにだけ掛かるので、 領域宣言後方の境界整列に影響しない。 複数のオブジェクトを密に並べたい場合に使用する。

PAGE_ALIGN(".progmem");
char object1[33] PROGMEM;
/* ここに隙間が空かない */
char object2[33] PROGMEM;

#define __DEPLOY(S)

多重参照されたマクロ内容を、マクロ名に変換する。変換後のマクロ名が未定義ならエラー。

#define USE_NMI NMI_vector_num
ISR(_vector(__DEPLOY(USE_NMI))) {...} /* NMI_vector と解釈される */

#define __QUOTE(S)

引数をダブルクォート文字列に書き換える。

IMPORT_BINFILE(".progmem", FooBin, __QUOTE(SKETCH_PATH) "/" "foo.bin");

#define IMPORT_BINFILE(sect, sym, file)

指定のバイナリファイルを指定セクションに取り込んで、指定のシンボルを名付ける。 ファイルパスは絶対パス文字列でなければならない。 <指定シンボル>が先頭番地を、<指定シンボル>_endが終了番地を示す。 いずれもexternと任意の型指定を書くことで C/C++から参照可能になる。

動的にファイルを読み込むものではなく、リンカエディタで静的に結合されることに注意。

IMPORT_BINFILE(".progmem", FooBin, __QUOTE(SKETCH_PATH) "/" "foo.bin");
extern const uint8_t FooBin[], FooBin_end[] PROGMEM;
const size_t _sizeof_FooBin = FooBin_end - FooBin; // ファイルサイズに一致

実際の使用例は[Import_BinFileサンプル]を参照のこと。

#define IMPORT_BINFILE_PART(sect, sym, file, ofs, siz)

指定のバイナリファイルを指定セクションに取り込んで、指定のシンボルを名付ける。 ファイルパスは絶対パス文字列でなければならない。 ofsでファイル先頭からの絶対位置、sizでそこからの取り込みバイト量を指定できる。 <指定シンボル>が先頭番地を、<指定シンボル>_endが終了番地を示す。 いずれもexternと任意の型指定を書くことで C/C++から参照可能になる。

動的にファイルを読み込むものではなく、リンカエディタで静的に結合されることに注意。

IMPORT_BINFILE_PART(".data", FooBinDat, __QUOTE(SKETCH_PATH) "/" "foo.bin", 0x2c, 0x30);
extern const uint8_t FooBinDat[], FooBinDat_end[];
const size_t _sizeof_FooBinDat = (FooBinDat_end - FooBinDat); // これは'siz'指定値に同じ

実際の使用例は[Import_BinFileサンプル]を参照のこと。

<api/delay_busywait.h>

計時器周辺機能を使わない busy-wait 遅延ループ。 故に割込禁止中でも動作するが、割込禁止でない場合は他の割込の影響を受ける。 時間精度はコンパイル時に決定される静的な F_CPU マクロ定数に依存する。

このヘッダが有効ならENABLE_DELAY_BUSYWAITマクロが1に定義される

AVR-GCC固有ビルトイン関数__builtin_avr_delay_cyclesで実装されている。

void delay_micros (uint32_t _us)

マイクロ秒単位指定。 引数はコンパイル時に決定される静的な定数でなければならず、変数での可変サイクル数は支援されない。 0では何もしない。

delay_micros(1000);
  • F_CPU=32MHzでの指定上限は 134217727L(134.2秒)

MacroAPI有効化時はdelayMicrosecondsの別名でも使える。

void delay_millis (uint32_t _ms)

ミリ秒単位指定。 引数はコンパイル時に決定される静的な定数でなければならず、変数での可変サイクル数は支援されない。 0では何もしない。

delay_millis(1000);
  • F_CPU=32MHzでの指定上限は 134217L(134.2秒)

MacroAPI有効化時はdelayの別名でも使える。

void _busy_loop_3_8 (uint8_t _count)

void _busy_loop_4_8 (uint8_t _count)

void _busy_loop_4_16 (uint16_t _count)

void _busy_loop_5_16 (uint16_t _count)

アセンブラで書かれた特殊な busy-wait 遅延ループ。引数は指定幅の動的変数でも良い。 "CPUサイクル数×指定値+α" の遅延を生成する。

uint8_t w = 20;
_busy_loop_3_8(w);

これらは特殊なタイミング調整用途で、普通には使用されない。reduceAVR にも対応する。

<api/macro_digital.h>

外部ピン入出力操作のためのマクロ宣言。 ENABLE_MACRO_APIが有効であれば別名も使用できる。

このヘッダが有効ならENABLE_MACRO_DIGITALマクロが1に定義される

依存性: <variant.h>

#define pinModeMacro(PIN_NAME, STATE)

  • STATE := INPUT | OUTPUT | INPUT_PULLUP

#define digitalWriteMacro(PIN_NAME, STATE)

  • STATE := LOW | HIGH | TOGGLE

#define digitalReadMacro(PIN_NAME)

  • Return: (boolean) 0 or not 0

#define openDrainWriteMacro(PIN_NAME, STATE)

  • STATE := LOW | HIGH | TOGGLE

これらはマクロであって 関数ではない。 引数に変数は使えない。 PIN_NAME<variant.h> で宣言された外部端子名のみが指定できる。 STATE<api/macro_digital.h> 内で宣言された指定ラベルのみが指定できる。

pinMode(LED_BUILTIN, OUT);              // これは正しい
pinMode(13, OUT);                       // 正しくない -- 思っていた結果にはならないだろう

digitalWrite(PIN_PA7, (state & 1));     // 定数ラベル以外は書けないのでエラー

if (state & 1) {                        // これはこのように書かねばならない
  digitalWrite(PIN_PA7, HIGH);
}
else {
  digitalWrite(PIN_PA7, LOW);
}

openDrainWriteMacroはオープンドレインでの外部端子操作を行う。 使用に際してはまず pinModeMacroINPUTまたはINPUT_PULLUPを設定する。

pinModeMacro(PIN_PA2, INPUT_PULLUP);  /* Enable pullup input */

openDrainWriteMacro(PIN_PA2, LOW);    /* Direction Sink */
openDrainWriteMacro(PIN_PA2, HIGH);   /* Direction Hi-Z */

openDrainWriteMacro(PIN_PA2, TOGGLE); /* Sink or Hi-Z toggle */

reduceAVR ではdigitalWriteMacroTOGGLEは機能しない。

digitalWriteMacro は対応するすべてのAVRで、1CPUサイクルで実行を完結する。 これでは動作が早すぎるために「書いてすぐ読んだ」場合は、正しい値を得られない可能性がある。 それが必要な場合は次のように 1CPUサイクルの遅延を挿入すること。

pinModeMacro(PIN_PA7, INPUT_PULLUP);    // Allow digital input
digitalWrite(PIN_PA7, TOGGLE);          // Write GPIO
__builtin_avr_nop();                    // 1 CPU cycle delay
bool state = digitalRead(PIN_PA7);      // Read back GPIO

なおスタートアップコードの暗黙のinitVariantを用いて MCU初期化した場合の外部端子既定状態は アナログ入力許可であって、デジタル入力禁止である。 digitalReadを使う場合、外部割込入力を使う場合、 およびデジタル周辺機能入力に使う場合は その前にpinModeあるいはpinControlRegister でデジタル入力を許可しなければならない。

#define pinControlRegister(PIN_NAME)

PIN_NAME に対応するPORTx.PINnCTRLレジスタを指すショートカット。 右辺にも左辺にも書ける。

pinControlRegister(PIN_PA7) = PORT_PULLUPEN_bm;
/* pinModeMacro(PIN_PA7, INPUT_PULLUP) あるいは */
/* PORTA_PIN7CTRL = PORT_PULLUPEN_bm に同等 */

reduceAVR でこれは機能しない。

#define pinPosition(PIN_NAME)

PIN_NAME に対応するPINn_bp値を返す。

portRegister(PIN_PA4).OUT &= ~_BV( pinPosition(PIN_PA4) );
/* digitalWriteMacro(PIN_PA4, LOW) に同等 */

#define portIntrruptVector(PIN_NAME)

PIN_NAME に対応するPORTx_PORT_vectラベルを指す別名。 外部ポート群割込を宣言する際に使う。

/* #define PIN_SPI0_SS PIN_PA6 であると仮定して */

/* 外部端子割込を許可 : 対応割込ハンドラ活性化 */
pinControlRegister(PIN_SPI0_SS) = PORT_ISC_LEVEL_gc;

/* 割込ハンドラ */
ISR( portIntrruptVector(PIN_SPI0_SS) ) {

  /* 割込完了フラグをクリア */
  portRegister(PIN_SPI0_SS).INTFLAGS = _BV( pinPosition(PIN_SPI0_SS) );
}

reduceAVR でこれは機能しない。

外部ポート群割込は最大8端子がひとつのベクタ/ハンドラを共有することに注意。
旧世代AVRと違って modernAVR 世代では割込完了フラグをクリアする必要があることに注意。
多重マクロ参照は__DEPLOYの展開補助が必要な場合があるので適宜対応のこと。

#define portRegister(PIN_NAME)

PIN_NAME に対応するPORTx群レジスタを指す別名。 レジスタ名を添えることで右辺にも左辺にも書ける。

portRegister(PIN_PA4).OUT &= ~_BV( pinPosition(PIN_PA4) );
/* digitalWriteMacro(PIN_PA4, LOW) に同等 */

reduceAVR でこれは機能しない。

LINKS

multix.jp/てくにかるむ(休眠中)
Multix Zinnia Product SDK [*AVR]
AVR.JP(日本語訳)
AVR-LIBC(日本語訳)

Clone this wiki locally