STM32G070 IAP HAL 库 在线升级

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

在这之前写了STM32G070 hal Flash读写操作。
在这个基础上,做个IAP在线升级。
IAP升级方法有多种:
1、双APP内存法:创建两个APP内存,一个升级,一个运行;
2、BootLoader引导法:当程序升级时候,跳转到BootLoader去,进行升级。
下面介绍双APP内存法,BootLoader引导法后面在写。


提示:下面代码需要引用#include “./BSP/STMFLASH/stmflash.h”,在我上一个文章里面,连接 stm32g070 Flash HAL读写操作

一、Flash分区

STM32G070 Flash总共128kb,对应地址是0x8000000 -0x8020000。
我这里对他分成4个区域,分别是:

1. BootLoader 区 0x08000000 – 0x08004800	18k
在代码中定义如下:
在这里插入图片描述

然后记录好上面地址,后面写代码需要!!
这里需要两套程序:

  1. IAP引导程序,这个程序只能用下载工具烧录。
  2. APP升级程序,也叫应用程序。

二、IAP引导程序实现

1.创建iap文件

iap.h
代码如下(示例):

/**
 ****************************************************************************************************
 * @file        iap.c
 * @author      935848559@qq.com
 * @version     V1.0
 * @date        2024-03-11
 * @brief       IAP 代码
 * @license     Copyright (c) 2020-2032, 
 ****************************************************************************************************
 * @attention
 *
 *
 ****************************************************************************************************
 */

#ifndef __IAP_H
#define __IAP_H


#include "main.h"
#include "./BSP/STMFLASH/stmflash.h"

typedef void (*iapfun)(void);                   /* 定义一个函数类型的参数 */

#define FLASH_APP1_ADDR         APP1_ADDR      /* 第一个应用程序起始地址(存放在内部FLASH) */
                                             
void IAP_printf(void);
void iap_load_app(uint32_t appxaddr);   /* 跳转到APP程序执行 */
void iap_write_appbin(uint32_t appxaddr,uint8_t *appbuf,uint32_t applen);   /* 在指定地址开始,写入bin */


#endif


上述代码需要引用#include “./BSP/STMFLASH/stmflash.h”,在我上一个文章里面,连接 stm32g070 Flash HAL读写操作
这里主要定义是FLASH_APP1_ADDR 程序地址,这个在后面配置时候需要。
iap.c

/**
 ****************************************************************************************************
 * @file        iap.c
 * @author      935848559@qq.com
 * @version     V1.0
 * @date        2024-03-11
 * @brief       IAP 代码
 * @license     Copyright (c) 2020-2032, 
 ****************************************************************************************************
 * @attention
 *
 *
 ****************************************************************************************************
 */


#include "./BSP/STMFLASH/IAP/iap.h"
iapfun jump2app;

/**
 * @brief       IAP 版本提示
 * @param       void
 * @retval      无
 */
void IAP_printf(void)
{
    printf("\r\n");
    printf("******************************************************\r\n");
    printf("*                                                    *\r\n");
    printf("*                IAP     Version: 0.0.2              *\r\n");
    printf("*                                                    *\r\n");
    printf("******************************************************\r\n");
}

/**
 * @brief       IAP写入APP BIN
 * @param       appxaddr : 应用程序的起始地址
 * @param       appbuf   : 应用程序CODE
 * @param       appsize  : 应用程序大小(字节)
 * @retval      无
 */
void iap_write_appbin(uint32_t appxaddr, uint8_t *appbuf, uint32_t appsize)
{
    if(Write_Flash(appxaddr,appbuf,appsize) != FLASH_OK)
        printf("iap_write_appbin error!!\r\n");
    else
        printf("iap_write_appbin ok!!\r\n");
    
}

/**
 * @brief       设置栈顶地址
 * @note        左侧的红X, 属于MDK误报, 实际是没问题的
 * @param       addr: 栈顶地址
 * @retval      无
 */
void sys_msr_msp(uint32_t addr)
{
    __set_MSP(addr);            /* 设置栈顶地址 */
}

iapfun JumpToApplication;

/**
 * @brief       跳转到应用程序段(执行APP)
 * @param       appxaddr : 应用程序的起始地址

 * @retval      无
 */
void iap_load_app(uint32_t appxaddr)
{

    if (((*(volatile  uint32_t *)appxaddr) & 0x2FFE0000) == 0x20000000)     /* 检查栈顶地址是否合法.可以放在内部SRAM共64KB(0x20000000) */
    {
        /* 用户代码区第二个字为程序开始地址(复位地址) */
        jump2app = (iapfun) * (volatile uint32_t *)(appxaddr + 4);
        printf("jump2app = %p\r\n",jump2app);   //打印main地址,我的测试程序显示地址是0x080050d1
        /* 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址) */
        sys_msr_msp(*(volatile uint32_t *)appxaddr);
        printf("appxaddr = %x\r\n",appxaddr);   //打印栈顶地址,应该是0x08005000

        /* 跳转到APP */
        jump2app();
    }
    else
    {
       printf("program in flash is error\r\n");
    }
}

