Skip to main content
Explorer
April 4, 2024
Question

STM32 Printf UART does not wait for transmission to complete

  • April 4, 2024
  • 2 replies
  • 2181 views

Hi, I am trying to implement printf with UART using DMA. The problem is when i use consecutive printf's in my code, transmission of the first data is not being waited. I implemented a control block to check if the transmission is completed but it does not work. At first, i wrote this control block by changing dmi.sending flag. It did not work, so i decided to get the state directly from huart. It did not work either. When i debug the code and add breakpoint to 

HAL_UART_Transmit_DMA(dmi.huart, (uint8_t*) ptr, len);

then the execution is successfull. I can see "123456\ntest\n" in the terminal. But when i stop debugging and run the code, i get this in the terminal:

1est
6
test

test

test

 

Why i can't control if the data is transmitted yet or not?

 

Here is my code

uart_write.c:

 

 

 

#include <ring_buffer.h>
#include "main.h"
#include <stdbool.h>
#include <uart_write.h>

struct dma_printf_info dmi;

void dma_printf_init(UART_HandleTypeDef *printf_huart) {
	dmi.huart = printf_huart;
	ring_init(&dmi.tx_ring);
	dmi.sending = false;
}

void dma_printf_putc(char *ptr, int len) {
	if (dmi.sending == false && ring_filled_space(&dmi.tx_ring) == 0) {
		dmi.sending = true;
		HAL_UART_Transmit_DMA(dmi.huart, (uint8_t*) ptr, len);
	} else {
		if (ring_empty_space(&dmi.tx_ring) >= len) {
			for (int i = 0; i < len; i++)
				ring_push(&dmi.tx_ring, ptr[i]);
		} else { //BUF OVFL
			while (1)
				;
		}
	}
}

void dma_printf_send_it(UART_HandleTypeDef *printf_huart) {
	if (dmi.huart != printf_huart) return;
	if (ring_filled_space(&dmi.tx_ring) > 0) {
		HAL_UART_Transmit_DMA(dmi.huart, (uint8_t*) dmi.tx_ring.buf, ring_filled_space(&dmi.tx_ring));
		ring_empty_out(&dmi.tx_ring);
	} else dmi.sending = false;
}

 

 

 

 

uart_write.h

 

 

 

#ifndef HAL_DMA_PRINTF_DMA_PRINTF_H
#define HAL_DMA_PRINTF_DMA_PRINTF_H
#include <ring_buffer.h>
#include "main.h"

struct dma_printf_info {
 struct dma_ring_buf tx_ring;
 volatile int sending;
 UART_HandleTypeDef *huart;
};

void dma_printf_init(UART_HandleTypeDef *printf_huart);
void dma_printf_putc(char* ptr, int len);
void dma_printf_send_it(UART_HandleTypeDef *printf_huart);
#endif //HAL_DMA_PRINTF_DMA_PRINTF_H

 

 

 

 

main.c

 

 

 

/* USER CODE BEGIN 0 */
int _write(int file, char *ptr, int len) {
	dma_printf_putc(ptr, len);
	return len;
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
	dma_printf_send_it(huart);
}
/* USER CODE END 0 */

int main(void) {
	/* USER CODE BEGIN 1 */

	/* 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_UART4_Init();
	/* USER CODE BEGIN 2 */
	dma_printf_init(&huart4);
	dma_scanf_init(&huart4);
	printf("123456\n");
	/* USER CODE END 2 */

	/* Infinite loop */
	/* USER CODE BEGIN WHILE */
	while (1) {
		/* USER CODE END WHILE */

		/* USER CODE BEGIN 3 */
		printf("test\n");
		HAL_Delay(1000);
	}
	/* USER CODE END 3 */
}

 

 

 

 

    This topic has been closed for replies.

    2 replies

    Graduate II
    April 4, 2024

    Well you can't guarantee the scope of the buffer during the transfer.

    You probably want to always put data in the ring buffer. And then serve the data from the ring, managing the chaining in the call back, and handling the wrapping cases.

    ÇSerp.1Author
    Explorer
    April 4, 2024

     

    Could you please elaborate a bit more? What does "can't guarantee the scope of the buffer during the transfer" mean and why it's not guaranteed in this example? If i put the printf data always to ring buffer, how can i understand when to print?

    ÇSerp.1Author
    Explorer
    April 4, 2024

    UPDATE

     

    I am toggling two LEDs hooked up to my oscilloscope to see if the data is sent to dma immideately or buffered before sending.

     

    uart_write.c

    #include <ring_buffer.h>
    #include "main.h"
    #include <stdbool.h>
    #include <uart_write.h>
    
    struct dma_printf_info dmi;
    
    void dma_printf_init(UART_HandleTypeDef *printf_huart) {
    	dmi.huart = printf_huart;
    	ring_init(&dmi.tx_ring);
    	dmi.sending = false;
    }
    
    void dma_printf_putc(char *ptr, int len) {
    	if (dmi.sending == false && ring_filled_space(&dmi.tx_ring) == 0) {
    		dmi.sending = true;
    		HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
    		HAL_UART_Transmit_DMA(dmi.huart, (uint8_t*) ptr, len);
    	} else if (dmi.sending == true) {
    		if (ring_empty_space(&dmi.tx_ring) >= len) {
    			HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);
    			for (int i = 0; i < len; i++)
    				ring_push(&dmi.tx_ring, ptr[i]);
    		} else { //BUF OVFL
    			while (1)
    				;
    		}
    	}
    }
    
    void dma_printf_send_it(UART_HandleTypeDef *printf_huart) {
    	if (dmi.huart != printf_huart) return;
    
    	if (ring_filled_space(&dmi.tx_ring) > 0) {
    		HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
    		HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);
    		HAL_UART_Transmit_DMA(dmi.huart, (uint8_t*) dmi.tx_ring.buf, ring_filled_space(&dmi.tx_ring));
    		ring_empty_out(&dmi.tx_ring);
    	} else {
    		dmi.sending = false;
    		HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
    	}
    }

     

     

    main.c

     /* USER CODE BEGIN WHILE */
    	while (1) {
     /* USER CODE END WHILE */
    
     /* USER CODE BEGIN 3 */
    		printf("123456\n");
    		printf("test\n");
    		HAL_Delay(1000);
    	}
     /* USER CODE END 3 */

     

    123456 should be sent immediately since the buffer is empty at the beginning. If execution of printf("test\n") is before the transmission of "123456\n", then test should be buffered and i should be able to see this on pin LED2. If the execution of printf("test\n") is after the transmission of "123456\n", it should be printed out immediately as well. So there are two options:

    1- I need to see 2 pulses in LED1 indicating data is sent immediately two times.

    2- I need to see LED1 rising and shortly after LED2 rising and LED1/2 falling at the same time when all the data is sent.

     

    When i check the waveform on the oscilloscope, I get only one pulse at LED1. which is absurd.