升级方式
启动方式
在STM32F10xxx里有三种启动方式:
BOOT0 | BOOT1 | 启动模式 | 说明 |
---|---|---|---|
0 | X | 用户闪存存储器 | 用户闪存存储器,也就是flash启动 |
1 | 0 | 系统存储器 | 系统存储器自动,用于串口下载 |
1 | 1 | SRAM启动 | SRAM启动,用于再SRAM中调试代码 |
系统存储器是厂家固化好的一片存储区,地址为0x1FFFF000,具有串口升级的功能,并把程序引导至用户闪存存储区。
用户闪存存储区是保存用户代码及数据的存储区吗,地址为0x08000000,可以放置用户的bootloader,用于IAP。
注意不同的系列的STM32固话的系统存储区代码是不同的,STM32F1XXX系列只支持USART1。
ISP升级
升级时先将boot0置1,电脑使用ISP下载工具与MCU通过串口连接后升级。
IAP升级
可以实现远程OTA,在0x08000000处放置用户bootloader,在后面地址放置用户APP及升级包存储区,通过APP程序来升级。下面介绍IAP的升级方法。
Bootloader程序说明
功能
bootloader的主要功能是判断有无升级包需要升级,需要升级则搬移程序、校验升级包。然后跳转到APP程序
代码
#define CODE_ADDRESS_START 0x08001800
#define UPDATE_ADDRESS_START 0x08013800 //第78K
#define UPDATE_CRC_ADDRESS_START 0x0801F800 //第126K
#define DEFAULT_VALUE 0xFFFFFFFF
#define VERIFICATION 0x55555555
#define PACKAGE_BIG 48*1024
#define CLOSE_ALL_INT() __set_PRIMASK(1)
typedef void (*Iapfun)(void);
static Iapfun jump2app;
FLASH_EraseInitTypeDef Flash;
uint32_t Flash_error;
int main(void)
{
HAL_Init();
SystemClock_Config();
//读出flash中参数
update_size = *(__IO uint32_t *)(UPDATE_CRC_ADDRESS_START);
update_crc = *(__IO uint32_t *)(UPDATE_CRC_ADDRESS_START+4);
if(update_size < PACKAGE_BIG){
update_data = *(__IO uint32_t *)(UPDATE_ADDRESS_START + update_size -4);
}
//有升级数据包,搬移程序
if((update_size!= DEFAULT_VALUE)&&(update_size <= PACKAGE_BIG)&&(update_size%4 ==0)&&\
(update_crc!= DEFAULT_VALUE)&&(update_data == VERIFICATION)){
//搬移前
__HAL_RCC_CRC_CLK_ENABLE();
CRC->CR = CRC_CR_RESET;
HAL_FLASH_Unlock();
Flash.NbPages = PACKAGE_BIG/FLASH_PAGE_SIZE;
Flash.PageAddress = CODE_ADDRESS_START;
Flash.TypeErase = FLASH_TYPEERASE_PAGES;
HAL_FLASHEx_Erase(&Flash,&Flash_error);
//搬移
for(i = 0; i < update_size/4; i++){
update_data = *(__IO uint32_t *)(UPDATE_ADDRESS_START+4*i);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD ,CODE_ADDRESS_START+4*i, update_data);
U32_Exchange(&update_data);
CRC->DR= update_data;
}
//搬移后
HAL_FLASH_Lock();
//升级成功
if(update_crc == CRC->DR){
HAL_FLASH_Unlock();
Flash.NbPages = 1;
Flash.PageAddress = UPDATE_CRC_ADDRESS_START;
Flash.TypeErase = FLASH_TYPEERASE_PAGES;
HAL_FLASHEx_Erase(&Flash,&Flash_error);
}
//升级失败
else{
NVIC_SystemReset();
}
}
//跳转到APP程序
jump2app=(Iapfun)*(volatile uint32_t *)(CODE_ADDRESS_START+4);
__set_MSP(*(volatile uint32_t *)CODE_ADDRESS_START);
CLOSE_ALL_INT();
jump2app();
}
__set_MSP(*(volatile uint32_t *)CODE_ADDRESS_START):STM32规定程序起始必须为栈顶指针。
jump2app=(Iapfun)*(volatile uint32_t *)(CODE_ADDRESS_START+4):用户程序为地址地址+4 且为中断向量表。
APP程序说明
功能
APP程序需要完成1.bin文件的接收、存储、校验。2通知bootloader升级包已准备好(写入flash特定位置特定参数)
代码
/**
* @功能:
* @变量:
*/
uint8_t Write_Update_Package(uint8_t number,uint16_t len, uint32_t *data)
{
uint16_t i;
if((number ==0)||(len>1024)){
return ERROR;
}
number--;
//第一包 开启CRC
if(number == 0){
__HAL_RCC_CRC_CLK_ENABLE();
CRC->CR = CRC_CR_RESET;
}
//保存数据 计算CRC
HAL_FLASH_Unlock();
for(i=0;i<len/4;i++){
//最后一个数据是CRC数据
if((number + 1 == OTA_para.total_package)&&(i == len/4-1)){
U32_Exchange(data+i);
if(data[i] == CRC->DR){
//校验成功
uint32_t total_length = OTA_para.total_package*1024 + len - 1028;
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD ,UPDATE_CRC_ADDRESS_START ,total_length);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD ,UPDATE_CRC_ADDRESS_START+4 ,data[i]);
HAL_FLASH_Lock();
return SUCCESS;
}
}
//其余正常数据,写FLASH 计算CRC
else{
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, UPDATE_ADDRESS_START + number*1024 + i*4, data[i]);
U32_Exchange(data+i);
CRC->DR= data[i];
}
}
HAL_FLASH_Lock();
return ERROR;
}
设置
- keil的Options中的Target选项卡,中IROM1的Start改为APP的地址。
- 如采用硬件库,则在system_stm32f1xx.c中 97行打开宏定义 #define USER_VECT_TAB_ADDRESS,并修改偏移#define VECT_TAB_OFFSET 0x00001800U。
BIN文件制作
生成
keil的Options中的User选项卡,在After Build/Rebuild中打钩,并填入 下面
fromelf --bin -o “$L@L.bin” “#L”
结构
数据结构为:数据(4*NBYTE)+验证码(4BYTE)+校验码(4BYTE)。
验证码用于判断BIN文件的有效性,防止升级成不相干的bin。
校验码用于判断BIN文件的完整性,防止BIN文件的损坏。
通过Hex Editor 可以对bin文件编辑。通过这里可以计算CRC-32/MPEG-2。
CRC32校验
STM32自带CRC硬件,可通过自身硬件快速进行CRC32校验。
校验格式:
CRC-32/MPEG-2:x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1
宽度:32
多项式:04C11DB7
初始值:FFFFFFFF
结果亦或值:00000000
输入反转:false
输出反转:false
需要注意的是,一些常用的校验工具校验文件时,采用的是高端存储方式,STM32采用的是低端存储方式,在STM32采用硬件校验时,需要将每个32位数据的4个字节的顺序变换:
void U32_Exchange(uint32_t *data)
{
uint32_t result=0;
result |= (*data & 0xFF000000UL) >> 24;
result |= (*data & 0x00FF0000UL) >> 8;
result |= (*data & 0x0000FF00UL) << 8;
result |= (*data & 0x000000FFUL) << 24;
*data = result;
}
CRC32硬件使用
使用前打开时钟并RESET
__HAL_RCC_CRC_CLK_ENABLE();
CRC->CR = CRC_CR_RESET;
使用时直接写入
U32_Exchange(data+i);
CRC->DR= data[i];
校验后直接读取
data[i] = CRC->DR;
STM32的CRC计算时会占用MCU的核心,所以程序无需等待,随写随读。