1. 程式人生 > >STM32的位段操作

STM32的位段操作

第一篇文章寫位段操作。

位操作就是可以讀/寫單獨的一個位元位,在STM32中沒有像51微控制器的sbit來實行位定義,但是它可以通過位帶別名區來實現。  在STM32中有兩個地方實現了位帶操作,一個是SRAM區的最低1MB空間,另一個是外設區最低1MB空間。

0x2000 0000 ~ 0x200f ffff (SRAM區中的最低1MB) 0x4000 0000 ~ 0x400f ffff (片上外設區中的最低1MB,已覆蓋了全部的片上外設的暫存器) 1 2 這兩個1MB的空間可以像普通RAM一樣操作外(修改內容時用讀-改-寫),它們還有自己的位帶別名區,位帶別名區把這1MB的空間的每一位膨脹為一個32位的字。確切的說,這個字就是一個地址,當操作這個地址時,就可以達到操作這個位帶區某個位的目的。  在位帶區中,每個位元位都對映到別名地址區的一個地址,注意,這只是只有LSB有效的字(最低一位有效的字)。當一個別名地址被訪問時,會把該地址轉換為為位帶操作。

對片上外設位帶區的某個位元位,記它的所在位元組的地址為A,位序號為n(0<=n<=7),則該位元位在別名區的地址為:

AliasAddr = 0x42000000 + ((A - 0x40000000) * 8 + n) * 4           = 0x42000000 + (A - 0x40000000) * 32 + n * 4 1 2 上式中,4表示一個字4個位元組,8表示一個位元組8個位元。

一開始,我對n(0<=n<=7)很不理解,既然n表示位序號,為什麼不是0<=n<=31呢?其實是我忽略了“所在位元組”四個字,也就是說在位帶區中,不是以一個暫存器一個暫存器為分隔單元,而是以一個位元組一個位元組來分隔單元的。    (1) A - 0x40000000 = 當前位元組偏離外設基地址的偏移位元組數  (2) 偏移位元組數 * 8 = 偏移了多少位  (3) 因為位帶區每一位對應位帶別名區的一個地址(4位元組),而地址是以位元組計算的,所以位帶別名區對應偏移量最後一個的地址 = 偏移了多少位 * 4  (4) n * 4 = 偏移量後面的n位對應位帶別名區的地址

計算如下 位帶區暫存器地址:0x40000000 0: 0x42000000 + ((0x40000000 - 0x40000000) * 8 + 0) * 4 = 0x42000000 + 0 = 42000000 1: 0x42000000 + ((0x40000000 - 0x40000000) * 8 + 1) * 4 = 0x42000000 + 0 = 42000004 2: 0x42000000 + ((0x40000000 - 0x40000000) * 8 + 2) * 4 = 0x42000000 + 0 = 42000008 3: 0x42000000 + ((0x40000000 - 0x40000000) * 8 + 3) * 4 = 0x42000000 + 0 = 4200000c 4: 0x42000000 + ((0x40000000 - 0x40000000) * 8 + 4) * 4 = 0x42000000 + 0 = 42000010 5: 0x42000000 + ((0x40000000 - 0x40000000) * 8 + 5) * 4 = 0x42000000 + 0 = 42000014 6: 0x42000000 + ((0x40000000 - 0x40000000) * 8 + 6) * 4 = 0x42000000 + 0 = 42000018 7: 0x42000000 + ((0x40000000 - 0x40000000) * 8 + 7) * 4 = 0x42000000 + 0 = 4200001c

位帶區暫存器地址:0x40000008 0: 0x42000000 + ((0x40000001 - 0x40000000) * 8 + 0) * 4 = 0x42000000 + 0x8 = 42000020 1: 0x42000000 + ((0x40000001 - 0x40000000) * 8 + 1) * 4 = 0x42000000 + 0x104 = 42000024 2: 0x42000000 + ((0x40000001 - 0x40000000) * 8 + 2) * 4 = 0x42000000 + 0x108 = 42000028 3: 0x42000000 + ((0x40000001 - 0x40000000) * 8 + 3) * 4 = 0x42000000 + 0x10c = 4200002c 4: 0x42000000 + ((0x40000001 - 0x40000000) * 8 + 4) * 4 = 0x42000000 + 0x110 = 42000030 5: 0x42000000 + ((0x40000001 - 0x40000000) * 8 + 5) * 4 = 0x42000000 + 0x114 = 42000034 6: 0x42000000 + ((0x40000001 - 0x40000000) * 8 + 6) * 4 = 0x42000000 + 0x118 = 42000038 7: 0x42000000 + ((0x40000001 - 0x40000000) * 8 + 7) * 4 = 0x42000000 + 0x11c = 4200003c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 如下圖: 

同理,對於SRAM位帶區的某個位元位,記它所在位元組地址為A,位序號為n(0<=n<=7),則該位元位在別名區的地址為:

AliasAddr = 0x22000000 + ((A - 0x20000000) * 8 + n) * 4            = 0x22000000 + (A - 0x20000000) * 32 + n * 4 1 2 只是對位帶基地址和位帶別名區基地址做了改變即可。

再舉個例子吧:  1) 往地址為0x40000001的位帶區寫入0x4466aadd (0b1000100011001101010101011011101)  2) 讀取地址為42000020的位帶別名區得1  讀取地址為42000024的位帶別名區得0  讀取地址為42000028的位帶別名區得1  3) 往地址為42000020的位帶別名區寫0, 往地址為42000024的位帶別名區寫1後,地址為0x40000001的位帶區的資料為0b1000100011001101010101011011110,即0x4466AADE。

為了方便操作,我們可以把這兩個公式合併成一個公式,把“位帶地址 + 位序號”轉換成別名地址。

//把位帶區地址 + 位序號轉換成位帶別名去的巨集 #define BITBAND(addr, bit_num) ((addr & 0xf0000000) + 0x02000000 + ((addr & 0x00ffffff) << 5) + (bit_num << 2)) 1 2 1) (addr & 0xf0000000) + 0x02000000: 區分SRAM還是外設,如果是外設,結果為4,再加0x2000000就等於0x4200000,0x42000000就是外設別名位帶區。  如果是SRAM,結果為2,再加上0x2000000就等於0x22000000,0x22000000就是SRAM別名位帶區。  2)addr & 0x00ffffff:遮蔽了最高2位,相當於減去0x20000000或者0x40000000。因為位帶區的有效範圍是1M,即0x100000,這樣子就做到了低6位有效。  3) << 5:等價於乘以32  4) << 2:等價於乘以4

運用該計算程式碼,位帶操作示例程式碼為:

//通過位帶區地址和位帶區的目標位,找到位帶別名區的地址 #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x02000000+((addr & 0x00FFFFFF)<<5)+(bitnum<<2))

// 把一個地址強制轉換成指標 #define MEM_ADDR(addr) *((volatile unsigned long *)(addr))

// 把位帶別名區地址轉換成指標 #define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))

//定義GPIOA的ODR暫存器 #define GPIOA_ODR_Addr (GPIOA_BASE + 12)

//設定GPIOA的引腳5為1,這是位操作 BIT_ADDR(GPIOA_ODR_Addr,5) = 1; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 位段操作,使得1MB的SRAM就有32MB的對應別名區空間,1位膨脹到32位,但效率更高,(在中斷的時候)具有更安全的作用。 ---------------------