技术文档

crc校验 单片机 嵌入式系统中串口传输的校验算法简介

小编 2024-10-17 技术文档 23 0

嵌入式系统中串口传输的校验算法简介

在做嵌入式系统时,经常需要通过串口将数据传送给PC机,为了保证数据的正确性,往往需要进行校验,在传送一个字节的时候可以进行奇偶校验,传送一组数据的时候可以采用CRC校验等,但是在发送一组数据的时候采用CRC校验单片机往往太占资源,这就需要有一种比较简单的校验方法。

我们可以采用先学习下IP校验的方式:

当发送IP包时,IP报头的校验和为:

1、 把校验和字段置为0;

2、 对IP头部中的每16bit进行二进制求和;

3、 如果和的高16bit不为0,则将和的高16bit和低16bit反复相加,直到和的高16bit为0,从而获得一个16bit的值;

4、 将该16bit的值取反,存入校验和字段。

当接收IP包时,需要对报头进行确认,检查IP头是否有误,算法同上2、3步,然后判断取反的结果是否为0,是则正确,否则有错。

实例:

IP头:

45 00 00 31

89 F5 00 00

6E 06 00 00(校验字段)

DE B7 45 5D -> 222.183.69.93

C0 A8 00 DC -> 192.168.0.220

计算:

4500 + 0031 +89F5 + 0000 + 6e06+ 0000 + DEB7 + 455D + C0A8 + 00DC =3 22C4

0003 + 22C4 = 22C7

~22C7 = DD38 ->即为应填充的校验和

当接受到IP数据包时,要检查IP头是否正确,则对IP头进行检验,方法同上:

计算:

4500 + 0031 +89F5 + 0000 + 6E06+ DD38 + DEB7 + 455D + C0A8 + 00DC =3 FFFC

0003 + FFFC = FFFF

~FFFF = 00000 ->正确

TCP首部检验和与IP首部校验和的计算方法相同,在程序中使用同一个函数来计算。

在嵌入式系统中,如果是8位单片机,则校验变成了

1、 把校验和字段置为0;

2、 对要传输的数组每8bit进行二进制求和;

3、 如果和的高8bit不为0,则将和的高8bit和低8bit反复相加,直到和的高8bit为0,从而获得一个8bit的值;

4、 将该8bit的值取反,存入校验和字段。

当接收数据时,对校验和进行确认,检查是否有误,算法同上2、3步,然后判断取反的结果是否为0,是则正确,否则有错。

c程序为

unsigned char checksum(unsigned char *s,unsigned char n)

{

unsigned int check = 0;

unsigned char i, ch = 0;

for(i=0;i<n;i++)

{

check += *s++;

}

check = (check>>8) + (check&0xff); //高八位+低八位

check += (check>>8); //为防止进位,再加一次

ch = check&0x00ff;

return ~ch;

}

上面就是如何在嵌入式系统中如何进行串口传输的校验了,你学会了没有?

AOVX资产监测设备之华大单片机Boot软件升级

近期笔者在研究如何从软件方面提升资产监测设备的性能,从而提升用户的使用体验感。

资产监测设备的主要作用是帮助用户监测设备的实时位置、实时状态避免运动中的货物出现丢失等情况。AOVX环境监测设备在智慧物流方面发挥了重要的作用,例如在运输贵重货物过程中,只需要将该设备安装在货物中,用户即可远程了解货物的位置、货物是否出现暴力扔件、以及避免货物在运输过程中的丢失。

资产监测设备中的华大单片机,对该设备的工作发挥了重要的作用。笔者的软件团队对单片机进行了软件升级。

具体流程如下:

