stm32cubemx IAP升级(一)

stm32cubemx IAP升级- Bootloader的制作

板卡:Nucleo-L412
平台:macbook pro
工具:vscode stm32cubemx stm32cubeProgramer cmake toolchain

分区

L412 自带128K的flash,所以我们可以这样分区,
printf(“|============ flash pration table ============|\r\n”);
printf(“| name | offset | size |\r\n”);
printf(“|---------------------------------------------|\r\n”);
printf(“| bootloader | 0x08000000 | 0x00005000 20K | \r\n”);
printf(“| setting | 0x08005000 | 0x00002000 8K |\r\n”);
printf(“| App | 0x08007000 | 0x0000C800 50K |\r\n”);
printf(“| download | 0x08013800 | 0x0000C800 50K |\r\n”);

bootloader从0x08000000-0x08005000 大小20K
setting 从0x08005000-0x08007000 大小08K
App 从0x08007000-0x08013800大小50K
Download 从0x08013800-0x08020000大小50k

整体思路

Bootloader从0x08000000地址运行,而App程序从地址0x8007000运行。
App运行时,可通过iic或者uart接收到上位机升级的指令。App接收到要升级的程序,存在Download分区,并要setting区升级标志位置为1,重启。bootloader从Setting读取升级标志位,如果升级位为1,从Download分区copy数据到App区,copy完成后跳转到0x08007000地址,实现App的升级。

Stm32cubemx配置

1、设置时钟源跟配置时钟树,这个根据自己的平台配置即可。
2、配置一路串口,用作打印用。
3、配置一路定时器,用于改变led灯闪烁频率,来区分正常启动还是升级的指示灯。
4、生成代码选择Makefile,因为一直做linux开发,实在用不惯keil。

Bootloader代码

1、开机打印分区信息

void print_text_message(void)
{
    #ifdef USER_APPLICATION_CODE   
    printf("|------------- Enter Application -------------|\r\n");
    #endif
    #ifdef USER_BOOTLOADER_CODE 
    printf("|------------- Enter BootLoader --------------|\r\n");
    #endif
    printf("\r\n");
    printf("Version: %s %s\r\n",APPCATION_VERSION_DATE,APPCATION_VERSION_TIME);
    printf("\r\n");
    printf("|----------L412 flash toal size 128K----------|\r\n");
    printf("\r\n");
    printf("|============ flash pration table ============|\r\n");
    printf("| name       | offset     | size              |\r\n");
    printf("|---------------------------------------------|\r\n");
    printf("| bootloader | 0x08000000 | 0x00005000   20K  | \r\n");
    printf("| setting    | 0x08005000 | 0x00002000   8K   |\r\n");
    printf("| App        | 0x08007000 | 0x0000C800   50K  |\r\n");
    printf("| download   | 0x08013800 | 0x0000C800   50K  |\r\n");
    printf("|=============================================|\r\n");
}

其实IAP升级是两个工程,两个工程里都有自己的Makefile。而有一部分代码是两个工程共用的,所以我们可以将共用代码放在另外一个目录里,在Makefile里引用或者编译成静态库即可。
整个工程的代码架构是这样的。Common里即是boot跟app共用的代码。
stm32cubemx IAP升级(一)_第1张图片
2、重定向printf函数 main.c里添加

#ifdef USE_UART_PRINTF  //Uart使用
int _write(int fd, char *ch, int len)
{
  HAL_UART_Transmit(&huart1, (uint8_t*)ch, len, 0xFFFF);
  return len;
}
#endif

#ifdef USE_SVO_PRINTF //Swv log使用
int _write(int file, char *ptr, int len)
{
	int DataIdx;
	for (DataIdx = 0; DataIdx < len; DataIdx++)
	{
		ITM_SendChar(*ptr++);       // 把printf函数重定向到ITM_SendChar
	}
	return len;
}
#endif

3、定时器
定时器开启函数 : HAL_TIM_Base_Start_IT(&htim2);
定时器回调函数:

led_freq为LED_TOGGLE_100_MS时 100ms闪烁一次
LED_TOGGLE_500_MS 时500ms闪烁一次

int led_cnt = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{

  if (htim->Instance == htim2.Instance)
  {
    if (led_freq == LED_TOGGLE_100_MS)
    {
      HAL_GPIO_TogglePin(UserLed_GPIO_Port,UserLed_Pin);
      led_cnt = 0;
    }
    else if (led_freq == LED_TOGGLE_500_MS)
    {
      if (led_cnt == LED_TOGGLE_500_MS)
      {
        HAL_GPIO_TogglePin(UserLed_GPIO_Port,UserLed_Pin);
        led_cnt = 0;
      }
    }

    led_cnt++;
  }
}

3、读取升级标志位:

