P89V51RD2的IAP升级实现2009-01-19 14:35关于IAP功能的描述请看RD2(P89V51RD2的简称,下同)的datasheet 。
回想起来来实现其实比较简单,主要是分开看它的存储空间就ok了,可以理解为为了实现这个功能RD2专门开辟了一块BootROM用,
void IAP_Init() { g_ucIAPFlag = 0xAA; #pragma asm CLR EA ANL 0xB1,#0xFC //0xB1是有关Flash操作的sfr LJMP 0x0 #pragma endasm
}
在keil中将上述代码强制指定到0x8000之后,反正没几个字节我直接放到?PR?IAP_INIT?IAP(0xFFE0)
了,当你需要进入升级模式的时候只需要调用上面的函数就可以了。
当RD2启动时会自动优先启动升级区(block1)的代码,这是硬件完成的。通过反汇编RD2原带的code,整理成我需要的IAP处理就行了(它原来有较麻烦的ISP相关处理一律删掉)
#include "reg320.h" #include "IAP.h"
/* ** auther sjing ** IAP功能:读取ID信息 ** 说明:BootRom反汇编整理得C函数 ,因不需要用到RD2的sfr故直接按反汇编结果返回 */ unsigned char ReadID_IAP(unsigned char ucIDType) { if(ucIDType==0) //厂商ID { return 0xBF; } else if(ucIDType==1) //器件ID { return 0x91; } else //ISP/IAP版本 { return 0x05; }
} /* ** auther sjing ** IAP功能:擦除用户区 ** 说明:BootRom反汇编整理得C函数 */ void EraseBlock0_IAP(void) { #pragma asm // mov r1,#0x01 //擦除功能代号 ORL 0B1H, #40H MOV 0B4H, #0F0H MOV 0B5H, #55H MOV 0B2H, #0DH LoopErase: MOV A, 0B6H JB ACC.2, LoopErase MOV A, #00H #pragma endasm }
/* ** auther sjing ** IAP功能:写入字节 ** 更新用户区程序 ** 说明:BootRom反汇编整理得C函数 */ unsigned char WriteByte_IAP(unsigned int uiAddr,unsigned char uiData) { DPH = (unsigned char)(uiAddr>>8); DPL = (unsigned char)(uiAddr&0x00FF); ACC = uiData; #pragma asm // mov r1,#0x02 //擦除指定扇区的128Byte ORL 0B1H, #40H MOV 0B4H, DPH MOV 0B3H, DPL MOV 0B5H, A MOV B, A MOV 0B2H, #0EH LoopWrite: MOV A, 0B6H JB ACC.2, LoopWrite
ORL 0B1H, #40H MOV 0B4H, DPH MOV 0B3H, DPL MOV 0B2H, #0CH MOV A, 0B5H XRL A, B //写进数据与希望写入数据异或,如不一样则写入失败 #pragma endasm
return ACC; //是否写入成功0=Success
}
/* ** auther sjing ** IAP功能:读取字节 ** 读取的只是Block0的代码 ** 说明:BootRom反汇编整理得C函数 */ unsigned char ReadByte_IAP(unsigned int uiAddr) { DPH = (unsigned char)(uiAddr>>8); DPL = (unsigned char)(uiAddr&0x00FF); #pragma asm // mov r1,#0x03 ORL 0B1H, #40H MOV 0B4H, DPH MOV 0B3H, DPL MOV 0B2H, #0CH MOV A, 0B5H #pragma endasm // PublicIAP(); return ACC; } //读到的数据
/* ** auther sjing ** IAP功能:烧录加密位和双倍时钟位 ** 说明:BootRom反汇编整理得C函数DPL=01H:烧录加密位 ;DPL=05H:烧录双倍时钟位 */ void ProSNOrEnClock_IAP(unsigned char ucType) { switch(ucType) { case 0x00: { #pragma asm // mov r1,#0x05 ORL 0B1H, #40H MOV 0B5H, #0AAH MOV 0B2H, #0FH Loop0: MOV A, 0B6H JB ACC.2, Loop0 #pragma endasm break; } case 0x01: { #pragma asm // mov r1,#0x05 ORL 0B1H, #40H MOV 0B5H, #0AAH MOV 0B2H, #3H Loop1: MOV A, 0B6H JB ACC.2, Loop1 #pragma endasm break; } case 0x02: { #pragma asm // mov r1,#0x05 ORL 0B1H, #40H MOV 0B5H, #0AAH MOV 0B2H, #5H Loop2: MOV A, 0B6H JB ACC.2, Loop2 #pragma endasm break; } case 0x03: { #pragma asm // mov r1,#0x05 ORL 0B1H, #40H MOV 0B4H, #5AH MOV 0B5H, #0AAH MOV 0B2H, #09H Loop3: MOV A, 0B6H JB ACC.2, Loop3 #pragma endasm break; } case 0x04: { #pragma asm // mov r1,#0x05 ORL 0B1H, #40H MOV 0B4H, #0AAH MOV 0B5H, #0AAH MOV 0B2H, #09H Loop4: MOV A, 0B6H JB ACC.2, Loop4 #pragma endasm break; } case 0x05: { #pragma asm // mov r1,#0x05 MOV A, 0B6H JB ACC.3, Loop5_2 Loop5_0: JNB TI, Loop5_0 ORL 0B1H, #40H MOV 0B4H, #55H MOV 0B5H, #0AAH MOV 0B2H, #08H
Loop5_1: MOV A, 0B6H JB ACC.2, Loop5_1
CLR 0C8H.2 MOV A,38H RRC A CLR ACC.7 MOV R1, A DB 0E5H ADDC A, R1 RRC A MOV R0, A MOV A, R1 RRC A CLR ACC.7 MOV R1, A MOV A, R0 RRC A MOV R0, A MOV A, R1 RRC A CLR ACC.7 CPL A MOV R1, A MOV A, R0 RRC A CPL A CLR C ADD A, #01H MOV 0CAH, A MOV 0CCH, A MOV A, R1 ADDC A, #00H MOV 0CBH, A MOV 0CDH, A SETB 0C8H.2 Loop5_2: CLR A #pragma endasm break; }
} }
/* ** auther sjing ** IAP功能:读取加密位和双倍时钟位 ** 读取的只是Block0的代码 ** 说明:返回参数: ACC.0=双倍时钟位(DBL_CLK) :0-12 时钟模式,1-6 时钟模式 (即双倍时钟) ACC.2=加密位(SB) :0-未加密,1-已加密 ACC.4=序列号是否匹配(S/N-match) //反汇编显示是ACC.5控制匹配为1,不匹配为0 ACC 的其它位都是 0 */ unsigned char ReadSNOrEnClock_IAP(void) { #pragma asm // mov r1,#0x07 MOV A, 0B6H MOV C, ACC.3 MOV ACC.4, C SWAP A ANL A, #0FH MOV C, 08H MOV ACC.4, C MOV R1, #9FH CJNE @R1, #0FH, LoopSN2 SETB ACC.5 SJMP LoopSN1 LoopSN2: CLR ACC.5 LoopSN1: #pragma endasm // PublicIAP(); return ACC; } //读到的数据
/* ** auther sjing ** IAP功能:擦除扇区 ** 擦除的只是前面需要更新的扇区(block0)最后一个用来保存PublicIAP()函数不能操作 ** 说明:BootRom反汇编整理得C函数 */
void SectorErase_IAP(unsigned int uiSector) { unsigned int uiAddr;
uiAddr = uiSector*128;//定位实际的地址,总共512Sector 每Sector 128Byte DPH = (unsigned char)(uiAddr>>8); DPL = (unsigned char)(uiAddr&0x00FF); #pragma asm // mov r1,#0x08 //擦除指定扇区,128Byte ORL 0B1H, #40H MOV 0B4H, DPH MOV 0B3H, DPL MOV 0B2H, #0BH LoopEraseS: MOV A, 0B6H JB ACC.2, LoopEraseS MOV A, #00H #pragma endasm
}
void Exit_IAP() { #pragma asm // ORL 0xB1,#0x02 //切回用户block ORL 0xB1,#0x01 //切回用户block LCALL 0x0 #pragma endasm }
其实我们需要的函数没有几个,简单举例
void main() { unsigned int uiTemp;
unsigned char ucTempRD[4];
g_ucFileEnd = 0xff; g_ucByteNum = 0; g_uiAddrIAP = 0;
CommInit();
ucTempRD[0] = ReadByte_IAP(0xFFE4); ucTempRD[1] = ReadByte_IAP(0xFFE5); ucTempRD[2] = ReadByte_IAP(0xFFE6); ucTempRD[3] = g_ucIAPFlag; if((ucTempRD[0]==0xAA)&&(ucTempRD[1]==0xF0)&&(ucTempRD[2]==0xC2)&&(g_ucIAPFlag!=0xAA)) //读取最后写入的程序是否正常如正常并且不是用户申请升级模式则进入用户程序 { Send_UART(ucTempRD,4); Exit_IAP(); } else { Send_UART("preparing,keep connect...",0); Send_UART(ucTempRD,4); EraseBlock0_IAP(); }
uiTemp = 1;
while(uiTemp) { IO_DOG_CLR = !IO_DOG_CLR; //just for 77e58 Receive_UART(); }
{ //虚调函数 ReadID_IAP(0); ReadByte_IAP(0); ProSNOrEnClock_IAP(0); ReadSNOrEnClock_IAP(); SectorErase_IAP(0); EraseBlock0_IAP(); } }
这样一个简单的IAP升级程序就完成了~ 网络真是伟大呀! 相关链接:https://bbs.21ic.com/upfiles/img/20092/200921212723739.rar |