Fix data corruption in vcp
This commit is contained in:
parent
6864761acf
commit
f0ecb8edfd
@ -1,8 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "usbd_cdc.h"
|
#include "usbd_cdc.h"
|
||||||
#include "usbd_def.h"
|
#include "usbd_def.h"
|
||||||
@ -16,9 +15,15 @@ namespace uart::detail {
|
|||||||
|
|
||||||
template<size_t Size>
|
template<size_t Size>
|
||||||
struct RingBuffer {
|
struct RingBuffer {
|
||||||
size_t head;
|
volatile size_t head;
|
||||||
size_t tail;
|
volatile size_t tail;
|
||||||
uint8_t data[Size];
|
volatile uint8_t data[Size];
|
||||||
|
};
|
||||||
|
|
||||||
|
template<size_t Size>
|
||||||
|
struct Buffer {
|
||||||
|
volatile size_t size;
|
||||||
|
volatile uint8_t data[Size];
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class cfg>
|
template<class cfg>
|
||||||
@ -36,80 +41,71 @@ class VirtualComPort {
|
|||||||
|
|
||||||
[[gnu::always_inline]] static bool rxByte(uint8_t& byte)
|
[[gnu::always_inline]] static bool rxByte(uint8_t& byte)
|
||||||
{
|
{
|
||||||
if(m_rxBuffer.head == m_rxBuffer.tail)
|
if(m_receiving || m_rxBuffer.head == m_rxBuffer.tail)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
m_reading = true;
|
||||||
const size_t newTail = (m_rxBuffer.tail + 1) % RX_BUFFER_SIZE;
|
const size_t newTail = (m_rxBuffer.tail + 1) % RX_BUFFER_SIZE;
|
||||||
byte = m_rxBuffer.data[newTail];
|
byte = m_rxBuffer.data[newTail];
|
||||||
m_rxBuffer.tail = newTail;
|
m_rxBuffer.tail = newTail;
|
||||||
|
m_reading = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[gnu::always_inline]] static void txByte(const uint8_t& byte)
|
[[gnu::always_inline]] static void txByte(const uint8_t& byte)
|
||||||
{
|
{
|
||||||
const size_t newHead = (m_txBuffer.head + 1) % TX_BUFFER_SIZE;
|
if(m_txBuffer.size == TX_BUFFER_SIZE) {
|
||||||
|
|
||||||
if(m_txBuffer.tail == newHead) {
|
|
||||||
flushTx();
|
flushTx();
|
||||||
}
|
}
|
||||||
|
m_txBuffer.data[m_txBuffer.size++] = byte;
|
||||||
m_txBuffer.data[newHead] = byte;
|
|
||||||
m_txBuffer.head = newHead;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[gnu::always_inline]] static bool peek(uint8_t& byte)
|
[[gnu::always_inline]] static bool peek(uint8_t& byte)
|
||||||
{
|
{
|
||||||
if(m_rxBuffer.head == m_rxBuffer.tail)
|
if(m_receiving || m_rxBuffer.head == m_rxBuffer.tail)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
m_reading = true;
|
||||||
const size_t newTail = (m_rxBuffer.tail + 1) % RX_BUFFER_SIZE;
|
const size_t newTail = (m_rxBuffer.tail + 1) % RX_BUFFER_SIZE;
|
||||||
byte = m_rxBuffer.data[newTail];
|
byte = m_rxBuffer.data[newTail];
|
||||||
|
m_reading = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[gnu::always_inline]] static void flushTx()
|
[[gnu::always_inline]] static void flushTx()
|
||||||
{
|
{
|
||||||
if(m_txBuffer.head == m_txBuffer.tail) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr auto usbReady = []() {
|
constexpr auto usbReady = []() {
|
||||||
USBD_CDC_HandleTypeDef* hcdc = static_cast<USBD_CDC_HandleTypeDef*>(hUsbDeviceFS.pClassData);
|
USBD_CDC_HandleTypeDef* hcdc = static_cast<USBD_CDC_HandleTypeDef*>(hUsbDeviceFS.pClassData);
|
||||||
return hcdc->TxState != 0;
|
return hcdc->TxState != 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr auto txPacket = [usbReady](volatile uint8_t* buffer, size_t length) {
|
while(usbReady())
|
||||||
USBD_CDC_SetTxBuffer(&hUsbDeviceFS, const_cast<uint8_t*>(buffer), length);
|
|
||||||
USBD_CDC_TransmitPacket(&hUsbDeviceFS);
|
|
||||||
while(!usbReady())
|
|
||||||
;
|
;
|
||||||
};
|
|
||||||
|
|
||||||
if(m_txBuffer.head > m_txBuffer.tail) {
|
std::memcpy(const_cast<uint8_t*>(m_usbAsyncTxBuffer.data), const_cast<uint8_t*>(m_txBuffer.data), m_txBuffer.size);
|
||||||
txPacket(&m_txBuffer.data[m_txBuffer.tail], m_txBuffer.head - m_txBuffer.tail);
|
m_usbAsyncTxBuffer.size = m_txBuffer.size;
|
||||||
}
|
m_txBuffer.size = 0;
|
||||||
else {
|
|
||||||
txPacket(&m_txBuffer.data[m_txBuffer.tail], TX_BUFFER_SIZE - 1 - m_txBuffer.tail);
|
|
||||||
txPacket(m_txBuffer.data, m_txBuffer.head + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_txBuffer.tail = m_txBuffer.head;
|
USBD_CDC_SetTxBuffer(&hUsbDeviceFS, const_cast<uint8_t*>(m_usbAsyncTxBuffer.data), m_usbAsyncTxBuffer.size);
|
||||||
|
USBD_CDC_TransmitPacket(&hUsbDeviceFS);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr auto TX_BUFFER_SIZE = 512;
|
static constexpr auto TX_BUFFER_SIZE = 64;
|
||||||
static constexpr auto RX_BUFFER_SIZE = 512;
|
static constexpr auto RX_BUFFER_SIZE = 64;
|
||||||
|
|
||||||
static volatile RingBuffer<TX_BUFFER_SIZE> m_txBuffer;
|
static Buffer<TX_BUFFER_SIZE> m_txBuffer;
|
||||||
static volatile RingBuffer<RX_BUFFER_SIZE> m_rxBuffer;
|
static Buffer<TX_BUFFER_SIZE> m_usbAsyncTxBuffer;
|
||||||
|
|
||||||
static std::array<volatile uint8_t, 64> m_usbAsyncRxBuffer;
|
static RingBuffer<RX_BUFFER_SIZE> m_rxBuffer;
|
||||||
|
static Buffer<RX_BUFFER_SIZE> m_usbAsyncRxBuffer;
|
||||||
|
static volatile bool m_receiving;
|
||||||
|
static volatile bool m_reading;
|
||||||
|
|
||||||
static int8_t CdcInit()
|
static int8_t CdcInit()
|
||||||
{
|
{
|
||||||
USBD_CDC_SetTxBuffer(&hUsbDeviceFS, const_cast<uint8_t*>(m_txBuffer.data), 0);
|
USBD_CDC_SetTxBuffer(&hUsbDeviceFS, const_cast<uint8_t*>(m_txBuffer.data), 0);
|
||||||
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, const_cast<uint8_t*>(m_usbAsyncRxBuffer.data()));
|
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, const_cast<uint8_t*>(m_usbAsyncRxBuffer.data));
|
||||||
return USBD_OK;
|
return USBD_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,36 +170,51 @@ class VirtualComPort {
|
|||||||
|
|
||||||
static int8_t CdcReceive([[maybe_unused]] uint8_t* buf, uint32_t* length)
|
static int8_t CdcReceive([[maybe_unused]] uint8_t* buf, uint32_t* length)
|
||||||
{
|
{
|
||||||
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
|
if(USBD_CDC_ReceivePacket(&hUsbDeviceFS) != USBD_OK)
|
||||||
|
return USBD_FAIL;
|
||||||
|
|
||||||
|
if(m_reading)
|
||||||
|
return USBD_FAIL;
|
||||||
|
|
||||||
|
m_receiving = true;
|
||||||
for(uint32_t i = 0; i < *length; ++i) {
|
for(uint32_t i = 0; i < *length; ++i) {
|
||||||
const auto byte = m_usbAsyncRxBuffer[i];
|
rxHandler(m_usbAsyncRxBuffer.data[i]);
|
||||||
rxHandler(byte);
|
|
||||||
}
|
}
|
||||||
|
m_receiving = false;
|
||||||
return USBD_OK;
|
return USBD_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[gnu::always_inline]] static inline void rxHandler(const uint8_t& data)
|
[[gnu::always_inline]] static inline void rxHandler(const volatile uint8_t& data)
|
||||||
{
|
{
|
||||||
const size_t newHead = (m_rxBuffer.head + 1) % RX_BUFFER_SIZE;
|
const size_t newHead = (m_rxBuffer.head + 1) % RX_BUFFER_SIZE;
|
||||||
|
|
||||||
if(newHead != m_rxBuffer.tail) {
|
// Overflow, overwrites last element
|
||||||
|
if(newHead == m_rxBuffer.tail) {
|
||||||
|
const size_t newTail = (m_rxBuffer.tail + 1) % RX_BUFFER_SIZE;
|
||||||
|
m_rxBuffer.tail = newTail;
|
||||||
|
}
|
||||||
|
|
||||||
m_rxBuffer.data[newHead] = data;
|
m_rxBuffer.data[newHead] = data;
|
||||||
m_rxBuffer.head = newHead;
|
m_rxBuffer.head = newHead;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// TODO: Handle overflow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class cfg>
|
template<class cfg>
|
||||||
volatile RingBuffer<VirtualComPort<cfg>::TX_BUFFER_SIZE> VirtualComPort<cfg>::m_txBuffer = {0, 0, {0}};
|
Buffer<VirtualComPort<cfg>::TX_BUFFER_SIZE> VirtualComPort<cfg>::m_txBuffer = {0, {0}};
|
||||||
|
|
||||||
template<class cfg>
|
template<class cfg>
|
||||||
volatile RingBuffer<VirtualComPort<cfg>::RX_BUFFER_SIZE> VirtualComPort<cfg>::m_rxBuffer = {0, 0, {0}};
|
Buffer<VirtualComPort<cfg>::TX_BUFFER_SIZE> VirtualComPort<cfg>::m_usbAsyncTxBuffer = {0, {0}};
|
||||||
|
|
||||||
template<class cfg>
|
template<class cfg>
|
||||||
std::array<volatile uint8_t, 64> VirtualComPort<cfg>::m_usbAsyncRxBuffer = {0};
|
RingBuffer<VirtualComPort<cfg>::RX_BUFFER_SIZE> VirtualComPort<cfg>::m_rxBuffer = {0, 0, {0}};
|
||||||
|
|
||||||
|
template<class cfg>
|
||||||
|
Buffer<VirtualComPort<cfg>::RX_BUFFER_SIZE> VirtualComPort<cfg>::m_usbAsyncRxBuffer = {0, {0}};
|
||||||
|
|
||||||
|
template<class cfg>
|
||||||
|
volatile bool VirtualComPort<cfg>::m_receiving = false;
|
||||||
|
|
||||||
|
template<class cfg>
|
||||||
|
volatile bool VirtualComPort<cfg>::m_reading = false;
|
||||||
|
|
||||||
} // namespace uart::detail
|
} // namespace uart::detail
|
||||||
|
Loading…
Reference in New Issue
Block a user