標籤:

stm32中的「位帶操作」淺析

(既然是stm32的相關解析,則默認讀者已掌握C語言基礎)

stm32中,一個地址單元就是一個位元組,即一個地址為8bit。

stm32基於Cortex-M3內核,通俗地講,位帶操作就是把位帶區中一個地址的8個位分別映射到位帶別名區的8個地址(LSB有效,即最低位有效),通過操作相應地址的方式實現操作某個位,如圖1-1。其支持位帶操作的兩個內存區域為:

  • SRAM區:0x20000000 ~ 0x200FFFFF,最低1M範圍;
  • 片上外設區: 0x40000000 ~ 0x400FFFFF,最低1M範圍;

圖1-1 位帶區和位帶別名區的膨脹關係(圖片來自網路)

由圖1-1可知,位帶區里每個地址的每1位膨脹為別名區里一個32位的字(32位里,1字=4位元組),如:0x20000000的第0位對應0x22000000,第1位對應0x22000004等。前文提到,別名區地址的LSB有效,可理解為位帶區里每個位的狀態只與別名區里地址存儲的字的奇偶性有關,通過換算可知,奇數的LSB為1,偶數的LSB為0。

那麼,應該如何換算位帶區里每一位對應的別名區地址呢?有一個公式可用。

  • 別名區地址 = 別名區起始地址 + (位位元組地址偏移量 * 8 + n) * 4(以位元組為單位時,n∈[0,7];以字為單位時,n∈[0,31])

則兩個區的計算公式為:

  • SRAM區:別名區地址 = 0x22000000 + (A - 0x20000000)* 32 + n * 4

  • 片上外設區:別名區地址 = 0x42000000 + (A - 0x40000000)* 32 + n * 4

其中,

A:位帶區位元組地址(GPIOx_BASE+偏移地址);

n:操作位號;

「*32」:前文提到,位帶區每1位膨脹為別名區里一個32位的字,1字=4位元組=32bit;

「*4」:1字=4位元組

我們以GPIOA為例,GPIOA.0~GPIOA.7設置推挽輸出、最大速度50MHz(0011); GPIOA.8~GPIOA.15設置浮空輸入(0100):

GPIOA->CRL = 0x33333333;nGPIOA->CRH = 0x44444444; n

接下來,進行簡單的操作,使輸入狀態反應到輸出狀態,利用公式:

typedef unsigned long u32;nnu32 *PAO3 = (u32 *)(0x42000000 + (GPIOA_BASE+Ox0C - 0x40000000) * 32 + 3 * 4);//輸出nu32 *PAI11 = (u32 *)(0x42000000 + (GPIOA_BASE+Ox09- 0x40000000) * 32 + 3 * 4);//輸入n//u32 *PAI11 = (u32 *)(0x42000000 + (GPIOA_BASE+Ox08 - 0x40000000) * 32 + 11 * 4);//輸入nnif(*PAI11 == 1){n *PAO3 = 1;n}else{n *PAO3 = 0;n}n

通過查手冊可知,GPIOA_ODR地址偏移為0Ch(h表示十六進位),GPIOA_IDR地址偏移為0x08,則可得出各自的偏移量。由於GPIOx_IDR佔用了兩個位元組的地址,第11位在第二個地址,所以地址偏移應為:

0x10808+1 = 0x10809

所以,代碼里的兩種方式都可以。

以上代碼得出了IDR寄存器在第11位的別名區地址和ODR寄存器在第3位的別名區地址,最後在debug時,通過操作輸入埠相應位,程序判斷輸入埠狀態值,設置LSB值為1或0(任意的奇數或偶數),進而操作輸出埠狀態,實現了如同51單片機里簡便的操作。如圖1-2:

圖1-2 程序debug截圖

看到這裡,應該對Cortex-M3里的位帶操作有了大概的了解了吧,別急,還沒結束,有不只一組GPIO埠,每組GPIO也都有不只一個引腳,這麼多的引腳難道要一個個照公式寫嗎,很明顯這不太現實。所以,我們使用宏定義的方式來簡化操作,做完這一步,則一勞永逸。

直接上代碼:

#define GPIOA_ODR (GPIOA_BASE + 0x0C) //位帶區位元組地址,即為公式中的「A」n#define GPIOA_IDR (GPIOA_BASE + 0x08)n...n#define GPIOE_ODR (GPIOE_BASE + 0x0C)n#define GPIOE_IDR (GPIOE_BASE + 0x08)nn#define BitBand(Addr, BitNum) *((volatile unsigned long *)((Addr & 0xF0000000) + 0x2000000 + ((Addr & 0xFFFFF) << 5) + (BitNum << 2))) //表達式n n#define PAout(n) BitBand(GPIOA_ODR, n) //輸出n#define PAin(n) BitBand(GPIOA_IDR, n) //輸入nnif(PAin(12) == 1){n PAout(4) = 1;n}else{n PAout(4) = 0;n}n

--------------------------------------------------------------------

文章首發於知乎,轉載請私信,並註明原文出處。


推薦閱讀:

CPU寄存器到底有多大?《深入理解計算機系統》說大概有幾百位元組,可是彙編課上卻說理論上有64kb

TAG:寄存器 |