3.8 CDC: Virtual Serial Port - How to use
The USB CDC Class Virtual Serial Port (VSP) implementation handles setup for serial communication over USB. It consists of a handler function and helper functions to access and transmit data.
The initialization of the Virtual Serial Port is done by calling the CDC Class initialization. No other initialization is needed. The virtual serial port is double buffered in terms of receiving data from the USB host. This is done because the host needs a buffer to store transmitted data and will overwrite any data that’s there.
The USB_CDCVirtualSerialPortHandler
function handles all Virtual Serial
Port related tasks. It needs to run in the main loop of the program or be called by
interrupts when needed to ensure that the buffers don’t fill up resulting in loss of
incoming data.
- Checks if the transmit buffer is empty - Skips the rest if it is
- Checks if the transmit pipe is busy - Skips the rest if it is
- Transmits data stored in the
transmit buffer and calls the
USB_CDCDataTransmitted
callback function
- Checks if the outgoing transactions succeeded - Skips the rest if not
- Checks if there is enough room in buffer to receive maximum size transaction - Skips rest if not
- Checks if the receive pipe is busy - Skips if it is
- Receives data if it is available
and writes it to a temporary buffer and calls the
USB_CDCDataReceived
callback function to handle the received data
The USB_CDCDataReceived
callback function moves the received data from
the temporary receive buffer and adds it to a circular buffer that’s used for external
communication. The USB_CDCDataTransmitted
callback function resets the
transmit buffer so that its ready for new data.
To access the received data, the USB_CDCRead
function returns the next
data byte from the receive buffer. The USB_CDCWrite
function adds one
provided byte to the transmit buffer. The CDC_TxBufferFull
function
returns true if the transmit buffer is full and can’t handle more data. These functions
and the USB_CDCVirtualSerialPortHandler
function use functions from the
circular buffer implementation to add and retrieve data from the buffers.
CIRCBUF_Enqueue
function adds
data to buffer if the buffer is not full and returns the status of the action. The
CIRCBUF_Dequeue
function pulls one byte from the buffer as long as
there is data in the buffer and returns the status of the action. The buffer
implementation also has some helper functions: CIRCBUF_Empty
and
CIRCBUF_Full
check if the buffer is empty or full while
CIRCBUF_FreeSpace
returns the number of empty slots in the buffer.
A new buffer is created by creating an uint8_t
array of the size of the
resulting buffer and then using the CIRCULAR_BUFFER_t
type defines to
create a circular buffer using the previously created
array:STATIC uint8_t bufferArray[BUFFER_SIZE];
STATIC CIRCULAR_BUFFER_t circularBuffer = {
.content = bufferArray,
.head = 0,
.tail = 0,
.maxLength = BUFFER_SIZE
};