STM32F103 OTA bootloader 怎样进入不同的APP分区?

暂时发现一个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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值