|
- // 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 <avr/boot.h>
- #include <avr/pgmspace.h>
- #include <avr/delay.h>
-
- #include "avrlib/gpio.h"
- #include "avrlib/serial.h"
- #include "avrlib/watchdog_timer.h"
-
- #include "avr_audio_bootloader/crc32.h"
- #include "avr_audio_bootloader/fsk/decoder.h"
-
- #include "grids/hardware_config.h"
-
- using namespace avrlib;
- using namespace grids;
- using namespace avr_audio_bootloader;
-
- MidiInput midi;
- Inputs inputs;
- Leds leds;
- Decoder decoder;
-
- uint16_t page = 0;
- uint8_t rx_buffer[SPM_PAGESIZE + 4];
-
- void (*main_entry_point)(void) = 0x0000;
-
- inline void Init() {
- cli();
- leds.set_mode(DIGITAL_OUTPUT);
- inputs.set_mode(DIGITAL_INPUT);
- inputs.EnablePullUpResistors();
- }
-
- void WriteBufferToFlash() {
- uint16_t i;
- const uint8_t* p = rx_buffer;
- eeprom_busy_wait();
-
- boot_page_erase(page);
- boot_spm_busy_wait();
-
- for (i = 0; i < SPM_PAGESIZE; i += 2) {
- uint16_t w = *p++;
- w |= (*p++) << 8;
- boot_page_fill(page + i, w);
- }
-
- boot_page_write(page);
- boot_spm_busy_wait();
- boot_rww_enable();
- }
-
- void FlashLedsOk() {
- for (uint8_t i = 0; i < 8; ++i) {
- _delay_ms(50);
- leds.Write(0xf);
- _delay_ms(50);
- leds.Write(LED_CLOCK);
- }
- }
-
- void FlashLedsError() {
- for (uint8_t i = 0; i < 5; ++i) {
- _delay_ms(120);
- leds.Write(0xf);
- _delay_ms(120);
- leds.Write(0x0);
- }
- }
-
- // MIDI loader -----------------------------------------------------------------
-
- static const uint8_t sysex_header[] = {
- 0xf0, // <SysEx>
- 0x00, 0x21, 0x02, // Mutable instruments manufacturer id.
- 0x00, 0x09, // Product ID for Grids.
- };
-
- enum SysExReceptionState {
- MATCHING_HEADER = 0,
- READING_COMMAND = 1,
- READING_DATA = 2,
- };
-
- inline void LoaderLoop() {
- 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 status = 0;
- uint8_t page_byte = 0;
-
- midi.Init();
- decoder.Init();
- decoder.Sync();
- decoder.set_packet_destination(rx_buffer);
- page = 0;
-
- TCCR2A = 0;
- TCCR2B = 2;
-
- while (1) {
- leds.Write(LED_CLOCK | (LED_BD >> ((page_byte + 3) & 0x3)));
-
- // Sample the clock input at 20kHz and feed to the FSK decoder.
- if (TCNT2 >= (F_CPU / 8 / 40000 - 1)) {
- TCNT2 = 0;
-
- switch (decoder.PushSample(inputs.Read() & INPUT_CLOCK)) {
- case DECODER_STATE_ERROR_SYNC:
- FlashLedsError();
- decoder.Sync();
- break;
-
- case DECODER_STATE_END_OF_TRANSMISSION:
- return;
- break;
-
- case DECODER_STATE_PACKET_RECEIVED:
- {
- uint32_t crc = crc32(0, rx_buffer, SPM_PAGESIZE);
- uint32_t expected_crc = \
- (static_cast<uint32_t>(rx_buffer[SPM_PAGESIZE + 0]) << 24) | \
- (static_cast<uint32_t>(rx_buffer[SPM_PAGESIZE + 1]) << 16) | \
- (static_cast<uint32_t>(rx_buffer[SPM_PAGESIZE + 2]) << 8) | \
- (static_cast<uint32_t>(rx_buffer[SPM_PAGESIZE + 3]) << 0);
- if (crc == expected_crc) {
- WriteBufferToFlash();
- page += SPM_PAGESIZE;
- ++page_byte;
- } else {
- FlashLedsError();
- }
- }
- decoder.Sync();
- break;
-
- default:
- break;
- }
- }
-
- // Poll the MIDI input.
- if (midi.readable()) {
- byte = midi.ImmediateRead();
- 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;
- status = 0;
- 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 < SPM_PAGESIZE) {
- 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 == SPM_PAGESIZE + 1 &&
- sysex_commands[0] == 0x7e &&
- sysex_commands[1] == 0x00 &&
- rx_buffer[rx_buffer_index - 1] == checksum) {
- // Block write.
- WriteBufferToFlash();
- page += SPM_PAGESIZE;
- ++page_byte;
- } else {
- FlashLedsError();
- }
- state = MATCHING_HEADER;
- bytes_read = 0;
- }
- break;
- }
- }
- }
- }
-
- int main(void) {
- ResetWatchdog();
- Init();
- _delay_ms(40);
- if (!(inputs.Read() & INPUT_SW_RESET)) {
- FlashLedsOk();
- LoaderLoop();
- FlashLedsOk();
- FlashLedsOk();
- }
- main_entry_point();
- }
|