单片机通用Bootloader框架-优化

单片机通用Bootloader框架-优化

这篇文章是对之前写的Bootloader的一次优化,之前博文

单片机通用Bootloader框架-优化

此次优化是看到这篇公众号推的(公众号写的真的太好了,每次必看):安富莱原文,主要讲了BL阶段需要跳转到APP程序执行前,如何省去,一些反初始化外设,那些复杂的操作,直接干净的跳转到APP区执行,文章中提到了RAM区一个段,不进行初始化的段,当单片机没有掉电的情况下,仅是软复位操作,不会改变这个段里面变量的数值,利用这个段去做一些事情,如BL需要的标志位,程序更新完毕了,改下这个段里面变量的标志位,复位一下在 执行初始化硬件前,根据标志位的情况去 执行跳转,免去由原来的跳转前反初始化外设(即恢复外设关闭中断这些操作)

另外片上一般都有BKP(即后备存储区),之前都是想到用来存放RTC是否是第一次配置的标志位,用它可以

比如下面的RTC配置,利用了 RTC_BKP_DR0数据区域存储标志位,只要MCU不掉电,这个数据一直在,而且不编程(动它)不会被改变


#include

#include "RTC_Port.h"
#include "main.h"
#include "rtc.h"

#ifdef __cplusplus
extern "C" {
#endif

#define RTC_FIRST_CONFIG_FLAG     0x0566

static RTC_TIME_DATE_Typedef_t Time_Date;

void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{

  UNUSED(hrtc);

}

void RTC_Port_Set_Alarm_Close(void)
{

  HAL_RTC_DeactivateAlarm(&hrtc, RTC_ALARM_A);
}

void RTC_Port_Set_Alarm(uint8_t H, uint8_t Min, uint8_t S, uint8_t W, uint32_t Mask, uint8_t Week_Day_Sel)
{
  RTC_AlarmTypeDef sAlarm = {0};

  sAlarm.AlarmTime.Hours = H;
  sAlarm.AlarmTime.Minutes = Min;
  sAlarm.AlarmTime.Seconds = S;
  sAlarm.AlarmTime.SubSeconds = 0;
  sAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  sAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;
  sAlarm.AlarmMask = Mask;
  sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
  if(Week_Day_Sel == 1)
  {
    sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_WEEKDAY;
  }
  else
  {
    sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
  }
  sAlarm.AlarmDateWeekDay = W;
  sAlarm.Alarm = RTC_ALARM_A;
  if(HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN) != HAL_OK)
  {
    printf("Set Alarm Error\n");
  }
}

int32_t RTC_Port_GetWeekDay(int y, int m, int d)
{
  int year = y + 2000;
  int month = m;
  int date = d;
  if(month == 1) month = 13, year--;
  if(month == 2) month = 14, year--;
  int weekdey = (date + 2 * month + 3 * (month + 1) / 5 + year + year / 4 - year / 100 + year / 400) % 7;
  return weekdey + 1;
}

uint32_t RTC_Port_Get_TimeStamp(void)
{
  struct tm tm_data;
  const RTC_TIME_DATE_Typedef_t *T = RTC_Port_Get_Time_Date();
  tm_data.tm_year = (int)T->Year + 2000 - 1900;
  tm_data.tm_mon  = (int)T->Month - 1;
  tm_data.tm_mday = (int)T->Day;
  tm_data.tm_hour = (int)T->Hour;
  tm_data.tm_min  = (int)T->Minute;
  tm_data.tm_sec  = (int)T->Second;

  return (uint32_t)mktime(&tm_data);
}

const RTC_TIME_DATE_Typedef_t *RTC_Port_Get_Time_Date(void)
{
  RTC_TimeTypeDef sTime = {0};
  RTC_DateTypeDef sDate = {0};

  if(HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
  {
    printf("rtc get time error.\r\n");
    return &Time_Date;
  }
  Time_Date.Hour = sTime.Hours;
  Time_Date.Minute = sTime.Minutes;
  Time_Date.Second = sTime.Seconds;

  if(HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
  {
    printf("rtc get date error.\r\n");
    return &Time_Date;
  }
  Time_Date.Year = 2000 + sDate.Year;
  Time_Date.Month = sDate.Month;
  Time_Date.Day = sDate.Date;
  Time_Date.Weekday = sDate.WeekDay;
  return &Time_Date;
}

void RTC_Port_Set_Time_Date(uint8_t H, uint8_t Min, uint8_t S, uint8_t D, uint8_t Month, uint8_t Y, uint8_t W)
{
  RTC_TimeTypeDef sTime = {0};
  RTC_DateTypeDef sDate = {0};
  sTime.Hours = H;
  sTime.Minutes = Min;
  sTime.Seconds = S;
  sDate.Date = D;
  sDate.Month = Month;
  sDate.Year = Y;
  sDate.WeekDay = W;

  sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  sTime.StoreOperation = RTC_STOREOPERATION_RESET;
  if(HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
  {
    printf("Set Time Error\n");
  }

  if(HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
  {
    printf("Set Date Error\n");
  }
}

bool RTC_Port_Is_First_Config(void)
{

  if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0) != RTC_FIRST_CONFIG_FLAG)
  {
    return true;
  }
  return false;
}

void RTC_Port_Init(void)
{

  if(RTC_Port_Is_First_Config() == true)
  {

    RTC_Port_Set_Time_Date(12, 12, 12, 9, 8, 22, 2);

    RTC_Port_Set_Alarm_Close();

    HAL_PWR_EnableBkUpAccess();
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, RTC_FIRST_CONFIG_FLAG);
    printf("RTC Port First Config.\r\n");
    return;
  }
  printf("RTC Port Init ok.\r\n");
}

#ifdef __cplusplus
}
#endif

定义一个标志位变量存储在不初始化的段

类型定义


typedef enum
{
  BL_UNKNOW_FLAG  = 0,
  BL_JUMP_APP     = 0x0566,
  BL_JUMP_BL      = 0x0577,
}BL_PORT_STEP_Typedef_t;

typedef struct
{
  BL_PORT_STEP_Typedef_t Flag;
  uint32_t App_Addr;
  uint32_t Stack_Base_Addr;
}BL_PORT_JUMP_Typedef_t;

在IAR中定义

我们打开IAR的分散加载文件 ***.icf,找到不初始化的段名 .noinit

单片机通用Bootloader框架-优化
static BL_PORT_JUMP_Typedef_t BL_Static_Jump_Data MATH_PORT_SECTION(".noinit");

MATH_PORT_SECTION是为了指定该变量分配到哪个段中,宏定义如下:不同的编译器不同关键字实现

#ifndef MATH_PORT_SECTION
  #if defined (__CC_ARM)
    #if ENABLE_SECTION_SPACE
      #define MATH_PORT_SECTION(x)                 __attribute__((section(x)))
    #else
      #define MATH_PORT_SECTION(x)
    #endif
      #define MATH_PORT_USED                       __attribute__((used))
      #define MATH_PORT_UNUSED                     __attribute__((unused))
  #elif defined (__IAR_SYSTEMS_ICC__)
    #if ENABLE_SECTION_SPACE
      #define MATH_PORT_SECTION(x)                 @ x
    #else
      #define MATH_PORT_SECTION(x)
    #endif
      #define MATH_PORT_USED                       __root
      #define MATH_PORT_UNUSED
  #elif defined (__GNUC__)
    #if ENABLE_SECTION_SPACE
      #define MATH_PORT_SECTION(x)                 __attribute__((section(x)))
    #else
      #define MATH_PORT_SECTION(x)
    #endif
      #define MATH_PORT_USED                       __attribute__((used))
      #define MATH_PORT_UNUSED                     __attribute__((unused))
  #else
      #define MATH_PORT_SECTION(x)
      #define MATH_PORT_USED
      #define MATH_PORT_UNUSED
  #endif

  #ifndef SECTION
    #ifdef __CC_ARM
        #define SECTION(x)                 __attribute__((section(x)))
        #define USED                       __attribute__((used))
        #define UNUSEDX                    __attribute__((unused))
    #elif defined (__IAR_SYSTEMS_ICC__)
        #define SECTION(x)                 @ x
        #define USED                       __root
    #elif defined (__GNUC__)
        #define SECTION(x)                 __attribute__((section(x)))
        #define USED                       __attribute__((used))
        #define UNUSED                     __attribute__((unused))
    #else
        #error not supported tool chain
    #endif
  #endif
#endif

找到map文件,可以看到变量被分配的地址及段:

单片机通用Bootloader框架-优化
单片机通用Bootloader框架-优化

在MDK中定义

单片机通用Bootloader框架-优化
为了统一都用 .noinit
***.sct分散加载文件中,修改如下:关键字 UNINIT
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

