How receive data with HAL_UART?

Elias Elnatã picture Elias Elnatã · May 30, 2019 · Viewed 9.2k times · Source

I'm learning about the STM32. I'm want receive data by UART byte-to-byte with interruption.

HAL_UART_Receive_IT(&huart1, buffer, length)

Where &huart1 is my uart gate, buffer is the input storage and length is the amount of input bytes. I use the following function to read data

static requestRead(void *buffer, uint16_t length)
{
    uint8_t teste;
    while (HAL_UART_Receive_IT(&huart1, buffer, length) != HAL_OK) osDelay(1);
    //HAL_UART_RxCpltCallback
}

I store my data in:

void StartDefaultTask(void const *argument)
{
    char sender[] = "Alaska Sending\n";
    uint8_t receive[10];
    uint8_t data[30];

    for (;;)
    {
        uint8_t i = 0;
        memset(data, 0, 30);

        requestRead(&receive, 1);
        data[i++] = receive;

        while (data != '\r')
        {
            requestRead(&receive, 1);
            data[i++] = receive;
        }

        //HAL_UART_Transmit(&huart1, data, i, HAL_MAX_DELAY);

    }
    /* USER CODE END StartDefaultTask */
}

My problem is the value receive and store. When I send by serial a string of character as Welcome to Alaska\n, only W is read and stored, then I need send again the buffer and again just store only W. How solve this?

Answer

followed Monica to Codidact picture followed Monica to Codidact · May 31, 2019

Well, there are a few issues here.

Arrays and their contents

data[i++] = receive;

stores the address of the receive buffer, a memory pointer value, into the data array. That's certainly not what you want. As this is a very basic C programming paradigm, I'd recommend reviewing the chapter on arrays and pointers in a good C textbook.

What you send and what you expect

while (data != '\r')

Even if you'd get the array address and its value right (see above), you are sending a string terminated with '\n', and check for a '\r' character, so change one or the other to get a match.

Missing volatile

uint8_t receive[10];

The receive buffer should be declared volatile, as it would be accessed by an interrupt handler. Otherwise the main program would miss writes to the buffer even if it had checked whether the receiving is complete (see below).

Working with hardware in realtime

while (HAL_UART_Receive_IT(&huart1, buffer, length) != HAL_OK) osDelay(1);

This would enable the UART receive (and error handling) interrupt to receive one byte. That's fine so far, but the function returns before receiving the byte, and as it's called again immediately, it would return HAL_BUSY the second time, and wait a millisecond before attempting it again. In that millisecond, it would miss most of the rest of the transmission, as bytes are arriving faster than that, and your program does nothing about it.

Moreover, the main program does not check when the receive is complete, possibly accessing the buffer before the interrupt handler places a value in it.

If you receive data using interrupts, you'd have to do something about that data in the interrupt handler. (If you don't use interrupts, but polling for data, then be sure that you'd meet the deadline imposed by the speed of the interface).

HAL is not suited for this kind of tasks

HAL has no interface for receiving an unknown length of data terminated by a specific value. Of course the main program can poll the receiver one byte at a time, but then it must ensure that the polling occurs faster than the data comes in. In other words, the program can do very little else while expecting a transmission.

There are some workarounds, but I won't even hint at them now, because they would lead to deadlocks in an RTOS environment which tend to occur at the most inconvenient times, and are quite hard to investigate and to properly avoid.

Write your interrupt handler instead

(all of this is detailed in the Reference Manual for your controller)

  • set the UART interrupt priority NVIC_SetPriority()
  • enable the interrupt with NVIC_EnableIRQ()
  • set the USART_CR1_RXNEIE bit to 1

in the interrupt handler,

  • read the SR register
  • if the RXNE bit is set,
    • read the data from the data register
    • store it in the buffer
    • set a global flag if it matches the terminating character
    • switch to another buffer if more data is expected while the program processes the first line.

Don't forget declaring declaring all variables touched by the interrupt handler as volatile.