是新的中断向量表,如图标号④所示,程序再根据我们设置的中断向量表偏移量,跳转到对应中断源新的中断服务程序中,如图标号⑤所示”。我对此的理解是:“当发生中断后,程序从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) //一定要判断是否擦除成功 return ERROR; }
说明:FLASH_ErasePage(uint32_t Page_Address)即为Flash擦除操作,按页擦除,每页2KB,Page_Address为页的起始地址,如0x08000000是第一页起始地址,0x08000800为第二页起始地址,这里的操作擦除了0x08008000—0x0807ffff地址空间的Flash。 ③写入:
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。 ④上锁:
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.0\\Project\\STM32F10x_StdPeriph_Template\\EWARM下找到一个stm32f10x_flash.icf文件,将其复制到工程目录中来,在打开IAR工程,将配置文件添加到工程中,如下图3-2所示
图3-1
②在工程中打开stm32f10x_flash.icf该文件,修改两个参数即可改变代码存放的地址空间,图下图3-2所示。
图3-2
4、关于PC指针的强制跳转,想在BootLoader程序中将PC指针跳转到用户代码处,可选择下面的操作
typedef void (*pFunction)(void); pFunction Jump_To_Application; uint32_t JumpAddress;
#define ApplicationAddress 0x08008000
if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000) //--------① {
JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4); //--------② Jump_To_Application = (pFunction)
JumpAddress; //--------③
__set_MSP(*(__IO uint32_t*)
ApplicationAddress); //--------④
Jump_To_Application(); //--------⑤ }
①因为用户程序开始位置(0x08008000处)的前4个字节存放的是堆栈的地址,堆栈地址必定是指向RAM空间的,而STM32的RAM空间起始地址为0x20000000,所以要进行判断。

