Browse Source

teensy3: usb_serial: Extract core usb_serial_port

Extract core USB serial functionality in a new usb_serial_port C class,
to allow creating multiple USB serial instances without duplicating all
code.

The original API is retained by creating static inline functions and
preprocessor macros using the old names, which are wrappers around the
new usb_serial_port functions and data members.

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
teensy4-core
Geert Uytterhoeven 4 years ago
parent
commit
6fb4c61b45
5 changed files with 610 additions and 404 deletions
  1. +7
    -268
      teensy3/usb_serial.c
  2. +1
    -136
      teensy3/usb_serial.h
  3. +300
    -0
      teensy3/usb_serial_port.c
  4. +78
    -0
      teensy3/usb_serial_port.h
  5. +224
    -0
      teensy3/usb_serial_template.h

+ 7
- 268
teensy3/usb_serial.c View File

@@ -29,278 +29,17 @@
*/

#include "usb_dev.h"
#include "usb_serial.h"
#include "core_pins.h" // for yield()
//#include "HardwareSerial.h"
#include <string.h> // for memcpy()

// defined by usb_dev.h -> usb_desc.h
#if defined(CDC_STATUS_INTERFACE) && defined(CDC_DATA_INTERFACE)
#if F_CPU >= 20000000

uint32_t usb_cdc_line_coding[2];
volatile uint32_t usb_cdc_line_rtsdtr_millis;
volatile uint8_t usb_cdc_line_rtsdtr=0;
volatile uint8_t usb_cdc_transmit_flush_timer=0;

static usb_packet_t *rx_packet=NULL;
static usb_packet_t *tx_packet=NULL;
static volatile uint8_t tx_noautoflush=0;

#define TRANSMIT_FLUSH_TIMEOUT 5 /* in milliseconds */

// get the next character, or -1 if nothing received
int usb_serial_getchar(void)
{
unsigned int i;
int c;

if (!rx_packet) {
if (!usb_configuration) return -1;
rx_packet = usb_rx(CDC_RX_ENDPOINT);
if (!rx_packet) return -1;
}
i = rx_packet->index;
c = rx_packet->buf[i++];
if (i >= rx_packet->len) {
usb_free(rx_packet);
rx_packet = NULL;
} else {
rx_packet->index = i;
}
return c;
}

// peek at the next character, or -1 if nothing received
int usb_serial_peekchar(void)
{
if (!rx_packet) {
if (!usb_configuration) return -1;
rx_packet = usb_rx(CDC_RX_ENDPOINT);
if (!rx_packet) return -1;
}
if (!rx_packet) return -1;
return rx_packet->buf[rx_packet->index];
}

// number of bytes available in the receive buffer
int usb_serial_available(void)
{
int count;
count = usb_rx_byte_count(CDC_RX_ENDPOINT);
if (rx_packet) count += rx_packet->len - rx_packet->index;
return count;
}

// read a block of bytes to a buffer
int usb_serial_read(void *buffer, uint32_t size)
{
uint8_t *p = (uint8_t *)buffer;
uint32_t qty, count=0;

while (size) {
if (!usb_configuration) break;
if (!rx_packet) {
rx:
rx_packet = usb_rx(CDC_RX_ENDPOINT);
if (!rx_packet) break;
if (rx_packet->len == 0) {
usb_free(rx_packet);
goto rx;
}
}
qty = rx_packet->len - rx_packet->index;
if (qty > size) qty = size;
memcpy(p, rx_packet->buf + rx_packet->index, qty);
p += qty;
count += qty;
size -= qty;
rx_packet->index += qty;
if (rx_packet->index >= rx_packet->len) {
usb_free(rx_packet);
rx_packet = NULL;
}
}
return count;
}

// discard any buffered input
void usb_serial_flush_input(void)
{
usb_packet_t *rx;

if (!usb_configuration) return;
if (rx_packet) {
usb_free(rx_packet);
rx_packet = NULL;
}
while (1) {
rx = usb_rx(CDC_RX_ENDPOINT);
if (!rx) break;
usb_free(rx);
}
}

