【STM32】IAP upgrade 01 bootloader implementation and APP configuration (main)

APP program and offset setting of interrupt vector table

Preface

Through previous understanding , we know that two conditions are required to implement IAP upgrade:
1. The APP program must start at an address with an offset of x after the IAP program;
2. The interrupt vector table of the APP program moves accordingly , the offset of movement is x;

1.APP program starting address setting

Starting address under default conditions

Under default conditions, the start address (Start) of IROM1 in the figure is generally 0x08000000, and the size (Size) is 0x100000, that is, the 1024K space starting from 0x08000000 is our program storage area.
Insert image description here

Set APP starting address

How to set the APP starting address stored in flash

Set the start address (Start) to 0x08010000 and the offset to 0x10000 (64K bytes, which is the space left for BootLoader). Therefore, the FLASH space (Size) left for APP is 0x80000-0x10000=0x70000 (448KB). After setting Start and Size, the starting address setting of the APP program is completed. IRAM is the address of memory. APP can exclusively occupy these memories and we do not need to modify it.

You need to ensure that the starting address of the APP is after the end position of the Bootloader program, and the offset is a multiple of 0x200.
Insert image description here

How to set the APP starting address stored in SRAM

Define the start address (Start) of IROM1 as: 0x20001000, and the size is 0x19000 (100K bytes), that is, starting from the address 0x20000000 offset 0x1000, the SRAM APP code is stored.
STM32F407ZGT6 has only one 128K (not counting CCM) on-chip SRAM. The location where the program is stored and the loading location of variables cannot be repeated, so we need to set the starting address of the address (SRAM) in IRAM1 to 0x2001A000, and the allocation size is only 0x6000( 24K bytes). The SRAM (excluding CCM) allocation of the entire STM32F407ZGT6 is as follows: the first 4K is used by the Bootloader program, the subsequent 100K is used to store the APP program, and the last 24K is used as the memory of the APP program.
Insert image description here

2. Reset the address of the interrupt vector table

The SCB->VTOR register stores the starting address of the interrupt vector table. By default it is determined by the boot mode of BOOT. Redirect this location so that the code can be launched from anywhere in the Flash area.

That is, after you jump to the app program, you have to run the app's interrupt vector table. To achieve this, you must redirect the interrupt vector table when you first enter the app.

/* 设置 NVIC 的向量表偏移寄存器,VTOR 低 9 位保留,即[8:0]保留 */
 SCB->VTOR = baseaddr | (offset & (uint32_t)0xFFFFFE00);

baseaddr is 基地址(即 APP 程序首地址), Offset is偏移量

For example, FLASH APP sets the interrupt vector table offset to 0x10000

SCB->VTOR = FLASH_BASE | ( 0x10000 & (uint32_t)0xFFFFFE00);

For SRAM APP, please refer to the Punctual Atomic Explorer Development Guide V1.1.

3. Set keil to generate bin file

The file generated by MDK by default is a .hex file, which is not convenient for IAP updates. We hope that the file generated is a .bin file.

The difference between hex and bin files

To put it simply: HEX documents are ascii code documents. It cannot be burned directly into the microcontroller. There must be a conversion process in between. But now many programmers are designed to directly import hex files for burning. In fact, this is by design. The bin file is a binary file and can be burned directly into the chip without conversion.

Set keil to generate bin file

In MDK, click the Options for Target→User tab. In the After Build/Rebuild column, check Run #1. We recommend using a relative address. In the input box after the same line of check, write the command line: fromelf - -bin -o …\[email protected] …\Output%L
Insert image description here
D:\Keil_v5\ARM\ARMCC\bin\fromelf.exe is the path to the format conversion tool fromelf.exe that comes with MDK.

Through this step of setting, we can call fromelf.exe after MDK is compiled successfully,
..\..\Output\%Lindicating the currently compiled link file (...\ is a relative path, indicating the superior directory. The compiler starts searching from the project file *.uvprojx by default. According to my You can understand the meaning of the path by looking at the location of the project file Output)
The command –bin –o…\[email protected] means generating a .bin file in the Output directory, and
@L under Keil means behind the Name of Executable under the Output tab. string, that is, an atk_f407.bin file is generated under the Output folder.

After getting the .bin file, we only need to transfer the bin file to the microcontroller to perform the IAP upgrade.

Implementation of Bootloader program

1. The writing of the APP file is consistent with the usual writing. You only need to set the offset address.
2. Set the offset of the new interrupt vector table.
These two points have been written before. The next step is to implement the bootloader.

Bootloader and app belong to two independent projects, not one project! ! !
Related information:
1. The function of writing STM32 internal Flash uses the Flash operation of STM32. STM32 on-chip Flash operation
2. Set the starting address of the bootloader to the first project started after reset.
In this example, the bootloader starts from 0x800 0000, and the APP starts from 0x800 8000. After the bootloader is executed, the APP is executed.

BootLoader's compiler settings
Insert image description here
App's compiler settings
Insert image description here

1. The bootloader program is also an app program

It is a tool we use specifically to transport APP programs, so it follows the same execution process as an ordinary program.
The IAP upgrade program (bootloader) startup process
Insert image description here
is simply to
initialize the clock, GPIO, and peripherals (according to the upgrade method to determine the corresponding peripherals for initialization), then this MCU will have the ability to communicate with the outside.
Insert image description here

For example, if you use the serial port to upgrade, the steps are

before main of bootloader

1. The microcontroller resets and starts execution from 0x800 0000.
2. Execute the Rest_Handler reset interrupt vector function at 0x0800 0004.
3. Execute the reset interrupt vector function and
Insert image description here
call the SystemInit function. In this function, the external crystal oscillator is turned on, the PLL is set, all interrupts are disabled, and the clock is set.

The _main label represents the entry address of an initialization subroutine _main in the C/C++ standard real-time library function. One of the main functions of this program is to initialize the stack (jump to the _user_initial_stackheap label to initialize the stack), initialize the image file, and finally jump to the main function in the C program. This also explains why all C programs must have a main function as the starting point of the program, because this is stipulated by the C/C++ standard real-time library.
4. Jump to the main function of the IAP upgrade program (bootloader).

After main of bootloader

1.Initialization

Initialize and run a normal program without a bootloader, just as it should be. In order to avoid ineffective power consumption losses, generally initialize whatever is needed. Clock initialization, serial port initialization, timer initialization, etc. (can be initialized if scheduled polling or timer delay is used)

2. Check whether the APP program is complete

If certain circumstances occur and the APP program is incomplete, it may cause the program to run away and enter a hardware error.

After the initialization is completed, first check whether the flag bit of the last address in the APP partition matches (for example, whether it is equal to 0x5a5a. Generally, special characters are added at the end of the APP program to check the integrity). If it matches, start a countdown (the time is about a few hundred milliseconds). Depends on the actual situation), wait to enter the APP program, during this period, it will also wait to receive the upgrade instruction; otherwise, it will always wait to receive the upgrade instruction.
Check whether the last four bytes of the estimated bin received are 0x5A5A

int CheckIfAPPCanJump(void)
{
    
    
    uint32_t *pFlag = (uint32_t *)(APP_FLASH_CODE_BASE + APP_FLASH_CODE_SIZE - 4);
    if (*pFlag == 0x5A5A)
    {
    
     return 1; }
    return 0;
}
3.Upgrade

After receiving the upgrade instruction, the upgrade process will be started. During the upgrade period, entry into the APP program is not allowed.

4. Jump to the APP program (how does IAP jump to the user program)

After the upgrade is completed, you need to jump to the APP program (you can jump automatically or add a jump command to jump manually). Preparations before jumping: 1.
Check whether the APP program exists. If it exists, continue to execute the APP. Upgrade
2. Turn off global interrupts (optional, if interrupted during upgrade)
3. Clear all interrupt flags (optional, if interrupted during upgrade)

void Disable_irq(void)
{
    
    
    uint8_t i;
 
	 __disable_irq(); // 关闭总中断
    //__enable_irq();
 
    /* 清除所有中断标志 */
    for(i= 0; i < CRS_IRQn; i++)
    {
    
    
        NVIC_ClearPendingIRQ((IRQn_Type)i);
    }
}

4. Jump to the APP startup program address (of course, you can also jump directly to the main function of the APP program, but you need to re-initialize the stack and other contents before jumping, so it is best to leave it to the startup file for processing and jump to resethandle)

4.1 Set jump address

typedef  void (*pFunction)(void);       //定义一个pFunction类型 这是个函数指针
pFunction Jump_To_Application;          //Jump_To_Application设置为pFunction类型

/*设定跳转地址,FLASH_USER_START_ADDR是APP程序的起始地址*/
JumpAddress = *(__IO uint32_t*) (FLASH_USER_START_ADDR + 4);
Jump_To_Application = (pFunction) JumpAddress;//将APP程序的中断向量表的Rest_Handler地址赋值给Jump_To_Application
__set_MSP(*(__IO uint32_t*) FLASH_USER_START_ADDR); //这里是将把应用程序起始地址设为栈顶指针
Jump_To_Application(); //设置PC指针为复位地址,你可以理解为跳转到应用程序的函数

Set the jump address, FLASH_USER_START_ADDR is the starting address of the APP program, +4 is because every four bytes of the interrupt vector table represents the address of an interrupt function. So JumpAddress points to the APP interrupt vector table Rest_Handler.

Convert the jump address JumpAddress to pFunction type, which can be understood as 编译器将其编译成一个函数.

2. Official demo, taking stm32f10x as an example

IAP/src/main.c 
 
int main(void)
{
    
    
  /* Flash unlock */
  /*flash解锁,因为需要操作flash*/
  FLASH_Unlock();
 
  /* Initialize Key Button mounted on STM3210X-EVAL board */
  /*初始化按键,demo中的升级触发条件为按键按下*/         
  STM_EVAL_PBInit(BUTTON_KEY, BUTTON_MODE_GPIO);   
 
  /* Test if Key push-button on STM3210X-EVAL Board is pressed */
  if (STM_EVAL_PBGetState(BUTTON_KEY)  == 0x00)
  {
    
     
    /* If Key is pressed */
    /*如果按键按下,即触发升级,进行升级*/
    /* Execute the IAP driver in order to re-program the Flash */
    /*初始化串口,demo里面使用的是usart1 + Y-MODe协议*/
    IAP_Init();
    SerialPutString("\r\n================================================================");
    SerialPutString("\r\n=          (C) COPYRIGHT 2010 STMicroelectronics           =");
    SerialPutString("\r\n=                                                          =");
    SerialPutString("\r\n=  In-Application Programming Application  (Version 3.3.0) =");
    SerialPutString("\r\n=                                                          =");
    SerialPutString("\r\n=                       By MCD Application Team            =");
    SerialPutString("\r\n============================================================");
    SerialPutString("\r\n\r\n");
    /*升级菜单,用户自己实现*/
     /*升级中可选关闭所有中断,防止升级被打断,但是在跳转到APP程序后要第一时间打开总中断*/
    Main_Menu ();
  }
  /* Keep the user application running */
  else//不升级 进入APP
  {
    
    
    /* Test if user code is programmed starting from address "ApplicationAddress" */
    /*升级条件不满足,跳转到用户程序处执行用户程序*/
    if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)//检测栈顶指针
    {
    
     
      /* Jump to user application */
      /*ApplicationAddress为用户程序的栈地址,+4便为用户程序的复位中断向量地址*/  
      JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
      Jump_To_Application = (pFunction) JumpAddress;
      /* Initialize user application's Stack Pointer */
      __set_MSP(*(__IO uint32_t*) ApplicationAddress);
      /*执行用户空间的复位中断向量函数,里面主要是进行系统时钟配置,执行用户空间的main函数数*/  
      Jump_To_Application();
    }
  }
 
  while (1)
  {
    
    }
}

