Skip to content

stm32/usb_cdc: Double data speed sending from micropython to computer #6172

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 45 additions & 27 deletions ports/stm32/usbd_cdc_interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,40 +171,64 @@ int8_t usbd_cdc_control(usbd_cdc_state_t *cdc_in, uint8_t cmd, uint8_t *pbuf, ui
return USBD_OK;
}

static uint16_t inline usbd_cdc_tx_buffer_mask(uint16_t val) {
return val & (USBD_CDC_TX_DATA_SIZE - 1);
}

static uint16_t inline usbd_cdc_tx_buffer_size(usbd_cdc_itf_t *cdc) {
return cdc->tx_buf_ptr_in - cdc->tx_buf_ptr_out;
}

static bool inline usbd_cdc_tx_buffer_empty(usbd_cdc_itf_t *cdc) {
return cdc->tx_buf_ptr_out == cdc->tx_buf_ptr_in;
}

static bool inline usbd_cdc_tx_buffer_will_be_empty(usbd_cdc_itf_t *cdc) {
return cdc->tx_buf_ptr_out_next == cdc->tx_buf_ptr_in;
}

static bool inline usbd_cdc_tx_buffer_full(usbd_cdc_itf_t *cdc) {
return usbd_cdc_tx_buffer_size(cdc) == USBD_CDC_TX_DATA_SIZE;
}

static uint16_t usbd_cdc_tx_send_length(usbd_cdc_itf_t *cdc) {
uint16_t to_end = USBD_CDC_TX_DATA_SIZE - usbd_cdc_tx_buffer_mask(cdc->tx_buf_ptr_out);
return MIN(usbd_cdc_tx_buffer_size(cdc), to_end);
}

static void usbd_cdc_tx_buffer_put(usbd_cdc_itf_t *cdc, uint8_t data) {
cdc->tx_buf[usbd_cdc_tx_buffer_mask(cdc->tx_buf_ptr_in)] = data;
cdc->tx_buf_ptr_in++;
}

static uint8_t *usbd_cdc_tx_buffer_getp(usbd_cdc_itf_t *cdc, uint16_t len) {
cdc->tx_buf_ptr_out_next += len;
return &cdc->tx_buf[usbd_cdc_tx_buffer_mask(cdc->tx_buf_ptr_out)];
}

// Called when the USB IN endpoint is ready to receive more data
// (cdc.base.tx_in_progress must be 0)
void usbd_cdc_tx_ready(usbd_cdc_state_t *cdc_in) {

usbd_cdc_itf_t *cdc = (usbd_cdc_itf_t *)cdc_in;
cdc->tx_buf_ptr_out = cdc->tx_buf_ptr_out_shadow;
cdc->tx_buf_ptr_out = cdc->tx_buf_ptr_out_next;

if (cdc->tx_buf_ptr_out == cdc->tx_buf_ptr_in && !cdc->tx_need_empty_packet) {
if (usbd_cdc_tx_buffer_empty(cdc) && !cdc->tx_need_empty_packet) {
// No outstanding data to send
return;
}

uint32_t len;
if (cdc->tx_buf_ptr_out > cdc->tx_buf_ptr_in) { // rollback
len = USBD_CDC_TX_DATA_SIZE - cdc->tx_buf_ptr_out;
} else {
len = cdc->tx_buf_ptr_in - cdc->tx_buf_ptr_out;
}

uint16_t len = usbd_cdc_tx_send_length(cdc);
// Should always succeed because cdc.base.tx_in_progress==0
USBD_CDC_TransmitPacket(&cdc->base, len, &cdc->tx_buf[cdc->tx_buf_ptr_out]);
USBD_CDC_TransmitPacket(&cdc->base, len, usbd_cdc_tx_buffer_getp(cdc, len));

cdc->tx_buf_ptr_out_shadow += len;
if (cdc->tx_buf_ptr_out_shadow == USBD_CDC_TX_DATA_SIZE) {
cdc->tx_buf_ptr_out_shadow = 0;
}

// According to the USB specification, a packet size of 64 bytes (CDC_DATA_FS_MAX_PACKET_SIZE)
// gets held at the USB host until the next packet is sent. This is because a
// packet of maximum size is considered to be part of a longer chunk of data, and
// the host waits for all data to arrive (ie, waits for a packet < max packet size).
// To flush a packet of exactly max packet size, we need to send a zero-size packet.
// See eg http://www.cypress.com/?id=4&rID=92719
cdc->tx_need_empty_packet = (len > 0 && len % usbd_cdc_max_packet(cdc->base.usbd->pdev) == 0 && cdc->tx_buf_ptr_out_shadow == cdc->tx_buf_ptr_in);
cdc->tx_need_empty_packet = (len > 0 && len % usbd_cdc_max_packet(cdc->base.usbd->pdev) == 0 && usbd_cdc_tx_buffer_will_be_empty(cdc));
}

// Attempt to queue data on the USB IN endpoint
Expand Down Expand Up @@ -291,10 +315,7 @@ int8_t usbd_cdc_receive(usbd_cdc_state_t *cdc_in, size_t len) {
}

int usbd_cdc_tx_half_empty(usbd_cdc_itf_t *cdc) {
int32_t tx_waiting = (int32_t)cdc->tx_buf_ptr_in - (int32_t)cdc->tx_buf_ptr_out;
if (tx_waiting < 0) {
tx_waiting += USBD_CDC_TX_DATA_SIZE;
}
int32_t tx_waiting = usbd_cdc_tx_buffer_size(cdc);
return tx_waiting <= USBD_CDC_TX_DATA_SIZE / 2;
}

Expand All @@ -317,8 +338,7 @@ int usbd_cdc_tx(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len, uint32_t
for (uint32_t i = 0; i < len; i++) {
// Wait until the device is connected and the buffer has space, with a given timeout
uint32_t start = HAL_GetTick();
while (cdc->connect_state == USBD_CDC_CONNECT_STATE_DISCONNECTED
|| ((cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1)) == cdc->tx_buf_ptr_out) {
while (cdc->connect_state == USBD_CDC_CONNECT_STATE_DISCONNECTED || usbd_cdc_tx_buffer_full(cdc)) {
usbd_cdc_try_tx(cdc);
// Wraparound of tick is taken care of by 2's complement arithmetic.
if (HAL_GetTick() - start >= timeout) {
Expand All @@ -333,8 +353,7 @@ int usbd_cdc_tx(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len, uint32_t
}

// Write data to device buffer
cdc->tx_buf[cdc->tx_buf_ptr_in] = buf[i];
cdc->tx_buf_ptr_in = (cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1);
usbd_cdc_tx_buffer_put(cdc, buf[i]);
}

usbd_cdc_try_tx(cdc);
Expand All @@ -357,7 +376,7 @@ void usbd_cdc_tx_always(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len) {
// If the buffer is full, wait until it gets drained, with a timeout of 500ms
// (wraparound of tick is taken care of by 2's complement arithmetic).
uint32_t start = HAL_GetTick();
while (((cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1)) == cdc->tx_buf_ptr_out && HAL_GetTick() - start <= 500) {
while (usbd_cdc_tx_buffer_full(cdc) && HAL_GetTick() - start <= 500) {
usbd_cdc_try_tx(cdc);
if (query_irq() == IRQ_STATE_DISABLED) {
// IRQs disabled so buffer will never be drained; exit loop
Expand All @@ -367,8 +386,7 @@ void usbd_cdc_tx_always(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len) {
}
}

cdc->tx_buf[cdc->tx_buf_ptr_in] = buf[i];
cdc->tx_buf_ptr_in = (cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1);
usbd_cdc_tx_buffer_put(cdc, buf[i]);
}
usbd_cdc_try_tx(cdc);
}
Expand Down
4 changes: 2 additions & 2 deletions ports/stm32/usbd_cdc_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
#define USBD_CDC_RX_DATA_SIZE (1024) // this must be 2 or greater, and a power of 2
#endif
#ifndef USBD_CDC_TX_DATA_SIZE
#define USBD_CDC_TX_DATA_SIZE (1024) // I think this can be any value (was 2048)
#define USBD_CDC_TX_DATA_SIZE (1024) // This must be a power of 2 and no greater than 16384
#endif

// Values for connect_state
Expand All @@ -60,7 +60,7 @@ typedef struct _usbd_cdc_itf_t {
uint8_t tx_buf[USBD_CDC_TX_DATA_SIZE]; // data for USB IN endpoind is stored in this buffer
uint16_t tx_buf_ptr_in; // increment this pointer modulo USBD_CDC_TX_DATA_SIZE when new data is available
volatile uint16_t tx_buf_ptr_out; // increment this pointer modulo USBD_CDC_TX_DATA_SIZE when data is drained
uint16_t tx_buf_ptr_out_shadow; // shadow of above
uint16_t tx_buf_ptr_out_next; // next position of above once transmission finished
uint8_t tx_need_empty_packet; // used to flush the USB IN endpoint if the last packet was exactly the endpoint packet size

volatile uint8_t connect_state; // indicates if we are connected
Expand Down
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy