Kalan's Blog

Kalan 頭像照片,在淡水拍攝,淺藍背景

四零二曜日電子報上線啦!訂閱訂起來

Software Engineer / Taiwanese / Life in Fukuoka
This blog supports RSS feed (all content), you can click RSS icon or setup through third-party service. If there are special styles such as code syntax in the technical article, it is still recommended to browse to the original website for the best experience.

Current Theme light

我會把一些不成文的筆記或是最近的生活雜感放在短筆記,如果有興趣的話可以來看看唷!

Please notice that currenly most of posts are translated by AI automatically and might contain lots of confusion. I'll gradually translate the post ASAP

ATOMIC_BLOCK in avr-libc

avr-libc contains <util/atomic.h>. My initial reaction was that AVR is a single-core architecture, so why would we need atomic operations? Therefore, I instinctively looked at the documentation.

The macros in this header file deal with code blocks that are guaranteed to be executed atomically or non-atomically. The term "atomic" in this context refers to the inability of the respective code to be interrupted.

Even in microcontrollers, there are various interrupts that can be used, so the execution of the code may be interrupted. By using atomic operations, we can ensure that the code block enclosed within it is not affected by interrupts (temporarily disabling interrupts).

ATOMIC_BLOCK(ATOMIC_FORCEON) {
  // do something critical
}

At first glance, it seems similar to the combination of cli() and sei(), but according to the documentation, it does more. The internal implementation is as follows:

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

It can accept type as a parameter, and the possible values for the parameter are ATOMIC_RESTORESTATE and ATOMIC_FORCEON.

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

It seems similar to the effect of sei(), but what's interesting is the use of __attribute__. In GCC, you can use __attribute__ to determine how variables or functions should be stored. This behavior is defined in the implementation of libc. For example, in AVR, you can write:

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

Here, PROGMEM is actually an __attribute__:

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

Usually, variables are stored in memory. However, in microcontrollers, memory is often limited. But unlike regular computers, microcontroller code is typically pre-written and directly burned into program memory (flash memory). If the code is not too large, there may be more available space in flash memory. In such cases, variables can be stored in flash memory using PROGMEM, thereby reducing the usage of RAM. To read such variables, you can use pgm_read_word.

Returning to ATOMIC_BLOCK, when we expand it completely, the code would look like this:

for (uint8_t sreg_save __attribute__((__cleanup__(__iRestore))) = 0;  __ToDo = __iCliRetVal(); __ToDo ; __ToDo = 0) {
   // do something critical
}

This ensures that the execution of the code is not affected by interrupts. It's quite fascinating to see the use of a for loop in this way. I never thought it could be applied like this.

Prev

Some tricks about reading files in node.js.

Next

Exploring the form and data retrieval mechanism of remix

If you found this article helpful, please consider buy me a drink ☕️ It'll make my ordinary day shine✨

Buy me a coffee