文章程式碼顯示

2017年11月11日 星期六

《筆記》C語言 - 02_1:Bitwise Operation、 ! 與 ~ 差別

Bitwise Operation


※ 在本章開始前,最好先保證你已經了解什麼是二進制表示法。

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(&reg,10);
 DecToBin(reg);
 set1(&reg,10);

 DecToBin(reg);

 return EXIT_SUCCESS;
}




! 與 ~ 差別

順帶一提,驚嘆號 ! 以及波浪符號 ~ 兩個取反常常會讓人搞混

驚嘆號:

驚嘆號為邏輯取反,最常使用於 if 判斷式。因為邏輯只有 0 跟 1 ,所以判斷式內只要不是 0 的都會視為 True ,取反後變成 False 。

※ 數字 0 代表邏輯 False ; 數字 1 代表邏輯 True


波浪符號:

位元取反。將每一個 bit 都從 0 變 1;1 變 0。




↓↓↓ 連結到部落格方針與索引 ↓↓↓

Blog 使用方針與索引