/***************************************************************************//** * @file usart3_dma.c * @brief UART with DMA *//****************************************************************************/ //------------------------------C library--------------------------------------- #include #include #include //----------------------------user includes------------------------------------- #include "usart3_dma.h" //------------------------------------------------------------------------------ /* Private typedefs ----------------------------------------------------------*/ typedef struct { uint8_t BufA[USART3_TXDMA_BUF_SIZE]; uint8_t BufB[USART3_TXDMA_BUF_SIZE]; int DataCntA; // data count in BufA int DataCntB; // data count in BufB int BufSelTx; // -1:none, 0:A, 1:B int BufSelWr; // 0:A, 1:B uint8_t FlushA; uint8_t FlushB; } Usart3TxBuf_t; typedef void (*FuncProcData)(const uint8_t* data, uint16_t len); /* Private macros ------------------------------------------------------------*/ #define ARRAY_COUNT(arr) (sizeof(arr) / sizeof((arr)[0])) /* Private variables ---------------------------------------------------------*/ uint8_t Usart3RxDmaBuf[USART3_RXDMA_BUF_SIZE] = {0}; Usart3TxBuf_t Usart3TxDmaBuf = { {0},{0}, 0, 0, -1, 0 }; static FuncProcData ProcRxData = NULL; // function to process received data, set by Usart3_DMA_Init /* Public variables ----------------------------------------------------------*/ /* Functions -----------------------------------------------------------------*/ /***************************************************************************//** * @brief USART3 init *//****************************************************************************/ void Usart3_DMA_Init(void (*procRxData)(const uint8_t* data, uint16_t len)) { ProcRxData = procRxData; // USART and DMA initialization code should be generated by Cube MX } /***************************************************************************//** * @brief USART3 Rx/Tx DMA task, to be called periodically in main loop *//****************************************************************************/ void Usart3_DMA_Task() { // Handle received data from USART3 DMA static uint32_t LastNDTR = USART3_RXDMA_BUF_SIZE; static uint32_t WritePos = 0U; uint32_t curNDTR = LL_DMA_GetBlkDataLength(USART3_GPDMA, USART3_DMA_RX_CHANNEL); uint32_t newBytes; if (curNDTR <= LastNDTR) { // no Wrap-Around newBytes = LastNDTR - curNDTR; }else { // NDTR is bigger than before: Wrap-Around occurred newBytes = LastNDTR + (USART3_RXDMA_BUF_SIZE - curNDTR); } if (newBytes > 0U) { uint32_t startPos = WritePos; // start of new data uint32_t endPos = (WritePos + newBytes) % USART3_RXDMA_BUF_SIZE; // end of new data if (startPos < endPos) { // no Wrap-Around ProcRxData(&Usart3RxDmaBuf[startPos], newBytes); } else { // first: to buffer end, second: from buffer begin uint32_t part1 = USART3_RXDMA_BUF_SIZE - startPos; ProcRxData(&Usart3RxDmaBuf[startPos], part1); uint32_t part2 = newBytes - part1; ProcRxData(&Usart3RxDmaBuf[0], part2); } // Store new Write Position and NDTR WritePos = endPos; // next start position for write LastNDTR = curNDTR; // NTDR value for next cycle } // Handle USART3 Tx DMA transfer complete if (LL_DMA_IsActiveFlag_TC(USART3_GPDMA, USART3_DMA_TX_CHANNEL)) { LL_DMA_ClearFlag_TC(USART3_GPDMA, USART3_DMA_TX_CHANNEL); Usart3TxBuf_t* d = &Usart3TxDmaBuf; switch (d->BufSelTx) { case 0: { // transfer from BufA completed d->BufSelTx = -1; // no transfer ongoing d->DataCntA = 0; // reset BufA data count if (d->FlushB) { // flush requested for BufB // start transfer from BufB LL_DMA_ClearFlag_TC(USART3_GPDMA, USART3_DMA_TX_CHANNEL); LL_DMA_SetSrcAddress(USART3_GPDMA, USART3_DMA_TX_CHANNEL, (uint32_t)d->BufB); LL_DMA_SetBlkDataLength(USART3_GPDMA, USART3_DMA_TX_CHANNEL, d->DataCntB); LL_DMA_EnableChannel(USART3_GPDMA, USART3_DMA_TX_CHANNEL); d->BufSelTx = 1; // mark transfer from BufB ongoing d->BufSelWr = 0; // switch write to BufA d->FlushB = 0; // clear flush request } }break; case 1: { // transfer from BufB completed d->BufSelTx = -1; // no transfer ongoing d->DataCntB = 0; // reset BufB data count if (d->FlushA) { // flush requested for BufA // start transfer from BufA LL_DMA_ClearFlag_TC(USART3_GPDMA, USART3_DMA_TX_CHANNEL); LL_DMA_SetSrcAddress(USART3_GPDMA, USART3_DMA_TX_CHANNEL, (uint32_t)d->BufA); LL_DMA_SetBlkDataLength(USART3_GPDMA, USART3_DMA_TX_CHANNEL, d->DataCntA); LL_DMA_EnableChannel(USART3_GPDMA, USART3_DMA_TX_CHANNEL); d->BufSelTx = 0; // mark transfer from BufA ongoing d->BufSelWr = 1; // switch write to BufB d->FlushA = 0; // clear flush request } }break; } } } /***************************************************************************//** * @brief Write data to USART3 Tx buffer, and optionally start DMA transfer if flush is requested * @param src: data to write * @param n: count of data * @param flush: if non-zero, request to start DMA transfer after writing data *//****************************************************************************/ uint8_t Usart3_TxBufWrite(const void* src, size_t n, uint8_t flush) { Usart3TxBuf_t* d = &Usart3TxDmaBuf; switch (d->BufSelWr) { case 0: { // BufA selected for write int newpos = d->DataCntA + n; if (newpos <= 2 * USART3_TXDMA_BUF_SIZE) { // enough space in the entire buffer if (d->BufSelTx == 1 && newpos > USART3_TXDMA_BUF_SIZE) { // transfer from BufB ongoing and BufA overflow return 4; // overflow: skip data } memcpy(&d->BufA[d->DataCntA], src, n); if (newpos > USART3_TXDMA_BUF_SIZE) { // BufA full d->DataCntA = USART3_TXDMA_BUF_SIZE; // BufA full // start transfer from BufA LL_DMA_ClearFlag_TC(USART3_GPDMA, USART3_DMA_TX_CHANNEL); LL_DMA_SetSrcAddress(USART3_GPDMA, USART3_DMA_TX_CHANNEL, (uint32_t)d->BufA); LL_DMA_SetBlkDataLength(USART3_GPDMA, USART3_DMA_TX_CHANNEL, d->DataCntA); LL_DMA_EnableChannel(USART3_GPDMA, USART3_DMA_TX_CHANNEL); d->BufSelTx = 0; // mark transfer from BufA ongoing d->BufSelWr = 1; // switch write to BufB d->DataCntB = newpos - USART3_TXDMA_BUF_SIZE; // adjust BufB write position if (flush) { // BufA flush already started d->FlushB = 1; // mark BufB flush requested } }else { // BufA not full yet d->DataCntA = newpos; if (flush) { if (d->BufSelTx == -1) { // flush requested and no transfer ongoing // start transfer from BufA LL_DMA_ClearFlag_TC(USART3_GPDMA, USART3_DMA_TX_CHANNEL); LL_DMA_SetSrcAddress(USART3_GPDMA, USART3_DMA_TX_CHANNEL, (uint32_t)d->BufA); LL_DMA_SetBlkDataLength(USART3_GPDMA, USART3_DMA_TX_CHANNEL, d->DataCntA); LL_DMA_EnableChannel(USART3_GPDMA, USART3_DMA_TX_CHANNEL); d->BufSelTx = 0; // mark transfer from BufA ongoing d->BufSelWr = 1; // switch write to BufB }else { d->FlushA = 1; // mark BufA flush requested } } } }else { // not enough space in the entire buffer return 1; // overflow: skip data } }break; case 1: { // BufB selected for write int newpos = d->DataCntB + n; if (newpos <= USART3_TXDMA_BUF_SIZE) { // enough space in BufB memcpy(&d->BufB[d->DataCntB], src, n); d->DataCntB = newpos; if (flush) { if (d->BufSelTx == -1) { // flush requested and no transfer ongoing // start transfer from BufB LL_DMA_ClearFlag_TC(USART3_GPDMA, USART3_DMA_TX_CHANNEL); LL_DMA_SetSrcAddress(USART3_GPDMA, USART3_DMA_TX_CHANNEL, (uint32_t)d->BufB); LL_DMA_SetBlkDataLength(USART3_GPDMA, USART3_DMA_TX_CHANNEL, d->DataCntB); LL_DMA_EnableChannel(USART3_GPDMA, USART3_DMA_TX_CHANNEL); d->BufSelTx = 1; // mark transfer from BufB ongoing d->BufSelWr = 0; // switch write to BufA }else { d->FlushB = 1; // mark BufB flush requested } } }else { // not enough space in BufB // try to flush BufB if possible if (d->BufSelTx == -1) { // no transfer ongoing // start transfer from BufB LL_DMA_ClearFlag_TC(USART3_GPDMA, USART3_DMA_TX_CHANNEL); LL_DMA_SetSrcAddress(USART3_GPDMA, USART3_DMA_TX_CHANNEL, (uint32_t)d->BufB); LL_DMA_SetBlkDataLength(USART3_GPDMA, USART3_DMA_TX_CHANNEL, d->DataCntB); LL_DMA_EnableChannel(USART3_GPDMA, USART3_DMA_TX_CHANNEL); d->BufSelTx = 1; // mark transfer from BufB ongoing d->BufSelWr = 0; // switch write to BufA } if (d->BufSelTx != 0) { // BufA ready to write (not being transmitted) d->BufSelWr = 0; // switch write to BufA if (n <= USART3_TXDMA_BUF_SIZE) { // enough space in BufA memcpy(d->BufA, src, n); d->DataCntA = n; if (flush) { // flush requested if (d->BufSelTx == -1) { // no transfer ongoing // start transfer from BufA LL_DMA_ClearFlag_TC(USART3_GPDMA, USART3_DMA_TX_CHANNEL); LL_DMA_SetSrcAddress(USART3_GPDMA, USART3_DMA_TX_CHANNEL, (uint32_t)d->BufA); LL_DMA_SetBlkDataLength(USART3_GPDMA, USART3_DMA_TX_CHANNEL, d->DataCntA); LL_DMA_EnableChannel(USART3_GPDMA, USART3_DMA_TX_CHANNEL); d->BufSelTx = 0; // mark transfer from BufA ongoing d->BufSelWr = 1; // switch write to BufB }else { // transfer ongoing d->FlushA = 1; // mark BufA flush requested } } }else { return 2; // overflow: skip data } }else { return 3; // overflow: skip data } } }break; } return 0; // no error }