|
楼主 |
发表于 2014-9-5 09:34:51
|
显示全部楼层
/*
* BOOT LOADER v3.04 Copyright (C) 2008 HI-TECH Software
* This software is freely distributable and may be used
* for any purpose. No warranty of any kind is provided
* and all use is entirely at your own risk.
*
* The boot loader is suitable for use with the following
* PICmicro controllers:
*
* 16F870, 16F871, 16F873, 16F873A, 16F874,
* 16F874A, 16F876, 16F876A, 16F877, 16F877A
*
* Refer to the accompanying bootldr.txt file for details.
*
* BOOTLDR.C
*/
#include <htc.h>
#include "bootldr.h"
/* Unprotect memory and disable the watchdog timer */
#ifdef MPLAB_ICD
__CONFIG(UNPROTECT & WDTDIS & DEBUGEN & LVPDIS);
#else
__CONFIG(UNPROTECT & WDTDIS);
#endif
// cksum - a variable used for calculating checksums. By declaring the
// variable "persistent" it will tell the compiler not to bother initializing
// it on startup and therefore code size will be smaller.
persistent unsigned char cksum;
// if "VERBOSE" is defined, additional feedback is given by the
// below routines.
#ifdef VERBOSE
const unsigned char START_MSG[]="HI-TECH Software (C)2008\n";
const unsigned char DOWNLOAD_MSG[]="\rDownload-";
/* PUTCH() - outputs a byte to the serial port */
void
putch(unsigned char byte)
{
while(!TXIF); /* set when register is empty */
TXREG = byte; /* output one byte */
}
/* Output a string via the serial port */
void
puts(const char *s)
{
while(s && *s)
putch(*s++);
}
#endif
/* Get a 4 bit HEX number from the serial port */
unsigned char
gx(void)
{
#if VERBOSE == 2
char tmp;
while(!RCIF);
tmp = RCREG;
putch(XOFF);
putch(tmp); // echo input nibbles to output
tmp -= '0';
if (tmp>9)
tmp -= ('A' - '0');
putch(XON);
return tmp;
#else
while(!RCIF);
if(RCREG>='A') //translate the ASCII to hex
return ((RCREG - (unsigned char)'A')+10);
return (RCREG - '0');
#endif
}
/* Get an 8 bit HEX number from the serial port */
unsigned char
g2x(void)
{
unsigned char input_byte;
input_byte=(gx()<<4); //get first nibble
input_byte|=gx(); //get second nibble
cksum+=input_byte; //compute checksum
return (input_byte);
}
/* check the checksum */
void
checksum(void)
{
g2x();
if(cksum!=0) // if checksum does not add to zero, bad check, reset
RESET();
#if VERBOSE == 2
putch('\r'); // echo each hex record on a new line
putch('\n'); // echo each hex record on a new line
#endif
}
/* Initiate a write to memory */
void
writemem(void)
{
char tmpadrh;
#ifdef IRREG_START
char tmpadr;
#endif
while(!TXIF);
TXREG = XOFF;
#ifdef IRREG_START
tmpadr = EEADR;
if (((tmpadrh=EEADRH)==0) && (tmpadr < 4)) // is the address < 4?
{
EEADRH = BOOT_START >> 8; //yes - then move it
EEADR += (BOOT_START & 0xFF);
}
#else
if (((tmpadrh = EEADRH)==0) && (EEADR < 4))
{
EEADRH = BOOT_START >> 8; //yes - then move it
}
#endif
while(WR); //configure for the write to memory
WREN=1;
EECON2=0x55;
EECON2=0xAA;
WR=1; //initiate the write
NOP();
NOP();
WREN=0;
EEADRH = tmpadrh; //swap back the address in case it was one we moved
#ifdef IRREG_START
EEADR = tmpadr; //swap back the address in case it was one we moved
#endif
while(!TXIF);
TXREG = XON;
}
void
main(void)
{
unsigned char LOW_ADDRESS,rectype,count;
INIT_COMMS(); // sets up the serial port for communication
LOW_ADDRESS = RCREG; //flush receive register
/* Bootloader waits for a specified time. If the serial port gets no response in */
/* this time, execution of existing program begins. */
#ifdef VERBOSE
puts(START_MSG); // print a welcome message
#endif
for(count=BOOT_TIMEOUT; count; --count)
{
if (RCIF) //have we recieved anything?
break;
#ifdef VERBOSE // display a countdown for user response
puts(DOWNLOAD_MSG);
putch('0'+count);
#else
TXREG=('0'+count); //display a countdown
while(!TXIF);
TXREG='\r';
#endif
INTCON=0; // disables interrupts.
T1CON=0x35; // pause during countdown
while(!TMR1IF); //wait...
TMR1IF=0; //clear the flag
T1CON=0x00; // disable the timer again
}
if (!RCIF) //did it timeout without recieving anything?
{ // no hex file to download, resume normal program
#asm
ljmp BOOT_START
#endasm
}
TXREG=':'; // prompt to indicate bootloader is ready to recieve hex file
/* receive a hex file via the serial port and write it to program memory */
for(;;) // loop until end of file
{
while (RCREG!=':'); // wait for start of hex record
#if VERBOSE == 2
putch(':');
#elif defined(VERBOSE)
putch('.');
#endif
cksum = count = g2x(); // get the byte count and reset the checksum
count>>=1; // byte count >> word count
EEDATA = g2x(); // get the high address byte
LOW_ADDRESS = g2x(); // get the low byte of the address
LOW_ADDRESS >>=1; // convert the hex file's byte address to a PIC word address
if((EEDATA&1)==1) // does the high byte need to roll a bit into the low address?
LOW_ADDRESS |= 0x80;
EEADRH = EEDATA >> 1; // byte to word conversion on high address byte
EEADR = LOW_ADDRESS & 0xFC; // point to start of 4 word block
EEPGD = 1; // destination is flash memory
if(EEDATA == 0x42) // unless this case,
EEPGD = 0; // when EEPROM should be selected
else
if(EEDATA >= 0x40){ // ignore any other special types such as CONFIG/IDLOC
#if VERBOSE == 2
putch('\r');
#endif
continue;
}
rectype = g2x(); // get the record type
if(rectype==1){ // END OF FILE record: prepare to run new program
checksum();
#ifdef VERBOSE
puts("Ok");
#else
TXREG=')';
#endif
#asm
ljmp _redirect
#endasm
}
else{ // this record is a data record
while((count != 0)||(EEADR&2)||(EEADR&1)){ // keep writing until all bytes done and 4 word block complete...
RD=1; // read data from destination address (may need to re-write this word)
NOP();
NOP();
if(EEADR == LOW_ADDRESS) // has the address reached the valid range yet?
rectype |= OK_BIT; // if so set a bit to indicate we are Ok to source from USART
// Test to see whether this address is to be written with new data.
if(count!=0){ // if there is still incoming data for this hex record
if((rectype & OK_BIT) == OK_BIT){ // has the address reached the valid range yet?
if(LOADER_SAFE){
EEDATA = g2x(); // get the data low byte
EEDATH = g2x(); // get the data high byte
}
count--; // decrement word count
}
}
writemem(); // write this to memory
if(++EEADR == 0) // select next address
EEADRH++;
} // end of this record and 4 word block
checksum();
}
}
}
void redirect(void) @ BOOT_START{
#asm
PCLATH equ 0Ah
PCL equ 02h
; this must be 4 word in length (including return)
movlw 0
movwf PCLATH
movwf PCL
#endasm
}
|
|