STM32 学习:IAP 简单的IAP例子

--- title: mcu-stm32-IAP-1-sample date: 2020-05-27 18:21:53 categories: tags: - iap - stm32 ---

章节概述

以一个最简单的例子示范IAP程序(没有文件通讯,没有跳转判断),需要借助IDE进行分区数据的划分以及下载。

准备

IDE:keil-MDK 5

MCU: STM32F103ZET6为例(Flash地址为0x08000000—0x0807ffff,共512KB)。

BSP:STM32-HAL

启动方式:FLASH启动

  • 前32KB存放BootLoader程序(0x08000000 ~ 0x08007fff
  • 剩余的空间存放APP程序(0x08008000 ~ 0x0807ffff

假定跳转的APP程序的实际物理地址为:0x08008000

分别新建2个工程,名字可以为:bootLoaderapp

从Bootloader跳转到APP

Bootloader

为了区分执行分区的不同,添加串口打印功能。(略)

例如:printf("Hello Bootloader\r\n");

添加下面的代码以实现跳转功能:

节选自CubeMX的例程:STM32Cube\Repository\xxx\Projects\Applications\IAP

#define NVIC_VectTab_RAM             ((u32)0x20000000)
#define NVIC_VectTab_FLASH           ((u32)0x08000000)

#define PHYSICAL_ADDRESS_Flash	  (0x08000000) // 程序烧写的物理地址
#define APPLICATION_POSADDR 	  (0x0000C000) // APP 偏移量
#define APPLICATION_ADDRESS 	  ((PHYSICAL_ADDRESS_Flash) | (APPLICATION_POSADDR)) // 最终跳转的地址,实际上就是0x08008000
typedef  void (*pFunction)(void);
pFunction JumpToApplication;
uint32_t JumpAddress;

void NVIC_SetVectorTable(uint32_t base, uint32_t offset)
{
    /* close interruption*/
    __set_FAULTMASK(1);

    /* set vector table*/
    //NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0xffset);
    SCB->VTOR = base | offset;

    /* open interruption*/
    __set_FAULTMASK(0);
}

int main(void)
{
    // 在BootLoader程序的中断向量表指向设置中应有这么一句:
    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);      //设置中断向量表指向
    
    /* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */
    if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000) //①
    {
        /* Jump to user application */
        JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);// ②
        JumpToApplication = (pFunction) JumpAddress;//③
        /* Initialize user application's Stack Pointer */
        __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS); // ④
        JumpToApplication(); // ⑤
    }

    while (1)
    {}
}

解析:

①因为用户程序开始位置(0x08008000处)的前4个字节存放的是堆栈的地址,堆栈地址必定是指向RAM空间的,而STM32的RAM空间起始地址为0x20000000,所以要进行判断。

APPLICATION_ADDRESS存放的是用户程序Flash的首地址

(*(volatile u32*)APPLICATION_ADDRESS)的意思是取用户程序首地址里面的数据,这个数据就是用户代码的堆栈地址,堆栈地址指向RAM,而RAM的起始地址是0x20000000,因此上面的判断语句执行:判断用户代码的堆栈地址是否落在:0x20000000~0x2001ffff区间中,这个区间的大小为128K,笔者查阅STM32各型号的RAM大小,目前RAM最大的容量可以做到192K+4K,时钟频率为168MHZ。一般情况下,我们使用的芯片较多的落在<128K RAM的区间,因此上面的判断语句是没有太大问题的。

②程序跳转地址的确认,前面已经说过0x08008004处的4个字节存放的是复位函数的入口地址,该句的意思为获得ApplicationAddress + 4地址处的数据,即为获得新的复位函数入口地址。

③令Jump_To_Application这个函数指针指向复位函数入口地址。

④堆栈的初始化,调用__set_MSP重新设定栈顶代地址,把栈顶地址设置为用户代码指向的栈顶地址。

