Use a ping-pong buffer for USB RX. Simplify code and improve flow control.

9600_baud
Rob Riggs 2020-07-04 14:45:20 -05:00
rodzic 88436fbd2a
commit 98ceec2559
3 zmienionych plików z 49 dodań i 53 usunięć

Wyświetl plik

@ -78,6 +78,9 @@
*/
/* USER CODE BEGIN EXPORTED_DEFINES */
#define APP_RX_DATA_SIZE 64
#define APP_TX_DATA_SIZE 64
/* USER CODE END EXPORTED_DEFINES */
/**
@ -91,6 +94,14 @@
/* USER CODE BEGIN EXPORTED_TYPES */
struct UsbCdcRxBuffer
{
uint8_t buffer[APP_RX_DATA_SIZE];
uint32_t size;
};
typedef struct UsbCdcRxBuffer UsbCdcRxBuffer_t;
/* USER CODE END EXPORTED_TYPES */
/**
@ -120,6 +131,10 @@ extern USBD_CDC_ItfTypeDef USBD_Interface_fops_FS;
/* USER CODE BEGIN EXPORTED_VARIABLES */
// USB CDC receive ping pong buffer.
extern UsbCdcRxBuffer_t* usbCdcRxBuffer_1;
extern UsbCdcRxBuffer_t* usbCdcRxBuffer_2;
/* USER CODE END EXPORTED_VARIABLES */
/**

Wyświetl plik

@ -97,8 +97,6 @@
/* USER CODE BEGIN PRIVATE_DEFINES */
/* Define size for the receive and transmit buffer over CDC */
/* It's up to user to redefine and/or remove those define */
#define APP_RX_DATA_SIZE 64
#define APP_TX_DATA_SIZE 64
/* USER CODE END PRIVATE_DEFINES */
/**
@ -124,8 +122,6 @@
*/
/* Create buffer for reception and transmission */
/* It's up to user to redefine and/or remove those define */
/** Received data over USB are stored in this buffer */
uint8_t UserRxBufferFS[APP_RX_DATA_SIZE];
/** Data to send over USB CDC are stored in this buffer */
uint8_t UserTxBufferFS[APP_TX_DATA_SIZE];
@ -138,6 +134,11 @@ USBD_CDC_LineCodingTypeDef LineCoding = {
0x08 // number of bits: 8
};
// USB CDC receive ping pong buffer.
UsbCdcRxBuffer_t usbCdcRxBuffer[2];
UsbCdcRxBuffer_t* usbCdcRxBuffer_1 = &usbCdcRxBuffer[0];
UsbCdcRxBuffer_t* usbCdcRxBuffer_2 = &usbCdcRxBuffer[1];
/* USER CODE END PRIVATE_VARIABLES */
/**
@ -195,7 +196,7 @@ static int8_t CDC_Init_FS(void)
/* USER CODE BEGIN 3 */
/* Set Application Buffers */
USBD_CDC_SetTxBuffer(&hUsbDeviceFS, UserTxBufferFS, 0);
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS);
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, usbCdcRxBuffer_1->buffer);
return (USBD_OK);
/* USER CODE END 3 */
}
@ -304,12 +305,6 @@ static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length)
* @brief Data received over USB OUT endpoint are sent over CDC interface
* through this function.
*
* @note
* This function will block any OUT packet reception on USB endpoint
* until exiting this function. If you exit this function before transfer
* is complete on CDC interface (ie. using DMA controller) it will result
* in receiving more data while previous ones are still not sent.
*
* @param Buf: Buffer of data to be received
* @param Len: Number of data received (in bytes)
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
@ -320,10 +315,8 @@ static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
if (!cdc_connected) {
osMessagePut(ioEventQueueHandle, CMD_USB_CDC_CONNECT, 0);
}
cdc_receive(Buf, *Len);
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
return (USBD_OK);
cdc_receive(Buf, *Len);
return (USBD_OK);
/* USER CODE END 6 */
}

Wyświetl plik

@ -13,36 +13,27 @@
extern "C" void TNC_Error_Handler(int dev, int err);
extern osMessageQId ioEventQueueHandle;
extern USBD_HandleTypeDef hUsbDeviceFS;
extern "C" void cdc_receive(const uint8_t* buf, uint32_t len)
{
using namespace mobilinkd::tnc;
if (mobilinkd::tnc::getUsbPort()->queue() != 0)
// This is running in an interrupt handler.
UsbCdcRxBuffer_t* usbCdcRxBuffer = (UsbCdcRxBuffer_t*)(buf);
usbCdcRxBuffer->size = len;
if (getUsbPort()->queue() != 0)
{
if (len == 1)
// This should always succeed.
if (osMessagePut(getUsbPort()->queue(), (uint32_t) buf, 0) == osOK)
{
// Send single byte via queue directly. Linux seems to do
// this for ttyACM ports.
osMessagePut(getUsbPort()->queue(), *buf, osWaitForever);
return;
}
auto frame = hdlc::acquire();
if (frame)
{
for (uint32_t i = 0; i != len; ++i)
{
frame->push_back(*buf++);
}
frame->source(hdlc::IoFrame::SERIAL_DATA);
if (osMessagePut(mobilinkd::tnc::getUsbPort()->queue(),
(uint32_t) frame,
osWaitForever) != osOK)
{
mobilinkd::tnc::hdlc::release(frame);
}
}
}
ERROR("USB packet dropped");
USBD_CDC_ReceivePacket(&hUsbDeviceFS); // re-enable receive.
}
extern "C" void startCDCTask(void const* arg)
@ -116,7 +107,7 @@ void UsbPort::add_char(uint8_t c)
void UsbPort::run()
{
frame_ = hdlc::acquire();
if (frame_ == nullptr) frame_ = hdlc::acquire();
while (true) {
osEvent evt = osMessageGet(queue(), osWaitForever);
@ -124,34 +115,31 @@ void UsbPort::run()
continue;
}
uint32_t c = evt.value.v;
auto usbCdcRxBuffer = (UsbCdcRxBuffer_t*) evt.value.p;
if (c < 0x100) // Assume single byte transfer.
{
add_char(c);
continue;
// Handle ping-pong buffers.
if (usbCdcRxBuffer == usbCdcRxBuffer_1) {
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, usbCdcRxBuffer_2->buffer);
} else {
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, usbCdcRxBuffer_1->buffer);
}
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
auto input = static_cast<hdlc::IoFrame*>(evt.value.p);
INFO("USB p %lu", usbCdcRxBuffer->size);
if (!isOpen()) {
hdlc::release(input);
continue;
if (isOpen()) {
for (uint32_t i = 0; i != usbCdcRxBuffer->size; ++i) {
add_char(usbCdcRxBuffer->buffer[i]);
}
}
for (uint8_t c : *input) {
add_char(c);
}
hdlc::release(input);
}
}
void UsbPort::init()
{
if (cdcTaskHandle_) return;
osMessageQDef(cdcQueue, 128, void*);
osMessageQDef(cdcQueue, 4, void*);
queue_ = osMessageCreate(osMessageQ(cdcQueue), 0);
osMutexDef(usbMutex);