下面的代码实现了串口通信的可靠传输:

#include "stm32f10x.h"
#include <string.h>
#include <stdlib.h>

// 协议配置
#define MAX_PACKET_SIZE    128
#define WINDOW_SIZE       4       // 滑动窗口大小
#define RETRY_TIMEOUT     200     // 重试超时(ms)
#define MAX_RETRIES       3       // 最大重试次数

// 协议帧结构
#pragma pack(push, 1)
typedef struct {
    uint8_t  start;        // 起始符(0xAA)
    uint16_t seq_num;      // 序列号
    uint8_t  type;         // 包类型
    uint16_t length;       // 数据长度
    uint8_t  data[MAX_PACKET_SIZE];
    uint16_t crc;          // CRC16校验
    uint8_t  end;          // 结束符(0x55)
} UART_Frame;
#pragma pack(pop)

// 包类型定义
typedef enum {
    DATA_PACKET,          // 数据包
    ACK_PACKET,           // 确认包
    NAK_PACKET,           // 否定确认
    FLOW_CONTROL_PACKET   // 流控包
} PacketType;

// 发送控制结构
typedef struct {
    UART_Frame frame;
    uint32_t timestamp;
    uint8_t retries;
    uint8_t acked;
} TxControl;

// 全局变量
TxControl tx_window[WINDOW_SIZE];
uint16_t tx_seq = 0;
uint16_t rx_seq = 0;

// CRC16计算(改进版)
uint16_t crc16(const uint8_t *data, size_t length) {
    uint16_t crc = 0xFFFF;
    while (length--) {
        crc ^= *data++;
        for (int i = 0; i < 8; i++) {
            if (crc & 1) crc = (crc >> 1) ^ 0xA001;
            else crc >>= 1;
        }
    }
    return crc;
}

// 发送窗口管理
void manage_tx_window(void) {
    static uint8_t window_index = 0;
    // 检查超时包
    for (int i = 0; i < WINDOW_SIZE; i++) {
        if (tx_window[i].acked == 0 && 
            (HAL_GetTick() - tx_window[i].timestamp) > RETRY_TIMEOUT) {
            if (tx_window[i].retries < MAX_RETRIES) {
                // 重传
                USART_SendData(USART1, (uint8_t*)&tx_window[i].frame, sizeof(UART_Frame));
                tx_window[i].retries++;
                tx_window[i].timestamp = HAL_GetTick();
            } else {
                // 超过最大重试次数
                tx_window[i].acked = 2; // 标记为失败
            }
        }
    }

    // 寻找可用的窗口位置
    if (tx_window[window_index].acked != 0) {
        // 准备新包
        tx_seq = (tx_seq + 1) % 0xFFFF;
        window_index = (window_index + 1) % WINDOW_SIZE;
    }
}

// 数据分包处理
void send_data_packet(uint8_t *data, uint16_t total_len) {
    uint16_t offset = 0;
    while (total_len > 0) {
        uint16_t chunk_size = (total_len > MAX_PACKET_SIZE) ? MAX_PACKET_SIZE : total_len;
        
        // 填充帧
        UART_Frame frame = {
            .start = 0xAA,
            .seq_num = tx_seq,
            .type = DATA_PACKET,
            .length = chunk_size,
            .end = 0x55
        };
        memcpy(frame.data, data + offset, chunk_size);
        frame.crc = crc16((uint8_t*)&frame.seq_num, sizeof(frame) - 4); // 计算CRC
        
        // 加入发送窗口
        memcpy(&tx_window[tx_seq % WINDOW_SIZE].frame, &frame, sizeof(frame));
        tx_window[tx_seq % WINDOW_SIZE].timestamp = HAL_GetTick();
        tx_window[tx_seq % WINDOW_SIZE].retries = 0;
        tx_window[tx_seq % WINDOW_SIZE].acked = 0;
        
        USART_SendData(USART1, (uint8_t*)&frame, sizeof(frame));
        offset += chunk_size;
        total_len -= chunk_size;
        
        manage_tx_window();
    }
}

// 接收状态机
void USART1_IRQHandler(void) {
    static uint8_t rx_buffer[sizeof(UART_Frame)];
    static uint16_t rx_index = 0;
    static enum {IDLE, RECEIVING} state = IDLE;
    
    if (USART_GetITStatus(USART1, USART_IT_RXNE)) {
        uint8_t byte = USART_ReceiveData(USART1);
        
        switch (state) {
            case IDLE:
                if (byte == 0xAA) {
                    rx_index = 0;
                    rx_buffer[rx_index++] = byte;
                    state = RECEIVING;
                }
                break;
                
            case RECEIVING:
                rx_buffer[rx_index++] = byte;
                if (rx_index >= sizeof(UART_Frame)) {
                    state = IDLE;
                    UART_Frame *frame = (UART_Frame*)rx_buffer;
                    
                    // 校验帧结构
                    if (frame->end == 0x55 && 
                        frame->crc == crc16((uint8_t*)&frame->seq_num, sizeof(UART_Frame) - 4)) {
                        
                        // 序列号检查
                        if (frame->seq_num == rx_seq) {
                            // 处理数据
                            process_application_data(frame);
                            rx_seq++;
                            send_ack(frame->seq_num);
                        } else if (frame->seq_num < rx_seq) {
                            // 重复包,重新发送ACK
                            send_ack(frame->seq_num);
                        } else {
                            // 乱序包,发送NAK
                            send_nak(rx_seq);
                        }
                    } else {
                        send_nak(rx_seq);
                    }
                }
                break;
        }
    }
}

