You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

239 lines
11 KiB
C

/***************************************************************************//**
* @file usart3_dma.c
* @brief UART with DMA
*//****************************************************************************/
//------------------------------C library---------------------------------------
#include <stddef.h>
#include <stdint.h>
#include <string.h>
//----------------------------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
}