260 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			260 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*-------------------------------------------------------------------------/
 | 
						|
/  Stand-alone MMC boot loader  R0.01
 | 
						|
/--------------------------------------------------------------------------/
 | 
						|
/
 | 
						|
/  Copyright (C) 2010, ChaN, all right reserved.
 | 
						|
/
 | 
						|
/ * This software is a free software and there is NO WARRANTY.
 | 
						|
/ * No restriction on use. You can use, modify and redistribute it for
 | 
						|
/   personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
 | 
						|
/ * Redistributions of source code must retain the above copyright notice.
 | 
						|
/
 | 
						|
/--------------------------------------------------------------------------/
 | 
						|
/ Dec 6, 2010  R0.01  First release
 | 
						|
/--------------------------------------------------------------------------/
 | 
						|
/ This is a stand-alone MMC/SD boot loader for megaAVRs. It requires a 4KB
 | 
						|
/ boot section for code, four GPIO pins for MMC/SD as shown in sch.jpg and
 | 
						|
/ nothing else. To port the boot loader into your project, follow the
 | 
						|
/ instruction described below.
 | 
						|
/
 | 
						|
/ 1. Setup the hardware. Attach a memory card socket to the any GPIO port
 | 
						|
/    where you like. Select boot size at least 4KB for the boot loader with
 | 
						|
/    BOOTSZ fuses and enable boot loader with BOOTRST fuse.
 | 
						|
/
 | 
						|
/ 2. Setup the software. Change the four port definitions in the asmfunc.S.
 | 
						|
/    Change MCU_TARGET, BOOT_ADR and MCU_FREQ in the Makefile. The BOOT_ADR
 | 
						|
/    is a BYTE address of boot section in the flash. Build the boot loader
 | 
						|
/    and write it to the device with a programmer.
 | 
						|
/
 | 
						|
/ 3. Build the application program and output it in binary form instead of
 | 
						|
/    hex format. Rename the file "app.bin" and put it into the memory card.
 | 
						|
/
 | 
						|
/ 4. Insert the card and turn the target power on. When the boot loader found
 | 
						|
/    the application file, the file is written into the flash memory prior to
 | 
						|
/    start the application program. On-board LED lights (if exist) during
 | 
						|
/    the flash programming operation.
 | 
						|
/
 | 
						|
/-------------------------------------------------------------------------*/
 | 
						|
 | 
						|
const char filename[13] ="FIRMWARE.BIN\0"; 	// EDIT FILENAME HERE
 | 
						|
#include <avr/io.h>
 | 
						|
#include <avr/pgmspace.h>
 | 
						|
#include <avr/eeprom.h>
 | 
						|
#include <util/delay.h>
 | 
						|
#include <string.h>
 | 
						|
#include "pff/src/pff.h"
 | 
						|
#include <avr/wdt.h> //Watchdog
 | 
						|
// The following code is recommended in http://avr-libc.nongnu.org/user-manual/group__avr__watchdog.html but is disabled for now because avr_boot doesn't currently do anything with mcusr_mirror so for now we will only reset MCUSR and disable WDT.
 | 
						|
//uint8_t mcusr_mirror __attribute__ ((section (".noinit")));void get_mcusr(void) __attribute__((naked)) __attribute__((section(".init3")));void get_mcusr(void){mcusr_mirror = MCUSR;MCUSR = 0;wdt_disable();}
 | 
						|
void disable_watchdog(void) __attribute__((naked)) __attribute__((section(".init3")));
 | 
						|
void disable_watchdog(void)
 | 
						|
{
 | 
						|
#if defined(MCUCSR)
 | 
						|
	MCUCSR = ~(_BV(WDRF));	//Some MCUs require the watchdog reset flag to be cleared before WDT can be disabled. & operation is skipped to spare few bytes as bits in MCUSR can only be cleared.
 | 
						|
#else
 | 
						|
	MCUSR = ~(_BV(WDRF));	//Some MCUs require the watchdog reset flag to be cleared before WDT can be disabled. & operation is skipped to spare few bytes as bits in MCUSR can only be cleared.
 | 
						|
#endif
 | 
						|
	wdt_disable();	//immediately disable watchdog in case it was running in the application to avoid perpetual reset loop
 | 
						|
}
 | 
						|
 | 
						|
#if BOOT_ADR > 0xFFFF
 | 
						|
  #define PGM_READ_BYTE(x) pgm_read_byte_far(x)
 | 
						|
