Cortex?-M3存儲器映像包括兩個位段(bit-band)區。這兩個位段區將別名存儲器區中的每個字映射到位段存儲器區的一個位,在別名存儲區寫入一個字具有對位段區的目標位執行讀-改-寫操作的相同效果。
在STM32F10xxx里,外設寄存器和SRAM都被映射到一個位段區里,這允許執行單一的位段的寫和讀操作。
下面的映射公式給出了別名區中的每個字是如何對應位帶區的相應位的:
bit_word_addr = bit_band_base + (byte_offset x 32) + (bit_number × 4)
其中:
bit_word_addr是別名存儲器區中字的地址,它映射到某個目標位。
bit_band_base是別名區的起始地址。
byte_offset是包含目標位的字節在位段里的序號
bit_number是目標位所在位置(0-31)
在 CM3 支持的位段中,有兩個區中實現了位段。
其中一個是 SRAM 區的最低 1MB 范圍, 0x20000000 ‐ 0x200FFFFF(SRAM 區中的最低 1MB);
第二個則是片內外設區的最低 1MB范圍, 0x40000000 ‐ 0x400FFFFF(片上外設區中的最低 1MB)。
在 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))
...
*DEVICE_REG0 = 0xab; //使用正常地址訪問寄存器
*DEVICE_REG0_BIT1 = 0x1;
還可以更簡化:
//把“位帶地址+位序號” 轉換成別名地址的宏
#define BITBAND(addr, bitnum)((addr & 0xF0000000)+0x2000000+((addr & 0xFFFFF)<<5)+(bitnum<<2))
//把該地址轉換成一個指針
#define MEM_ADDR(addr) *((volatile unsigned long *) (addr))
于是:
MEM_ADDR(DEVICE_REG0) = 0xAB; //使用正常地址訪問寄存器
MEM_ADDR(BITBAND(DEVICE_REG0,1)) = 0x1; //使用位段別名地址
注意:當你使用位段功能時,要訪問的變量必須用 volatile 來定義。因為 C 編譯器并不知道同一個比特可以有兩個地址。所以就要通過 volatile,使得編譯器每次都如實地把新數值寫入存儲器,而不再會出于優化的考慮 ,在中途使用寄存器來操作數據的復本,直到最后才把復本寫回。
實際上,在寫程序是都有這樣的定義:
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
然后定義:#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
最后操作:#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //輸出 ODR保存要輸出的數據;IDR保存讀入的數據 |