- struct AES_ctx ctx;
 
-     uint8_t KEY[32];
 
-     uint8_t IV[16];
 
-     memcpy(KEY, RT_FOTA_ALGO_AES_KEY, 32);
 
-     memcpy(IV, RT_FOTA_ALGO_AES_IV, 16);
 
-     AES_init_ctx_iv(&ctx, KEY, IV);
- /**
 
-  * 使用AES_CBC模式解密缓冲区。
 
-  *
 
-  * @param ctx 密钥上下文,包含解密所需的轮密钥和初始化向量。
 
-  * @param buf 待解密的数据缓冲区。
 
-  * @param length 要解密的数据长度,必须是AES块长度的倍数。
 
-  *
 
-  * 注意:这个函数假设输入数据长度是AES块长度的倍数,如果不是,可能会导致未被解密的数据被错误地处理。
 
-  */
 
- void AES_CBC_decrypt_buffer(struct AES_ctx *ctx, uint8_t *buf, size_t length);
 
 
每调用一次后,解密的IV都会变,不能从任意位置解密,必须从头开始,解密才能正确。
如需再次解密需要重新初始化 AES_init_ctx_iv(&ctx, KEY, IV);
三、接收解析文件头
定义一个共同体
 
- union   RBL{
 
-     struct
 
-     {
 
-         char type[4];              /* RBL 字符头 */
 
-         uint16_t fota_algo;        /* 算法配置: 表示是否加密或者使用了压缩算法 */
 
-         uint8_t fm_time[6];        /* 原始 bin 文件的时间戳, 6 位时间戳, 使用了 4 字节, 包含年月日信息 */
 
-         char app_part_name[16];    /* app 执行分区名 ,实际是作为固件型号使用*/
 
-         char download_version[24]; /* 固件代码版本号 */
 
-         char current_version[24];  /* 这个域在 rbl 文件生成时都是一样的,我用于表示 app
 
-                                       分区当前运行固件的版本号,判断是否固件需要升级 */
 
-         uint32_t code_crc;         /* 代码的 CRC32 校验值, 它是的打包后的校验值, 即 rbl 文件 96 字节后的数据 */
 
-         uint32_t hash_val;         /* hash校验值 */
初始化一个共同体RBL OTA_part,接收使用的是串口空闲中断或者接收超时模式,判断接收的是不是96字节,如果是的,赋值给共同体的数组,memcpy(&OTA_part.data[0], Recve_buffer, 96);获取到文件的大小,CRC等数据信息,先判断固件型号是否和本机一致,如一致则开始接收程序。
四、接收文件并写入备份区
当文件头判断都正确后就开始接收加密后的APP了,由于上位机是按照256字节发送的,直接按页写入就可以了,并累加写入的大小,并计算crc,直到写入的字节等于头文件中的文件大小,- body_crc = rt_fota_step_crc(body_crc, Recve_buffer, len); // 计算CRC
 
-         // 快速解锁闪存以准备写入
 
-         FLASH_Unlock_Fast();
 
-         // 计算写入地址
 
-         uint32_t address = (uint32_t)BACKUP_IMAGE_START_ADD + (file_count * FLASH_PAGE_SIZE);
 
-         if (len >= 256)
 
-         {
 
-             // 写入一页数据
 
-             FLASH_ProgramPage_Fast(address, Recve_buffer); // 写入一页数据
 
-             // 写入完成后锁定闪存
 
-             FLASH_Lock_Fast(); // 快速编程模式上锁
 
-             file_count++;      // 文件写入计数
 
-             filesize += len;
 
-             UART_IAP_SendData(0X03); // 发送确认
 
-         }
 
-         else
 
-         {
 
-             // 写入剩余的数据
 
-             // 循环写入剩余的数据
 
-             union flash_data {
 
-                 uint8_t buf[256]; /* data */
 
-                 uint32_t page[64];
 
-             } flash;
 
-             for (uint32_t i = 0; i < len; i++)
 
-             {
 
-                 flash.buf[i] = Recve_buffer[i];
 
-             }
 
-             FLASH_ProgramPage_Fast(address, flash.page); // 写入数据
 
-             // 写入完成后锁定闪存
 
-             FLASH_Lock_Fast(); // 快速编程模式上锁
 
-             filesize += len;
 
-             if ((filesize) != OTA_part.fota_part_head.com_size)
 
-             {
 
-                 printf("文件大小不匹配\r\n");
 
-                 Flag_UART_IAP = 0;
 
-                 UART_IAP_SendData(0X05); // 发送错误确认
 
-                 return;
 
-             }
 
-         }
- // 从用户闪存区读取96字节数据到程序文件结构体,再次判断
 
-                 IAP_EEPROM_READ(FILE_FLAG_ADD, &OTA_part.data[0], 96);
 
-  printf("文件头 = %s\r\n", OTA_part.data);
 
-  printf("版本号 = %s\r\n", OTA_part.fota_part_head.download_version);
 
-  printf("名称 = %s\r\n", OTA_part.fota_part_head.app_part_name);
 
-  printf("代码大小 = %d\r\n", OTA_part.fota_part_head.raw_size);
 
-  printf("hash_val = %x\r\n", OTA_part.fota_part_head.hash_val);
{
// 设置升级标志
                    u32 updateFlag = IMAGE_FLAG_UPDATE;
                    // 擦除存储升级标志的EEPROM区域
                    IAP_EEPROM_ERASE(UPDATA_FLAG_STORAGE_ADD, FLASH_PAGE_SIZE);
                    // 写入升级标志到EEPROM
                    IAP_EEPROM_WRITE(UPDATA_FLAG_STORAGE_ADD, (u8 *)&updateFlag, 4);
          // 复位系统
          NVIC_SystemReset();
}
五、把备份区文件写入APP区
   重启后首先判断升级标志,如果有升级标志,就开始把备份区的数据写入到APP区,首先读取文件头里的大小,根据大小开始循环读取写入,
- // 循环复制数据,这样可以确保即使 com_size 不能被 READ_DATA_LEN 整除,循环次数也会多一次,从而覆盖所有数据。
 
-     for (i = 0; i < ((OTA_part.fota_part_head.com_size + READ_DATA_LEN - 1) / READ_DATA_LEN);
 
-          i++) // i < (BACKUP_IMAGE_MAX_SIZE / READ_DATA_LEN)
 
-     {
 
-         // 计算当前循环对应的闪存地址
 
-         flashAddr = USER_IMAGE_START_ADD + i * READ_DATA_LEN;
 
-         // 从用户闪存区读取数据到缓冲区
 
-         IAP_EEPROM_READ((flashAddr + USER_IMAGE_MAX_SIZE), (u8 *)pBuf, READ_DATA_LEN);
 
 
-         AES_CBC_decrypt_buffer(&ctx, (u8 *)pBuf, READ_DATA_LEN);
 
 
-         // 将缓冲区的数据写入备份闪存区
 
-         IAP_EEPROM_WRITE(flashAddr, (u8 *)pBuf, READ_DATA_LEN);
 
 
-         // 检查写入的数据是否正确
 
-         for (j = 0; j < (READ_DATA_LEN / 4); j++)
 
-         {
 
-             // 如果数据不匹配,返回操作失败状态
 
-             if (pBuf[j] != *(u32 *)(flashAddr + 4 * j))
 
-                 return NoREADY;
 
-         }
 
-     }
写入完成后可以再次校验CRC32和HASH,判断是否一致,
- // 初始化flash地址
 
-     flashAddr = USER_IMAGE_START_ADD;
 
-     hash = FNV1A_32_INIT;
 
-     // 循环计算数据,直到达到代码大小
 
-     for (uint32_t i = 0; i < raw_size; i++)
 
-     {
 
-         hash ^= *(u8 *)(flashAddr + i);
 
-         hash *= FNV_PRIME_32;
 
-     }
 
-     printf("hash_val = %x\r\n", hash);
 
-     if (hash != OTA_part.fota_part_head.hash_val)
 
-     {
 
-         printf("hash is wrong!\r\n");
 
-         return NoREADY;
 
-     }
-  printf("update success!\r\n");
 
-             printf("Run APP1!\r\n");
 
-             EEprom_Write_Byte(UPDATA_FLAG_STORAGE_ADD, 0); // 写入完成
 
-             Delay_Ms(100);
 
-             NVIC_EnableIRQ(Software_IRQn);
 
-             NVIC_SetPendingIRQ(Software_IRQn);