// Maximum number of transmit packets to queue so we don't starve other endpoints for memory
#define TX_PACKET_LIMIT 8

// When the PC isn't listening, how long do we wait before discarding data? If this is
// too short, we risk losing data during the stalls that are common with ordinary desktop
// software. If it's too long, we stall the user's program when no software is running.
#define TX_TIMEOUT_MSEC 70
#if F_CPU == 256000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 1706)
#elif F_CPU == 240000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 1600)
#elif F_CPU == 216000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 1440)
#elif F_CPU == 192000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 1280)
#elif F_CPU == 180000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 1200)
#elif F_CPU == 168000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 1100)
#elif F_CPU == 144000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 932)
#elif F_CPU == 120000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 764)
#elif F_CPU == 96000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 596)
#elif F_CPU == 72000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 512)
#elif F_CPU == 48000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 428)
#elif F_CPU == 24000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 262)
#endif

// When we've suffered the transmit timeout, don't wait again until the computer
// begins accepting data. If no software is running to receive, we'll just discard
// data as rapidly as Serial.print() can generate it, until there's something to
// actually receive it.
static uint8_t transmit_previous_timeout=0;


// transmit a character. 0 returned on success, -1 on error
int usb_serial_putchar(uint8_t c)
{
return usb_serial_write(&c, 1);
}


int usb_serial_write(const void *buffer, uint32_t size)
{
uint32_t ret = size;
uint32_t len;
uint32_t wait_count;
const uint8_t *src = (const uint8_t *)buffer;
uint8_t *dest;

tx_noautoflush = 1;
while (size > 0) {
if (!tx_packet) {
wait_count = 0;
while (1) {
if (!usb_configuration) {
tx_noautoflush = 0;
return -1;
}
if (usb_tx_packet_count(CDC_TX_ENDPOINT) < TX_PACKET_LIMIT) {
tx_noautoflush = 1;
tx_packet = usb_malloc();
if (tx_packet) break;
tx_noautoflush = 0;
}
if (++wait_count > TX_TIMEOUT || transmit_previous_timeout) {
transmit_previous_timeout = 1;
return -1;
}
yield();
}
}
transmit_previous_timeout = 0;
len = CDC_TX_SIZE - tx_packet->index;
if (len > size) len = size;
dest = tx_packet->buf + tx_packet->index;
tx_packet->index += len;
size -= len;
while (len-- > 0) *dest++ = *src++;
if (tx_packet->index >= CDC_TX_SIZE) {
tx_packet->len = CDC_TX_SIZE;
usb_tx(CDC_TX_ENDPOINT, tx_packet);
tx_packet = NULL;
}
usb_cdc_transmit_flush_timer = TRANSMIT_FLUSH_TIMEOUT;
}
tx_noautoflush = 0;
return ret;
}

int usb_serial_write_buffer_free(void)
{
uint32_t len;

tx_noautoflush = 1;
if (!tx_packet) {
if (!usb_configuration ||
usb_tx_packet_count(CDC_TX_ENDPOINT) >= TX_PACKET_LIMIT ||
(tx_packet = usb_malloc()) == NULL) {
tx_noautoflush = 0;
return 0;
}
}
len = CDC_TX_SIZE - tx_packet->index;
// TODO: Perhaps we need "usb_cdc_transmit_flush_timer = TRANSMIT_FLUSH_TIMEOUT"
// added here, so the SOF interrupt can't take away the available buffer
// space we just promised the user could write without blocking?
// But does this come with other performance downsides? Could it lead to
// buffer data never actually transmitting in some usage cases? More
// investigation is needed.
// https://github.com/PaulStoffregen/cores/issues/10#issuecomment-61514955
tx_noautoflush = 0;
return len;
}

