STM32F103 GPIO Programming Fundamentals
Overview
In the previous article, we discussed the reset and clock configuration for the STM32F103C8T6 minimal system. At this point, the system clock (SYSCLK) has been configured to 72MHz through a 9x multiplication of the external 8MHz high-speed oscillator (HSE) using the PLL. With the minimal system established, we can proceed with software programming to control the various pins and internal peripherals of the STM32F103. This article focuses on utilizing the General Purpose Input/Output (GPIO) peripherals, concluding with a practical LED blinking experiment.
Hardware and Software Platforms
- Hardware: STM32F103C8T6 minimal system board
- Software: MDK5 (Keil Microcontroller Development Kit)
Reference Documentation
- STM32F10x Reference Manual (Chinese)
- STM32F103xCDE Datasheet (Chinese)
- STM32F103C8T6 Core Board Schematic
- The Definitive Guide to Cortex-M3
GPIO Introduction
GPIO stands for General Purpose Input/Output, which indicates that a pin can function as either an input or output, or serve in a multiplexed capacity. The term "general purpose" refers to pins not connected to any internal peripherals in contrast to "multiplexed" pins, which are connected to other internal peripherals. For example, PA9 and PA10 can serve as standard GPIO pins or be multiplexed as USART1 TX and RX respectively. In microcontroller terminology, "IO" is a general term for pins, with PA9 and PA10 being specific IO instances.
GPIO Operating Modes
GPIO Structure
The GPIO architecture follows two primary paths: output and input, corresponding to the respective operating modes.
Output Modes
There are four output modes: push-pull output, multiplexed push-pull output, open-drain output, and multiplexed open-drain output. The distinctions between these modes are illustrated in the diagram. The output driver serves as the central component, with inputs available from either path ① or ②, and outputs that can drive either PMOS for high levels or NMOS for low levels. With two input options and two output choices, this results in 2×2=4 possible configurations.
Push-Pull Output
In push-pull output (①+③ and ④), the microcontroller core directly controls whether the IO pin outputs a high level (3.3V) or low level (0V). This control is achieved by writing to the Bit Set/Reset Register (BSRR) or directly to the Output Data Register (ODR). Setting the corresponding bit in BSRR to 1 results in a high-level output (3.3V), while setting it to 0 results in a low-level output (0V). For instance, when PA9 is configured as push-pull output, setting PA9 to output 3.3V can be accomplished with GPIOA->BSRR |= (1<<9), which sets bit 9 of ODR to 1, resulting in a high-level output. Alternatively, the ODR register can be directly configured with GPIOA->ODR |= (1<<9). In the LED blinking experiment, the IO pin is configured in push-pull output mode, with the microcontroller core directly controlling the pin's logic level.
The term "push-pull" originates from the PMOS and NMOS transistors at positions ③ and ④. These transistors are never on simultaneously; when the PMOS is on, it outputs a high level (VDD), and when the NMOS is on, it outputs a low level (VSS). When the PMOS conducts, current flows from VDD to the IO pin, resembling a "push" action. Conversely, when the NMOS conducts, current flows from the IO pin to VSS, resembling a "pull" action.
Multiplexed Push-Pull Output
The difference between multiplexed push-pull output and standard push-pull output lies in the input source to the driver. In multiplexed mode, the input comes from path ② (controlled by internal peripherals), whereas in standard push-pull mode, the input comes from path ① (controlled directly by the core). When using path ②, the core controls on-chip peripherals, which in turn manage the IO pin's voltage levels. For example, PA9 can be multiplexed as USART1_TX. When using USART1 for data transmission, PA9 is configured in multiplexed output mode, and the code controls USART1, which then manages the voltage changes on PA9 according to the serial communication protocol.
Open-Drain Output
The distinction between push-pull and open-drain output lies in positions ③ and ④. In push-pull mode, both PMOS and NMOS can be controlled. However, in open-drain mode, only the NMOS can be controlled, making it impossible to output a high level directly. To achieve a high-level output, an external pull-up resistor is required. This mode is rarely used, as there are few peripherals in the STM32F10x that require open-drain configuration for their GPIO pins.
Multiplexed Open-Drain Output
Multiplexed open-drain output differs from standard open-drain output in that the driver's input source is path ② (controlled by on-chip peripherals) rather than path ① (controlled by the core).
Input Modes
There are four input modes: floating input, analog input, pull-up input, and pull-down input. In these modes, the output driver is disabled, rendering the pin incapable of output signals and allowing only input operations. In input mode, the voltage level on the IO pin is controlled by external circuitry, with the microcontroller passively reading voltage changes. This is the reverse of output mode, where the microcontroller actively controls the pin's voltage level.
Floating Input
In floating input mode, the microcontroller core reads the Input Data Register (IDR) to determine whether the IO pin is at a high or low level. For example, in a button experiment, the IO pin can be configured as floating input, and the microcontroller reads the IDR value (1 or 0) to detect if the button is pressed.
Analog Input
Analog input mode allows the pin to read analog signals without conversion to digital values. In contrast to floating input, where a TTL Schmitt trigger converts analog signals to digital values, analog input mode preserves the analog signal for peripherals like ADCs. In ADC experiments, the IO pin must be configured as analog input to capture analog values. Similarly, DAC experiments require special attention to pin configuration.
Pull-Up and Pull-Down Input
Pull-up and pull-down are not separate modes but are implemented alongside floating input. In the STM32F1 series, they are listed as distinct modes, with the internal circuitry effectively adding a pull-up or pull-down resistor to the floating input configuration. Configuring as pull-up or pull-down sets the corresponding bit in the ODR register to 1 or 0. When multiplexing pins for USARTx_RX, pull-up input is required. These are weak pull-up/pull-down configurations, allowing external circuitry to override the internal pull when necessary.
LED Blinking Experiment
Hardware Connection
Connect the microcontroller core board's PC13 pin to an LED, ensuring a current-limiting resistor (approximately 10KΩ) is in series to prevent LED damage.
Software Implementation
LED.h Header File
#ifndef __LED_H
#define __LED_H
#include "stm32f10x.h"
#define LED_RCC_CLK RCC_APB2Periph_GPIOC
#define LED_GPIO_PORT GPIOC
#define LED_GPIO_PIN GPIO_Pin_13
#define LED_TURN_ON() GPIO_ResetBits(LED_GPIO_PORT, LED_GPIO_PIN)
#define LED_TURN_OFF() GPIO_SetBits(LED_GPIO_PORT, LED_GPIO_PIN)
void LED_Initialize(void);
#endif
LED.c Source File
#include "led.h"
void LED_Initialize(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// Enable the clock for GPIO port C
RCC_APB2PeriphClockCmd(LED_RCC_CLK, ENABLE);
// Configure LED pin (PC13) as push-pull output
GPIO_InitStructure.GPIO_Pin = LED_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(LED_GPIO_PORT, &GPIO_InitStructure);
// Turn off LED initially
LED_TURN_OFF();
}
main.c File
#include "led.h"
int main(void)
{
// Initialize LED hardware
LED_Initialize();
// Turn on LED
LED_TURN_ON();
while(1)
{
// Infinite loop - LED remains on
}
}
Summary
The eight GPIO modes can be effectively categorized into three primary modes: floating input, push-pull output, and multiplexed push-pull output. The LED blinking experiment utilizes push-pull output mode, while button reading experiments employ floating input mode. For serial communication using USART1, both multiplexed push-pull output (for TX) and floating input (for RX) are used. When configuring a pin as a general-purpose IO, push-pull output is typically used for output applications, and floating input is used for input applications. When configuring pins for multiplexed functions, the appropriate mode can be determined by referring to the peripheral GPIO configuration section in the reference manual.