3.gpio位帶實現
//在stm32中,如果我們要使pb0埠輸出低電平,可以用如下語句
gpiob->odr |=0
<<0;
//在51微控制器中我們是這樣的
p0 =
0xff
;//匯流排操作
//還有一種方法
sbit led1 = p0^0;
led1 =0;
//位操作
在stm32中能不能實現位操作呢,當然是可以的,這種方法我們叫做位帶操作。位操作就是可以單獨的對乙個位元位讀和寫。
在stm32中,有兩個地方實現了位帶,乙個是 sram 區的最低 1mb 空間,另乙個是外設區最低 1mb 空間。這兩個 1mb 的空間除了可以像正常的 ram 一樣操作外,他們還有自己的位帶別名區,位帶別名區把這 1mb 的空間的每乙個位膨脹成乙個 32 位的字,當訪問位帶別名區的這些字時,就可以達到訪問位帶區某個位元位的目的。
例子:pb的odr暫存器位址為0x40010c0c,假設我們就操作pb0,即操作odr暫存器第0位,那麼這個位新的位址addr= =0x42000000+ (0x40010c0c-0x40000000)84 +0*4。
給這個位址命名為pbout(0);
aliasaddr= =0x42000000+ (a-0x40000000)84 +n*4
0x42000000 是外設位帶別名區的起始位址, 0x40000000 是外設位帶區的起始位址,(a-0x40000000)表示該位元前面有多少個位元組,乙個位元組有 8 位,所以8,乙個位膨脹後是 4 個位元組,所以4, n 表示該位元在 a 位址的序號,因為乙個位經膨脹後是四個位元組,所以也*4。
aliasaddr= =0x22000000+ (a-0x20000000)84 +n*4
公式分析同上
為了方便操作,我們可以把這兩個公式合併成乙個公式,把「位帶位址+位序號」轉
換成別名區位址統一成乙個巨集。
// 把「位帶位址+位序號」轉換成別名位址的巨集
2 #define bitband
(addr, bitnum)
((addr &
0xf0000000)+
0x02000000+(
(addr &
0x00ffffff
)<<5)
+(bitnum<<2)
)
addr & 0xf0000000 是為了區別 sram 還是外設,實際效果就是取出 4 或者 2,如果是外設,則取出的是 4, +0x02000000 之後就等於 0x42000000, 0x42000000 是外設別名區的起始位址。如果是 sram,則取出的是 2, +0x02000000 之後就等於 0x22000000,0x22000000 是 sram 別名區的起始位址。
addr & 0x00ffffff 遮蔽了高三位,相當於減去 0x20000000 或者 0x40000000,但是為什麼是遮蔽高三位?因為外設的最高位址是: 0x2010 0000, 跟起始位址 0x20000000 相減的時候,總是低 5 位才有效,所以乾脆就把高三位遮蔽掉來達到減去起始位址的效果,具體遮蔽掉多少位跟最高位址有關。 sram 同理分析即可。 <<5 相當於84, <<2 相當於*4,這兩個我們在上面分析過。
最後我們就可以通過指標的形式操作這些位帶別名區位址,最終實現位帶區的位元位操作。
// 把乙個位址轉換成乙個指標
#define mem_addr(addr) *((volatile unsigned long *)(addr))
// 把位帶別名區位址轉換成指標
#define bit_addr(addr, bitnum) mem_addr(bitband(addr, bitnum))
// 把「位帶位址+位序號」轉換成別名位址的巨集
#define bitband(addr, bitnum) ((addr & 0xf0000000)+0x02000000+((addr & 0x00ffffff)<<5)+(bitnum<<2))
// 把乙個位址轉換成乙個指標
#define mem_addr(addr) *((unsigned long *)(addr))
// 把位帶別名區位址轉換成指標
#define bit_addr(addr, bitnum) mem_addr(bitband(addr, bitnum))
//gpio odr 和 idr 暫存器位址對映
#define gpioa_odr_addr (gpioa_base+12)
//0x4001080c
#define gpiob_odr_addr (gpiob_base+12)
//0x40010c0c
#define gpioc_odr_addr (gpioc_base+12)
//0x4001100c
#define gpiod_odr_addr (gpiod_base+12)
//0x4001140c
#define gpioa_idr_addr (gpioa_base+8)
//0x40010808
#define gpiob_idr_addr (gpiob_base+8)
//0x40010c08
#define gpioc_idr_addr (gpioc_base+8)
//0x40011008
#define gpiod_idr_addr (gpiod_base+8)
//0x40011408
//單獨操作 gpio 的某乙個 io 口, n(0,1,2...16),n 表示具體是哪乙個 io 口
#define paout(n) bit_addr(gpioa_odr_addr,n)
//輸出
#define pain(n) bit_addr(gpioa_idr_addr,n)
//輸入
#define pbout(n) bit_addr(gpiob_odr_addr,n)
//輸出
#define pbin(n) bit_addr(gpiob_idr_addr,n)
//輸入
#define pcout(n) bit_addr(gpioc_odr_addr,n)
//輸出
#define pcin(n) bit_addr(gpioc_idr_addr,n)
//輸入
#define pdout(n) bit_addr(gpiod_odr_addr,n)
//輸出
#define pdin(n) bit_addr(gpiod_idr_addr,n)
//輸入
這樣就可以使用pbout(0),相比之下還是很方便的。 stm32之位帶操作
stm32相對於8位微控制cpu來說實在強大的不得了,依稀記得51控制i o空的時候是 sbit led1 p0 0 然而我們在32卻沒有想 sbit 類似的關鍵字進行i o的某位進行操作。於是引入了 位帶操作的概念 什麼事位帶操作?標準的定義是 通過訪問位帶別名區來實現,即通過將每個位元位膨脹成乙...
STM32筆記(四)位帶操作介紹
位帶操作就是對可以單一的位元bit進行讀寫,在51微控制器中可以用關鍵字sbit來實現位定義,在stm32微控制器中就沒有這樣的關鍵字,取而代之的是通過訪問位帶別名區來實現位帶操作的。在 stm32 中,有兩個地方實現了位帶,乙個是sram 區的最低1mb範圍,另乙個是片內外設 區的最低 1mb 範...
STM32 Contex M的位帶操作
位帶操作的思想在30年前就已經有了,還是8051開創的先河。如今,contex m3將此能力進化,這裡的位帶操作位定址區威力大幅度增強。有兩個區中實現了位帶。其中乙個是 sram 區的最低 1mb 範圍,第二個則是片內外設區的最低 1mb 範圍。這兩個區中的位址除了可以像普通的 ram 一樣使用外,...