硬汉嵌入式论坛

 找回密码
 立即注册
查看: 21308|回复: 147
收起左侧

[BOOT/IAP] 实战技能分享,一劳永逸的解决BOOT跳转APP失败问题,含MDK AC5,AC6和IAR,同时制作了一个视频操作说明

  [复制链接]

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106590
QQ
发表于 2021-11-17 00:05:07 | 显示全部楼层 |阅读模式
注:看文档不是很清楚的话,专门也制作了视频版,见楼主位末尾。

背景知识:

BOOT跳转到APP,就跟我们程序里面函数调用跳转是一样的,并不会复位外设,需要用户手动去操作。导致我们BOOT跳转APP经常会遇到这样那样的问题,根本原因还是BOOT跳转前没有提供一个干净的环境给APP运行,这个环境如果可以达到和程序刚上电时的状态是最好的。
一般情况下,大家的跳转程序应该是下面这种玩法,各种倒腾中断,外设复位等,那个遗漏了,在APP里面都会有意想不到的效果。

  1. static void JumpToApp(void)
  2. {
  3.         uint32_t i=0;
  4.         void (*SysMemBootJump)(void);        /* 声明一个函数指针 */
  5.         __IO uint32_t BootAddr = 0x08100000; /* STM32H7的系统BootLoader地址 */
  6.         
  7.         /* 设置所有时钟到默认状态,使用HSI时钟 */
  8.         HAL_RCC_DeInit();

  9.         /* 关闭全局中断 */
  10.         DISABLE_INT();

  11.         /* 关闭滴答定时器,复位到默认值 */
  12.         SysTick->CTRL = 0;
  13.         SysTick->LOAD = 0;
  14.         SysTick->VAL = 0;

  15.         /* 关闭所有中断,清除所有中断挂起标志 */
  16.         for (i = 0; i < 8; i++)
  17.         {
  18.                 NVIC->ICER[i]=0xFFFFFFFF;
  19.                 NVIC->ICPR[i]=0xFFFFFFFF;
  20.         }        

  21.         /* 使能全局中断 */
  22.         ENABLE_INT();

  23.         /* 跳转到系统BootLoader,首地址是MSP,地址+4是复位中断服务程序地址 */
  24.         SysMemBootJump = (void (*)(void)) (*((uint32_t *) (BootAddr + 4)));

  25.         /* 设置主堆栈指针 */
  26.         __set_MSP(*(uint32_t *)BootAddr);
  27.         
  28.         /* 在RTOS工程,这条语句很重要,设置为特权级模式,使用MSP指针 */
  29.         __set_CONTROL(0);

  30.         /* 跳转到系统BootLoader */
  31.         SysMemBootJump();

  32.         /* 跳转成功的话,不会执行到这里,用户可以在这里添加代码 */
  33.         while (1)
  34.         {

  35.         }
  36. }
复制代码


解决办法:

我们跳转前,人为的做一个跳转操作,提供一个干净的运行环境,思路框图如下:

1.png

框图含义:我们的正常BOOT里面有各种操作,跳转前逐个复位太繁琐,经常会有各种遗漏没考虑到,特别是BOOT里面用到了,APP也用到的外设。

那么我们就可以人为的执行一个软件复位,复位后直接跳转到APP即可,这里就有一个核心,就是我们要设置一个不被编译器初始化的变量,我们可以BOOT和APP里面都使用。


MDK AC5设置:

AC5设置设置最简单,定义下即可。

  1. uint32_t g_JumpInit __attribute__((at(0x20000000), zero_init));
复制代码

MDK AC6设置:

  1. uint32_t g_JumpInit __attribute__( ( section( ".bss.NoInit")));
复制代码

分享加载设置:
  1. ; *************************************************************
  2. ; *** Scatter-Loading Description File generated by uVision ***
  3. ; *************************************************************

  4. LR_IROM1 0x08000000 0x00200000  {    ; load region size_region
  5.   ER_IROM1 0x08000000 0x00200000  {  ; load address = execution address
  6.    *.o (RESET, +First)
  7.    *(InRoot$$Sections)
  8.    .ANY (+RO)
  9.   }
  10.   
  11.   RW_IRAM1 0x20000000 UNINIT 0x00000004  {  ; RW data - 128KB DTCM
  12.           *(.bss.NoInit)
  13.   }
  14.   
  15.   RW_IRAM2 0x24000000 0x00080000  {  ; RW data - 512KB AXI SRAM
  16.     .ANY (+RW +ZI)
  17.   }
  18. }
复制代码


IAR设置:

定义:
  1. #pragma location = ".NoInit"  
  2. uint32_t g_JumpInit;
复制代码
分散加载设置:
  1. /*###ICF### Section handled by ICF editor, don't touch! ****/
  2. /*-Editor annotation file-*/
  3. /* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_0.xml" */
  4. /*-Specials-*/
  5. define symbol __ICFEDIT_intvec_start__ = 0x08000000;
  6. /*-Memory Regions-*/
  7. define symbol __ICFEDIT_region_ROM_start__     = 0x08000000;
  8. define symbol __ICFEDIT_region_ROM_end__       = 0x081FFFFF;
  9. define symbol __ICFEDIT_region_RAM_start__     = 0x24000000;
  10. define symbol __ICFEDIT_region_RAM_end__       = 0x2407FFFF;
  11. define symbol __ICFEDIT_region_ITCMRAM_start__ = 0x00000000;
  12. define symbol __ICFEDIT_region_ITCMRAM_end__   = 0x0000FFFF;
  13. /*-Sizes-*/
  14. define symbol __ICFEDIT_size_cstack__ = 0x1000;
  15. define symbol __ICFEDIT_size_heap__   = 0x800;
  16. /**** End of ICF editor section. ###ICF###*/


  17. define memory mem with size = 4G;
  18. define region ROM_region      = mem:[from __ICFEDIT_region_ROM_start__   to __ICFEDIT_region_ROM_end__];
  19. define region RAM_region      = mem:[from __ICFEDIT_region_RAM_start__   to __ICFEDIT_region_RAM_end__];
  20. define region ITCMRAM_region  = mem:[from __ICFEDIT_region_ITCMRAM_start__ to __ICFEDIT_region_ITCMRAM_end__];
  21. define region NoInit_region  = mem:[from 0x20000000 to 0x20000004];

  22. define block CSTACK    with alignment = 8, size = __ICFEDIT_size_cstack__   { };
  23. define block HEAP      with alignment = 8, size = __ICFEDIT_size_heap__     { };

  24. initialize by copy { readwrite };
  25. do not initialize  { section .noinit };

  26. place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec };

  27. place in ROM_region   { readonly };
  28. place in RAM_region   { readwrite,
  29.                         block CSTACK, block HEAP };
  30. place in NoInit_region  {section .NoInit};

  31. do not initialize  { section .NoInit };
复制代码


案例下载:

APP.7z (2.58MB)
BOOT.7z (2.59MB)


视频操作讲解说明:













评分

参与人数 1金币 +20 收起 理由
missfox + 20 很给力!

查看全部评分

回复

使用道具 举报

6

主题

636

回帖

654

积分

金牌会员

积分
654
QQ
发表于 2021-11-17 07:21:42 来自手机 | 显示全部楼层
实际项目中,我都是用芯片内部flash区域来判断是否跳转用户程序,其它都差不多,浪费了一点flash资源,不过感觉现在得芯片也不差那点
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106590
QQ
 楼主| 发表于 2021-11-17 07:56:43 | 显示全部楼层
yklstudent 发表于 2021-11-17 07:21
实际项目中,我都是用芯片内部flash区域来判断是否跳转用户程序,其它都差不多,浪费了一点flash资源,不过 ...

内部Flash也不错,就是要搞了内部Flash的擦写操作,产品操作不频繁的话,也是不错的选择。

像H7这种不太合适,最小擦除需要128KB。
回复

使用道具 举报

33

主题

203

回帖

302

积分

高级会员

积分
302
发表于 2021-11-17 08:48:39 | 显示全部楼层
有电池的话,跳转变量放备份区也可以吧
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106590
QQ
 楼主| 发表于 2021-11-17 08:51:28 | 显示全部楼层
waterx3 发表于 2021-11-17 08:48
有电池的话,跳转变量放备份区也可以吧

没问题。
回复

使用道具 举报

0

主题

97

回帖

97

积分

初级会员

