串口转IAP然后到APP

串口转IAP,然后到APP,串口发送11次TEST后,再进入IAP,如此循环往复工作。下面的程序经过实测,验证可行,目的是用来交流和学习。

IAP程序的main.c

#include "stm32f10x.h"//使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
#include "stdio.h"
#include "delay.h"

#include "STM32_FLASH.h"
#include "USART3.h"
#include "IAP.h"
#include "CommonVariable.h"
#include "AT24cxx.h"

/********************************************************************
函数: 运行IAP程序.		//
输入: 无
返回: 无.不再返回.
说明:
    由于APP是在IAP的基础上运行的,因此,IAP一定是有效的,这里不再作IAP有效性检查.
    APP跳IAP
************************************************************************/
void app_jump_to_iap(void)
{
	u32  IapSpInitVal;//IAP程序的SP初值
	u32	 IapJumpAddr;//IAP程序的跳转地址.即IAP程序的入口
	void (*pIapFun)(void);//定义一个函数指针,用于指向APP程序入口

//  RCC_DeInit();
	NVIC_SystemReset();//恢复NVIC为复位状态.使中断不再发生.
//	INTX_DISABLE();//关闭所有中断
	__set_CONTROL(0);//将PSP指针切换为MSP指针
	IapSpInitVal = *(vu32 *)STM32_FLASH_BASE_ADDRESS;//取APP的SP初值.
	IapJumpAddr = *(vu32 *)(STM32_FLASH_BASE_ADDRESS + 4);//取程序入口.
	__set_MSP(IapSpInitVal);//设置SP
	pIapFun = (void (*)(void))IapJumpAddr;//生成跳转函数
	(*pIapFun)();//跳转,不再返回
}

/*********************************************************************
 *函数名:iap_jump_to_app
 *描述  :执行跳入APP
 *输入  :appAddr
 *输出  :无
 *返回值:无
 *说明:
***********************************************************************/
u8 iap_jump_to_app(u32 appAddr)
{
	u32 app_msp_addr;
	u32 app_jump_addr;
	void  (*pAppFun)(void);//定义一个函数指针,用于指向app程序入口
	app_msp_addr = (*(vu32 *)FLASH_APP1_ADDR);//取栈顶地址保存的数据
	app_jump_addr = (*(vu32 *)(FLASH_APP1_ADDR + 4));//取出APP程序复位中断向量的地址
	if ((app_msp_addr & 0x2FFE0000) != 0x20000000)//检查app栈顶地址是否合法,判断是否已经下载程序
		return 1;

	RCC_DeInit();
	INTX_DISABLE();//关闭所有中断
	FLASH_Lock();
	__set_MSP(app_msp_addr);//设置MSP指针指向app向量表的栈顶地址保存的地址
	pAppFun = (void (*)(void))app_jump_addr;//生成跳转函数
	(*pAppFun)();//跳转,PC指针执行复位
	return 0;
}

const char CPU_Reset_REG[]="\r\nCPU reset!\r\n";
const char CPU_Run_REG[]="\r\nIAP Run!\r\n";
int main(void)
{
	u16 oldcount=0;	//老的串口接收数据值
	u16 applenth=0;	//接收到的app代码长度
	u8 retdata;

	SystemInit();
	delay_init(); //延时函数初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
	USART3_Init(115200);
	printf("%s",CPU_Reset_REG);//调试串口输出"\r\nCPU reset!\r\n"

  EEPROM_PIN_INIT();
	IAPWorkFlag = EEPROM_U8_Data_Read1(EEPROM_IAPWorkFlag_ADDRESS);
	if(IAPWorkFlag>1)
	{
		IAPWorkFlag=1;
		EEPROM_U8_Data_Write(IAPWorkFlag,EEPROM_IAPWorkFlag_ADDRESS);
	}
//	if(IAPWorkFlag==1) Erase_APP_Program(FLASH_APP1_ADDR,USART3_RX_Buffer_Size);//擦除FLASH
	
	printf("%s",CPU_Run_REG);//调试串口输出"\r\nIAP Run!\r\n"
	printf("IAPWorkFlag=%u\r\n",IAPWorkFlag);
	if(IAPWorkFlag==1)USART3_RX_Buffer_Load_Index=0;

	INTX_ENABLE();//开启所有中断
	while(1)
	{
    if(IAPWorkFlag==0)//执行APP程序
		{			
		  retdata=iap_jump_to_app(FLASH_APP1_ADDR);//若APP程序不能执行,则返回1
		  if(retdata)
		  {
				IAPWorkFlag=1;//执行"IAP bootLoader"
		  }
	  }

		if(IAPWorkFlag==1)//接收APP的bin数据
		{
			delay_ms(10);//串口1等待新新数据
	 	  if(USART3_RX_Buffer_Load_Index)
		  {
			  if(oldcount==USART3_RX_Buffer_Load_Index)//新周期内,没有收到任何数据,认为本次数据接收完成.
			  {
				  applenth=USART3_RX_Buffer_Load_Index;//接收到的app代码长度
				  oldcount=0;
				  USART3_RX_Buffer_Load_Index=0;
				  printf("用户程序接收完成!\r\n");
				  printf("代码长度:%dBytes\r\n",applenth);

				  printf("开始更新固件...\r\n");	
 				  if(((*(vu32*)(USART3_RX_Buffer_BASE_ADDRESS+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
				  {
					  iap_write_appbin(FLASH_APP1_ADDR,USART3_RX_Buffer,applenth);//更新FLASH代码   
					  printf("固件更新完成!\r\n");	
						IAPWorkFlag=2;//允许执行FLASH中的用户代码
				  }
			  }
			  else oldcount=USART3_RX_Buffer_Load_Index;			
		  }
	  }

		if(IAPWorkFlag==2)//执行FLASH中的用户代码
		{
			printf("开始执行FLASH用户代码!!\r\n");
			if( ( ( *(vu32*)(FLASH_APP1_ADDR+4) )&0xFF000000 )==0x08000000 )//判断是否为0X08XXXXXX.
			{
			  USART3_RX_Buffer[0]='\0';USART3_RX_Buffer[1]='\0';USART3_RX_Buffer[2]='\0';USART3_RX_Buffer[3]='\0';
			  USART3_RX_Buffer[4]='\0';USART3_RX_Buffer[5]='\0';USART3_RX_Buffer[6]='\0';
        IAPWorkFlag=0;
				EEPROM_U8_Data_Write(IAPWorkFlag,EEPROM_IAPWorkFlag_ADDRESS);			
//				iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
			}
			else
			{
				printf("非FLASH应用程序,无法执行!\r\n"); 
        IAPWorkFlag=1;//重新执行IAP				
			}
		}
	}
}

IAP的delay.c程序

#include "delay.h"

static u8  fac_us=0;//us延时倍乘数			   
static u16 fac_ms=0;//ms延时倍乘数,在ucos下,代表每个节拍的ms数
	
	
#if SYSTEM_SUPPORT_OS							//如果SYSTEM_SUPPORT_OS定义了,说明要支持OS了(不限于UCOS).
//当delay_us/delay_ms需要支持OS的时候需要三个与OS相关的宏定义和函数来支持
//首先是3个宏定义:
//    delay_osrunning:用于表示OS当前是否正在运行,以决定是否可以使用相关函数
//delay_ostickspersec:用于表示OS设定的时钟节拍,delay_init将根据这个参数来初始哈systick
// delay_osintnesting:用于表示OS中断嵌套级别,因为中断里面不可以调度,delay_ms使用该参数来决定如何运行
//然后是3个函数:
//  delay_osschedlock:用于锁定OS任务调度,禁止调度
//delay_osschedunlock:用于解锁OS任务调度,重新开启调度
//    delay_ostimedly:用于OS延时,可以引起任务调度.

//本例程仅作UCOSII和UCOSIII的支持,其他OS,请自行参考着移植
//支持UCOSII
#ifdef 	OS_CRITICAL_METHOD						//OS_CRITICAL_METHOD定义了,说明要支持UCOSII				
#define delay_osrunning		OSRunning			//OS是否运行标记,0,不运行;1,在运行
#define delay_ostickspersec	OS_TICKS_PER_SEC	//OS时钟节拍,即每秒调度次数
#define delay_osintnesting 	OSIntNesting		//中断嵌套级别,即中断嵌套次数
#endif

//支持UCOSIII
#ifdef 	CPU_CFG_CRITICAL_METHOD					//CPU_CFG_CRITICAL_METHOD定义了,说明要支持UCOSIII	
#define delay_osrunning		OSRunning			//OS是否运行标记,0,不运行;1,在运行
#define delay_ostickspersec	OSCfg_TickRate_Hz	//OS时钟节拍,即每秒调度次数
#define delay_osintnesting 	OSIntNestingCtr		//中断嵌套级别,即中断嵌套次数
#endif


//us级延时时,关闭任务调度(防止打断us级延迟)
void delay_osschedlock(void)
{
#ifdef CPU_CFG_CRITICAL_METHOD   				//使用UCOSIII
	OS_ERR err; 
	OSSchedLock(&err);							//UCOSIII的方式,禁止调度,防止打断us延时
#else											//否则UCOSII
	OSSchedLock();								//UCOSII的方式,禁止调度,防止打断us延时
#endif
}

//us级延时时,恢复任务调度
void delay_osschedunlock(void)
{	
#ifdef CPU_CFG_CRITICAL_METHOD   				//使用UCOSIII
	OS_ERR err; 
	OSSchedUnlock(&err);						//UCOSIII的方式,恢复调度
#else											//否则UCOSII
	OSSchedUnlock();							//UCOSII的方式,恢复调度
#endif
}

//调用OS自带的延时函数延时
//ticks:延时的节拍数
void delay_ostimedly(u32 ticks)
{
#ifdef CPU_CFG_CRITICAL_METHOD
	OS_ERR err; 
	OSTimeDly(ticks,OS_OPT_TIME_PERIODIC,&err);	//UCOSIII延时采用周期模式
#else
	OSTimeDly(ticks);							//UCOSII延时
#endif 
}
 
//systick中断服务函数,使用ucos时用到
void SysTick_Handler(void)
{	
	if(delay_osrunning==1)						//OS开始跑了,才执行正常的调度处理
	{
		OSIntEnter();							//进入中断
		OSTimeTick();       					//调用ucos的时钟服务程序               
		OSIntExit();       	 					//触发任务切换软中断
	}
}
#endif

			   
//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init()
{
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
	u32 reload;
#endif

	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);	//选择外部时钟  HCLK/8
	fac_us=SystemCoreClock/8000000;				//为系统时钟的1/8

#if SYSTEM_SUPPORT_OS //如果需要支持OS.
	reload=SystemCoreClock/8000000;				//每秒钟的计数次数 单位为M  
	reload*=1000000/delay_ostickspersec;		//根据delay_ostickspersec设定溢出时间
												//reload为24位寄存器,最大值:16777216,在72M下,约合1.86s左右	
	fac_ms=1000/delay_ostickspersec;			//代表OS可以延时的最少单位	   

	SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;   	//开启SYSTICK中断
	SysTick->LOAD=reload; 						//每1/delay_ostickspersec秒中断一次	
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;   	//开启SYSTICK    

#else //不支持OS
	fac_ms=(u16)fac_us*1000;					//非OS下,代表每个ms需要的systick时钟数   
#endif
}								    

#if SYSTEM_SUPPORT_OS //如果需要支持OS.
//延时nus
//nus为要延时的us数.		    								   
void delay_us(u32 nus)
{		
	u32 ticks;
	u32 told,tnow,tcnt=0;
	u32 reload=SysTick->LOAD; //LOAD的值	    	 
	ticks=nus*fac_us; 							//需要的节拍数	  		 
	tcnt=0;
	delay_osschedlock();						//阻止OS调度,防止打断us延时
	told=SysTick->VAL;        					//刚进入时的计数器值
	while(1)
	{
		tnow=SysTick->VAL;	
		if(tnow!=told)
		{	    
			if(tnow<told)tcnt+=told-tnow;		//这里注意一下SYSTICK是一个递减的计数器就可以了.
			else tcnt+=reload-tnow+told;	    
			told=tnow;
			if(tcnt>=ticks)break;				//时间超过/等于要延迟的时间,则退出.
		}  
	};
	delay_osschedunlock();						//恢复OS调度									    
}
//延时nms
//nms:要延时的ms数
void delay_ms(u16 nms)
{	
	if(delay_osrunning&&delay_osintnesting==0)	//如果OS已经在跑了,并且不是在中断里面(中断里面不能任务调度)	    
	{		 
		if(nms>=fac_ms)							//延时的时间大于OS的最少时间周期 
		{ 
   			delay_ostimedly(nms/fac_ms);		//OS延时
		}
		nms%=fac_ms;							//OS已经无法提供这么小的延时了,采用普通方式延时    
	}
	delay_us((u32)(nms*1000));					//普通方式延时  
}

#else //不用OS时
//延时nus
//nus为要延时的us数.		    								   
void delay_us(u32 nus)
{		
	u32 temp;	    	 
	SysTick->LOAD=nus*fac_us; 					//时间加载	  		 
	SysTick->VAL=0x00;        					//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;	//开始倒数	  
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));		//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//关闭计数器
	SysTick->VAL =0X00;      					 //清空计数器	 
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864 
void delay_ms(u16 nms)
{	 		  	  
	u32 temp;		   
	SysTick->LOAD=(u32)nms*fac_ms;				//时间加载(SysTick->LOAD为24bit)
	SysTick->VAL =0x00;							//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;	//开始倒数  
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));		//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//关闭计数器
	SysTick->VAL =0X00;       					//清空计数器	  	    
} 
#endif

IAP的SYS.c程序

#include "sys.h"

//THUMB指令不支持汇编内联
//采用如下方法实现执行汇编指令WFI  
void WFI_SET(void)
{
	__ASM volatile("wfi");		  
}
//关闭所有中断
void INTX_DISABLE(void)
{		  
	__ASM volatile("cpsid i");
}
//开启所有中断
void INTX_ENABLE(void)
{
	__ASM volatile("cpsie i");		  
}
//设置栈顶地址
//addr:栈顶地址
void __asm  MSR_MSP(u32 addr) 
{
    MSR MSP, r0
    BX r14
}



sys.h文件如下:
#ifndef __SYS_H
#define __SYS_H	
#include "stm32f10x.h"

#define CLI()  __set_PRIMASK(1)         //关闭总中断
#define SEI()  __set_PRIMASK(0)        //打开总中断

typedef enum
{
  FALSE = 0, TRUE  = !FALSE
}
bool;

#ifndef NULL
#define NULL ((void *)0)
#endif
	 
//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C 
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C 
#define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C 
#define GPIOD_ODR_Addr    (GPIOD_BASE+12) //0x4001140C 
#define GPIOE_ODR_Addr    (GPIOE_BASE+12) //0x4001180C 
#define GPIOF_ODR_Addr    (GPIOF_BASE+12) //0x40011A0C    
#define GPIOG_ODR_Addr    (GPIOG_BASE+12) //0x40011E0C    

