Xshell->MCU Ymodem协议实现

使用Xshell进行ymodem传输时的一个小trick(用于IAP编程中的bootloader程序

Ymodem通信信号

符号 数值 含义
SOH 0x01 128字节数据包
STX 0x02 1024字节数据包
EOT 0x04 结束传输
ACK 0x06 正确接收回应
NAK 0x15 错误接收回应
CAN 0x18 传输终止
C 0x43 请求数据

1、MCU发送"C"命令

cs 复制代码
            else
            {
                // 没有收到数据,根据状态发送请求
                if (t_sts == 0 || t_sts == 3)
                {
                    fwt_cnt = 0;
                    UART_Send_Byte('C'); // 请求发送
                }

                if (t_sts == 1 || t_sts == 2)
                {
                    fwt_cnt = 0;
                    UART_Send_Byte(0X15); // 发送NAK
                }

                // 超时处理
                if (ot++ > 20) t_cmp = ERR_YMODEM_OVERTIME;
            }

2、处理Xshell发送的起始帧

cs 复制代码
                    case 133:
                    case 134:
                    case 136:
                        // 处理文件头帧
                        {
                            int off = 0;
                            int index = 0;

                            if (fwt_cnt > 133)
                            {
                                while (fwt_buf[index++] != 0X0D);
                                off = index;
                            }

                            crc = calcrc(fwt_buf + 3 + off, 128);

                            fwt_cnt = 0;

                            if (t_sts == 0)
                            {
                                if (fwt_buf[1 + off] == 0X00 && fwt_buf[2 + off] == 0xff)
                                {
                                    if ((((unsigned short)crc) == (((int)fwt_buf[131 + off]) << 8) | (fwt_buf[132 + off])))
                                    {
                                        // 解析文件名
                                        strcpy(filename + 1, fwt_buf + 3 + off);

                                        // 创建新文件
                                        f_open(&file, filename, FA_CREATE_ALWAYS | FA_WRITE);

                                        t_sts = 1;

                                        fwt_cnt = 0;
                                        UART_Send_Byte(0x06); // 发送ACK
                                    }
                                    else UART_Send_Byte(0x15); // CRC错误,发送NAK
                                }
                                else UART_Send_Byte(0x15); // 帧头错误,发送NAK
                            }
                            break;
                        }

3、t_sts==0执行一次1步骤

4、处理Xshell发送的数据帧

cs 复制代码
                        case 1029:
                        // 处理数据帧
                        t_sts = 2;

                        crc = calcrc(fwt_buf + 3, 1024);

                        fwt_cnt = 0;

                        {
                            if (fwt_buf[1] == to_fn)
                            {
                                to_fn++;

                                if ((fwt_buf[1] + fwt_buf[2]) == 0xff)
                                {
                                    if ((((unsigned short)crc) == (((int)fwt_buf[1027]) << 8) | (fwt_buf[1028])))
                                    {
                                        unsigned int nw = 0;

                                        // 写入数据到文件
                                        f_write(&file, fwt_buf + 3, 1024, &nw);

                                        UART_Send_Byte(0x06); // 发送ACK
                                    }
                                    else UART_Send_Byte(0x15); // CRC错误
                                }
                                else UART_Send_Byte(0x15); // 序号校验错误
                            }
                            else UART_Send_Byte(0x15); // 序号错误
                        }
                        break;

                    default:
                        fwt_cnt = 0;
                        // 未知帧类型
                        // if(t_sts==0 || t_sts==1) UART_Send_Byte(0X15);
                        break;
                }
            }

5、处理Xshell发送的EOT命令

cs 复制代码
                   case 1:
                        // 处理EOT帧
                        fwt_cnt = 0;
                        if (0X04 == fwt_buf[0])
                        {
                            if (t_sts == 2)
                            {
                                t_sts = 3;
                                UART_Send_Byte(0x15); // 发送NAK
                            }
                            else if (t_sts == 3)
                            {
                                t_sts = 4;
                                fwt_cnt = 0;
                                UART_Send_Byte(0x06); // 发送ACK
                                UART_Send_Byte('C');  // 发送'C'
                            }
                        }
                        break;

7、t_sts== 3再一次接收到EOT执行步骤5

8、处理Xshell发送的结束帧

cs 复制代码
                    case 135:
                        // 处理结束帧
                        if (t_sts == 4)
                        {
                            if (fwt_buf[1] == 0X00 && fwt_buf[2] == 0xff)
                            {
                                if (fwt_buf[131] == 0 && fwt_buf[132] == 0)
                                {
                                    t_sts = 5;
                                    fwt_cnt = 0;

                                    UART_Send_Byte(0x06); // 发送ACK

                                    {
                                        UART_Send_Byte(0X4F);//MCU回应表示接收完成
                                        UART_Send_Byte(0X4F);
                                    }
                                }
                                else UART_Send_Byte(0x15);
                            }
                            else UART_Send_Byte(0x15);
                        }
                        break;

