前言:
Xmodem、Ymodem和Zmodem协议是最常用的三种通信协议。
Xmodem协议是最早的,传输128字节信息块。
Ymodem是Xmodem的改进版协议,具有传输快速稳定的优点。它可以一次传输1024字节的信息块,同时还支持传输多个文件。
平常所说的Ymodem协议是指的Ymodem-1K,除此还有Ymodem-g(没有CRC校验,不常用)。
YModem-1K用1024字节信息块传输取代标准的128字节传输,数据的发送回使用CRC校验,保证数据传输的正确性。它每传输一个信息块数据时,就会等待接收端回应ACK信号,接收到回应后,才会继续传输下一个信息块,保证数据已经全部接收。
1.起始帧的数据格式
YModem的起始帧并不直接传输文件的数据,而是将文件名与文件的大小放在数据帧中传输,它的帧长=3字节数据首部+128字节数据+2字节CRC16校验码=133字节。它的数据结构如下:
SOH 00 FF filename filezise NUL CRCH CRCL
其中SOH=0x01,表示这个数据帧中包含着128个字节的数据(STX表示1024字节,初始帧只有128个),00表示数据帧序号,初始是0,依次向下排,FF是帧序号的取反,filename是要传输的文件名,如USTB_V3_1.0.1.26_NMEA.Bin,它在数据帧中的格式为:55 53 54 42 5F 56 33 5F 31 2E 30 2E 31 2E 32 36 5F 4E 4D 45 41 2E 42 69 6E 00,也就是把ASCII码转成十六进制,但是最后一定要在文件名后加上00,表示文件名的结束;filesize表示文件的大小,如上面的USTB_V3_1.0.1.26_NMEA.Bin大小是132KB,也就是135168Byte,转换成十六进制就是0x21000,它在数据帧中的格式就是32 31 30 30 30 00,也就是ASCII的“21000”,同样最后要加上00表示结束,NUL就是数据部分的128字节中除去文件名和文件大小占据的剩下的字节都用00填充,CRCH和CRCL分别表示16位CRC校验码的高8位与低8位。
2.数据帧的数据格式
YModem的数据帧中会预留1024字节空间用来传输文件数据,它跟起始帧接收差不多,如下:
STX 01 FEdata[1024] CRCH CRCL
其中STX=0x02,表示这帧数据帧后面包含着1024字节的数据部分;01是表示帧序号,FE是它的取反,再下一帧数据就是02 FD,以此类推;data[1024]表示存放着1024字节的文件数据;CRCH与CRCL是CRC16检验码的高8位与低8位。
如果文件数据的最后剩余的数据在128~1024之前,则还是使用STX的1024字节传输,但是剩余空间全部用0x1A填充,如下结构:
STX 01 FE data[1024] 1A 1A……… CRCH CRCL
有一种特殊的情况:如果文件大小小于等于128字节或者文件数据最后剩余的数据小于128字节,则YModem会选择SOH数据帧用128字节来传输数据,如果数据不满128字节,剩余的数据用0x1A填充这是数据帧的结构就变成了:
文件大小小于128字节: SOH 01 FE data[ ] 1A ...1A CRCH CRCL
文件最后剩余数据小于128字节: SOH 01 FE data[ ] 1A...1A CRCH CRCL
3.结束帧数据结构
YModem的结束帧数据也采用SOH的128字节数据帧,它的结构如下:
SOH 00 FF NUL[128] CRCH CRCL
结束帧同样以SOH开头,表示后面跟着128字节大小的数据;结束帧的帧序也认为是00 FF;结束帧的128字节的数据部分不存放任何信息,即全部用00填充。
4.文件传输过程
略;
注:完整工程请联系作者获取,可以定制上位机配合使用,网上也能找到相关上位机可以使用。
代码:
ymodem.c
#include "ymodem.h"
#include "common.h"
#include "string.h"
#include "stdio.h"
#include "ymodem.h"
#include "usart.h"
#include "flash_if.h"
uint8_t ymodem_file_name[FILE_NAME_LENGTH];
uint8_t tab_1024[1024] = {0};
#ifdef __EEPDEBUG
#define ymodem_ymodem_printf(format, ...) \
printf("[%s:%d->%s]:"format, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
#else
#define ymodem_ymodem_printf(format, ...)
#endif
/**
* @brief 整形转换为字符串
* @param str: The string
* @param intnum: The intger to be converted
* @retval None
*/
void ymode_int_str(uint8_t* str, int32_t intnum)
{
uint32_t i, Div = 1000000000, j = 0, Status = 0;
for (i = 0; i < 10; i++)
{
str[j++] = (intnum / Div) + 48;
intnum = intnum % Div;
Div /= 10;
if ((str[j-1] == '0') & (Status == 0))
{
j = 0;
}
else
{
Status++;
}
}
}
/**
* @brief Convert a string to an integer
* @param inputstr: The string to be converted
* @param intnum: The intger value
* @retval 1: Correct
* 0: Error
*/
uint32_t ymode_str_int(uint8_t *inputstr, int32_t *intnum)
{
uint32_t i = 0, res = 0;
uint32_t val = 0;
if (inputstr[0] == '0' && (inputstr[1] == 'x' || inputstr[1] == 'X'))
{
if (inputstr[2] == '\0')
{
return 0;
}
for (i = 2; i < 11; i++)
{
if (inputstr[i] == '\0')
{
*intnum = val;
res = 1;
break;
}
if (ISVALIDHEX(inputstr[i]))
{
val = (val << 4) + CONVERTHEX(inputstr[i]);
}
else
{
res = 0;
break;
}
}
if (i >= 11)
{
res = 0;
}
}
else
{
for (i = 0; i < 11; i++)
{
if (inputstr[i] == '\0')
{
*intnum = val;
res = 1;
break;
}
else if ((inputstr[i] == 'k' || inputstr[i] == 'K') && (i > 0))
{
val = val << 10;
*intnum = val;
res = 1;
break;
}
else if ((inputstr[i] == 'm' || inputstr[i] == 'M') && (i > 0))
{
val = val << 20;
*intnum = val;
res = 1;
break;
}
else if (ISVALIDDEC(inputstr[i]))
{
val = val * 10 + CONVERTDEC(inputstr[i]);
}
else
{
res = 0;
break;
}
}
if (i >= 11)
{
res = 0;
}
}
return res;
}
/**
* @brief Update CRC16 for input byte
* @param CRC input value
* @param input byte
* @retval Updated CRC value
*/
uint16_t ymodem_invert_uint32(uint16_t crcIn, uint8_t byte)
{
uint32_t crc = crcIn;
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 CRC value
*/
uint16_t ymodem_crc(const uint8_t* data, uint32_t size)
{
uint32_t crc = 0;
const uint8_t* dataEnd = data+size;
while(data<dataEnd)
{
crc = ymodem_invert_uint32(crc,*data++);
}
crc = ymodem_invert_uint32(crc,0);
crc = ymodem_invert_uint32(crc,0);
return (crc&0xffffu);
}
/**
* @brief 从发送方接收一个数据包
* @param data: 存储接收到的数据包的缓冲区
* @param length: 数据包的长度(返回参数)
* @param timeout: 接收超时时间
* 0: 传输结束
* -1: 发送方中止传输
* >0: 数据包的长度
* @retval 0: 正常返回
* -1: 超时或数据包错误
* 1: 用户中止
*/
int32_t ymodem_receive_packet (uint8_t *data, int32_t *length, uint32_t timeout)
{
uint16_t i, packet_size, computedcrc;
uint8_t c;
*length = 0;
// 接收一个字节
if (ymode_revice_char(&c, timeout) != 0)
{
return -1; // 没读到数据,超时或错误
}
switch (c)
{
case SOH:
packet_size = PACKET_SIZE; // 128字节数据包
break;
case STX:
packet_size = PACKET_1K_SIZE; // 1KB数据包
break;
case EOT:
return 0; // 传输结束
case CA:
// 接收到CA字符,判断是否为中止传输信号
if ((ymode_revice_char(&c, timeout) == 0) && (c == CA))
{
*length = -1; // 中止传输
return 0;
}
else
{
return -1; // 错误
}
case ABORT1: // 用户中止信号
case ABORT2:
return 1;
default:
return -1; // 错误
}
*data = c; // 存储接收到的字符
for (i = 1; i < (packet_size + PACKET_OVERHEAD); i++)
{
// 接收剩余的数据字节
if (ymode_revice_char(data + i, timeout) != 0)
{
return -1; // 接收超时或错误
}
}
if (data[PACKET_SEQNO_INDEX] != ((data[PACKET_SEQNO_COMP_INDEX] ^ 0xff) & 0xff))
{
return -1; // 数据包序列号错误
}
/* 计算CRC校验值 */
computedcrc = ymodem_crc(&data[PACKET_HEADER], (uint32_t)packet_size);
/* 检查接收到的CRC校验值与已计算的CRC校验值是否匹配
data[packet_size+3]<<8) | data[packet_size+4] 包含了接收到的CRC校验值
computedcrc 包含了已计算的CRC校验值 */
if (computedcrc != (uint16_t)((data[packet_size+3]<<8) | data[packet_size+4]))
{
/* CRC错误 */
return -1;
}
*length = packet_size; // 设置数据包的长度
return 0; // 正常返回
}
/**
* @brief 使用Ymodem协议接收文件
* @param buf: 存储文件数据的缓冲区的首地址
* @retval 文件的大小,若返回负数表示出现错误
*/
int32_t ymodem_receive_handle (uint8_t *buf)
{
uint8_t packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD], file_size[FILE_SIZE_LENGTH], *file_ptr, *buf_ptr;
int32_t i, packet_length, session_done, file_done, packets_received, errors, session_begin, size = 0;
uint32_t flashdestination, ramsource;
// 初始化Flash目标地址
flashdestination = APPLICATION_ADDRESS;
ymodem_flash_init();//FLASH解锁,,每次进来都解锁下防止又自锁
// 外层循环,用于控制整个Ymodem传输会话
for (session_done = 0, errors = 0, session_begin = 0; ;)
{
// 内层循环,用于接收并处理Ymodem数据包
for (packets_received = 0, file_done = 0, buf_ptr = buf; ;)
{
switch (ymodem_receive_packet(packet_data, &packet_length, NAK_TIMEOUT))
{
case 0:
errors = 0;
switch (packet_length)
{
// 发送方中止传输
case -1:
ymodem_put_char(ACK);
return 0;
// 传输结束
case 0:
ymodem_put_char(ACK);
file_done = 1;
break;
// 普通数据包
default:
if ((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0xff))
{
// 接收到的数据包序列号不匹配,请求重发
ymodem_put_char(NAK);
}
else
{
if (packets_received == 0)
{
// 文件名数据包
if (packet_data[PACKET_HEADER] != 0)
{
// 解析文件名和文件大小
for (i = 0, file_ptr = packet_data + PACKET_HEADER; (*file_ptr != 0) && (i < FILE_NAME_LENGTH);)
{
ymodem_file_name[i++] = *file_ptr++;
}
ymodem_file_name[i++] = '\0';
for (i = 0, file_ptr++; (*file_ptr != ' ') && (i < (FILE_SIZE_LENGTH - 1));)
{
file_size[i++] = *file_ptr++;
}
file_size[i++] = '\0';
ymode_str_int(file_size, &size);
// 检查文件大小是否超过Flash容量
if (size > (USER_FLASH_SIZE + 1))
{
// 文件大小超过Flash容量,结束会话
ymodem_put_char(CA);
ymodem_put_char(CA);
return -1;
}
// 擦除用户应用区域
ymodem_flash_erase(APPLICATION_ADDRESS);
// 发送ACK确认数据包接收,并请求发送CRC校验
ymodem_put_char(ACK);
ymodem_put_char(CRC16);
}
else
{
// 文件名数据包为空,结束会话
ymodem_put_char(ACK);
file_done = 1;
session_done = 1;
break;
}
}
else
{
// 数据包
// 将数据包中的数据拷贝到缓冲区
memcpy(buf_ptr, packet_data + PACKET_HEADER, packet_length);
ramsource = (uint32_t)buf;
// 将接收到的数据写入Flash内存
if (ymodem_flash_write(flashdestination, (uint32_t*) ramsource, (uint16_t) packet_length/4) == 0)
{
// 数据写入成功,发送ACK确认数据包接收
flashdestination += packet_length;
ymodem_put_char(ACK);
}
else
{
// 写入Flash内存时发生错误,结束会话
ymodem_put_char(CA);
ymodem_put_char(CA);
return -2;
}
}
packets_received++;
session_begin = 1;
}
}
break;
case 1:
// 超时错误,发送CA(中止)字符
ymodem_put_char(CA);
ymodem_put_char(CA);
return -3;
default:
if (session_begin > 0)
{
errors++;
}
if (errors > MAX_ERRORS)
{
// 传输错误次数超过最大限制,发送CA(中止)字符
ymodem_put_char(CA);
ymodem_put_char(CA);
//ymodem_ymodem_printf("222");
return 0;
}
// 发送CRC校验请求,等待继续传输数据(每隔大约1秒发送一个CRC校验请求)
ymodem_put_char(CRC16);
break;
}
if (file_done != 0)
{
break;
}
}
if (session_done != 0)
{
break;
}
}
// 返回文件的大小
return (int32_t)size;
}
/**
* @brief 准备第一块
* @param timeout
* @retval None
*/
void ymodem_prepare_intial_packet(uint8_t *data, const uint8_t* ymodem_file_name, uint32_t *length)
{
uint16_t i, j;
uint8_t file_ptr[10];
/* Make first three packet */
data[0] = SOH;
data[1] = 0x00;
data[2] = 0xff;
/* ymodem_file_name packet has valid data */
for (i = 0; (ymodem_file_name[i] != '\0') && (i < FILE_NAME_LENGTH); i++)
{
data[i + PACKET_HEADER] = ymodem_file_name[i];
}
data[i + PACKET_HEADER] = 0x00;
ymode_int_str (file_ptr, *length);
for (j =0, i = i + PACKET_HEADER + 1; file_ptr[j] != '\0' ; )
{
data[i++] = file_ptr[j++];
}
for (j = i; j < PACKET_SIZE + PACKET_HEADER; j++)
{
data[j] = 0;
}
}
/**
* @brief 准备数据包
* @param timeout
* @retval None
*/
void ymodem_prepare_packet(uint8_t *SourceBuf, uint8_t *data, uint8_t pktNo, uint32_t sizeBlk)
{
uint16_t i, size, packetSize;
uint8_t* file_ptr;
/* Make first three packet */
packetSize = sizeBlk >= PACKET_1K_SIZE ? PACKET_1K_SIZE : PACKET_SIZE;
size = sizeBlk < packetSize ? sizeBlk :packetSize;
if (packetSize == PACKET_1K_SIZE)
{
data[0] = STX;
}
else
{
data[0] = SOH;
}
data[1] = pktNo;
data[2] = (~pktNo);
file_ptr = SourceBuf;
/* ymodem_file_name packet has valid data */
for (i = PACKET_HEADER; i < size + PACKET_HEADER; i++)
{
data[i] = *file_ptr++;
}
if ( size <= packetSize)
{
for (i = size + PACKET_HEADER; i < packetSize + PACKET_HEADER; i++)
{
data[i] = 0x1A; /* EOF (0x1A) or 0x00 */
}
}
}
/**
* @brief Transmit a file using the ymodem protocol
* @param buf: Address of the first byte
* @retval The size of the file
*/
uint8_t ymodem_transmit_handle (uint8_t *buf, const uint8_t* sendymodem_file_name, uint32_t sizeFile)
{
uint8_t packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD];
uint8_t ymodem_file_name[FILE_NAME_LENGTH];
uint8_t *buf_ptr;
uint16_t tempCRC, blkNumber;
uint8_t receivedC[2], i;
uint32_t errors = 0, ackReceived = 0, size = 0, pktSize;
for (i = 0; i < (FILE_NAME_LENGTH - 1); i++)
{
ymodem_file_name[i] = sendymodem_file_name[i];
}
/* Prepare first block */
ymodem_prepare_intial_packet(&packet_data[0], ymodem_file_name, &sizeFile);
do
{
/* Send Packet */
ymode_transmit_buffer(packet_data, PACKET_SIZE + PACKET_HEADER);
/* Send CRC or Check Sum based on CRC16_F */
tempCRC = ymodem_crc(&packet_data[3], PACKET_SIZE);
ymodem_put_char(tempCRC >> 8);
ymodem_put_char(tempCRC & 0xFF);
/* Wait for Ack and 'C' */
if (ymode_revice_char(&receivedC[0], 1000000) == 0)
{
if (receivedC[0] == ACK)
{
/* Packet transfered correctly */
ackReceived = 1;
}
}
else
{
errors++;
}
}
while (!ackReceived && (errors < 0x0A));
if (errors >= 0x0A)
{
return errors;
}
buf_ptr = buf;
size = sizeFile;
blkNumber = 0x01;
/* Here 1024 bytes package is used to send the packets */
while (size)
{
/* Prepare next packet */
ymodem_prepare_packet(buf_ptr, &packet_data[0], blkNumber, size);
ackReceived = 0;
receivedC[0]= 0;
errors = 0;
do
{
/* Send next packet */
if (size >= PACKET_1K_SIZE)
{
pktSize = PACKET_1K_SIZE;
}
else
{
pktSize = PACKET_SIZE;
}
ymode_transmit_buffer(packet_data, pktSize + PACKET_HEADER);
/* Send CRC or Check Sum based on CRC16_F */
tempCRC = ymodem_crc(&packet_data[3], pktSize);
ymodem_put_char(tempCRC >> 8);
ymodem_put_char(tempCRC & 0xFF);
/* Wait for Ack */
if (ymode_revice_char(&receivedC[0], 1000000) == 0)
{
if (receivedC[0] == ACK)
{
ackReceived = 1;
if (size > pktSize)
{
buf_ptr += pktSize;
size -= pktSize;
if (blkNumber == (USER_FLASH_SIZE/1024))
{
return 0xFF; /* error */
}
else
{
blkNumber++;
}
}
else
{
buf_ptr += pktSize;
size = 0;
}
}
}
else
{
errors++;
}
}
while(!ackReceived && (errors < 0x0A));
/* Resend packet if NAK for a count of 10 else end of commuincation */
if (errors >= 0x0A)
{
return errors;
}
}
ackReceived = 0;
receivedC[0] = 0x00;
receivedC[1] = 0x00;
errors = 0;
do
{
ymodem_put_char(EOT);
/* Send (EOT); */
/* Wait for Ack */
ymodem_get_char(&receivedC[0]);
if (receivedC[0] == ACK)
{
ackReceived = 1;
}
else
{
errors++;
}
}
while (!ackReceived && (errors < 0x0A));
if (errors >= 0x0A)
{
return errors;
}
/* Last packet preparation */
ackReceived = 0;
receivedC[0] = 0x00;
receivedC[1] = 0x00;
errors = 0;
packet_data[0] = SOH;
packet_data[1] = 0;
packet_data [2] = 0xFF;
for (i = PACKET_HEADER; i < (PACKET_SIZE + PACKET_HEADER); i++)
{
packet_data [i] = 0x00;
}
do
{
/* Send Packet */
ymode_transmit_buffer(packet_data, PACKET_SIZE + PACKET_HEADER);
/* Send CRC or Check Sum based on CRC16_F */
tempCRC = ymodem_crc(&packet_data[3], PACKET_SIZE);
ymodem_put_char(tempCRC >> 8);
ymodem_put_char(tempCRC & 0xFF);
/* Wait for Ack and 'C' */
if (ymode_revice_char(&receivedC[1], 1000000) == 0)
{
if (receivedC[1] == ACK)
{
/* Packet transfered correctly */
ackReceived = 1;
}
}
else
{
errors++;
}
}
while (!ackReceived && (errors < 0x0A));
/* Resend packet if NAK for a count of 10 else end of commuincation */
if (errors >= 0x0A)
{
return errors;
}
receivedC[0] = 0x00;
do
{
ymodem_put_char(EOT);
/* Send (EOT); */
/* Wait for Ack */
if ((ymode_revice_char(&receivedC[0], 1000000) == 0) && receivedC[0] == ACK)
{
ackReceived = 1;
}
else
{
errors++;
}
/* Clear Overrun flag of the USART2 */
}
while (!ackReceived && (errors < 0x0A));
if (errors >= 0x0A)
{
return errors;
}
return 0; /* file trasmitted successfully */
}
void ymodem_download(void)
{
int32_t size = 0;
size = ymodem_receive_handle(&tab_1024[0]);
if (size > 0)
{
ymodem_ymodem_printf("download success!\n");
}
else
{
ymodem_ymodem_printf("download fail:%d!\n", size);
}
}
/**
* @brief Upload a file via serial port.
* @param None
* @retval None
*/
void ymodem_upload(void)
{
uint8_t status = 0 ;
uint8_t ch;
ymode_revice_char(&ch, -1);
if (ch == CRC16)
{
/* Transmit the flash image through ymodem protocol */
status = ymodem_transmit_handle((uint8_t*)APPLICATION_ADDRESS, (const uint8_t*)"UploadedFlashImage.bin", USER_FLASH_SIZE);
if (status != 0)
{
}
else
{
}
}
}
void jump_application(void)
{
typedef void (*pFunction)(void);
pFunction Jump_To_Application;
uint32_t JumpAddress;
JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);
Jump_To_Application = (pFunction) JumpAddress;
__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
Jump_To_Application();
}
ymodem.h
#ifndef _YMODEM_H_
#define _YMODEM_H_
#define CMD_STRING_SIZE 128
#include "main.h"
#define IS_AF(c) ((c >= 'A') && (c <= 'F'))
#define IS_af(c) ((c >= 'a') && (c <= 'f'))
#define IS_09(c) ((c >= '0') && (c <= '9'))
#define ISVALIDHEX(c) IS_AF(c) || IS_af(c) || IS_09(c)
#define ISVALIDDEC(c) IS_09(c)
#define CONVERTDEC(c) (c - '0')
#define CONVERTHEX_alpha(c) (IS_AF(c) ? (c - 'A'+10) : (c - 'a'+10))
#define CONVERTHEX(c) (IS_09(c) ? (c - '0') : CONVERTHEX_alpha(c))
#define PACKET_SEQNO_INDEX (1)
#define PACKET_SEQNO_COMP_INDEX (2)
#define PACKET_HEADER (3)
#define PACKET_TRAILER (2)
#define PACKET_OVERHEAD (PACKET_HEADER + PACKET_TRAILER)
#define PACKET_SIZE (128)
#define PACKET_1K_SIZE (1024)
#define FILE_NAME_LENGTH (256)
#define FILE_SIZE_LENGTH (16)
#define SOH (0x01) /* start of 128-byte data packet */
#define STX (0x02) /* start of 1024-byte data packet */
#define EOT (0x04) /* end of transmission */
#define ACK (0x06) /* acknowledge */
#define NAK (0x15) /* negative acknowledge */
#define CA (0x18) /* two of these in succession aborts transfer */
#define CRC16 (0x43) /* 'C' == 0x43, request 16-bit CRC */
#define ABORT1 (0x41) /* 'A' == 0x41, abort by user */
#define ABORT2 (0x61) /* 'a' == 0x61, abort by user */
#define NAK_TIMEOUT (0x100000)
#define MAX_ERRORS (10000)
#endif
common.c
/* Includes ------------------------------------------------------------------*/
#include "common.h"
#include "main.h"
/**
* @brief Print a character on the HyperTerminal
* @param c: The character to be printed
* @retval None
*/
void ymodem_put_char(uint8_t c)
{
HAL_UART_Transmit(&huart1, &c, 1, 0xff);
}
/**
* @brief Test to see if a key has been pressed on the HyperTerminal
* @param key: The key pressed
* @retval 1: Correct
* 0: Error
*/
uint32_t ymodem_get_char(uint8_t *key)
{
if ( __HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET)
{
HAL_UART_Receive(&huart1, key, 1, 0xff);
return 1;
}
else
{
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_ORE) != RESET)
{
__HAL_UART_CLEAR_OREFLAG(&huart1);
}
return 0;
}
}
/**
* @brief 接收一个字节
* @param 目标字符地址
* @param 超时时间
* @retval 0: 接收成功
* -1: 接收超时
*/
int ymode_revice_char(uint8_t *c, int timeout)
{
if(timeout == -1) {
while(ymodem_get_char(c) == 1);
} else {
while (timeout-- > 0) {
if (ymodem_get_char(c) == 1) {
return 0;
}
}
return -1;
}
return 0;
}
/**
* @brief 接收一个字节
* @param 目标字符地址
* @param 超时时间
* @retval 0: 接收成功
* -1: 接收超时
*/
int ymode_transmit_buffer(uint8_t *c, int len)
{
uint16_t i;
i = 0;
while (i < len) {
ymodem_put_char(c[i]);
i++;
}
}
flash_if.c
#include "flash_if.h"
/**
* @brief 解锁FLASH
* @param None
* @retval None
*/
void ymodem_flash_init(void)
{
/* Unlock the Program memory */
HAL_FLASH_Unlock();
/* Clear all FLASH flags */
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR);
/* Unlock the Program memory */
HAL_FLASH_Lock();
}
/**
*@brief此功能擦除所有用户闪光区域
*@param start:用户闪光区的开始
*@retval FLASHIF_OK:用户闪光区成功擦除
*FLASHIF_ERASEKO:发生错误
*/
uint32_t ymodem_flash_erase(uint32_t start)
{
uint32_t NbrOfPages = 0;
uint32_t PageError = 0;
FLASH_EraseInitTypeDef pEraseInit;
HAL_StatusTypeDef status = HAL_OK;
/* Unlock the Flash to enable the flash control register access *************/
HAL_FLASH_Unlock();
/* Get the sector where start the user flash area */
NbrOfPages = (USER_FLASH_END_ADDRESS - start)/FLASH_PAGE_SIZE;
pEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
pEraseInit.PageAddress = start;
pEraseInit.NbPages = NbrOfPages;
status = HAL_FLASHEx_Erase(&pEraseInit, &PageError);
/* Lock the Flash to disable the flash control register access (recommended
to protect the FLASH memory against possible unwanted operation) *********/
HAL_FLASH_Lock();
if (status != HAL_OK)
{
/* Error occurred while page erase */
return FLASHIF_ERASEKO;
}
return FLASHIF_OK;
}
/**
*@brief此函数在flash中写入数据缓冲区(数据以32位对齐)。
*@note写入数据缓冲区后,将检查闪存内容。
*@param destination:目标位置的起始地址
*@param p_source:要写入数据的缓冲区上的指针
*@param length:数据缓冲区的长度(单位为32位字)
*@retval uint32_t0:数据成功写入闪存
*1:在闪存中写入数据时出错
*2:闪存中的写入数据与预期数据不同
*/
uint32_t ymodem_flash_write(uint32_t destination, uint32_t *p_source, uint32_t length)
{
uint32_t i = 0;
/* Unlock the Flash to enable the flash control register access *************/
HAL_FLASH_Unlock();
for (i = 0; (i < length) && (destination <= (USER_FLASH_END_ADDRESS-4)); i++)
{
/* Device voltage range supposed to be [2.7V to 3.6V], the operation will
be done by word */
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, destination, *(uint32_t*)(p_source+i)) == HAL_OK)
{
/* Check the written value */
if (*(uint32_t*)destination != *(uint32_t*)(p_source+i))
{
/* Flash content doesn't match SRAM content */
return(FLASHIF_WRITINGCTRL_ERROR);
}
/* Increment FLASH destination address */
destination += 4;
}
else
{
/* Error occurred while writing data in Flash memory */
return (FLASHIF_WRITING_ERROR);
}
}
/* Lock the Flash to disable the flash control register access (recommended
to protect the FLASH memory against possible unwanted operation) *********/
HAL_FLASH_Lock();
return (FLASHIF_OK);
}
flash_if.h
#ifndef __FLASH_IF_H
#define __FLASH_IF_H
#include "stm32f3xx_hal.h"
enum
{
FLASHIF_OK = 0,
FLASHIF_ERASEKO,
FLASHIF_WRITINGCTRL_ERROR,
FLASHIF_WRITING_ERROR,
FLASHIF_PROTECTION_ERRROR
};
#define APPLICATION_ADDRESS (uint32_t)0x08006000 //APP起始地址
#define FLASH_PAGE_STEP FLASH_PAGE_SIZE //页大小
#define USER_FLASH_END_ADDRESS 0x08016000 //APP结束地址
#define USER_FLASH_SIZE (USER_FLASH_END_ADDRESS - USER_FLASH_END_ADDRESS) //APP大小
#define ABS_RETURN(x,y) ((x) < (y)) ? ((y)-(x)) : ((x)-(y))
#define FLASH_SECTOR_NUMBER ((uint32_t)(ABS_RETURN(APPLICATION_ADDRESS,FLASH_START_BANK1))>>12)
#define FLASH_PROTECTED_SECTORS (~(uint32_t)((1 << FLASH_SECTOR_NUMBER) - 1))
void ymodem_flash_init(void);
uint32_t ymodem_flash_erase(uint32_t StartSector);
uint32_t ymodem_flash_write(uint32_t destination, uint32_t *p_source, uint32_t length);
#endif
Ymodem协议数据帧格式详解
博客介绍了Xmodem、Ymodem和Zmodem三种常用通信协议,重点阐述Ymodem协议。Ymodem是Xmodem改进版,传输快速稳定。详细说明了Ymodem起始帧、数据帧、结束帧的数据格式,还提及文件传输过程略,最后给出相关代码文件名。
1万+

被折叠的 条评论
为什么被折叠?