#define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808 
#define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08 
#define GPIOC_IDR_Addr    (GPIOC_BASE+8) //0x40011008 
#define GPIOD_IDR_Addr    (GPIOD_BASE+8) //0x40011408 
#define GPIOE_IDR_Addr    (GPIOE_BASE+8) //0x40011808 
#define GPIOF_IDR_Addr    (GPIOF_BASE+8) //0x40011A08 
#define GPIOG_IDR_Addr    (GPIOG_BASE+8) //0x40011E08 
 
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

//以下为汇编函数
void WFI_SET(void);		//执行WFI指令
void INTX_DISABLE(void);//关闭所有中断
void INTX_ENABLE(void);	//开启所有中断
void MSR_MSP(u32 addr);	//设置堆栈地址

#endif

IAP的STM32_FLASH.c程序如下:

#include "STM32_FLASH.h"
#include "CommonVariable.h"

void STMFLASH_Write(u32 tWriteAddr,u16 *pBuffer,u16 tSize);
void STMFLASH_Read(u32 tReadAddr,u16 *pBuffer,u16 tSize) ;
//void Erase_APP_Program(u32 tWriteAddr,u16 tSize);

//函数功能:从CPU的FLASH地址tReadAddr处读取半字(16位数据)
//tReadAddr:CPU的FLASH地址(此地址必须为2的倍数!!)
//返回值:返回半字(16位数据)
u16 STMFLASH_ReadHalfWord(u32 tReadAddr)
{
	return *(vu16*)tReadAddr;
}

#if STM32_FLASH_WREN	//如果使能了写

u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字节

//函数功能:将pBuffer[]中前tSize个半字,从FLASH起始地址为tWriteAddr开始处写入,在写之前不检查能否写入
//tWriteAddr:CPU的FLASH起始地址
//pBuffer:待写数据缓冲区的起始指针
//tSize:需要写入"半字(16位)"的个数
void STMFLASH_Write_NoCheck(u32 tWriteAddr,u16 *pBuffer,u16 tSize)
{
	u16 i;
	for(i=0;i<tSize;i++)//循环写入tSize个"半字"
	{
		FLASH_ProgramHalfWord(tWriteAddr,pBuffer[i]);
		tWriteAddr=tWriteAddr+2;//CPU的FLASH地址增加2
	}
}

