Search code examples
stm32uartfreertosdmanucleo

Sending data over UART from a buffer filled by DMA


I have an ADC set up with DMA to fill a buffer in circular mode. I've verified with the debugger that this buffer is being filled with the expected data.

I am using a FreeRTOS task to send this data over UART based on the interrupts provided by the DMA functionality. When the buffer is half full, I send the first half; when the buffer is full, I send the second half.

I am using a Nucleo L476RG.

Here is my code:

#include "ADC.hpp"

#include <string.h>

#include "FreeRTOS.h"
#include "semphr.h"
#include "task.h"

#include "main.h"
#include "utils/print.hpp"

#include "stm32l4xx_hal.h"

extern ADC_HandleTypeDef hadc3;
extern UART_HandleTypeDef huart2;

extern volatile SemaphoreHandle_t print_lock;

namespace ADC
{

volatile SemaphoreHandle_t adc_lock;

volatile uint16_t adc_buf[ADC_BUF_SIZE];
volatile uint8_t buf[] = "abcdefg\r\n";

void task(void *arg)
{
    adc_lock = xSemaphoreCreateBinary();

    HAL_ADC_Start_DMA(&hadc3, (uint32_t *)adc_buf, ADC_BUF_SIZE);

    HAL_StatusTypeDef result;

    uint16_t offset = ADC_BUF_SIZE >> 1;

    while (1)
    {
        xSemaphoreTake(adc_lock, HAL_MAX_DELAY);

        offset = (offset + (ADC_BUF_SIZE >> 1)) % ADC_BUF_SIZE;

        xSemaphoreTake(print_lock, HAL_MAX_DELAY);
        while ((result = HAL_UART_Transmit(&huart2, (uint8_t *)adc_buf+offset*2, ADC_BUF_SIZE, HAL_MAX_DELAY)) == HAL_BUSY) {}
        xSemaphoreGive(print_lock);
    }

    vTaskDelete(nullptr);
}

} // namespace ADC

void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
{
    HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
    xSemaphoreGiveFromISR(ADC::adc_lock, nullptr);
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
    HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
    xSemaphoreGiveFromISR(ADC::adc_lock, nullptr);
}

Here is a screenshot from the debugger showing correct data in adc_buf

enter image description here

When looking at the received data using screen /dev/ttyACM0 115200, then converting to hex using vscode, this is what is received. I have already verified that the baudrate is correct.

enter image description here

I've tried adding a buffer with a long string message and sending that over and over, and it is sent and received correctly. Only data from the DMA buffer is being sent as 0xFD rather than the correct bytes. All other data is sent and received correctly.

Here are the settings for the UART and the ADC/DMA.

enter image description here enter image description here enter image description here


Solution

  • When looking at the received data using screen /dev/ttyACM0 115200, then converting to hex using vscode, this is what is received.

    Rather than perform an extra conversion step, suggest that you use a better tool such as minicom, which has a built-in hex display mode.


    Addendum

    Switching to minicom fixed it!

    So apparently there's an issue when you perform the conversion "using vscode".

    This result is consistent with your other testing of "adding a buffer with a long string message and sending that over and over, and it is sent and received correctly."
    In hindsight, the next step in your testing could have been to perform the same conversion "using vscode" on this text data. Presumably you would not get the expected ASCII code values, and would then suspect that this conversion step was faulty.

    It's being sent from the USB-A port on the Nucleo, so maybe that's why it's ttyACM0?

    Yes. That is a USB-to-USB connection using CDC ACM, a protocol that can be used for emulating serial ports over USB. The PC is the USB host (as always), and the Nucleo board is a USB gadget implementing a virtual COM port.

    There is no (actual) UART or USART involved with such a USB connection, so you are not "send[ing] this data over UART", nor is the USART2 Mode and Configuration relevant.
    When your Nucleo program uses the HAL to access the huart2 pseudo-device, apparently this third "UART" is the virtual COM port using the USB connection. Assuming that the huart2 pseudo-device refers to the third USART device, USART2, is incorrect.