历史上的今天
返回首页

历史上的今天

今天是:2025年04月21日(星期一)

正在发生

2021年04月21日 | IAR环境下STM32+IAP方案的实现

2021-04-21 来源:eefocus

一、什么是IAP,为什么要IAP


IAP即为In Application Programming(在应用中编程),一般情况下,以STM32F10x系列芯片为主控制器的设备在出厂时就已经使用J-Link仿真器将应用代码烧录了,如果在设备使用过程中需要进行应用代码的更换、升级等操作的话,则可能需要将设备返回原厂并拆解出来再使用J-Link重新烧录代码,这就增加了很多不必要的麻烦。站在用户的角度来说,就是能让用户自己来更换设备里边的代码程序而厂家这边只需要提供给用户一个代码文件即可。


而IAP却能很好的解决掉这个难题,一片STM32芯片的Code(代码)区内一般只有一个用户程序。而IAP方案则是将代码区划分为两部分,两部分区域各存放一个程序,一个叫bootloader(引导加载程序),另一个较user application(用户应用程序)。bootloader在出厂时就固定下来了,在需要变更user application时只需要通过触发bootloader对userapplication的擦除和重新写入即可完成用户应用的更换。如图1-1所示

图 1-1


在程序执行初始进入bootloader,在bootloader里面检测条件是否被触发(可通过按键是否被按下、串口是否接收到特定的数据、U盘是否插入等等),如果有则进行对user application进行擦除和重新写入操作,如果没有则直接跳转到user application执行应用;如果有则进行擦除用户代码并重新写入新的用户代码。


二、STM32F103ZET6硬件条件


STM32F103ZET6的启动方式有三种:内置FLASH启动、内置SRAM启动、系统存储器ROM启动,通过BOOT0和BOOT1引脚的设置可以选择从哪中方式启动,这里选择内置的FLASH启动。其FLASH的地址为0x08000000—0x0807ffff,共512KB,这些都能从芯片数据手册中直接得到。而这里首要的一个问题是中断的问题。正常情况下发生中断的过程为:发生中断(中断请求)à到中断向量表查找中断函数入口地址à跳转到中断函数à执行中断函数à中断返回。也就是说在STM32的内置的Flash中有一个中断向量表来存放各个中断服务函数的入口地址,内置Flash的分配情况大致如下图2-1。

图2-1


在只有一个程序的情况下,程序执行的走向应该如图2-2所示(借用网友的原图)。

图2-2


STM32F10x有一个中断向量表,这个中断向量表存放在代码开始部分的后4个字节处(即0x08000004),代码开始的4个字节存放的是堆栈栈顶的地址,当发生中断后程序通过查找该表得到相应的中断服务程序入口地址,然后再跳到相应的中断服务程序中执行。上电后从0x08000004处取出复位中断向量的地址,然后跳转到复位中断程序的入口(标号①所示),执行结束后跳转到main函数中(标号②所示)。在执行main函数的过程中发生中断,则STM32强制将PC指针指回中断向量表处(标号③所示),从中断向量表中找到相应的中断函数入口地址,跳转到相应的中断服务函数(标号④所示),执行完中断函数后再返回到main函数中来(标号⑤所示)。


若在STM32F103x中使用IAP方案,则内置的Flash分配情况大致如下图2-3。



图2-3


在内置的Flash里面添加一个BootLoader程序,BootLoader程序和userapplication各有一个中断向量表,假设BootLoader程序占用的空间为N+M字节,则程序的走向应该如图2-2所示(借用网友的原图并做改动,其中虚线部分为原图步骤④⑤的走向,本人改为指向灰色部分)。


图2-2


上电初始程序依然从0x08000004处取出复位中断向量地址,执行复位中断函数后跳转到IAP的main(标号①所示),在IAP的main函数执行完成后强制跳转到0x08000004+N+M处(标号②所示),最后跳转到新的main函数中来(标号③所示),当发生中断请求后,程序跳转到新的中断向量表中取出新的中断函数入口地址,再跳转到新的中断服务函数中执行(标号④⑤所示),执行完中断函数后再返回到main函数中来(标号⑥所示)。