⑤跳转到新的复位函数。设置PC指针为复位地址。

App

为了区分执行分区的不同,添加串口打印功能。(略)

例如:printf("Hello App\r\n");

添加下面的代码以配合跳转功能:

#define NVIC_VectTab_RAM             ((u32)0x20000000)
#define NVIC_VectTab_FLASH           ((u32)0x08000000)

#define APPLICATION_POSADDR 	  (0x0000C000) // APP 偏移量

void NVIC_SetVectorTable(uint32_t base, uint32_t offset)
{
    /* close interruption*/
    __set_FAULTMASK(1);

    /* set vector table*/
    //NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0xffset);
    SCB->VTOR = base | offset;

    /* open interruption*/
    __set_FAULTMASK(0);
}

int main(void)
{
    // 在用户程序的中断向量表指向设置用应有这么一句:
    NVIC_SetVectorTable(NVIC_VectTab_FLASH, APPLICATION_POSADDR);      //设置中断向量表指向

    ...;
}

工程设置

程序写完了,但是编译还需要进行设置。

BootLoader工程

为了确保Address Range不超过0x08008000,超过会导致app分区被覆盖。

点击:MDK-Options-Debug-Settings

  • Flash Download,勾选Reset and RunProgramReset and Run
  • 点击列表中的Flash(如果没有则需要点击Add进行添加)
    • 修改Size为:0x08004000(只要不超过0x8000即可)

编译,烧写。

APP 工程

为了修改链接地址,以及烧录范围。

MDK-Options-Target-Read/Only Memory Areas

  • 勾选IROM1:填入0x08008000

MDK-Options-Target-Debug-Settings

  • 点击Flash Download,勾选Reset and RunProgramReset and Run
  • 点击列表中的Flash(如果没有则需要点击Add进行添加)
    • 修改Start为:0x08008000
    • 修改Size为:0x08004000(只要不超过0x80000即可)

编译,烧写。

现象

观察到下面的打印信息。

Hello Bootloader
Hello app

注意到2段程序都有NVIC_SetVectorTable函数,可以设置将其注释掉,然后做如下的实验:

  • 配置BootLoader中打开了定时器中断,写好对应的处理函数,例如在中断响应中打印"Boot"。

  • 配置User app中同样打开了定时器中断,写好了对应的处理函数,例如在中断响应中打印"User"。

观察到,尽管两边都实现了中断以及响应,但是都在执行着BootLoader的中断响应。

IAP实现总结

STM32的IAP方案实现需要在进行用户程序之前加一段Bootloader程序。

BootLoader程序的作用就是:

  • 要么:设置新的栈顶指针,直接跳转到用户程序。
  • 要么:删除原有的用户程序,读取*.bin文件数据并将数据重新写入新的用户程序。

对于用户程序相比普通的编程只需要做三步改动即可:

  • 改变代码存放的地址空间
  • 改变中断向量表
  • 修改生成*.bin文件

实际上的IAP工程

本文介绍了简单的IAP调整,没有对文件的传输以及写入。

传输有很多方法,可以是先把hex文件转换为bin。再通过通讯方式完成文件传输、写入;或者直接传输HEX文件,再在MCU进行解析(难)

我们讨论对FLASH的读写。

对FLASH的读写

问:如何进行对STM32的Flash进行擦除和写入操作。

答:STM32-HAL库的读写非常方便,但是要注意读写地址与页的关系

使用STM32的固件库函数,只需调用几个库函数即可轻松解决,使用的固件库为stm32f10x_flash.c文件,对Flash的操作过程简要为:Flash解锁Flash擦除Flash写入Flash上锁。(对Flash编程的更详细操作参考STM32F10xxx闪存编程手册)

//解锁Flash
FLASH_Unlock();            
FLASH_SetLatency(FLASH_Latency_2);            //因为系统时钟为72M所以要设置两个时钟周期的延时