//函数功能:将pBuffer[]中前tSize个半字,从FLASH起始地址为tWriteAddr开始处写入,在写之前检查能否写入
//tWriteAddr:起始地址(此地址必须为2的倍数!!)
//pBuffer:数据指针
//tSize:半字(16位)数(就是要写入的16位数据的个数.)
void STMFLASH_Write(u32 tWriteAddr,u16 *pBuffer,u16 tSize)
{
	u32 tSectorNumber;             //扇区编号
	u16 tOffsetAddrWithinSector;   //扇区内偏移地址(按照半字计算)
	u16 tSizeOfSectorRemainingPart;//扇区内剩余空间大小(按照半字计算)
	u32 tOffsetAddrOfFlash;        //去掉0X08000000后的地址
	u32 tPage_Address;//页地址
	u16 t;
	u16 i;

	if( tWriteAddr<STM32_FLASH_BASE_ADDRESS||(tWriteAddr>=(STM32_FLASH_BASE_ADDRESS+1024*STM32_FLASH_SIZE)) )return;//非法地址

	FLASH_Unlock();//解锁

	tOffsetAddrOfFlash=tWriteAddr-STM32_FLASH_BASE_ADDRESS;  //实际偏移地址.
	tSectorNumber=tOffsetAddrOfFlash/STM_SECTOR_SIZE;//扇区编号,对于STM32F103RBT6来说扇区编号范围为0~127
	tOffsetAddrWithinSector=(tOffsetAddrOfFlash%STM_SECTOR_SIZE)/2;       //在扇区内的偏移(2个字节为基本单位.)
	tSizeOfSectorRemainingPart=STM_SECTOR_SIZE/2-tOffsetAddrWithinSector; //计算当前扇区的剩余空间大小   
	if(tSize<=tSizeOfSectorRemainingPart)tSizeOfSectorRemainingPart=tSize;//没有越过当前扇区的范围
	while(1)//循环写FLASH 
	{	
		tPage_Address=tSectorNumber*STM_SECTOR_SIZE;tPage_Address=tPage_Address+STM32_FLASH_BASE_ADDRESS;//计算页地址
		STMFLASH_Read(tPage_Address,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
		for(i=0;i<tSizeOfSectorRemainingPart;i++)//校验数据
		{
			t=tOffsetAddrWithinSector+i;
			if(STMFLASH_BUF[t]!=0XFFFF)break;//需要擦除  	  
		}
		if(i<tSizeOfSectorRemainingPart)//需要擦除
		{
			FLASH_ErasePage(tPage_Address);//擦除这个扇区
			for(i=0;i<tSizeOfSectorRemainingPart;i++)//将"pBuffer[]中的数据"覆盖"STMFLASH_BUF[]中需要修改的部分"
			{
				t=tOffsetAddrWithinSector+i;
				STMFLASH_BUF[t]=pBuffer[i];	  
			}
			STMFLASH_Write_NoCheck(tPage_Address,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区  
		}
		else STMFLASH_Write_NoCheck(tWriteAddr,pBuffer,tSizeOfSectorRemainingPart);//写已经擦除了的,直接写入扇区剩余区间.
		
		if(tSize==tSizeOfSectorRemainingPart)break;//写入结束了
		else//写入未结束
		{
			tSectorNumber++;//扇区编号增1
			tOffsetAddrWithinSector=0;//偏移位置为0
			pBuffer=pBuffer+tSizeOfSectorRemainingPart;//修改pBuffer指针,指向需要写的数据
			tWriteAddr=tWriteAddr+tSizeOfSectorRemainingPart;//修改FLASH写地址
			tSize=tSize-tSizeOfSectorRemainingPart;//计算"待写数据"的个数
			if(tSize>(STM_SECTOR_SIZE/2))tSizeOfSectorRemainingPart=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
			else tSizeOfSectorRemainingPart=tSize;//下一个扇区可以写完了
		}	 
	}
	
	FLASH_Lock();//上锁
}
#endif

//从CPU的FLASH地址tReadAddr处读取tSize个半字(16位数据),保存到首地址为pBuffer的缓冲区中
//tReadAddr:起始地址
//pBuffer:数据指针
//tSize:半字(16位)数
void STMFLASH_Read(u32 tReadAddr,u16 *pBuffer,u16 tSize)   	
{
	u16 i;
	for(i=0;i<tSize;i++)
	{
		pBuffer[i]=STMFLASH_ReadHalfWord(tReadAddr);
		//从CPU的FLASH地址tReadAddr处读取半字(16位数据)
		tReadAddr=tReadAddr+2;//偏移2个字节.	
	}
}


STM32_FLASH.h如下:
#ifndef __STMFLASH_H__
#define __STMFLASH_H__
#include "sys.h"

#define STM32_FLASH_WREN           1            //使能FLASH写入(0表示不使能;1表示使能)

#define STM32_FLASH_SIZE           512          //所选STM32的FLASH容量大小(单位为K)

#if STM32_FLASH_SIZE<256     //CPU的FLASH空间小于256K字节
#define STM_SECTOR_SIZE 1024 //每个扇区为1024个字节
#else 
#define STM_SECTOR_SIZE	2048 //每个扇区为2048个字节
#endif

extern void STMFLASH_Write(u32 tWriteAddr,u16 *pBuffer,u16 tSize);
extern void STMFLASH_Read(u32 tReadAddr,u16 *pBuffer,u16 tSize) ;
//extern void Erase_APP_Program(u32 tWriteAddr,u16 tSize);							   
#endif

IAP的CommonVariable.c如下:

#include "CommonVariable.h"

u8 USART3_RX_Buffer[USART3_RX_Buffer_Size] __attribute__ ( (at(USART3_RX_Buffer_BASE_ADDRESS)) );//USART3接收缓冲区
u16 USART3_RX_Buffer_Load_Index __attribute__ ( (at(USART3_RX_Buffer_Load_Index_BASE_ADDRESS)) );//USART3接收的字节数

//#pragma arm section zidata="NO_INIT"
u8 IAPWorkFlag __attribute__ ( ( at(IAPWorkFlag_BASE_ADDRESS),zero_init ) );


CommonVariable.h如下:
#ifndef __CommonVariable_H
#define __CommonVariable_H
#include "sys.h"

#define STM32_FLASH_BASE_ADDRESS   0x08000000   //STM32 FLASH的起始地址
#define FLASH_APP1_ADDR  0x08010000
//第一个应用程序起始地址(存放在FLASH)
//保留0X08000000~0X0800FFFF的空间为Bootloader使用(64KB)

#define STM32_RAM_BASE_ADDRESS     0x20000000   //STM32 RAM的起始地址
#define STM32_RAM_SIZE             64      //所选STM32的RAM容量大小(单位为K)

#define USART3_RX_Buffer_Size  		(10*1024) 	//定义最大接收字节数 10K
#define USART3_RX_Buffer_BASE_ADDRESS   (STM32_RAM_BASE_ADDRESS + (STM32_RAM_SIZE * 1024) - USART3_RX_Buffer_Size) //USART3_RX_Buffer[]的首地址
extern u8  USART3_RX_Buffer[USART3_RX_Buffer_Size]; //接收缓冲,最大USART3_RX_Buffer_Size个字节.末字节为换行符

#define USART3_RX_Buffer_Load_Index_BASE_ADDRESS USART3_RX_Buffer_BASE_ADDRESS-4 //USART3_RX_Buffer_Load_Index的首地址
extern u16 USART3_RX_Buffer_Load_Index;//接收的字节数

#define IAPWorkFlag_BASE_ADDRESS   USART3_RX_Buffer_Load_Index_BASE_ADDRESS-4    //IAPWorkFlag的首地址
extern u8  IAPWorkFlag;

typedef  void (*DefineIAPFunction)(void);   //定义一个函数类型的参数

#endif

IAP的USART3.c如下:

#include "USART3.h"
#include "stdio.h"
#include "CommonVariable.h"


void USART3_Init(u32 bound);

/*
//函数功能:串口3发送一个字节
void USART3_SendByte(  unsigned char ch )
{
  USART_SendData(USART3, (unsigned char) ch);
  while( USART_GetFlagStatus(USART3,USART_FLAG_TC)!= SET); //等待发送完成标志位被置1	
}

//函数功能:串口3发送字符串
void USART3_SendString(  unsigned char *str)
{
  unsigned int k=0;
  do{
      USART3_SendByte(  *(str + k) );
      k++;
    }while(*(str + k)!='\0');
}
*/

//函数功能:串口3初始化
void USART3_Init(u32 bound)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); //设置USART3的APB1外设时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //使能GPIOB时钟

  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_10;             //选择PIN10,是USART3的TXD
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	       //设置引脚为复用推挽输出	  
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //设置引脚的最高工作速率为50MHz
  GPIO_Init(GPIOB, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_11;             //选择PIN11,是USART3的RXD
	//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;          //设置引脚为输入上拉
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;	 //设置引脚为输入悬浮	  
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //设置引脚的最高工作速率为50MHz
  GPIO_Init(GPIOB, &GPIO_InitStructure); 

	//NVIC_PriorityGroup_4设置NVIC中断分组4:表示抢占优先级为4位,取值为0~15,没有响应优先级,取值为0
  //NVIC_PriorityGroup_3设置NVIC中断分组3:表示抢占优先级为3位,取值为0~7,响应优先级只有1位,取值为0~1
	//NVIC_PriorityGroup_2设置NVIC中断分组3:表示抢占优先级为2位,取值为0~3,响应优先级只有2位,取值为0~3
	//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
	NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;         //选择中断源为USART3_IRQn
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 7; //设置抢先优先级为7
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;        //设置响应优先级为0
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
   //USART 初始化设置
	USART_InitStructure.USART_BaudRate = bound;                //串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//串口采用8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;     //设置停止位数为1位
	USART_InitStructure.USART_Parity = USART_Parity_No;        //设置无需进行奇偶校验
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件CTS和RTS流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//串口为收发模式
	USART_Init(USART3, &USART_InitStructure); //初始化串口3

  USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启串口接收中断,接收寄存器非空中断使能

/在串口中断服务函数中发送数据配置开始//
//	USART_ITConfig(USART3,USART_IT_TXE,DISABLE); //Disnables Transmit Data Register empty interrupt
//	USART_ITConfig(USART3,USART_IT_TC,ENABLE);  //Enables Transmission complete interrupt
/在串口中断服务函数中发送数据配置结束//

  USART_Cmd(USART3, ENABLE);  //使能串口3

	USART3_RX_Buffer_Load_Index=0;
//	if(IAPWorkFlag==0) IAPWorkFlag=1;
//	if(IAPWorkFlag>3) IAPWorkFlag=1;
//1表示执行IAP
//2表示执行SRAM中的用户代码
//3表示执行FLASH中的用户代码
}

//串口3中断服务程序
void USART3_IRQHandler(void)
{
	u8 Res;
	if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)//接收中断(接收到的数据必须是0x0d 0x0a结尾)
	{
		Res =USART_ReceiveData(USART3);//读串口3

		if(USART3_RX_Buffer_Load_Index<USART3_RX_Buffer_Size)
		{
			USART3_RX_Buffer[USART3_RX_Buffer_Load_Index]=Res;
			USART3_RX_Buffer_Load_Index++;			 									     
		}
	} 
}

#if USART3_Printf_Enable == 1
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
	int handle;
};
FILE __stdout;

//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
	x = x;
}

//重定义fputc函数
//函数功能:发送ch的值给USART3串口
int fputc(int ch, FILE *f)
{
  USART_SendData(USART3, (unsigned char) ch);
  while( USART_GetFlagStatus(USART3,USART_FLAG_TC)!= SET); //等待发送完成标志位被置1	
	return ch;
}

#elif USART3_Printf_Enable == 2 //使用JLINK虚拟串口的printf功能
#define ITM_Port8(n)    (*((volatile unsigned char *)(0xE0000000+4*n)))
#define ITM_Port16(n)   (*((volatile unsigned short*)(0xE0000000+4*n)))
#define ITM_Port32(n)   (*((volatile unsigned long *)(0xE0000000+4*n)))
#define DEMCR           (*((volatile unsigned long *)(0xE000EDFC)))
#define TRCENA          0x01000000

struct __FILE
{
  int handle; /* Add whatever you need here */
};
FILE __stdout;
FILE __stdin;

int fputc(int ch, FILE *f) 
{
	if (DEMCR & TRCENA)
	{
    while (ITM_Port32(0) == 0);
   ITM_Port8(0) = ch;
  }
  return(ch);
}
#endif


USART3.h文件如下:
#ifndef __USART3_H
#define __USART3_H
#include "sys.h"

