summaryrefslogtreecommitdiffstats
path: root/firmware
diff options
context:
space:
mode:
authorcpldcpu <cpldcpu@gmail.com>2014-01-05 19:35:47 +0100
committercpldcpu <cpldcpu@gmail.com>2014-01-05 19:35:47 +0100
commit568359df2c8ad4293dfc14b57db9b01fb2f1adab (patch)
tree5a783fd1979d2084c1d7caf912a8f53e05cdd72d /firmware
parent570ffd3e2f81f4ec03ed362c94b26573748864a1 (diff)
downloadmicronucleus-568359df2c8ad4293dfc14b57db9b01fb2f1adab.tar.gz
micronucleus-568359df2c8ad4293dfc14b57db9b01fb2f1adab.tar.bz2
micronucleus-568359df2c8ad4293dfc14b57db9b01fb2f1adab.zip
commandline: Support for new v2 transmission protocol
The block transfer is now done in the address and indexfield of a setup-packet to save a lot of memory in the firmware: This requires twice the number of transmissions, but is effectively faster due to less bus congestion and resends.
Diffstat (limited to 'firmware')
-rw-r--r--firmware/main.c226
-rw-r--r--firmware/usbconfig.h2
2 files changed, 107 insertions, 121 deletions
diff --git a/firmware/main.c b/firmware/main.c
index 513a956..246b2f9 100644
--- a/firmware/main.c
+++ b/firmware/main.c
@@ -52,6 +52,7 @@ enum {
cmd_device_info=0,
cmd_transfer_page=1,
cmd_erase_application=2,
+ cmd_write_data=3,
cmd_exit=4,
cmd_write_page=64, // internal commands start at 64
};
@@ -66,9 +67,21 @@ static inline void eraseApplication(void);
static void writeFlashPage(void);
static void writeWordToPageBuffer(uint16_t data);
static uint8_t usbFunctionSetup(uint8_t data[8]);
-static uint8_t usbFunctionWrite(uint8_t *data, uint8_t length);
static inline void leaveBootloader(void);
+// 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))) \
+ ); \
+}))
+
// 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.
@@ -86,34 +99,24 @@ static inline void eraseApplication(void) {
ptr -= SPM_PAGESIZE;
boot_page_erase(ptr);
}
+
+ // clear page buffer as a precaution before filling the buffer in case
+ // a previous write operation failed and there is still something in the buffer.
+ __boot_page_fill_clear();
- // Code size is increase if this stuff is removed?!?
- currentAddress.w = 0;
- for (i=0; i<8; i++) writeWordToPageBuffer(0xFFFF); // Write first 8 words to fill in vectors.
- writeFlashPage();
+ // Write reset vector into first page.
+ currentAddress.w = 0;
+ writeWordToPageBuffer(0xffff);
+ command=cmd_write_page;
}
// simply write currently stored page in to already erased flash memory
static inline void writeFlashPage(void) {
-
- boot_page_write(currentAddress.w - 2); // will halt CPU, no waiting required
-
+ if (currentAddress.w<=BOOTLOADER_ADDRESS)
+ boot_page_write(currentAddress.w - 2); // will halt CPU, no waiting required
}
-// 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) {
@@ -121,10 +124,10 @@ static void writeWordToPageBuffer(uint16_t data) {
// the device can not be bricked.
// Saving user-reset-vector is done in the host tool, starting with
// firmware V2
-
- if (currentAddress.w == RESET_VECTOR_OFFSET * 2) {
- data = 0xC000 + (BOOTLOADER_ADDRESS/2) - 1;
- }
+
+ if (currentAddress.w == RESET_VECTOR_OFFSET * 2) {
+ data = 0xC000 + (BOOTLOADER_ADDRESS/2) - 1;
+ }
#if (!OSCCAL_RESTORE) && OSCCAL_16_5MHz
if (currentAddress.w == BOOTLOADER_ADDRESS - TINYVECTOR_OSCCAL_OFFSET) {
@@ -134,58 +137,41 @@ static void writeWordToPageBuffer(uint16_t data) {
boot_page_fill(currentAddress.w, data);
- // increment progmem address by one word
currentAddress.w += 2;
}
// This function is never called, it is just here to suppress a compiler warning.
USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq) { return 0; }
-/* ------------------------------------------------------------------------ */
-static uint8_t usbFunctionSetup(uint8_t data[8]) {
- usbRequest_t *rq = (void *)data;
-
- static uint8_t replyBuffer[4] = {
+ PROGMEM const uint8_t replyBuffer[4] = {
(((uint16_t)PROGMEM_SIZE) >> 8) & 0xff,
((uint16_t)PROGMEM_SIZE) & 0xff,
SPM_PAGESIZE,
MICRONUCLEUS_WRITE_SLEEP
- };
+ };
+
+/* ------------------------------------------------------------------------ */
+static uint8_t usbFunctionSetup(uint8_t data[8]) {
+ usbRequest_t *rq = (void *)data;
+
idlePolls.b[1]=0; // reset idle polls when we get usb traffic
if (rq->bRequest == cmd_device_info) { // get device info
- usbMsgPtr = replyBuffer;
+ usbMsgPtr = (usbMsgPtr_t)replyBuffer;
return 4;
- } else if (rq->bRequest == cmd_transfer_page) { // transfer page
- // clear page buffer as a precaution before filling the buffer in case
- // a previous write operation failed and there is still something in the buffer.
- __boot_page_fill_clear();
- currentAddress.w = rq->wIndex.word;
- return USB_NO_MSG; // hands off work to usbFunctionWrite
+ } else if (rq->bRequest == cmd_transfer_page) { // initialize write page
+ currentAddress.w = rq->wIndex.word;
+ } else if (rq->bRequest == cmd_write_data) { // Write data
+ writeWordToPageBuffer(rq->wValue.word);
+ writeWordToPageBuffer(rq->wIndex.word);
+ if ((currentAddress.b[0] % SPM_PAGESIZE) == 0)
+ command=cmd_write_page; // ask runloop to write our page
} else {
// Handle cmd_erase_application and cmd_exit
command=rq->bRequest;
- return 0;
}
-}
-
-// read in a page over usb, and write it in to the flash write buffer
-static uint8_t usbFunctionWrite(uint8_t *data, uint8_t length) {
- do {
- // make sure we don't write over the bootloader!
- if (currentAddress.w >= BOOTLOADER_ADDRESS) 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
- uint8_t isLast = ((currentAddress.b[0] % SPM_PAGESIZE) == 0);
- if (isLast) command=cmd_write_page; // ask runloop to write our page
-
- return isLast; // let V-USB know we're done with this request
+ return 0;
}
static void initHardware (void)
@@ -235,13 +221,12 @@ static inline void leaveBootloader(void) {
}
#endif
- asm volatile ("rjmp __vectors - 2"); // jump to application reset vector at end of flash
+ asm volatile ("rjmp __vectors - 2"); // jump to application reset vector at end of flash
- for (;;); // Make sure function does not return to help compiler optimize
+ for (;;); // Make sure function does not return to help compiler optimize
}
void USB_INTR_VECTOR(void);
-
int main(void) {
uint8_t ackSent=0;
bootLoaderInit();
@@ -263,73 +248,74 @@ int main(void) {
USB_INTR_PENDING = 1<<USB_INTR_PENDING_BIT;
- while ( !(USB_INTR_PENDING & (1<<USB_INTR_PENDING_BIT)) );
- USB_INTR_VECTOR();
+ while ( !(USB_INTR_PENDING & (1<<USB_INTR_PENDING_BIT)) );
+ USB_INTR_VECTOR();
- command=cmd_local_nop;
- PORTB|=_BV(PB1);
- USB_INTR_PENDING = 1<<USB_INTR_PENDING_BIT;
- usbPoll();
-
- // Test whether another interrupt occured during the processing of USBpoll.
- // If yes, we missed a data packet on the bus. This is not a big issue, since
- // USB seems to allow timeout of up the two packets. (On my machine an USB
- // error is triggered after the third missed packet.)
- // The most critical situation occurs when a PID IN packet is missed due to
- // it's short length. Each packet + timeout takes around 45µs, meaning that
- // usbpoll must take less than 90µs or resyncing is not possible.
- // To avoid synchronizing of the interrupt routine, we must not call it while
- // a packet is transmitted. Therefore we have to wait until the bus is idle again.
- //
- // Just waiting for EOP (SE0) or no activity for 6 bus cycles is not enough,
- // as the host may have been sending a multi-packet transmission (eg. OUT or SETUP)
- // In that case we may resynch within a transmission, causing errors.
- //
- // A safer way is to wait until the bus was idle for the time it takes to send
- // an ACK packet by the client (10.5µs on D+) but not as long as bus
- // time out (12µs)
- //
- // TODO: Fix usb receiver to ignore DATA1/0 packets without preceding OUT or SETUP
-
- if (USB_INTR_PENDING & (1<<USB_INTR_PENDING_BIT)) // Usbpoll() intersected with data packet
- {
- PORTB|=_BV(PB0);
- uint8_t ctr;
-
- // loop takes 5 cycles
- asm volatile(
- " ldi %0,%1 \n\t"
- "loop%=: sbic %2,%3 \n\t"
- " ldi %0,%1 \n\t"
- " subi %0,1 \n\t"
- " brne loop%= \n\t"
- : "=&d" (ctr)
- : "M" ((uint8_t)(10.0f*(F_CPU/1.0e6f)/5.0f+0.5)), "I" (_SFR_IO_ADDR(USBIN)), "M" (USB_CFG_DPLUS_BIT)
- );
-
- PORTB&=~_BV(PB0);
- }
- PORTB&=~_BV(PB1);
+ command=cmd_local_nop;
+ PORTB|=_BV(PB1);
+ USB_INTR_PENDING = 1<<USB_INTR_PENDING_BIT;
+ usbPoll();
+
+ // Test whether another interrupt occured during the processing of USBpoll.
+ // If yes, we missed a data packet on the bus. This is not a big issue, since
+ // USB seems to allow timeout of up the two packets. (On my machine an USB
+ // error is triggered after the third missed packet.)
+ // The most critical situation occurs when a PID IN packet is missed due to
+ // it's short length. Each packet + timeout takes around 45µs, meaning that
+ // usbpoll must take less than 90µs or resyncing is not possible.
+ // To avoid synchronizing of the interrupt routine, we must not call it while
+ // a packet is transmitted. Therefore we have to wait until the bus is idle again.
+ //
+ // Just waiting for EOP (SE0) or no activity for 6 bus cycles is not enough,
+ // as the host may have been sending a multi-packet transmission (eg. OUT or SETUP)
+ // In that case we may resynch within a transmission, causing errors.
+ //
+ // A safer way is to wait until the bus was idle for the time it takes to send
+ // an ACK packet by the client (10.5µs on D+) but not as long as bus
+ // time out (12µs)
+ //
+ // TODO: Fix usb receiver to ignore DATA1/0 packets without preceding OUT or SETUP
+
+ if (USB_INTR_PENDING & (1<<USB_INTR_PENDING_BIT)) // Usbpoll() intersected with data packet
+ {
+ PORTB|=_BV(PB0);
+ uint8_t ctr;
+
+ // loop takes 5 cycles
+ asm volatile(
+ " ldi %0,%1 \n\t"
+ "loop%=: sbic %2,%3 \n\t"
+ " ldi %0,%1 \n\t"
+ " subi %0,1 \n\t"
+ " brne loop%= \n\t"
+ : "=&d" (ctr)
+ : "M" ((uint8_t)(10.0f*(F_CPU/1.0e6f)/5.0f+0.5)), "I" (_SFR_IO_ADDR(USBIN)), "M" (USB_CFG_DPLUS_BIT)
+ );
+
+ PORTB&=~_BV(PB0);
+ }
+ PORTB&=~_BV(PB1);
+
+ if (command == cmd_local_nop) continue;
+ /* if (!ackSent) {ackSent=1;continue;}
+ ackSent=0;*/
+
+ USB_INTR_PENDING = 1<<USB_INTR_PENDING_BIT;
+ while ( !(USB_INTR_PENDING & (1<<USB_INTR_PENDING_BIT)) );
+ USB_INTR_VECTOR();
- if (command == cmd_local_nop) continue;
-/* if (!ackSent) {ackSent=1;continue;}
- ackSent=0;*/
-
- USB_INTR_PENDING = 1<<USB_INTR_PENDING_BIT;
- while ( !(USB_INTR_PENDING & (1<<USB_INTR_PENDING_BIT)) );
- USB_INTR_VECTOR();
-
- idlePolls.w++;
-
- // Try to execute program if bootloader exit condition is met
- // if (AUTO_EXIT_MS&&(idlePolls.w==AUTO_EXIT_MS*10L)) command=cmd_exit;
+ idlePolls.w++;
+
+ // Try to execute program if bootloader exit condition is met
+ // if (AUTO_EXIT_MS&&(idlePolls.w==AUTO_EXIT_MS*10L)) command=cmd_exit;
LED_MACRO( idlePolls.b[1] );
if (command==cmd_erase_application)
eraseApplication();
- else if (command==cmd_write_page)
+ // Attention: eraseApplication will set command=cmd_write_page!
+ if (command==cmd_write_page)
writeFlashPage();
/* main event loop runs as long as no program is uploaded or existing program is not executed */
diff --git a/firmware/usbconfig.h b/firmware/usbconfig.h
index 3dda6b2..65864df 100644
--- a/firmware/usbconfig.h
+++ b/firmware/usbconfig.h
@@ -77,7 +77,7 @@
* The value is in milliamperes. [It will be divided by two since USB
* communicates power requirements in units of 2 mA.]
*/
-#define USB_CFG_IMPLEMENT_FN_WRITE 1
+#define USB_CFG_IMPLEMENT_FN_WRITE 0
/* Set this to 1 if you want usbFunctionWrite() to be called for control-out
* transfers. Set it to 0 if you don't need it and want to save a couple of
* bytes.