Chinaunix首页 | 论坛 | 博客

fx

  • 博客访问: 1370114
  • 博文数量: 115
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 3964
  • 用 户 组: 普通用户
  • 注册时间: 2013-05-02 14:36
文章分类
文章存档

2022年(2)

2019年(2)

2018年(10)

2017年(1)

2016年(50)

2015年(12)

2014年(9)

2013年(29)

分类: C/C++

2016-08-23 20:15:37

移植驱动代码时发现工程中使用了如下形式的代码

typedef struct BlkTestTag{

         uint16_t m_u1Var1;

         uint8_t     reserve[2];

         uint32_t m_u4Var2;

}BlkTest;

 

uint8_t test_array[8] = {0x01,0x02,0x00,0x00,0x05,0x06,0x07,0x08};

 

pblkTest = (BlkTest *)test_array;

u2Var1 = pblkTest->m_u1Var1;

u4Var2 = pblkTest->m_u4Var2;

本意是为了将结构体的值以数组常量形式给出。

var1 = 0x0201;  var2 = 0x08070605

 

再将这些代码移植到另一款MCU时发现工作不正常。之后发现移植的这款MCU是大端的,而我们之前的使用的MCU都是小端的。于是定位上上面的数组赋值代码。

在小端MCU下会按我们想要的结果来赋值,如下图。Var1var2的结果是我们想要的。


但是如果再大端MCU上执行的话结果就大不一样。




原因在于数组uint8_t test_array[8] = {0x01,0x02,0x00,0x00,0x05,0x06,0x07,0x08};

其数据在内存中是从低地址到高地址存放的,如下图



0x20000000地址处为0x01, 0x20000000地址处为0x02……

所以直接将数组名转换成结构体指针后,在访问m_u1Var1成员时,该成员为2字节无符号整型,所以他会将0x20000000 0x20000001 地址处的两个值当做 m_u1Var1

 

所以如果是MCU 低地址0x20000000的值0x01会被解释为低字节

                              高地址0x20000001的值0x02会被解释为高字节

于是值就是 0x0201

         如果是MCU 低地址0x20000000的值0x01会被解释为高字节

                                 高地址0x20000001的值0x02会被解释为低字节

于是值就是 0x0102

这个数组到结构体的转换问题,在知道了大小端后问题定位还比较容易。

另一个关于联合体的问题就有点偏,即字节内的bit序在大小端MCU上也是不同的。

工程中存在如下代码

typedef union UnTestTag{

         uint8_t var;

         struct{

                   uint8_t bit0:1;

                   uint8_t bit1:1;

                   uint8_t bit2:1;

                   uint8_t bit3:1;

                   uint8_t bit4:1;

                   uint8_t bit5:1;

                   uint8_t bit6:1;

                   uint8_t bit7:1;

         }pixels;

}UnTest;

         在对联合体赋值后

unTest.var = 0x11;    二进制值为 00010001 

 

上面的联合体定义就是为了判断某些位,所以我们希望的是赋值0x11后, 位段的 unTest.pixels.bit0 unTest.pixels.bit4的值应该为1 才对。
即我们希望的是名字能反应真正的bit值

小端MCU中运行时的确是正确的。



但是在大端MCU运行时 结果如下图。

即 联合体中  unTest.pixels.bit0的值反应的不再是真正的二进制中的bit0

反而是unTest.pixels.bit7  反应的是真正的bit0. 

unTest.pixels.bit0- unTest.pixels.bit7   反应的真实值其实是bit7-bit0





一般情况下 字节内的bit序 是不可见的,除非使用了上面的位段的情况下才会发现。 
既然字节内bit序与大小端有段,那么为什么使用在使用 <<,  >>之类操作符时。都可以正常运行,这是芯片根据会自己更具自己大小端类型自动做相应的处理。

具体如下:
对于小端 如果 bit0 - bit7为   1000 1000
小端MCU 认为这个值为 0x11  当做 << 操作后  二进制为0100 0100   小端MCU认为其值为0x22 ,的确是0x11的左移操作结果
可以看出这里的 << 实际操作是向 bit7方向移位了

对于大端 如果bit0-bit7为   1000 1000
大端MCU认为这个值为0x88  当做 << 操作后   二进制为 0001 0000 ,大端MCU认为其值为0x10, 也的确是 0x88左移溢出后的结果

但是这里的 << 实际操作是向bit0方向移位了


附上一段简单测试代码,在keil工程中 选择模拟器调试



然后勾选big endian的情况下 debug实例代码,然后打端点查看变量值,结果就是大端情形下的。不勾选 big endian 再debug跑一次就是小端情形下的



typedef struct BlkTestTag{
    uint16_t m_u1Var1;
    uint8_t reserve[2];
    uint32_t m_u4Var2;
}BlkTest;

uint8_t test_array[8] = {0x01,0x02,0x00,0x00,0x05,0x06,0x07,0x08};

typedef union UnTestTag{
uint8_t var;
    struct{
        uint8_t bit0:1;
        uint8_t bit1:1;
        uint8_t bit2:1;
        uint8_t bit3:1;
        uint8_t bit4:1;
        uint8_t bit5:1;
        uint8_t bit6:1;
        uint8_t bit7:1;
    }pixels;
}UnTest;


BlkTest *pblkTest;
uint16_t u2Var1;
uint32_t u4Var2;
UnTest unTest;

int main(void){


    pblkTest = (BlkTest *)test_array;
    u2Var1 = pblkTest->m_u1Var1;
    u4Var2 = pblkTest->m_u4Var2;

    unTest.var = 0x11;


/*测试大小端 <<操作的
    unTest.pixels.bit0 = 1;
    unTest.pixels.bit4 = 1;

    unTest.var <<= 1;
*/
    while(1);
    return 0;
}



阅读(13207) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~