Skip to content

Công nghệ 9 giây

“Website hướng dẫn, thử nghiệm và giải thích công nghệ theo cách dễ hiểu cho mọi người.”

Menu
  • Home
  • Công nghệ
  • STM32
  • ESP32
  • Altium Desinger
  • Kiến thức điện máy
Menu

📘 BÀI 21 – GPIO STM32F103: TỪ REGISTER ĐẾN CHÂN GPIO THỰC TẾ

Posted on August 20, 2026August 23, 2026 by congnghe9s

📘 BÀI 21 – GPIO STM32F103: ĐỌC INPUT, PULL-UP/PULL-DOWN VÀ NÚT NHẤN

Ở Bài 17, chúng ta đã học Memory Map và hiểu rằng GPIO thực chất là các thanh ghi nằm trong vùng địa chỉ Peripheral.

Ở Bài 18–20, ta đã bắt đầu đi vào GPIO.

Hôm nay chúng ta nối các kiến thức đó thành một bài rất quan trọng:

STM32 làm thế nào để đọc trạng thái chân GPIO?

Đây chính là nền tảng để sau này đọc nút nhấn, công tắc, cảm biến, encoder, relay feedback, fingerprint sensor…


1. GPIO có hai nhiệm vụ lớn

Một chân GPIO có thể được dùng theo hai hướng:

STM32 ────> GPIO ────> THIẾT BỊ
             OUTPUT

hoặc:

THIẾT BỊ ────> GPIO ────> STM32
                  INPUT

Ví dụ:

OUTPUT

STM32 điều khiển LED:

STM32
 PA8 ───────────> LED

STM32 xuất:

0 → LED OFF
1 → LED ON

INPUT

Nút nhấn đưa tín hiệu vào STM32:

Button ─────────> PA0

STM32 đọc:

PA0 = 0
PA0 = 1

2. Điều quan trọng nhất: INPUT không phải là “0 hoặc 1 cố định”

Khi cấu hình một chân là INPUT, STM32 không tự nhiên biết nó phải là 0 hay 1.

Ví dụ:

PA0 = INPUT

Nếu PA0 không được nối với nguồn nào:

       ?
       |
      PA0

thì điện áp trên PA0 có thể dao động.

Ta gọi đó là:

Floating input – ngõ vào bị thả nổi

Khi đó chương trình có thể đọc:

0
1
0
0
1
1
0
...

một cách không ổn định.


3. Tại sao INPUT bị Floating?

Hãy tưởng tượng PA0 giống như một cái “tai”:

             PA0
              |
              |
             👂

Nếu không có tín hiệu rõ ràng:

5V ───── ?

GND ───── ?

thì nó không biết đang nghe HIGH hay LOW.

Vì vậy ta phải tạo một trạng thái mặc định.

Có hai cách phổ biến:

PULL-UP
PULL-DOWN

4. Pull-Up là gì?

Pull-Up nghĩa là kéo chân INPUT lên mức HIGH.

Ví dụ:

             3.3V
               |
             [R]
               |
               +──── PA0

Điện trở kéo PA0 lên 3.3V.

Khi không có tác động:

PA0 = HIGH

5. Kết hợp Pull-Up với nút nhấn

Đây là mạch cực kỳ quan trọng.

             3.3V
               |
            Pull-Up
               |
               +──────── PA0
               |
             BUTTON
               |
              GND

Không nhấn

Button hở:

3.3V
 |
 R
 |
 +──── PA0
 |
 X
 |
GND

Do Pull-Up:

PA0 = 1

Nhấn nút

Button đóng:

3.3V
 |
 R
 |
 +──── PA0
 |
BUTTON
 |
GND

PA0 bị kéo xuống GND:

PA0 = 0

Vậy:

Button PA0
Không nhấn 1
Nhấn 0

Đây gọi là:

Active LOW


6. Pull-Down thì ngược lại

Mạch:

             3.3V
               |
             BUTTON
               |
               +──── PA0
               |
            Pull-Down
               |
              GND

Không nhấn

PA0 = 0

Nhấn

PA0 = 1
Button PA0
Không nhấn 0
Nhấn 1

Đây là:

Active HIGH


7. STM32F103 có Pull-Up/Pull-Down bên trong

Đây là điều rất tiện.

Ta không nhất thiết phải dùng điện trở ngoài.

STM32F103 có thể cấu hình GPIO input với:

Input Floating
Input Pull-Up
Input Pull-Down

Ví dụ:

PA0 → Input Pull-Up

thì bên trong STM32 sẽ tạo đường kéo lên.

Tư duy đơn giản:

             STM32
        ┌───────────────┐
3.3V ──>│ Pull-Up        │
        │      │         │
        │      +──── PA0 │
        └───────────────┘

8. Thanh ghi nào chứa trạng thái INPUT?

Đây là phần rất quan trọng đối với việc học STM32 theo hướng register-level.

GPIO có thanh ghi:

GPIOx_IDR

Trong đó:

IDR = Input Data Register

Ví dụ:

GPIOA_IDR

là thanh ghi chứa trạng thái input của Port A.


9. IDR có 16 bit

STM32F103 có:

PA0
PA1
PA2
...
PA15

Do đó GPIOA_IDR có 16 bit tương ứng.

Bit:  15 14 13 12 ... 3 2 1 0
      |  |  |  |      | | | |
      PA15          PA3 PA2 PA1 PA0

Ví dụ:

GPIOA_IDR = 0b0000000000000101

thì:

PA2 = 1
PA1 = 0
PA0 = 1

10. Đọc PA0 bằng Bit Mask

Đây là lúc kiến thức Bài 16 – BIT, MASK, & quay trở lại.

Ta muốn kiểm tra:

bit 0

Mask:

(1 << 0)

hay:

0x0001

Sau đó:

GPIOA->IDR & (1 << 0)

11. Vì sao dùng &?

Giả sử:

GPIOA->IDR = 0000 0000 0000 0101

Ta muốn kiểm tra PA0:

0000 0000 0000 0101
&
0000 0000 0000 0001
-------------------
0000 0000 0000 0001

Kết quả:

1

→ PA0 đang HIGH.

Nếu:

GPIOA->IDR = 0000 0000 0000 0100

thì:

0000 0000 0000 0100
&
0000 0000 0000 0001
-------------------
0000 0000 0000 0000

Kết quả:

0

→ PA0 đang LOW.


12. Cách viết trong C

Ví dụ:

if (GPIOA->IDR & (1 << 0))
{
    // PA0 = HIGH
}
else
{
    // PA0 = LOW
}

Có thể viết ngắn hơn:

if (GPIOA->IDR & (1 << 0))
{
    // HIGH
}

13. Đọc PA5 thì sao?

PA5 tương ứng:

bit 5

Mask:

(1 << 5)

Đọc:

if (GPIOA->IDR & (1 << 5))
{
    // PA5 HIGH
}

14. Đọc PB12?

Tương tự:

if (GPIOB->IDR & (1 << 12))
{
    // PB12 HIGH
}

Công thức tổng quát:

GPIOx->IDR & (1 << PIN)

Ví dụ:

GPIOA->IDR & (1 << 0)   // PA0
GPIOA->IDR & (1 << 5)   // PA5
GPIOB->IDR & (1 << 12)  // PB12
GPIOC->IDR & (1 << 13)  // PC13

15. Bài thực hành đầu tiên

Ta sẽ dùng:

  • STM32 Blue Pill
  • PA0 làm INPUT
  • PC13 làm OUTPUT
  • nút nhấn
  • LED onboard PC13

Mục tiêu:

Nhấn nút
   ↓
PA0 = LOW
   ↓
STM32 phát hiện
   ↓
PC13 đổi trạng thái LED

16. Code kiểu Register-Level

Ví dụ cấu hình PA0 Input Pull-Up và PC13 Output.

#include "stm32f103xb.h"

void delay(volatile uint32_t t)
{
    while(t--);
}