void usb_serial_flush_output(void)
{
if (!usb_configuration) return;
tx_noautoflush = 1;
if (tx_packet) {
usb_cdc_transmit_flush_timer = 0;
tx_packet->len = tx_packet->index;
usb_tx(CDC_TX_ENDPOINT, tx_packet);
tx_packet = NULL;
} else {
usb_packet_t *tx = usb_malloc();
if (tx) {
usb_cdc_transmit_flush_timer = 0;
usb_tx(CDC_TX_ENDPOINT, tx);
} else {
usb_cdc_transmit_flush_timer = 1;
}
}
tx_noautoflush = 0;
}

void usb_serial_flush_callback(void)
{
if (tx_noautoflush) return;
if (tx_packet) {
tx_packet->len = tx_packet->index;
usb_tx(CDC_TX_ENDPOINT, tx_packet);
tx_packet = NULL;
} else {
usb_packet_t *tx = usb_malloc();
if (tx) {
usb_tx(CDC_TX_ENDPOINT, tx);
} else {
usb_cdc_transmit_flush_timer = 1;
}
}
}



#if F_CPU >= 20000000
#include "usb_serial_port.h"

struct usb_serial_port usb_serial_instance = {
.cdc_rx_endpoint = CDC_RX_ENDPOINT,
.cdc_tx_endpoint = CDC_TX_ENDPOINT,
.cdc_tx_size = CDC_TX_SIZE,
};
#endif // F_CPU
#endif // CDC_STATUS_INTERFACE && CDC_DATA_INTERFACE

+ 1
- 136
teensy3/usb_serial.h View File

@@ -34,142 +34,7 @@
#include "usb_desc.h"

#if (defined(CDC_STATUS_INTERFACE) && defined(CDC_DATA_INTERFACE)) || defined(USB_DISABLED)

#include <inttypes.h>

#if F_CPU >= 20000000 && !defined(USB_DISABLED)

#include "core_pins.h" // for millis()

// C language implementation
#ifdef __cplusplus
extern "C" {
#endif
int usb_serial_getchar(void);
int usb_serial_peekchar(void);
int usb_serial_available(void);
int usb_serial_read(void *buffer, uint32_t size);
void usb_serial_flush_input(void);
int usb_serial_putchar(uint8_t c);
int usb_serial_write(const void *buffer, uint32_t size);
int usb_serial_write_buffer_free(void);
void usb_serial_flush_output(void);
void usb_serial_flush_callback(void);
extern uint32_t usb_cdc_line_coding[2];
extern volatile uint32_t usb_cdc_line_rtsdtr_millis;
extern volatile uint32_t systick_millis_count;
extern volatile uint8_t usb_cdc_line_rtsdtr;
extern volatile uint8_t usb_cdc_transmit_flush_timer;
extern volatile uint8_t usb_configuration;
#ifdef __cplusplus
}
#endif

#define USB_SERIAL_DTR 0x01
#define USB_SERIAL_RTS 0x02

