2021-11-06 11:02 AM
Hello Friends,
I have a problem about Uart send via DMA. I had created a configuration via cube. the configuration has Uart2 Rx on DMA1 channel6 and Uart2 Tx on DMA1 channel7. Both of DMA channels are in normal mode. Uart2 interrupts are closed. My MCU platform is Nucleo-L476RG. I use hercules for uart tx rx tests. My aim is to send and receive messages with DMA without Uart Rx and Tx interrputs. I have to use HAL library but I don't like it, I'm used to using CMSIS library :(
I can receive messages many times from terminal without uart receive interrupt, but I can send message only once. When I try to send another message to terminal, there is no happen. I checked it in debug mode, I saw that HAL_UART_STATE_READY was busy in stm32l4xx_hal_uart.c file. I would like to point that message that was sent only once after reset can sent completely. HAL_UART_TxCpltCallback doesn't run when DMA send proccess is complete. But DMA1_Channel7_IRQHandler run two times (transfer half and full complete).
My second question is to receive message with DMA but I don't want to get interrupt half complete transfer. I want to get full trasfer interrupt only. I found HTIE flag in CCR register in datasheet and close it after HAL_UART_Receive_DMA function. Is it correct approach for this?
You can see my part of code in below.
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdio.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define Uart_Print(__message) HAL_UART_Transmit(&huart2,(uint8_t *)__message,strlen(__message),10)
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart2;
DMA_HandleTypeDef hdma_usart2_rx;
DMA_HandleTypeDef hdma_usart2_tx;
/* USER CODE BEGIN PV */
uint8_t UART2_rxBuffer[12] = {0};
uint8_t UART2_txBuffer[] = {"volkan benzer\n\0"};
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
uint32_t waitTimer = 0;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_DMA (&huart2, UART2_rxBuffer, 12);
hdma_usart2_rx.Instance->CCR &= 0xFFFFFFFB; //close half complete interrupt
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if((HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin) == 0) && (waitTimer == 0))
{
HAL_UART_Transmit_DMA (&huart2, UART2_txBuffer, strlen((char *)UART2_txBuffer));
waitTimer = HAL_GetTick();
}
else
{
if(waitTimer != 0)
{
if(HAL_GetTick() > (waitTimer + 100))
waitTimer = 0;
}
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
HAL_UART_Receive_DMA(&huart2, UART2_rxBuffer, 12);
Uart_Print("Rx CallBack\n\0");
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
}
/* USER CODE END 4 */
void DMA1_Channel6_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel6_IRQn 0 */
/* USER CODE END DMA1_Channel6_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart2_rx);
/* USER CODE BEGIN DMA1_Channel6_IRQn 1 */
hdma_usart2_rx.Instance->CCR &= 0xFFFFFFFB;
Uart_Print("Rx Kesme geldi\n\0");
/* USER CODE END DMA1_Channel6_IRQn 1 */
}
/**
* @brief This function handles DMA1 channel7 global interrupt.
*/
void DMA1_Channel7_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel7_IRQn 0 */
/* USER CODE END DMA1_Channel7_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart2_tx);
/* USER CODE BEGIN DMA1_Channel7_IRQn 1 */
//HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
/* USER CODE END DMA1_Channel7_IRQn 1 */
}
2021-11-06 11:20 AM
There are many examples of how to use UART with DMA in the CubeMX repository. Here is one:
2021-11-08 04:14 PM
Before the next send
HAL_UART_DMAStop (&huart2);
HAL_UART_Abort (&huart2);
Must be executed. This will change as follows.
huart-> gState = HAL_UART_STATE_READY;
huart-> RxState = HAL_UART_STATE_READY;
huart-> ReceptionType = HAL_UART_RECEPTION_STANDARD;
To know the end of DMA transmission USART_ISR_TXE is good to monitor.
2021-11-08 10:47 PM
Tx only ?
This is non blocking code
this may be too much for you...
too complicated for normal folk.
but this will run at full tilt, without any code added to the HAL RxTx interrupts
use Tx DMA as Normal Buffer
and Rx DMA as Circular Buffer
#define URxSize 0x0100 // 256 bytes
#define UTxSize 0x2000 // 8k // can be lowered if you use USB only, Uart serial buffers cant cope with data overrun.
void initSerialStreams() //initialiseStreams
{
for (int i = 0; i < Streams; i++)
U[i].uart = 0; // clear all stream devices
U[0].uart = 1; //USB port;
U[1].uart = &huart1;
// U[2].uart = &huart2;
// U[3].uart = &huart5;
// Zero DataBuffers on startup
// not really needed but good to check when installing this code.
// can use pattern fill like 0xA5
int bufLength = (Streams * (URxSize + UTxSize)) >> 2; //4 bytes to a DWORD
uint32_t * ptr = URxBuf; // buffer start
while(!(--bufLength < 0))
*ptr++ = 0; // or 0xA5
for (int i = 0; i < Streams; i++)
{
U[i].RxBufSize = URxSize;
U[i].TxBufSize = UTxSize;
U[i].TxBuf = (char*) UTxBuf + i*UTxSize;
U[i].RxBuf = (char*) URxBuf + i*URxSize;
U[i].TxPtrIN = 0;
U[i].TxPtrOUT = 0;
U[i].RxPtrIN = 0;
U[i].RxPtrOUT = 0;
U[i].TxAlmostFull = false;
U[i].TxBufHasData = false;
U[i].checkReInitialisation = false;
if (U[i].uart > 20) // not including the USB ports
initUartRxDMA(i);
}
}
this code was too long :(
2021-11-08 10:47 PM
// *********** Configure Stream Buffers ************** //
typedef struct
{
int TxPtrIN, TxPtrOUT, RxPtrIN, RxPtrOUT;
int TxAlmostFull, TxBufHasData;
int TxBufSize, RxBufSize;
char *TxBuf, *RxBuf;
UART_HandleTypeDef *uart;
uint16_t hasReceivedByte;
}SPorts;
SPorts U[Streams];
// poll this in the while (1) loop
void CheckTxBuffers(void) // Tx buffers are linear, not circular
{
for (int i = 0; i < Streams; i++) // port number
{
SPorts *thisUart = &U[i];
if (thisUart->TxBufHasData)
{
if (thisUart->uart > 20) // not USB
{
char uartState = HAL_UART_GetState(thisUart->uart);
if ((uartState == HAL_UART_STATE_READY) || (uartState == HAL_UART_STATE_BUSY_RX))
{
thisUart->TxBufHasData = false; // sending now
//current cache buffer Length
int serialBufferLength = thisUart->TxPtrIN - thisUart->TxPtrOUT;
//SCB_CleanDCache_Region(PtrOut, serialBufferLength);
char * PtrOut = thisUart->TxBuf + thisUart->TxPtrOUT;
if (HAL_UART_Transmit_DMA(thisUart->uart, PtrOut, serialBufferLength) == HAL_OK)
{
thisUart->TxPtrOUT = thisUart->TxPtrIN;
thisUart->TxBuf[thisUart->TxPtrIN] = 0; // null
}
else
{
//_Error_Handler(__FILE__, __LINE__); /* Transfer error in transmission process */
}
else
{
// send to USB port
int serialBufferLength = thisUart->TxPtrIN - thisUart->TxPtrOUT;
char * PtrOut = thisUart->TxBuf + thisUart->TxPtrOUT;
//SCB_CleanDCache_Region(PtrOut, serialBufferLength);
#ifdef installUSB
reportUSBTxisBusy = true;
char CDCBusy = true;
uint8_t FSCDCState = checkFSCDCComplete();
if (!FSCDCState) // not busy
{
uint16_t currentBufferLength = serialBufferLength;
const int maxPacketSize = 256;
if (currentBufferLength)
{
if(currentBufferLength < 8192) //CDC_DATA_FS_MAX_PACKET_SIZE is 64 ?)
{
if (currentBufferLength < maxPacketSize) {
CDCBusy = CDC_Transmit_FS(thisUart->TxBuf + thisUart->TxPtrOUT, currentBufferLength);
if (!CDCBusy) //success
{
thisUart->TxPtrOUT += currentBufferLength;
thisUart->TxBufHasData = false;
if (thisUart->TxPtrOUT == thisUart->TxPtrIN)
{
thisUart->TxPtrOUT = 0;
thisUart->TxPtrIN = 0;
}
}
}
else
{
CDCBusy = CDC_Transmit_FS(thisUart->TxBuf + thisUart->TxPtrOUT, maxPacketSize);
if (!CDCBusy)
{
thisUart->TxPtrOUT += maxPacketSize;
if (thisUart->TxPtrOUT == thisUart->TxPtrIN) {
thisUart->TxBufHasData = false;
thisUart->TxPtrOUT = 0;
thisUart->TxPtrIN = 0;
}
}
}
}
else
{
int length = sprintf(thisUart->TxBuf, "\ndumped oversize frame\e[K\n");
CDCBusy = CDC_Transmit_FS(thisUart->TxBuf, length);
if (!CDCBusy)
{
thisUart->TxPtrOUT = thisUart->TxPtrIN;
thisUart->TxBufHasData = false; // sending now
}
}
}
}
#endif
}
}
}
}
// add to print buffer;
int putsPort(int port, char * ptr2) // port 1 is uart1
{
const char*ptr = ptr2;
const int DefaultPort = 0; // USB port
int length = 0;
{
if (port >= Streams)
port = DefaultPort;
// check String has data
// does the first element have a char ?
if(ptr[length]) {
checkTxDMA(port); // check this port's queue length and shift the queue around if needed
SPorts *uart = &U[port];
uint32_t PtrIN = uart->TxPtrIN;
uint32_t ascii = 0;// = ptr[length];
do {
// add this text
// transfer to DMA buffer to null terminator 0x00
// PTRin should not get to the end of the buffer
ascii = ptr[length++];
uart->TxBuf[PtrIN++] = ascii;
if (PtrIN >= UTxSize)
PtrIN = 0;
} while (ascii) ;
uart->TxBuf[PtrIN] = 0;
uart->TxBufHasData = true;
uart->TxPtrIN = PtrIN;
}
}
return length;
}
void checkTxDMA(int port) // check DMA queue length
{
SPorts *thisUart = &U[port];
thisUart->TxAlmostFull = false;
if(thisUart->TxPtrIN >= ((UTxSize * 3) / 4)) // we are about to run out of room
thisUart->TxAlmostFull = true;
if (thisUart->TxAlmostFull) {
int currentBufferLength, newBufferLength;
currentBufferLength = thisUart->TxPtrIN - thisUart->TxPtrOUT;
newBufferLength = 0;
if (currentBufferLength) {
//CleanDCache_UartTxBuf(port);
if (currentBufferLength < UTxSize - 2) {
while (thisUart->TxPtrIN > thisUart->TxPtrOUT)
thisUart->TxBuf[newBufferLength++] = thisUart->TxBuf[thisUart->TxPtrOUT++];
thisUart->TxPtrOUT = 0;
thisUart->TxPtrIN = newBufferLength;
}
else {
// this is a fatal error
// reset and clean up
thisUart->TxPtrOUT = 0;
thisUart->TxPtrIN = 0;
char errorMessage[96];
int msg_length = 0;
// sprintf(errorMessage, "currentBufferLength %d is too high, some of the string has been dumped port %d, length %d\n", currentBufferLength, port, currentBufferLength);
// printTW(console, errorMessage); // seperate print error message redirected to the console
while (errorMessage[msg_length] > 0) {
// transfer to DMA buffer to null terminator 0x00
thisUart->TxBuf[thisUart->TxPtrIN++] = errorMessage[msg_length++];
if(thisUart->TxPtrIN >= UTxSize) thisUart->TxPtrIN = 0;
//SCB_CleanDCache_Region(thisUart->TxBuf, UTxSize);
}
}
}
else {
thisUart->TxPtrOUT = 0;
thisUart->TxPtrIN = 0;
}
thisUart->TxAlmostFull = false;
}
}
2021-11-08 10:51 PM
and this bit:
void initUartRxDMA(int port)
{
UART_HandleTypeDef *huart = U[port].uart;
{
int length = 0;
if (HAL_UART_Receive_DMA(U[port].uart, U[port].RxBuf, U[port].RxBufSize) != HAL_OK)
{
length += sprintf(string + length, "init Uart %d Rx DMA Buffer Failed\n", port + 1);
HAL_StatusTypeDef reInitDMABuffers = HAL_UART_DMAStop(U[port].uart);
HAL_Delay(5);
if (HAL_UART_Receive_DMA(U[port].uart, U[port].RxBuf, U[port].RxBufSize) != HAL_OK)
length += sprintf(string + length, "init Uart %d Rx DMA Buffer Failed Twice\n", port + 1);
else
length += sprintf(string + length, "init Uart %d Rx DMA Buffer OK!\n", port + 1);
}
else
length += sprintf(string + length, "init Uart %d Rx DMA Buffer OK!\n", port + 1);
}
HAL_Delay(10);
//printTW(console, string);
}
2021-11-09 12:31 AM
Dear @volkan
In order to be able to complete the DMA transfer, from UART point of view, UART IRQ should be enabled, and IRQ Handler in your it.c file should be calling HAL_UART_IRQHandler().
Here is what happens :
In code example mentionned by @TDK above, you will observe :
/* NVIC for USART, to catch the TX complete */
HAL_NVIC_SetPriority(USARTx_IRQn, 0, 1);
HAL_NVIC_EnableIRQ(USARTx_IRQn);
void USARTx_IRQHandler(void)
{
HAL_UART_IRQHandler(&UartHandle);
}
Hope this helps.
2021-11-11 06:16 AM
Dear @Guenael Cadier
Thanks for your reply. I will test it asap.
2021-11-11 06:17 AM
Dear Tsuna,
Thank you for your reply. I will test it asap.
2021-11-11 06:18 AM
Dear Tj,
Thank you for your reply. I will test it asap.