Add USB CDC serial port code

This implements a USB CDC serial port using the Nuvoton USBD driver. The
USB handling is based on Nuvoton's BSP sample code, especially the IRQ
handlers and descriptor buffer configuration. The descriptors have been
adapted to be similar to the AVR version, and RX/TX functions have been
written to implement an API closer to LUFA, which is what the SIMM
programmer common code needs.
This commit is contained in:
Doug Brown 2023-08-06 20:35:28 -07:00 committed by Doug Brown
parent abb1352f30
commit 5e8224e35e
3 changed files with 775 additions and 0 deletions

194
hal/m258ke/descriptors.c Normal file
View File

@ -0,0 +1,194 @@
/****************************************************************************//**
* @file descriptors.c
* @version V0.10
* @brief USBD descriptors
*
* SPDX-License-Identifier: Apache-2.0
* @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved.
******************************************************************************/
#include "usbcdc_hw.h"
/// VID and PID of our device
#define USB_VID 0x16D0
#define USB_PID 0x06AA
/// Device descriptor
const uint8_t deviceDescriptor[] = {
LEN_DEVICE, // bLength
DESC_DEVICE, // bDescriptorType
0x10, 0x01, // bcdUSB
0x02, // bDeviceClass
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
EP0_MAX_PKT_SIZE, // bMaxPacketSize0
// idVendor
USB_VID & 0x00FF, (USB_VID & 0xFF00) >> 8,
// idProduct
USB_PID & 0x00FF, (USB_PID & 0xFF00) >> 8,
0x02, 0x00, // bcdDevice
0x01, // iManufacturer
0x02, // iProduct
0x03, // iSerialNumber
0x01 // bNumConfigurations
};
/// Config descriptor (includes interface, endpoint descriptors)
const uint8_t configDescriptor[] =
{
LEN_CONFIG, // bLength
DESC_CONFIG, // bDescriptorType
0x3E, 0x00, // wTotalLength
0x02, // bNumInterfaces
0x01, // bConfigurationValue
0x00, // iConfiguration
0xC0, // bmAttributes
0xFA, // MaxPower
// Interface descriptor: communication class interface
LEN_INTERFACE, // bLength
DESC_INTERFACE, // bDescriptorType
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x01, // bNumEndpoints
0x02, // bInterfaceClass
0x02, // bInterfaceSubClass
0x01, // bInterfaceProtocol
0x00, // iInterface
// Header functional descriptor
0x05, // Size of the descriptor, in bytes
0x24, // CS_INTERFACE descriptor type
0x00, // Header functional descriptor subtype
0x10, 0x01, // Communication device compliant to the communication spec. ver. 1.10
// Abstract control management functional descriptor
0x04, // Size of the descriptor, in bytes
0x24, // CS_INTERFACE descriptor type
0x02, // Abstract control management functional descriptor subtype
0x06, // bmCapabilities
// Union functional descriptor
0x05, // bLength
0x24, // bDescriptorType: CS_INTERFACE descriptor type
0x06, // bDescriptorSubType
0x00, // bMasterInterface
0x01, // bSlaveInterface0
// Endpoint descriptor
LEN_ENDPOINT, // bLength
DESC_ENDPOINT, // bDescriptorType
(EP_INPUT | INT_IN_EP_NUM), // bEndpointAddress
EP_INT, // bmAttributes
EP2_MAX_PKT_SIZE, 0x00, // wMaxPacketSize
0xFF, // bInterval
// Interface descriptor: data class interface
LEN_INTERFACE, // bLength
DESC_INTERFACE, // bDescriptorType
0x01, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
0x0A, // bInterfaceClass
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface
// Endpoint descriptor
LEN_ENDPOINT, // bLength
DESC_ENDPOINT, // bDescriptorType
(EP_OUTPUT | BULK_OUT_EP_NUM), // bEndpointAddress
EP_BULK, // bmAttributes
EP4_MAX_PKT_SIZE, 0x00, // wMaxPacketSize
0x01, // bInterval
// Endpoint descriptor
LEN_ENDPOINT, // bLength
DESC_ENDPOINT, // bDescriptorType
(EP_INPUT | BULK_IN_EP_NUM), // bEndpointAddress
EP_BULK, // bmAttributes
EP3_MAX_PKT_SIZE, 0x00, // wMaxPacketSize
0x01, // bInterval
};
/// Language descriptor
const uint8_t languageStringDescriptor[4] =
{
4, // bLength
DESC_STRING, // bDescriptorType
0x09, 0x04 // English (United States)
};
/// Vendor string descriptor
const uint8_t vendorStringDescriptor[] =
{
22,
DESC_STRING,
'D', 0,
'o', 0,
'u', 0,
'g', 0,
' ', 0,
'B', 0,
'r', 0,
'o', 0,
'w', 0,
'n', 0,
};
/// Product string descriptor
const uint8_t productStringDescriptor[] =
{
48, // bLength
DESC_STRING, // bDescriptorType
'M', 0,
'a', 0,
'c', 0,
' ', 0,
'R', 0,
'O', 0,
'M', 0,
' ', 0,
'S', 0,
'I', 0,
'M', 0,
'M', 0,
' ', 0,
'P', 0,
'r', 0,
'o', 0,
'g', 0,
'r', 0,
'a', 0,
'm', 0,
'm', 0,
'e', 0,
'r', 0,
};
/// Serial number string descriptor
const uint8_t serialStringDescriptor[] =
{
4, // bLength
DESC_STRING, // bDescriptorType
'0', 0,
};
/// Array of string descriptors
const uint8_t * const stringDescriptors[4] =
{
languageStringDescriptor,
vendorStringDescriptor,
productStringDescriptor,
serialStringDescriptor
};
/// Descriptor info used by usbd.c
const S_USBD_INFO_T gsInfo =
{
deviceDescriptor,
configDescriptor,
stringDescriptors,
};

