|
花了一天的时间才做好,真开心。
本来以为会很麻烦,原来麻烦的地方只是如何将APP传输到设备中。
写上位机软件就弄了半天,其它的IAP程序如何擦写如何跳转网上都有现成的例子,修修改改就能用。
具体的思路是把IAP程序和APP程序分成两个区域,IAP占ROM中的0x08000000-0x08002FFF,0x08003000之后的地址都归APP使用。APP的intvec设为0x08003000,ROM Start也需设为0x08003000。
因为我要做GPRS远程升级,下载APP的功能代码比较多,所以程序下载的协议就没放在IAP中了,直接用APP内原有的通讯协议来做。
下面放上IAP程序的代码,供有需要的朋友参考,部分代码抄自网上。
开发环境:IAR for ARM,使用STM32F103ZE。
需要注意的有几点:
1、SystemInit()函数会修改中断中断向量表偏移量,所以要修改中断向量表偏移量的话,NVIC_SetVectorTable函数要放在SystemInit的后面。
2、本例子中用到W25Q64作为存储区,所以如果要使用的话要自己选个片内地址或弄个外部存储器。修改读写函数Read_count和Page_Program即可。
- #include "stm32f10x.h"
- #include "w25q64.h"
- #define TRUE 0
- #define FALSE 1
- #define FLASH_PAGE_SIZE 2048 //FLASH页大小
- #define FLASH_PAGE_MAX 256 //FLASH页数量
- #define ApplicationAddress 0x08003000
- #define ADD_IAP 0x400000 //IAP数据起始地址
- #define ADD_IAP_FLAG 0x480000 //标志存放区
- #define IAP_UPDATA_FLAG 0x98123021 //升级标志
- typedef struct{
- u32 flag;//标记
- u32 add;//起始地址,以0开始
- u32 len;//长度
- u32 crc;//crc校验码
- }IapDef;
- typedef void (*pFunction)(void);
- pFunction Jump_To_Application;
- uint32_t JumpAddress;
- u8 buff[FLASH_PAGE_SIZE];
- u32 FlashErase()//擦除FLASH
- {
- u32 i;
- //擦除
- for(i=0;i<(FLASH_PAGE_MAX-(ApplicationAddress&0xFFFFF)/FLASH_PAGE_SIZE);i++)
- {
- if(FLASH_ErasePage(ApplicationAddress+i*2048) != FLASH_COMPLETE) //判断是否擦除成功
- return FALSE;
- }
- return(TRUE);
- }
- u32 FlashWrite(u32 ADDR,u8 *buf,u32 len)//写入FLASH
- {
- //写入:
- u32 i;
- unsigned short temp; //临时数据
- for(i=0;i<(len/2);i++)
- {
- temp = (buf[2*i+1]<<8) | buf[2*i]; //2个字节整合为1个半字
- if(FLASH_ProgramHalfWord(ApplicationAddress+ADDR,temp) != FLASH_COMPLETE) //判断是否写入成功
- {
- return(FALSE);
- }
- ADDR +=2; //地址要加2,因为每次写入的是2个字节(1个半字)
- }
- return(TRUE);
- }
- IapDef LoadIap()//从W25中读取标记结构体
- {
- IapDef iap;
- Read_count(ADD_IAP_FLAG,(u8 *)&iap,sizeof(IapDef));
- return(iap);
- }
- u32 IapCrc(u32 add,u32 len)//计算并返回CRC值
- {
- u32 crc,i;
- u32 tmp_len;
-
- CRC_ResetDR();//复位CRC数据寄存器
-
- while(len)
- {
- if(FLASH_PAGE_SIZE<len)
- {
- tmp_len=FLASH_PAGE_SIZE;
- }
- else
- {
- tmp_len=len;
- }
- len-=tmp_len;
- Read_count(ADD_IAP+add,buff,tmp_len);
-
- for(i=0;i<tmp_len;i++)
- {
- CRC->DR=buff[i];
- }
- add+=tmp_len;
- }
- crc=CRC_GetCRC();
- return(crc);
- }
- void SaveIap(IapDef iap)//将标记结构体写入W25
- {
- Sector_Erase(ADD_IAP_FLAG);//擦除W25Q64的扇区
- Page_Program(ADD_IAP_FLAG,(u8 *)&iap,sizeof(IapDef));//将结构体写入W25Q64
- }
- int main()
- {
- u32 len;
- RCC_DeInit();
- //NVIC_SetVectorTable( NVIC_VectTab_FLASH, 0 );//重定向中断向量表
- SystemInit();/* 配置系统时钟为72M */
- RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE );
- w25_int();
-
- IapDef iap,iap2;
- iap=LoadIap();
- iap2=iap;
- if(iap.flag==IAP_UPDATA_FLAG)
- {
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC,ENABLE);//开启CRC模块时钟
- if(iap.crc==IapCrc(iap.add,iap.len))//核对要升级数据的CRC校验码
- {
- //解锁
- FLASH_Unlock(); //解锁Flash
- FLASH_SetLatency(FLASH_Latency_2); //因为系统时钟为72M所以要
-
- FlashErase();//擦除FLASH
-
- while(iap.len)
- {
- if(iap.len>FLASH_PAGE_SIZE)
- {
- len=FLASH_PAGE_SIZE;
- }
- else
- {
- len=iap.len;
- }
- iap.len-=len;
- Read_count(ADD_IAP+iap.add,buff,len);
- FlashWrite(iap.add,buff,len);
- iap.add+=len;
- }
- //上锁:
- FLASH_Lock(); //Flash 上锁,一个固件库函数即可实现。
-
- iap2.flag=0;
- SaveIap(iap2);/*程序更新完毕后将IAP升级标志去除
- 之所以用iap2是为了保留add/len/crc的数据。
- */
- }
- }
-
- //跳转
- 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();
- }
-
- return 0;
- }
复制代码 |
阿莫论坛20周年了!感谢大家的支持与爱护!!
如果天空是黑暗的,那就摸黑生存;
如果发出声音是危险的,那就保持沉默;
如果自觉无力发光,那就蜷伏于牆角。
但是,不要习惯了黑暗就为黑暗辩护;
也不要为自己的苟且而得意;
不要嘲讽那些比自己更勇敢的人。
我们可以卑微如尘土,但不可扭曲如蛆虫。
|