我们在这里需要注意的是跳转地址,跳转地址是APP地址+4,这个地址里面指针指向地址就是我们的程序运行地址。
我的APP程序跳转指向地址是0x080050d1,这里偏移50d1,是因为APP地址我这边设定就是0x800500。 偏移d1是地址程序main函数指针。
在这里插入图片描述

下图是正常STM32程序地址,基地址+4指向是0x80000d1。
在这里插入图片描述

2.main.c

在while循环里面读取到串口传来的APP代码,然后在上电重新启动,在while循环之前,升级app1程序,并加入判断程序是否升级成功。
代码如下:

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_DMA_Init();
  MX_USART3_UART_Init();
  MX_USART4_UART_Init();
  /* USER CODE BEGIN 2 */
    uint8_t t;
          uint32_t oldcount = 0;      /* 老的串口接收数据值 */
    uint32_t applenth = 0;      /* 接收到的app代码长度 */
    
    
 // IAP_printf();
//  extern DMA_HandleTypeDef hdma_usart3_rx;
//  debug_rxStC.Phdma_rx = &hdma_usart3_rx;
//   HAL_UARTEx_ReceiveToIdle_DMA(&huart3,debug_rxStC.rx_buf,USART_REC_LEN);   
  HAL_UART_Receive_IT(&huart3, (uint8_t *)&aRxBuffer3, 1);
  
  uint32_t config = stmflash_read_word(CONFIG_ADDR);
  printf("config = %x\r\n",config);
  #define LEN 10240
  if(config == UPDATE_FLAG)
  {
      STMFLASH_Read(OTA_ADDR,debug_rxStC.rx_buf,LEN);   //读取OTA_ADDR上面的程序,放到内存里面,进行升级
      Write_Flash(APP1_ADDR,debug_rxStC.rx_buf,LEN);    //把内存数据写到APP1_ADDR
      Write_Config(UPDATE_OVER);                        //重置标识位
      iap_load_app(APP1_ADDR);  //跳转程序APP1
  }
  else
    printf("config error\r\n");  
  
  if(config == UPDATE_OVER)
  {
      printf("UPDATE_OVER  iap_load_app\r\n");  
     iap_load_app(APP1_ADDR); 
  }
  
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
         if (debug_rxStC.rx_cnt)
        {
            if (oldcount == debug_rxStC.rx_cnt)   /* 新周期内,没有收到任何数据,认为本次数据接收完成 */
            {
                applenth = debug_rxStC.rx_cnt;
                oldcount = 0;
                debug_rxStC.rx_cnt = 0;
                printf("用户程序接收完成!\r\n");
                printf("代码长度:%dBytes\r\n", applenth);
            }
            else oldcount = debug_rxStC.rx_cnt;
   
            if(applenth >9000)
            {
                printf("用户程序开始升级!\r\n");
                Flash_test(debug_rxStC.rx_buf, applenth); 
            }
       }

        LED_G_TOGGLE();
        LED0_TOGGLE();
        HAL_Delay(500);
      
        
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

三、app程序实现

app程序主要就是3点:

  1. 配置keil
  2. 在main中重映射向量表
  3. 生成bin文件

1.配置keil

在这里插入图片描述
keil偏移地址可以不改,修改原因是为了防止编写代码超了APP内存区域。如果程序比较小,可以不用管的。主要修改就是编译的起始地址。我这里改成是0x0800500,对应就是APP1_ADDR 0x08005000 //APP1 跳转地址

1.在main中重映射向量表

SCB->VTOR = APP1_ADDR; //重映射向量表 下面代码就是这个一句话最重要,APP1_ADDR 0x08005000 //APP1 跳转地址

int main(void)
{
  /* USER CODE BEGIN 1 */
   SCB->VTOR = APP1_ADDR;    //重映射向量表
  /* 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_DMA_Init();
  MX_USART3_UART_Init();
  MX_USART4_UART_Init();
  /* USER CODE BEGIN 2 */
  uint8_t i;

  
  IAP_printf();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
      LED0_TOGGLE();
      LED_R_TOGGLE();
      HAL_Delay(100);
     printf("app1程序!\r\n");  
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

3.生成.bin 文件

生成bin文件方法有很多,这里就介绍一个,用keil自带方法,网上也有很多,这里不赘述
在这里插入图片描述
在4中添加C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin --output .\test.bin .\G070-warehouse\G070-warehouse.axf
这个是我的文件路径。具体方法,可以自行查找,资料比较多,就演示一下!

四、测试结果

在这里插入图片描述
用串口发送bin文件,接收后,重新上电,就完成升级!

五、出现的问题

在IAP代码中,一开始总是升级不成功,程序完成写入后,跳转的APP中就死机,无法仿真,后来发现是使用的DMA串口空闲中断 接收出现错误,可以接收但是数据不完整。这里使用的串口中断接收,没有问题。

总结

IAP升级总结就是4点:

  1. boot程序:分好区域,重新写入引导程序,引导程序就是flash读取和写入。
  2. boot程序的keil的flash可以不用配置,配置flash大小就是防止boot超出内存。
  3. 逻辑:用config区,提示升级标志,这个区域可以用结构体解析,存储应用其他信息
  4. app程序:就是重新配置中断向量表和keil的flash编译地址。
  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值