// C++ interface
#ifdef __cplusplus
#include "Stream.h"
class usb_serial_class : public Stream
{
public:
constexpr usb_serial_class() {}
void begin(long) {
//uint32_t millis_begin = systick_millis_count;
//disabled for now - causes more trouble than it solves?
//while (!(*this)) {
// wait up to 2.5 seconds for Arduino Serial Monitor
// Yes, this is a long time, but some Windows systems open
// the port very slowly. This wait allows programs for
// Arduino Uno to "just work" (without forcing a reboot when
// the port is opened), and when no PC is connected the user's
// sketch still gets to run normally after this wait time.
//if ((uint32_t)(systick_millis_count - millis_begin) > 2500) break;
//}
}
void end() { /* TODO: flush output and shut down USB port */ };
virtual int available() { return usb_serial_available(); }
virtual int read() { return usb_serial_getchar(); }
virtual int peek() { return usb_serial_peekchar(); }
virtual void flush() { usb_serial_flush_output(); } // TODO: actually wait for data to leave USB...
virtual void clear(void) { usb_serial_flush_input(); }
virtual size_t write(uint8_t c) { return usb_serial_putchar(c); }
virtual size_t write(const uint8_t *buffer, size_t size) { return usb_serial_write(buffer, size); }
size_t write(unsigned long n) { return write((uint8_t)n); }
size_t write(long n) { return write((uint8_t)n); }
size_t write(unsigned int n) { return write((uint8_t)n); }
size_t write(int n) { return write((uint8_t)n); }
virtual int availableForWrite() { return usb_serial_write_buffer_free(); }
using Print::write;
void send_now(void) { usb_serial_flush_output(); }
uint32_t baud(void) { return usb_cdc_line_coding[0]; }
uint8_t stopbits(void) { uint8_t b = usb_cdc_line_coding[1]; if (!b) b = 1; return b; }
uint8_t paritytype(void) { return usb_cdc_line_coding[1] >> 8; } // 0=none, 1=odd, 2=even
uint8_t numbits(void) { return usb_cdc_line_coding[1] >> 16; }
uint8_t dtr(void) { return (usb_cdc_line_rtsdtr & USB_SERIAL_DTR) ? 1 : 0; }
uint8_t rts(void) { return (usb_cdc_line_rtsdtr & USB_SERIAL_RTS) ? 1 : 0; }
operator bool() { return usb_configuration && (usb_cdc_line_rtsdtr & USB_SERIAL_DTR) &&
((uint32_t)(systick_millis_count - usb_cdc_line_rtsdtr_millis) >= 15);
}
size_t readBytes(char *buffer, size_t length) {
size_t count=0;
unsigned long startMillis = millis();
do {
count += usb_serial_read(buffer + count, length - count);
if (count >= length) return count;
} while(millis() - startMillis < _timeout);
setReadError();
return count;
}

};
extern usb_serial_class Serial;
extern void serialEvent(void);
#endif // __cplusplus


#else // F_CPU < 20000000

// Allow Arduino programs using Serial to compile, but Serial will do nothing.
#ifdef __cplusplus
#include "Stream.h"
class usb_serial_class : public Stream
{
public:
constexpr usb_serial_class() {}
void begin(long) { };
void end() { };
virtual int available() { return 0; }
virtual int read() { return -1; }
virtual int peek() { return -1; }
virtual void flush() { }
virtual void clear() { }
virtual size_t write(uint8_t c) { return 1; }
virtual size_t write(const uint8_t *buffer, size_t size) { return size; }
size_t write(unsigned long n) { return 1; }
size_t write(long n) { return 1; }
size_t write(unsigned int n) { return 1; }
size_t write(int n) { return 1; }
int availableForWrite() { return 0; }
using Print::write;
void send_now(void) { }
uint32_t baud(void) { return 0; }
uint8_t stopbits(void) { return 1; }
uint8_t paritytype(void) { return 0; }
uint8_t numbits(void) { return 8; }
uint8_t dtr(void) { return 1; }
uint8_t rts(void) { return 1; }
operator bool() { return true; }
};

extern usb_serial_class Serial;
extern void serialEvent(void);
#endif // __cplusplus


#endif // F_CPU

#include "usb_serial_template.h"
#endif // CDC_STATUS_INTERFACE && CDC_DATA_INTERFACE

#endif // USBserial_h_

+ 300
- 0
teensy3/usb_serial_port.c View File

@@ -0,0 +1,300 @@
/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2017 PJRC.COM, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

#include "usb_dev.h"
#include "usb_serial_port.h"
#include "core_pins.h" // for yield()
//#include "HardwareSerial.h"
#include <string.h> // for memcpy()

// defined by usb_dev.h -> usb_desc.h
#if defined(CDC_STATUS_INTERFACE) && defined(CDC_DATA_INTERFACE)
#if F_CPU >= 20000000

#define TRANSMIT_FLUSH_TIMEOUT 5 /* in milliseconds */

// get the next character, or -1 if nothing received
int __usb_serial_getchar(struct usb_serial_port *port)
{
unsigned int i;
int c;

if (!port->rx_packet) {
if (!usb_configuration) return -1;
port->rx_packet = usb_rx(port->cdc_rx_endpoint);
if (!port->rx_packet) return -1;
}
i = port->rx_packet->index;
c = port->rx_packet->buf[i++];
if (i >= port->rx_packet->len) {
usb_free(port->rx_packet);
port->rx_packet = NULL;
} else {
port->rx_packet->index = i;
}
return c;
}

