Переглянути джерело

Improve USB serial transfers

main
PaulStoffregen 5 роки тому
джерело
коміт
533a0c2c00
2 змінених файлів з 126 додано та 84 видалено
  1. +68
    -19
      teensy4/usb.c
  2. +58
    -65
      teensy4/usb_serial.c

+ 68
- 19
teensy4/usb.c Переглянути файл

@@ -6,6 +6,11 @@
#include <string.h>
#include "debug/printf.h"

//#define LOG_SIZE 20
//uint32_t transfer_log_head=0;
//uint32_t transfer_log_count=0;
//uint32_t transfer_log[LOG_SIZE];

// device mode, page 3155

typedef struct endpoint_struct endpoint_t;
@@ -100,6 +105,12 @@ void usb_init(void)

CCM_CCGR6 |= CCM_CCGR6_USBOH3(CCM_CCGR_ON); // turn on clocks to USB peripheral
printf("BURSTSIZE=%08lX\n", USB1_BURSTSIZE);
//USB1_BURSTSIZE = USB_BURSTSIZE_TXPBURST(4) | USB_BURSTSIZE_RXPBURST(4);
USB1_BURSTSIZE = 0x0404;
printf("BURSTSIZE=%08lX\n", USB1_BURSTSIZE);
printf("USB1_TXFILLTUNING=%08lX\n", USB1_TXFILLTUNING);

// Before programming this register, the PHY clocks must be enabled in registers
// USBPHYx_CTRLn and CCM_ANALOG_USBPHYx_PLL_480_CTRLn.

@@ -171,6 +182,8 @@ void usb_init(void)
//printf("USB1_ENDPTCTRL2=%08lX\n", USB1_ENDPTCTRL2);
//printf("USB1_ENDPTCTRL3=%08lX\n", USB1_ENDPTCTRL3);
USB1_USBCMD = USB_USBCMD_RS;
//transfer_log_head = 0;
//transfer_log_count = 0;
}


@@ -518,34 +531,65 @@ void usb_prepare_transfer(transfer_t *transfer, const void *data, uint32_t len,
transfer->callback_param = param;
}

#if 0
void usb_print_transfer_log(void)
{
uint32_t i, count;
printf("log %d transfers\n", transfer_log_count);
count = transfer_log_count;
if (count > LOG_SIZE) count = LOG_SIZE;

for (i=0; i < count; i++) {
if (transfer_log_head == 0) transfer_log_head = LOG_SIZE;
transfer_log_head--;
uint32_t log = transfer_log[transfer_log_head];
printf(" %c %X\n", log >> 8, (int)(log & 255));
}
}
#endif

static void schedule_transfer(endpoint_t *endpoint, uint32_t epmask, transfer_t *transfer)
{
transfer->next = 1;
// when we stop at 6, why is the last transfer missing from the USB output?
//if (transfer_log_count >= 6) return;

//uint32_t ret = (*(const uint8_t *)transfer->pointer0) << 8;
if (endpoint->callback_function) {
transfer->status |= (1<<15);
}
__disable_irq();
// 41.5.6.6.3 Executing A Transfer Descriptor, page 2468 (RT1060 manual, Rev 1, 12/2018)
// Not exactly the same process as NXP suggests....
if ((USB1_ENDPTPRIME & epmask) || (USB1_ENDPTSTATUS & epmask)) {
transfer_t *last = endpoint->last_transfer;
if (last) {
//digitalWriteFast(1, HIGH);
// Executing A Transfer Descriptor, page 2468 (RT1060 manual, Rev 1, 12/2018)
transfer_t *last = endpoint->last_transfer;
if (last) {
last->next = (uint32_t)transfer;
if (USB1_ENDPTPRIME & epmask) goto end;
//digitalWriteFast(2, HIGH);
//ret |= 0x01;
uint32_t status;
do {
USB1_USBCMD |= USB_USBCMD_ATDTW;
last->next = (uint32_t)transfer;
if ((USB1_ENDPTPRIME & epmask) ||
((USB1_ENDPTSTATUS & epmask) && (USB1_USBCMD & USB_USBCMD_ATDTW))) {
endpoint->last_transfer = transfer;
__enable_irq();
return;
}
}
status = USB1_ENDPTSTATUS;
} while (!(USB1_USBCMD & USB_USBCMD_ATDTW));
//USB1_USBCMD &= ~USB_USBCMD_ATDTW;
if (status & epmask) goto end;
//ret |= 0x02;
}
//digitalWriteFast(4, HIGH);
endpoint->next = (uint32_t)transfer;
endpoint->status = 0;
USB1_ENDPTPRIME |= epmask;
endpoint->first_transfer = transfer;
end:
endpoint->last_transfer = transfer;
USB1_ENDPTPRIME |= epmask;
__enable_irq();
//digitalWriteFast(4, LOW);
//digitalWriteFast(3, LOW);
//digitalWriteFast(2, LOW);
//digitalWriteFast(1, LOW);
//if (transfer_log_head > LOG_SIZE) transfer_log_head = 0;
//transfer_log[transfer_log_head++] = ret;
//transfer_log_count++;
}
// ENDPTPRIME - Software should write a one to the corresponding bit when
// posting a new transfer descriptor to an endpoint queue head.
@@ -631,14 +675,19 @@ void usb_receive(int endpoint_number, transfer_t *transfer)
uint32_t usb_transfer_status(const transfer_t *transfer)
{
uint32_t status, cmd;
//int count=0;
cmd = USB1_USBCMD;
do {
while (1) {
__disable_irq();
USB1_USBCMD = cmd | USB_USBCMD_ATDTW;
status = transfer->status;
cmd = USB1_USBCMD;
} while (!(cmd & USB_USBCMD_ATDTW));
return status;
__enable_irq();
if (cmd & USB_USBCMD_ATDTW) return status;
//if (!(cmd & USB_USBCMD_ATDTW)) continue;
//if (status & 0x80) break; // for still active, only 1 reading needed
//if (++count > 1) break; // for completed, check 10 times
}
}



+ 58
- 65
teensy4/usb_serial.c Переглянути файл

@@ -247,83 +247,76 @@ int usb_serial_putchar(uint8_t c)
return usb_serial_write(&c, 1);
}