int main(void)
{
    // Enable clock GPIOA và GPIOC
    RCC->APB2ENR |= (1 << 2);   // IOPAEN
    RCC->APB2ENR |= (1 << 4);   // IOPCEN

    // PA0 = Input Pull-Up
    GPIOA->CRL &= ~(0xF << 0);
    GPIOA->CRL |=  (0x8 << 0);

    // PA0 Pull-Up
    GPIOA->ODR |= (1 << 0);

    // PC13 = Output Push-Pull, 2 MHz
    GPIOC->CRH &= ~(0xF << 20);
    GPIOC->CRH |=  (0x2 << 20);

    while (1)
    {
        if ((GPIOA->IDR & (1 << 0)) == 0)
        {
            // Button pressed
            GPIOC->ODR &= ~(1 << 13);
        }
        else
        {
            // Button released
            GPIOC->ODR |= (1 << 13);
        }
    }
}

Lưu ý: LED onboard PC13 trên nhiều Blue Pill là active-low, nên cách bật/tắt LED có thể nhìn “ngược” với mức logic của PC13.


17. Có một chi tiết rất đáng chú ý

Ở đây ta có:

GPIOA->ODR |= (1 << 0);

nhưng PA0 đang là INPUT.

Tại sao lại ghi ODR?

Vì khi cấu hình:

Input Pull-Up / Pull-Down

thì trên STM32F1, bit tương ứng trong ODR được dùng để chọn:

ODR bit = 1 → Pull-Up
ODR bit = 0 → Pull-Down

Đây là đặc điểm rất quan trọng của STM32F1.


18. CRL và CRH

STM32F103 chia cấu hình GPIO thành hai thanh ghi:

GPIOx_CRL
GPIOx_CRH

CRL

Dùng cho:

Pin 0 → Pin 7

CRH

Dùng cho:

Pin 8 → Pin 15

Ví dụ:

PA0 → CRL
PA5 → CRL
PA7 → CRL

PA8 → CRH
PA13 → CRH
PA15 → CRH

19. Mỗi GPIO chiếm 4 bit

Đây là điểm bạn cần nhớ thật kỹ.

Trong CRL:

PA0 → bit 0..3
PA1 → bit 4..7
PA2 → bit 8..11
PA3 → bit 12..15
...
PA7 → bit 28..31

Ví dụ:

PA0

chiếm:

bits 0,1,2,3

nên ta dùng:

GPIOA->CRL &= ~(0xF << 0);

Sau đó:

GPIOA->CRL |= (0x8 << 0);

20. 0x8 nghĩa là gì?

Với GPIOF1:

MODE[1:0]
CNF[1:0]

Mỗi pin có 4 bit:

CNF1 CNF0 MODE1 MODE0

Input Pull-Up/Pull-Down:

1000

tức:

0x8

Vì vậy:

GPIOA->CRL |= (0x8 << 0);

có nghĩa:

Cấu hình PA0 thành Input Pull-Up/Pull-Down.

Sau đó ODR quyết định:

ODR = 1 → Pull-Up
ODR = 0 → Pull-Down

21. Một sơ đồ tư duy cực kỳ quan trọng

Bạn hãy nối các bài đã học lại:

              STM32F103
                  │
                  ▼
             GPIO Peripheral
                  │
       ┌──────────┴──────────┐
       │                     │
     INPUT                 OUTPUT
       │                     │
       ▼                     ▼
     IDR                    ODR
       │                     │
       ▼                     ▼
  đọc chân GPIO         xuất chân GPIO
       │                     │
       ▼                     ▼
  Button/Sensor          LED/Motor

Và cấu hình:

GPIOx_CRL / GPIOx_CRH

quyết định GPIO hoạt động như thế nào.


22. Đừng nhầm IDR và ODR

Đây là lỗi người mới học STM32 rất hay mắc.

IDR

GPIOA->IDR

→ đọc trạng thái chân

ODR

GPIOA->ODR

→ ghi/xuất dữ liệu ra chân

Tư duy:

THẾ GIỚI BÊN NGOÀI
       │
       ▼
      GPIO
       │
       ▼
      IDR
       │
       ▼
      CPU

Còn:

