位域結構體詳細解析


 這一節,我們針對大家提出的有關定義寄存器結構體的問題進行解析。在NIOS II軟件開發過程中,如果使用我們提出的寄存器操作方式的話,首先需要定義一個寄存器結構體,之所以這樣做是為了在軟件書寫過程中操作方便,更是為了增強程序的可讀性。我們就拿UART來舉例說明。

      首先,我們看一下UART的寄存器說明,如下表所示

點擊看大圖

      我們通過上表可以看到,UART包括6個寄存器(由於最后一個寄存器一般不用,所以建立的結構體中沒有加入它),假設基地址為0x00的話,那么他們的地址分別為0x00,0x01,0x02,0x03,0x04,0x05。也就是說,各個寄存器之間是存在順序的。那么,在我們建立結構體過程中也要注意他們的順序問題。建立的結構體如下所示

typedef struct{
    union{
        struct{
            volatile unsigned long int RECEIVE_DATA    :8;
            volatile unsigned long int NC                :24;
        } BITS;
        volatile unsigned long int WORD;
    } RXDATA;

    union{
        struct{
            volatile unsigned long int TRANSMIT_DATA    :8;
            volatile unsigned long int NC                 :24;
        } BITS;
        volatile unsigned long int WORD;
    } TXDATA;

    union{
        struct{
            volatile unsigned long int PE       :1;
            volatile unsigned long int FE       :1;
            volatile unsigned long int BRK      :1;
            volatile unsigned long int ROE      :1;
            volatile unsigned long int TOE      :1;
            volatile unsigned long int TMT      :1;
            volatile unsigned long int TRDY     :1;
            volatile unsigned long int RRDY     :1;
            volatile unsigned long int E        :1;
            volatile unsigned long int NC       :1;
            volatile unsigned long int DCTS     :1;
            volatile unsigned long int CTS      :1;
            volatile unsigned long int EOP      :1;
            volatile unsigned long int NC1      :19;
        } BITS;
        volatile unsigned long int WORD;
    } STATUS;

    union{
        struct{
            volatile unsigned long int IPE       :1;
            volatile unsigned long int IFE       :1;
            volatile unsigned long int IBRK      :1;
            volatile unsigned long int IROE      :1;
            volatile unsigned long int ITOE      :1;
            volatile unsigned long int ITMT      :1;
            volatile unsigned long int ITRDY     :1;
            volatile unsigned long int IRRDY     :1;
            volatile unsigned long int IE        :1;
            volatile unsigned long int TRBK         :1;
            volatile unsigned long int IDCTS     :1;
            volatile unsigned long int RTS       :1;
            volatile unsigned long int IEOP      :1;
            volatile unsigned long int NC        :19;
        } BITS;
        volatile unsigned long int WORD;
    } CONTROL;

    union{
        struct{
            volatile unsigned long int BAUD_RATE_DIVISOR    :16;
            volatile unsigned long int NC                      :16;
        } BITS;
        volatile unsigned long int WORD;
    } DIVISOR;

}UART_STR;

      對於這樣一個大的結構體,我們來逐層分析一下:

第一, 整個結構體由5個共用體組成,共同體的順序是由寄存器的偏移量決定的,這一點前面已經有所敘述。

第二, 每個共用體由一個結構體和一個volatile unsigned long int型的變量組成。

第三, 共用體中的結構體由位域構成,位域中的內容也是存在順序的,這個順序是由寄存器的結構決定,而且是由低到高排列。其中,NC表示該位為空位或保留,不能對其進行操作。

      通過大家的反饋,除了語法問題以外,有兩個問題需要說明一下:

1. 為什么里面的變量都定義成unsigned long int

      首先需要說明一點,在NIOS II中,unsigned long int是32位,跟unsigned int是一樣的,unsigned long long int才是64位的。

有人會問,在寄存器的表格中,寄存器的位數是0到15的,也就是16位,那你為什么定義成32位的呢?其實,這個問題涉及到了NIOS II的地址對齊問題,它是屬於硬件構架的范疇。

當系統中存在數據寬度不匹配的主從端口時就要考慮地址對齊的問題。地址對齊分為兩類,一類是靜態地址對齊,另一類就是動態地址對齊。一般來說存儲器外設使用動態地址對齊,而寄存器外設使用靜態地址對齊,之所以是這樣,是由動態地址對齊和靜態地址對齊的特點決定的,在靜態地址對齊方式下,主端單次傳輸對應從端口的一次傳輸,而在動態地址對齊方式下,一個主端口讀傳輸,則要引起多次從端口讀傳輸。想要更加具體的了解他們特點的,大家自行查找吧,我在這里就不詳細敘述了。

     我們要將寄存器定義為unsigned long int類型,就跟這個靜態地址對齊有關系了。現在我們是UART端口16位,而NIOS II主端口32位的情況,在這種情況下,NIOS II主端口與16位UART端口進行數據傳輸時,只有32位的低16位有效,但是高16位也占用了地址空間,也就是說,UART端口的16位實際上是占用了32位的。假設我們現在的基地址是0X00,那么6個寄存器他們相對基地址的偏移分別為0X00,0X01,0X02,0X03,0X04,0X05;那么,從主端口看,這6個寄存器的地址分別為0X00,0X04,0X08,0X0C,0X10,0X14,而不是0X00,0X01,0X02,0X03,0X04,0X05,也不是0X00,0X02,0X06,0X08,0X0A,0XC,這一點大家要特別注意。

2. 為什么要建立這樣一個共用體呢,又有位域結構體又有一個volatile unsigned long int WORD變量,WORD有啥用呢?

      共用體的特點就是其中的成員占用同一個存儲空間。也就說,由位域組成的結構體跟WORD是占用同一存儲空間,而且他們都是volatile unsigned long int類型,那么,結構體中的每一個位域成員都對應WORD的一個位。當我們需要單獨處理一個位的時候,我們就可以用位域,如下所示  

RS232->CONTROL.BITS.IRRDY=1;//接收准備好中斷使能

如果我們想要對狀態寄存器整體清零呢,我們就可以用到WORD了,如下所示  

RS232->STATUS.WORD=0;//清除狀態寄存器

對於其他的寄存器都是一樣的,在這里不再重復了。

好了,這一節內容就講完了。如果大家對此有疑問,或者發現我講的內容有問題可以直接跟我聯系,郵箱:avic633@gmail.com;qq:984597569,同時歡迎大家加入我們的NIOS群:109711029,大家共同探討NIOS技術!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM