If in Part 14 we sent signals through the pin, this time we read signals from the pin. Inputs like buttons and switches are a basic feature of embedded systems, but there is a trap that beginners often fall into. This is floating input — an input pin that is not connected to anything is in a floating state, neither 0 nor 1, so its value fluctuates depending on noise. To prevent this, we use a Pull-up/Pulldown resistor, and the STM32 has this built into the chip, which can be enabled via a register.
In this part, we set PA0 as input, enable the internal pull-up, then read the button state via IDR to control the PC13 LED. You will learn 4-bit settings for input mode, the method of selecting through Pull-up/Pulldown ODR, and active logic (active low/high).
Learning Objectives
- Set GPIO to input mode and read pin status with
IDR. - Explain the danger of floating input and the necessity of Of-pull-up/pull-down.
- On F1, select via internal Pull-up/Pulldown ODR bits.
- Implement judgment logic according to active Low/High button wiring.
- Turn the LED on and off with button input.
Target Environment
- Compiler: Keil MDK-ARM (Arm Compiler 6).
- Device: STM32F103C8 (μVision simulator).
- Verification: μVision simulator (force input on GPIOA IDR, observe GPIOC). Judgment logic is PC
gcc(pc_test.c).
Key Concepts
1) Input Mode 4 Bits
Input has MODE=00, and CNF determines the type of input.
| CNF | Input Type | 4-bit Code |
|---|---|---|
| 00 | Analog | 0x0 |
| 01 | Floating input (reset default) | 0x4 |
| 10 | Pull-up/Pulldown input | 0x8 |
Button input usually uses Pull-up/Pulldown input (0x8).
2) Why floating is dangerous
If there is nothing connected to the input pin, the pin voltage floats between 0 and 1 according to surrounding noise. This causes chattering, making it read as if pressed even when the button is not pressed. Setting a default using pull-up (normally fixed at 1) or pull-down (normally fixed at 0) solves this problem.
3) Pull-up/Pulldown selection by ODR
When CNF=10 (Pull-up/Pulldown input) on F1, which one it is is determined by the ODR bit.
| ODR bit | Effect |
|---|---|
| 1 | Internal pull-up (normally IDR=1) |
| 0 | Internal pull-down (normally IDR=0) |
GPIOA->CRL |= (0x8u << 0); /*PA0 = Pull-up/Pull-down input*/
GPIOA->ODR |= (1u << 0); /*ODR=1 → Pull-up*/
4) Reading — IDR
The corresponding bit of IDR (Input Data Register) is the pin status. Mask it to see just one bit.
if ((GPIOA->IDR & (1u << 0)) == 0u) { /*PA0 is 0 (pressed)*/ }
5) Active logic — pull-up + GND button = Active low
If you place the button between PA0 and GND and enable the pull-up:
- Not pressed → pull-up keeps 1 → IDR=1
- Pressed → connected to GND → IDR=0
That is, Pressed = 0 (Active Low). If it is a pull-down + VCC button, it is the opposite: Pressed = 1 (Active High).
Creating a μVision Project
Project → New µVision Project…→ name itstm32_gpio_in.- Device: STM32F103C8.
Manage Run-Time Environment→ CMSIS:CORE, Device:Startup.- After adding
main.c, copy and paste. - Apply
STM32F10X_MDtoC/C++ → Define. Debug → Use Simulator,-pSTM32F103C8.- Rebuild(F7) → Debug(Ctrl+F5).
Examples
Example 1 — main.c: Control LED with Button
#include "stm32f10x.h"
/*
*Lecture 15 Example — Controlling LED with Button Input.
*Target: STM32F103C8, Compiler: Keil MDK-ARM.
*
*Set PA0 as 'Input + Internal Pull-up' and connect the button between PA0 and GND.
*- Not pressed: pull-up pulls it to 1 → IDR bit = 1
*- Pressed: connected to GND → IDR bit = 0 (active-low button)
*Turn on PC13 LED when the button is pressed (LED is also active-low, so pin=0 is ON).
*/
#define BTN_PIN 0u /* PA0 */
#define LED_PIN 13u /* PC13 */
int main(void)
{
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN;
/*PA0: Input + Pull-up/Pull-down. CNF=10, MODE=00 → 4-bit 0x8.
*Select pull-up or pull-down using ODR bit (ODR=1 → pull-up).*/
GPIOA->CRL &= ~(0xFu << (BTN_PIN * 4u));
GPIOA->CRL |= (0x8u << (BTN_PIN * 4u));
GPIOA->ODR |= (1u << BTN_PIN); /*Select pull-up*/
/*PC13: Push-pull output 2MHz = 0x2 (CRH)*/
GPIOC->CRH &= ~(0xFu << ((LED_PIN - 8u) * 4u));
GPIOC->CRH |= (0x2u << ((LED_PIN - 8u) * 4u));
while (1) {
if ((GPIOA->IDR & (1u << BTN_PIN)) == 0u) {
GPIOC->BRR = (1u << LED_PIN); /*Pressed: LED ON (=0)*/
} else {
GPIOC->BSRR = (1u << LED_PIN); /*Not pressed: LED OFF (=1)*/
}
}
}
Example 2 — pc_test.c: Input Judgment Logic Check
#include <stdio.h>
#include <stdint.h>
/*
*Lecture 15 PC Checking — Button press determination and LED decision logic in pull-up input.
*When the pull-up + button is connected to GND, if not pressed 1 (pull-up), if pressed 0 (active low).
*Check the determination for various IDR values without hardware.
*/
static int is_pressed(uint32_t idr, unsigned int pin)
{
return ((idr & (1u << pin)) == 0u) ? 1 : 0; /*If the bit is 0, it is pressed.*/
}
int main(void)
{
uint32_t samples[4] = { 0x0001u, 0x0000u, 0x0021u, 0x0020u };
unsigned int pin = 0u;
int i;
for (i = 0; i < 4; i++) {
int pressed = is_pressed(samples[i], pin);
printf("IDR=0x%04lX -> %s -> LED %s\n",
(unsigned long)samples[i],
pressed ? "PRESSED" : "released",
pressed ? "ON" : "OFF");
}
return 0;
}
Checking with Simulator
- Since there is no button in the simulator, directly change the IDR of
Peripherals → GPIOAor force the pin with a debug command. Example: In the Command window,PORTA &= ~0x0001(PA0=0, pressed) /PORTA |= 0x0001(PA0=1, not pressed). - When PA0=0, check via GPIOC ODR if PC13 is 0 (LED ON); when PA0=1, PC13 should be 1 (LED OFF).
- Expected Behavior: PC13 immediately follows in the opposite state according to the PA0 input.
Verification on a PC
gcc pc_test.c -o pc_test && ./pc_test
Expected output (stdout):
IDR=0x0001 -> released -> LED OFF
IDR=0x0000 -> PRESSED -> LED ON
IDR=0x0021 -> released -> LED OFF
IDR=0x0020 -> PRESSED -> LED ON
Common Mistakes
Q. The value fluctuates randomly even when the button is not pressed.
A. Pull-up/Pulldown is not turned on, so it is in a floating state. Set CNF=10 (0x8) and select pull-up (1) or pull-down (0) with ODR.
Q. Even with CNF=10, the pull-up does not work.
A. On the F1, Pull-up/Pulldown is selected with the ODR bit. In input mode, ODR=1 means pull-up, 0 means pull-down. Do not forget to set ODR.
Q. Will the pin change if I write to IDR?
A. IDR is read-only. Output is done with With-ODR/BSRR/BRR. IDR only reads the current input state of the pin.
Q. Sometimes the button acts as if it was pressed twice.
A. Mechanical buttons have contact bounce. This section deals only with simple reading, and debouncing is properly handled with a state machine in section 21.
Summary
- Input type is determined by MODE=00, CNF (0x8 = Pull-up/Pulldown input).
- To avoid floating, turn on Pull-up/Pulldown, and F1 selects that output to ODR.
- Pin state is read via the read-only
IDR. - Pull-up + GND button = pressed state is 0 (active low).
- Chattering is handled in debouncing (section 21).
Try it yourself
Solve the assignment below. The reference solution is shown below.