Serial DMA transmission based on STM32

Problem Description

Use the STM32 serial port for DMA transmission (Noraml mode), and call the send function log_printf () twice in a certain task, but the data sent back is not as expected on the serial debugging assistant. Part of the data sent for the first time is covered by the data sent for the second time, as shown in the figure:

Serial DMA transmission based on STM32

The task code is as follows:

/ * Log_Task funcTIon * / void Log_Task (void const * argument)

{/ * USER CODE BEGIN Log_Task * /

/ * Infinite loop * /

for (;;)

{if (router_rx_flag == 1)

{

router_rx_flag = 0;

log_printf ("Get ok");

log_printf ("% s", router_rx_buffer);

}

osDelay (100);

} / * USER CODE END Log_Task * /} 1234567891011121314151617

As you can see from the code, the expected result should look like this:

Serial DMA transmission based on STM32

The log_printf function code is as follows:

/ *

* Name: log_printf

* Function: Print out the log content on serial port 1

* Input: format the output string

* Output: None

* /

void log_printf (const char * format, ...)

{

va_list arg;

staTIc char tx_buffer [256] = {""};

// put the data into the buffer after processing

va_start (arg, format);

vsprintf ((char *) tx_buffer, format, arg);

va_end (arg);

// Start sending data

send_to_router ((u8 *) tx_buffer, strlen (tx_buffer));

}

The send_to_router function code is as follows:

void send_to_router (unsigned char * buffer, unsigned int length)

{

// Wait for the last data to be sent

while (HAL_DMA_GetState (& hdma_usart1_tx) == HAL_DMA_STATE_BUSY) osDelay (1);

/ * Turn off DMA * /

__HAL_DMA_DISABLE (& hdma_usart1_tx);

// Start sending data

HAL_UART_Transmit_DMA (& huart1, buffer, length);

// while (HAL_DMA_GetState (& hdma_usart1_tx) == HAL_DMA_STATE_BUSY) osDelay (1); / * Put it here to ensure that each transmission is complete, but it will take time * /

}

The serial port interrupt receiving and processing functions are as follows:

/ *

* Name: router_parse

* Function: Receive the analysis of router data and call it in the callback function

* Input: Data length received by serial port 1 when idle interrupt

* Output: None

* /

void router_parse (uint16_t buffer_len)

{

char * p_start = NULL, * p_end = NULL;

/ * Only extract one frame of NMEA data, starting with $ and ending with * /

p_start = strchr (usart1_rx_buffer, '$');

if (p_start! = NULL)

{

p_end = strchr (p_start, '');

if (p_end! = NULL)

{

memcpy (router_rx_buffer, p_start, (p_end-p_start + 1)); / * save data * /

router_rx_flag = 1;

}

}

}

Analysis process

I used to think it was in the send_to_router function

// Wait for the last data to be sent

while (HAL_DMA_GetState (& hdma_usart1_tx) == HAL_DMA_STATE_BUSY) osDelay (1);

The problem with this sentence is that for some reason, the data in the DMA buffer is not completely transmitted, but the DMA status is released. As a result, a new round of transmission is restarted, causing the second half of the previous data to be overwritten. But no matter how debugging, this conjecture can not be confirmed, DMA peripherals have not made any exceptions.

Today, I took a closer look. "Getckey" and "Get ok" and "$ Mickey", why the second half of the content sent for the second time covers the content sent for the first time, generally should not be the first half "( Dollar sign, typographical error here) Mic "? The cause of the problem may not be related to the status bit. So I reviewed the send_to_router function: void send_to_router (unsigned char * buffer, unsigned int length) suddenly thought that the input parameter is just a pointer, the send buffer is in the log_printf function

staTIc char tx_buffer [256] = {""};

To summarize, the whole process of sending process is as follows:

log_printf ("Get ok"); when "Get ok" is loaded into tx_buffer, with a send length of 8 bytes.

In the send_to_router function, HAL_UART_Transmit_DMA (& huart1, buffer, length); enables the transmission of this 8 bytes.

8 bytes may only complete the sending of "Get", log_printf ("% s", router_rx_buffer); (ie log_printf ("$ Mickey");) has started execution.

"$ Mickey" is loaded into tx_buffer, with a send length of 9 bytes.

In the send_to_router function, because the last time the data has not been sent completely, enter the DMA state waiting loop. However, the content of the address originally pointed to by the DMA send pointer char * buffer "ok" has been replaced by "ckey", so it becomes "Getckey". For display reasons, only see "Getckey".

Solution

Put while (HAL_DMA_GetState (& hdma_usart1_tx) == HAL_DMA_STATE_BUSY) osDelay (1); this sentence can be placed in the buffer area before the tx_buffer loading step:

/ *

* Name: log_printf

* Function: Print out the log content on serial port 1

* Input: format the output string

* Output: None

* /

void log_printf (const char * format, ...)

{

va_list arg;

staTIc char tx_buffer [256] = {""};

// Wait for the last data to be sent

while (HAL_DMA_GetState (& hdma_usart1_tx) == HAL_DMA_STATE_BUSY) osDelay (1);

// put the data into the buffer after processing

va_start (arg, format);

vsprintf ((char *) tx_buffer, format, arg);

va_end (arg);

// Start sending data

send_to_router ((u8 *) tx_buffer, strlen (tx_buffer));

}

As for the code in the send_to_router function, you can keep or delete it.

Afterword

The DMA serial port transmission function of STM32 has been used for a long time. The routine is basically as described in the previous blog post "Using DMA + printf + uart1 in iar". Later, I started to use STM32CubeMX, and made some modifications to the previous routine. After successful debugging, it has been used until now. During this period, this problem has troubled me for a long time. Although a little attention can be avoided when writing the code, people who are technical know that: a thousand miles of embankment, broken by the ants, letting go of any small details may cause in the future Major disaster. Thank you for being able to find the cause of the problem today.

Looking back again, it looks like "Using DMA + printf + uart1 in iar", in fact, the answer to this question was written in it very early. . .

To find a time, I will write a blog post using Normal mode of DMA serial port, or use Cube to create a project. At that time, another example will be used to completely reproduce and solve this problem.

6V AGM Battery


The RIMA 6V AGM Battery is part of our UN series sealed lead acid battery,

UN series is a high quality VRLA battery, made of 99.997% pure lead, the battery is perfect designed for standby use, but also good at cycle use. The battery is based on AGM battery technology, which means that the electrolyte is absorbed by a fiberglass separator, preventing its leakage, this way the battery can work in any positions.


General Future:

5-12 years design life(25℃)

Non-spillable construction

Sealed and maintenance-free

High reliability and stability

High purity raw material: long life and low self-discharge


Standards:

Compliance with IEC, BS, JIS and EU standards.

UL, CE Certified

ISO45001,ISO 9001 and ISO 14001 certified production facilities


Application:

Uninterruptible Power Supply (UPS)

Emergency backup power supply

Auto control system

Communication power supply

Alarm and security system

Electric Power System (EPS)




6V Battery Rechargeable,6V Battery Power Wheels,6V Battery Ride On Toys,6V Battery 4.5 Ah

OREMA POWER CO., LTD. , https://www.oremabattery.com