diff options
author | cpldcpu <cpldcpu@gmail.com> | 2013-11-04 00:22:17 +0100 |
---|---|---|
committer | cpldcpu <cpldcpu@gmail.com> | 2013-11-04 00:22:17 +0100 |
commit | f3456ea1cfc56ebfa1fcea33be93856c0a71df37 (patch) | |
tree | 40fdff10bbd1bb0229cbb27062790ea87a18c041 /firmware/main_old.c | |
parent | 5b7617bfb1763f14a9e1e8792447d75cd076d2fa (diff) | |
download | micronucleus-f3456ea1cfc56ebfa1fcea33be93856c0a71df37.tar.gz micronucleus-f3456ea1cfc56ebfa1fcea33be93856c0a71df37.tar.bz2 micronucleus-f3456ea1cfc56ebfa1fcea33be93856c0a71df37.zip |
Updated USB Driver, nanite, oscal
Diffstat (limited to 'firmware/main_old.c')
-rw-r--r-- | firmware/main_old.c | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/firmware/main_old.c b/firmware/main_old.c new file mode 100644 index 0000000..527923e --- /dev/null +++ b/firmware/main_old.c @@ -0,0 +1,487 @@ +/* Name: main.c + * Project: Micronucleus + * Author: Jenna Fox + * Creation Date: 2007-12-08 + * Tabsize: 4 + * Copyright: (c) 2012 Jenna Fox + * Portions Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH (USBaspLoader) + * Portions Copyright: (c) 2012 Louis Beaudoin (USBaspLoader-tiny85) + * License: GNU GPL v2 (see License.txt) + */ + +#define MICRONUCLEUS_VERSION_MAJOR 1 +#define MICRONUCLEUS_VERSION_MINOR 6 +// how many milliseconds should host wait till it sends another erase or write? +// needs to be above 4.5 (and a whole integer) as avr freezes for 4.5ms +#define MICRONUCLEUS_WRITE_SLEEP 8 + +// Use the old delay routines without NOP padding. This saves memory. +#define __DELAY_BACKWARD_COMPATIBLE__ + + +#include <avr/io.h> +#include <avr/interrupt.h> +#include <avr/pgmspace.h> +#include <avr/wdt.h> +#include <avr/boot.h> +//#include <avr/eeprom.h> +#include <util/delay.h> +//#include <string.h> + +static void leaveBootloader() __attribute__((__noreturn__)); + +#include "bootloaderconfig.h" +#include "usbdrv/usbdrv.c" + +/* ------------------------------------------------------------------------ */ + +#ifndef ulong +# define ulong unsigned long +#endif +#ifndef uint +# define uint unsigned int +#endif + +#ifndef BOOTLOADER_CAN_EXIT +# define BOOTLOADER_CAN_EXIT 0 +#endif + +/* allow compatibility with avrusbboot's bootloaderconfig.h: */ +#ifdef BOOTLOADER_INIT +# define bootLoaderInit() BOOTLOADER_INIT +# define bootLoaderExit() +#endif +#ifdef BOOTLOADER_CONDITION +# define bootLoaderCondition() BOOTLOADER_CONDITION +#endif + +/* device compatibility: */ +#ifndef GICR /* ATMega*8 don't have GICR, use MCUCR instead */ +# define GICR MCUCR +#endif + +/* ------------------------------------------------------------------------ */ + +#define addr_t uint + +// typedef union longConverter{ +// addr_t l; +// uint w[sizeof(addr_t)/2]; +// uchar b[sizeof(addr_t)]; +// } longConverter_t; + +//////// Stuff Bluebie Added +// postscript are the few bytes at the end of programmable memory which store tinyVectors +// and used to in USBaspLoader-tiny85 store the checksum iirc +#define POSTSCRIPT_SIZE 6 +#define PROGMEM_SIZE (BOOTLOADER_ADDRESS - POSTSCRIPT_SIZE) /* max size of user program */ + +// verify the bootloader address aligns with page size +#if BOOTLOADER_ADDRESS % SPM_PAGESIZE != 0 +# error "BOOTLOADER_ADDRESS in makefile must be a multiple of chip's pagesize" +#endif + +#ifdef AUTO_EXIT_MS +# if AUTO_EXIT_MS < (MICRONUCLEUS_WRITE_SLEEP * (BOOTLOADER_ADDRESS / SPM_PAGESIZE)) +# warning "AUTO_EXIT_MS is shorter than the time it takes to perform erase function - might affect reliability?" +# warning "Try increasing AUTO_EXIT_MS if you have stability problems" +# endif +#endif + +// events system schedules functions to run in the main loop +static uchar events = 0; // bitmap of events to run +#define EVENT_ERASE_APPLICATION 1 +#define EVENT_WRITE_PAGE 2 +#define EVENT_EXECUTE 4 + +// controls state of events +#define fireEvent(event) events |= (event) +#define isEvent(event) (events & (event)) +#define clearEvents() events = 0 + +// length of bytes to write in to flash memory in upcoming usbFunctionWrite calls +//static unsigned char writeLength; + +// becomes 1 when some programming happened +// lets leaveBootloader know if needs to finish up the programming +static uchar didWriteSomething = 0; + +uint16_t idlePolls = 0; // how long have we been idle? + +static uint16_t vectorTemp[2]; // remember data to create tinyVector table before BOOTLOADER_ADDRESS +static addr_t currentAddress; // current progmem address, used for erasing and writing + +#ifdef RESTORE_OSCCAL + static uint8_t osccal_default; // due to compiler insanity, having this as global actually saves memory +#endif + +/* ------------------------------------------------------------------------ */ +static inline void eraseApplication(void); +static void writeFlashPage(void); +static void writeWordToPageBuffer(uint16_t data); +static void fillFlashWithVectors(void); +static uchar usbFunctionSetup(uchar data[8]); +static uchar usbFunctionWrite(uchar *data, uchar length); +static inline void initForUsbConnectivity(void); +static inline void tiny85FlashInit(void); +static inline void tiny85FlashWrites(void); +//static inline void tiny85FinishWriting(void); +static inline void leaveBootloader(void); + +// erase any existing application and write in jumps for usb interrupt and reset to bootloader +// - Because flash can be erased once and programmed several times, we can write the bootloader +// - vectors in now, and write in the application stuff around them later. +// - if vectors weren't written back in immediately, usb would fail. +static inline void eraseApplication(void) { + // erase all pages until bootloader, in reverse order (so our vectors stay in place for as long as possible) + // while the vectors don't matter for usb comms as interrupts are disabled during erase, it's important + // to minimise the chance of leaving the device in a state where the bootloader wont run, if there's power failure + // during upload + addr_t ptr = BOOTLOADER_ADDRESS; + + cli(); + while (ptr) { + ptr -= SPM_PAGESIZE; + + boot_page_erase(ptr); + boot_spm_busy_wait(); + } + + currentAddress = 0; + fillFlashWithVectors(); + sei(); +} + +// simply write currently stored page in to already erased flash memory +static void writeFlashPage(void) { + uint8_t previous_sreg = SREG; // backup current interrupt setting + didWriteSomething = 1; + cli(); + boot_page_write(currentAddress - 2); + boot_spm_busy_wait(); // Wait until the memory is written. + SREG = previous_sreg; // restore interrupts to previous state +} + +// clear memory which stores data to be written by next writeFlashPage call +#define __boot_page_fill_clear() \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "sts %0, %1\n\t" \ + "spm\n\t" \ + : \ + : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ + "r" ((uint8_t)(__BOOT_PAGE_FILL | (1 << CTPB))) \ + ); \ +})) + +// write a word in to the page buffer, doing interrupt table modifications where they're required +static void writeWordToPageBuffer(uint16_t data) { + uint8_t previous_sreg; + + // first two interrupt vectors get replaced with a jump to the bootloader's vector table + if (currentAddress == (RESET_VECTOR_OFFSET * 2) || currentAddress == (USBPLUS_VECTOR_OFFSET * 2)) { + data = 0xC000 + (BOOTLOADER_ADDRESS/2) - 1; + } + + // at end of page just before bootloader, write in tinyVector table + // see http://embedded-creations.com/projects/attiny85-usb-bootloader-overview/avr-jtag-programmer/ + // for info on how the tiny vector table works + if (currentAddress == BOOTLOADER_ADDRESS - TINYVECTOR_RESET_OFFSET) { + data = vectorTemp[0] + ((FLASHEND + 1) - BOOTLOADER_ADDRESS)/2 + 2 + RESET_VECTOR_OFFSET; + } else if (currentAddress == BOOTLOADER_ADDRESS - TINYVECTOR_USBPLUS_OFFSET) { + data = vectorTemp[1] + ((FLASHEND + 1) - BOOTLOADER_ADDRESS)/2 + 1 + USBPLUS_VECTOR_OFFSET; +#ifndef RESTORE_OSCCAL + } else if (currentAddress == BOOTLOADER_ADDRESS - TINYVECTOR_OSCCAL_OFFSET) { + data = OSCCAL; +#endif + } + + + // clear page buffer as a precaution before filling the buffer on the first page + // in case the bootloader somehow ran after user program and there was something + // in the page buffer already + if (currentAddress == 0x0000) __boot_page_fill_clear(); + + previous_sreg = SREG; // backup previous interrupt settings + cli(); // ensure interrupts are disabled + boot_page_fill(currentAddress, data); + SREG = previous_sreg; // restore previous interrupt setting + + // only need to erase if there is data already in the page that doesn't match what we're programming + // TODO: what about this: if (pgm_read_word(currentAddress) & data != data) { ??? should work right? + //if (pgm_read_word(currentAddress) != data && pgm_read_word(currentAddress) != 0xFFFF) { + //if ((pgm_read_word(currentAddress) & data) != data) { + // fireEvent(EVENT_PAGE_NEEDS_ERASE); + //} + + // increment progmem address by one word + currentAddress += 2; +} + +// fills the rest of this page with vectors - interrupt vector or tinyvector tables where needed +static void fillFlashWithVectors(void) { + //int16_t i; + // + // fill all or remainder of page with 0xFFFF (as if unprogrammed) + //for (i = currentAddress % SPM_PAGESIZE; i < SPM_PAGESIZE; i += 2) { + // writeWordToPageBuffer(0xFFFF); // is where vector tables are sorted out + //} + + // TODO: Or more simply: + + +#if SPM_PAGESIZE<256 + do { + writeWordToPageBuffer(0xFFFF); + } while ((uchar)currentAddress % SPM_PAGESIZE); +#else + do { + writeWordToPageBuffer(0xFFFF); + } while (currentAddress % SPM_PAGESIZE); +#endif + + + writeFlashPage(); +} + +/* ------------------------------------------------------------------------ */ + +static uchar usbFunctionSetup(uchar data[8]) { + usbRequest_t *rq = (void *)data; + idlePolls = 0; // reset idle polls when we get usb traffic + + static uchar replyBuffer[4] = { + (((uint)PROGMEM_SIZE) >> 8) & 0xff, + ((uint)PROGMEM_SIZE) & 0xff, + SPM_PAGESIZE, + MICRONUCLEUS_WRITE_SLEEP + }; + + if (rq->bRequest == 0) { // get device info + usbMsgPtr = replyBuffer; + return 4; + + } else if (rq->bRequest == 1) { // write page + //writeLength = rq->wValue.word; + currentAddress = rq->wIndex.word; + + return USB_NO_MSG; // hands off work to usbFunctionWrite + + } else if (rq->bRequest == 2) { // erase application + fireEvent(EVENT_ERASE_APPLICATION); + + } else { // exit bootloader +# if BOOTLOADER_CAN_EXIT + fireEvent(EVENT_EXECUTE); +# endif + } + + return 0; +} + + +// read in a page over usb, and write it in to the flash write buffer +static uchar usbFunctionWrite(uchar *data, uchar length) { + //if (length > writeLength) length = writeLength; // test for missing final page bug + //writeLength -= length; + + do { + // remember vectors or the tinyvector table + if (currentAddress == RESET_VECTOR_OFFSET * 2) { + vectorTemp[0] = *(short *)data; + } + + if (currentAddress == USBPLUS_VECTOR_OFFSET * 2) { + vectorTemp[1] = *(short *)data; + } + + // make sure we don't write over the bootloader! + if (currentAddress >= BOOTLOADER_ADDRESS) { + //__boot_page_fill_clear(); + break; + } + + writeWordToPageBuffer(*(uint16_t *) data); + data += 2; // advance data pointer + length -= 2; + } while(length); + + // if we have now reached another page boundary, we're done + //uchar isLast = (writeLength == 0); + +#if SPM_PAGESIZE<256 + // Hack to reduce code size + uchar isLast = ((((uchar)currentAddress) % SPM_PAGESIZE) == 0); +#else + uchar isLast = ((currentAddress % SPM_PAGESIZE) == 0); +#endif + + // definitely need this if! seems usbFunctionWrite gets called again in future usbPoll's in the runloop! + if (isLast) fireEvent(EVENT_WRITE_PAGE); // ask runloop to write our page + + return isLast; // let vusb know we're done with this request +} + +/* ------------------------------------------------------------------------ */ + +void PushMagicWord (void) __attribute__ ((naked)) __attribute__ ((section (".init3"))); + +// put the word "B007" at the bottom of the stack (RAMEND - RAMEND-1) +void PushMagicWord (void) { + asm volatile("ldi r16, 0xB0"::); + asm volatile("push r16"::); + asm volatile("ldi r16, 0x07"::); + asm volatile("push r16"::); +} + +/* ------------------------------------------------------------------------ */ + +static inline void initForUsbConnectivity(void) { + usbInit(); + /* enforce USB re-enumerate: */ + usbDeviceDisconnect(); /* do this while interrupts are disabled */ + _delay_ms(500); + usbDeviceConnect(); + sei(); +} + +static inline void tiny85FlashInit(void) { + // check for erased first page (no bootloader interrupt vectors), add vectors if missing + // this needs to happen for usb communication to work later - essential to first run after bootloader + // being installed + + if(pgm_read_byte(RESET_VECTOR_OFFSET * 2+1) == 0xff) fillFlashWithVectors(); // write vectors if flash is empty + + // TODO: necessary to reset currentAddress? + currentAddress = 0; +} + +static inline void tiny85FlashWrites(void) { + _delay_us(2000); // TODO: why is this here? - it just adds pointless two level deep loops seems like? + // write page to flash, interrupts will be disabled for > 4.5ms including erase + + // TODO: Do we need this? Wouldn't the programmer always send full sized pages? + +#if SPM_PAGESIZE<256 + // Hack to reduce code size + if ((uchar)currentAddress % SPM_PAGESIZE) +#else + if (currentAddress % SPM_PAGESIZE) +#endif + { + // when we aren't perfectly aligned to a flash page boundary + fillFlashWithVectors(); // fill up the rest of the page with 0xFFFF (unprogrammed) bits + } else { + writeFlashPage(); // otherwise just write it + } +} + +// finishes up writing to the flash, including adding the tinyVector tables at the end of memory +// TODO: can this be simplified? EG: currentAddress = PROGMEM_SIZE; fillFlashWithVectors(); +// static inline void tiny85FinishWriting(void) { +// // make sure remainder of flash is erased and write checksum and application reset vectors +// if (didWriteSomething) { +// while (currentAddress < BOOTLOADER_ADDRESS) { +// fillFlashWithVectors(); +// } +// } +// } + +// reset system to a normal state and launch user program +static inline void leaveBootloader(void) { + _delay_ms(10); // removing delay causes USB errors + + //DBG1(0x01, 0, 0); + bootLoaderExit(); + cli(); + usbDeviceDisconnect(); /* do this while interrupts are disabled */ + + USB_INTR_ENABLE = 0; + USB_INTR_CFG = 0; /* also reset config bits */ + + // clear magic word from bottom of stack before jumping to the app + *(uint8_t*)(RAMEND) = 0x00; // A single write is sufficient to invalidate magic word + // *(uint8_t*)(RAMEND-1) = 0x00; + +#ifndef RESTORE_OSCCAL + // adjust clock to previous calibration value, so user program always starts with same calibration + // as when it was uploaded originally + // TODO: Test this and find out, do we need the +1 offset? + unsigned char stored_osc_calibration = pgm_read_byte(BOOTLOADER_ADDRESS - TINYVECTOR_OSCCAL_OFFSET); + if (stored_osc_calibration != 0xFF && stored_osc_calibration != 0x00) { + //OSCCAL = stored_osc_calibration; // this should really be a gradual change, but maybe it's alright anyway? + // do the gradual change - failed to score extra free bytes anyway in 1.06 + + while (OSCCAL > stored_osc_calibration) OSCCAL--; + while (OSCCAL < stored_osc_calibration) OSCCAL++; + } +#endif + // jump to application reset vector at end of flash + asm volatile ("rjmp __vectors - 4"); +} + +int main(void) { + /* initialize */ + #ifdef RESTORE_OSCCAL + osccal_default = OSCCAL; + #endif + #if (!SET_CLOCK_PRESCALER) && LOW_POWER_MODE + uint8_t prescaler_default = CLKPR; + #endif + + MCUSR=0; + wdt_disable(); /* main app may have enabled watchdog */ + tiny85FlashInit(); + bootLoaderInit(); + + + if (bootLoaderStartCondition()) { + #if LOW_POWER_MODE + // turn off clock prescalling - chip must run at full speed for usb + // if you might run chip at lower voltages, detect that in bootLoaderStartCondition + CLKPR = 1 << CLKPCE; + CLKPR = 0; + #endif + + initForUsbConnectivity(); + do { + usbPoll(); + _delay_us(100); + + // these next two freeze the chip for ~ 4.5ms, breaking usb protocol + // and usually both of these will activate in the same loop, so host + // needs to wait > 9ms before next usb request + if (isEvent(EVENT_ERASE_APPLICATION)) eraseApplication(); + if (isEvent(EVENT_WRITE_PAGE)) tiny85FlashWrites(); + +# if BOOTLOADER_CAN_EXIT + if (isEvent(EVENT_EXECUTE)) { // when host requests device run uploaded program + break; + } +# endif + + clearEvents(); + + } while(bootLoaderCondition()); /* main event loop runs so long as bootLoaderCondition remains truthy */ + } + + // set clock prescaler to desired clock speed (changing from clkdiv8, or no division, depending on fuses) + #if LOW_POWER_MODE + #ifdef SET_CLOCK_PRESCALER + CLKPR = 1 << CLKPCE; + CLKPR = SET_CLOCK_PRESCALER; + #else + CLKPR = 1 << CLKPCE; + CLKPR = prescaler_default; + #endif + #endif + + // slowly bring down OSCCAL to it's original value before launching in to user program + #ifdef RESTORE_OSCCAL + while (OSCCAL > osccal_default) { OSCCAL -= 1; } + #endif + leaveBootloader(); +} + +/* ------------------------------------------------------------------------ */ |