C語言灼狰、嵌入式位操作技巧總結(jié)

一、位操作簡單介紹

首先浮禾,以下是按位運算符:

嵌入式編程中交胚,常常需要對一些寄存器進(jìn)行配置,有的情況下需要改變一個字節(jié)中的某一位或者幾位盈电,但是又不想改變其它位原有的值蝴簇,這時就可以使用按位運算符進(jìn)行操作。下面進(jìn)行舉例說明匆帚,假如有一個8位的TEST寄存器:

當(dāng)我們要設(shè)置第0位bit0的值為1時熬词,可能會這樣進(jìn)行設(shè)置:

TEST = 0x01;

但是,這樣設(shè)置是不夠準(zhǔn)確的吸重,因為這時候已經(jīng)同時操作到了高7位:bit1~bit7互拾,如果這高7位沒有用到的話,這么設(shè)置沒有什么影響嚎幸;但是颜矿,如果這7位正在被使用,結(jié)果就不是我們想要的了嫉晶。

在這種情況下骑疆,我們就可以借用按位操作運算符進(jìn)行配置。

對于二進(jìn)制位操作來說替废,不管該位原來的值是0還是1箍铭,它跟0進(jìn)行&運算,得到的結(jié)果都是0椎镣,而跟1進(jìn)行&運算坡疼,將保持原來的值不變;不管該位原來的值是0還是1衣陶,它跟1進(jìn)行|運算柄瑰,得到的結(jié)果都是1,而跟0進(jìn)行|運算剪况,將保持原來的值不變教沾。

所以,此時可以設(shè)置為:

TEST = TEST | 0x01;

其意義為:TEST寄存器的高7位均不變译断,最低位變成1了授翻。在實際編程中,常改寫為:

TEST |= 0x01;

這種寫法可以一定程度上簡化代碼孙咪,是 C 語言常用的一種編程風(fēng)格堪唐。設(shè)置寄存器的某一位還有另一種操作方法,以上的等價方法如:

TEST |= (0x01 << 0);

第幾位要置1就左移幾位翎蹈。

同樣的淮菠,要給TEST的低4位清0,高4位保持不變荤堪,可以進(jìn)行如下配置:

TEST &= 0xF0;

二合陵、嵌入式中位操作一些常見用法

1、一個32bit數(shù)據(jù)的位澄阳、字節(jié)讀取操作

(1)獲取單字節(jié):

#define GET_LOW_BYTE0(x)    ((x >>  0) & 0x000000ff)    /* 獲取第0個字節(jié) */
#define GET_LOW_BYTE1(x)    ((x >>  8) & 0x000000ff)    /* 獲取第1個字節(jié) */
#define GET_LOW_BYTE2(x)    ((x >> 16) & 0x000000ff)    /* 獲取第2個字節(jié) */
#define GET_LOW_BYTE3(x)    ((x >> 24) & 0x000000ff)    /* 獲取第3個字節(jié) */

示例:

(2)獲取某一位:

#define GET_BIT(x, bit) ((x & (1 << bit)) >> bit)   /* 獲取第bit位 */

示例:

2拥知、一個32bit數(shù)據(jù)的位、字節(jié)清零操作

(1)清零某個字節(jié):

#define CLEAR_LOW_BYTE0(x)  (x &= 0xffffff00)   /* 清零第0個字節(jié) */
#define CLEAR_LOW_BYTE1(x)  (x &= 0xffff00ff)   /* 清零第1個字節(jié) */
#define CLEAR_LOW_BYTE2(x)  (x &= 0xff00ffff)   /* 清零第2個字節(jié) */
#define CLEAR_LOW_BYTE3(x)  (x &= 0x00ffffff)   /* 清零第3個字節(jié) */

示例:

(2)清零某一位:

#define CLEAR_BIT(x, bit)   (x &= ~(1 << bit))  /* 清零第bit位 */

示例:

3碎赢、一個32bit數(shù)據(jù)的位低剔、字節(jié)置1操作

(1)置某個字節(jié)為1:

