MCU上使用的稳定Uart通讯协议(环形队列)
协议的主要内容:
接收:字节间超时判断、环形队列接收、非阻塞式接收整帧数据、接收查错;
发送:未应答重发(超过3次后反馈错误指令,若有应答继续发送原来数据)、
可选发送次数和间隔时间、CRC校验、环形队列解码;
若在RTOS中使用需添加互斥锁;
Uart总结:
- Uart—通用异步收发器,按位进行数据收发的一种串行通信接口,相比于IIC\SPI,Uart没有CLK线使其保持同步,在RS485、LIN总线中可见等。
- 使用硬件串口时,只需了解中断/DMA的逻辑即可实现收发,在TX/RX时,使用的是同名异址寄存器SBUF,接收/发送寄存器SBUF的地址虽然相同,但在物理上它们是2个不同的寄存器,一个只能读取,一个只能写入,以此保证不会出现紊乱。
- 各种串口、并口协议只需搞懂 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函数中直接处理以达到高处理速度
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)