f407ve_freertos/components/f4ll_cpp/serialconsole.h

173 lines
4.8 KiB
C++

/*
* interrupt.h
*
* Created on: Aug 29, 2019
* Author: abody
*/
#ifndef CONSOLEHANDLER_H_
#define CONSOLEHANDLER_H_
#include <cstring>
#include "usart.h"
#include <f4ll_cpp/dmahelper.h>
#include <f4ll_cpp/uartbase.h>
namespace f4ll_cpp {
template<size_t bufSize> class SerialConsole : protected UartBase
{
public:
struct Buffer {
volatile bool busy = false;
volatile bool error = false;
uint8_t len;
char buffer[bufSize];
};
struct ISerialConsoleCallback { // these will be called from interrupt context
virtual void LineReceived(void *userParam, Buffer *buffer) = 0;
virtual void TransmissionComplete(void *userParam, Buffer *buffer) = 0;
};
SerialConsole(USART_TypeDef *uart, DMA_TypeDef *dma, uint32_t stream_rx, uint32_t stream_tx,
ISerialConsoleCallback *callback, void *callbackUserParam);
void Send(char const *buffer, uint8_t length = 0);
void SendLine(char const *buffer, uint8_t length = 0);
void HandleRxDmaIrq();
void HandleTxDmaIrq();
void HandleUsartIrq();
private:
void SetupTransmit(void *buffer, uint16_t length);
bool m_activeRxBuffer = false;
Buffer m_rxBuffers[2];
Buffer m_txBuffer;
ISerialConsoleCallback *m_callback;
void *m_callbackUserParam;
};
template<size_t bufSize> SerialConsole<bufSize>::SerialConsole(
USART_TypeDef *uart, DMA_TypeDef *dma, uint32_t stream_rx, uint32_t stream_tx,
ISerialConsoleCallback *callback, void *callbackUserParam
)
: UartBase(uart, dma, stream_rx, stream_tx)
, m_callback(callback), m_callbackUserParam(callbackUserParam)
{
LL_USART_EnableIT_IDLE(uart);
SetupReceive(m_rxBuffers[m_activeRxBuffer].buffer, bufSize);
}
template<size_t bufSize> void SerialConsole<bufSize>::SetupTransmit(void *buffer, uint16_t length)
{
m_txBuffer.busy = true;
UartBase::SetupTransmit(buffer, length);
}
template<size_t bufSize> void SerialConsole<bufSize>::HandleRxDmaIrq()
{
if(*m_rxDma.GetIsReg() & m_rxDma.GetTcMask()) {
*m_rxDma.GetIfcReg() = m_rxDma.GetTcMask();
m_rxBuffers[m_activeRxBuffer].busy = true;
if(m_callback)
m_callback->LineReceived(m_callbackUserParam, &m_rxBuffers[m_activeRxBuffer]);
}
if(*m_rxDma.GetIsReg() & m_rxDma.GetTeMask()) {
*m_rxDma.GetIfcReg() = m_rxDma.GetTeMask();
}
m_activeRxBuffer = !m_activeRxBuffer;
SetupReceive(m_rxBuffers[m_activeRxBuffer].buffer, bufSize);
}
template<size_t bufSize> void SerialConsole<bufSize>::HandleTxDmaIrq()
{
if(*m_txDma.GetIsReg() & m_txDma.GetTcMask()) { // DMA transfer complete
*m_txDma.GetIfcReg() = m_txDma.GetTcMask();
LL_USART_EnableIT_TC(m_uart);
LL_DMA_DisableStream(m_txDma.GetDma(), m_txDma.GetStream());
}
if(*m_txDma.GetIsReg() & m_txDma.GetTeMask()) {
*m_txDma.GetIfcReg() = m_txDma.GetTeMask();
m_txBuffer.error = true;
LL_USART_EnableIT_TC(m_uart);
LL_DMA_DisableStream(m_txDma.GetDma(), m_txDma.GetStream());
}
}
template<size_t bufSize> void SerialConsole<bufSize>::HandleUsartIrq()
{
if(LL_USART_IsActiveFlag_IDLE(m_uart) && LL_USART_IsEnabledIT_IDLE(m_uart)) { // receiver idle
// we assume that new line marker will arrive without an idle cycle even if it is CRLF
LL_USART_ClearFlag_IDLE(m_uart);
uint16_t rcvdLen = bufSize - LL_DMA_GetDataLength(m_rxDma.GetDma(), m_rxDma.GetStream());
if(rcvdLen ) {
bool newLine = false;;
do {
auto lastChar = m_rxBuffers[m_activeRxBuffer].buffer[rcvdLen-1];
if( lastChar == '\r' || lastChar == '\n')
newLine = true;
else
break;
} while(--rcvdLen);
if(newLine) {
m_rxBuffers[m_activeRxBuffer].buffer[rcvdLen] = 0;
m_rxBuffers[m_activeRxBuffer].len = rcvdLen;
LL_DMA_DisableStream(m_rxDma.GetDma(), m_rxDma.GetStream());
}
}
}
if(LL_USART_IsActiveFlag_TC(m_uart) && LL_USART_IsEnabledIT_TC(m_uart)) { // transmission complete
LL_USART_DisableIT_TC(m_uart);
LL_USART_DisableDirectionTx(m_uart); // enforcing an idle frame
LL_USART_EnableDirectionTx(m_uart);
m_txBuffer.busy = false;
if(m_callback)
m_callback->TransmissionComplete(m_callbackUserParam, &m_txBuffer);
}
}
template<size_t bufSize> void SerialConsole<bufSize>::Send(char const *buffer, uint8_t length)
{
if(!length) {
auto computedLength = strlen(buffer);
if(computedLength <= (uint8_t)-1)
length = computedLength;
}
if(length) {
while( m_txBuffer.busy );
memcpy(m_txBuffer.buffer, buffer, length);
SetupTransmit(m_txBuffer.buffer, length);
}
}
template<size_t bufSize> void SerialConsole<bufSize>::SendLine(char const *buffer, uint8_t length)
{
if(!length) {
auto computedLength = strlen(buffer);
if(computedLength <= (uint8_t)-1)
length = computedLength;
}
if(length) {
while( m_txBuffer.busy );
memcpy(m_txBuffer.buffer, buffer, length);
if(m_txBuffer.buffer[length-1] != '\n') {
m_txBuffer.buffer[length++] = '\r';
m_txBuffer.buffer[length++] = '\n';
}
SetupTransmit(m_txBuffer.buffer, length);
}
}
} // f4ll_cpp
#endif /* CONSOLEHANDLER_H_ */