Added teensy mods

This commit is contained in:
Lukas Bachschwell 2016-07-15 13:23:57 +02:00
parent 82775e8c66
commit f95db9c535
4 changed files with 857 additions and 0 deletions

View File

@ -0,0 +1,91 @@
Teensy-USB-MIDI-CLOCK-SEQUENER-INTERFACE
========================================
USB MIDI CLOCK SYNC FOR ANALOG CV/GATE SEQUENCERS!
Original Source! http://www.goo.gl/Xh2skw
// Teensyduino MIDI Beat Clock Example
// by Sebastian Tomczak
// 29 August 2011
How To Deal With Real Time MIDI Beat Clock Data in Teensyduino
The Teensy is an affordable, compact and easy-to-use microcontroller development board. Part of the attraction of Teensy is theTeensyduino add-on for the Arduino software environment, which adds support for the Teensy board. One of the strengths of Teensy and Teensyduino is the ability to have the Teensy board appear as a USB HID MIDI device. This allows for the creation of interfaces and projects that register as native USB MIDI devices on the major operating systems.
Currently, the usbMIDI.read() function in Teensyduino reads a range of standard MIDI commands (sent from the computer host to the Teensy board), such as note on, note off, continuous controllers etc. However, there is no support for detecting and dealing with MIDI sync signals (also known as MIDI beat clock and real time system bytes).
I've edited the Teensyduino package to include a function made for dealing with real time system bytes. Thanks to Paul Stoffregen for his fantastic work with the Teensy board, and for his help in pushing me in the right direction with this. My edited files can be found here:http://milkcrate.com.au/_other/downloads/other/TeensyduinoRealTimeClockUpdate_20110829/
To add the functions to the Teensyduino code, simply right-click on the Arduino application and select "Show Package Contents". Copy and replace the files from the link above to Contents > Resources > Java > hardware > teensy > cores > usb_midi.
This will add a new handle (setHandleRealTimeSystem()) and a new function (RealTimeSystem()) that can be used in conjunction with usbMIDI.read() to receive MIDI beat clock data. To use the new code, set up a MIDI input sketch as you would normally with Teensyduino. Then:
1) add:
usbMIDI.setHandleRealTimeSystem(RealTimeSystem);
to void setup()
2) add a new function to the sketch:
void RealTimeSystem(byte realtimebyte) {
}
In the new function in (2) above, the byte realtimebyte is used for the MIDI beat clock data. The following bytes indicate which MIDI beat clock bytes have been received.
clock tick byte (at 24 pulses per quarter note) = 248;
start byte = 250;
continue byte = 251;
stop byte = 252;
Below is a working example, with code and a demo video. The LED that is part of the Teensy board lights up for every quarter note of sync data that is received via the USB cable.
// Teensyduino MIDI Beat Clock Example
// by Sebastian Tomczak
// 29 August 2011
byte counter;
byte CLOCK = 248;
byte START = 250;
byte CONTINUE = 251;
byte STOP = 252;
void setup() {
Serial.begin(31250);
pinMode(11, OUTPUT);
digitalWrite(11, HIGH);
usbMIDI.setHandleRealTimeSystem(RealTimeSystem);
}
void loop() {
usbMIDI.read();
}
void RealTimeSystem(byte realtimebyte) {
if(realtimebyte == 248) {
counter++;
if(counter == 24) {
counter = 0;
digitalWrite(11, HIGH);
}
if(counter == 12) {
digitalWrite(11, LOW);
}
}
if(realtimebyte == START || realtimebyte == CONTINUE) {
counter = 0;
digitalWrite(11, HIGH);
}
if(realtimebyte == STOP) {
digitalWrite(11, LOW);
}
}

View File

@ -0,0 +1,80 @@
// Teensy2.0 MIDI CLOCK INTERFACE //
// by 2kohm //
// 18. Aprill 2014 //
// http://stromakustik.de //
// //
// Teensyduino MIDI Beat Clock //
// by Sebastian Tomczak //
// 29 August 2011 //
// http://www.goo.gl/Xh2skw //
// Bytes
byte counter;
byte CLOCK = 1;
byte START = 1;
byte CONTINUE = 5;
byte STOP = 20;
byte divider;
// Var
int pot1 = 0;
int clockdivider = clockdivider; // MIDI Clock Divider
// Setup
void setup()
{
Serial.begin(31250);
pinMode(10, OUTPUT);
pinMode(1, INPUT);
pinMode(11, OUTPUT);
digitalWrite(10, HIGH);
// MIDI RealTime System
usbMIDI.setHandleRealTimeSystem(RealTimeSystem);
}
void loop()
{
// Midi Clock Divider
divider = (analogRead(0)/250); // Read Pot 1 (scale 0 - 4)
if(divider == 0) { clockdivider = 24;} // 1/4
if(divider == 1) { clockdivider = 12;} // 1/8
if(divider == 2) { clockdivider = 6;} // 1/16
if(divider == 3) { clockdivider = 3;} // 1/32
if(divider == 4) { clockdivider = 2;} // 1/64
Serial.println(divider); // Display the Pot value ( 0 - 4)
usbMIDI.read();
}
void RealTimeSystem(byte realtimebyte)
{
if(realtimebyte == 248)
{
counter++;
if(counter == clockdivider)
{
counter = 0;
digitalWrite(10, HIGH);
}
if(counter == 1)
{
digitalWrite(10, LOW);
}
}
if(realtimebyte == START || realtimebyte == CONTINUE)
{
counter = 0;
digitalWrite(10, HIGH) ;
}
if(realtimebyte == STOP)
{
digitalWrite(10, LOW);
}
}

View File