//#define USART3_Printf_Enable  0 //不使用printf功能
#define USART3_Printf_Enable  1 //使用USART3的printf功能
//#define USART3_Printf_Enable  2 //使用JLINK虚拟串口的printf功能




extern void USART3_Init(u32 bound);

#endif

IAP的IAP.c如下

#include "IAP.h"
#include "STM32_FLASH.h"
#include "CommonVariable.h"

DefineIAPFunction Jump_To_APP;
u16 iapbuf[1024];

//appxaddr:应用程序的起始地址
//appbuf:应用程序CODE.
//appsize:应用程序大小(字节)
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)
{
	u16 t;
	u16 i=0;
	u16 temp;
	u32 fwaddr=appxaddr;//当前写入的地址
	u8 *dfu=appbuf;

	for(t=0;t<appsize;t+=2)
	{
		temp=(u16)dfu[1]<<8;temp+=(u16)dfu[0];//合成半字(16位),生成"待写数据"
		dfu+=2;//将dfu指针加2,为下次读数据做准备
		iapbuf[i++]=temp;//将"待写数据"保存到iapbuf[]中
		if(i==1024)//满1K个半字,则执行写入CPU的FLASH
		{
			i=0;
			STMFLASH_Write(fwaddr,iapbuf,1024);
			fwaddr+=2048;//偏移2048  16=2*8.所以要乘以2
		}
	}
	if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去
}

//跳转到应用程序段
//appxaddr:用户代码起始地址
void iap_load_app(u32 appxaddr)
{
	if( ( (*(vu32*)appxaddr)&0x2FFE0000 )==0x20000000 )	//检查栈顶地址是否合法
	{
		Jump_To_APP=(DefineIAPFunction)*(vu32*)(appxaddr+4);//用户代码区第二个字为程序开始地址(复位地址)
		MSR_MSP(*(vu32*)appxaddr);//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
		Jump_To_APP();            //跳转到APP
	}
}


IAP.h文件如下:
#ifndef __IAP_H__
#define __IAP_H__
#include "sys.h"

void iap_load_app(u32 appxaddr); //跳转到APP程序执行
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 applen);//在指定地址开始,写入bin

#endif

IAP的AT24cxx.c如下:

#include "AT24cxx.h"
#include "sys.h"
#include "delay.h"

//函数功能:sel=1设置GPIOB9为输出口,sel=0,则设置GPIOB9为输入上拉
void EEPROM_SDA_PIN_Configuration(u8 sel)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	if(sel==1)
	{
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;     //选择第9脚
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //选择推挽输出
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO口的最高工作频率为50MHz
		GPIO_Init(GPIOB, &GPIO_InitStructure); //GPIOB
  }
	else
	{
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;     //选择第9脚
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;     //选择输入上拉
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO口的最高工作频率为50MHz
		GPIO_Init(GPIOB, &GPIO_InitStructure); //GPIOB
	}
}

//函数功能:sel=1设置GPIOB8为输出口,sel=0,则设置GPIOB8为输入上拉
void EEPROM_SCL_PIN_Configuration(u8 sel)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	if(sel==1)
	{
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;         //选择第1脚
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //选择推挽输出
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO口的最高工作频率为50MHz
		GPIO_Init(GPIOB, &GPIO_InitStructure); //GPIOB
  }
	else
	{
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;         //选择第1脚
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;     //选择输入上拉
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO口的最高工作频率为50MHz
		GPIO_Init(GPIOB, &GPIO_InitStructure); //GPIOB
	}
}

//函数功能:EEPROM引脚初始化
void EEPROM_I2c_Init(void)
{
	GPIO_InitTypeDef   GPIO_InitStructure;
  //使用GPIO_InitTypeDef定义一个结构变量GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOB, ENABLE ); //在配置外设之前,必须先使能GPIOB的外设时钟
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8; //选择第1脚
  GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //设置引脚的最高输出速率为50MHz
  GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;   //设置引脚工作模式为推挽输出方式
  GPIO_Init( GPIOB, &GPIO_InitStructure); 
  //根据GPIO_InitStructure结构变量指定的参数初始化GPIOB的外设寄存器
	EEPROM_SCL_SET();  //PB1输出高电平

	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; //选择第9脚
  GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //设置引脚的最高输出速率为50MHz
  GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;  //设置引脚工作模式为推挽输出方式
  GPIO_Init( GPIOB, &GPIO_InitStructure); 
  //根据GPIO_InitStructure结构变量指定的参数初始化GPIOB的外设寄存器
	EEPROM_SDA_SET();  //PB0输出高电平
	
	IWDG_ReloadCounter();  //喂狗,按照IWDG重装载寄存器IWDG_RLR的值重装载IWDG计数器,独立看门狗的溢出周期为6400ms
  delay_ms(10); //上电等待时间为10ms;
	IWDG_ReloadCounter();  //喂狗,按照IWDG重装载寄存器IWDG_RLR的值重装载IWDG计数器,独立看门狗的溢出周期为6400ms
}

//主机发送"启动条件"函数;
 void I2CStart_EEPROM(void)
 {
	 //EA=0;                   //屏蔽所有中断
	 EEPROM_SCL_CLR();  //SCL输出低电平
	 EEPROM_SDA_PIN_Configuration(1); //设置SDA为输出口
	 EEPROM_SDA_SET();  //SDA输出高电平;
	 delay_us(10);           //空闲时间至少保持4.7us;
	 EEPROM_SDA_SET(); //EEPROM SDA输出高电平
	 EEPROM_SCL_SET(); //EEPROM SCL输出高电平,将EEPROM SDA脚和EEPROM SCL置为高电平,为发送启动条件做准备
	 delay_us(7);      //启动建立时间要至少为4.7us
	 EEPROM_SDA_CLR(); //在EEPROM CL为高电平期间,EEPROM SDA脚出现下降沿,表示"起动I2C"
	 delay_us(6);      //启动保持时间为4us;
   EEPROM_SCL_CLR(); //EEPROM SCL脚置低电平,为下次移位做准备;
 }

 //主机发送"停止条件"函数;
void I2CStop_EEPROM(void)
{
//将EEPROM SDA脚和EEPROM SCL置为低电平,为发送"停止条件"做准备
	EEPROM_SCL_CLR();  //SCL输出低电平
	EEPROM_SDA_PIN_Configuration(1); //设置SDA为输出口
	EEPROM_SDA_CLR(); //EEPROM SDA输出低电平
	delay_us(5); //时钟低电平时间至少保持4.7us
	EEPROM_SCL_SET(); //EEPROM SCL输出高电平
	delay_us(10); //停止条件建立时间至少为4us;
	EEPROM_SDA_SET(); //EEPROM SDA输出高电平,在EEPROM_SCLK为高电平期间,EEPROM_IO脚出现上降沿,表示"停止I2C"
	delay_us(14); //停止条件保持时间至少为4us;
 //EA=1;                   //开所有中断允许位
}
 
 //从I2C输出一个字节
void write_I2C_byte_To_EEPROM(unsigned char txByte)
{
  unsigned char mask;
  unsigned char tmpValue;
	
	IWDG_ReloadCounter();  //喂狗,按照IWDG重装载寄存器IWDG_RLR的值重装载IWDG计数器,独立看门狗的溢出周期为6400ms
	EEPROM_SCL_CLR();  //SCL输出低电平
	EEPROM_SDA_PIN_Configuration(1); //设置SDA为输出口
	EEPROM_SDA_SET();  //SDA输出高电平;
	delay_us(5);       //时钟高电平时间至少保持4us
  for(mask=0x80; mask>0;) //shift bit for masking (8 times)
  {
		tmpValue=(unsigned char)(mask & txByte);
		if ( tmpValue == 0 ) EEPROM_SDA_CLR();  //SDA输出低电平
		else EEPROM_SDA_SET();  //SDA输出高电平;
		delay_us(2);       //数据建立时间至少为0.25us
		EEPROM_SCL_SET();  //SCL输出高电平 //generate clock pulse on SCL
		delay_us(10);       //时钟高电平时间至少保持4us
		EEPROM_SCL_CLR();  //SCL输出低电平
		delay_us(10);       //时钟低电平时间至少保持4.7us
		mask=(unsigned char)(mask>>1); //将mask的值右移一位,左端补0,为写下一位值做准备
	}
}

