avr-libc 中的 ATOMIC_BLOCK

作成者:カランカラン
💡

質問やフィードバックがありましたら、フォームからお願いします

目次

    本文は台湾華語で、ChatGPT で翻訳している記事なので、不確かな部分や間違いがあるかもしれません。ご了承ください

    avr-libcには、<util/atomic.h>というものがあります。最初の反応は、AVRは単一コアなのになぜatomicが必要なのかということですので、説明を見てみました。

    このヘッダーファイルのマクロは、アトミックまたは非アトミックに実行されることが保証されたコードブロックに対応しています。この文脈での「アトミック」という用語は、対応するコードが割り込みを受けることができないことを指します。

    マイクロコントローラでも、さまざまな割り込みを使用することができるため、プログラムの実行は途中で中断される可能性があります。atomicを使用することで、その中に含まれるコードセグメントが割り込みに影響を受けないように保証することができます(一時的に割り込みを停止します)。

    ATOMIC_BLOCK(ATOMIC_FORCEON) {
      // 重要な処理を行う
    }

    直感的には、cli()sei()の組み合わせに似ていますが、ドキュメントの説明によると、さらに多くのことが行われているようです。内部の実装は次のようになっています:

    #define ATOMIC_BLOCK(type) for ( type, __ToDo = __iCliRetVal(); \
                               __ToDo ; __ToDo = 0 )

    typeを引数として受け取ることができ、ATOMIC_RESTORESTATEATOMIC_FORCEONの2つの引数を渡すことができます。

    #define ATOMIC_RESTORESTATE uint8_t sreg_save \
        __attribute__((__cleanup__(__iRestore))) = SREG

    これはseiの効果に似ていますが、より興味深いのは__attribute__の使用です。GCCでは、変数や関数がどのように保存されるべきかを決定するために__attribute__を使用することができます。これはlibcの実装で定義されている動作であり、AVRの場合は次のように書くことができます:

    #include <avr/pgmspace.h>
    const int my_var[2] PROGMEM = { 1, 2 };

    ここでのPROGMEMは実際には__attribute__です:

    #ifndef __ATTR_PROGMEM__
    #define __ATTR_PROGMEM__ __attribute__((__progmem__))
    #endif

    通常、変数はメモリに格納されますが、マイクロコントローラではメモリが非常に制限されています。ただし、一般的なコンピュータとは異なり、通常、マイクロコントローラのコードは事前に書かれ、コンパイル後にプログラムメモリ(フラッシュメモリ)に直接書き込まれます。プログラムコードが少ない場合、フラッシュメモリに余分なスペースが残ることがあり、その場合はPROGMEMを使用して変数をフラッシュメモリに格納し、メモリ使用量を減らすことができます。読み取り時には、pgm_read_wordを使用して変数を読み取ることができます。

    ATOMIC_BLOCKに戻ると、展開すると次のようなコードになります:

    for (uint8_t sreg_save __attribute__((__cleanup__(__iRestore))) = 0;  __ToDo = __iCliRetVal(); __ToDo ; __ToDo = 0) {
       // 重要な処理を行う
    }

    このようにして、割り込みの影響を受けずにコードの実行が保証されます。forループを使用するこのテクニックは、驚くべきものであり、このように応用することは全く考えていませんでした。

    この記事が役に立ったと思ったら、下のリンクからコーヒーを奢ってくれると嬉しいです ☕ 私の普通の一日が輝かしいものになります ✨

    Buy me a coffee