#define TX_NUM 4
#define TX_SIZE 64 /* should be a multiple of CDC_TX_SIZE */
#define TX_NUM 7
#define TX_SIZE 256 /* should be a multiple of CDC_TX_SIZE */
static transfer_t tx_transfer[TX_NUM] __attribute__ ((used, aligned(32)));
static uint8_t txbuffer[TX_SIZE * TX_NUM];
static uint8_t tx_head=0;
static uint16_t tx_available=0;

extern volatile uint32_t systick_millis_count;

int usb_serial_write(const void *buffer, uint32_t size)
{
if (!usb_configuration) return 0;
if (size > TX_SIZE) size = TX_SIZE;
uint32_t sent=0;
const uint8_t *data = (const uint8_t *)buffer;

transfer_t *xfer = tx_transfer + tx_head;
int count=0;
while (1) {
uint32_t status = usb_transfer_status(xfer);
if (count > 2000) {
printf("status = %x\n", status);
//while (1) ;
}
if (!(status & 0x80)) break;
count++;
//if (count > 50) break; // TODO: proper timout?
// TODO: check for USB offline
}
uint8_t *txdata = txbuffer + (tx_head * TX_SIZE);
memcpy(txdata, buffer, size);
usb_prepare_transfer(xfer, txdata, size, 0);
usb_transmit(CDC_TX_ENDPOINT, xfer);
if (++tx_head >= TX_NUM) tx_head = 0;
return size;
#if 0
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;
if (!usb_configuration) return 0;
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();
transfer_t *xfer = tx_transfer + tx_head;
int waiting=0;
uint32_t wait_begin_at=0;
while (!tx_available) {
//digitalWriteFast(3, HIGH);
uint32_t status = usb_transfer_status(xfer);
if (status & 0x7F) {
printf("ERROR status = %x, i=%d, ms=%u\n", status, tx_head, systick_millis_count);
}
if (!(status & 0x80)) {
// TODO: what if status has errors???
tx_available = TX_SIZE;
break;
}/* else {
if (!waiting) {
wait_begin_at = systick_millis_count;
waiting = 1;
} else {
if (systick_millis_count - wait_begin_at > 250) {
printf("\nstop, waited too long\n");
printf("status = %x\n", status);
printf("tx head=%d\n", tx_head);
printf("TXFILLTUNING=%08lX\n", USB1_TXFILLTUNING);
usb_print_transfer_log();
while (1) ;
}
}
} */
// TODO: proper timout?
// TODO: check for USB offline
}
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;
//digitalWriteFast(3, LOW);

uint8_t *txdata = txbuffer + (tx_head * TX_SIZE) + (TX_SIZE - tx_available);

if (size >= tx_available) {
memcpy(txdata, data, tx_available);
//*(txbuffer + (tx_head * TX_SIZE)) = 'A' + tx_head; // to see which buffer
//*(txbuffer + (tx_head * TX_SIZE) + 1) = ' '; // really see it
usb_prepare_transfer(xfer, txbuffer + (tx_head * TX_SIZE), TX_SIZE, 0);
usb_transmit(CDC_TX_ENDPOINT, xfer);
if (++tx_head >= TX_NUM) tx_head = 0;
size -= tx_available;
sent += tx_available;
data += tx_available;
tx_available = 0;
} else {
memcpy(txdata, data, size);
tx_available -= size;
sent += size;
size = 0;
}
usb_cdc_transmit_flush_timer = TRANSMIT_FLUSH_TIMEOUT;
}
tx_noautoflush = 0;
return ret;
#endif
return 0;
return sent;
}

int usb_serial_write_buffer_free(void)

Завантаження…
Відмінити
Зберегти