STM32基于YModem协议串口升级程序的实现

本文详细介绍了YModem协议在STM32串口升级中的工作原理、帧格式、数据包处理以及实际应用示例。通过实例演示了如何使用YModem进行文件传输,包括起始帧、数据帧和结束帧的构造,以及握手信号和错误校验的处理。

   YModem协议是由XModem协议演变而来的,每包数据可以达到1024字节,是一个非常高效的文件传输协议。

Ymodem是一种错误纠正协议。使用较大数据块的调制解调采用这种协议,以获得更高的工作效率。采用Ymodem协议的调制解调器以1024字节数的块发送数据。成功接收的不会被确认。有错误的块被确认(NAK),并重发。Ymodem类似于Xmodem-1K,不同之处是提供批处理模式(batch mode)。在批处理模式下,可以使用一个命令发送一些文件。Ymodem使用循环冗余码校验作为错误校验方式。


1.1  YMODEM 帧格式
YMODEM有两种帧格式,主要区别是信息块长度不一样。


1.1.1  帧头
帧头表示两种数据帧长度,主要是信息块长度不同。 当帧头为SOH(0x01)时,信息块为128字节; 当帧头为STX(0x02)时,信息块为1024字节。

1.1.2 包序号
数据包序号只有1字节,因此计算范围是0~255;对于数据包大于255的,序号归零重复计算。

1.1.3  帧长度
以SOH(0x01)开始的数据包,信息块是128字节,该类型帧总长度为133字节; 以STX(0x02)开始的数据包,信息块是1024字节,该类型帧总长度为1029字节。

1.1.4  校验
YMODEM采用的是CRC16校验算法,校验值为2字节,传输时CRC高八位在前,低八位在后;CRC计算数据为信息块数据,不包含帧头、包号、包号反码。

1.2  YMODEM起始帧
YMODEM起始帧并不直接传输文件内容,而是先将文件名和文件大小置于数据帧中传输;起始帧是以SOH 133字节长度帧传输,格式如下:


其中包号为固定为0; filename为文件名称,文件名称后必须加0x00作为结束; filesize为文件大小值,文件大小值后必须加0x00作为结束; 余下未满128字节数据区域,则以0x00填充。 可以看出起始帧也是遵守1.1中YMODEM包格式的。

1.3  YMODEM数据帧
YMODEM数据帧传输,在信息块填充有效数据。 传输有效数据时主要考虑的是最后一包数据的是处理,SOH帧和STX帧有不同的处理。 (1)对于SOH帧,若余下数据小于128字节,则以0x1A填充,该帧长度仍为133字节。 (2)对于STX帧需考虑几种情况:

1)余下数据等于1024字节,以1029长度帧发送;

2)余下数据小于1024字节,但大于128字节,以1029字节帧长度发送,无效数据以0x1A填充;

3)余下数据等于128字节,以133字节帧长度发送;

4)余下数据小于128字节,以133字节帧长度发送,无效数据以0x1A填充。

1.4  YMODEM结束帧
YMODEM的结束帧采用SOH 133字节长度帧传输,该帧不携带数据(空包),即数据区、校验都以0x00填充。


1.5  YMODEM握手信号
握手信号由接收方发起,在发送方开始传输文件前,接收方需发送YMODEM_C (字符C,ASII码为0x43)命令,发送方收到后,开始传输起始帧。

1.6  YMODEM命令

1.7 官方一个YMODEM传输过程
 


1)其中“sb foo.*<CR>”指的是Linux中的sb命令,可在Linux终端执行 sb --help查看使用方法

2)上图中YMODEM协议传输的数据块是128字节,其实YMODEM协议还支持1024字节的数据块或者128节和1024字节混合模式。详情请查阅YMODEM协议手册。

3)SOH 表示本数据块大小为128字节

4)STX 表示本数据块大小为1024字节

5)本文中EOT指令仅有一次会话,即上图中的最后一次

1.7.1  起始帧的数据格式
YMODEM的起始帧的数据块大小为128字节,传输的是文件名、文件大小、文件修改日期等信息,其中文件名和文件大小信息是必须的。128字节的剩余部分用空字符填充(也就是0)。

SOH 00 FF foo.c 3232 NUL[118] CRCH CRCL
1)SOH:表示本帧数据块大小为128字节

2)00: 表示数据帧序号,初始是0,依次向下递增,FF是帧序号的取反

3)foo.c:是要传输的文件名,是ASCII字符串(以空字符结尾)

4)3232:表示文件的大小,是ASCII字符串(以空字符结尾)

5)NUL[118]:剩余部分用空字符填充

6)CRCH/L: 表示16位CRC校验码的高8位与低8位

1.7.2  数据帧的数据格式
YMODEM的数据帧的数据块大小可以是128字节或者1024字节。

// 128字节的数据块 

SOH 01 FE data[128] CRCH CRCL
// 1024字节的数据块 

STX 01 FE data[1024] CRCH CRCL
一般会使用1024字节的数据块进行传输,这样可以加快传输速度,如果最后文件数据不足1024字节,则将其拆分为128字节的数据块进行传输,如果拆分后有不足128字节的数据依然按照128字节的数据块进行传输,但是剩余空间全部用0x1A填充,以表示文件结束。

