单片机通用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
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文件,可以看到变量被分配的地址及段:
在MDK中定义
为了统一都用
.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;
}
Original: https://blog.csdn.net/weixin_42892101/article/details/127808322
Author: aron566
Title: 单片机通用Bootloader框架-优化
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/652411/
转载文章受原作者版权保护。转载请注明原作者出处!