ES: PY32 UART

USART on the PY32F0xx enables serial communication for debug output, PC connectivity, and inter-device messaging. This guide covers the complete USART1 configuration workflow using the PY32 HAL, including clock enable, GPIO setup, baud rate configuration, and enabling the peripheral.

USART (Universal Synchronous/Asynchronous Receiver/Transmitter) is the serial communication backbone of most embedded projects. On the PY32F0xx, USART is used for debug output, communicating with a host PC, connecting GPS modules, and interfacing with other microcontrollers.

The PY32F0xx USART implementation is functionally identical to STM32 USART, making any existing STM32 USART knowledge directly applicable.


USART vs UART

The PY32F0xx implements USART — a superset of UART that adds synchronous mode (a shared clock signal between transmitter and receiver). In practice, asynchronous UART mode is used for virtually all applications, which requires no clock line.

md
UART  (Asynchronous): TX and RX wires only, both sides pre-agree on baud rate
USART (Synchronous):  TX, RX, and CLK wires — master drives the clock

Most applications use UART mode (no CLK signal).


USART1 Pin Mapping on PY32F003

SignalDefault GPIO Pin
USART1_TXPA9 (or PA1 on remapped)
USART1_RXPA10 (or PA0 on remapped)

On the PY32F003 in TSSOP20 package, PA1 (TX) and PA0 (RX) are the accessible USART pins.


Complete USART1 Configuration

Step 1: Enable Clocks

cpp
// Enable clock for GPIOA (for TX and RX pins)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

// Enable clock for USART1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

Both GPIOA and USART1 are on the APB2 bus on the PY32F003.

Step 2: Configure GPIO Pins for USART

cpp
GPIO_InitTypeDef GPIO_InitStructure;

// Configure PA9 (or PA1) as USART1 TX
// Alternate function push-pull output
GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;   // PA9 = TX
GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF; // Alternate function
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

// Configure PA10 (or PA0) as USART1 RX
// Floating input — let the transmitting device drive the signal
GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_10;   // PA10 = RX
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);

Step 3: Configure USART1 Settings

cpp
USART_InitTypeDef USART_InitStructure;

USART_InitStructure.USART_BaudRate            = 9600;
USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
USART_InitStructure.USART_StopBits            = USART_StopBits_1;
USART_InitStructure.USART_Parity              = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode                = USART_Mode_Tx | USART_Mode_Rx;

USART_Init(USART1, &USART_InitStructure);

Configuration Options

ParameterCommon ValuesNotes
USART_BaudRate9600, 19200, 115200Must match the connected device
USART_WordLengthUSART_WordLength_8b, USART_WordLength_9b8-bit is standard
USART_StopBitsUSART_StopBits_1, USART_StopBits_21 stop bit is standard
USART_ParityUSART_Parity_No, USART_Parity_Even, USART_Parity_OddNo parity is most common
USART_ModeUSART_Mode_Tx, USART_Mode_Rx, or bothEnable TX, RX, or full-duplex

Step 4: Enable USART1

cpp
USART_Cmd(USART1, ENABLE);


Sending Data Over USART

To transmit a single byte, write to the USART data register and wait for the transmit buffer to be empty:

cpp
void USART_SendByte(USART_TypeDef *USARTx, uint8_t data) {
    // Wait until transmit data register is empty
    while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
    USART_SendData(USARTx, data);
}

To transmit a string:

cpp
void USART_SendString(USART_TypeDef *USARTx, const char *str) {
    while (*str) {
        USART_SendByte(USARTx, (uint8_t)*str);
        str++;
    }
}

Usage:

cpp
USART_SendString(USART1, "Hello from PY32!\r\n");


Receiving Data Over USART

To receive a single byte (polling mode):

cpp
uint8_t USART_ReceiveByte(USART_TypeDef *USARTx) {
    // Wait until data is received
    while (USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) == RESET);
    return (uint8_t)USART_ReceiveData(USARTx);
}

For non-blocking reception, use interrupt-driven mode:

cpp
// Enable RXNE interrupt in USART
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

// Configure NVIC for USART1
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel                   = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
NVIC_Init(&NVIC_InitStructure);

// ISR: called when a byte is received
void USART1_IRQHandler(void) {
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
        uint8_t received = USART_ReceiveData(USART1);
        // Process received byte here
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    }
}


Complete USART Echo Example

This example initializes USART1 and echoes back every received character:

cpp
#include "py32f0xx.h"
#include "py32f0xx_gpio.h"
#include "py32f0xx_rcc.h"
#include "py32f0xx_usart.h"

void USART1_Init(void) {
    // Enable clocks
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA  | RCC_APB2Periph_USART1, ENABLE);

    // Configure GPIO
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;   // TX
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_10;   // RX
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // Configure USART
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate            = 9600;
    USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits            = USART_StopBits_1;
    USART_InitStructure.USART_Parity              = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode                = USART_Mode_Tx | USART_Mode_Rx;
    USART_Init(USART1, &USART_InitStructure);

    USART_Cmd(USART1, ENABLE);
}

int main(void) {
    USART1_Init();
    USART_SendString(USART1, "PY32 USART Echo Ready\r\n");

    while (1) {
        // Wait for and echo received byte
        uint8_t byte = USART_ReceiveByte(USART1);
        USART_SendByte(USART1, byte);
    }
}


USART Wiring

md
PY32F003          USB-TTL Adapter / Other Device
--------          --------------------------------
PA9  (TX) ----->  RX
PA0  (RX) <-----  TX
GND       ------  GND

Always cross-connect TX to RX and share a common GND reference.


Final Thoughts

USART on the PY32F0xx is straightforward to configure and is the first peripheral most developers bring up in a new project — typically to enable printf-style debug output. The configuration flow matches STM32 exactly, making any existing STM32 USART code directly portable.

Interrupt-driven reception is preferred for any non-trivial application because it keeps the CPU free during idle periods rather than polling the receive buffer in a tight loop.

USART is the voice of the PY32 — it is how the microcontroller communicates with the world.