📄 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 + -