前言
鉴于项目需求,学习并移植YModem在华大Hc32L系列MCU上,时间短,有欠缺的地方欢迎指正
一、YMODEM协议理解
示例:这里参照了一篇YModem协议说明,供参考,下载链接在结尾处,有需要的可自取。
Ymodem协议解释:
SENDER:发送方。(文件名:fileName.bin)
RECEIVER:接收方。
具体握手的步骤如下:
1、接收方发送一个字符’C’,也就是十六进制’43’。代表接收方已经处于接收数据的状态。
2、发送方接收到’C’之后,发送头帧数据包,内容如下:
SOH 00 FF fileName.bin NULL[116] CRC CRC
数据包内容解释:
2-1、SOH(第一字节):表示数据区大小有128字节。(STX表示本数据包数据区大小1024字节)。
2-2、00(第二字节):数据块编号。第一包为00,第二包为01,此后依次累加。FF后,继续从00循环。
2-3、FF(第三字节):数据块编号的反码。编号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做应答,然后通信正式结束。
二、程序设计
1.程序设计说明
Boot程序用UART0作数据传输,UART1作数据打印,while循环调用YModem_Receive_Proc函数处理数据。
App程序用UART1作数据打印,while循环调用systick获取当前时间量,1s打印一次数据。
2.BOOT程序
代码如下(示例):
/**
* @brief Function Main
*/
int main(void)
{
uint32_t len;
Flash_Init(NULL,4,FALSE);
Flash_WaitCycle(FlashWaitCycle1);
Tsk_SystemInit();
Tsk_Uart0Init();
Tsk_Uart1Init();
__enable_irq();
printf("This is Boot\r\n");
printf("Please send the bin file to MCU\r\n");
Hal_Uart0_Send_Str("This is Boot,Please send the bin file to MCU.\r\n");
while(1)
{
YModem_Receive_Proc(buff,&len);
}
}
/*************************************************************
Function : YModem_Receive
Description: ymodem接收
Input : buf-存放接收到的数据
length 接收数据的总长度
return :
0 -发送端传输中止
-1 -固件过大
-2 -flash烧写错误
-3 -用户终止
*************************************************************/
int YModem_Receive_Proc(unsigned char *buf, unsigned int *length)
{
unsigned int i,size;
unsigned int errors;
unsigned int packets_received;
unsigned int packet_length;
unsigned int app_bin_datalen;
unsigned char *file_ptr, *buf_ptr;
*length = 0;
size = 0;
errors = 0;
packets_received = 0;
app_bin_datalen = 0;
buf_ptr = buf;
memset(buf_ptr,0xff,FLASH_PAGE_SIZE);
while(1) //死循环,不断接收数据
{
switch(YModem_RecvPacket(packet_data, &packet_length, NAK_TIMEOUT))//接收数据包
{
case 0: //接收正常
errors = 0;
if(packet_length)//接收数据中
{
if((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0xff))
{
//接收错误的数据,回复NAK
YModem_SendByte(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); )
{
if(i < 16) file_name[i++] = *file_ptr++;//保存文件名
else
{
file_name[i++] = '\0'; //文件名以'\0'结束
file_ptr++;
}
}
file_name[i++] = '\0';//文件名以'\0'结束
for(i = 0, file_ptr++; (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH); )
{
file_size[i++] = *file_ptr++;//保存文件大小
}
file_size[i++] = '\0';//文件大小以'\0'结束
size = w_atoi((char *)file_size);//将文件大小字符串转换成整型
*length = size;
if(UpdateAppCodeStart(file_name, size)) //
{
YModem_SendByte(CA);
YModem_SendByte(CA);//连续发送2次中止符CA
return -1;//返回
}
size = 0;
YModem_SendByte(ACK); //回复ACk
YModem_SendByte(CRC16);//发送'C',询问数据
printf("------------->>first packet\r\n");
}
else//文件名数据包为空,结束传输
{
YModem_SendByte(ACK);//回复ACK
return 0;
}
}
else
{
file_ptr = packet_data + PACKET_HEADER;
while(packet_length)
{
if(packet_length >= (FLASH_PAGE_SIZE - app_bin_datalen))
{
printf("------------->>packet_length:%d\r\n",packet_length);
memcpy(buf_ptr + app_bin_datalen, file_ptr, (FLASH_PAGE_SIZE - app_bin_datalen));//拷贝数据
file_ptr += (FLASH_PAGE_SIZE - app_bin_datalen);
packet_length -= (FLASH_PAGE_SIZE - app_bin_datalen);
//接收到1页固件资料,就更新写入
if(UpdateAppCode(buf_ptr))
{
printf("------------->>error -2\r\n");
YModem_SendByte(CA);
YModem_SendByte(CA);//flash烧写错误,连续发送2次中止符CA
return -2;//烧写错误
}
size += FLASH_PAGE_SIZE;
app_bin_datalen = 0;
memset(buf_ptr,0xff,FLASH_PAGE_SIZE);
}
else
{
printf("-------------<<packet_length:%d\r\n",packet_length);
//由 file_ptr 所指内存区域复制 packet_length 个字节到 buf_ptr + app_bin_datalen 所指内存区域。
memcpy(buf_ptr + app_bin_datalen, file_ptr, packet_length);//拷贝数据
app_bin_datalen += packet_length;
packet_length = 0;
}
}
YModem_SendByte(ACK);//flash烧写成功,回复ACK
}
packets_received++;//收到数据包的个数
}
}
else
{
YModem_SendByte(ACK);
//接收完成
UpdateAppCodeEnd(buf_ptr, app_bin_datalen);
size += app_bin_datalen;
printf("------------->>size:%d\r\n",size);
return size;
}
break;
case -2: //用户中止
printf("ack -2\r\n");
YModem_SendByte(CA);
YModem_SendByte(CA); //连续发送2次中止符CA
return -3; //烧写中止
case -3:
//发送端中止传输
printf("ack -3\r\n");
YModem_SendByte(ACK);//回复ACK
return 0;
case -1:
if(packets_received > 0) //传输过程中发生错误
{
errors++;
if(errors > MAX_ERRORS) //错误超过上限
{
printf("errors %d\r\n",errors);
YModem_SendByte(CA);
YModem_SendByte(CA);//连续发送2次中止符CA
return 0; //传输过程发生过多错误
}
}
if(eot_flag)
{
YModem_SendByte(CRC16); //发送'C',继续接收
if(ca_index++ > 0)
{
eot_flag = 0;
printf("------------->>JumpToApp\r\n");
Hal_Uart0_Send_Str("------>>Ok\r\n");
iap_load_app(APP_FW_START_ADDR);
}
}
else
{
YModem_SendByte(CRC16); //发送'C',继续接收
}
break;
}
}
}
3.APP程序
/**
* @brief Function Main
*/
int main(void)
{
uint32_t timedelay;
SCB->VTOR = 0x4000;
Flash_Init(NULL,4,FALSE);
Flash_WaitCycle(FlashWaitCycle1);
Tsk_SystemInit();
Tsk_Uart0Init();
Tsk_Uart1Init();
__enable_irq();
timedelay = uTick;
printf("-------------->>This is App\r\n");
while(1)
{
if((uTick - timedelay) > 1000)
{
timedelay = uTick;
printf("-------------->>App run %d\r\n",i++);
}
}
}
三、功能验证
四、总结
1.Boot程序注意数据收完跳转APP程序的方式;
2.App程序注意设置偏移地址
源文件下载地址:https://download.csdn.net/download/qq_34413603/34864984?spm=1001.2014.3001.5501