← Back to Embedded C series
🔌
STM32
STM32 · Prereq: lesson 18

19. USART Serial Communication

Now the chip talks to people·PCs in text. UART (universal async serial) is everywhere — debug messages, sensor modules, bootloaders. We set USART1 to 9600bps, send a greeting, and echo received characters back. μVision's Serial window shows the traffic directly, making UART the easiest peripheral to verify in the simulator.

USARTUARTBRRTXERXNEecho
Duration
~1.5 hours
Level
📊 Intermediate
Prerequisite
🎯 Lesson 18
OUTCOME
Set USART1 (PA9/PA10) with BRR=fCK/baud and poll TXE/RXNE to send a string and echo characters.

What you'll learn

  • 1Understand UART async communication and the 8N1 frame
  • 2Configure USART1's pins (PA9/PA10) and clock
  • 3Compute and set baud via BRR
  • 4Implement polling send/receive with TXE/RXNE flags
  • 5Verify traffic in μVision's Serial window

Introduction

UART exchanges data at an agreed baud with no clock line. The sender's TX crosses to the receiver's RX, sharing GND. The common setup is 8N1 (data8·no parity·stop1).

Key concepts

1) USART1 pins·clock and baud

SignalPinMode
TXPA9AF push-pull (0xB)
RXPA10floating input (0x4)
c
/* USART1 is APB2 (RCC_APB2ENR_USART1EN). fCK = PCLK2 = 72MHz */
BRR = fCK / baud;   /* 9600 → 72000000/9600 = 7500 */

2) Send/receive (polling)

c
while (!(USART1->SR & USART_SR_TXE)) { }   /* until TX buffer empty */
USART1->DR = c;                            /* send one char */

if (USART1->SR & USART_SR_RXNE) c = USART1->DR;   /* reading clears RXNE */

Enable TE (transmit)·RE (receive)·UE (USART enable) in CR1: `CR1 = TE | RE | UE`.

Core example

c
#include "stm32f10x.h"
static void usart1_init(void){
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN | RCC_APB2ENR_USART1EN;
    GPIOA->CRH &= ~((0xFu<<4) | (0xFu<<8));
    GPIOA->CRH |= (0xBu<<4);   /* PA9 TX = AF push-pull */
    GPIOA->CRH |= (0x4u<<8);   /* PA10 RX = floating input */
    USART1->BRR = 7500u;       /* 9600 @ 72MHz */
    USART1->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
}
static void tx(char c){ while(!(USART1->SR & USART_SR_TXE)){} USART1->DR=(uint16_t)c; }
int main(void){
    usart1_init();
    const char *s = "Hello, STM32 UART!\r\n"; while(*s) tx(*s++);
    while (1) { if (USART1->SR & USART_SR_RXNE) tx((char)USART1->DR); }  /* echo */
}

BRR check: 9600→7500, 19200→3750, 115200→625. The greeting appears in View → Serial Windows → UART #1 and typed characters echo back.

Common mistakes

Q. Garbled characters.

A. Baud mismatch. Confirm the fCK used for BRR (USART1=PCLK2=72MHz) matches the actual clock and the terminal baud.

Q. Only the first char appears, then it stops.

A. Writing DR consecutively without waiting for TXE overwrites the previous char. Wait with while(!(SR & TXE)) before each char.

Q. I left PA9/PA10 as plain I/O.

A. TX(PA9) must be AF push-pull (0xB) and RX(PA10) an input for USART signals to reach the pins.

Summary

  • UART is async over two wires (TX/RX) at a matched baud (8N1 common)
  • USART1 is PA9(TX)/PA10(RX), APB2 bus, BRR=fCK/baud
  • Send after TXE, receive after RXNE
  • View traffic directly in the simulator's UART #1 window
  • TX must be AF push-pull

Exercises

  1. Write print_num() that sends an integer as a decimal string
  2. Echo lowercase letters converted to uppercase
  3. Compute and apply the BRR for 115200bps
Example code / lecture materials

All lecture materials and example code (with homework and answers) are openly available on GitHub.

View on GitHub ↗