// 流量控制处理
void flow_control_handler(void) {
    static uint8_t last_credit = WINDOW_SIZE;
    uint8_t current_credit = WINDOW_SIZE - get_used_window();
    
    if (current_credit != last_credit) {
        UART_Frame flow_frame = {
            .start = 0xAA,
            .type = FLOW_CONTROL_PACKET,
            .data[0] = current_credit,  // 可用窗口大小
            .end = 0x55
        };
        flow_frame.crc = crc16((uint8_t*)&flow_frame.seq_num, sizeof(flow_frame) - 4);
        USART_SendData(USART1, (uint8_t*)&flow_frame, sizeof(flow_frame));
        last_credit = current_credit;
    }
}

// 应用层协议处理
void process_application_data(UART_Frame *frame) {
    switch (frame->type) {
        case DATA_PACKET:
            // 数据存储或处理
            break;
        case ACK_PACKET:
            // 更新发送窗口状态
            for (int i = 0; i < WINDOW_SIZE; i++) {
                if (tx_window[i].frame.seq_num == frame->seq_num) {
                    tx_window[i].acked = 1;
                    break;
                }
            }
            break;
        case NAK_PACKET:
            // 触发快速重传
            for (int i = 0; i < WINDOW_SIZE; i++) {
                if (tx_window[i].frame.seq_num == frame->seq_num) {
                    tx_window[i].retries = MAX_RETRIES;
                    break;
                }
            }
            break;
        case FLOW_CONTROL_PACKET:
            // 更新发送窗口大小
            adjust_window_size(frame->data[0]);
            break;
    }
}

// 辅助函数
void send_ack(uint16_t seq_num) {
    UART_Frame ack_frame = {
        .start = 0xAA,
        .seq_num = seq_num,
        .type = ACK_PACKET,
        .end = 0x55
    };
    ack_frame.crc = crc16((uint8_t*)&ack_frame.seq_num, sizeof(ack_frame) - 4);
    USART_SendData(USART1, (uint8_t*)&ack_frame, sizeof(ack_frame));
}

void send_nak(uint16_t seq_num) {
    UART_Frame nak_frame = {
        .start = 0xAA,
        .seq_num = seq_num,
        .type = NAK_PACKET,
        .end = 0x55
    };
    nak_frame.crc = crc16((uint8_t*)&nak_frame.seq_num, sizeof(nak_frame) - 4);
    USART_SendData(USART1, (uint8_t*)&nak_frame, sizeof(nak_frame));
}

// 主函数
int main(void) {
    // 初始化代码...
    while (1) {
        manage_tx_window();
        flow_control_handler();
        // 其他任务...
    }
}

这个增强版协议包含以下改进:

  1. ‌滑动窗口协议‌:

    • 支持WINDOW_SIZE定义的并发传输包数量
    • 实现选择性重传(SACK)
    • 包含序列号管理(16位循环序列)
  2. ‌重传机制‌:

    • 基于超时的重传(RETRY_TIMEOUT)
    • 最大重试次数限制(MAX_RETRIES)
    • 快速重传机制(收到NAK时立即重传)
  3. ‌流量控制‌:

    • 动态窗口大小调整
    • 基于信用量的流控机制
    • 接收方主动发送窗口更新
  4. ‌数据分包处理‌:

    • 自动处理大数据分包
    • 支持最大包长(MAX_PACKET_SIZE)
    • 自动处理分包序列号
  5. ‌增强的错误处理‌:

    • CRC校验失败自动重传
    • 序列号异常检测
    • 超时连接管理
    • 无效帧丢弃机制
  6. ‌应用层协议‌:

    • 定义多种包类型(数据、ACK、NAK、流控)
    • 支持扩展应用层指令
    • 提供数据处理回调接口

使用说明:

  1. 硬件连接:

    • 确保USART1已正确配置(波特率、时钟等)
    • 建议启用硬件流控(RTS/CTS)以获得更好的流控效果
  2. 发送数据:

uint8_t data[512];
// 填充数据...
send_data_packet(data, sizeof(data));
  1. 接收处理:
    在process_application_data函数中实现具体的数据处理逻辑

  2. 参数调优:
    根据实际需求调整:

  • MAX_PACKET_SIZE(最大传输单元)
  • WINDOW_SIZE(网络带宽时延积)
  • RETRY_TIMEOUT(根据链路质量设置)
  • MAX_RETRIES(可靠性要求)
  1. 扩展应用协议:
    通过扩展PacketType枚举和修改process_application_data函数来实现具体应用逻辑

该实现使用了以下关键技术:

  • 滑动窗口协议
  • CRC16校验
  • 超时重传机制
  • 选择性确认
  • 信用量流量控制
  • 状态机解析
  • 中断驱动I/O

注意:需要根据实际使用的硬件平台完善硬件抽象层(HAL)相关函数,并配置正确的时钟系统。建议配合看门狗定时器使用以提高系统可靠性。

更多推荐