STM32CubeIDE 使用Ymodem协议通过串口在线更新程序IAP
一:单片机的BootLoader
Bootloader部分移植ST官网的例程,官网例程的IAP有多个文件,为了移植方便我把多个文件合成了一个ymodem.c文件和ymodem.h文件

/* * ymodem.c * * Created on: Jun 6, 2023 * Author: MingYi-LZQ */ #include "main.h" #include "ymodem.h" /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ /* Private function prototypes -----------------------------------------------*/ /* Private functions ---------------------------------------------------------*/ /** * @brief Convert an Integer to a string 将整数转换为字符串 * @param p_str: The string output pointer 字符串输出指针 * @param intnum: The integer to be converted 要转换的整数 * @retval None */ void Int2Str(uint8_t *p_str, uint32_t intnum) { uint32_t i, divider = 1000000000, pos = 0, status = 0; for (i = 0; i < 10; i++) { p_str[pos++] = (intnum / divider) + 48; intnum = intnum % divider; divider /= 10; if ((p_str[pos-1] == '0') & (status == 0)) { pos = 0; } else { status++; } } } /** * @brief Convert a string to an integer 将字符串转换为整数 * @param p_inputstr: The string to be converted 要转换的字符串 * @param p_intnum: The integer value 整数值 * @retval 1: Correct * 0: Error */ uint32_t Str2Int(uint8_t *p_inputstr, uint32_t *p_intnum) { uint32_t i = 0, res = 0; uint32_t val = 0; if ((p_inputstr[0] == '0') && ((p_inputstr[1] == 'x') || (p_inputstr[1] == 'X'))) { i = 2; while ( ( i < 11 ) && ( p_inputstr[i] != '\0' ) ) { if (ISVALIDHEX(p_inputstr[i])) { val = (val << 4) + CONVERTHEX(p_inputstr[i]); } else { /* Return 0, Invalid input 返回0,无效输入 */ res = 0; break; } i++; } /* valid result 有效的结果 */ if (p_inputstr[i] == '\0') { *p_intnum = val; res = 1; } } else /* max 10-digit decimal input 最大10位十进制输入 */ { while ( ( i < 11 ) && ( res != 1 ) ) { if (p_inputstr[i] == '\0') { *p_intnum = val; /* return 1 */ res = 1; } else if (((p_inputstr[i] == 'k') || (p_inputstr[i] == 'K')) && (i > 0)) { val = val << 10; *p_intnum = val; res = 1; } else if (((p_inputstr[i] == 'm') || (p_inputstr[i] == 'M')) && (i > 0)) { val = val << 20; *p_intnum = val; res = 1; } else if (ISVALIDDEC(p_inputstr[i])) { val = val * 10 + CONVERTDEC(p_inputstr[i]); } else { /* return 0, Invalid input */ res = 0; break; } i++; } } return res; } /** * @brief Print a string on the HyperTerminal 在超级终端上打印一个字符串 * @param p_string: The string to be printed 要打印的字符串 * @retval None */ void Serial_PutString(uint8_t *p_string) { uint16_t length = 0; while (p_string[length] != '\0') { length++; } HAL_UART_Transmit(&huart1, p_string, length, TX_TIMEOUT); } /** * @brief Transmit a byte to the HyperTerminal 向超终端发送一个字节 * @param param The byte to be sent * @retval HAL_StatusTypeDef HAL_OK if OK */ HAL_StatusTypeDef Serial_PutByte( uint8_t param ) { return HAL_UART_Transmit(&huart1, ¶m, 1, TX_TIMEOUT); } /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ pFunction JumpToApplication; uint32_t JumpAddress; uint32_t FlashProtection = 0; uint8_t aFileName[FILE_NAME_LENGTH]; /* Private function prototypes -----------------------------------------------*/ void SerialDownload(void); void SerialUpload(void); /* Private functions ---------------------------------------------------------*/ /** * @brief Download a file via serial port * @param None * @retval None */ void SerialDownload(void) { uint8_t number[11] = {0}; uint32_t size = 0; COM_StatusTypeDef result; Serial_PutString((uint8_t *)"Waiting for the file to be sent ... (press 'a' to abort)\n\r"); result = Ymodem_Receive( &size ); if (result == COM_OK) { Serial_PutString((uint8_t *)"\n\n\r Programming Completed Successfully!\n\r--------------------------------\r\n Name: "); Serial_PutString(aFileName); Int2Str(number, size); Serial_PutString((uint8_t *)"\n\r Size: "); Serial_PutString(number); Serial_PutString((uint8_t *)" Bytes\r\n"); Serial_PutString((uint8_t *)"-------------------\n"); } else if (result == COM_LIMIT) { Serial_PutString((uint8_t *)"\n\n\rThe image size is higher than the allowed space memory!\n\r"); } else if (result == COM_DATA) { Serial_PutString((uint8_t *)"\n\n\rVerification failed!\n\r"); } else if (result == COM_ABORT) { Serial_PutString((uint8_t *)"\r\n\nAborted by user.\n\r"); } else { Serial_PutString((uint8_t *)"\n\rFailed to receive the file!\n\r"); } } /** * @brief Upload a file via serial port. * @param None * @retval None */ void SerialUpload(void) { uint8_t status = 0; Serial_PutString((uint8_t *)"\n\n\rSelect Receive File\n\r"); HAL_UART_Receive(&huart1, &status, 1, RX_TIMEOUT); if ( status == CRC16) { /* Transmit the flash image through ymodem protocol */ status = Ymodem_Transmit((uint8_t*)APPLICATION_ADDRESS, (const uint8_t*)"UploadedFlashImage.bin", USER_FLASH_SIZE); if (status != 0) { Serial_PutString((uint8_t *)"\n\rError Occurred while Transmitting File\n\r"); } else { Serial_PutString((uint8_t *)"\n\rFile uploaded successfully \n\r"); } } } /** * @brief Display the Main Menu on HyperTerminal * @param None * @retval None */ void Main_Menu(void) { uint8_t key = 0; Serial_PutString((uint8_t *)"\r\n======================================================================"); Serial_PutString((uint8_t *)"\r\n= (C) COPYRIGHT 2016 STMicroelectronics ="); Serial_PutString((uint8_t *)"\r\n= ="); Serial_PutString((uint8_t *)"\r\n= STM32F1xx In-Application Programming Application (Version 1.0.0) ="); Serial_PutString((uint8_t *)"\r\n= ="); Serial_PutString((uint8_t *)"\r\n= By MCD Application Team ="); Serial_PutString((uint8_t *)"\r\n======================================================================"); Serial_PutString((uint8_t *)"\r\n\r\n"); /* Test if any sector of Flash memory where user application will be loaded is write protected */ FlashProtection = FLASH_If_GetWriteProtectionStatus(); while (1) { Serial_PutString((uint8_t *)"\r\n=================== Main Menu ============================\r\n\n"); Serial_PutString((uint8_t *)" Download image to the internal Flash ----------------- 1\r\n\n"); Serial_PutString((uint8_t *)" Upload image from the internal Flash ----------------- 2\r\n\n"); Serial_PutString((uint8_t *)" Execute the loaded application ----------------------- 3\r\n\n"); if(FlashProtection != FLASHIF_PROTECTION_NONE) { Serial_PutString((uint8_t *)" Disable the write protection ------------------------- 4\r\n\n"); } else { Serial_PutString((uint8_t *)" Enable the write protection -------------------------- 4\r\n\n"); } Serial_PutString((uint8_t *)"==========================================================\r\n\n"); /* Clean the input path */ __HAL_UART_FLUSH_DRREGISTER(&huart1); /* Receive key */ HAL_UART_Receive(&huart1, &key, 1, RX_TIMEOUT); switch (key) { case '1' : /* Download user application in the Flash */ SerialDownload(); break; case '2' : /* Upload user application from the Flash */ SerialUpload(); break; case '3' : Serial_PutString((uint8_t *)"Start program execution......\r\n\n"); /* execute the new program */ JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4); /* Jump to user application */ JumpToApplication = (pFunction) JumpAddress; /* Initialize user application's Stack Pointer */ __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS); JumpToApplication(); break; case '4' : if (FlashProtection != FLASHIF_PROTECTION_NONE) { /* Disable the write protection */ if (FLASH_If_WriteProtectionConfig(FLASHIF_WRP_DISABLE) == FLASHIF_OK) { Serial_PutString((uint8_t *)"Write Protection disabled...\r\n"); Serial_PutString((uint8_t *)"System will now restart...\r\n"); /* Launch the option byte loading */ HAL_FLASH_OB_Launch(); } else { Serial_PutString((uint8_t *)"Error: Flash write un-protection failed...\r\n"); } } else { if (FLASH_If_WriteProtectionConfig(FLASHIF_WRP_ENABLE) == FLASHIF_OK) { Serial_PutString((uint8_t *)"Write Protection enabled...\r\n"); Serial_PutString((uint8_t *)"System will now restart...\r\n"); /* Launch the option byte loading */ HAL_FLASH_OB_Launch(); } else { Serial_PutString((uint8_t *)"Error: Flash write protection failed...\r\n"); } } break; default: Serial_PutString((uint8_t *)"Invalid Number ! ==> The number should be either 1, 2, 3 or 4\r"); break; } } } /** * @brief 解锁闪存进行写入访问 * @param None * @retval None */ void FLASH_If_Init(void) { /* 解锁程序内存 */ HAL_FLASH_Unlock(); /* 清除所有闪存标志 */ __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR); /* 锁定程序内存 */ HAL_FLASH_Lock(); } /** * @brief 此函数对所有用户闪存区域进行擦除a * @param start: 用户闪存区域的开始 * @retval FLASHIF_OK : 用户闪存区域成功擦除 * FLASHIF_ERASEKO : 发生了错误 */ uint32_t FLASH_If_Erase(uint32_t start) { uint32_t NbrOfPages = 0; uint32_t PageError = 0; FLASH_EraseInitTypeDef pEraseInit; HAL_StatusTypeDef status = HAL_OK; /* 解锁闪存以启用闪存控制寄存器访问 *************/ HAL_FLASH_Unlock(); /* 获取启动用户闪存区域的扇区 */ 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); /* 锁定闪存以禁用闪存控制寄存器访问(建议保护闪存免受可能不必要的操作)*********/ HAL_FLASH_Lock(); if (status != HAL_OK) { /* 页面擦除时发生错误 */ return FLASHIF_ERASEKO; } return FLASHIF_OK; } /* Public functions ---------------------------------------------------------*/ /** * @brief 此函数在闪存中写入数据缓冲区 (数据是32位对齐的). * @note 写入数据缓冲区后,检查闪存内容 * @param destination: 目标位置的起始地址 * @param p_source: 指向要写入数据的缓冲区的指针 * @param length: 数据缓冲区的长度(单位是32位字) * @retval uint32_t 0: 数据成功地写入闪存 * 1: 在闪存中写入数据时发生错误 * 2: 闪存中的写入数据不同于预期的数据 */ uint32_t FLASH_If_Write(uint32_t destination, uint32_t *p_source, uint32_t length) { uint32_t i = 0; /* 解锁闪存以启用闪存控制寄存器访问 *************/ HAL_FLASH_Unlock(); for (i = 0; (i < length) && (destination <= (USER_FLASH_END_ADDRESS-4)); i++) { /* 设备电压范围应为[2.7V至3.6V],操作将通过Word完成*/ if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, destination, *(uint32_t*)(p_source+i)) == HAL_OK) { /* 检查写入的值 */ if (*(uint32_t*)destination != *(uint32_t*)(p_source+i)) { /* 闪存内容与SRAM内容不匹配 */ return(FLASHIF_WRITINGCTRL_ERROR); } /* 增量Flash目的地地址 */ destination += 4; } else { /* 在闪存中写入数据时发生错误 */ return (FLASHIF_WRITING_ERROR); } } /* 锁定闪存以禁用闪存控制寄存器访问(建议保护闪存免受可能不必要的操作 *********/ HAL_FLASH_Lock(); return (FLASHIF_OK); } /** * @brief 返回应用程序闪存区域的写保护状态 * @param None * @retval 如果应用程序区域中的扇区是写保护的,则返回值是一个组合 可能的值 : FLASHIF_PROTECTION_WRPENABLED, FLASHIF_PROTECTION_PCROPENABLED, ... * 如果没有扇区是写保护的 FLASHIF_PROTECTION_NONE 被退回. */ uint32_t FLASH_If_GetWriteProtectionStatus(void) { uint32_t ProtectedPAGE = FLASHIF_PROTECTION_NONE; FLASH_OBProgramInitTypeDef OptionsBytesStruct; /* 解锁闪存以启用闪存控制寄存器访问*************/ HAL_FLASH_Unlock(); /* 检查用户闪存区域内是否有写保护扇区 ****/ HAL_FLASHEx_OBGetConfig(&OptionsBytesStruct); /* 锁定闪存以禁用闪存控制寄存器访问(建议保护闪存免受可能不必要的操作) *********/ HAL_FLASH_Lock(); /* 获取已写入保护的页面 ****************************************/ ProtectedPAGE = ~(OptionsBytesStruct.WRPPage) & FLASH_PAGE_TO_BE_PROTECTED; /* 检查所需页面是否已经写入保护 ***********************/ if(ProtectedPAGE != 0) { /* 用户闪存区域内的一些扇区被写入保护 */ return FLASHIF_PROTECTION_WRPENABLED; } else { /* 用户闪存区域内没有写保护扇区 */ return FLASHIF_PROTECTION_NONE; } } /** * @brief 配置用户闪存区域的写保护状态. * @param protectionstate : FLASHIF_WRP_DISABLE or FLASHIF_WRP_ENABLE the protection * @retval uint32_t FLASHIF_OK if change is applied. */ uint32_t FLASH_If_WriteProtectionConfig(uint32_t protectionstate) { uint32_t ProtectedPAGE = 0x0; FLASH_OBProgramInitTypeDef config_new, config_old; HAL_StatusTypeDef result = HAL_OK; /* 获取页面写入保护状态 ****************************************/ HAL_FLASHEx_OBGetConfig(&config_old); /* 参数表示我们是否打开或关闭保护 */ config_new.WRPState = (protectionstate == FLASHIF_WRP_ENABLE ? OB_WRPSTATE_ENABLE : OB_WRPSTATE_DISABLE); /* 我们只想修改写保护 */ config_new.OptionType = OPTIONBYTE_WRP; /* 没有读取保护,保持BOR和重置设置*/ config_new.RDPLevel = OB_RDP_LEVEL_0; config_new.USERConfig = config_old.USERConfig; /* 获取已写入保护的页面 ****************************************/ ProtectedPAGE = config_old.WRPPage | FLASH_PAGE_TO_BE_PROTECTED; /* 解锁闪存以启用闪存控制寄存器访问 *************/ HAL_FLASH_Unlock(); /* 解锁选项字节*************************************************/ HAL_FLASH_OB_Unlock(); /* 删除所有选项字节 ***********************************************/ result = HAL_FLASHEx_OBErase(); if (result == HAL_OK) { config_new.WRPPage = ProtectedPAGE; result = HAL_FLASHEx_OBProgram(&config_new); } return (result == HAL_OK ? FLASHIF_OK: FLASHIF_PROTECTION_ERRROR); } /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ #define CRC16_F /* activate the CRC16 integrity 激活CRC16 */ /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ /* @note ATTENTION - please keep this variable 32bit alligned 请保持此变量32位对齐*/ uint8_t aPacketData[PACKET_1K_SIZE + PACKET_DATA_INDEX + PACKET_TRAILER_SIZE]; /* Private function prototypes -----------------------------------------------*/ static void PrepareIntialPacket(uint8_t *p_data, const uint8_t *p_file_name, uint32_t length); static void PreparePacket(uint8_t *p_source, uint8_t *p_packet, uint8_t pkt_nr, uint32_t size_blk); static HAL_StatusTypeDef ReceivePacket(uint8_t *p_data, uint32_t *p_length, uint32_t timeout); uint16_t UpdateCRC16(uint16_t crc_in, uint8_t byte); uint16_t Cal_CRC16(const uint8_t* p_data, uint32_t size); uint8_t CalcChecksum(const uint8_t *p_data, uint32_t size); /* Private functions ---------------------------------------------------------*/ /** * @brief Receive a packet from sender * @param data * @param length * 0: end of transmission * 2: abort by sender * >0: packet length * @param timeout * @retval HAL_OK: normally return * HAL_BUSY: abort by user */ static HAL_StatusTypeDef ReceivePacket(uint8_t *p_data, uint32_t *p_length, uint32_t timeout) { uint32_t crc; uint32_t packet_size = 0; HAL_StatusTypeDef status; uint8_t char1; *p_length = 0; status = HAL_UART_Receive(&huart1, &char1, 1, timeout); if (status == HAL_OK) { switch (char1) { case SOH: packet_size = PACKET_SIZE; break; case STX: packet_size = PACKET_1K_SIZE; break; case EOT: break; case CA: if ((HAL_UART_Receive(&huart1, &char1, 1, timeout) == HAL_OK) && (char1 == CA)) { packet_size = 2; } else { status = HAL_ERROR; } break; case ABORT1: case ABORT2: status = HAL_BUSY; break; default: status = HAL_ERROR; break; } *p_data = char1; if (packet_size >= PACKET_SIZE ) { status = HAL_UART_Receive(&huart1, &p_data[PACKET_NUMBER_INDEX], packet_size + PACKET_OVERHEAD_SIZE, timeout); /* Simple packet sanity check 简单的数据包健全检查 */ if (status == HAL_OK ) { if (p_data[PACKET_NUMBER_INDEX] != ((p_data[PACKET_CNUMBER_INDEX]) ^ NEGATIVE_BYTE)) { packet_size = 0; status = HAL_ERROR; } else { /* Check packet CRC 检查分组CRC */ crc = p_data[ packet_size + PACKET_DATA_INDEX ] << 8; crc += p_data[ packet_size + PACKET_DATA_INDEX + 1 ]; if (Cal_CRC16(&p_data[PACKET_DATA_INDEX], packet_size) != crc ) { packet_size = 0; status = HAL_ERROR; } } } else { packet_size = 0; } } } *p_length = packet_size; return status; } /** * @brief Prepare the first block 准备第一个块区 * @param p_data: output buffer * @param p_file_name: name of the file to be sent * @param length: length of the file to be sent in bytes * @retval None */ static void PrepareIntialPacket(uint8_t *p_data, const uint8_t *p_file_name, uint32_t length) { uint32_t i, j = 0; uint8_t astring[10]; /* first 3 bytes are constant 前3个字节是常数 */ p_data[PACKET_START_INDEX] = SOH; p_data[PACKET_NUMBER_INDEX] = 0x00; p_data[PACKET_CNUMBER_INDEX] = 0xff; /* Filename written 文件名写的 */ for (i = 0; (p_file_name[i] != '\0') && (i < FILE_NAME_LENGTH); i++) { p_data[i + PACKET_DATA_INDEX] = p_file_name[i]; } p_data[i + PACKET_DATA_INDEX] = 0x00; /* file size written 写入文件大小*/ Int2Str (astring, length); i = i + PACKET_DATA_INDEX + 1; while (astring[j] != '\0') { p_data[i++] = astring[j++]; } /* padding with zeros 填充零 */ for (j = i; j < PACKET_SIZE + PACKET_DATA_INDEX; j++) { p_data[j] = 0; } } /** * @brief Prepare the data packet 准备数据包 * @param p_source: pointer to the data to be sent 指向要发送的数据的指针 * @param p_packet: pointer to the output buffer 指向输出缓冲区的指针 * @param pkt_nr: number of the packet * @param size_blk: length of the block to be sent in bytes 以字节为单位发送的块的长度 * @retval None */ static void PreparePacket(uint8_t *p_source, uint8_t *p_packet, uint8_t pkt_nr, uint32_t size_blk) { uint8_t *p_record; uint32_t i, size, packet_size; /* Make first three packet */ packet_size = size_blk >= PACKET_1K_SIZE ? PACKET_1K_SIZE : PACKET_SIZE; size = size_blk < packet_size ? size_blk : packet_size; if (packet_size == PACKET_1K_SIZE) { p_packet[PACKET_START_INDEX] = STX; } else { p_packet[PACKET_START_INDEX] = SOH; } p_packet[PACKET_NUMBER_INDEX] = pkt_nr; p_packet[PACKET_CNUMBER_INDEX] = (~pkt_nr); p_record = p_source; /* Filename packet has valid data */ for (i = PACKET_DATA_INDEX; i < size + PACKET_DATA_INDEX;i++) { p_packet[i] = *p_record++; } if ( size <= packet_size) { for (i = size + PACKET_DATA_INDEX; i < packet_size + PACKET_DATA_INDEX; i++) { p_packet[i] = 0x1A; /* EOF (0x1A) or 0x00 */ } } } /** * @brief Update CRC16 for input byte * @param crc_in input value * @param input byte * @retval None */ uint16_t UpdateCRC16(uint16_t crc_in, uint8_t byte) { uint32_t crc = crc_in; 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 None */ uint16_t Cal_CRC16(const uint8_t* p_data, uint32_t size) { uint32_t crc = 0; const uint8_t* dataEnd = p_data+size; while(p_data < dataEnd) crc = UpdateCRC16(crc, *p_data++); crc = UpdateCRC16(crc, 0); crc = UpdateCRC16(crc, 0); return crc&0xffffu; } /** * @brief Calculate Check sum for YModem Packet * @param p_data Pointer to input data * @param size length of input data * @retval uint8_t checksum value */ uint8_t CalcChecksum(const uint8_t *p_data, uint32_t size) { uint32_t sum = 0; const uint8_t *p_data_end = p_data + size; while (p_data < p_data_end ) { sum += *p_data++; } return (sum & 0xffu); } /* Public functions ---------------------------------------------------------*/ /** * @brief Receive a file using the ymodem protocol with CRC16. * @param p_size The size of the file. * @retval COM_StatusTypeDef result of reception/programming */ COM_StatusTypeDef Ymodem_Receive ( uint32_t *p_size ) { uint32_t i, packet_length, session_done = 0, file_done, errors = 0, session_begin = 0 ,packets_received; uint32_t flashdestination, ramsource, filesize; uint8_t *file_ptr; uint8_t file_size[FILE_SIZE_LENGTH], tmp ; COM_StatusTypeDef result = COM_OK; /* Initialize flashdestination variable */ flashdestination = APPLICATION_ADDRESS; while ((session_done == 0) && (result == COM_OK)) { packets_received = 0; file_done = 0; while ((file_done == 0) && (result == COM_OK)) { switch (ReceivePacket(aPacketData, &packet_length, DOWNLOAD_TIMEOUT)) { case HAL_OK: errors = 0; switch (packet_length) { case 2: /* Abort by sender */ Serial_PutByte(ACK); result = COM_ABORT; break; case 0: /* End of transmission */ Serial_PutByte(ACK); file_done = 1; break; default: /**************if (aPacketData[PACKET_NUMBER_INDEX] != (packets_received & 0xff))****************/ /**************:原始产生错误的判断条件//if (aPacketData[PACKET_NUMBER_INDEX] != packets_received) */ /**************:修改后的判断条件//if (aPacketData[PACKET_NUMBER_INDEX] != (packets_received & 0xff)) */ /* Normal packet 正常的包*/ if (aPacketData[PACKET_NUMBER_INDEX] != (packets_received & 0xff)) { Serial_PutByte(NAK); } else { if (packets_received == 0) { /* File name packet 文件名包*/ if (aPacketData[PACKET_DATA_INDEX] != 0) { /* File name extraction 文件名提取*/ i = 0; file_ptr = aPacketData + PACKET_DATA_INDEX; while ( (*file_ptr != 0) && (i < FILE_NAME_LENGTH)) { aFileName[i++] = *file_ptr++; } /* File size extraction 文件大小提取 */ aFileName[i++] = '\0'; i = 0; file_ptr ++; while ( (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH)) { file_size[i++] = *file_ptr++; } file_size[i++] = '\0'; Str2Int(file_size, &filesize); /* Test the size of the image to be sent 测试要发送的数据的大小 */ /* Image size is greater than Flash size 数据大小大于闪存大小*/ if (*p_size > (USER_FLASH_SIZE + 1)) { /* End session 结束传输*/ tmp = CA; HAL_UART_Transmit(&huart1, &tmp, 1, NAK_TIMEOUT); HAL_UART_Transmit(&huart1, &tmp, 1, NAK_TIMEOUT); result = COM_LIMIT; } /* erase user application area 擦除用户应用程序区域*/ FLASH_If_Erase(APPLICATION_ADDRESS); *p_size = filesize; Serial_PutByte(ACK); Serial_PutByte(CRC16); } /* File header packet is empty, end session 文件头数据包为空,结束会话 */ else { Serial_PutByte(ACK); file_done = 1; session_done = 1; break; } } else /* Data packet 数据包;*/ { ramsource = (uint32_t) & aPacketData[PACKET_DATA_INDEX]; /* Write received data in Flash 在闪存中写入接收到的数据 */ if (FLASH_If_Write(flashdestination, (uint32_t*) ramsource, packet_length/4) == FLASHIF_OK) { flashdestination += packet_length; Serial_PutByte(ACK); } else /* An error occurred while writing to Flash memory 写入闪存时发生错误*/ { /* End session */ Serial_PutByte(CA); Serial_PutByte(CA); result = COM_DATA; } } packets_received ++; session_begin = 1; } break; } break; case HAL_BUSY: /* Abort actually 实际上中止 */ Serial_PutByte(CA); Serial_PutByte(CA); result = COM_ABORT; break; default: if (session_begin > 0) { errors ++; } if (errors > MAX_ERRORS) { /* Abort communication */ Serial_PutByte(CA); Serial_PutByte(CA); } else { Serial_PutByte(CRC16); /* Ask for a packet */ } break; } } } return result; } /** * @brief Transmit a file using the ymodem protocol * @param p_buf: Address of the first byte * @param p_file_name: Name of the file sent * @param file_size: Size of the transmission * @retval COM_StatusTypeDef result of the communication */ COM_StatusTypeDef Ymodem_Transmit (uint8_t *p_buf, const uint8_t *p_file_name, uint32_t file_size) { uint32_t errors = 0, ack_recpt = 0, size = 0, pkt_size; uint8_t *p_buf_int; COM_StatusTypeDef result = COM_OK; uint32_t blk_number = 1; uint8_t a_rx_ctrl[2]; uint8_t i; #ifdef CRC16_F uint32_t temp_crc; #else /* CRC16_F */ uint8_t temp_chksum; #endif /* CRC16_F */ /* Prepare first block - header */ PrepareIntialPacket(aPacketData, p_file_name, file_size); while (( !ack_recpt ) && ( result == COM_OK )) { /* Send Packet */ HAL_UART_Transmit(&huart1, &aPacketData[PACKET_START_INDEX], PACKET_SIZE + PACKET_HEADER_SIZE, NAK_TIMEOUT); /* Send CRC or Check Sum based on CRC16_F */ #ifdef CRC16_F temp_crc = Cal_CRC16(&aPacketData[PACKET_DATA_INDEX], PACKET_SIZE); Serial_PutByte(temp_crc >> 8); Serial_PutByte(temp_crc & 0xFF); #else /* CRC16_F */ temp_chksum = CalcChecksum (&aPacketData[PACKET_DATA_INDEX], PACKET_SIZE); Serial_PutByte(temp_chksum); #endif /* CRC16_F */ /* Wait for Ack and 'C' */ if (HAL_UART_Receive(&huart1, &a_rx_ctrl[0], 1, NAK_TIMEOUT) == HAL_OK) { if (a_rx_ctrl[0] == ACK) { ack_recpt = 1; } else if (a_rx_ctrl[0] == CA) { if ((HAL_UART_Receive(&huart1, &a_rx_ctrl[0], 1, NAK_TIMEOUT) == HAL_OK) && (a_rx_ctrl[0] == CA)) { HAL_Delay( 2 ); __HAL_UART_FLUSH_DRREGISTER(&huart1); result = COM_ABORT; } } } else { errors++; } if (errors >= MAX_ERRORS) { result = COM_ERROR; } } p_buf_int = p_buf; size = file_size; /* Here 1024 bytes length is used to send the packets */ while ((size) && (result == COM_OK )) { /* Prepare next packet */ PreparePacket(p_buf_int, aPacketData, blk_number, size); ack_recpt = 0; a_rx_ctrl[0] = 0; errors = 0; /* Resend packet if NAK for few times else end of communication */ while (( !ack_recpt ) && ( result == COM_OK )) { /* Send next packet */ if (size >= PACKET_1K_SIZE) { pkt_size = PACKET_1K_SIZE; } else { pkt_size = PACKET_SIZE; } HAL_UART_Transmit(&huart1, &aPacketData[PACKET_START_INDEX], pkt_size + PACKET_HEADER_SIZE, NAK_TIMEOUT); /* Send CRC or Check Sum based on CRC16_F */ #ifdef CRC16_F temp_crc = Cal_CRC16(&aPacketData[PACKET_DATA_INDEX], pkt_size); Serial_PutByte(temp_crc >> 8); Serial_PutByte(temp_crc & 0xFF); #else /* CRC16_F */ temp_chksum = CalcChecksum (&aPacketData[PACKET_DATA_INDEX], pkt_size); Serial_PutByte(temp_chksum); #endif /* CRC16_F */ /* Wait for Ack */ if ((HAL_UART_Receive(&huart1, &a_rx_ctrl[0], 1, NAK_TIMEOUT) == HAL_OK) && (a_rx_ctrl[0] == ACK)) { ack_recpt = 1; if (size > pkt_size) { p_buf_int += pkt_size; size -= pkt_size; if (blk_number == (USER_FLASH_SIZE / PACKET_1K_SIZE)) { result = COM_LIMIT; /* boundary error */ } else { blk_number++; } } else { p_buf_int += pkt_size; size = 0; } } else { errors++; } /* Resend packet if NAK for a count of 10 else end of communication */ if (errors >= MAX_ERRORS) { result = COM_ERROR; } } } /* Sending End Of Transmission char */ ack_recpt = 0; a_rx_ctrl[0] = 0x00; errors = 0; while (( !ack_recpt ) && ( result == COM_OK )) { Serial_PutByte(EOT); /* Wait for Ack */ if (HAL_UART_Receive(&huart1, &a_rx_ctrl[0], 1, NAK_TIMEOUT) == HAL_OK) { if (a_rx_ctrl[0] == ACK) { ack_recpt = 1; } else if (a_rx_ctrl[0] == CA) { if ((HAL_UART_Receive(&huart1, &a_rx_ctrl[0], 1, NAK_TIMEOUT) == HAL_OK) && (a_rx_ctrl[0] == CA)) { HAL_Delay( 2 ); __HAL_UART_FLUSH_DRREGISTER(&huart1); result = COM_ABORT; } } } else { errors++; } if (errors >= MAX_ERRORS) { result = COM_ERROR; } } /* Empty packet sent - some terminal emulators need this to close session */ if ( result == COM_OK ) { /* Preparing an empty packet */ aPacketData[PACKET_START_INDEX] = SOH; aPacketData[PACKET_NUMBER_INDEX] = 0; aPacketData[PACKET_CNUMBER_INDEX] = 0xFF; for (i = PACKET_DATA_INDEX; i < (PACKET_SIZE + PACKET_DATA_INDEX); i++) { aPacketData [i] = 0x00; } /* Send Packet */ HAL_UART_Transmit(&huart1, &aPacketData[PACKET_START_INDEX], PACKET_SIZE + PACKET_HEADER_SIZE, NAK_TIMEOUT); /* Send CRC or Check Sum based on CRC16_F */ #ifdef CRC16_F temp_crc = Cal_CRC16(&aPacketData[PACKET_DATA_INDEX], PACKET_SIZE); Serial_PutByte(temp_crc >> 8); Serial_PutByte(temp_crc & 0xFF); #else /* CRC16_F */ temp_chksum = CalcChecksum (&aPacketData[PACKET_DATA_INDEX], PACKET_SIZE); Serial_PutByte(temp_chksum); #endif /* CRC16_F */ /* Wait for Ack and 'C' */ if (HAL_UART_Receive(&huart1, &a_rx_ctrl[0], 1, NAK_TIMEOUT) == HAL_OK) { if (a_rx_ctrl[0] == CA) { HAL_Delay( 2 ); __HAL_UART_FLUSH_DRREGISTER(&huart1); result = COM_ABORT; } } } return result; /* file transmitted successfully */ }

/* * ymodem.h * * Created on: Jun 6, 2023 * Author: MingYi-LZQ */ #ifndef INC_YMODEM_H_ #define INC_YMODEM_H_ /** * @brief Comm status structures definition */ typedef enum { COM_OK = 0x00, COM_ERROR = 0x01, COM_ABORT = 0x02, COM_TIMEOUT = 0x03, COM_DATA = 0x04, COM_LIMIT = 0x05 } COM_StatusTypeDef; /** * @} */ /* Exported constants --------------------------------------------------------*/ /* Packet structure defines */ #define PACKET_HEADER_SIZE ((uint32_t)3) #define PACKET_DATA_INDEX ((uint32_t)4) #define PACKET_START_INDEX ((uint32_t)1) #define PACKET_NUMBER_INDEX ((uint32_t)2) #define PACKET_CNUMBER_INDEX ((uint32_t)3) #define PACKET_TRAILER_SIZE ((uint32_t)2) #define PACKET_OVERHEAD_SIZE (PACKET_HEADER_SIZE + PACKET_TRAILER_SIZE - 1) #define PACKET_SIZE ((uint32_t)128) #define PACKET_1K_SIZE ((uint32_t)1024) /* /-------- Packet in IAP memory ------------------------------------------\ * | 0 | 1 | 2 | 3 | 4 | ... | n+4 | n+5 | n+6 | * |------------------------------------------------------------------------| * | unused | start | number | !num | data[0] | ... | data[n] | crc0 | crc1 | * \------------------------------------------------------------------------/ * the first byte is left unused for memory alignment reasons */ #define FILE_NAME_LENGTH ((uint32_t)64) #define FILE_SIZE_LENGTH ((uint32_t)16) #define SOH ((uint8_t)0x01) /* start of 128-byte data packet */ #define STX ((uint8_t)0x02) /* start of 1024-byte data packet */ #define EOT ((uint8_t)0x04) /* end of transmission */ #define ACK ((uint8_t)0x06) /* acknowledge */ #define NAK ((uint8_t)0x15) /* negative acknowledge */ #define CA ((uint32_t)0x18) /* two of these in succession aborts transfer */ #define CRC16 ((uint8_t)0x43) /* 'C' == 0x43, request 16-bit CRC */ #define NEGATIVE_BYTE ((uint8_t)0xFF) #define ABORT1 ((uint8_t)0x41) /* 'A' == 0x41, abort by user */ #define ABORT2 ((uint8_t)0x61) /* 'a' == 0x61, abort by user */ #define NAK_TIMEOUT ((uint32_t)0x100000) #define DOWNLOAD_TIMEOUT ((uint32_t)1000) /* One second retry delay */ #define MAX_ERRORS ((uint32_t)5) /* Exported functions ------------------------------------------------------- */ COM_StatusTypeDef Ymodem_Receive(uint32_t *p_size); COM_StatusTypeDef Ymodem_Transmit(uint8_t *p_buf, const uint8_t *p_file_name, uint32_t file_size); /* Imported variables --------------------------------------------------------*/ extern uint8_t aFileName[FILE_NAME_LENGTH]; /* Private variables ---------------------------------------------------------*/ typedef void (*pFunction)(void); extern pFunction JumpToApplication; extern uint32_t JumpAddress; /* Base address of the Flash sectors */ #define ADDR_FLASH_PAGE_0 ((uint32_t)0x08000000) /* Base @ of Page 0, 2 Kbytes */ #define ADDR_FLASH_PAGE_1 ((uint32_t)0x08000800) /* Base @ of Page 1, 2 Kbytes */ #define ADDR_FLASH_PAGE_2 ((uint32_t)0x08001000) /* Base @ of Page 2, 2 Kbytes */ #define ADDR_FLASH_PAGE_3 ((uint32_t)0x08001800) /* Base @ of Page 3, 2 Kbytes */ #define ADDR_FLASH_PAGE_4 ((uint32_t)0x08002000) /* Base @ of Page 4, 2 Kbytes */ #define ADDR_FLASH_PAGE_5 ((uint32_t)0x08002800) /* Base @ of Page 5, 2 Kbytes */ #define ADDR_FLASH_PAGE_6 ((uint32_t)0x08003000) /* Base @ of Page 6, 2 Kbytes */ #define ADDR_FLASH_PAGE_7 ((uint32_t)0x08003800) /* Base @ of Page 7, 2 Kbytes */ #define ADDR_FLASH_PAGE_8 ((uint32_t)0x08004000) /* Base @ of Page 8, 2 Kbytes */ #define ADDR_FLASH_PAGE_9 ((uint32_t)0x08004800) /* Base @ of Page 9, 2 Kbytes */ #define ADDR_FLASH_PAGE_10 ((uint32_t)0x08005000) /* Base @ of Page 10, 2 Kbytes */ #define ADDR_FLASH_PAGE_11 ((uint32_t)0x08005800) /* Base @ of Page 11, 2 Kbytes */ #define ADDR_FLASH_PAGE_12 ((uint32_t)0x08006000) /* Base @ of Page 12, 2 Kbytes */ #define ADDR_FLASH_PAGE_13 ((uint32_t)0x08006800) /* Base @ of Page 13, 2 Kbytes */ #define ADDR_FLASH_PAGE_14 ((uint32_t)0x08007000) /* Base @ of Page 14, 2 Kbytes */ #define ADDR_FLASH_PAGE_15 ((uint32_t)0x08007800) /* Base @ of Page 15, 2 Kbytes */ #define ADDR_FLASH_PAGE_16 ((uint32_t)0x08008000) /* Base @ of Page 16, 2 Kbytes */ #define ADDR_FLASH_PAGE_17 ((uint32_t)0x08008800) /* Base @ of Page 17, 2 Kbytes */ #define ADDR_FLASH_PAGE_18 ((uint32_t)0x08009000) /* Base @ of Page 18, 2 Kbytes */ #define ADDR_FLASH_PAGE_19 ((uint32_t)0x08009800) /* Base @ of Page 19, 2 Kbytes */ #define ADDR_FLASH_PAGE_20 ((uint32_t)0x0800A000) /* Base @ of Page 20, 2 Kbytes */ #define ADDR_FLASH_PAGE_21 ((uint32_t)0x0800A800) /* Base @ of Page 21, 2 Kbytes */ #define ADDR_FLASH_PAGE_22 ((uint32_t)0x0800B000) /* Base @ of Page 22, 2 Kbytes */ #define ADDR_FLASH_PAGE_23 ((uint32_t)0x0800B800) /* Base @ of Page 23, 2 Kbytes */ #define ADDR_FLASH_PAGE_24 ((uint32_t)0x0800C000) /* Base @ of Page 24, 2 Kbytes */ #define ADDR_FLASH_PAGE_25 ((uint32_t)0x0800C800) /* Base @ of Page 25, 2 Kbytes */ #define ADDR_FLASH_PAGE_26 ((uint32_t)0x0800D000) /* Base @ of Page 26, 2 Kbytes */ #define ADDR_FLASH_PAGE_27 ((uint32_t)0x0800D800) /* Base @ of Page 27, 2 Kbytes */ #define ADDR_FLASH_PAGE_28 ((uint32_t)0x0800E000) /* Base @ of Page 28, 2 Kbytes */ #define ADDR_FLASH_PAGE_29 ((uint32_t)0x0800E800) /* Base @ of Page 29, 2 Kbytes */ #define ADDR_FLASH_PAGE_30 ((uint32_t)0x0800F000) /* Base @ of Page 30, 2 Kbytes */ #define ADDR_FLASH_PAGE_31 ((uint32_t)0x0800F800) /* Base @ of Page 31, 2 Kbytes */ #define ADDR_FLASH_PAGE_32 ((uint32_t)0x08010000) /* Base @ of Page 32, 2 Kbytes */ #define ADDR_FLASH_PAGE_33 ((uint32_t)0x08010800) /* Base @ of Page 33, 2 Kbytes */ #define ADDR_FLASH_PAGE_34 ((uint32_t)0x08011000) /* Base @ of Page 34, 2 Kbytes */ #define ADDR_FLASH_PAGE_35 ((uint32_t)0x08011800) /* Base @ of Page 35, 2 Kbytes */ #define ADDR_FLASH_PAGE_36 ((uint32_t)0x08012000) /* Base @ of Page 36, 2 Kbytes */ #define ADDR_FLASH_PAGE_37 ((uint32_t)0x08012800) /* Base @ of Page 37, 2 Kbytes */ #define ADDR_FLASH_PAGE_38 ((uint32_t)0x08013000) /* Base @ of Page 38, 2 Kbytes */ #define ADDR_FLASH_PAGE_39 ((uint32_t)0x08013800) /* Base @ of Page 39, 2 Kbytes */ #define ADDR_FLASH_PAGE_40 ((uint32_t)0x08014000) /* Base @ of Page 40, 2 Kbytes */ #define ADDR_FLASH_PAGE_41 ((uint32_t)0x08014800) /* Base @ of Page 41, 2 Kbytes */ #define ADDR_FLASH_PAGE_42 ((uint32_t)0x08015000) /* Base @ of Page 42, 2 Kbytes */ #define ADDR_FLASH_PAGE_43 ((uint32_t)0x08015800) /* Base @ of Page 43, 2 Kbytes */ #define ADDR_FLASH_PAGE_44 ((uint32_t)0x08016000) /* Base @ of Page 44, 2 Kbytes */ #define ADDR_FLASH_PAGE_45 ((uint32_t)0x08016800) /* Base @ of Page 45, 2 Kbytes */ #define ADDR_FLASH_PAGE_46 ((uint32_t)0x08017000) /* Base @ of Page 46, 2 Kbytes */ #define ADDR_FLASH_PAGE_47 ((uint32_t)0x08017800) /* Base @ of Page 47, 2 Kbytes */ #define ADDR_FLASH_PAGE_48 ((uint32_t)0x08018000) /* Base @ of Page 48, 2 Kbytes */ #define ADDR_FLASH_PAGE_49 ((uint32_t)0x08018800) /* Base @ of Page 49, 2 Kbytes */ #define ADDR_FLASH_PAGE_50 ((uint32_t)0x08019000) /* Base @ of Page 50, 2 Kbytes */ #define ADDR_FLASH_PAGE_51 ((uint32_t)0x08019800) /* Base @ of Page 51, 2 Kbytes */ #define ADDR_FLASH_PAGE_52 ((uint32_t)0x0801A000) /* Base @ of Page 52, 2 Kbytes */ #define ADDR_FLASH_PAGE_53 ((uint32_t)0x0801A800) /* Base @ of Page 53, 2 Kbytes */ #define ADDR_FLASH_PAGE_54 ((uint32_t)0x0801B000) /* Base @ of Page 54, 2 Kbytes */ #define ADDR_FLASH_PAGE_55 ((uint32_t)0x0801B800) /* Base @ of Page 55, 2 Kbytes */ #define ADDR_FLASH_PAGE_56 ((uint32_t)0x0801C000) /* Base @ of Page 56, 2 Kbytes */ #define ADDR_FLASH_PAGE_57 ((uint32_t)0x0801C800) /* Base @ of Page 57, 2 Kbytes */ #define ADDR_FLASH_PAGE_58 ((uint32_t)0x0801D000) /* Base @ of Page 58, 2 Kbytes */ #define ADDR_FLASH_PAGE_59 ((uint32_t)0x0801D800) /* Base @ of Page 59, 2 Kbytes */ #define ADDR_FLASH_PAGE_60 ((uint32_t)0x0801E000) /* Base @ of Page 60, 2 Kbytes */ #define ADDR_FLASH_PAGE_61 ((uint32_t)0x0801E800) /* Base @ of Page 61, 2 Kbytes */ #define ADDR_FLASH_PAGE_62 ((uint32_t)0x0801F000) /* Base @ of Page 62, 2 Kbytes */ #define ADDR_FLASH_PAGE_63 ((uint32_t)0x0801F800) /* Base @ of Page 63, 2 Kbytes */ #define ADDR_FLASH_PAGE_64 ((uint32_t)0x08020000) /* Base @ of Page 64, 2 Kbytes */ #define ADDR_FLASH_PAGE_65 ((uint32_t)0x08020800) /* Base @ of Page 65, 2 Kbytes */ #define ADDR_FLASH_PAGE_66 ((uint32_t)0x08021000) /* Base @ of Page 66, 2 Kbytes */ #define ADDR_FLASH_PAGE_67 ((uint32_t)0x08021800) /* Base @ of Page 67, 2 Kbytes */ #define ADDR_FLASH_PAGE_68 ((uint32_t)0x08022000) /* Base @ of Page 68, 2 Kbytes */ #define ADDR_FLASH_PAGE_69 ((uint32_t)0x08022800) /* Base @ of Page 69, 2 Kbytes */ #define ADDR_FLASH_PAGE_70 ((uint32_t)0x08023000) /* Base @ of Page 70, 2 Kbytes */ #define ADDR_FLASH_PAGE_71 ((uint32_t)0x08023800) /* Base @ of Page 71, 2 Kbytes */ #define ADDR_FLASH_PAGE_72 ((uint32_t)0x08024000) /* Base @ of Page 72, 2 Kbytes */ #define ADDR_FLASH_PAGE_73 ((uint32_t)0x08024800) /* Base @ of Page 73, 2 Kbytes */ #define ADDR_FLASH_PAGE_74 ((uint32_t)0x08025000) /* Base @ of Page 74, 2 Kbytes */ #define ADDR_FLASH_PAGE_75 ((uint32_t)0x08025800) /* Base @ of Page 75, 2 Kbytes */ #define ADDR_FLASH_PAGE_76 ((uint32_t)0x08026000) /* Base @ of Page 76, 2 Kbytes */ #define ADDR_FLASH_PAGE_77 ((uint32_t)0x08026800) /* Base @ of Page 77, 2 Kbytes */ #define ADDR_FLASH_PAGE_78 ((uint32_t)0x08027000) /* Base @ of Page 78, 2 Kbytes */ #define ADDR_FLASH_PAGE_79 ((uint32_t)0x08027800) /* Base @ of Page 79, 2 Kbytes */ #define ADDR_FLASH_PAGE_80 ((uint32_t)0x08028000) /* Base @ of Page 80, 2 Kbytes */ #define ADDR_FLASH_PAGE_81 ((uint32_t)0x08028800) /* Base @ of Page 81, 2 Kbytes */ #define ADDR_FLASH_PAGE_82 ((uint32_t)0x08029000) /* Base @ of Page 82, 2 Kbytes */ #define ADDR_FLASH_PAGE_83 ((uint32_t)0x08029800) /* Base @ of Page 83, 2 Kbytes */ #define ADDR_FLASH_PAGE_84 ((uint32_t)0x0802A000) /* Base @ of Page 84, 2 Kbytes */ #define ADDR_FLASH_PAGE_85 ((uint32_t)0x0802A800) /* Base @ of Page 85, 2 Kbytes */ #define ADDR_FLASH_PAGE_86 ((uint32_t)0x0802B000) /* Base @ of Page 86, 2 Kbytes */ #define ADDR_FLASH_PAGE_87 ((uint32_t)0x0802B800) /* Base @ of Page 87, 2 Kbytes */ #define ADDR_FLASH_PAGE_88 ((uint32_t)0x0802C000) /* Base @ of Page 88, 2 Kbytes */ #define ADDR_FLASH_PAGE_89 ((uint32_t)0x0802C800) /* Base @ of Page 89, 2 Kbytes */ #define ADDR_FLASH_PAGE_90 ((uint32_t)0x0802D000) /* Base @ of Page 90, 2 Kbytes */ #define ADDR_FLASH_PAGE_91 ((uint32_t)0x0802D800) /* Base @ of Page 91, 2 Kbytes */ #define ADDR_FLASH_PAGE_92 ((uint32_t)0x0802E000) /* Base @ of Page 92, 2 Kbytes */ #define ADDR_FLASH_PAGE_93 ((uint32_t)0x0802E800) /* Base @ of Page 93, 2 Kbytes */ #define ADDR_FLASH_PAGE_94 ((uint32_t)0x0802F000) /* Base @ of Page 94, 2 Kbytes */ #define ADDR_FLASH_PAGE_95 ((uint32_t)0x0802F800) /* Base @ of Page 95, 2 Kbytes */ #define ADDR_FLASH_PAGE_96 ((uint32_t)0x08030000) /* Base @ of Page 96, 2 Kbytes */ #define ADDR_FLASH_PAGE_97 ((uint32_t)0x08030800) /* Base @ of Page 97, 2 Kbytes */ #define ADDR_FLASH_PAGE_98 ((uint32_t)0x08031000) /* Base @ of Page 98, 2 Kbytes */ #define ADDR_FLASH_PAGE_99 ((uint32_t)0x08031800) /* Base @ of Page 99, 2 Kbytes */ #define ADDR_FLASH_PAGE_100 ((uint32_t)0x08032000) /* Base @ of Page 100, 2 Kbytes */ #define ADDR_FLASH_PAGE_101 ((uint32_t)0x08032800) /* Base @ of Page 101, 2 Kbytes */ #define ADDR_FLASH_PAGE_102 ((uint32_t)0x08033000) /* Base @ of Page 102, 2 Kbytes */ #define ADDR_FLASH_PAGE_103 ((uint32_t)0x08033800) /* Base @ of Page 103, 2 Kbytes */ #define ADDR_FLASH_PAGE_104 ((uint32_t)0x08034000) /* Base @ of Page 104, 2 Kbytes */ #define ADDR_FLASH_PAGE_105 ((uint32_t)0x08034800) /* Base @ of Page 105, 2 Kbytes */ #define ADDR_FLASH_PAGE_106 ((uint32_t)0x08035000) /* Base @ of Page 106, 2 Kbytes */ #define ADDR_FLASH_PAGE_107 ((uint32_t)0x08035800) /* Base @ of Page 107, 2 Kbytes */ #define ADDR_FLASH_PAGE_108 ((uint32_t)0x08036000) /* Base @ of Page 108, 2 Kbytes */ #define ADDR_FLASH_PAGE_109 ((uint32_t)0x08036800) /* Base @ of Page 109, 2 Kbytes */ #define ADDR_FLASH_PAGE_110 ((uint32_t)0x08037000) /* Base @ of Page 110, 2 Kbytes */ #define ADDR_FLASH_PAGE_111 ((uint32_t)0x08037800) /* Base @ of Page 111, 2 Kbytes */ #define ADDR_FLASH_PAGE_112 ((uint32_t)0x08038000) /* Base @ of Page 112, 2 Kbytes */ #define ADDR_FLASH_PAGE_113 ((uint32_t)0x08038800) /* Base @ of Page 113, 2 Kbytes */ #define ADDR_FLASH_PAGE_114 ((uint32_t)0x08039000) /* Base @ of Page 114, 2 Kbytes */ #define ADDR_FLASH_PAGE_115 ((uint32_t)0x08039800) /* Base @ of Page 115, 2 Kbytes */ #define ADDR_FLASH_PAGE_116 ((uint32_t)0x0803A000) /* Base @ of Page 116, 2 Kbytes */ #define ADDR_FLASH_PAGE_117 ((uint32_t)0x0803A800) /* Base @ of Page 117, 2 Kbytes */ #define ADDR_FLASH_PAGE_118 ((uint32_t)0x0803B000) /* Base @ of Page 118, 2 Kbytes */ #define ADDR_FLASH_PAGE_119 ((uint32_t)0x0803B800) /* Base @ of Page 119, 2 Kbytes */ #define ADDR_FLASH_PAGE_120 ((uint32_t)0x0803C000) /* Base @ of Page 120, 2 Kbytes */ #define ADDR_FLASH_PAGE_121 ((uint32_t)0x0803C800) /* Base @ of Page 121, 2 Kbytes */ #define ADDR_FLASH_PAGE_122 ((uint32_t)0x0803D000) /* Base @ of Page 122, 2 Kbytes */ #define ADDR_FLASH_PAGE_123 ((uint32_t)0x0803D800) /* Base @ of Page 123, 2 Kbytes */ #define ADDR_FLASH_PAGE_124 ((uint32_t)0x0803E000) /* Base @ of Page 124, 2 Kbytes */ #define ADDR_FLASH_PAGE_125 ((uint32_t)0x0803E800) /* Base @ of Page 125, 2 Kbytes */ #define ADDR_FLASH_PAGE_126 ((uint32_t)0x0803F000) /* Base @ of Page 126, 2 Kbytes */ #define ADDR_FLASH_PAGE_127 ((uint32_t)0x0803F800) /* Base @ of Page 127, 2 Kbytes */ /* Error code */ enum { FLASHIF_OK = 0, FLASHIF_ERASEKO, FLASHIF_WRITINGCTRL_ERROR, FLASHIF_WRITING_ERROR, FLASHIF_PROTECTION_ERRROR }; /* protection type */ enum{ FLASHIF_PROTECTION_NONE = 0, FLASHIF_PROTECTION_PCROPENABLED = 0x1, FLASHIF_PROTECTION_WRPENABLED = 0x2, FLASHIF_PROTECTION_RDPENABLED = 0x4, }; /* protection update */ enum { FLASHIF_WRP_ENABLE, FLASHIF_WRP_DISABLE }; /* 定义从哪里加载用户应用程序的地址 Note: 此区域为IAP代码保留 */ #define FLASH_PAGE_STEP FLASH_PAGE_SIZE /* Size of page : 2 Kbytes */ #define APPLICATION_ADDRESS (uint32_t)0x8008000 /* Start user code address: ADDR_FLASH_PAGE_8 */ //文件大小64k /* Notable Flash addresses */ #define USER_FLASH_END_ADDRESS 0x8040000 /* 定义用户应用程序大小 */ //64k #define USER_FLASH_SIZE ((uint32_t)0x00010000) /* Small default template application */ //已修改 默认 3 /* 定义表示可以写入保护的用户闪存区域的位图(检查仅限于第8-39页). */ #define FLASH_PAGE_TO_BE_PROTECTED (OB_WRP_PAGES8TO9 | OB_WRP_PAGES10TO11 | OB_WRP_PAGES12TO13 | OB_WRP_PAGES14TO15 | \ OB_WRP_PAGES16TO17 | OB_WRP_PAGES18TO19 | OB_WRP_PAGES20TO21 | OB_WRP_PAGES22TO23 | \ OB_WRP_PAGES24TO25 | OB_WRP_PAGES26TO27 | OB_WRP_PAGES28TO29 | OB_WRP_PAGES30TO31 | \ OB_WRP_PAGES32TO33 | OB_WRP_PAGES34TO35 | OB_WRP_PAGES36TO37 | OB_WRP_PAGES38TO39 | \ OB_WRP_PAGES40TO41 | OB_WRP_PAGES42TO43 | OB_WRP_PAGES44TO45 | OB_WRP_PAGES46TO47 | \ OB_WRP_PAGES48TO49 | OB_WRP_PAGES50TO51 | OB_WRP_PAGES52TO53 | OB_WRP_PAGES54TO55 | \ OB_WRP_PAGES56TO57 | OB_WRP_PAGES58TO59 | OB_WRP_PAGES60TO61) /* Exported macro ------------------------------------------------------------*/ /* ABSoulute value */ #define ABS_RETURN(x,y) (((x) < (y)) ? (y) : (x)) /* 从加载用户程序的位置获取扇区数 */ #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)) /* Exported functions ------------------------------------------------------- */ void FLASH_If_Init(void); uint32_t FLASH_If_Erase(uint32_t StartSector); uint32_t FLASH_If_GetWriteProtectionStatus(void); uint32_t FLASH_If_Write(uint32_t destination, uint32_t *p_source, uint32_t length); uint32_t FLASH_If_WriteProtectionConfig(uint32_t protectionstate); /* Exported types ------------------------------------------------------------*/ /* Exported constants --------------------------------------------------------*/ /* Constants used by Serial Command Line Mode */ #define TX_TIMEOUT ((uint32_t)100) #define RX_TIMEOUT HAL_MAX_DELAY /* Exported macro ------------------------------------------------------------*/ #define IS_CAP_LETTER(c) (((c) >= 'A') && ((c) <= 'F')) #define IS_LC_LETTER(c) (((c) >= 'a') && ((c) <= 'f')) #define IS_09(c) (((c) >= '0') && ((c) <= '9')) #define ISVALIDHEX(c) (IS_CAP_LETTER(c) || IS_LC_LETTER(c) || IS_09(c)) #define ISVALIDDEC(c) IS_09(c) #define CONVERTDEC(c) (c - '0') #define CONVERTHEX_ALPHA(c) (IS_CAP_LETTER(c) ? ((c) - 'A'+10) : ((c) - 'a'+10)) #define CONVERTHEX(c) (IS_09(c) ? ((c) - '0') : CONVERTHEX_ALPHA(c)) /* Exported functions ------------------------------------------------------- */ void Int2Str(uint8_t *p_str, uint32_t intnum); uint32_t Str2Int(uint8_t *inputstr, uint32_t *intnum); void Serial_PutString(uint8_t *p_string); HAL_StatusTypeDef Serial_PutByte(uint8_t param); /* Imported variables --------------------------------------------------------*/ extern uint8_t aFileName[FILE_NAME_LENGTH]; /* Private variables ---------------------------------------------------------*/ typedef void (*pFunction)(void); /* Exported types ------------------------------------------------------------*/ /* Exported constants --------------------------------------------------------*/ /* Exported macro ------------------------------------------------------------*/ /* Exported functions ------------------------------------------------------- */ void Main_Menu(void); #endif /* INC_YMODEM_H_ */
在ymodem.h文件里需要指定用户APP起始地址APPLICATION_ADDRESS,用户结束地址USER_FLASH_END_ADDRESS,以及用户应用程序大小。
bootloader的Flash地址是从0x8000000-0x8007ffff 总共占用Flash大小32K.
app的Flash地址是从0x8008000-0x803ffff 总共占用Flash大小256K-32K = 224K = 0x00038000
添加完这两个文件后,只需在main.c文件添加使用即可。

