/* * usart_handler.c * * Created on: Sep 16, 2019 * Author: abody */ #include #include #include #include #if defined(HAVE_DIAG) #include "diag.h" #endif #include "f4ll_c/dmahelper.h" #include "f4ll_c/crcscheduler.h" #ifndef MOCKABLE #define MOCKABLE(x) x #endif #ifndef DIAG_RX_BUFFER_SWITCH # define DIAG_RX_BUFFER_SWITCH(x) #endif #ifndef DIAG_INTERRUPT_IN # define DIAG_INTERRUPT_IN() #endif #ifndef DIAG_INTERRUPT_OUT # define DIAG_INTERRUPT_OUT() #endif #define STARTMARKER 0x95 static inline uint32_t RoundUpTo4(uint32_t inp) { return (inp + 3) & 0xfffc; } #ifndef USART_STATS_DISABLED static inline void StatsIncOverrun(struct usart_stats *s) { ++s->overrun; } static inline void StatsIncHdrError(struct usart_stats *s, uint32_t hdr) { ++s->hdrError; s->lastErrHdr = hdr; } static inline void StatsIncPayloadError(struct usart_stats *s, uint32_t pep1, uint32_t pep2) { ++s->payloadErrror; s->pep1 = pep1; s->pep2 = pep2; } static inline void StatsIncDmaError(struct usart_stats *s) { ++s->dmaError; } static inline void StatsIncRcvd(struct usart_stats *s) { ++s->rcvd; } static inline void StatsIncPremature_hdr(struct usart_stats *s) { ++s->premature_hdr; } static inline void StatsIncPremature_payload(struct usart_stats *s) { ++s->premature_payload; } static inline void StatsIncSent(struct usart_stats *s) { ++s->sent; } static inline void StatsAddSkiped(struct usart_stats *s, uint8_t cnt) { s->skiped += s->rcvd > 2 ? cnt : 0; } #else // USART_STATS_DISABLED #define StatsIncOverrun(x) #define StatsIncHdrError(x,y) #define StatsIncPayloadError(x,y,z) #define StatsIncDmaError(x) #define StatsIncRcvd(x) #define StatsIncPremature_hdr(x) #define StatsIncPremature_payload(x) #define StatsIncSent(x) #define StatsAddSkiped(x) #endif // USART_STATS_DISABLED void MOCKABLE(Pu_Init)( struct usartstatus_t *st, USART_TypeDef *usart, DMA_TypeDef *dma, uint32_t stream_rx, uint32_t stream_tx, struct crcstatus_t *crcStatus, pku_packetreceivedcallback_t packetReceivedCallback, void * packetReceivedCallbackParam) { uint32_t status = usart->SR; volatile uint32_t tmpreg = usart->DR; // clearing some of the error/status bits in the USART (void) tmpreg; (void) status; st->usart = usart; Dma_Init(&st->rxDmaInfo, dma, stream_rx); Dma_Init(&st->txDmaInfo, dma, stream_tx); st->txBuffer.busy = 0; st->txBuffer.error = 0; st->txBuffer.requestedLength = 0; st->txBuffer.usartStatus = st; 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->rxBuffers[0].usartStatus = st; st->rxBuffers[1].usartStatus = st; st->packetReceivedCallback = packetReceivedCallback; st->packetReceivedCallbackParam = packetReceivedCallbackParam; st->rxSerial = -1; st->txSerial = 0; st->activeRxBuf = 0; st->crcStatus = crcStatus; Crc_AttachTask(crcStatus, &st->crcSlot, st->crcTasks, 2); memset(&st->stats, 0, sizeof(st->stats)); *Dma_GetIfcReg(dma, stream_rx) = Dma_GetTcMask(stream_rx) | Dma_GetHtMask(stream_rx) | Dma_GetTeMask(stream_rx) | Dma_GetFeMask(stream_rx) | Dma_GetDmeMask(stream_rx); *Dma_GetIfcReg(dma, stream_tx) = Dma_GetTcMask(stream_tx) | Dma_GetHtMask(stream_tx) | Dma_GetTeMask(stream_tx) | Dma_GetFeMask(stream_tx) | Dma_GetDmeMask(stream_tx); 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(usart); } uint8_t* MOCKABLE(Pu_GetTxBuffer)(struct usartstatus_t *status) { return status->txBuffer.packet.payload; } uint8_t MOCKABLE(Pu_CheckHeader)(struct usartpacket_t *packet) { return packet->header.startByte == STARTMARKER && (packet->header.startByte ^ packet->header.serial ^ packet->header.payloadLength) == packet->header.hash; } uint8_t MOCKABLE(Pu_Post)(struct usartstatus_t *status, uint8_t const *payload, uint8_t length, struct crcstatus_t *crcStatus, uint8_t waitForCrcQueue) { struct usart_buffer_t *buffer = &status->txBuffer; uint8_t hash = STARTMARKER; buffer->packet.header.startByte = STARTMARKER; buffer->packet.header.serial = status->txSerial; hash ^= status->txSerial++; buffer->packet.header.payloadLength = length; hash ^= length; buffer->packet.header.hash = hash; uint16_t payloadLength = RoundUpTo4(length); if(payload) memcpy(status->txBuffer.packet.payload, payload, length); status->txBuffer.requestedLength = sizeof(struct usartpacketheader_t) + payloadLength + sizeof(uint32_t); // +4 for the hash status->txBuffer.busy = 1; status->txBuffer.error = 0; Crc_Enqueue(status->crcStatus, &status->crcSlot, 0, &status->txBuffer.packet, sizeof(status->txBuffer.packet.header) + payloadLength, NULL, (uint32_t*)(status->txBuffer.packet.payload + payloadLength)); while(waitForCrcQueue && Crc_IsSlotQueued(&status->crcSlot, 0)); Pu_SetupTransmit(status->usart, status->txDmaInfo.dma, status->txDmaInfo.stream, &status->txBuffer.packet, status->txBuffer.requestedLength); StatsIncSent(&status->stats); return 0; } void MOCKABLE(Pu_SetupReceive)(struct usartstatus_t *status) { uint8_t packetIndex = status->activeRxBuf; LL_DMA_ConfigAddresses(status->rxDmaInfo.dma, status->rxDmaInfo.stream, LL_USART_DMA_GetRegAddr(status->usart), (uint32_t)&status->rxBuffers[packetIndex], LL_DMA_DIRECTION_PERIPH_TO_MEMORY); 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->usart); LL_USART_ClearFlag_ORE(status->usart); LL_DMA_EnableStream(status->rxDmaInfo.dma, status->rxDmaInfo.stream); } void MOCKABLE(Pu_SetupTransmit)(USART_TypeDef *usart, DMA_TypeDef* dma, uint32_t stream, void *buffer, uint32_t length) { LL_DMA_ConfigAddresses(dma, stream, (uint32_t)buffer, LL_USART_DMA_GetRegAddr(usart), LL_DMA_DIRECTION_MEMORY_TO_PERIPH); LL_DMA_SetDataLength(dma, stream, length); LL_USART_EnableDMAReq_TX(usart); LL_DMA_EnableStream(dma, stream); } void MOCKABLE(Pu_ConsumePacket)(struct usartstatus_t *status, uint8_t packetIndex) { struct usart_buffer_t *buffer = &status->rxBuffers[packetIndex]; if(buffer->busy) { if(buffer->error) StatsIncPayloadError(&status->stats, buffer->errorInfo, *(uint32_t*) (buffer->packet.payload + RoundUpTo4(buffer->packet.header.payloadLength))); 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 MOCKABLE(Pu_HandleRxDmaIrq)(struct usartstatus_t *status) { DIAG_INTERRUPT_IN(); StatsIncRcvd(&status->stats); if(*status->rxDmaInfo.isReg & status->rxDmaInfo.tcMask) { *status->rxDmaInfo.ifcReg = status->rxDmaInfo.tcMask; if(Pu_CheckHeader(&status->rxBuffers[status->activeRxBuf].packet)) { Crc_Enqueue(status->crcStatus, &status->crcSlot, 1, &status->rxBuffers[status->activeRxBuf].packet, RoundUpTo4(status->rxBuffers[status->activeRxBuf].packet.header.payloadLength) + sizeof(struct usartpacketheader_t), Pu_RxCrcComputedCallback, &status->rxBuffers[status->activeRxBuf]); } else { StatsIncHdrError(&status->stats, *(uint32_t*)&status->rxBuffers[status->activeRxBuf].packet.header); status->rxBuffers[status->activeRxBuf].error = 1; } } 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); Pu_SetupReceive(status); DIAG_INTERRUPT_OUT(); } void MOCKABLE(Pu_RxCrcComputedCallback)(void *callbackParm, uint32_t calculatedCrc, uint8_t success) { struct usart_buffer_t *ub = (struct usart_buffer_t*) callbackParm; if(!success) ub->error = 1; else if(*(uint32_t*) (ub->packet.payload + RoundUpTo4(ub->packet.header.payloadLength)) == calculatedCrc) ub->busy = 1; else { ub->error = ub->busy = 1; ub->errorInfo = calculatedCrc; } if(ub->usartStatus->packetReceivedCallback) ub->usartStatus->packetReceivedCallback(ub->usartStatus->packetReceivedCallbackParam, ub); } void MOCKABLE(Pu_HandleTxDmaIrq)(struct usartstatus_t *status) { DIAG_INTERRUPT_IN(); if(*status->txDmaInfo.isReg & status->txDmaInfo.tcMask) { // DMA transfer complete *status->txDmaInfo.ifcReg = status->txDmaInfo.tcMask; LL_USART_EnableIT_TC(status->usart); LL_DMA_DisableStream(status->txDmaInfo.dma, status->txDmaInfo.stream); } if(*status->txDmaInfo.isReg & status->txDmaInfo.teMask) { *status->txDmaInfo.ifcReg = status->txDmaInfo.teMask; status->txBuffer.error = 1; LL_USART_EnableIT_TC(status->usart); LL_DMA_DisableStream(status->txDmaInfo.dma, status->txDmaInfo.stream); 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; DIAG_INTERRUPT_OUT(); } void MOCKABLE(Pu_HandleUsartIrq)(struct usartstatus_t *status) { DIAG_INTERRUPT_IN(); if(LL_USART_IsActiveFlag_IDLE(status->usart) && LL_USART_IsEnabledIT_IDLE(status->usart)) { // receiver idle LL_USART_ClearFlag_IDLE(status->usart); uint16_t rcvdLen = status->rxBuffers[status->activeRxBuf].requestedLength - LL_DMA_GetDataLength(status->rxDmaInfo.dma, status->rxDmaInfo.stream); if(rcvdLen >= sizeof(struct usartpacketheader_t)) { if(Pu_CheckHeader(&status->rxBuffers[status->activeRxBuf].packet)) { if(rcvdLen >= sizeof(struct usartpacketheader_t) + RoundUpTo4(status->rxBuffers[status->activeRxBuf].packet.header.payloadLength) + sizeof(uint32_t)) LL_DMA_DisableStream(status->rxDmaInfo.dma, status->rxDmaInfo.stream); else StatsIncPremature_payload(&status->stats); } else { status->rxBuffers[status->activeRxBuf].error = 1; status->rxBuffers[status->activeRxBuf].busy = 1; LL_DMA_DisableStream(status->rxDmaInfo.dma, status->rxDmaInfo.stream); } } else StatsIncPremature_hdr(&status->stats); } if(LL_USART_IsActiveFlag_TC(status->usart) && LL_USART_IsEnabledIT_TC(status->usart)) { // transmission complete LL_USART_DisableIT_TC(status->usart); LL_USART_DisableDirectionTx(status->usart); // enforcing an idle frame LL_USART_EnableDirectionTx(status->usart); status->txBuffer.busy = 0; } DIAG_INTERRUPT_OUT(); }