积分
97
发表于 2021-11-17 08:59:20 | 显示全部楼层
waterx3 发表于 2021-11-17 08:48
有电池的话,跳转变量放备份区也可以吧

没有电池也可以的。
回复

使用道具 举报

0

主题

30

回帖

30

积分

新手上路

积分
30
发表于 2021-11-17 09:21:24 | 显示全部楼层
硬汉哥,那这个意思,我是不是把变量放到外部EEPROM中也可以呀?
回复

使用道具 举报

0

主题

26

回帖

26

积分

新手上路

积分
26
发表于 2021-11-17 10:04:46 | 显示全部楼层
APP启动后,不用配置中断向量表吗
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106590
QQ
 楼主| 发表于 2021-11-17 10:25:47 | 显示全部楼层
BruceWang 发表于 2021-11-17 09:21
硬汉哥,那这个意思,我是不是把变量放到外部EEPROM中也可以呀?

没问题的。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106590
QQ
 楼主| 发表于 2021-11-17 10:27:36 | 显示全部楼层
miaoguoqiang 发表于 2021-11-17 10:04
APP启动后,不用配置中断向量表吗

要的,中断向量是在APP里面已经配置好了,这个地方不用动。
回复

使用道具 举报

9

主题

103

回帖

130

积分

初级会员

积分
130
发表于 2021-11-17 10:34:05 | 显示全部楼层
我们也是这么做的,补充一下。
123.png
回复

使用道具 举报

4

主题

61

回帖

73

积分

初级会员

积分
73
QQ
发表于 2021-11-17 10:45:14 | 显示全部楼层
Segger Embedder Studio实现方法如下。注意AXI_RAM区域不行,数值一上电就直接复位了。DTCM区域可以实现:
1. XML文件中增加一个地址范围的定义(需定义在DTCM中。DTCM的前0x400个字节我留给中断向量表了所以我这里从0x20000400开始):
  1. // BOOT标志 = 4B            
  2.     <MemorySegment name="USER_MEM"          start="0x20000400" size="0x00000004" access="Read/Write" />   
复制代码

2. icf文件中增加定义相关区域的属性(在icf文件中,memory > region > block > section,这里没用到block):
  1. define region region_USER_RAM   = USER_MEM;                     // 链接XML里的USER_MEM区域,名为region_USER_RAM


  2. do not initialize                           { section .USER_RAM };                                // 指示这个section不要初始化


  3. place in region_USER_RAM                          { section .USER_RAM, section .USER_RAM.* };         // 定义section,以便在代码中使用。并定义了这个section所属的region。
复制代码


3. 在代码中定义标志变量:
  1. __attribute__((section(".USER_RAM"))) static u32 user;
复制代码
并在Bootloader程序中判断:
  1. if (user == 0x5A) {      
  2.     // 跳转到APP                                                               
  3.     } else {
  4.     // 置标志后重启
  5.     user = 0x5A;
  6.     HAL_NVIC_SystemReset();
  7.     }
复制代码
附:XML文件和ICF文件右击工程名,在这两处可直接打开:

1.png
回复

使用道具 举报

2

主题

267

回帖

273

积分

高级会员

积分
273
发表于 2021-11-17 13:49:39 | 显示全部楼层
yklstudent 发表于 2021-11-17 07:21
实际项目中,我都是用芯片内部flash区域来判断是否跳转用户程序,其它都差不多,浪费了一点flash资源,不过 ...

那样直接上个flashDB很香的。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106590
QQ
 楼主| 发表于 2021-11-17 13:51:30 | 显示全部楼层
xy201207 发表于 2021-11-17 10:34
我们也是这么做的,补充一下。

谢谢分享。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106590
QQ
 楼主| 发表于 2021-11-17 13:52:53 | 显示全部楼层
偶尔也很菜 发表于 2021-11-17 10:45
Segger Embedder Studio实现方法如下。注意AXI_RAM区域不行,数值一上电就直接复位了。DTCM区域可以实现:
...

跟我的方法一样,我昨晚测试就是不行,真邪门。
回复

使用道具 举报

4

主题

61

回帖

73

积分

初级会员

积分
73
QQ
发表于 2021-11-17 16:21:12 | 显示全部楼层
eric2013 发表于 2021-11-17 13:52
跟我的方法一样,我昨晚测试就是不行,真邪门。

