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

前言

这一部分主要是介绍一些背景,熟悉的读者可以直接跳到调试部分。

三种 MCU 烧写程序的方式

  1. ICP(In Circuit Programming):在电路编程,通过JTAG/SWD协议下载用户应用程序到 MCU 中。
  2. ISP(In System Programming):在系统编程,一般通用的做法是上位机通过将程序文件烧录到 MUC 内部的存储器(Flash)中。比如常使用的 STM32 单片机就是当 BOOT0 = 1, BOOT1 = 0 时让系统存储器从此处启动,ST 原厂内置的 Bootloader 程序就在这个区域存储,这个 Bootloader 中提供了串口下载程序的固件。
  3. IAP (In Application Programming):在应用编程,是指开发者将 Flash 分成若干区域在用户程序运行过程中对其进行更改,目的是为了在产品发布之后仍然可以方便的通过预留的通信端口对产品中的程序进行更新升级。在实现此功能时,一般将 MCU 的内部分为 boot 和 app 两块存储区,MCU 上电后从 boot 区开始运行,并判断是否满足对 app 区程序进行烧写的条件,若满足则烧写后再跳转到 app 区的程序进行执行。

MCU 实现 IAP 的通用程序(以 stm32 为例)

这一部分网上有很多帖子可以去学习,但通用的思路流程都是按照官方的代码写的,这里只记录一下关键的代码部分。
可以学习参考的帖子:
STM32 IAP 在线升级原理全解析(侧重流程描述)
以STM32为例简述 IAP升级(侧重代码解释)

stm32 启动流程

想做好 IAP 功能,必须先对 MCU 的启动过程有一个比较深入的理解,网上有很多资料讲解,这里不做赘述。

IAP 分区

在这里插入图片描述
在bootloader 区申请的动态内存不释放不会影响到应用程序:当从 bootloader 程序跳转到应用程序的起始地址后,应用程序会重新分配和初始化 RAM,所以 bootloader 程序在进入应用程序前不需要担心申请的动态内存没有释放会对进入应用程序后产生影响。
在bootloader层开启的外设会影响到应用层:在 bootloader 程序开启的外设进入应用程序后是不会自动复位的,因此在跳转到 app 区前一定要关闭 bootloader 中使用的这些资源,比如定时器中断、引脚中断等。

关键代码

跳转函数:

typedef  void (*iapfun)(void);				//定义一个函数类型的参数.     用于将PC指针指向新的APP程序的起始地址
typedef unsigned  int uint32_t;
#define     __IO    volatile 
iapfun jump2app; 

void iap_load_app(u32 appxaddr)
{
	if(((*(__IO uint32_t*)appxaddr)&0x2FFE0000)==0x20000000)	//检查栈顶地址是否合法.(注:正常来说对应4K的RAM地址范围为 0x2000 0000 -- 0x2000 0FFF,所以检索相与值为0x2FFF EFFF)
	{ 
		RCC_DeInit(); // 复位时钟
		__disable_irq();	//禁止中断
		Delayms(5);
		jump2app=(iapfun)*(__IO uint32_t*)(appxaddr+4);		//用户代码区第二个字为程序开始地址(复位地址)		
		__set_MSP(*(__IO uint32_t*)appxaddr);					//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
		jump2app();									//跳转到APP.
	}
}

这里最主要的就是刚开始对栈顶地址的判断以及对函数指针的理解。
但是这里判断相与的条件应该根据具体所使用 MCU 的 RAM 大小去写,不如写成if ((appxaddr > RAM_BASE) && (appxaddr <= (RAM_BASE + RAM_SIZE)))这样的形式,参数需要根据自己使用的 MCU 的实际 RAM 大小和起始地址去填写。

Ymodem 协议

当编写后程序后,我们需要先将 bootloader 的代码烧录到 MCU 中,然后在运行 bootloader 代码的时候判断是否要进行 IAP 升级,如果需要的话就需要将 application 的代码也发送给 MCU,并由 bootloader 的代码接收它并将它写入到正确的位置然后跳转执行。
我们经常使用的串口传输虽然有奇偶校验来检查错误,但对于通信来讲,上位机向 MCU 串口传输数据,当串口收到错误数据的时候也只能抛弃,上位机根本不知道 MCU 的情况,因此 IAP 升级不会采用串口直接传输数据的方式,而是通过一种发送应答机制来完成 app.bin 文件的传输。常用的协议有XModem、YModem、Zmodem,并且常用的终端软件如 Xshell 中也集成了这类协议的传输方式,免去了我们自己编写上位机程序的麻烦。

通信信号

符号数值含义
SOH0x01128字节数据包
STX0x021024字节数据包
EOT0x04结束传输
ACK0x06正确接收回应
NAK0x15错误接收回应
CAN0x18传输终止
C0x43请求数据

通信流程

在这里插入图片描述

  1. 首先由接收方发送C(0x43)表示请求建立连接。
  2. 发送方收到后会发送第一帧数据,第一位是帧头,SOH表示数据长度为128字节;第二位是帧序(0-255超过255再从0开始循环);第三位是帧序的取反值;之后是要传输的文件姓名和文件大小的信息,文件名结束字符为’\0’,文件大小结束字符为’ '(空格);再往后是填充的空字节和最后两字节的 CRC 校验码。
  3. 接收方收到第一帧数据化发送 ACK 代表收到,并发送一个 C 请求文件的内容数据。
  4. 发送方开始发送数据帧,接收方收到后发送 ACK 继续请求下一帧数据。
  5. 结束时,最后一帧数据不够会由 0x1A 填充,接收方接收到最后一帧数据后回复 ACK ,然后发送方发送 EOT 表示传输结束。
  6. 接收方在收到第一个 EOT 后会发送 NAK 请求重传,发送方再次发送 EOT,然后接收方回复 ACK 表示收到,并发送 C 请求结束帧。
  7. 接收方发送结束帧,其数据部分全为0,接收方收到后回复 ACK。
    在实际使用的过程中,并不是严格遵循上述的流程,比如有的程序在第一个 EOT 后没有发送 NAK 直接回复了 ACK。

