实验笔记:校验和与串口通信实例
数据通信中的错误校验
数据通信难免发生错误,为了让接收端判断数据传输过程是否发生错误,需要在发送的数据中传送额外的附加数据,附加数据常用的是校验和与CRC。CRC的计算很占用时间,适用于对数据准确性要求很苛刻的场合;校验和对数据准确性的判断能力低于CRC,但占用时间极少,为了缩短程序执行时间和简化程序编写,建议优先选用校验和作为数据校验方式。
校验和( CheckSum):
校验和的方法就是把需要发送或接收的一组数据(或字符的 ASCII码)进行相加计算,计算其总和后将此数据与某一数字(通常是256)相除,取其余数,将此余数组合成发送数据的一部分发送出去。
同样,接收数据的一方也以相同的方式将所发送过来的数据进行相加计算,并与发送方所发过来的计算值比较,若其值相同,则代表所发送的数据是正确的,反之则是错误的,检查错误时,接收方可能要求发送方重新发送,以确保数据的正确性。
例如,被发送的数值为0xAB 0xCD 0xEF 0x01 0x020 x03,将它们的数值相加结果是0x026D,以十进制表示为621,与256相除后取余数,其值为109,再转换成十六进制为0x6D。
发送数据时在数据的尾端再加上一个字节0x6D,因此实际发送出去的数据成为:
0xAB 0xCD 0xEF 0x01 0x02 0x03 0x6D,
对方收到所发送的数据后会根据以上方式再进行一次计算,如果计算出来的结果是0x6D,表示此次发送数据是正确的。
在单片机中,一般是定义一个无符号整型变量,用这个无符号整型变量存储多个二进制数值相加的结果,然后将这个整型变量对256求余数(C语言中的运算符为%),求余后的数值不会大于255,是整数,所以还需要强制转换成无符号char型才能发送出去,这里的强制转换实际上就是把整数的低字节保留,高字节舍弃。
校验和一般用于环境干扰不大或对数据准确性要求不算太高的场合,最大的优点是计算过程简单,占用单片机时间极少,缺点是有少数错误检查不出来,比如发送2个数据,100与200,校验和(100+200)%256=44,若数据发送过程中受到干扰100变成了110,200变成了190,接收方对数据校验(110+190)%256=44,与发送方计算的校验和相同,这时就把错误的数据当作正确的数据处理了。
例:串口通信的完整格式与校验和实例
通信协议:每次计算机串口助手向单片机发送5个字节的数据,
第一个字节为0x7E,数据开始标志(即帧头),后面3个字节为任意数据,最后一个字节为前4个数据和的低字节(高字节忽略),即校验和。
单片机接收到5个字节后,如果校验正确,发回第1字节0x7E作为帧头,2、3、4字节为接收的2、3、4字节加1后的数据,第5字节为前4个字节的校验和。
使用11.0592MHz内部R/C时钟,波特率9600,最为常见的N.8.1帧格式(无奇偶校验、8位数据位、1位停止位)。
测试方法:在STC串口助手发送区输入数据:7E,12,34,56,1A,选择HEX发送,HEX显示,单击发送后接收窗口立即显示7E,13,35,57,1D,则测试成功。如果串口助手向单片机发送数据后接收不到单片机返回数据,重点检查波特率设置是否正确。
51单片机串口接收和发送校验和测试程序:接收采用中断方式,发送采用查询方式,选择T2作串口波特率发生器,这样可以使T0,T1空出来做别的事。
关于格式化输出:
%0x和%x都是以十六进制格式右对齐输出,输出的是无符号数。
在不指定占宽情况下以数据的实际宽度输出,而系统又自动消除左端的无效0,所以%0x和%x在显示效果上没有什么不同。
在指定占宽的情况下,在指定的输出占宽范围内,实际数据宽度不足时用%0x作控制的前面用0补齐,而用%x作控制的前面用空格补齐。如:
printf(“%04X\n”);
表示十六进制显示,占4个字符的位置,不足的补0。
源代码:
#include "STC15W4K.H"
#define uchar unsigned char
#define uint unsigned int
AUXR |= 0x10; //启动定时器2
ES=1; //开启UART1中断
EA=1; //开启总中断
}
void sendcombytes(uchar *ptr, uchar len)
{
uchar idata i;
for(i=0; i<len; i++)
{
SBUF=*(ptr+i);
while(TI==0);
TI=0;
}
}
//串口1中断服务程序
void UART1(void) interrupt 4
{
if(RI) //如果串口1发生接收中断
{
RI=0; //清接收中断标志
if(RecCount==5) RecCount=0;
RecBuf[RecCount]=SBUF;
if(RecCount==0) //判断帧头是否正确
{
if(RecBuf[RecCount]==FMBEGIN) //帧头正确
{
RecCount++;
}
else
{ //等待接收帧头
RecCount=0;
}
}
else
{
RecCount++;
}
}
}
uchar CheckSum(uchar *ptr, uchar len)
{
uchar idata i;
uchar idata a;
uint idata Value=0;
for(i=0; i<len; i++)
{
Value=Value+ptr[i];
}
a=Value;
return(a);
}
//主函数入口
void main()
{
uchar data i;
uchar idata CheckValue;
UART1_init(); //串行口初始化
while(1)
{
if(RecCount==5)
{
RecCount=0;
CheckValue=CheckSum(RecBuf,4);
if(CheckValue==RecBuf[4])
{
SendBuf[0]=FMBEGIN;
for(i=1; i<4; i++)
{
SendBuf[i]=RecBuf[i]+1;
}
CheckValue=CheckSum(SendBuf,4);
SendBuf[4]=CheckValue;
sendcombytes(SendBuf,5);
}
else
{
SendBuf[0]=FMBEGIN;
for(i=1; i<5; i++)
{
SendBuf[i]=0xaa;
}
sendcombytes(SendBuf,5);
}
}
}//while(1)
}
实验效果:
联系客服