/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2023 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "ymodem.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ int __io_putchar(int ch) { HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 1000); return ch; } /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ if (1) { /* Execute the IAP driver in order to reprogram the Flash */ FLASH_If_Init(); /* Display main menu */ Main_Menu(); } /* Keep the user application running */ else { /* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */ if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000) { /* Jump to user application */ JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4); JumpToApplication = (pFunction) JumpAddress; /* Initialize user application's Stack Pointer */ __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS); JumpToApplication(); } } /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI48; RCC_OscInitStruct.HSI48State = RCC_HSI48_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI48; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK) { Error_Handler(); } PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1; PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK1; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */
二:单片机的APP
APP部分需要修改两处地方
F0系列的单片机:
在STM32F0XXXXX_FLASH.ld文件中修改用户APP的RAM和FLASH起始地址ORIGIN以及LENGTH
在main入口重映射中断向量表
//F0系列的单片机没有SCB->VTOR这个寄存器,使用如下方法重映射中断向量表 memcpy((void*)0x20000000, (void*)0x08008000, 0xBC); __HAL_SYSCFG_REMAPMEMORY_SRAM();
拷贝长度0xBC的计算方法在startup_stm32f091rctx.s文件中有(171-125+1)个word中断向量,每个word四个字节,所以拷贝长度为(171-125+1)*4=188 = 0xBC
非F0系列的单片机:
只需修改FLASH的ORIGIN以及LENGTH即可
在main入口添加一句SCB->VTOR = APPLICATION_ADDRESS;重映射中断向量表
三:通过上位机发送文件更新单片机程序
Ymodem协议上位机源码如下
C++源码

