/* * uart_handler.c * * Created on: Sep 16, 2019 * Author: abody */ #include #include "globals.h" #include "diag.h" #include "usart_handler.h" #include "dma_helper.h" #include "crc_handler.h" #include "memcpy_dma.h" #ifndef DIAG_RX_BUFFER_SWITCH # define DIAG_RX_BUFFER_SWITCH(x) #endif void InitUartStatus( UARTSTATUS *st, USART_TypeDef *uart, DMA_TypeDef *dma, uint32_t stream_rx, uint32_t stream_tx, struct crcstatus_t *crcStatus, uint8_t rxCrcSlot, uint8_t txCrcSlot) { st->uart = uart; InitDmaInfo(&st->rxDmaInfo, dma, stream_rx); InitDmaInfo(&st->txDmaInfo, dma, stream_tx); st->txBuffer.busy = 0; st->txBuffer.error = 0; st->txBuffer.requestedLength = 0; st->rxBuffers[0].busy = 0; st->rxBuffers[1].busy = 0; st->rxBuffers[0].error = 0; st->rxBuffers[1].error = 0; st->rxBuffers[0].requestedLength = 0; st->rxBuffers[1].requestedLength = 0; st->rxSerial = -1; st->txSerial = 0; st->activeRxBuf = 0; st->crcStatus = crcStatus; st->txCrcSlot = txCrcSlot; st->rxCrcSlot = rxCrcSlot; memset(&st->stats, 0, sizeof(st->stats)); LL_DMA_EnableIT_TC(dma, stream_rx); LL_DMA_EnableIT_TE(dma, stream_rx); LL_DMA_EnableIT_TC(dma, stream_tx); LL_DMA_EnableIT_TE(dma, stream_tx); LL_USART_EnableIT_IDLE(uart); } static inline void BuildHeader(UARTBUFFER *buffer, uint8_t serial, uint8_t length) { uint8_t hash = STARTMARKER; buffer->packet.header.startByte = STARTMARKER; buffer->packet.header.serial = serial; hash ^= serial; buffer->packet.header.payloadLength = length - 1; hash ^= length - 1; buffer->packet.header.hash = hash; } static inline uint8_t CheckHeader(UARTPACKET *packet) { return packet->header.startByte == STARTMARKER && (packet->header.startByte ^ packet->header.serial ^ packet->header.payloadLength) == packet->header.hash; } uint8_t PostPacket(UARTSTATUS *status, uint8_t const *payload, uint16_t length, struct crcstatus_t *crcStatus) { if(length > 256) return 1; BuildHeader(&status->txBuffer, status->txSerial++, length); uint16_t payloadLength = (length+3) & 0xfffc; // round up to 4 if(payload) MemcpyDma(status->txBuffer.packet.payload, payload, length); status->txBuffer.requestedLength = sizeof(UARTPACKETHEADER) + payloadLength + sizeof(uint32_t); // +4 for the hash status->txBuffer.busy = 1; status->txBuffer.error = 0; EnqueueCrcTask(crcStatus, status->txCrcSlot, status->txBuffer.packet.payload, length, NULL, (uint32_t*)(status->txBuffer.packet.payload + payloadLength)); while(IsSlotQueued(crcStatus, status->txCrcSlot)); SetupTransmit(status->uart, status->txDmaInfo.dma, status->txDmaInfo.stream, &status->txBuffer.packet, status->txBuffer.requestedLength); StatsIncSent(&status->stats); return 0; } void SetupReceive(UARTSTATUS *status) { uint8_t packetIndex = status->activeRxBuf; LL_DMA_ConfigAddresses(status->rxDmaInfo.dma, status->rxDmaInfo.stream, LL_USART_DMA_GetRegAddr(status->uart), (uint32_t)&status->rxBuffers[packetIndex], LL_DMA_GetDataTransferDirection(status->rxDmaInfo.dma, status->rxDmaInfo.stream)); status->rxBuffers[packetIndex].requestedLength = sizeof(status->rxBuffers[packetIndex].packet); LL_DMA_SetDataLength(status->rxDmaInfo.dma, status->rxDmaInfo.stream, status->rxBuffers[packetIndex].requestedLength); // payload already have extra room for hash LL_USART_EnableDMAReq_RX(status->uart); LL_USART_ClearFlag_ORE(status->uart); LL_DMA_EnableStream(status->rxDmaInfo.dma, status->rxDmaInfo.stream); } void ConsumePacket(UARTSTATUS *status, uint8_t packetIndex, struct crcstatus_t *crcStatus) { UARTBUFFER *buffer = &status->rxBuffers[packetIndex]; if(buffer->busy) { if(buffer->error) StatsIncPayloadError(&status->stats, buffer->errorInfo, *(uint32_t*) (buffer->packet.payload + ((buffer->packet.header.payloadLength + 1 + 3) & 0xfffc))); else { uint8_t diff = buffer->packet.header.serial - status->rxSerial; if(diff > 1) StatsAddSkiped(&status->stats, diff - 1); status->rxSerial = buffer->packet.header.serial; } } buffer->busy = buffer->error = 0; } void SetupTransmit(USART_TypeDef *uart, DMA_TypeDef* dma, uint32_t stream, void *buffer, uint32_t length) { LL_DMA_ConfigAddresses(dma, stream, (uint32_t)buffer, LL_USART_DMA_GetRegAddr(uart), LL_DMA_DIRECTION_MEMORY_TO_PERIPH); LL_DMA_SetDataLength(dma, stream, length); LL_USART_EnableDMAReq_TX(uart); LL_DMA_EnableStream(dma, stream); } void RxCrcComputedCallback(void *callbackParm, uint32_t calculatedCrc, uint8_t success) { UARTBUFFER *ub = (UARTBUFFER*) callbackParm; if(!success) ub->error = 1; else if(*(uint32_t*) (ub->packet.payload + ((ub->packet.header.payloadLength + 1 + 3) & 0xfffc)) == calculatedCrc) ub->busy = 1; else { ub->error = ub->busy = 1; ub->errorInfo = calculatedCrc; } } void HandleUsartRxDmaIrq(UARTSTATUS *status) { StatsIncRcvd(&status->stats); if(*status->rxDmaInfo.isReg & status->rxDmaInfo.tcMask) { *status->rxDmaInfo.ifcReg = status->rxDmaInfo.tcMask; if(CheckHeader(&status->rxBuffers[status->activeRxBuf].packet)) EnqueueCrcTask(status->crcStatus, status->rxCrcSlot, status->rxBuffers[status->activeRxBuf].packet.payload, status->rxBuffers[status->activeRxBuf].packet.header.payloadLength +1, RxCrcComputedCallback, &status->rxBuffers[status->activeRxBuf]); else { StatsIncHdrError(&status->stats, *(uint32_t*)&status->rxBuffers[status->activeRxBuf].packet.header); status->rxBuffers[status->activeRxBuf].error = 1; } } else if(*status->rxDmaInfo.isReg & status->rxDmaInfo.teMask) { *status->rxDmaInfo.ifcReg = status->rxDmaInfo.teMask; status->rxBuffers[status->activeRxBuf].error = 1; } status->activeRxBuf ^= 1; DIAG_RX_BUFFER_SWITCH(status->activeRxBuf); if(status->rxBuffers[status->activeRxBuf].busy) StatsIncOverrun(&status->stats); SetupReceive(status); } void HandleUsartTxDmaIrq(UARTSTATUS *status) { if(*status->txDmaInfo.isReg & status->txDmaInfo.tcMask) { // DMA transfer complete *status->txDmaInfo.ifcReg = status->txDmaInfo.tcMask; LL_USART_EnableIT_TC(status->uart); LL_DMA_DisableStream(status->txDmaInfo.dma, status->txDmaInfo.stream); } else if(*status->txDmaInfo.isReg & status->txDmaInfo.teMask) { *status->txDmaInfo.ifcReg = status->txDmaInfo.teMask; status->txBuffer.error = 1; StatsIncDmaError(&status->stats); } if(*status->txDmaInfo.isReg & status->txDmaInfo.feMask) *status->txDmaInfo.ifcReg = status->txDmaInfo.feMask; if(*status->txDmaInfo.isReg & status->txDmaInfo.htMask) *status->txDmaInfo.ifcReg = status->txDmaInfo.htMask; if(*status->txDmaInfo.isReg & status->txDmaInfo.dmeMask) *status->txDmaInfo.ifcReg = status->txDmaInfo.dmeMask; } void HandleUsartIrq(UARTSTATUS *status) { if(LL_USART_IsActiveFlag_IDLE(status->uart) && LL_USART_IsEnabledIT_IDLE(status->uart)) { // receiver idle LL_USART_ClearFlag_IDLE(status->uart); uint16_t rcvdLen = status->rxBuffers[status->activeRxBuf].requestedLength - LL_DMA_GetDataLength(status->rxDmaInfo.dma, status->rxDmaInfo.stream); if(rcvdLen >= sizeof(UARTPACKETHEADER)) { if(CheckHeader(&status->rxBuffers[status->activeRxBuf].packet)) { if(rcvdLen >= sizeof(UARTPACKETHEADER) + ((status->rxBuffers[status->activeRxBuf].packet.header.payloadLength + 1 + 3) &0xfffc) + sizeof(uint32_t)) LL_DMA_DisableStream(status->rxDmaInfo.dma, status->rxDmaInfo.stream); else StatsIncPremature_payload(&status->stats); } else { status->rxBuffers[status->activeRxBuf].error = 1; LL_DMA_DisableStream(status->rxDmaInfo.dma, status->rxDmaInfo.stream); } } else StatsIncPremature_hdr(&status->stats); } else if(LL_USART_IsActiveFlag_TC(status->uart) && LL_USART_IsEnabledIT_TC(status->uart)) { // transmission complete LL_USART_DisableIT_TC(status->uart); LL_USART_DisableDirectionTx(status->uart); // enforcing an idle frame LL_USART_EnableDirectionTx(status->uart); status->txBuffer.busy = 0; } }