**135字节的原因:**按照Ymodem协议标准是133,但是经过Xshell的传输完成会添加两个0x4F做结尾,表示传输完成。

延时设计

cs 复制代码
            {
                unsigned short i = 0;

                // 根据状态设置等待时间
                if (t_sts == 0)
                    wt = 1000;
                else
                    wt = 120;

                for (i = 0; i < wt; i++) HAL_Delay(1);
            }

CAN命令处理

cs 复制代码
                    case 10:
                        // CAN处理取消帧
                        fwt_cnt = 0;
                        if (0X18 == fwt_buf[0])
                        {
                            unsigned char buf[21] = {0X18, 0X18, 0X18, 0X18, 0X18, 0X08, 0X20, 0X08, 0X08, 0X20, 0X08, 0X08, 0X20, 0X08, 0X08, 0X20, 0X08, 0X08, 0X20, 0X08};
                            unsigned char i = 0;

                            t_sts = 6;

                            for (i = 0; i < 20; i++)
                                UART_Send_Byte(buf[i]);

                            t_cmp = ERR_YMODEM_SUCCESS;
                        }
                        break;

CRC校验算法

crc16几种标准校验算法及c语言代码

cs 复制代码
/**
 * @brief  计算CRC
 * @param  ptr : 数据缓冲指针
 *         count : 数据长度
 * @retval uint32_t : CRC值
 * @verbatim
 * @note  CRC16_XMODEM 
 * @endverbatim
 */

int calcrc(char *ptr, int count)
{
    int crc = 0;//CRC寄存器初始值为0x0000
    char i = 0;

    while ((--count) >= 0)
    {
        crc = crc ^ (int)*ptr++ << 8;
        i = 8;
        do
        {
            if (crc & 0x8000)
                crc = crc << 1 ^ 0x1021;//^异或 相异为1 相同为0
            else
                crc = crc << 1;
        } while (--i);
    }

    return crc;
}

完整代码

cs 复制代码
static FIL file;

extern char filename[20];

/**
  * @brief  Ymodem下载文件
  * @param  无
  * @retval err_code : 参考ERR_YMODEM定义
  * @verbatim
  * 具体实现参考YMODEM协议标准与XSHELL协议扩展
  * @endverbatim
  */