#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <windows.h> #include <thread> bool IsStopPrintfReceive = false; class SerialPort { public: HANDLE hSerial; //构造函数,打开串口并设置参数 SerialPort(const char* portName) { std::string fullPortName = "\\\\.\\" + std::string(portName); hSerial = CreateFileA( fullPortName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hSerial == INVALID_HANDLE_VALUE) { std::cerr << "Error opening serial port\n"; exit(1); } // 初始化串口参数 DCB dcbSerialParams = { 0 }; COMMTIMEOUTS timeouts = { 0 }; dcbSerialParams.DCBlength = sizeof(dcbSerialParams); if (!GetCommState(hSerial, &dcbSerialParams)) { std::cerr << "Error getting serial port state\n"; CloseHandle(hSerial); exit(1); } // 设置串口参数 dcbSerialParams.BaudRate = CBR_115200; // 波特率为115200 dcbSerialParams.ByteSize = 8; // 数据位为8位 dcbSerialParams.StopBits = ONESTOPBIT; // 停止位为1位 dcbSerialParams.Parity = NOPARITY; // 无校验位 if (!SetCommState(hSerial, &dcbSerialParams)) { std::cerr << "Error setting serial port state\n"; CloseHandle(hSerial); exit(1); } // 设置超时时间 timeouts.ReadIntervalTimeout = 50; // 读取数据之间的间隔时间 timeouts.ReadTotalTimeoutConstant = 50; // 读取数据的固定超时时间 timeouts.ReadTotalTimeoutMultiplier = 10; // 读取数据的超时时间倍数 timeouts.WriteTotalTimeoutConstant = 50; // 写入数据的固定超时时间 timeouts.WriteTotalTimeoutMultiplier = 10; // 写入数据的超时时间倍数 if (!SetCommTimeouts(hSerial, &timeouts)) { std::cerr << "Error setting serial port timeouts\n"; CloseHandle(hSerial); exit(1); } } //析构函数,关闭串口 ~SerialPort() { CloseHandle(hSerial); } /** * @brief 重置串口超时时间 * * @param timeout 读写超时时间 * @return true 重置成功 * @return false 重置失败 */ bool resetTimeout(DWORD timeout) { COMMTIMEOUTS timeouts = { 0 }; timeouts.ReadIntervalTimeout = 50; // 读取数据之间的间隔时间 timeouts.ReadTotalTimeoutConstant = timeout; // 读取数据的固定超时时间 timeouts.ReadTotalTimeoutMultiplier = 10; // 读取数据的超时时间倍数 timeouts.WriteTotalTimeoutConstant = timeout; // 写入数据的固定超时时间 timeouts.WriteTotalTimeoutMultiplier = 10; // 写入数据的超时时间倍数 if (!SetCommTimeouts(hSerial, &timeouts)) { std::cerr << "Error setting serial port timeouts\n"; CloseHandle(hSerial); exit(1); } return true; } //向串口写入数据 bool write(const char* data) { DWORD bytes_written; if (!WriteFile(hSerial, data, strlen(data), &bytes_written, NULL)) { std::cerr << "Error writing to serial port\n"; return false; } return true; } bool write(const uint8_t data) { DWORD bytes_written; if (!WriteFile(hSerial, &data, 1, &bytes_written, NULL)) { std::cerr << "Error writing to serial port\n"; return false; } return true; } /** * @brief 向串口写入数据 * * @param data 要写入的数据 * @param start_index 数据的起始位置 * @param length 数据的长度 * @param timeout 写入数据的超时时间 * @return true 写入成功 * @return false 写入失败 */ bool write(const uint8_t* data, uint32_t start_index, uint32_t length, DWORD timeout) { DWORD bytes_written; COMMTIMEOUTS timeouts = { 0 }; timeouts.WriteTotalTimeoutConstant = timeout; // 写入数据的固定超时时间 timeouts.WriteTotalTimeoutMultiplier = 10; // 写入数据的超时时间倍数 if (!SetCommTimeouts(hSerial, &timeouts)) { std::cerr << "Error setting serial port timeouts\n"; CloseHandle(hSerial); exit(1); } if (!WriteFile(hSerial, data + start_index, length, &bytes_written, NULL)) { std::cerr << "Error writing to serial port\n"; return false; } return true; } //从串口读取数据 bool read(char* buffer, DWORD buffer_size, DWORD& bytes_read) { if (!ReadFile(hSerial, buffer, buffer_size, &bytes_read, NULL)) { std::cerr << "Error reading from serial port\n"; return false; } return true; } //从串口读取数据 bool read(uint8_t* buffer, uint32_t length, DWORD timeout, DWORD& bytes_read) { COMMTIMEOUTS timeouts = { 0 }; timeouts.ReadIntervalTimeout = MAXDWORD; timeouts.ReadTotalTimeoutConstant = timeout; timeouts.ReadTotalTimeoutMultiplier = 10; if (!SetCommTimeouts(hSerial, &timeouts)) { std::cerr << "Error setting serial port timeouts\n"; CloseHandle(hSerial); exit(1); } if (!ReadFile(hSerial, buffer, length, &bytes_read, NULL)) { std::cerr << "Error reading from serial port\n"; return false; } return true; } }; /** * @brief Convert an Integer to a string 将整数转换为字符串 * @param p_str: The string output pointer 字符串输出指针 * @param intnum: The integer to be converted 要转换的整数 * @retval None */ void Int2Str(uint8_t* p_str, uint32_t intnum) { uint32_t i, divider = 1000000000, pos = 0, status = 0; for (i = 0; i < 10; i++) { p_str[pos++] = (intnum / divider) + 48; intnum = intnum % divider; divider /= 10; if ((p_str[pos - 1] == '0') & (status == 0)) { pos = 0; } else { status++; } } } /** * @brief Update CRC16 for input byte * @param crc_in input value * @param input byte * @retval None */ uint16_t UpdateCRC16(uint16_t crc_in, uint8_t byte) { uint32_t crc = crc_in; 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 None */ uint16_t Cal_CRC16(const uint8_t* p_data, uint32_t size) { uint32_t crc = 0; const uint8_t* dataEnd = p_data + size; while (p_data < dataEnd) crc = UpdateCRC16(crc, *p_data++); crc = UpdateCRC16(crc, 0); crc = UpdateCRC16(crc, 0); return crc & 0xffffu; } /** * @brief Comm status structures definition */ typedef enum { COM_OK = 0x00, COM_ERROR = 0x01, COM_ABORT = 0x02, COM_TIMEOUT = 0x03, COM_DATA = 0x04, COM_LIMIT = 0x05 } COM_StatusTypeDef; /* Packet structure defines */ #define PACKET_HEADER_SIZE ((uint32_t)3) #define PACKET_DATA_INDEX ((uint32_t)4) #define PACKET_START_INDEX ((uint32_t)1) #define PACKET_NUMBER_INDEX ((uint32_t)2) #define PACKET_CNUMBER_INDEX ((uint32_t)3) #define PACKET_TRAILER_SIZE ((uint32_t)2) #define PACKET_OVERHEAD_SIZE (PACKET_HEADER_SIZE + PACKET_TRAILER_SIZE - 1) #define PACKET_SIZE ((uint32_t)128) #define PACKET_1K_SIZE ((uint32_t)1024) /* /-------- Packet in IAP memory ------------------------------------------\ * | 0 | 1 | 2 | 3 | 4 | ... | n+4 | n+5 | n+6 | * |------------------------------------------------------------------------| * | unused | start | number | !num | data[0] | ... | data[n] | crc0 | crc1 | * \------------------------------------------------------------------------/ * the first byte is left unused for memory alignment reasons */ #define FILE_NAME_LENGTH ((uint32_t)64) #define FILE_SIZE_LENGTH ((uint32_t)16) #define SOH ((uint8_t)0x01) /* start of 128-byte data packet */ #define STX ((uint8_t)0x02) /* start of 1024-byte data packet */ #define EOT ((uint8_t)0x04) /* end of transmission */ #define ACK ((uint8_t)0x06) /* acknowledge */ #define NAK ((uint8_t)0x15) /* negative acknowledge */ #define CA ((uint32_t)0x18) /* two of these in succession aborts transfer */ #define CRC16 ((uint8_t)0x43) /* 'C' == 0x43, request 16-bit CRC */ #define NEGATIVE_BYTE ((uint8_t)0xFF) #define ABORT1 ((uint8_t)0x41) /* 'A' == 0x41, abort by user */ #define ABORT2 ((uint8_t)0x61) /* 'a' == 0x61, abort by user */ #define NAK_TIMEOUT ((uint32_t)0x100000) #define DOWNLOAD_TIMEOUT ((uint32_t)1000) /* One second retry delay */ #define MAX_ERRORS ((uint32_t)5) #define USER_FLASH_SIZE ((uint32_t)0x00010000) /* Small default template application */ /** * @brief Prepare the first block * @param p_data: output buffer * @param p_file_name: name of the file to be sent * @param length: length of the file to be sent in bytes * @retval None */ static void PrepareIntialPacket(uint8_t* p_data, const uint8_t* p_file_name, uint32_t length) { uint32_t i, j = 0; uint8_t astring[10] = {0}; /* first 3 bytes are constant */ p_data[PACKET_START_INDEX] = SOH; p_data[PACKET_NUMBER_INDEX] = 0x00; p_data[PACKET_CNUMBER_INDEX] = 0xff; /* Filename written */ for (i = 0; (p_file_name[i] != '\0') && (i < FILE_NAME_LENGTH); i++) { p_data[i + PACKET_DATA_INDEX] = p_file_name[i]; } p_data[i + PACKET_DATA_INDEX] = 0x00; /* file size written */ Int2Str(astring, length); i = i + PACKET_DATA_INDEX + 1; while (astring[j] != '\0') { p_data[i++] = astring[j++]; } /* padding with zeros */ for (j = i; j < PACKET_SIZE + PACKET_DATA_INDEX; j++) { p_data[j] = 0; } } /** * @brief Prepare the data packet * @param p_source: pointer to the data to be sent * @param p_packet: pointer to the output buffer * @param pkt_nr: number of the packet * @param size_blk: length of the block to be sent in bytes * @retval None */ static void PreparePacket(uint8_t* p_source, uint8_t* p_packet, uint8_t pkt_nr, uint32_t size_blk) { uint8_t* p_record; uint32_t i, size, packet_size; /* Make first three packet */ packet_size = size_blk >= PACKET_1K_SIZE ? PACKET_1K_SIZE : PACKET_SIZE; size = size_blk < packet_size ? size_blk : packet_size; if (packet_size == PACKET_1K_SIZE) { p_packet[PACKET_START_INDEX] = STX; } else { p_packet[PACKET_START_INDEX] = SOH; } p_packet[PACKET_NUMBER_INDEX] = pkt_nr; p_packet[PACKET_CNUMBER_INDEX] = (~pkt_nr); p_record = p_source; /* Filename packet has valid data */ for (i = PACKET_DATA_INDEX; i < size + PACKET_DATA_INDEX; i++) { p_packet[i] = *p_record++; } if (size <= packet_size) { for (i = size + PACKET_DATA_INDEX; i < packet_size + PACKET_DATA_INDEX; i++) { p_packet[i] = 0x1A; /* EOF (0x1A) or 0x00 */ } } } /* @note ATTENTION - please keep this variable 32bit alligned 请保持此变量32位对齐*/ uint8_t aPacketData[PACKET_1K_SIZE + PACKET_DATA_INDEX + PACKET_TRAILER_SIZE]; void send_file(SerialPort* serial, const char* file_path) { uint32_t errors = 0, ack_recpt = 0, size = 0, pkt_size; uint8_t* p_buf_int; COM_StatusTypeDef result = COM_OK; uint32_t blk_number = 1; uint8_t a_rx_ctrl[2]; uint8_t i; uint32_t temp_crc; uint8_t* p_file_name; uint32_t file_size; DWORD bytes_read; FILE* file = fopen(file_path, "rb"); if (!file) { std::cerr << "Error opening file\n"; return; } //提取file_path路径里的文件名并读取文件的大小 std::string path(file_path); std::string filename = path.substr(path.find_last_of("\\/") + 1); uint8_t files[128]; strcpy((char*)files, filename.c_str()); p_file_name = files; fseek(file, 0, SEEK_END); file_size = ftell(file); fseek(file, 0, SEEK_SET); std::cout << "Sending file: " << filename << ", size: " << file_size << " bytes\n"; //uint8_t data[32000]; //fread(data, 1, file_size, file); p_buf_int = (uint8_t*)malloc(file_size * sizeof(byte)); fread(p_buf_int, 1, file_size, file); fclose(file); /* Prepare first block - header */ PrepareIntialPacket(aPacketData, p_file_name, file_size); while ((!ack_recpt) && (result == COM_OK)) { /* Send Packet */ serial->write(aPacketData, PACKET_START_INDEX, PACKET_SIZE + PACKET_HEADER_SIZE, NAK_TIMEOUT); /* Send CRC or Check Sum based on CRC16_F */ temp_crc = Cal_CRC16(&aPacketData[PACKET_DATA_INDEX], PACKET_SIZE); serial->write((uint8_t)(temp_crc >> 8)); serial->write((uint8_t)(temp_crc & 0xFF)); /* Wait for Ack and 'C' */ if (serial->read(&a_rx_ctrl[0], 1, NAK_TIMEOUT, bytes_read) == true) { if (a_rx_ctrl[0] == ACK) { ack_recpt = 1; } else if (a_rx_ctrl[0] == CA) { if ((serial->read(&a_rx_ctrl[0], 1, NAK_TIMEOUT, bytes_read) == true) && (a_rx_ctrl[0] == CA)) { Sleep(2); PurgeComm(serial->hSerial, PURGE_RXCLEAR); result = COM_ABORT; } } } else { errors++; } if (errors >= MAX_ERRORS) { result = COM_ERROR; } } //p_buf_int = data; size = file_size; /* Here 1024 bytes length is used to send the packets */ while ((size) && (result == COM_OK)) { /* Prepare next packet */ PreparePacket(p_buf_int, aPacketData, blk_number, size); ack_recpt = 0; a_rx_ctrl[0] = 0; errors = 0; /* Resend packet if NAK for few times else end of communication */ while ((!ack_recpt) && (result == COM_OK)) { /* Send next packet */ if (size >= PACKET_1K_SIZE) { pkt_size = PACKET_1K_SIZE; } else { pkt_size = PACKET_SIZE; } /* Send CRC or Check Sum based on CRC16_F */ temp_crc = Cal_CRC16(&aPacketData[PACKET_DATA_INDEX], pkt_size); aPacketData[pkt_size + PACKET_HEADER_SIZE + PACKET_START_INDEX + 0] = (uint8_t)(temp_crc >> 8); aPacketData[pkt_size + PACKET_HEADER_SIZE + PACKET_START_INDEX + 1] = (uint8_t)(temp_crc & 0xFF); serial->write(aPacketData, PACKET_START_INDEX, pkt_size + PACKET_HEADER_SIZE + 2, NAK_TIMEOUT); PurgeComm(serial->hSerial, PURGE_RXCLEAR); uint8_t progress = (uint8_t)((float)(file_size - size) / file_size * 100); printf("current progress:%d%%\n", progress); /* Wait for Ack */ if ((serial->read(&a_rx_ctrl[0], 1, NAK_TIMEOUT, bytes_read) == true)) { if (a_rx_ctrl[0] == ACK) { ack_recpt = 1; if (size > pkt_size) { p_buf_int += pkt_size; size -= pkt_size; if (blk_number == (USER_FLASH_SIZE / PACKET_1K_SIZE)) { result = COM_LIMIT; /* boundary error */ } else { blk_number++; } } else { p_buf_int += pkt_size; size = 0; } } } else { errors++; } /* Resend packet if NAK for a count of 10 else end of communication */ if (errors >= MAX_ERRORS) { result = COM_ERROR; } } } /* Sending End Of Transmission char */ ack_recpt = 0; a_rx_ctrl[0] = 0x00; errors = 0; while ((!ack_recpt) && (result == COM_OK)) { serial->write(EOT); /* Wait for Ack */ if (serial->read(&a_rx_ctrl[0], 1, NAK_TIMEOUT, bytes_read) == true) { if (a_rx_ctrl[0] == ACK) { ack_recpt = 1; } else if (a_rx_ctrl[0] == CA) { if ((serial->read(&a_rx_ctrl[0], 1, NAK_TIMEOUT, bytes_read) == true) && (a_rx_ctrl[0] == CA)) { Sleep(2); PurgeComm(serial->hSerial, PURGE_RXCLEAR); result = COM_ABORT; } } } else { errors++; } if (errors >= MAX_ERRORS) { result = COM_ERROR; } } /* Empty packet sent - some terminal emulators need this to close session */ if (result == COM_OK) { /* Preparing an empty packet */ aPacketData[PACKET_START_INDEX] = SOH; aPacketData[PACKET_NUMBER_INDEX] = 0; aPacketData[PACKET_CNUMBER_INDEX] = 0xFF; for (i = PACKET_DATA_INDEX; i < (PACKET_SIZE + PACKET_DATA_INDEX); i++) { aPacketData[i] = 0x00; } /* Send Packet */ serial->write(aPacketData, PACKET_START_INDEX, PACKET_SIZE + PACKET_HEADER_SIZE, NAK_TIMEOUT); /* Send CRC or Check Sum based on CRC16_F */ temp_crc = Cal_CRC16(&aPacketData[PACKET_DATA_INDEX], PACKET_SIZE); serial->write((uint8_t)(temp_crc >> 8)); serial->write((uint8_t)(temp_crc & 0xFF)); /* Wait for Ack and 'C' */ if (serial->read(&a_rx_ctrl[0], 1, NAK_TIMEOUT, bytes_read) == true) { if (a_rx_ctrl[0] == CA) { Sleep(2); PurgeComm(serial->hSerial, PURGE_RXCLEAR); result = COM_ABORT; } } } printf("current progress:100%%\n"); serial->resetTimeout(50); IsStopPrintfReceive = false; } void receive_thread(SerialPort* serial) { char buffer[32]; DWORD bytes_read; while (true) { if (IsStopPrintfReceive == false) { if (serial->read(buffer, sizeof(buffer), bytes_read)) { if (bytes_read > 0) { std::cout.write(buffer, bytes_read);// Print received data } } } else { Sleep(1); } } } void send_thread(SerialPort* serial) { char input[32]; while (true) { std::cin.getline(input, sizeof(input)); if (input[0] == '6') { IsStopPrintfReceive = true; std::string file_path = "C:\\Users\\ME-LZQ\\Desktop\\STM32F091RC\\app\\1062\\Debug\\ZhuKongBan.bin"; send_file(serial, file_path.c_str()); } else serial->write(input); } } int main() { SerialPort serial("COM5"); std::thread t1(receive_thread, &serial); std::thread t2(send_thread, &serial); t1.join(); t2.join(); return 0; }
C#源码

