IAR STM32 BootLoader

2021-06-23  本文已影响0人  XBruce

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 ...........};
上一篇 下一篇

猜你喜欢

热点阅读