/* * ll_hsusart_impl.h * * Created on: Oct 29, 2019 * Author: abody */ #include #include "f4ll/ll_hsusart.h" namespace f4ll { template static inline T RoundUpTo4(T input) { return (input + 3) & (((T)-1) - 3); } LL_HsUsart::LL_HsUsart(USART_TypeDef *usart, DMA_TypeDef *dma, uint32_t streamRx, uint32_t streamTx) : LL_UsartCore(usart, dma, streamRx, streamTx) { LL_CrcHandler::Instance().AttachSlot(m_crcSlot); } uint8_t *LL_HsUsart::GetTxPacketBuffer(void) { return m_txBuffer.packet.payload; } USART_TypeDef* LL_HsUsart::GetUsart(void) { return m_usart; } LL_HsUsart::Stats const & LL_HsUsart::GetStats(void) { return m_stats; } bool LL_HsUsart::IsTxBusy() { return m_txBuffer.busy; } bool LL_HsUsart::IsTxFailed() { return m_txBuffer.error; } bool LL_HsUsart::IsRxBusy(bool second) { return m_rxBuffers[second].busy; } bool LL_HsUsart::IsRxFailed(bool second) { return m_rxBuffers[second].error; } void LL_HsUsart::RxProcessed(bool second) { m_rxBuffers[second].busy = false; m_rxBuffers[second].error = false; } void LL_HsUsart::SetCallback(IHsUsartCallback *callback, uintptr_t callbackParam) { m_userCallback = callback; m_userCallbackParam = callbackParam; } void LL_HsUsart::BuildHeader(Packet &packet, uint8_t serialNo, uint8_t length) { uint8_t hash = STARTMARKER; packet.header.startByte = STARTMARKER; packet.header.serial = serialNo; hash ^= serialNo; packet.header.payloadLength = length; hash ^= length; packet.header.hash = hash; } bool LL_HsUsart::CheckHeader(PacketHeader &header) { return header.startByte == STARTMARKER && (header.startByte ^ header.serial ^ header.payloadLength) == header.hash; } void LL_HsUsart::PostPacket(uint8_t const *payload, uint8_t length, bool waitForCrcQueue) { uint16_t payloadLength = RoundUpTo4((uint16_t)length); BuildHeader(m_txBuffer.packet, m_txSerialNo++, length); if(payload) memcpy(m_txBuffer.packet.payload, payload, length); m_txBuffer.requestedLength = sizeof(m_txBuffer.packet.header) + payloadLength + sizeof(uint32_t); m_txBuffer.busy = true; m_txBuffer.error = false; LL_CrcHandler::Instance().Enqueue(m_crcSlot, 0, &m_txBuffer.packet, sizeof(PacketHeader) + payloadLength, nullptr, reinterpret_cast(m_txBuffer.packet.payload + payloadLength)); while(waitForCrcQueue && LL_CrcHandler::Instance().IsQueued(m_crcSlot, 0)); SetupTransmit(&m_txBuffer.packet, m_txBuffer.requestedLength); ++m_stats.sent; } void LL_HsUsart::SetupReceive() { m_rxBuffers[m_rxBufferSelector].requestedLength = sizeof(m_rxBuffers[m_rxBufferSelector].packet); LL_UsartCore::SetupReceive(&m_rxBuffers[m_rxBufferSelector], sizeof(m_rxBuffers[m_rxBufferSelector].packet)); } void LL_HsUsart::ReceiverIdle(void) { uint16_t rcvdLen = m_rxBuffers[m_rxBufferSelector].requestedLength - LL_DMA_GetDataLength(m_rxDma.GetDma(), m_rxDma.GetStream()); if(rcvdLen >= sizeof(PacketHeader)) { if(CheckHeader(m_rxBuffers[m_rxBufferSelector].packet.header)) { if(rcvdLen >= sizeof(PacketHeader) + RoundUpTo4((uint16_t)m_rxBuffers[m_rxBufferSelector].packet.header.payloadLength) + sizeof(uint32_t)) LL_DMA_DisableStream(m_rxDma.GetDma(), m_rxDma.GetStream()); else ++m_stats.premature_payload; } else { m_rxBuffers[m_rxBufferSelector].error = 1; LL_DMA_DisableStream(m_rxDma.GetDma(), m_rxDma.GetStream()); } } else ++m_stats.premature_hdr; } void LL_HsUsart::TransmissionComplete(void) { LL_USART_DisableDirectionTx(m_usart); // enforcing an idle frame LL_USART_EnableDirectionTx(m_usart); m_txBuffer.busy = 0; } void LL_HsUsart::RxDmaTransferComplete(void) { if(CheckHeader(m_rxBuffers[m_rxBufferSelector].packet.header)) LL_CrcHandler::Instance().Enqueue(m_crcSlot, 1, &m_rxBuffers[m_rxBufferSelector].packet, sizeof(PacketHeader) + RoundUpTo4((uint16_t)m_rxBuffers[m_rxBufferSelector].packet.header.payloadLength), this, m_rxBufferSelector); else { ++m_stats.hdrError; m_rxBuffers[m_rxBufferSelector].error = true; } SwitchRxBuffers(); } void LL_HsUsart::RxDmaHalfTransfer(void) { } void LL_HsUsart::RxDmaError(LL_DmaHelper::DmaErrorType reason) { m_rxBuffers[m_rxBufferSelector].error = 1; ++m_stats.rxDmaError; SwitchRxBuffers(); } void LL_HsUsart::TxDmaTransferComplete(void) { LL_USART_EnableIT_TC(m_usart); LL_DMA_DisableStream(m_txDma.GetDma(), m_txDma.GetStream()); } void LL_HsUsart::TxDmaHalfTransfer(void) { } void LL_HsUsart::TxDmaError(LL_DmaHelper::DmaErrorType reason) { m_txBuffer.error = 1; ++m_stats.txDmaError; } void LL_HsUsart::SwitchRxBuffers(void) { ++m_stats.rcvd; m_rxBufferSelector = !m_rxBufferSelector; if(m_rxBuffers[m_rxBufferSelector].busy) ++m_stats.overrun; SetupReceive(); } void LL_HsUsart::CrcSucceeded(uintptr_t callbackParam, uint32_t crc, uint8_t task) { Buffer &buf(m_rxBuffers[static_cast(callbackParam)]); buf.busy = 1; if(*(uint32_t*) (buf.packet.payload + RoundUpTo4((uint16_t)buf.packet.header.payloadLength)) != crc) { buf.error = 1; buf.errorInfo = crc; ++m_stats.payloadErrror; } if(m_userCallback) buf.busy = !m_userCallback->PacketReceived(this, m_userCallbackParam, buf.packet); } void LL_HsUsart::CrcFailed(uintptr_t callbackParam, uint32_t crc, uint8_t task) { Buffer &buf(m_rxBuffers[static_cast(callbackParam)]); buf.busy = buf.error = true; buf.errorInfo = 0; ++m_stats.payloadErrror; if(m_userCallback) buf.busy = !m_userCallback->PacketReceived(this, m_userCallbackParam, buf.packet); } } // namespace f4ll