STM32G4单片机基于Ymode协议IAP代码升级详解

STM32G4单片机-基于Ymode协议的IAP代码升级

  • YMode协议说明
  • 数据帧格式
  • 命令类型
  • 起始帧格式
  • 数据帧格式
  • 结束帧格式
  • YMODE协议数据传输流程
  • 代码实现过程
  • YMode协议说明

    YModem 协议是由 XModem 协议演变而来的,每包数据可以达到 1024 字节,是一个非常高效的文件传输协议。我们平常所说的 Ymodem 协议是指的 Ymodem-1K,除此还有 Ymodem-g(没有 CRC 校验,不常用)。YModem-1K 协议用 1024 字节数据帧传输取代了标准的 128 字节数据帧传输,发送的数据会使用 CRC 校验,保证数据传输的正确性。它每传输一个信息块时,就会等待接收端返回 ACK 信号,接收到响应信号后,才会继续传输下一个信息块,从而保证能够接收到全部数据。
    所有声称支持 Ymodem 协议的项目必须满足以下最低要求:
    发送端应在第一个数据包中发送路径名(文件名)。
    路径名应为以 “NULL” 结尾的 ASCII 字符串,如下所述:
     1)除非有特别要求,否则只发送文件名部分。
     2)不发送驱动器符号。
     3)不区分文件名中大小写字母的系统应仅以小写形式发送路径名。
    接收端应使用第一个数据包中发送的路径名作为接收文件名。当接收端接收到第一个数据包并成功打开输出文件后,应该返回一个 “ACK” 字符给发送端用来确认这个数据包,然后接收端再发送 “C” 或 “NAK” 来开始进行正常的 Ymodem 传输。接收端在接收每个文件时要能接受 128 字节和 1024 字节两种数据包的任意混合发送。发送端可以在 1024 字节和 128 字节两种数据包之间任意切换发送。发送端不能更改未确认的数据包的长度。在每次文件传输的末尾,发送端只能发送最多 10 次 “EOT” 字符,直到它收到一个 “ACK” 字符。(这是 Xmodem 规范的一部分。)发送路径名为空的路径名数据包来表示传输会话的结束,该路径名数据包应与其它路径名数据包一样被确认。
      没有满足以上要求的程序不兼容 Ymodem 协议,不应被描述为支持 Ymodem 协议。

    数据帧格式

    命令类型

    起始帧格式

    Ymode协议的第一帧数据是文件的名称和大小数据。
    帧头为 SOH,表示起始帧中包含着128个字节的数据。
    起始帧帧序号固定为 0x00,帧序号反码为 0xFF。
    FILENAME 是要传输的文件名,如 Template.bin,它在数据帧中的格式为:0x54,0x65,0x6D,0x70,0x6C,0x61,0x74,0x65,0x2E,0x62,0x69,0x6E,0x00,也就是把 ASCII 码转成十六进制,但是最后一定要在文件名后加上 0x00,表示文件名的结束。
    FILESIZE 是要传输的文件大小,如上面的 Template.bin 大小 107544 byte,转换成十六进制就是 0x1A418。它在数据帧中的格式可以是十进制数的 0x31,0x30,0x37,0x35,0x34,0x34,也可以是十六进制数的 0x30,0x78,0x31,0x41,0x34,0x31,0x38,同样最后要加上 0x00 表示结束。
    NULL 是数据部分若 FILENAME 和 FILESIZE 加起来不满 128 字节则以 0x00 填充剩余字节。
    CRC 表示的是数据部分的 CRC 校验,不包含帧头、帧序号和帧序号反码。校验值为 2 字节,传输时 CRC 高八位在前,低八位在后。

    数据帧格式

    帧头为 SOH 时,表示数据帧的数据段为 128 个字节;帧头为 STX 时,表示数据帧的数据段为 1024 个字书。
    数据帧帧序号的取值范围为 0~255,帧序号反码是它的取反;
    ·DATA 表示要传输的文件数据;
    ·FILL 是数据部分若 DATA 不满 128/1024 字节则以 0x1A 填充剩余字节
    CRC 表示的是数据部分的 CRC 校验,不包含帧头、帧序号和帧序号反码。校验值为2字节,传输时CRC 高八位在前,低八位在后。

    结束帧格式

    帧头为 SOH,表示结束帧中包含着 128 个字节的数据。
    结束帧帧序号固定为 0x00,帧序号反码为 0xFF。
    结束帧的 128 字节的数据部分不存放任何信息,即全部用 0x00 填充。
    CRC 表示的是数据部分的 CRC 校验,不包含帧头、帧序号和帧序号反码。校验值为2字节,传输时CRC 高八位在前,低八位在后。

    YMODE协议数据传输流程

    代码实现过程

    这里只列出IAP过程代码,详细代码后续以压缩包的形式上传,有需要的自己下载

    void iap_task(void)
    {
    	uint8_t i=0;
    	uint16_t crc = 0;
    	printf("\r\nMotor Control STM32G431 Bootloader\r\n");
    	printf("Please send file with YMODEM\r\n");	
    	while(1)
    	{
    		
    		iap_data_len = uart3_get_receive_data(iap_buf,IAP_BUF_LEN);
    		switch(iap_status)
    		{
    			case WAIT_CONNECT:
    					if(systick_time_diff(iap_tick) >= CONNECT_DATA_CYCLE)//1秒发一次YMODE链接指令
    					{
    						iap_tick = HAL_GetTick();
    						HAL_UART_Transmit(&huart3,(uint8_t *)&YMODEM_CMD_C,1,100);
    					}
    					if(systick_time_diff(jump_app_tick) >= WAIT_CONNECT_TIMEOUT)//超时直接跳转到APP
    					{
    							jump_to_app();
    							jump_app_tick = HAL_GetTick();
    					}
    					if(iap_data_len == (YMODEM_SOH_DATA_LEN + 5))//接收到SOH数据帧
    					{
    						if((iap_buf[0] == YMODEM_CMD_SOH) &&  (iap_buf[1] == 0x00) && (iap_buf[2] == 0xFF))
    						{
    							packet_number = iap_buf[1];
    							crc = cal_crc16(&iap_buf[3],iap_data_len-5);
    							if(crc == ((iap_buf[iap_data_len-2]<<8)|iap_buf[iap_data_len-1]))
    							{
    								uint16_t find_index = 3;
    								uint8_t file_name_end_flag = 0;
    								while(find_index < (iap_data_len - 5))
    								{
    									if(iap_buf[find_index] == 0x00)
    									{
    										file_name_end_flag = 1;
    										find_index++;
    										i=0;
    									}
    									if(file_name_end_flag == 0)
    									{
    										filename[i++] = iap_buf[find_index];
    									}
    									if(file_name_end_flag)
    									{
    										if((iap_buf[find_index] >= 0x30) && (iap_buf[find_index] <= 0x39))
    										{
    											filelength[i++] = iap_buf[find_index];
    											file_length *= 10;
    											file_length += (iap_buf[find_index] - 0x30);
    										}
    										if(iap_buf[find_index] == 0x20)
    										{
    											break;
    										}
    									}
    									find_index++;
    								}
    								if(file_length <= APP_MAX_LENGTH)//判断数据是否超出允许的大小
    								{
    									HAL_UART_Transmit(&huart3,(uint8_t *)&YMODEM_CMD_ACK,1,100);
    									//擦除文件指定大小的扇区
    									if (FLASH_EraseSector(FLASH_GetSector(ApplactionAddr),FLASH_GetSectorNum(APP_MAX_LENGTH)) != FLASH_OK) {
    										// 错误处理
    									}
    									packet_number++;
    									iap_status = DATA_RECEIVE;
    									program_addr = ApplactionAddr;
    									HAL_UART_Transmit(&huart3,(uint8_t *)&YMODEM_CMD_C,1,100);
    									iap_tick = HAL_GetTick();
    									jump_app_tick = HAL_GetTick();
    								}
    							}
    						}
    					}					
    				break;
    			case DATA_RECEIVE:
    					if(iap_data_len)
    					{
    						if(iap_buf[0] == YMODEM_CMD_SOH)
    						{
    							if(iap_data_len != (YMODEM_SOH_DATA_LEN + 5))
    							{
    								iap_status = WAIT_CONNECT;
    								break;
    							}
    							if(packet_number != iap_buf[1])
    							{
    								iap_status = WAIT_CONNECT;
    								break;
    							}
    							packet_number++;
    							crc = cal_crc16(&iap_buf[3],YMODEM_SOH_DATA_LEN);
    							if(crc != ((iap_buf[iap_data_len-2]<<8)|iap_buf[iap_data_len-1]))
    							{
    								iap_status = WAIT_CONNECT;
    								break;
    							}
    							if((program_addr + YMODEM_SOH_DATA_LEN - ApplactionAddr) <= file_length)
    							{
    								memcpy(program_buf,&iap_buf[3],YMODEM_SOH_DATA_LEN);
    								FLASH_WriteData(program_addr,YMODEM_SOH_DATA_LEN,program_buf);
    								program_addr += YMODEM_SOH_DATA_LEN;
    							}
    							else
    							{
    									uint16_t program_len = file_length - (program_addr - ApplactionAddr);
    									if(program_len)
    									{
    											memcpy(program_buf,&iap_buf[3],program_len);
    											FLASH_WriteData(program_addr,program_len,program_buf);
    											program_addr += program_len;
    									}
    							}
    							HAL_UART_Transmit(&huart3,(uint8_t *)&YMODEM_CMD_ACK,1,100);
    							iap_tick = HAL_GetTick();
    							jump_app_tick = HAL_GetTick();
    						}
    						else if(iap_buf[0] == YMODEM_CMD_STX)
    						{
    							if(iap_data_len != (YMODEM_STX_DATA_LEN + 5))
    							{
    								iap_status = WAIT_CONNECT;
    								break;
    							}
    							if(packet_number != iap_buf[1])
    							{
    								iap_status = WAIT_CONNECT;
    								break;
    							}
    							packet_number++;
    							crc = cal_crc16(&iap_buf[3],YMODEM_STX_DATA_LEN);
    							if(crc != ((iap_buf[iap_data_len-2]<<8)|iap_buf[iap_data_len-1]))
    							{
    								iap_status = WAIT_CONNECT;
    								break;
    							}
    							if((program_addr + YMODEM_STX_DATA_LEN - ApplactionAddr) <= file_length)
    							{
    								memcpy(program_buf,&iap_buf[3],YMODEM_STX_DATA_LEN);
    								FLASH_WriteData(program_addr,YMODEM_STX_DATA_LEN,program_buf);
    								program_addr += YMODEM_STX_DATA_LEN;
    							}
    							else
    							{
    								uint16_t program_len = file_length - (program_addr - ApplactionAddr);
    								if(program_len)
    								{
    									memcpy(program_buf,&iap_buf[3],program_len);
    									FLASH_WriteData(program_addr,program_len,program_buf);
    									program_addr += program_len;
    								}
    							}
    							HAL_UART_Transmit(&huart3,(uint8_t *)&YMODEM_CMD_ACK,1,100);
    							iap_tick = HAL_GetTick();
    							jump_app_tick = HAL_GetTick();
    						}
    						else if(iap_buf[0] == YMODEM_CMD_EOT)
    						{
    							HAL_UART_Transmit(&huart3,(uint8_t *)&YMODEM_CMD_NAK,1,100);
    							iap_status = RECEIVE_FINISH;
    							iap_tick = HAL_GetTick();
    							jump_app_tick = HAL_GetTick();
    						}
    					}
    				break;
    			case RECEIVE_FINISH:
    				if(iap_data_len)
    				{
    					if(iap_buf[0] == YMODEM_CMD_EOT)
    					{
    						HAL_UART_Transmit(&huart3,(uint8_t *)&YMODEM_CMD_ACK,1,100);
    						HAL_Delay(100);
    						HAL_UART_Transmit(&huart3,(uint8_t *)&YMODEM_CMD_C,1,100);
    						iap_status = RECEIVE_END;
    						iap_tick = HAL_GetTick();
    						jump_app_tick = HAL_GetTick();
    					}
    				}
    				break;
    			case RECEIVE_END:
    				if(iap_data_len == (YMODEM_SOH_DATA_LEN + 5))
    				{
    					HAL_UART_Transmit(&huart3,(uint8_t *)&YMODEM_CMD_ACK,1,100);
    					iap_status = JUMP_TO_APP;
    					HAL_Delay(1000);
    					printf("\r\nProgramming Completed Successfully!\r\n");
    					iap_tick = HAL_GetTick();
    				}
    				if(systick_time_diff(iap_tick) >= CONNECT_DATA_CYCLE)
    				{
    					iap_tick = HAL_GetTick();
    					iap_status = JUMP_TO_APP;
    				}
    				break;
    			case JUMP_TO_APP:
    				printf("Jump to APP\r\n");
    				jump_to_app();
    				printf("\r\nJump to APP Error!!!\r\n");
    				iap_status = WAIT_CONNECT;
    				break;
          default:
            break;
    		}
    	}
    }
    

    作者:仁杞

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32G4单片机基于Ymode协议IAP代码升级详解

    发表回复