IAR STM32 BootLoader
STM32 Bootloader与APP
IROM中可以分成两个区域,起始代码运行地址为0x08000000,这是基本固定的,可以将IROM的0x08000000 ~ 0x08002000这8KB的空间存放Bootloader代码,而0x08002000 ~ 上限的位置存放APP代码。
需要实现两个工程,分别为bootloader工程与app工程。
1.1 先说bootloader工程,以IAR为例
建立基本stm32工程需要的工程文件等,工程的建立这里不去说明,有了工程后,需要将该工程代码的链接地址配置为0x08000000 ~ 0x08002000,对于IAR可以在icf文件中进行修改参数达到效果,也可在工程配置中修改达到效果,中断向量表的地址为程序运行起始地址就行。
1.1.1 在icf中修改链接地址
下面是一个icf文件的例子:
/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_4.xml" */
/*-Specials-*/
define symbol __ICFEDIT_intvec_start__ = 0x08000000;
/*-Memory Regions-*/
define symbol __ICFEDIT_region_IROM1_start__ = 0x08000000;
define symbol __ICFEDIT_region_IROM1_end__ = 0x08008000;
define symbol __ICFEDIT_region_IROM2_start__ = 0x0;
define symbol __ICFEDIT_region_IROM2_end__ = 0x0;
define symbol __ICFEDIT_region_EROM1_start__ = 0x0;
define symbol __ICFEDIT_region_EROM1_end__ = 0x0;
define symbol __ICFEDIT_region_EROM2_start__ = 0x0;
define symbol __ICFEDIT_region_EROM2_end__ = 0x0;
define symbol __ICFEDIT_region_EROM3_start__ = 0x0;
define symbol __ICFEDIT_region_EROM3_end__ = 0x0;
define symbol __ICFEDIT_region_IRAM1_start__ = 0x20000000;
define symbol __ICFEDIT_region_IRAM1_end__ = 0x2000BFFF;
define symbol __ICFEDIT_region_IRAM2_start__ = 0x10000000;
define symbol __ICFEDIT_region_IRAM2_end__ = 0x10003FFF;
define symbol __ICFEDIT_region_ERAM1_start__ = 0x0;
define symbol __ICFEDIT_region_ERAM1_end__ = 0x0;
define symbol __ICFEDIT_region_ERAM2_start__ = 0x0;
define symbol __ICFEDIT_region_ERAM2_end__ = 0x0;
define symbol __ICFEDIT_region_ERAM3_start__ = 0x0;
define symbol __ICFEDIT_region_ERAM3_end__ = 0x0;
/*-Sizes-*/
define symbol __ICFEDIT_size_cstack__ = 0x2000;
define symbol __ICFEDIT_size_proc_stack__ = 0x0;
define symbol __ICFEDIT_size_heap__ = 0x2000;
/**** End of ICF editor section. ###ICF###*/
define memory mem with size = 4G;
define region IROM_region = mem:[from __ICFEDIT_region_IROM1_start__ to __ICFEDIT_region_IROM1_end__]
| mem:[from __ICFEDIT_region_IROM2_start__ to __ICFEDIT_region_IROM2_end__];
define region EROM_region = mem:[from __ICFEDIT_region_EROM1_start__ to __ICFEDIT_region_EROM1_end__]
| mem:[from __ICFEDIT_region_EROM2_start__ to __ICFEDIT_region_EROM2_end__]
| mem:[from __ICFEDIT_region_EROM3_start__ to __ICFEDIT_region_EROM3_end__];
define region IRAM_region = mem:[from __ICFEDIT_region_IRAM1_start__ to __ICFEDIT_region_IRAM1_end__];
define region CRAM_region = mem:[from __ICFEDIT_region_IRAM2_start__ to __ICFEDIT_region_IRAM2_end__];
define region ERAM_region = mem:[from __ICFEDIT_region_ERAM1_start__ to __ICFEDIT_region_ERAM1_end__]
| mem:[from __ICFEDIT_region_ERAM2_start__ to __ICFEDIT_region_ERAM2_end__]
| mem:[from __ICFEDIT_region_ERAM3_start__ to __ICFEDIT_region_ERAM3_end__];
define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { };
define block PROC_STACK with alignment = 8, size = __ICFEDIT_size_proc_stack__ { };
define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { };
initialize by copy { readwrite };
if (isdefinedsymbol(__USE_DLIB_PERTHREAD))
{
// Required in a multi-threaded application
initialize by copy with packing = none { section __DLIB_PERTHREAD };
}
place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec };
place in IROM_region { readonly };
place in EROM_region { readonly section application_specific_ro };
place in IRAM_region { readwrite, block CSTACK, block PROC_STACK, block HEAP };
place in CRAM_region { section .textrw };
place in ERAM_region { readwrite section application_specific_rw };
ICFEDIT_intvec_start是中断向量表位置,按照上面说的bootloader地址范围,这里要填起始地址0x08000000,__ICFEDIT_region_ROM_start和ICFEDIT_region_ROM_end是配置这部分bootloader代码在IROM中的位置,根据上面说的划分区域,这里start应是0x08000000、end应是0x08008000,后面的RAM_Start与RAM_end是内存的起始地址与结束地址,IROM和RAM的范围应该能根据实际芯片手册去划分修改。修改后到时编译好程序,链接脚本就会将程序链接到对应IROM中。
直接通过icf文件修改,其实就是一个链接脚本
之后就可以开始撸起bootloader代码了,看你的需求是什么,普遍bootloader的需求都是起到一个搬运工的作用,当发现有app的升级文件需要升级时,从一个存放的介质中搬运到IROM中对应的APP代码位置,然后跳转到APP代码位置进行运行。
#ifndef __TOTOAL_H__
#define __TOTOAL_H__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/****************ST*********************/
#include "stm32l1xx.h"
#include "misc.h"
#include "stm32l1xx_flash.h"
#include "stm32l1xx_usart.h"
#include "stm32l1xx_gpio.h"
#include "stm32l1xx_rcc.h"
#include "stm32l1xx_syscfg.h"
/**************************************/
/*****************SELF********************/
#include "bsp.h"
#include "spi.h"
#include "flash.h"
#include "USART.h"
/***************************************/
#define CONFIG_RELASE 0
#if (CONFIG_RELASE == 0)
#define Debug(fmt,arg...) printf(fmt,##arg)
#else
#define Debug(fmt,arg...)
#endif
#define u8 unsigned char
#define u16 unsigned short
#define u32 unsigned int
#define SPIFLASHBUF_MAX_LEN (4096) //spi flash最大缓冲
#define APPLICATIONADDRESS (0x08002000)
extern unsigned char SPI_FLASH_BUF[]; //Spi Flash使用的临时缓冲
#endif //__TOTOAL_H__
.cpp
#include "total.h"
unsigned char SPI_FLASH_BUF[4096]; //Spi Flash使用的临时缓冲
typedef void (*pFunction)(void);
pFunction Jump_To_Application;
unsigned int JumpAddress;
extern int LoopCount;
/*************************************
*
* Funciton Name : System_Initializes
* Function :
*
*************************************/
void System_Initializes(void)
{
unsigned int UpdateFileLogo = 0;
unsigned int WriteUpdateFileLogo = 0x00000000;
memset(SPI_FLASH_BUF, 0, sizeof(SPI_FLASH_BUF));
BSP_Initializes();
Debug("----[Boot]BootLoader Running... \r\n");
SysTick_Config(36000000 / 1000);
// 读取外部flash有无升级文件需要更新
SPI_Flash_Read((unsigned char *)&UpdateFileLogo, CONFIG_UPDATE_FILE_ADDR_LOGO, 4);
// 是否有未更新的升级文件在外部flash中
if ( UpdateFileLogo == 0x98765432 )
{
unsigned int i = 0;
unsigned int FalshData = 0x0;
FLASH_Unlock();
//擦除内部app程序flash位置
for ( i = 0; i < 100; i++ )
{
FLASH_ErasePage(0x8002000 + (0x400 * i)); //1K 1K擦
}
// 将外部flash存储升级文件的数据,复制到IROM中的APP区域,,升级包的校验什么的都应该去做处理计算 0x19000 100KB
for ( i = 0; i < 0x19000 / 4; i++ )
{
SPI_Flash_Read((unsigned char *)&FalshData, CONFIG_FLASH_SAVE_UPDATE_INDEX + i * 4, 4);
FLASH_FastProgramWord(0x8002000 + i * 4, FalshData);
}
FLASH_Lock();
// 更新完毕,将外部flash代表有无未更新的升级标志变为无更新
SPI_Flash_Write((unsigned char *)&WriteUpdateFileLogo, CONFIG_UPDATE_FILE_ADDR_LOGO, 4);
NVIC_SystemReset(); //复位
}
while ( LoopCount > 0 );
__disable_irq();
/* Test if user code is programmed starting from address "ApplicationAddress" */
if (((*(__IO uint32_t*)APPLICATIONADDRESS) & 0x2FFE0000 ) == 0x20000000)
{
Debug("----[Boot]Jump_To_Application!!!\r\n");
/* Jump to user application */
JumpAddress = *(__IO uint32_t*) (APPLICATIONADDRESS + 4);
Jump_To_Application = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) APPLICATIONADDRESS);
Jump_To_Application();
}
else
{
Debug("----[Boot]APPLICATIONADDRESS is ERROR!!!\r\n");
}
}
int main(void)
{
System_Initializes();
while (1);
}
说明:主要就是判断介质中是否有升级文件需要升级,如果有则擦除IROM中APP代码对应的位置,然后将介质中的APP升级文件搬运到IROM中的APP位置去,没问题后,定义一个函数指针指向APP应用程序代码段的地址,就是链接地址0x08002000,然后调用函数指针直接到这个代码段运行,运行之前设置一下APP链接地址数的堆栈。栈顶设置为起始地址的前4个字节,函数指针指向的其实是这个起始地址的后4个字节,表示中断向量表的复位中断向量位置。所以起始地址+4,然后运行。
1.2 APP工程,APP的工程搭建好后,同样的要修改链接位置,将程序代码链接到指定的位置去,同样的可以通过icf文件修改,也可以直接通过工程修改,这里就不在进行说明,IROM链接的起始地址为0x08002000,上限就是IROM的上限即可。配置好链接地址后,就可以撸起你的代码了,但如果想要让bootloader跳转到app能够成功,则必须在app代码的开始设置好中断向量的表偏移,因为app的代码是在0x08002000开始,所以中断向量表的位置设置为0x08002000即可。
/* 设置中断向量表位置 0x08002000 */
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x2000);
如此就完成了bootloader与app工程。一般bootloader写好后是不需要重复的更改的,而只会该应用程序,可以将bootloader代码编译成bin文件,因为bin文件全都是数据,所以可以转换成一个数组,表示一个代码段,然后将这个数据放入到APP的工程中,将这个bootloader的数组指定到一个IROM的位置,这样就可以达到一次烧写两个工程的效果。
如何将bootloader的数组指定到固定的IROM中,使用#pragma loation = 0Xxxxxxxxx __root 就可以指定,我是这么指定的,指定到了0x08000000位置。同时APP的工程链接地址要为0x08002000,因为按照上面说的对IROM的划分,应该要如此划分。
#pragma location = 0x08000000
__root const unsigned char STM32L15_BootLoader[]={200,20,0,32,117,20,0,8,5,18,0,8,7,18,0,8,0 ...........};