summaryrefslogtreecommitdiff
path: root/bootloader/avr_code
diff options
context:
space:
mode:
authorbzztbomb <skinny@knowhere.net>2011-04-05 19:57:16 -0700
committerbzztbomb <skinny@knowhere.net>2011-04-05 19:57:16 -0700
commit73e16950d73578900d54f3470b57402a9e908c30 (patch)
tree4e24dd9b04bd432139c3ed61866459ac77e4024a /bootloader/avr_code
parent6827a350052baf3999cfa336411f5ec89bb9e769 (diff)
Public firmware release.
Diffstat (limited to 'bootloader/avr_code')
-rw-r--r--bootloader/avr_code/Makefile40
-rw-r--r--bootloader/avr_code/bootloadercfg.h59
-rw-r--r--bootloader/avr_code/main.c306
3 files changed, 405 insertions, 0 deletions
diff --git a/bootloader/avr_code/Makefile b/bootloader/avr_code/Makefile
new file mode 100644
index 0000000..dbc0699
--- /dev/null
+++ b/bootloader/avr_code/Makefile
@@ -0,0 +1,40 @@
+MCU=atmega328p
+BOOTLOADER_ADDRESS=0x7800
+
+CC=avr-gcc
+OBJCOPY=avr-objcopy
+# optimize for size:
+CFLAGS= -mmcu=$(MCU) -I. -I../ -I../midi/ -I../../Libraries/twi/ -Wall -Wstrict-prototypes -Os -fpack-struct -fshort-enums -funsigned-bitfields
+LDFLAGS += -Wl,-Ttext=$(BOOTLOADER_ADDRESS)
+
+SRC = main.c \
+ bootloadercfg.h \
+ ../../Libraries/twi/twi.c \
+ ../midi/bytequeue/interrupt_setting.c \
+ ../midi/bytequeue/bytequeue.c \
+
+OBJ = ${SRC:.c=.o}
+
+#-------------------
+all: main.hex
+#-------------------
+
+.c.o:
+ @echo CC $<
+ @$(CC) -c $(CFLAGS) -Os -o $*.o $<
+
+main.hex : main.out
+ $(OBJCOPY) -R .eeprom -O ihex main.out main.hex
+
+main.out : $(OBJ)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o main.out -Wl,-Map,main.map $(OBJ)
+
+program: main.hex
+ avrdude -pm328p -cavrispmkII -Pusb -e -Ulock:w:0x3F:m -Uefuse:w:0x00:m -Uhfuse:w:0xda:m -Ulfuse:w:0xff:m
+ avrdude -pm328p -cavrispmkII -Pusb -Uflash:w:main.hex:i -Ulock:w:0x0F:m
+
+#-------------------
+clean:
+ rm -f *.o *.map *.out *.hex
+ rm -rf ../../Libraries/*/*.o
+#-------------------
diff --git a/bootloader/avr_code/bootloadercfg.h b/bootloader/avr_code/bootloadercfg.h
new file mode 100644
index 0000000..c756366
--- /dev/null
+++ b/bootloader/avr_code/bootloadercfg.h
@@ -0,0 +1,59 @@
+/*
+ * A sysex bootloader for avr chips.
+ * Copyright 2010 Alex Norman
+ *
+ * This file is part of SysexBoot.
+ *
+ * SysexBoot 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.
+ *
+ * SysexBoot 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 SysexBoot. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BOOTLOADERCFG_H
+#define BOOTLOADERCFG_H
+
+/*
+
+//turn on port b bit zero as input
+//turn on pullups
+#define BOOTLOADER_INIT \
+ DDRB = 0xFE; \
+ PORTB = 0x01;
+
+//a zero tells us that the button is down
+#define BOOTLOADER_CONDITION (!(PINB & _BV(PINB0)))
+*/
+
+//turn on port b bit zero as input
+//turn on pullups
+#ifndef BOOTLOADER_INIT
+#define BOOTLOADER_INIT
+#endif
+
+//a zero tells us that the button is down
+#ifndef BOOTLOADER_CONDITION
+#define BOOTLOADER_CONDITION (!(PIND & _BV(PIND4)))
+#endif
+
+#ifndef INDICATE_BOOTLOADER_MODE
+#define INDICATE_BOOTLOADER_MODE \
+ PORTB = 170;
+#endif
+
+//could be reduced [probably] to decrease ram size, but needs to be at least 43
+//long in current implementation
+#ifndef MIDIIN_BUF_SIZE
+#define MIDIIN_BUF_SIZE 128
+#endif
+
+#endif
diff --git a/bootloader/avr_code/main.c b/bootloader/avr_code/main.c
new file mode 100644
index 0000000..c7c09c8
--- /dev/null
+++ b/bootloader/avr_code/main.c
@@ -0,0 +1,306 @@
+/*
+ * A sysex bootloader for avr chips.
+ * Copyright 2010 Alex Norman
+ *
+ * This file is part of SysexBoot.
+ *
+ * SysexBoot 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.
+ *
+ * SysexBoot 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 SysexBoot. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#define F_CPU 16000000UL
+#define IN_BUFSIZE 260
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/pgmspace.h>
+#include <avr/boot.h>
+#include <util/delay.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include "twi.h"
+#include "bytequeue/bytequeue.h"
+#include "bootloadercfg.h"
+#include "midibootcommon.h"
+
+#define SYSEX_BEGIN 0xF0
+#define SYSEX_END 0xF7
+
+#define MIDI_CIN_SYSEX_START 0x4
+#define MIDI_CIN_SYSEX_ENDS_1 0x5
+#define MIDI_CIN_SYSEX_ENDS_2 0x6
+#define MIDI_CIN_SYSEX_ENDS_3 0x7
+
+// From I2C master
+uint8_t midiInBuf[MIDIIN_BUF_SIZE];
+volatile byteQueue_t midiByteQueue;
+
+// To I2C master
+#define MIDIOUT_BUF_SIZE 64
+uint8_t midiOutBuf[MIDIOUT_BUF_SIZE];
+volatile byteQueue_t midiOutQueue;
+
+uint8_t onReceiveService(uint8_t inByte)
+{
+ if (bytequeue_enqueue((byteQueue_t*) &midiByteQueue, inByte))
+ {
+ if (bytequeue_length((byteQueue_t*) &midiByteQueue) < MIDIIN_BUF_SIZE - 1)
+ return 1;
+ }
+ return 0;
+}
+
+void onRequestService(void)
+{
+ uint8_t data[5];
+ uint8_t i;
+
+ // Packet length
+ data[0] = bytequeue_length((byteQueue_t*) &midiOutQueue);
+ if (data[0] > 3)
+ data[0] = 3;
+ // USB midi status byte
+ data[1] = MIDI_CIN_SYSEX_START;
+ for (i = 0; i < data[0]; i++)
+ {
+ data[i+2] = bytequeue_get((byteQueue_t*) &midiOutQueue, i);
+ // Search for SYSEX_END and set USB status byte if needed.
+ if (data[i+2] == SYSEX_END)
+ {
+ data[1] += (i+1);
+ }
+ }
+ // If we're not sending a full packet, it better have a sysex_end in it
+ if ((data[1] == MIDI_CIN_SYSEX_START) && (data[0] < 3))
+ {
+ data[0] = 0;
+ } else {
+ bytequeue_remove((byteQueue_t*) &midiOutQueue, data[0]);
+ data[0] = 4;
+ }
+
+ twi_transmit(data, data[0]+1);
+ if (bytequeue_length((byteQueue_t*) &midiOutQueue) == 0)
+ PORTC &= ~(_BV(1));
+}
+
+void (*jump_to_app)(void) = 0x0;
+
+void exit_bootloader(void){
+ cli();
+ boot_rww_enable();
+ MCUCR = _BV(IVCE);
+ MCUCR = 0;
+ jump_to_app();
+}
+
+void midi_send_byte(uint8_t b)
+{
+ bytequeue_enqueue((byteQueue_t*) &midiOutQueue, b);
+ PORTC |= _BV(1); // notify PIC we've got data
+}
+
+void midi_send_encoded_byte(uint8_t b)
+{
+ midi_send_byte(b >> 4);
+ midi_send_byte(b & 0xF);
+}
+
+void send_sysex_start(void) {
+ uint8_t i = 0;
+ midi_send_byte(SYSEX_BEGIN);
+ for(i = 0; i < MIDIBOOT_SYSEX_ID_LEN; i++)
+ midi_send_byte(midiboot_sysex_id[i]);
+}
+
+void send_sysex_end(void) {
+ midi_send_byte(SYSEX_END);
+}
+
+//The ack is just our sysex id in a sysex message, that is all
+void send_ack(void){
+ send_sysex_start();
+ send_sysex_end();
+}
+
+int main(void) {
+ uint8_t i, size, curByte;
+ bool cancelTimeout = false;
+ bool inSysexMode = false;
+ uint16_t inByteIndex = 0;
+ uint16_t pageAddress = 0;
+ uint16_t counterLow = 0;
+
+ //page size is 128, need 2 more bytes for page address
+ //128 + 2 == 130..
+ //we actually don't need so much space but, whatever
+ uint8_t tmpUnpackedData[IN_BUFSIZE / 2];
+ //once we pack it, it is 149 bytes
+ uint8_t tmpPackedData[IN_BUFSIZE];
+ midiboot_sysex_t sysexMode = MIDIBOOT_INVALID;
+
+ //set up the bootloader conditions
+ MCUCR = _BV(IVCE);
+ MCUCR = _BV(IVSEL);
+
+ // Everything INPUT except LED pins
+ DDRC = _BV(1) | _BV(2) | _BV(3);
+ // Enable pullups, set LED red
+ PORTC = 0xFB;
+
+ bytequeue_init((byteQueue_t *)&midiByteQueue, midiInBuf, MIDIIN_BUF_SIZE);
+ bytequeue_init((byteQueue_t*) &midiOutQueue, midiOutBuf, MIDIOUT_BUF_SIZE);
+
+ // Init I2C
+ twi_setAddress(4);
+ twi_attachSlaveTxEvent(onRequestService);
+ twi_attachSlaveRxEvent(onReceiveService);
+ twi_init();
+
+ sei();
+
+ //do main loop
+ while(true){
+ // Cheesy timeout method, but frugal on bytes
+ if ((++counterLow == 0) && (!cancelTimeout))
+ {
+ exit_bootloader();
+ }
+
+ //read data from the queue and deal with it
+ size = bytequeue_length((byteQueue_t *)&midiByteQueue);
+ //deal with input data
+ for(i = 0; i < size; i++){
+ curByte = bytequeue_get((byteQueue_t *)&midiByteQueue, i);
+
+ if(curByte == SYSEX_BEGIN){
+ inSysexMode = true;
+ inByteIndex = 0;
+ sysexMode = MIDIBOOT_INVALID;
+ } else if(curByte == SYSEX_END){
+ if(inSysexMode){
+ //see what mode we're in
+ switch(sysexMode){
+ case MIDIBOOT_LEAVE_BOOT:
+ exit_bootloader();
+ break;
+ case MIDIBOOT_GETPAGESIZE:
+ //if we've been sent the correct size packet then send back our data
+ if(inByteIndex == (1 + MIDIBOOT_SYSEX_ID_LEN)){
+ cancelTimeout = true;
+ //pack the contents of SPM_PAGESIZE into tmpPackedData
+ //SPM_PAGESIZE is 2 bytes wide
+ send_sysex_start();
+ midi_send_byte(MIDIBOOT_GETPAGESIZE);
+ midi_send_encoded_byte(SPM_PAGESIZE >> 8);
+ midi_send_encoded_byte(SPM_PAGESIZE & 0xFF);
+ send_sysex_end();
+ }
+ break;
+ //actually write the page that has been filled up through MIDIBOOT_FILLPAGE
+ case MIDIBOOT_WRITEPAGE:
+
+ cli();
+
+ //erase the page
+ eeprom_busy_wait ();
+ boot_page_erase (pageAddress);
+ boot_spm_busy_wait ();
+ //write the page
+ boot_page_write (pageAddress); // Store buffer in flash page.
+ boot_spm_busy_wait(); // Wait until the memory is written.
+
+ sei();
+
+ send_ack();
+ break;
+ //write data into the temporary page buffer [boot_page_fill]
+ case MIDIBOOT_FILLPAGE:
+ if(inByteIndex > (MIDIBOOT_SYSEX_ID_LEN + 1)) {
+ const uint16_t packedSize = inByteIndex - MIDIBOOT_SYSEX_ID_LEN - 1;
+ const uint16_t unpackedSize = packedSize / 2;
+ if(unpackedSize <= 64){
+ //the first two bytes are the address
+ uint16_t bytesToWrite = unpackedSize - 2;
+ uint16_t writeStartAddr;
+
+ //unpack our addr+data
+ uint16_t n;
+ for (n = 0; n < packedSize; n+=2)
+ {
+ tmpUnpackedData[n >> 1] = (tmpPackedData[n] << 4) | tmpPackedData[n+1];
+ }
+
+ //grab the start address
+ writeStartAddr = (tmpUnpackedData[0] << 8) | tmpUnpackedData[1];
+ //page address, just zero out the lower bits, used for WRITEPAGE
+ pageAddress = writeStartAddr & ~(SPM_PAGESIZE - 1);
+
+ cli();
+
+ //fill the temp page buffer
+ uint8_t * pageData = tmpUnpackedData + 2;
+ uint16_t j;
+ for(j = 0; j < bytesToWrite; j+=2){
+ uint16_t w = (((uint16_t)pageData[j + 1]) << 8) + pageData[j];
+ boot_page_fill (writeStartAddr + j, w);
+ }
+
+ sei();
+ //ack that we've done this so that we can get some more data
+ send_ack();
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ inSysexMode = false;
+ } else if(inSysexMode){
+ //make sure the sysex prefix matches ours
+ if((inByteIndex < MIDIBOOT_SYSEX_ID_LEN) && (curByte != midiboot_sysex_id[inByteIndex])){
+ inSysexMode = false;
+ } else {
+ //we're in sysex mode and we've matched the midiboot_sysex_id
+ //the next byte tells us what to do
+ if(inByteIndex == MIDIBOOT_SYSEX_ID_LEN){
+ if(curByte >= MIDIBOOT_LEAVE_BOOT && curByte <= MIDIBOOT_WRITEPAGE)
+ {
+ sysexMode = curByte;
+ }
+ else
+ inSysexMode = false;
+ //if we're filling a page, write it to the tmpPackedData, we'll unpack later
+ } else if (sysexMode == MIDIBOOT_FILLPAGE){
+ //the first MIDIBOOT_SYSEX_ID_LEN + 1 bytes have already been dealt with
+ uint16_t index = inByteIndex - MIDIBOOT_SYSEX_ID_LEN - 1;
+ if(index < IN_BUFSIZE) {
+ tmpPackedData[index] = curByte;
+ } else {
+ //XXX ERROR!!!
+ sysexMode = MIDIBOOT_INVALID;
+ inSysexMode = false;
+ }
+ }
+ //increment the index
+ inByteIndex++;
+ }
+ }
+ }
+
+ //advance the pointer
+ bytequeue_remove((byteQueue_t *)&midiByteQueue, size);
+ }
+
+ return 0;
+}