AVR不具bit Addressable的功能。雖然部份MCU,例如AVR,具有位元定址(bit addressable)的功能,而可以用組語來執行單bit讀寫,也有C編譯器提供專用C指令來執行單位元操作。然而這類C語法並沒有受C的統一規則ANSI所規範,因此不同編譯器可能存在差異,有礙於應用程式可攜性。如果可以用ANSI C的標準語法來處理,則無需擔心可攜性問題。
GCC不支援單位元操作,所有單位元操作都需要以C語言所提供的單位元運算指令來執行。然而C語言標準位元操作指令對人而言並不直覺,為了增加程式碼的可讀性,常見的方式是利用巨集指令來取代單位運算指令串。
以下我們先介紹標準ANSI C中可用於進行位元計算的運算元,然後再介紹用這些運算元所組成的常用位元運算巨集指令,包含暫存器部份位元旗標蓋寫,以暫存器部份位元擷取:
| : 各位元『或』運算(bit OR)
例如:
0xA3 | 0x1E = 0b1010 0011 or 0b0001 1110=0b10111111=0xBF
& : 各位元『及』運算(bit AND)
例如:
0xA3 & 0x1E = 0b1010 0011 and 0b0001 1110=0b00000010=0x02
~ : 各位元『反』運算(bit NOT)
例如:
~0xE1= ~ 0b1110 0001=0b0001 1110=0x1E
^ : 各位元『加』運算(bitwise EXOR)
例如:
0xA3 ^ 0x1E = 0b1010 0011 XOR 0b0001 1110=0b10111101=0xBD
<< : 各位元『左移』運算(bit LEFT SHIFT)
例如:
0xA3 << 3= 0b1010 0011 << 3 =0b00011000=0x18
>> : 各位元『右移』運算(bit RIGHT SHIFT)
例如:
0xA3 >> 2= 0b1010 0011 >> 2 =0b0010 1000=0x28
C語言,除了提供了程式指令經編譯器編譯成可執行碼來指導MCU如何執行運算之外,也提供了前置指令,用來指導C編譯器該如何編譯程式。前置指令,由一個以#領頭前置指令字串起頭,後面會跟隨一些參數字串,共同組成。
最常見的前置指令為#include,這個前置指令許多寫過程式的人都用過,是用來引入檔案內容,加入到正在編譯的程式碼之前以供編譯器參考,其內容多半都是關於函式庫函式定義的一些前置指令集。它的參數字串有兩種形態
#include"file-name"
指示編譯器先到目前的目錄 (路徑 )尋找名為file-name 的檔案,如果找不到 ,再到IDE系統設定的目錄尋找 。
#include
指示編譯器直接到IDE系統設定的目錄尋找名為file-name 的檔案。
C語言可以利用前置指令,定義一個較簡單易記的字串做為巨集,來替代,一個常數,或則一串C語言指令串。關於巨集的定義有巨集定義#define、解除定義#undef、檢查是否己經定義#ifdef,以及查檢是否尚未定義#ifndef。
#definemacro-name string
其中為macro-name為巨集名稱,string為巨集要替代的字串。最簡單的巨集定義是以幫助記憶的巨集名,取代常用的常數。
例如:
#defineMAX_SIZE 100
floatbalance[MAX_SIZE];
定義完成後只要程式中出現MAX_SIZE 編譯器在編譯前便會用100來代替它
floatbalance[MAX_SIZE];//編譯器會編譯float balance[100]
好處是維護程式時若想改矩陣大小時,可以用名稱找到,避免到處都有100常數,重覆太多不好找。
macro-name巨集名稱也可以與函式一樣帶有參數。當程式中出現巨集名稱時,編譯器除了會把巨集名稱換成它所取代的字串之外,所有定義取代字串內參數也會以真實變數取代。
例如:
#include
#defineMIN(a,b) ((a)<(b)) ? (a):(b)
//(a) 小於 (b) 嗎? 若是則結果為(a)否則結果是(b)
int main(){
int x,y;
:
x=10,y=20;
printf(“the minimum is:%d”,MIN(x,y));
}
其中printf()指令行會被取代為
printf(“theminimum is:%d”,((x)<(y)) ? (x):(y));
之後再編譯。
利用前述的巨集定義,我們可以將常用的位元運算式子定義成有助於記憶的含參數巨集名稱,之後便可以像使用函式一樣利用巨集來執行位元運算。以下我們介紹一些常用的位元運算巨集。
BIT(m): 指定第m個位元為1
#define BIT(m) (0x01<<(m))
//create an integer with m'th bit being 1
BIT(0) : 0b00000001=0x01,
BIT(3) : 0b00001000=0x08
BIT_SET(p,m):設定變數p的第m位元為1
#define BIT_SET(p,m)((p)|=(BIT(m)))
//sets the m'th bit of p tobe 1
設:A=0x0F;//0b0000 1111
BIT_SET(A,4) =>A=(0b0000 1111) | (0b0001 0000)
=(0b0001 1111)=0x1F
BIT_CLEAR(p,m):清除變數p的第m位元為0
#define BIT_CLEAR(p,m) ((p)&=~(BIT(m)))
//sets the mth bit of p to 0
設:A=0x0F;//0b0000 1111
BIT_CLEAR(A,3) => A=(0b0000 1111)& ~(0b0000 1000)
=(0b0000 1111)& (0b1111 0111)=0b00000111=0x07
BIT_CHECK(p,m):檢視變數p的第m位元清除其他位元
#define BIT_CHECK(p,m) ((p)&(BIT(m)))
假設:A=0x0F,m=3;//A=0b0000 1111
BIT_CHECK(A,m);
((p)&(BIT(m)))=0b00001111 & 0b0000 1000
= 0b0000 1000=0x08
BIT_GET(p,m):取得變數p的第m位元的值。
#define BIT_GET(p,m) (((p)&(BIT(m))) >> m)
假設:A=0x0F,m=3;//A=0b0000 1111
BIT_GET(A,3);
(((p)&(BIT(m))) >> m))
=(0b0000 1111 & b0000 1000)>>3=0b0000 0001=0x01。
BIT_PUT(c,p,m):將c蓋寫到變數p的第m位元上。
#defineBIT_PUT(c,p,m) (c ? BIT_SET(p,m) : BIT_CLEAR(p,m))
//if c==1 (c ?) set m'th bit of p to 1, else(:) clear m'th bit of p to 0
設: A=0x0F=0b0000 1111,m=3
若(c==1)
BIT_PUT(c,A,m)=BIT_SET(0x0F,3)= (0b0000 1111) | (0b0000 1000)=0b0000 1111= 0x0F
若(c==0)
BIT_PUT(c,A,m)=BIT_CLEAR(0x0F,3)= (0b0000 1111) & (0b1111 0111)= 0b0000 0111= 0x07
• BIT_FLIP(p,m):翻轉變數p的第m位元值,所謂翻轉是指把0變1,把1變為0
#defineBIT_FLIP(p,m) BIT_PUT((!(BIT_GET(p,m))),p,m)
//flipthe mth bit of p, 1->0, 0->1
設: A=0x0F=0b00001111,m=3
BIT_FLIP(A,3)
=BIT_PUT((!(BIT_GET(0x0F,3))),0x0F,3)
=BIT_PUT((0),0x0F,3)=BIT_CLEAR(0x0F,3)=0b0000 0111= 0x07
即將0b00001111 的bit3 由1換為0
設: B=0x07=0b0000 0111,m=3
BIT_FLIP(B,3)
=BIT_PUT((!(BIT_GET(0x07,3))),0x07,3)
=BIT_PUT((1),0x07,3)=BIT_SET((1),0x07,3)
= (0b0000 0111) | (0b0000 1000)=(0b0000 1111)=0x1F
即將0b00000111 的bit3 由0換為1
• BITS_SET(DEST, MASK):把目標變數DEST,由遮罩MASK中的1位元所指定的位元值設為1
#defineBITS_SET(DEST, MASK) ((DEST)|=(MASK))
設若DEST=0x15,MASK=0x0E=0b0000 1110 (b3,b2,b1)
BITS_SET(DEST,MASK) => DEST = DEST | MASK = 0x15 | 0x0E
=0b00010101 | 0b0000 1110=0b0001 1111 =0x1F
即將 0b0001 0101 的 b3,b2,b1 均設為1
• BITS_CLEAR(DEST, MASK):把目標變數DEST中,由遮罩MASK中的1位元所指定的位元值設為0
#defineBITS_CLEAR(DEST, MASK) ((DEST)&=(~MASK))
設若DEST=0x15,MASK=0x0E=0b0000 1110 (b3,b2,b1)
BITS_CLEAR(DEST,MASK) =>(DEST)=((DEST)&(~MASK))
=0b0001 0101 & 0b1111 0001=0b0001 0001 =0x11
即將 0b0001 0101 的 b3,b2,b1 均清為0
• REGFPT(HWREG_p,Mask,Shift,Data):擷取暫存器住址HWREG_p ,由遮罩MASK中的1位元所指定的位元群,並將此群往右平移SHIFT 位之後存入位址Data_p。
#define REGFGT(HWREG_p,MASK,SHIFT,Data_p) ((*Data_p)= ((*HWREG_p) & MASK) >> SHIFT)
若 MASK=0b0000 1110, SHIFT=1, REG=0x15 = 0b0001 0101
(*HWREG_p & MASK)=0b0001 0101 & 0b0000 1110=0b0000 0100
((*HWREG_p) & (MASK))>> SHIFT= 0b0000 0010
即取 REG 的bit3:1 平移到 bit2:0 存入*Data_p
• REGFPT( HWREG_p,,MASK, SHIFT, Data):將資料源變數Data向左平移SHIFT格後蓋寫暫存器(變數)REG中由遮罩MASK中的1位元所指定的位元群。
#define REGFPT( HWREG_p, MASK, SHIFT, Data) ( *HWREG_p = BITS_CLEAR(*HWREG_p,MASK) | ((Data<
若 MASK=0b0000 1110,SHIFT=1,*HWREG_p=0x15=0b0001 0101,Data=4
BITS_CLEAR(*HWREG_p,MASK)=0b0001 0101 & 0b1111 0001
= 0b0001 0001
(Data <
=0b0000 1000 &0b0000 1110 =0b0000 1000
*HWREG_p =0b0001 0001 | 0b0000 1000=0b0001 1001 與原值 0b0001 0101 比較,為b3:1的010被 100(即4)蓋寫