实现过程:
一:内部flash 的空间划分
0XFFFF |
保留区
应用程序区
BOOTLOADER可操作区域
|
| |
0XF000 | |
| |
0X1000 | |
0X1000 | |
| |
0X0000 |
二:中断向量软件映射
MCU中断向量分布在复位(0x0000)以后,按规则排布,若改变编译器设置,如51,可改变程序在FLASH中的位置,可改变中断向量起始点,如改到这样0X1000,即:就是将0X0000地址内容映射到0X1000,把0X1000作为一个虚拟的复位地址,编译后,中断向量安装在0X1000后。但是,硬件跳转是不能改变的。就是说中断发生后,仍然进入以0X0000为复位地址的向量表。但是,这个向量表中,不再是直接跳转到中断服务程序,而是需要手动跳转到虚拟中断向量表,再由虚拟中断向量表跳转到中断服务程序。但是其原始向量表可能为空,所以需要加入程序判进行手动跳转到新向量表。
所以编译的时候,将APP程序中断向量表虚拟,BOOT程序使用原始的向量表。所以任何时候中断发生后,将执行BOOT中断服务程序。在BOOT中断服务程序中判断当前程序是执行BOOT区还是APP区,如果是执行APP区,就跳转到APP中断服务程序即可(入口向量已经规则地排布在0X1000以后)。
51的中断向量排布规则是: LJMP 3+8*INTERRUPT_VECTOR_NUMBER
图解:
原始地址(向量表) |
作用 |
描述 |
0000H |
RESET |
POWER UP ENTRY |
0003H |
INTERRUPT 0入口 |
|
000BH |
INTERRUPT 1入口 |
|
0013H |
INTERRUPT 2入口 |
|
… |
… |
… |
虚拟后地址(向量表) |
作用 |
描述 |
1000H |
虚拟复位地址 |
|
1003H |
INTERRUPT 0虚拟入口 |
进入中断 |
100BH |
INTERRUPT 1虚拟入口 |
进入中断 |
… |
… |
… |
注意:中断向量虚拟后,并不是说发生中断程序就转入虚拟后的向量表。而是:中断向量表的实质是一条跳转指令,跳转到相关ISR,虚拟后,这个跳转指令被放在了新的地址,而不是原始默认地址。所以,需要在原始地址处再放一辅助向量表(中断发生后,硬件挑到此处),用于跳转到新的向量表。这个辅助向量表有BOOT区完成。
1:STARTUP.A51文件设置:
;vector_base EQU 01000h
;vector
CSEG AT 03h
LJMP vect + 03h ;// 0 外部中断0 (/INT0)
;user_address EQU 01000h
user_process:
LJMP vect;user_address
CSEG AT 0bh
LJMP vect + 0bh ;// 1 定时器0溢出
CSEG AT 13h
LJMP vect + 013h ;// 2 外部中断1 (/INT1)
CSEG AT 1bh
LJMP vect + 01bh ;// 3 定时器1溢出
CSEG AT 23h
LJMP vect + 023h ;// 4 UART0
CSEG AT 2bh
LJMP vect + 02bh ;// 5 定时器2溢出
CSEG AT 33h
LJMP vect + 033h ;// 6 SPI0
CSEG AT 3bh
LJMP vect + 03bh ;// 7 SMB0
CSEG AT 43h
LJMP vect + 043h ;// 8 USB0
CSEG AT 4bh
LJMP vect + 04bh ;// 9 ADC0窗口比较
CSEG AT 53h
LJMP vect + 053h ;// 10 ADC0转换结束
CSEG AT 5bh
LJMP vect + 05bh ;// 11 可编程计数器阵列
CSEG AT 63h
LJMP vect + 063h ;// 12 比较器0
CSEG AT 6bh
LJMP vect + 06bh ;// 13 比较器1
CSEG AT 73h
LJMP vect + 073h ;// 14 定时器3溢出
CSEG AT 7bh
LJMP vect + 07bh ;// 15 VBUS电平
CSEG AT 83h
LJMP vect + 083h ;// 16 UART1
CSEG AT 000h
?C_STARTUP: LJMP STARTUP1
2:应用程序区工程设置:
3:bootloader程序和 APP 应用程序的初始化时钟必须是一致的, 否则容易造成死机的现象
三:BOOTLOADER 程序
1:主程序的编写
main(void)
{
uint8 uartRX;
uint16 timeout;
void (*boot)( void);
Sys_Clk_Init(); // 初始化设备
PORT_Init();
UART0_Init();
FLSCL = 0x86;
if (1) {
Uart_Print("\n");
Uart_Print("**************BootLoader v1.0***************\n");
Uart_Print(">Please press 's' to start programming.\n");
while (1)
{
for(timeout=0;timeout<4000;timeout++)
{
if(RI0)
{
uartRX=SBUF0;
RI0=0;
break;
}
delay_ms(10);
}
if (uartRX=='s')
{RTload();
uartRX = 0;
}
else if(vect == 0x02)
{
goto USE_CODE;
}
}
}
USE_CODE:
user_process();
while(1)
{
}
2:片内FLASH的 操作
unsigned char FLASH_ByteRead (FLADDR addr)
{
bit EA_SAVE = EA;
char code * data pread;
unsigned char byte;
EA = 0;
pread = (char code *) addr;
byte = *pread;
EA = EA_SAVE;
return byte;
}
void FLASH_ByteWrite (FLADDR addr, char byte)
{
bit EA_SAVE = EA;
char xdata * data pwrite;
EA = 0;
pwrite = (char xdata *) addr;
PFE0CN = PFE0CN & 0xFE;
PSCTL = 0x01;
FLKEY = 0xA5;
FLKEY = 0xF1;
*pwrite = byte;
PSCTL = 0;
EA = EA_SAVE;
}
void FLASH_PageErase (FLADDR addr)
{
bit EA_SAVE = EA;
char xdata * data pwrite;
EA = 0;
pwrite = (char xdata *) addr;
FLKEY = 0xA5;
FLKEY = 0xF1;
PSCTL = 0x03;
*pwrite = 0;
PSCTL = 0;
EA = EA_SAVE;
}
可以正常使用串口更新程序, 有需要的, 可以给我留言,一起起步!
用户377235 2016-6-21 09:47
用户3733014 2016-4-5 13:45
用户377235 2015-8-4 16:05
用户1810322 2015-1-27 14:53
用户377235 2013-6-8 09:05
我正在做c8051f340的串口升级,没有用到中断向量的映射,看了你的方法,感觉挺好的,能给我发个完整的程序借鉴下吗,谢谢!邮箱:282679163@qq.com
用户377235 2013-3-7 13:43
非常感谢!!!!
用户1630181 2012-12-18 10:14
用户377235 2012-11-27 17:41
用户378258 2011-9-26 14:37