/***************************************************************************//** * @file usart2_dma.c * @brief UART with DMA *//****************************************************************************/ //------------------------------C library--------------------------------------- #include #include #include //----------------------------user includes------------------------------------- #include "usart2_dma.h" //------------------------------------------------------------------------------ /* Private typedefs ----------------------------------------------------------*/ typedef struct { uint8_t BufA[USART2_TXDMA_BUF_SIZE]; uint8_t BufB[USART2_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; } Usart2TxBuf_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 Usart2RxDmaBuf[USART2_RXDMA_BUF_SIZE] = {0}; Usart2TxBuf_t Usart2TxDmaBuf = { {0},{0}, 0, 0, -1, 0 }; static FuncProcData ProcRxData = NULL; // function to process received data, set by Usart2_DMA_Init /* Public variables ----------------------------------------------------------*/ /* Functions -----------------------------------------------------------------*/ /***************************************************************************//** * @brief USART2 init *//****************************************************************************/ void Usart2_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 USART2 Rx/Tx DMA task, to be called periodically in main loop *//****************************************************************************/ void Usart2_DMA_Task() { // Handle received data from USART2 DMA static uint32_t LastNDTR = USART2_RXDMA_BUF_SIZE; static uint32_t WritePos = 0U; uint32_t curNDTR = LL_DMA_GetBlkDataLength(USART2_GPDMA, USART2_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 + (USART2_RXDMA_BUF_SIZE - curNDTR); } if (newBytes > 0U) { uint32_t startPos = WritePos; // start of new data uint32_t endPos = (WritePos + newBytes) % USART2_RXDMA_BUF_SIZE; // end of new data if (startPos < endPos) { // no Wrap-Around ProcRxData(&Usart2RxDmaBuf[startPos], newBytes); } else { // first: to buffer end, second: from buffer begin uint32_t part1 = USART2_RXDMA_BUF_SIZE - startPos; ProcRxData(&Usart2RxDmaBuf[startPos], part1); uint32_t part2 = newBytes - part1; ProcRxData(&Usart2RxDmaBuf[0], part2); } // Store new Write Position and NDTR WritePos = endPos; // next start position for write LastNDTR = curNDTR; // NTDR value for next cycle } // Handle USART2 Tx DMA transfer complete if (LL_DMA_IsActiveFlag_TC(USART2_GPDMA, USART2_DMA_TX_CHANNEL)) { LL_DMA_ClearFlag_TC(USART2_GPDMA, USART2_DMA_TX_CHANNEL); Usart2TxBuf_t* d = &Usart2TxDmaBuf; 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(USART2_GPDMA, USART2_DMA_TX_CHANNEL); LL_DMA_SetSrcAddress(USART2_GPDMA, USART2_DMA_TX_CHANNEL, (uint32_t)d->BufB); LL_DMA_SetBlkDataLength(USART2_GPDMA, USART2_DMA_TX_CHANNEL, d->DataCntB); LL_DMA_EnableChannel(USART2_GPDMA, USART2_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(USART2_GPDMA, USART2_DMA_TX_CHANNEL); LL_DMA_SetSrcAddress(USART2_GPDMA, USART2_DMA_TX_CHANNEL, (uint32_t)d->BufA); LL_DMA_SetBlkDataLength(USART2_GPDMA, USART2_DMA_TX_CHANNEL, d->DataCntA); LL_DMA_EnableChannel(USART2_GPDMA, USART2_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 USART2 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 Usart2_TxBufWrite(const void* src, size_t n, uint8_t flush) { Usart2TxBuf_t* d = &Usart2TxDmaBuf; switch (d->BufSelWr) { case 0: { // BufA selected for write int newpos = d->DataCntA + n; if (newpos <= 2 * USART2_TXDMA_BUF_SIZE) { // enough space in the entire buffer if (d->BufSelTx == 1 && newpos > USART2_TXDMA_BUF_SIZE) { // transfer from BufB ongoing and BufA overflow return 4; // overflow: skip data } memcpy(&d->BufA[d->DataCntA], src, n); if (newpos > USART2_TXDMA_BUF_SIZE) { // BufA full d->DataCntA = USART2_TXDMA_BUF_SIZE; // BufA full // start transfer from BufA LL_DMA_ClearFlag_TC(USART2_GPDMA, USART2_DMA_TX_CHANNEL); LL_DMA_SetSrcAddress(USART2_GPDMA, USART2_DMA_TX_CHANNEL, (uint32_t)d->BufA); LL_DMA_SetBlkDataLength(USART2_GPDMA, USART2_DMA_TX_CHANNEL, d->DataCntA); LL_DMA_EnableChannel(USART2_GPDMA, USART2_DMA_TX_CHANNEL); d->BufSelTx = 0; // mark transfer from BufA ongoing d->BufSelWr = 1; // switch write to BufB d->DataCntB = newpos - USART2_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(USART2_GPDMA, USART2_DMA_TX_CHANNEL); LL_DMA_SetSrcAddress(USART2_GPDMA, USART2_DMA_TX_CHANNEL, (uint32_t)d->BufA); LL_DMA_SetBlkDataLength(USART2_GPDMA, USART2_DMA_TX_CHANNEL, d->DataCntA); LL_DMA_EnableChannel(USART2_GPDMA, USART2_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 <= USART2_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(USART2_GPDMA, USART2_DMA_TX_CHANNEL); LL_DMA_SetSrcAddress(USART2_GPDMA, USART2_DMA_TX_CHANNEL, (uint32_t)d->BufB); LL_DMA_SetBlkDataLength(USART2_GPDMA, USART2_DMA_TX_CHANNEL, d->DataCntB); LL_DMA_EnableChannel(USART2_GPDMA, USART2_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(USART2_GPDMA, USART2_DMA_TX_CHANNEL); LL_DMA_SetSrcAddress(USART2_GPDMA, USART2_DMA_TX_CHANNEL, (uint32_t)d->BufB); LL_DMA_SetBlkDataLength(USART2_GPDMA, USART2_DMA_TX_CHANNEL, d->DataCntB); LL_DMA_EnableChannel(USART2_GPDMA, USART2_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 <= USART2_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(USART2_GPDMA, USART2_DMA_TX_CHANNEL); LL_DMA_SetSrcAddress(USART2_GPDMA, USART2_DMA_TX_CHANNEL, (uint32_t)d->BufA); LL_DMA_SetBlkDataLength(USART2_GPDMA, USART2_DMA_TX_CHANNEL, d->DataCntA); LL_DMA_EnableChannel(USART2_GPDMA, USART2_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 }