mirror of
https://github.com/dougg3/mac-rom-simm-programmer.git
synced 2024-12-01 19:50:18 +00:00
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:
parent
7dd0f3f9db
commit
7d83075f10
194
hal/m258ke/descriptors.c
Normal file
194
hal/m258ke/descriptors.c
Normal 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
468
hal/m258ke/usbcdc.c
Normal 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
113
hal/m258ke/usbcdc_hw.h
Normal 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_ */
|
Loading…
Reference in New Issue
Block a user