C语言、嵌入式位操作精华技巧大汇总
最近有點忙,好久沒分享筆記了~今天分享關于位操作的一點小筆記。
一、位操作簡單介紹
首先,以下是按位運算符:
在嵌入式編程中,常常需要對一些寄存器進行配置,有的情況下需要改變一個字節中的某一位或者幾位,但是又不想改變其它位原有的值,這時就可以使用按位運算符進行操作。下面進行舉例說明,假如有一個8位的TEST寄存器:
當我們要設置第0位bit0的值為1時,可能會這樣進行設置:
TEST = 0x01;但是,這樣設置是不夠準確的,因為這時候已經同時操作到了高7位:bit1~bit7,如果這高7位沒有用到的話,這么設置沒有什么影響;但是,如果這7位正在被使用,結果就不是我們想要的了。
在這種情況下,我們就可以借用按位操作運算符進行配置。
對于二進制位操作來說,不管該位原來的值是0還是1,它跟0進行&運算,得到的結果都是0,而跟1進行&運算,將保持原來的值不變;不管該位原來的值是0還是1,它跟1進行|運算,得到的結果都是1,而跟0進行|運算,將保持原來的值不變。
所以,此時可以設置為:
TEST = TEST | 0x01;其意義為:TEST寄存器的高7位均不變,最低位變成1了。在實際編程中,常改寫為:
TEST |= 0x01;這種寫法可以一定程度上簡化代碼,是 C 語言常用的一種編程風格。設置寄存器的某一位還有另一種操作方法,以上的等價方法如:
TEST |= (0x01 << 0);第幾位要置1就左移幾位。
同樣的,要給TEST的低4位清0,高4位保持不變,可以進行如下配置:
TEST &= 0xF0;二、嵌入式中位操作的用法
1、一個32bit數據的位、字節讀取操作
(1)獲取單字節:
左右滑動查看全部代碼>>>
#define GET_LOW_BYTE0(x) ((x >> 0) & 0x000000ff) /* 獲取第0個字節 */ #define GET_LOW_BYTE1(x) ((x >> 8) & 0x000000ff) /* 獲取第1個字節 */ #define GET_LOW_BYTE2(x) ((x >> 16) & 0x000000ff) /* 獲取第2個字節 */ #define GET_LOW_BYTE3(x) ((x >> 24) & 0x000000ff) /* 獲取第3個字節 */示例:
(2)獲取某一位:
左右滑動查看全部代碼>>>
#define GET_BIT(x, bit) ((x & (1 << bit)) >> bit) /* 獲取第bit位 */示例:
2、一個32bit數據的位、字節清零操作
(1)清零某個字節:
左右滑動查看全部代碼>>>
#define CLEAR_LOW_BYTE0(x) (x &= 0xffffff00) /* 清零第0個字節 */ #define CLEAR_LOW_BYTE1(x) (x &= 0xffff00ff) /* 清零第1個字節 */ #define CLEAR_LOW_BYTE2(x) (x &= 0xff00ffff) /* 清零第2個字節 */ #define CLEAR_LOW_BYTE3(x) (x &= 0x00ffffff) /* 清零第3個字節 */示例:
(2)清零某一位:
左右滑動查看全部代碼>>>
#define CLEAR_BIT(x, bit) (x &= ~(1 << bit)) /* 清零第bit位 */示例:
3、一個32bit數據的位、字節置1操作
(1)置某個字節為1:
左右滑動查看全部代碼>>>
#define SET_LOW_BYTE0(x) (x |= 0x000000ff) /* 第0個字節置1 */ #define SET_LOW_BYTE1(x) (x |= 0x0000ff00) /* 第1個字節置1 */ #define SET_LOW_BYTE2(x) (x |= 0x00ff0000) /* 第2個字節置1 */ #define SET_LOW_BYTE3(x) (x |= 0xff000000) /* 第3個字節置1 */示例:
(2)置位某一位:
左右滑動查看全部代碼>>>
#define SET_BIT(x, bit) (x |= (1 << bit)) /* 置位第bit位 */4、判斷某一位或某幾位連續位的值
(1)判斷某一位的值
舉例說明:判斷0x68第3位的值。
也就是說,要判斷第幾位的值,if里就左移幾位(當然別過頭了)。在嵌入式編程中,可通過這樣的方式來判斷寄存器的狀態位是否被置位。
(2)判斷某幾位連續位的值
左右滑動查看全部代碼>>>
/* 獲取第[n:m]位的值 */ #define BIT_M_TO_N(x, m, n) ((unsigned int)(x << (31-(n))) >> ((31 - (n)) + (m)))示例:
這是一個查詢連續狀態位的例子,因為有些情況不止有0、1兩種狀態,可能會有多種狀態,這種情況下就可以用這種方法來取出狀態位,再去執行相應操作。
以上是對32bit數據的一些操作進行總結,其它位數的數據類似,可根據需要進行修改。
三、STM32寄存器配置
STM32有幾套固件函數庫,這些固件庫函數以函數的形式進行1層或者多層封裝(軟件開發中很重要的思想之一:分層思想),但是到了最里面的一層就是對寄存器的配置。
我們平時都比較喜歡固件庫來開發,大概是因為固件庫用起來比較簡單,用固件庫寫出來的代碼比較容易閱讀。
最近一段時間一直在配置寄存器,越發地發現使用寄存器來進行一些外設的配置也是很容易懂的。
使用寄存器的方式編程無非就是往寄存器的某些位置1、清零以及對寄存器一些狀態位進行判斷、讀取寄存器的內容等。
這些基本操作在上面的例子中已經有介紹,我們依舊以實例來鞏固上面的知識點(以STM32F1xx為例):
(1)寄存器配置
看一下GPIO功能的端口輸出數據寄存器 ?(GPIOx_ODR) (x=A..E) ?:
假設我們要讓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操作) */貌似第二種方法更麻煩?還得去細心地去構造一個數據。
但是,其實第二種方法其實是ST推薦我們用的方法,為什么這么說呢?因為ST官方已經把這些我們要用到的值給我們配好了,在stm32f10x.h中:
這個頭文件中存放的就是外設寄存器的一些位配置。
所以我們的方法二等價于:
GPIOA->ODR |= GPIO_ODR_ODR10; /* PA10輸出高(置1操作) */ GPIOA->ODR &= ~GPIO_ODR_ODR10; /* PA10輸出低(清0操作) */兩種方法都是很好的方法,但方法一似乎更好理解。
配置連續幾位的方法也是一樣的,就不介紹了。簡單介紹配置不連續位的方法,以TIM1的CR1寄存器為例:
設置CEN位為1、設置CMS[1:0]位為01、設置CKD[1:0]位為10:
TIM1->CR1 |= (0x1 << 1)| (0x1 << 5) |(0x2 << 8);這是組合的寫法。當然,像上面一樣拆開來寫也是可以的。
(2)判斷標志位
以狀態寄存器(USART_SR) 為例:
判斷RXNE是否被置位:
/* 數據寄存器非空,RXNE標志置位 */ if (USART1->SR & (1 << 5)) {/* 其它代碼 */USART1->SR &= ~(1 << 5); /* 清零RXNE標志 */ }或者:
/* 數據寄存器非空,RXNE標志置位 */ if (USART1->SR & USART_SR_RXNE) {/* 其它代碼 */USART1->SR &= ~USART_SR_RXNE; /* 清零RXNE標志 */ }四、總結
以上就是本次關于位操作的一點總結筆記,有必要掌握。雖然說在用STM32的時候有庫函數可以用,但是最接近芯片內部原理的還是寄存器。有可能之后有用到其它芯片沒有像ST這樣把寄存器相關配置封裝得那么好,那就不得不直接操控寄存器了。
此外,使用庫函數的方式代碼占用空間大,用寄存器的話,代碼占用空間小。之前有個需求,我能用的Flash的空間大小只有4KB,遇到類似這樣的情況就不能那么隨性的用庫函數了。
最后,應用的時候當然是怎么簡單就怎么用。學從“難”處學,用從易處用,與君共勉~
END:以上筆記中如有錯誤,歡迎指出!謝謝。歡迎收藏、轉發、在看~
??最 后??
?若覺得文章不錯,轉發分享,也是我們繼續更新的動力。
5T資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,PCB、FPGA、DSP、labview、單片機、等等!
在公眾號內回復「更多資源」,即可免費獲取,期待你的關注~
長按識別圖中二維碼關注
總結
以上是生活随笔為你收集整理的C语言、嵌入式位操作精华技巧大汇总的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 钛白粉概念股票龙头一览表,2022钛白粉
- 下一篇: amc是什么股票
