数据结构
建一个环形缓冲区
// 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辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。