468
hal/m258ke/usbcdc.c Normal file
View File

@ -0,0 +1,468 @@
/*
* usbcdc.c
*
* Created on: Jun 19, 2023
* Author: Doug
*
* Copyright (C) 2011-2023 Doug Brown
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* Portions of this code also came from Nuvoton's BSP, originally
* licensed as Apache-2.0:
* @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved.
*
*/
#include "usbcdc_hw.h"
#include <stdbool.h>
// Undocumented register for HIRC trim from Nuvoton's samples
#define TRIM_INIT (SYS_BASE + 0x118)
#define SET_LINE_CODE 0x20
#define GET_LINE_CODE 0x21
#define SET_CONTROL_LINE_STATE 0x22
/// Setup buffer uses first 8 bytes of USB SRAM
#define SETUP_BUF_BASE 0
#define SETUP_BUF_LEN 8
/// EP0 and EP1 share the same USB SRAM, 64 bytes of control
#define EP0_BUF_BASE (SETUP_BUF_BASE + SETUP_BUF_LEN)
#define EP0_BUF_LEN EP0_MAX_PKT_SIZE
#define EP1_BUF_BASE (SETUP_BUF_BASE + SETUP_BUF_LEN)
#define EP1_BUF_LEN EP1_MAX_PKT_SIZE
/// EP2 uses 8 bytes for interrupt IN (not really used though)
#define EP2_BUF_BASE (EP1_BUF_BASE + EP1_BUF_LEN)
#define EP2_BUF_LEN EP2_MAX_PKT_SIZE
/// EP3 uses 64 bytes for bulk IN
#define EP3_BUF_BASE (EP2_BUF_BASE + EP2_BUF_LEN)
#define EP3_BUF_LEN EP3_MAX_PKT_SIZE
/// EP4 uses 64 bytes for bulk OUT
#define EP4_BUF_BASE (EP3_BUF_BASE + EP3_BUF_LEN)
#define EP4_BUF_LEN EP4_MAX_PKT_SIZE
/// Struct to represent the current line coding
typedef struct
{
uint32_t baudRate; // Baud rate
uint8_t stopBits; // stop bit
uint8_t parity; // parity
uint8_t dataBits; // data bits
} CDCLineCoding;
static void USBCDC_SendDataInBuffer(void);
static void USBCDC_InitEndpoints(void);
static void USBCDC_ClassRequest(void);
/// Default HIRC trim value in case of errors
static uint32_t trimInit;
/// Toggle flag for ensuring the received endpoint 4 packet is the expected one
static volatile uint32_t ep4OutToggle = 0;
/// Line coding for CDC device, not used by this firmware
static CDCLineCoding cdcLineCoding = {0, 0, 0, 0};
/// Control signal for CDC device, not used by this firmware
static uint16_t cdcCtrlSignal;
/// Buffer to send to the CDC serial port
static uint8_t cdcTxBuf[EP3_MAX_PKT_SIZE];
/// Current position in the TX buffer
static uint32_t cdcTxBufPos = 0;
/// Flag that is true if a TX is currently active
static volatile bool cdcTxActive = false;
/// Buffer we read into
static uint8_t cdcRxBuf[EP4_MAX_PKT_SIZE];
/// Current length of RX buffer
static uint32_t cdcRxLen = 0;
/// Current position into RX buffer
static uint32_t cdcRxPos = 0;
/// Flag that is true if new RX data is ready to read
static volatile bool cdcRxReady = false;
/** Initializes the USB CDC serial port
*
*/
void USBCDC_Init(void)
{
// Set up USB
USBD_Open(&gsInfo, USBCDC_ClassRequest);
USBCDC_InitEndpoints();
USBD_Start();
NVIC_EnableIRQ(USBD_IRQn);
// Backup the default trim value, go back to it if there's an error
trimInit = M32(TRIM_INIT);
// Clear USB SOF flag; it will be used to detect if we have a USB
// signal available to use for HIRC trim
USBD_CLR_INT_FLAG(USBD_INTSTS_SOFIF_Msk);
}
/** Performs any necessary periodic tasks for the USB CDC serial port
*
*/
void USBCDC_Check(void)
{
// If we haven't enabled auto trim, and we have a USB signal available,
// then do it!
if (((SYS->HIRCTRIMCTL & SYS_HIRCTRIMCTL_FREQSEL_Msk) != 0x1) &&
(USBD->INTSTS & USBD_INTSTS_SOFIF_Msk))
{
// Clear SOF flag for next time
USBD_CLR_INT_FLAG(USBD_INTSTS_SOFIF_Msk);
// Start USB trim:
// - HIRC trim reference is USB clock
// - Enable auto trim, and trim to 48 MHz
// - Use 4 cycles for trimming
// - Boundary enabled (value = 10)
SYS->HIRCTRIMCTL = (0x1UL << SYS_HIRCTRIMCTL_REFCKSEL_Pos)
| (0x1UL << SYS_HIRCTRIMCTL_FREQSEL_Pos)
| (0x0UL << SYS_HIRCTRIMCTL_LOOPSEL_Pos)
| (0x1UL << SYS_HIRCTRIMCTL_BOUNDEN_Pos)
| (10UL << SYS_HIRCTRIMCTL_BOUNDARY_Pos);
}
// If we detect a clock error or trim failure, disable auto trim. We can try again later.
if (SYS->HIRCTRIMSTS & (SYS_HIRCTRIMSTS_CLKERIF_Msk | SYS_HIRCTRIMSTS_TFAILIF_Msk))
{
// Restore original trim value we read at startup
M32(TRIM_INIT) = trimInit;
// Disable USB trim
SYS->HIRCTRIMCTL = 0;
// Clear the error flags
SYS->HIRCTRIMSTS = SYS_HIRCTRIMSTS_CLKERIF_Msk | SYS_HIRCTRIMSTS_TFAILIF_Msk;
// Clear the SOF flag so the next time it's set we know we have a USB signal
USBD_CLR_INT_FLAG(USBD_INTSTS_SOFIF_Msk);
}
// Flush the USB CDC port every main loop just like LUFA does
USBCDC_Flush();
}
/** Sends a byte out the USB serial port
*
* @param b The byte
*/
void USBCDC_SendByte(uint8_t b)
{
// Fill up our buffer to send out the USB serial port
cdcTxBuf[cdcTxBufPos++] = b;
if (cdcTxBufPos == EP3_MAX_PKT_SIZE)
{
// If we reached a full packet size, send the data in the buffer
USBCDC_SendDataInBuffer();
}
}
/** Reads a byte from the USB serial port, if available
*
* @return The byte, or -1 if there is nothing available
*/
int16_t USBCDC_ReadByte(void)
{
// Assume not ready
int16_t ret = -1;
// If we have data ready to read out of the USB controller's endpoint buffer, grab it now
if (cdcRxLen == 0 && cdcRxReady)
{
// Flag that we handled the read event
cdcRxReady = false;
// Reset our read pointer just in case
cdcRxPos = 0;
// Read all of the data out from the USB controller
cdcRxLen = USBD_GET_PAYLOAD_LEN(EP4);
USBD_MemCopy(cdcRxBuf, (uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP4)), cdcRxLen);
// We grabbed all of the packet data, so tell the USB controller we're done with it
USBD_SET_PAYLOAD_LEN(EP4, EP4_MAX_PKT_SIZE);
}
// If we have something left in our buffer since the last read, use it
if (cdcRxLen > 0)
{
ret = cdcRxBuf[cdcRxPos++];
// If we finished reading from the buffer, mark it as finished.
if (cdcRxPos == cdcRxLen)
{
cdcRxPos = 0;
cdcRxLen = 0;
}
}
return ret;
}
/** Sends out any remaining data in the TX buffer
*
*/
void USBCDC_Flush(void)
{
// If we have something waiting to send, send it out now
if (cdcTxBufPos > 0)
{
bool needsZLP = cdcTxBufPos == EP3_MAX_PKT_SIZE;
USBCDC_SendDataInBuffer();
// If we are flushing after sending a full packet of data,
// send a ZLP afterward to indicate end of transmit to host
if (needsZLP)
{
USBCDC_SendDataInBuffer();
}
}
}
/** Sends the data in the EP3 TX buffer to the host
*
*/
static void USBCDC_SendDataInBuffer(void)
{
// Wait for any previous transmit to finish first
while (cdcTxActive);
// Send out the packet
USBD_MemCopy((uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP3)), cdcTxBuf, cdcTxBufPos);
cdcTxActive = true;
USBD_SET_PAYLOAD_LEN(EP3, cdcTxBufPos);
// Reset our buffer position; we've given all the data to the USB controller
cdcTxBufPos = 0;
}
/** IRQ handler called when USB endpoint 3 is ready (CDC TX data finished transferring)
*
*/
void EP3_Handler(void)
{
// All we have to do is flag that we no longer have an active transmission.
// USBCDC_SendDataInBuffer() will handle the rest.
cdcTxActive = false;
}
/** IRQ handler called when USB endpoint 4 is ready (CDC RX data ready to read)
*
*/
void EP4_Handler(void)
{
// Verify the toggle has changed. If it's still the same, re-request the data.
if (ep4OutToggle == (USBD->EPSTS0 & USBD_EPSTS0_EPSTS4_Msk))
{
USBD_SET_PAYLOAD_LEN(EP4, EP4_MAX_PKT_SIZE);
}
// Toggle changed, so we're good to go. Toggle for next time and flag that we
// have data ready to read. USBCDC_ReadByte() will handle the rest.
else
{
ep4OutToggle = USBD->EPSTS0 & USBD_EPSTS0_EPSTS4_Msk;
cdcRxReady = true;
}
}
/** Main IRQ handler for USB device controller
*
*/
void USBD_IRQHandler(void)
{
uint32_t intStatus = USBD_GET_INT_FLAG();
uint32_t busState = USBD_GET_BUS_STATE();
// Bus interrupt
if (intStatus & USBD_INTSTS_BUSIF_Msk)
{
// Clear the flag
USBD_CLR_INT_FLAG(USBD_INTSTS_BUSIF_Msk);
// The bus was reset - SE0 status for long enough
if (busState & USBD_STATE_USBRST)
{
// Enable USB, reset everything
USBD_ENABLE_USB();
USBD_SwReset();
ep4OutToggle = 0;
}
// Entered suspend status (bus idle)
if (busState & USBD_STATE_SUSPEND)
{
// Disable PHY while we're suspended
USBD_DISABLE_PHY();
}
if (busState & USBD_STATE_RESUME)
{
// Re-enable USB (mainly just the PHY is what we want)
USBD_ENABLE_USB();
}
}
// USB event
if (intStatus & USBD_INTSTS_USBIF_Msk)
{
// Setup packet?
if (intStatus & USBD_INTSTS_SETUP_Msk)
{
// Clear the flag...
USBD_CLR_INT_FLAG(USBD_INTSTS_SETUP);
// Stop EP0 and EP1 which are the control endpoints
USBD_STOP_TRANSACTION(EP0);
USBD_STOP_TRANSACTION(EP1);
// Now handle the received setup packet
USBD_ProcessSetupPacket();
}
// EP0 - control in
if (intStatus & USBD_INTSTS_EPEVT0_Msk)
{
USBD_CLR_INT_FLAG(USBD_INTSTS_EPEVT0_Msk);
USBD_CtrlIn();
}
// EP1 - control out
if (intStatus & USBD_INTSTS_EPEVT1_Msk)
{
USBD_CLR_INT_FLAG(USBD_INTSTS_EPEVT1_Msk);
USBD_CtrlOut();
}
// Do nothing for EP2 for now
// EP3 - bulk in for CDC data
if (intStatus & USBD_INTSTS_EPEVT3_Msk)
{
USBD_CLR_INT_FLAG(USBD_INTSTS_EPEVT3_Msk);
EP3_Handler();
}
// EP4 - bulk out for CDC data
if (intStatus & USBD_INTSTS_EPEVT4_Msk)
{
USBD_CLR_INT_FLAG(USBD_INTSTS_EPEVT4_Msk);
EP4_Handler();
}
}
}
/** Initializes endpoint buffers in USB SRAM
*
*/
void USBCDC_InitEndpoints(void)
{
// First 8 bytes are for setup packets
USBD->STBUFSEG = SETUP_BUF_BASE;
// EP0 = control IN, address 0, 64 bytes
USBD_CONFIG_EP(EP0, USBD_CFG_CSTALL | USBD_CFG_EPMODE_IN | 0);
USBD_SET_EP_BUF_ADDR(EP0, EP0_BUF_BASE);
// EP1 = control OUT, address 0, uses same 64 bytes as control IN
USBD_CONFIG_EP(EP1, USBD_CFG_CSTALL | USBD_CFG_EPMODE_OUT | 0);
USBD_SET_EP_BUF_ADDR(EP1, EP1_BUF_BASE);
// EP2 = interrupt IN, address 2, 8 bytes
USBD_CONFIG_EP(EP2, USBD_CFG_EPMODE_IN | INT_IN_EP_NUM);
USBD_SET_EP_BUF_ADDR(EP2, EP2_BUF_BASE);
// EP3 = bulk IN, address 3, 64 bytes
USBD_CONFIG_EP(EP3, USBD_CFG_EPMODE_IN | BULK_IN_EP_NUM);
USBD_SET_EP_BUF_ADDR(EP3, EP3_BUF_BASE);
// EP4 = bulk OUT, address 4, 64 bytes
USBD_CONFIG_EP(EP4, USBD_CFG_EPMODE_OUT | BULK_OUT_EP_NUM);
USBD_SET_EP_BUF_ADDR(EP4, EP4_BUF_BASE);
// Mark that we are able to receive on EP4
USBD_SET_PAYLOAD_LEN(EP4, EP4_MAX_PKT_SIZE);
}
/** USB class request callback for CDC device
*
*/
void USBCDC_ClassRequest(void)
{
uint8_t buf[8];
// Grab the setup packet...
USBD_GetSetupPacket(buf);
// Device to host?
if (buf[0] & 0x80)
{
switch (buf[1])
{
case GET_LINE_CODE:
if (buf[4] == 0)
{
USBD_MemCopy((uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP0)), (uint8_t *)&cdcLineCoding, 7);
}
// Data stage
USBD_SET_DATA1(EP0);
USBD_SET_PAYLOAD_LEN(EP0, 7);
// Status stage
USBD_PrepareCtrlOut(0, 0);
break;
default:
// Stall
USBD_SET_EP_STALL(EP0);
USBD_SET_EP_STALL(EP1);
break;
}
}
// Host to device
else
{
switch (buf[1])
{
case SET_CONTROL_LINE_STATE:
if (buf[4] == 0)
{
cdcCtrlSignal = (buf[3] << 8) | buf[2];
}
// Status stage
USBD_SET_DATA1(EP0);
USBD_SET_PAYLOAD_LEN(EP0, 0);
break;
case SET_LINE_CODE:
if (buf[4] == 0)
{
USBD_PrepareCtrlOut((uint8_t *)&cdcLineCoding, 7);
}
// Status stage
USBD_SET_DATA1(EP0);
USBD_SET_PAYLOAD_LEN(EP0, 0);
break;
default:
// Stall
USBD_SET_EP_STALL(EP0);
USBD_SET_EP_STALL(EP1);
break;
}
}
}

