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

2023-05-16

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函数中直接处理以达到高处理速度
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Uart串口通讯协议与环形队列(裸机/RTOS) 的相关文章

  • UART串口通信

    目录 一 通信特点二 通信应用三 接线示意图三 UART通信协议四 STM32F4 串口使用1 资源分布2 特性3 UART框图4 使用方法5 相关库函数6 函数实例 五 实战 上位机控制开发板小灯 一 通信特点 异步 串行 全双工 一般描
  • 一文理解UART通信

    还记得当年的打印机 xff0c 鼠标和调制解调器吗 他们都有巨大笨重的连接器和粗电缆 xff0c 并且必须拧到你的电脑上 这些设备正是使用UART协议与计算机进行通信 虽然USB几乎完全取代了旧的电缆和连接器 xff0c 但UART绝对没有
  • C51_day5:串口通信UART

    3 1 串口基本认知 串行接口简称串口 xff0c 也称串行通信接口或串行通讯接口 xff08 通常指COM接口 xff09 xff0c 是采用串行通信方式的扩展接口 串行接口 xff08 Serial Interface xff09 是指
  • UART串口通信协议详解

    UART xff1a 通信异步收发器 xff0c 串行 异步通信总线 xff0c 两条数据线 xff08 收发 xff09 xff0c 全双工 xff08 可以同时接收和发送 xff09 一 UART帧格式 xff08 UART协议 xff
  • Node.js笔记:SerialPort(串口)模块使用(基于9.x.x)

    文章目录 目的 模块安装 基础使用 扫描端口 打开端口 发送数据 接收数据 错误处理 数据解析器 SerialPort类 构造方法 属性 事件 方法 命令行工具 总结 目的 上位机与各种电路模块间常常采用串口进行通讯 Node js中可以使
  • RT-Thread uart2串口dma idle接收不断帧

    硬件STM32F407 IDE使用RT Thread Studio uart2串口使用这两个引脚 功能 IO端口 UART2 TX PA2 UART2 RX PA3 UART2 DMA接收配置 先使能DMA接收 RX缓冲区可以稍微调大些 b
  • 一起学nRF51xx 6 - uart

    前言 通用异步接收器 发送器提供快速 全双工 内置流量控制的异步串行通信 CTS RTS 在硬件方面支持高达1Mbps波特率 支持奇偶校验和第9位数据生成 用于每个UART接口线的GPIO可从芯片上的GPIO中任选 而且可独立配置 这使得芯
  • Threadx 定时器timer

    文章目录 定时器管理结构 定时器链表 定时器激活链表 定时器工作原理 定时器API 定时器创建 tx timer create 删除定时器 tx timer delete 修改 tx timer change Threadx 操作系统定时器
  • mega328p-ADC,PWM,UART驱动

    ADC驱动 函 数 名 Ai Init 函数功能 Ai端口初始化 输入参数 void 输出参数 void 返 回 值 void 参考文档 void 创 件 人 程强刚 创建日期 2016 02 09 修改历史 void Ai Init vo
  • FreeRTOS ------- 任务(task)

    在学习RTOS的时候 个人觉得带着问题去学习 会了解到更多 1 什么是任务 在FreeRTOS中 每个执行线程都被称为 任务 每个任务都是在自己权限范围内的一个小程序 其具有程序入口每个任务都是在自己权限范围内的一个小程序 其具有程序入口通
  • OSAL

    OSAL为 Operating System Abstraction Layer 即操作系统抽象层 支持多任务运行 它并不是一个传统意义上的操作系统 但是实现了部分类似操作系统的功能 OSAL概念是由TI公司在ZIGBEE协议栈引入 他的意
  • FreeRTOS-创建删除任务

    1 FreeRTOSConfig h文件 FreeRTOSConfig h配置文件作用 对FreeRTOS进行功能配置和裁剪 以及API函数的使能 相关的宏可以分为三大类 INCLUDE 配置FreeRTOS中可选的API函数 config
  • 自己动手写RTOS:02-在M3内核上实现pendsvc

    自己动手写RTOS 自己动手写RTOS 01基础知识和理论部分 自己动手写RTOS 02 在M3内核上实现pendsvc 文章目录 自己动手写RTOS 一 M3内核的相关知识 1 1寄存器 1 2特殊寄存器 1 3堆栈 二 pendSVC实
  • RTX线程通信之——线程标志

    文章目录 Thread Flags 概念 RTX线程标志API 案例 LED灯同步闪亮 小结 参考资料 Thread Flags In a real application we need to be able to communicate
  • 以字符串形式接收数字(uart)

    我正在尝试通过 uart 接收一个包装为字符串的数字 我发送数字 1000 所以我得到 4 个字节 空字符 但是 当我使用 atoi 将数组转换为数字并将整数与 1000 进行比较时 我并不总是得到正确的数字 这是我用于接收号码的中断处理函
  • 将 DKM 项目链接到内核映像 (VIP) 项目作为 VxWorks Workbench4 中的子项目/额外模块

    如何将 DKM 项目与内核映像 VIP 项目链接 加载 以便我可以从内核映像项目的 usrAppInit c 调用 DKM 项目 应用程序 的入口点函数 以在启动时自动启动应用程序 有人可以描述步骤或向我指出任何文档吗 将 DKM 项目添加
  • RT-Thread 内核基础(六)

    RT Thread内核配置示例 RT Thread的一个重要特性是高度可裁剪性 支持对内核进行精细调整 对组件进行灵活拆卸 配置主要是通过修改工程目录下的rtconfig h文件来进行 用户可以通过打开 关闭该文件中的宏定义来对代码进行条件
  • 抢占和上下文切换的区别

    一点介绍 我目前正在编写一个小型 读微型 RTOS 内核 它应该与内核中的大多数内容是一体的 然而 我找不到关于下面列出的一些事情的太多信息 这会很有帮助 除此之外 它实际上不是某种大学项目 而是我按照自己的意愿做的事情 回答所有问题的一个
  • 小型 ARM 微控制器的 RTOS 内核之间的可量化差异 [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 有许多不同的 RTOS 可用于微控制器 我专门寻找支持 ARM Cortex M 处理器的 RTOS 另外 我对闭源解决方案不感兴趣 试图从网站
  • 如何检测来自 QNX 中 ncurses 的屏幕调整大小事件?

    我无法配置为接收有关使用 ncurses QNX Momentics 更改终端大小的事件 我使用Putyy作为终端 通过COM端口传输数据 我的问题是如何实现使用远程终端时接收屏幕变化事件 FILE fcons fopen dev ser1

随机推荐