344 lines
7.8 KiB
C
344 lines
7.8 KiB
C
/*****************************************************************************
|
|
*
|
|
* Minimal Bootloader for Atmega644(p/a)
|
|
*
|
|
*
|
|
* Based on:
|
|
*
|
|
* AVRPROG compatible boot-loader
|
|
* Version : 0.85 (Dec. 2008)
|
|
* Compiler : avr-gcc 4.1.2 / avr-libc 1.4.6
|
|
* size : depends on features and startup ( minmal features < 512 words)
|
|
* by : Martin Thomas, Kaiserslautern, Germany
|
|
* eversmith@heizung-thomas.de
|
|
* Additional code and improvements contributed by:
|
|
* - Uwe Bonnes
|
|
* - Bjoern Riemer
|
|
* - Olaf Rempel
|
|
*
|
|
* License : Copyright (c) 2006-2008 M. Thomas, U. Bonnes, O. Rempel
|
|
* Free to use. You have to mention the copyright
|
|
* owners in source-code and documentation of derived
|
|
* work. No warranty! (Yes, you can insert the BSD
|
|
* license here)
|
|
*
|
|
******************************************************************************/
|
|
|
|
|
|
/* MCU frequency */
|
|
#ifndef F_CPU
|
|
#define F_CPU 8000000
|
|
#endif
|
|
|
|
/* UART Baudrate */
|
|
#define BAUDRATE 115200
|
|
|
|
/* use "Double Speed Operation" */
|
|
#define UART_DOUBLESPEED
|
|
|
|
/* use second UART on mega128 / can128 / mega162 / mega324p / mega644p */
|
|
//#define UART_USE_SECOND
|
|
|
|
/* Device-Type:
|
|
For AVRProg the BOOT-option is prefered
|
|
which is the "correct" value for a bootloader.
|
|
avrdude may only detect the part-code for ISP */
|
|
#define DEVTYPE DEVTYPE_BOOT
|
|
// #define DEVTYPE DEVTYPE_ISP
|
|
|
|
/*
|
|
* Pin "STARTPIN" on port "STARTPORT" in this port has to grounded
|
|
* (active low) to start the bootloader
|
|
*/
|
|
#define BLPORT PORTD
|
|
#define BLDDR DDRD
|
|
#define BLPIN PIND
|
|
#define BLPNUM PIND0
|
|
|
|
/*
|
|
* Watchdog-reset is issued at exit
|
|
* define the timeout-value here (see avr-libc manual)
|
|
*/
|
|
#define EXIT_WDT_TIME WDTO_250MS
|
|
|
|
void __vector_default(void) { ; }
|
|
|
|
/*
|
|
* define the following if the bootloader should not output
|
|
* itself at flash read (will fake an empty boot-section)
|
|
*/
|
|
#define READ_PROTECT_BOOTLOADER
|
|
|
|
|
|
#define VERSION_HIGH '0'
|
|
#define VERSION_LOW '8'
|
|
|
|
|
|
#ifdef UART_DOUBLESPEED
|
|
// #define UART_CALC_BAUDRATE(baudRate) (((F_CPU*10UL) / ((baudRate) *8UL) +5)/10 -1)
|
|
#define UART_CALC_BAUDRATE(baudRate) ((uint32_t)((F_CPU) + ((uint32_t)baudRate * 4UL)) / ((uint32_t)(baudRate) * 8UL) - 1)
|
|
#else
|
|
// #define UART_CALC_BAUDRATE(baudRate) (((F_CPU*10UL) / ((baudRate)*16UL) +5)/10 -1)
|
|
#define UART_CALC_BAUDRATE(baudRate) ((uint32_t)((F_CPU) + ((uint32_t)baudRate * 8UL)) / ((uint32_t)(baudRate) * 16UL) - 1)
|
|
#endif
|
|
|
|
|
|
#include <stdint.h>
|
|
#include <avr/io.h>
|
|
#include <avr/wdt.h>
|
|
#include <avr/boot.h>
|
|
#include <avr/pgmspace.h>
|
|
//#include <avr/interrupt.h>
|
|
//#include <util/delay.h>
|
|
|
|
#include "chipdef.h"
|
|
|
|
uint8_t gBuffer[SPM_PAGESIZE];
|
|
|
|
static void sendchar(uint8_t data)
|
|
{
|
|
while (!(UART_STATUS & (1<<UART_TXREADY)));
|
|
UART_DATA = data;
|
|
}
|
|
|
|
static uint8_t recvchar(void)
|
|
{
|
|
while (!(UART_STATUS & (1<<UART_RXREADY)));
|
|
return UART_DATA;
|
|
}
|
|
|
|
static inline void eraseFlash(void)
|
|
{
|
|
// erase only main section (bootloader protection)
|
|
uint32_t addr = 0;
|
|
while (APP_END > addr) {
|
|
boot_page_erase(addr); // Perform page erase
|
|
boot_spm_busy_wait(); // Wait until the memory is erased.
|
|
addr += SPM_PAGESIZE;
|
|
}
|
|
boot_rww_enable();
|
|
}
|
|
|
|
static inline void recvBuffer(pagebuf_t size)
|
|
{
|
|
pagebuf_t cnt;
|
|
uint8_t *tmp = gBuffer;
|
|
|
|
for (cnt = 0; cnt < sizeof(gBuffer); cnt++) {
|
|
*tmp++ = (cnt < size) ? recvchar() : 0xFF;
|
|
}
|
|
}
|
|
|
|
static inline uint16_t writeFlashPage(uint16_t waddr, pagebuf_t size)
|
|
{
|
|
uint32_t pagestart = (uint32_t)waddr<<1;
|
|
uint32_t baddr = pagestart;
|
|
uint16_t data;
|
|
uint8_t *tmp = gBuffer;
|
|
|
|
do {
|
|
data = *tmp++;
|
|
data |= *tmp++ << 8;
|
|
|
|
if ( baddr < APP_END )
|
|
{
|
|
boot_page_fill(baddr, data); // call asm routine.
|
|
}
|
|
|
|
baddr += 2; // Select next word in memory
|
|
size -= 2; // Reduce number of bytes to write by two
|
|
} while (size); // Loop until all bytes written
|
|
|
|
boot_page_write(pagestart);
|
|
boot_spm_busy_wait();
|
|
boot_rww_enable(); // Re-enable the RWW section
|
|
|
|
return baddr>>1;
|
|
}
|
|
|
|
|
|
static inline uint16_t readFlashPage(uint16_t waddr, pagebuf_t size)
|
|
{
|
|
uint32_t baddr = (uint32_t)waddr<<1;
|
|
uint16_t data;
|
|
|
|
do {
|
|
#ifndef READ_PROTECT_BOOTLOADER
|
|
#warning "Bootloader not read-protected"
|
|
#if defined(RAMPZ)
|
|
data = pgm_read_word_far(baddr);
|
|
#else
|
|
data = pgm_read_word_near(baddr);
|
|
#endif
|
|
#else
|
|
// don't read bootloader
|
|
if ( baddr < APP_END ) {
|
|
#if defined(RAMPZ)
|
|
data = pgm_read_word_far(baddr);
|
|
#else
|
|
data = pgm_read_word_near(baddr);
|
|
#endif
|
|
}
|
|
else {
|
|
data = 0xFFFF; // fake empty
|
|
}
|
|
#endif
|
|
sendchar(data); // send LSB
|
|
sendchar((data >> 8)); // send MSB
|
|
baddr += 2; // Select next word in memory
|
|
size -= 2; // Subtract two bytes from number of bytes to read
|
|
} while (size); // Repeat until block has been read
|
|
|
|
return baddr>>1;
|
|
}
|
|
|
|
|
|
|
|
static void send_boot(void)
|
|
{
|
|
sendchar('A');
|
|
sendchar('V');
|
|
sendchar('R');
|
|
sendchar('B');
|
|
sendchar('O');
|
|
sendchar('O');
|
|
sendchar('T');
|
|
}
|
|
|
|
static void (*jump_to_app)(void) = 0x0000;
|
|
|
|
int main(void)
|
|
{
|
|
|
|
bootloader_wdt_off();
|
|
|
|
uint16_t address = 0;
|
|
uint8_t device = 0, val;
|
|
|
|
BLDDR &= ~(1<<BLPNUM); // set as Input
|
|
BLPORT |= (1<<BLPNUM); // Enable pullup
|
|
|
|
|
|
UART_STATUS = ( 1<<UART_DOUBLE );
|
|
UART_BAUD_HIGH = 0;
|
|
UART_BAUD_LOW = 25;
|
|
|
|
UART_CTRL = UART_CTRL_DATA;
|
|
UART_CTRL2 = UART_CTRL2_DATA;
|
|
|
|
if (BLPIN & (1<<BLPNUM)) {
|
|
// jump to main app if pin is not grounded and GPIOR2 is zero
|
|
jump_to_app(); // Jump to application sector
|
|
}
|
|
|
|
|
|
for(;;) {
|
|
val = recvchar();
|
|
// Autoincrement?
|
|
if (val == 'a') {
|
|
sendchar('Y'); // Autoincrement is quicker
|
|
|
|
//write address
|
|
} else if (val == 'A') {
|
|
address = recvchar(); //read address 8 MSB
|
|
address = (address<<8) | recvchar();
|
|
sendchar('\r');
|
|
|
|
// Buffer load support
|
|
} else if (val == 'b') {
|
|
sendchar('Y'); // Report buffer load supported
|
|
sendchar((sizeof(gBuffer) >> 8) & 0xFF); // Report buffer size in bytes
|
|
sendchar(sizeof(gBuffer) & 0xFF);
|
|
|
|
// Start buffer load
|
|
} else if (val == 'B') {
|
|
pagebuf_t size;
|
|
size = recvchar() << 8; // Load high byte of buffersize
|
|
size |= recvchar(); // Load low byte of buffersize
|
|
val = recvchar(); // Load memory type ('E' or 'F')
|
|
recvBuffer(size);
|
|
|
|
if (device == DEVTYPE) {
|
|
if (val == 'F') {
|
|
address = writeFlashPage(address, size);
|
|
}
|
|
sendchar('\r');
|
|
} else {
|
|
sendchar(0);
|
|
}
|
|
|
|
// Block read
|
|
} else if (val == 'g') {
|
|
pagebuf_t size;
|
|
size = recvchar() << 8; // Load high byte of buffersize
|
|
size |= recvchar(); // Load low byte of buffersize
|
|
val = recvchar(); // Get memtype
|
|
|
|
if (val == 'F') {
|
|
address = readFlashPage(address, size);
|
|
}
|
|
|
|
// Chip erase
|
|
} else if (val == 'e') {
|
|
if (device == DEVTYPE) {
|
|
eraseFlash();
|
|
}
|
|
sendchar('\r');
|
|
|
|
// Exit upgrade
|
|
} else if (val == 'E') {
|
|
wdt_enable(EXIT_WDT_TIME); // Enable Watchdog Timer to give reset
|
|
sendchar('\r');
|
|
|
|
// Enter programming mode
|
|
} else if (val == 'P') {
|
|
sendchar('\r');
|
|
|
|
// Leave programming mode
|
|
} else if (val == 'L') {
|
|
sendchar('\r');
|
|
|
|
// return programmer type
|
|
} else if (val == 'p') {
|
|
sendchar('S'); // always serial programmer
|
|
|
|
// Return device type
|
|
} else if (val == 't') {
|
|
sendchar(DEVTYPE);
|
|
sendchar(0);
|
|
|
|
// clear and set LED ignored
|
|
} else if ((val == 'x') || (val == 'y')) {
|
|
recvchar();
|
|
sendchar('\r');
|
|
|
|
// set device
|
|
} else if (val == 'T') {
|
|
device = recvchar();
|
|
sendchar('\r');
|
|
|
|
// Return software identifier
|
|
} else if (val == 'S') {
|
|
send_boot();
|
|
|
|
// Return Software Version
|
|
} else if (val == 'V') {
|
|
sendchar(VERSION_HIGH);
|
|
sendchar(VERSION_LOW);
|
|
|
|
// Return Signature Bytes (it seems that
|
|
// AVRProg expects the "Atmel-byte" 0x1E last
|
|
// but shows it first in the dialog-window)
|
|
} else if (val == 's') {
|
|
sendchar(SIG_BYTE3);
|
|
sendchar(SIG_BYTE2);
|
|
sendchar(SIG_BYTE1);
|
|
|
|
/* ESC */
|
|
} else if(val != 0x1b) {
|
|
sendchar('?');
|
|
}
|
|
}
|
|
return 0;
|
|
}
|