// peek at the next character, or -1 if nothing received
int __usb_serial_peekchar(struct usb_serial_port *port)
{
if (!port->rx_packet) {
if (!usb_configuration) return -1;
port->rx_packet = usb_rx(port->cdc_rx_endpoint);
if (!port->rx_packet) return -1;
}
if (!port->rx_packet) return -1;
return port->rx_packet->buf[port->rx_packet->index];
}

// number of bytes available in the receive buffer
int __usb_serial_available(struct usb_serial_port *port)
{
int count;
count = usb_rx_byte_count(port->cdc_rx_endpoint);
if (port->rx_packet)
count += port->rx_packet->len - port->rx_packet->index;
return count;
}

// read a block of bytes to a buffer
int __usb_serial_read(struct usb_serial_port *port, void *buffer,
uint32_t size)
{
uint8_t *p = (uint8_t *)buffer;
uint32_t qty, count=0;

while (size) {
if (!usb_configuration) break;
if (!port->rx_packet) {
rx:
port->rx_packet = usb_rx(port->cdc_rx_endpoint);
if (!port->rx_packet) break;
if (port->rx_packet->len == 0) {
usb_free(port->rx_packet);
goto rx;
}
}
qty = port->rx_packet->len - port->rx_packet->index;
if (qty > size) qty = size;
memcpy(p, port->rx_packet->buf + port->rx_packet->index, qty);
p += qty;
count += qty;
size -= qty;
port->rx_packet->index += qty;
if (port->rx_packet->index >= port->rx_packet->len) {
usb_free(port->rx_packet);
port->rx_packet = NULL;
}
}
return count;
}

// discard any buffered input
void __usb_serial_flush_input(struct usb_serial_port *port)
{
usb_packet_t *rx;

if (!usb_configuration) return;
if (port->rx_packet) {
usb_free(port->rx_packet);
port->rx_packet = NULL;
}
while (1) {
rx = usb_rx(port->cdc_rx_endpoint);
if (!rx) break;
usb_free(rx);
}
}

// Maximum number of transmit packets to queue so we don't starve other endpoints for memory
#define TX_PACKET_LIMIT 8

// When the PC isn't listening, how long do we wait before discarding data? If this is
// too short, we risk losing data during the stalls that are common with ordinary desktop
// software. If it's too long, we stall the user's program when no software is running.
#define TX_TIMEOUT_MSEC 70
#if F_CPU == 256000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 1706)
#elif F_CPU == 240000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 1600)
#elif F_CPU == 216000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 1440)
#elif F_CPU == 192000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 1280)
#elif F_CPU == 180000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 1200)
#elif F_CPU == 168000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 1100)
#elif F_CPU == 144000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 932)
#elif F_CPU == 120000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 764)
#elif F_CPU == 96000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 596)
#elif F_CPU == 72000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 512)
#elif F_CPU == 48000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 428)
#elif F_CPU == 24000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 262)
#endif

// When we've suffered the transmit timeout, don't wait again until the computer
// begins accepting data. If no software is running to receive, we'll just discard
// data as rapidly as Serial.print() can generate it, until there's something to
// actually receive it.
static uint8_t transmit_previous_timeout=0;


// transmit a character. 0 returned on success, -1 on error
int __usb_serial_putchar(struct usb_serial_port *port, uint8_t c)
{
return __usb_serial_write(port, &c, 1);
}