system_info_get_update_flag(&update_flag);

MI_BOOL system_info_get_update_flag(MI_U8 *flag)
{
    MI_SystemInfo system_info;

    memset(&system_info,0,sizeof(system_info));
    stm32_flash_read(SYSTEM_INFO_START_ADDR,(uint8_t *)&system_info,sizeof(system_info));
    *flag = system_info.update_flag;

    return MI_TRUE;
}

升级标志位,是结构体形式写进0x08005000地址的,所以读的时候按一个字节从0x08005000以结构体形式再读出来即可。

/**
 * 系统信息结构体
*/
typedef struct system_info
{
    /* data */
    MI_CHAR model_name[64];
    MI_CHAR band_name[12];

    MI_CHAR soft_version[36];
    MI_CHAR settings_version[36];
    MI_CHAR app_version[36];
    MI_CHAR boot_version[36];

    MI_U32 build_date;
    MI_U32 build_time;

    MI_U8  update_flag;

    MI_GainMuteInfo gain_mute_info;


}MI_SystemInfo;

MI_BOOL stm32_flash_read(MI_U32 dest_addr, MI_U8* buff, MI_U32 Len)
{

    MI_U32 i;
    for(i = 0; i < Len; i++){
            buff[i] = *(__IO MI_U8*)(dest_addr + i);
    }
    /* Return a valid address to avoid HardFault */
    return 0;
}

main.c读取时代码如下:

 system_info_get_update_flag(&update_flag);

  //create_setting_data();

  if (update_flag == SYSTEM_UPDATE)
  {
    // new Copy Application bin and update

    HAL_TIM_Base_Start_IT(&htim2);

    printf("Now Need copy Application bin and Update\r\n");

    HAL_Delay(1000);

    printf("\r\n");

    led_freq = LED_TOGGLE_100_MS;
    copy_download_to_app();

    printf("\r\n");
    printf("update success,and go to Application\r\n");

    //升级成功后,将升级标志位清0
    system_info_set_update_flag(SYSTEM_NO_UPGRADE);
    
    led_freq = LED_TOGGLE_500_MS;
    HAL_Delay(4000);

    jumpApplication();

  }
  else if (update_flag == SYSTEM_NO_UPGRADE)
  {
    printf("\r\n");
    printf("Now go to Application ,please wait! \r\n");
    printf("\r\n");
    HAL_Delay(1000);
    jumpApplication();
  }

Copy数据代码

MI_BOOL copy_download_to_app(void)
{
    MI_U8 buffer[1024] = {0};  //每次从download读1K字节,然后写入1K
    MI_U32 w_count = 0;
    MI_U32 w_len = 0;

    w_count = APP_SECTOR_SIZE / sizeof(buffer);
    w_len = sizeof(buffer);
    // 将App区域擦除
    stm32_erase_flash(APP_START_SECTOR_ADDR,APP_END_SECTOR_ADDR);
    for (int i = 0;i < w_count;i++)
    {
        stm32_flash_read(DOWNLOAD_START_SECTOR_ADDR + (w_len * i),buffer,w_len);
        HAL_Delay(500);
        stm32_flash_write(APP_START_SECTOR_ADDR + (w_len * i),buffer,w_len);
        
        printf("update.............................%03d [100] \r\n",((i+1) * 100 /w_count));
    }

    return MI_TRUE;
}

4、App跳转

MI_VOID jumpApplication(MI_VOID)
{
  HAL_GPIO_DeInit(UserLed_GPIO_Port,UserLed_Pin);
  HAL_UART_MspDeInit(&huart1);
  HAL_TIM_Base_MspDeInit(&htim2); 
  jump_app(APP_START_SECTOR_ADDR);
}
  
uint8_t jump_app(uint32_t app_addr) 
{
    uint32_t jump_addr;
    jump_callback cb;

    if (((*(__IO uint32_t*)app_addr) & 0x2FFE0000 ) == 0x20000000) 
    {  
        jump_addr = *(__IO uint32_t*) (app_addr + 4);  
        cb = (jump_callback)jump_addr;  
        __set_MSP(*(__IO uint32_t*)app_addr);  
        //__set_CONTROL(0);
        cb();
        return 1;
    } 
    return 0;
}

5、修改STM32L412RBTxP_FLASH.ld

/* Specify the memory areas */
MEMORY
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 40K
RAM2 (xrw)      : ORIGIN = 0x10000000, LENGTH = 8K
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 20K
}

因为bootloader从0x08000000启动,大小为20K,故修改如上。

6、编译完成后,用stm32cubemxProgramer将Bootloader.bin烧录进板卡,地址填0x08000000.

整体代码在
https://download.csdn.net/download/weixin_43932857/87693349

你可能感兴趣的:(stm32,Iap升级,stm32)