AdaptiveBrightness/AdaptiveBrightnessFirmware/Inc/uart_vcp.hpp

210 lines
6.6 KiB
C++

#pragma once
#include <array>
#include <cstdint>
#include "usbd_cdc.h"
#include "usbd_def.h"
extern "C" {
extern USBD_CDC_ItfTypeDef USBD_Interface_fops_FS;
extern USBD_HandleTypeDef hUsbDeviceFS;
}
namespace uart::detail {
template<size_t Size>
struct RingBuffer {
size_t head;
size_t tail;
uint8_t data[Size];
};
template<class cfg>
class VirtualComPort {
public:
static constexpr auto DATA_BITS = cfg::DATA_BITS;
[[gnu::always_inline]] static void init()
{
USBD_Interface_fops_FS.Init = CdcInit;
USBD_Interface_fops_FS.DeInit = CdcDeInit;
USBD_Interface_fops_FS.Control = CdcControl;
USBD_Interface_fops_FS.Receive = CdcReceive;
}
[[gnu::always_inline]] static bool rxByte(uint8_t& byte)
{
if(m_rxBuffer.head == m_rxBuffer.tail)
return false;
const size_t newTail = (m_rxBuffer.tail + 1) % RX_BUFFER_SIZE;
byte = m_rxBuffer.data[newTail];
m_rxBuffer.tail = newTail;
return true;
}
[[gnu::always_inline]] static void txByte(const uint8_t& byte)
{
const size_t newHead = (m_txBuffer.head + 1) % TX_BUFFER_SIZE;
if(m_txBuffer.tail == newHead) {
flushTx();
}
m_txBuffer.data[newHead] = byte;
m_txBuffer.head = newHead;
}
[[gnu::always_inline]] static bool peek(uint8_t& byte)
{
if(m_rxBuffer.head == m_rxBuffer.tail)
return false;
const size_t newTail = (m_rxBuffer.tail + 1) % RX_BUFFER_SIZE;
byte = m_rxBuffer.data[newTail];
return true;
}
[[gnu::always_inline]] static void flushTx()
{
if(m_txBuffer.head == m_txBuffer.tail) {
return;
}
constexpr auto usbReady = []() {
USBD_CDC_HandleTypeDef* hcdc = static_cast<USBD_CDC_HandleTypeDef*>(hUsbDeviceFS.pClassData);
return hcdc->TxState != 0;
};
constexpr auto txPacket = [usbReady](volatile uint8_t* buffer, size_t length) {
USBD_CDC_SetTxBuffer(&hUsbDeviceFS, const_cast<uint8_t*>(buffer), length);
USBD_CDC_TransmitPacket(&hUsbDeviceFS);
while(!usbReady())
;
};
if(m_txBuffer.head > m_txBuffer.tail) {
txPacket(&m_txBuffer.data[m_txBuffer.tail], m_txBuffer.head - m_txBuffer.tail);
}
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;
}
private:
static constexpr auto TX_BUFFER_SIZE = 512;
static constexpr auto RX_BUFFER_SIZE = 512;
static volatile RingBuffer<TX_BUFFER_SIZE> m_txBuffer;
static volatile RingBuffer<RX_BUFFER_SIZE> m_rxBuffer;
static std::array<volatile uint8_t, 64> m_usbAsyncRxBuffer;
static int8_t CdcInit()
{
USBD_CDC_SetTxBuffer(&hUsbDeviceFS, const_cast<uint8_t*>(m_txBuffer.data), 0);
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, const_cast<uint8_t*>(m_usbAsyncRxBuffer.data()));
return USBD_OK;
}
static int8_t CdcDeInit()
{
USBD_Interface_fops_FS.Init = nullptr;
USBD_Interface_fops_FS.DeInit = nullptr;
USBD_Interface_fops_FS.Control = nullptr;
USBD_Interface_fops_FS.Receive = nullptr;
return USBD_OK;
}
static int8_t CdcControl(uint8_t cmd, [[maybe_unused]] uint8_t* buf, [[maybe_unused]] uint16_t length)
{
switch(cmd) {
case CDC_SEND_ENCAPSULATED_COMMAND:
break;
case CDC_GET_ENCAPSULATED_RESPONSE:
break;
case CDC_SET_COMM_FEATURE:
break;
case CDC_GET_COMM_FEATURE:
break;
case CDC_CLEAR_COMM_FEATURE:
break;
case CDC_SET_LINE_CODING:
/*******************************************************************************/
/* Line Coding Structure */
/*-----------------------------------------------------------------------------*/
/* Offset | Field | Size | Value | Description */
/* 0 | dwDTERate | 4 | Number |Data terminal rate, in bits per second*/
/* 4 | bCharFormat | 1 | Number | Stop bits */
/* 0 - 1 Stop bit */
/* 1 - 1.5 Stop bits */
/* 2 - 2 Stop bits */
/* 5 | bParityType | 1 | Number | Parity */
/* 0 - None */
/* 1 - Odd */
/* 2 - Even */
/* 3 - Mark */
/* 4 - Space */
/* 6 | bDataBits | 1 | Number Data bits (5, 6, 7, 8 or 16). */
/*******************************************************************************/
break;
case CDC_GET_LINE_CODING:
break;
case CDC_SET_CONTROL_LINE_STATE:
break;
case CDC_SEND_BREAK:
break;
default:
break;
}
return USBD_OK;
}
static int8_t CdcReceive([[maybe_unused]] uint8_t* buf, uint32_t* length)
{
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
for(uint32_t i = 0; i < *length; ++i) {
const auto byte = m_usbAsyncRxBuffer[i];
rxHandler(byte);
}
return USBD_OK;
}
[[gnu::always_inline]] static inline void rxHandler(const uint8_t& data)
{
const size_t newHead = (m_rxBuffer.head + 1) % RX_BUFFER_SIZE;
if(newHead != m_rxBuffer.tail) {
m_rxBuffer.data[newHead] = data;
m_rxBuffer.head = newHead;
}
else {
// TODO: Handle overflow
}
}
};
template<class cfg>
volatile RingBuffer<VirtualComPort<cfg>::TX_BUFFER_SIZE> VirtualComPort<cfg>::m_txBuffer = {0, 0, {0}};
template<class cfg>
volatile RingBuffer<VirtualComPort<cfg>::RX_BUFFER_SIZE> VirtualComPort<cfg>::m_rxBuffer = {0, 0, {0}};
template<class cfg>
std::array<volatile uint8_t, 64> VirtualComPort<cfg>::m_usbAsyncRxBuffer = {0};
} // namespace uart::detail