Introduction to STM32 Key Handling: From Basics to Complete Implementation (with Code Examples)
🔑 What is a “Key”?
In embedded development, keys (Button/Key) are one of the most common forms of human-machine interaction. It is essentially a mechanical switch:
Based on the above schematic analysis, we can conclude:
- • Not Pressed: The circuit is open, and the pin is pulled high by a pull-up resistor.
- • Pressed: The circuit is closed, and the pin is pulled low.
👉 Therefore, we usually agree that: Pressed = Low, Released = High.
💡 Note: If your hardware connections are different, the logic will be reversed, and adjustments should be made according to the actual situation.
⚙️ Hardware and Level Agreement
Assuming we have four independent keys connected as follows:
- • KEY1 → PB0
- • KEY2 → PB1
- • KEY3 → PB2
- • KEY4 → PA0
And configured as:
- •
<span>GPIO_MODE_INPUT</span> - •
<span>GPIO_PULLUP</span>(Pull-up input, grounding when pressed results in low level)
💻 Complete Code Implementation
1️⃣ Header File <span>key_app.h</span>
#ifndef __KEY_APP_H__
#define __KEY_APP_H__
#include "bsp_system.h"
void key_proc(void);
#endif
2️⃣ Source File <span>key_app.c</span>
#include "key_app.h"
uint8_t key_val = 0; // Current key state
uint8_t key_old = 0; // Previous key state
uint8_t key_down = 0; // Pressed key
uint8_t key_up = 0; // Released key
/**
* @brief Read key state
* @return Returns the key number. 0 indicates no key pressed, 1-4 indicates the corresponding key is pressed.
*/
uint8_t key_read(void)
{
uint8_t temp = 0;
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_RESET) temp = 1;
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1) == GPIO_PIN_RESET) temp = 2;
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2) == GPIO_PIN_RESET) temp = 3;
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) temp = 4;
return temp;
}
/**
* @brief Key processing function
* Scans the current state and generates "press/release" events
*/
void key_proc(void)
{
key_val = key_read();
key_down = key_val & (key_old ^ key_val); // Just pressed key
key_up = ~key_val & (key_old ^ key_val); // Just released key
key_old = key_val;
// Key pressed
switch(key_down)
{
// Key 1 pressed, led1 on
case 1:
led1_on();
break;
// Key 2 pressed, led2 on
case 2:
led2_on();
break;
// Key 3 pressed, led3 on
case 3:
led3_on();
break;
// Key 4 pressed, led4 on
case 4:
led4_on();
break;
}
// Key released
switch(key_up)
{
// Key 1 released, led1 off
case 1:
led1_off();
break;
// Key 2 released, led2 off
case 2:
led2_off();
break;
// Key 3 released, led3 off
case 3:
led3_off();
break;
// Key 4 released, led4 off
case 4:
led4_off();
break;
}
}
🧩 Why These Three Lines?
Many people may be confused at this point: why use this bitwise operation? Below is a line-by-line explanation.
1)<span>key_old ^ key_val</span> —— Find the changes
The characteristic of XOR (<span>^</span>) is: the same is 0, different is 1.
- • If this time and the last state are the same → no change → the result is 0.
- • If this time and the last state are different → there is a change → the result is non-zero.
So <span>(key_old ^ key_val)</span><span> is the </span><strong><span>changing part</span></strong><span>.</span>
2)<span>key_down = key_val & (key_old ^ key_val);</span>
- • Condition one: this time is pressed (
<span>key_val</span>is 1). - • Condition two: different from the last time (
<span>key_old ^ key_val</span>is 1).
If both conditions are met, it indicates thatthis key is “just pressed”.
3)<span>key_up = ~key_val & (key_old ^ key_val);</span>
- • Condition one: this time not pressed (
<span>~key_val</span>is 1). - • Condition two: different from the last time (
<span>key_old ^ key_val</span>is 1).
If both conditions are met, it indicates thatthis key is “just released”.
4)<span>key_old = key_val;</span>
Finally, the current state must be saved for the next comparison; otherwise, it will not be possible to determine the change.
🚀 How to Use in Projects?
- 1. Periodic Call (in the scheduler or timer, call every 10~30ms): You can refer to previous articles on the public account for knowledge about the scheduler.
key_proc(); // Scan keys
- 1. Detect “Just Pressed” Keys:
switch (key_down) {
case 1: /* KEY1 pressed */ break;
case 2: /* KEY2 pressed */ break;
case 3: /* KEY3 pressed */ break;
case 4: /* KEY4 pressed */ break;
default: break;
}
- 1. Detect “Just Released” Keys:
switch (key_up) {
case 1: /* KEY1 released */ break;
case 2: /* KEY2 released */ break;
case 3: /* KEY3 released */ break;
case 4: /* KEY4 released */ break;
default: break;
}
📌 Precautions
- 1. Numbering Model:
- • The return value is 0~4 numbering, not a bitmap.
- • When multiple keys are pressed simultaneously, only the last detected key (fixed priority) will be returned.
- • Since it is a number, the writing should be
<span>if (key_down == 2)</span>or<span>switch-case</span>. - • Do not use bitwise operations
<span>if (key_down & 0x02)</span>.
✅ Summary
- • This code implements a minimal usable key driver: supports numbering return and detects press and release events.
- • Advantages: simple and intuitive, suitable for beginners and small projects.
- • Limitations: does not support simultaneous detection of multiple keys, needs to be expanded in the project according to requirements.