/* * ll_hsusart_impl.h * * Created on: Oct 29, 2019 * Author: abody */ #include #include "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) : m_usart(usart) , m_rxDma(dma, streamRx) , m_txDma(dma, streamTx) { LL_CrcHandler::Instance().AttachSlot(m_crcSlot); LL_DMA_EnableIT_TC(dma, streamRx); LL_DMA_EnableIT_TE(dma, streamRx); LL_DMA_EnableIT_TC(dma, streamTx); LL_DMA_EnableIT_TE(dma, streamTx); LL_USART_EnableIT_IDLE(usart); memset(&m_stats, 0, sizeof(m_stats)); } 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)); LL_DMA_ConfigAddresses(m_txDma.GetDma(), m_txDma.GetStream(), reinterpret_cast(&m_txBuffer.packet), LL_USART_DMA_GetRegAddr(m_usart), LL_DMA_DIRECTION_MEMORY_TO_PERIPH); LL_DMA_SetDataLength(m_txDma.GetDma(), m_txDma.GetStream(), m_txBuffer.requestedLength); LL_USART_EnableDMAReq_TX(m_usart); LL_DMA_EnableStream(m_txDma.GetDma(), m_txDma.GetStream()); ++m_stats.sent; } void LL_HsUsart::SetupReceive() { int packetIndex = m_rxBufferSelector; LL_DMA_ConfigAddresses(m_rxDma.GetDma(), m_rxDma.GetStream(), LL_USART_DMA_GetRegAddr(m_usart), reinterpret_cast(&m_rxBuffers[packetIndex]), LL_DMA_DIRECTION_PERIPH_TO_MEMORY); m_rxBuffers[packetIndex].requestedLength = sizeof(m_rxBuffers[packetIndex].packet); LL_DMA_SetDataLength(m_rxDma.GetDma(), m_rxDma.GetStream(), m_rxBuffers[packetIndex].requestedLength); // payload already have extra room for hash LL_USART_EnableDMAReq_RX(m_usart); LL_USART_ClearFlag_ORE(m_usart); LL_DMA_EnableStream(m_rxDma.GetDma(), m_rxDma.GetStream()); } void LL_HsUsart::UsartIrq(void) { if(LL_USART_IsActiveFlag_IDLE(m_usart) && LL_USART_IsEnabledIT_IDLE(m_usart)) { // receiver idle LL_USART_ClearFlag_IDLE(m_usart); 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; } else if(LL_USART_IsActiveFlag_TC(m_usart) && LL_USART_IsEnabledIT_TC(m_usart)) { // transmission complete LL_USART_DisableIT_TC(m_usart); LL_USART_DisableDirectionTx(m_usart); // enforcing an idle frame LL_USART_EnableDirectionTx(m_usart); m_txBuffer.busy = 0; } } void LL_HsUsart::RxDmaIrq() { ++m_stats.rcvd; if(*m_rxDma.GetIsReg() & m_rxDma.GetTcMask()) { *m_rxDma.GetIcfReg() = m_rxDma.GetTcMask(); 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; } } else if(*m_rxDma.GetIsReg() & m_rxDma.GetTeMask()) { *m_rxDma.GetIcfReg() = m_rxDma.GetTeMask(); m_rxBuffers[m_rxBufferSelector].error = 1; ++m_stats.dmaError; } m_rxBufferSelector = !m_rxBufferSelector; if(m_rxBuffers[m_rxBufferSelector].busy) ++m_stats.overrun; SetupReceive(); } void LL_HsUsart::CrcSucceeded(uintptr_t callbackParam, uint32_t crc, int prio) { 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, int prio) { 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); } void LL_HsUsart::TxDmaIrq() { if(*m_txDma.GetIsReg() & m_txDma.GetTcMask()) { // DMA transfer complete *m_txDma.GetIcfReg() = m_txDma.GetTcMask(); LL_USART_EnableIT_TC(m_usart); LL_DMA_DisableStream(m_txDma.GetDma(), m_txDma.GetStream()); } else if(*m_txDma.GetIsReg() & m_txDma.GetTeMask()) { *m_txDma.GetIcfReg() = m_txDma.GetTeMask(); m_txBuffer.error = 1; ++m_stats.dmaError; } if(*m_txDma.GetIsReg() & m_txDma.GetFeMask()) *m_txDma.GetIcfReg() = m_txDma.GetFeMask(); if(*m_txDma.GetIsReg() & m_txDma.GetHtMask()) *m_txDma.GetIcfReg() = m_txDma.GetHtMask(); if(*m_txDma.GetIsReg() & m_txDma.GetDmeMask()) *m_txDma.GetIcfReg() = m_txDma.GetDmeMask(); } } // namespace f4ll