数据帧格式

  1. 起始帧
帧头包号包号反码文件名称文件大小填充区校验高位校验低位
SOH0x000xfffilename + 0x00file size + 0x00NULL(0x00)CRC-HCRC-L

起始帧并不直接传输文件内容,以 SOH 133字节长度传输文件名和大小,包号固定位0。
2. 数据帧

帧头包号包号反码有效数据填充区校验高位校验低位
SOH/STXPNXPNDATA0x1ACRC-HCRC-L

SOH 和 STX是根据数据包的长度来进行区分的。
对于 SOH 帧,若余下数据小于128字节,则以0x1A 填充,该帧长度仍为133字节。
对于 STX 帧:

  • 余下数据等于1024字节,以1029字节帧长度发送;
  • 余下数据小于1024字节,但大于128字节,以1029字节帧长度发送,无效数据以0x1A填充;
  • 余下数据等于128字节,以133字节帧长度发送;
  • 余下数据小于128字节,以133字节帧长度发送,无效数据以0x1A填充。
  1. 结束帧
帧头包号包号反码数据区校验高位校验低位
SOH0x000xffNULL(0x00)CRC-HCRC-L

结束帧采用 SOH 133字节长度传输,该帧不携带数据(空包)。

用Xshell 进行Ymodem协议传输数据

说了这么久,终于到正文了,归结到底起始就是一句话:
如果使用 xshell 的 ymodem 协议传输数据时,明明看到数据已经传输完毕但没有显示传输完成,然后进度框卡住的话,需要接收方再发送一个字符’O’(大写的 O, 0x4F)。
也就是说,不管代码是怎么写的,只要能看到数据整体其实是已经传输结束的话,最后只需要再多发一个’O’就可以了。这应该是 xshell 自身的传输完毕判断的一个条件,网上这篇文章中有写到一句:
bootloader使用完善ymodem协议(优化完善stm的ymodem协议),以及xshell终端结束(超详细教程,简单好学,看了秒懂)
(:我一开始还以为是数字0,也不知道为什么最后要发送一个字母O)。

调试方法

安装软件:虚拟串口 + 串口调试助手(随便一个能用的就行)
虚拟串口安装可以参考文章

  1. 使用虚拟串口软件开一对虚拟串口,串口助手打开其中的一个,xshell 打开其中的另一个。
    在这里插入图片描述
  2. 准备一个文件,随便一个有点数据的文件就可以,右击 xshell 界面选择传输->ymodem->用ymodem发送。然后可以在串口助手看到传来的第一条消息 rb -E ,这应该也是 xshell 传输附加的信息。
    在这里插入图片描述
  3. 此时需要接送方发送第一个传输请求C(0x43),因此在串口助手输入0x43并点击发送。可以看到,这就是 ymodem 协议中的起始帧,取消勾选16进制显示后可以看到传输的文件名和文件大小,此时 xshell 传输框也会显示传输的文件名和文件大小。
    在这里插入图片描述
  4. 收到起始帧后,接收方需要首先发送一个ACK(0x06),紧跟着发送一个C。但是可以看到的是,当发送完ACK后没有发送C时,发送方已经将下一个数据帧发送了过来。由于文件大小只有15个字节,因此使用了 SOH 发送,没有数据的地方用0x1A进行了填充。
  5. 接收到数据帧之后需要继续回复 ACK,此时可以看到收到了第一个 EOT(0x04),代表数据传输完成。
    在这里插入图片描述
  6. 接收方此时回复一个 NAK(0x15),代表数据重传请求。发送方收到之后将再次发送一个 EOT 进行确认。
    在这里插入图片描述
  7. 之后接收方将回复一个 ACK 代表确认,并发送一个 C 请求最后的结束帧。发送方收到 C 后将发送结束帧,此时其实数据已经传输完成,然而可以看到 xshell 中传输框并没有显示发送完成,将传输完成自动关闭的选项勾选上发现这个框也不会自动关闭。
    在这里插入图片描述
  8. 标准的 ymodem 协议此时接收方还需要回复一个 ACK,但是回复了之后 xshell 中传输仍然不会显示完成。必须回复一个 O(0x4F),才会让 xshell 认为ymodem 传输已经完成并且可以自动关闭对话框,此时串口助手将收到 xshell 发来的传输完成的信息。因此,在程序中最好先用串口接收数据的函数处理掉xshell最后传过来的这些数据(比如用一些延迟判断,100ms内串口没收到数据的话才让程序往下执行),不然跳转到app区的程序可能会遇到不可预计的错误。
    在这里插入图片描述

一点心得

  1. 虽然 ymodem 通信的标准协议写的很清楚,但网上的各种软件和代码并未完全遵循,因此在自己实现的时候只要确保通信双方能成功接收到数据即可,不必完全按照标准的通信流程去写。
  2. xshell 在传输完成后会通过串口返回一个提示数据,如果是使用 MCU 进行 IAP 编程的话,必须在代码中处理掉这个数据。不然当程序跳转到 app 区时可能会出现启动卡死的现象。
  3. 尤其是当 app 区的代码中有用串口输出字母大 O 时,会让 xshell 误认为是 ymodem 通信结束的信号。
  • 9
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值