CPU
 │
 ▼
ODR
 │
 ▼
GPIO
 │
 ▼
THIẾT BỊ

23. Một bài tập rất quan trọng

Không cần chạy code ngay. Hãy tự tính.

Giả sử:

GPIOA->IDR = 0b0000000000100101;

Hãy xác định:

PA0 = ?
PA1 = ?
PA2 = ?
PA5 = ?

Ta có:

bit:  5 4 3 2 1 0
      1 0 0 1 0 1

Vậy:

PA0 = 1
PA1 = 0
PA2 = 1
PA5 = 1

24. Bài tập số 2

Cho:

GPIOB->IDR = 0x0800;

Hỏi:

PB11 = ?
PB10 = ?
PB12 = ?

Ta biết:

0x0800 = 1 << 11

Do đó:

PB11 = 1
PB10 = 0
PB12 = 0

25. Bài tập số 3 – cực kỳ quan trọng

Nếu:

if (GPIOA->IDR & (1 << 7))
{
    ...
}

thì câu lệnh này đang kiểm tra chân nào?

Đáp án:

PA7

Nếu:

GPIOC->IDR & (1 << 13)

thì:

PC13

26. Kiến thức bạn cần thuộc sau Bài 21

Bạn chưa cần thuộc hàng trăm thanh ghi.

Chỉ cần nắm chắc:

GPIOx_CRL
GPIOx_CRH
GPIOx_IDR
GPIOx_ODR

và hiểu:

CRL/CRH → cấu hình GPIO

IDR → đọc GPIO

ODR → xuất GPIO

Đặc biệt:

GPIOx->IDR & (1 << n)

có nghĩa:

Kiểm tra trạng thái chân GPIO số n.


🎯 Bài tập thực hành Bài 21

Với Blue Pill của bạn, hãy thử bài sau:

PA0  → nút nhấn
PC13 → LED onboard

Yêu cầu:

Không nhấn nút → LED OFF

Nhấn nút → LED ON

Sau khi làm được, hãy nâng cấp:

Nhấn lần 1 → LED ON
Nhấn lần 2 → LED OFF
Nhấn lần 3 → LED ON
...

Lúc đó chúng ta sẽ gặp một vấn đề rất thú vị:

Tại sao chỉ cần giữ nút nhấn mà LED có thể bị đổi trạng thái hàng nghìn lần?

Đó chính là button debounce và state detection.

➡️ Đây sẽ là cầu nối rất đẹp sang BÀI 22 – BUTTON DEBOUNCE, EDGE DETECTION: FALLING EDGE, RISING EDGE VÀ CÁCH STM32 “BIẾT” MỘT LẦN NHẤN NÚT.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recent Posts

  • Bạn có thể hướng dẫn tôi quy trình sửa chữa một mainboard laptop cũng như mainboard của máy tính để bàn không?
  • Có những mức nguồn DC nào trên một bo mạch mainboard laptop?Kỹ thuật đốt dòng để kiểm tra nguồn laptop được thực hiện như thế nào?
  • bài tiếp theo hợp lý là: BÀI 63 – SENSOR FUSION THỰC CHIẾN: ENCODER + IMU MPU6050 + EKF + ODOMETRY X,Y,θ TRÊN STM32F103
  • BÀI 62 – EKF (EXTENDED KALMAN FILTER) CHO ROBOT 2 BÁNH: FUSION ENCODER + IMU + ƯỚC LƯỢNG X, Y, θ, V, ω
  • BÀI 61 – KALMAN FILTER CHO ENCODER + IMU: ƯỚC LƯỢNG TỐC ĐỘ, GÓC θ VÀ SENSOR FUSION

Recent Comments

No comments to show.

Archives

  • September 2026
  • August 2026
  • July 2026
  • June 2026
  • May 2026
  • February 2026

Categories

  • Altium Desinger
  • Công nghệ
  • ESP32
  • Khác
  • Kiến thức điện máy
  • STM32
Live 3D Globes Visitor
©2026 Công nghệ 9 giây | Design: Newspaperly WordPress Theme