在嵌入式开发中,串口通信是最常用的外设通信方式之一。然而,直接在中断里处理数据、或者在主循环里阻塞等待接收,都会带来实时性差、数据丢失、代码耦合等问题。

本文将分享一套基于环形队列(Ring Buffer)+ 中断接收发送 + 主循环时间片调度的串口数据处理方案,并提供完整的代码示例,可直接应用于 8051、STM32、ESP32 等各类 MCU 平台。

一、为什么需要环形队列?

传统的串口处理方式存在以下痛点:

  1. 中断里处理业务逻辑:中断执行时间过长,会影响其他中断的响应,甚至导致数据丢包。

  2. 全局变量传递数据:代码耦合度高,难以维护,且容易出现数据竞争。

  3. 阻塞式接收:主循环被串口占用,无法执行其他任务,实时性差。

环形队列(Ring Buffer)是一种先进先出(FIFO)的循环数据结构,完美解决了上述问题:

  • 解耦中断与业务:中断只负责把数据写入队列,主循环负责读取处理,各司其职。

  • 避免数据丢失:即使主循环暂时繁忙,数据也会暂存在队列中,不会丢失。

  • 非阻塞设计:主循环可以按时间片调度,兼顾串口处理与其他任务。


二、整体架构设计

本方案的核心架构分为三层:

  1. 中断层:串口接收/发送中断,仅负责数据的入队/出队,执行时间极短。

  2. 驱动层:环形队列的初始化、入队、出队、判空、判满等核心操作。

  3. 业务层:主循环按时间片调度,从队列中读取数据并解析处理,执行业务逻辑。

数据流向:


串口接收中断 → 数据写入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 关键要点

  1. 中断极简原则:中断里只做数据入队/出队,不做任何业务逻辑,保证中断执行时间在微秒级。

  2. 队列大小适配:根据实际波特率和主循环调度周期调整队列大小,避免队列溢出导致数据丢失。

  3. 校验机制:必须加入帧头匹配、校验和验证,避免串口干扰导致的错误数据。

  4. 时间片分配:接收扫描放在1ms时间片,保证实时性;发送扫描放在10ms时间片,降低CPU占用。

8.2 优化建议

  1. 加入溢出统计:队列满时增加计数器,便于调试和优化队列大小。

  2. 增加超时机制:接收帧不完整时,超时后丢弃,避免占用缓冲区。

  3. 多串口扩展:将队列结构体参数化,支持多串口独立队列。

  4. DMA配合:高性能MCU可配合DMA使用,进一步降低CPU占用。


九、结语

环形队列+中断+时间片调度的方案,是嵌入式串口通信的经典最佳实践。它不仅解决了数据丢失、实时性差等问题,还让代码结构更清晰、可维护性更高。

本文提供的代码可直接移植到各类MCU平台,只需根据具体芯片的串口寄存器和中断号做少量修改即可。希望这篇文章能帮助你写出更健壮、更高效的串口通信代码。

如果觉得有用,欢迎点赞收藏,有问题可以在评论区交流~

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