基于串口Zmodem协议实现IAP升级

基于串口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

四、关键流程说明

  1. 握手阶段

    • 上位机发送ZMODEM_UPGRADE命令
    • Bootloader回复ZMODEM_ACK确认
  2. 数据传输

    • 文件分块(每块≤1024字节)

    • 每个数据包包含:

      • 4字节头部(类型+地址)
      • 2字节长度
      • 数据内容
      • 2字节CRC16
  3. 错误处理

    • 超时重传机制(默认5次)
    • CRC校验失败自动请求重传
  4. 完成升级

    • 发送ZMODEM_EOT结束符
    • Bootloader校验整个文件完整性
    • 设置跳转标志位并重启

 

专注于matlab/simulink,电子电路,编程