#define SET_LOW_BYTE0(x)    (x |= 0x000000ff)   /* 第0個字節(jié)置1 */   
#define SET_LOW_BYTE1(x)    (x |= 0x0000ff00)   /* 第1個字節(jié)置1 */   
#define SET_LOW_BYTE2(x)    (x |= 0x00ff0000)   /* 第2個字節(jié)置1 */   
#define SET_LOW_BYTE3(x)    (x |= 0xff000000)   /* 第3個字節(jié)置1 */

示例:

(2)置位某一位:

#define SET_BIT(x, bit) (x |= (1 << bit))   /* 置位第bit位 */

4、判斷某一位或某幾位連續(xù)位的值

(1)判斷某一位的值

舉例說明:判斷0x68第3位的值肮塞。

也就是說襟齿,要判斷第幾位的值,if里就左移幾位(當(dāng)然別過頭了)峦嗤。在嵌入式編程中蕊唐,可通過這樣的方式來判斷寄存器的狀態(tài)位是否被置位。

(2)判斷某幾位連續(xù)位的值

/* 獲取第[n:m]位的值 */
#define BIT_M_TO_N(x, m, n)  ((unsigned int)(x << (31-(n))) >> ((31 - (n)) + (m)))

示例:

這是一個查詢連續(xù)狀態(tài)位的例子烁设,因為有些情況不止有0替梨、1兩種狀態(tài),可能會有多種狀態(tài)装黑,這種情況下就可以用這種方法來取出狀態(tài)位副瀑,再去執(zhí)行相應(yīng)操作。

以上是對32bit數(shù)據(jù)的一些操作進(jìn)行總結(jié)恋谭,其它位數(shù)的數(shù)據(jù)類似糠睡,可根據(jù)需要進(jìn)行修改。

三疚颊、STM32寄存器配置

STM32有幾套固件庫狈孔,這些固件庫函數(shù)以函數(shù)的形式進(jìn)行1層或者多層封裝(軟件開發(fā)中很重要的思想之一:分層思想)信认,但是到了最里面的一層就是對寄存器的配置。我們平時都比較喜歡固件庫來開發(fā)均抽,大概是因為固件庫用起來比較簡單嫁赏,用固件庫寫出來的代碼比較容易閱讀。最近一段時間一直在配置寄存器油挥,越發(fā)地發(fā)現(xiàn)使用寄存器來進(jìn)行一些外設(shè)的配置也是很容易懂的潦蝇。使用寄存器的方式編程無非就是往寄存器的某些位置1、清零以及對寄存器一些狀態(tài)位進(jìn)行判斷深寥、讀取寄存器的內(nèi)容等攘乒。

這些基本操作在上面的例子中已經(jīng)有介紹,我們依舊以實例來鞏固上面的知識點(以STM32F1xx為例):

(1)寄存器配置

看一下GPIO功能的端口輸出數(shù)據(jù)寄存器 (GPIOx_ODR) (x=A..E) :

假設(shè)我們要讓PA10引腳輸出高惋鹅、輸出低则酝,可以這么做:

方法一:

GPIOA->ODR |= 1 << 10;      /* PA10輸出高(置1操作) */
GPIOA->ODR &= ~(1 << 10);  /* PA10輸出低(清0操作) */

也可用我們上面的置位、清零的宏定義:

SET_BIT(GPIOA->ODR, 10);    /* PA10輸出高(置1操作) */
CLEAR_BIT(GPIOA->ODR, 10);  /* PA10輸出低(清0操作) */

方法二:

GPIOA->ODR |= (uint16_t)0x0400;   /* PA10輸出高(置1操作) */
GPIOA->ODR &= ~(uint16_t)0x0400;  /* PA10輸出低(清0操作) */

貌似第二種方法更麻煩负饲?還得去細(xì)心地去構(gòu)造一個數(shù)據(jù)堤魁。

但是,其實第二種方法其實是ST推薦我們用的方法返十,為什么這么說呢妥泉?因為ST官方已經(jīng)把這些我們要用到的值給我們配好了,在stm32f10x.h中:

這個頭文件中存放的就是外設(shè)寄存器的一些位配置洞坑。

所以我們的方法二等價于:

