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;
}
相关推荐
果丁智能5 小时前
智能锁赋能网约房民宿数字化管控:身份核验+远程授权,筑牢安全防线、降本增效
网络·数据库·人工智能·安全·智能家居
wp123_16 小时前
射频前端无源器件观察:Coilcraft WBC1-1TLC vs TONEVEE WBT1-1CT 国产与进口巴伦变压器的技术博弈
网络
Mr..Jackey6 小时前
瑞佑 RUI Builder 图形化 UI 设计工具
arm开发·人工智能·单片机·ui·人机交互·ra8889·lcd控制芯片
映翰通朱工7 小时前
工业4G网关无公网IP远程运维实战(内网终端异地访问方案)
运维·服务器·网络·安全·智能路由器
天南散修7 小时前
MT7916 BA流程
网络·驱动开发·wifi·802.11
Yang96117 小时前
多功能一体化,成都鼎讯 LDMN-JM1 满足石油煤矿设备检定与训练需求
网络·能源
IP老炮不瞎唠8 小时前
Python 价格监控如何实现?思路与实用方法分享
运维·服务器·网络
睡不醒男孩0308239 小时前
CLup 6.x 版本中针对StarRocks 存算一体集群的完整操作手册
java·服务器·网络·clup
dog2509 小时前
网络长尾延时的重尾本质
开发语言·网络·php
戴西软件9 小时前
戴西 DLM 许可授权管理系统:破解无网络环境下工业软件授权难题,助力制造企业降本增效
网络·人工智能·python·深度学习·程序人生·算法·制造