⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 iap.c

📁 LPC2214的IAP应用编程
💻 C
字号:
/****************************************Copyright (c)************************************************
**
**                              	  深圳托普林科技有限公司
**                                 http://www.sztopping.com
**
**----------------------------------------------------------------------------------------------------
名称:IAP.c
功能:LPC2200的IAP功能。
作者:Albert
日期:2005-05-08
说明:调用IAP服务程序,完成FLASH编程操作。将paramout添加到Watch窗口,观察返回值。
*****************************************************************************************************/  

#include  "config.h"
#include  "LPC2294.h"
#include  "IAP.h"


/**************************************************
名称: 定义IAP命令字
**************************************************/
#define  IAP_SELSECTOR        50	//选择扇区(准备编程扇区)
#define  IAP_RAMTOFLASH       51	//将RAM内容复制到FLASH	
#define  IAP_ERASESECTOR      52	//擦除扇区
#define  IAP_BLANKCHK         53	//扇区擦空
#define  IAP_READPARTID       54	//读器件ID
#define  IAP_BOOTCODEID       55	//读BOOT代码版本
#define  IAP_COMPARE          56	//比较

/**************************************************
名称: 定义IAP返回状态字
**************************************************/
#define  CMD_SUCCESS          0		//命令成功执行
#define  INVALID_COMMAND      1		//无效命令
#define  SRC_ADDR_ERROR       2 	//源地址没有以字为边界
#define  DST_ADDR_ERROR       3		//目标地址的边界错误
#define  SRC_ADDR_NOT_MAPPED  4		//源地址没有位于存储器映射中
#define  DST_ADDR_NOT_MAPPED  5		//目标地址没有位于到存储器映射中
#define  COUNT_ERROR          6		//字节计数值不是4的倍数或者一个非法值
#define  INVALID_SECTOR       7		//扇区号无效或结束扇区小于起始扇区号
#define  SECTOR_NOT_BLANK     8		//扇区非空	
#define  SECTOR_NOT_PREPARED_FOR_WRITE_OPERATION 9	//为写操作准备扇区命令未执行
#define  COMPARE_ERROR        10	//源和目标数据不相等
#define  BUSY                 11	//FLASH编程硬件不相等


/**************************************************
名称: 宏定义(Flash操作)
**************************************************/
#define  IapGetAddr(addr)	(volatile uint16 *)(0x80000000|(addr<<1))	//转换地址

//typedef  void (*Iap_Handle)(void);

/**************************************************
名称: IAP参数定义
**************************************************/
uint32  paramin[8];                			// IAP入口参数缓冲区
uint32  paramout[8];               			// IAP出口参数缓冲区

uint16  UpDateAllCodeIdNum;					// 更新代码包ID数

uint8  *pIapData=(uint8 *)0x40001000;		// IAP写FLASH时数据入口指针

											//	IAP扇子区号(以8K为单位) 去掉BOOT区和IAP代码区(16K)
uint8  IapSectorCode[30]={0,1,2,3,4,5,6,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,11,12,13,14,15};
         
/****************************************************************************
* 名称:SelSector()
* 功能:IAP操作扇区选择,命令代码50。
* 入口参数:sec1        起始扇区
*          sec2        终止扇区
* 出口参数:IAP返回值(paramout缓冲区) 	CMD_SUCCESS,BUSY,INVALID_SECTOR
****************************************************************************/
void  SelSector(uint8 sec1, uint8 sec2)
{  paramin[0] = IAP_SELSECTOR;              // 设置命令字
   paramin[1] = sec1;                       // 设置参数
   paramin[2] = sec2;
   iap_entry(paramin, paramout);         	// 调用IAP服务程序
}


/****************************************************************************
* 名称:RamToFlash()
* 功能:复制RAM的数据到FLASH,命令代码51。
* 入口参数:dst        目标地址,即FLASH起始地址。以512字节为分界
*          src        源地址,即RAM地址。地址必须字对齐
*          no         复制字节个数,为512/1024/4096/8192
* 出口参数:IAP返回值(paramout缓冲区) CMD_SUCCESS,SRC_ADDR_ERROR,DST_ADDR_ERROR,
SRC_ADDR_NOT_MAPPED,DST_ADDR_NOT_MAPPED,COUNT_ERROR,BUSY,未选择扇区
****************************************************************************/
void  RamToFlash(uint32 dst, uint32 src, uint32 no)
{  paramin[0] = IAP_RAMTOFLASH;             // 设置命令字
   paramin[1] = dst;                        // 设置参数
   paramin[2] = src;
   paramin[3] = no;
   paramin[4] = Fosc/1000;					// 当不使用PLL功能时,Fcclk=Fosc
   iap_entry(paramin, paramout);         	// 调用IAP服务程序
}


