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;
}
相关推荐
XINVRY-FPGA6 小时前
XC7A100T-2FGG484I Xilinx Artix-7 FPGA
arm开发·嵌入式硬件·fpga开发·硬件工程·信息与通信·信号处理·fpga
sky北城6 小时前
读书笔记整理--网络学习与概念整合
网络·智能路由器
极客范儿6 小时前
新华三H3CNE网络工程师认证—OSPF多区域概念与配置
网络·智能路由器
lingzhilab6 小时前
零知IDE——基于STM32F407VET6和ESP-01的SHT2X温湿度监测与云传输系统
stm32·单片机·嵌入式硬件
望获linux6 小时前
【实时Linux实战系列】FPGA 与实时 Linux 的协同设计
大数据·linux·服务器·网络·数据库·fpga开发·操作系统
国科安芯7 小时前
高辐射环境下AS32S601ZIT2型MCU的抗辐照性能与应用潜力分析
网络·人工智能·单片机·嵌入式硬件·fpga开发
wang09077 小时前
网络协议之DNS
网络·网络协议
wanhengidc7 小时前
云手机的魅力与优势
网络·游戏·智能手机·架构·云计算
Rousson10 小时前
硬件学习笔记--82 连接器的选用原则与流程
笔记·单片机·学习