#C51串口通讯4-#一串数据#中断即时解析用户自定义协议(握手接收应答)

系列文章目录-串口通讯

/C51串口通讯调试目录:/
第5章:#C51串口通讯5-#一串数据#中断定时+超时接收+接收应答+CRC校验

第4章:#C51串口通讯4-#一串数据#中断即时解析用户自定义协议(握手接收应答)

第3章:#C51串口通讯3-#一串数据#中断即时解析用户自定义协议

第2章:#C51串口通讯2-#一串数据#定时中断实现超时接收(推荐)

第1章:#C51串口通讯1-#一串数据#接收与发送(基础概念)


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 系列文章目录-串口通讯
  • 前言
  • 一、场景
  • 二、编程实现
    • 1.自定义协议
    • 2.代码设计
    • 3.测试验证
  • 总结


前言

提示:
1.上一章测试一种方法:简单协议下利用串口中断实时接收数据并校验后进行解析。
2.实际项目开发时,主机下发命令后,从机首先进行握手确认,数据错误情况下要进行相应回应(如错误指令)。
3.本章继续丰富开发,增加主从应答机制


提示:以下是本篇文章正文内容,下面案例可供参考

一、场景

示例:
主机下发命令,从机中断解析并应答,主函数处理事件

二、编程实现

1.自定义协议

如:

帧头 地址 数据类型 数据长度 数据区 校验
AA66AA / 01~03 和校验&&异或校验

##主机类型定义命令类型(查询,设置,器件控制等等)
以控制数码管显示0x01为例

##从机握手应答:
a.数据正确回复:BB66BB8000[SUM_CHECK][XOR_CHECK]
b.和校验错误回复:BB66BB8100[SUM_CHECK][XOR_CHECK]
c.异或校验错误回复:BB66BB8200[SUM_CHECK][XOR_CHECK]

数据区数值显示在数码管上,仅作最大2组显示为例

2.代码设计

第一步:基于已调试过的工程,确认一串数据正确被接收并返回

第二步:配置UART中断服务函数:
1.判断帧头2.数据长度确认3.数据缓存4.校验判断5.应答:正确或错误提示

void uart_ISR() interrupt 4
{	
	UC i;
	if(RI)
	{
		RI = 0;
		
		//超时接收处理方案
//		timer_start = 1;
//		recv_buf[recv_Cnt] = SBUF;
//		recv_Cnt++;
//		ctimer_Cnt = 0;

		//中断即时解析处理方案
		recv_data = SBUF;	
		switch(machine_step)
		{
			case 0:
				if(0xAA == recv_data)		//1.帧头符合,状态+1,下一次中断进入后继续检测,否则丢弃
				{
					machine_step = 1;		
				}
				else
				{
					machine_step = 0;
				}
				break;
			case 1:
				if(0x66 == recv_data)
				{
					machine_step = 2;
				}
				else
				{
					machine_step = 0;
				}
				break;
			case 2:
				if(0xAA == recv_data)
				{
					machine_step = 3;
					recv_Cnt = 0;			//2.即将进入数据区,计数启动
				}
				else
				{
					machine_step = 0;
				}
				break;
			case 3:
				sum_check = recv_data;
				xor_check = recv_data;
				recv_buf[recv_Cnt] = recv_data;	//3.数据类型,存第一个数
				machine_step = 4;
				break;
			case 4:
				data_length = recv_data;	//	数据长度				
				sum_check += recv_data;
				xor_check ^= recv_data;
				recv_Cnt++;
				recv_buf[recv_Cnt] = recv_data; 
				machine_step = 5;
				break;
			case 5:
				sum_check += recv_data;
				xor_check ^= recv_data;
				recv_Cnt++;
				recv_buf[recv_Cnt] = recv_data;
				if(data_length < recv_Cnt)			//不满足时,依旧是状态5
				{
					machine_step = 6;
				}				
				break;
			case 6:
				if(sum_check == recv_data)	//5.校验判断 //recv_data已经是新的一个字节,
				{
					machine_step = 7;	
				}
				else
				{
					machine_step = 0;			//累加和错误,下次重新开始判断
					sum_check = 0;				//校验清0,否则下一串数据在状态3时出错
					xor_check = 0;	
					
					//7.接收应答:和校验错误提示  
					for(i = 0; i<7; i++)
					{
						sendByte(sum_check_error[i]);
					}
					
				}
				break;
			case 7:
				if(xor_check == recv_data)		
				{
					recv_flag = 1;			//6.数据正确,标志置1			
					cRealLen = recv_Cnt + 1;		//缓冲区长度(数据类型+数据长度+数据区)

					//7.接收应答:接收正确
					for(i = 0; i<7; i++)
					{
						sendByte(recv_correct[i]);
					}
				}
				else
				{
					machine_step = 0;			//异或校验错误,下次重新开始判断

					//8.接收应答:异或校验错误提示
					for(i = 0; i<7; i++)
					{
						sendByte(xor_check_error[i]);
					}
				}
				sum_check = 0;				//校验清0,否则下一串数据在状态3时出错
				xor_check = 0;	
				machine_step = 0;			//++的数都要记得在某个地方清0	
				recv_Cnt = 0;				//不清也ok,养成习惯清除
				break;				
				
			default:break;
		}
		
	}
}

其中,便于演示,应答提示提前分配ROM常量:

unsigned char code recv_correct[] = {0xBB,0x66,0xBB,0x80,0x00,0x80,0x80};
unsigned char code sum_check_error[] = {0xBB,0x66,0xBB,0x81,0x00,0x81,0x81};
unsigned char code xor_check_error[] = {0xBB,0x66,0xBB,0x82,0x00,0x82,0x82};

第3步:主函数处理:
缓冲区数据:数据类型+数据长度+数据区
功能:数据区数值显示在数码管上,16进制

if(recv_flag)
	{
		recv_flag = 0;
//		timer_start = 0;		//关掉定时器,防止T0一直在执行
		

		for (i = 0; i < cRealLen; i++)
		{
			uart_Recv[i] = vbuf[i];
		}	
//		sendString(vbuf); //test
		sendString(uart_Recv);
		Exchange_Func();
		
		clr_recvbuffer(vbuf);		
	}
void Exchange_Func(void)
{
	if(0x01 == uart_Recv[0])		//仅作演示,数据类型01控制数码管显示
	{
		switch(uart_Recv[1])		//数据长度代表显示的位数
		{
			case 1:
				SEG_DisBuf[0] = 23;
				SEG_DisBuf[1] = 23; 		
				SEG_DisBuf[2] = uart_Recv[2] >> 4;
				SEG_DisBuf[3] = uart_Recv[2] & 0x0F;
				break;
			case 2:
				SEG_DisBuf[0] = uart_Recv[2] >> 4;
				SEG_DisBuf[1] = uart_Recv[2] & 0x0F; 	
				SEG_DisBuf[2] = uart_Recv[3] >> 4;
				SEG_DisBuf[3] = uart_Recv[3] & 0x0F;
				break;
			case 3:
				break;
			default:break;
		}
	}

3.测试验证

实测ok
#C51串口通讯4-#一串数据#中断即时解析用户自定义协议(握手接收应答)_第1张图片


总结

1.限制于无仿真器,故在UART中断服务函数中调试时,插入几处sendByte(),方便观察进行到哪一步。发现数据紊乱,原因在于一帧数据溢出进入服务函数时,调用的sendByte()又进入一次函数,数据被覆盖。
2.样例为方便演示。实际情况下,接收应答的处理可以设置标志位,在主函数中处理,中断服务函数中工作尽量少。
3.样例较上一章优点在于:不局限数据长度、接收应答机制

上一章:#C51串口通讯3-#一串数据#中断即时解析用户自定义协议(固定长度)

下一章:#C51串口通讯5-#一串数据#中断定时+超时接收+接收应答+CRC校验

你可能感兴趣的:(IC_51,串口通信,c语言,单片机)