04.MCU串口接收数据

数据结构

建一个环形缓冲区

// xmodem 3 + 128byte数据 + 2
// 环形缓冲区至少需要一个完整包的空间,考虑到中断可能连续接收,增大到 512 以应对偶尔的处理延迟。
#define XMODEM_RX_BUF_SIZE 512

typedef struct {
    uint8_t buf[XMODEM_RX_BUF_SIZE];
    volatile uint16_t head;
    volatile uint16_t tail;
} RingBuffer_t;

static RingBuffer_t g_ring = {.head = 0, .tail = 0};

/*-------------------- 控制块 --------------------*/
/*-------------------- 控制结构体 --------------------*/
typedef struct {
    XmodemState_t state;
    uint8_t buffer[128];      // 当前包数据
    uint8_t block_index;      // 包序号
    uint8_t block_index_comp; // 包序号补码
    uint16_t crc;             // 接收到的CRC
    uint16_t calc_crc;        // 计算出的CRC
    uint8_t checksum;         // 校验和(非CRC模式)
    uint8_t rx_count;         // 当前包已收字节计数
    uint32_t timeout_counter; // 超时计数器(需外部累加)
    uint8_t retry_count;      // 当前重试次数
    uint8_t use_crc;          // 1=CRC16, 0=校验和
    uint32_t total_bytes;     // 总接收字节数(仅统计)
    uint8_t retry_max;        // 最大重试次数
} XmodemCtrl_t;
static XmodemCtrl_t g_ctrl;
typedef enum {
    XMODEM_IDLE,      // 空闲
    XMODEM_WAIT_SOH,  // 等待包起始
    XMODEM_RECV_DATA, // 接收数据块
    XMODEM_RECV_CRC,  // 接收校验字节
    XMODEM_COMPLETE,  // 完成
    XMODEM_ABORT      // 中止
} XmodemState_t;
/*-------------------- 复位控制块 --------------------*/
static void reset_control(void) {
    memset(&g_ctrl, 0, sizeof(g_ctrl));
    g_ctrl.state = XMODEM_IDLE;
    g_ctrl.retry_max = 10;
    g_ctrl.use_crc = 1;
}

void xmodem_init(void) {
    reset_control();
    g_ring.head = 0;
    g_ring.tail = 0;
    g_xmodem_active = 0;
}

/*
    head(生产者写位置),tail(消费者读位置)
*/
void xmodem_push_byte(uint8_t ch) {
    // 用取模运算实现环形。
    uint16_t next = (g_ring.head + 1) % XMODEM_RX_BUF_SIZE;
    // 入队操作:计算下一个位置,判断是否满(head+1 == tail),若满则丢弃。
    //  因为是单线程,所以无锁
    if (next != g_ring.tail) {
        g_ring.buf[g_ring.head] = ch;
        g_ring.head = next;
    }
}

**
 * @brief 启动Xmodem接收,强制CRC模式,最多10次握手尝试
 */
void xmodem_start(void) {
    /* 清空环形缓冲区 */
    g_ring.head = 0;
    g_ring.tail = 0;

    /* 初始化控制块 */
    reset_control();
    g_ctrl.state = XMODEM_WAIT_SOH;
    g_ctrl.retry_count = 0;
    g_ctrl.retry_max = 10;
    g_xmodem_active = 1;
    g_handshake_ok = 0; // 复位握手标志

    shell_uart_write_string("\r\nXmodem receiver started (CRC mode). Waiting for sender...\r\n");
}

/**
 * @brief Xmodem状态机(仅CRC校验)
 */
void xmodem_process(void) {
    uint8_t ch = 0;
    uint8_t has_data = 0;
    XmodemCtrl_t *p = &g_ctrl;

    /* 取数据 */
    if (g_ring.head != g_ring.tail) {
        ch = g_ring.buf[g_ring.tail];
        g_ring.tail = (g_ring.tail + 1) % XMODEM_RX_BUF_SIZE;
        has_data = 1;
        p->timeout_counter = 0; // 收到数据复位超时
        p->retry_count = 0;
    }

    /* 无数据时,根据状态处理超时 */
    if (!has_data) {
        p->timeout_counter++;
        if (p->state == XMODEM_WAIT_SOH) {
            // 握手阶段:由 xmodem_start 的重试机制控制,但此处我们可以增加超时退出
            // 为了简单,我们使用固定的次数判断(20ms周期,10秒 = 500次)
            if (p->timeout_counter >= 500) { // 10秒无数据且仍处于握手状态
                g_xmodem_active = 0;
                shell_uart_write_string("\r\nXmodem handshake timeout.\r\n");
                shell_uart_write_string(SHELL_PROMPT);
                return;
            }
            // 如果在握手阶段且未超时,但需要定期发送 'C' 进行重试
            // 这里每2秒重发一次 'C',根据 timeout_counter 计数
            if (p->timeout_counter % 100 == 0) { // 每 100*20ms = 2秒
                send_byte(CRC);
            }
        } else if (p->state == XMODEM_RECV_DATA) {
            // 数据接收阶段超时检测(10秒)
            if (p->timeout_counter >= 500) {
                g_xmodem_active = 0;
                shell_uart_write_string("\r\nXmodem timeout: no data for 10 seconds. Aborting...\r\n");
                send_byte(CAN);
                shell_uart_write_string(SHELL_PROMPT);
                return;
            }
        }
        return; // 无数据,退出
    }

    /* 有数据,状态机处理 */
    switch (p->state) {
    case XMODEM_WAIT_SOH:
        if (ch == SOH) {
            p->state = XMODEM_RECV_DATA;
            p->rx_count = 0;
            p->crc = 0;
            g_handshake_ok = 1; // 握手成功
        } else if (ch == EOT) {
            // 传输结束
            send_byte(ACK);
            p->state = XMODEM_COMPLETE;
            g_xmodem_active = 0;
            g_handshake_ok = 2; // 握手失败(空文件)
            
            shell_uart_write_string("\r\nXMODEM_COMPLETE!\r\n");
            shell_uart_write_string(" p->total_bytes:");
            shell_uart_write_uint(p->total_bytes);
            shell_uart_write_string("\r\n");
            shell_uart_write_string(SHELL_PROMPT);
        } else if (ch == CAN) {
            send_byte(ACK);
            p->state = XMODEM_ABORT;
            g_xmodem_active = 0;
            g_handshake_ok = 2; // 握手失败(取消)
            shell_uart_write_string("\r\nTransfer cancelled by sender\r\n");
            shell_uart_write_string(SHELL_PROMPT);
        }
        break;

    case XMODEM_RECV_DATA:
        // 原有的数据接收逻辑(保持不变)
        if (p->rx_count == 0) {
            p->block_index = ch;  // 第1字节:块号
        } else if (p->rx_count == 1) {
            p->block_index_comp = ch; // 第2字节:块号补码校验
            if ((uint8_t)(p->block_index + p->block_index_comp) != 0xFF) {
                send_byte(NAK);
                p->state = XMODEM_WAIT_SOH;
                p->rx_count = 0;
                break;
            }
        } else if (p->rx_count < 130) {
            p->buffer[p->rx_count - 2] = ch;
        } else if (p->rx_count == 130) {
            p->crc = (uint16_t)ch << 8;
        } else if (p->rx_count == 131) {
            p->crc |= ch;
            p->calc_crc = crc16(p->buffer, 128);
            if (p->calc_crc == p->crc) {
                send_byte(ACK);
                p->total_bytes += 128;
                xmodem_data_received(p->buffer, 128);
            } else {
                send_byte(NAK);
            }
            p->state = XMODEM_WAIT_SOH;
            p->rx_count = 0;
            break; // 需要手动跳出,防止执行最后的 p->rx_count++
        }
        p->rx_count++;
        break;

    default:
        break;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容