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;
case EOT:
break;
case CA:
if ((HAL_UART_Receive(&UartHandle, &char1, 1, timeout) == HAL_OK) && (char1 == CA))
{
packet_size = 2;
}
else
{
status = HAL_ERROR;
}
break;
case ABORT1:
case ABORT2:
status = HAL_BUSY;
break;
default:
status = HAL_ERROR;
break;
}
*p_data = char1;
if (packet_size >= PACKET_SIZE )
{
status = HAL_UART_Receive(&UartHandle, &p_data[PACKET_NUMBER_INDEX], packet_size + PACKET_OVERHEAD_SIZE, timeout);
/* Simple packet sanity check */
if (status == HAL_OK )
{
if (p_data[PACKET_NUMBER_INDEX] != ((p_data[PACKET_CNUMBER_INDEX]) ^ NEGATIVE_BYTE))
{
packet_size = 0;
status = HAL_ERROR;
}
else
{
/* Check packet CRC */
crc = p_data[ packet_size + PACKET_DATA_INDEX ] << 8;
crc += p_data[ packet_size + PACKET_DATA_INDEX + 1 ];
if (HAL_CRC_Calculate(&CrcHandle, (uint32_t*)&p_data[PACKET_DATA_INDEX], packet_size) != crc )
{
packet_size = 0;
status = HAL_ERROR;
}
}
}
else
{
packet_size = 0;
}
}
}
*p_length = packet_size;
return status;
}
/**
* @brief Prepare the first block
* @param p_data: output buffer
* @param p_file_name: name of the file to be sent
* @param length: length of the file to be sent in bytes
* @retval None
*/
static void PrepareIntialPacket(uint8_t *p_data, const uint8_t *p_file_name, uint32_t length)
{
uint32_t i, j = 0;
uint8_t astring[10];
/* first 3 bytes are constant */
p_data[PACKET_START_INDEX] = SOH;
p_data[PACKET_NUMBER_INDEX] = 0x00;
p_data[PACKET_CNUMBER_INDEX] = 0xff;
/* Filename written */
for (i = 0; (p_file_name[i] != '\0') && (i < FILE_NAME_LENGTH); i++)
{
p_data[i + PACKET_DATA_INDEX] = p_file_name[i];
}
p_data[i + PACKET_DATA_INDEX] = 0x00;
/* file size written */
Int2Str (astring, length);
i = i + PACKET_DATA_INDEX + 1;
while (astring[j] != '\0')
{
p_data[i++] = astring[j++];
}
/* padding with zeros */
for (j = i; j < PACKET_SIZE + PACKET_DATA_INDEX; j++)
{
p_data[j] = 0;
}
}
/**
* @brief Prepare the data packet
* @param p_source: pointer to the data to be sent
* @param p_packet: pointer to the output buffer
* @param pkt_nr: number of the packet
* @param size_blk: length of the block to be sent in bytes
* @retval None
*/
static void PreparePacket(uint8_t *p_source, uint8_t *p_packet, uint8_t pkt_nr, uint32_t size_blk)
{
uint8_t *p_record;
uint32_t i, size, packet_size;
/* Make first three packet */
packet_size = size_blk >= PACKET_1K_SIZE ? PACKET_1K_SIZE : PACKET_SIZE;
size = size_blk < packet_size ? size_blk : packet_size;
if (packet_size == PACKET_1K_SIZE)
{
p_packet[PACKET_START_INDEX] = STX;
}
else
{
p_packet[PACKET_START_INDEX] = SOH;
}
p_packet[PACKET_NUMBER_INDEX] = pkt_nr;
p_packet[PACKET_CNUMBER_INDEX] = (~pkt_nr);
p_record = p_source;
/* Filename packet has valid data */
for (i = PACKET_DATA_INDEX; i < size + PACKET_DATA_INDEX;i++)
{
p_packet[i] = *p_record++;
}
if ( size <= packet_size)
{
for (i = size + PACKET_DATA_INDEX; i < packet_size + PACKET_DATA_INDEX; i++)
{
p_packet[i] = 0x1A; /* EOF (0x1A) or 0x00 */
}
}
}
/**
* @brief Update CRC16 for input byte
* @param crc_in input value
* @param input byte
* @retval None
*/
uint16_t UpdateCRC16(uint16_t crc_in, uint8_t byte)
{
uint32_t crc = crc_in;
uint32_t in = byte | 0x100;
do
{
crc <<= 1;
in <<= 1;
if(in & 0x100)
++crc;
if(crc & 0x10000)
crc ^= 0x1021;
}
while(!(in & 0x10000));
return crc & 0xffffu;
}
/**
* @brief Cal CRC16 for YModem Packet
* @param data
* @param length
* @retval None
*/
uint16_t Cal_CRC16(const uint8_t* p_data, uint32_t size)
{
uint32_t crc = 0;
const uint8_t* dataEnd = p_data+size;
while(p_data < dataEnd)
crc = UpdateCRC16(crc, *p_data++);
crc = UpdateCRC16(crc, 0);
crc = UpdateCRC16(crc, 0);
return crc&0xffffu;
}
/**
* @brief Calculate Check sum for YModem Packet
* @param p_data Pointer to input data
* @param size length of input data
* @retval uint8_t checksum value
*/
uint8_t CalcChecksum(const uint8_t *p_data, uint32_t size)
{
uint32_t sum = 0;
const uint8_t *p_data_end = p_data + size;
while (p_data < p_data_end )
{
sum += *p_data++;
}
return (sum & 0xffu);
}
/* Public functions ---------------------------------------------------------*/
/**
* @brief Receive a file using the ymodem protocol with CRC16.
* @param p_size The size of the file.
* @retval COM_StatusTypeDef result of reception/programming
*/
COM_StatusTypeDef Ymodem_Receive ( uint32_t *p_size )
{
uint32_t i, packet_length, session_done = 0, file_done, errors = 0, session_begin = 0;
uint32_t flashdestination, ramsource, filesize;
uint8_t *file_ptr;
uint8_t file_size[FILE_SIZE_LENGTH], tmp, packets_received;
COM_StatusTypeDef result = COM_OK;
/* Initialize flashdestination variable */
flashdestination = APPLICATION_ADDRESS;
while ((session_done == 0) && (result == COM_OK))
{
packets_received = 0;
file_done = 0;
while ((file_done == 0) && (result == COM_OK))
{
switch (ReceivePacket(aPacketData, &packet_length, DOWNLOAD_TIMEOUT))
{
case HAL_OK:
errors = 0;
switch (packet_length)
{
case 2:
/* Abort by sender */
Serial_PutByte(ACK);
result = COM_ABORT;
break;
case 0:
/* End of transmission */
Serial_PutByte(ACK);
file_done = 1;
break;
default:
/* Normal packet */
if (aPacketData[PACKET_NUMBER_INDEX] != packets_received)
{
Serial_PutByte(NAK);
}
else
{
if (packets_received == 0)
{
/* File name packet */
if (aPacketData[PACKET_DATA_INDEX] != 0)
{
/* File name extraction */
i = 0;
file_ptr = aPacketData + PACKET_DATA_INDEX;
while ( (*file_ptr != 0) && (i < FILE_NAME_LENGTH))
{
aFileName[i++] = *file_ptr++;
}
/* File size extraction */
aFileName[i++] = '\0';
i = 0;
file_ptr ++;
while ( (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH))
{
file_size[i++] = *file_ptr++;
}
file_size[i++] = '\0';
Str2Int(file_size, &filesize);
/* Test the size of the image to be sent */
/* Image size is greater than Flash size */
if (*p_size > (USER_FLASH_SIZE + 1))
{
/* End session */
tmp = CA;
HAL_UART_Transmit(&UartHandle, &tmp, 1, NAK_TIMEOUT);
HAL_UART_Transmit(&UartHandle, &tmp, 1, NAK_TIMEOUT);
result = COM_LIMIT;
}
/* erase user application area */
FLASH_If_Erase(APPLICATION_ADDRESS);
*p_size = filesize;
Serial_PutByte(ACK);
Serial_PutByte(CRC16);
}
/* File header packet is empty, end session */
else
{
Serial_PutByte(ACK);
file_done = 1;
session_done = 1;
break;
}
}
else /* Data packet */
{
ramsource = (uint32_t) & aPacketData[PACKET_DATA_INDEX];
/* Write received data in Flash */
if (FLASH_If_Write(flashdestination, (uint32_t*) ramsource, packet_length/4) == FLASHIF_OK)
{
flashdestination += packet_length;
Serial_PutByte(ACK);
}
else /* An error occurred while writing to Flash memory */
{
/* End session */
Serial_PutByte(CA);
Serial_PutByte(CA);
result = COM_DATA;
}
}
packets_received ++;
session_begin = 1;
}
break;
}
break;
case HAL_BUSY: /* Abort actually */
Serial_PutByte(CA);
Serial_PutByte(CA);
result = COM_ABORT;
break;
default:
if (session_begin > 0)
{
errors ++;
}
if (errors > MAX_ERRORS)
{
/* Abort communication */
Serial_PutByte(CA);
Serial_PutByte(CA);
}
else
{
Serial_PutByte(CRC16); /* Ask for a packet */
}
break;
}
}
}
return result;
}
/**
* @brief Transmit a file using the ymodem protocol
* @param p_buf: Address of the first byte
* @param p_file_name: Name of the file sent
* @param file_size: Size of the transmission
* @retval COM_StatusTypeDef result of the communication
*/
COM_StatusTypeDef Ymodem_Transmit (uint8_t *p_buf, const uint8_t *p_file_name, uint32_t file_size)
{
uint32_t errors = 0, ack_recpt = 0, size = 0, pkt_size;
uint8_t *p_buf_int;
COM_StatusTypeDef result = COM_OK;
uint32_t blk_number = 1;
uint8_t a_rx_ctrl[2];
uint8_t i;
#ifdef CRC16_F
uint32_t temp_crc;
#else /* CRC16_F */
uint8_t temp_chksum;
#endif /* CRC16_F */
/* Prepare first block - header */
PrepareIntialPacket(aPacketData, p_file_name, file_size);
while (( !ack_recpt ) && ( result == COM_OK ))
{
/* Send Packet */
HAL_UART_Transmit(&UartHandle, &aPacketData[PACKET_START_INDEX], PACKET_SIZE + PACKET_HEADER_SIZE, NAK_TIMEOUT);
/* Send CRC or Check Sum based on CRC16_F */
#ifdef CRC16_F
temp_crc = HAL_CRC_Calculate(&CrcHandle, (uint32_t*)&aPacketData[PACKET_DATA_INDEX], PACKET_SIZE);
Serial_PutByte(temp_crc >> 8);
Serial_PutByte(temp_crc & 0xFF);
#else /* CRC16_F */
temp_chksum = CalcChecksum (&aPacketData[PACKET_DATA_INDEX], PACKET_SIZE);
Serial_PutByte(temp_chksum);
#endif /* CRC16_F */
/* Wait for Ack and 'C' */
if (HAL_UART_Receive(&UartHandle, &a_rx_ctrl[0], 1, NAK_TIMEOUT) == HAL_OK)
{
if (a_rx_ctrl[0] == ACK)
{
ack_recpt = 1;
}
else if (a_rx_ctrl[0] == CA)
{
if ((HAL_UART_Receive(&UartHandle, &a_rx_ctrl[0], 1, NAK_TIMEOUT) == HAL_OK) && (a_rx_ctrl[0] == CA))
{
HAL_Delay( 2 );
__HAL_UART_FLUSH_DRREGISTER(&UartHandle);
__HAL_UART_CLEAR_IT(&UartHandle, UART_CLEAR_OREF);
result = COM_ABORT;
}
}
}
else
{
errors++;
}
if (errors >= MAX_ERRORS)
{
result = COM_ERROR;
}
}
p_buf_int = p_buf;
size = file_size;
/* Here 1024 bytes length is used to send the packets */
while ((size) && (result == COM_OK ))
{
/* Prepare next packet */
PreparePacket(p_buf_int, aPacketData, blk_number, size);
ack_recpt = 0;
a_rx_ctrl[0] = 0;
errors = 0;
/* Resend packet if NAK for few times else end of communication */
while (( !ack_recpt ) && ( result == COM_OK ))
{
/* Send next packet */
if (size >= PACKET_1K_SIZE)
{
pkt_size = PACKET_1K_SIZE;
}
else
{
pkt_size = PACKET_SIZE;
}
HAL_UART_Transmit(&UartHandle, &aPacketData[PACKET_START_INDEX], pkt_size + PACKET_HEADER_SIZE, NAK_TIMEOUT);
/* Send CRC or Check Sum based on CRC16_F */
#ifdef CRC16_F
temp_crc = HAL_CRC_Calculate(&CrcHandle, (uint32_t*)&aPacketData[PACKET_DATA_INDEX], pkt_size);
Serial_PutByte(temp_crc >> 8);
Serial_PutByte(temp_crc & 0xFF);
#else /* CRC16_F */
temp_chksum = CalcChecksum (&aPacketData[PACKET_DATA_INDEX], pkt_size);
Serial_PutByte(temp_chksum);
#endif /* CRC16_F */
/* Wait for Ack */
if ((HAL_UART_Receive(&UartHandle, &a_rx_ctrl[0], 1, NAK_TIMEOUT) == HAL_OK) && (a_rx_ctrl[0] == ACK))
{
ack_recpt = 1;
if (size > pkt_size)
{
p_buf_int += pkt_size;
size -= pkt_size;
if (blk_number == (USER_FLASH_SIZE / PACKET_1K_SIZE))
{
result = COM_LIMIT; /* boundary error */
}
else
{
blk_number++;
}
}
else
{
p_buf_int += pkt_size;
size = 0;
}
}
else
{
errors++;
}
/* Resend packet if NAK for a count of 10 else end of communication */
if (errors >= MAX_ERRORS)
{
result = COM_ERROR;
}
}
}
/* Sending End Of Transmission char */
ack_recpt = 0;
a_rx_ctrl[0] = 0x00;
errors = 0;
while (( !ack_recpt ) && ( result == COM_OK ))
{
Serial_PutByte(EOT);
/* Wait for Ack */
if (HAL_UART_Receive(&UartHandle, &a_rx_ctrl[0], 1, NAK_TIMEOUT) == HAL_OK)
{
if (a_rx_ctrl[0] == ACK)
{
ack_recpt = 1;
}
else if (a_rx_ctrl[0] == CA)
{
if ((HAL_UART_Receive(&UartHandle, &a_rx_ctrl[0], 1, NAK_TIMEOUT) == HAL_OK) && (a_rx_ctrl[0] == CA))
{
HAL_Delay( 2 );
__HAL_UART_FLUSH_DRREGISTER(&UartHandle);
__HAL_UART_CLEAR_IT(&UartHandle, UART_CLEAR_OREF);
result = COM_ABORT;
}
}
}
else
{
errors++;
}
if (errors >= MAX_ERRORS)
{
result = COM_ERROR;
}
}
/* Empty packet sent - some terminal emulators need this to close session */
if ( result == COM_OK )
{
/* Preparing an empty packet */
aPacketData[PACKET_START_INDEX] = SOH;
aPacketData[PACKET_NUMBER_INDEX] = 0;
aPacketData[PACKET_CNUMBER_INDEX] = 0xFF;
for (i = PACKET_DATA_INDEX; i < (PACKET_SIZE + PACKET_DATA_INDEX); i++)
{
aPacketData [i] = 0x00;
}
/* Send Packet */
HAL_UART_Transmit(&UartHandle, &aPacketData[PACKET_START_INDEX], PACKET_SIZE + PACKET_HEADER_SIZE, NAK_TIMEOUT);
/* Send CRC or Check Sum based on CRC16_F */
#ifdef CRC16_F
temp_crc = HAL_CRC_Calculate(&CrcHandle, (uint32_t*)&aPacketData[PACKET_DATA_INDEX], PACKET_SIZE);
Serial_PutByte(temp_crc >> 8);
Serial_PutByte(temp_crc & 0xFF);
#else /* CRC16_F */
temp_chksum = CalcChecksum (&aPacketData[PACKET_DATA_INDEX], PACKET_SIZE);
Serial_PutByte(temp_chksum);
#endif /* CRC16_F */
/* Wait for Ack and 'C' */
if (HAL_UART_Receive(&UartHandle, &a_rx_ctrl[0], 1, NAK_TIMEOUT) == HAL_OK)
{
if (a_rx_ctrl[0] == CA)
{
HAL_Delay( 2 );
__HAL_UART_FLUSH_DRREGISTER(&UartHandle);
__HAL_UART_CLEAR_IT(&UartHandle, UART_CLEAR_OREF);
result = COM_ABORT;
}
}
}
return result; /* file transmitted successfully */
}
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __YMODEM_H_
#define __YMODEM_H_
/* Includes ------------------------------------------------------------------*/
/* Exported types ------------------------------------------------------------*/
/**
* @brief Comm status structures definition
*/
typedef enum
{
COM_OK = 0x00,
COM_ERROR = 0x01,
COM_ABORT = 0x02,
COM_TIMEOUT = 0x03,
COM_DATA = 0x04,
COM_LIMIT = 0x05
} COM_StatusTypeDef;
/**
* @}
*/
/* Exported constants --------------------------------------------------------*/
/* Packet structure defines */
#define PACKET_HEADER_SIZE ((uint32_t)3)
#define PACKET_DATA_INDEX ((uint32_t)4)
#define PACKET_START_INDEX ((uint32_t)1)
#define PACKET_NUMBER_INDEX ((uint32_t)2)
#define PACKET_CNUMBER_INDEX ((uint32_t)3)
#define PACKET_TRAILER_SIZE ((uint32_t)2)
#define PACKET_OVERHEAD_SIZE (PACKET_HEADER_SIZE + PACKET_TRAILER_SIZE - 1)
#define PACKET_SIZE ((uint32_t)128)
#define PACKET_1K_SIZE ((uint32_t)1024)
/* /-------- Packet in IAP memory ------------------------------------------\
* | 0 | 1 | 2 | 3 | 4 | ... | n+4 | n+5 | n+6 |
* |------------------------------------------------------------------------|
* | unused | start | number | !num | data[0] | ... | data[n] | crc0 | crc1 |
* \------------------------------------------------------------------------/
* the first byte is left unused for memory alignment reasons */
#define FILE_NAME_LENGTH ((uint32_t)64)
#define FILE_SIZE_LENGTH ((uint32_t)16)
#define SOH ((uint8_t)0x01) /* start of 128-byte data packet */
#define STX ((uint8_t)0x02) /* start of 1024-byte data packet */
#define EOT ((uint8_t)0x04) /* end of transmission */
#define ACK ((uint8_t)0x06) /* acknowledge */
#define NAK ((uint8_t)0x15) /* negative acknowledge */
#define CA ((uint32_t)0x18) /* two of these in succession aborts transfer */
#define CRC16 ((uint8_t)0x43) /* 'C' == 0x43, request 16-bit CRC */
#define NEGATIVE_BYTE ((uint8_t)0xFF)
#define ABORT1 ((uint8_t)0x41) /* 'A' == 0x41, abort by user */
#define ABORT2 ((uint8_t)0x61) /* 'a' == 0x61, abort by user */
#define NAK_TIMEOUT ((uint32_t)0x100000)
#define DOWNLOAD_TIMEOUT ((uint32_t)1000) /* One second retry delay */
#define MAX_ERRORS ((uint32_t)5)
/* Exported functions ------------------------------------------------------- */
COM_StatusTypeDef Ymodem_Receive(uint32_t *p_size);
COM_StatusTypeDef Ymodem_Transmit(uint8_t *p_buf, const uint8_t *p_file_name, uint32_t file_size);
#include "main.h"
#include "common.h"
#include "flash_if.h"
#include "menu.h"
#include "ymodem.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
pFunction JumpToApplication;
uint32_t JumpAddress;
uint32_t FlashProtection = 0;
uint8_t aFileName[FILE_NAME_LENGTH];
/* Private function prototypes -----------------------------------------------*/
void SerialDownload(void);
void SerialUpload(void);
/* Private functions ---------------------------------------------------------*/
/**
* @brief Download a file via serial port
* @param None
* @retval None
*/
void SerialDownload(void)
{
uint8_t number[11] = {0};
uint32_t size = 0;
COM_StatusTypeDef result;
Serial_PutString("Waiting for the file to be sent ... (press 'a' to abort)\n\r");
result = Ymodem_Receive( &size );
if (result == COM_OK)
{
Serial_PutString("\n\n\r Programming Completed Successfully!\n\r--------------------------------\r\n Name: ");
Serial_PutString(aFileName);
Int2Str(number, size);
Serial_PutString("\n\r Size: ");
Serial_PutString(number);
Serial_PutString(" Bytes\r\n");
Serial_PutString("-------------------\n");
}
else if (result == COM_LIMIT)
{
Serial_PutString("\n\n\rThe image size is higher than the allowed space memory!\n\r");
}
else if (result == COM_DATA)
{
Serial_PutString("\n\n\rVerification failed!\n\r");
}
else if (result == COM_ABORT)
{
Serial_PutString("\r\n\nAborted by user.\n\r");
}
else
{
Serial_PutString("\n\rFailed to receive the file!\n\r");
}
}
/**
* @brief Upload a file via serial port.
* @param None
* @retval None
*/
void SerialUpload(void)
{
uint8_t status = 0;
Serial_PutString("\n\n\rSelect Receive File\n\r");
HAL_UART_Receive(&UartHandle, &status, 1, RX_TIMEOUT);
if ( status == CRC16)
{
/* Transmit the flash image through ymodem protocol */
status = Ymodem_Transmit((uint8_t*)APPLICATION_ADDRESS, (const uint8_t*)"UploadedFlashImage.bin", USER_FLASH_SIZE);
if (status != 0)
{
Serial_PutString("\n\rError Occurred while Transmitting File\n\r");
}
else
{
Serial_PutString("\n\rFile uploaded successfully \n\r");
}
}
}
/**
* @brief Display the Main Menu on HyperTerminal
* @param None
* @retval None
*/
void Main_Menu(void)
{
uint8_t key = 0;
Serial_PutString("\r\n======================================================================");
Serial_PutString("\r\n= (C) COPYRIGHT 2015 STMicroelectronics =");
Serial_PutString("\r\n= =");
Serial_PutString("\r\n= STM32L0xx In-Application Programming Application (Version 1.0.0) =");
Serial_PutString("\r\n= =");
Serial_PutString("\r\n= By MCD Application Team =");
Serial_PutString("\r\n======================================================================");
Serial_PutString("\r\n\r\n");
/* Test if any sector of Flash memory where user application will be loaded is write protected */
FlashProtection = FLASH_If_GetWriteProtectionStatus();
while (1)
{
Serial_PutString("\r\n=================== Main Menu ============================\r\n\n");
Serial_PutString(" Download image to the internal Flash ----------------- 1\r\n\n");
Serial_PutString(" Upload image from the internal Flash ----------------- 2\r\n\n");
Serial_PutString(" Execute the loaded application ----------------------- 3\r\n\n");
if(FlashProtection != FLASHIF_PROTECTION_NONE)
{
Serial_PutString(" Disable the write protection ------------------------- 4\r\n\n");
}
else
{
Serial_PutString(" Enable the write protection -------------------------- 4\r\n\n");
}
Serial_PutString("==========================================================\r\n\n");
/* Clean the input path */
__HAL_UART_FLUSH_DRREGISTER(&UartHandle);
__HAL_UART_CLEAR_IT(&UartHandle, UART_CLEAR_OREF);
/* Receive key */
HAL_UART_Receive(&UartHandle, &key, 1, RX_TIMEOUT);
switch (key)
{
case '1' :
/* Download user application in the Flash */
SerialDownload();
break;
case '2' :
/* Upload user application from the Flash */
SerialUpload();
break;
case '3' :
Serial_PutString("Start program execution......\r\n\n");
/* execute the new program */
JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);
/* Jump to user application */
JumpToApplication = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
JumpToApplication();
break;
case '4' :
if (FlashProtection != FLASHIF_PROTECTION_NONE)
{
/* Disable the write protection */
if (FLASH_If_WriteProtectionConfig(FLASHIF_WRP_DISABLE) == FLASHIF_OK)
{
Serial_PutString("Write Protection disabled...\r\n");
Serial_PutString("System will now restart...\r\n");
/* Launch the option byte loading */
HAL_FLASH_OB_Launch();
}
else
{
Serial_PutString("Error: Flash write un-protection failed...\r\n");
}
}
else
{
if (FLASH_If_WriteProtectionConfig(FLASHIF_WRP_ENABLE) == FLASHIF_OK)
{
Serial_PutString("Write Protection enabled...\r\n");
Serial_PutString("System will now restart...\r\n");
/* Launch the option byte loading */
HAL_FLASH_OB_Launch();
}
else
{
Serial_PutString("Error: Flash write protection failed...\r\n");
}
}
break;
default:
Serial_PutString("Invalid Number ! ==> The number should be either 1, 2, 3 or 4\r");
break;
}
}
}
基于Ymodem协议串口升级程序的实现过程-C文档类资源-CSDN下载
网盘下载:
-
链接:https://pan.baidu.com/s/1eu5yzMwSNFWisqr-T7aJPw
-
提取码:w30a
CRC xmodem计算:
https://github.com/caoliuchao/STM32F103-UartIAP/blob/master/IAP/Src/ymodem.c
STM32F103-UartIAP/ymodem.c at master · caoliuchao/STM32F103-UartIAP · GitHub
stm32f4_SerialPort_bootloader/ymodem.c at master · RdMaxes/stm32f4_SerialPort_bootloader · GitHub
https://github.com/Gooost/STM32-IAP/blob/master/User/ymodem.c
【YModem】YModem串口IAP升级例程+YModem串口工具_lunei的博客-CSDN博客_ymodem工具
STM32 CUbeIDE 使用Ymodem协议进行串口IAP升级_跳墙网https://www.tqwba.com/x_d/jishu/235595.html
【开源】串口YMODEM实现IAP程序升级(附工程源码)_freemote的博客-CSDN博客_ymodem源码
以上两段代码为YModem协议接收数据及处理数据的过程,在此之前我们需先初始化系统以及结构,同时需要注意以下几点:
1、Bootloader中尽可能不使用中断,因此此处串口接收数据采用查询接收方式;
2、Bootloader中不要让程序卡死或者进入某个死循环,应在适当的地方进行软件复位;
3、在Keil环境下涉及内存拷贝时,尽量不用memcpy();
4、注意数组长度越界或者溢出错误;
5、注意YModem协议第一包数据的包号为00。
switch (packet_length) | |
{ | |
/* Abort by sender */ | |
case - 1: | |
Send_Byte(ACK); | |
return 0; | |
/* End of transmission */ | |
case 0: | |
if(flag_EOT==0) //first EOT | |
{ | |
Send_Byte(NACK); | |
flag_EOT = 1; | |
} | |
else if (flag_EOT==1) //second EOT | |
{ | |
Send_Byte(ACK); | |
Send_Byte('C'); | |
file_done = 1; | |
} | |
break; |