int __usb_serial_write(struct usb_serial_port *port, const void *buffer,
uint32_t size)
{
uint32_t ret = size;
uint32_t len;
uint32_t wait_count;
const uint8_t *src = (const uint8_t *)buffer;
uint8_t *dest;

port->tx_noautoflush = 1;
while (size > 0) {
if (!port->tx_packet) {
wait_count = 0;
while (1) {
if (!usb_configuration) {
port->tx_noautoflush = 0;
return -1;
}
if (usb_tx_packet_count(port->cdc_tx_endpoint) < TX_PACKET_LIMIT) {
port->tx_noautoflush = 1;
port->tx_packet = usb_malloc();
if (port->tx_packet) break;
port->tx_noautoflush = 0;
}
if (++wait_count > TX_TIMEOUT || transmit_previous_timeout) {
transmit_previous_timeout = 1;
return -1;
}
yield();
}
}
transmit_previous_timeout = 0;
len = port->cdc_tx_size - port->tx_packet->index;
if (len > size) len = size;
dest = port->tx_packet->buf + port->tx_packet->index;
port->tx_packet->index += len;
size -= len;
while (len-- > 0) *dest++ = *src++;
if (port->tx_packet->index >= port->cdc_tx_size) {
port->tx_packet->len = port->cdc_tx_size;
usb_tx(port->cdc_tx_endpoint, port->tx_packet);
port->tx_packet = NULL;
}
port->cdc_transmit_flush_timer = TRANSMIT_FLUSH_TIMEOUT;
}
port->tx_noautoflush = 0;
return ret;
}

int __usb_serial_write_buffer_free(struct usb_serial_port *port)
{
uint32_t len;

port->tx_noautoflush = 1;
if (!port->tx_packet) {
if (!usb_configuration ||
usb_tx_packet_count(port->cdc_tx_endpoint) >= TX_PACKET_LIMIT ||
(port->tx_packet = usb_malloc()) == NULL) {
port->tx_noautoflush = 0;
return 0;
}
}
len = port->cdc_tx_size - port->tx_packet->index;
// TODO: Perhaps we need "port->cdc_transmit_flush_timer = TRANSMIT_FLUSH_TIMEOUT"
// added here, so the SOF interrupt can't take away the available buffer
// space we just promised the user could write without blocking?
// But does this come with other performance downsides? Could it lead to
// buffer data never actually transmitting in some usage cases? More
// investigation is needed.
// https://github.com/PaulStoffregen/cores/issues/10#issuecomment-61514955
port->tx_noautoflush = 0;
return len;
}

void __usb_serial_flush_output(struct usb_serial_port *port)
{
if (!usb_configuration) return;
port->tx_noautoflush = 1;
if (port->tx_packet) {
port->cdc_transmit_flush_timer = 0;
port->tx_packet->len = port->tx_packet->index;
usb_tx(port->cdc_tx_endpoint, port->tx_packet);
port->tx_packet = NULL;
} else {
usb_packet_t *tx = usb_malloc();
if (tx) {
port->cdc_transmit_flush_timer = 0;
usb_tx(port->cdc_tx_endpoint, tx);
} else {
port->cdc_transmit_flush_timer = 1;
}
}
port->tx_noautoflush = 0;
}

void __usb_serial_flush_callback(struct usb_serial_port *port)
{
if (port->tx_noautoflush) return;
if (port->tx_packet) {
port->tx_packet->len = port->tx_packet->index;
usb_tx(port->cdc_tx_endpoint, port->tx_packet);
port->tx_packet = NULL;
} else {
usb_packet_t *tx = usb_malloc();
if (tx) {
usb_tx(port->cdc_tx_endpoint, tx);
} else {
port->cdc_transmit_flush_timer = 1;
}
}
}




#endif // F_CPU
#endif // CDC_STATUS_INTERFACE && CDC_DATA_INTERFACE

+ 78
- 0
teensy3/usb_serial_port.h View File

@@ -0,0 +1,78 @@
/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2017 PJRC.COM, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

#ifndef usb_serial_port_h_
#define usb_serial_port_h_

#if F_CPU >= 20000000 && !defined(USB_DISABLED)

