bootldr.c

来自「pic单片机例程」· C语言 代码 · 共 273 行

C
273
字号
/*
 * 	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
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?