分享

位域结构体

 花园图书馆88 2013-08-10

位域结构体详细解析

 这一节,我们针对大家提出的有关定义寄存器结构体的问题进行解析。在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 int32位,跟unsigned int是一样的,unsigned long long int才是64位的。

有人会问,在寄存器的表格中,寄存器的位数是015的,也就是16位,那你为什么定义成32位的呢?其实,这个问题涉及到了NIOS II的地址对齐问题,它是属于硬件构架的范畴。

当系统中存在数据宽度不匹配的主从端口时就要考虑地址对齐的问题。地址对齐分为两类,一类是静态地址对齐,另一类就是动态地址对齐。一般来说存储器外设使用动态地址对齐,而寄存器外设使用静态地址对齐,之所以是这样,是由动态地址对齐和静态地址对齐的特点决定的,在静态地址对齐方式下,主端单次传输对应从端口的一次传输,而在动态地址对齐方式下,一个主端口读传输,则要引起多次从端口读传输。想要更加具体的了解他们特点的,大家自行查找吧,我在这里就不详细叙述了。

     我们要将寄存器定义为unsigned long int类型,就跟这个静态地址对齐有关系了。现在我们是UART端口16位,而NIOS II主端口32位的情况,在这种情况下,NIOS II主端口与16UART端口进行数据传输时,只有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;//清除状态寄存器

对于其他的寄存器都是一样的,在这里不再重复了。

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多