【STM32】位操作、按位与、按位或、按位异或、取反、左移、右移等基础 C 语言知识补充
文章目錄
- 1 位操作
- 1.1 按位與
- 1.2 按位或
- 1.3 按位異或
- 1.4 取反
- 1.5 左移
- 1.6 右移
- 2 單片機中常用操作
- 2.1 不改變其他位時,對某幾個位設定值
- 2.2 移位操作提高代碼可讀性
- 2.3 取反操作使用技巧
1 位操作
| & | 按位與 |
| | | 按位或 |
| ^ | 按位異或 |
| ~ | 取反 |
| << | 左移 |
| >> | 右移 |
c 語言中存在以上 6 個位操作運算符,且它們只能用于整形操作數。
總結:對于原二進制數來說,&0是屏蔽,&1是不變。
總結:對于原二進制數來說,|0是不變,|1是置1。
總結:對于原二進制數來說,^0是不變,^1是反轉。
1.1 按位與
按位與的定義是:同一二進制位上的數字都是1的話,& 的結果為1,否則為0。
| 0 & 0 | 0 |
| 0 & 1 | 0 |
| 1 & 0 | 0 |
| 1 & 1 | 1 |
根據這個特性,& 操作常常用來屏蔽特定的二進制位。
例如:0000 1111 & 0000 0011 = 0000 0011
| 與運算 | 0 0 0 0 1 1 1 1 |
| & | 0 0 0 0 0 0 1 1 |
| 結果 | 0 0 0 0 0 0 1 1 |
可以看見,1111的前兩位被屏蔽成為0了。
所以如果想清空數據,只需要將原二進制數與上 &0 就可以了。0的位數對應原二進制數的位數,對各位進行屏蔽,全部置0。
相對的,&可以利用0來屏蔽,也可以用1來讀取。
例如: 一個二進制數 1101 1001,我只想要它的后四位,怎么辦呢?
只需要進行如下操作:1101 1001 & 0000 1111即可。
| 與運算 | 1 1 0 1 1 0 0 1 |
| & | 0 0 0 0 1 1 1 1 |
| 結果 | 0 0 0 0 1 0 0 1 |
其實該方法是屏蔽和讀取的結合,&0保證消除無用位,&1保證有用數據的完整性。
總結:對于原二進制數來說,&0是屏蔽,&1是不變。
1.2 按位或
定義:只要參與運算的雙方其中有一個是1,結果就是1.同0才為0。
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
主要用作將某些特定位置1。
例如:1010 0000 | 0000 1111 = 1010 1111。
| 或運算 | 1 0 1 0 0 0 0 0 |
| | | 0 0 0 0 1 1 1 1 |
| 結果 | 1 0 1 0 1 1 1 1 |
總結:對于原二進制數來說,|0是不變,|1是置1。
1.3 按位異或
只要參與運算的雙方互異,結果就為1,否則為0。
| 0 ^ 0 | 0 |
| 0 ^ 1 | 1 |
| 1 ^ 0 | 1 |
| 1 ^ 1 | 0 |
可以通過上面的定義看到,一個數^1 的話就會0變成1,1變成0,而^0則不對原數進行改變。所以根據此特性可以對特定位進行0 1 反轉。
例如: 1100 1100 ^ 0000 1100 = 1100 0000。
| 異或運算 | 1 1 0 0 1 1 0 0 |
| ^ | 0 0 0 0 1 1 0 0 |
| 結果 | 1 1 0 0 0 0 0 0 |
同樣的,如果對一個數進行^0,代表保留原值。
總結:對于原二進制數來說,^0是不變,^1是反轉。
1.4 取反
對一個二進制數進行取反。1變0,0變1。
唯一需要注意的一點是,~的優先級是邏輯運算符中最高的,必須優先計算。
1.5 左移
左移與右移比較類似,是將目標二進制數字向左/右移動相應的位數。
左移補0:1111 1111 << 1 == 1111 1110,換算十進制的話是原來數值的2倍。
| 左移 | 1 1 1 1 1 1 1 1 |
| << | 1 |
| 結果 | 1 1 1 1 1 1 1 0 |
1.6 右移
右移看情況:負數補1,正數補0。需要看符號位。同樣,換算為十進制數值變為原來的1/2.
| 右移 | 1 1 1 1 1 1 1 1 |
| >> | 1 |
| 結果 | 0 1 1 1 1 1 1 1 |
總結:左乘右除。
2 單片機中常用操作
2.1 不改變其他位時,對某幾個位設定值
比如要改變 GPIOA 的狀態,可以先對寄存器的值進行 & 清零操作
GPIOA -> CRL &= 0XFFFFFF0F; // 將第 4-7 位清 0然后再與需要設置的值進行 | 或運算
GPIOA -> CRL |= 0X00000040; // 設置相應位的值,且不改變其他位的值2.2 移位操作提高代碼可讀性
以固件庫的 GPIO 初始化的函數里一行代碼為例
GPIOx -> BSRR = (((uint32_t)0x01) << pinpos);這個操作就是將 BSRR 寄存器的第 pinpos 位設置為 1。
為什么要通過左移而不是直接設定呢?其實,這是為了提高代碼的可讀性以及可重用性。這行代碼可以直觀明了的知道,是將第 pinpos 位設置為 1。
如果寫成
GPIOx -> BSRR = 0x0030;這樣的代碼就不容易看出,也不好重用了。
類似的代碼還有:
GPIOA -> ORT |= 1<<5; // PA.5 輸出高,不改變其他位這樣我們一目了然,5 告訴我們是第 5 位也就是第 6 個端口,1 告訴我們是設置成了 1。
2.3 取反操作使用技巧
SR 寄存器的每一位都代表一個狀態,某個時刻我們希望去設置某一位的值為 0,同時其他位都保留位 1,簡單的作法是直接給寄存器設置一個值:
TIMx -> SR = 0xFFF7;這樣做法可讀性交較差。
看看庫函數中代碼是如何使用的:
TIMx -> SR = (uint16_t)~TIM_FLAG;而 TIM_FLAG 是通過宏定義完成的值:
#define TIM_FLAG_Update ((uint16_t)0x0001) #define TIM_FLAG_CC1 ((uint16_t)0x0002)看這個就容易明白,可以直接從宏定義重看出 TIM_FLAG_Update 就是設置的第 0 位了,可讀性較強。
Ref: C語言中的邏輯運算符:按位與,按位或,按位異或,取反,左右移位
Ref: STM32 不完全手冊 - 立創開源
總結
以上是生活随笔為你收集整理的【STM32】位操作、按位与、按位或、按位异或、取反、左移、右移等基础 C 语言知识补充的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Paper】2018_Nonlinea
- 下一篇: 【STM32】typedef 类型及配合