对于步骤④⑤,网友认为是:“在main执行的过程中,如果CPU得到一个中断请求,PC指针仍强制跳转到地址0x08000004中断向量表处,而不是新的中断向量表,如图标号④所示,程序再根据我们设置的中断向量表偏移量,跳转到对应中断源新的中断服务程序中,如图标号⑤所示”。我对此的理解是:“当发生中断后,程序从0x08000004(旧)处的中断向量表中得到相应的中断服务函数入口地址,继而跳转到相应的中断服务程序”。但是旧的中断向量列表里边存放的是IAP程序中断函数的入口地址,它是如何得到user程序中断函数的入口地址呢?所以我觉得此种说法是错误的。“当发生中断时PC指针强制会跳转到0x08000004处”这种说法并没有错,只是忽略了后续的一些知识要点而导致这个说法出现矛盾。


对于步骤④⑤我认为的是,在main函数的执行过程中,如果CPU得到一个中断请求,PC指针本来应该跳转到0x08000004处的中断向量表,由于我们设置了中断向量表偏移量为N+M,因此PC指针被强制跳转到0x08000004+N+M处的中断向量表中得到相应的中断函数地址(待求证),再跳转到相应新的中断服务函数,执行结束后返回到main函数中来。


三、实现过程


STM32F103ZET6的Flash地址为0x08000000—0x0807ffff共512KB,把这512KB的空间分为两块,第一块大小为32KB存放BootLoader程序,剩余的空间存放用户程序(根据实际情况分配这两块空间的大小,BootLoader程序占用的空间越小越好,则BootLoader地址为0x08000000—0x08007fff,用户程序地址为0x08008000—0x0807ffff。BootLoader流程图大致应该如下:


1、初始化时钟。


2、初始化中断向量表地址。


3、初始化按键。(使用按键触发方式,上电时如果按键被按下则进行用户程序更新操作)


4、初始化串口。


5、检测按键是否被按下,是则执行步骤6,否则执行步骤10。


6、擦除用户程序(擦除0x08008000—0x0807ffff地址空间Flash)。


7、从串口读取新的用户代码数据,把代码写入用户程序空间。


8、检测串口数据接收完毕?是则执行步骤9,否则跳回步骤7。


9、用户程序更新完毕,等待重新上电或硬件复位。


10、跳转到用户程序(强制将PC指针跳转到0x08008000+4处)。


到这里首先要解决的问题就有:


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


2、中断向量表偏移如何设置。


3、如何改变代码存放的地址空间(因为BootLoader要存放在0x08000000处,用户程序要存放在0x08008000处,而默认的代码存放的地址空间为0x08000000)。


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


5、串口接收的用户代码数据是什么样的代码数据,是一种什么样的文件。


问题的解决:


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


①解锁:


FLASH_Unlock();//解锁Flash


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


②擦除:


for(i=0;i<240;i++)


{


if(FLASH_ErasePage(FLASH_ADDR+i*2048)!= FLASH_COMPLETE)//一定要判断是否擦除成功


returnERROR;


}


说明:FLASH_ErasePage(uint32_t Page_Address)即为Flash擦除操作,按页擦除,每页2KB,Page_Address为页的起始地址,如0x08000000是第一页起始地址,0x08000800为第二页起始地址,这里的操作擦除了0x08008000—0x0807ffff地址空间的Flash。


③写入:


unsignedchar 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。


④上锁:


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


2、关于中断向量表的偏移设置,对于BootLoader程序只需设置中断向量表的指向在0x08000000处,对于用户程序需要设置中断向量表的指向在0x08008000处即可。


①在BootLoader程序的中断向量表指向设置中应有这么一句:


NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);//设置中断向量表指向


其中NVIC_VectTab_FLASH是个宏定义,的值为0x08000000。


②在用户程序的中断向量表指向设置用应有这么一句:


NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x8000);//设置中断向量表指向


3、确认代码存放的地址空间,在IAR和在Keil中的设置是不同的,网上有在Keil中设置的方法,设立介绍在IAR软件环境下的设置方法。


①在固件库目录STM32F10x_StdPeriph_Lib_V3.5.0ProjectSTM32F10x_StdPeriph_TemplateEWARM下找到一个stm32f10x_flash.icf文件,将其复制到工程目录中来,在打开IAR工程,将配置文件添加到工程中,如下图3-2所示



图3-1


②在工程中打开stm32f10x_flash.icf该文件,修改两个参数即可改变代码存放的地址空间,图下图3-2所示。


图3-2


