📄 bootldr.c
字号:
void table_write(void)
{
if(DO_NOT_INCREMENT) // 地址已经载入 TBLPTR, 不需要预先增加
asm("tblwt*"); // 不修改TBLPTR,短周期写操作
else
asm("tblwt+*"); // TBLPTR在写前递增,短周期写操作
DO_NOT_INCREMENT=0; // 第二次开始,TBLPTR在写前递增
}
/*============================================================================
写8字节缓冲区到 FLASH
============================================================================*/
void flash16(void)
{
unsigned char j;
if(DO_NOT_INCREMENT) // 地址已经载入TBLPTR
TBLPTRL&=0xF0; // TBLPTRL的低4位清0(16字节为1块)
for(j=0;j<4;j++)
{
if(DO_NOT_INCREMENT) // 地址已经载入TBLPTR
TBLPTRL&=0xF0; // TBLPTRL的低3位清0(16字节为1块)
for(index=0;index<16;) // 一共写16个字节
{
TABLAT = buff[index++]; // 程序存储器表锁存
table_write(); // 写1个字节
}
DO_NOT_INCREMENT=1;
WREN=1; // 允许闪存程序/数据EEPROM的写周期
EECON2=0x55; // 向EECON2写入0x55,必须执行的步骤
EECON2=0xAA; // 向EECON2写入0xAA,必须执行的步骤
WR=1; // 启动数据EEPROM擦写周期或程序存储器擦写周期
// zap(); // 启动闪存程序写入(长周期写操作)
}
while(WR); // 等待写入结束
WREN=0; // 禁止闪存程序/数据EEPROM的写周期
NOP();
NOP();
}
/*============================================================================
填充清除字节
============================================================================*/
void clear_buffer(void)
{
buff[0] = buff[1] = buff[2] = buff[3] =FILL_BYTE;
buff[4] = buff[5] = buff[6] = buff[7] =FILL_BYTE;
buff[8] = buff[9] = buff[10] = buff[11] =FILL_BYTE;
buff[12] = buff[13] = buff[14] = buff[15] =FILL_BYTE;
}
/*============================================================================
主程序
============================================================================*/
void main(void)
{
init_comms(); // 初始化串口
puts(START_MSG);
INTCON3 =0; // 禁止其他中断
PIE2 =0; // 禁止其他中断
INTCON =0; // 禁止所有中断
RD1=1;
//----------------------------------------------------------------------------
T0CON =0x96; // 采用1:128的分频
TMR1H =0x67; // 使用这个值产生1秒延时
TMR1L =0x60;
for(delay_time=BOOT_TIMEOUT; delay_time ; --delay_time)
{
if (RCIF) //这里可以加一些条件限制的
break;
puts(DOWNLOAD_MSG); // 给用户一个倒数计秒
putch('0'+delay_time); // 显示ASCLL码
while(!TRMT);
TXREG='\r';
while(!TMR0IF); // 等待定时器0超时
TMR0IF=0;
TMR1H=0x67; // 使用这个值产生1秒延时
TMR1L=0x60;
}
T0CON=0x00; // 再次停止定时器
RD1=0;
/*******************************************************************************
* 如果串口没有响应,Bootloader 将等待一个设定的时间后,运行原来的固有的程序
*******************************************************************************/
if (!RCIF) // 如果没有下载到HEX文件, 恢复运行原来程序
{
(*((void(*)(void))PROG_START))();
}
//==============================================================================
puts(FILE_MSG); // 立即请求你下载一个新的程序
TBLPTRU=0; // 表指针最高字节=0
erase=PROG_START; // PROG_START=0x400
while(1) //清除原来的程序,准备下载的程序使用
{
TBLPTRL=(unsigned char)erase; // 闪存表指针低字节=0x400
TBLPTRH=(unsigned char)(erase>>8); // 闪存表指针高字节=0x04
EECON1=0x90; // 准备FLASH删除
zap(); // 启动闪存程序写入(长周期写操作)
erase+=64;
//----------------------------------------------------------------------------
#ifdef UPPER_ADDRESS_BYTE // 高位地址
if(CARRY) // 闪存表指针低字节溢出(+64后)
TBLPTRU++; // 闪存表指针高字节+1
if(TBLPTRU==UPPER_ADDRESS_BYTE)
#endif
if(erase==MEM_TOP)
break;
}
//----------------------------------------------------------------------------
#ifdef UPPER_ADDRESS_BYTE // 高位地址
TBLPTRU=0; // 表指针最高字节=0
#endif
/*****************************************************************************************
* 通过串口接收HEX文件,然后写到程序空间
*****************************************************************************************/
for(;;) // l循环
{
while (RCREG!=':'); // 等待 HEX 文件开始
// :冒号是每一条Intel HEX记录的开始
putch(':'); // 输出到上位机字符':'
cksum = bcount = g2x(); // 取数据长度
// :冒号后面是一个字节的数据长度
EEADRH = TBLPTRH = g2x(); // 接着是高位地址
EEADR = TBLPTRL = g2x(); // 接着是地位地址
DO_NOT_INCREMENT = 1; // 地址已经载入TBLPTR,不需要预先增加
rectype = g2x(); // 接着是数据类型
//****************************************************************************************
switch(rectype) // 根据取数据类型
{
case DATA: // 数据记录DATA=0
//---------------------------------------------------------------------------------------
if( (FLASH) && (TBLPTRU==0) && (TBLPTRH < (unsigned char)(PROG_START>>8)) )
break; // 保护bootloader避免被改写,高位地址<PROG_START>>8,退出
//---------------------------------------------------------------------------------------
clear_buffer(); // 缓冲区16字节全部填充0xFF
while(bcount--)
{
TABLAT = EEDATA = buff[(EEADR&0xF)] = g2x(); // 取数据
//将数据依次读到buff[0]-buff[15]
if((CONFIG)||(EEPROM)) // 如果是配置或EEPRON
{
if(CONFIG) // 配置字节
table_write(); // 每次写1个字节
zap(); // EEPROM不写
}
else // 程序每次写8个字节
if((EEADR&0xF)==15) // 到第16个字节了
{
flash16(); // 每次写16个字节
clear_buffer(); // 清除缓冲
}
EEADR++;
}
if(((EEADR&0x0F)!=0)&&(FLASH)) // 写最后非满16个字节FLASH
flash16(); // 由于前面没有写
checksum(); // 接收校验字节并检查校验和
break;
//--------------------------------------------------------------------------------------
case END: // 文件结束END=1
checksum(); // 接收校验字节并检查校验和
puts(SUCSESS_MSG); // 提示升级成功
(*((void(*)(void))PROG_START))(); // 运行新程序
break;
//---------------------------------------------------------------------------------------
case EXTEND_ADDRESS: // 扩展地址记录 EXTEND_ADDRESS=4
while(bcount--) // EE\Config\ID?
{
EEADR=g2x(); // 2字节的扩展地址,先高后低,实际用低的
}
EEPGD=1; // 先默认EEPGD=1,访问闪速程序存储器
if(EEADR==0xF0) // 只有 EEPGD=0,CFGS=0
EEPGD=0; // 访问数据EEPROM
CFGS=0;
if((EEADR&0xF0)==0x30) // 只要CFGS=1,就
CFGS=1; // 访问配置寄存器
TBLPTRU=EEADR;
checksum(); // 接收校验字节并检查校验和
break;
//---------------------------------------------------------------------------------------
}//switch(rectype)
}//for(;;)
}//void main(void)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -