文章目录
- 前言
- 一、YMODEM协议理解
- 二、程序设计
- 1.程序设计说明
- 2.BOOT程序
- 3.APP程序
- 三、功能验证
- 四、总结
前言
鉴于项目需求,学习并移植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)) {//接收错误的数据,回复NAKYModem_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次中止符CAreturn -1;//返回}size = 0;YModem_SendByte(ACK); //回复ACkYModem_SendByte(CRC16);//发送'C',询问数据printf("------------->>first packet\r\n");}else//文件名数据包为空,结束传输{YModem_SendByte(ACK);//回复ACKreturn 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次中止符CAreturn -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次中止符CAreturn -3; //烧写中止case -3://发送端中止传输printf("ack -3\r\n");YModem_SendByte(ACK);//回复ACKreturn 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次中止符CAreturn 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