Bootloader说明
编写bootloader程序步骤说明:
- 根据程序代码区域的大小划分空间
- 编写Bootloader
- 码APP
1、阅读手册,划分空间
我用的是这个型号的,他的空间有256KB是可以用来运行代码的。
所以可以这样划分:
- Bootloader FLASH_BASE(((uint32_t)0x08000000U)) 32KB
- APP1 FLASH_BASE | 0x08000 64KB
- APP2 FLASH_BASE | 0x18000 64KB
程序流程规划:
2、编写Bootloader
主函数:
#define BOOTLOADER_APP1_ADDRESS 0x08008000U //定义APP地址
#define BOOTLOADER_APP2_ADDRESS 0x08018000U
int main(){
//1、初始化【串口、flash、定时器等外设】
//2、初始化延时等待,例如等待多久后开始进入app区执行
//3、判断升级标志位,如果有就更新程序
while(1){
//2秒超时时间到、开始跳转
if(isTimeOut(&time,2)){
if(((*(__IO uint32_t *)(BOOTLOADER_APP1_ADDRESS + 4)) & 0xFF000000) == 0x08000000){
iap_load_app(BOOTLOADER_APP1_ADDRESS);
}else{
//这里没有可执行程序APP1
//检查APP2区域是否有程序
if(((*(__IO uint32_t *)(BOOTLOADER_APP2_ADDRESS + 4)) & 0xFF000000) == 0x08000000){
iap_load_app(BOOTLOADER_APP2_ADDRESS);
}else{
//APP1 APP2均无程序
}
}
}else{
//等待其他操作,可以在这里选择启动模式【因为咱们有两个APP区,所以在这里可以选择强制从其中一个启动】
...
}
}
}
iap.h
#ifndef __IAP_H__
#define __IAP_H__
#include "systick.h"
//用户根据自己的需要设置
#define STM32_FLASH_SIZE 1024 //所选STM32的FLASH容量大小(单位为K)
#if STM32_FLASH_SIZE < 256
#define STM_SECTOR_SIZE 1024 //字节
#else
#define STM_SECTOR_SIZE 2048
#endif
//
//FLASH起始地址
#define GD32_FLASH_BASE 0x08000000 //GD32 FLASH的起始地址
typedef void (*iapfun)(void); //定义一个函数类型的参数.
void iap_load_app(uint32_t appxaddr); //跳转到APP程序执行
void iap_write_appbin(uint32_t appxaddr,uint8_t *appbuf,uint32_t applen); //在指定地址开始,写入bin
#endif
iap.c
iapfun jump2app;
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(uint32_t addr)
{
MSR MSP, r0 //set Main Stack value
BX r14
}
//WriteAddr:应用程序的起始地址,必须为某扇区的起始地址
//pBuffer:应用程序CODE.
//NumToWrite:应用程序大小(字节).
void iap_write_appbin(uint32_t WriteAddr,uint8_t *pBuffer,uint32_t NumToWrite)
{
uint32_t i = 0;
uint16_t temp;
if (WriteAddr < GD32_FLASH_BASE || (WriteAddr >= (GD32_FLASH_BASE + 1024 * STM32_FLASH_SIZE)))
{
printf("地址越界!\r\n");
return;
}
if(WriteAddr % STM_SECTOR_SIZE)
{
printf("地址非FLASH扇区首地址!\r\n");
return;
}
/*开始写入*/
fmc_unlock();
while(1)
{
if(NumToWrite == 0)//上一个扇区刚好完整写完
{
break;
}
else if(NumToWrite < STM_SECTOR_SIZE)//剩余要写入的内容不到一个扇区
{
fmc_page_erase(WriteAddr); //擦除这个扇区
for(i = 0; i < NumToWrite; i += 2)
{
temp = (uint16_t)pBuffer[i + 1] << 8;
temp |= (uint16_t)pBuffer[i];
fmc_halfword_program(WriteAddr, temp);
WriteAddr += 2;
}
break; //写入结束,退出while
}
else
{
fmc_page_erase(WriteAddr); //擦除这个扇区
//写入整个扇区
for(i = 0; i < STM_SECTOR_SIZE; i += 2)
{
temp = (uint16_t)pBuffer[i + 1] << 8;
temp |= (uint16_t)pBuffer[i];
fmc_halfword_program(WriteAddr, temp);
WriteAddr += 2;
}
pBuffer += STM_SECTOR_SIZE;
NumToWrite -= STM_SECTOR_SIZE;
}
}
fmc_lock(); //上锁
}
//跳转到应用程序段
//appxaddr:用户代码起始地址
void iap_load_app(uint32_t appxaddr)
{
unsigned char i = 0;
if(((*(__IO uint32_t*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法
{
/* 首地址是MSP,地址+4是复位中断服务程序地址 */
jump2app=(iapfun)*(__IO uint32_t*)(appxaddr+4);
/* 关闭全局中断 */
__set_PRIMASK(1);
/* 关闭滴答定时器,复位到默认值 */
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;
/* 设置所有时钟到默认状态 */
//RCC_DeInit();
rcu_deinit();
/* 关闭所有中断,清除所有中断挂起标志 */
for (i = 0; i < 8; i++)
{
NVIC->ICER[i]=0xFFFFFFFF;
NVIC->ICPR[i]=0xFFFFFFFF;
}
/* 使能全局中断 */
__set_PRIMASK(0);
/* 在RTOS工程,这条语句很重要,设置为特权级模式,使用MSP指针 */
__set_CONTROL(0);
MSR_MSP(*(__IO uint32_t*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
jump2app(); //跳转到APP.
/* 跳转成功的话,不会执行到这里,用户可以在这里添加代码 */
printf("-----------jump error!-----------\n");
while (1)
{
}
}
}
3、码APP
那么APP区主要干几件事
这里记得设置一下
然后bin文件这也需要设置一下:
// 根据你的实际工程添加 生成路径 axf文件路径
fromelf --bin --output=.\bin\controller.bin .\Objects\controller.axf
int main(){
//0、配置中断向量表
SCB->VTOR = FLASH_BASE | 0x08000;
//1、初始化
while(1){
//2、接收升级数据包
updatarecieve();
if(updata){
//3、更新升级标志位
//4、复位
NVIC_SystemReset();
}
}
}
具体的代码有点多,也没有整理。大家感兴趣直接上gitee看吧。