Browse Source

USB serial receive optimization on Teensy 4.0 - work in progress

teensy4-core
PaulStoffregen 5 years ago
parent
commit
eb5d86692a
1 changed files with 133 additions and 44 deletions
  1. +133
    -44
      teensy4/usb_serial.c

+ 133
- 44
teensy4/usb_serial.c View File

@@ -72,15 +72,13 @@ static uint8_t rx_buffer[RX_NUM * CDC_RX_SIZE_480];
static uint16_t rx_count[RX_NUM];
static uint16_t rx_index[RX_NUM];
static uint16_t rx_packet_size=0;
static volatile uint8_t rx_head;
static volatile uint8_t rx_tail;
static uint8_t rx_list[RX_NUM + 1];
static volatile uint32_t rx_available;
static void rx_queue_transfer(int i);
static void rx_event(transfer_t *t);

static void rx_event(transfer_t *t)
{
int len = rx_packet_size - ((t->status >> 16) & 0x7FFF);
int index = t->callback_param;
//printf("rx event, len=%d, i=%d\n", len, index);
rx_count[index] = len;
rx_index[index] = 0;
}

void usb_serial_reset(void)
{
@@ -90,6 +88,8 @@ void usb_serial_reset(void)

void usb_serial_configure(void)
{
int i;

printf("usb_serial_configure\n");
if (usb_high_speed) {
tx_packet_size = CDC_TX_SIZE_480;
@@ -104,73 +104,162 @@ void usb_serial_configure(void)
memset(rx_transfer, 0, sizeof(rx_transfer));
memset(rx_count, 0, sizeof(rx_count));
memset(rx_index, 0, sizeof(rx_index));
rx_head = 0;
rx_tail = 0;
rx_available = 0;
usb_config_tx(CDC_ACM_ENDPOINT, CDC_ACM_SIZE, 0, NULL); // size same 12 & 480
usb_config_rx(CDC_RX_ENDPOINT, rx_packet_size, 0, rx_event);
usb_config_tx(CDC_TX_ENDPOINT, tx_packet_size, 1, NULL);
usb_prepare_transfer(rx_transfer + 0, rx_buffer + 0, rx_packet_size, 0);
usb_receive(CDC_RX_ENDPOINT, rx_transfer + 0);
for (i=0; i < RX_NUM; i++) rx_queue_transfer(i);
timer_config(usb_serial_flush_callback, TRANSMIT_FLUSH_TIMEOUT);
}


// get the next character, or -1 if nothing received
int usb_serial_getchar(void)
/*************************************************************************/
/** Receive **/
/*************************************************************************/

static void rx_queue_transfer(int i)
{
if (rx_index[0] < rx_count[0]) {
int c = rx_buffer[rx_index[0]++];
if (rx_index[0] >= rx_count[0]) {
// reschedule transfer
usb_prepare_transfer(rx_transfer + 0, rx_buffer + 0, rx_packet_size, 0);
usb_receive(CDC_RX_ENDPOINT, rx_transfer + 0);
}
return c;
}
return -1;
NVIC_DISABLE_IRQ(IRQ_USB1);
printf("rx queue i=%d\n", i);
usb_prepare_transfer(rx_transfer + i, rx_buffer + i * CDC_RX_SIZE_480, rx_packet_size, i);
usb_receive(CDC_RX_ENDPOINT, rx_transfer + i);
NVIC_ENABLE_IRQ(IRQ_USB1);
}

// peek at the next character, or -1 if nothing received
int usb_serial_peekchar(void)
// called by USB interrupt when any packet is received
static void rx_event(transfer_t *t)
{
if (rx_index[0] < rx_count[0]) {
return rx_buffer[rx_index[0]];
int len = rx_packet_size - ((t->status >> 16) & 0x7FFF);
int i = t->callback_param;
printf("rx event, len=%d, i=%d\n", len, i);
if (len > 0) {
// received a packet with data
uint32_t head = rx_head;
if (head != rx_tail) {
// a previous packet is still buffered
uint32_t ii = rx_list[head];
uint32_t count = rx_count[ii];
if (len <= CDC_RX_SIZE_480 - count) {
// previous buffer has enough free space for this packet's data
memcpy(rx_buffer + ii * CDC_RX_SIZE_480 + count,
rx_buffer + i * CDC_RX_SIZE_480, len);
rx_count[ii] = count + len;
rx_available += len;
rx_queue_transfer(i);
// TODO: trigger serialEvent
return;
}
}
// add this packet to rx_list
rx_count[i] = len;
rx_index[i] = 0;
if (++head > RX_NUM) head = 0;
rx_list[head] = i;
rx_head = head;
rx_available += len;
// TODO: trigger serialEvent
} else {
// received a zero length packet
rx_queue_transfer(i);
}

return -1;
}

// number of bytes available in the receive buffer
int usb_serial_available(void)
{
return rx_count[0] - rx_index[0];
}
//static int maxtimes=0;

// read a block of bytes to a buffer
int usb_serial_read(void *buffer, uint32_t size)
{
// Quick and dirty to make it at least limp...
uint8_t *p = (uint8_t *)buffer;
uint32_t count=0;

while (size) {
int ch = usb_serial_getchar();
if (ch == -1) break;
*p++ = (uint8_t)ch;
size--;
count++;
NVIC_DISABLE_IRQ(IRQ_USB1);
//if (++maxtimes > 15) while (1) ;
uint32_t tail = rx_tail;
//printf("usb_serial_read, size=%d, tail=%d, head=%d\n", size, tail, rx_head);
while (count < size && tail != rx_head) {
if (++tail > RX_NUM) tail = 0;
uint32_t i = rx_list[tail];
uint32_t len = size - count;
uint32_t avail = rx_count[i] - rx_index[i];
//printf("usb_serial_read, count=%d, size=%d, i=%d, index=%d, len=%d, avail=%d, c=%c\n",
//count, size, i, rx_index[i], len, avail, rx_buffer[i * CDC_RX_SIZE_480]);
if (avail > len) {
// partially consume this packet
memcpy(p, rx_buffer + i * CDC_RX_SIZE_480 + rx_index[i], len);
rx_available -= len;
rx_index[i] += len;
count += len;
} else {
// fully consume this packet
memcpy(p, rx_buffer + i * CDC_RX_SIZE_480 + rx_index[i], avail);
p += avail;
rx_available -= avail;
count += avail;
rx_tail = tail;
rx_queue_transfer(i);
}
}
NVIC_ENABLE_IRQ(IRQ_USB1);
return count;
}

// peek at the next character, or -1 if nothing received
int usb_serial_peekchar(void)
{
uint32_t tail = rx_tail;
if (tail == rx_head) return -1;
if (++tail > RX_NUM) tail = 0;
uint32_t i = rx_list[tail];
return rx_buffer[i * CDC_RX_SIZE_480 + rx_index[i]];
}

// number of bytes available in the receive buffer
int usb_serial_available(void)
{
return rx_available;
}

// discard any buffered input
void usb_serial_flush_input(void)
{
if (rx_index[0] < rx_count[0]) {
rx_index[0] = rx_count[0];
usb_prepare_transfer(rx_transfer + 0, rx_buffer + 0, rx_packet_size, 0);
usb_receive(CDC_RX_ENDPOINT, rx_transfer + 0);
uint32_t tail = rx_tail;
while (tail != rx_head) {
if (++tail > RX_NUM) tail = 0;
uint32_t i = rx_list[tail];
rx_available -= rx_count[i] - rx_index[i];
rx_queue_transfer(i);
rx_tail = tail;
}
}


// get the next character, or -1 if nothing received
int usb_serial_getchar(void)
{
uint8_t c;
if (usb_serial_read(&c, 1)) return c;
return -1;
}













/*************************************************************************/
/** Transmit **/
/*************************************************************************/


// 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.

Loading…
Cancel
Save