diff --git a/cores/arduino/Stream.h b/cores/arduino/Stream.h index bd5311b84a..db3dbe48d7 100644 --- a/cores/arduino/Stream.h +++ b/cores/arduino/Stream.h @@ -94,12 +94,12 @@ class Stream : public Print float parseFloat(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR); // float version of parseInt - size_t readBytes( char *buffer, size_t length); // read chars from stream into buffer + virtual size_t readBytes( char *buffer, size_t length); // read chars from stream into buffer size_t readBytes( uint8_t *buffer, size_t length) { return readBytes((char *)buffer, length); } // terminates if length characters have been read or timeout (see setTimeout) // returns the number of characters placed in the buffer (0 means no valid data found) - size_t readBytesUntil( char terminator, char *buffer, size_t length); // as readBytes with terminator character + virtual size_t readBytesUntil( char terminator, char *buffer, size_t length); // as readBytes with terminator character size_t readBytesUntil( char terminator, uint8_t *buffer, size_t length) { return readBytesUntil(terminator, (char *)buffer, length); } // terminates if length characters have been read, timeout, or if the terminator character detected // returns the number of characters placed in the buffer (0 means no valid data found) diff --git a/cores/arduino/USBSerial.cpp b/cores/arduino/USBSerial.cpp index b11d4f5c8d..f4be38c847 100644 --- a/cores/arduino/USBSerial.cpp +++ b/cores/arduino/USBSerial.cpp @@ -24,125 +24,125 @@ #include "usbd_desc.h" #include "wiring.h" -#define USB_TIMEOUT 50 /* USB Device Core handle declaration */ extern USBD_HandleTypeDef hUSBD_Device_CDC; -extern __IO uint32_t device_connection_status; extern __IO uint32_t lineState; -extern __IO uint8_t UserTxBuffer[APP_TX_DATA_SIZE]; -extern __IO uint8_t UserRxBuffer[APP_RX_DATA_SIZE]; -extern __IO uint32_t UserTxBufPtrIn; -extern __IO uint32_t UserTxBufPtrOut; -extern __IO uint32_t UserRxBufPtrIn; -extern __IO uint32_t UserRxBufPtrOut; USBSerial SerialUSB; +void USBSerial::begin() { + pinMode(PA12, OUTPUT); + digitalWrite(PA12, LOW); + delay(100); + CDC_init(); +} + void USBSerial::begin(uint32_t /* baud_count */) { - // uart config is ignored in USB-CDC + begin(); } void USBSerial::begin(uint32_t /* baud_count */, uint8_t /* config */) { - // uart config is ignored in USB-CDC + begin(); } -void USBSerial::end(void) { - - USBD_LL_DeInit(&hUSBD_Device_CDC); +void USBSerial::end() { + CDC_deInit(); } -int USBSerial::availableForWrite(void) +int USBSerial::availableForWrite() { - int ret_val; - - /* UserTxBufPtrOut can be modified by TIM ISR, so in order to be sure that the */ - /* value that we read is correct, we need to disable TIM Interrupt. */ - CDC_disable_TIM_Interrupt(); - - if (UserTxBufPtrIn >= UserTxBufPtrOut) { - ret_val = (APP_TX_DATA_SIZE - 1 - UserTxBufPtrIn + UserTxBufPtrOut); - } else { - ret_val = (UserTxBufPtrOut - UserTxBufPtrIn - 1); - } - - CDC_enable_TIM_Interrupt(); - - return ret_val; + // just transmit queue size, available for write + return static_cast(CDC_TransmitQueue_WriteSize(&TransmitQueue)); } size_t USBSerial::write(uint8_t ch) { - - /* UserTxBufPtrOut can be modified by TIM ISR, so in order to be sure that the */ - /* value that we read is correct, we need to disable TIM Interrupt. */ - CDC_disable_TIM_Interrupt(); - - if (((UserTxBufPtrIn + 1) % APP_TX_DATA_SIZE) == UserTxBufPtrOut) { - // Buffer full!!! Force a flush to not loose data and go on - CDC_flush(); - } - UserTxBuffer[UserTxBufPtrIn] = ch; - UserTxBufPtrIn = ((UserTxBufPtrIn + 1) % APP_TX_DATA_SIZE); - - CDC_enable_TIM_Interrupt(); - - return 1; + // just write single-byte buffer. + return write(&ch, 1); } size_t USBSerial::write(const uint8_t *buffer, size_t size){ - size_t i = 0; - for (i=0; i < size; i++) { - if (write(buffer[i]) != 1) { - break; - } + size_t rest = size; + while(rest > 0) { + // Determine buffer size available for write + auto portion = (size_t)CDC_TransmitQueue_WriteSize(&TransmitQueue); + // Truncate it to content size (if rest is greater) + if (rest < portion) { + portion = rest; + } + if (portion > 0) { + // Only if some space in the buffer exists. + // TS: Only main thread calls write and writeSize methods, + // it's thread-safe since IRQ does not affects + // TransmitQueue write position + CDC_TransmitQueue_Enqueue(&TransmitQueue, buffer, portion); + rest -= portion; + buffer += portion; + // After storing data, start transmitting process + CDC_continue_transmit(); + } } - return i; + return size; } int USBSerial::available(void) { - return ((APP_RX_DATA_SIZE + (UserRxBufPtrIn - UserRxBufPtrOut)) % APP_RX_DATA_SIZE); + // just ReceiveQueue size, available for reading + return static_cast(CDC_ReceiveQueue_ReadSize(&ReceiveQueue)); } int USBSerial::read(void) { - if (UserRxBufPtrOut == UserRxBufPtrIn) { - return -1; - } else { - unsigned char c = UserRxBuffer[UserRxBufPtrOut]; - UserRxBufPtrOut = ((UserRxBufPtrOut + 1) % APP_RX_DATA_SIZE); + // Dequeue only one char from queue + // TS: it safe, because only main thread affects ReceiveQueue->read pos + auto ch = CDC_ReceiveQueue_Dequeue(&ReceiveQueue); + // resume receive process, if possible CDC_resume_receive(); - return c; - } + return ch; +} + +size_t USBSerial::readBytes(char *buffer, size_t length) { + uint16_t read; + auto rest = static_cast(length); + _startMillis = millis(); + do { + read = CDC_ReceiveQueue_Read(&ReceiveQueue, reinterpret_cast(buffer), rest); + CDC_resume_receive(); + rest -= read; + buffer += read; + if (rest == 0) return length; + } while(millis() - _startMillis < _timeout); + return length - rest; +} + +size_t USBSerial::readBytesUntil(char terminator, char *buffer, size_t length) { + uint16_t read; + auto rest = static_cast(length); + _startMillis = millis(); + do { + bool found = CDC_ReceiveQueue_ReadUntil(&ReceiveQueue, static_cast(terminator), + reinterpret_cast(buffer), rest, &read); + CDC_resume_receive(); + rest -= read; + buffer += read; + if (found) { + return length - rest; + } + if (rest == 0) { + return length; + } + } while(millis() - _startMillis < _timeout); + return length - rest; } int USBSerial::peek(void) { - if (UserRxBufPtrOut == UserRxBufPtrIn) { - return -1; - } else { - unsigned char c = UserRxBuffer[UserRxBufPtrOut]; - return c; - } + // Peek one symbol, it can't change receive avaiablity + return CDC_ReceiveQueue_Peek(&ReceiveQueue); } void USBSerial::flush(void) { - /* UserTxBufPtrOut can be modified by TIM ISR, so in order to be sure that the */ - /* value that we read is correct, we need to disable TIM Interrupt. */ - CDC_disable_TIM_Interrupt(); - CDC_flush(); - CDC_enable_TIM_Interrupt(); -} - -uint8_t USBSerial::pending(void) { - return 0; -} - -uint8_t USBSerial::isConnected(void) { - - if (device_connection_status == 1) { - return 1; - } else { - return 0; - } + // Wait for TransmitQueue read size becomes zero + // TS: safe, because it not be stopped while receive 0 + while(CDC_TransmitQueue_ReadSize(&TransmitQueue) > 0) {} } uint32_t USBSerial::baud() { diff --git a/cores/arduino/USBSerial.h b/cores/arduino/USBSerial.h index 10b28f996d..bf4f07ecfe 100644 --- a/cores/arduino/USBSerial.h +++ b/cores/arduino/USBSerial.h @@ -27,8 +27,7 @@ // Serial over CDC class USBSerial : public Stream { public: - USBSerial(void) {}; - + void begin(); void begin(uint32_t); void begin(uint32_t, uint8_t); void end(void); @@ -37,6 +36,8 @@ class USBSerial : public Stream { virtual int availableForWrite(void); virtual int peek(void); virtual int read(void); + virtual size_t readBytes( char *buffer, size_t length); // read chars from stream into buffer + virtual size_t readBytesUntil( char terminator, char *buffer, size_t length); // as readBytes with terminator character virtual void flush(void); virtual size_t write(uint8_t); virtual size_t write(const uint8_t *buffer, size_t size); @@ -64,9 +65,6 @@ class USBSerial : public Stream { MARK_PARITY = 3, SPACE_PARITY = 4, }; - - uint8_t isConnected(); - uint8_t pending(); }; extern USBSerial SerialUSB; diff --git a/cores/arduino/stm32/usb/cdc/cdc_queue.c b/cores/arduino/stm32/usb/cdc/cdc_queue.c new file mode 100644 index 0000000000..c1c338e160 --- /dev/null +++ b/cores/arduino/stm32/usb/cdc/cdc_queue.c @@ -0,0 +1,231 @@ +/** + ****************************************************************************** + * @file cdc_queue.c + * @author makarenya + * @version V1.0.0 + * @date 23-December-2018 + * @brief Provides methods to manipulate with CDC cyclic buffers + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2017 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + + +#include "cdc_queue.h" + +// Initialize read and write position of queue +void CDC_TransmitQueue_Init(CDC_TransmitQueue_TypeDef *queue) { + queue->read = 0; + queue->write = 0; +} + +// Determine size, available for write in queue +int CDC_TransmitQueue_WriteSize(CDC_TransmitQueue_TypeDef *queue) { + return (queue->read + CDC_TRANSMIT_QUEUE_BUFFER_SIZE - queue->write - 1) + % CDC_TRANSMIT_QUEUE_BUFFER_SIZE; +} + +// Determine size of data, stored in queue +int CDC_TransmitQueue_ReadSize(CDC_TransmitQueue_TypeDef *queue) { + return (queue->write + CDC_TRANSMIT_QUEUE_BUFFER_SIZE - queue->read) + % CDC_TRANSMIT_QUEUE_BUFFER_SIZE; +} + +// Write provided data into queue. +void CDC_TransmitQueue_Enqueue(CDC_TransmitQueue_TypeDef *queue, + const uint8_t *buffer, uint32_t size) { + uint32_t sizeToEnd = CDC_TRANSMIT_QUEUE_BUFFER_SIZE - queue->write; + if (sizeToEnd > size) { + memcpy(&queue->buffer[queue->write], &buffer[0], size); + } else { + memcpy(&queue->buffer[queue->write], &buffer[0], sizeToEnd); + memcpy(&queue->buffer[0], &buffer[sizeToEnd], size - sizeToEnd); + } + queue->write = (uint16_t)((queue->write + size) % + CDC_TRANSMIT_QUEUE_BUFFER_SIZE); +} + +// Read flat block from queue biggest as possible, but max QUEUE_MAX_PACKET_SIZE +uint8_t *CDC_TransmitQueue_ReadBlock(CDC_TransmitQueue_TypeDef *queue, + uint16_t *size) { + if (queue->write >= queue->read) { + *size = queue->write - queue->read; + } else { + *size = CDC_TRANSMIT_QUEUE_BUFFER_SIZE - queue->read; + } + queue->reserved = *size; + return &queue->buffer[queue->read]; +} + +void CDC_TransmitQueue_CommitRead(CDC_TransmitQueue_TypeDef *queue) { + queue->read = (queue->read + queue->reserved) % + CDC_TRANSMIT_QUEUE_BUFFER_SIZE; +} + +// Initialize read and write position of queue. +void CDC_ReceiveQueue_Init(CDC_ReceiveQueue_TypeDef *queue) { + queue->read = 0; + queue->write = 0; + queue->length = CDC_RECEIVE_QUEUE_BUFFER_SIZE; +} + +// Reserve block in queue and return pointer to it. +uint8_t *CDC_ReceiveQueue_ReserveBlock(CDC_ReceiveQueue_TypeDef *queue) { + const uint16_t limit = + CDC_RECEIVE_QUEUE_BUFFER_SIZE - CDC_QUEUE_MAX_PACKET_SIZE; + volatile uint16_t read = queue->read; + + if (read <= queue->write) { + // if write is limited only by buffer size. + if (queue->write < limit || (queue->write == limit && read > 0)) { + // if size in the rest of buffer is enough for full packet plus 1 byte + // or if it tight enough and write position can be set to 0 + return queue->buffer + queue->write; + } else if (read > CDC_QUEUE_MAX_PACKET_SIZE) { + // if size in the rest is not enough, but enough size in head + queue->length = queue->write; + queue->write = 0; + return queue->buffer + queue->write; + } + } else if (queue->write + CDC_QUEUE_MAX_PACKET_SIZE < read) { + // write position must be less than read position + // after reading largest possible packet + return queue->buffer + queue->write; + } + return 0; +} + +// Commits block in queue and make it available for reading +void CDC_ReceiveQueue_CommitBlock(CDC_ReceiveQueue_TypeDef *queue, + uint16_t size) { + queue->write += size; + if (queue->write >= queue->length) { + queue->length = CDC_RECEIVE_QUEUE_BUFFER_SIZE; + } + if (queue->write >= CDC_RECEIVE_QUEUE_BUFFER_SIZE) { + queue->write = 0; + } +} + +// Determine size, available for read +int CDC_ReceiveQueue_ReadSize(CDC_ReceiveQueue_TypeDef *queue) { + // reading length after write make guarantee, that length >= write + // and determined reading size will be smaller or equal than real one. + volatile uint16_t write = queue->write; + volatile uint16_t length = queue->length; + if (write >= queue->read) { + return write - queue->read; + } else { + return length + write - queue->read; + } +} + +// Read one byte from queue. +int CDC_ReceiveQueue_Dequeue(CDC_ReceiveQueue_TypeDef *queue) { + volatile uint16_t write = queue->write; + volatile uint16_t length = queue->length; + if (queue->read == length) queue->read = 0; + if (write == queue->read) return -1; + uint8_t ch = queue->buffer[queue->read++]; + if (queue->read >= length) { + queue->read = 0; + } + return ch; +} + +// Peek byte from queue. +int CDC_ReceiveQueue_Peek(CDC_ReceiveQueue_TypeDef *queue) { + volatile uint16_t write = queue->write; + volatile uint16_t length = queue->length; + if (queue->read >= length) queue->read = 0; + if (write == queue->read) return -1; + return queue->buffer[queue->read]; +} + +uint16_t CDC_ReceiveQueue_Read(CDC_ReceiveQueue_TypeDef *queue, + uint8_t *buffer, uint16_t size) { + volatile uint16_t write = queue->write; + volatile uint16_t length = queue->length; + uint16_t available; + + if (queue->read >= length) queue->read = 0; + if (write >= queue->read) { + available = write - queue->read; + } else { + available = length - queue->read; + } + if (available < size) { + size = available; + } + + memcpy(buffer, &queue->buffer[queue->read], size); + queue->read = queue->read + size; + if (queue->read >= length) { + queue->read = 0; + } + return size; +} + +bool CDC_ReceiveQueue_ReadUntil(CDC_ReceiveQueue_TypeDef *queue, + uint8_t terminator, uint8_t *buffer, uint16_t size, uint16_t* fetched) { + volatile uint16_t write = queue->write; + volatile uint16_t length = queue->length; + uint16_t available; + + if (queue->read >= length) queue->read = 0; + if (write >= queue->read) { + available = write - queue->read; + } else { + available = length - queue->read; + } + if (available < size) { + size = available; + } + + uint8_t* start = &queue->buffer[queue->read]; + for(uint16_t i = 0; i < size; i++) { + uint8_t ch = start[i]; + if (ch == terminator) { + queue->read += (uint16_t)(i + 1); + if (queue->read >= length) { + queue->read = 0; + } + *fetched = i; + return true; + } else { + buffer[i] = ch; + } + } + + *fetched = size; + queue->read += size; + if (queue->read >= length) { + queue->read = 0; + } + return false; +} diff --git a/cores/arduino/stm32/usb/cdc/cdc_queue.h b/cores/arduino/stm32/usb/cdc/cdc_queue.h new file mode 100644 index 0000000000..108cdc1281 --- /dev/null +++ b/cores/arduino/stm32/usb/cdc/cdc_queue.h @@ -0,0 +1,94 @@ +/** + ****************************************************************************** + * @file cdc_queue.h + * @author makarenya + * @version V1.0.0 + * @date 23-December-2018 + * @brief Header for cdc_queue.c module + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __CDC_QUEUE_H +#define __CDC_QUEUE_H + +/* Includes ------------------------------------------------------------------*/ +#include +#include "usbd_def.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#if USE_USB_HS +#define CDC_QUEUE_MAX_PACKET_SIZE USB_HS_MAX_PACKET_SIZE +#else +#define CDC_QUEUE_MAX_PACKET_SIZE USB_FS_MAX_PACKET_SIZE +#endif +#define CDC_TRANSMIT_QUEUE_BUFFER_SIZE ((uint16_t)(CDC_QUEUE_MAX_PACKET_SIZE * 2)) +#define CDC_RECEIVE_QUEUE_BUFFER_SIZE ((uint16_t)(CDC_QUEUE_MAX_PACKET_SIZE * 3)) + +typedef struct { + uint8_t buffer[CDC_TRANSMIT_QUEUE_BUFFER_SIZE]; + volatile uint16_t write; + volatile uint16_t read; + volatile uint16_t reserved; +} CDC_TransmitQueue_TypeDef; + +typedef struct { + uint8_t buffer[CDC_RECEIVE_QUEUE_BUFFER_SIZE]; + volatile uint16_t write; + volatile uint16_t read; + volatile uint16_t length; +} CDC_ReceiveQueue_TypeDef; + +void CDC_TransmitQueue_Init(CDC_TransmitQueue_TypeDef* queue); +int CDC_TransmitQueue_WriteSize(CDC_TransmitQueue_TypeDef* queue); +int CDC_TransmitQueue_ReadSize(CDC_TransmitQueue_TypeDef* queue); +void CDC_TransmitQueue_Enqueue(CDC_TransmitQueue_TypeDef* queue, const uint8_t* buffer, uint32_t size); +uint8_t* CDC_TransmitQueue_ReadBlock(CDC_TransmitQueue_TypeDef* queue, uint16_t* size); +void CDC_TransmitQueue_CommitRead(CDC_TransmitQueue_TypeDef *queue); + +void CDC_ReceiveQueue_Init(CDC_ReceiveQueue_TypeDef* queue); +int CDC_ReceiveQueue_ReadSize(CDC_ReceiveQueue_TypeDef* queue); +int CDC_ReceiveQueue_Dequeue(CDC_ReceiveQueue_TypeDef* queue); +int CDC_ReceiveQueue_Peek(CDC_ReceiveQueue_TypeDef* queue); +uint16_t CDC_ReceiveQueue_Read(CDC_ReceiveQueue_TypeDef* queue, uint8_t* buffer, uint16_t size); +bool CDC_ReceiveQueue_ReadUntil(CDC_ReceiveQueue_TypeDef* queue, uint8_t terminator, uint8_t* buffer, + uint16_t size, uint16_t* fetched); +uint8_t* CDC_ReceiveQueue_ReserveBlock(CDC_ReceiveQueue_TypeDef* queue); +void CDC_ReceiveQueue_CommitBlock(CDC_ReceiveQueue_TypeDef* queue, uint16_t size); + +#ifdef __cplusplus +} +#endif + +#endif // __CDC_QUEUE_H \ No newline at end of file diff --git a/cores/arduino/stm32/usb/cdc/usbd_cdc.c b/cores/arduino/stm32/usb/cdc/usbd_cdc.c index 1674333598..7c8b5a997b 100644 --- a/cores/arduino/stm32/usb/cdc/usbd_cdc.c +++ b/cores/arduino/stm32/usb/cdc/usbd_cdc.c @@ -678,6 +678,7 @@ static uint8_t USBD_CDC_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum) { USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)pdev->pClassData; PCD_HandleTypeDef *hpcd = pdev->pData; + USBD_CDC_ItfTypeDef* ctrl = (USBD_CDC_ItfTypeDef *)pdev->pUserData; if(pdev->pClassData != NULL) { @@ -692,6 +693,10 @@ static uint8_t USBD_CDC_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum) else { hcdc->TxState = 0U; + if (ctrl->Transferred) + { + ctrl->Transferred(); + } } return USBD_OK; } diff --git a/cores/arduino/stm32/usb/cdc/usbd_cdc.h b/cores/arduino/stm32/usb/cdc/usbd_cdc.h index 1cddfeaf8b..556ac47ef7 100644 --- a/cores/arduino/stm32/usb/cdc/usbd_cdc.h +++ b/cores/arduino/stm32/usb/cdc/usbd_cdc.h @@ -104,6 +104,7 @@ typedef struct _USBD_CDC_Itf int8_t (* DeInit) (void); int8_t (* Control) (uint8_t cmd, uint8_t* pbuf, uint16_t length); int8_t (* Receive) (uint8_t* Buf, uint32_t *Len); + int8_t (* Transferred) (void); }USBD_CDC_ItfTypeDef; diff --git a/cores/arduino/stm32/usb/cdc/usbd_cdc_if.c b/cores/arduino/stm32/usb/cdc/usbd_cdc_if.c index 76b3b54690..4c1603a0d3 100644 --- a/cores/arduino/stm32/usb/cdc/usbd_cdc_if.c +++ b/cores/arduino/stm32/usb/cdc/usbd_cdc_if.c @@ -21,6 +21,7 @@ #ifdef USBD_USE_CDC /* Includes ------------------------------------------------------------------*/ +#include #include "usbd_cdc_if.h" #ifdef USE_USB_HS @@ -37,28 +38,13 @@ /* It's up to user to redefine and/or remove those define */ extern USBD_HandleTypeDef hUSBD_Device_CDC; /* Received Data over USB are stored in this buffer */ -__IO uint8_t UserRxBuffer[APP_RX_DATA_SIZE]; -__IO uint8_t StackRxBuffer[CDC_MAX_PACKET_SIZE]; -/* Send Data over USB CDC are stored in this buffer */ -__IO uint8_t UserTxBuffer[APP_TX_DATA_SIZE]; -__IO uint8_t StackTxBuffer[APP_TX_DATA_SIZE]; - -__IO uint32_t UserTxBufPtrIn = 0; /* Increment this pointer or roll it back to - start address when data are received over write call */ -__IO uint32_t UserTxBufPtrOut = 0; /* Increment this pointer or roll it back to - start address when data are sent over USB */ - -__IO uint32_t UserRxBufPtrIn = 0; /* Increment this pointer or roll it back to - start address when data are received over USB */ -__IO uint32_t UserRxBufPtrOut = 0; /* Increment this pointer or roll it back to - start address when data are sent over read call */ +CDC_TransmitQueue_TypeDef TransmitQueue; +CDC_ReceiveQueue_TypeDef ReceiveQueue; __IO uint32_t lineState = 0; -__IO bool receiveSuspended = false; -__IO bool sendZLP = false; +__IO bool receivePended = true; -stimer_t CDC_TimHandle; /** USBD_CDC Private Function Prototypes */ @@ -66,15 +52,15 @@ static int8_t USBD_CDC_Init (void); static int8_t USBD_CDC_DeInit (void); static int8_t USBD_CDC_Control (uint8_t cmd, uint8_t* pbuf, uint16_t length); static int8_t USBD_CDC_Receive (uint8_t* pbuf, uint32_t *Len); -static void CDC_TIM_Config(void); -void CDC_TIM_PeriodElapsedCallback(stimer_t *htim); +static int8_t USBD_CDC_Transferred (void); USBD_CDC_ItfTypeDef USBD_CDC_fops = { USBD_CDC_Init, USBD_CDC_DeInit, USBD_CDC_Control, - USBD_CDC_Receive + USBD_CDC_Receive, + USBD_CDC_Transferred }; USBD_CDC_LineCodingTypeDef linecoding = @@ -95,12 +81,11 @@ USBD_CDC_LineCodingTypeDef linecoding = */ static int8_t USBD_CDC_Init(void) { - /* Configure and start the TIM Base generation */ - CDC_TIM_Config(); - /* Set Application Buffers */ - USBD_CDC_SetTxBuffer(&hUSBD_Device_CDC, (uint8_t *)UserTxBuffer, 1); - USBD_CDC_SetRxBuffer(&hUSBD_Device_CDC, (uint8_t *)StackRxBuffer); + CDC_TransmitQueue_Init(&TransmitQueue); + CDC_ReceiveQueue_Init(&ReceiveQueue); + receivePended = true; + USBD_CDC_SetRxBuffer(&hUSBD_Device_CDC, CDC_ReceiveQueue_ReserveBlock(&ReceiveQueue)); return (USBD_OK); } @@ -217,155 +202,75 @@ static int8_t USBD_CDC_Control (uint8_t cmd, uint8_t* pbuf, uint16_t length) * @param Len: Number of data received (in bytes) * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL */ -static int8_t USBD_CDC_Receive (uint8_t* Buf, uint32_t *Len) -{ - uint32_t packetSize = *Len; - - if (packetSize > 0) { - if (UserRxBufPtrIn + packetSize > APP_RX_DATA_SIZE) { - memcpy(((uint8_t *)UserRxBuffer + UserRxBufPtrIn), &Buf[0], - (APP_RX_DATA_SIZE - UserRxBufPtrIn)); - memcpy((uint8_t *)UserRxBuffer, - &Buf[(APP_RX_DATA_SIZE - UserRxBufPtrIn)], - (packetSize - (APP_RX_DATA_SIZE - UserRxBufPtrIn))); - UserRxBufPtrIn = ((UserRxBufPtrIn + packetSize) % APP_RX_DATA_SIZE); - } else { - memcpy(((uint8_t *)UserRxBuffer + UserRxBufPtrIn), Buf, packetSize); - UserRxBufPtrIn = ((UserRxBufPtrIn + packetSize) % APP_RX_DATA_SIZE); - } - } - - if ((UserRxBufPtrOut + APP_RX_DATA_SIZE - UserRxBufPtrIn - 1) % - APP_RX_DATA_SIZE + 1 >= CDC_MAX_PACKET_SIZE) { - USBD_CDC_ReceivePacket( - &hUSBD_Device_CDC); // Initiate next USB packet transfer once a packet - // is received and there is enouch space in the - // buffer - } else { - receiveSuspended = true; - } - return (USBD_OK); +static int8_t USBD_CDC_Receive (uint8_t* Buf, uint32_t *Len) { + // it always contains required amount of free space for writing + CDC_ReceiveQueue_CommitBlock(&ReceiveQueue, (uint16_t)(*Len)); + receivePended = false; + // If there is enough space in the queue for a full buffer, continue receive + CDC_resume_receive(); + return USBD_OK; } -void CDC_flush(void) -{ - uint8_t status; - if(UserTxBufPtrOut != UserTxBufPtrIn) - { - if(UserTxBufPtrOut > UserTxBufPtrIn) /* Roll-back */ - { - memcpy((uint8_t *)&StackTxBuffer[0], - (uint8_t *)&UserTxBuffer[UserTxBufPtrOut], - (APP_TX_DATA_SIZE - UserTxBufPtrOut)); - memcpy((uint8_t *)&StackTxBuffer[APP_TX_DATA_SIZE - UserTxBufPtrOut], - (uint8_t *)&UserTxBuffer[0], UserTxBufPtrIn); - - USBD_CDC_SetTxBuffer( - &hUSBD_Device_CDC, (uint8_t *)&StackTxBuffer[0], - (APP_TX_DATA_SIZE - UserTxBufPtrOut + UserTxBufPtrIn)); - } else { - USBD_CDC_SetTxBuffer(&hUSBD_Device_CDC, - (uint8_t *)&UserTxBuffer[UserTxBufPtrOut], - (UserTxBufPtrIn - UserTxBufPtrOut)); - } +static int8_t USBD_CDC_Transferred (void) { + CDC_TransmitQueue_CommitRead(&TransmitQueue); + CDC_continue_transmit(); +} - do { - if (lineState == 0) { // Device disconnected - status = USBD_OK; - } else { - status = USBD_CDC_TransmitPacket(&hUSBD_Device_CDC); - } - } while (status == USBD_BUSY); +void CDC_init(void) { + /* Init Device Library */ + if (USBD_Init(&hUSBD_Device_CDC, &CDC_Desc, 0) == USBD_OK) { - if (status == USBD_OK) { - UserTxBufPtrOut = UserTxBufPtrIn; - } - } -} + /* Add Supported Class */ + if (USBD_RegisterClass(&hUSBD_Device_CDC, USBD_CDC_CLASS) == USBD_OK) { -void CDC_resume_receive(void) { - if (receiveSuspended) { - if ((UserRxBufPtrOut + APP_RX_DATA_SIZE - UserRxBufPtrIn - 1) % - APP_RX_DATA_SIZE + 1 >= CDC_MAX_PACKET_SIZE) { - USBD_CDC_ReceivePacket( - &hUSBD_Device_CDC); // Initiate next USB packet transfer once a packet - // is received and there is enouch space in the - // buffer - receiveSuspended = false; + /* Add CDC Interface Class */ + if (USBD_CDC_RegisterInterface(&hUSBD_Device_CDC, &USBD_CDC_fops) == USBD_OK) { + /* Start Device Process */ + USBD_Start(&hUSBD_Device_CDC); + } } } } -void CDC_disable_TIM_Interrupt(void) -{ - HAL_NVIC_DisableIRQ(CDC_TIM_IRQn); -} - -void CDC_enable_TIM_Interrupt(void) -{ - HAL_NVIC_EnableIRQ(CDC_TIM_IRQn); -} - -static void CDC_TIM_Config(void) -{ - /* Set TIMx instance */ - CDC_TimHandle.timer = CDC_TIM; - /* Initialize CDC_TIM peripheral as follow: - + Period = 10000 - 1 - + Prescaler = ((SystemCoreClock/2)/10000) - 1 - + ClockDivision = 0 - + Counter direction = Up - */ - TimerHandleInit(&CDC_TimHandle, (uint16_t)((CDC_POLLING_INTERVAL*1000) - 1), ((uint32_t)(getTimerClkFreq(CDC_TIM) / (1000000)) - 1)); - HAL_NVIC_SetPriority(CDC_TIM_IRQn, 6, 0); - - attachIntHandle(&CDC_TimHandle, CDC_TIM_PeriodElapsedCallback); +void CDC_deInit(void) { + USBD_Stop(&hUSBD_Device_CDC); + USBD_CDC_DeInit(); + USBD_DeInit(&hUSBD_Device_CDC); } -void CDC_TIM_PeriodElapsedCallback(stimer_t *htim) -{ - UNUSED(htim); - if (UserTxBufPtrOut == UserTxBufPtrIn && - sendZLP == false) // Nothing to do, return immediately - return; - - uint8_t status; - uint16_t packetLength; - - if (UserTxBufPtrOut > UserTxBufPtrIn) { /* Roll-back */ - memcpy((uint8_t *)&StackTxBuffer[0], - (uint8_t *)&UserTxBuffer[UserTxBufPtrOut], - (APP_TX_DATA_SIZE - UserTxBufPtrOut)); - memcpy((uint8_t *)&StackTxBuffer[APP_TX_DATA_SIZE - UserTxBufPtrOut], - (uint8_t *)&UserTxBuffer[0], UserTxBufPtrIn); - - packetLength = (APP_TX_DATA_SIZE - UserTxBufPtrOut + UserTxBufPtrIn); - - USBD_CDC_SetTxBuffer(&hUSBD_Device_CDC, (uint8_t *)&StackTxBuffer[0], - packetLength); - } else if (UserTxBufPtrOut != UserTxBufPtrIn) { - packetLength = (UserTxBufPtrIn - UserTxBufPtrOut); - - USBD_CDC_SetTxBuffer(&hUSBD_Device_CDC, - (uint8_t *)&UserTxBuffer[UserTxBufPtrOut], - packetLength); - } else { - packetLength = 0; - - USBD_CDC_SetTxBuffer(&hUSBD_Device_CDC, NULL, 0); // Send Zero Length Packet - } - - if (lineState == 0) { // Device disconnected - status = USBD_OK; - } else { - status = USBD_CDC_TransmitPacket(&hUSBD_Device_CDC); +void CDC_continue_transmit(void) { + uint16_t size; + uint8_t *buffer; + USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *) hUSBD_Device_CDC.pClassData; + // TS: This method can be called both in the main thread + // (via USBSerial::write) and in the IRQ stream (via USBD_CDC_Transferred), + // BUT the main thread cannot pass this condition while waiting for a IRQ! + // This is not possible because TxState is not zero while waiting for data + // transfer ending! The IRQ thread is uninterrupted, since its priority + // is higher than that of the main thread. So this method is thread safe. + if (hcdc->TxState == 0U) { + buffer = CDC_TransmitQueue_ReadBlock(&TransmitQueue, &size); + if (size > 0) { + USBD_CDC_SetTxBuffer(&hUSBD_Device_CDC, buffer, size); + // size newer exceed PMA buffer and USBD_CDC_TransmitPacket make full + // copy of block in PMA, so no need to worry about buffer damage + USBD_CDC_TransmitPacket(&hUSBD_Device_CDC); + } } +} - if (status == USBD_OK) { - UserTxBufPtrOut = UserTxBufPtrIn; - - sendZLP = packetLength % CDC_MAX_PACKET_SIZE == 0; +void CDC_resume_receive(void) { + // TS: main and IRQ threads can't pass it at same time, because + // IRQ may occur only if receivePended is true. So it thread-safe! + if (!receivePended) { + uint8_t* block = CDC_ReceiveQueue_ReserveBlock(&ReceiveQueue); + if (block != NULL) { + receivePended = true; + // Set new buffer + USBD_CDC_SetRxBuffer(&hUSBD_Device_CDC, block); + USBD_CDC_ReceivePacket(&hUSBD_Device_CDC); + } } } diff --git a/cores/arduino/stm32/usb/cdc/usbd_cdc_if.h b/cores/arduino/stm32/usb/cdc/usbd_cdc_if.h index 9956d42e97..7408f30d49 100644 --- a/cores/arduino/stm32/usb/cdc/usbd_cdc_if.h +++ b/cores/arduino/stm32/usb/cdc/usbd_cdc_if.h @@ -30,8 +30,10 @@ /* Includes ------------------------------------------------------------------*/ #include "usbd_cdc.h" +#include "cdc_queue.h" #include "timer.h" + #ifndef APP_RX_DATA_SIZE #define APP_RX_DATA_SIZE 2048 #endif @@ -39,21 +41,6 @@ #define APP_TX_DATA_SIZE 2048 #endif -#ifndef CDC_TIM -#ifdef TIM6 -#define CDC_TIM TIM6 -#else -#define CDC_TIM TIM4 -#endif -#endif -#ifndef CDC_TIM_IRQn -#ifdef TIM6 -#define CDC_TIM_IRQn TIM6_IRQn -#else -#define CDC_TIM_IRQn TIM4_IRQn -#endif -#endif - /* Periodically, the state of the buffer "UserTxBuffer" is checked. The period depends on CDC_POLLING_INTERVAL */ #define CDC_POLLING_INTERVAL 2 /* in ms. The max is 65 and the min is 1 */ @@ -62,13 +49,16 @@ /* Exported constants --------------------------------------------------------*/ extern USBD_CDC_ItfTypeDef USBD_CDC_fops; +extern CDC_TransmitQueue_TypeDef TransmitQueue; +extern CDC_ReceiveQueue_TypeDef ReceiveQueue; + /* Exported macro ------------------------------------------------------------*/ /* Exported functions ------------------------------------------------------- */ -void CDC_disable_TIM_Interrupt(void); -void CDC_enable_TIM_Interrupt(void); -void CDC_flush(void); +void CDC_continue_transmit(void); void CDC_resume_receive(void); +void CDC_init(void); +void CDC_deInit(void); #ifdef __cplusplus } diff --git a/cores/arduino/stm32/usb/usbd_conf.c b/cores/arduino/stm32/usb/usbd_conf.c index c02d8c35cb..2d91232d9a 100644 --- a/cores/arduino/stm32/usb/usbd_conf.c +++ b/cores/arduino/stm32/usb/usbd_conf.c @@ -25,6 +25,12 @@ #else /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ +// size in words, byte size divided by 2 +#define PMA_EP0_OUT_ADDR (8 * 4) +#define PMA_EP0_IN_ADDR (PMA_EP0_OUT_ADDR + USB_MAX_EP0_SIZE / 2) +#define PMA_CDC_OUT_ADDR (PMA_EP0_IN_ADDR + USB_MAX_EP0_SIZE / 2) +#define PMA_CDC_IN_ADDR (PMA_CDC_OUT_ADDR + USB_FS_MAX_PACKET_SIZE / 2) +#define PMA_CDC_CMD_ADDR (PMA_CDC_IN_ADDR + USB_FS_MAX_PACKET_SIZE / 2) /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ PCD_HandleTypeDef g_hpcd; @@ -475,10 +481,11 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev) HAL_PCDEx_SetTxFiFo(&g_hpcd, 1, 0x40); HAL_PCDEx_SetTxFiFo(&g_hpcd, 2, 0x40); #else - HAL_PCDEx_PMAConfig(&g_hpcd, 0x00 , PCD_SNG_BUF, 0x18); - HAL_PCDEx_PMAConfig(&g_hpcd, 0x80 , PCD_SNG_BUF, 0x58); - HAL_PCDEx_PMAConfig(&g_hpcd, 0x81 , PCD_SNG_BUF, 0x100); - HAL_PCDEx_PMAConfig(&g_hpcd, 0x82 , PCD_SNG_BUF, 0x100); + HAL_PCDEx_PMAConfig(&g_hpcd, 0x00, PCD_SNG_BUF, PMA_EP0_OUT_ADDR); + HAL_PCDEx_PMAConfig(&g_hpcd, 0x80, PCD_SNG_BUF, PMA_EP0_IN_ADDR); + HAL_PCDEx_PMAConfig(&g_hpcd, 0x01, PCD_SNG_BUF, PMA_CDC_OUT_ADDR); + HAL_PCDEx_PMAConfig(&g_hpcd, 0x81, PCD_SNG_BUF, PMA_CDC_IN_ADDR); + HAL_PCDEx_PMAConfig(&g_hpcd, 0x82, PCD_SNG_BUF, PMA_CDC_CMD_ADDR); #endif #endif /* USE_USB_HS */ return USBD_OK; diff --git a/cores/arduino/stm32/usbd_interface.c b/cores/arduino/stm32/usbd_interface.c index 57ae34b4f9..5657223c65 100644 --- a/cores/arduino/stm32/usbd_interface.c +++ b/cores/arduino/stm32/usbd_interface.c @@ -72,21 +72,6 @@ void usbd_interface_init(void) /* Start Device Process */ USBD_Start(&hUSBD_Device_HID); #endif /* USBD_USE_HID_COMPOSITE */ -#ifdef USBD_USE_CDC - /* Init Device Library */ - if (USBD_Init(&hUSBD_Device_CDC, &CDC_Desc, 0) == USBD_OK) { - - /* Add Supported Class */ - if (USBD_RegisterClass(&hUSBD_Device_CDC, USBD_CDC_CLASS) == USBD_OK) { - - /* Add CDC Interface Class */ - if (USBD_CDC_RegisterInterface(&hUSBD_Device_CDC, &USBD_CDC_fops) == USBD_OK) { - /* Start Device Process */ - USBD_Start(&hUSBD_Device_CDC); - } - } - } -#endif /* USBD_USE_CDC */ } #ifdef USBD_USE_HID_COMPOSITE