// 擦除
for(i=0;i<240;i++)
{
    if(FLASH_ErasePage(FLASH_ADDR+i*2048) != FLASH_COMPLETE)      //一定要判断是否擦除成功
        return ERROR;
    /*
    说明:FLASH_ErasePage(uint32_t Page_Address)即为Flash擦除操作,按页擦除,每页2KB,Page_Address为页的起始地址,如0x08000000是第一页起始地址,0x08000800为第二页起始地址,这里的操作擦除了0x08008000—0x0807ffff地址空间的Flash。
    */
}


#if 0
unsigned char buf[1024];            //假设待写入的代码数据
unsigned short temp;            //临时数据

for(i=0;i<512;i++)
{
    temp = (buf[2*i+1]<<8) | buf[2*i];            //2个字节整合为1个半字

    if(FLASH_ProgramHalfWord(ADDR,temp) != FLASH_COMPLETE)      //判断是否写入成功
    {
        Return ERROR;
    }
    ADDR +=2;      //地址要加2,因为每次写入的是2个字节(1个半字)
}
/*
说明:因为STM32的Flash写入为双字节(1个半字)写入,FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)函数即为对地址为Address写入1个半字的Data,每次写入完成后地址要加2。
*/

#endif


FLASH_Lock();      //Flash 上锁,一个固件库函数即可实现。

问:怎么进行PC指针的强制跳转,跳转时需要做些什么。

答:关闭中断是可选的,设置栈顶指针是必须的。

  • arm内核通用:disable_irq(); //关闭所有中断 enable_irq();//打开所有中断
  • 设置栈顶指针:__set_MSP();

附录:存储容量与起止地址的关系式

以字节(Byte)为计算单位:存储容量 = 储存单元数 × 储存字长(字节) 。

以Kb为计算单位:储存单元数 × 储存字长(字节)÷ 1024 。

首尾地址的差再加一即是储存单元的个数,每一个储存单元存放一个字节,可以得到总储存单元一共有多少字节;

再根据1M = 1024 K = 1024 × 1024Byte,那么就可以很容易地计算出三者的关系了。

例如:容量6K,首地址0x0000,末地址为?

解答:6K = 6 × 1024 Byte ,未地址 = 6 × 1024 + 000H = 1800H

附录:确认代码的链接地址

为什么在IDE中设置有关的地址呢,为了解答这个问题就需要我们知道:链接地址、运行地址、加载地址、存储地址关系。

  • 运行地址 与 链接地址:是类似的。

  • 加载地址 与 存储地址:是等价的。

链接地址

在程序编译的时候,每个目标文件都是由源代码编译得到,最终多个目标文件链接生成一个最终的可执行文件,而链接地址就是指示链接器,各个目标文件的在可执行程序中的位置。

比如,一个可执行程序a.out由a.o、b.o、c.o组成,那么最终的a.out中谁在前、谁在中间、谁在结尾,都可以通过制定链接地址来决定。

链接地址是静态的,在进行程序编译的时候指定的。

注意,CPU不关心链接地址是物理地址还是虚拟地址。

总结:

1、链接地址是给编译器用的,用来计算代码中相关地址偏移的

2、只要和PC值相关的就是位置无关代码(相对偏移),和PC无关的就是位置相关代码(绝对值)

运行地址

程序实际在内存中运行时候的地址,比如CPU要执行一条指令,那么必然要通过给PC赋值,从对应的地址空间中去取出来,那么这个地址就是实际的运行地址。

运行地址是动态的,如果你将程序加载到内存中时,改变存放在内存的地址,那么运行地址也就随之改变了。

注意,CPU同样不关心运行地址是虚拟地址还是物理地址。

链接地址和运行地址区别

首先要区分开两个概念:位置相关代码和位置有关代码。位置无关代码:mov、b或bl等指令,在跳转时,地址是PC+相对偏移量,这样的指令没有绝对地址,都是相对PC的偏移,这样,及时运行地址和链接地址不一样,也不影响实际代码的执行。