//从I2C读入一个字节
unsigned char read_I2C_byte_Fr_EEPROM()
{
  unsigned char mask,rxByte=0;
	
	IWDG_ReloadCounter();  //喂狗,按照IWDG重装载寄存器IWDG_RLR的值重装载IWDG计数器,独立看门狗的溢出周期为6400ms
	EEPROM_SCL_CLR();  //SCL输出低电平
	EEPROM_SDA_PIN_Configuration(0); //设置SDA为输入口
	delay_us(5);       //时钟高电平时间至少保持4us
  for(mask=0x80;mask>0;) //shift bit for masking (8 times)
	{
		EEPROM_SCL_SET();  //SCL输出高电平 //start clock on SCL-line
		delay_us(4);       //时钟高电平时间至少保持4us
		if( EEPROM_SDA() ) rxByte=(unsigned char)(rxByte | mask); //read bit
		EEPROM_SCL_CLR();  //SCL输出低电平
		delay_us(5);       //时钟低电平时间至少保持4.7us
		mask=(unsigned char)(mask>>1); //将mask的值右移一位,左端补0,为读取下一位值做准备
	}
	return(rxByte);
}

//函数说明:I2C专用,等待从器件接收方的应答
bool WaitAck_Fr_EEPROM(void)
{
	 unsigned char errtime=255;//因故障接收方无ACK,超时值为255.
	 
	 EEPROM_SCL_CLR();  //SCL输出低电平
	 EEPROM_SDA_PIN_Configuration(0); //设置SDA为输入口
	 delay_us(2); //数据建立时间至少为0.25us;
	 EEPROM_SCL_SET();  //SCL输出高电平,//EEPROM SCL上升沿到来时,EEPROM发送ACK信号;
	 delay_us(10); //时钟高电平时间至少保持4us;

	 while(EEPROM_SDA())
	 {
		 errtime--;
     delay_ms(1); //等待1ms 
		 if (!errtime)
			 {
				 I2CStop_EEPROM();
					delay_ms(3000);
	        IWDG_ReloadCounter();  //喂狗,按照IWDG重装载寄存器IWDG_RLR的值重装载IWDG计数器,独立看门狗的溢出周期为6400ms
				 return FALSE;
       }
	 }
	 EEPROM_SCL_CLR();  //SCL输出低电平
	 delay_us(5); //时钟低电平时间至少保持4.7us;
	 return TRUE;
}
 
 //函数说明:I2C专用,CPU为接收方,EEPROM为发送方时,CPU发送应答信号;
 void SendAck_To_EEPROM(void)
 {
	 EEPROM_SCL_CLR();  //SCL输出低电平
	 EEPROM_SDA_PIN_Configuration(1); //设置SDA为输出口
	 EEPROM_SDA_CLR(); //将EEPROM_IO脚设置为低电平,该位值是即发出的"应答信号";
	 delay_us(1); //数据建立时间至少为0.25us;
	 EEPROM_SCL_SET();  //SCL输出高电平//EEPROM SCL上升沿到来时,EEPROM收到一位值;
	 delay_us(4); //时钟高电平时间至少保持4us;
	 EEPROM_SCL_CLR();  //SCL输出低电平//EEPROM_SCLK脚置低电平,一个移位时钟结束;
	 EEPROM_SDA_PIN_Configuration(0); //设置SDA为输入口
	 delay_us(5); //时钟低电平时间至少保持4.7us;
 }

//函数说明:I2C专用,主器件为接收方,从器件为发送方时,非应答信号。
void SendNotAck_To_EEPROM(void)
{
	EEPROM_SCL_CLR();  //SCL输出低电平
	EEPROM_SDA_PIN_Configuration(1); //设置SDA为输出口//将EEPROM_IO脚设置为高电平,该位值是即发出的"非应答信号";
  delay_us(1);       //数据建立时间至少为0.25us;
	EEPROM_SCL_SET();  //SCL输出高电平//EEPROM_SCLK脚上升沿到来时,EEPROM收到一位值;
  delay_us(4);       //时钟高电平时间至少保持4us;
	EEPROM_SCL_CLR();  //SCL输出低电平//EEPROM_SCLK脚置低电平,一个移位时钟结束;
	EEPROM_SDA_PIN_Configuration(0); //设置SDA为输入口
  delay_us(5); //时钟低电平时间至少保持4.7us;
}

//开始24LC256驱动程序
 /**-------------------------------------------------------------------------------
 函数说明:从EEPROM芯片24LC256的某个地址单元addr,读取到rerurn_value中.
           并将读到的值返回;
 ---------------------------------------------------------------------------------*/
u8 EEPROM_U8_Data_Read1(u16 addr)
{
	u8 rerurn_value;
  union EEPROM_Addr_TYPE  temp;

  temp.Address=addr;
  //delay_ms(1);                       //在连续进行"字节连续读"时,此处必须加delay_ms(5),否则程序可能读不到EEPROM中的数据;
  I2CStart_EEPROM();                          //发开始条件
  write_I2C_byte_To_EEPROM(0xA0);             //发送"写器件地址",24LC256的"写器件地址"为0xA0
  WaitAck_Fr_EEPROM();                        // 等待从器件接收方的应答 
  write_I2C_byte_To_EEPROM(temp.b[1]);  //发送器件的子地址高8位值;
  WaitAck_Fr_EEPROM();                        // 等待从器件接收方的应答 
  write_I2C_byte_To_EEPROM(temp.b[0]);     //发送器件的子地址低8位值;
  WaitAck_Fr_EEPROM();                        //等待EEPROM_IO脚被24LC256拉低;

  //delay_ms(1); 
  I2CStart_EEPROM();                      //发重新开始条件
  write_I2C_byte_To_EEPROM(0xA1);         //发送"读器件地址"
  WaitAck_Fr_EEPROM();                    //等待EEPROM_IO脚被24LC256拉低;
  rerurn_value=read_I2C_byte_Fr_EEPROM();
  SendNotAck_To_EEPROM();                //CPU发送不应答信号
  I2CStop_EEPROM();
  return(rerurn_value);
}

/**-------------------------------------------------------------------------------
 函数说明:从EEPROM芯片24LC256的某个地址单元开始,连续读取count个字节;
           设置要读的第一个地址addr,并设置读取的字节数count,则会一次把数据读取到buff中.
 ---------------------------------------------------------------------------------*/
