筆者博客鏈接:蠟筆小新沒(méi)有博客
希望可以和志同道合的朋友多交流染突!
STM32中的位帶操作: 名字為位帶操作拒秘,實(shí)際上是對(duì)位的操作阔墩,位操作就是可以單獨(dú)的對(duì)一個(gè)比特位讀和寫(xiě)悼泌,這個(gè)在 51 單片機(jī)中非常常見(jiàn)。 51 單片機(jī)中通過(guò)關(guān)鍵字 sbit 來(lái)實(shí)現(xiàn)位定義涩赢, STM32 沒(méi)有這樣的關(guān)鍵字戈次,而是通過(guò)訪問(wèn)位帶別名區(qū)來(lái)實(shí)現(xiàn)。STM32 的全部寄存器都可以通過(guò)訪問(wèn)位帶別名區(qū)的方式來(lái)達(dá)到訪問(wèn)原始寄存器比特位的效果筒扒,這比 51 單片機(jī)強(qiáng)大很多怯邪。因?yàn)?51 單片機(jī)里面并不是所有的寄存器都是可以比特位操作,有些寄存器還是得字節(jié)操作花墩,比如 SBUF悬秉。
51單片機(jī)中的位操作:
51單片機(jī)中可以對(duì)寄存器實(shí)現(xiàn)單個(gè)位的操作,靠的就是關(guān)鍵字sbit
冰蘑,如
sbit led=P1^0; led=1;
就可實(shí)現(xiàn)對(duì)P1.0位置1的效果和泌。
為什么STM32不推崇直接進(jìn)行位操作?
本人認(rèn)為STM32是32位MCU祠肥,一次處理32位數(shù)據(jù)武氓,所以一次只處理一位的數(shù)據(jù)未必大材小用了,除非特殊情況仇箱,否則都以32位處理县恕。
如何處理STM32中要對(duì)某一位進(jìn)行操作時(shí)的情況?
要知道STM32中采用庫(kù)函數(shù)編程工碾,所以有很多的對(duì)位操作的任務(wù)都用具體的函數(shù)來(lái)完成,而這些函數(shù)都已經(jīng)做好了我們只需要知道怎么用就行百姓。但我們?nèi)匀豢梢宰约簩?shí)現(xiàn)位操作渊额,這種神操作就是位帶操作 。
位帶區(qū)與位帶區(qū)別名:
在 STM32 中,有兩個(gè)地方實(shí)現(xiàn)了位帶旬迹,一個(gè)是 SRAM 區(qū)的最低 1MB 空間火惊,令一個(gè)是外設(shè)區(qū)最低 1MB 空間。這兩個(gè) 1MB 的空間除了可以像正常的 RAM 一樣操作外奔垦,他們還有自己的位帶別名區(qū)屹耐,位帶別名區(qū)把這 1MB 的空間的==每一個(gè)位膨脹成32 位 (要知道 STM32 的系統(tǒng)總線是 32 位的,按照 4 個(gè)字節(jié)訪問(wèn)的時(shí)候是最快的椿猎,所以膨脹成 4 個(gè)字節(jié)來(lái)訪問(wèn)是最高效的惶岭。)==,當(dāng)訪問(wèn)位帶別名區(qū)的這些字時(shí)犯眠,就可以達(dá)到訪問(wèn)位帶區(qū)某個(gè)比特位的目的按灶。
位帶區(qū)就是就是可以進(jìn)行位帶操作的寄存器的映射地址。
位帶區(qū)別名可以理解為將位帶區(qū)每一個(gè)位都膨脹32倍(用一個(gè)字節(jié)代表一個(gè)位筐咧,以便于32位MCU操作)后的地址鸯旁。
如何實(shí)現(xiàn)位帶操作?
要進(jìn)行位帶操作需要知道被操作的位的地址量蕊,因?yàn)镾RAM和外設(shè)中都可以位帶操作铺罢,所以形式上可以將位帶操作歸納為倆個(gè)公式。
對(duì)于位帶區(qū)的某個(gè)比特残炮,記它所在字節(jié)的地址為 A,位序號(hào)為 n(0<=n<=7)韭赘,則其在位帶區(qū)別名地址為:
外設(shè):AliasAddr= =0x42000000+ (A-0x40000000)*8*4 +n*4
SRAM:AliasAddr= =0x22000000+ (A-0x20000000)*8*4 +n*4
==用外設(shè)解釋公式:0X42000000 是外設(shè)位帶別名區(qū)的起始地址, 0x40000000 是外設(shè)位帶區(qū)的起始地址吉殃,(A-0x40000000)表示該比特前面有多少個(gè)字節(jié)辞居,一個(gè)字節(jié)有 8 位,所以8蛋勺,一個(gè)位膨脹后是 4 個(gè)字節(jié)瓦灶,所以4, n 表示該比特在 A 地址的序號(hào)抱完,因?yàn)橐粋€(gè)位經(jīng)過(guò)膨脹后是四個(gè)字節(jié)贼陶,所以也*4。==
當(dāng)然巧娱,也可以將倆個(gè)公式合二為一:
// 把“位帶地址+位序號(hào)”轉(zhuǎn)換成別名地址的宏
AliasAdd = (addr & 0xF0000000)+0x02000000+((addr &0x00FFFFFF)<<5)+(bitnum<<2)
知曉了位帶區(qū)別名的地址碉怔,然后將此地址轉(zhuǎn)換為指針類型就可以通過(guò)位帶操作對(duì)原始的為進(jìn)行操作。
附上野火的位帶操作代碼:
#include "stm32f10x.h"
#include "./led/bsp_led.h"
#define PCout(n) (*(unsigned int*)(((GPIOC_BASE+0x0c) & 0xF0000000)+0x02000000+(((GPIOC_BASE+0x0c) &0x000FFFFF)<<5)+(n<<2)))
#define PBout(n) (*(unsigned int*)(((GPIOB_BASE+0x0c) & 0xF0000000)+0x02000000+(((GPIOB_BASE+0x0c) &0x000FFFFF)<<5)+(n<<2)))
void delay(uint32_t count)
{
for(; count!=0; count--);
}
int main(void)
{
LED_GPIO_Config(); //LED初始化函數(shù)
while(1)
{
PCout(2) = 1;
//GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
delay(0xfffff);
PCout(2) = 0;
//GPIO_ResetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
delay(0xfffff);
PCout(3) = 1;
//GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);
delay(0xfffff);
PCout(3) = 0;
//GPIO_ResetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);
delay(0xfffff);
}
代碼簡(jiǎn)述:用宏定義的方法來(lái)操作GPIOC中的ODR寄存器和IDR寄存器中的某一位來(lái)實(shí)現(xiàn)led燈的亮滅
#define PCout(n) (*(unsigned int*)(((GPIOC_BASE+0x0c) & 0xF0000000)+0x02000000+(((GPIOC_BASE+0x0c) &0x000FFFFF)<<5)+(n<<2)))
文章部分參考:【野火?】《零死角玩轉(zhuǎn)STM32》