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;
}
相关推荐
Deitymoon3 小时前
STM32——蓝牙模块双串口控制led
stm32·单片机·嵌入式硬件
其实防守也摸鱼3 小时前
CTF密码学综合教学指南--第五章
开发语言·网络·笔记·python·安全·网络安全·密码学
S1998_1997111609•X5 小时前
论mysql国盾shell-sfa犯罪行为集团下的分项工程及反向注入原理尐深度纳米算法下的鐌檵鄐鉎行为
网络·数据库·网络协议·百度·开闭原则
AI精钢5 小时前
AI Agent 从上线到删库跑路始末
网络·人工智能·云原生·aigc
xiangw@GZ5 小时前
智能锁TouchKey的抗干扰设计【2】-软件算法
嵌入式硬件
iCxhust5 小时前
微机原理实践教程(C语言篇)---A001闪烁灯
c语言·开发语言·汇编·单片机·嵌入式硬件·51单片机·微机原理
一起搞IT吧5 小时前
相机Camera日志实例分析之二十:相机Camx【照片后置4800/5000/6400万拍照】单帧流程日志详解
android·嵌入式硬件·数码相机·智能手机
笨笨饿7 小时前
69_如何给自己手搓一个串口
linux·c语言·网络·单片机·嵌入式硬件·算法·个人开发
geneculture7 小时前
《智能通信速分多次传输技术(VDMT)》专利文件的全文汉英双语对照版本
服务器·网络·人工智能·融智学的重要应用·哲学与科学统一性·融智时代(杂志)·人机间性
xrui588 小时前
2026实战:深度解析 Gemini 3.1 镜像站函数调用在自动化运维工单中的应用
linux·服务器·网络