1. 程式人生 > >暫存器位讀寫,結構體位域定義,位域操作,位操作

暫存器位讀寫,結構體位域定義,位域操作,位操作

1.2.3 新增位域結構體

1)增加位域定義

我們經常需要直接訪問暫存器中的某個位域。C281x C/C++標頭檔案及外設示例所涉及的位域結構體方法,為多數片上外設暫存器提供了位域定義。例如,可以為CPU 定時器(CPU-Timer)中的每個暫存器定義一個位域結構體型別。CPU 定時器(CPU-Timer)控制暫存器的位域定義如下所示:

//*****************************************************************************  
//DSP281x_headers\include\DSP281x_CpuTimers.h CPU 定時器標頭檔案  
//*****************************************************************************  
struct TCR_BITS //定義一個TCR_BITS 結構體型別(不是變數)  
{ Uint16 rsvd1:4; //3:0 保留,從最低位開始,順序取位到最高位。取低4 位  
Uint16 TSS:1; //4 定時器開始/停止,取第5 位  
Uint16 TRB:1; //5 定時器重灌,取第6 位  
Uint16 rsvd2:4; //9:6 保留,取第7 位到第10 位  
Uint16 SOFT:1; //10 模擬模式,取第11 位  
Uint16 FREE:1; //11 模擬模式,取第12 位  
Uint16 rsvd3:2; //12:13 保留,取第13 位到第14 位  
Uint16 TIE:1; //14 輸出使能,取第15 位  
Uint16 TIF:1; //15 中斷標誌,取第16 位  
}; 

然後,通過共用體進行宣告,以便訪問位域結構體定義的各個成員或者16 位或32位暫存器的值。例如,定時器的控制暫存器共用體如下所示:

//*****************************************************************************  
//DSP281x_headers\include\DSP281x_CpuTimers.h CPU 定時器標頭檔案  
//*****************************************************************************  
union TCR_REG //定義共用體型別TCR_REG(不是變數)  
{ Uint16 all;  
struct TCR_BITS bit; //bit 是一個具有TCR_BITS 結構體型別的變數  
};  
//all 和bit 是共用體的兩個成員,它們都是16 位結構,佔用記憶體的同一單元
一旦每個暫存器的位域結構體型別和共用體的定義都建立起來了,則在CPU 定時器(CPU-Timer)的暫存器結構體型別中,各個成員可通過採用共用體定義的形式重寫:
//*****************************************************************************  
//DSP281x_headers\include\DSP281x_CpuTimers.h CPU 定時器標頭檔案  
//*****************************************************************************  
struct CPUTIMER_REGS  
{ union TIM_GROUP TIM; //定時器計數暫存器,TIM 是一個具有 TIM_GROUP 共  
//用體型別的變數  
union PRD_GROUP PRD; //定時器週期暫存器  
union TCR_REG TCR; //定時器控制暫存器  
Uint16 rsvd1; //保留  
union TPR_REG TPR; //定時器預定標暫存器低位  
union TPRH_REG TPRH; //定時器預定標暫存器高位  
}; 

現在,既可以通過C 程式碼以位域的方法訪問CpuTimer 暫存器中的某位,也可以對整個暫存器進行訪問:
//*****************************************************************************  
//使用者原始檔  
//*****************************************************************************  
CpuTimer0Regs.TCR.bit.TSS = 1; //訪問一個單獨的位域的示例  
CpuTimer0Regs.TCR.all = TSS_MASK; //訪問整個暫存器的示例 

採用位域結構體的方法具有以下優點:

(1)無須使用者確定掩模值,就可對位域進行操作;

(2)可在CCS 觀察窗中看到暫存器和位域的值;

(3)當使用CCS 時,編輯器會提供一張現有結構體/位域成員的列表以供選擇。這一功能是CCS 自動完成的,它使編寫程式碼變得更容易,而不必查閱暫存器和位域名檔案。

掩模值是指位掩碼(位遮蔽碼),在下面的程式碼段中,常數TCR_MASK 是位掩碼是用於置位或清除較大欄位中的一個特殊位的常數值。

#define TCR_MASK 0x0010  
…  
CpuTimer0Regs.TCR.all = TCR_MASK; 

2)使用位域時,“讀—修改—寫”的注意事項當對暫存器中的單個位域進行寫操作時,硬體將執行一個讀—修改—寫的操作,即讀出暫存器中的內容,修改單個位域的值及回寫整個暫存器。上述操作在F28x 上的單個週期內完成。當發生回寫操作時,暫存器內的其他位將被寫入讀出時所讀到的同一個數值。有些暫存器沒有采用共用體定義,是因為不推薦採用這種方式訪問,也存在一些例外情況,包括:

(1)具有寫1 清除位的暫存器,如事件管理標誌暫存器;

(2)無論在什麼時候訪問暫存器,都必須用特殊方式對位進行寫入操作的暫存器,如看門狗控制暫存器。

沒有位域結構體和共用體定義的暫存器,不使用*.bit 或*.all 名稱進行訪問,例如:

//*****************************************************************************  
//使用者原始檔  
//*****************************************************************************  
SysCtrlRegs.WDCR = 0x0068; 

3)程式碼長度考量

