/*************************************************************************** * Copyright (C) 11/2019 by Olaf Rempel * * 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 #include #include #include #define VERSION_STRING "TWIBOOT v2.1" #define EEPROM_SUPPORT 1 #define LED_SUPPORT 1 /* 25ms @8MHz */ #define TIMER_RELOAD (0xFF - 195) /* 40 * 25ms */ #define TIMEOUT 40 #if LED_SUPPORT #define LED_INIT() DDRB = ((1<> 8) & 0xFF, BOOTLOADER_START & 0xFF, #if (EEPROM_SUPPORT) ((E2END +1) >> 8 & 0xFF), (E2END +1) & 0xFF #else 0x00, 0x00 #endif }; /* wait 40 * 25ms = 1s */ static uint8_t boot_timeout = TIMEOUT; volatile static uint8_t cmd = CMD_WAIT; /* flash buffer */ static uint8_t buf[SPM_PAGESIZE]; static uint16_t addr; /* ************************************************************************* * write_flash_page * ************************************************************************* */ static void write_flash_page(void) { uint16_t pagestart = addr; uint8_t size = SPM_PAGESIZE; uint8_t *p = buf; if (pagestart >= BOOTLOADER_START) { 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(); } /* write_flash_page */ #if (EEPROM_SUPPORT) /* ************************************************************************* * read_eeprom_byte * ************************************************************************* */ static uint8_t read_eeprom_byte(void) { EEARL = addr; EEARH = (addr >> 8); EECR |= (1<> 8); EEDR = val; addr++; #if defined (__AVR_ATmega8__) EECR |= (1<= sizeof(buf) +3) { write_flash_page(); ack = 0x00; } break; #if (EEPROM_SUPPORT) case CMD_WRITE_EEPROM: write_eeprom_byte(data); break; #endif /* (EEPROM_SUPPORT) */ default: ack = 0x00; break; } break; } return ack; } /* TWI_data_write */ /* ************************************************************************* * TWI_data_read * ************************************************************************* */ static uint8_t TWI_data_read(uint8_t bcnt) { uint8_t data; switch (cmd) { case CMD_READ_VERSION: bcnt %= sizeof(info); data = info[bcnt]; break; case CMD_READ_CHIPINFO: bcnt %= sizeof(chipinfo); data = chipinfo[bcnt]; break; case CMD_READ_FLASH: data = pgm_read_byte_near(addr++); break; #if (EEPROM_SUPPORT) case CMD_READ_EEPROM: data = read_eeprom_byte(); break; #endif /* (EEPROM_SUPPORT) */ default: data = 0xFF; break; } return data; } /* TWI_data_read */ /* ************************************************************************* * TWI_vect * ************************************************************************* */ ISR(TWI_vect) { static uint8_t bcnt; uint8_t control = TWCR; switch (TWSR & 0xF8) { /* SLA+W received, ACK returned -> receive data and ACK */ case 0x60: bcnt = 0; LED_RT_ON(); break; /* prev. SLA+W, data received, ACK returned -> receive data and ACK */ case 0x80: if (TWI_data_write(bcnt++, TWDR) == 0x00) { control &= ~(1< send data */ case 0xA8: bcnt = 0; LED_RT_ON(); /* prev. SLA+R, data sent, ACK returned -> send data */ case 0xB8: TWDR = TWI_data_read(bcnt++); break; /* prev. SLA+W, data received, NACK returned -> IDLE */ case 0x88: /* STOP or repeated START -> IDLE */ case 0xA0: /* prev. SLA+R, data sent, NACK returned -> IDLE */ case 0xC0: LED_RT_OFF(); control |= (1< reset hardware */ default: control |= (1< 1) { boot_timeout--; } else if (boot_timeout == 1) { /* trigger app-boot */ cmd = CMD_BOOT_APPLICATION; } } /* TIMER0_OVF_vect */ static void (*jump_to_app)(void) __attribute__ ((noreturn)) = 0x0000; /* * For newer devices the watchdog timer remains active even after a * system reset. So disable it as soon as possible. * automagically called on startup */ #if defined (__AVR_ATmega88__) || defined (__AVR_ATmega168__) || \ defined (__AVR_ATmega328P__) /* ************************************************************************* * disable_wdt_timer * ************************************************************************* */ void disable_wdt_timer(void) __attribute__((naked, section(".init3"))); void disable_wdt_timer(void) { MCUSR = 0; WDTCSR = (1<