Check the top pointer on the stack

Upgraded menu demo, more customized according to needs

void Main_Menu(void)
{
    
    
  uint8_t key = 0;
  
  /* Get the number of block (4 or 2 pages) from where the user program will be loaded */
  /*计算IAP占用的flash页数*/  
  BlockNbr = (FlashDestination - 0x08000000) >> 12;
 
  /* Compute the mask to test if the Flash memory, where the user program will be
     loaded, is write protected */
#if defined (STM32F10X_MD) || defined (STM32F10X_MD_VL)
  UserMemoryMask = ((uint32_t)~((1 << BlockNbr) - 1));
#else /* USE_STM3210E_EVAL */
  if (BlockNbr < 62)
  {
    
    
    UserMemoryMask = ((uint32_t)~((1 << BlockNbr) - 1));
  }
  else
  {
    
    
    UserMemoryMask = ((uint32_t)0x80000000);
  }
#endif /* (STM32F10X_MD) || (STM32F10X_MD_VL) */
 
 
  /* Test if any page of Flash memory where program user will be loaded is write protected */
  /*检测flash中用户空间的写保护锁是否开启*/ 
  if ((FLASH_GetWriteProtectionOptionByte() & UserMemoryMask) != UserMemoryMask)
  {
    
    
    FlashProtection = 1;
  }
  else
  {
    
    
    FlashProtection = 0;
  }
 
  while (1)
  {
    
    
    SerialPutString("\r\n================== Main Menu ============================\r\n\n");
    SerialPutString("  Download Image To the STM32F10x Internal Flash ------- 1\r\n\n");
    SerialPutString("  Upload Image From the STM32F10x Internal Flash ------- 2\r\n\n");
    SerialPutString("  Execute The New Program ------------------------------ 3\r\n\n");
    
    if(FlashProtection != 0)
    {
    
    
      SerialPutString("  Disable the write protection ------------------------- 4\r\n\n");
    }
    
    SerialPutString("==========================================================\r\n\n");
    
    key = GetKey();
 
    if (key == 0x31)
    {
    
    
      /* Download user application in the Flash */
      /*下载程序*/  
      SerialDownload();
    }
    else if (key == 0x32)
    {
    
    
      /* Upload user application from the Flash */
      SerialUpload();
    }
    else if (key == 0x33)
    {
    
    
      JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
 
      /* Jump to user application */
      Jump_To_Application = (pFunction) JumpAddress;
      /* Initialize user application's Stack Pointer */
      __set_MSP(*(__IO uint32_t*) ApplicationAddress);
      Jump_To_Application();
    }
    else if ((key == 0x34) && (FlashProtection == 1))
    {
    
    
      /* Disable the write protection of desired pages */
      FLASH_DisableWriteProtectionPages();
    }
    else
    {
    
    
      if (FlashProtection == 0)
      {
    
    
        SerialPutString("Invalid Number ! ==> The number should be either 1, 2 or 3\r");
      }
      else
      {
    
    
        SerialPutString("Invalid Number ! ==> The number should be either 1, 2, 3 or 4\r");
      } 
    }
  }
}

Enter APP

See the previous article for the initial setting of the APP code.
The APP interrupt vector table needs to be offset . After you jump to the app program, you have to run the app's interrupt vector table later. To achieve this, you must redirect the interrupt vector table when you first enter the app.

Pay attention to resetting the offset of the interrupt vector table (register SCB->VTOR) at the beginning of the main function, otherwise the APP cannot run normally

void SetVectorTable(void)
{
    
    
    uint8_t i;
 
    SCB->VTOR = APP_FLASH_CODE_BASE;
 
    /* 清除所有中断标志 */
    for(i= 0; i < CRS_IRQn; i++)
    {
    
    
        NVIC_ClearPendingIRQ((IRQn_Type)i);
    }
    
    /* 在BOOT中跳转之前若关闭了全局中断, 此需要重新打开 */
    __enable_irq();
}

Guess you like

Origin blog.csdn.net/apythonlearner/article/details/133176744