从没想过串口通信CRC校验不通过是因为大小端引起的,真是好痛的领悟。。。假设你是玩单片机的,又和安卓屏或者其它类型的屏幕通信,那要注意了。对方的通信协议数据可能是这样定义的:
data.D1_SN = Mcont.chipId;
data.D2_warning = Mcont.warning;
data.D3_fliter_time = Mcont.save_data.filter_time;
data.D4_ecell_time = Mcont.save_data.vtimer_all;
data.D5_mcu1_ver = MCU1_SVER;
data.D6_muc2_ver = Mcont.slave.D16_ver;
data.D7_main_state = Mcont.status;
data.D8_work_state = Mcont.point;
data.Dx:内存有可能是一个字节,也有可能是两个以上的字节,假设是两个字节,数据只发送一个uint16_t类型的数据,数值为0x1234,那么这个数据通过串口实际的输出是什么?
串口发送: 第一个字节 第二个字节
0x12 0x34(大端)
0x34 0x12(小端)
实际痛苦的经历是这样的,接收数据和发送方确认是对的,但是CRC校验无论如何都通不过,经过一番仔细操作后发现,CRC校验高低字节顺序是不对的,于是呼呼呼。。。问发送端要了他的CRC校验代码(实锤了):
//***CRC校验 CRC16-MODBUS类型 eof为帧尾0表示没有 web尾0x23 endian 大小端模式 0大端 1小端**********************************************
void addCrc16_MODBUS(uint8_t *data1, uint16_t len,uint8_t eof,uint8_t endian)
{
uint16_t i, pos, crc = 0xFFFF;
for (pos = 0; pos < len; pos++)
{
if ((signed char)data1[pos] < 0)
{
crc ^= (int)((signed char)data1[pos]) + 256;
}
else
crc ^= (int)((signed char)data1[pos]);
for (i = 8; i != 0; i--)
{
if ((crc & 0x0001) != 0)
{
crc >>= 1;
crc ^= 0xA001;
}
else
{
crc >>= 1;
}
}
}
if(endian)
{
data1[len] = crc;
data1[len + 1] = crc >> 8;
}
else{
data1[len] = crc >> 8;
data1[len + 1] = crc;
}
if(eof)
data1[len + 2] = eof; //帧尾
}
第一次见CRC还有带大小端转换的功能,长见识了:
我们接触大部分单片机都是小端模式,但是也有大端模式例如经典的51单片机。
如何写一段代码判断设备是大端还是小端模式:
#include <stdio.h>
int main(){
union{
int n;
char ch;
} data;
data.n = 0x00000001; //也可以直接写作 data.n = 1;
if(data.ch == 1){
printf("Little-endian\n");
}else{
printf("Big-endian\n");
}
return 0;
}
在电脑上的运行结果是:
Little-endian
实际大小端模式在内存中的区别如下: