arduino 大小端数据问题

来源:互联网 发布:写筒谱的软件 编辑:程序博客网 时间:2024/04/28 18:14

以前在百度空间写的文章,现在百度空间关闭了,搬到这里吧,做个笔记吧。


arduino数据的存储方式是小端的,调试了两天的程序,终于发现这个问题了。

下面说一下发现问题的过程:

首先我定义了一个结构体,用来接收串口发送来的数据,串口发送来的数据首先放到数组中,再强制转换成结构体类型,定义结构体的意义就在于我想实现一种自定义的协议。

协议如下

命令头+命令长度+数据长度+命令+错误代码+数据+校验码

由于命令头是固定的,我的命令以0xFE开头,因此没有定义到结构体中。

结构体如下

/******************
 * UartCmdLen:UartCmd + UartErrCode
 * UartDataLen:UartDataBuf
 *******************/
typedef struct __sUartData{
  uint8_t UartCmdLen;
  uint8_t UartDataLen;
  uint16_t UartCmd;
  uint8_t  UartErrCode;
  uint8_t  UartDataBuf[1];
}
*psUartData;

 

定义的数组

uint8_t UartBuf[UART_MAX_LEN] = {  0 };

用于存放串口接收的数据。

把数组的数据转换成结构体格式

psUartDataRevSend = (psUartData)UartBuf;

这样访问发送来的数据就方便多了,比如psUartDataRevSend->UartCmdLen ,psUartDataRevSend->UartDataLen等。

但是发现发送来的数据发生了错误。读取数组的数据没有错误,访问结构体中uint8_t类型的数据也正常,uint16_t类型的数据却不符。

比如我发送如下数据(16进制)

FE 03 04 0003 00 00000000 FA

发送的不含空格,为方便大家看清协议,添加了空格。我发现收到的是0x300(或者是0x000300),而不是0x0003(也就是0x03),我怀疑uint16_t类型的数据多了一个字节。

首先我以为是int类型长度的问题,arduino uno的int是2个字节,arduino due的是4个字节,我的是uno,板子是uno,ide中选择的也是uno,

应该不是类型长度的问题。在arduino ide中如下定义也是编译通过的 uint8_t t=0xffffffff;,这个明显超出了范围,不知道原因啊。

其次我也为时结构体对齐的问题,我用sizeof()测试结构体的大小也是6,也不是这个问题,为保险起见,我用

#pragma pack(1)指定对齐方式,但是还是不对,应该也不是这个原因。

百思不得其解的时候,我决定多用几组数据去测试,后面是得到的uint16_t成员变量的值(16进制)

FE0304 0130 0001000000C9      3001
FE0304 0003 0000000000FA      300
FE0304 0102 0304050607FE      201
FE0304 0310 0001000000C9      1003

这样大家可能没发现什么问题,我给大家补齐一下

FE0304 0130 0001000000C9      3001
FE0304 0003 0000000000FA      0300
FE0304 0102 0304050607FE      0201
FE0304 0310 0001000000C9      1003

再转一下

FE0304    01 30    0001000000C9                    30 01
FE0304    00 03    0000000000FA                    03 00
FE0304    01 02    0304050607FE                    02 01
FE0304    03 10    0001000000C9                    10 03

应该发现uint16_t类型的数据高低位颠倒了,这就是大小端存储方式不同造成的。

下面分别是2字节和4字节转换的宏命令。arduino论坛有用union来转换4字节的方法,至于更大的数据float也是用union,再大的数据就是用数组,数组从大到小读取就能实现转换了。

 #define htons(A)  ((((uint16_t)(A) & 0xff00) >> 8 ) | (((uint16_t)(A) & 0x00ff) << 8 ))

   #define htonl(A)  ((((uint32_t)(A) & 0xff000000) >> 24)  |  (((uint32_t)(A) & 0x00ff0000) >> 8 )  |  (((uint32_t)(A) & 0x0000ff00) << 8 )  | (((uint32_t)(A) & 0x000000ff) << 24)) 

 

这样就能正确识别命令了htons(psUartDataRevSend->UartCmd);

下面show一下具体的使用代码

 switch(htons(psUartDataRevSend->UartCmd))
  { 
  case 0x0002:  //检测串口状态
    //check_com( psUartDataRevSend );
    break;
  case 0x0003: //查询卡号
    Serial.print("111");
    break;

  case 0x0110: //修改密码方式
    //updata_key( psUartDataRevSend );
    break;

  case 0x0120: //读数据块方式
    //block_read( psUartDataRevSend );
    break;

  case 0x0130: //写数据块方式
    //block_write( psUartDataRevSend );
    break;

  case 0x0140: //板子控制
    //ctrl_by_board( psUartDataRevSend );
    break; 

  default:
    psUartDataRevSend->UartErrCode = ERROR_NOCMD;
    break;
  }


0 0
原创粉丝点击