【STM32】IAP升级01 bootloader实现以及APP配置(主要)

news2024/12/24 9:46:39

APP程序以及中断向量表的偏移设置

前言

通过之前的了解 之前的了解,我们知道实现IAP升级需要两个条件:
1.APP程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始;
2.APP程序的中断向量表相应的移动,移动的偏移量为 x;

1.APP 程序起始地址设置

默认条件下的起始地址

默认的条件下,图中 IROM1 的起始地址(Start)一般为 0x08000000,大小(Size)为 0x100000,即从 0x08000000 开始的 1024K 空间为我们的程序存储区。
在这里插入图片描述

设置APP起始地址

存储在flash上的APP起始地址设置方法

设置起始地址(Start)为 0x08010000,偏移量为 0x10000(64K 字节,即留给 BootLoader 的空间),因而,留给 APP 用的 FLASH 空间(Size)为 0x80000-0x10000=0x70000(448KB)。设置好 Start 和 Size,就完成 APP 程序的起始地址设置。IRAM 是内存的地址,APP 可以独占这些内存,我们不需要修改。

需要确保 APP 起始地址在 Bootloader 程序结束位置之后,并且偏移量为 0x200 的倍数即可
在这里插入图片描述

存储在SRAM上的APP起始地址设置方法

将 IROM1 的起始地址(Start)定义为:0x20001000,大小为 0x19000(100K 字节),即从地址 0x20000000 偏移0x1000 开始,存放 SRAM APP 代码。
STM32F407ZGT6 只有一个 128K(不算 CCM)的片内 SRAM,存放程序的位置与变量的加载位置不能重复,所以我们需要设置 IRAM1 中的地址(SRAM)的起始地址变为 0x2001A000,分配大小只有 0x6000(24K 字节)。整个 STM32F407ZGT6 的 SRAM(不含 CCM)分配情况为:最开始的 4K 给 Bootloader 程序使用,随后的 100K 存放 APP 程序,最后 24K,用作 APP 程序的内存。
在这里插入图片描述

2. 重新设置中断向量表的地址

SCB->VTOR寄存器存放的是中断向量表的起始地址。默认的情况它由 BOOT 的启动模式决定。重定向这个位置,这样就可以从 Flash 区域的任意位置启动代码。

是你跳转到app程序后,你后续得跑app的中断向量表。要实现这个就必须在刚进入app的时候重定向中断向量表

/* 设置 NVIC 的向量表偏移寄存器,VTOR 低 9 位保留,即[8:0]保留 */
 SCB->VTOR = baseaddr | (offset & (uint32_t)0xFFFFFE00);

baseaddr 为基地址(即 APP 程序首地址),Offset 为偏移量

比如 FLASH APP 设置中断向量表偏移量为 0x10000

SCB->VTOR = FLASH_BASE | ( 0x10000 & (uint32_t)0xFFFFFE00);

SRAM APP 的情况可以参考正点原子探索者开发指南V1.1。

3.设置keil生成bin文件

MDK 默认生成的文件是.hex 文件,并不方便用作 IAP更新,我们希望生成的文件是.bin 文件.

hex和bin文件的区别

简单来说:HEX文档是ascii码的文档。 是不能直接烧到单片机中的。 中间要有转换程序。 但是现在很多编程器都设计成直接可以导入hex文件烧录的,其实这是做了设计的。 bin文件是二进制文件,是可以直接烧到芯片中,中间不用转换的。

设置keil生成bin文件

在 MDK 点击 Options for Target→User 选项卡,在 After Build/Rebuild一栏中,勾选 Run #1,我们推荐使用相对地址,在勾选的同一行后的输入框并写入命令行:fromelf --bin -o …\Output@L.bin …\Output%L
在这里插入图片描述
D:\Keil_v5\ARM\ARMCC\bin\fromelf.exe是MDK 自带的格式转换工具 fromelf.exe的路径。

通过这一步设置,我们就可以在 MDK 编译成功之后,调用 fromelf.exe,
..\..\Output\%L 表示当前编译的链接文件(…\是相对路径,表示上级目录,编译器默认从工程文件*.uvprojx 开始查找,根据我的工程文件 Output 的位置就能明白路径的含义)
指令–bin –o …\Output@L.bin表示在 Output 目录下生成一个.bin 文件,
@L 在 Keil 的下表示 Output 选项卡下的 Name of Executable 后面的字符串,即在 Output 文件夹下生成一个 atk_f407.bin 文件。

在得到.bin 文件之后,我们只需要将这个 bin 文件传送给单片机,即可执行 IAP 升级。

