Files

copied
Last update 6 years 3 months by Olivier Gillet
Filesedgesbootloader
..
bootloader.cc
makefile
bootloader.cc
// Copyright 2011 Olivier Gillet. // // 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, either version 3 of the License, or // (at your option) any later version. // 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, see <http://www.gnu.org/licenses/>. // // ----------------------------------------------------------------------------- // // Bootloader supporting MIDI SysEx update. // // Caveat: assumes the firmware flashing is always done from first to last // block, in increasing order. Random access flashing is not supported! #include "avrlibx/io/gpio.h" #include "avrlibx/io/usart.h" #include "avrlibx/system/init.h" #include "avrlibx/system/time.h" #include "avrlibx/third_party/sp_driver/sp_driver.h" #include "edges/hardware_config.h" using namespace avrlibx; using namespace edges; MidiIO midi; Leds leds; Switches switches; uint16_t page = 0; uint8_t rx_buffer[APP_SECTION_PAGE_SIZE + 1]; typedef void (*MainEntryPoint)(void) __attribute__ ((noreturn)); MainEntryPoint main_entry_point = (MainEntryPoint)0x0000; inline void Init() { SysInit(); leds.set_direction(OUTPUT); switches.set_direction(INPUT); switches.set_mode(PORT_MODE_PULL_UP); midi.Init(); } void WriteBufferToFlash() { SP_LoadFlashPage(rx_buffer); SP_EraseWriteApplicationPage(page); SP_WaitForSPM(); NVM_CMD = NVM_CMD_NO_OPERATION_gc; } void FlashLedsOk() { for (uint8_t i = 0; i < 8; ++i) { leds.Write(0xf); ConstantDelay(50); leds.Write(8); ConstantDelay(50); } } void FlashLedsError() { leds.Write(0); for (uint8_t i = 0; i < 5; ++i) { ConstantDelay(100); leds.Toggle(); } } static const uint8_t sysex_header[] = { 0xf0, // <SysEx> 0x00, 0x21, 0x02, // Mutable instruments manufacturer id. 0x00, 0x0a, // Product ID for "any product". }; enum SysExReceptionState { MATCHING_HEADER = 0, READING_COMMAND = 1, READING_DATA = 2, }; inline void MidiLoop() { uint8_t byte; uint16_t bytes_read = 0; uint16_t rx_buffer_index; uint8_t state = MATCHING_HEADER; uint8_t checksum; uint8_t sysex_commands[2]; uint8_t page_byte = 0; midi.Init(); page = 0; while (1) { leds.Write(8 | (4 >> ((page_byte + 3) & 0x3))); byte = midi.Read(); // In case we see a realtime message in the stream, safely ignore it. if (byte > 0xf0 && byte != 0xf7) { continue; } switch (state) { case MATCHING_HEADER: if (byte == sysex_header[bytes_read]) { ++bytes_read; if (bytes_read == sizeof(sysex_header)) { bytes_read = 0; state = READING_COMMAND; } } else { bytes_read = 0; } break; case READING_COMMAND: if (byte < 0x80) { sysex_commands[bytes_read++] = byte; if (bytes_read == 2) { bytes_read = 0; rx_buffer_index = 0; checksum = 0; state = READING_DATA; } } else { state = MATCHING_HEADER; bytes_read = 0; } break; case READING_DATA: if (byte < 0x80) { if (bytes_read & 1) { rx_buffer[rx_buffer_index] |= byte & 0xf; if (rx_buffer_index < APP_SECTION_PAGE_SIZE) { checksum += rx_buffer[rx_buffer_index]; } ++rx_buffer_index; } else { rx_buffer[rx_buffer_index] = (byte << 4); } ++bytes_read; } else if (byte == 0xf7) { if (sysex_commands[0] == 0x7f && sysex_commands[1] == 0x00 && bytes_read == 0) { // Reset. return; } else if (rx_buffer_index == APP_SECTION_PAGE_SIZE + 1 && sysex_commands[0] == 0x7e && sysex_commands[1] == 0x00 && rx_buffer[rx_buffer_index - 1] == checksum) { // Block write. WriteBufferToFlash(); page += APP_SECTION_PAGE_SIZE; ++page_byte; } else { FlashLedsError(); } state = MATCHING_HEADER; bytes_read = 0; } break; } } } int main(void) { Init(); ConstantDelay(25); if (!(switches.Read() & 1)) { FlashLedsOk(); MidiLoop(); FlashLedsOk(); } SP_LockSPM(); EIND = 0x00; main_entry_point(); }
Report a bug