Communication Interfaces
Communication interfaces enable data transfer between devices to expand hardware systems. Communication protocols define rules for data exchange between devices.
Common communication interfaces include:
- USART: TX (transmit) and RX (receive) pins
- I2C: SCL (serial clock) and SDA (serial data)
- SPI: SCLK (serial clock), MOSI (master output slave input), MISO (master input slave output), and CS (chip select)
- CAN: CAN_H and CAN_L differential signal pins
- USB: DP (data positive) and DM (data minus) differential pins
Full-duplex communication allows simultaneous bidirectional data transfer using separate transmit and receive lines. I2C and SPI are synchronous protocols with dedicated clock lines, while USART, I2C, and SPI use single-ended signaling relative to GND. CAN and USB use differential signaling for improved noise immunity.
Serial Communication
Serial interfaces are widely used due to their low cost, simplicity, and ease of implementation. They enable communication between microcontrollers, computers, and various peripheral modules.
Common serial communication components include USB-to-serial converters, gyroscope sensor modules, and Bluetooth serial modules.
Hardware Configuration
Basic bidirectional serial communication requires cross-connecting TX and RX pins between devices. Unidirectional communication can use a single wire. Level-shifting chips are necessary when devices operate at different voltage levels.
Voltage Standards
Voltage standards define how logical 1 and 0 are represented:
- TTL: +3.3V/+5V for 1, 0V for 0
- RS232: -3V to -15V for 1, +3V to +15V for 0
- RS485: +2V to +6V differential for 1, -2V to -6V for 0
Serial Parameters and Timing
Key serial communication parameters:
- Baud Rate: Communication speed in bits per second
- Start Bit: Low pulse indicating frame start
- Data Bits: Payload data with LSB first transmission
- Parity Bit: Optional error detection
- Stop Bit: High pulse marking frame end
Each byte is encapsulated in a data frame consisting of start bit, data bits, optional parity bit, and stop bit(s).
USART Overview
USART (Universal Synchronous/Asynchronous Receiver/Transmitter) is STM32's integrated hardware peripheral for serial communication. It automatically generates data frame timing from byte data and can reassemble received data frames into bytes.
Key features include:
- Baud rates up to 4.5 Mbps
- Configurable data length (8/9 bits)
- Selectable stop bits (0.5/1/1.5/2)
- Optional parity (none/even/odd)
- Support for synchronous mode, hardware flow control, DMA, and various protocols
STM32F103C8T6 provides USART1 (APB2), USART2 (APB1), and USART3 (APB1).
USART Architecture
The USART module includes transmit/receive data registers (TDR/RDR) sharing the same address. TDR is write-only while RDR is read-only. Data moves from TDR to transmit shift register for serial output, with TXE flag indicating when TDR is ready for new data. The receive path operates similarly with RXNE flag indicating available received data.
Hardware flow control uses nRTS (request to send) and nCTS (clear to send) pins to prevent data overrun. The module also provides clock output for synchronous operation and supports multiple interrupt sources.
Data Frame Structure
Data frames consist of start bit, data bits (including optional parity), and stop bits. The word length parameter determines total bits per frame including parity.
Input Data Sampling
Receiver circuitry detects start bits by monitoring for high-to-low transitions. It samples data at 16x the baud rate, taking three samples near the bit center for reliability. The baud rate generator divides the peripheral clock to achieve desired communication speed.
Data Transmission Modes
Data can be transmitted in HEX (raw binary) or text (character-encoded) format. Data packets can have fixed or variable length with header/trailer bytes.
Serial Transmission Implementation
Hardware Setup
Connect TX and RX pins crosswise between devices and ensure proper driver installation.
Initialization Code
void Serial_Initialize(void)
{
// Enable clocks
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// Configure GPIO
GPIO_InitTypeDef gpio_config;
gpio_config.GPIO_Mode = GPIO_Mode_AF_PP;
gpio_config.GPIO_Pin = GPIO_Pin_9;
gpio_config.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_config);
// USART configuration
USART_InitTypeDef usart_config;
usart_config.USART_BaudRate = 9600;
usart_config.USART_WordLength = USART_WordLength_8b;
usart_config.USART_StopBits = USART_StopBits_1;
usart_config.USART_Parity = USART_Parity_No;
usart_config.USART_Mode = USART_Mode_Tx;
usart_config.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART1, &usart_config);
USART_Cmd(USART1, ENABLE);
}
void Serial_TransmitByte(uint8_t data_byte)
{
USART_SendData(USART1, data_byte);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
void Serial_TransmitArray(uint8_t* data_array, uint16_t length)
{
for(uint16_t index = 0; index < length; index++) {
Serial_TransmitByte(data_array[index]);
}
}
void Serial_TransmitString(const char* text_string)
{
while(*text_string) {
Serial_TransmitByte(*text_string++);
}
}
Main Application
int main(void)
{
Serial_Initialize();
Serial_TransmitByte('X');
uint8_t test_data[] = {0x41, 0x42, 0x43};
Serial_TransmitArray(test_data, 3);
Serial_TransmitString("Test Message\r\n");
while(1) {
// Main loop
}
}
Serial Reception Implementation
Polling Method
int main(void)
{
Serial_Initialize();
while(1) {
if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET) {
uint8_t received = USART_ReceiveData(USART1);
// Process received data
}
}
}
Interrupt Method
volatile uint8_t rx_buffer;
volatile uint8_t data_ready;
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET) {
rx_buffer = USART_ReceiveData(USART1);
data_ready = 1;
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
int main(void)
{
Serial_Initialize();
while(1) {
if(data_ready) {
data_ready = 0;
// Process rx_buffer
}
}
}
HEX Data Packet Communication
uint8_t tx_packet[4] = {0x01, 0x02, 0x03, 0x04};
uint8_t rx_packet[4];
volatile uint8_t packet_received;
void Serial_SendPacket(void)
{
Serial_TransmitByte(0xFF); // Header
Serial_TransmitArray(tx_packet, 4);
Serial_TransmitByte(0xFE); // Trailer
}
void USART1_IRQHandler(void)
{
static uint8_t state = 0;
static uint8_t index = 0;
uint8_t incoming = USART_ReceiveData(USART1);
switch(state) {
case 0: // Wait for header
if(incoming == 0xFF) {
state = 1;
index = 0;
}
break;
case 1: // Receive data
rx_packet[index++] = incoming;
if(index >= 4) {
state = 2;
index = 0;
}
break;
case 2: // Wait for trailer
if(incoming == 0xFE) {
state = 0;
packet_received = 1;
}
break;
}
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
Text Data Packet Communication
char text_buffer[100];
volatile uint8_t text_ready;
void USART1_IRQHandler(void)
{
static uint8_t rx_state = 0;
static uint8_t buf_index = 0;
uint8_t incoming = USART_ReceiveData(USART1);
switch(rx_state) {
case 0: // Wait for start marker
if(incoming == '@' && !text_ready) {
rx_state = 1;
buf_index = 0;
}
break;
case 1: // Receive text
if(incoming == '\r') {
rx_state = 2;
} else {
text_buffer[buf_index++] = incoming;
}
break;
case 2: // Wait for end marker
if(incoming == '\n') {
rx_state = 0;
text_buffer[buf_index] = '\0';
text_ready = 1;
}
break;
}
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}