/****************************************************************************
* 名称:EraseSector()
* 功能:扇区擦除,命令代码52。
* 入口参数:sec1       起始扇区
*          sec2       终止扇区
* 出口参数:IAP返回值(paramout缓冲区) CMD_SUCCESS,BUSY,INVALID_SECTOR,未选择扇区
****************************************************************************/
void  EraseSector(uint8 sec1, uint8 sec2)
{  paramin[0] = IAP_ERASESECTOR;            // 设置命令字
   paramin[1] = sec1;                       // 设置参数
   paramin[2] = sec2;
   paramin[3] = Fosc/1000;					// 当不使用PLL功能时,Fcclk=Fosc
   iap_entry(paramin, paramout);         	// 调用IAP服务程序
}

/****************************************************************************
* 名称:BlankCHK()
* 功能:扇区查空,命令代码53。
* 入口参数:sec1       起始扇区
*          sec2       终止扇区
* 出口参数:IAP返回值(paramout缓冲区) CMD_SUCCESS,BUSY,INVALID_SECTOR,SECTOR_NOT_BLANK
****************************************************************************/
void  BlankCHK(uint8 sec1, uint8 sec2)
{  paramin[0] = IAP_BLANKCHK;               // 设置命令字
   paramin[1] = sec1;                       // 设置参数
   paramin[2] = sec2;
   iap_entry(paramin, paramout);         	// 调用IAP服务程序
}


/****************************************************************************
* 名称:ReadParID()
* 功能:读器件ID,命令代码54。
* 入口参数:无
* 出口参数:IAP返回值(paramout缓冲区)  CMD_SUCCESS
****************************************************************************/
void  ReadParID(void)
{  paramin[0] = IAP_READPARTID;             // 设置命令字
   iap_entry(paramin, paramout);         	// 调用IAP服务程序
}


/****************************************************************************
* 名称:BootCodeID()
* 功能:读取boot代码版本号,命令代码55。
* 入口参数:无
* 出口参数:IAP返回值(paramout缓冲区)  CMD_SUCDESS
****************************************************************************/
void  BootCodeID(void)
{  paramin[0] = IAP_BOOTCODEID;             // 设置命令字
   iap_entry(paramin, paramout);         	// 调用IAP服务程序
}


/****************************************************************************
* 名称:Compare()
* 功能:校验数据,命令代码56。
* 入口参数:dst        目标地址,即RAM/FLASH起始地址。地址必须字对齐
*          src        源地址,即FLASH/RAM地址。地址必须字对齐
*          no         复制字节个数,必须能被4整除
* 出口参数:IAP返回值(paramout缓冲区)  CMD_SUCCESS,COMPARE_ERROR,ADDR_ERROR
****************************************************************************/
void  Compare(uint32 dst, uint32 src, uint32 no)
{  paramin[0] = IAP_COMPARE;                // 设置命令字
   paramin[1] = dst;                        // 设置参数
   paramin[2] = src;
   paramin[3] = no;
   iap_entry(paramin, paramout);        	// 调用IAP服务程序
}