位置相关代码:ldr r0, =label,这里的label(标签)实际就是链接地址,这条指令实质就是将标号的地址放到PC里实现跳转,但是如果实际运行的地址和链接的地址不一样,这样就会由于跳转的地址不是实际运行地址而出错,这就叫做位置相关代码。

条件相关代码和条件无关代码之间的本质区别就是:指令中相关地址是运行地址还是链接地址,如果是运行地址那么就是位置无关代码,因为运行地址是变化的相对量;如果是加载地址,那么就是绝对量(链接时候指定好的),这就是位置相关代码了。

总结,当链接指定的地址和实际运行的地址一样的时候,链接地址==运行地址;当链接指定的地址和实际运行的地址不一样的时候,如果整个代码中的地址都是相对偏移量,那么整个程序仍然运行畅通无阻,否则,整个程序运行结果就会出错(因为指定运行地址和实际运行地址不符)。

加载地址

每一个程序一开始都是存放在flash中的,而运行是在内存中,这个时候就需要从flash中将指令读取到内存中(运行地址),flash的地址就是加载地址(存储地址)。

指令在flash中存放的存储地址,就是存储地址。

所以,加载地址指的是将代码从一个地址A搬移到地址B,这时候地址A就是加载地址。

附录:STM32F0xx 实现中断向量表重定义

在STM32F103等cortex-m3/m4内核的单片机上可以通过设置SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;该寄存器的值来实现中断向量表的重定义。

在Coretext-M3与M4核中,在System Control Block中存在一个向量表偏移量寄存器 VTOR(0xE000ED08),系统产生中断后,内核通过这个寄存器的值来找到中断向量表的地址,进而执行中断例程代码。

当然,此寄存器的值是可以修改的,它的默认值为0。实际上在大部分的M3和M4的工程中,一般都是在SystemInit函数中对此寄存器的值进行设置。

由于STM32F0XX采用的是M0核,它是没有这个VTOR寄存器的,那么它又是怎么找到中断向量表的地址的呢?

如何将中断向量表的寻找位置从0x0800 0000修改到0x0800 3000(假设为APP的地址)? 我们重新回顾之前的分析,可以得出有2种方法:

  • 修改寄存器VTOR的值(M3/M4 使用)
  • 内存重映射(M0使用)

通过将SRAM重映射到地址0x0000 0000,那么,M0系统产生中断后,CPU还是从地址0x0000 0000寻找中断入口,但是,实际上不再是寻址0x0800 0000,而是寻址0x2000 0000,这么一来,接下来我们就只需要将中断向量表整个拷贝到SRAM上,也就是0x2000 0000上,就这样,CPU就可以正常寻址中断向量表了。

基于内存重映射实现的基本思想:

1、将中断向量表放入到RAM的起始地址(只需要在应用程序中保留RAM其实地址的0x100大小不使用即可)。

2、在bootload中将应用程序的中断向量表从Flash中拷贝到RAM中。

3、设置STM32F0xx中断向量表位于RAM中,主要用到的寄存器如下:

具体实现代码如下:

/*
 * Function: void clock_init(void)
 * Parameter: none
 * Return: none
*/
int main(void)
{
    memcpy((void*)0x20000000, (void*)APP_FLASHADDR, VECTOR_SIZE);
    SYSCFG->CFGR1 |= 0x03; // SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_SRAM);

    JumpToApp();
    while (1);
}

memcpy((void*)0x20000000, (void*)APP_FLASHADDR, VECTOR_SIZE);

  • APP_FLASHADDR是应用程序的起址地址,从这里开始的VECTOR_SIZE字节,存放是的应用程序的中断向量表。
  • VECTOR_SIZE是指中断向量表的大小,具体多大可以在startup.s文件里计算得到。

VECTOR_SIZE 的计算:

