USART3 DMA RX/TX works
- Rx/Tx handler in usart3_dma.* - CubeMX: USART and DMA init - DMA CH0: Tx - DMA CH1: Rx - DMA Port0 for periph transfer - DMA Port1 for mem transfer - Continuous Rx handling via DMA pointer (no INT, no TC flag) - Tx: collect data into tx buf or send immediatelymaster
parent
2e7c0f82a6
commit
336c397135
@ -0,0 +1,18 @@
|
||||
#ifndef __USART3_IT_H__
|
||||
#define __USART3_IT_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "usart3_dma_cfg.h"
|
||||
|
||||
|
||||
extern uint8_t Usart3RxDmaBuf[USART3_RXDMA_BUF_SIZE];
|
||||
|
||||
extern void Usart3_DMA_Init(void (*)(const uint8_t* data, uint16_t len));
|
||||
extern void Usart3_DMA_Task();
|
||||
extern void Usart3_PutByte(uint8_t d);
|
||||
extern void Usart3_PutData(const void* src, uint16_t n);
|
||||
extern int16_t Usart3_GetByte(void);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
#ifndef __USART3_IT_CFG_H__
|
||||
#define __USART3_IT_CFG_H__
|
||||
|
||||
#include "stm32h5xx_ll_dma.h"
|
||||
|
||||
|
||||
#define USART3_RXDMA_BUF_SIZE 2048
|
||||
#define USART3_TXDMA_BUF_SIZE 2048
|
||||
|
||||
#define USART3_GPDMA GPDMA1
|
||||
#define USART3_DMA_TX_CHANNEL LL_DMA_CHANNEL_0
|
||||
#define USART3_DMA_RX_CHANNEL LL_DMA_CHANNEL_1
|
||||
|
||||
|
||||
#endif // __USART3_CFG_H__
|
||||
@ -0,0 +1,238 @@
|
||||
/***************************************************************************//**
|
||||
* @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
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue