基于串口Zmodem协议实现IAP升级
一、系统架构设计
+---------------------+ +---------------------+
| 上位机 | | 目标设备 |
| - Zmodem发送端 |<------>| - Bootloader |
| - 串口通信 | | - Flash存储区 |
| - 文件分块 | | - CRC校验模块 |
+---------------------+ +---------------------+
| |
| 1.发送升级命令 | 2.进入接收模式
|------------------------->|
| |
| 3.传输文件数据 | 4.接收数据校验
|<-------------------------|
| |
| 5.发送结束标志 | 6.跳转APP
|------------------------->|
二、关键代码实现
1. Bootloader初始化
#include "DSP28x_Project.h"
#define ZMODEM_SOH 0x01
#define ZMODEM_STX 0x02
#define ZMODEM_EOT 0x04
#define ZMODEM_ACK 0x06
#define ZMODEM_NAK 0x15
#define ZMODEM_CAN 0x18
void Zmodem_Init() {
// 配置串口1
InitUart1(115200, UART_DATA_8D_1N_1S); // 波特率115200,8N1
UART1Regs.UARTFFTX.bit.TXFFIENA = 1; // 启用发送FIFO
UART1Regs.UARTFFRX.bit.RXFFIENA = 1; // 启用接收FIFO
}
// 检测升级命令
void Check_Upgrade_Cmd() {
if(UART1Regs.UARTRXBUF == ':') { // 接收到起始符
Delay_Us(100000); // 等待完整命令
if(StrCmp((char*)UART1Regs.UARTRXBUF, "ZMODEM_UPGRADE")) {
Enter_Zmodem_Mode(); // 进入Zmodem接收模式
}
}
}
2. Zmodem协议核心
typedef struct {
uint8_t header;
uint16_t length;
uint32_t addr;
uint8_t data[1024];
uint16_t crc;
} ZModem_Packet;
uint16_t Zmodem_CalculateCRC(uint8_t *buf, int len) {
uint16_t crc = 0;
while(len--) {
crc ^= (uint16_t)(*buf++) << 8;
for(int i=0; i<8; i++) {
crc = (crc & 0x8000) ? (crc << 1) ^ 0x1021 : crc << 1;
}
}
return crc;
}
void Zmodem_Send_Packet(uint8_t type, uint32_t addr, uint8_t *data, int len) {
ZModem_Packet pkt;
pkt.header = type;
pkt.length = len;
pkt.addr = addr;
memcpy(pkt.data, data, len);
pkt.crc = Zmodem_CalculateCRC(data, len);
UART1_WriteBuffer(&pkt, sizeof(pkt));
}
void Zmodem_Recv_Callback() {
static ZModem_Packet rx_pkt;
if(UART1Regs.UARTRXBUF & 0x8000) { // 接收完成
if(Check_CRC((uint8_t*)rx_pkt.crc, rx_pkt.data, rx_pkt.length)) {
Write_Flash(rx_pkt.addr, rx_pkt.data, rx_pkt.length);
UART1_WriteBuffer(&ZMODEM_ACK, 1); // 发送ACK
} else {
UART1_WriteBuffer(&ZMODEM_NAK, 1); // 发送NAK
}
}
}
3. Flash写入实现
#define APP_START_ADDR 0x08010000
#define SECTOR_SIZE 0x1000
void Write_Flash(uint32_t addr, uint8_t *buf, int len) {
Flash_Erase(addr, SECTOR_SIZE); // 擦除对应扇区
Flash_Program(addr, buf, len); // 写入数据
Flash_Verify(addr, buf, len); // 校验写入
}
三、上位机实现(Python示例)
import serial
import zlib
class ZmodemSender:
def __init__(self, port, baudrate=115200):
self.ser = serial.Serial(port, baudrate)
self.buffer = bytearray()
def send_file(self, filepath):
with open(filepath, 'rb') as f:
while True:
data = f.read(1024)
if not data:
break
# 添加CRC校验
crc = zlib.crc32(data) & 0xffff
packet = data + crc.to_bytes(2, 'little')
self.ser.write(packet)
# 发送结束标志
self.ser.write(b'\x04') # EOT
# 使用示例
sender = ZmodemSender('/dev/ttyUSB0')
sender.send_file('app.bin')
参考代码 利用串口通过zmodem协议在iap下更新APP程序 www.youwenfan.com/contentcnr/56845.html
四、关键流程说明
-
握手阶段
- 上位机发送
ZMODEM_UPGRADE命令 - Bootloader回复
ZMODEM_ACK确认
- 上位机发送
-
数据传输
-
文件分块(每块≤1024字节)
-
每个数据包包含:
- 4字节头部(类型+地址)
- 2字节长度
- 数据内容
- 2字节CRC16
-
-
错误处理
- 超时重传机制(默认5次)
- CRC校验失败自动请求重传
-
完成升级
- 发送
ZMODEM_EOT结束符 - Bootloader校验整个文件完整性
- 设置跳转标志位并重启
- 发送