From 729a9f55fc815fbe2bcb64ca9f447755c2645779 Mon Sep 17 00:00:00 2001 From: etagle Date: Thu, 28 Dec 2017 03:32:46 -0300 Subject: [PATCH] Improved Sw SPI on DUE HAL a bit more. Now the USB MSD can transfer at 750k/s. Previously, it was 500k/s. I think this is the maximum achievable speed using Sw SPI. --- Marlin/src/HAL/HAL_DUE/HAL_spi_Due.cpp | 327 ++++++++++++++++++------ Marlin/src/HAL/HAL_DUE/usb/conf_usb.h | 55 ++-- Marlin/src/HAL/HAL_DUE/usb/usb_task.c | 341 ++++++++++++++++--------- Marlin/src/HAL/HAL_DUE/usb/usb_task.h | 14 +- 4 files changed, 513 insertions(+), 224 deletions(-) diff --git a/Marlin/src/HAL/HAL_DUE/HAL_spi_Due.cpp b/Marlin/src/HAL/HAL_DUE/HAL_spi_Due.cpp index 30386661b..95123615c 100644 --- a/Marlin/src/HAL/HAL_DUE/HAL_spi_Due.cpp +++ b/Marlin/src/HAL/HAL_DUE/HAL_spi_Due.cpp @@ -111,6 +111,9 @@ #define DELAY_NS(x) DELAY_CYCLES( (x) * (F_CPU/1000000) / 1000) typedef uint8_t (*pfnSpiTransfer) (uint8_t b); + typedef void (*pfnSpiRxBlock)(uint8_t* buf, uint32_t nbyte); + typedef void (*pfnSpiTxBlock)(const uint8_t* buf, uint32_t nbyte); + /* ---------------- Macros to be able to access definitions from asm */ @@ -183,26 +186,32 @@ /* Bit 0 */ " str %[mosi_mask],[%[mosi_port], %[idx],LSL #2]" "\n\t" /* Access the proper SODR or CODR registers based on that bit */ " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ - " nop" "\n\t" + " nop" "\n\t" /* Result will be 0 */ " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ - : [mosi_mask]"+r"( MOSI_MASK ), - [mosi_port]"+r"( MOSI_PORT_PLUS30 ), - [sck_mask]"+r"( SCK_MASK ), - [sck_port]"+r"( SCK_PORT_PLUS30 ), - [idx]"+r"( idx ), - [txval]"+r"( bout ) - : + : [idx]"+r"( idx ) + : [txval]"r"( bout ) , + [mosi_mask]"r"( MOSI_MASK ), + [mosi_port]"r"( MOSI_PORT_PLUS30 ), + [sck_mask]"r"( SCK_MASK ), + [sck_port]"r"( SCK_PORT_PLUS30 ) : "cc" ); return 0; } + // Calculates the bit band alias address and returns a pointer address to word. + // addr: The byte address of bitbanding bit. + // bit: The bit position of bitbanding bit. +#define BITBAND_ADDRESS(addr, bit) \ + (((uint32_t)(addr) & 0xF0000000) + 0x02000000 + ((uint32_t)(addr)&0xFFFFF)*32 + (bit)*4) + // run at ~8 .. ~10Mhz - Rx version (Tx line not altered) static uint8_t spiTransferRx0(uint8_t bout) { // using Mode 0 - int bin = 0, work = 0; - register uint32_t MISO_PORT_PLUS3C = ((uint32_t) PORT(MISO_PIN)) + 0x3C; /* PDSR of port */ + register uint32_t bin; + register uint32_t work; + register uint32_t BITBAND_MISO_PORT = BITBAND_ADDRESS( ((uint32_t)PORT(MISO_PIN))+0x3C, PIN_SHIFT(MISO_PIN)); /* PDSR of port in bitband area */ register uint32_t SCK_PORT_PLUS30 = ((uint32_t) PORT(SCK_PIN)) + 0x30; /* SODR of port */ register uint32_t SCK_MASK = PIN_MASK(SCK_PIN); UNUSED(bout); @@ -213,70 +222,61 @@ /* bit 7 */ " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ - " ldr %[work],[%[miso_port]]" "\n\t" /* PDSR */ + " ldr %[work],[%[bitband_miso_port]]" "\n\t" /* PDSR on bitband area for required bit: work will be 1 or 0 based on port */ " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ - " lsrs %[work],%[work],%[miso_shift]" "\n\t" /* Isolate input into carry */ - " adc %[bin],%[bin],%[bin]" "\n\t" /* Shift left result and add the carry */ + " bfi %[bin],%[work],#7,#1" "\n\t" /* Store read bit as the bit 7 */ /* bit 6 */ " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ - " ldr %[work],[%[miso_port]]" "\n\t" /* PDSR */ + " ldr %[work],[%[bitband_miso_port]]" "\n\t" /* PDSR on bitband area for required bit: work will be 1 or 0 based on port */ " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ - " lsrs %[work],%[work],%[miso_shift]" "\n\t" /* Isolate input into carry */ - " adc %[bin],%[bin],%[bin]" "\n\t" /* Shift left result and add the carry */ + " bfi %[bin],%[work],#6,#1" "\n\t" /* Store read bit as the bit 6 */ /* bit 5 */ " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ - " ldr %[work],[%[miso_port]]" "\n\t" /* PDSR */ + " ldr %[work],[%[bitband_miso_port]]" "\n\t" /* PDSR on bitband area for required bit: work will be 1 or 0 based on port */ " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ - " lsrs %[work],%[work],%[miso_shift]" "\n\t" /* Isolate input into carry */ - " adc %[bin],%[bin],%[bin]" "\n\t" /* Shift left result and add the carry */ + " bfi %[bin],%[work],#5,#1" "\n\t" /* Store read bit as the bit 5 */ /* bit 4 */ " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ - " ldr %[work],[%[miso_port]]" "\n\t" /* PDSR */ + " ldr %[work],[%[bitband_miso_port]]" "\n\t" /* PDSR on bitband area for required bit: work will be 1 or 0 based on port */ " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ - " lsrs %[work],%[work],%[miso_shift]" "\n\t" /* Isolate input into carry */ - " adc %[bin],%[bin],%[bin]" "\n\t" /* Shift left result and add the carry */ + " bfi %[bin],%[work],#4,#1" "\n\t" /* Store read bit as the bit 4 */ /* bit 3 */ " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ - " ldr %[work],[%[miso_port]]" "\n\t" /* PDSR */ + " ldr %[work],[%[bitband_miso_port]]" "\n\t" /* PDSR on bitband area for required bit: work will be 1 or 0 based on port */ " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ - " lsrs %[work],%[work],%[miso_shift]" "\n\t" /* Isolate input into carry */ - " adc %[bin],%[bin],%[bin]" "\n\t" /* Shift left result and add the carry */ + " bfi %[bin],%[work],#3,#1" "\n\t" /* Store read bit as the bit 3 */ /* bit 2 */ " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ - " ldr %[work],[%[miso_port]]" "\n\t" /* PDSR */ + " ldr %[work],[%[bitband_miso_port]]" "\n\t" /* PDSR on bitband area for required bit: work will be 1 or 0 based on port */ " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ - " lsrs %[work],%[work],%[miso_shift]" "\n\t" /* Isolate input into carry */ - " adc %[bin],%[bin],%[bin]" "\n\t" /* Shift left result and add the carry */ + " bfi %[bin],%[work],#2,#1" "\n\t" /* Store read bit as the bit 2 */ /* bit 1 */ " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ - " ldr %[work],[%[miso_port]]" "\n\t" /* PDSR */ + " ldr %[work],[%[bitband_miso_port]]" "\n\t" /* PDSR on bitband area for required bit: work will be 1 or 0 based on port */ " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ - " lsrs %[work],%[work],%[miso_shift]" "\n\t" /* Isolate input into carry */ - " adc %[bin],%[bin],%[bin]" "\n\t" /* Shift left result and add the carry */ + " bfi %[bin],%[work],#1,#1" "\n\t" /* Store read bit as the bit 1 */ /* bit 0 */ " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ - " ldr %[work],[%[miso_port]]" "\n\t" /* PDSR */ + " ldr %[work],[%[bitband_miso_port]]" "\n\t" /* PDSR on bitband area for required bit: work will be 1 or 0 based on port */ " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ - " lsrs %[work],%[work],%[miso_shift]" "\n\t" /* Isolate input into carry */ - " adc %[bin],%[bin],%[bin]" "\n\t" /* Shift left result and add the carry */ + " bfi %[bin],%[work],#0,#1" "\n\t" /* Store read bit as the bit 0 */ - : [miso_port]"+r"( MISO_PORT_PLUS3C ), - [sck_mask]"+r"( SCK_MASK ), - [sck_port]"+r"( SCK_PORT_PLUS30 ), - [bin]"+r"(bin), + : [bin]"+r"(bin), [work]"+r"(work) - : [miso_shift]"M"( PIN_SHIFT(MISO_PIN) + 1 ) /* So we move to the carry */ + : [bitband_miso_port]"r"( BITBAND_MISO_PORT ), + [sck_mask]"r"( SCK_MASK ), + [sck_port]"r"( SCK_PORT_PLUS30 ) : "cc" ); - return (uint8_t)bin; + return bin; } // run at ~4Mhz @@ -317,10 +317,182 @@ return b; } - // Pointers to generic functions + // Pointers to generic functions for byte transfers static pfnSpiTransfer spiTransferTx = spiTransferX; static pfnSpiTransfer spiTransferRx = spiTransferX; + // Block transfers run at ~8 .. ~10Mhz - Tx version (Rx data discarded) + static void spiTxBlock0(const uint8_t* ptr, uint32_t todo) { + register uint32_t MOSI_PORT_PLUS30 = ((uint32_t) PORT(MOSI_PIN)) + 0x30; /* SODR of port */ + register uint32_t MOSI_MASK = PIN_MASK(MOSI_PIN); + register uint32_t SCK_PORT_PLUS30 = ((uint32_t) PORT(SCK_PIN)) + 0x30; /* SODR of port */ + register uint32_t SCK_MASK = PIN_MASK(SCK_PIN); + register uint32_t work; + register uint32_t txval; + + /* The software SPI routine */ + __asm__ __volatile__( + ".syntax unified" "\n\t" // is to prevent CM0,CM1 non-unified syntax + + " loop%=:" "\n\t" + " ldrb.w %[txval], [%[ptr]], #1" "\n\t" /* Load value to send, increment buffer */ + " mvn %[txval],%[txval]" "\n\t" /* Negate value */ + + /* Bit 7 */ + " ubfx %[work],%[txval],#7,#1" "\n\t" /* Place bit 7 in bit 0 of work*/ + + " str %[mosi_mask],[%[mosi_port], %[work],LSL #2]" "\n\t" /* Access the proper SODR or CODR registers based on that bit */ + " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ + " ubfx %[work],%[txval],#6,#1" "\n\t" /* Place bit 6 in bit 0 of work*/ + " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ + + /* Bit 6 */ + " str %[mosi_mask],[%[mosi_port], %[work],LSL #2]" "\n\t" /* Access the proper SODR or CODR registers based on that bit */ + " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ + " ubfx %[work],%[txval],#5,#1" "\n\t" /* Place bit 5 in bit 0 of work*/ + " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ + + /* Bit 5 */ + " str %[mosi_mask],[%[mosi_port], %[work],LSL #2]" "\n\t" /* Access the proper SODR or CODR registers based on that bit */ + " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ + " ubfx %[work],%[txval],#4,#1" "\n\t" /* Place bit 4 in bit 0 of work*/ + " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ + + /* Bit 4 */ + " str %[mosi_mask],[%[mosi_port], %[work],LSL #2]" "\n\t" /* Access the proper SODR or CODR registers based on that bit */ + " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ + " ubfx %[work],%[txval],#3,#1" "\n\t" /* Place bit 3 in bit 0 of work*/ + " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ + + /* Bit 3 */ + " str %[mosi_mask],[%[mosi_port], %[work],LSL #2]" "\n\t" /* Access the proper SODR or CODR registers based on that bit */ + " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ + " ubfx %[work],%[txval],#2,#1" "\n\t" /* Place bit 2 in bit 0 of work*/ + " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ + + /* Bit 2 */ + " str %[mosi_mask],[%[mosi_port], %[work],LSL #2]" "\n\t" /* Access the proper SODR or CODR registers based on that bit */ + " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ + " ubfx %[work],%[txval],#1,#1" "\n\t" /* Place bit 1 in bit 0 of work*/ + " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ + + /* Bit 1 */ + " str %[mosi_mask],[%[mosi_port], %[work],LSL #2]" "\n\t" /* Access the proper SODR or CODR registers based on that bit */ + " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ + " ubfx %[work],%[txval],#0,#1" "\n\t" /* Place bit 0 in bit 0 of work*/ + " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ + + /* Bit 0 */ + " str %[mosi_mask],[%[mosi_port], %[work],LSL #2]" "\n\t" /* Access the proper SODR or CODR registers based on that bit */ + " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ + " subs %[todo],#1" "\n\t" /* Decrement count of pending words to send, update status */ + " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ + " bne.n loop%=" "\n\t" /* Repeat until done */ + + : [ptr]"+r" ( ptr ) , + [todo]"+r" ( todo ) , + [work]"+r"( work ) , + [txval]"+r"( txval ) + : [mosi_mask]"r"( MOSI_MASK ), + [mosi_port]"r"( MOSI_PORT_PLUS30 ), + [sck_mask]"r"( SCK_MASK ), + [sck_port]"r"( SCK_PORT_PLUS30 ) + : "cc" + ); + } + + static void spiRxBlock0(uint8_t* ptr, uint32_t todo) { + register uint32_t bin; + register uint32_t work; + register uint32_t BITBAND_MISO_PORT = BITBAND_ADDRESS( ((uint32_t)PORT(MISO_PIN))+0x3C, PIN_SHIFT(MISO_PIN)); /* PDSR of port in bitband area */ + register uint32_t SCK_PORT_PLUS30 = ((uint32_t) PORT(SCK_PIN)) + 0x30; /* SODR of port */ + register uint32_t SCK_MASK = PIN_MASK(SCK_PIN); + + /* The software SPI routine */ + __asm__ __volatile__( + ".syntax unified" "\n\t" // is to prevent CM0,CM1 non-unified syntax + + " loop%=:" "\n\t" + + /* bit 7 */ + " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ + " ldr %[work],[%[bitband_miso_port]]" "\n\t" /* PDSR on bitband area for required bit: work will be 1 or 0 based on port */ + " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ + " bfi %[bin],%[work],#7,#1" "\n\t" /* Store read bit as the bit 7 */ + + /* bit 6 */ + " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ + " ldr %[work],[%[bitband_miso_port]]" "\n\t" /* PDSR on bitband area for required bit: work will be 1 or 0 based on port */ + " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ + " bfi %[bin],%[work],#6,#1" "\n\t" /* Store read bit as the bit 6 */ + + /* bit 5 */ + " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ + " ldr %[work],[%[bitband_miso_port]]" "\n\t" /* PDSR on bitband area for required bit: work will be 1 or 0 based on port */ + " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ + " bfi %[bin],%[work],#5,#1" "\n\t" /* Store read bit as the bit 5 */ + + /* bit 4 */ + " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ + " ldr %[work],[%[bitband_miso_port]]" "\n\t" /* PDSR on bitband area for required bit: work will be 1 or 0 based on port */ + " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ + " bfi %[bin],%[work],#4,#1" "\n\t" /* Store read bit as the bit 4 */ + + /* bit 3 */ + " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ + " ldr %[work],[%[bitband_miso_port]]" "\n\t" /* PDSR on bitband area for required bit: work will be 1 or 0 based on port */ + " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ + " bfi %[bin],%[work],#3,#1" "\n\t" /* Store read bit as the bit 3 */ + + /* bit 2 */ + " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ + " ldr %[work],[%[bitband_miso_port]]" "\n\t" /* PDSR on bitband area for required bit: work will be 1 or 0 based on port */ + " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ + " bfi %[bin],%[work],#2,#1" "\n\t" /* Store read bit as the bit 2 */ + + /* bit 1 */ + " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ + " ldr %[work],[%[bitband_miso_port]]" "\n\t" /* PDSR on bitband area for required bit: work will be 1 or 0 based on port */ + " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ + " bfi %[bin],%[work],#1,#1" "\n\t" /* Store read bit as the bit 1 */ + + /* bit 0 */ + " str %[sck_mask],[%[sck_port]]" "\n\t" /* SODR */ + " ldr %[work],[%[bitband_miso_port]]" "\n\t" /* PDSR on bitband area for required bit: work will be 1 or 0 based on port */ + " str %[sck_mask],[%[sck_port],#0x4]" "\n\t" /* CODR */ + " bfi %[bin],%[work],#0,#1" "\n\t" /* Store read bit as the bit 0 */ + + " subs %[todo],#1" "\n\t" /* Decrement count of pending words to send, update status */ + " strb.w %[bin], [%[ptr]], #1" "\n\t" /* Store read value into buffer, increment buffer pointer */ + " bne.n loop%=" "\n\t" /* Repeat until done */ + + : [ptr]"+r"(ptr), + [todo]"+r"(todo), + [bin]"+r"(bin), + [work]"+r"(work) + : [bitband_miso_port]"r"( BITBAND_MISO_PORT ), + [sck_mask]"r"( SCK_MASK ), + [sck_port]"r"( SCK_PORT_PLUS30 ) + : "cc" + ); + } + + static void spiTxBlockX(const uint8_t* buf, uint32_t todo) { + do { + (void) spiTransferTx(*buf++); + } while (--todo); + } + + static void spiRxBlockX(uint8_t* buf, uint32_t todo) { + do { + *buf++ = spiTransferRx(0xff); + } while (--todo); + } + + // Pointers to generic functions for block tranfers + static pfnSpiTxBlock spiTxBlock = spiTxBlockX; + static pfnSpiRxBlock spiRxBlock = spiRxBlockX; + void spiBegin() { SET_OUTPUT(SS_PIN); WRITE(SS_PIN, HIGH); @@ -329,6 +501,38 @@ SET_OUTPUT(MOSI_PIN); } + uint8_t spiRec() { + WRITE(SS_PIN, LOW); + WRITE(MOSI_PIN, 1); /* Output 1s 1*/ + uint8_t b = spiTransferRx(0xFF); + WRITE(SS_PIN, HIGH); + return b; + } + + void spiRead(uint8_t* buf, uint16_t nbyte) { + uint32_t todo = nbyte; + if (todo == 0) return; + + WRITE(SS_PIN, LOW); + WRITE(MOSI_PIN, 1); /* Output 1s 1*/ + spiRxBlock(buf,nbyte); + WRITE(SS_PIN, HIGH); + } + + void spiSend(uint8_t b) { + WRITE(SS_PIN, LOW); + (void) spiTransferTx(b); + WRITE(SS_PIN, HIGH); + } + + void spiSendBlock(uint8_t token, const uint8_t* buf) { + + WRITE(SS_PIN, LOW); + (void) spiTransferTx(token); + spiTxBlock(buf,512); + WRITE(SS_PIN, HIGH); + } + /** * spiRate should be * 0 : 8 - 10 MHz @@ -344,15 +548,21 @@ case 0: spiTransferTx = spiTransferTx0; spiTransferRx = spiTransferRx0; + spiTxBlock = spiTxBlock0; + spiRxBlock = spiRxBlock0; break; case 1: spiTransferTx = spiTransfer1; spiTransferRx = spiTransfer1; + spiTxBlock = spiTxBlockX; + spiRxBlock = spiRxBlockX; break; default: spiDelayCyclesX4 = (F_CPU/1000000) >> (6 - spiRate); spiTransferTx = spiTransferX; spiTransferRx = spiTransferX; + spiTxBlock = spiTxBlockX; + spiRxBlock = spiRxBlockX; break; } @@ -361,41 +571,6 @@ WRITE(SCK_PIN, LOW); } - uint8_t spiRec() { - WRITE(SS_PIN, LOW); - WRITE(MOSI_PIN, 1); /* Output 1s 1*/ - uint8_t b = spiTransferRx(0xFF); - WRITE(SS_PIN, HIGH); - return b; - } - - void spiRead(uint8_t* buf, uint16_t nbyte) { - if (nbyte == 0) return; - WRITE(SS_PIN, LOW); - WRITE(MOSI_PIN, 1); /* Output 1s 1*/ - for (int i = 0; i < nbyte; i++) { - buf[i] = spiTransferRx(0xff); - } - WRITE(SS_PIN, HIGH); - } - - void spiSend(uint8_t b) { - WRITE(SS_PIN, LOW); - (void) spiTransferTx(b); - WRITE(SS_PIN, HIGH); - } - - void spiSendBlock(uint8_t token, const uint8_t* buf) { - - WRITE(SS_PIN, LOW); - (void) spiTransferTx(token); - - for (uint16_t i = 0; i < 512; i++) { - (void) spiTransferTx(buf[i]); - } - WRITE(SS_PIN, HIGH); - } - #pragma GCC reset_options #else diff --git a/Marlin/src/HAL/HAL_DUE/usb/conf_usb.h b/Marlin/src/HAL/HAL_DUE/usb/conf_usb.h index 8227fd6fc..4f780e009 100644 --- a/Marlin/src/HAL/HAL_DUE/usb/conf_usb.h +++ b/Marlin/src/HAL/HAL_DUE/usb/conf_usb.h @@ -47,8 +47,12 @@ #ifndef _CONF_USB_H_ #define _CONF_USB_H_ +#undef UNUSED /* To avoid a macro clash as macros.h already defines it */ +#include "../../../core/macros.h" /* For ENABLED()/DISABLED() */ +#include "../../../../Configuration.h" /* For CUSTOM_MACHINE_NAME definition - We just need the name, no C++ allowed! */ #include "compiler.h" + /** * USB Device Configuration * @{ @@ -61,15 +65,15 @@ #define USB_DEVICE_MINOR_VERSION 0 #define USB_DEVICE_POWER 100 // Consumption on Vbus line (mA) #define USB_DEVICE_ATTR \ - (USB_CONFIG_ATTR_SELF_POWERED) + (USB_CONFIG_ATTR_SELF_POWERED) // (USB_CONFIG_ATTR_BUS_POWERED) -// (USB_CONFIG_ATTR_REMOTE_WAKEUP|USB_CONFIG_ATTR_SELF_POWERED) -// (USB_CONFIG_ATTR_REMOTE_WAKEUP|USB_CONFIG_ATTR_BUS_POWERED) +// (USB_CONFIG_ATTR_REMOTE_WAKEUP|USB_CONFIG_ATTR_SELF_POWERED) +// (USB_CONFIG_ATTR_REMOTE_WAKEUP|USB_CONFIG_ATTR_BUS_POWERED) //! USB Device string definitions (Optional) -#define USB_DEVICE_MANUFACTURE_NAME "MARLIN 3D" -#define USB_DEVICE_PRODUCT_NAME "CDC and MSC" -#define USB_DEVICE_SERIAL_NAME "123985739853" // Disk SN for MSC +#define USB_DEVICE_MANUFACTURE_NAME "marlinfw.org" +#define USB_DEVICE_PRODUCT_NAME CUSTOM_MACHINE_NAME +#define USB_DEVICE_SERIAL_NAME "123985739853" /** * Device speeds support @@ -94,10 +98,11 @@ * @{ */ #define UDC_VBUS_EVENT(b_vbus_high) -#define UDC_SOF_EVENT() -#define UDC_SUSPEND_EVENT() -#define UDC_RESUME_EVENT() -#define UDC_GET_EXTRA_STRING() usb_task_extra_string() +#define UDC_SOF_EVENT() +#define UDC_SUSPEND_EVENT() +#define UDC_RESUME_EVENT() +#define UDC_GET_EXTRA_STRING() usb_task_extra_string() +#define USB_DEVICE_SPECIFIC_REQUEST() usb_task_other_requests() //@} /** @@ -246,30 +251,30 @@ */ //! USB Interfaces descriptor structure #define UDI_COMPOSITE_DESC_T \ - usb_iad_desc_t udi_cdc_iad; \ - udi_cdc_comm_desc_t udi_cdc_comm; \ - udi_cdc_data_desc_t udi_cdc_data; \ - udi_msc_desc_t udi_msc + usb_iad_desc_t udi_cdc_iad; \ + udi_cdc_comm_desc_t udi_cdc_comm; \ + udi_cdc_data_desc_t udi_cdc_data; \ + udi_msc_desc_t udi_msc //! USB Interfaces descriptor value for Full Speed #define UDI_COMPOSITE_DESC_FS \ - .udi_cdc_iad = UDI_CDC_IAD_DESC_0, \ - .udi_cdc_comm = UDI_CDC_COMM_DESC_0, \ - .udi_cdc_data = UDI_CDC_DATA_DESC_0_FS, \ - .udi_msc = UDI_MSC_DESC_FS + .udi_cdc_iad = UDI_CDC_IAD_DESC_0, \ + .udi_cdc_comm = UDI_CDC_COMM_DESC_0, \ + .udi_cdc_data = UDI_CDC_DATA_DESC_0_FS, \ + .udi_msc = UDI_MSC_DESC_FS //! USB Interfaces descriptor value for High Speed #define UDI_COMPOSITE_DESC_HS \ - .udi_cdc_iad = UDI_CDC_IAD_DESC_0, \ - .udi_cdc_comm = UDI_CDC_COMM_DESC_0, \ - .udi_cdc_data = UDI_CDC_DATA_DESC_0_HS, \ - .udi_msc = UDI_MSC_DESC_HS + .udi_cdc_iad = UDI_CDC_IAD_DESC_0, \ + .udi_cdc_comm = UDI_CDC_COMM_DESC_0, \ + .udi_cdc_data = UDI_CDC_DATA_DESC_0_HS, \ + .udi_msc = UDI_MSC_DESC_HS //! USB Interface APIs #define UDI_COMPOSITE_API \ - &udi_api_cdc_comm, \ - &udi_api_cdc_data, \ - &udi_api_msc + &udi_api_cdc_comm, \ + &udi_api_cdc_data, \ + &udi_api_msc //@} diff --git a/Marlin/src/HAL/HAL_DUE/usb/usb_task.c b/Marlin/src/HAL/HAL_DUE/usb/usb_task.c index af5a2e96f..2931dbbc4 100644 --- a/Marlin/src/HAL/HAL_DUE/usb/usb_task.c +++ b/Marlin/src/HAL/HAL_DUE/usb/usb_task.c @@ -40,9 +40,8 @@ * \asf_license_stop * */ -/* - * Support and FAQ: visit Atmel Support - */ + +// Support and FAQ: visit Atmel Support #ifdef ARDUINO_ARCH_SAM @@ -52,135 +51,241 @@ static volatile bool main_b_msc_enable = false; static volatile bool main_b_cdc_enable = false; -void HAL_init(void) { - udd_disable(); - UDD_SetStack(&USBD_ISR); +void HAL_idletask(void) { + // Attend SD card access from the USB MSD -- Prioritize access to improve speed + int delay = 2; + while (main_b_msc_enable && --delay > 0) { + if (udi_msc_process_trans()) delay = 10000; - // Start USB stack to authorize VBus monitoring - udc_start(); + // Reset the watchdog, just to be sure + REG_WDT_CR = WDT_CR_WDRSTT | WDT_CR_KEY(0xA5); + } } -void HAL_idletask(void) -{ +bool usb_task_msc_enable(void) { return ((main_b_msc_enable = true)); } +void usb_task_msc_disable(void) { main_b_msc_enable = false; } +bool usb_task_msc_isenabled(void) { return main_b_msc_enable; } - // Attend SD card access from the USB MSD -- Priotize access to improve speed - int delay = 2; - while (main_b_msc_enable && --delay > 0 ) { - if (udi_msc_process_trans()) { - delay = 10000; - } - - /* Reset the watchdog, just to be sure */ - REG_WDT_CR = WDT_CR_WDRSTT | WDT_CR_KEY(0xA5); - } -} - -/*! \brief Example of extra USB string management - * This feature is available for single or composite device - * which want implement additional USB string than - * Manufacture, Product and serial number ID. - * - * return true, if the string ID requested is know and managed by this functions - */ -bool usb_task_extra_string(void) -{ - static uint8_t udi_cdc_name[] = "CDC interface"; - static uint8_t udi_msc_name[] = "MSC interface"; - - struct extra_strings_desc_t{ - usb_str_desc_t header; - le16_t string[Max(sizeof(udi_cdc_name)-1, sizeof(udi_msc_name)-1)]; - }; - static UDC_DESC_STORAGE struct extra_strings_desc_t extra_strings_desc = { - .header.bDescriptorType = USB_DT_STRING - }; - - uint8_t i; - uint8_t *str; - uint8_t str_lgt=0; - - // Link payload pointer to the string corresponding at request - switch (udd_g_ctrlreq.req.wValue & 0xff) { - case UDI_CDC_IAD_STRING_ID: - str_lgt = sizeof(udi_cdc_name)-1; - str = udi_cdc_name; - break; - case UDI_MSC_STRING_ID: - str_lgt = sizeof(udi_msc_name)-1; - str = udi_msc_name; - break; - default: - return false; - } - - if (str_lgt!=0) { - for( i=0; i udd_g_ctrlreq.req.wLength) { - udd_g_ctrlreq.payload_size = udd_g_ctrlreq.req.wLength; - } - return true; -} - -bool usb_task_msc_enable(void) -{ - main_b_msc_enable = true; - return true; -} - -void usb_task_msc_disable(void) -{ - main_b_msc_enable = false; -} - -bool usb_task_msc_isenabled(void) -{ - return main_b_msc_enable; -} - -bool usb_task_cdc_enable(uint8_t port) -{ - main_b_cdc_enable = true; - return true; -} - -void usb_task_cdc_disable(uint8_t port) -{ - main_b_cdc_enable = false; -} - -bool usb_task_cdc_isenabled(void) -{ - return main_b_cdc_enable; -} +bool usb_task_cdc_enable(const uint8_t port) { return ((main_b_cdc_enable = true)); } +void usb_task_cdc_disable(const uint8_t port) { main_b_cdc_enable = false; } +bool usb_task_cdc_isenabled(void) { return main_b_cdc_enable; } /*! \brief Called by CDC interface * Callback running when CDC device have received data */ -void usb_task_cdc_rx_notify(uint8_t port) -{ -} +void usb_task_cdc_rx_notify(const uint8_t port) { } /*! \brief Configures communication line * * \param cfg line configuration */ -void usb_task_cdc_config(uint8_t port, usb_cdc_line_coding_t * cfg) -{ +void usb_task_cdc_config(const uint8_t port, usb_cdc_line_coding_t *cfg) { } + +void usb_task_cdc_set_dtr(const uint8_t port, const bool b_enable) { + if (b_enable) { + } else { + } } -void usb_task_cdc_set_dtr(uint8_t port, bool b_enable) -{ - if (b_enable) { - } else { - } +/// Microsoft WCID descriptor +typedef struct USB_MicrosoftCompatibleDescriptor_Interface { + uint8_t bFirstInterfaceNumber; + uint8_t reserved1; + uint8_t compatibleID[8]; + uint8_t subCompatibleID[8]; + uint8_t reserved2[6]; +} __attribute__((packed)) USB_MicrosoftCompatibleDescriptor_Interface; + +typedef struct USB_MicrosoftCompatibleDescriptor { + uint32_t dwLength; + uint16_t bcdVersion; + uint16_t wIndex; + uint8_t bCount; + uint8_t reserved[7]; + USB_MicrosoftCompatibleDescriptor_Interface interfaces[]; +} __attribute__((packed)) USB_MicrosoftCompatibleDescriptor; + +// 3D Printer compatible descriptor +static USB_MicrosoftCompatibleDescriptor microsoft_compatible_id_descriptor = { + .dwLength = sizeof(USB_MicrosoftCompatibleDescriptor) + + 1*sizeof(USB_MicrosoftCompatibleDescriptor_Interface), + .bcdVersion = 0x0100, + .wIndex = 0x0004, + .bCount = 1, + .reserved = {0, 0, 0, 0, 0, 0, 0}, + .interfaces = { + { + .bFirstInterfaceNumber = 0, + .reserved1 = 1, + .compatibleID = "3DPRINT", + .subCompatibleID = {0, 0, 0, 0, 0, 0, 0, 0}, + .reserved2 = {0, 0, 0, 0, 0, 0}, + } + } +}; + +#define xstr(s) str(s) +#define str(s) #s + +#define MS3DPRINT_CONFIG u"MS3DPrintConfig" +#define MS3DPRINT_CONFIG_DATA \ + u"Base=SD\0"\ + u"Job3DOutputAreaWidth=" xstr(X_BED_SIZE) "000\0"\ + u"Job3DOutputAreaDepth=" xstr(Y_BED_SIZE) "000\0"\ + u"Job3DOutputAreaHeight=" xstr(Z_MAX_POS) "000\0"\ + u"filamentdiameter=1750\0" + +typedef struct USB_MicrosoftExtendedPropertiesDescriptor { + uint32_t dwLength; + uint16_t bcdVersion; + uint16_t wIndex; + uint16_t bCount; + uint32_t dwPropertySize; + uint32_t dwPropertyDataType; + uint16_t wPropertyNameLength; + uint16_t PropertyName[sizeof(MS3DPRINT_CONFIG)/sizeof(uint16_t)]; + uint32_t dwPropertyDataLength; + uint16_t PropertyData[sizeof(MS3DPRINT_CONFIG_DATA)/sizeof(uint16_t)]; +} __attribute__((packed)) USB_MicrosoftExtendedPropertiesDescriptor; + +static USB_MicrosoftExtendedPropertiesDescriptor microsoft_extended_properties_descriptor = { + .dwLength = sizeof(USB_MicrosoftExtendedPropertiesDescriptor), + .bcdVersion = 0x0100, + .wIndex = 0x0005, + .bCount = 1, + + .dwPropertySize = 4 + 4 + 2 + 4 + sizeof(MS3DPRINT_CONFIG) + sizeof(MS3DPRINT_CONFIG_DATA), + .dwPropertyDataType = 7, // (1=REG_SZ, 4=REG_DWORD, 7=REG_MULTI_SZ) + .wPropertyNameLength = sizeof(MS3DPRINT_CONFIG), + .PropertyName = MS3DPRINT_CONFIG, + .dwPropertyDataLength = sizeof(MS3DPRINT_CONFIG_DATA), + .PropertyData = MS3DPRINT_CONFIG_DATA +}; + +/************************************************************************************************** +** WCID configuration information +** Hooked into UDC via UDC_GET_EXTRA_STRING #define. +*/ +bool usb_task_extra_string(void) { + static uint8_t udi_msft_magic[] = "MSFT100\xEE"; + static uint8_t udi_cdc_name[] = "CDC interface"; + static uint8_t udi_msc_name[] = "MSC interface"; + + struct extra_strings_desc_t { + usb_str_desc_t header; + le16_t string[Max(Max(sizeof(udi_cdc_name) - 1, sizeof(udi_msc_name) - 1), sizeof(udi_msft_magic) - 1)]; + }; + static UDC_DESC_STORAGE struct extra_strings_desc_t extra_strings_desc = { + .header.bDescriptorType = USB_DT_STRING + }; + + uint8_t *str; + uint8_t str_lgt = 0; + + // Link payload pointer to the string corresponding at request + switch (udd_g_ctrlreq.req.wValue & 0xff) { + case UDI_CDC_IAD_STRING_ID: + str_lgt = sizeof(udi_cdc_name) - 1; + str = udi_cdc_name; + break; + case UDI_MSC_STRING_ID: + str_lgt = sizeof(udi_msc_name) - 1; + str = udi_msc_name; + break; + case 0xEE: + str_lgt = sizeof(udi_msft_magic) - 1; + str = udi_msft_magic; + break; + default: + return false; + } + + for (uint8_t i = 0; i < str_lgt; i++) + extra_strings_desc.string[i] = cpu_to_le16((le16_t)str[i]); + + extra_strings_desc.header.bLength = 2 + str_lgt * 2; + udd_g_ctrlreq.payload_size = extra_strings_desc.header.bLength; + udd_g_ctrlreq.payload = (uint8_t*)&extra_strings_desc; + + // if the string is larger than request length, then cut it + if (udd_g_ctrlreq.payload_size > udd_g_ctrlreq.req.wLength) { + udd_g_ctrlreq.payload_size = udd_g_ctrlreq.req.wLength; + } + + return true; } -#endif \ No newline at end of file +/************************************************************************************************** +** Handle device requests that the ASF stack doesn't +*/ +bool usb_task_other_requests(void) { + uint8_t* ptr = 0; + uint16_t size = 0; + + if (Udd_setup_type() == USB_REQ_TYPE_VENDOR) { + //if (udd_g_ctrlreq.req.bRequest == 0x30) + if (1) { + if (udd_g_ctrlreq.req.wIndex == 0x04) { + ptr = (uint8_t*)µsoft_compatible_id_descriptor; + size = (udd_g_ctrlreq.req.wLength); + if (size > microsoft_compatible_id_descriptor.dwLength) + size = microsoft_compatible_id_descriptor.dwLength; + } + else if (udd_g_ctrlreq.req.wIndex == 0x05) { + ptr = (uint8_t*)µsoft_extended_properties_descriptor; + size = (udd_g_ctrlreq.req.wLength); + if (size > microsoft_extended_properties_descriptor.dwLength) + size = microsoft_extended_properties_descriptor.dwLength; + } + else + return false; + } + } + + udd_g_ctrlreq.payload_size = size; + if (size == 0) { + udd_g_ctrlreq.callback = 0; + udd_g_ctrlreq.over_under_run = 0; + } + else + udd_g_ctrlreq.payload = ptr; + + return true; +} + +void HAL_init(void) { + uint16_t *ptr; + + udd_disable(); + UDD_SetStack(&USBD_ISR); + + // Start USB stack to authorize VBus monitoring + udc_start(); + + // Patch in filament diameter - Be careful: String is in UNICODE (2bytes per char) + ptr = µsoft_extended_properties_descriptor.PropertyData[0]; + while (ptr[0] || ptr[1]) { // Double 0 flags end of resource + + // Found the filamentdiameter= unicode string + if (ptr[0] == 'r' && ptr[1] == '=') { + char diam[16]; + char *sptr; + + // Patch in the filament diameter + sprintf_P(diam, PSTR("%d"), (int)((DEFAULT_NOMINAL_FILAMENT_DIA) * 1000.0)); + + // And copy it to the proper place, expanding it to unicode + sptr = &diam[0]; + ptr += 2; + while (*sptr) *ptr++ = *sptr++; + + // Done! + break; + } + + // Go to the next character + ptr++; + } +} + +#endif // ARDUINO_ARCH_SAM diff --git a/Marlin/src/HAL/HAL_DUE/usb/usb_task.h b/Marlin/src/HAL/HAL_DUE/usb/usb_task.h index 01f498fb9..f535c9df7 100644 --- a/Marlin/src/HAL/HAL_DUE/usb/usb_task.h +++ b/Marlin/src/HAL/HAL_DUE/usb/usb_task.h @@ -66,33 +66,37 @@ void usb_task_msc_disable(void); * * \retval true if cdc startup is successfully done */ -bool usb_task_cdc_enable(uint8_t port); +bool usb_task_cdc_enable(const uint8_t port); /*! \brief Closes the communication port * This is called by CDC interface when USB Host disable it. */ -void usb_task_cdc_disable(uint8_t port); +void usb_task_cdc_disable(const uint8_t port); /*! \brief Save new DTR state to change led behavior. * The DTR notify that the terminal have open or close the communication port. */ -void usb_task_cdc_set_dtr(uint8_t port, bool b_enable); +void usb_task_cdc_set_dtr(const uint8_t port, const bool b_enable); /*! \brief Called by UDC when USB Host request a extra string different * of this specified in USB device descriptor */ bool usb_task_extra_string(void); +/*! \brief Called by UDC when USB Host performs unknown requests + */ +bool usb_task_other_requests(void); + /*! \brief Called by CDC interface * Callback running when CDC device have received data */ -void usb_task_cdc_rx_notify(uint8_t port); +void usb_task_cdc_rx_notify(const uint8_t port); /*! \brief Configures communication line * * \param cfg line configuration */ -void usb_task_cdc_config(uint8_t port, usb_cdc_line_coding_t * cfg); +void usb_task_cdc_config(const uint8_t port, usb_cdc_line_coding_t *cfg); /* The USB device interrupt */