int ymodem_down_file(void)
{
    unsigned char t_cmp = 0;      // 传输完成标志
    unsigned char to_fn = 1;      // 文件块序号
    unsigned char frame_n = 0;    // 帧号(未使用)
    unsigned char t_sts = 0;      // 传输状态
    unsigned short wt = 0;        // 等待时间
    unsigned int ot = 0;          // 超时计数
    unsigned short crc = 0;       // CRC校验值

    fw_transfering = 1;
    fwt_cnt = 0;

    printf("Download File via Ymodem\r\n");

    {
        while (!t_cmp)
        {
            {
                unsigned short i = 0;

                // 根据状态设置等待时间
                if (t_sts == 0)
                    wt = 1000;
                else
                    wt = 120;

                for (i = 0; i < wt; i++) HAL_Delay(1);
            }

            if (fwt_cnt > 0)
            {
                ot = 0;

                switch (fwt_cnt)
                {
                    case 1:
                        // 处理EOT帧
                        fwt_cnt = 0;
                        if (0X04 == fwt_buf[0])
                        {
                            if (t_sts == 2)
                            {
                                t_sts = 3;
                                UART_Send_Byte(0x15); // 发送NAK
                            }
                            else if (t_sts == 3)
                            {
                                t_sts = 4;
                                fwt_cnt = 0;
                                UART_Send_Byte(0x06); // 发送ACK
                                UART_Send_Byte('C');  // 发送'C'
                            }
                        }
                        break;

                    case 10:
                        // CAN处理取消帧
                        fwt_cnt = 0;
                        if (0X18 == fwt_buf[0])
                        {
                            unsigned char buf[21] = {0X18, 0X18, 0X18, 0X18, 0X18, 0X08, 0X20, 0X08, 0X08, 0X20, 0X08, 0X08, 0X20, 0X08, 0X08, 0X20, 0X08, 0X08, 0X20, 0X08};
                            unsigned char i = 0;

                            t_sts = 6;

                            for (i = 0; i < 20; i++)
                                UART_Send_Byte(buf[i]);

                            t_cmp = ERR_YMODEM_SUCCESS;
                        }
                        break;

                    case 133:
                    case 134:
                    case 136:
                        // 处理文件头帧
                        {
                            int off = 0;
                            int index = 0;

                            if (fwt_cnt > 133)
                            {
                                while (fwt_buf[index++] != 0X0D);
                                off = index;
                            }

                            crc = calcrc(fwt_buf + 3 + off, 128);

                            fwt_cnt = 0;

                            if (t_sts == 0)
                            {
                                if (fwt_buf[1 + off] == 0X00 && fwt_buf[2 + off] == 0xff)
                                {
                                    if ((((unsigned short)crc) == (((int)fwt_buf[131 + off]) << 8) | (fwt_buf[132 + off])))
                                    {
                                        // 解析文件名
                                        strcpy(filename + 1, fwt_buf + 3 + off);

                                        // 创建新文件
                                        f_open(&file, filename, FA_CREATE_ALWAYS | FA_WRITE);

                                        t_sts = 1;

                                        fwt_cnt = 0;
                                        UART_Send_Byte(0x06); // 发送ACK
                                    }
                                    else UART_Send_Byte(0x15); // CRC错误,发送NAK
                                }
                                else UART_Send_Byte(0x15); // 帧头错误,发送NAK
                            }
                            break;
                        }

                    case 135:
                        // 处理结束帧
                        if (t_sts == 4)
                        {
                            if (fwt_buf[1] == 0X00 && fwt_buf[2] == 0xff)
                            {
                                if (fwt_buf[131] == 0 && fwt_buf[132] == 0)
                                {
                                    t_sts = 5;
                                    fwt_cnt = 0;

                                    UART_Send_Byte(0x06); // 发送ACK

                                    {
                                        UART_Send_Byte(0X4F);//O
                                        UART_Send_Byte(0X4F);
                                    }
                                }
                                else UART_Send_Byte(0x15);
                            }
                            else UART_Send_Byte(0x15);
                        }
                        break;

                    case 1029:
                        // 处理数据帧
                        t_sts = 2;

                        crc = calcrc(fwt_buf + 3, 1024);

                        fwt_cnt = 0;

                        {
                            if (fwt_buf[1] == to_fn)
                            {
                                to_fn++;

                                if ((fwt_buf[1] + fwt_buf[2]) == 0xff)
                                {
                                    if ((((unsigned short)crc) == (((int)fwt_buf[1027]) << 8) | (fwt_buf[1028])))
                                    {
                                        unsigned int nw = 0;

                                        // 写入数据到文件
                                        f_write(&file, fwt_buf + 3, 1024, &nw);

                                        UART_Send_Byte(0x06); // 发送ACK
                                    }
                                    else UART_Send_Byte(0x15); // CRC错误
                                }
                                else UART_Send_Byte(0x15); // 序号校验错误
                            }
                            else UART_Send_Byte(0x15); // 序号错误
                        }
                        break;

                    default:
                        fwt_cnt = 0;
                        // 未知帧类型
                        // if(t_sts==0 || t_sts==1) UART_Send_Byte(0X15);
                        break;
                }
            }
            else
            {
                // 没有收到数据,根据状态发送请求
                if (t_sts == 0 || t_sts == 3)
                {
                    fwt_cnt = 0;
                    UART_Send_Byte('C'); // 请求发送
                }

                if (t_sts == 1 || t_sts == 2)
                {
                    fwt_cnt = 0;
                    UART_Send_Byte(0X15); // 发送NAK
                }

                // 超时处理
                if (ot++ > 20) t_cmp = ERR_YMODEM_OVERTIME;
            }
        }

        // 文件同步与关闭
        f_sync(&file);
        f_close(&file);

        fw_transfering = 0;
    }

    printf("\r\nFirmware Transfer Complete\r\n");

    return t_cmp;
}
相关推荐
贝塔实验室10 分钟前
Altium Designer 6.3 PCB LAYOUT教程(四)
驱动开发·嵌入式硬件·硬件架构·硬件工程·信息与通信·基带工程·pcb工艺
星辰pid1 小时前
stm32的gpio模式到底该怎么选择?(及iic,spi,定时器原理介绍)
stm32·单片机·嵌入式硬件
创业之路&下一个五年1 小时前
按照ip的转换为二进制的方式理解a\b\c类地址的边界
服务器·网络·tcp/ip
陌路201 小时前
Linux29初识网络:核心概念与分层逻辑
网络
brave and determined2 小时前
可编程逻辑器件学习(day3):FPGA设计方法、开发流程与基于FPGA的SOC设计详解
嵌入式硬件·fpga开发·soc·仿真·电路·时序·可编程逻辑器件
axuan126512 小时前
10.【NXP 号令者RT1052】开发——实战-RT 看门狗(RTWDOG)
单片机·嵌入式硬件·mcu
-大头.2 小时前
Rust高级类型与零成本抽象实战
stm32·单片机·rust
Acrelhuang2 小时前
覆盖全场景需求:Acrel-1000 变电站综合自动化系统的技术亮点与应用
大数据·网络·人工智能·笔记·物联网
阿猿收手吧!2 小时前
【Linux网络】shutdown()与close()的区别
linux·网络
AuroraDPY5 小时前
计算机网络:基于TCP协议的自定义协议实现网络计算器功能
网络·tcp/ip·计算机网络