UART/RS-232 Communication Module
Overview
This document describes the UART (serial) communication subsystem used for RS-232 style communication with the motor inverter. The implementation uses DMA with circular buffering and Idle Line detection for efficient, non-blocking data reception.
File Locations
- Headers:
Core/Inc/App/Communication/uart_comm.h - Source:
Core/Src/App/Communication/uart_comm.c
Physical Layer Configuration
- Interface: UART4 (STM32L562xx)
- Baud Rate: 115200 baud (configurable)
- Data Format: 8 bits, no parity, 1 stop bit (8N1)
- Transmission Delay: 25ms (UART_DELAY constant)
Reception Mechanism: DMA with Circular Buffering
Circular Buffer Setup
- Buffer Size: 2048 bytes (
UART_BUFFER_SIZE) - Architecture: Circular ring buffer managed by DMA
- Memory Location:
inverter_data.Buffer.rx_buffer
The circular buffer allows continuous data reception without frame boundaries or buffer overflow concerns:
typedef struct {
uint16_t old_pos; // Previous processing position
uint16_t current_pos; // Current DMA position (updated by Idle Line interrupt)
uint8_t new_uart_data_flag; // Flag set when new data is available
char rx_buffer[UART_BUFFER_SIZE];
} Buffer_t;Circular Buffer State Diagram
Circular Buffer Memory Layout
Buffer (2048 bytes)
┌─────────────────────────────────────────────────────────┐
│ 0x00 ... old_pos ... current_pos ... 0x800 │
│ ↓ ↓ │
│ [PROCESSED] [NEW DATA] [EMPTY] │
│ ↑ Next DMA write│
└─────────────────────────────────────────────────────────┘
↓ Wraps around
┌──────────────────┘
│
└→ old_pos = 0x000
current_pos wraps to buffer startDiagram showing wraparound handling:
Idle Line Detection
The UART peripheral monitors the receive line and triggers an interrupt when the line remains idle (no transitions) for a character period. This mechanism eliminates the need for explicit frame delimiters:
Trigger Events:
HAL_UART_RXEVENT_IDLE- Line idle for one character duration (11 bits @ 115200 baud ≈ 95 µs)HAL_UART_RXEVENT_TC- DMA Transfer Complete (buffer full)
Idle Line Detection Timing:
Idle Line State Diagram:
Callback Implementation:
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size){
if (huart->Instance == UART4) {
if (huart->RxEventType == HAL_UART_RXEVENT_TC ||
huart->RxEventType == HAL_UART_RXEVENT_IDLE) {
// Update current position in circular buffer
inverter_data.Buffer.current_pos = Size % UART_BUFFER_SIZE;
// Signal that new data is available for processing
inverter_data.Buffer.new_uart_data_flag = 1;
}
}
}Advantages of Idle Line Detection:
- No need for start/stop markers
- Automatically detects complete messages
- Tolerant to variable message lengths
- Low CPU overhead (interrupt-driven)
- Handles network traffic bursts efficiently
Message Reception Window Processing
Data is processed in "windows" defined by the buffer positions:
void inverter_uart_poll_rx(uint8_t *rx_buffer) {
if (inverter_data.Buffer.new_uart_data_flag == 0) {
return; // No new data
}
// Process data from old_pos to current_pos
process_uart_rx_window(rx_buffer,
inverter_data.Buffer.old_pos,
inverter_data.Buffer.current_pos);
// Update position for next window
inverter_data.Buffer.old_pos = inverter_data.Buffer.current_pos;
inverter_data.Buffer.new_uart_data_flag = 0;
}Window Extraction:
- Handles circular buffer wraparound correctly
- Extracts data between old and current positions
- Implements overflow protection (discards oldest data if destination buffer is too small)
- Null-terminates extracted data for string parsing
Message Protocol
Inverter Data Format (Received)
The inverter sends telemetry data as key=value pairs, one per line, delimited by CR/LF:
S=2.45
a=0.12
PWM=45
U=48.2
I=15.3
RPM=2450
con=42
mot=35Example Message Extraction Sequence:
Supported Fields:
S,T,t- Throttle/Speed input (Volts)a,A- Auxiliary/Brake input (Volts)PWM- Pulse Width Modulation value (%)U- Input voltage (Volts)I- Phase current (Amperes)RPM- Rotational speed (revolutions per minute)con- Motor controller temperature (°C)mot- Motor temperature (°C)
Parsing
Messages are parsed line-by-line using standard C string functions:
- Lines are extracted between CR/LF delimiters
- Each line containing
=is parsed as a key=value pair - Values are extracted using
sscanf()andstrstr() - Unknown fields are silently ignored
// Parse throttle (can be S, T, or t depending on inverter mode)
if ((ptr = strstr(temp_msg, "S=")) != NULL)
sscanf(ptr, "S=%f", &inverter_data.RS232_Message.S);
else if ((ptr = strstr(temp_msg, "T=")) != NULL)
sscanf(ptr, "T=%f", &inverter_data.RS232_Message.S);Command Transmission
Commands are sent to the inverter in two modes:
Normal Mode (Speed Control)
Sends speed control commands using a compact encoding:
void inverter_uart_send_command(Inverter_Data_t *inverter_data,
uint8_t programming_mode,
uint8_t force_send)Speed Encoding Examples:
Speed Command Reference Table:
| Speed (%) | Command | Description |
|---|---|---|
| 0% | 0 | No movement |
| 1-9% | s1- to s1--------- | 1% to 9% (1 dash per %) |
| 10% | s10 | Base 10% |
| 11-19% | s10+ to s10++++++++++ | 10% + adjustment |
| 20-99% | s0X + adj | Tens digit + dashes/plus |
| 100% | m | Full power |
Example Transmissions:
// Example 1: 45% speed (s04++)
inverter_data.left_percentage = 45;
inverter_uart_send_command(&inverter_data, 0, 0);
// Sends: "s04++" (4 from tens, ++ adds 5)
// Example 2: 8% speed (s1--------)
inverter_data.right_percentage = 8;
inverter_uart_send_command(&inverter_data, 0, 0);
// Sends: "s1--------" (8 dashes = 8%)
// Example 3: 100% speed (m)
inverter_data.left_percentage = 100;
inverter_uart_send_command(&inverter_data, 0, 0);
// Sends: "m"Transmission Timing:
Programming Mode (Pass-through)
In programming mode, raw data from CAN is forwarded directly to the inverter. Up to 14 bytes can be sent per transmission.
RS-232 Switch Command
void inverter_uart_switch_to_rs232_mode(void) {
HAL_UART_Transmit(&huart4, (uint8_t*)"s", 1, UART_DELAY);
}Sends a simple s character to switch inverter to RS-232 communication mode.
Non-Programming Command Period
- Period: 3ms (
UART_NON_PROG_TX_PERIOD_MS) - Controls update rate of speed commands in normal operation
- Only sends if speed percentage has changed
Data Structure
typedef struct {
struct {
float S; // Throttle input (Volts)
float a; // Aux/Brake input (Volts)
uint16_t PWM; // PWM percentage
float U; // Input voltage (Volts)
float I; // Phase current (Amperes)
uint16_t RPM; // Rotational speed
uint8_t con; // Controller temperature (°C)
int8_t mot; // Motor temperature (°C)
} RS232_Message;
struct {
uint16_t old_pos;
uint16_t current_pos;
uint8_t new_uart_data_flag;
uint8_t previous_percentage;
char rx_buffer[UART_BUFFER_SIZE];
} Buffer;
char raw_data_tx[15];
uint8_t left_percentage, right_percentage;
Programming_Message_t prog_message;
Inverter_Message_t inv_message;
} Inverter_Data_t;Error Handling
Error Recovery Flow:
Error Handling Details:
- Null Buffer Detection: Detects and flags null pointer access in circular buffer operations
- Buffer Overflow Protection: Older data is discarded if incoming message exceeds destination buffer size
- Malformed Messages: Lines without
=are silently ignored - Parsing Failures: Missing or invalid fields retain previous values
Integration Notes
- Polling function
inverter_uart_poll_rx()should be called periodically from the main task loop - Idle Line interrupt must be enabled in the HAL UART configuration
- DMA must be configured for continuous circular mode
- Buffer is shared with programming module for pass-through functionality
- UART4 handle (
huart4) must be properly initialized by STM32CubeMX
Key Takeaways
- Non-blocking reception through DMA circular buffering
- Automatic frame detection using Idle Line interrupt (~95 µs threshold)
- Simple protocol with key=value pairs and standard line delimiters
- Compact speed encoding minimizes message overhead
- Resilient parsing that handles missing or invalid fields gracefully
- Pass-through capable for remote diagnostics and over-the-air programming
