avr-libc 當中的ATOMIC_BLOCK
# 開發筆記avr-libc 當中有個 <util/atomic.h>,第一個反應是明明 avr 都是單核才對,為什麼還需要 atomic,所以下意識看了一下說明。
The macros in this header file deal with code blocks that are guaranteed to be excuted Atomically or Non-Atmomically. The term “Atomic” in this context refers to the unability of the respective code to be interrupted.
就算是在微控制器,也是有各種 interrupt 可以使用,因此程式碼的執行可能中途被打斷,透過 atomic 可以保證包在裡面的程式碼片段不會被 interrupt 影響(暫時中止 interrupt)。
ATOMIC_BLOCK(ATOMIC_FORCEON) {
// do something critical
}
體感上來看很像 cli() 跟 sei() 的組合,但根據文件上的說明似乎還有做更多事情。內部的實作是這樣子:
#define ATOMIC_BLOCK(type) for ( type, __ToDo = __iCliRetVal(); \
__ToDo ; __ToDo = 0 )
可以接收 type 當作參數,可以傳入的參數有 ATOMIC_RESTORESTATE 跟 ATOMIC_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
一般來說變數會存在記憶體裡頭,然而在微控制器當中記憶體往往少得可憐。不過跟一般電腦不太一樣的是,通常微控制器的程式碼是預先寫好編譯後直接燒入 program memory(flash memory),如果程式碼不多的話, flash memory 反而會剩下比較多空間,這時候就可以透過 PROGMEM 讓變數存在 flash memory 裡頭,進而減少記憶體的使用量。在讀取時可以用 pgm_read_word 來讀取變數。
回到 ATMOIC_BLOCK,我們把它全部展開後程式碼會像這樣子:
for (uint8_t sreg_save __attribute__((__cleanup__(__iRestore))) = 0; __ToDo = __iCliRetVal(); __ToDo ; __ToDo = 0) {
// do something critical
}
來確保程式碼的執行必定不會受到 interrupt 影響。用 for loop 這招感覺挺神奇的,完全沒有想過可以這樣應用。
相關文章
- 別再用 AWS Access Key 了Access Key 是 AWS 上容易被忽略的安全風險。用 OIDC 搭配 IAM Role,讓 GitHub Actions 不需要任何 secret 就能安全操作 AWS 資源
- 資料庫主鍵:AUTO_INCREMENT、UUID 與 UUIDv7後端開發常需要決定主鍵,要用 auto increment 還是 UUID?碰撞怎麼辦?UUIDv7 跟 created_at + index 的效能差多少?實際跑了 2000 萬筆資料與設計決策告訴你
- Zeabur 使用心得分享一般獨立開發者要部署服務時都會選擇 Vercel 之類的平台,但有時候需要更進階的需求如資料庫連接時,Vercel 就沒那麼方便,而一般雲端服務商的價格對獨立開發來說也很貴,這篇文章分享了一些使用 Zeabur 的心得,推薦給大家!
- 鍵盤入坑指南 - 韌體篇本篇為 IT 2023 鐵人賽文章:鍵盤入坑指南 - 韌體篇