How to Receive Variable-Length Data Over RS485 Using HAL_UART_Receive_IT on STM32F4?

Hello everyone, So I have this custom STM32F4 MCU board and need to receive various data sequences from a Master board over an RS485 network. The lengths of the data sequences are variable, such as 10 bytes, 25 bytes, etc. I need to handle each sequence and respond accordingly. How can I effectively use the HAL functions HAL_UART_Receive_IT and HAL_UART_RxCpltCallback to properly receive variable-length data? I noticed that the third argument of HAL_UART_Receive_IT requires a fixed number of bytes, like this:
HAL_UART_Receive_IT(&huart1, &uart1_rx_byte, no_of_bytes);
HAL_UART_Receive_IT(&huart1, &uart1_rx_byte, no_of_bytes);
Can you suggest a good implementation for this? The frame format for receiving data is as follows:
| Start byte | Slave Addr | Byte count | Func. ID | Data1 | Data2 | ... | Data n | Checksum byte 1 | Checksum byte 2 |
| Start byte | Slave Addr | Byte count | Func. ID | Data1 | Data2 | ... | Data n | Checksum byte 1 | Checksum byte 2 |
Where Byte count represents the number of bytes from Func. ID to Checksum byte 2.
Solution:
define the states for your state machine ```c typedef enum { RX_STATE_WAIT_FOR_HEADER, RX_STATE_WAIT_FOR_PAYLOAD...
Jump to solution
4 Replies
Enthernet Code
Enthernet Code4mo ago
Hello @Sterling to handle variable-length data sequences, you can use a state machine approach within your HAL_UART_RxCpltCallback function. The idea is to first receive the header of the frame which includes the Start byte, Slave Addr, and Byte count. Once you have the Byte count, you can set up the UART to receive the remaining bytes, let me know if u need code examples to understand it better
Sterling
Sterling4mo ago
Very much @Enthernet Code ... A few code samples would help 🙏
32bitSaviour
32bitSaviour4mo ago
Hey @Sterling something like the should sort you out.
enum recv_state {
IDLE, START, SLAVEADDR, BYTE_COUNT, DATA
};

#define START_BYTE 0

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
switch (current_state) {
case IDLE:
case START:
if (uart1_rx_byte == START_BYTE) {
rx_index = 0;
rx_buffer[rx_index++] = uart1_rx_byte[0];
current_state = SLAVEADDR;
}
HAL_UART_Receive_IT(&huart1, uart1_rx_byte, 1);
break;

case SLAVEADDR:
rx_buffer[rx_index++] = uart1_rx_byte[0];
current_state = BYTE_COUNT;
HAL_UART_Receive_IT(&huart1, uart1_rx_byte, 1);
break;

case BYTE_COUNT:
rx_buffer[rx_index++] = uart1_rx_byte[0];
expected_byte_count = uart1_rx_byte[0];
current_state = DATA;
HAL_UART_Receive_IT(&huart1, uart1_rx_byte, expected_byte_count);
return;

case DATA:
for (int i = 0; i < expected_byte_count){
rx_buffer[rx_index++] = uart1_rx_byte[i];
}

if (rx_index >= expected_byte_count + 3) {
current_state = IDLE;
}
HAL_UART_Receive_IT(&huart1, uart1_rx_byte, 1);
break;

default:
current_state = IDLE;
break;
}
}
enum recv_state {
IDLE, START, SLAVEADDR, BYTE_COUNT, DATA
};

#define START_BYTE 0

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
switch (current_state) {
case IDLE:
case START:
if (uart1_rx_byte == START_BYTE) {
rx_index = 0;
rx_buffer[rx_index++] = uart1_rx_byte[0];
current_state = SLAVEADDR;
}
HAL_UART_Receive_IT(&huart1, uart1_rx_byte, 1);
break;

case SLAVEADDR:
rx_buffer[rx_index++] = uart1_rx_byte[0];
current_state = BYTE_COUNT;
HAL_UART_Receive_IT(&huart1, uart1_rx_byte, 1);
break;

case BYTE_COUNT:
rx_buffer[rx_index++] = uart1_rx_byte[0];
expected_byte_count = uart1_rx_byte[0];
current_state = DATA;
HAL_UART_Receive_IT(&huart1, uart1_rx_byte, expected_byte_count);
return;

case DATA:
for (int i = 0; i < expected_byte_count){
rx_buffer[rx_index++] = uart1_rx_byte[i];
}

if (rx_index >= expected_byte_count + 3) {
current_state = IDLE;
}
HAL_UART_Receive_IT(&huart1, uart1_rx_byte, 1);
break;

default:
current_state = IDLE;
break;
}
}
Solution
Enthernet Code
Enthernet Code4mo ago
define the states for your state machine
typedef enum {
RX_STATE_WAIT_FOR_HEADER,
RX_STATE_WAIT_FOR_PAYLOAD
} RxState;

RxState rx_state = RX_STATE_WAIT_FOR_HEADER;
typedef enum {
RX_STATE_WAIT_FOR_HEADER,
RX_STATE_WAIT_FOR_PAYLOAD
} RxState;

RxState rx_state = RX_STATE_WAIT_FOR_HEADER;
define buffers and variables to store the received data
#define HEADER_SIZE

uint8_t uart_rx_header[HEADER_SIZE];
uint8_t uart_rx_payload[256];
uint8_t uart_rx_byte;
uint8_t byte_count = 0;
#define HEADER_SIZE

uint8_t uart_rx_header[HEADER_SIZE];
uint8_t uart_rx_payload[256];
uint8_t uart_rx_byte;
uint8_t byte_count = 0;
Initialize UART reception in the main function
HAL_UART_Receive_IT(&huart1, &uart_rx_byte, 1);
HAL_UART_Receive_IT(&huart1, &uart_rx_byte, 1);
Implement the HAL_UART_RxCpltCallback to handle the reception
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
static uint8_t header_index = 0;
static uint8_t payload_index = 0;

if (huart->Instance == USART1) {
switch (rx_state) {
case RX_STATE_WAIT_FOR_HEADER:
uart_rx_header[header_index++] = uart_rx_byte;

if (header_index == HEADER_SIZE) {
byte_count = uart_rx_header[2];
rx_state = RX_STATE_WAIT_FOR_PAYLOAD;
header_index = 0;
payload_index = 0;

HAL_UART_Receive_IT(huart, uart_rx_payload, byte_count + 2);
} else {
HAL_UART_Receive_IT(huart, &uart_rx_byte, 1);
}
break;

case RX_STATE_WAIT_FOR_PAYLOAD;

rx_state = RX_STATE_WAIT_FOR_HEADER;
HAL_UART_Receive_IT(huart, &uart_rx_byte, 1);
break;
}
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
static uint8_t header_index = 0;
static uint8_t payload_index = 0;

if (huart->Instance == USART1) {
switch (rx_state) {
case RX_STATE_WAIT_FOR_HEADER:
uart_rx_header[header_index++] = uart_rx_byte;

if (header_index == HEADER_SIZE) {
byte_count = uart_rx_header[2];
rx_state = RX_STATE_WAIT_FOR_PAYLOAD;
header_index = 0;
payload_index = 0;

HAL_UART_Receive_IT(huart, uart_rx_payload, byte_count + 2);
} else {
HAL_UART_Receive_IT(huart, &uart_rx_byte, 1);
}
break;

case RX_STATE_WAIT_FOR_PAYLOAD;

rx_state = RX_STATE_WAIT_FOR_HEADER;
HAL_UART_Receive_IT(huart, &uart_rx_byte, 1);
break;
}
}
}
Want results from more Discord servers?
Add your server