串口环形队列数据处理实战——基于中断+主循环时间片调度的完整方案
在嵌入式开发中,串口通信是最常用的外设通信方式之一。然而,直接在中断里处理数据、或者在主循环里阻塞等待接收,都会带来实时性差、数据丢失、代码耦合等问题。
本文将分享一套基于环形队列(Ring Buffer)+ 中断接收发送 + 主循环时间片调度的串口数据处理方案,并提供完整的代码示例,可直接应用于 8051、STM32、ESP32 等各类 MCU 平台。
一、为什么需要环形队列?
传统的串口处理方式存在以下痛点:
-
中断里处理业务逻辑:中断执行时间过长,会影响其他中断的响应,甚至导致数据丢包。
-
全局变量传递数据:代码耦合度高,难以维护,且容易出现数据竞争。
-
阻塞式接收:主循环被串口占用,无法执行其他任务,实时性差。
环形队列(Ring Buffer)是一种先进先出(FIFO)的循环数据结构,完美解决了上述问题:
-
解耦中断与业务:中断只负责把数据写入队列,主循环负责读取处理,各司其职。
-
避免数据丢失:即使主循环暂时繁忙,数据也会暂存在队列中,不会丢失。
-
非阻塞设计:主循环可以按时间片调度,兼顾串口处理与其他任务。
二、整体架构设计
本方案的核心架构分为三层:
-
中断层:串口接收/发送中断,仅负责数据的入队/出队,执行时间极短。
-
驱动层:环形队列的初始化、入队、出队、判空、判满等核心操作。
-
业务层:主循环按时间片调度,从队列中读取数据并解析处理,执行业务逻辑。
数据流向:
串口接收中断 → 数据写入RX环形队列 → 主循环1ms时间片读取 → 数据解析处理
业务层数据 → 写入TX环形队列 → 串口发送中断 → 数据发送出去
三、头文件定义(Uart_Ring.h)
#ifndef _UART_RING_H_
#define _UART_RING_H_
//============================================================================
// 配置区(唯一可修改)
//============================================================================
#define TX_DATA_UART_LEN 10U
#define RX_DATA_UART_LEN 10U
#define TX_FRAME_UART_LEN (2U + TX_DATA_UART_LEN + 1U)
#define RX_FRAME_UART_LEN (2U + RX_DATA_UART_LEN + 1U)
#define FRAME_UART_H1 0x55 //头码1
#define FRAME_UART_H2 0xAA //头码2
#define FRAME_UART_HEAD_LEN 2U //头码长度
#define FRAME_UART_TAIL_LEN 1U //尾码长度(校验码)
#define FRAME_UART_LEN_LEN 1U //长度码长度
//============================================================================
#define TX_FIFO_SIZE (TX_FRAME_UART_LEN ) //TX_FRAME_UART_LEN*2,防止丢帧,减少计算量
#define RX_FIFO_SIZE (RX_FRAME_UART_LEN + RX_FRAME_UART_LEN) //RX_FRAME_UART_LEN*2,防止丢帧,减少计算量
#define RX_FIFO_DATA_SIZE (RX_FRAME_UART_LEN + 6U) //RX_FRAME_UART_LEN+6,至少提供3次头码矫正机会,否则加入新数据后再处理
#define SEND_200MS_CNT 10U //发送200ms一次,20ms扫描一次,10次*20ms=200ms
#define SEND_FORCE_2S_CNT 10U //强制2s发送一次,无变化,累计更新10次*200ms=2000ms,2s
#define Uart_Ring_TxEn() (uart_ring_tx_Strt = 1,uart_ring_tx_dptr = 1,US1CON3 =uart_tx_buff[0]) //使能发送
#define Uart_Ring_TxDs() (uart_ring_tx_Strt = 0,uart_ring_tx_dptr = 0) //禁用发送
//============================================================================
//============================================================================
// 定义接收功能码
//============================================================================
#define FRAME_UART_RX_CMD0 0x00
#define FRAME_UART_RX_CMD1 0x01
#define FRAME_UART_RX_CMD2 0x02
#define FRAME_UART_RX_CMD3 0x03
#define FRAME_UART_RX_CMD4 0x04
#define FRAME_UART_RX_CMD5 0x05
#define FRAME_UART_RX_CMD6 0x06
#define FRAME_UART_RX_CMD7 0x07
#define FRAME_UART_RX_CMD8 0x08
#define FRAME_UART_RX_CMD9 0x09
#define FRAME_UART_RX_CMD10 0x0A
#define FRAME_UART_RX_CMD11 0x0B
#define FRAME_UART_RX_CMD12 0x0C
#define FRAME_UART_RX_CMD13 0x0D
#define FRAME_UART_RX_CMD14 0x0E
#define FRAME_UART_RX_CMD15 0x0F
//============================================================================
// 接收功能码应答码,应答码为功能码+0x80
//============================================================================
#define FRAME_UART_RX_ACK0 0x80
#define FRAME_UART_RX_ACK1 0x81
#define FRAME_UART_RX_ACK2 0x82
#define FRAME_UART_RX_ACK3 0x83
#define FRAME_UART_RX_ACK4 0x84
#define FRAME_UART_RX_ACK5 0x85
#define FRAME_UART_RX_ACK6 0x86
#define FRAME_UART_RX_ACK7 0x87
#define FRAME_UART_RX_ACK8 0x88
#define FRAME_UART_RX_ACK9 0x89
#define FRAME_UART_RX_ACK10 0x8A
#define FRAME_UART_RX_ACK11 0x8B
#define FRAME_UART_RX_ACK12 0x8C
#define FRAME_UART_RX_ACK13 0x8D
#define FRAME_UART_RX_ACK14 0x8E
#define FRAME_UART_RX_ACK15 0x8F
//============================================================================
// 定义发送功能码
//============================================================================
#define FRAME_UART_TX_CMD0 0x00
#define FRAME_UART_TX_CMD1 0x01
#define FRAME_UART_TX_CMD2 0x02
#define FRAME_UART_TX_CMD3 0x03
#define FRAME_UART_TX_CMD4 0x04
#define FRAME_UART_TX_CMD5 0x05
#define FRAME_UART_TX_CMD6 0x06
#define FRAME_UART_TX_CMD7 0x07
#define FRAME_UART_TX_CMD8 0x08
#define FRAME_UART_TX_CMD9 0x09
#define FRAME_UART_TX_CMD10 0x0A
#define FRAME_UART_TX_CMD11 0x0B
#define FRAME_UART_TX_CMD12 0x0C
#define FRAME_UART_TX_CMD13 0x0D
#define FRAME_UART_TX_CMD14 0x0E
#define FRAME_UART_TX_CMD15 0x0F
#define FRAME_UART_TX_WAIT 0x00
//============================================================================
// 环形缓冲区结构体
//============================================================================
typedef struct
{
unsigned char *buf_in; //写入指针
unsigned char *buf_out; //读取指针
}uart_ring_t;
#ifdef _UART_RING_C_
//============================================================================
// 定义发送缓冲区数组
//============================================================================
// volatile unsigned char uart_tx_buff[TX_FRAME_UART_LEN]; //发送缓冲区
volatile unsigned char uart_tx_fifo[TX_FIFO_SIZE]; //发送缓冲区队列
//============================================================================
//============================================================================
// 定义发送数据数组
volatile unsigned char uart_tx_data[TX_FRAME_UART_LEN]; //发送数据
//============================================================================
// 定义发送环形缓冲区
volatile uart_ring_t uart_tx_ring;
//============================================================================
//============================================================================
// 定义接收缓冲区数组
//============================================================================
volatile unsigned char uart_rx_fifo[RX_FIFO_SIZE]; //接收缓冲区
//============================================================================
//============================================================================
// 定义接收数据数组,去掉头码和尾码
//============================================================================
volatile unsigned char uart_rx_data[RX_FIFO_DATA_SIZE];
//============================================================================
//============================================================================
// 定义接收环形缓冲区
volatile uart_ring_t uart_rx_ring;
//============================================================================
//============================================================================
vchar uart_ring_tx_dptr; ////串口发送指针
vchar uart_ring_tx_Strt; ////串口发送允许发送(1==允许开始发送,0==不允许发送)
xchar uart_ring_tx_Freh; ////串口发送刷新率
xchar uart_ring_tx_Flag; ////串口发送数据异动,1==有异动,0==无异动
xchar uart_ring_tx_cout; ////串口发送数据发送计数(200ms基础发送一次)
//============================================================================
xchar TxTx_Psoc_Fast; ////加速发送
xchar Ferr_Uart_Ere8; ////串口接收故障
//============================================================================
#else
//============================================================================
// 定义发送缓冲区数组
//============================================================================
// extern volatile unsigned char uart_tx_buff[TX_FRAME_UART_LEN]; //发送缓冲区
extern volatile unsigned char uart_tx_fifo[TX_FIFO_SIZE]; //发送缓冲区队列
//============================================================================
//============================================================================
// 定义发送数据数组
extern volatile unsigned char uart_tx_data[TX_FRAME_UART_LEN]; //发送数据
//============================================================================
// 定义发送环形缓冲区
extern volatile uart_ring_t uart_tx_ring;
//============================================================================
//============================================================================
// 定义接收缓冲区数组
//============================================================================
extern volatile unsigned char uart_rx_fifo[RX_FIFO_SIZE]; //接收缓冲区
//============================================================================
//============================================================================
// 定义接收数据数组,去掉头码和尾码
//============================================================================
extern volatile unsigned char uart_rx_data[RX_FIFO_DATA_SIZE];
//============================================================================
//============================================================================
// 定义接收环形缓冲区
extern volatile uart_ring_t uart_rx_ring;
//============================================================================
//============================================================================
extern vchar uart_ring_tx_dptr; ////串口发送指针
extern vchar uart_ring_tx_Strt; ////串口发送允许发送(1==允许开始发送,0==不允许发送)
extern xchar uart_ring_tx_Freh; ////串口发送刷新率
extern xchar uart_ring_tx_Flag; ////串口发送数据异动,1==有异动,0==无异动
extern xchar uart_ring_tx_cout; ////串口发送数据发送计数(200ms基础发送一次)
//============================================================================
extern xchar TxTx_Psoc_Fast; ////加速发送
extern xchar Ferr_Uart_Ere8; ////串口接收故障
//============================================================================
#endif /* _UART_RING_C_ */
//============================================================================
/**
* 函数名:void uart_ring_init(void)
* 功 能:初始化UART环形缓冲区,包含缓冲区清零
* 参 数:无
* 返回值:无
* 备 注:
*/
void uart_ring_init(void);
//============================================================================
/**
* 函数名:void uart_ring_rx_buff(unsigned char dat)
* 功 能:串口一个字节数据一个字节数据接收,并且写入环形缓冲区,串口接收中断调用
* 参 数:接收到的字节
* 返回值:无
* 备 注:
*/
void uart_ring_rx_buff(unsigned char dat);
//============================================================================
/**
* 函数名:unsigned char uart_ring_tx_buff_read(void)
* 功 能:从发送缓冲区读取一个字节,写入串口
* 参 数:unsigned char *dat:数据指针,通过它可以获取到数据
* 返回值:无
* 备 注:
*/
unsigned char uart_ring_tx_buff_read(void);
//============================================================================
/**
* 函数名:unsigned char uart_ring_tx_buff_is_empty(void)
* 功 能:判断发送缓冲区是否为空
* 参 数:无
* 返回值:1:缓冲区有数据;0:缓冲区无数据
* 备 注:
*/
unsigned char uart_ring_tx_buff_is_empty(void);
//============================================================================
/**
* 函数名:void uart_ring_rx_scan(void)
* 功 能:1ms扫描一次,有数据则将数据帧拷贝到接收数据区(uart_rx_data[RX_DATA_UART_LEN]),一次只处理约一帧数据,避免这部分程序阻塞时间过长
* 参 数:无
* 返回值:无
* 备 注:解析数据子函数在uart_ring_rx_scan()中调用
*/
void uart_ring_rx_scan(void);
//============================================================================
/**
* 函数名:void uart_ring_tx_scan(void)
* 功 能:发送数据更新及装载入缓冲区,目前不使用环形队列,直接使用数组
* 参 数:无
* 返回值:无
* 备 注:
*/
void uart_ring_tx_scan(void);
//============================================================================
#endif /* _UART_RING_H_ */
四、核心实现(Uart_Ring.c)
#define _UART_RING_C_
// #include "uart_ring.h"
#include "StdHead.h"
/*-----------------------------------------------------------------------------------------------20260606*/
static unsigned int uart_ring_rx_err_cnt = 0; //接收错误计数,1ms计数一次
static unsigned char uart_ring_rx_ack_state_cmd = 0; //接收ACK状态命令,收到命令+0x80发送出去,表示收到命令
static unsigned char uart_ring_tx_ack_state_cmd = 0; //发送数据等待ACK状态命令,
static unsigned int uart_ring_tx_ack_cnt = 0; //发送数据等待ACK计数
static unsigned char uart_ring_tx_ack_rept_cnt = 0; //发送数据等待ACK重复发送计数
static unsigned char uart_ring_tx_state = 0; //发送状态
static unsigned char uart_ring_tx_state_temp = 0; //发送状态临时
static SwitchType_t uart_ring_tx_ack_state1 ; //发送数据等待ACK状态,BIT0:CMM0,BIT1:CMM1,BIT2:CMM2,BIT3:CMM3,BIT4:CMM4,BIT5:CMM5,BIT6:CMM6,BIT7:CMM7
// static SwitchType_t uart_ring_tx_ack_state2 ; //发送数据等待ACK状态,BIT0:CMM8,BIT1:CMM9,BIT2:CMM10,BIT3:CMM11,BIT4:CMM12,BIT5:CMM13,BIT6:CMM14,BIT7:CMM15
/*-----------------------------------------------------------------------------------------------20260606*/
void uart_ring_tx_buff_write(unsigned char dat);
void uart_ring_tx_buff_write_n(unsigned char *dat, unsigned char len);
unsigned char uart_ring_tx_buff_is_empty(void);
unsigned char uart_ring_tx_buff_residue_len(void);
static void uart_ring_rx_err(void);
static void uart_rx_handle(unsigned char *handle_data);
void uart_ring_rx_data_ack(void);
void uart_ring_rx_data_set_ack(unsigned char ack_cmd);
void uart_ring_tx_data_ack_err(void);
void uart_ring_tx_start(void);
/*-----------------------------------------------------------------------------------------------20260606*/
//校验和计算函数
unsigned char checksum(unsigned char *dat, unsigned char len)
{
unsigned char sum = 0;
unsigned char number = 0;
for (number = 0; number < len; number++)
{
sum += dat[number];
}
return sum;
}
/*-----------------------------------------------------------------------------------------------20260606*/
/**
* 函数名:void *my_memcpy(void *dest, const void *src, unsigned char count)
* 功 能:自定义内存拷贝函数,支持内存区域重叠,安全拷贝
* 参 数:dest - 目标地址
* src - 源地址
* count - 拷贝长度
* 返回值:返回目标地址 dest
*/
void *my_memcpy(void *dest, const void *src, unsigned char count)
{
unsigned char *pdest = (unsigned char *)dest;
const unsigned char *psrc = (const unsigned char *)src;
unsigned short i;
// 空指针判断
if(dest == NULL || src == NULL)
{
return NULL;
}
// 无重叠 → 正向拷贝
// 有重叠 → 反向拷贝(防止覆盖数据)
if((pdest <= psrc) || (pdest > psrc + count))
{
for(i = 0; i < count; i ++)
{
pdest[i] = psrc[i];
}
}
else
{
for(i = count; i > 0; i --)
{
pdest[i - 1] = psrc[i - 1];
}
}
return dest;
}
/*-----------------------------------------------------------------------------------------------20260606*/
/**
* 函数名:void uart_ring_init(void)
* 功 能:初始化UART环形缓冲区,包含缓冲区清零
* 参 数:无
* 返回值:无
* 备 注:
*/
void uart_ring_init(void)
{
// unsigned int i;
// //======================== 发送缓冲区清零 ========================
// for(i = 0; i < sizeof(uart_tx_fifo); i++)
// {
// uart_tx_fifo[i] = 0x00;
// }
// 发送指针初始化
uart_tx_ring.buf_in = (unsigned char *)uart_tx_fifo; // 写指针指向缓冲区开头
uart_tx_ring.buf_out = (unsigned char *)uart_tx_fifo; // 读指针指向缓冲区开头
// //======================== 接收缓冲区清零 ========================
// for(i = 0; i < sizeof(uart_rx_fifo); i++)
// {
// uart_rx_fifo[i] = 0x00;
// }
// 接收指针初始化
uart_rx_ring.buf_in = (unsigned char *)uart_rx_fifo; // 写指针指向缓冲区开头
uart_rx_ring.buf_out = (unsigned char *)uart_rx_fifo; // 读指针指向缓冲区开头
uart_ring_tx_ack_state1.all = 0;
// uart_ring_tx_ack_state2.all = 0;
}
/*-----------------------------------------------------------------------------------------------20260606*/
/**
* 函数名:void uart_ring_rx_buff(unsigned char dat)
* 功 能:串口一个字节数据一个字节数据接收,并且写入环形缓冲区,串口接收中断调用
* 参 数:接收到的字节
* 返回值:无
* 备 注:
*/
void uart_ring_rx_buff(unsigned char dat)
{
if(1 == (uart_rx_ring.buf_out - uart_rx_ring.buf_in)) //缓冲区满
{////这里意思是写入指针已经到达了读取指针的前一个位置,说明缓冲区已经满了
}
else if((uart_rx_ring.buf_in > uart_rx_ring.buf_out) && ((uart_rx_ring.buf_in - uart_rx_ring.buf_out) >= sizeof(uart_rx_fifo))) //缓冲区满
{////正序且写指针与读指针相差大于缓冲区大小,说明缓冲区已经满了
}
else
{
if(uart_rx_ring.buf_in >= ((unsigned char *)uart_rx_fifo + sizeof(uart_rx_fifo))) //写指针地址越界,重新指向缓冲区开头
{
uart_rx_ring.buf_in = (unsigned char *)uart_rx_fifo;
}
*uart_rx_ring.buf_in = dat;
uart_rx_ring.buf_in++; //写指针后移
}
}
/*-----------------------------------------------------------------------------------------------20260606*/
/**
* 函数名:unsigned char uart_ring_rx_buff_read(void)
* 功 能:从接收环形队列读一个字节
* 参 数:无
* 返回值:接收到的字节
* 备 注:
*/
unsigned char uart_ring_rx_buff_read(void)
{
unsigned char dat = 0;
if(uart_rx_ring.buf_in != uart_rx_ring.buf_out) //缓冲区有数据
{
if(uart_rx_ring.buf_out >= ((unsigned char *)uart_rx_fifo + sizeof(uart_rx_fifo))) //读指针地址越界,重新指向缓冲区开头
{
uart_rx_ring.buf_out = (unsigned char *)uart_rx_fifo;
}
dat = *uart_rx_ring.buf_out;
uart_rx_ring.buf_out++; //读指针后移
}
return dat;
}
/*-----------------------------------------------------------------------------------------------20260606*/
//判断是否有数据
/**
* 函数名:unsigned char uart_ring_rx_buff_is_empty(void)
* 功 能:判断接收环形队列是否为空
* 参 数:无
* 返回值:1:非空,0:空
* 备 注:
*/
unsigned char uart_ring_rx_buff_is_empty(void)
{
if(uart_rx_ring.buf_in != uart_rx_ring.buf_out) //缓冲区有数据
{
return 1;
}
else
{
return 0;
}
}
/*-----------------------------------------------------------------------------------------------20260606*/
/////////////////发送数据///////////////////////////////////
//从发送缓冲区读取一个字节,写入串口
/**
* 函数名:unsigned char uart_ring_tx_buff_read(void)
* 功 能:从发送缓冲区读取一个字节,写入串口
* 参 数:unsigned char *dat:数据指针,通过它可以获取到数据
* 返回值:无
* 备 注:
*/
unsigned char uart_ring_tx_buff_read(void)
{
unsigned char dat;
if(uart_tx_ring.buf_in != uart_tx_ring.buf_out) //缓冲区有数据
{
if(uart_tx_ring.buf_out >= ((unsigned char *)uart_tx_fifo + sizeof(uart_tx_fifo))) //读指针地址越界,重新指向缓冲区开头
{
uart_tx_ring.buf_out = (unsigned char *)uart_tx_fifo;
}
dat = *uart_tx_ring.buf_out;
uart_tx_ring.buf_out++; //读指针后移
}
return dat;
}
/*-----------------------------------------------------------------------------------------------20260606*/
//往发送缓冲区写入一个字节
/**
* 函数名:void uart_ring_tx_buff_write(unsigned char dat)
* 功 能:往发送缓冲区写入一个字节
* 参 数:unsigned char dat:要写入的数据
* 返回值:无
* 备 注:
*/
void uart_ring_tx_buff_write(unsigned char dat)
{
if(1 == uart_tx_ring.buf_out - uart_tx_ring.buf_in) //缓冲区已满
{
}
else if((uart_tx_ring.buf_in > uart_tx_ring.buf_out) && ((uart_tx_ring.buf_in - uart_tx_ring.buf_out) > sizeof(uart_tx_fifo))) //缓冲区已满
{
}
else
{
if(uart_tx_ring.buf_in >= ((unsigned char *)uart_tx_fifo + sizeof(uart_tx_fifo))) //写指针地址越界,重新指向缓冲区开头
{
uart_tx_ring.buf_in = (unsigned char *)uart_tx_fifo;
}
*uart_tx_ring.buf_in = dat;
uart_tx_ring.buf_in++; //写指针后移
}
}
/*-----------------------------------------------------------------------------------------------20260606*/
//往发送缓冲区写入多个字节
/**
* 函数名:void uart_ring_tx_buff_write_n(unsigned char *dat, unsigned char len)
* 功 能:往发送缓冲区写入多个字节
* 参 数:unsigned char *dat:要写入的数据
* unsigned char len:要写入的字节数
* 返回值:无
* 备 注:使用前,请确保缓冲区有足够的空间,有对应缓冲区剩余长度读取函数
*/
void uart_ring_tx_buff_write_n(unsigned char *dat, unsigned char len)
{
unsigned char i;
for(i = 0; i < len; i++)
{
uart_ring_tx_buff_write(*dat++);
}
}
/*-----------------------------------------------------------------------------------------------20260606*/
//判断发送缓冲区是否为空
/**
* 函数名:unsigned char uart_ring_tx_buff_is_empty(void)
* 功 能:判断发送缓冲区是否为空
* 参 数:无
* 返回值:1:缓冲区有数据;0:缓冲区无数据
* 备 注:
*/
unsigned char uart_ring_tx_buff_is_empty(void)
{
if(uart_tx_ring.buf_in != uart_tx_ring.buf_out) //缓冲区无数据
{
return 1;
}
else
{
return 0;
}
}
/*-----------------------------------------------------------------------------------------------20260606*/
//判断缓冲区剩余长度
/**
* 函数名:unsigned char uart_ring_tx_buff_residue_len(void)
* 功 能:判断发送缓冲区剩余长度,确保数据发送完整不覆盖
* 参 数:无
* 返回值:缓冲区剩余长度
* 备 注:
*/
unsigned char uart_ring_tx_buff_residue_len(void)
{
if(uart_tx_ring.buf_in > uart_tx_ring.buf_out)
{////正序,写指针大于读指针,总长度-(写指针-读指针)=剩余长度
return (sizeof(uart_tx_fifo) - (uart_tx_ring.buf_in - uart_tx_ring.buf_out));
}
else if(uart_tx_ring.buf_in < uart_tx_ring.buf_out)
{////反序,写指针小于读指针,读指针-写指针=剩余长度
return (uart_tx_ring.buf_out - uart_tx_ring.buf_in);
}
else
{////写指针等于读指针,缓冲区为空
return (sizeof(uart_tx_fifo));
}
}
/*-----------------------------------------------------------------------------------------------20260606*/
// //判断缓冲区数据长度
// /**
// * 函数名:unsigned char uart_ring_tx_buff_data_len(void)
// * 功 能:判断发送缓冲区数据长度
// * 参 数:无
// * 返回值:缓冲区数据长度
// * 备 注:
// * */
// unsigned char uart_ring_tx_buff_data_len(void)
// {
// if(uart_tx_ring.buf_in > uart_tx_ring.buf_out)
// {////正序,写指针大于读指针
// return (uart_tx_ring.buf_in - uart_tx_ring.buf_out);
// }
// else if(uart_tx_ring.buf_in < uart_tx_ring.buf_out)
// {////反序,写指针小于读指针
// return (sizeof(uart_tx_fifo) - (uart_tx_ring.buf_out - uart_tx_ring.buf_in));
// }
// else
// {////写指针等于读指针,缓冲区为空
// return 0;
// }
// }
/*-----------------------------------------------------------------------------------------------20260606*/
/**
* 函数名:void uart_ring_rx_scan(void)
* 功 能:1ms扫描一次,有数据则将数据帧拷贝到接收数据区(uart_rx_data[RX_DATA_UART_LEN]),一次只处理约一帧数据,避免这部分程序阻塞时间过长
* 参 数:无
* 返回值:无
* 备 注:解析数据子函数在uart_ring_rx_scan()中调用
*/
void uart_ring_rx_scan(void)
{//1ms进入一次
static unsigned char rx_in = 0;
unsigned char offset = 0;
unsigned char rx_data_len = 0;
//有数据就取出,数据大小只定一帧大小,剩余数据再次进来再取,一次只处理一帧数据,避免这部分程序阻塞时间过长
while((rx_in < sizeof(uart_rx_data)) && (uart_ring_rx_buff_is_empty())) //缓冲区有数据
{
uart_rx_data[rx_in++] = uart_ring_rx_buff_read();
}
while((rx_in - offset) >= FRAME_UART_HEAD_LEN) ////满足帧头
{
if(uart_rx_data[offset] != FRAME_UART_H1)
{
offset++;
continue; //不满足帧头,结束本次往下执行,进入下一次循环,!!!注意,这里没有break,因为要继续往下执行,直到找到帧头
}
if(uart_rx_data[offset + 1] != FRAME_UART_H2)
{
offset++;
continue; //不满足帧头,结束本次往下执行,进入下一次循环,!!!注意,这里没有break,因为要继续往下执行,直到找到帧头
}
rx_data_len = uart_rx_data[offset + 2] + FRAME_UART_HEAD_LEN + FRAME_UART_TAIL_LEN + FRAME_UART_LEN_LEN;
if((rx_in - offset) < rx_data_len) //数据帧长度不够
{
break; //数据帧长度不够,直接结束while循环,往下执行
}
//头码匹配成功,计数校验和,校验和与校验码比较
if((checksum(uart_rx_data + offset, rx_data_len - 1) )!= uart_rx_data[offset + rx_data_len - 1])
{
offset += 2; //头码可能碰巧,头码两个不同的字节,因此加2;校验和错误,结束本次往下执行,进入下一次循环
continue; //校验和错误,结束本次往下执行,进入下一次循环,!!!注意,这里没有break,因为要继续往下执行,直到找到帧头
}
//校验和正确,进入解析环节(函数)
uart_rx_handle(uart_rx_data + offset + FRAME_UART_HEAD_LEN);
offset += rx_data_len; //完成解析,进入下一次循环,offset加2,继续往下寻找帧头
}////end while
if(rx_in >= offset)
{
rx_in -= offset;
}
if(rx_in > 0 )
{////..如果数据帧不完整,则将剩余的数据拷贝到数据帧的开头
my_memcpy((char *)uart_rx_data, (const char *)uart_rx_data + offset, rx_in);
}
////这里通信故障判断;解析正常,计数自动清零
uart_ring_rx_err();
uart_ring_tx_data_ack_err();
}
/*-----------------------------------------------------------------------------------------------20260606*/
/*-----------------------------------------------------------------------------------------------20260606*/
//=============================数据接收主要修改下面的函数==开始======================================================================
/**
* 函数名:static void uart_ring_rx_err(void)
* 功 能:通信故障判断
* 参 数:无
* 返回值:无
* 备 注:
*/
static void uart_ring_rx_err(void)
{
if(++uart_ring_rx_err_cnt > 8000){Ferr_Uart_Ere8 = TRUE;}
}
/*-----------------------------------------------------------------------------------------------20260606*/
////..UART1==9600接收,显示板(上位机程序)接收和解析数据
////..数据格式:2Byte头码+1Byte位操作+1Byte故障+4Byte蜂鸣器+1Byte校验码
//// 数据RxRx_Psoc_Rdat[0]=0x55
//// 数据RxRx_Psoc_Rdat[1]=0xAA
//// 数据RxRx_Psoc_Rdat[2]==位操作1
//// 数据RxRx_Psoc_Rdat[2]-->bit0:咖啡锅炉加热开关
//// 数据RxRx_Psoc_Rdat[2]-->bit1:蒸汽锅炉加热开关
//// 数据RxRx_Psoc_Rdat[2]-->bit2:咖啡水泵开关
//// 数据RxRx_Psoc_Rdat[2]-->bit3:蒸汽水泵开关
//// 数据RxRx_Psoc_Rdat[2]-->bit4:出冰电机开关
//// 数据RxRx_Psoc_Rdat[2]-->bit5:冷水热水阀开关
//// 数据RxRx_Psoc_Rdat[2]-->bit6:脱冰阀开关
//// 数据RxRx_Psoc_Rdat[2]-->bit7:咖啡阀开关
//// 数据RxRx_Psoc_Rdat[3]==位操作2
//// 数据RxRx_Psoc_Rdat[3]-->bit0:双通阀1开关
//// 数据RxRx_Psoc_Rdat[3]-->bit1:双通阀2开关
//// 数据RxRx_Psoc_Rdat[3]-->bit2:蒸汽阀开关
//// 数据RxRx_Psoc_Rdat[3]-->bit3:紫外线灯开关
//// 数据RxRx_Psoc_Rdat[3]-->bit4:电机电流检测左开关
//// 数据RxRx_Psoc_Rdat[3]-->bit5:电机电流检测右开关
//// 数据RxRx_Psoc_Rdat[3]-->bit6:制冰红外发射管1开关
//// 数据RxRx_Psoc_Rdat[3]-->bit7:杯子感应红外发射管2开关
//// 数据RxRx_Psoc_Rdat[4]==位操作3
//// 数据RxRx_Psoc_Rdat[4]-->bit0:制冰水泵开关
//// 数据RxRx_Psoc_Rdat[4]-->bit1:冰水箱抽水水泵开关
//// 数据RxRx_Psoc_Rdat[4]-->bit2:冷凝风机开关
//// 数据RxRx_Psoc_Rdat[4]-->bit3:离心泵开关
//// 数据RxRx_Psoc_Rdat[4]-->bit4:冷水热水阀2开关
//// 数据RxRx_Psoc_Rdat[4]-->bit5:压缩机开关
//// 数据RxRx_Psoc_Rdat[4]-->bit6:压缩机频率开关
//// 数据RxRx_Psoc_Rdat[5]==咖啡锅炉加热占空比(ms)
//// 数据RxRx_Psoc_Rdat[6]==咖啡锅炉加热周期(ms)
//// 数据RxRx_Psoc_Rdat[7]==蒸汽锅炉加热占空比(ms)
//// 数据RxRx_Psoc_Rdat[8]==蒸汽锅炉加热周期(ms)
//// 数据RxRx_Psoc_Rdat[9]==咖啡水泵占空比(ms)
//// 数据RxRx_Psoc_Rdat[10]==咖啡水泵周期(ms)
//// 数据RxRx_Psoc_Rdat[11]==蒸汽水泵占空比(ms)
//// 数据RxRx_Psoc_Rdat[12]==蒸汽水泵周期(ms)
//// 数据RxRx_Psoc_Rdat[13]== 校验码
/*-----------------------------------------------------------------------------------------------20260606*/
/**
* 函数名:void uart_rx_handle(unsigned char *handle_data)
* 功 能:接收数据解析
* 参 数:handle_data:接收数据数组
* 返回值:无
* 备 注:uart_rx_data数组作为传入参数,地址不一定从该数组首地址开始,传入时需要注意 ;还有不要取超出数组长度
*/
static void uart_rx_handle(unsigned char *handle_data)
{////数据没问题才进入到此
unsigned char rx_data_len = handle_data[0];
unsigned char rx_data_cmd = handle_data[1];
/*-----------------------------------------------------------------------------------------------20260606*/
uart_ring_rx_err_cnt = 0; //通信正常,计数清零
Ferr_Uart_Ere8 = 0; //通信正常,标志清零
/*-----------------------------------------------------------------------------------------------20260606*/
switch(rx_data_cmd)
{
case FRAME_UART_RX_CMD1:
{////位操作字节
if(rx_data_len < 4)break; ////数据长度不对
Work_Switch_State1.all = handle_data[2]; ////位操作字节1
Work_Switch_State2.all = handle_data[3]; ////位操作字节2
Work_Switch_State3.all = handle_data[4]; ////位操作字节3
}break;
case FRAME_UART_RX_CMD2:
{////加热占空比和加热周期
if(rx_data_len < 5)break; ////数据长度不对
Work_Coff_Heat_Rec.Duty = handle_data[2]; ////加热占空比
Work_Coff_Heat_Rec.Cycle = handle_data[3]; ////加热周期
Work_Stem_Heat_Rec.Duty = handle_data[4]; ////加热占空比
Work_Stem_Heat_Rec.Cycle = handle_data[5]; ////加热周期
}break;
case FRAME_UART_RX_CMD3:
{////水泵占空比和水泵周期
if(rx_data_len < 5)break; ////数据长度不对
Work_Coff_Pump_Rec.Duty = handle_data[2]; ////水泵占空比
Work_Coff_Pump_Rec.Cycle = handle_data[3]; ////水泵周期
Work_Stem_Pump_Rec.Duty = handle_data[4]; ////水泵占空比
Work_Stem_Pump_Rec.Cycle = handle_data[5]; ////水泵周期
}break;
////应答处理
case FRAME_UART_RX_ACK1:
{
uart_ring_tx_ack_state1.bits.BIT1 = 0; ////成功接收到指令1
}break;
case FRAME_UART_RX_ACK2:
{
uart_ring_tx_ack_state1.bits.BIT2 = 0; ////成功接收到指令2
}break;
case FRAME_UART_RX_ACK3:
{
uart_ring_tx_ack_state1.bits.BIT3 = 0; ////成功接收到指令3
}break;
case FRAME_UART_RX_ACK4:
{
uart_ring_tx_ack_state1.bits.BIT4 = 0; ////成功接收到指令4
}break;
case FRAME_UART_RX_ACK5:
{
uart_ring_tx_ack_state1.bits.BIT5 = 0; ////成功接收到指令5
}break;
// case FRAME_UART_RX_ACK6:
// {
// uart_ring_tx_ack_state1.bits.BIT6 = 0; ////成功接收到指令6
// }break;
// case FRAME_UART_RX_ACK7:
// {
// uart_ring_tx_ack_state1.bits.BIT7 = 0; ////成功接收到指令7
// }break;
default:
{
break;
}
}
if(rx_data_cmd < 0x80)
{
uart_ring_rx_data_set_ack(rx_data_cmd+0x80); ////接收数据应答
}
}
/*-----------------------------------------------------------------------------------------------20260606*/
/**
* 函数名:void uart_ring_rx_data_set_ack(unsigned char ack_cmd)
* 功 能:设置接收数据应答
* 参 数:无
* 返回值:无
* 备 注:
*/
void uart_ring_rx_data_set_ack(unsigned char ack_cmd)
{
uart_ring_rx_ack_state_cmd = ack_cmd;
}
/*-----------------------------------------------------------------------------------------------20260606*/
//================================接收数据解析结束======================================================================================
/*-----------------------------------------------------------------------------------------------20260606*/
////..UART1==9600发送,电源板发送和装载数据
////..数据格式:2Byte头码+1Byte位操作+1Byte故障+4Byte蜂鸣器+1Byte校验码
//// 数据TxTx_Psoc_Data[0]=0x55
//// 数据TxTx_Psoc_Data[1]=0xAA
//// 数据TxTx_Psoc_Data[2]=位操作
//// 数据TxTx_Psoc_Data[2]-->bit0==掉冰开关
//// 数据TxTx_Psoc_Data[2]-->bit1==接冰开关
//// 数据TxTx_Psoc_Data[2]-->bit2==水位开关
//// 数据TxTx_Psoc_Data[2]-->bit3==缺水开关
//// 数据TxTx_Psoc_Data[2]-->bit4==冰胆水位低
//// 数据TxTx_Psoc_Data[2]-->bit5==冰胆水位高
//// 数据TxTx_Psoc_Data[3]==NTC1的ADC值高8位
//// 数据TxTx_Psoc_Data[4]==NTC1的ADC值低8位
//// 数据TxTx_Psoc_Data[5]==NTC2的ADC值高8位
//// 数据TxTx_Psoc_Data[6]==NTC2的ADC值低8位
//// 数据TxTx_Psoc_Data[7]==NTC3的ADC值高8位
//// 数据TxTx_Psoc_Data[8]==NTC3的ADC值低8位
//// 数据TxTx_Psoc_Data[9]==电流的ADC值高8位---出冰电机
//// 数据TxTx_Psoc_Data[10]==电流的ADC值低8位---出冰电机
//// 数据TxTx_Psoc_Data[11]==电流的ADC值高8位---红外接收1
//// 数据TxTx_Psoc_Data[12]==电流的ADC值低8位---红外接收1
//// 数据TxTx_Psoc_Data[13]==电流的ADC值高8位---红外接收2
//// 数据TxTx_Psoc_Data[14]==电流的ADC值低8位---红外接收2
//// 数据TxTx_Psoc_Data[15]==流量计1数值高8位
//// 数据TxTx_Psoc_Data[16]==流量计1数值低8位
//// 数据TxTx_Psoc_Data[17]==流量计2数值高8位
//// 数据TxTx_Psoc_Data[18]==流量计2数值低8位
//// 数据TxTx_Psoc_Data[19]==NTC1的温度值高8位
//// 数据TxTx_Psoc_Data[20]==NTC1的温度值低8位
//// 数据TxTx_Psoc_Data[21]==NTC2的温度值高8位
//// 数据TxTx_Psoc_Data[22]==NTC2的温度值低8位
//// 数据TxTx_Psoc_Data[23]==NTC3的温度值高8位
//// 数据TxTx_Psoc_Data[24]==NTC3的温度值低8位
//// 数据TxTx_Psoc_Data[25]==软件版本号
//// 数据TxTx_Psoc_Data[26]==接收是否出现故障,0==无故障,非0==有故障
//// 数据TxTx_Psoc_Data[27]==校验码
/*-----------------------------------------------------------------------------------------------20260606*/
/*-----------------------------------------------------------------------------------------------20260606*/
/**
* 函数名:void uart_ring_rx_data_ack(void)
* 功 能:接收数据应答
* 参 数:无
* 返回值:无
* 备 注:应答数据格式:0x55 0xAA 0x01 (收到功能码+0x80) 校验和
*/
void uart_ring_rx_data_ack(void)
{
if(uart_ring_tx_buff_residue_len() < 6)return; ////缓冲区长度不足
uart_tx_data[0] = FRAME_UART_H1;
uart_tx_data[1] = FRAME_UART_H2;
uart_tx_data[2] = 0x01;
uart_tx_data[3] = uart_ring_rx_ack_state_cmd;
uart_tx_data[4] = checksum(uart_tx_data,4);
uart_ring_tx_buff_write_n(uart_tx_data,5);
uart_ring_rx_ack_state_cmd = 0x00;
uart_ring_tx_start();////启动发送
}
/*-----------------------------------------------------------------------------------------------20260606*/
//发送数据接收应答
/**
* 函数名:void uart_ring_tx_data_ack(void)
* 功 能:发送数据应答
* 参 数:无
* 返回值:无
* 备 注:二进制信号对应功能码
*/
void uart_ring_tx_data_ack_err(void)
{
if(++uart_ring_tx_ack_cnt < 200)return;
uart_ring_tx_ack_cnt = 0;
if(uart_ring_tx_ack_state1.bits.BIT1)
{
uart_ring_tx_state = FRAME_UART_TX_CMD1;
if(++uart_ring_tx_ack_rept_cnt > 3)//未应答前重复发送3次
{
uart_ring_tx_ack_rept_cnt = 0;
uart_ring_tx_ack_state1.bits.BIT1 = 0;
}
return;
}
if(uart_ring_tx_ack_state1.bits.BIT2)
{
uart_ring_tx_state = FRAME_UART_TX_CMD2;
if(++uart_ring_tx_ack_rept_cnt > 3)//未应答前重复发送3次
{
uart_ring_tx_ack_rept_cnt = 0;
uart_ring_tx_ack_state1.bits.BIT2 = 0;
}
return;
}
if(uart_ring_tx_ack_state1.bits.BIT3)
{
uart_ring_tx_state = FRAME_UART_TX_CMD3;
if(++uart_ring_tx_ack_rept_cnt > 3)//未应答前重复发送3次
{
uart_ring_tx_ack_rept_cnt = 0;
uart_ring_tx_ack_state1.bits.BIT3 = 0;
}
return;
}
if(uart_ring_tx_ack_state1.bits.BIT4)
{
uart_ring_tx_state = FRAME_UART_TX_CMD4;
if(++uart_ring_tx_ack_rept_cnt > 3)//未应答前重复发送3次
{
uart_ring_tx_ack_rept_cnt = 0;
uart_ring_tx_ack_state1.bits.BIT4 = 0;
}
return;
}
if(uart_ring_tx_ack_state1.bits.BIT5)
{
uart_ring_tx_state = FRAME_UART_TX_CMD5;
if(++uart_ring_tx_ack_rept_cnt > 3)//未应答前重复发送3次
{
uart_ring_tx_ack_rept_cnt = 0;
uart_ring_tx_ack_state1.bits.BIT5 = 0;
}
return;
}
// if(uart_ring_tx_ack_state1.bits.BIT6)
// {
// uart_ring_tx_state = FRAME_UART_TX_CMD6;
// if(++uart_ring_tx_ack_rept_cnt > 3)//未应答前重复发送3次
// {
// uart_ring_tx_ack_rept_cnt = 0;
// uart_ring_tx_ack_state1.bits.BIT6 = 0;
// }
// return;
// }
// if(uart_ring_tx_ack_state1.bits.BIT7)
// {
// uart_ring_tx_state = FRAME_UART_TX_CMD7;
// if(++uart_ring_tx_ack_rept_cnt > 3)//未应答前重复发送3次
// {
// uart_ring_tx_ack_rept_cnt = 0;
// uart_ring_tx_ack_state1.bits.BIT7 = 0;
// }
// return;
// }
}
/*-----------------------------------------------------------------------------------------------20260606*/
/**
* 函数名:void uart_ring_tx_dispatch(void)
* 功 能:调度发送
* 参 数:无
* 返 回:无
* 备 注:
*/
void uart_ring_tx_dispatch(void)
{
// if(++uart_ring_tx_Freh < 10);////刷新率,20ms*10=200ms
// uart_ring_tx_Freh = 0;
static unsigned int uart_ring_poewr_version_cnt = 0;
uart_ring_poewr_version_cnt++;
if(uart_ring_tx_state == FRAME_UART_TX_WAIT)
{
if(++uart_ring_tx_Freh > 10)////刷新率,20ms*10=200ms
{
uart_ring_tx_Freh = 0;
uart_ring_tx_state = FRAME_UART_TX_CMD1;
uart_ring_tx_state_temp = FRAME_UART_TX_CMD1;
return;
}
}
switch(uart_ring_tx_state_temp)
{
case FRAME_UART_TX_WAIT:
{
}break;
case FRAME_UART_TX_CMD1:
{
if(uart_ring_tx_state == FRAME_UART_TX_WAIT)
{
uart_ring_tx_state = FRAME_UART_TX_CMD2;
uart_ring_tx_state_temp = FRAME_UART_TX_CMD2;
}
}break;
case FRAME_UART_TX_CMD2:
{
if(uart_ring_tx_state == FRAME_UART_TX_WAIT)
{
uart_ring_tx_state = FRAME_UART_TX_CMD3;
uart_ring_tx_state_temp = FRAME_UART_TX_CMD3;
}
}break;
case FRAME_UART_TX_CMD3:
{
if(uart_ring_tx_state == FRAME_UART_TX_WAIT)
{
uart_ring_tx_state = FRAME_UART_TX_CMD4;
uart_ring_tx_state_temp = FRAME_UART_TX_CMD4;
}
}break;
case FRAME_UART_TX_CMD4:
{
if((uart_ring_tx_state == FRAME_UART_TX_WAIT) && (uart_ring_poewr_version_cnt >= 100)) ////2s发送一次
{
uart_ring_poewr_version_cnt = 0;
uart_ring_tx_state = FRAME_UART_TX_CMD5;
uart_ring_tx_state_temp = FRAME_UART_TX_CMD5;
}
}break;
case FRAME_UART_TX_CMD5:
{
}break;
default:
{
}break;
}
}
/*-----------------------------------------------------------------------------------------------20260606*/
//启动发送
/**
* 函数名:void uart_ring_tx_start(void)
* 功 能:启动中断串口发送,已发送无需重新启动发送
* 参 数:无
* 返回值:无
* 备 注:无
*/
void uart_ring_tx_start(void)
{
US1CON3 = uart_ring_tx_buff_read();
}
/*-----------------------------------------------------------------------------------------------20260606*/
/**
* 函数名:void uart_ring_tx_scan(void)
* 功 能:发送数据更新及装载入缓冲区,用环形队列,直接使用数组
* 参 数:无
* 返回值:无
* 备 注:
*/
void uart_ring_tx_scan(void)
{
unsigned char eddr = 0;
unsigned char i = 0;
unsigned int temp = 0;
// // if (Tcnt_Disp_Dptr != 1)return;////4ms一周期
if (Tcnt_10ms_Dptr != 1){return;}////20ms一周期
////尽快应答发送
if(uart_ring_rx_ack_state_cmd >= 0x80)
{
uart_ring_rx_data_ack();
return;
}
////调度发送
uart_ring_tx_dispatch();
////发送数据更新及装载入缓冲区
switch(uart_ring_tx_state)
{
case FRAME_UART_TX_CMD0:
{
}break;
case FRAME_UART_TX_CMD1:
{
if(uart_ring_tx_buff_residue_len() < 11)return; ////缓冲区长度不足,预留1个字节
uart_tx_data[eddr] = FRAME_UART_H1;eddr++;
uart_tx_data[eddr] = FRAME_UART_H2;eddr++;
uart_tx_data[eddr] = 0x06;eddr++;
uart_tx_data[eddr] = FRAME_UART_TX_CMD1;eddr++;
i = 0;
if(Swc_Ice_Fall.Fswc_State)i |= 0x01;
if(Swc_Ice_Join.Fswc_State)i |= 0x02;
if(Swc_Wate_Levl.Fswc_State)i |= 0x04;
if(Swc_Wate_Depl.Fswc_State)i |= 0x08;
if(Swc_Ice_Wate_Low.Fswc_State)i |= 0x10;
if(Swc_Ice_Wate_High.Fswc_State)i |= 0x20;
uart_tx_data[eddr] = i;eddr++; ////开关状态
uart_tx_data[eddr] = (unsigned char)(Radc_Data_NTC0 >> 8);eddr++;////NTC1的ADC值高8位
uart_tx_data[eddr] = (unsigned char)(Radc_Data_NTC0 & 0x00ff);eddr++;////NTC1的ADC值低8位
uart_tx_data[eddr] = (unsigned char)(Radc_Data_NTC1 >> 8);eddr++;////NTC2的ADC值高8位
uart_tx_data[eddr] = (unsigned char)(Radc_Data_NTC1 & 0x00ff);eddr++;////NTC2的ADC值低8位
uart_tx_data[eddr] = checksum(uart_tx_data,eddr);eddr++;////校验和
uart_ring_tx_buff_write_n(uart_tx_data,eddr);////写入发送缓冲区
uart_ring_tx_ack_state1.bits.BIT1 = 1;////已装载数据发送中,在接收到ACK后清除
uart_ring_tx_ack_cnt = 0;////发送计数清零
uart_ring_tx_state = FRAME_UART_TX_WAIT;////等待发送状态
uart_ring_tx_start();////启动发送
}break;
case FRAME_UART_TX_CMD2:
{
if(uart_ring_tx_buff_residue_len() < 12)return; ////缓冲区长度不足,预留1个字节
uart_tx_data[eddr] = FRAME_UART_H1;eddr++;
uart_tx_data[eddr] = FRAME_UART_H2;eddr++;
uart_tx_data[eddr] = 0x07;eddr++;////数据长度
uart_tx_data[eddr] = FRAME_UART_TX_CMD2;eddr++;////命令码
uart_tx_data[eddr] = (unsigned char)(Radc_Data_NTC2 >> 8);eddr++;////NTC3的ADC值高8位
uart_tx_data[eddr] = (unsigned char)(Radc_Data_NTC2 & 0x00ff);eddr++;////NTC3的ADC值低8位
uart_tx_data[eddr] = (unsigned char)(Radc_Data_CURR0 >> 8);eddr++;////电流的ADC值高8位---出冰电机
uart_tx_data[eddr] = (unsigned char)(Radc_Data_CURR0 & 0x00ff);eddr++;////电流的ADC值低8位---出冰电机
uart_tx_data[eddr] = (unsigned char)(Radc_Data_CURR1 >> 8);eddr++;////电流的ADC值高8位---红外接收1
uart_tx_data[eddr] = (unsigned char)(Radc_Data_CURR1 & 0x00ff);eddr++;////电流的ADC值低8位---红外接收1
uart_tx_data[eddr] = checksum(uart_tx_data,eddr);eddr++;////校验和
uart_ring_tx_buff_write_n(uart_tx_data,eddr);////写入发送缓冲区
uart_ring_tx_ack_state1.bits.BIT2 = 1;////已装载数据发送中,在接收到ACK后清除
uart_ring_tx_ack_cnt = 0;////发送计数清零
uart_ring_tx_state = FRAME_UART_TX_WAIT;////等待发送状态
uart_ring_tx_start();////启动发送
}break;
case FRAME_UART_TX_CMD3:
{
if(uart_ring_tx_buff_residue_len() < 12)return; ////缓冲区长度不足,预留1个字节
uart_tx_data[eddr] = FRAME_UART_H1;eddr++;
uart_tx_data[eddr] = FRAME_UART_H2;eddr++;
uart_tx_data[eddr] = 0x07;eddr++;////数据长度
uart_tx_data[eddr] = FRAME_UART_TX_CMD3;eddr++;////命令码
uart_tx_data[eddr] = (unsigned char)(Radc_Data_CURR2 >> 8);eddr++;////电流的ADC值高8位---红外接收2
uart_tx_data[eddr] = (unsigned char)(Radc_Data_CURR2 & 0x00ff);eddr++;////电流的ADC值低8位---红外接收2
temp = Get_Flow_Pulses_Cnt_Last1();//获取流量计1脉冲计数(1秒内)
uart_tx_data[eddr] = (unsigned char)(temp >> 8);eddr++;////流量计1脉冲计数高8位
uart_tx_data[eddr] = (unsigned char)(temp & 0x00ff);eddr++;////流量计1脉冲计数低8位
temp = Get_Flow_Pulses_Cnt_Last2();//获取流量计2脉冲计数(1秒内)
uart_tx_data[eddr] = (unsigned char)(temp >> 8);eddr++;////流量计2脉冲计数高8位
uart_tx_data[eddr] = (unsigned char)(temp & 0x00ff);eddr++;////流量计2脉冲计数低8位
uart_tx_data[eddr] = checksum(uart_tx_data,eddr);eddr++;////校验和
uart_ring_tx_buff_write_n(uart_tx_data,eddr);////写入发送缓冲区
uart_ring_tx_ack_state1.bits.BIT3 = 1;////已装载数据发送中,在接收到ACK后清除
uart_ring_tx_ack_cnt = 0;////发送计数清零
uart_ring_tx_state = FRAME_UART_TX_WAIT;////等待发送状态
uart_ring_tx_start();////启动发送
}break;
case FRAME_UART_TX_CMD4:
{
if(uart_ring_tx_buff_residue_len() < 12)return; ////缓冲区长度不足,预留1个字节
uart_tx_data[eddr] = FRAME_UART_H1;eddr++;
uart_tx_data[eddr] = FRAME_UART_H2;eddr++;
uart_tx_data[eddr] = 0x07;eddr++;////数据长度
uart_tx_data[eddr] = FRAME_UART_TX_CMD4;eddr++;////命令码
temp = (unsigned int)(Radc_Dat_Ctab0);//获取温度计1数值强制转换成unsigned int
uart_tx_data[eddr] = (unsigned char)(temp >> 8);eddr++;////温度计1数值高8位
uart_tx_data[eddr] = (unsigned char)(temp & 0x00ff);eddr++;////温度计1数值低8位
temp = (unsigned int)(Radc_Dat_Ctab1);//获取温度计2数值强制转换成unsigned int
uart_tx_data[eddr] = (unsigned char)(temp >> 8);eddr++;////温度计2数值高8位
uart_tx_data[eddr] = (unsigned char)(temp & 0x00ff);eddr++;////温度计2数值低8位
temp = (unsigned int)(Radc_Dat_Ctab2);//获取温度计3数值强制转换成unsigned int
uart_tx_data[eddr] = (unsigned char)(temp >> 8);eddr++;////温度计3数值高8位
uart_tx_data[eddr] = (unsigned char)(temp & 0x00ff);eddr++;////温度计3数值低8位
uart_tx_data[eddr] = checksum(uart_tx_data,eddr);eddr++;////校验和
uart_ring_tx_buff_write_n(uart_tx_data,eddr);////写入发送缓冲区
uart_ring_tx_ack_state1.bits.BIT4 = 1;////已装载数据发送中,在接收到ACK后清除
uart_ring_tx_ack_cnt = 0;////发送计数清零
uart_ring_tx_state = FRAME_UART_TX_WAIT;////等待发送状态
uart_ring_tx_start();////启动发送
}break;
case FRAME_UART_TX_CMD5:
{
if(uart_ring_tx_buff_residue_len() < 7)return; ////缓冲区长度不足,预留1个字节
uart_tx_data[eddr] = FRAME_UART_H1;eddr++;
uart_tx_data[eddr] = FRAME_UART_H2;eddr++;
uart_tx_data[eddr] = 0x02;eddr++;////数据长度
uart_tx_data[eddr] = FRAME_UART_TX_CMD5;eddr++;////命令字
uart_tx_data[eddr] = Msys_Power_Prog_Vers;eddr++;////软件版本号
uart_tx_data[eddr] = checksum(uart_tx_data,eddr);eddr++;////校验和
uart_ring_tx_buff_write_n(uart_tx_data,eddr);////写入发送缓冲区
uart_ring_tx_ack_state1.bits.BIT5 = 1;////已装载数据发送中,在接收到ACK后清除
uart_ring_tx_ack_cnt = 0;////发送计数清零
uart_ring_tx_state = FRAME_UART_TX_WAIT;////等待发送状态
uart_ring_tx_start();////启动发送
}break;
case FRAME_UART_TX_CMD6:
{
}break;
default:
{
}break;
}
}
五、中断服务函数
串口中断使用例子:
void USCI1Interrupt() interrupt 15
{
unsigned char rx_value;
if(US1CON0&0x02)////发送中断
{
USCI1_ClearFlag(USCI1_UART_FLAG_TI); ///清除发送中断标志位
if(uart_ring_tx_buff_is_empty()) ////发送缓冲区是否为空
{
US1CON3 = uart_ring_tx_buff_read(); ////发送数据
}
}
if(US1CON0&0x01)////接收中断
{
USCI1_ClearFlag(USCI1_UART_FLAG_RI); ///清除接收中断标志位
rx_value = US1CON3; ////接收数据
uart_ring_rx_buff(rx_value);
}
}
六、主循环时间片调度
void main(void)
{
IcResourceInit();
uart_ring_init(); ////环形缓冲区初始化
while(1)
{
if(Fsec_Tcnt_T1ms)
{////1ms时间片,执行比较频繁的任务;若次级较频繁任务,结合Tcnt_Disp_Dptr进行分时处理,周期为4ms;
Fsec_Tcnt_T1ms = 0;
if(++Tcnt_Disp_Dptr >= 4){Tcnt_Disp_Dptr = 0;CLRWDT();}
Time_Data_Func();////X定时
uart_ring_rx_scan();////串口接收环形队列处理
}
////10ms时间片
if(Fsec_Tcnt_10ms)
{////10ms时间片,执行较不频繁的任务
Fsec_Tcnt_10ms = 0;
if(++Tcnt_10ms_Dptr >= 2){Tcnt_10ms_Dptr = 0;}////分散执行
uart_ring_tx_scan();////串口发送环形队列处理
}
}
}
七、完整使用示例
以下是一个完整的使用示例,展示了如何发送一帧数据,以及如何处理接收到的数据。
7.1 发送一帧数据
/**
* 函数名:void uart_ring_tx_scan(void)
* 功 能:发送数据更新及装载入缓冲区,用环形队列,直接使用数组
* 参 数:无
* 返回值:无
* 备 注:
*/
void uart_ring_tx_scan(void)
{
unsigned char eddr = 0;
unsigned char i = 0;
unsigned int temp = 0;
// // if (Tcnt_Disp_Dptr != 1)return;////4ms一周期
if (Tcnt_10ms_Dptr != 1){return;}////20ms一周期
////尽快应答发送
if(uart_ring_rx_ack_state_cmd >= 0x80)
{
uart_ring_rx_data_ack();
return;
}
////调度发送
uart_ring_tx_dispatch();
////发送数据更新及装载入缓冲区
switch(uart_ring_tx_state)
{
case FRAME_UART_TX_CMD0:
{
}break;
case FRAME_UART_TX_CMD1:
{
if(uart_ring_tx_buff_residue_len() < 11)return; ////缓冲区长度不足,预留1个字节
uart_tx_data[eddr] = FRAME_UART_H1;eddr++;
uart_tx_data[eddr] = FRAME_UART_H2;eddr++;
uart_tx_data[eddr] = 0x06;eddr++;
uart_tx_data[eddr] = FRAME_UART_TX_CMD1;eddr++;
i = 0;
if(Swc_Ice_Fall.Fswc_State)i |= 0x01;
if(Swc_Ice_Join.Fswc_State)i |= 0x02;
if(Swc_Wate_Levl.Fswc_State)i |= 0x04;
if(Swc_Wate_Depl.Fswc_State)i |= 0x08;
if(Swc_Ice_Wate_Low.Fswc_State)i |= 0x10;
if(Swc_Ice_Wate_High.Fswc_State)i |= 0x20;
uart_tx_data[eddr] = i;eddr++; ////开关状态
uart_tx_data[eddr] = (unsigned char)(Radc_Data_NTC0 >> 8);eddr++;////NTC1的ADC值高8位
uart_tx_data[eddr] = (unsigned char)(Radc_Data_NTC0 & 0x00ff);eddr++;////NTC1的ADC值低8位
uart_tx_data[eddr] = (unsigned char)(Radc_Data_NTC1 >> 8);eddr++;////NTC2的ADC值高8位
uart_tx_data[eddr] = (unsigned char)(Radc_Data_NTC1 & 0x00ff);eddr++;////NTC2的ADC值低8位
uart_tx_data[eddr] = checksum(uart_tx_data,eddr);eddr++;////校验和
uart_ring_tx_buff_write_n(uart_tx_data,eddr);////写入发送缓冲区
uart_ring_tx_ack_state1.bits.BIT1 = 1;////已装载数据发送中,在接收到ACK后清除
uart_ring_tx_ack_cnt = 0;////发送计数清零
uart_ring_tx_state = FRAME_UART_TX_WAIT;////等待发送状态
uart_ring_tx_start();////启动发送
}break;
//..............
default:
{
}break;
}
}
7.2 处理接收到的数据
/**
* 函数名:void uart_rx_handle(unsigned char *handle_data)
* 功 能:接收数据解析
* 参 数:handle_data:接收数据数组
* 返回值:无
* 备 注:uart_rx_data数组作为传入参数,地址不一定从该数组首地址开始,传入时需要注意 ;还有不要取超出数组长度
*/
static void uart_rx_handle(unsigned char *handle_data)
{////数据没问题才进入到此
unsigned char rx_data_len = handle_data[0];
unsigned char rx_data_cmd = handle_data[1];
/*-----------------------------------------------------------------------------------------------20260606*/
uart_ring_rx_err_cnt = 0; //通信正常,计数清零
Ferr_Uart_Ere8 = 0; //通信正常,标志清零
/*-----------------------------------------------------------------------------------------------20260606*/
switch(rx_data_cmd)
{
case FRAME_UART_RX_CMD1:
{////位操作字节
if(rx_data_len < 4)break; ////数据长度不对
Work_Switch_State1.all = handle_data[2]; ////位操作字节1
Work_Switch_State2.all = handle_data[3]; ////位操作字节2
Work_Switch_State3.all = handle_data[4]; ////位操作字节3
}break;
case FRAME_UART_RX_CMD2:
{////加热占空比和加热周期
if(rx_data_len < 5)break; ////数据长度不对
Work_Coff_Heat_Rec.Duty = handle_data[2]; ////加热占空比
Work_Coff_Heat_Rec.Cycle = handle_data[3]; ////加热周期
Work_Stem_Heat_Rec.Duty = handle_data[4]; ////加热占空比
Work_Stem_Heat_Rec.Cycle = handle_data[5]; ////加热周期
}break;
case FRAME_UART_RX_CMD3:
{////水泵占空比和水泵周期
if(rx_data_len < 5)break; ////数据长度不对
Work_Coff_Pump_Rec.Duty = handle_data[2]; ////水泵占空比
Work_Coff_Pump_Rec.Cycle = handle_data[3]; ////水泵周期
Work_Stem_Pump_Rec.Duty = handle_data[4]; ////水泵占空比
Work_Stem_Pump_Rec.Cycle = handle_data[5]; ////水泵周期
}break;
////应答处理
case FRAME_UART_RX_ACK1:
{
uart_ring_tx_ack_state1.bits.BIT1 = 0; ////成功接收到指令1
}break;
case FRAME_UART_RX_ACK2:
{
uart_ring_tx_ack_state1.bits.BIT2 = 0; ////成功接收到指令2
}break;
case FRAME_UART_RX_ACK3:
{
uart_ring_tx_ack_state1.bits.BIT3 = 0; ////成功接收到指令3
}break;
case FRAME_UART_RX_ACK4:
{
uart_ring_tx_ack_state1.bits.BIT4 = 0; ////成功接收到指令4
}break;
case FRAME_UART_RX_ACK5:
{
uart_ring_tx_ack_state1.bits.BIT5 = 0; ////成功接收到指令5
}break;
// case FRAME_UART_RX_ACK6:
// {
// uart_ring_tx_ack_state1.bits.BIT6 = 0; ////成功接收到指令6
// }break;
// case FRAME_UART_RX_ACK7:
// {
// uart_ring_tx_ack_state1.bits.BIT7 = 0; ////成功接收到指令7
// }break;
default:
{
break;
}
}
if(rx_data_cmd < 0x80)
{
uart_ring_rx_data_set_ack(rx_data_cmd+0x80); ////接收数据应答
}
}
八、关键要点与优化建议
8.1 关键要点
-
中断极简原则:中断里只做数据入队/出队,不做任何业务逻辑,保证中断执行时间在微秒级。
-
队列大小适配:根据实际波特率和主循环调度周期调整队列大小,避免队列溢出导致数据丢失。
-
校验机制:必须加入帧头匹配、校验和验证,避免串口干扰导致的错误数据。
-
时间片分配:接收扫描放在1ms时间片,保证实时性;发送扫描放在10ms时间片,降低CPU占用。
8.2 优化建议
-
加入溢出统计:队列满时增加计数器,便于调试和优化队列大小。
-
增加超时机制:接收帧不完整时,超时后丢弃,避免占用缓冲区。
-
多串口扩展:将队列结构体参数化,支持多串口独立队列。
-
DMA配合:高性能MCU可配合DMA使用,进一步降低CPU占用。
九、结语
环形队列+中断+时间片调度的方案,是嵌入式串口通信的经典最佳实践。它不仅解决了数据丢失、实时性差等问题,还让代码结构更清晰、可维护性更高。
本文提供的代码可直接移植到各类MCU平台,只需根据具体芯片的串口寄存器和中断号做少量修改即可。希望这篇文章能帮助你写出更健壮、更高效的串口通信代码。
如果觉得有用,欢迎点赞收藏,有问题可以在评论区交流~
更多推荐

所有评论(0)