-
Notifications
You must be signed in to change notification settings - Fork 0
Macro_API
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
メンバーにも関係なく、こちらで上書きされることに注意。
この場合別名マクロを無効化しないと、意図しないコードが出力あるいはエラーになることだろう。
これはプログラムメモリ拡張属性等とそれを補うマクロ等を定義している。
この属性を付加した SRAMデータ領域宣言は、スタートアップコードで初期化されないことを保証する。 暗黙のゼロ初期化もされない。
char sram_store[128] NIMEM;
この属性を付加した データ領域宣言は、初期値を与えてフラッシュメモリ領域に配置される。必然的にconst
を併用する。
読込アクセス方法はPROGMEM
属性に同じ。
効能もPROGMEM
属性に同じだが、リンクプロセス時に最後方へ配置される。
const char message[] ROMEM = "Hello World!";
この属性を付加した データ領域宣言は、初期値を与えずに フラッシュメモリ領域に配置される。
最終出力HEX/binファイルには含まれない。
読込アクセス方法はPROGMEM
属性に同じ。
char storage[] NVMEM;
自己フラッシュメモリ書換領域を事前予約および型宣言するために使用する。
当該機能を使用しない場合は単に無意味なスペースを費やすに過ぎない。
領域宣言に付加するとそれを(ボード選択に依存する)フラッシュメモリページ境界に合わせる。
PROGMEM
ROMEM
NVMEM
と同時に使用できる。
当該オブジェクト先頭はページ境界となり、次のオブジェクトとの間には強制的にパディングが入る。 つまり該当フラッシュメモリページ内には、ただひとつのオブジェクトだけが配置される。
char object1[33] PROGMEM PGM_ALIGN;
/* ここに隙間が空く */
char object2[33] PROGMEM PGM_ALIGN;
後続の指定セクションに属す領域宣言を(ボード選択に依存する)フラッシュメモリページ境界に合わせる。
セクション名は ".text"
".progmem"
".nvmem"
が有効。
PGM_ALIGN
との違いは直後のオブジェクトにだけ掛かるので、 領域宣言後方の境界整列に影響しない。 複数のオブジェクトを密に並べたい場合に使用する。
PAGE_ALIGN(".progmem");
char object1[33] PROGMEM;
/* ここに隙間が空かない */
char object2[33] PROGMEM;
多重参照されたマクロ内容を、マクロ名に変換する。変換後のマクロ名が未定義ならエラー。
#define USE_NMI NMI_vector_num
ISR(_vector(__DEPLOY(USE_NMI))) {...} /* NMI_vector と解釈される */
引数をダブルクォート文字列に書き換える。
IMPORT_BINFILE(".progmem", FooBin, __QUOTE(SKETCH_PATH) "/" "foo.bin");
指定のバイナリファイルを指定セクションに取り込んで、指定のシンボルを名付ける。
ファイルパスは絶対パス文字列でなければならない。
<指定シンボル>
が先頭番地を、<指定シンボル>_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サンプル]を参照のこと。
指定のバイナリファイルを指定セクションに取り込んで、指定のシンボルを名付ける。
ファイルパスは絶対パス文字列でなければならない。
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サンプル]を参照のこと。
計時器周辺機能を使わない busy-wait 遅延ループ。 故に割込禁止中でも動作するが、割込禁止でない場合は他の割込の影響を受ける。 時間精度はコンパイル時に決定される静的な F_CPU マクロ定数に依存する。
このヘッダが有効ならENABLE_DELAY_BUSYWAIT
マクロが1に定義される
AVR-GCC固有ビルトイン関数
__builtin_avr_delay_cycles
で実装されている。
マイクロ秒単位指定。 引数はコンパイル時に決定される静的な定数でなければならず、変数での可変サイクル数は支援されない。 0では何もしない。
delay_micros(1000);
- F_CPU=32MHzでの指定上限は 134217727L(134.2秒)
MacroAPI
有効化時はdelayMicroseconds
の別名でも使える。
ミリ秒単位指定。 引数はコンパイル時に決定される静的な定数でなければならず、変数での可変サイクル数は支援されない。 0では何もしない。
delay_millis(1000);
- F_CPU=32MHzでの指定上限は 134217L(134.2秒)
MacroAPI
有効化時はdelay
の別名でも使える。
アセンブラで書かれた特殊な busy-wait 遅延ループ。引数は指定幅の動的変数でも良い。 "CPUサイクル数×指定値+α" の遅延を生成する。
uint8_t w = 20;
_busy_loop_3_8(w);
これらは特殊なタイミング調整用途で、普通には使用されない。reduceAVR にも対応する。
外部ピン入出力操作のためのマクロ宣言。
ENABLE_MACRO_API
が有効であれば別名も使用できる。
このヘッダが有効ならENABLE_MACRO_DIGITAL
マクロが1に定義される
依存性: <variant.h>
-
STATE
:=INPUT
|OUTPUT
|INPUT_PULLUP
-
STATE
:=LOW
|HIGH
|TOGGLE
- Return: (boolean) 0 or not 0
-
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
はオープンドレインでの外部端子操作を行う。
使用に際してはまず
pinModeMacro
でINPUT
または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 では
digitalWriteMacro
でTOGGLE
は機能しない。
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
でデジタル入力を許可しなければならない。
PIN_NAME に対応するPORTx.PINnCTRL
レジスタを指すショートカット。
右辺にも左辺にも書ける。
pinControlRegister(PIN_PA7) = PORT_PULLUPEN_bm;
/* pinModeMacro(PIN_PA7, INPUT_PULLUP) あるいは */
/* PORTA_PIN7CTRL = PORT_PULLUPEN_bm に同等 */
reduceAVR でこれは機能しない。
PIN_NAME に対応するPINn_bp
値を返す。
portRegister(PIN_PA4).OUT &= ~_BV( pinPosition(PIN_PA4) );
/* digitalWriteMacro(PIN_PA4, LOW) に同等 */
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
の展開補助が必要な場合があるので適宜対応のこと。
PIN_NAME に対応するPORTx
群レジスタを指す別名。
レジスタ名を添えることで右辺にも左辺にも書ける。
portRegister(PIN_PA4).OUT &= ~_BV( pinPosition(PIN_PA4) );
/* digitalWriteMacro(PIN_PA4, LOW) に同等 */
reduceAVR でこれは機能しない。
Twitter(X): @askn37
BlueSky Social: @multix.jp
GitHub: https://github.com/askn37/
Product: https://askn37.github.io/
Copyright (c) 2022,2023 askn (K.Sato) multix.jp
Released under the MIT license
https://opensource.org/licenses/mit-license.php
https://www.oshwa.org/
multix.jp/てくにかるむ(休眠中)
Multix Zinnia Product SDK [*AVR]
AVR.JP(日本語訳)
AVR-LIBC(日本語訳)