;LR_IROM1 0x08020000 0x001E0000  {    ; load region size_region
;  ER_IROM1 0x08020000 0x001E0000  {  ; load address = execution address
LR_IROM1 0x08040000 0x001C0000  {    ; load region size_region
  ER_IROM1 0x08040000 0x001C0000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_RAM1 0x00000000 0x00010000  {  ; 64KB ITCM Code Data 400MHz
     *(USE_ITCM_SPACE)
     *(.text.aidis.ro)
   *(.text.aidis.math)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 UNINIT 0x0000000C  { ; 12Bytes DTCM Data 400MHz
    *(.noinit)
   .ANY (+RW +ZI)
  }

  RW_IRAM1 0x2000000C 0x0001FFF4  { ; 128KB-12Bytes DTCM Data 400MHz
    *(USE_DTCM_SPACE)
   .ANY (+RW +ZI)
  }
  RW_IRAM2 0x24000000 0x00080000  { ; 512KB Main Data 200MHz
   .ANY (+RW +ZI)
  }
    RW_IRAM3 0x30000000 0x00020000  { ; 128KB D2 DMA Data 200MHz
    *(USE_LCD_DMA_BUF_SPACE)
  }
  RW_IRAM4 0x30020000 0x00020000  { ; 128KB D2 DMA Data 200MHz
    *(USE_IDEL1_DMA_BUF_SPACE)
  }
  RW_IRAM5 0x30040000 0x00008000  { ; 32KB D2 DMA Data 200MHz
    *(USE_IDEL2_DMA_BUF_SPACE)
    .ANY (+RW +ZI)
  }
  RW_IRAM6 0x38000000 0x00010000  { ; 64KB D3 DMA Data 200MHz
   *(USE_DMA_BUF_SPACE)
  }
    RW_IRAM7 0x38800000 0x00001000  {   ; 4KB Low Power Save Data 200MHz
   *(USE_BACKUP_BUF_SPACE)
  }
}

修改跳转接口

原来跳转的地方,改为直接复位,这样在BL想要跳转到APP分区时,将APP分区的信息给到标志变量,然后软复位,软复位后先检查是否需要跳转,需要跳转直接跳转到APP分区,不需要进行硬件恢复操作


static void Jump_To_Application(uint32_t App_Addr, uint32_t Stack_Base_Addr)
{
  uint32_t JumpAddress = 0;
  printf("jump to : %08X, StackBase : %08X\r\n", App_Addr, Stack_Base_Addr);
  printf("read top address of stack : %08X\r\n", (*(__IO uint32_t *)App_Addr));
  printf("check stack in:%08X\r\n\n", ((*(__IO uint32_t *)App_Addr) & STACK_ADDR_MASK));

  if(((*(__IO uint32_t *)App_Addr) & STACK_ADDR_MASK) == Stack_Base_Addr)
  {

    JumpAddress = *(__IO uint32_t *)(App_Addr + 4);
    JumpToApplication = (pJumpFunction)JumpAddress;

    if(App_Addr == USER_BL_ADDR)
    {
      BL_Static_Jump_Data.Flag = BL_JUMP_BL;
    }
    else
    {
      BL_Static_Jump_Data.Flag = BL_JUMP_APP;
    }

    BL_Static_Jump_Data.App_Addr = App_Addr;
    BL_Static_Jump_Data.Stack_Base_Addr = Stack_Base_Addr;

    printf("System Reset.\r\n");

    HAL_NVIC_SystemReset();
    return;
#if 0

    __disable_irq();

    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;

    HAL_RCC_DeInit();

    HAL_DeInit();
    Serial_Port_DeInit();

    for(int i = 0; i < 8; i++)
    {
      NVIC->ICER[i] = 0xFFFFFFFF;
      NVIC->ICPR[i] = 0xFFFFFFFF;
    }

    __enable_irq();

    __set_MSP(*(__IO uint32_t*)App_Addr);
#if USE_RTOS_BOOTLOADER

    __set_CONTROL(0);
#endif
    JumpToApplication();
#endif
  }
#if 0
  printf("Try Jump Faild.\r\n");
#endif
}

在初始化硬件之前,执行以下判断跳转:


bool Bootloader_Port_Try_Jump_To_Application(void)
{

  if(BL_JUMP_APP != BL_Static_Jump_Data.Flag)
  {
    return false;
  }
  uint32_t JumpAddress = 0;

  if(((*(__IO uint32_t *)BL_Static_Jump_Data.App_Addr) & STACK_ADDR_MASK) == BL_Static_Jump_Data.Stack_Base_Addr)
  {

    JumpAddress = *(__IO uint32_t *)(BL_Static_Jump_Data.App_Addr + 4);
    JumpToApplication = (pJumpFunction)JumpAddress;

    JumpToApplication();
  }
  return true;
}

单片机通用Bootloader框架-优化

Original: https://blog.csdn.net/weixin_42892101/article/details/127808322
Author: aron566
Title: 单片机通用Bootloader框架-优化

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/652411/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球