具体流程如下:/**

** \brief 上位机数据帧解析及处理**** \param [in] None**** \retval Ok APP程序升级完成,并接受到跳转至APP命令** \retval OperationInProgress 数据处理中** \retval Error 通讯错误********************************************************************************/en_result_t Modem_Process(void){uint8_t u8Cmd, u8FlashAddrValid, u8Cnt, u8Ret;uint16_t u16DataLength, u16PageNum, u16Ret;uint32_t u32FlashAddr, u32FlashLength, u32Temp;

if (enFrameRecvStatus == FRAME_RECV_PROC_STATUS) //有数据帧待处理, enFrameRecvStatus值在串口中断中调整

{

u8Cmd = u8FrameData[PACKET_CMD_INDEX]; //获取帧指令码

if (PACKET_CMD_TYPE_DATA == u8FrameData[PACKET_TYPE_INDEX]) //如果是数据指令

{

u8FlashAddrValid = 0u;

u32FlashAddr = u8FrameData[PACKET_ADDRESS_INDEX] + //读取地址值

(u8FrameData[PACKET_ADDRESS_INDEX + 1] << 8) +

(u8FrameData[PACKET_ADDRESS_INDEX + 2] << 16) +

(u8FrameData[PACKET_ADDRESS_INDEX + 3] << 24);

if ((u32FlashAddr >= (FLASH_BASE + BOOT_SIZE)) && (u32FlashAddr < (FLASH_BASE + FLASH_SIZE))) //如果地址值在有效范围内

{

u8FlashAddrValid = 1u; //标记地址有效

}

}

switch (u8Cmd) //根据指令码跳转执行

{

case PACKET_CMD_HANDSHAKE : //握手帧 指令码

u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //返回状态为:正确

Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //发送应答帧给上位机

break;

case PACKET_CMD_ERASE_FLASH : //擦除flash 指令码

if ((u32FlashAddr % FLASH_SECTOR_SIZE) != 0) //如果擦除地址不是页首地址

{

u8FlashAddrValid = 0u; //标记地址无效

}

if (1u == u8FlashAddrValid) //如果地址有效

{

u32Temp = u8FrameData[PACKET_DATA_INDEX] + //获取待擦除flash尺寸

(u8FrameData[PACKET_DATA_INDEX + 1] << 8) +

(u8FrameData[PACKET_DATA_INDEX + 2] << 16) +

(u8FrameData[PACKET_DATA_INDEX + 3] << 24);

u16PageNum = FLASH_PageNumber(u32Temp); //计算需擦除多少页

for (u8Cnt=0; u8Cnt<u16PageNum; u8Cnt++) //根据需要擦除指定数量的扇区

{

u8Ret = Flash_EraseSector(u32FlashAddr + (u8Cnt * FLASH_SECTOR_SIZE));

if (Ok != u8Ret) //如果擦除失败,反馈上位机错误代码

{

u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ERROR;

break;

}

}

if (Ok == u8Ret) //如果全部擦除成功,反馈上位机成功

{

u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK;

}else //如果擦除失败,反馈上位机错误超时标志

{

u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_TIMEOUT;

}

}

else //地址无效,反馈上位机地址错误

{

u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR;

}

Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //发送应答帧到上位机

break;

case PACKET_CMD_APP_DOWNLOAD : //数据下载 指令码

if (1u == u8FlashAddrValid) //如果地址有效

{

u16DataLength = u8FrameData[FRAME_LENGTH_INDEX] + (u8FrameData[FRAME_LENGTH_INDEX + 1] << 8)

- PACKET_INSTRUCT_SEGMENT_SIZE; //获取数据包中的数据长度(不包含指令码指令类型等等)

if (u16DataLength > PACKET_DATA_SEGMENT_SIZE) //如果数据长度大于最大长度

{

u16DataLength = PACKET_DATA_SEGMENT_SIZE; //设置数据最大值

}

u8Ret = Flash_WriteBytes(u32FlashAddr, (uint8_t *)&u8FrameData[PACKET_DATA_INDEX], u16DataLength); //把所有数据写入flash

if (Ok != u8Ret) //如果写数据失败

{

u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ERROR; //反馈上位机错误 标志

}

else //如果写数据成功

{

u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反馈上位机成功 标志

}

}

else //如果地址无效

{

u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR; //反馈上位机地址错误

}

Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //发送应答帧到上位机

break;

case PACKET_CMD_CRC_FLASH : //查询flash校验值 指令码

if (1u == u8FlashAddrValid) //如果地址有效

{

u32FlashLength = u8FrameData[PACKET_DATA_INDEX] +

(u8FrameData[PACKET_DATA_INDEX + 1] << 8) +

(u8FrameData[PACKET_DATA_INDEX + 2] << 16) +

(u8FrameData[PACKET_DATA_INDEX + 3] << 24); //获取待校验flash大小

if ((u32FlashLength + u32FlashAddr) > (FLASH_BASE + FLASH_SIZE)) //如果flash长度超出有效范围

{

u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_FLASH_SIZE_ERROR; //反馈上位机flash尺寸错误

}else

{

u16Ret = Cal_CRC16(((unsigned char *)u32FlashAddr), u32FlashLength);//读取flash指定区域的值并计算crc值

u8FrameData[PACKET_FLASH_CRC_INDEX] = (uint8_t)u16Ret; //把crc值存储到应答帧

u8FrameData[PACKET_FLASH_CRC_INDEX+1] = (uint8_t)(u16Ret>>8);

u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反馈上位机成功 标志

}

}

else //如果地址无效

{

u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR; //反馈上位机地址错误

}

Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE+2); //发送应答帧到上位机

break;

case PACKET_CMD_JUMP_TO_APP : //跳转至APP 指令码

Flash_EraseSector(BOOT_PARA_ADDRESS); //擦除BOOT parameter 扇区

u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反馈上位机成功

Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //发送应答帧到上位机

return Ok; //APP更新完成,返回OK,接下来执行跳转函数,跳转至APP

case PACKET_CMD_APP_UPLOAD : //数据上传

if (1u == u8FlashAddrValid) //如果地址有效

{

u32Temp = u8FrameData[PACKET_DATA_INDEX] +

(u8FrameData[PACKET_DATA_INDEX + 1] << 8) +

(u8FrameData[PACKET_DATA_INDEX + 2] << 16) +

(u8FrameData[PACKET_DATA_INDEX + 3] << 24); //读取上传数据长度

if (u32Temp > PACKET_DATA_SEGMENT_SIZE) //如果数据长度大于最大值

{

u32Temp = PACKET_DATA_SEGMENT_SIZE; //设置数据长度为最大值

}

Flash_ReadBytes(u32FlashAddr, (uint8_t *)&u8FrameData[PACKET_DATA_INDEX], u32Temp); //读flash数据

u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反馈上位机成功 标志

Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE + u32Temp);//发送应答帧到上位机

}

else //如果地址无效

{

u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR; //反馈上位机地址错误 标志

Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //发送应答帧到上位机

}

break;

case PACKET_CMD_START_UPDATE : //启动APP更新(此指令正常在APP程序中调用)

u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反馈上位机成功 标志

Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //发送应答帧到上位机

break;

}

enFrameRecvStatus = FRAME_RECV_IDLE_STATUS; //帧数据处理完成,帧接收状态恢复到空闲状态

}

return OperationInProgress; //返回,APP更新中。。。

}

同样从事软件工作相关的用户,对资产监测设备感兴趣的话可以参考该文档。

相关问答

单片机 中 ADC 是如何进行采集的?

摘要:本文设计并实现了基于2.4GHzISM频段射频收发芯片nRF2401的计算机短距离无线数据采集系统。该系统采用PC作为系统控制中心,以C8051F021单片机...

51 单片机 和stm 单片机 的区别?

两者的主要区别如下:1、内核:51单片机采用的是51Core,8Bit@2MHzMax(分频后),0.06DMIPS;STM32采用的是ARMCortex-M3,32Bit@72MHz,1....

猜你喜欢