Kalan's Blog

Software Engineer / Taiwanese / Life in Fukuoka

Current Theme light

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

作者

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

愷開 | Kalan

Hi, I'm Kai. I'm Taiwanese and moved to Japan in 2019 for work. Currently settled in Fukuoka. In addition to being familiar with frontend development, I also have experience in IoT, app development, backend, and electronics. Recently, I started playing electric guitar! Feel free to contact me via email for consultations or collaborations or music! I hope to connect with more people through this blog.