/****************************************************************************
* 名称:IapFlashReadMem()
* 功能:芯片连接读
* 入口参数:buf:读数据存放缓冲区指针,size:字节数,Addr:读首地址(SST39VF160内部地址)
* 出口参数:返回size为读取字节数大小
****************************************************************************/
void IapFlashReadMem(uint8 *pBuf,uint16 size,uint32 Addr)
{
	volatile uint16 *p_Addr;
	volatile uint16 data=0;
	uint16 i=0;
	uint16 length=0;
	uint8  sizeflag = 0;
	uint8  addrflag = 0;
	uint8  ftype = 0;
	uint32 addrf=0;
	
	addrf = Addr;
	if(addrf & 0x00000001)
	{
		addrf -= 1;
		addrflag = 1;			//奇数地址
	}
		
	//SST39VF160地址转化为MCU外部地址
	p_Addr = (volatile uint16  *)(0x80000000 | (addrf & 0x1FFFFF));  
	
	length = size >> 1;							//字节转换为字后的长度
	if((size % 2)!=0)	
		sizeflag = 1;							//奇数标志
	
	if(addrflag == 0)
	{
		if(sizeflag == 0)
			ftype = 0;
		else
			ftype = 1;
	}
	else
	{
		if(sizeflag == 0)
		{
			length -= 1;		//长度-1
			ftype = 2;
		}
		else
			ftype = 3;
	}
			
	if((ftype == 2)||(ftype == 3))				//前字的半字
	{
		data = *p_Addr;
		*pBuf++ = (uint8)((data >> 8)& 0x00ff);
		p_Addr++;
	}
		
	for(i=0;i<length;i++)						//读FLASH
	{
		data = *p_Addr;
		*pBuf++ = (uint8)(data & 0x00ff);		//第1字节
		*pBuf++ = (uint8)((data >> 8) & 0x00ff);//第2字节
		p_Addr++; 	//16bit
	}
	
	if((ftype == 1)||(ftype == 2))				//剩下1个字节
	{
		data = *p_Addr;					
		*pBuf = (uint8)(data & 0x00ff);			//取1BYTE
	}
}

/****************************************************************************
* 名称:IapCalSectorById()
* 功能:根据ID计算内部FLASH的扇区号及是否是扇区起始地址
* 入口参数:codeid:升级代码包ID,*addr:片内FLASH地址,*pflag:是否为扇区起始标志
* 出口参数:返回片内FLASH扇区号
****************************************************************************/
uint8 IapCalSectorById(uint16 codeid,uint32 *addr,uint8 *pflag)
{
	uint8  sectorid = 0;
	uint32 addrs = 0;
	uint8  flag = 0;
	
	if(codeid >= (( 240 * 1024 ) / IAP_CODE_PAGE_SIZE))					// 小于0x3C000
		return 0xff;
		
	sectorid = IapSectorCode[codeid/((8 * 1024)/IAP_CODE_PAGE_SIZE)];	//内部FLASH扇区号
	addrs = codeid * IAP_CODE_PAGE_SIZE;								//内部FLASH地址
	if(addrs >= 0x3C000)
	{
		addrs = 0;
		return 0xff;
	}
	
	if(codeid % 16)
		flag = 1;
	else if((sectorid >= 9) && (sectorid <= 15))
		flag = 1;
	else if((sectorid >= 17) && (sectorid <= 23))		
		flag = 1;
	
	*addr = addrs;
	*pflag = flag;

	return sectorid;
}

/****************************************************************************
* 名   称: ResetWdog()
* 功   能: 复位看门狗
* 入口参数: 
* 出口参数: 
****************************************************************************/
void IapResetWdog(void)
{
	uint16 i;

	IO1SET = (1<<18);
	for(i=0;i<50;i++);
	IO1CLR = (1<<18);
}

/****************************************************************************
* 名   称: IapDelay()
* 功   能: IAP延时函数
* 入口参数: 
* 出口参数: 
****************************************************************************/
void IapDelay(void)
{
	uint32 i;
	
	for(i=0;i<10000;i++);	    
}

