
本期話題
STM32 單片機的SRAM有兩個區支持位帶(bit-band)操作。那么,什么是位帶,位帶操作的原理是怎樣的呢?
今天來梳理一下這個知識點。
聊一聊

CM3 使用如下術語來表示位帶存儲的相關地址。
位帶區:支持位帶操作的地址區
位帶別名:對別名地址的訪問最終會變換成對位帶區的訪問(注意:有一個地址映射過程)
Cortex-M3 存儲器映像包括兩個位段(bit-band)區。這兩個位段區將別名存儲器區中的每個字映射到位段存儲器區的一個位,在別名存儲區寫入一個字具有對位段區的目標位執行讀-改-寫操作的相同效果。
兩個位帶區分別為:
第一個是SRAM 區的最低 1MB 范圍,地址范圍為 0x2000_0000-0x200F_FFFF
第二個是片內外設區的最低 1MB 范圍,地址范圍 0x4000_0000-0x400F_FFFF
這兩個位帶中的地址除了可以像普通的 RAM 一樣使用外,它們還都有自己的“位帶別名區”,位帶別名區把每個比特膨脹成一個 32 位的字。當你通過位帶別名區訪問這些字時,就可以達到訪問原始比特的目的。
位帶區與位帶別名區的膨脹對應關系如下圖

在位帶區中,每個比特都映射到別名地址區的一個字,這是個只有 LSB 才有效的字。當一個別名地址被訪問時,會先把該地址變換成位帶地址。
在 STM32F10xxx 里,外設寄存器 和 SRAM 都被映射到一個位段區里,這允許執行單一的位段的寫和讀操作。
下面的映射公式給出了別名區中的每個字是如何對應位帶區的相應位的:
bit_word_addr = bit_band_base + (byte_offset × 32) + (bit_number × 4)其中:
bit_word_addr 是別名存儲器區中字的地址,它映射到某個目標位。
bit_band_base 是別名區的起始地址。
byte_offset 是包含目標位的字節在位段里的序號
bit_number是目標位所在位置(0-31)
舉個例子,如何映射別名區中 SRAM 地址為 0x20000300 的字節中的位 2:
0x22006008 = 0x22000000 + (0x300 × 32) + (2 × 4)對 0x22006008 地址的寫操作與對 SRAM 中地址 0x20000300 字節的位 2 執行讀-改-寫操作有著相同的效果。
位帶操作的優越性
有了位帶操作以后,對于讀/寫位帶別名區就可以完成以上操作。
位帶操作的優越性,最容易想到的就是通過 GPIO 的管腳來單獨控制每盞 LED 的點亮與熄滅,也對操作串行接口器件提供了很大的方便。
位帶操作對于硬件 I/O 密集型的底層程序最有用處了。對于大范圍使用位標志的系統程序來說,位帶機制也是一大福音。
位帶操作還能用來化簡跳轉的判斷。當跳轉依據是某個位時,以前必須這樣做:
位帶操作還有一個重要的好處是,在多任務中,用于實現共享資源在任務間的“互鎖”訪問。多任務的共享資源必須滿足一次只有一個任務訪問它,亦即所謂的“原子操作”。
多任務的共享資源必須滿足一次只有一個任務訪問它(所謂的“原子操作”)。
以前的讀-改-寫需要 3 條指令,導致這中間留有兩個空當能被中斷,于是可能會出現如下圖所示的紊亂危象:

C語言操作位帶
在 C 編譯器中并沒有直接支持位帶操作。比如, C 編譯器并不知道同一塊內存能夠使用不同的地址來訪問,也不知道對位帶別名區的訪問只對 LSB 有效。
欲在 C 中使用位帶操作,最簡單的做法就是#define 一個位帶別名區的地址。
#define DEVICE_REG0 ? ? ? ((volatile unsigned long *) (0x40000000))
#define DEVICE_REG0_BIT0 ((volatile unsigned long *) (0x42000000))
#define DEVICE_REG0_BIT1 ((volatile unsigned long *) (0x42000004))我們可以建立一個把“位帶地址+位序號”轉換成別名地址的宏,再建立一個把別名地址轉換成指針類型的宏:
//把“位帶地址+位序號”轉換成別名地址的宏
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr & 0xFFFFF)<<5)+(bitnum<<2))
//把該地址轉換成一個指針
#define MEM_ADDR(addr) *((volatile unsigned long *) (adr))
當使用位帶功能時,要訪問的變量必須用 volatile 來定義。因為 C 編譯器并不知道同一個比特可以有兩個地址。
所以就要通過 volatile,使得編譯器每次都如實地把新數值寫入存儲器,而不再會出于優化的考慮,在中途使用寄存器來操作數據的復本,直到最后才把復本寫回——這會導致按不同的方式訪問同一個位會得到不一致的結果。
最后
在 STM32 單片機實際開發中,一般會使用標準庫或者HAL庫,我們直接使用庫提供的操作API即可完成相應的工作,也就是庫幫我們完成了位帶轉換。
我們掌握了位帶操作的原理,知其然、知其所以然,假如遇到相關問題,可以從原理上分析、并解決問題。
好了,感謝閱讀,加油~


覺得文章不錯,點擊“分享”、“贊”、“在看” 唄!