【STM32】制作一个bootloader

工作环境:STM32CubeMX+Keil

相关环境准备这里就不介绍了。

bootloader是什么

  • bootloader就是单片机启动时候运行的一段小程序,这段程序负责单片机固件的更新,也就是单片机选择性的自己给自己下载程序。可以更新,可以不更新,更新的话,boot loader更新完程序后,跳转到新程序运行;不更新的话,boot loader直接跳转到原来的程序去运行。
  • boot loader更新完程序后并不擦除自己,下次启动后依然先运行boot loader程序,又可以选择性的更新或者不更新程序,所以boot loader就是用来管理单片机程序的更新。
  • 在实际的单片机工程项目中,如果加入了boot loader功能,就可以给单片机日后升级程序留出一个接口,方便日后单片机程序更新。当然,这就需要创建亮哥工程项目,一个位boot loader工程,一个为app工程。
  • bootloader工程生成的文件通常下载到ROM或者Flash中的首地址,这样可以保证上电后先运行boot loader程序。而app工程生成的文件则下载到ROM或者Flash中boot loader后面的地址中。也就是说,存在ROM或者Flash中的内容是分为两部分的。
  • 要实现同一个ROM或者Flash中保存两段程序,并且保证不能相互覆盖,则需要在下载程序时指定地址。如在keil中,可以进行如下调整(需要知道Flash的扇区地址)。

【STM32】制作一个bootloader_第1张图片

升级流程

在介绍升级流程之前。我们必须先搞清楚MCU型号对应的Flash分区情况,可以直接查看手册(当然,其实我们也可以在hal库代码中找到相关信息),如下图:

【STM32】制作一个bootloader_第2张图片

因为上文说到,我们有两个程序需要下载到Flash,bootloader文件一般下载在最开始的地址,application一般在后面的地址。

【STM32】制作一个bootloader_第3张图片

上图即为我们预定义的flash分配情况。我们一般是按照整个扇区进行利用的,这样便于擦除和写入。

bootloader从起始地址开始,这个没有疑问。我分配了扇区0给它(这个大小是具体情况而定)。

ota_flag这个是用于存放是否升级的标志位。存放在扇区1,ota_flag只占用4字节,当写入0x87654321的时候,表示需要升级,否则,无需升级。

application占用了两个连续的扇区,扇区2和扇区3。

ota_data也占用了两个连续的扇区,扇区4和扇区5。

【STM32】制作一个bootloader_第4张图片

代码示例

#ifndef BOOTLOADER_H
#define BOOTLOADER_H

#ifdef __cpluscplus
extern "c"
{
#endif

#define FLASH_SECTOR_STEP_SIZE  (128*1024)
#define FLASH_SECTOR_0_ADDRESS  (0x08000000)

#define MCU_OTA_FLAG_ADDRESS (FLASH_SECTOR_0_ADDRESS+ FLASH_SECTOR_STEP_SIZE * FLASH_SECTOR_1)
#define APPLICATION_ADDRESS  (FLASH_SECTOR_0_ADDRESS+ FLASH_SECTOR_STEP_SIZE * FLASH_SECTOR_2)

#define MCU_OTA_DATA_ADDRESS (FLASH_SECTOR_0_ADDRESS+ FLASH_SECTOR_STEP_SIZE * FLASH_SECTOR_4)
#define MCU_OTA_FLAG_VALUE   (0x87654321)

int erase_mcu_ota_flag();
int write_mcu_ota_flag();
int erase_application_sectors();
int write_application_sectors();
int mcu_reset();


#ifdef __cpluscplus
}
#endif
#endif
#include "bootloader.h"
#include "stm32h7xx_hal.h"
#include "stm32h7xx_hal_flash.h"
#include 


int erase_mcu_ota_flag()
{
	  int ret=0;
    uint32_t SectorError = 0;
	
		FLASH_EraseInitTypeDef sector;
		sector.TypeErase = FLASH_TYPEERASE_SECTORS;
		sector.VoltageRange = FLASH_VOLTAGE_RANGE_3;
		sector.Sector = FLASH_SECTOR_1;
		sector.NbSectors = 1;
	
		HAL_FLASH_Unlock();
		ret = HAL_FLASHEx_Erase(&sector, &SectorError);
		HAL_FLASH_Lock();
	  return ret;
}

int write_mcu_ota_flag()
{
	  int ret=0;
		HAL_FLASH_Unlock();
		ret = HAL_FLASH_Program(FLASH_PSIZE_WORD, MCU_OTA_FLAG_ADDRESS, MCU_OTA_FLAG_VALUE);
		HAL_FLASH_Lock();
	  return ret;
}

int erase_application_sectors()
{
	  int ret=0;
    uint32_t SectorError = 0;
	
		FLASH_EraseInitTypeDef sector;
		sector.TypeErase = FLASH_TYPEERASE_SECTORS;
		sector.VoltageRange = FLASH_VOLTAGE_RANGE_3;
		sector.Sector = FLASH_SECTOR_2;
		sector.NbSectors = 2;
	
		HAL_FLASH_Unlock();
		ret = HAL_FLASHEx_Erase(&sector, &SectorError);
		HAL_FLASH_Lock();
	  return ret;
}

int write_application_sectors()
{
	  int ret=0;
		HAL_FLASH_Unlock();
	  for(int i=0;i<MCU_OTA_DATA_ADDRESS;i+=4)
	  {
			ret = HAL_FLASH_Program(FLASH_PSIZE_WORD, APPLICATION_ADDRESS+i, *(__IO uint32_t*)(MCU_OTA_DATA_ADDRESS+i));
	  }
		HAL_FLASH_Lock();
	  return ret;
}

int mcu_reset()
{
	 __set_FAULTMASK(1);
   __NVIC_SystemReset();
}
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "bootloader.h"
#include 
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef void (*pFunction)();
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
	
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */


/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	uint32_t JumpAddress;
    pFunction Jump_To_Application;
	if(*(__IO uint32_t*)MCU_OTA_FLAG_ADDRESS != MCU_OTA_FLAG_VALUE)
	{
		  JumpAddress = *(__IO uint32_t*)(APPLICATION_ADDRESS+4);
      Jump_To_Application = (pFunction)JumpAddress;

      __set_MSP(*(__IO uint32_t*)APPLICATION_ADDRESS);
      Jump_To_Application();
	}
	else
	{ 
		  printf("mcu_ota");  
		
		  erase_application_sectors();
		
		  write_application_sectors();
		
		  erase_mcu_ota_flag();
		
		  mcu_reset();
	}
	
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Supply configuration update enable
  */
  HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);

  /** Configure the main internal regulator output voltage
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);

  while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 1;
  RCC_OscInitStruct.PLL.PLLN = 75;
  RCC_OscInitStruct.PLL.PLLP = 2;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  RCC_OscInitStruct.PLL.PLLR = 2;
  RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3;
  RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
  RCC_OscInitStruct.PLL.PLLFRACN = 0;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
                              |RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
  RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

总结

r line source number

  • @retval None
    */
    void assert_failed(uint8_t file, uint32_t line)
    {
    /
    USER CODE BEGIN 6 /
    /
    User can add his own implementation to report the file name and line number,
    ex: printf(“Wrong parameters value: file %s on line %d\r\n”, file, line) /
    /
    USER CODE END 6 /
    }
    #endif /
    USE_FULL_ASSERT */
    ``

总结

关于地址跳转部分,可以参考STM32 Bootloader程序中Jump2App函数分析

你可能感兴趣的:(STM32,stm32,bootloader)