分类: 嵌入式
2018-03-04 16:15:17
之前我们分析了IAP的基本工作原理和编程应该注意的细节问题,接着上篇,我们来看看具体的编码问题。
上篇基本将IAP工作的机理和程序组成以及运行路程分析过了,所以我们只看看关键模块的编码。
首先分析IAP,关键模块有三部分:通讯,FLASH操作,引导跳转。
我们先来谈谈通讯问题。可以将,无论什么通讯,都可以完成代码的传输,USART也好,USB也好,CAN也好,等等,只要是通讯外设,你都可以用来传输外设,但是考虑到实用性,也许USART是简单也是最最常用的。我们以就以USART为例来讲通讯。
文件的传输一定要稳定,传输过程中不可以丢数据,否则传输的文件就有问题,导致最后的APP程序存在问题。所以我们使用USART时需要选择合适波特率,要尽量大保证传输速率,但又不能太大导致丢帧。然后开启中断在中断将接收到的数据存放到一个数组里即可,随后处理即可。编码相信大家都会,就不在这里复述了。
我们需要将接受到的APP程序写到合适的FLASH地址,以便后面引导启动。
首先应该明确STM32F4的FLASH地址为0x08000000-0x080FFFFF,若果越过这个空间写肯定是有问题的。下面我们看看FLASH写入的步骤。
1.校验写入地址有效性
//写入地址必须处于FLASH区间并且地址为4的倍数【按字写入】 if(WriteAddr < FLASH_BASEADDR || WriteAddr%4) //addr error return ;
2.FLASH解锁,缓冲区除能
//必须要解锁FLASH并保证数据缓存处于关闭状态才可以进行FLASH写入 FLASH_Unlock(); //ready to clear sector FLASH_DataCacheCmd(DISABLE);
3.判断是否要擦除缓冲区
//当指定地址内数据不为OXFFFFFFFF时,我们需要擦除这个地址内的内容以便我们重新写入,这里要注意的是我们擦除的时候是以整个扇区为单位擦除的,所在扇区内容都会消失,这就是我们之前为什么建议大家将伪BOOT放在单独的扇区进行处理的原因 if(FLASH_ReadWord(AddStart) != 0XFFFFFFFF)
{
FlashStatus = FLASH_EraseSector(FLASH_GetFlashSector(AddStart), VoltageRange_3);// vcc = 2.7~3.6v if(FlashStatus != FLASH_COMPLETE) break;//error }
4.重写【我们通过串口或其他外设接收到的数据为八位,而FLASH操作要求按字写入,所以这块一定要注意数据的转化】
//调用系统函数重新将数据按字写入 while(WriteAddr < AddEnd)
{ if(FLASH_ProgramWord(WriteAddr, *pBuffer)!= FLASH_COMPLETE) break;//error WriteAddr += 4;
pBuffer++;
}
4.失能缓存,FLASH上锁,完成写入操作
FLASH_DataCacheCmd(ENABLE); //write flash
finsh FLASH_Lock();
到这里我们的FLASH写入模块算是完成了,主要抓住FLASH操作的核心便可以完成FLASH的写入操作。
引导跳转的核心步骤有两步:
1.地址空间有效性校验
if(((*(vu32*) LoadAddr)&0x2FFE0000) == 0x20000000) //check the addr of StackTop
2.保存复位中断向量
typedef void(*LoadAddrFunVar)(void);
//... LoadAddrFunVar StartLoad;
//... StartLoad = (LoadAddrFunVar)*(vu32*)(LoadAddr+4); //load the addr
3.将用户堆栈指针指向用户堆栈栈顶
//set Main Stack value __asm void MSR_MSP(u32 addr) {
MSR MSP, r0
BX LR
} //... MSR_MSP(*(vu32*)LoadAddr);
4.引导跳转
StartLoad(); //start
那么,我们的APP程序该怎么编写呢?
其实我们的APP程序和我们平时写的程序没有任何区别,唯独要增加两个工作:
我们首先需要通过KEIL->Options->Target->IROM1设置我们的APP程序需要写入的FLASH空间
一般来讲,这个空间可以设置成为我们之前的引导程序所占空间之外其余的任何FLASH空间,但是我们设置的时候需要考虑:
比如图中我将APP写入到了扇区1【引导后面的扇区】,Size设置成剩余的FLASH大小。这是没有问题的。
还有很重要的一步便是中断向量表重定向,原因在上篇文章里已经讲过,这里我们看看具体怎么操作。
STM32提供了中断向量表的偏移设置寄存器SCB->VTOR,我们可以在程序开始的时候调用它完成重定向,也可以调用函数NVIC_SetVectorTable()完成中断向量表的重定向。
SCB->VTOR = (FLASH_BASE |0x4000); //or NVIC_SetVectorTable(FLASH_BASE,0x4000);
这里的0x4000就是我代码写入FLASH地址0x08004000相对于FLASH_BASE[0x08000000]的偏移量。
那么我们的APP程序写好了,如何传输呢?
这里要强调一下我们需要使用keil自带的fromelf.exe,将编译连接后生成的axf文件转化为可以直接写入FLASH的二进制数据文件——
.bin文件。只有将程序转化为存储器中直接存储的二进制文件,才能不经过任何处理写入到存储器后运行,而hex和axf文件则不满足要求。如何使用
fromelf.exe呢?我们点击option->User->After Build/Rebuild,勾选RUN#1,后面输入
//根据实际情况修改keil安装目录和链接输出文件夹位置
keil安装目录\KEIL\ARM\ARMCC\bin\fromelf.exe --bin -o ..\OBJ\FileName.bin ..\OBJ\FileName.axf
这样我们在build或者rebuild后会执行这段代码,使用fromelf工具将axf转化为bin文件。然后通过串口助手将bin文件发送到开发板就ok啦。
这样,我们整个IAP从boot到APP都基完成啦~
以上就是IAP的完整的学习笔记啦~笔记1主要分享了IAP的工作和引导机理,笔记2主要进行了关键代码解析。写完这两篇,个人对STM32的启 动,程序加载,存储器存储有了更深的了解,相信大家也对学到了不少知识。以上内容纯属个人见解,如果大家发现有什么不对的地方,欢迎指正哈~
最后,贴上自己的IAP程序,依靠USART控制,亲测可行。