173 lines
4.8 KiB
C++
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_ */
|