GPIOA->ODR |= GPIO_ODR_ODR10;   /* PA10輸出高(置1操作) */
GPIOA->ODR &= ~GPIO_ODR_ODR10;  /* PA10輸出低(清0操作) */

兩種方法都是很好的方法盲链,但方法一似乎更好理解。

配置連續(xù)幾位的方法也是一樣的迟杂,就不介紹了刽沾。簡單介紹配置不連續(xù)位的方法,以TIM1的CR1寄存器為例:

設(shè)置CEN位為1排拷、設(shè)置CMS[1:0]位為01侧漓、設(shè)置CKD[1:0]位為10:

TIM1->CR1 |= (0x1 << 1)| (0x1 << 5) |(0x2 << 8);

這是組合的寫法。當(dāng)然监氢,像上面一樣拆開來寫也是可以的布蔗。

(2)判斷標(biāo)志位

以狀態(tài)寄存器(USART_SR) 為例:

判斷RXNE是否被置位:

/* 數(shù)據(jù)寄存器非空,RXNE標(biāo)志置位 */
if (USART1->SR & (1 << 5))
{
    /* 其它代碼 */
    
    USART1->SR &= ~(1 << 5);  /* 清零RXNE標(biāo)志 */
}

或者:

/* 數(shù)據(jù)寄存器非空浪腐,RXNE標(biāo)志置位 */
if (USART1->SR & USART_SR_RXNE)
{
    /* 其它代碼 */
    
    USART1->SR &= ~USART_SR_RXNE;  /* 清零RXNE標(biāo)志 */
}

四纵揍、總結(jié)

以上就是本次關(guān)于位操作的一點總結(jié)筆記,有必要掌握议街。雖然說在用STM32的時候有庫函數(shù)可以用泽谨,但是最接近芯片內(nèi)部原理的還是寄存器。有可能之后有用到其它芯片沒有像ST這樣把寄存器相關(guān)配置封裝得那么好,那就不得不直接操控寄存器了吧雹。

此外骨杂,使用庫函數(shù)的方式代碼占用空間大,用寄存器的話雄卷,代碼占用空間小腊脱。之前有個需求,我能用的Flash的空間大小只有4KB龙亲,遇到類似這樣的情況就不能那么隨性的用庫函數(shù)了。

最后悍抑,應(yīng)用的時候當(dāng)然是怎么簡單就怎么用鳄炉。學(xué)從“難”處學(xué),用從易處用搜骡,與君共勉~

END:以上筆記中如有錯誤拂盯,歡迎指出!謝謝
歡迎關(guān)注微信公眾號【嵌入式大雜燴】查看更多筆記记靡。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谈竿,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子摸吠,更是在濱河造成了極大的恐慌空凸,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寸痢,死亡現(xiàn)場離奇詭異呀洲,居然都是意外死亡,警方通過查閱死者的電腦和手機啼止,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門道逗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人献烦,你說我怎么就攤上這事滓窍。” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵两波,是天一觀的道長磷籍。 經(jīng)常有香客問我,道長锦亦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任令境,我火速辦了婚禮杠园,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘舔庶。我一直安慰自己抛蚁,他們只是感情好陈醒,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瞧甩,像睡著了一般钉跷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肚逸,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天爷辙,我揣著相機與錄音,去河邊找鬼朦促。 笑死膝晾,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的务冕。 我是一名探鬼主播血当,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼禀忆!你這毒婦竟也來了臊旭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤箩退,失蹤者是張志新(化名)和其女友劉穎离熏,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乏德,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡撤奸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了喊括。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胧瓜。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖郑什,靈堂內(nèi)的尸體忽然破棺而出府喳,到底是詐尸還是另有隱情,我是刑警寧澤蘑拯,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布钝满,位于F島的核電站,受9級特大地震影響申窘,放射性物質(zhì)發(fā)生泄漏弯蚜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一剃法、第九天 我趴在偏房一處隱蔽的房頂上張望碎捺。 院中可真熱鬧,春花似錦、人聲如沸收厨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽诵叁。三九已至雁竞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拧额,已是汗流浹背碑诉。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留侥锦,地道東北人联贩。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像捎拯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子盲厌,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容