单片机中OTA升级流程及bootload软件框架
OTA升级流程及bootload软件框架
- 为什么要进行OTA升级
- OTA升级的流程
- bootload软件框架
- bootload软件源码(针对接收HEX文件)
为什么要进行OTA升级
OTA 英文全称是Over-the-Air Technology,即空间下载技术,是通过移动通信(GSM或CDMA)的空中接口对SIM卡数据及应用进行远程管理的技术。在我们常用的电子设备中,通过会进行软件优化或者固件升级,这个时候我们的产品可能已经在全国各地,不可能亲自去进行固件升级,所以就需要利用OTA技术进行升级。
OTA升级的流程
这里的升级流程框架我是按照我之前的一个项目写的,该项目中所用单片机的FLASH大小为128K,其中包含我的FLASH分配,和APP和BOOTLOAD对于升级流程实现
bootload软件框架
这里主要是bootload代码的实现框架和一些具体的细节问题,可以和下面的后面的代码部分相结合阅读。
bootload软件源码(针对接收HEX文件)
这里我就只放一下IAP的头文件和源文件
#ifndef _IAP_H_
#define _IAP_H_
#include "stm32f10x.h"
#define FlashBaseAddress 0x08000000
#define ApplicationAddress 0x8003000
#define ApplicationSize 0xD000 // 52KB
#define ApplicationBackup (ApplicationAddress + ApplicationSize)
typedef void (*pFunction)(void);
#define UPGRADE_FLAG_START ((uint16_t)0x1010)
#define UPGRADE_FLAG_RECV_COMPLETE ((uint16_t)0x2020)
#define UPGRADE_FLAG_END ((uint16_t)0x3030) // 注意如果没有读到0x600的值也视为已经完成
extern volatile uint8_t updateFinished;
extern uint32_t maxProgramAdd;
void jumpToApplication(void);//跳转到APP程序
uint8_t IsUpgradeStarted(void);//开始Bootload升级标记
uint8_t IsUpgradeRecEnd(void);//接收Bootload升级标记
uint8_t IsUpgradeComplete(void);Bootload升级完成标记
uint8_t EraseFlash(uint32_t baseAddress);//擦除FLASH
uint8_t WriteUpgradeFlag(uint16_t flagValue);//写半字
uint8_t WriteMaxProgramAddress(uint32_t address);//写OTA接收程序最大地址
uint8_t readMaxProgramAddress(uint32_t* addressValue);//读OTA接收程序最大地址
static uint8_t MassCopy(void);//根据程序大小,从Application备份区拷贝到Application区
uint8_t copyApplication(void);//擦除Application区,从备份区拷贝新程序到Application区
uint8_t HEX_File_Parsing(uint8_t * data,uint8_t len);//Hex文件解析
#endif
#include "iap.h"
#include "Flash.h"
#include "EEPROM.h"
#include "stdio.h"
pFunction Jump_To_Application;
uint32_t JumpAddress;
uint32_t maxProgramAdd=0;
volatile uint8_t updateFinished = 0;
void jumpToApplication(void)//跳转到APP程序
{
// __set_PRIMASK(1);
// __DSB();
// __ISB();
// SysTick->CTRL = 0;
if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)
{
JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
Jump_To_Application = (pFunction) JumpAddress;
__set_MSP(*(__IO uint32_t*) ApplicationAddress);
Jump_To_Application();
}
}
uint8_t IsUpgradeStarted(void)
{
uint16_t FlashData;
uint8_t FreshStatus=readSysU16(0x600,&FlashData);
if(FreshStatus==0 && FlashData == UPGRADE_FLAG_START)
{
return 0;
}
return 1;
}
uint8_t IsUpgradeRecEnd(void)
{
uint16_t FlashData;
uint8_t FreshStatus=readSysU16(0x600,&FlashData);
FreshStatus |= readMaxProgramAddress(&maxProgramAdd);
if(FreshStatus==0 && FlashData == UPGRADE_FLAG_RECV_COMPLETE)
{
return 0;
}
return 1;
}
uint8_t IsUpgradeComplete(void)
{
uint16_t FlashData;
uint8_t FreshStatus=readSysU16(0x600,&FlashData);
if(FreshStatus==0 && FlashData == UPGRADE_FLAG_END)
{
printf("\r\n升级成功了!");
return 0;
}
return 1;
}
/**
* @brief 擦除ApplicationSize大小的Flash
* @param None
* @retval None
*/
uint8_t EraseFlash(uint32_t baseAddress)
{
uint8_t flashStatus = 0;
for (uint32_t i = 0; i < ApplicationSize; i+=PAGE_SIZE)
{
flashStatus = FLASH_ErasePage(baseAddress + i);
if (flashStatus != FLASH_COMPLETE)
return flashStatus;
}
return 0;
}
/**
* @brief Writes/upadtes variable data in EEPROM.
* @param flagValue: new value
* @retval Success or error status:
* - 0: Success
* - PAGE_FULL: if valid page is full
* - NO_VALID_PAGE: if no valid page was found
* - Flash error code: on write Flash error
*/
uint8_t WriteUpgradeFlag(uint16_t flagValue)
{
uint8_t error = writeSysU16(0x0600, flagValue);
return error == FLASH_COMPLETE ? 0 : error;
}
/**
* @brief Writes/upadtes variable data in EEPROM.
* @param flagValue: new value
* @retval Success or error status:
* - 0: Success
* - PAGE_FULL: if valid page is full
* - NO_VALID_PAGE: if no valid page was found
* - Flash error code: on write Flash error
*/
uint8_t WriteMaxProgramAddress(uint32_t address)
{
uint8_t error = writeSysU16(0x0601, (u16)(address >> 16));
if (error == FLASH_COMPLETE) error = writeSysU16(0x0602, ((u16)(address & 0xFF)));
return error == FLASH_COMPLETE ? 0 : error;
}
/**
* @brief Returns the last stored max programAddress
* @param addressValue: Global variable contains the read variable value
* @retval Success or error status:
* - 0: if variable was found
* - 1: if the variable was not found
* - NO_VALID_PAGE: if no valid page was found.
*/
uint8_t readMaxProgramAddress(uint32_t* addressValue)
{
u16 hb,lb;
u8 flashStatus = readSysU16(0x0601, &hb) || readSysU16(0x0602, &lb);
*addressValue = (((u32)hb) << 16) | lb;
if (flashStatus == 0 && *addressValue > ApplicationAddress && *addressValue < ApplicationAddress + ApplicationSize) {
return 0;
}
return 1;
}
/**
* @brief 根据程序大小,从Application备份区拷贝到Application区
* @param None
* @retval None
*/
static uint8_t MassCopy(void)
{
uint8_t flashStatus = 0;
for (uint32_t i = 0; i <= (maxProgramAdd - ApplicationAddress); i+= 2)
{
flashStatus = FLASH_ProgramHalfWord(ApplicationAddress + i, readFlashU16(ApplicationBackup + i));
if (flashStatus != FLASH_COMPLETE) return flashStatus;
}
return 0;
}
/**
* @brief 擦除Application区,从备份区拷贝新程序到Application区
* @param None
* @retval None
*/
uint8_t copyApplication(void)
{
uint8_t Error = 0;
printf("\r\n 开始擦除APP!");
Error = EraseFlash(ApplicationAddress);
if(Error)
return (Error);
Error = MassCopy();
if(Error)
return (Error);
Error = WriteUpgradeFlag(UPGRADE_FLAG_END); // 擦除bootloader标志
updateFinished = 1;
return Error;
}
//Hex文件解析
uint8_t HEX_File_Parsing(uint8_t * data,uint8_t len)
{
/*数据格式::|LL|aaaa|TT|data|CC|
** :冒号 HEX文件每一行首字节都是由冒号开头
** LL:数据域长度
** AAAA:地址域
** TT:类型域 00数据记录
01文件结束记录
02扩展段地址记录
03开始段地址记录
04扩展线性地址记录
05开始线性地址记录
** data:数据域
** CC:校验
*/
uint8_t i;
uint8_t crctotal=0;
uint8_t flashStatus=0;
uint16_t Flashdata=0;
if(data[0]!=0x3A)//开头是不冒号
{
printf("\r\n error1");
return 1;
}
for(uint8_t i=1;i<len-1;i++)//hex文件校验和
{
crctotal+=data[i];
}
if(crctotal!=(uint8_t)(0x100-data[len-1]))//crc校验不通过
{
printf("\r\n error2");
return 1;
}
if(data[4]==0x04)//04扩展线性地址记录
{
uint32_t Freshaddress=(data[5]<<8)+data[6];
if(Freshaddress!=0x0800)
return 1;
}
else if (data[4]==0x00)//00数据记录
{
uint32_t Freshaddress=FlashBaseAddress + ((data[2]<<8)+data[3]);//这里应该是APP的地址
maxProgramAdd=Freshaddress+data[1];//最大地址等于 目前最新地址+该行数据长度
if(maxProgramAdd>=ApplicationBackup && maxProgramAdd<= ApplicationAddress)//如果地址越界
{
printf("\r\n error3");
return 1;
}
for(i=0;i<data[1];i+=2)
{
uint32_t WriteAdree=Freshaddress+i;
Flashdata=data[6+i]<<8;//半字高8位
Flashdata+=data[5+i];//半字低8位
// printf("\r\n要写的数据是%x",Flashdata);
// printf(" 要写的地址是%x",WriteAdree);
// printf("\r\n要写的地址是%x",ApplicationBackup + (WriteAdree - ApplicationAddress));
flashStatus = FLASH_ProgramHalfWord(ApplicationBackup + (WriteAdree - ApplicationAddress),Flashdata);
if (flashStatus != FLASH_COMPLETE)
{
printf("\r\n error4 %d",flashStatus);
return flashStatus;
}
}
}
return 0;
}
如果看完有帮助的可以点赞收藏一下,还有不懂的可以直接联系我哦~
送外卖的CV工程师: 博主 你好,如果输入5V或者3.7V,可以将图中的电阻全部设置为10K吗
宠溺22: 可以分享吗
Htick: 可以参考一下这个 https://github.com/ShenQiu-1999/mcp2515-driver
lyglary: 可以分享下代码吗?大神?lyglary@126.com,多谢
lyglary: 可以分享下代码吗?大神?273164608@qq.com,多谢