113
hal/m258ke/usbcdc_hw.h Normal file
View File

@ -0,0 +1,113 @@
/*
* usbcdc_hw.h
*
* Created on: Jun 19, 2023
* Author: Doug
*
* Copyright (C) 2011-2023 Doug Brown
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* Portions of this code also came from Nuvoton's BSP, originally
* licensed as Apache-2.0:
* @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved.
*
*/
#ifndef HAL_M258KE_USBCDC_HW_H_
#define HAL_M258KE_USBCDC_HW_H_
#include <stdint.h>
#include <stdbool.h>
#include "nuvoton/NuMicro.h"
#include "nuvoton/usbd.h"
/// Maximum packet sizes of each endpoint
#define EP0_MAX_PKT_SIZE 64
#define EP1_MAX_PKT_SIZE 64
#define EP2_MAX_PKT_SIZE 8
#define EP3_MAX_PKT_SIZE 64
#define EP4_MAX_PKT_SIZE 64
/// Assigned endpoint numbers for CDC serial port
#define INT_IN_EP_NUM 2
#define BULK_IN_EP_NUM 3
#define BULK_OUT_EP_NUM 4
/** Initializes the USB CDC serial port
*
*/
void USBCDC_Init(void);
/** Disconnects the USB CDC device from the host
*
*/
static inline void USBCDC_Disable(void)
{
USBD_SET_SE0();
}
/** Performs any necessary periodic tasks for the USB CDC serial port
*
*/
void USBCDC_Check(void);
/** Sends a byte out the USB serial port
*
* @param b The byte
*/
void USBCDC_SendByte(uint8_t b);
/** Sends a block of data over the USB CDC serial port
*
* @param data The data to send
* @param len The number of bytes
* @return True on success, false on failure
*/
static inline bool USBCDC_SendData(uint8_t const *data, uint16_t len)
{
while (len--)
{
USBCDC_SendByte(*data++);
}
return true;
}
/** Reads a byte from the USB serial port, if available
*
* @return The byte, or -1 if there is nothing available
*/
int16_t USBCDC_ReadByte(void);
/** Reads a byte from the USB CDC serial port. Blocks until one is available.
*
* @return The byte read
*/
static inline uint8_t USBCDC_ReadByteBlocking(void)
{
int16_t b;
do
{
b = USBCDC_ReadByte();
} while (b < 0);
return (uint8_t)b;
}
/** Flushes remaining data out to the USB serial port
*
*/
void USBCDC_Flush(void);
#endif /* HAL_M258KE_USBCDC_HW_H_ */