void EEPROM_U8_Data_Readn(u8 *buff,u8 count,u16 addr)
{
	unsigned char i;
	union EEPROM_Addr_TYPE  temp;
	 
	temp.Address=addr;
	delay_ms(1);                          //在连续进行"字节连续读"时,此处必须加delay_ms(5),否则程序可能读不到EEPROM中的数据;
	I2CStart_EEPROM();                    //发开始条件
	write_I2C_byte_To_EEPROM(0xA0);       //发送"写器件地址",24LC256的"写器件地址"为0xA0
	WaitAck_Fr_EEPROM();                  // 等待从器件接收方的应答 
	write_I2C_byte_To_EEPROM(temp.b[1]);  //发送器件的子地址高8位值;
	WaitAck_Fr_EEPROM();                  // 等待从器件接收方的应答
	write_I2C_byte_To_EEPROM(temp.b[0]);  //发送器件的子地址低8位值;
	WaitAck_Fr_EEPROM();                  //等待EEPROM_IO脚被24LC256拉低;
	 
	I2CStart_EEPROM();                    //发重新开始条件
  write_I2C_byte_To_EEPROM(0xA1);       //发送"读器件地址"
	WaitAck_Fr_EEPROM();                   //等待EEPROM_IO脚被24LC256拉低;
	for (i=0;i<count;i++)
  {
		buff[i]=read_I2C_byte_Fr_EEPROM();
		if (i!=count-1) SendAck_To_EEPROM(); //除最后一个字节外,其他都要从MASTER发应答。
  }
	SendNotAck_To_EEPROM();                //CPU发送不应答信号
	I2CStop_EEPROM();
}

/**-------------------------------------------------------------------------------
 函数说明:将x的值写入指定的EEPROM芯片24LC256的某个地址单元addr;
      注意:在I2CStop_EEPROM()和I2CStart_EEPROM()两句之间,必须有delay_ms(5)语句,否则,读写可能不正确;
 ---------------------------------------------------------------------------------*/
void EEPROM_U8_Data_Write(u8 x,u16 addr)
{
	union EEPROM_Addr_TYPE  temp;
	
	IWDG_ReloadCounter();  //喂狗,按照IWDG重装载寄存器IWDG_RLR的值重装载IWDG计数器,独立看门狗的溢出周期为6400ms
	CLI(); //关闭总中断

  temp.Address=addr;
  I2CStart_EEPROM();                   //发开始条件
  write_I2C_byte_To_EEPROM(0xA0);      //发送"写器件地址",24LC256的"写器件地址"为0xA0
  WaitAck_Fr_EEPROM();                 //等待EEPROM_IO脚被EEPROM拉低;
  write_I2C_byte_To_EEPROM(temp.b[1]); //发送器件的子地址高8位值;
  WaitAck_Fr_EEPROM();                 // 等待从器件接收方的应答 
  write_I2C_byte_To_EEPROM( temp.b[0] ); //发送器件的子地址低8位值;
  WaitAck_Fr_EEPROM();             //等待EEPROM_IO脚被EEPROM拉低;
  write_I2C_byte_To_EEPROM(x); //将value的值写入EEPROM;
  WaitAck_Fr_EEPROM();             //等待EEPROM_IO脚被EEPROM拉低;
  I2CStop_EEPROM();

	SEI();//打开总中断
  delay_ms(5);           //写等待时间至少保持4ms;
}

//函数功能:初始化EEPROM引脚;
void EEPROM_PIN_INIT(void)
{
	EEPROM_I2c_Init();
  delay_ms(10); //上电等待时间为10ms;
}

//函数功能:将双字节数x保存到EEPROM中addr地址中
void EEPROM_U16_Data_Write(u16 x,u16 addr)
{
	union EEPROM_Uint16_Data_TYPE  temp;

	temp.Data=x;
	EEPROM_U8_Data_Write(temp.b[0],addr);
	addr++;
	EEPROM_U8_Data_Write(temp.b[1],addr);
	addr++;
}

//函数功能:从EEPROM中读取双字节数
u16 EEPROM_U16_Data_Read(u16 addr)
{
	union EEPROM_Uint16_Data_TYPE  temp;

	temp.Data=0;
	temp.b[0]=EEPROM_U8_Data_Read1(addr);
	addr++;
	temp.b[1]=EEPROM_U8_Data_Read1(addr);
	addr++;
	return(temp.Data);
}

//函数功能:将双字节数x保存到EEPROM中addr地址中
void EEPROM_U32_Data_Write(u32 x,u16 addr)
{
	union EEPROM_U32_Data_TYPE  temp;

	temp.Data=x;
	EEPROM_U8_Data_Write(temp.b[0],addr);
	addr++;
	EEPROM_U8_Data_Write(temp.b[1],addr);
	addr++;
	EEPROM_U8_Data_Write(temp.b[2],addr);
	addr++;
	EEPROM_U8_Data_Write(temp.b[3],addr);
	addr++;
}

//函数功能:从EEPROM中读取双字节数
u32 EEPROM_U32_Data_Read(u16 addr)
{
	union EEPROM_U32_Data_TYPE  temp;

	temp.Data=0;
	temp.b[0]=EEPROM_U8_Data_Read1(addr);
	addr++;
	temp.b[1]=EEPROM_U8_Data_Read1(addr);
	addr++;
	temp.b[2]=EEPROM_U8_Data_Read1(addr);
	addr++;
	temp.b[3]=EEPROM_U8_Data_Read1(addr);
	addr++;
	return(temp.Data);
}

//函数功能:将浮点数x保存到EEPROM中addr地址中,浮点数占用4个字节
void EEPROM_Float_Data_Write(float x,u16 addr)
{
	union EEPROM_FLOAT_DATA_TYPE  temp;

	temp.float_data=x;
	EEPROM_U8_Data_Write(temp.b[0],addr);
	addr++;
	EEPROM_U8_Data_Write(temp.b[1],addr);
	addr++;
	EEPROM_U8_Data_Write(temp.b[2],addr);
	addr++;
	EEPROM_U8_Data_Write(temp.b[3],addr);
}

//函数功能:从EEPROM中读取浮点数,浮点数占用4个字节
float EEPROM_Float_Data_Read(u16 addr)
{
	union EEPROM_FLOAT_DATA_TYPE  temp;

	temp.float_data=0;
	temp.b[0]=EEPROM_U8_Data_Read1(addr);
	addr++;
	temp.b[1]=EEPROM_U8_Data_Read1(addr);
	addr++;
	temp.b[2]=EEPROM_U8_Data_Read1(addr);
	addr++;
	temp.b[3]=EEPROM_U8_Data_Read1(addr);
	return(temp.float_data);
}

//函数功能:将浮点数x保存到EEPROM中addr地址中,浮点数占用8个字节
void EEPROM_Double_Data_Write(double x,u16 addr)
{
	union EEPROM_DOUBLE_DATA_TYPE  temp;

	temp.float_data=x;
	EEPROM_U8_Data_Write(temp.b[0],addr);
	addr++;
	EEPROM_U8_Data_Write(temp.b[1],addr);
	addr++;
	EEPROM_U8_Data_Write(temp.b[2],addr);
	addr++;
	EEPROM_U8_Data_Write(temp.b[3],addr);
	addr++;
	EEPROM_U8_Data_Write(temp.b[4],addr);
	addr++;
	EEPROM_U8_Data_Write(temp.b[5],addr);
	addr++;
	EEPROM_U8_Data_Write(temp.b[6],addr);
	addr++;
	EEPROM_U8_Data_Write(temp.b[7],addr);
}

//函数功能:从EEPROM中读取浮点数,浮点数占用8个字节
double EEPROM_Double_Data_Read(u16 addr)
{
	union EEPROM_DOUBLE_DATA_TYPE  temp;

	temp.float_data=0;
	temp.b[0]=EEPROM_U8_Data_Read1(addr);
	addr++;
	temp.b[1]=EEPROM_U8_Data_Read1(addr);
	addr++;
	temp.b[2]=EEPROM_U8_Data_Read1(addr);
	addr++;
	temp.b[3]=EEPROM_U8_Data_Read1(addr);
	addr++;
	temp.b[4]=EEPROM_U8_Data_Read1(addr);
	addr++;
	temp.b[5]=EEPROM_U8_Data_Read1(addr);
	addr++;
	temp.b[6]=EEPROM_U8_Data_Read1(addr);
	addr++;
	temp.b[7]=EEPROM_U8_Data_Read1(addr);
	return(temp.float_data);
}

