avr-libc 中的 ATOMIC_BLOCK

作成者:カランカラン
💡

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

目次

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

    avr-libc の中には <util/atomic.h> というファイルがあります。最初の印象は、avr は単核であるべきなのに、なぜ atomic が必要なのかということです。そのため、無意識に説明を見てみました。

    このヘッダーファイルのマクロは、原子性を持つか持たないかが保証されたコードブロックを扱います。この文脈での「Atomic」という用語は、該当するコードが中断されることができないことを指します。

    マイクロコントローラーでも、さまざまな割り込みが利用できるため、コードの実行が途中で中断される可能性があります。atomic を使用することで、内部にあるコード片が割り込みの影響を受けないこと(割り込みを一時的に中止する)を保証できます。

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

    体感としては cli()sei() の組み合わせのように見えますが、ドキュメントの説明によると、さらなる処理が行われているようです。内部の実装は次のようになります。

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

    type をパラメータとして受け取ることができ、渡すことができるパラメータには ATOMIC_RESTORESTATEATOMIC_FORCEON があります。

    #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 loop を使用するこの方法は非常に興味深く、こんな使い方ができるとは思いもしませんでした。

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

    Buy me a coffee