1.7.3 结束帧数据结构
当文件传输结束时,除了发送EOT传输结束指令外,还需要发送一个结束帧。YMODEM的结束帧与起始帧的数据格式相同,数据块大小为128字节,但是结束帧的数据块要全用空字符填充。

SOH 3A C5 NUL[128] CRCH CRCL
 流程

1.     第一步先由接收方,发送一个字符'C'

2.     发送方收到'C'后,发送第一帧数据包,内容如下:

SOH 00 FF Foo.c  NUL[118] CRCH CRCL
3.     接收方收到第一帧数据包后,发送ACK正确应答,然后再发送一个字符'C'

4.     发送方收到'C'后,开始发送第二帧,第二帧中的数据存放的是第一包数据

5.     接收方收到数据后,发送一个ACK然后等待下一包数据传送完毕,继续ACK应答。直到所有数据传输完毕

6.     数据传输完毕后,发送方发EOT,第一次接收方以NAK应答,进行二次确认

7.     发送方收到NAK后,重发EOT,接收方第二次收到结束符,就以ACK应答

8.     最后接收方再发送一个'C',发送方在没有第二个文件要传输的情况下,发送如下数据

SOH 3A C5 NUL[128] CRCH CRCL
接收方应答ACK后,正式结束数据传输

STM32 串口升级程序下载:

基于Ymodem协议串口升级程序的实现过程-C文档类资源-CSDN下载

SENDER:发送方。(文件名:fileName.bin)
RECEIVER:接收方。
具体握手的步骤如下:
1、接收方发送一个字符‘C’,也就是十六进制‘43’。代表接收方已经处于接收数据的状态。
2、发送方接收到‘C’之后,发送头帧数据包,内容如下:
SOH 00 FF fileName.bin NULL[116] CRC CRC
数据包内容解释:
 2-1 SOH(第1字节):表示本数据区大小有128字节。(STX表示本数据包数据区大小1024字节)。
 2-2 00 (第2字节):数据块编号。 第一包为00,第二包为01,此后依次累加。FF后,继续从00循环。
 2-3 FF (第3字节):数据块编号的反码。 编号00—FF,01—FE,此后依次类推。
 2-4 fileName.bin NULL[116]:数据区。128字节。 fileName.bin是文件名,超级终端下,在文件名后面 
 还有文件大小。 数据区不足128字节的,用0x00补齐。
 2-5 CRC校验(最后2个字节):16位CRC校验,高位字节在前,地位字节在后。(注意:只有数据区参与了 
 CRC校验,不包含头、编码、编码反码)。
3、接收方收到数据包后,发送ACK正确应答,然后发送一个字符‘C’。
4、发送方收到‘C’后,开始发送第二帧数据。第二帧数据存放的是第一包数据。
5、接收方收好数据包后,发送ACK正确应答,然后等待下一包数据传送完毕,继续ACK应答。(循环)
6、数据传输完毕后,发送方第一次发EOT,第一次接收方以NAK应答,进行二次确认。
7、发送方收到NAK后,第二次发EOT。接收方第二次收到结束符,依次以ACK和C做应答。
8、发送方收到ACK和C之后,发送结束符—>SOH 00 FF 00…00[128个00] CRCH CRCL。
9、接收方收到结束符之后,以ACK做应答,然后通信正式结束。
/* Includes ------------------------------------------------------------------*/
#include "flash_if.h"
#include "common.h"
#include "ymodem.h"
#include "string.h"
#include "main.h"
#include "menu.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define CRC16_F       /* activate the CRC16 integrity */
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* @note ATTENTION - please keep this variable 32bit alligned */
uint8_t aPacketData[PACKET_1K_SIZE + PACKET_DATA_INDEX + PACKET_TRAILER_SIZE];

/* Private function prototypes -----------------------------------------------*/
static void PrepareIntialPacket(uint8_t *p_data, const uint8_t *p_file_name, uint32_t length);
static void PreparePacket(uint8_t *p_source, uint8_t *p_packet, uint8_t pkt_nr, uint32_t size_blk);
static HAL_StatusTypeDef ReceivePacket(uint8_t *p_data, uint32_t *p_length, uint32_t timeout);
uint16_t UpdateCRC16(uint16_t crc_in, uint8_t byte);
uint16_t Cal_CRC16(const uint8_t* p_data, uint32_t size);
uint8_t CalcChecksum(const uint8_t *p_data, uint32_t size);

/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Receive a packet from sender
  * @param  data
  * @param  length
  *     0: end of transmission
  *     2: abort by sender
  *    >0: packet length
  * @param  timeout
  * @retval HAL_OK: normally return
  *         HAL_BUSY: abort by user
  */
static HAL_StatusTypeDef ReceivePacket(uint8_t *p_data, uint32_t *p_length, uint32_t timeout)
{
  uint32_t crc;
  uint32_t packet_size = 0;
  HAL_StatusTypeDef status;
  uint8_t char1;

  *p_length = 0;
  status = HAL_UART_Receive(&UartHandle, &char1, 1, timeout);

  if (status == HAL_OK)
  {
    switch (char1)
    {
      case SOH:
        packet_size = PACKET_SIZE;
        break;
      case STX:
        packet_size = PACKET_1K_SIZE;
        break;
    
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值