採用位域定義訪問暫存器,可使程式碼變得易讀、易修改和易維護。當需要對暫存器中單獨某位域進行訪問或者查詢時,使用這種方法也非常有效。然而,值得注意的是:當對一個暫存器進行一定數量的訪問時,使用*.bit 位域定義形式進行訪問將導致比使用*.all 形式對暫存器進行寫操作需要更多的程式碼,例如:

//*****************************************************************************  
//使用者原始檔  
//*****************************************************************************  
CpuTimer0Regs.TCR.bit.TSS = 1; //1 = 停止定時器  
CpuTimer0Regs.TCR.bit.TRB = 1; //1 = 重灌定時器  
CpuTimer0Regs.TCR.bit.SOFT = 1; //當SOFT=1 且FREE=1 時,定時器自由執行  
CpuTimer2Regs.TCR.bit.FREE = 1;  
CpuTimer2Regs.TCR.bit.TIE = 1; //1 = 使能定時器中斷
採用上述的方法,可以得到可讀性非常強並且易於修改的程式碼。不足是程式碼有些長。如果使用者更加關心程式碼的長度,可使用*.all 結構對暫存器進行一次性的寫操作。
CpuTimer0Regs.TCR.all = TCR_MASK; //TCR_MASK 可在檔案頭部用#define 定義 

1.2.4 共用體結構體位域的應用例項

【例】設count 是一個16 位的無符號整型計數器,最大計數為十六進位制0xffff,要求將這個計數值以十六進位制半位元組的形式分解出來。

對於上述例項通常採用移位的方法求解,而採用共用體結構體位域的方法不需要通過移位運算。以下,對CCS 在標頭檔案中大量使用的共用體結構體位域進行註解。

先定義一個共用體結構體位域:

Uint16 cont,g,s,b,q; //16 位無符號整型變數定義  
cont=0xfedc; //對cont 賦值  
…  
union //共用體型別定義  
{ Uint16 i; //定義i 為16 位無符號整型變數  
struct //結構體型別定義  
{  
Uint16 low:4; //最低4 位在前。從最低4 位開始,取每4 位構成半位元組  
Uint16 mid0:4;  
Uint16 mid1:4;  
Uint16 high:4; //最高4 位在後  
}HalfByte; //HalfByte 為具有所定義的結構體型別的變數  
}Count; //Count為具有所定義的共用體型別的變數

union 定義一個共用體型別,它包含兩個成員:一個是16 位無符號整型變數i,另一個是包含4 個半位元組變數(low,mid0,mid1,high)的結構體型別。它們佔用同一個記憶體單元,通過對i(Count.i)進行賦值,可以完成對結構體4 個變數的賦值。

上面的程式,在定義共用體型別和結構體型別的同時,直接完成了這兩個型別變數的定義,而未定義共用體和結構體型別名。即HalfByte 是一個具有所定義的結構體型別的變數,Count 是一個具有所定義的共用體型別的變數。理解了共用體與結構體之間的關係,下面的賦值指令就清楚了。

Count.i = cont; //對共用體型別成員i 進行賦值

g = Count.HalfByte.low; //將cont 的0~3 位賦值給g,g=0x000c 
s = Count.HalfByte.mid0; //將cont 的4~7 位賦值給s,s=0x000d 
b = Count.HalfByte.mid1; //將cont 的8~11 位賦值給b,b=0x000e 
q = Count.HalfByte.high; //將cont 的12~15 位賦值給q,q=0x000f 

通過共用體結構體定義,當對共用體型別成員i 進行賦值時,由於結構體型別變數HalfByte 與i 佔用同一個記憶體單元,因此,也就完成了對HalfByte 的各成員的賦值。

C 語言的共用體結構體位域定義,可以完成對暫存器位域的訪問。至於被訪問的位域在記憶體中的具體位置則由編譯器安排,程式設計者可以不必關注。

下面是一個訪問暫存器位域的例子,供讀者參考。

先建立一個共用體結構體位域定義,將某個暫存器的16 位,從最低位到最高位分別

定義為Bit1,Bit2,…,Bit16。

union //共用體型別定義  
{ Uint16 all; //定義all 為16 位無符號整型變數  
struct //結構體型別定義  
{  
Uint16 Bit1:1; //0 位Bit1 取暫存器最低位0 位,以下順序取1 位直到最高位  
Uint16 Bit2:1; //1  
Uint16 Bit3:1; //2  
Uint16 Bit4:1; //3  
Uint16 Bit5:1; //4  
Uint16 Bit6:1; //5  
Uint16 Bit7:1; //6  
Uint16 Bit8:1; //7  
Uint16 Bit9:1; //8  
Uint16 Bit10:1; //9  
Uint16 Bit11:1; //10  
Uint16 Bit12:1; //11  
Uint16 Bit13:1; //12  
Uint16 Bit14:1; //13  
Uint16 Bit15:1; //14  
Uint16 Bit16:1; //15  
}bit; //bit為具有所定義的結構體型別的變數  
}CtrlBit; //CtrlBit 為具有所定義的共用體型別的變數

有了上面的定義之後,要訪問某一個位或某些位就很容易了。比如要置Bit4,Bit8,Bit12 及Bit16 為1,可用兩種方法進行:

方法一:

CtrlBit.bit.Bit4 = 1;  
CtrlBit.bit.Bit8 = 1;  
CtrlBit.bit.Bit12 = 1;  
CtrlBit.bit.Bit16 = 1;
方法二:
CtrlBit.all = 0x8888;