// C language implementation
#ifdef __cplusplus
extern "C" {
#endif
struct usb_packet_struct;
struct usb_serial_port {
/* public */
uint32_t cdc_line_coding[2];
volatile uint32_t cdc_line_rtsdtr_millis;
volatile uint8_t cdc_line_rtsdtr;
volatile uint8_t cdc_transmit_flush_timer;

/* private */
struct usb_packet_struct *rx_packet;
struct usb_packet_struct *tx_packet;
volatile uint8_t tx_noautoflush;
const uint8_t cdc_rx_endpoint;
const uint8_t cdc_tx_endpoint;
const uint8_t cdc_tx_size;
};

extern volatile uint32_t systick_millis_count;
extern volatile uint8_t usb_configuration;

int __usb_serial_getchar(struct usb_serial_port *port);
int __usb_serial_peekchar(struct usb_serial_port *port);
int __usb_serial_available(struct usb_serial_port *port);
int __usb_serial_read(struct usb_serial_port *port, void *buffer,
uint32_t size);
void __usb_serial_flush_input(struct usb_serial_port *port);
int __usb_serial_putchar(struct usb_serial_port *port, uint8_t c);
int __usb_serial_write(struct usb_serial_port *port, const void *buffer,
uint32_t size);
int __usb_serial_write_buffer_free(struct usb_serial_port *port);
void __usb_serial_flush_output(struct usb_serial_port *port);
void __usb_serial_flush_callback(struct usb_serial_port *port);
#ifdef __cplusplus
}
#endif

#endif // F_CPU

#endif // usb_serial_port_h_

+ 224
- 0
teensy3/usb_serial_template.h View File

@@ -0,0 +1,224 @@
/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2017 PJRC.COM, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

#include <inttypes.h>

#include "usb_serial_port.h"

#if F_CPU >= 20000000 && !defined(USB_DISABLED)

#include "core_pins.h" // for millis()

// C language implementation
#ifdef __cplusplus
extern "C" {
#endif
extern struct usb_serial_port usb_serial_instance;

static inline int usb_serial_getchar(void)
{
return __usb_serial_getchar(&usb_serial_instance);
}

static inline int usb_serial_peekchar(void)
{
return __usb_serial_peekchar(&usb_serial_instance);
}

static inline int usb_serial_available(void)
{
return __usb_serial_available(&usb_serial_instance);
}

static inline int usb_serial_read(void *buffer, uint32_t size)
{
return __usb_serial_read(&usb_serial_instance, buffer, size);
}

static inline void usb_serial_flush_input(void)
{
__usb_serial_flush_input(&usb_serial_instance);
}

static inline int usb_serial_putchar(uint8_t c)
{
return __usb_serial_putchar(&usb_serial_instance, c);
}

static inline int usb_serial_write(const void *buffer, uint32_t size)
{
return __usb_serial_write(&usb_serial_instance, buffer, size);
}

static inline int usb_serial_write_buffer_free(void)
{
return __usb_serial_write_buffer_free(&usb_serial_instance);
}

static inline void usb_serial_flush_output(void)
{
__usb_serial_flush_output(&usb_serial_instance);
}

static inline void usb_serial_flush_callback(void)
{
__usb_serial_flush_callback(&usb_serial_instance);
}

#define usb_cdc_line_coding usb_serial_instance.cdc_line_coding
#define usb_cdc_line_rtsdtr_millis usb_serial_instance.cdc_line_rtsdtr_millis
#define usb_cdc_line_rtsdtr usb_serial_instance.cdc_line_rtsdtr
#define usb_cdc_transmit_flush_timer usb_serial_instance.cdc_transmit_flush_timer

#ifdef __cplusplus
}
#endif

#define USB_SERIAL_DTR 0x01
#define USB_SERIAL_RTS 0x02

// C++ interface
#ifdef __cplusplus
#include "Stream.h"
class usb_serial_class : public Stream
{
public:
constexpr usb_serial_class() {}
void begin(long) {
//uint32_t millis_begin = systick_millis_count;
//disabled for now - causes more trouble than it solves?
//while (!(*this)) {
// wait up to 2.5 seconds for Arduino Serial Monitor
// Yes, this is a long time, but some Windows systems open
// the port very slowly. This wait allows programs for
// Arduino Uno to "just work" (without forcing a reboot when
// the port is opened), and when no PC is connected the user's
// sketch still gets to run normally after this wait time.
//if ((uint32_t)(systick_millis_count - millis_begin) > 2500) break;
//}
}
void end() { /* TODO: flush output and shut down USB port */ };
virtual int available() { return usb_serial_available(); }
virtual int read() { return usb_serial_getchar(); }
virtual int peek() { return usb_serial_peekchar(); }
virtual void flush() { usb_serial_flush_output(); } // TODO: actually wait for data to leave USB...
virtual void clear(void) { usb_serial_flush_input(); }
virtual size_t write(uint8_t c) { return usb_serial_putchar(c); }
virtual size_t write(const uint8_t *buffer, size_t size) { return usb_serial_write(buffer, size); }
size_t write(unsigned long n) { return write((uint8_t)n); }
size_t write(long n) { return write((uint8_t)n); }
size_t write(unsigned int n) { return write((uint8_t)n); }
size_t write(int n) { return write((uint8_t)n); }
virtual int availableForWrite() { return usb_serial_write_buffer_free(); }
using Print::write;
void send_now(void) { usb_serial_flush_output(); }
uint32_t baud(void) { return usb_serial_instance.cdc_line_coding[0]; }
uint8_t stopbits(void) {
uint8_t b = usb_serial_instance.cdc_line_coding[1];
if (!b) b = 1;
return b;
}
uint8_t paritytype(void) {
// 0=none, 1=odd, 2=even
return usb_serial_instance.cdc_line_coding[1] >> 8;
}
uint8_t numbits(void) {
return usb_serial_instance.cdc_line_coding[1] >> 16;
}
uint8_t dtr(void) {
return (usb_serial_instance.cdc_line_rtsdtr & USB_SERIAL_DTR) ?
1 : 0;
}
uint8_t rts(void) {
return (usb_serial_instance.cdc_line_rtsdtr & USB_SERIAL_RTS) ?
1 : 0;
}
operator bool() {
return usb_configuration &&
(usb_serial_instance.cdc_line_rtsdtr & USB_SERIAL_DTR) &&
((uint32_t)(systick_millis_count - usb_serial_instance.cdc_line_rtsdtr_millis) >= 15);
}
size_t readBytes(char *buffer, size_t length) {
size_t count=0;
unsigned long startMillis = millis();
do {
count += usb_serial_read(buffer + count, length - count);
if (count >= length) return count;
} while(millis() - startMillis < _timeout);
setReadError();
return count;
}

};
extern usb_serial_class Serial;
extern void serialEvent(void);
#endif // __cplusplus


#else // F_CPU < 20000000

// Allow Arduino programs using Serial to compile, but Serial will do nothing.
#ifdef __cplusplus
#include "Stream.h"
class usb_serial_class : public Stream
{
public:
constexpr usb_serial_class() {}
void begin(long) { };
void end() { };
virtual int available() { return 0; }
virtual int read() { return -1; }
virtual int peek() { return -1; }
virtual void flush() { }
virtual void clear() { }
virtual size_t write(uint8_t c) { return 1; }
virtual size_t write(const uint8_t *buffer, size_t size) { return size; }
size_t write(unsigned long n) { return 1; }
size_t write(long n) { return 1; }
size_t write(unsigned int n) { return 1; }
size_t write(int n) { return 1; }
int availableForWrite() { return 0; }
using Print::write;
void send_now(void) { }
uint32_t baud(void) { return 0; }
uint8_t stopbits(void) { return 1; }
uint8_t paritytype(void) { return 0; }
uint8_t numbits(void) { return 8; }
uint8_t dtr(void) { return 1; }
uint8_t rts(void) { return 1; }
operator bool() { return true; }
};

extern usb_serial_class Serial;
extern void serialEvent(void);
#endif // __cplusplus


#endif // F_CPU

Loading…
Cancel
Save