根据startup.s中的语句可以推导出来。

每一个DCD都代表一个中断向量,例如:

  DCD     USART1_IRQHandler              ; USART1
;这里的“USART1_IRQHandler"其实就是UART1中断服务程序USART1_IRQHandler这个函数,同时,它也代表这个函数的入口地址。  

这张表包括45个元素,每个元素是一个长度为4字节的地址。除了第一个地址是SP(堆栈指针)外,其它的地址都是某个中断服务程序的入口地址。

针对本例,就应该是45*4=180(0xB4)个字节。

在执行完以上两行代码后,若发生中断,CPU就会去SRAM(即0x2000 0000处)取中断向量了,所以,以0x20000000作为起始地址之后的VECTOR_SIZE个字节就不能被改动了。为了达到这VECTOR_SIZE个字节不被修改的目的,如下两种方法可以实现。

在工程文件内修改SRAM的起始地址及长度:

MDK-Options-Target-Read/Write Memory Areas

  • 勾选IRAM1:填入0x020000B4(大于或等于0x020000B4即可)

如果使用了分散加载文件,则在分散加载文件中修改SRAM的起始地址及长度也能达到目的。

原文地址:https://www.cnblogs.com/schips/p/13081597.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


  译序:JWMediaPlayer是开源的网页使用的Flash播放器。本文采摘于JWPlayer的官方文档,讲解了JWPlayer对于RTMP的使用方法,我们可以从JWPlayer客户端的角度来了解RTMP协议。以下是官方原文:      简介    RTMP(RealTimeMessagingProtocol
    Flash编程原理都是只能将1写为0,而不能将0写成1.所以在Flash编程之前,必须将对应的块擦除,而擦除的过程就是将所有位都写为1的过程,块内的所有字节变为0xFF.因此可以说,编程是将相应位写0的过程,而擦除是将相应位写1的过程,两者的执行过程完全相反.一、Nor和NandFlash
 上传setenvgatewayip192.168.1.1;setenvserverip192.168.1.7;setenvipaddr192.168.1.156;mw.b0x820000000xff0x1000000sfprobe0sfread0x8200000000x1000000tftp0x82000000test.bin0x1000000 下载mw.b82000000ff1000000tftp82000000test.bi
Error:FlashDownloadFailed-"Cortex-M3"出现一般有两种情况:1.SWD模式下,Debug菜单中,Reset菜单选项(Autodetect/HWreset/sysresetReq/Vectreset)默认是AutoDetect,改成SysResetReq即可。2.Jtag模式下,主要是芯片大小选错。Flash->ConfigureFalshTools配置窗口,切换到“Utilities"
jPlayer是一个用于控制和播放mp3文件的jQuery插件。它在后台使用Flash来播放mp3文件,前台播放器外观完全可以使用XHML/CSS自定义。支持:有一点比较好的是,在支持html5的浏览器上会使用html5的标签audio或者video,而不支持的浏览器上使用swf来播放选择需要播放的Mp3文件。播放、暂停
#ifndef__FONTUPD_H__#define__FONTUPD_H__#include"sys.h" //字库信息结构体定义33字节__packedtypedefstruct{u8fontok;//字库存在标志,0XAA,字库正常;其他,字库不存在u32ugbkaddr;//unigbk的地址u32ugbksize;//unigbk的大小u32f12addr;//gbk12地址u32g
ROM(ReadOnlyMemory)和RAM(RandomAccessMemory)指的都是半导体存储器。ROM在系统停止供电的时候仍然可以保持数据,而RAM通常都是在掉电之后就丢失数据,但是访问速度快。典型的RAM就是计算机的内存。RAM有两大类,一种称为静态RAM(StaticRAM/SRAM),SRAM速度非常快,是目前读写最快的存储
JSpc端和移动端实现复制到剪贴板功能实现在网页上复制文本到剪切板,一般是使用JS+Flash结合的方法,网上有很多相关文章介绍。随着HTML5技术的发展,Flash已经在很多场合不适用了,甚至被屏蔽。本文介绍的一款JS插件,实现了纯JS方法复制文本到剪切板。插件名是Clipboard.js,该插件不依
例子:R0=1R1=1R2=10R3=e000ed10R12=0LR=fffffff9(中断返回值)PC=0PSR=60000013或60000016或60000036(Z、C、EXCEPT_NUM:RTC_WKUP_IRQn、EXTI0_IRQn、USART2_IRQn)BFAR=e000ed38(不关心)CFSR=20000(INVSTATE:Invalidstateusagefault thePCvaluestackedf
 内存接口概念首先来分析下操作GPIO控制器和操作UART控制器两者的区别如图是S3C2440是个片上系统,有GPIO控制器(接有GPIO管脚),有串口控制器(接有TXDRXD引脚)配置GPIO控制器相应的寄存器,即可让引脚输出高低电平;配置UART控制器相应的寄存器,即可让引脚输出波形。前者相对简单,类
小编导语:    近几年来,网页游戏成为了游戏界关注的焦点,由于其制作简单,成本低并且收益率较高,因此成为了众多游戏厂商追逐的对象,但是除了商家夸张的炒作宣传外,很少有页游佳作出现。然而,随着Unity3D游戏引擎的出现,网页游戏的3D化成了页游冲出重围的杀手锏,那么在flash网页游戏称
1.指定数组到特定的Flash单元#pragmalocation=0x000FFF00 __rootconstcharFlash_config[]={0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0
继续研究发现,计算机的固件真的很有趣。参考了一些重要的资料,比如http://donovan6000.blogspot.com/2013/06/insyde-bios-modding-advanced-and-power-tabs.html等,对于IDA的使用也了解了一些。最后,总结一下目前看来可行性的方案:0.基础知识储备,包括UEFIBIOS的概念,InsydeBIOS的
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>navigator对象<itle></head><body><buttononclick="checkFlash()">检测</button>
修改网上流传的flash-marker.js(function(global,factory){typeofexports==='object'&&typeofmodule!=='undefined'?module.exports=factory():typeofdefine==='function'&&define.amd?define(factory
shareObject本地缓存存储位置:win7系统用户到C:\Users\[你的用户名]\AppData\Roaming\Macromedia\FlashPlayer\#SharedObjects\XP或2003用户到:C:\DocumentsandSettings\用户名\ApplicationData\Macromedia\FlashPlayer\#SharedObjects\ ---------------------作者:iteye_
安装谷歌浏览器之后经常遇到Flash崩溃或者浏览器在浏览Flash内容时卡死的情况。在网上查找资料大多都认为应该是浏览器自带的Flash插件工作模式引起的问题,解决方法如下:首先在地址栏输入chrome://plugins/显示浏览器使用的插件。点击右上角的详细信息,可以看到Flash插件为进程外
之前一直使用的W25Q16spiflash都没问题,换了一款W25Q80后发现工作不正常,经过测试,初步定位到问题在于初始化SPI后是否将CS拉高。于是又去查看了一下原厂代码:发现原厂的代码初始化SPI接口时是专门拉高CS的。结论:网上很多代码初始化SPI接口时没有专门拉高CS,对某些型号可能确实
======================================================NANDFlash最小存储单元:写数据操作:通过对控制闸(ControlGate)施加高电压,然后允许源极(SOURCE)和汲极(RRAIN)间的N信道(N-Channel)流入电子,等到电流够强,电子获得足够能量时,便会越过浮置闸(FloatingGate)底下的二氧化硅层(S
安装CnarioPlayer3.8.1.156或其他版本时,有时会出现如下提示:Warning4154.AdobeFlashPlayer13...notcorrectlyinstalled:请前往AdobeFlash网站,并选择下图示的版本下载安装: