GPIO is the foundation of all embedded hardware interaction on the PY32F0xx. Before configuring USART, SPI, or I2C, those peripheral pins must be set up as GPIO in the correct mode. Understanding GPIO configuration on PY32 also means understanding the HAL API that applies to every other peripheral.
GPIO Features on PY32F0xx
The GPIO module in the PY32F0xx family supports:
| Feature | Description |
|---|---|
| Input modes | Floating, pull-up, pull-down |
| Output modes | Push-pull, open-drain |
| Alternate function | Route peripheral signals (UART, SPI, I2C) through GPIO pins |
| Analog mode | ADC and DAC operation |
| Interrupt handling | Rising, falling, or both edge triggers on any GPIO pin |
Required Headers
#include "py32f0xx.h" // Main device header
#include "py32f0xx_gpio.h" // GPIO configuration functions
#include "py32f0xx_rcc.h" // RCC for enabling GPIO clocks
Step 1: Enable GPIO Clock
Each GPIO port (GPIOA, GPIOB, GPIOF) requires its clock to be enabled before pins can be configured. This is done through the RCC (Reset and Clock Control) module:
// Enable clock for GPIOA
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// Enable clock for GPIOB
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
Forgetting to enable the GPIO clock is one of the most common mistakes in PY32/STM32-style HAL development. Without the clock, all register writes to the GPIO peripheral are silently ignored.
Step 2: Initialize GPIO Pins
Use the GPIO_InitTypeDef structure to configure pin mode, speed, and other parameters:
GPIO_InitTypeDef GPIO_InitStructure;
// Configure PA0 as floating digital input
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// Configure PA1 as push-pull output at 50 MHz
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO Mode Options
| Mode Constant | Type | Description |
|---|---|---|
GPIO_Mode_IN_FLOATING | Input | No internal pull resistor |
GPIO_Mode_IPU | Input | Internal pull-up resistor |
GPIO_Mode_IPD | Input | Internal pull-down resistor |
GPIO_Mode_Out_PP | Output | Push-pull (drives HIGH and LOW) |
GPIO_Mode_Out_OD | Output | Open-drain (drives LOW, floats HIGH) |
GPIO_Mode_AF | Alternate | Routes peripheral signal through pin |
GPIO_Mode_AIN | Analog | ADC/DAC input — disables digital functions |
GPIO Speed Options
| Speed Constant | Maximum Frequency |
|---|---|
GPIO_Speed_2MHz | 2 MHz |
GPIO_Speed_10MHz | 10 MHz |
GPIO_Speed_50MHz | 50 MHz |
For GPIO toggling, GPIO_Speed_50MHz is the standard choice. For I2C (open-drain) pins, lower speeds reduce EMI.
Step 3: Set or Read GPIO Pins
Set Output HIGH
GPIO_SetBits(GPIOA, GPIO_Pin_1); // Set PA1 to HIGH (3.3V)
Set Output LOW
GPIO_ResetBits(GPIOA, GPIO_Pin_1); // Set PA1 to LOW (0V)
Toggle Output
// Toggle PA1 state (read-modify-write on ODR)
GPIOA->ODR ^= GPIO_Pin_1;
Read Input State
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)) {
// PA0 is HIGH
} else {
// PA0 is LOW
}
Read Entire Port
uint16_t port_value = GPIO_ReadInputData(GPIOA);
// Check individual bits with bitwise AND
if (port_value & GPIO_Pin_3) {
// PA3 is HIGH
}
Complete GPIO Example: LED and Button
#include "py32f0xx.h"
#include "py32f0xx_gpio.h"
#include "py32f0xx_rcc.h"
void GPIO_Config(void) {
GPIO_InitTypeDef GPIO_InitStructure;
// Enable clocks
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// Configure PA0 as input with pull-up (button: active LOW)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // Pull-up
GPIO_Init(GPIOA, &GPIO_InitStructure);
// Configure PB5 as push-pull output (LED)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
int main(void) {
GPIO_Config();
while (1) {
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0) {
// Button is pressed (active LOW with pull-up)
GPIO_SetBits(GPIOB, GPIO_Pin_5); // LED ON
} else {
GPIO_ResetBits(GPIOB, GPIO_Pin_5); // LED OFF
}
}
}
Step 4: Configure GPIO Interrupts (External Interrupts)
GPIO pins on the PY32F0xx can trigger interrupts through the EXTI (External Interrupt) controller. This enables event-driven firmware rather than polling in a loop.
Required additional headers:
#include "py32f0xx_exti.h" // External interrupt configuration
#include "py32f0xx_nvic.h" // NVIC interrupt controller
Configure EXTI and NVIC:
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// Connect EXTI Line0 to PA0
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
// Configure EXTI Line0 for rising edge trigger
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
// Enable EXTI Line0 interrupt in NVIC
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
Implement the interrupt service routine:
void EXTI0_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
// Handle the interrupt event
GPIO_ToggleBits(GPIOB, GPIO_Pin_5); // Toggle LED on button press
// Clear the interrupt flag
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
Always clear the interrupt pending bit inside the ISR, or the interrupt will immediately trigger again.
GPIO API Summary
| Function | Description |
|---|---|
GPIO_Init(port, &init) | Initialize GPIO pin(s) |
GPIO_SetBits(port, pins) | Set specified pins HIGH |
GPIO_ResetBits(port, pins) | Set specified pins LOW |
GPIO_ReadInputDataBit(port, pin) | Read single input pin state |
GPIO_ReadInputData(port) | Read all input pins on a port |
GPIO_ReadOutputDataBit(port, pin) | Read output register bit |
GPIO_EXTILineConfig(port, pin) | Connect GPIO pin to EXTI line |
EXTI_Init(&init) | Configure external interrupt |
EXTI_GetITStatus(line) | Check if interrupt is pending |
EXTI_ClearITPendingBit(line) | Clear interrupt flag |
Final Thoughts
GPIO configuration on PY32F0xx follows the STM32-compatible HAL pattern precisely. The same GPIO_InitTypeDef structure, GPIO_Init() function, and EXTI/NVIC configuration flow used here applies to virtually every STM32 family microcontroller as well.
Mastering this pattern:
- Enables you to configure any GPIO pin for any purpose
- Gives you the foundation to configure peripheral pins (USART, SPI, I2C)
- Provides interrupt-driven input handling without polling
GPIO on PY32 is the starting point for every firmware project on this platform.