※ 在本章開始前,最好先保證你已經了解什麼是二進制表示法。
Bitwise Operation 在操作嵌入式系統裡面的暫存器很常使用到,概念為對一個或數個 bit 進行位元操作,有主要以下三種狀況
假設有一個暫存器 reg = 0b0100 0010
#include "stdio.h"
void DecToBin(int data);
int main(void) {
char reg = 0b01000010;
printf("Original reg = "); //標準庫中並沒有可以直接顯示二進制的轉換指定詞
DecToBin(reg); //但可以使用 DecToBin 這個函式將二進制列印出來(函式的概念待後續章節討論)
printf("\n");
//指定 reg 的 bit2 為 1
int reg1 = reg | (0b00000100);
printf("Set bit2, reg = ");
DecToBin(reg1);
printf("\n");
//接著指定 reg 的 bit1 為 0
int reg2 = reg1& ~(0b00000010);
printf("And ReSet bit1, reg = ");
DecToBin(reg2);
printf("\n");
//接著反轉 reg 的 bit3 ( 1變0;0變1 )
int reg3 = reg2 ^ (0b00001000);
printf("And Toggle bit3, reg = ");
DecToBin(reg3);
return 0;
}
void DecToBin(int data){
int data_buffer[32] = {0};
for (int i=0; i < 31; i++){
data_buffer[i] = data & 1;
data = data >> 1;
}
for (int j = 31; j >= 0; j--){
if (j == 27 || j == 23 || j == 19 || j == 15 || j == 11 || j == 7 || j == 3)
printf(" ");
printf("%d", data_buffer[j]);
}
}
以上述的方式進行位元操作是可行的,但我們想要更聰明方便一點的方法。
我們定義一個巨集 _BV(a) 直接做為一個位元遮罩(mask) , 如此一來我們就可以直接指定哪一個 bit 了
#include "stdio.h"
#define _BV(a) (1 << a)
void DecToBin(int data);
int main(void) {
char reg = 0b01000010;
printf("Original reg = ");
DecToBin(reg);
printf("\n");
//指定 reg 的 bit2 為 1
int reg1 = reg | _BV(2);
printf("Set bit2, reg = ");
DecToBin(reg1);
printf("\n");
//接著指定 reg 的 bit1 為 0
int reg2 = reg1& ~_BV(1);
printf("And ReSet bit1, reg = ");
DecToBin(reg2);
printf("\n");
//接著反轉 reg 的 bit3 ( 1變0;0變1)
int reg3 = reg2 ^ _BV(3);
printf("And Toggle bit3, reg = ");
DecToBin(reg3);
return 0;
}
void DecToBin(int data){
int data_buffer[32] = {0};
for (int i=0; i < 31; i++){
data_buffer[i] = data & 1;
data = data >> 1;
}
for (int j = 31; j >= 0; j--){
if (j == 27 || j == 23 || j == 19 || j == 15 || j == 11 || j == 7 || j == 3)
printf(" ");
printf("%d", data_buffer[j]);
}
}
對一個暫存器而言,若要一次設定數個 bit 使用 _BV也很方便,只要將各個 bit 做 OR 就好,例如
#include "stdio.h"
#define _BV(a) (1 << a)
void DecToBin(int data);
int main(void) {
char reg = 0b01000010;
printf("Original reg = ");
DecToBin(reg);
printf("\n");
//指定 reg 的 bit2,5,7 為 1
int reg1 = reg | ( _BV(2) | _BV(5) | _BV(7) );
printf("Set bit2,5,7 , reg = ");
DecToBin(reg1);
printf("\n");
//接著指定 reg 的 bi5,6,7 為 0
int reg2 = reg1 & ~( _BV(5) | _BV(6) | _BV(7) );
printf("ReSet bit5,6,7 , reg = ");
DecToBin(reg2);
return 0;
}
void DecToBin(int data){
int data_buffer[32] = {0};
for (int i=0; i < 31; i++){
data_buffer[i] = data & 1;
data = data >> 1;
}
for (int j = 31; j >= 0; j--){
if (j == 27 || j == 23 || j == 19 || j == 15 || j == 11 || j == 7 || j == 3)
printf(" ");
printf("%d", data_buffer[j]);
}
}
20190214 重寫
#include "stdio.h"
#include "stdlib.h"
// 假設最大可以餵入 32 bits 的數值,將其二進制列印出來
void DecToBin(int num){
char num_temp[32] = {0};
for (int j=31; j>=0;j--){
num_temp[j] = (num>>j)&0x01;
printf("%x",num_temp[j]);
if ( j%4 == 0){ printf(" "); }
}
}
// 指定位數置 1
void set1(int* num, int SETNUM){
printf("第 %d 位數置 1\n", SETNUM);
*num |= (1<< (SETNUM-1) );
}
// 指定位數清 0
void set0(int* num, int SETNUM){
printf("第 %d 位數清 0\n", SETNUM);
*num &= ~(1<< (SETNUM-1) );
}
int main(void) {
setvbuf(stdout,NULL,_IONBF,0);
int reg = 0x1234ff00;
printf("reg = %d\n", reg);
DecToBin(reg);
set0(®,10);
DecToBin(reg);
set1(®,10);
DecToBin(reg);
return EXIT_SUCCESS;
}
! 與 ~ 差別
順帶一提,驚嘆號 ! 以及波浪符號 ~ 兩個取反常常會讓人搞混。
驚嘆號:
驚嘆號為邏輯取反,最常使用於 if 判斷式。因為邏輯只有 0 跟 1 ,所以判斷式內只要不是 0 的都會視為 True ,取反後變成 False 。
※ 數字 0 代表邏輯 False ; 數字 1 代表邏輯 True
波浪符號:
位元取反。將每一個 bit 都從 0 變 1;1 變 0。