Bootloader程序的实现

1、APP文件的编写与通常编写一致,只需要设定好偏移地址
2、新的中断向量表的偏移设置好
这两点在前文已经写过,接下来就是bootloader的实现了。

Bootloader 和 app 属于两个独立的工程,不是一个工程!!!
相关资料:
1.写 STM32 内部 Flash 的功能用到 STM32 的 Flash操作。STM32片上Flash操作
2.设置bootloader的起始地址为复位之后第一个启动的工程。
这个例子中,bootloader从0x800 0000开始,APP从0x800 8000开始,执行完bootloader之后再执行APP。

BootLoader 的编译器设置
在这里插入图片描述
App 的编译器设置
在这里插入图片描述

1.bootloader程序也是一个app程序

是我们专门用来搬运APP程序的工具,所以也和是和普通程序一样的执行流程。
IAP升级程序(bootloader)启动流程
在这里插入图片描述
简单来说
初始化好时钟,GPIO,外设(依照升级方法来确定初始化对应的外设),那么这块MCU就有了可以和外部通信的能力。
在这里插入图片描述

例如,使用串口升级,那么步骤就是

bootloader的main之前

1.单片机复位,从0x800 0000开始执行。
2.执行0x0800 0004处的Rest_Handler复位中断向量函数
3. 复位中断向量函数执行
在这里插入图片描述
调用SystemInit函数。这个函数里面开启了外部晶振,设置了PLL,除能了所有中断,设置了时钟

_main 标号表示 C/C++标准实时库函数里的一个初始化子程序 _main的入口地址。该程序的一个主要作用是初始化堆栈(跳转_user_initial_stackheap标号进行初始化堆栈),并初始化映像文件,最后跳转到C程序中的main函数。这也正解释了为什么所有的C程序必须有一个main函数作为程序的起点,因为这是由C/C++标准实时库所规定的。
4.跳转到IAP升级程序(bootloader)的main函数。

bootloader的main之后

1.初始化

初始化和正常没有bootloader 的程序一下,该咋样就咋样,为了避免无效功耗损失,一般是需要什么就初始化什么。时钟初始化,串口初始化,定时器初始化等(如果有用到定时轮询或者定时器延时等可以初始化)

2.检查APP 程序是否完整

若出现某些情况导致APP程序不全,有可能导致程序跑飞,进入硬件错误。

初始化完成后,首先检测 APP 分区中最后地址的标志位是否符合(如是否等于0x5a5a,一般APP程序的末尾加入特殊字符,以检测完整性),如果符合,启动倒计时(时间大约几百毫秒就行,根据实际情况决定),等待进入 APP 程序,在此期间,也会等待接收升级的指令;否则,则一直等待接收升级的指令。
check收到的估计bin的最后四个字节是否为0x5A5A

int CheckIfAPPCanJump(void)
{
    uint32_t *pFlag = (uint32_t *)(APP_FLASH_CODE_BASE + APP_FLASH_CODE_SIZE - 4);
    if (*pFlag == 0x5A5A)
    { return 1; }
    return 0;
}
3.升级

接收到升级指令后,则启动升级流程,升级期间不允许进入 APP 程序。

4.跳转APP程序(IAP 如何实现跳转到用户程序)

升级完成后,需要跳转到 APP 程序(可以自动跳转,也可以增加跳转指令手动跳转),跳转前的准备:
1.检测 APP 程序是否存在,若存在,则继续往下执行
2.关闭全局中断
3.清除所有中断标志位

void SetVectorTable(void)
{
    uint8_t i;
 
	 __disable_irq(); // 关闭总中断
    //__enable_irq();
 
    /* 清除所有中断标志 */
    for(i= 0; i < CRS_IRQn; i++)
    {
        NVIC_ClearPendingIRQ((IRQn_Type)i);
    }
}

4.跳转 APP 启动程序地址(当然,也能直接跳转到 APP 程序的 main 函数,但在跳转前需要重新初始化堆栈等内容,所以最好还是交给启动文件处理,跳转到resethandle)

4.1 设定跳转地址

typedef  void (*pFunction)(void);       //定义一个pFunction类型 这是个函数指针
pFunction Jump_To_Application;          //Jump_To_Application设置为pFunction类型

/*设定跳转地址,FLASH_USER_START_ADDR是APP程序的起始地址*/
JumpAddress = *(__IO uint32_t*) (FLASH_USER_START_ADDR + 4);
Jump_To_Application = (pFunction) JumpAddress;//将APP程序的中断向量表的Rest_Handler地址赋值给Jump_To_Application
__set_MSP(*(__IO uint32_t*) FLASH_USER_START_ADDR); //这里是将把应用程序起始地址设为栈顶指针
Jump_To_Application(); //设置PC指针为复位地址,你可以理解为跳转到应用程序的函数

