twiboot/main.c

454 lines
10 KiB
C
Raw Normal View History

2007-09-21 21:39:09 +02:00
/***************************************************************************
2010-08-15 16:44:54 +02:00
* Copyright (C) 08/2010 by Olaf Rempel *
2007-09-21 21:39:09 +02:00
* razzor@kopf-tisch.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; version 2 of the License, *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/boot.h>
#include <avr/pgmspace.h>
#define F_CPU 8000000
#include <util/delay.h>
2010-07-11 16:51:50 +02:00
/*
2010-08-15 16:44:54 +02:00
* atmega8:
2010-08-15 17:01:06 +02:00
* Fuse H: 0xda (512 words bootloader)
* Fuse L: 0x84 (8Mhz internal RC-Osz., 2.7V BOD)
2010-08-15 16:44:54 +02:00
*
2010-07-11 16:51:50 +02:00
* atmega88:
* Fuse E: 0xfa (512 words bootloader)
* Fuse H: 0xdd (2.7V BOD)
* Fuse L: 0xc2 (8Mhz internal RC-Osz.)
2010-08-15 16:44:54 +02:00
*
* atmega168:
* Fuse E: 0xfa (512 words bootloader)
* Fuse H: 0xdd (2.7V BOD)
* Fuse L: 0xc2 (8Mhz internal RC-Osz.)
2010-07-11 16:51:50 +02:00
*/
2010-08-15 16:44:54 +02:00
#if defined (__AVR_ATmega8__)
#define VERSION_STRING "TWIBOOT m8v2.0"
#define SIGNATURE_BYTES 0x1E, 0x93, 0x07
#elif defined (__AVR_ATmega88__)
#define VERSION_STRING "TWIBOOT m88v2.0"
#define SIGNATURE_BYTES 0x1E, 0x93, 0x0A
#elif defined (__AVR_ATmega168__)
#define VERSION_STRING "TWIBOOT m168v2.0"
#define SIGNATURE_BYTES 0x1E, 0x94, 0x06
#else
#error MCU not supported
#endif
2010-08-15 17:01:06 +02:00
#define EEPROM_SUPPORT 1
2010-07-11 16:51:50 +02:00
/* 25ms @8MHz */
#define TIMER_RELOAD (0xFF - 195)
/* 40 * 25ms */
#define TIMEOUT 40
2007-09-21 21:39:09 +02:00
#define LED_RT (1<<PORTB4)
#define LED_GN (1<<PORTB5)
#define TWI_ADDRESS 0x21
2010-07-11 16:51:50 +02:00
/* SLA+R */
2007-09-21 21:39:09 +02:00
#define CMD_WAIT 0x00
2010-07-11 16:51:50 +02:00
#define CMD_READ_VERSION 0x01
#define CMD_READ_MEMORY 0x02
/* internal mappings */
2010-08-15 16:44:54 +02:00
#define CMD_READ_CHIPINFO (0x10 | CMD_READ_MEMORY)
2010-07-11 16:51:50 +02:00
#define CMD_READ_FLASH (0x20 | CMD_READ_MEMORY)
#define CMD_READ_EEPROM (0x30 | CMD_READ_MEMORY)
#define CMD_READ_PARAMETERS (0x40 | CMD_READ_MEMORY) /* only in APP */
/* SLA+W */
#define CMD_SWITCH_APPLICATION CMD_READ_VERSION
#define CMD_WRITE_MEMORY CMD_READ_MEMORY
/* internal mappings */
#define CMD_BOOT_BOOTLOADER (0x10 | CMD_SWITCH_APPLICATION) /* only in APP */
#define CMD_BOOT_APPLICATION (0x20 | CMD_SWITCH_APPLICATION)
2010-08-15 16:44:54 +02:00
#define CMD_WRITE_CHIPINFO (0x10 | CMD_WRITE_MEMORY) /* invalid */
2010-07-11 16:51:50 +02:00
#define CMD_WRITE_FLASH (0x20 | CMD_WRITE_MEMORY)
#define CMD_WRITE_EEPROM (0x30 | CMD_WRITE_MEMORY)
#define CMD_WRITE_PARAMETERS (0x40 | CMD_WRITE_MEMORY) /* only in APP */
/* CMD_SWITCH_APPLICATION parameter */
#define BOOTTYPE_BOOTLOADER 0x00 /* only in APP */
#define BOOTTYPE_APPLICATION 0x80
/* CMD_{READ|WRITE}_* parameter */
2010-08-15 16:44:54 +02:00
#define MEMTYPE_CHIPINFO 0x00
2010-07-11 16:51:50 +02:00
#define MEMTYPE_FLASH 0x01
#define MEMTYPE_EEPROM 0x02
#define MEMTYPE_PARAMETERS 0x03 /* only in APP */
2007-09-21 21:39:09 +02:00
/*
2010-07-11 16:51:50 +02:00
* LED_GN blinks with 20Hz (while bootloader is running)
* LED_RT blinks on TWI activity
2008-02-17 23:04:52 +01:00
*
2007-09-21 21:39:09 +02:00
* bootloader twi-protocol:
2010-07-11 16:51:50 +02:00
* - abort boot timeout:
* SLA+W, 0x00, STO
2007-09-21 21:39:09 +02:00
*
2010-07-11 16:51:50 +02:00
* - show bootloader version
* SLA+W, 0x01, SLA+R, {16 bytes}, STO
2007-09-21 21:39:09 +02:00
*
2010-07-11 16:51:50 +02:00
* - start application
* SLA+W, 0x01, 0x80, STO
2007-09-21 21:39:09 +02:00
*
2010-08-15 16:44:54 +02:00
* - read chip info: 3byte signature, 1byte page size, 2byte flash size, 2byte eeprom size
2010-10-16 19:28:05 +02:00
* SLA+W, 0x02, 0x00, 0x00, 0x00, SLA+R, {4 bytes}, STO
2007-09-21 21:39:09 +02:00
*
2010-07-11 16:51:50 +02:00
* - read one (or more) flash bytes
* SLA+W, 0x02, 0x01, addrh, addrl, SLA+R, {* bytes}, STO
2007-09-21 21:39:09 +02:00
*
* - read one (or more) eeprom bytes
2010-07-11 16:51:50 +02:00
* SLA+W, 0x02, 0x02, addrh, addrl, SLA+R, {* bytes}, STO
*
* - write one flash page (64bytes on mega8)
* SLA+W, 0x02, 0x01, addrh, addrl, {64 bytes}, STO
2007-09-21 21:39:09 +02:00
*
2010-07-11 16:51:50 +02:00
* - write one (or more) eeprom bytes
* SLA+W, 0x02, 0x02, addrh, addrl, {* bytes}, STO
2007-09-21 21:39:09 +02:00
*/
2010-07-11 16:51:50 +02:00
const static uint8_t info[16] = VERSION_STRING;
2010-08-15 16:44:54 +02:00
const static uint8_t chipinfo[8] = {
SIGNATURE_BYTES,
SPM_PAGESIZE,
(APP_END >> 8) & 0xFF,
APP_END & 0xFF,
2010-08-15 17:01:06 +02:00
#if (EEPROM_SUPPORT)
2010-10-16 19:28:05 +02:00
((E2END +1) >> 8 & 0xFF),
(E2END +1) & 0xFF
2010-08-15 17:01:06 +02:00
#else
0x00, 0x00
#endif
2010-08-15 16:44:54 +02:00
};
2007-09-21 21:39:09 +02:00
/* wait 40 * 25ms = 1s */
2010-07-11 16:51:50 +02:00
static uint8_t boot_timeout = TIMEOUT;
2007-09-21 21:39:09 +02:00
volatile static uint8_t cmd = CMD_WAIT;
/* flash buffer */
static uint8_t buf[SPM_PAGESIZE];
static uint16_t addr;
2010-07-11 16:51:50 +02:00
static void write_flash_page(void)
2007-09-21 21:39:09 +02:00
{
uint16_t pagestart = addr;
uint8_t size = SPM_PAGESIZE;
uint8_t *p = buf;
if (pagestart >= APP_END)
return;
boot_page_erase(pagestart);
boot_spm_busy_wait();
do {
uint16_t data = *p++;
data |= *p++ << 8;
boot_page_fill(addr, data);
addr += 2;
size -= 2;
} while (size);
boot_page_write(pagestart);
boot_spm_busy_wait();
boot_rww_enable();
}
2010-08-15 17:01:06 +02:00
#if (EEPROM_SUPPORT)
2010-07-11 16:51:50 +02:00
static uint8_t read_eeprom_byte(void)
{
EEARL = addr;
EEARH = (addr >> 8);
EECR |= (1<<EERE);
addr++;
return EEDR;
}
static void write_eeprom_byte(uint8_t val)
2007-09-21 21:39:09 +02:00
{
EEARL = addr;
EEARH = (addr >> 8);
EEDR = val;
addr++;
2010-08-15 16:44:54 +02:00
#if defined (__AVR_ATmega8__)
EECR |= (1<<EEMWE);
EECR |= (1<<EEWE);
#elif defined (__AVR_ATmega88__) || defined (__AVR_ATmega168__)
2008-06-17 18:02:07 +02:00
EECR |= (1<<EEMPE);
EECR |= (1<<EEPE);
2010-08-15 16:44:54 +02:00
#endif
2007-09-21 21:39:09 +02:00
eeprom_busy_wait();
}
2010-08-15 17:01:06 +02:00
#endif /* EEPROM_SUPPORT */
2007-09-21 21:39:09 +02:00
ISR(TWI_vect)
{
2010-07-11 16:51:50 +02:00
static uint8_t bcnt;
uint8_t data;
uint8_t ack = (1<<TWEA);
2007-09-21 21:39:09 +02:00
switch (TWSR & 0xF8) {
/* SLA+W received, ACK returned -> receive data and ACK */
case 0x60:
bcnt = 0;
PORTB |= LED_RT;
TWCR |= (1<<TWINT) | (1<<TWEA);
break;
/* prev. SLA+W, data received, ACK returned -> receive data and ACK */
case 0x80:
2010-07-11 16:51:50 +02:00
data = TWDR;
switch (bcnt) {
case 0:
switch (data) {
case CMD_SWITCH_APPLICATION:
case CMD_WRITE_MEMORY:
bcnt++;
/* no break */
case CMD_WAIT:
2007-09-21 21:39:09 +02:00
/* abort countdown */
boot_timeout = 0;
break;
default:
2010-07-11 16:51:50 +02:00
/* boot app now */
2010-10-16 19:28:05 +02:00
cmd = CMD_BOOT_APPLICATION;
2010-07-11 16:51:50 +02:00
ack = (0<<TWEA);
2007-09-21 21:39:09 +02:00
break;
}
2010-07-11 16:51:50 +02:00
cmd = data;
break;
2007-09-21 21:39:09 +02:00
2010-07-11 16:51:50 +02:00
case 1:
switch (cmd) {
case CMD_SWITCH_APPLICATION:
if (data == BOOTTYPE_APPLICATION) {
cmd = CMD_BOOT_APPLICATION;
}
ack = (0<<TWEA);
break;
2007-09-21 21:39:09 +02:00
2010-07-11 16:51:50 +02:00
case CMD_WRITE_MEMORY:
2007-09-24 10:32:51 +02:00
bcnt++;
2010-08-15 16:44:54 +02:00
if (data == MEMTYPE_CHIPINFO) {
cmd = CMD_WRITE_CHIPINFO;
2007-09-24 10:32:51 +02:00
2010-07-11 16:51:50 +02:00
} else if (data == MEMTYPE_FLASH) {
cmd = CMD_WRITE_FLASH;
2010-08-15 17:01:06 +02:00
#if (EEPROM_SUPPORT)
2010-07-11 16:51:50 +02:00
} else if (data == MEMTYPE_EEPROM) {
cmd = CMD_WRITE_EEPROM;
2010-08-15 17:01:06 +02:00
#endif
2010-07-11 16:51:50 +02:00
} else {
ack = (0<<TWEA);
}
break;
2010-08-15 16:44:54 +02:00
default:
ack = (0<<TWEA);
break;
2007-09-21 21:39:09 +02:00
}
2010-07-11 16:51:50 +02:00
break;
2007-09-21 21:39:09 +02:00
2010-07-11 16:51:50 +02:00
case 2:
case 3:
addr <<= 8;
addr |= data;
2007-09-21 21:39:09 +02:00
bcnt++;
2010-07-11 16:51:50 +02:00
break;
default:
switch (cmd) {
case CMD_WRITE_FLASH:
buf[bcnt -4] = data;
if (bcnt < sizeof(buf) +3) {
bcnt++;
} else {
write_flash_page();
ack = (0<<TWEA);
}
break;
2010-08-15 17:01:06 +02:00
#if (EEPROM_SUPPORT)
2010-07-11 16:51:50 +02:00
case CMD_WRITE_EEPROM:
write_eeprom_byte(data);
bcnt++;
break;
2010-08-15 17:01:06 +02:00
#endif
2010-08-15 16:44:54 +02:00
default:
ack = (0<<TWEA);
break;
2010-07-11 16:51:50 +02:00
}
break;
2007-09-21 21:39:09 +02:00
}
2010-07-11 16:51:50 +02:00
if (ack == 0x00)
bcnt = 0;
TWCR |= (1<<TWINT) | ack;
2007-09-21 21:39:09 +02:00
break;
/* SLA+R received, ACK returned -> send data */
case 0xA8:
bcnt = 0;
PORTB |= LED_RT;
/* prev. SLA+R, data sent, ACK returned -> send data */
case 0xB8:
switch (cmd) {
2010-07-11 16:51:50 +02:00
case CMD_READ_VERSION:
data = info[bcnt++];
2007-09-21 21:39:09 +02:00
bcnt %= sizeof(info);
break;
2010-08-15 16:44:54 +02:00
case CMD_READ_CHIPINFO:
data = chipinfo[bcnt++];
bcnt %= sizeof(chipinfo);
2007-09-21 21:39:09 +02:00
break;
case CMD_READ_FLASH:
2010-07-11 16:51:50 +02:00
data = pgm_read_byte_near(addr++);
2007-09-21 21:39:09 +02:00
break;
2010-08-15 17:01:06 +02:00
#if (EEPROM_SUPPORT)
2007-09-21 21:39:09 +02:00
case CMD_READ_EEPROM:
2010-07-11 16:51:50 +02:00
data = read_eeprom_byte();
2007-09-21 21:39:09 +02:00
break;
2010-08-15 17:01:06 +02:00
#endif
2007-09-21 21:39:09 +02:00
default:
2010-07-11 16:51:50 +02:00
data = 0xFF;
2007-09-21 21:39:09 +02:00
break;
}
2010-07-11 16:51:50 +02:00
TWDR = data;
2007-09-21 21:39:09 +02:00
TWCR |= (1<<TWINT) | (1<<TWEA);
break;
/* STOP or repeated START */
case 0xA0:
/* data sent, NACK returned */
case 0xC0:
PORTB &= ~LED_RT;
TWCR |= (1<<TWINT) | (1<<TWEA);
break;
/* illegal state -> reset hardware */
case 0xF8:
TWCR |= (1<<TWINT) | (1<<TWSTO) | (1<<TWEA);
break;
}
}
ISR(TIMER0_OVF_vect)
{
2010-07-11 16:51:50 +02:00
/* restart timer */
TCNT0 = TIMER_RELOAD;
2007-09-21 21:39:09 +02:00
/* blink LED while running */
PORTB ^= LED_GN;
/* count down for app-boot */
if (boot_timeout > 1)
boot_timeout--;
/* trigger app-boot */
else if (boot_timeout == 1)
cmd = CMD_BOOT_APPLICATION;
}
2010-08-15 16:44:54 +02:00
static void (*jump_to_app)(void) __attribute__ ((noreturn)) = 0x0000;
2007-09-21 21:39:09 +02:00
/*
* For newer devices (mega88) the watchdog timer remains active even after a
* system reset. So disable it as soon as possible.
* automagically called on startup
*/
2010-08-15 16:44:54 +02:00
#if defined (__AVR_ATmega88__) || defined (__AVR_ATmega168__)
void disable_wdt_timer(void) __attribute__((naked, section(".init3")));
void disable_wdt_timer(void)
{
MCUSR = 0;
WDTCSR = (1<<WDCE) | (1<<WDE);
WDTCSR = (0<<WDE);
}
2010-08-15 16:44:54 +02:00
#endif
2010-08-15 16:44:54 +02:00
int main(void) __attribute__ ((noreturn));
2007-09-21 21:39:09 +02:00
int main(void)
{
DDRB = LED_GN | LED_RT;
PORTB = LED_GN;
2007-10-05 23:16:44 +02:00
/* move interrupt-vectors to bootloader */
2010-08-15 16:44:54 +02:00
/* timer0: running with F_CPU/1024, OVF interrupt */
#if defined (__AVR_ATmega8__)
2010-10-17 18:23:41 +02:00
GICR = (1<<IVCE);
GICR = (1<<IVSEL);
2010-08-15 16:44:54 +02:00
TCCR0 = (1<<CS02) | (1<<CS00);
TIMSK = (1<<TOIE0);
#elif defined (__AVR_ATmega88__) || defined (__AVR_ATmega168__)
2010-10-17 18:23:41 +02:00
MCUCR = (1<<IVCE);
MCUCR = (1<<IVSEL);
2008-06-17 18:02:07 +02:00
TCCR0B = (1<<CS02) | (1<<CS00);
TIMSK0 = (1<<TOIE0);
2010-08-15 16:44:54 +02:00
#endif
2007-09-21 21:39:09 +02:00
/* TWI init: set address, auto ACKs with interrupts */
TWAR = (TWI_ADDRESS<<1);
TWCR = (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
sei();
while (cmd != CMD_BOOT_APPLICATION);
cli();
/* Disable TWI but keep address! */
TWCR = 0x00;
/* disable timer0 */
2010-10-17 18:23:41 +02:00
/* move interrupt vectors back to application */
2010-08-15 16:44:54 +02:00
#if defined (__AVR_ATmega8__)
TCCR0 = 0x00;
TIMSK = 0x00;
2010-10-17 18:23:41 +02:00
GICR = (1<<IVCE);
GICR = (0<<IVSEL);
2010-08-15 16:44:54 +02:00
#elif defined (__AVR_ATmega88__) || defined (__AVR_ATmega168__)
2008-06-17 18:02:07 +02:00
TIMSK0 = 0x00;
TCCR0B = 0x00;
2007-09-21 21:39:09 +02:00
2008-06-17 18:02:07 +02:00
MCUCR = (1<<IVCE);
MCUCR = (0<<IVSEL);
2010-10-17 18:23:41 +02:00
#endif
2007-09-21 21:39:09 +02:00
PORTB = 0x00;
jump_to_app();
}