using System; using System.Collections.Generic; using System.IO; using System.IO.Ports; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace 更新下位机 { class Program { static SerialPortWrapper serialWrapper; public static bool IsStopPrintfReceive { get; set; } = false; static void Main(string[] args) { serialWrapper = new SerialPortWrapper("COM5"); Thread t1 = new Thread(new ThreadStart(ReceiveThread)); Thread t2 = new Thread(new ThreadStart(SendThread)); t1.Start(); t2.Start(); t1.Join(); t2.Join(); } // 接收线程 static public void ReceiveThread() { byte[] buffer = new byte[2048]; int bytes_read; while (true) { if (!IsStopPrintfReceive) { bytes_read = serialWrapper.Read2(buffer, 0,buffer.Length); if (bytes_read > 0) { Console.Write(Encoding.ASCII.GetString(buffer, 0, bytes_read)); // 打印接收到的数据 } } else { Thread.Sleep(1000); // Sleep为毫秒单位,这里休眠1秒 } } } // 发送线程 static public void SendThread() { string input; while (true) { input = Console.ReadLine(); if (serialWrapper.Write(input)) { Console.WriteLine("Sent: " + input); if (input[0] == '6') { IsStopPrintfReceive = true; string file_path = @"C:\Users\ME-LZQ\Desktop\STM32F091RC\app\1062\Debug\ZhuKongBan.bin"; YmodemCommunication.SendFile(serialWrapper, file_path); IsStopPrintfReceive = false; } } } } } public class SerialPortWrapper { private SerialPort _serialPort; // 使用.NET内置的SerialPort类实现串口通信 /** * 构造函数,打开串口并设置参数 * @param portName 串口名称(如"COM1") */ public SerialPortWrapper(string portName) { _serialPort = new SerialPort(portName); // 创建SerialPort对象实例 // 设置串口参数 _serialPort.BaudRate = 115200; // 波特率为115200 _serialPort.DataBits = 8; // 数据位为8位 _serialPort.StopBits = StopBits.One; // 停止位为1位 _serialPort.Parity = Parity.None; // 无校验位 try { _serialPort.Open(); // 打开串口 } catch (UnauthorizedAccessException ex) { Console.WriteLine("未经授权访问串口"); return; } catch (IOException ex) { Console.WriteLine($"无法打开串口:{ex.Message}"); return; } // 设置读写超时时间 _serialPort.ReadTimeout = 50; // 读取数据的固定超时时间(单位:毫秒) _serialPort.WriteTimeout = 50; // 写入数据的固定超时时间(单位:毫秒) } /** * 析构函数,关闭串口 */ ~SerialPortWrapper() { if (_serialPort != null && _serialPort.IsOpen) { _serialPort.Close(); // 关闭串口 } } /** * 重置串口读写超时时间 * @param timeout 新的读写超时时间(单位:毫秒) * @return 是否成功重置超时时间 */ public bool ResetTimeout(int timeout) { _serialPort.ReadTimeout = timeout; _serialPort.WriteTimeout = timeout; return true; } // 向串口写入字符串数据 public bool Write(string data) { try { _serialPort.Write(data); // 将字符串写入串口 return true; } catch (InvalidOperationException ex) { Console.WriteLine("写入串口失败:" + ex.Message); return false; } } // 向串口写入单个字节数据 public bool Write(byte data) { try { _serialPort.Write(new[] { data }, 0, 1); // 将字节数组(长度为1)写入串口 return true; } catch (InvalidOperationException ex) { Console.WriteLine("写入串口失败:" + ex.Message); return false; } } /** * 向串口写入指定长度的数据 * @param data 要写入的数据缓冲区 * @param start_index 数据的起始位置 * @param length 数据的长度 * @return 是否成功写入数据 */ public bool Write(byte[] data, int start_index, int length, int timeout) { try { _serialPort.WriteTimeout = timeout; _serialPort.Write(data, start_index, length); // 将字节数组的部分内容写入串口 return true; } catch (InvalidOperationException ex) { Console.WriteLine("写入串口失败:" + ex.Message); return false; } } public int Read2(byte[] buffer, int offset, int count) { // 设置一个较大的超时时间(这里使用int类型的最大值) _serialPort.ReadTimeout = int.MaxValue; // 尝试读取数据 return _serialPort.Read(buffer, offset, count); } /** * 从串口读取指定长度的数据 * @param buffer 存储读取数据的目标缓冲区 * @param length 需要读取的字节数 * @param timeout 读取操作的超时时间(单位:毫秒) * @return 是否成功读取数据 */ public bool Read(byte[] buffer, int length, int timeout) { _serialPort.ReadTimeout = timeout; try { int bytes_read = _serialPort.Read(buffer, 0, length); // 从串口读取数据 return true; } catch (TimeoutException) { Console.WriteLine("串口读取超时"); return false; } catch (InvalidOperationException ex) { Console.WriteLine("读取串口失败:" + ex.Message); return false; } } // 清除接收缓冲区(模拟) public void ClearReceiveBuffer() { while (_serialPort.BytesToRead > 0) { var buffer = new byte[_serialPort.BytesToRead]; _serialPort.Read(buffer, 0, buffer.Length); } } } public class YmodemCommunication { // 定义通讯状态枚举类型 public enum COM_StatusTypeDef { COM_OK = 0x00, COM_ERROR = 0x01, COM_ABORT = 0x02, COM_TIMEOUT = 0x03, COM_DATA = 0x04, COM_LIMIT = 0x05 } // 数据包结构定义常量 private const int PACKET_HEADER_SIZE = 3; private const int PACKET_DATA_INDEX = 4; private const int PACKET_START_INDEX = 1; private const int PACKET_NUMBER_INDEX = 2; private const int PACKET_CNUMBER_INDEX = 3; private const int PACKET_TRAILER_SIZE = 2; private const int PACKET_OVERHEAD_SIZE = PACKET_HEADER_SIZE + PACKET_TRAILER_SIZE - 1; private const int PACKET_SIZE = 128; private const int PACKET_1K_SIZE = 1024; private const int FILE_NAME_LENGTH = 64; private const int FILE_SIZE_LENGTH = 16; // 协议中特定字节含义的常量 private const byte SOH = 0x01; private const byte STX = 0x02; private const byte EOT = 0x04; private const byte ACK = 0x06; private const byte NAK = 0x15; private const int CA = 0x18; private const byte CRC16 = 0x43; private const byte NEGATIVE_BYTE = 0xFF; private const byte ABORT1 = 0x41; private const byte ABORT2 = 0x61; private const int NAK_TIMEOUT = 0x100000; private const int DOWNLOAD_TIMEOUT = 1000; // 重试延迟时间:1秒 private const int MAX_ERRORS = 5; private const int USER_FLASH_SIZE = 0x00010000; // 默认的小型模板应用大小 // 初始化一个足够大的缓冲区用于存储数据包 private static readonly byte[] aPacketData = new byte[PACKET_1K_SIZE + PACKET_DATA_INDEX + PACKET_TRAILER_SIZE]; // 将整数转换为字符数组(模拟C语言中的字符串) public static void IntToCharArray(int intnum, char[] p_str) { int divider = 1000000000; int pos = 0, status = 0; for (int i = 0; i < 10; i++) { p_str[pos++] = (char)((intnum / divider) + '0'); intnum %= divider; divider /= 10; // 若当前字符为'0'且状态为初始态,则清空之前填充的字符 if ((p_str[pos - 1] == '0') && (status == 0)) { pos = 0; } else { status++; } } } // 更新CRC16值 public static ushort UpdateCRC16(ushort crc_in, byte byteValue) { uint crc = crc_in; uint inValue = byteValue | 0x100u; do { crc <<= 1; inValue <<= 1; // 按照CRC16校验规则更新crc值 if ((inValue & 0x100u) != 0) crc++; if ((crc & 0x10000u) != 0) crc ^= 0x1021u; } while ((inValue & 0x10000u) == 0); return (ushort)(crc & 0xffff); } // 计算YModem协议数据包的CRC16校验值 public static ushort CalculateCRC16(byte[] data, int offset, int size) { uint crc = 0; // 遍历数据并逐个更新CRC值 for (int i = offset; i < offset + size; i++) crc = UpdateCRC16((ushort)crc, data[i]); // 根据协议要求额外进行两次UpdateCRC16操作 crc = UpdateCRC16((ushort)crc, 0); crc = UpdateCRC16((ushort)crc, 0); return (ushort)(crc & 0xffff); } // 准备初始数据包(文件名和长度信息) private static void PrepareInitialPacket(byte[] p_data, string p_file_name, int length) { int i = 0, j = 0; char[] astring = new char[10]; // 前3个字节固定不变 p_data[PACKET_START_INDEX] = SOH; p_data[PACKET_NUMBER_INDEX] = 0x00; p_data[PACKET_CNUMBER_INDEX] = 0xff; // 写入文件名 for (; i < FILE_NAME_LENGTH && i < p_file_name.Length; i++) { p_data[i + PACKET_DATA_INDEX] = (byte)p_file_name[i]; } p_data[i + PACKET_DATA_INDEX] = 0x00; // 文件名以'\0'结束 // 写入文件大小 IntToCharArray(length, astring); i += PACKET_DATA_INDEX + 1; for (; j < astring.Length && astring[j] != '\0'; j++, i++) { p_data[i] = (byte)astring[j]; } // 用0填充剩余空间 for (j = i; j < PACKET_SIZE + PACKET_DATA_INDEX; j++) { p_data[j] = 0; } } // 准备数据包内容 private static void PreparePacket(byte[] p_source, int recordIndex, byte[] p_packet, byte pkt_nr, int size_blk) { int packet_size, size, i; // 确定数据包大小 packet_size = size_blk >= PACKET_1K_SIZE ? PACKET_1K_SIZE : PACKET_SIZE; size = size_blk < packet_size ? size_blk : packet_size; if (packet_size == PACKET_1K_SIZE) { p_packet[PACKET_START_INDEX] = STX; } else { p_packet[PACKET_START_INDEX] = SOH; } // 设置包序号和校验序号 p_packet[PACKET_NUMBER_INDEX] = pkt_nr; p_packet[PACKET_CNUMBER_INDEX] = (byte)(~pkt_nr); // 将有效数据写入数据包 for (i = PACKET_DATA_INDEX; i < size + PACKET_DATA_INDEX; i++) { p_packet[i] = p_source[recordIndex++]; } // 如果数据大小小于等于包大小,则用EOF标识填充剩余空间 if (size <= packet_size) { for (i = size + PACKET_DATA_INDEX; i < packet_size + PACKET_DATA_INDEX; i++) { p_packet[i] = 0x1A; // EOF (0x1A) 或者填充0x00 } } } public static void SendFile(SerialPortWrapper serialWrapper, string filePath) { int size = 0; int pktSize; uint errors = 0, ackRecpt = 0; byte[] pBufInt; COM_StatusTypeDef result = COM_StatusTypeDef.COM_OK; byte blkNumber = 1; byte[] aRxCtrl = new byte[2]; byte i; uint tempCrc; string fileName; long fileSize; uint bytesRead; int currentIndex = 0; // 打开文件并获取相关信息 using (FileStream file = File.Open(filePath, FileMode.Open, FileAccess.Read)) { fileName = Path.GetFileName(filePath); fileSize = file.Length; Console.WriteLine($"发送文件:{fileName},大小:{fileSize} 字节"); // 分配缓冲区并读取文件内容 pBufInt = new byte[fileSize]; file.Read(pBufInt, 0, (int)fileSize); // 准备首包 - 文件头信息 PrepareInitialPacket(aPacketData, fileName, (int)fileSize); while (0 == ackRecpt && result == COM_StatusTypeDef.COM_OK) { // 发送数据包及CRC校验值 serialWrapper.Write(aPacketData, PACKET_START_INDEX, PACKET_SIZE + PACKET_HEADER_SIZE, NAK_TIMEOUT); tempCrc = CalculateCRC16(aPacketData, PACKET_DATA_INDEX, PACKET_SIZE); serialWrapper.Write((byte)(tempCrc >> 8)); serialWrapper.Write((byte)(tempCrc & 0xFF)); // 等待ACK或CA响应 if (serialWrapper.Read(aRxCtrl, 1, NAK_TIMEOUT)) { if (aRxCtrl[0] == ACK) { ackRecpt = 1; } else if (aRxCtrl[0] == CA) { if (serialWrapper.Read(aRxCtrl, 1, NAK_TIMEOUT) && aRxCtrl[0] == CA) { Thread.Sleep(2); serialWrapper.ClearReceiveBuffer(); result = COM_StatusTypeDef.COM_ABORT; } } } else { errors++; } if (errors >= MAX_ERRORS) { result = COM_StatusTypeDef.COM_ERROR; } } size = (int)fileSize; // 按照每包1024字节长度发送后续数据包 while ((size > 0) && (result == COM_StatusTypeDef.COM_OK)) { // 准备下一个数据包 PreparePacket(pBufInt, currentIndex, aPacketData, blkNumber, size); ackRecpt = 0; Array.Clear(aRxCtrl, 0, aRxCtrl.Length); errors = 0; // 若收到NAK则重发数据包,直到接收到ACK或达到最大错误次数 while (0 == ackRecpt && result == COM_StatusTypeDef.COM_OK) { /* Send next packet */ if (size >= PACKET_1K_SIZE) { pktSize = PACKET_1K_SIZE; } else { pktSize = PACKET_SIZE; } // 发送下一数据包及其CRC校验值 tempCrc = CalculateCRC16(aPacketData, PACKET_DATA_INDEX, pktSize); aPacketData[pktSize + PACKET_HEADER_SIZE + PACKET_START_INDEX + 0] = ((byte)(tempCrc >> 8)); aPacketData[pktSize + PACKET_HEADER_SIZE + PACKET_START_INDEX + 1] = ((byte)(tempCrc & 0xFF)); serialWrapper.Write(aPacketData, PACKET_START_INDEX, pktSize + PACKET_HEADER_SIZE + 2, NAK_TIMEOUT); serialWrapper.ClearReceiveBuffer(); // 计算进度并输出 uint progress = (uint)((float)(fileSize - size) / fileSize * 100); Console.WriteLine($"当前进度:{progress}%"); // 等待ACK响应 if (serialWrapper.Read(aRxCtrl, 1, NAK_TIMEOUT)) { if (aRxCtrl[0] == ACK) { ackRecpt = 1; if (size > pktSize) { currentIndex += pktSize; size -= pktSize; if (blkNumber == (USER_FLASH_SIZE / PACKET_1K_SIZE)) { result = COM_StatusTypeDef.COM_LIMIT; /* boundary error */ } else { blkNumber++; } } else { currentIndex += pktSize; size = 0; } } } else { errors++; } if (errors >= MAX_ERRORS) { result = COM_StatusTypeDef.COM_ERROR; } } } // 发送结束传输字符EOT ackRecpt = 0; Array.Clear(aRxCtrl, 0, aRxCtrl.Length); errors = 0; while (0 == ackRecpt && result == COM_StatusTypeDef.COM_OK) { serialWrapper.Write(EOT); // 等待ACK响应 if (serialWrapper.Read(aRxCtrl, 1, NAK_TIMEOUT)) { if (aRxCtrl[0] == ACK) { ackRecpt = 1; } else if (aRxCtrl[0] == CA) { if ((serialWrapper.Read(aRxCtrl, 1, NAK_TIMEOUT)) && (aRxCtrl[0] == CA)) { Thread.Sleep(2); serialWrapper.ClearReceiveBuffer(); result = COM_StatusTypeDef.COM_ABORT; } } } else { errors++; } if (errors >= MAX_ERRORS) { result = COM_StatusTypeDef.COM_ERROR; } } // 发送空数据包以关闭会话 if (result == COM_StatusTypeDef.COM_OK) { // 准备空数据包 aPacketData[PACKET_START_INDEX] = SOH; aPacketData[PACKET_NUMBER_INDEX] = 0; aPacketData[PACKET_CNUMBER_INDEX] = 0xFF; for (i = PACKET_DATA_INDEX; i < (PACKET_SIZE + PACKET_DATA_INDEX); i++) { aPacketData[i] = 0x00; } // 发送数据包及CRC校验值 serialWrapper.Write(aPacketData, PACKET_START_INDEX, PACKET_SIZE + PACKET_HEADER_SIZE, NAK_TIMEOUT); tempCrc = CalculateCRC16(aPacketData, PACKET_DATA_INDEX, PACKET_SIZE); serialWrapper.Write((byte)(tempCrc >> 8)); serialWrapper.Write((byte)(tempCrc & 0xFF)); // 等待CA响应 if (serialWrapper.Read(aRxCtrl, 1, NAK_TIMEOUT)) { if (aRxCtrl[0] == CA) { Thread.Sleep(2); serialWrapper.ClearReceiveBuffer(); result = COM_StatusTypeDef.COM_ABORT; } } } Console.WriteLine("当前进度:100%"); serialWrapper.ResetTimeout(50); } } } }
使用方法:烧好bootloader的板子上电,进入菜单,摁1进入下载镜像文件STM32F091RC_APP.bin到内部Flash,等待回复C摁下6开始把STM32F091RC_APP.bin传输到flash,进度达到100%即传输完毕。摁3程序启动APP。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2021-06-07 WPF使用MVVMLight的ViewModel 访问控件的属性方法事件以及多页面传递信息