暂时发现一个bin很难应用到APP A区和B区, bin文件的头尾都有不同, 应该是startup里面用了很多绝对跳转地址. 能解决的兄弟希望你留下你的建议, 谢谢!
需求: ota升级通常要把代码(bin文件)通过串口/can/usb/蓝牙/4G传输到mcu, 这个传输要么通过booltoader处理, 要么通过app处理, 我觉得最好是app处理, 这样减少bottloader的代码量, 避免重复.
app收到bin, 虽然可以存在外部flash上面, 但是最好存到另外一个app区, 这样减少了一个片外再转存片内的过程, 速度快, 成本低, 最重要是可以两个app区相互备份, 升级不成功就换到上次成功的app分区.
mcu片内分区4个: (128K ROM, 1k/page)
0x800000开始: bottloader
0x8002000开始: app A
0x8011000开始: app B
0x801fb00开始: EEPROM A
0x801fc00开始: EEPROM B
1. mcuA收到bin文件后, 把bin写入到更外一个appB分区, 在eeprom里面写入引导到分区B的标志, 然后跳转到bootloader
2. bootlaoder, 开启看门狗, 根据eeprom里面的app分区的标志, 跳转的相应的app分区
3. app 开始运行, 不断喂狗, 经过2s后, 在eeprom里面写入APP B引导成功标志.
4. 如果app B不正常, 比如不能喂狗, 看门狗复位到bootloader, bootloader检查不到有APP B引导成功标志, 恢复到app A. 或者烧录次数/运行成功次数不匹配, 重启在bootloader恢复到app A.
跳转的关键:
1. 设一个函数指针, 指向app A /app B的首地址+4
APP_FUNC jump2app;//定义一个函数指针
jump2app=(APP_FUNC)*(volatile uint32_t *)(app_start_addr+4); /* 获取复位地址 */
2. 设置复位矢量指针
__asm void MSR_MSP(uint32_t addr)
{
MSR MSP, r0
BX r14;
}
MSR_MSP(app_start_addr); /* 设置栈指针 */
__set_MSP(*(volatile uint32_t *)app_start_addr); /* 设置栈指针 */
SCB->VTOR = app_start_addr;
完整测试代码:
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//用的是STM32F103VETx芯片
#define ENABLE_INT() __set_PRIMASK(0) /* 使能全局中断 */
#define DISABLE_INT() __set_PRIMASK(1) /* 禁止全局中断 */
#define PAGE_SIZE 0x400 //1024 byte
#define APP_A_ADDR (0x8000000 + PAGE_SIZE * 8) //0x8002000
#define APP_B_ADDR (0x8000000 + PAGE_SIZE * (8+1+59)) //0x8011000 (128-8-2)=59
#define EEPROM0_ADDR (0x8000000 + PAGE_SIZE * 126) //0x801fb00
#define EEPROM1_ADDR (0x8000000 + PAGE_SIZE * 127) //0x801fc00 top: 0x8020000
typedef void (*APP_FUNC)(); //函数指针类型定义
__asm void MSR_MSP(uint32_t addr)
{
MSR MSP, r0
BX r14;
}
void jump2appAB(uint8_t isAppA)
{
APP_FUNC jump2app;//定义一个函数指针
uint32_t app_start_addr;
if(isAppA)
app_start_addr = APP_A_ADDR;
else
app_start_addr = APP_B_ADDR;
jump2app=(APP_FUNC)*(volatile uint32_t *)(app_start_addr+4); /* 获取复位地址 */
/* 跳转之前关闭相应的中断 */
DISABLE_INT(); //这里关闭全部中断是最为保险的,怕打断跳转过程。后面的APP的main里面也要开启全部中断,否则跟中断的函数全部无效
for (uint32_t i = 0; i < 8; i++) /* 关闭所有中断,清除所有中断挂起标志 */
{
NVIC->ICER[i]=0xFFFFFFFF;
NVIC->ICPR[i]=0xFFFFFFFF;
}
MSR_MSP(app_start_addr); /* 设置栈指针 */
__set_MSP(*(volatile uint32_t *)app_start_addr); /* 设置栈指针 */
SCB->VTOR = app_start_addr;
__set_CONTROL(0); /* 在RTOS工程,这条语句很重要,设置为特权级模式,SP使用MSP指针 */
while(1)
{
if(isAppA)
printf("jump to APP A!\r\n");
else
printf("jump to APP B!\r\n");
jump2app(); /* 跳转至APP */
}
}
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the USART1 and Loop until the end of transmission */
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/* 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();
MX_IWDG_Init();
/* USER CODE BEGIN 2 */
ENABLE_INT();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_IWDG_Refresh(&hiwdg);
HAL_Delay(1000);
printf("I am app B.\r\n");
printf("tick is %d\r\n", HAL_GetTick());
printf("sys clk is %d\r\n", HAL_RCC_GetSysClockFreq());
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
代码要判断是否有效:
1. checksum/CRC
2. 代码的内容和长度合法
bool isAppOK(void)
{
u32 byteLen = *((uint32_t *)EEPROM0_LEN);
if((byteLen > (APP_PAGE_CNT * PAGE_SIZE)) || (byteLen < (APP_PAGE_CNT * 5)))
return false;
// 检查初始栈指针是否合法: // 合法:SP 在 0x20000000~0x2001FFFF 范围内(假设 RAM 大小 ≤128KB)
uint32_t ee = *(__IO uint32_t *)APP_B_ADDR;
if ((ee < 0x20000000) || (ee > 0x2001FFFF))
return false;
//向量表的第二个值是复位向量(Reset Handler),必须指向 Flash 或 ROM 中的合法代码地址(如 0x0800xxxx)。
ee = *(__IO uint32_t *)(APP_B_ADDR+4);
if ((ee < 0x8000800) || (ee > 0x8001000))
return false;
u32 sum = checksum((uint8_t *)APP_B_ADDR, byteLen);
if(sum != *((uint32_t *)EEPROM0_SUM))
return false;
else
return true;
}