/****************************************************************************
* 名称:IapMain()
* 功能:IAP函数调用,测试。通过观察IAP出口参数来判断操作是否成功。
* 说明:程序没有进行出错处理。
*      要设置编译参数-apcs /interwork,因为IAP为Thumb指令。
*      使用IAP功能时,片内RAM的顶端的32字节保留给IAP操作使用,所以在STARTUP.S文件中
*      初始化用户模式堆栈为StackUsr-20*4,以保证IAP正常操作。
****************************************************************************/
void IapMain(uint16 CodeIdNum)
{  
	uint8  i,j;
	uint16 CodeIdNo = 0;			// 当前代码包ID号
	uint32 addr = 0;				// 片内FLASH地址
	uint8  flag = 0;				// 是否为起始扇区地址
	uint8  SectorNo = 0;			// 扇区号
	uint8  errorflag = 0;
	int    tmp = 0;
   
   	//Iap_Handle IapCompeted = (Iap_Handle)(0x0000);		//IAP完成返回
   
   	MAMCR = 0;						// 关闭MAM
   	PLLCON = 0;						// 关闭PLL
   	PLLFEED = 0xaa;
   	PLLFEED = 0x55;  
   	 
   	__asm			//关闭总中断
   	{
   		MRS  tmp,CPSR
   		ORR  tmp,tmp,#0x80
   		MSR  CPSR_c,tmp
   	}
	
	for(i=0;i<3;i++)												//判断CHIP ID是否正确
	{
		ReadParID();
		if(paramout[0] == CMD_SUCCESS)
			break;
		IapDelay();
	}
	if(i >= 3)
		errorflag = 1;

	if(errorflag == 0)
	{
		for(i=0;i<3;i++)											//判断BOOT ID是否正确				
		{
			BootCodeID();
			if(paramout[0] == CMD_SUCCESS)
				break;
			IapDelay();
		}
		if(i >= 3)
			errorflag = 1;
	}
	
	if(errorflag == 0)
	{
		while(CodeIdNo < CodeIdNum)									//根据升级代码包ID计算InFlash扇区号,并开始写FLASH和校验	
		{
			IapResetWdog();
		
			IapFlashReadMem(pIapData,IAP_CODE_PAGE_SIZE,IAP_UPDATE_CODE_ADDR + CodeIdNo * IAP_CODE_PAGE_SIZE);
		
			SectorNo = IapCalSectorById(CodeIdNo,&addr,&flag);
			if(SectorNo == 0xff)
			{
				while(1){IapResetWdog();}
			}
			for(j=0;j<3;j++)
			{
				if(flag == 0)
				{
					for(i=0;i<3;i++)
					{
						SelSector(SectorNo,SectorNo);    				 // 选择扇区		
						EraseSector(SectorNo,SectorNo);  				 // 擦除扇区
   						BlankCHK(SectorNo,SectorNo);     				 // 查空扇区	
						if(paramout[0] == CMD_SUCCESS)
							break;
					}
					if(i>= 3)
					{
						while(1){IapResetWdog();}
					}
				}
				SelSector(SectorNo,SectorNo);      						// 选择扇区		
				RamToFlash(addr, (uint32)pIapData, IAP_CODE_PAGE_SIZE); // 写数据到扇区 		
				BlankCHK(SectorNo,SectorNo);     						// 查空扇区	
				Compare(addr, (uint32)pIapData, IAP_CODE_PAGE_SIZE);    // 比较数据		
				if(paramout[0] == CMD_SUCCESS)
					break;
			}
			CodeIdNo++;
		}
	}
	__asm				//打开总中断
	{
		MRS tmp,CPSR
		BIC tmp,tmp,#0x80
		MSR CPSR_c,tmp
	}

/* 设置系统各部分时钟 */
    PLLCON = 1;
#if (Fpclk / (Fcclk / 4)) == 1
    VPBDIV = 0;
#endif
#if (Fpclk / (Fcclk / 4)) == 2
    VPBDIV = 2;
#endif
#if (Fpclk / (Fcclk / 4)) == 4
    VPBDIV = 1;
#endif

#if (Fcco / Fcclk) == 2
    PLLCFG = ((Fcclk / Fosc) - 1) | (0 << 5);
#endif
#if (Fcco / Fcclk) == 4
    PLLCFG = ((Fcclk / Fosc) - 1) | (1 << 5);
#endif
#if (Fcco / Fcclk) == 8
    PLLCFG = ((Fcclk / Fosc) - 1) | (2 << 5);
#endif
#if (Fcco / Fcclk) == 16
    PLLCFG = ((Fcclk / Fosc) - 1) | (3 << 5);
#endif
    PLLFEED = 0xaa;
    PLLFEED = 0x55;
    while((PLLSTAT & (1 << 10)) == 0);
    PLLCON = 3;
    PLLFEED = 0xaa;
    PLLFEED = 0x55;
    
/* 设置存储器加速模块 */
    MAMCR = 0;
#if Fcclk < 20000000
    MAMTIM = 1;
#else
#if Fcclk < 40000000
    MAMTIM = 2;
#else
    MAMTIM = 3;
#endif
#endif       


}



⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -