Uart串口通讯协议与环形队列(裸机/RTOS)

MCU上使用的稳定Uart通讯协议(环形队列)

协议的主要内容:

接收:字节间超时判断、环形队列接收、非阻塞式接收整帧数据、接收查错;
发送:未应答重发(超过3次后反馈错误指令,若有应答继续发送原来数据)、
	 可选发送次数和间隔时间、CRC校验、环形队列解码;
若在RTOS中使用需添加互斥锁;

Uart总结:

  1. Uart—通用异步收发器,按位进行数据收发的一种串行通信接口,相比于IIC\SPI,Uart没有CLK线使其保持同步,在RS485、LIN总线中可见等。
  2. 使用硬件串口时,只需了解中断/DMA的逻辑即可实现收发,在TX/RX时,使用的是同名异址寄存器SBUF,接收/发送寄存器SBUF的地址虽然相同,但在物理上它们是2个不同的寄存器,一个只能读取,一个只能写入,以此保证不会出现紊乱。
  3. 各种串口、并口协议只需搞懂 0 / 1 表达的方式,其他大致相同。

移植过多种MCU、SOC,暂无问题
数据帧:
在这里插入图片描述

接收和发送数据都存放在循环队列中,以队列为空判断是否发送或处理接收到的数据

可在头文件中使用宏控制参数

#define UART_REC_BUF_GROUP 10   //接收循环队列
#define UART_SEND_BUF_GROUP 10  //发送循环队列
#define UART_BUF_SIZE 	30	    //发送与接收长度
#define UART_FIXED_BYTE 6		  //一帧数据中的固定字节数
#define UART_LENGTH_POSITION 2  //数组中代表帧长度 的位置

#define UART_REC_OVER_TIME_CNT 	100  //500ms ,字节与字节之间接收超时设置的阈值时间
#define UART_SEND_TIMES 		3          //一个数据帧发送次数
#define UART_SAME_DATA_INTERVAL_TIME 10 	//同一帧多次发送的间隔时间
#define UART_SEND_FINISH_INTERVAL_TIME 50   //发送完成间隔时间
#define ACK_OVERTIME_TIME 200	 	 		//ACK应答超时时间
#define UART_ACK_RESEND_TIMES 3		 		//限制的重发次数

#define STARTMARK 0x41 			//起始符//0xAA  
#define ENDMARK   0x55      	//0x55  //结束符
#define ENDMARK_SIZE 1			//结束符个数
#define CRC_CODE_SIZE 2			//CRC校验码个数
/* 
	1byte起始符  1byte类型(最高位预留,剩下7位为数据类型)  1byte数据长度   nbyte数据    2byte校验码  1byte结束符
	((type & 0x7F) == UartEventType)
	数据长度 = nbyte + 6byte
	CRC校验参数模型:CRC-16/MODBUS  x16+x15+x2+1
*/
//能使用堆空间时,初始化使用malloc();创建堆空间
typedef struct Queue 			//头删,尾插
{
	int MaxSize;				//最大个数
	int head;        			//队头
	int fail;					//队尾
	unsigned char (*DataBuf)[UART_BUF_SIZE]; //队列缓冲区
}UartQueue;

extern uchar Gu8RecStep;				//接收步骤
//extern uchar Gu8UartRecByteSum;  		//接收字节数
extern uchar Gu8UartRecFeedDog;     	//喂狗,字节与字节之间的接收时间是否超时
extern uchar Gu8UartRecQueueBufScriptNub;//接收内存下标

extern uchar UartRecQueueBuf[UART_REC_BUF_GROUP][UART_BUF_SIZE];//缓冲区
extern uchar UartSendQueueBuf[UART_SEND_BUF_GROUP][UART_BUF_SIZE]; 
extern uchar UartSendBuf[UART_BUF_SIZE];	

extern UartQueue UartRecQueueInfo;	//接收队列
extern UartQueue UartSendQueueInfo; //发送队列

//=========================================================
typedef enum //串口接收或发送的第二个字节为当前数据帧的类型            
{       
    UART_FET,			//自定义
	UART_CONTROL,	
    UART_ALARM,			
	UART_CONFIG,		
	UART_ACK,			//应答类型
}UartEventType;
//====================================
void UartQueueAdd(UartQueue *UartQueue,uchar info[]);   //入队
void UartSendData_Task(UartQueue *UartQueue);			  
void UartRecDataHandle_Task(UartQueue *UartQueue); 	
uint UartCrcCheck(uchar *buf,uchar lenth);				         //CRC校验
//======================UserDemo
/*使用时,先用Handle处理数据,再用SendData发送SendBuf*/
extern void UartQueueInit(UartQueue *UartQueue,uchar (*Buf)[],bit Type);  //初始化队列,并添加一个队列
extern void UartSendDataHandle(const uchar *DataBuf,uchar DataBufLength,UartEventType DataType,uchar *SendBuf);  //制作数据帧
extern void UartSendData(uchar SendBuf[]);
extern void Uart_Task(void); 		//1ms软件定时器中

串口发送

unsigned char InitBuf[] = {"Init"};	//你要发送的原始数据
//处理数据,添加数据类型、长度等信息,若在Linux下,添加fd描述符
UartSendDataHandle(InitBuf,sizeof(InitBuf)-1,UART_CONFIG,UartSendBuf);
UartSendData(UartSendBuf);//发送数据,可以和第二步整合在一起

串口接收

//在Uart中断服务函数中
temp = UART_BUF; 		  //不同内核sfr名字不一定相同
	
Gu8UartRecFeedDog = 1; 	  //检测字节与字节之间接收是否超时 
switch(Gu8RecStep)
{
	case 0:
		if(temp == STARTMARK)
		{
			UartRecQueueInfo.DataBuf[UartRecQueueInfo.fail][Gu8UartRecQueueBufScriptNub++] = temp;  
			Gu8RecStep++;
		}
	break;
		
	case 1:
		if(Gu8UartRecQueueBufScriptNub <= UART_BUF_SIZE) 
		{
			
			UartRecQueueInfo.DataBuf[UartRecQueueInfo.fail][Gu8UartRecQueueBufScriptNub++] = temp;				
			//结束符和[2]位的长度同时判断	
			if((UartRecQueueInfo.DataBuf[UartRecQueueInfo.fail][Gu8UartRecQueueBufScriptNub - 1 ] == ENDMARK) && (UartRecQueueInfo.DataBuf[UartRecQueueInfo.fail][UART_LENGTH_POSITION] == Gu8UartRecQueueBufScriptNub)) 
			{
				UartRecQueueInfo.fail = (UartRecQueueInfo.fail+1)%UartRecQueueInfo.MaxSize; //入队
				
				Gu8RecStep = 0;		
				Gu8UartRecQueueBufScriptNub = 0;
			}
		}
		else
		{
			Gu8RecStep = 0;
			Gu8UartRecQueueBufScriptNub = 0;
		}
		break;
	default:
		break;
}

使用注意点

/*
1. 发送和接收的数据存在循环队列中,判断队列是否为空作为发送条件或处理条件,1ms定时器中轮询;
2. 发送的次数可变,同一数据的发送间隔时间和不同数据的发送间隔时间可调;
3. 接收数据设置超时丢弃数据,超时时间,发送无应答时,超时重发,超过一定次数上报。
4. 数据发送协议发生变化时,需要修改部分代码,一般是数字 和 buf[1]&0x7F;
5. 不校验结束标志ENDMARK;
6. 不能在uart中断服务函数中直接入队,可能出现同时修改同一个buf的情况,
		在rtos下需要添加互斥锁保护此buf;
7. 若接受数据频繁,将接收任务设置高优先级,或者在Main函数中直接处理以达到高处理速度

你可能感兴趣的:(c语言,开发语言,嵌入式,单片机)