/* * usart_handler.c * * Created on: Sep 16, 2019 * Author: abody */ #include #include #include #include #include "diag.h" #include "f4ll_c/dmahelper.h" #include "f4ll_c/crcscheduler.h" #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; } void Pku_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->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->txBuffer.usartStatus = st; st->rxBuffers[0].usartStatus = st; st->rxBuffers[1].usartStatus = st; st->packetReceivedCallback = packetReceivedCallback; st->packetReceivedCallbacParam = 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* Pku_GetTxBuffer(struct usartstatus_t *status) { return status->txBuffer.packet.payload; } static inline void BuildHeader(struct usart_buffer_t *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(struct usartpacket_t *packet) { return packet->header.startByte == STARTMARKER && (packet->header.startByte ^ packet->header.serial ^ packet->header.payloadLength) == packet->header.hash; } uint8_t Pku_Post(struct usartstatus_t *status, uint8_t const *payload, uint16_t length, struct crcstatus_t *crcStatus, uint8_t waitForCrcQueue) { // static uint32_t count = 0; // ITM->PORT[1].u32 = count++; if(length > 256) return 1; BuildHeader(&status->txBuffer, status->txSerial++, length); 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.payload, length, NULL, (uint32_t*)(status->txBuffer.packet.payload + payloadLength)); while(waitForCrcQueue && Crc_IsSlotQueued(&status->crcSlot, 0)); Pku_SetupTransmit(status->usart, status->txDmaInfo.dma, status->txDmaInfo.stream, &status->txBuffer.packet, status->txBuffer.requestedLength); StatsIncSent(&status->stats); return 0; } void Pku_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 Pku_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 + 1))); 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 Pku_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 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 + 1)) == calculatedCrc) ub->busy = 1; else { ub->error = ub->busy = 1; ub->errorInfo = calculatedCrc; } if(ub->usartStatus->packetReceivedCallback) ub->usartStatus->packetReceivedCallback(ub->usartStatus->packetReceivedCallbacParam, ub); } void Pku_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(CheckHeader(&status->rxBuffers[status->activeRxBuf].packet)) Crc_Enqueue(status->crcStatus, &status->crcSlot, 1, 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); Pku_SetupReceive(status); DIAG_INTERRUPT_OUT(); } void Pku_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); } 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; DIAG_INTERRUPT_OUT(); } void Pku_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(CheckHeader(&status->rxBuffers[status->activeRxBuf].packet)) { if(rcvdLen >= sizeof(struct usartpacketheader_t) + RoundUpTo4(status->rxBuffers[status->activeRxBuf].packet.header.payloadLength + 1) + 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->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(); }