????支持了位帶操作后桂肌,可以使用普通的加載/存儲(chǔ)指令來對單一的比特進(jìn)行讀寫。在 CM3 中佩耳,有兩個(gè)區(qū)中實(shí)現(xiàn)了位帶谭跨。其中一個(gè)是 SRAM 區(qū)的最低 1MB 范圍螃宙,第二個(gè)則是片內(nèi)外設(shè)區(qū)的最低 1MB范圍。這兩個(gè)區(qū)中的地址除了可以像普通的 RAM 一樣使用外挂捅,它們還都有自己的“位帶別名區(qū)”堂湖,位帶別名區(qū)把每個(gè)比特膨脹成一個(gè) 32 位的字。當(dāng)你通過位帶別名區(qū)訪問這些字時(shí)伺糠,就可以達(dá)到訪問原始比特的目的训桶。
????位帶操作的概念其實(shí) 30 年前就有了渊迁,那還是8051單片機(jī)開創(chuàng)的先河,如今琉朽,CM3 將此能力進(jìn)化箱叁,這里的位帶操作是 8051 位尋址區(qū)的威力大幅加強(qiáng)版。
????CM3 使用如下術(shù)語來表示位帶存儲(chǔ)的相關(guān)地址:
????????位帶區(qū)??? 支持位帶操作的地址區(qū)
????????位帶別名? 對別名地址的訪問最終作用到位帶區(qū)的訪問上(這中途有一個(gè)地址映射過程)
????在位帶區(qū)中耕漱,每個(gè)比特都映射到別名地址區(qū)的一個(gè)字——這是只有 LSB 有效的字螟够。當(dāng)一個(gè)別名地址被訪問時(shí),會(huì)先把該地址變換成位帶地址若河。對于讀操作寞宫,讀取位帶地址中的一個(gè)字,再把需要的位右移到 LSB辈赋,并把 LSB 返回。對于寫操作悟民,把需要寫的位左移至對應(yīng)的位序號處逾雄,然后執(zhí)行一個(gè)原子的“讀-改-寫”過程鸦泳。
????支持位帶操作的兩個(gè)內(nèi)存區(qū)的范圍是:
????????0x2000_0000‐0x200F_FFFF(SRAM 區(qū)中的最低 1MB)
? ? ? ? 0x4000_0000‐0x400F_FFFF(片上外設(shè)區(qū)中的最低1MB)
????對 SRAM 位帶區(qū)的某個(gè)比特做鹰,記它所在字節(jié)地址為 A,位序號為 n(0<=n<=7)钾麸,則該比特在別名區(qū)的地址為:
????????AliasAddr=0x22000000+((A-0x20000000)*8+n)*4=0x22000000+(A-0x20000000)*32+n*4
????對于片上外設(shè)位帶區(qū)的某個(gè)比特炕桨,記它所在字節(jié)的地址為 A,位序號為 n(0<=n<=7)献宫,則該比特在別名區(qū)的地址為:
????????AliasAddr=0x42000000+((A-0x40000000)*8+n)*4=0x42000000+(A-0x40000000)*32+n*4
????上式中姊途,“*4”表示一個(gè)字為 4 個(gè)字節(jié),“*8”表示一個(gè)字節(jié)中有 8 個(gè)比特捷兰。
????舉例:
????位帶操作的作用:
????????1立叛、GPIO的管腳單獨(dú)控制(常用,重要)
????????2贡茅、使用標(biāo)志位時(shí)簡化判斷(理解秘蛇,可以嘗試使用)
????????3其做、多任務(wù)中,用于實(shí)現(xiàn)共享資源在任務(wù)間的“互鎖”訪問(不懂)
????在C編譯器中并沒有直接支持位帶操作赁还,所以庶柿,C編譯器并不知道同一塊內(nèi)存能夠使用不同的地址來訪問,也不知道對位帶別名區(qū)的訪問只對LSB有效秽浇。欲在C中使用位帶操作,最簡單的做法就是#define一個(gè)位帶別名區(qū)的地址甚负。
????以設(shè)置GPIOA柬焕,bit2為例理解位帶操作:
????GPIOA基址?? 0x4001 0800
????端口輸入數(shù)據(jù)寄存器? 0x4001 0800+0x8=0x4001 0808
????端口輸出數(shù)據(jù)寄存器? 0x4001 0800+0xC=0x4001 080C
????輸入寄存器位帶別名
? ????????Addr=0x4200 0000+((0x40010808-0x4000 0000)*32+2*4=0x4221 0108
????輸出寄存器位帶別名
? ????????Addr=0x4200 0000+((0x4001080C-0x4000 0000)*32+2*4=0x4221 0188
#define PA2in? ((volatileunsigned long *)(0x4221 0108))
#define PA2out ((volatile unsigned long *)(0x4221 0118))
*PA2out=1;??????????????? //PA2輸出1
????為簡化位帶操作斑举,將位帶別名計(jì)算定義成一個(gè)宏:
#define BITBAND(addr, bitnum) ((addr & 0xF0000000) + 0x2000000 +
((addr & 0xFFFFF) << 5)+(bitnum << 2))
????將該地址轉(zhuǎn)化成一個(gè)指針:
#define MEM_ADDR(addr)? *((volatileunsigned long? *)(addr))
????實(shí)現(xiàn)位帶操作的宏:
#define BIT_ADDR(addr, bitnum)??MEM_ADDR(BITBAND(addr, bitnum))
????I/O口寄存器地址映射:
#define GPIOA_ODR_Addr???(GPIOA_BASE+12) //0x4001080C,PA輸出寄存器
#define GPIOA_IDR_Addr??? ?(GPIOA_BASE+8)?//0x40010808赎懦,PA輸入寄存器
????對單一I/O的操作
#define PAout(n)??BIT_ADDR(GPIOA_ODR_Addr,n)? //輸出
#define PAin(n)??? ?BIT_ADDR(GPIOA_IDR_Addr,n)? //輸入
????定義I/O口
#define LED1 PAout(2)
????則實(shí)現(xiàn)點(diǎn)亮LED只需
????LED1=1;or LED1=0当悔;
????注意:當(dāng)使用位帶功能時(shí)盲憎,要訪問的變量必須用volatile來定義。因?yàn)镃編譯器并不知道同一個(gè)比特可以有兩個(gè)地址宏多。所以就要通過volatile,使得編譯器每次都如實(shí)地把新數(shù)值寫入存儲(chǔ)器更胖,而不再會(huì)出于優(yōu)化的考慮饵逐,在中途使用寄存器來操作數(shù)據(jù)的復(fù)本,直到最后才把復(fù)本寫回——這會(huì)導(dǎo)致按不同的方式訪問同一個(gè)位會(huì)得到不一致的結(jié)果捞烟。