4、关于PC指针的强制跳转,想在BootLoader程序中将PC指针跳转到用户代码处,可选择下面的操作


typedefvoid (*pFunction)(void);


pFunctionJump_To_Application;


uint32_tJumpAddress;


#defineApplicationAddress0x08008000


if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)//--------①


{


JumpAddress= *(__IO uint32_t*) (ApplicationAddress + 4);//--------②


Jump_To_Application= (pFunction) JumpAddress;//--------③


__set_MSP(*(__IOuint32_t*) ApplicationAddress);//--------④


Jump_To_Application();//--------⑤


}


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


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


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


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


⑤跳转到新的复位函数。


5、通过串口来接收代码数据,就是PC机通过串口将代码数据发送到STM32中去。这里就涉及到两个问题:


①数据怎么得来。


②数据传输的过程需要遵循的协议,什么时候开始,什么时候结束。


解决①:一般我们就将*.hex文件使用JFlash-ARM打开再通过Jlink仿真器烧录到STM32芯片中,但是*.hex文件里边包含的数据不纯粹是代码数据还有一些别的东西,而*.bin文件数据就全部是代码数据。


在IAR软件环境中打开一个用户工程,先设置好中断向量表偏移和代码存放的地址空间后(前面已介绍过这两种方法)。设置工程如下图3-3所示,确认后重新编译工程,在工程的DebugExe目录下会相应生成一个xxx.bin文件,这就是所需要的代码文件。


图3-3


②数据通过串口来传输文件常用的协议有XModem、YModem、ZModem这三种协议,在PC端使用这些协议传输文件只需要PC的超级终端或者终端工具SecureCRT即可,但是在STM32这边的编程会增加一些困难(因为要先去读懂、解析这些协议,在通过编程来实现)。也可选择自己定义一套简单的传输协议,但同样会有一些困难(因为要在PC端进行文件和串口编程)。总之不管通过什么办法都行,只要能将xxx.bin文件数据通过串口全部发送到STM32并且STM32能够全部接收到这些数据并写入Flash即可(我选择后者,自定义传输协议并用VC进行文件和串口编程)。


四、结束语


总的来说STM32的IAP方案实现需要在进行用户程序之前加一段Bootloader程序,BootLoader程序的作用就是:


①什么都不做,直接跳转到用户程序。


②删除原有的用户程序,读取*.bin文件数据并将数据重新写入新的用户程序。


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


①改变中断向量表。


②改变代码存放的地址空间


③修改生成*.bin文件


使用通过UART的IAP方案并不是很好的选择,这只是IAP方案的一个机制,因为能使用PC机通过串口升级程序,同样能通过Jlink烧写程序,并且自定义的串口通讯协议在没有校CRC校验的情况下不能及时发现数据传输过程发生的错误。这里推荐使用SD卡(或U盘)进行用户程序更新,将*.bin文件复制到SD卡(或U盘)中,STM32再通过读取SD卡(或U盘)的*.bin文件进行用户程序更新,这也避免了STM32与PC笨重的通讯,只需插一个SD卡(或U盘)更显得人性化一些,但需要去弄懂STM32如何与SD卡(或U盘)的通讯。

推荐阅读

史海拾趣

DAESAN公司的发展小趣事

在电子行业中,技术创新是企业发展的核心驱动力。DAESAN公司深知这一点,因此一直致力于技术研发和创新。他们投入大量资金和资源,引进先进的生产设备和技术人才,不断推出具有竞争力的新产品。其中,一款高性能的电子芯片引起了行业的广泛关注。这款芯片采用了最新的纳米技术和低功耗设计,不仅性能卓越,而且节能环保。DAESAN公司凭借这一技术创新,成功打开了高端电子市场的大门。

Connection One公司的发展小趣事

面对日益激烈的市场竞争,Connection One公司不断推陈出新,研发出一系列创新产品。这些产品不仅性能卓越,而且设计新颖,深受消费者喜爱。公司因此赢得了大量忠实客户,业绩持续攀升。

Gravitech公司的发展小趣事

为了进一步提升国际竞争力,Connection One公司开始实施全球化战略布局。公司在全球范围内设立研发中心和销售网络,以便更好地了解当地市场需求并提供定制化产品。这一举措不仅加速了公司的国际化进程,还为其带来了更多的商业机会。

Custom LeatherCraft Manufacturing Co Inc公司的发展小趣事

为了扩大市场份额,CLC开始实施全球化战略。他们在世界各地寻找优质的原材料供应商,并与当地的合作伙伴建立长期合作关系。同时,他们还积极参加国际展览和贸易活动,展示自己的产品和技术实力。这些努力使CLC的品牌影响力逐渐扩大,产品也走进了更多的国家和地区。

Advanced Detector Corp公司的发展小趣事

随着技术的不断成熟和市场的扩大,ADC开始将业务扩展到更广泛的领域。公司不仅继续深耕探测器领域,还逐渐拓展到传感器、测量仪器等多个领域。通过不断推出多样化的产品,ADC成功满足了不同客户的需求,并在市场上取得了良好的口碑。

AdaptivEnergy公司的发展小趣事

随着技术的不断成熟和市场的扩大,ADC开始将业务扩展到更广泛的领域。公司不仅继续深耕探测器领域,还逐渐拓展到传感器、测量仪器等多个领域。通过不断推出多样化的产品,ADC成功满足了不同客户的需求,并在市场上取得了良好的口碑。

问答坊 | AI 解惑

谁有 ACD资料

谁有ACD 资料啊…

查看全部问答>

无线信号 高手进

我在这招了半天也莫有找到我要的答案,发个帖看看有没有高手给我支支招。 关于无线信号如何检测的问题,方法越具体越多越好。。。用示波器的话说明具体的用法。我琢磨很久了,奈何能力有限。。。…

查看全部问答>

打印机驱动开发,请问能学到东西吗?

不知道这里有没有了解打印机驱动开发的?我即将进入这个行业,感觉有点迷茫, 不知道有没有前途,能学到多少东西,似乎做这个以后转行也困难些。打印机驱动和其他的设备驱动开发相似吗? 有了解或者正在做这个的能说说吗? …

查看全部问答>

条码手持终端应用程序开发!!!

条码手持终端应用程序开发!!! 本人在条码行业有多年的工作经验,一直从事条码手持终端应用程序的开发 开发过多种设备: CASIO DT900,DT300,DTX10; Cipher 711 ; SYMBOL MC50,MC1000,PPT8800; Intermec 700系列 有需要的请联系本人 QQ:6 ...…

查看全部问答>

急!!!如何在PC机上实现对单片机的控制(用VC++6.0编程)

小弟正在想弄一个在PC机上实现对单片机的控制,但不知道从哪个方面入手? 希望各位达人给予小弟一些指点.发个程序给小弟参考参考(用C语言编写的)…

查看全部问答>

一道关于晶体管参数计算的题目

晶体管T接成如图所示的共基极电路时,测得Ic=10mA,Ib=0.1mA;而在发射极断开时,测得的Ic=1uA。试求该晶体管的Icbo、Iceo、a(阿尔法)、B(贝塔)的数值。  …

查看全部问答>

正式入手STM8S103K3

经历了很多推销的宣传,今天买了10片样品,准备把以前ATMEL8的东西都移植过来. 有几个问题,一直没搞明白: 1. STM8S103K3会不会象ATMEL那样,1000元就能把代码都读出来?STM8S103K3的     解密难度有多大? 2. 以前一直用ICC和KEIL开 ...…

查看全部问答>

#define 、typedef 你会用哪一个?

#define  uchar unsigned char typedef  unsigned char uchar不仅学单片机的,可能玩单片机的人也是用到#define 的。 当然uchar 最后用起来都是一样的最近整理51模块化程序  感觉规范代码还是有必要的 用typedef 来定义标识符 ...…

查看全部问答>

用io口控制k9k8G08U0A写不进去数(急)

这是个试验程序,就是把整页写入同一个数据,,另外ID什么都可以读出来,,个人感觉数据是可以读出来的,,但读出来的全是FF,2112个FF后是64个0x15,我把读命令改错的话,读出来的是那个输入的数据,说明内部没把数据读出来,这是不是说明FF读出来 ...…

查看全部问答>

diy在跟进中。。。。。

手头有别的事在忙,diy延期,截个图说明一下,diy在缓慢跟进中。。。。   由于没有人参加硬件部分,为了使更多人参与进来,真正实现“全民DIY”,硬件设计上将作部分调整,更多功能留给参与者自己去扩充。具体微调部分,待layout完了,统一 ...…

查看全部问答>