Bootloader开发流程

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看吧。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FlechazoCLF

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值