设定跳转地址,FLASH_USER_START_ADDR是APP程序的起始地址,+4是因为中断向量表每四个字节代表一个中断函数的地址。所以JumpAddress指向APP中断向量表的Rest_Handler.

将跳转地址JumpAddress强制转换pFunction类型,可以理解为编译器将其编译成一个函数

2.官方的demo,以stm32f10x为例

IAP/src/main.c 
 
int main(void)
{
  /* Flash unlock */
  /*flash解锁,因为需要操作flash*/
  FLASH_Unlock();
 
  /* Initialize Key Button mounted on STM3210X-EVAL board */
  /*初始化按键,demo中的升级触发条件为按键按下*/         
  STM_EVAL_PBInit(BUTTON_KEY, BUTTON_MODE_GPIO);   
 
  /* Test if Key push-button on STM3210X-EVAL Board is pressed */
  if (STM_EVAL_PBGetState(BUTTON_KEY)  == 0x00)
  { 
    /* If Key is pressed */
    /*如果按键按下,即触发升级,进行升级*/
    /* Execute the IAP driver in order to re-program the Flash */
    /*初始化串口,demo里面使用的是usart1 + Y-MODe协议*/
    IAP_Init();
    SerialPutString("\r\n================================================================");
    SerialPutString("\r\n=          (C) COPYRIGHT 2010 STMicroelectronics           =");
    SerialPutString("\r\n=                                                          =");
    SerialPutString("\r\n=  In-Application Programming Application  (Version 3.3.0) =");
    SerialPutString("\r\n=                                                          =");
    SerialPutString("\r\n=                       By MCD Application Team            =");
    SerialPutString("\r\n============================================================");
    SerialPutString("\r\n\r\n");
    /*升级菜单,用户自己实现*/
    Main_Menu ();
  }
  /* Keep the user application running */
  else//不升级 进入APP
  {
    /* Test if user code is programmed starting from address "ApplicationAddress" */
    /*升级条件不满足,跳转到用户程序处执行用户程序*/
    if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)//检测栈顶指针
    { 
      /* Jump to user application */
      /*ApplicationAddress为用户程序的栈地址,+4便为用户程序的复位中断向量地址*/  
      JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
      Jump_To_Application = (pFunction) JumpAddress;
      /* Initialize user application's Stack Pointer */
      __set_MSP(*(__IO uint32_t*) ApplicationAddress);
      /*执行用户空间的复位中断向量函数,里面主要是进行系统时钟配置,执行用户空间的main函数数*/  
      Jump_To_Application();
    }
  }
 
  while (1)
  {}
}

检测栈顶指针

升级菜单demo,更具需求裁剪