我反复上下电测试多次都没问题,你是哪的问题啊,一上电一条指令都不运行数值也会变化?
回复

使用道具 举报

1

主题

10

回帖

13

积分

新手上路

积分
13
QQ
发表于 2021-11-17 17:04:09 | 显示全部楼层
F1是不是不行啊,复位之后RAM区域也都复位了
回复

使用道具 举报

1

主题

10

回帖

13

积分

新手上路

积分
13
QQ
发表于 2021-11-17 17:27:27 | 显示全部楼层
试了下可以了,忘记勾选RAM区域不初始化的勾勾了
回复

使用道具 举报

1

主题

10

回帖

13

积分

新手上路

积分
13
QQ
发表于 2021-11-17 17:51:21 | 显示全部楼层
xy201207 发表于 2021-11-17 10:34
我们也是这么做的,补充一下。

请教下,对于第一次上电做的校验一般做哪些内容啊
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106590
QQ
 楼主| 发表于 2021-11-17 17:56:22 | 显示全部楼层
偶尔也很菜 发表于 2021-11-17 16:21
我反复上下电测试多次都没问题,你是哪的问题啊,一上电一条指令都不运行数值也会变化?

你方便的话,试试主RAM使用AXI SRAM,直接将DTCM RAM的头0x2000 0000到0x2000 0004给这个未初始化变量使用。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106590
QQ
 楼主| 发表于 2021-11-17 17:56:59 | 显示全部楼层
夜歌 发表于 2021-11-17 17:27
试了下可以了,忘记勾选RAM区域不初始化的勾勾了

好的,直接设置option也行。
回复

使用道具 举报

4

主题

61

回帖

73

积分

初级会员

积分
73
QQ
发表于 2021-11-17 21:13:36 | 显示全部楼层
eric2013 发表于 2021-11-17 17:56
你方便的话,试试主RAM使用AXI SRAM,直接将DTCM RAM的头0x2000 0000到0x2000 0004给这个未初始化变量使 ...

试了下,也可以的。你的工程发过来我试试?
1.png
回复

使用道具 举报

23

主题

1403

回帖

1472

积分

至尊会员

积分
1472
发表于 2021-11-17 21:23:44 | 显示全部楼层
mark 好贴。
代码不规范,亲人两行泪!
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106590
QQ
 楼主| 发表于 2021-11-17 21:26:14 | 显示全部楼层
偶尔也很菜 发表于 2021-11-17 21:13
试了下,也可以的。你的工程发过来我试试?

楼主位的boot程序里面的SES程序就是我配置的,不行,方便的话,你看看
QQ截图20211117212629.png
回复

使用道具 举报

1

主题

10

回帖

13

积分

新手上路

积分
13
QQ
发表于 2021-11-18 09:33:55 | 显示全部楼层
xy201207 发表于 2021-11-17 10:34
我们也是这么做的,补充一下。

可否请教下,第一次上电时,校验是个什么流程?APP下载的时候在头尾添加了固定的一些信息么,然后下载完成后去头尾读取,读到了说明下载成功?校验完成?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106590
QQ
 楼主| 发表于 2021-11-18 10:49:28 | 显示全部楼层
夜歌 发表于 2021-11-18 09:33
可否请教下,第一次上电时,校验是个什么流程?APP下载的时候在头尾添加了固定的一些信息么,然后下载完 ...

crc就行

成功实现MDK自动生成hex文件的crc值并附加到hex文件末尾(bin也支持),然后跟STM32的硬件CRC计算值做比较
http://www.armbbs.cn/forum.php?m ... 7379&fromuid=58
(出处: 硬汉嵌入式论坛)
回复

使用道具 举报

1

主题

10

回帖

13

积分

新手上路

积分
13
QQ
发表于 2021-11-18 10:50:44 | 显示全部楼层
eric2013 发表于 2021-11-18 10:49
crc就行

成功实现MDK自动生成hex文件的crc值并附加到hex文件末尾(bin也支持),然后跟STM32的硬件CRC ...

好,谢谢,我研究下
回复

使用道具 举报

8

主题

25

回帖

49

积分

初级会员