void Save_String_To_EEPROM(char *pString,u16 len_max,u16 addr)
{
	char *p;
	uint16_t i;
	uint16_t address;

	i=0;
	address=addr;
	p=pString;
	while(i<len_max)
	{
		if(*p!='\0')
		{
			EEPROM_U8_Data_Write(*p,address);
		}
		else
		{
			EEPROM_U8_Data_Write('\0',address);//写结束符号
			i=len_max;
		}
		address++;
		p++;
		i++;
	}
}

//函数功能:将pString中的字符串保存到EEPROM中,起始地址为addr
void Read_String_From_EEPROM(u8 *pString,unsigned char len_max,u16 addr)
{
	EEPROM_U8_Data_Readn(pString,len_max,addr);
}



AT24cxx.h文件如下:
#ifndef _24LC256_H
#define _24LC256_H

#include "stm32f10x.h" //使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
#include "sys.h"  //启用bool定义

#define EEPROM_IAPWorkFlag_ADDRESS   4095

//24LC256共计512页,每页64字节,合计4K字节

#define	EEPROM_SCL_SET()  GPIO_SetBits(GPIOB,GPIO_Pin_8)    //设置PB8输出高电平
#define	EEPROM_SCL_CLR()  GPIO_ResetBits(GPIOB,GPIO_Pin_8)  //设置PB8输出低电平
#define EEPROM_SCL()      GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_8)//读取PB8

#define	EEPROM_SDA_SET()  GPIO_SetBits(GPIOB,GPIO_Pin_9)    //设置PB9输出高电平
#define	EEPROM_SDA_CLR()  GPIO_ResetBits(GPIOB,GPIO_Pin_9)  //设置PB9输出低电平
#define EEPROM_SDA()      GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)//读取PB9

#define EEPROM_Total_Page_Number  512  //24LC256共计512页,每页64字节
#define Byte_Number_Per_Page  64
extern unsigned char Write_to_value[Byte_Number_Per_Page];


union EEPROM_Addr_TYPE
{ unsigned char b[2];   //b[1]和ptr的高8位值相等;b[0]和Address的低8位值相等;
  uint16_t Address;
};

union EEPROM_Uint16_Data_TYPE
{ unsigned char b[2];   //b[1]和Data的高8位值相等;b[0]和Data的低8位值相等;
  uint16_t Data;
};

union EEPROM_U32_Data_TYPE
{ u8 b[4];   //b[1]和Data的高8位值相等;b[0]和Data的低8位值相等;
  u32 Data;
};

union EEPROM_FLOAT_DATA_TYPE
{ u8 b[4];   //b[3]和float_data的高8位值相等;b[0]和float_data的低8位值相等;
  float float_data;
};

union EEPROM_DOUBLE_DATA_TYPE
{ u8 b[8];   //b[7]和float_data的高8位值相等;b[0]和float_data的低8位值相等;
  double float_data;
};

extern void EEPROM_PIN_INIT(void);

extern void EEPROM_U8_Data_Write(u8 x,u16 addr);
extern u8 EEPROM_U8_Data_Read1(u16 addr);
extern void EEPROM_U8_Data_Readn(u8 *buff,u8 count,u16 addr);


extern void EEPROM_U16_Data_Write(u16 x,u16 addr);
extern u16 EEPROM_U16_Data_Read(u16 addr);
extern void EEPROM_U32_Data_Write(u32 x,u16 addr);
extern u32 EEPROM_U32_Data_Read(u16 addr);
extern void EEPROM_Float_Data_Write(float x,u16 addr);
extern float EEPROM_Float_Data_Read(u16 addr);
extern void EEPROM_Double_Data_Write(double x,u16 addr);
extern double EEPROM_Double_Data_Read(u16 addr);
extern void Save_String_To_EEPROM(char *pString,u16 len_max,u16 addr);
extern void Read_String_From_EEPROM(u8 *pString,unsigned char len_max,u16 addr);

#endif

 以上是IAP程序部分;

下面是APP程序:

APP的main.c如下:

#include "stm32f10x.h"//使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
#include "stdio.h"
#include "delay.h"
#include "string.h"

#include "CommonVariable.h"
//#include "USART3.h"
#include "AT24cxx.h"

/********************************************************************
函数: 运行IAP程序.		//
输入: 无
返回: 无.不再返回.
说明:
    由于APP是在IAP的基础上运行的,因此,IAP一定是有效的,这里不再作IAP有效性检查.
    APP跳IAP
************************************************************************/
void app_jump_to_iap(void)
{
	u32  IapSpInitVal;//IAP程序的SP初值
	u32	 IapJumpAddr;//IAP程序的跳转地址.即IAP程序的入口
	void (*pIapFun)(void);//定义一个函数指针,用于指向APP程序入口

//  RCC_DeInit();
	NVIC_SystemReset();//恢复NVIC为复位状态.使中断不再发生.
//	INTX_DISABLE();//关闭所有中断
	__set_CONTROL(0);//将PSP指针切换为MSP指针
	IapSpInitVal = *(vu32 *)STM32_FLASH_BASE_ADDRESS;//取APP的SP初值.
	IapJumpAddr = *(vu32 *)(STM32_FLASH_BASE_ADDRESS + 4);//取程序入口.
	__set_MSP(IapSpInitVal);//设置SP
	pIapFun = (void (*)(void))IapJumpAddr;//生成跳转函数
	(*pIapFun)();//跳转,不再返回
}

const char APP_Run_REG[]="\r\nAPP Run!\r\n";
const char CPU_OK_REG[]="\r\nOK!\r\n";
int main(void)
{
	u8 cnt;
//	SCB->VTOR = FLASH_BASE | 0x10000;
	SCB->VTOR = FLASH_APP1_ADDR;//中断向量表重定义

	delay_init(); //延时函数初始化
  EEPROM_PIN_INIT();
	printf("%s",APP_Run_REG);//调试串口输出"\r\nAPP Run!\r\n"
	cnt=0;
	INTX_ENABLE();//开启所有中断
	while(1)
	{
    if( NULL!=strstr( (char*)USART3_RX_Buffer,"TEST\r\n") )
		{
			USART3_RX_Buffer[0]='\0';USART3_RX_Buffer[1]='\0';USART3_RX_Buffer[2]='\0';USART3_RX_Buffer[3]='\0';
			USART3_RX_Buffer[4]='\0';USART3_RX_Buffer[5]='\0';USART3_RX_Buffer[6]='\0';
			USART3_RX_Buffer_Load_Index=0;
			printf("%s",CPU_OK_REG);
			cnt++;
			if(cnt>10)//执行"IAP bootLoader"
			{
				printf("Ready Update...\r\n");	
				IAPWorkFlag=1;
				EEPROM_U8_Data_Write(IAPWorkFlag,EEPROM_IAPWorkFlag_ADDRESS);
				app_jump_to_iap();
			}
		}
	}
}

APP的sys.c和IAP的相同,这里不再添加。

APP的delay.c和IAP的相同,这里不再添加。

APP的CommonVariable.c和IAP的相同,这里不再添加。

APP的USART3.c和IAP的相同,这里不再添加。

  以上是APP程序部分;
IAP和APP分别创建工程,这里不再赘述。

STM32F103VET6工程配置介绍如下:

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值