Browse Source

USB serial receive on IMXRT

main
PaulStoffregen 5 years ago
parent
commit
7e959d4a14
5 changed files with 211 additions and 16 deletions
  1. +156
    -8
      teensy4/usb.c
  2. +1
    -4
      teensy4/usb_desc.h
  3. +4
    -0
      teensy4/usb_dev.h
  4. +48
    -4
      teensy4/usb_serial.c
  5. +2
    -0
      teensy4/usb_serial.h

+ 156
- 8
teensy4/usb.c View File

static void endpoint0_complete(void); static void endpoint0_complete(void);




static void run_callbacks(endpoint_t *ep);

static void run_callbacks(endpoint_t *ep)
{
transfer_t *t, *next;

printf("run_callbacks\n");
t = ep->first_transfer;
while (t && (uint32_t)t != 1) {
if (!(t->status & (1<<7))) {
// transfer not active anymore
next = (transfer_t *)t->next;
ep->callback_function(t);
} else {
// transfer still active
ep->first_transfer = t;
return;
}
t = next;
}
// all transfers completed
ep->first_transfer = NULL;
ep->last_transfer = NULL;
}



__attribute__((section(".progmem"))) __attribute__((section(".progmem")))
void usb_init(void) void usb_init(void)
endpoint0_notify_mask = 0; endpoint0_notify_mask = 0;
endpoint0_complete(); endpoint0_complete();
} }
if (completestatus & endpointN_notify_mask) {
// TODO: callback functions...
completestatus &= endpointN_notify_mask;
if (completestatus) {
int i; // TODO: optimize with __builtin_ctz()
for (i=2; i < NUM_ENDPOINTS; i++) {
if (completestatus & (1 << i)) { // receive
run_callbacks(endpoint_queue_head + i * 2);
}
if (completestatus & (1 << (i + 16))) { // transmit
run_callbacks(endpoint_queue_head + i * 2 + 1);
}
}
} }
} }
} }
// TODO; is this ever really a problem? // TODO; is this ever really a problem?
//printf("reset too slow\n"); //printf("reset too slow\n");
} }
#if defined(CDC_STATUS_INTERFACE) && defined(CDC_DATA_INTERFACE)
usb_serial_reset();
#endif
endpointN_notify_mask = 0;
// TODO: Free all allocated dTDs // TODO: Free all allocated dTDs
//if (++reset_count >= 3) { //if (++reset_count >= 3) {
// shut off USB - easier to see results in protocol analyzer // shut off USB - easier to see results in protocol analyzer
//printf(" ep=%d: cfg=%08lX - %08lX - %08lX\n", i + 1, n, m, p); //printf(" ep=%d: cfg=%08lX - %08lX - %08lX\n", i + 1, n, m, p);
reg++; reg++;
} }
// TODO: configure all queue heads with max packet length, zlt & mult
endpoint_queue_head[CDC_ACM_ENDPOINT*2+1].config = (CDC_ACM_ENDPOINT << 16);
endpoint_queue_head[CDC_RX_ENDPOINT*2+0].config = (CDC_RX_SIZE << 16) | (1 << 29);
endpoint_queue_head[CDC_TX_ENDPOINT*2+1].config = (CDC_TX_SIZE << 16) | (1 << 29);

// TODO: de-allocate any pending transfers?
#if defined(CDC_STATUS_INTERFACE) && defined(CDC_DATA_INTERFACE)
usb_serial_configure();
#endif
endpoint0_receive(NULL, 0, 0); endpoint0_receive(NULL, 0, 0);
return; return;


#endif #endif
} }


static void usb_endpoint_config(endpoint_t *qh, uint32_t config, void (*callback)(transfer_t *))
{
memset(qh, 0, sizeof(endpoint_t));
qh->config = config;
qh->callback_function = callback;
}

void usb_config_rx(uint32_t ep, uint32_t packet_size, int do_zlp, void (*cb)(transfer_t *))
{
uint32_t config = (packet_size << 16) | (do_zlp ? 0 : (1 << 29));
if (ep < 2 || ep > NUM_ENDPOINTS) return;
usb_endpoint_config(endpoint_queue_head + ep * 2, config, cb);
if (cb) endpointN_notify_mask |= (1 << ep);
}

void usb_config_tx(uint32_t ep, uint32_t packet_size, int do_zlp, void (*cb)(transfer_t *))
{
uint32_t config = (packet_size << 16) | (do_zlp ? 0 : (1 << 29));
if (ep < 2 || ep > NUM_ENDPOINTS) return;
usb_endpoint_config(endpoint_queue_head + ep * 2 + 1, config, cb);
if (cb) endpointN_notify_mask |= (1 << (ep + 16));
}



void usb_prepare_transfer(transfer_t *transfer, const void *data, uint32_t len, uint32_t param) void usb_prepare_transfer(transfer_t *transfer, const void *data, uint32_t len, uint32_t param)
{ {
transfer->next = 1; transfer->next = 1;
transfer->callback_param = param; transfer->callback_param = param;
} }



static void schedule_transfer(endpoint_t *endpoint, uint32_t epmask, transfer_t *transfer)
{
if (endpoint->callback_function) {
transfer->status |= (1<<15);
} else {
//transfer->status |= (1<<15);
// remove all inactive transfers
}
__disable_irq();
#if 0
if (endpoint->last_transfer) {
if (!(endpoint->last_transfer->status & (1<<7))) {
endpoint->last_transfer->next = (uint32_t)transfer;
} else {
// Case 2: Link list is not empty, page 3182
endpoint->last_transfer->next = (uint32_t)transfer;
if (USB1_ENDPTPRIME & epmask) {
endpoint->last_transfer = transfer;
__enable_irq();
printf(" case 2a\n");
return;
}
uint32_t stat;
uint32_t cmd = USB1_USBCMD;
do {
USB1_USBCMD = cmd | USB_USBCMD_ATDTW;
stat = USB1_ENDPTSTATUS;
} while (!(USB1_USBCMD & USB_USBCMD_ATDTW));
USB1_USBCMD = cmd & ~USB_USBCMD_ATDTW;
if (stat & epmask) {
endpoint->last_transfer = transfer;
__enable_irq();
printf(" case 2b\n");
return;
}
}
} else {
endpoint->first_transfer = transfer;
}
endpoint->last_transfer = transfer;
#endif
// Case 1: Link list is empty, page 3182
endpoint->next = (uint32_t)transfer;
endpoint->status = 0;
endpoint->first_transfer = (uint32_t)transfer;
endpoint->last_transfer = (uint32_t)transfer;
USB1_ENDPTPRIME |= epmask;
while (USB1_ENDPTPRIME & epmask) ;
__enable_irq();
//printf(" case 1\n");


// ENDPTPRIME - momentarily set by hardware during hardware re-priming
// operations when a dTD is retired, and the dQH is updated.

// ENDPTSTAT - Transmit Buffer Ready - set to one by the hardware as a
// response to receiving a command from a corresponding bit
// in the ENDPTPRIME register. . Buffer ready is cleared by
// USB reset, by the USB DMA system, or through the ENDPTFLUSH
// register. (so 0=buffer ready, 1=buffer primed for transmit)

}

void usb_transmit(int endpoint_number, transfer_t *transfer)
{
if (endpoint_number < 2 || endpoint_number > NUM_ENDPOINTS) return;
endpoint_t *endpoint = endpoint_queue_head + endpoint_number * 2 + 1;
uint32_t mask = 1 << (endpoint_number + 16);
schedule_transfer(endpoint, mask, transfer);
}

void usb_receive(int endpoint_number, transfer_t *transfer)
{
if (endpoint_number < 2 || endpoint_number > NUM_ENDPOINTS) return;
endpoint_t *endpoint = endpoint_queue_head + endpoint_number * 2;
uint32_t mask = 1 << endpoint_number;
schedule_transfer(endpoint, mask, transfer);
}







#if 0
void usb_transmit(int endpoint_number, transfer_t *transfer) void usb_transmit(int endpoint_number, transfer_t *transfer)
{ {
// endpoint 0 reserved for control // endpoint 0 reserved for control
// register. (so 0=buffer ready, 1=buffer primed for transmit) // register. (so 0=buffer ready, 1=buffer primed for transmit)


} }
#endif


/*struct endpoint_struct { /*struct endpoint_struct {
uint32_t config; uint32_t config;

+ 1
- 4
teensy4/usb_desc.h View File

#define CDC_ACM_SIZE 16 #define CDC_ACM_SIZE 16
#define CDC_RX_SIZE 64 #define CDC_RX_SIZE 64
#define CDC_TX_SIZE 64 #define CDC_TX_SIZE 64
#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_INTERRUPT + ENDPOINT_TRANSMIT_UNUSED
#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_INTERRUPT
#define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_UNUSED #define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_UNUSED
#define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_BULK #define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_BULK
//#define ENDPOINT2_CONFIG ENDPOINT_TRANSMIT_ONLY
//#define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_ONLY
//#define ENDPOINT4_CONFIG ENDPOINT_TRANSMIT_ONLY


#elif defined(USB_KEYBOARDONLY) #elif defined(USB_KEYBOARDONLY)
#define VENDOR_ID 0x16C0 #define VENDOR_ID 0x16C0

+ 4
- 0
teensy4/usb_dev.h View File

void usb_init(void); void usb_init(void);
void usb_init_serialnumber(void); void usb_init_serialnumber(void);


void usb_config_rx(uint32_t ep, uint32_t packet_size, int do_zlp, void (*cb)(transfer_t *));
void usb_config_tx(uint32_t ep, uint32_t packet_size, int do_zlp, void (*cb)(transfer_t *));

void usb_prepare_transfer(transfer_t *transfer, const void *data, uint32_t len, uint32_t param); void usb_prepare_transfer(transfer_t *transfer, const void *data, uint32_t len, uint32_t param);
void usb_transmit(int endpoint_number, transfer_t *transfer); void usb_transmit(int endpoint_number, transfer_t *transfer);
void usb_receive(int endpoint_number, transfer_t *transfer);





+ 48
- 4
teensy4/usb_serial.c View File



#define TRANSMIT_FLUSH_TIMEOUT 5 /* in milliseconds */ #define TRANSMIT_FLUSH_TIMEOUT 5 /* in milliseconds */




#define RX_NUM 3
static transfer_t rx_transfer[RX_NUM] __attribute__ ((used, aligned(32)));
static uint8_t rx_buffer[RX_NUM * 512];
static uint16_t rx_count[RX_NUM];
static uint16_t rx_index[RX_NUM];

static void rx_event(transfer_t *t)
{
int len = 512 - ((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)
{
printf("usb_serial_reset\n");
// deallocate all transfer descriptors
}

void usb_serial_configure(void)
{
printf("usb_serial_configure\n");
usb_config_tx(CDC_ACM_ENDPOINT, CDC_ACM_SIZE, 0, NULL);
usb_config_rx(CDC_RX_ENDPOINT, CDC_RX_SIZE, 0, rx_event);
usb_config_tx(CDC_TX_ENDPOINT, CDC_TX_SIZE, 0, NULL);
usb_prepare_transfer(rx_transfer + 0, rx_buffer + 0, 512, 0);
usb_receive(CDC_RX_ENDPOINT, rx_transfer + 0);
}


// get the next character, or -1 if nothing received // get the next character, or -1 if nothing received
int usb_serial_getchar(void) int usb_serial_getchar(void)
{ {
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, 512, 0);
usb_receive(CDC_RX_ENDPOINT, rx_transfer + 0);
}
return c;
}
#if 0 #if 0
unsigned int i; unsigned int i;
int c; int c;
// number of bytes available in the receive buffer // number of bytes available in the receive buffer
int usb_serial_available(void) int usb_serial_available(void)
{ {
return rx_count[0] - rx_index[0];
#if 0 #if 0
int count; int count;
count = usb_rx_byte_count(CDC_RX_ENDPOINT); count = usb_rx_byte_count(CDC_RX_ENDPOINT);
} }




static transfer_t transfer __attribute__ ((used, aligned(32)));
static transfer_t txtransfer __attribute__ ((used, aligned(32)));
static uint8_t txbuffer[1024]; static uint8_t txbuffer[1024];
//static uint8_t txbuffer1[1024]; //static uint8_t txbuffer1[1024];
//static uint8_t txbuffer2[1024]; //static uint8_t txbuffer2[1024];
int count=0; int count=0;
//digitalWriteFast(13, HIGH); //digitalWriteFast(13, HIGH);
while (1) { while (1) {
uint32_t status = transfer.status;
uint32_t status = txtransfer.status;
if (count > 10) printf("status = %x\n", status); if (count > 10) printf("status = %x\n", status);
if (!(status & 0x80)) break; if (!(status & 0x80)) break;
count++; count++;
//digitalWriteFast(13, LOW); //digitalWriteFast(13, LOW);
delayMicroseconds(1); // TODO: this must not be the answer! delayMicroseconds(1); // TODO: this must not be the answer!
memcpy(txbuffer, buffer, size); memcpy(txbuffer, buffer, size);
usb_prepare_transfer(&transfer, txbuffer, size, 0);
usb_transmit(CDC_TX_ENDPOINT, &transfer);
usb_prepare_transfer(&txtransfer, txbuffer, size, 0);
usb_transmit(CDC_TX_ENDPOINT, &txtransfer);


#if 0 #if 0
uint32_t ret = size; uint32_t ret = size;

+ 2
- 0
teensy4/usb_serial.h View File

#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
void usb_serial_reset(void);
void usb_serial_configure(void);
int usb_serial_getchar(void); int usb_serial_getchar(void);
int usb_serial_peekchar(void); int usb_serial_peekchar(void);
int usb_serial_available(void); int usb_serial_available(void);

Loading…
Cancel
Save