@ -0,0 +1,561 @@
/* USB API for Teensy USB Development Board
* http://www.pjrc.com/teensy/teensyduino.html
* Copyright (c) 2008 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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 <avr/io.h>
#include <avr/pgmspace.h>
#include <stdint.h>
#include "usb_common.h"
#include "usb_private.h"
#include "usb_api.h"
#include "wiring.h"
void usb_midi_class::sendNoteOff(uint8_t note, uint8_t velocity, uint8_t channel)
{
send_raw(0x08, 0x80 | ((channel - 1) & 0x0F), note & 0x7F, velocity & 0x7F);
}
void usb_midi_class::sendNoteOn(uint8_t note, uint8_t velocity, uint8_t channel)
{
send_raw(0x09, 0x90 | ((channel - 1) & 0x0F), note & 0x7F, velocity & 0x7F);
}
void usb_midi_class::sendPolyPressure(uint8_t note, uint8_t pressure, uint8_t channel)
{
send_raw(0x0A, 0xA0 | ((channel - 1) & 0x0F), note & 0x7F, pressure & 0x7F);
}
void usb_midi_class::sendControlChange(uint8_t control, uint8_t value, uint8_t channel)
{
send_raw(0x0B, 0xB0 | ((channel - 1) & 0x0F), control & 0x7F, value & 0x7F);
}
void usb_midi_class::sendProgramChange(uint8_t program, uint8_t channel)
{
send_raw(0x0C, 0xC0 | ((channel - 1) & 0x0F), program & 0x7F, 0);
}
void usb_midi_class::sendAfterTouch(uint8_t pressure, uint8_t channel)
{
send_raw(0x0D, 0xD0 | ((channel - 1) & 0x0F), pressure & 0x7F, 0);
}
void usb_midi_class::sendPitchBend(uint16_t value, uint8_t channel)
{
send_raw(0x0E, 0xE0 | ((channel - 1) & 0x0F), value & 0x7F, (value >> 7) & 0x7F);
}
void usb_midi_class::sendSysEx(uint8_t length, const uint8_t *data)
{
// TODO: MIDI 2.5 lib automatically adds start and stop bytes
while (length > 3) {
send_raw(0x04, data[0], data[1], data[2]);
data += 3;
length -= 3;
}
if (length == 3) {
send_raw(0x07, data[0], data[1], data[2]);
} else if (length == 2) {
send_raw(0x06, data[0], data[1], 0);
} else if (length == 1) {
send_raw(0x05, data[0], 0, 0);
}
}
void usb_midi_class::send_raw(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3)
{
uint8_t intr_state, timeout;
if (!usb_configuration) return;
intr_state = SREG;
cli();
UENUM = MIDI_TX_ENDPOINT;
timeout = UDFNUML + 2;
while (1) {
// are we ready to transmit?
if (UEINTX & (1<<RWAL)) break;
SREG = intr_state;
if (UDFNUML == timeout) return;
if (!usb_configuration) return;
intr_state = SREG;
cli();
UENUM = MIDI_TX_ENDPOINT;
}
UEDATX = b0;
UEDATX = b1;
UEDATX = b2;
UEDATX = b3;
if (!(UEINTX & (1<<RWAL))) UEINTX = 0x3A;
SREG = intr_state;
}
void usb_midi_class::send_now(void)
{
uint8_t intr_state;
if (!usb_configuration) return;
intr_state = SREG;
cli();
UENUM = MIDI_TX_ENDPOINT;
if (UEBCLX != MIDI_TX_SIZE) {
UEINTX = 0x3A;
}
SREG = intr_state;
}
// Convert 10 bit linear measurements to a logarthmic scale
// suitable for sending as MIDI velocity numbers. The
// "range" parameter should be probably be between 30 to 60,
// with 36 probably a good default.
//
// This function uses fast 16 bit unsigned integer math. :-)
//
uint8_t usb_midi_class::analog2velocity(uint16_t val, uint8_t range)
{
#if 0
if (val == 0) return 0;
float scale = 1.0 + (20.0 / (float)range) * log10((float)val / 1023.0);
if (scale < 0) return 0;
return 127 * scale;
#else
uint8_t i, e, b;
uint16_t s=0;
static const uint8_t PROGMEM table[] = {225,124,65,34,17,9,4,2,1};
if (val == 0) return 0;
if (val >= 1023) return 127;
for (e=0; (val & 512) == 0; e++) val <<= 1;
for (i=0; i<9; i++) { // cordic algorithm
uint16_t x = val + (val >> (i + 1));
if (x < 1024) {
val = x;
s += pgm_read_byte(table + i);
}
}
s += e * 385;
s <<= 4;
s += (range >> 1);
s /= range;
if (s >= 1024) return 0;
s = 1024 - s;
if (s > 511) {
s -= 512;
b = 64;
} else if (s > 255) {
s -= 256;
b = 32;
} else {
b = 0;
}
return b + ((s * 127) >> 10);
#endif
}
bool usb_midi_class::read(uint8_t channel)
{
uint8_t c, intr_state;
uint8_t b0, b1, b2, b3, type1, type2;
intr_state = SREG;
cli();
if (!usb_configuration) {
SREG = intr_state;
return false;
}
UENUM = MIDI_RX_ENDPOINT;
retry:
c = UEINTX;
if (!(c & (1<<RWAL))) {
if (c & (1<<RXOUTI)) {
UEINTX = 0x6B;
goto retry;
}
SREG = intr_state;
return false;
}
b0 = UEDATX;
b1 = UEDATX;
b2 = UEDATX;
b3 = UEDATX;
if (!(UEINTX & (1<<RWAL))) UEINTX = 0x6B;
SREG = intr_state;
type1 = b0 & 0x0F;
type2 = b1 & 0xF0;
c = b1 & 0x0F;
if (type1 >= 0x08 && type1 <= 0x0F) {
if (channel && channel != c) {
// ignore other channels when user wants single channel read
return false;
}
if (type1 == 0x08 && type2 == 0x80) {
msg_type = 0; // Note off
if (handleNoteOff) (*handleNoteOff)(c, b2, b3);
goto return_message;
}
if (type1 == 0x09 && type2 == 0x90) {
if (b3) {
msg_type = 1; // Note on
if (handleNoteOn) (*handleNoteOn)(c, b2, b3);
} else {
msg_type = 0; // Note off
if (handleNoteOff) (*handleNoteOff)(c, b2, b3);
}
goto return_message;
}
if (type1 == 0x0A && type2 == 0xA0) {
msg_type = 2; // Poly Pressure
if (handleVelocityChange) (*handleVelocityChange)(c, b2, b3);
goto return_message;
}
if (type1 == 0x0B && type2 == 0xB0) {
msg_type = 3; // Control Change
if (handleControlChange) (*handleControlChange)(c, b2, b3);
goto return_message;
}
if (type1 == 0x0C && type2 == 0xC0) {
msg_type = 4; // Program Change
if (handleProgramChange) (*handleProgramChange)(c, b2);
goto return_message;
}
if (type1 == 0x0D && type2 == 0xD0) {
msg_type = 5; // After Touch
if (handleAfterTouch) (*handleAfterTouch)(c, b2);
goto return_message;
}
if (type1 == 0x0E && type2 == 0xE0) {
msg_type = 6; // Pitch Bend
if (handlePitchChange) (*handlePitchChange)(c,
(b2 & 0x7F) | ((b3 & 0x7F) << 7));
goto return_message;
}
if (type1 == 0x0F && type2 == 0xF0) {
msg_type = 8; // Real Time System
if (handleRealTimeSystem) (*handleRealTimeSystem)(b1);
goto return_message;
}
return false;
return_message:
// only update these when returning true for a parsed message
// all other return cases will preserve these user-visible values
msg_channel = c;
msg_data1 = b2;
msg_data2 = b3;
return true;
}
if (type1 == 0x04) {
read_sysex_byte(b1);
read_sysex_byte(b2);
read_sysex_byte(b3);
return false;
}
if (type1 >= 0x05 && type1 <= 0x07) {
read_sysex_byte(b1);
if (type1 >= 0x06) read_sysex_byte(b2);
if (type1 == 0x07) read_sysex_byte(b3);
msg_data1 = msg_sysex_len;
msg_sysex_len = 0;
return true;
}
return false;
}
void usb_midi_class::read_sysex_byte(uint8_t b)
{
if (msg_sysex_len < USB_MIDI_SYSEX_MAX) {
msg_sysex[msg_sysex_len++] = b;
}
}
static volatile uint8_t prev_byte=0;
void usb_serial_class::begin(long speed)
{
// make sure USB is initialized
usb_init();
uint16_t begin_wait = (uint16_t)millis();
while (1) {
if (usb_configuration) {
delay(200); // a little time for host to load a driver
return;
}
if (usb_suspended) {
uint16_t begin_suspend = (uint16_t)millis();
while (usb_suspended) {
// must remain suspended for a while, because
// normal USB enumeration causes brief suspend
// states, typically under 0.1 second
if ((uint16_t)millis() - begin_suspend > 250) {
return;
}
}
}
// ... or a timout (powered by a USB power adaptor that
// wiggles the data lines to keep a USB device charging)
if ((uint16_t)millis() - begin_wait > 2500) return;
}
prev_byte = 0;
}
void usb_serial_class::end()
{
usb_shutdown();
delay(25);
}
// number of bytes available in the receive buffer
int usb_serial_class::available()
{
uint8_t c;
c = prev_byte; // assume 1 byte static volatile access is atomic
if (c) return 1;
c = readnext();
if (c) {
prev_byte = c;
return 1;
}
return 0;
}
// get the next character, or -1 if nothing received
int usb_serial_class::read()
{
uint8_t c;
c = prev_byte;
if (c) {
prev_byte = 0;
return c;
}
c = readnext();
if (c) return c;
return -1;
}
int usb_serial_class::peek()
{
uint8_t c;
c = prev_byte;
if (c) return c;
c = readnext();
if (c) {
prev_byte = c;
return c;
}
return -1;
}
// get the next character, or 0 if nothing
uint8_t usb_serial_class::readnext(void)
{
uint8_t c, c2, intr_state;
// interrupts are disabled so these functions can be
// used from the main program or interrupt context,
// even both in the same program!
intr_state = SREG;
cli();
if (!usb_configuration) {
SREG = intr_state;
return 0;
}
UENUM = DEBUG_RX_ENDPOINT;
try_again:
if (!(UEINTX & (1<<RWAL))) {
// no packet in buffer
SREG = intr_state;
return 0;
}
// take one byte out of the buffer
c = UEDATX;
if (c == 0) {
// if we see a zero, discard it and
// discard the rest of this packet
UEINTX = 0x6B;
goto try_again;
}
// if this drained the buffer, release it
if (!(UEINTX & (1<<RWAL))) UEINTX = 0x6B;
SREG = intr_state;
return c;
}
// discard any buffered input
void usb_serial_class::flush()
{
uint8_t intr_state;
if (usb_configuration) {
intr_state = SREG;
cli();
UENUM = DEBUG_RX_ENDPOINT;
while ((UEINTX & (1<<RWAL))) {
UEINTX = 0x6B;
}
SREG = intr_state;
}
prev_byte = 0;
}
// transmit a character.
#if ARDUINO >= 100
size_t usb_serial_class::write(uint8_t c)
#else
#define setWriteError()
void usb_serial_class::write(uint8_t c)
#endif
{
//static uint8_t previous_timeout=0;
uint8_t timeout, intr_state;
// if we're not online (enumerated and configured), error
if (!usb_configuration) goto error;
// interrupts are disabled so these functions can be
// used from the main program or interrupt context,
// even both in the same program!
intr_state = SREG;
cli();
UENUM = DEBUG_TX_ENDPOINT;
// if we gave up due to timeout before, don't wait again
#if 0
// this seems to be causig a lockup... why????
if (previous_timeout) {
if (!(UEINTX & (1<<RWAL))) {
SREG = intr_state;
return;
}
previous_timeout = 0;
}
#endif
// wait for the FIFO to be ready to accept data
timeout = UDFNUML + TRANSMIT_TIMEOUT;
while (1) {
// are we ready to transmit?
if (UEINTX & (1<<RWAL)) break;
SREG = intr_state;
// have we waited too long? This happens if the user
// is not running an application that is listening
if (UDFNUML == timeout) {
//previous_timeout = 1;
goto error;
}
// has the USB gone offline?
if (!usb_configuration) goto error;
// get ready to try checking again
intr_state = SREG;
cli();
UENUM = DEBUG_TX_ENDPOINT;
}
// actually write the byte into the FIFO
UEDATX = c;
// if this completed a packet, transmit it now!
if (!(UEINTX & (1<<RWAL))) {
UEINTX = 0x3A;
debug_flush_timer = 0;
} else {
debug_flush_timer = TRANSMIT_FLUSH_TIMEOUT;
}
SREG = intr_state;
#if ARDUINO >= 100
return 1;
#endif
error:
#if ARDUINO >= 100
setWriteError();
return 0;
#else
return;
#endif
}
// These are Teensy-specific extensions to the Serial object
// immediately transmit any buffered output.
// This doesn't actually transmit the data - that is impossible!
// USB devices only transmit when the host allows, so the best
// we can do is release the FIFO buffer for when the host wants it
void usb_serial_class::send_now(void)
{
uint8_t intr_state;
intr_state = SREG;
cli();
if (debug_flush_timer) {
UENUM = DEBUG_TX_ENDPOINT;
while ((UEINTX & (1<<RWAL))) {
UEDATX = 0;
}
UEINTX = 0x3A;
debug_flush_timer = 0;
}
SREG = intr_state;
}
uint32_t usb_serial_class::baud(void)
{
return (DEBUG_TX_SIZE * 1000 / DEBUG_TX_INTERVAL);
}
uint8_t usb_serial_class::stopbits(void)
{
return 0;
}
uint8_t usb_serial_class::paritytype(void)
{
return 0;
}
uint8_t usb_serial_class::numbits(void)
{
return 8;
}
uint8_t usb_serial_class::dtr(void)
{
return 0;
}
uint8_t usb_serial_class::rts(void)
{
return 0;
}
// Preinstantiate Objects //////////////////////////////////////////////////////
usb_serial_class Serial = usb_serial_class();
usb_midi_class usbMIDI = usb_midi_class();

View File

@ -0,0 +1,125 @@
#ifndef USBserial_h_
#define USBserial_h_
#include <inttypes.h>
#include "Stream.h"
#define USB_MIDI_SYSEX_MAX 60 // maximum sysex length we can receive
#define NoteOff 0
#define NoteOn 1
#define AfterTouchPoly 2
#define ControlChange 3
#define ProgramChange 4
#define AfterTouchChannel 5
#define PitchBend 6
#define SystemExclusive 7
class usb_midi_class
{
public:
void sendNoteOff(uint8_t note, uint8_t velocity, uint8_t channel);
void sendNoteOn(uint8_t note, uint8_t velocity, uint8_t channel);
void sendPolyPressure(uint8_t note, uint8_t pressure, uint8_t channel);
void sendControlChange(uint8_t control, uint8_t value, uint8_t channel);
void sendProgramChange(uint8_t program, uint8_t channel);
void sendAfterTouch(uint8_t pressure, uint8_t channel);
void sendPitchBend(uint16_t value, uint8_t channel);
void sendSysEx(uint8_t length, const uint8_t *data);
void send_now(void);
uint8_t analog2velocity(uint16_t val, uint8_t range);
bool read(uint8_t channel=0);
inline uint8_t getType(void) {
return msg_type;
};
inline uint8_t getChannel(void) {
return msg_channel;
};
inline uint8_t getData1(void) {
return msg_data1;
};
inline uint8_t getData2(void) {
return msg_data2;
};
inline uint8_t * getSysExArray(void) {
return msg_sysex;
};
inline void setHandleNoteOff(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) {
handleNoteOff = fptr;
};
inline void setHandleNoteOn(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) {
handleNoteOn = fptr;
};
inline void setHandleVelocityChange(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) {
handleVelocityChange = fptr;
};
inline void setHandleControlChange(void (*fptr)(uint8_t channel, uint8_t control, uint8_t value)) {
handleControlChange = fptr;
};
inline void setHandleProgramChange(void (*fptr)(uint8_t channel, uint8_t program)) {
handleProgramChange = fptr;
};
inline void setHandleAfterTouch(void (*fptr)(uint8_t channel, uint8_t pressure)) {
handleAfterTouch = fptr;
};
inline void setHandlePitchChange(void (*fptr)(uint8_t channel, uint16_t pitch)) {
handlePitchChange = fptr;
};
inline void setHandleRealTimeSystem(void (*fptr)(uint8_t realtimebyte)) {
handleRealTimeSystem = fptr;
};
private:
void send_raw(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3);
void read_sysex_byte(uint8_t b);
uint8_t msg_channel;
uint8_t msg_type;
uint8_t msg_data1;
uint8_t msg_data2;
uint8_t msg_sysex[USB_MIDI_SYSEX_MAX];
uint8_t msg_sysex_len;
void (*handleNoteOff)(uint8_t ch, uint8_t note, uint8_t vel);
void (*handleNoteOn)(uint8_t ch, uint8_t note, uint8_t vel);
void (*handleVelocityChange)(uint8_t ch, uint8_t note, uint8_t vel);
void (*handleControlChange)(uint8_t ch, uint8_t, uint8_t);
void (*handleProgramChange)(uint8_t ch, uint8_t);
void (*handleAfterTouch)(uint8_t ch, uint8_t);
void (*handlePitchChange)(uint8_t ch, uint16_t);
void (*handleRealTimeSystem)(uint8_t rtb);
};
extern usb_midi_class usbMIDI;
class usb_serial_class : public Stream
{
public:
// standard Arduino functions
void begin(long);
void end();
virtual int available();
virtual int read();
virtual int peek();
virtual void flush();
#if ARDUINO >= 100
virtual size_t write(uint8_t);
#else
virtual void write(uint8_t);
#endif
using Print::write;
// Teensy extensions
void send_now(void);
uint32_t baud(void);
uint8_t stopbits(void);
uint8_t paritytype(void);
uint8_t numbits(void);
uint8_t dtr(void);
uint8_t rts(void);
private:
uint8_t readnext(void);
};
extern usb_serial_class Serial;
#endif