diff --git a/STM32F1/cores/maple/libmaple/usb/stm32f1/usb_cdcacm.c b/STM32F1/cores/maple/libmaple/usb/stm32f1/usb_cdcacm.c index 448c05d..7a5a772 100644 --- a/STM32F1/cores/maple/libmaple/usb/stm32f1/usb_cdcacm.c +++ b/STM32F1/cores/maple/libmaple/usb/stm32f1/usb_cdcacm.c @@ -261,18 +261,28 @@ static ONE_DESCRIPTOR String_Descriptor[N_STRING_DESCRIPTORS] = { /* I/O state */ -#define CDC_SERIAL_BUFFER_SIZE 512 +#define CDC_SERIAL_RX_BUFFER_SIZE 256 // must be power of 2 +#define CDC_SERIAL_RX_BUFFER_SIZE_MASK (CDC_SERIAL_RX_BUFFER_SIZE-1) /* Received data */ -static volatile uint8 vcomBufferRx[CDC_SERIAL_BUFFER_SIZE]; -/* Read index into vcomBufferRx */ -static volatile uint32 rx_offset = 0; -/* Number of bytes left to transmit */ -static volatile uint32 n_unsent_bytes = 0; -/* Are we currently sending an IN packet? */ -static volatile uint8 transmitting = 0; -/* Number of unread bytes */ -static volatile uint32 n_unread_bytes = 0; +static volatile uint8 vcomBufferRx[CDC_SERIAL_RX_BUFFER_SIZE]; +/* Write index to vcomBufferRx */ +static volatile uint32 rx_head; +/* Read index from vcomBufferRx */ +static volatile uint32 rx_tail; + +#define CDC_SERIAL_TX_BUFFER_SIZE 256 // must be power of 2 +#define CDC_SERIAL_TX_BUFFER_SIZE_MASK (CDC_SERIAL_TX_BUFFER_SIZE-1) +// Tx data +static volatile uint8 vcomBufferTx[CDC_SERIAL_TX_BUFFER_SIZE]; +// Write index to vcomBufferTx +static volatile uint32 tx_head; +// Read index from vcomBufferTx +static volatile uint32 tx_tail; +// Are we currently sending an IN packet? +static volatile int8 transmitting; + + /* Other state (line coding, DTR/RTS) */ @@ -393,30 +403,34 @@ void usb_cdcacm_putc(char ch) { ; } -/* This function is blocking. +/* This function is non-blocking. * - * It copies data from a usercode buffer into the USB peripheral TX + * It copies data from a user buffer into the USB peripheral TX * buffer, and returns the number of bytes copied. */ -uint32 usb_cdcacm_tx(const uint8* buf, uint32 len) { - /* Last transmission hasn't finished, so abort. */ - while ( usb_cdcacm_is_transmitting()>0 ) ; // wait for end of transmission +uint32 usb_cdcacm_tx(const uint8* buf, uint32 len) +{ + if (len==0) return 0; // no data to send - /* We can only put USB_CDCACM_TX_EPSIZE bytes in the buffer. */ - if (len > USB_CDCACM_TX_EPSIZE) { - len = USB_CDCACM_TX_EPSIZE; - } + uint32 head = tx_head; // load volatile variable + uint32 tx_unsent = (head - tx_tail) & CDC_SERIAL_TX_BUFFER_SIZE_MASK; - /* Queue bytes for sending. */ - if (len) { - usb_copy_to_pma(buf, len, USB_CDCACM_TX_ADDR); + // We can only put bytes in the buffer if there is place + if (len > (CDC_SERIAL_TX_BUFFER_SIZE-tx_unsent-1) ) { + len = (CDC_SERIAL_TX_BUFFER_SIZE-tx_unsent-1); } - // We still need to wait for the interrupt, even if we're sending - // zero bytes. (Sending zero-size packets is useful for flushing - // host-side buffers.) - usb_set_ep_tx_count(USB_CDCACM_TX_ENDP, len); - n_unsent_bytes = len; - transmitting = 1; - usb_set_ep_tx_stat(USB_CDCACM_TX_ENDP, USB_EP_STAT_TX_VALID); + if (len==0) return 0; // buffer full + + uint16 i; + // copy data from user buffer to USB Tx buffer + for (i=0; i0 ? transmitting : 0); } uint16 usb_cdcacm_get_pending(void) { - return n_unsent_bytes; + return (tx_head - tx_tail) & CDC_SERIAL_TX_BUFFER_SIZE_MASK; } -/* Nonblocking byte receive. +/* Non-blocking byte receive. * * Copies up to len bytes from our private data buffer (*NOT* the PMA) * into buf and deq's the FIFO. */ -uint32 usb_cdcacm_rx(uint8* buf, uint32 len) { +uint32 usb_cdcacm_rx(uint8* buf, uint32 len) +{ /* Copy bytes to buffer. */ uint32 n_copied = usb_cdcacm_peek(buf, len); /* Mark bytes as read. */ - n_unread_bytes -= n_copied; - rx_offset = (rx_offset + n_copied) % CDC_SERIAL_BUFFER_SIZE; + uint16 tail = rx_tail; // load volatile variable + tail = (tail + n_copied) & CDC_SERIAL_RX_BUFFER_SIZE_MASK; + rx_tail = tail; // store volatile variable - /* If all bytes have been read, re-enable the RX endpoint, which - * was set to NAK when the current batch of bytes was received. */ - if (n_unread_bytes == 0) { - usb_set_ep_rx_count(USB_CDCACM_RX_ENDP, USB_CDCACM_RX_EPSIZE); + uint32 rx_unread = (rx_head - tail) & CDC_SERIAL_RX_BUFFER_SIZE_MASK; + // If buffer was emptied to a pre-set value, re-enable the RX endpoint + if ( rx_unread <= 64 ) { // experimental value, gives the best performance usb_set_ep_rx_stat(USB_CDCACM_RX_ENDP, USB_EP_STAT_RX_VALID); - } - + } return n_copied; } -/* Nonblocking byte lookahead. +/* Non-blocking byte lookahead. * * Looks at unread bytes without marking them as read. */ -uint32 usb_cdcacm_peek(uint8* buf, uint32 len) { +uint32 usb_cdcacm_peek(uint8* buf, uint32 len) +{ int i; - uint32 head = rx_offset; + uint32 tail = rx_tail; + uint32 rx_unread = (rx_head-tail) & CDC_SERIAL_RX_BUFFER_SIZE_MASK; - if (len > n_unread_bytes) { - len = n_unread_bytes; + if (len > rx_unread) { + len = rx_unread; } for (i = 0; i < len; i++) { - buf[i] = vcomBufferRx[head]; - head = (head + 1) % CDC_SERIAL_BUFFER_SIZE; + buf[i] = vcomBufferRx[tail]; + tail = (tail + 1) & CDC_SERIAL_RX_BUFFER_SIZE_MASK; } return len; } -uint32 usb_cdcacm_peek_ex(uint8* buf, uint32 offset, uint32 len) { +uint32 usb_cdcacm_peek_ex(uint8* buf, uint32 offset, uint32 len) +{ int i; - uint32 head = (rx_offset + offset) % CDC_SERIAL_BUFFER_SIZE; + uint32 tail = (rx_tail + offset) & CDC_SERIAL_RX_BUFFER_SIZE_MASK ; + uint32 rx_unread = (rx_head-tail) & CDC_SERIAL_RX_BUFFER_SIZE_MASK; - if (len + offset > n_unread_bytes) { - len = n_unread_bytes - offset; + if (len + offset > rx_unread) { + len = rx_unread - offset; } for (i = 0; i < len; i++) { - buf[i] = vcomBufferRx[head]; - head = (head + 1) % CDC_SERIAL_BUFFER_SIZE; + buf[i] = vcomBufferRx[tail]; + tail = (tail + 1) & CDC_SERIAL_RX_BUFFER_SIZE_MASK; } return len; @@ -495,12 +513,12 @@ uint32 usb_cdcacm_peek_ex(uint8* buf, uint32 offset, uint32 len) { /* Roger Clark. Added. for Arduino 1.0 API support of Serial.peek() */ int usb_cdcacm_peek_char() { - if (n_unread_bytes == 0) + if (usb_cdcacm_data_available() == 0) { return -1; } - return vcomBufferRx[rx_offset]; + return vcomBufferRx[rx_tail]; } uint8 usb_cdcacm_get_dtr() { @@ -534,41 +552,75 @@ int usb_cdcacm_get_n_data_bits(void) { return line_coding.bDataBits; } - /* * Callbacks */ - -static void vcomDataTxCb(void) { - n_unsent_bytes = 0; - transmitting = 0; +static void vcomDataTxCb(void) +{ + uint32 tail = tx_tail; // load volatile variable + uint32 tx_unsent = (tx_head - tail) & CDC_SERIAL_TX_BUFFER_SIZE_MASK; + if (tx_unsent==0) { + if ( (--transmitting)==0) goto flush; // no more data to send + return; // it was already flushed, keep Tx endpoint disabled + } + transmitting = 1; + // We can only send up to USB_CDCACM_TX_EPSIZE bytes in the endpoint. + if (tx_unsent > USB_CDCACM_TX_EPSIZE) { + tx_unsent = USB_CDCACM_TX_EPSIZE; + } + // copy the bytes from USB Tx buffer to PMA buffer + uint32 *dst = usb_pma_ptr(USB_CDCACM_TX_ADDR); + uint16 tmp = 0; + uint16 val; + int i; + for (i = 0; i < tx_unsent; i++) { + val = vcomBufferTx[tail]; + tail = (tail + 1) & CDC_SERIAL_TX_BUFFER_SIZE_MASK; + if (i&1) { + *dst++ = tmp | (val<<8); + } else { + tmp = val; + } + } + if ( tx_unsent&1 ) { + *dst = tmp; + } + tx_tail = tail; // store volatile variable +flush: + // enable Tx endpoint + usb_set_ep_tx_count(USB_CDCACM_TX_ENDP, tx_unsent); + usb_set_ep_tx_stat(USB_CDCACM_TX_ENDP, USB_EP_STAT_TX_VALID); } -static void vcomDataRxCb(void) { - uint32 ep_rx_size; - uint32 tail = (rx_offset + n_unread_bytes) % CDC_SERIAL_BUFFER_SIZE; - uint8 ep_rx_data[USB_CDCACM_RX_EPSIZE]; + +static void vcomDataRxCb(void) +{ + uint32 head = rx_head; // load volatile variable + + uint32 ep_rx_size = usb_get_ep_rx_count(USB_CDCACM_RX_ENDP); + // This copy won't overwrite unread bytes as long as there is + // enough room in the USB Rx buffer for next packet + uint32 *src = usb_pma_ptr(USB_CDCACM_RX_ADDR); + uint16 tmp = 0; + uint8 val; uint32 i; - - usb_set_ep_rx_stat(USB_CDCACM_RX_ENDP, USB_EP_STAT_RX_NAK); - ep_rx_size = usb_get_ep_rx_count(USB_CDCACM_RX_ENDP); - /* This copy won't overwrite unread bytes, since we've set the RX - * endpoint to NAK, and will only set it to VALID when all bytes - * have been read. */ - usb_copy_from_pma((uint8*)ep_rx_data, ep_rx_size, - USB_CDCACM_RX_ADDR); - for (i = 0; i < ep_rx_size; i++) { - vcomBufferRx[tail] = ep_rx_data[i]; - tail = (tail + 1) % CDC_SERIAL_BUFFER_SIZE; + if (i&1) { + val = tmp>>8; + } else { + tmp = *src++; + val = tmp&0xFF; + } + vcomBufferRx[head] = val; + head = (head + 1) & CDC_SERIAL_RX_BUFFER_SIZE_MASK; } + rx_head = head; // store volatile variable - n_unread_bytes += ep_rx_size; - - if ( n_unread_bytes == 0 ) { - usb_set_ep_rx_count(USB_CDCACM_RX_ENDP, USB_CDCACM_RX_EPSIZE); - usb_set_ep_rx_stat(USB_CDCACM_RX_ENDP, USB_EP_STAT_RX_VALID); - } + uint32 rx_unread = (head - rx_tail) & CDC_SERIAL_RX_BUFFER_SIZE_MASK; + // only enable further Rx if there is enough room to receive one more packet + if ( rx_unread < (CDC_SERIAL_RX_BUFFER_SIZE-USB_CDCACM_RX_EPSIZE) ) { + usb_set_ep_rx_stat(USB_CDCACM_RX_ENDP, USB_EP_STAT_RX_VALID); + } if (rx_hook) { rx_hook(USB_CDCACM_HOOK_RX, 0); @@ -646,10 +698,11 @@ static void usbReset(void) { SetDeviceAddress(0); /* Reset the RX/TX state */ - n_unread_bytes = 0; - n_unsent_bytes = 0; - rx_offset = 0; - transmitting = 0; + rx_head = 0; + rx_tail = 0; + tx_head = 0; + tx_tail = 0; + transmitting = -1; } static RESULT usbDataSetup(uint8 request) { diff --git a/STM32F1/cores/maple/usb_serial.cpp b/STM32F1/cores/maple/usb_serial.cpp index ab8816b..24b0014 100644 --- a/STM32F1/cores/maple/usb_serial.cpp +++ b/STM32F1/cores/maple/usb_serial.cpp @@ -100,44 +100,23 @@ size_t n = 0; size_t USBSerial::write(const char *str) { size_t n = 0; - this->write(str, strlen(str)); + this->write((const uint8*)str, strlen(str)); return n; } -size_t USBSerial::write(const void *buf, uint32 len) { +size_t USBSerial::write(const uint8 *buf, uint32 len) +{ size_t n = 0; if (!this->isConnected() || !buf) { return 0; } uint32 txed = 0; - uint32 old_txed = 0; - uint32 start = millis(); - - uint32 sent = 0; - - while (txed < len && (millis() - start < USB_TIMEOUT)) { - sent = usb_cdcacm_tx((const uint8*)buf + txed, len - txed); - txed += sent; - if (old_txed != txed) { - start = millis(); - } - old_txed = txed; + while (txed < len) { + txed += usb_cdcacm_tx((const uint8*)buf + txed, len - txed); } - -#if 0 -// this code leads to a serious performance drop and appears to be -// unnecessary - everything seems to work fine without, -jcw, 2015-11-05 -// see http://stm32duino.com/posting.php?mode=quote&f=3&p=7746 - if (sent == USB_CDCACM_TX_EPSIZE) { - while (usb_cdcacm_is_transmitting() != 0) { - } - /* flush out to avoid having the pc wait for more data */ - usb_cdcacm_tx(NULL, 0); - } -#endif - return n; + return n; } int USBSerial::available(void) { @@ -168,14 +147,10 @@ void USBSerial::flush(void) return; } -uint32 USBSerial::read(void *buf, uint32 len) { - if (!buf) { - return 0; - } - +uint32 USBSerial::read(uint8 * buf, uint32 len) { uint32 rxed = 0; while (rxed < len) { - rxed += usb_cdcacm_rx((uint8*)buf + rxed, len - rxed); + rxed += usb_cdcacm_rx(buf + rxed, len - rxed); } return rxed; @@ -183,13 +158,13 @@ uint32 USBSerial::read(void *buf, uint32 len) { /* Blocks forever until 1 byte is received */ int USBSerial::read(void) { - uint8 b; + int8 b; /* this->read(&b, 1); return b; */ - if (usb_cdcacm_rx(&b, 1)==0) + if (usb_cdcacm_rx((uint8*)&b, 1)==0) { return -1; } diff --git a/STM32F1/cores/maple/usb_serial.h b/STM32F1/cores/maple/usb_serial.h index 740ab0f..e50a16c 100644 --- a/STM32F1/cores/maple/usb_serial.h +++ b/STM32F1/cores/maple/usb_serial.h @@ -53,7 +53,7 @@ public: virtual int available(void);// Changed to virtual - uint32 read(void *buf, uint32 len); + uint32 read(uint8 * buf, uint32 len); // uint8 read(void); // Roger Clark. added functions to support Arduino 1.0 API diff --git a/STM32F1/system/libmaple/usb/stm32f1/usb_reg_map.h b/STM32F1/system/libmaple/usb/stm32f1/usb_reg_map.h index 6683264..9e9214c 100644 --- a/STM32F1/system/libmaple/usb/stm32f1/usb_reg_map.h +++ b/STM32F1/system/libmaple/usb/stm32f1/usb_reg_map.h @@ -349,8 +349,8 @@ static inline void usb_clear_status_out(uint8 ep) { void usb_copy_to_pma(const uint8 *buf, uint16 len, uint16 pma_offset); void usb_copy_from_pma(uint8 *buf, uint16 len, uint16 pma_offset); -static inline void* usb_pma_ptr(uint32 offset) { - return (void*)(USB_PMA_BASE + 2 * offset); +static inline uint32 * usb_pma_ptr(uint32 offset) { + return (uint32*)(USB_PMA_BASE + 2 * offset); } /*