【GD32L233C-START评测】9、IAP程序升级——基于YMODEM协议
[复制链接]
本帖最后由 freeelectron 于 2022-2-7 12:57 编辑
相关文章:
【GD32L233C-START评测】1、优点与缺点都很明显的GD32L233C-START(开箱)
【GD32L233C-START评测】2、非阻塞方式点灯,blink,blink,blink……
【GD32L233C-START评测】3、pwm实现呼吸灯
【GD32L233C-START评测】4、串口不定长数据接收
【GD32L233C-START评测】5、Flash读写——使用内部Flash存储数据
【GD32L233C-START评测】6、硬件I2C驱动0.96吋OLED
【GD32L233C-START评测】7、硬件SPI1驱动RC522
【GD32L233C-START评测】8、获取MCU96位唯一ID、SRAM、FLASH大小
1、什么是IAP
IAP是In Application Programming的缩写,即在应用编程,IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。可以通过串口、USB、网络、无线等方式进行升级数据的传输。
2、IAP要点
(1)程序分两部分,BOOT和APP分别编写;
(2)flash空间划分(flash空间足够大的情况下,可以分成APP1和APP2进行备份升级);
(3)flash可编程(读、写、擦除);GD32L233flash编程,可查看【GD32L233C-START评测】5、Flash读写——使用内部Flash存储数据,里面有关于flash的的读、写、擦除操作;
(4)从BOOT跳转到APP的时候需要,关闭中断,判断栈顶地址、设置栈指针;
(5)APP程序编译设置,flash起始地址;
(6)App程序中设置,中断开和中断向量位置。
3、YMODEM协议
3.1、YMODEM 帧格式
Ymodem 有两种帧格式,主要区别是信息块长度不一样。
帧头 包序号 包序号 取反 信息块 校验
SOH/STX PN XPN DATA CRC
1byte 1byte 1byte 128/1024byte 2byte
3.1.1、帧头
帧头表示两种数据帧长度,主要是信息块长度不同。
当帧头为SOH(0x01)时,信息块为128字节;
当帧头为STX(0x02)时,信息块为1024字节。
3.1.2、包序号
数据包序号只有1字节,因此计算范围是0~255;对于数据包大于255的,序号归零重复计算。
3.1.3、帧长度
以SOH(0x01)开始的数据包,信息块是128字节,该类型帧总长度为133字节;
以STX(0x02)开始的数据包,信息块是1024字节,该类型帧总长度为1029字节。
3.1.4、校验
Ymodem采用的是CRC16校验算法,校验值为2字节,传输时CRC高八位在前,低八位在后;CRC计算数据为信息块数据,不包含帧头、包号、包号反码。
3.2、YMODEM起始帧
Ymodem起始帧并不直接传输文件内容,而是先将文件名和文件大小置于数据帧中传输;起始帧是以SOH 133字节长度帧传输,格式如下。
帧头 包序号 包序号 取反 文件名称 文件大小 填充区 校验
SOH 0x00 0xFF filename+0x00 filesize+0x00 n字节0x00 CRC16
其中包号为固定为0;
filename为文件名称,文件名称后必须加0x00作为结束;
filesize为文件大小值,文件大小值后必须加0x00作为结束;
余下未满128字节数据区域,则以0x00填充。
可以看出起始帧也是遵守3.1中Ymodem包格式的。
3.3、YMODEM数据帧
Ymodem数据帧传输,在信息块填充有效数据。
传输有效数据时主要考虑的是最后一包数据的是处理,SOH帧和STR帧有不同的处理。
(1)对于SOH帧,若余下数据小于128字节,则以0x1A填充,该帧长度仍为133字节。
(2)对于STX帧需考虑几种情况:
余下数据等于1024字节,以1029长度帧发送;
余下数据小于1024字节,但大于128字节,以1029字节帧长度发送,无效数据以0x1A填充;
余下数据等于128字节,以133字节帧长度发送;
余下数据小于128字节,以133字节帧长度发送,无效数据以0x1A填充。
3.4、YMODEM结束帧
Ymodem的结束帧采用SOH 133字节长度帧传输,该帧不携带数据(空包),即数据区、校验都以0x00填充。
帧头 包序号 包序号 取反 信息块 校验
0x01 0x00 0xff 128个0x00 0x00 0x00
3.5、YMODEM握手信号
握手信号由接收方发起,在发送方开始传输文件前,接收方需发送YMODEM_C (字符C,ASII码为0x43)命令,发送方收到后,开始传输起始帧。
3.6、YMODEM命令
命令 命令码 说明
SOH 0x01 128字节数据包
STX 0x02 1024字节的数据包
EOT 0x04 结束传输
ACK 0x06 回应
NAK 0x15 没回应,需要重传当前数据包
CA 0x18 取消传输
C 0x43 握手
4、YMODEM工具
5、核心代码
(1)操作菜单,通过输入代码,选择执行的操作
void Main_Menu(void)
{
uint8_t key = 0;
while (1)
{
SerialPutString("\r\n================== Main Menu ===============================\r\n\n");
SerialPutString(" Download Image To the GD32L233CCT6 Internal Flash ------- 1\r\n\n");
SerialPutString(" Upload Image From the GD32L233CCT6 Internal Flash ------- 2\r\n\n");
SerialPutString(" Execute The New Program --------------------------------- 3\r\n\n");
SerialPutString("=============================================================\r\n\n");
key = GetKey();
if (key == 0x31)
{
/* Download user application in the Flash */
SerialDownload();
}
else if (key == 0x32)
{
/* Upload user application from the Flash */
SerialUpload();
}
else if (key == 0x33)
{
if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000) //判断栈顶地址是否在合法范围内
{
JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
/* Jump to user application */
Jump_To_Application = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) ApplicationAddress);
Jump_To_Application();
}
}
else
{
SerialPutString("Invalid Number ! ==> The number should be either 1, 2 or 3\r");
}
}
}
(2)ymodem发送
uint8_t Ymodem_Transmit (uint8_t *buf, const uint8_t* sendFileName, uint32_t sizeFile)
{
uint8_t packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD];
uint8_t FileName[FILE_NAME_LENGTH];
uint8_t *buf_ptr, tempCheckSum ;
uint16_t tempCRC, blkNumber;
uint8_t receivedC[2], CRC16_F = 0, i;
uint32_t errors, ackReceived, size = 0, pktSize;
errors = 0;
ackReceived = 0;
for (i = 0; i < (FILE_NAME_LENGTH - 1); i++)
{
FileName = sendFileName;
}
CRC16_F = 1;
/* Prepare first block */
Ymodem_PrepareIntialPacket(&packet_data[0], FileName, &sizeFile);
do
{
/* Send Packet */
Ymodem_SendPacket(packet_data, PACKET_SIZE + PACKET_HEADER);
/* Send CRC or Check Sum based on CRC16_F */
if (CRC16_F)
{
tempCRC = Cal_CRC16(&packet_data[3], PACKET_SIZE);
Send_Byte(tempCRC >> 8);
Send_Byte(tempCRC & 0xFF);
}
else
{
tempCheckSum = CalChecksum (&packet_data[3], PACKET_SIZE);
Send_Byte(tempCheckSum);
}
/* Wait for Ack and 'C' */
if (Receive_Byte(&receivedC[0], 10000) == 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 */
/* Resend packet if NAK for a count of 10 else end of commuincation */
while (size)
{
/* Prepare next packet */
Ymodem_PreparePacket(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;
}
Ymodem_SendPacket(packet_data, pktSize + PACKET_HEADER);
/* Send CRC or Check Sum based on CRC16_F */
/* Send CRC or Check Sum based on CRC16_F */
if (CRC16_F)
{
tempCRC = Cal_CRC16(&packet_data[3], pktSize);
Send_Byte(tempCRC >> 8);
Send_Byte(tempCRC & 0xFF);
}
else
{
tempCheckSum = CalChecksum (&packet_data[3], pktSize);
Send_Byte(tempCheckSum);
}
/* Wait for Ack */
if ((Receive_Byte(&receivedC[0], 0xffffff) == 0) && (receivedC[0] == ACK))
{
ackReceived = 1;
if (size > pktSize)
{
buf_ptr += pktSize;
size -= pktSize;
if (blkNumber == (APP_IMAGE_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;
errors = 0;
do
{
Send_Byte(EOT);
/* Send (EOT); */
/* Wait for Ack */
if ((Receive_Byte(&receivedC[0], 0xffffff) == 0) && receivedC[0] == ACK)
{
ackReceived = 1;
}
else
{
errors++;
}
}while (!ackReceived && (errors < 0x0A));
if (errors >= 0x0A)
{
return errors;
}
/* Last packet preparation */
ackReceived = 0;
receivedC[0] = 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 = 0x00;
}
do
{
/* Send Packet */
Ymodem_SendPacket(packet_data, PACKET_SIZE + PACKET_HEADER);
/* Send CRC or Check Sum based on CRC16_F */
tempCRC = Cal_CRC16(&packet_data[3], PACKET_SIZE);
Send_Byte(tempCRC >> 8);
Send_Byte(tempCRC & 0xFF);
/* Wait for Ack and 'C' */
if (Receive_Byte(&receivedC[0], 0xffffff) == 0)
{
if (receivedC[0] == 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;
}
do
{
Send_Byte(EOT);
/* Send (EOT); */
/* Wait for Ack */ //0xffffff
if ((Receive_Byte(&receivedC[0], 0xfff) == 0) && receivedC[0] == ACK)
{
ackReceived = 1;
}
else
{
errors++;
}
}while (!ackReceived && (errors < 0x0A));
if (errors >= 0x0A)
{
return errors;
}
return 0; /* file trasmitted successfully */
}
(3)ymodem接收
int32_t Ymodem_Receive (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;
/* Initialize FlashDestination variable */
FlashDestination = ApplicationAddress;
for (session_done = 0, errors = 0, session_begin = 0; ;)
{
for (packets_received = 0, file_done = 0, buf_ptr = buf; ;)
{
switch (Receive_Packet(packet_data, &packet_length, NAK_TIMEOUT))
{
case 0:
errors = 0;
switch (packet_length)
{
/* Abort by sender */
case - 1:
Send_Byte(ACK);
return 0;
/* End of transmission */
case 0:
Send_Byte(ACK);
file_done = 1;
break;
/* Normal packet */
default:
if ((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0xff))
{
Send_Byte(NAK);
}
else
{
if (packets_received == 0) //第一个包
{
/* Filename packet */
if (packet_data[PACKET_HEADER] != 0)
{
/* Filename packet has valid data */
for (i = 0, file_ptr = packet_data + PACKET_HEADER; (*file_ptr != 0) && (i < FILE_NAME_LENGTH);)
{
file_name[i++] = *file_ptr++;
}
file_name[i++] = '\0';
for (i = 0, file_ptr ++; (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH);)
{
file_size[i++] = *file_ptr++;
}
file_size[i++] = '\0';
Str2Int(file_size, &size);
/* Test the size of the image to be sent */
/* Image size is greater than Flash size */
if (size > (FLASH_SIZE - 1)) //文件大小,大于flash容量
{
/* End session */
Send_Byte(CA);
Send_Byte(CA);
return -1;
}
FlashErase(FlashDestination,FlashDestination+size);
Send_Byte(ACK);
Send_Byte(CRC16);
}
/* Filename packet is empty, end session */
else
{
Send_Byte(ACK);
file_done = 1;
session_done = 1;
break;
}
}
/* Data packet */
else
{
memcpy(buf_ptr, packet_data + PACKET_HEADER, packet_length);
RamSource = (uint32_t)buf;
{
FlashWrite(packet_length,buf_ptr,FlashDestination);
FlashDestination+=packet_length;
}
Send_Byte(ACK);
}
packets_received ++;
session_begin = 1;
}
}
break;
case 1:
Send_Byte(CA);
Send_Byte(CA);
return -3;
default:
if (session_begin > 0)
{
errors ++;
}
if (errors > MAX_ERRORS)
{
Send_Byte(CA);
Send_Byte(CA);
return 0;
}
Send_Byte(CRC16);
break;
}
if (file_done != 0)
{
break;
}
}
if (session_done != 0)
{
break;
}
}
return (int32_t)size;
}
6、现象
(1)上电复位,选择操作
(2)升级程序
(3)读取mcu中的app部分flash
注:这里从mcu读取了app所有空间的flash。
|