积分
49
发表于 2021-11-19 11:21:24 | 显示全部楼层
硬汉,有个疑问想请教下,像H750这些跑在外部flash里的,要先开启内存映射,假如软复位后就初始化QSPI开启内存映射跳转到外部flash执行程序,那QSPI外部flash不是一直运行在一个比较低的频率?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106590
QQ
 楼主| 发表于 2021-11-19 12:45:28 | 显示全部楼层
老鸟kkk 发表于 2021-11-19 11:21
硬汉,有个疑问想请教下,像H750这些跑在外部flash里的,要先开启内存映射,假如软复位后就初始化QSPI开启 ...

如果是外部QSPI Flash的话,得初始化QSPI Flash再跳转。
回复

使用道具 举报

6

主题

18

回帖

36

积分

新手上路

积分
36
发表于 2021-11-24 09:40:22 | 显示全部楼层
大佬,sct设置以后,需要编译不?我在Target里面设置的IRAM1是0x20000000, 0x20000, 一编译sct的文件就会覆盖。要么就在target里面把IRAM1后面的NOINIT勾上,但是128KB的RAM都不复位的话,会不会有什么影响?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106590
QQ
 楼主| 发表于 2021-11-24 09:59:28 | 显示全部楼层
xinhaic 发表于 2021-11-24 09:40
大佬,sct设置以后,需要编译不?我在Target里面设置的IRAM1是0x20000000, 0x20000, 一编译sct的文件就会覆 ...

你这个地方没有设置吧,要设置这里:
QQ截图20211124095928.png
回复

使用道具 举报

6

主题

18

回帖

36

积分

新手上路

积分
36
发表于 2021-11-24 14:16:05 | 显示全部楼层
eric2013 发表于 2021-11-24 09:59
你这个地方没有设置吧,要设置这里:

哦哦,原来如此,多谢硬汉大佬指点
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

积分
2
发表于 2021-11-25 09:24:47 | 显示全部楼层
偶尔也很菜 发表于 2021-11-17 21:13
试了下,也可以的。你的工程发过来我试试?

请问这个软件是什么啊,还能看到内存和占用率?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106590
QQ
 楼主| 发表于 2021-11-25 12:03:46 | 显示全部楼层
土豆炒饭 发表于 2021-11-25 09:24
请问这个软件是什么啊,还能看到内存和占用率?

这个是Embedded Studio
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

积分
2
发表于 2021-11-29 19:01:39 | 显示全部楼层
eric2013 发表于 2021-11-25 12:03
这个是Embedded Studio

谢谢大佬!
回复

使用道具 举报

12

主题

176

回帖

212

积分

高级会员

积分
212
发表于 2021-11-30 19:57:19 | 显示全部楼层
请问硬汉,NVIC_SystemReset之后,所有的引脚都复位了,如果硬件需要一个引脚输出高电平保持整个系统供电,这种情况怎么用您这方法进行跳转呢?谢谢
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106590
QQ
 楼主| 发表于 2021-11-30 21:41:02 | 显示全部楼层
gallop020142 发表于 2021-11-30 19:57
请问硬汉,NVIC_SystemReset之后,所有的引脚都复位了,如果硬件需要一个引脚输出高电平保持整个系统供电, ...

弄个上拉电阻。
回复

使用道具 举报

12

主题

176

回帖

212

积分

高级会员

积分
212
发表于 2021-12-1 08:05:29 | 显示全部楼层

确实从硬件上能解决这问题,谢谢
回复

使用道具 举报

7

主题

49

回帖

75

积分

初级会员

积分
75
发表于 2022-1-17 17:06:46 来自手机 | 显示全部楼层
这个变量的地址有什么讲究吗?我用的F411芯片,设置成0x20000000,编译报错,所以可否将变量设置在RAM区靠后的位置?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106590
QQ
 楼主| 发表于 2022-1-17 17:18:58 | 显示全部楼层
wling597074509 发表于 2022-1-17 17:06
这个变量的地址有什么讲究吗?我用的F411芯片,设置成0x20000000,编译报错,所以可否将变量设置在RAM区靠 ...

编译报错不正常,貌似是定义有误。方便的话,贴下你的定义方式。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-4-26 08:01 , Processed in 0.470758 second(s), 32 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表