void Main_Menu(void)
{
  uint8_t key = 0;
  
  /* Get the number of block (4 or 2 pages) from where the user program will be loaded */
  /*计算IAP占用的flash页数*/  
  BlockNbr = (FlashDestination - 0x08000000) >> 12;
 
  /* Compute the mask to test if the Flash memory, where the user program will be
     loaded, is write protected */
#if defined (STM32F10X_MD) || defined (STM32F10X_MD_VL)
  UserMemoryMask = ((uint32_t)~((1 << BlockNbr) - 1));
#else /* USE_STM3210E_EVAL */
  if (BlockNbr < 62)
  {
    UserMemoryMask = ((uint32_t)~((1 << BlockNbr) - 1));
  }
  else
  {
    UserMemoryMask = ((uint32_t)0x80000000);
  }
#endif /* (STM32F10X_MD) || (STM32F10X_MD_VL) */
 
 
  /* Test if any page of Flash memory where program user will be loaded is write protected */
  /*检测flash中用户空间的写保护锁是否开启*/ 
  if ((FLASH_GetWriteProtectionOptionByte() & UserMemoryMask) != UserMemoryMask)
  {
    FlashProtection = 1;
  }
  else
  {
    FlashProtection = 0;
  }
 
  while (1)
  {
    SerialPutString("\r\n================== Main Menu ============================\r\n\n");
    SerialPutString("  Download Image To the STM32F10x Internal Flash ------- 1\r\n\n");
    SerialPutString("  Upload Image From the STM32F10x Internal Flash ------- 2\r\n\n");
    SerialPutString("  Execute The New Program ------------------------------ 3\r\n\n");
    
    if(FlashProtection != 0)
    {
      SerialPutString("  Disable the write protection ------------------------- 4\r\n\n");
    }
    
    SerialPutString("==========================================================\r\n\n");
    
    key = GetKey();
 
    if (key == 0x31)
    {
      /* Download user application in the Flash */
      /*下载程序*/  
      SerialDownload();
    }
    else if (key == 0x32)
    {
      /* Upload user application from the Flash */
      SerialUpload();
    }
    else if (key == 0x33)
    {
      JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
 
      /* Jump to user application */
      Jump_To_Application = (pFunction) JumpAddress;
      /* Initialize user application's Stack Pointer */
      __set_MSP(*(__IO uint32_t*) ApplicationAddress);
      Jump_To_Application();
    }
    else if ((key == 0x34) && (FlashProtection == 1))
    {
      /* Disable the write protection of desired pages */
      FLASH_DisableWriteProtectionPages();
    }
    else
    {
      if (FlashProtection == 0)
      {
        SerialPutString("Invalid Number ! ==> The number should be either 1, 2 or 3\r");
      }
      else
      {
        SerialPutString("Invalid Number ! ==> The number should be either 1, 2, 3 or 4\r");
      } 
    }
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1045669.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为什么 0.1 + 0.1 !== 0.2

总结了几个很有意思的基础题目&#xff0c;分享一下。 为什么 0.1 0.1 ! 0.2 看到这个问题&#xff0c;不得不想到计算机中的数据类型&#xff0c;其中浮点数表示有限的精度。那么它就无法精确的表示所有的十进制小数&#xff0c;所以在在某些情况下&#xff0c;浮点数的运算…

得帆信息3大核心产品全部入选!中国信通院2023《高质量数字化转型产品及服务全景图》

由中国信息通信研究院&#xff08;以下简称“中国信通院”&#xff09;主办的“2023数字生态发展大会”暨中国信通院“铸基计划”年中会议在京召开&#xff0c;会上中国信通院全面解读并复盘了行业数字化转型的发展趋势&#xff0c;同时正式发布了最新的《高质量数字化转型产品…

从零开始的LINUX(一)

LINUX本质是一种操作系统&#xff0c;用于对软硬件资源进行管理&#xff0c;其管理的方式是指令。指令是先于图形化界面产生的&#xff0c;相比起图形化界面&#xff0c;指令显然更加难以理解&#xff0c;但两者只是形式上的不同&#xff0c;本质并没有区别。 简单的指令&…

雅思考试需要注意些什么?

雅思考试是国际英语语言测试体系&#xff08;IELTS&#xff09;的一部分&#xff0c;是许多想要在国际舞台上学习、工作或移民的人的必备考试。为了在雅思考试中取得成功&#xff0c;考生需要注意一些重要事项。本文知识人网小编将探讨一些关键的注意事项&#xff0c;以帮助考生…

经典算法:最短点对

软件架构师何志丹 说明 旧文新发&#xff0c;改了错别字&#xff0c;死链等。尽量保持“原汁原味”。 难点 如何测试。我的解决方式是&#xff1a;a,三种解法&#xff0c;看结果是否一致。b,小数据&#xff08;100个点&#xff09;&#xff0c;人工排查。第一种方法&#x…

【数据结构】—超级详细的归并排序(含C语言实现)

​ 食用指南&#xff1a;本文在有C基础的情况下食用更佳 &#x1f525;这就不得不推荐此专栏了&#xff1a;C语言 ♈️今日夜电波&#xff1a;斜陽—ヨルシカ 0:30━━━━━━️&#x1f49f;──────── 3:20 …

baichuan2 chat模型sft指令微调数据格式分析

一、前言 百川官网&#xff1a;https://www.baichuan-ai.com/ 模型权重&#xff1a;https://huggingface.co/baichuan-inc/Baichuan2-13B-Chat 记录一下 baichuan 2 的 tokenizer 及 chat 数据构建格式。 二、数据处理代码 根据官方 github 的 finetune 代码&#xff0c;将其…

民企再续“助学故事”,恒昌公益两所“云杉校园”如何聚木成林?

撰稿|多客 来源|贝多财经 “生物世界丰富多彩、五花八门、琳琅满目&#xff0c;可谓大千世界芸芸众生”……这是遵义市正安县安场镇光明完全小学图书馆收藏的一本名为《闯入生物世界》书中所写景象。 在这所学校&#xff0c;课外书籍按照年级及类别进行划分&#xff0c;如一…

M1/M2 Parallels Desktop 19虚拟机安装Windows11教程(超详细)

引言 在Window上VMware最强&#xff0c;在Mac上毫无疑问Parallels Desktop为最强&#xff01; 今天带来的是最新版Parallels Desktop 19虚拟机安装Windows11的教程。 1. 安装Parallels Desktop 19虚拟机 https://blog.csdn.net/weixin_52799373/article/details/133316608 …

RocketMQ 事务消息发送

目录 事务消息介绍 应用场景 功能原理 使用限制 使用示例 使用建议​ 事务消息介绍 在一些对数据一致性有强需求的场景&#xff0c;可以用 RocketMQ 事务消息来解决&#xff0c;从而保证上下游数据的一致性。 应用场景 分布式事务的诉求 分布式系统调用的特点为一个核…

【数据结构】深度剖析最优建堆及堆的经典应用 - 堆排列与topk问题

&#x1f6a9;纸上得来终觉浅&#xff0c; 绝知此事要躬行。 &#x1f31f;主页&#xff1a;June-Frost &#x1f680;专栏&#xff1a;数据结构 &#x1f525;该文章分别探讨了向上建堆和向下建堆的复杂度和一些堆的经典应用 - 堆排列与topk问题。 ❗️该文章内的思想需要用到…

排序学习总结

取每个对象的内接矩形框&#xff0c;然后再排序&#xff0c;根据排序的结果确定原对象顺序。 inner_rectangle1(RegionAffineTrans1, Row1, Column1, Row2, Column2) gen_rectangle1(Rect,Row1, Column1, Row2, Column2) sort_region(Rect,RectSort,character,true, row)count…

Oracle物化视图(Materialized View)

与Oracle普通视图仅存储查询定义不同&#xff0c;物化视图&#xff08;Materialized View&#xff09;会将查询结果"物化"并保存下来&#xff0c;这意味着物化视图会消耗存储空间&#xff0c;物化的数据需要一定的刷新策略才能和基表同步&#xff0c;在使用和管理上比…

【嵌入式】使用嵌入式芯片唯一ID进行程序加密实现

目录 一 背景说明 二 原理介绍 三 设计实现 四 参考资料 一 背景说明 项目程序需要进行加密处理。 考虑利用嵌入式芯片的唯一UID&#xff0c;结合Flash读写来实现。 加密后的程序&#xff0c;可以使得从芯片Flash中读取出来的文件&#xff08;一般为HEX格式&#xff09;不能…

C#WPF命令Command使用实例

本文实例演示C#WPF命令使用实例 定义: 命令(Command):命令表示一个任务单元,并且可跟踪该任务的状态,实际上是实现了ICommand接口的类。然而,命令实际上可以包括任务执行的逻辑代码,也可以不包括从而仅作为联系命令源与命令目标的媒介。比如,WPF 默认的接口实现类Ro…

el-collapse 嵌套中 el-checkbox作为标题,选中复选框与el-tree联动

<el-drawertitle"应用授权":visible.sync"menuDrawer"><el-collapse accordion style"padding: 15px"><el-collapse-item v-for"item in platList"><template slot"title"><el-checkbox v-model…

PostMan、ApiFox等工具Post请求中@RequestParam和@RequestBody的混合使用如何传参

方法签名 PostMapping("/mms/sendAudit")public R sendAudit(RequestParam("mmsId") Long mmsId,RequestParam("ecId") Long ecId,RequestBody(required false) SignMatchRule signMatchRule) {以ApiFox为例子 RequestParam的Params的参数正常…

基于51单片机0.001s精度秒表9.999s仿真设计(源码+仿真+原理图+PCB+报告+器件清单+讲解)

基于51单片机0.001s精度秒表9.999s仿真设计&#xff08;源码仿真原理图PCB报告器件清单讲解 讲解视频1 功能说明&#xff1a;2 仿真电路&#xff1a;3 原理图&#xff1a;4 PCB&#xff1a;5 程序&#xff1a;6 资料清单&&下载链接&#xff1a; 基于51单片机0.001s精度…

2023年【煤气】报名考试及煤气免费试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 煤气报名考试根据新煤气考试大纲要求&#xff0c;安全生产模拟考试一点通将煤气模拟考试试题进行汇编&#xff0c;组成一套煤气全真模拟考试试题&#xff0c;学员可通过煤气免费试题全真模拟&#xff0c;进行煤气自测…

什么是图像翻译

域(Domain)&#xff1a;一系列具有相同风格的图像集合。 图像翻译(Image Translation)&#xff1a;从一张图像到另一张图像的变换&#xff0c;也是域迁移。 &#xff08;风格迁移、图像上色、图像分割...&#xff09; 图像翻译GAN模型分类 根据作用区域&#xff1a;…