#else
 | 
						|
  #define PGM_READ_BYTE(x) pgm_read_byte(x)
 | 
						|
#endif
 | 
						|
 | 
						|
#if USE_UART
 | 
						|
  #include "uart/uart.h"
 | 
						|
#endif
 | 
						|
 | 
						|
#if USE_LED
 | 
						|
void init_leds();
 | 
						|
void led_power_on();
 | 
						|
void led_power_off();
 | 
						|
void led_power_toggle();
 | 
						|
void led_write_on();
 | 
						|
void led_write_off();
 | 
						|
void led_write_toggle();
 | 
						|
#endif
 | 
						|
void flash_erase (DWORD);		/* Erase a flash page (asmfunc.S) */
 | 
						|
void flash_write (DWORD, const BYTE*);	/* Program a flash page (asmfunc.S) */
 | 
						|
 | 
						|
 | 
						|
FATFS Fatfs;					// Petit-FatFs work area 
 | 
						|
BYTE Buff[SPM_PAGESIZE];	        	// Page data buffer 
 | 
						|
 | 
						|
 | 
						|
 | 
						|
static uint8_t pagecmp(const DWORD fa, uint8_t buff[SPM_PAGESIZE])
 | 
						|
{
 | 
						|
	UINT i;
 | 
						|
	uint8_t b_flash,b_buff;
 | 
						|
	for (i = 0; i < SPM_PAGESIZE; i++) {
 | 
						|
                b_flash = PGM_READ_BYTE(fa+i);
 | 
						|
                b_buff = buff[i];
 | 
						|
		if ( b_flash != b_buff) {
 | 
						|
			#if USE_UART  //output first difference
 | 
						|
			  UART_puthex32(fa);UART_puts(PSTR(":"));
 | 
						|
			  UART_puthex(b_flash);UART_puts(PSTR(" "));
 | 
						|
			  UART_puthex(b_buff); UART_newline();
 | 
						|
			#endif
 | 
						|
			return 1;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	#if USE_UART  //output first difference
 | 
						|
		UART_puthex32(fa);UART_puts(PSTR(":"));
 | 
						|
		UART_puts(PSTR("="));UART_newline();
 | 
						|
	#endif
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void doFlash() {
 | 
						|
	DWORD fa;	/* Flash address */
 | 
						|
	UINT br;	/* Bytes read */
 | 
						|
        #if USE_LED
 | 
						|
	  uint8_t i;
 | 
						|
          for(i=0;i<50;i++) { led_write_toggle();_delay_ms(100);} //Start Programming: Flash WRITE Wildly for 5 secs
 | 
						|
        #endif
 | 
						|
 | 
						|
 | 
						|
	for (fa = 0; fa < BOOT_ADR; fa += SPM_PAGESIZE) {	/* Update all application pages */
 | 
						|
 | 
						|
		memset(Buff, 0xFF, SPM_PAGESIZE);		/* Clear buffer */
 | 
						|
		pf_read(Buff, SPM_PAGESIZE, &br);		/* Load a page data */
 | 
						|
							
 | 
						|
		if (pagecmp(fa, Buff)) {		/* Only flash if page is changed */
 | 
						|
			#if USE_LED
 | 
						|
			  led_write_off();
 | 
						|
			  //led_power_on();
 | 
						|
			#endif
 | 
						|
			flash_erase(fa);		/* Erase a page */
 | 
						|
			flash_write(fa, Buff);		/* Write it if the data is available */	
 | 
						|
			
 | 
						|
		} else {
 | 
						|
 | 
						|
		#if USE_LED
 | 
						|
		  //led_power_off();
 | 
						|
		  led_write_on();
 | 
						|
		#endif
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void checkFile() {
 | 
						|
        uint8_t fresult;
 | 
						|
 | 
						|
	fresult = pf_mount(&Fatfs);	/* Initialize file system */
 | 
						|
 | 
						|
	if (fresult != FR_OK) { /* File System could not be mounted */
 | 
						|
	  #if USE_UART
 | 
						|
		UART_puts(PSTR("File not mounted"));
 | 
						|
		UART_newline();
 | 
						|
	  #endif
 | 
						|
		
 | 
						|
          #if USE_LED
 | 
						|
	    //uint8_t i;
 | 
						|
            //led_write_on();
 | 
						|
            //for(i=0;i<2*fresult;i++) { led_power_toggle();_delay_ms(500);}//Give error number while Write led is on
 | 
						|
            //led_write_off();
 | 
						|
          #endif
 | 
						|
          return;
 | 
						|
	}
 | 
						|
/*
 | 
						|
	
 | 
						|
    WORD flashver = eeprom_read_word((const uint16_t *)E2END - 1);
 | 
						|
	if (flashver > 999) {
 | 
						|
		flashver = 0;
 | 
						|
	}
 | 
						|
	BYTE y, tmp;
 | 
						|
	WORD x;
 | 
						|
	BYTE found = 0;
 | 
						|
	
 | 
						|
	for (x = flashver+10; x > flashver; x--) {
 | 
						|
		y = x / 100;
 | 
						|
		filename[5] = y + 0x30;
 | 
						|
		tmp = x % 100;
 | 
						|
 | 
						|
		y = tmp / 10;
 | 
						|
		filename[6] = y + 0x30;
 | 
						|
		tmp = x % 10;
 | 
						|
 | 
						|
		filename[7] = tmp + 0x30;
 | 
						|
 | 
						|
		if (pf_open(filename) == FR_OK) { // File opens normally	
 | 
						|
			found = 1;
 | 
						|
			doProgram();
 | 
						|
		}
 | 
						|
		led_power_toggle();
 | 
						|
	}
 | 
						|
        
 | 
						|
	if (found == 0) {*/
 | 
						|
 | 
						|
        fresult = pf_open(filename);
 | 
						|
 | 
						|
	if (fresult != FR_OK) { /* File could not be opened */
 | 
						|
		
 | 
						|
	  #if USE_UART
 | 
						|
		UART_puts(PSTR("File not open"));
 | 
						|
		UART_newline();
 | 
						|
	  #endif
 | 
						|
          #if USE_LED
 | 
						|
	    //uint8_t i;
 | 
						|
            //led_power_on();
 | 
						|
            //for(i=0;i<2*fresult;i++) { led_write_toggle();_delay_ms(500);}//Give error number while Power led is on
 | 
						|
            //led_power_off();
 | 
						|
          #endif
 | 
						|
          return;
 | 
						|
	}
 | 
						|
 | 
						|
        doFlash();
 | 
						|
 | 
						|
	#if USE_LED
 | 
						|
          led_write_off();
 | 
						|
          //led_power_off();
 | 
						|
          _delay_ms(2000);
 | 
						|
	  uint8_t i;
 | 
						|
          //for(i=0;i<40;i++) { led_power_toggle();_delay_ms(50);}//SUCCESS FLASH WILDLY for 2 secs
 | 
						|
      	  for(i=0;i<40;i++) { led_write_toggle();_delay_ms(50);}//SUCCESS FLASH WILDLY for 2 secs
 | 
						|
        #endif
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
int main (void)
 | 
						|
{
 | 
						|
	#if USE_LED
 | 
						|
          init_leds();
 | 
						|
          uint8_t i=0;
 | 
						|
        #endif
 | 
						|
 | 
						|
	#if USE_UART
 | 
						|
	  UART_init();
 | 
						|
	  UART_puts(PSTR("AVR_BOOT"));
 | 
						|
          UART_newline();
 | 
						|
	#endif
 | 
						|
	while (1) {
 | 
						|
                #if USE_LED
 | 
						|
                  //led_power_on();_delay_ms(200);led_power_off();  //Test Power Led
 | 
						|
                  led_write_on();_delay_ms(200);led_write_off();  //Test Write Led
 | 
						|
		#endif
 | 
						|
 | 
						|
                checkFile();
 | 
						|
 | 
						|
		if (pgm_read_word(0) != 0xFFFF) ((void(*)(void))0)();	  //EXIT BOOTLOADER
 | 
						|
    
 | 
						|
		#if USE_UART
 | 
						|
			UART_puts(PSTR("retry"));
 | 
						|
			UART_newline();
 | 
						|
		#endif
 | 
						|
                #if USE_LED
 | 
						|
                  //for(i=0;i<10;i++) { led_power_toggle();_delay_ms(200);} //SOMETHING WENT WRONG: Flash Power LED
 | 
						|
            		for(i=0;i<10;i++) { led_write_toggle();_delay_ms(200);} //SOMETHING WENT WRONG: Flash Write LED
 | 
						|
		#endif
 | 
						|
	       _delay_ms(5000);              // Retry
 | 
						|
	}
 | 
						|
}
 |