Implemented templated vcp wrapper
This commit is contained in:
parent
7d75537427
commit
70e3d52dea
158
AdaptiveBrightnessFirmware/Inc/terminal.hpp
Normal file
158
AdaptiveBrightnessFirmware/Inc/terminal.hpp
Normal file
@ -0,0 +1,158 @@
|
||||
#pragma once
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr auto ENDL = "\r\n";
|
||||
constexpr auto HELP_CMD = "help";
|
||||
constexpr auto READ_CMD = "read";
|
||||
|
||||
constexpr auto VERSION_CMD = "version";
|
||||
constexpr auto VERSION = "1.0";
|
||||
|
||||
static inline bool substringEquals(const char* str1, const char* str2, const size_t& size)
|
||||
{
|
||||
return (std::strncmp(str1, str2, size) == 0);
|
||||
}
|
||||
|
||||
static inline bool stringEquals(const char* str1, const char* str2, const size_t& size)
|
||||
{
|
||||
if(size == std::strlen(str2)) {
|
||||
return substringEquals(str1, str2, size);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class Uart>
|
||||
class Terminal {
|
||||
public:
|
||||
static void init()
|
||||
{
|
||||
m_serial.init();
|
||||
|
||||
m_serial << detail::ENDL;
|
||||
printVersion();
|
||||
m_serial << detail::ENDL << "$ ";
|
||||
}
|
||||
|
||||
static void callback()
|
||||
{
|
||||
if(receiveInput()) {
|
||||
parseInput();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr auto INPUT_BUFFER_SIZE = 128;
|
||||
static constexpr auto BACKSPACE = uint8_t{0x7f};
|
||||
static constexpr auto CTRL_C = uint8_t{0x03};
|
||||
|
||||
static Uart m_serial;
|
||||
static char m_inputBuffer[INPUT_BUFFER_SIZE];
|
||||
static uint16_t m_inputSize;
|
||||
|
||||
static bool receiveInput()
|
||||
{
|
||||
uint8_t inputByte;
|
||||
|
||||
while(m_serial.rxByte(inputByte)) {
|
||||
if(std::isprint(inputByte) || inputByte == CTRL_C) {
|
||||
m_inputBuffer[m_inputSize++] = inputByte;
|
||||
|
||||
// Handle Ctrl + C
|
||||
if(inputByte == CTRL_C) {
|
||||
m_serial << "^C" << detail::ENDL;
|
||||
return true;
|
||||
}
|
||||
// Echo
|
||||
else {
|
||||
m_serial << static_cast<char>(inputByte);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle backspace
|
||||
if(inputByte == BACKSPACE && m_inputSize > 0) {
|
||||
m_serial << "\b \b";
|
||||
--m_inputSize;
|
||||
}
|
||||
// Handle line terminator
|
||||
else if(inputByte == '\r' || inputByte == '\n') {
|
||||
// Consume possible second line terminator
|
||||
if(m_serial.peek(inputByte) && (inputByte == '\r' || inputByte == '\n')) {
|
||||
m_serial.rxByte(inputByte);
|
||||
}
|
||||
m_serial << detail::ENDL;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(m_inputSize >= INPUT_BUFFER_SIZE) {
|
||||
m_serial << detail::ENDL << "WARNING: Terminal input buffer overflow!" << detail::ENDL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void parseInput()
|
||||
{
|
||||
if(m_inputSize) {
|
||||
if(m_inputBuffer[m_inputSize - 1] == CTRL_C) {
|
||||
handleCtrlC();
|
||||
}
|
||||
else {
|
||||
if(detail::substringEquals(m_inputBuffer, detail::HELP_CMD, m_inputSize)) {
|
||||
printHelp();
|
||||
}
|
||||
else if(detail::substringEquals(m_inputBuffer, detail::READ_CMD, m_inputSize)) {
|
||||
readSensor();
|
||||
}
|
||||
else if(detail::substringEquals(m_inputBuffer, detail::VERSION_CMD, m_inputSize)) {
|
||||
printVersion();
|
||||
}
|
||||
else {
|
||||
printUnknown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_inputSize = 0;
|
||||
m_serial << "$ ";
|
||||
}
|
||||
|
||||
static void handleCtrlC() { m_serial << "Abort!" << detail::ENDL; }
|
||||
|
||||
static void printHelp()
|
||||
{
|
||||
m_serial << "AdaptiveBrightness command overview: " << detail::ENDL;
|
||||
m_serial << detail::HELP_CMD << " .......: prints this help message" << detail::ENDL;
|
||||
m_serial << detail::READ_CMD << " .......: reads and displays all LDR values" << detail::ENDL;
|
||||
m_serial << detail::VERSION_CMD << " ....: displays firmware version" << detail::ENDL;
|
||||
}
|
||||
|
||||
static void readSensor() { m_serial << "Sensor values: 1 2 3" << detail::ENDL; }
|
||||
|
||||
static void printVersion() { m_serial << "AdaptiveBrightness v" << detail::VERSION << detail::ENDL; }
|
||||
|
||||
static void printUnknown()
|
||||
{
|
||||
m_serial << "Unknown command \"";
|
||||
for(uint16_t i = 0; i < m_inputSize; ++i) {
|
||||
m_serial << static_cast<char>(m_inputBuffer[i]);
|
||||
}
|
||||
m_serial << "\"" << detail::ENDL;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Uart>
|
||||
char Terminal<Uart>::m_inputBuffer[INPUT_BUFFER_SIZE];
|
||||
|
||||
template<class Uart>
|
||||
uint16_t Terminal<Uart>::m_inputSize = 0;
|
329
AdaptiveBrightnessFirmware/Inc/uart.hpp
Normal file
329
AdaptiveBrightnessFirmware/Inc/uart.hpp
Normal file
@ -0,0 +1,329 @@
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "uart_config.hpp"
|
||||
#include "uart_vcp.hpp"
|
||||
|
||||
namespace uart {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename...>
|
||||
struct always_false : std::false_type {
|
||||
};
|
||||
template<typename... Ts>
|
||||
inline constexpr auto always_false_v = always_false<Ts...>::value;
|
||||
|
||||
template<typename T, T Limit, size_t Base>
|
||||
static constexpr size_t cntDigits()
|
||||
{
|
||||
T num = Limit;
|
||||
size_t cnt = 0;
|
||||
|
||||
do {
|
||||
num /= Base;
|
||||
++cnt;
|
||||
} while(num > 0);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
template<typename T, size_t Base>
|
||||
static constexpr size_t maxNumDigits()
|
||||
{
|
||||
constexpr T MinVal = std::numeric_limits<T>::min();
|
||||
constexpr T MaxVal = std::numeric_limits<T>::max();
|
||||
|
||||
constexpr T MinDigits = cntDigits<T, MinVal, Base>();
|
||||
constexpr T MaxDigits = cntDigits<T, MaxVal, Base>();
|
||||
|
||||
return (MinDigits < MaxDigits) ? MaxDigits : MinDigits;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class Driver>
|
||||
class Uart {
|
||||
public:
|
||||
// Constructing a uart object does not initialize the driver to allow different specializations with the same
|
||||
// back-end to exists at the same time
|
||||
// Note that init must be called every time when switching specializations with the same back-end
|
||||
Uart() = default;
|
||||
|
||||
// Moving and copying uart objects is not supported
|
||||
Uart(const Uart&) = delete;
|
||||
Uart(Uart&&) = delete;
|
||||
Uart& operator=(const Uart&) = delete;
|
||||
Uart& operator=(Uart&&) = delete;
|
||||
|
||||
// Before using the uart init must be called
|
||||
static void init() { Driver::init(); }
|
||||
|
||||
static void txByte(const uint8_t& byte) { Driver::txByte(byte); }
|
||||
|
||||
static bool rxByte(uint8_t& byte) { return Driver::rxByte(byte); }
|
||||
|
||||
static bool peek(uint8_t& byte) { return Driver::peek(byte); }
|
||||
|
||||
static bool peek() { return Driver::peek(); }
|
||||
|
||||
static void flushTx() { Driver::flushTx(); }
|
||||
|
||||
static void txString(const char* str)
|
||||
{
|
||||
static_assert(Driver::DATA_BITS == DataBits::EIGHT, "Strings are only supported with 8 data bits");
|
||||
|
||||
while(char ch = *str++)
|
||||
txByte(ch);
|
||||
}
|
||||
|
||||
template<typename T, size_t Base = 10, size_t Padding = 0, char PadChar = '0', bool LowerCase = true>
|
||||
static void txNumber(const T& val)
|
||||
{
|
||||
static_assert(std::is_integral_v<T>, "Only supported on integral types");
|
||||
static_assert(Base >= 2, "Numbers with base less than 2 make no sense");
|
||||
static_assert(Base <= 16, "Numbers with base higher than 16 are not supported");
|
||||
static_assert(Padding <= detail::maxNumDigits<T, Base>(), "Cannot pad more than maximum length of number");
|
||||
|
||||
constexpr char AlphaChar = (LowerCase) ? 'a' : 'A';
|
||||
constexpr size_t NumDigits = detail::maxNumDigits<T, Base>();
|
||||
|
||||
T digits = val;
|
||||
|
||||
if(digits < 0) {
|
||||
digits = -digits;
|
||||
txByte('-');
|
||||
}
|
||||
|
||||
uint8_t buffer[NumDigits];
|
||||
uint8_t* bufEnd = buffer + NumDigits - 1;
|
||||
|
||||
do {
|
||||
const uint8_t lastDigit = digits % Base;
|
||||
*bufEnd-- = (lastDigit < 10) ? ('0' + lastDigit) : (AlphaChar + lastDigit - 10);
|
||||
digits /= Base;
|
||||
} while(digits > 0);
|
||||
|
||||
if(Padding > 0) {
|
||||
size_t strLen = buffer + NumDigits - (bufEnd + 1);
|
||||
|
||||
if(Padding > strLen) {
|
||||
for(size_t i = Padding; i > strLen && bufEnd >= buffer; --i) {
|
||||
*bufEnd-- = PadChar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(uint8_t* buf = bufEnd + 1; buf < buffer + NumDigits; ++buf) {
|
||||
txByte(*buf);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Output stream overloads
|
||||
|
||||
Uart& operator<<(const char* str)
|
||||
{
|
||||
txString(str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Uart& operator<<(const char& val)
|
||||
{
|
||||
txByte(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Uart& operator<<(const signed char& val)
|
||||
{
|
||||
txNumber(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Uart& operator<<(const unsigned char& val)
|
||||
{
|
||||
txNumber(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Uart& operator<<(const short& val)
|
||||
{
|
||||
txNumber(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Uart& operator<<(const unsigned short& val)
|
||||
{
|
||||
txNumber(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Uart& operator<<(const int& val)
|
||||
{
|
||||
txNumber(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Uart& operator<<(const unsigned int& val)
|
||||
{
|
||||
txNumber(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Uart& operator<<(const long& val)
|
||||
{
|
||||
txNumber(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Uart& operator<<(const unsigned long& val)
|
||||
{
|
||||
txNumber(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Uart& operator<<(const long long& val)
|
||||
{
|
||||
txNumber(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Uart& operator<<(const unsigned long long& val)
|
||||
{
|
||||
txNumber(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
Uart& operator<<(float) const
|
||||
{
|
||||
static_assert(detail::always_false_v<Ts...>, "Not supported by hardware");
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
Uart& operator<<(double) const
|
||||
{
|
||||
static_assert(detail::always_false_v<Ts...>, "Not supported by hardware");
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
Uart& operator<<(long double) const
|
||||
{
|
||||
static_assert(detail::always_false_v<Ts...>, "Not supported by hardware");
|
||||
}
|
||||
|
||||
Uart& operator<<(const bool& val)
|
||||
{
|
||||
txString(val ? "true" : "false");
|
||||
return *this;
|
||||
}
|
||||
|
||||
Uart& operator<<(const void* val)
|
||||
{
|
||||
txString("0x");
|
||||
txNumber<uint32_t, 16, 4, '0', false>(reinterpret_cast<uint32_t>(val));
|
||||
return *this;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Input stream overloads
|
||||
|
||||
template<typename... Ts>
|
||||
Uart& operator>>(char&) const
|
||||
{
|
||||
static_assert(detail::always_false_v<Ts...>, "Not implemented");
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
Uart& operator>>(unsigned char&) const
|
||||
{
|
||||
static_assert(detail::always_false_v<Ts...>, "Not implemented");
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
Uart& operator>>(short&) const
|
||||
{
|
||||
static_assert(detail::always_false_v<Ts...>, "Not implemented");
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
Uart& operator>>(unsigned short&) const
|
||||
{
|
||||
static_assert(detail::always_false_v<Ts...>, "Not implemented");
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
Uart& operator>>(int&) const
|
||||
{
|
||||
static_assert(detail::always_false_v<Ts...>, "Not implemented");
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
Uart& operator>>(unsigned int&) const
|
||||
{
|
||||
static_assert(detail::always_false_v<Ts...>, "Not implemented");
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
Uart& operator>>(long&) const
|
||||
{
|
||||
static_assert(detail::always_false_v<Ts...>, "Not implemented");
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
Uart& operator>>(unsigned long&) const
|
||||
{
|
||||
static_assert(detail::always_false_v<Ts...>, "Not implemented");
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
Uart& operator>>(long long&) const
|
||||
{
|
||||
static_assert(detail::always_false_v<Ts...>, "Not implemented");
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
Uart& operator>>(unsigned long long&) const
|
||||
{
|
||||
static_assert(detail::always_false_v<Ts...>, "Not implemented");
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
Uart& operator>>(float&) const
|
||||
{
|
||||
static_assert(detail::always_false_v<Ts...>, "Not supported by hardware");
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
Uart& operator>>(double&) const
|
||||
{
|
||||
static_assert(detail::always_false_v<Ts...>, "Not supported by hardware");
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
Uart& operator>>(long double&) const
|
||||
{
|
||||
static_assert(detail::always_false_v<Ts...>, "Not supported by hardware");
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
Uart& operator>>(bool&) const
|
||||
{
|
||||
static_assert(detail::always_false_v<Ts...>, "Not implemented");
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
Uart& operator>>(const void*&) const
|
||||
{
|
||||
static_assert(detail::always_false_v<Ts...>, "Not implemented");
|
||||
}
|
||||
};
|
||||
|
||||
template<typename cfg = Config<>>
|
||||
using Vcp = Uart<detail::VirtualComPort<cfg>>;
|
||||
|
||||
} // namespace uart
|
32
AdaptiveBrightnessFirmware/Inc/uart_config.hpp
Normal file
32
AdaptiveBrightnessFirmware/Inc/uart_config.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
namespace uart {
|
||||
|
||||
enum class DataBits {
|
||||
FIVE,
|
||||
SIX,
|
||||
SEVEN,
|
||||
EIGHT,
|
||||
NINE,
|
||||
};
|
||||
|
||||
enum class StopBits {
|
||||
ONE,
|
||||
TWO,
|
||||
};
|
||||
|
||||
enum class Parity {
|
||||
NONE,
|
||||
ODD,
|
||||
EVEN,
|
||||
};
|
||||
|
||||
template<uint32_t baudRate = 9600, DataBits dataBits = DataBits::EIGHT, Parity parity = Parity::NONE, StopBits stopBits = StopBits::ONE>
|
||||
struct Config {
|
||||
static constexpr auto BAUD_RATE = baudRate;
|
||||
static constexpr auto DATA_BITS = dataBits;
|
||||
static constexpr auto PARITY = parity;
|
||||
static constexpr auto STOP_BITS = stopBits;
|
||||
};
|
||||
|
||||
} // namespace uart
|
209
AdaptiveBrightnessFirmware/Inc/uart_vcp.hpp
Normal file
209
AdaptiveBrightnessFirmware/Inc/uart_vcp.hpp
Normal file
@ -0,0 +1,209 @@
|
||||
#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
|
@ -92,21 +92,6 @@ extern USBD_CDC_ItfTypeDef USBD_Interface_fops_FS;
|
||||
|
||||
/* USER CODE BEGIN EXPORTED_VARIABLES */
|
||||
|
||||
/* USER CODE END EXPORTED_VARIABLES */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @defgroup USBD_CDC_IF_Exported_FunctionsPrototype USBD_CDC_IF_Exported_FunctionsPrototype
|
||||
* @brief Public functions declaration.
|
||||
* @{
|
||||
*/
|
||||
|
||||
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len);
|
||||
|
||||
/* USER CODE BEGIN EXPORTED_FUNCTIONS */
|
||||
|
||||
/* USER CODE END EXPORTED_FUNCTIONS */
|
||||
|
||||
/**
|
||||
|
@ -43,7 +43,6 @@ Src/adc.c \
|
||||
Src/usb_device.c \
|
||||
Src/usbd_conf.c \
|
||||
Src/usbd_desc.c \
|
||||
Src/usbd_cdc_if.c \
|
||||
Src/stm32f0xx_it.c \
|
||||
Src/stm32f0xx_hal_msp.c \
|
||||
Drivers/STM32F0xx_HAL_Driver/Src/stm32f0xx_ll_usb.c \
|
||||
@ -75,6 +74,7 @@ Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c
|
||||
# C++ sources
|
||||
CXX_SOURCES = \
|
||||
Src/main.cpp \
|
||||
Src/uart_vcp.cpp \
|
||||
Src/vcp_receiver.cpp
|
||||
|
||||
# ASM sources
|
||||
|
@ -5,9 +5,9 @@
|
||||
|
||||
#include "adc.h"
|
||||
#include "init.h"
|
||||
#include "usbd_cdc_if.h"
|
||||
#include "terminal.hpp"
|
||||
#include "uart.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "vcp_receiver.hpp"
|
||||
|
||||
std::array<uint16_t, 3> sampleLightSensors()
|
||||
{
|
||||
@ -30,9 +30,10 @@ std::array<uint16_t, 3> sampleLightSensors()
|
||||
|
||||
int main()
|
||||
{
|
||||
init();
|
||||
uart::Vcp<> serial;
|
||||
|
||||
VcpReceiver vcpReceiver;
|
||||
init();
|
||||
serial.init();
|
||||
|
||||
HAL_GPIO_WritePin(RED_LED_GPIO_Port, RED_LED_Pin, GPIO_PIN_RESET);
|
||||
HAL_GPIO_WritePin(GREEN_LED_GPIO_Port, GREEN_LED_Pin, GPIO_PIN_RESET);
|
||||
@ -47,24 +48,23 @@ int main()
|
||||
uint8_t data = 0;
|
||||
|
||||
while(true) {
|
||||
if(vcpReceiver.rxByte(data)) {
|
||||
CDC_Transmit_FS(&data, 1);
|
||||
if(serial.rxByte(data)) {
|
||||
serial.txByte(data);
|
||||
}
|
||||
|
||||
const auto ldrValues = sampleLightSensors();
|
||||
|
||||
HAL_GPIO_WritePin(BLUE_LED_GPIO_Port, BLUE_LED_Pin, GPIO_PIN_RESET);
|
||||
|
||||
std::array<uint8_t, 32> printBuffer;
|
||||
std::array<char, 32> printBuffer;
|
||||
|
||||
for(uint8_t i = 0; i < ldrValues.size(); ++i) {
|
||||
const auto ldrID = i + 1;
|
||||
const auto percentage = ldrValues[i] * 100 / 0xFFF;
|
||||
const auto bufLen = std::sprintf(reinterpret_cast<char*>(printBuffer.data()), "LDR%d: %04hu - %03d%%\r\n%s", ldrID, ldrValues[i], percentage,
|
||||
(i == 2) ? "\r\n" : "");
|
||||
const auto bufLen = std::sprintf(printBuffer.data(), "LDR%d: %04hu - %03d%%\r\n%s", ldrID, ldrValues[i], percentage, (i == 2) ? "\r\n" : "");
|
||||
if(bufLen > 0) {
|
||||
while(CDC_Transmit_FS(printBuffer.data(), bufLen) == USBD_BUSY)
|
||||
;
|
||||
serial.txString(printBuffer.data());
|
||||
serial.flushTx();
|
||||
}
|
||||
}
|
||||
|
||||
|
10
AdaptiveBrightnessFirmware/Src/uart_vcp.cpp
Normal file
10
AdaptiveBrightnessFirmware/Src/uart_vcp.cpp
Normal file
@ -0,0 +1,10 @@
|
||||
#include "uart_vcp.hpp"
|
||||
|
||||
extern "C" {
|
||||
USBD_CDC_ItfTypeDef USBD_Interface_fops_FS = {
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
};
|
||||
}
|
@ -1,312 +0,0 @@
|
||||
/* USER CODE BEGIN Header */
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file : usbd_cdc_if.c
|
||||
* @version : v2.0_Cube
|
||||
* @brief : Usb device for Virtual Com Port.
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© Copyright (c) 2020 STMicroelectronics.
|
||||
* All rights reserved.</center></h2>
|
||||
*
|
||||
* This software component is licensed by ST under Ultimate Liberty license
|
||||
* SLA0044, the "License"; You may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at:
|
||||
* www.st.com/SLA0044
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
/* USER CODE END Header */
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "usbd_cdc_if.h"
|
||||
|
||||
/* USER CODE BEGIN INCLUDE */
|
||||
|
||||
/* USER CODE END INCLUDE */
|
||||
|
||||
/* Private typedef -----------------------------------------------------------*/
|
||||
/* Private define ------------------------------------------------------------*/
|
||||
/* Private macro -------------------------------------------------------------*/
|
||||
|
||||
/* USER CODE BEGIN PV */
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
|
||||
/* USER CODE END PV */
|
||||
|
||||
/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
|
||||
* @brief Usb device library.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @addtogroup USBD_CDC_IF
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @defgroup USBD_CDC_IF_Private_TypesDefinitions USBD_CDC_IF_Private_TypesDefinitions
|
||||
* @brief Private types.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* USER CODE BEGIN PRIVATE_TYPES */
|
||||
|
||||
/* USER CODE END PRIVATE_TYPES */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @defgroup USBD_CDC_IF_Private_Defines USBD_CDC_IF_Private_Defines
|
||||
* @brief Private defines.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* 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 512
|
||||
#define APP_TX_DATA_SIZE 512
|
||||
/* USER CODE END PRIVATE_DEFINES */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @defgroup USBD_CDC_IF_Private_Macros USBD_CDC_IF_Private_Macros
|
||||
* @brief Private macros.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* USER CODE BEGIN PRIVATE_MACRO */
|
||||
|
||||
/* USER CODE END PRIVATE_MACRO */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @defgroup USBD_CDC_IF_Private_Variables USBD_CDC_IF_Private_Variables
|
||||
* @brief Private variables.
|
||||
* @{
|
||||
*/
|
||||
/* 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];
|
||||
|
||||
/* USER CODE BEGIN PRIVATE_VARIABLES */
|
||||
|
||||
/* USER CODE END PRIVATE_VARIABLES */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @defgroup USBD_CDC_IF_Exported_Variables USBD_CDC_IF_Exported_Variables
|
||||
* @brief Public variables.
|
||||
* @{
|
||||
*/
|
||||
|
||||
extern USBD_HandleTypeDef hUsbDeviceFS;
|
||||
|
||||
/* USER CODE BEGIN EXPORTED_VARIABLES */
|
||||
|
||||
/* USER CODE END EXPORTED_VARIABLES */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @defgroup USBD_CDC_IF_Private_FunctionPrototypes USBD_CDC_IF_Private_FunctionPrototypes
|
||||
* @brief Private functions declaration.
|
||||
* @{
|
||||
*/
|
||||
|
||||
static int8_t CDC_Init_FS(void);
|
||||
static int8_t CDC_DeInit_FS(void);
|
||||
static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length);
|
||||
static int8_t CDC_Receive_FS(uint8_t* pbuf, uint32_t *Len);
|
||||
|
||||
/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */
|
||||
|
||||
/* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
USBD_CDC_ItfTypeDef USBD_Interface_fops_FS =
|
||||
{
|
||||
CDC_Init_FS,
|
||||
CDC_DeInit_FS,
|
||||
CDC_Control_FS,
|
||||
CDC_Receive_FS
|
||||
};
|
||||
|
||||
/* Private functions ---------------------------------------------------------*/
|
||||
/**
|
||||
* @brief Initializes the CDC media low layer over the FS USB IP
|
||||
* @retval USBD_OK if all operations are OK else USBD_FAIL
|
||||
*/
|
||||
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);
|
||||
return (USBD_OK);
|
||||
/* USER CODE END 3 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief DeInitializes the CDC media low layer
|
||||
* @retval USBD_OK if all operations are OK else USBD_FAIL
|
||||
*/
|
||||
static int8_t CDC_DeInit_FS(void)
|
||||
{
|
||||
/* USER CODE BEGIN 4 */
|
||||
return (USBD_OK);
|
||||
/* USER CODE END 4 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Manage the CDC class requests
|
||||
* @param cmd: Command code
|
||||
* @param pbuf: Buffer containing command data (request parameters)
|
||||
* @param length: Number of data to be sent (in bytes)
|
||||
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
|
||||
*/
|
||||
static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length)
|
||||
{
|
||||
/* USER CODE BEGIN 5 */
|
||||
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;
|
||||
|
||||
/*******************************************************************************/
|
||||
/* 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). */
|
||||
/*******************************************************************************/
|
||||
case CDC_SET_LINE_CODING:
|
||||
|
||||
break;
|
||||
|
||||
case CDC_GET_LINE_CODING:
|
||||
|
||||
break;
|
||||
|
||||
case CDC_SET_CONTROL_LINE_STATE:
|
||||
|
||||
break;
|
||||
|
||||
case CDC_SEND_BREAK:
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return (USBD_OK);
|
||||
/* USER CODE END 5 */
|
||||
}
|
||||
|
||||
extern void vcpReceiveCallback(uint8_t* buf, uint32_t len);
|
||||
|
||||
/**
|
||||
* @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
|
||||
* untill 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
|
||||
*/
|
||||
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
|
||||
{
|
||||
/* USER CODE BEGIN 6 */
|
||||
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
|
||||
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
|
||||
vcpReceiveCallback(Buf, *Len);
|
||||
return (USBD_OK);
|
||||
/* USER CODE END 6 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief CDC_Transmit_FS
|
||||
* Data to send over USB IN endpoint are sent over CDC interface
|
||||
* through this function.
|
||||
* @note
|
||||
*
|
||||
*
|
||||
* @param Buf: Buffer of data to be sent
|
||||
* @param Len: Number of data to be sent (in bytes)
|
||||
* @retval USBD_OK if all operations are OK else USBD_FAIL or USBD_BUSY
|
||||
*/
|
||||
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
|
||||
{
|
||||
uint8_t result = USBD_OK;
|
||||
/* USER CODE BEGIN 7 */
|
||||
USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData;
|
||||
if (hcdc->TxState != 0){
|
||||
return USBD_BUSY;
|
||||
}
|
||||
USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len);
|
||||
result = USBD_CDC_TransmitPacket(&hUsbDeviceFS);
|
||||
/* USER CODE END 7 */
|
||||
return result;
|
||||
}
|
||||
|
||||
/* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */
|
||||
|
||||
/* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
Loading…
Reference in New Issue
Block a user