Nội Dung Bài Viết
Trong bài viết này, chúng ta sẽ tìm hiểu cách lập trình STM32F103 thanh ghi để cấu hình GPIO input STM32F103 và đọc trạng thái nút nhấn thông qua thanh ghi GPIO_IDR. Ví dụ minh họa sử dụng vi điều khiển STM32F103RCT6, cấu hình chân PC1 làm input pull-up để điều khiển bật/tắt LED PA8.
Bài viết tập trung vào lập trình bare-metal, không sử dụng thư viện HAL, giúp bạn hiểu rõ bản chất hoạt động của GPIO trong STM32.
Mục tiêu bài lập trình STM32F103 thanh ghi cấu hình GPIO input
- Cấu hình PC1 làm GPIO input pull-up
- Điều khiển LED PA8 (LED0)
Define struct thanh ghi GPIO_IDR cho lập trình STM32F103 thanh ghi

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // GPIO Input Data Register typedef union { struct { __IM uint32_t IDR0 : 1; // Bit 0: Input data for pin 0 __IM uint32_t IDR1 : 1; // Bit 1: Input data for pin 1 __IM uint32_t IDR2 : 1; // Bit 2: Input data for pin 2 __IM uint32_t IDR3 : 1; // Bit 3: Input data for pin 3 __IM uint32_t IDR4 : 1; // Bit 4: Input data for pin 4 __IM uint32_t IDR5 : 1; // Bit 5: Input data for pin 5 __IM uint32_t IDR6 : 1; // Bit 6: Input data for pin 6 __IM uint32_t IDR7 : 1; // Bit 7: Input data for pin 7 __IM uint32_t IDR8 : 1; // Bit 8: Input data for pin 8 __IM uint32_t IDR9 : 1; // Bit 9: Input data for pin 9 __IM uint32_t IDR10 : 1; // Bit 10: Input data for pin 10 __IM uint32_t IDR11 : 1; // Bit 11: Input data for pin 11 __IM uint32_t IDR12 : 1; // Bit 12: Input data for pin 12 __IM uint32_t IDR13 : 1; // Bit 13: Input data for pin 13 __IM uint32_t IDR14 : 1; // Bit 14: Input data for pin 14 __IM uint32_t IDR15 : 1; // Bit 15: Input data for pin 15 uint32_t reserved: 16; // Bit 16-31: Reserved }; __IOM uint32_t reg; } GPIO_IDR_Type; |
Các thanh ghi cấu hình GPIO Input trong lập trình STM32F103 thanh ghi
Thanh ghi GPIO_IDR trong STM32F103

GPIO_IDR là một thanh ghi rất quan trọng trong bộ GPIO của vi điều khiển STM32F1 (và nhiều dòng STM32 khác).
Thanh ghi GPIO_IDR – Thanh ghi dữ liệu ngõ vào của một cổng GPIO. GPIOx_IDR dùng để đọc trạng thái logic tại các chân GPIO (ngõ vào). Thanh ghi này chỉ cho phép đọc (read-only) và phản ánh mức điện áp hiện tại trên các chân được cấu hình làm đầu vào.
Mỗi bit trong GPIOx_IDR tương ứng với một chân pin của cổng đó:
- PA0 ↔ bit 0
- PA1 ↔ bit 1
- v.v…
Khi đọc GPIOx_IDR, ta biết được:
- Mức logic 0 (chân ở mức thấp – GND)
- Mức logic 1 (chân ở mức cao – 3.3V)
Ví dụ thanh ghi IDR
Nghĩa là:
- PA2 = 1 (mức cao)
- PA4 = 1 (mức cao)
- Các chân còn lại = 0
Thanh ghi GPIO_ODR – Thanh ghi dữ liệu ngõ ra

GPIOx_ODR dùng để:
- Ghi giá trị logic ra các chân output
- Hoặc đọc lại trạng thái đang được xuất ra (nếu cấu hình chân là output)
Khi một chân GPIO được cấu hình là output (push-pull hoặc open-drain), thanh ghi GPIOx_ODR quyết định mức logic xuất ra.
Khi chân được cấu hình là input, bit tương ứng trong ODR không điều khiển đầu ra, nhưng có tác dụng khi cấu hình input pull-up / pull-down.
Ví dụ thanh ghi ODR
Nghĩa là:
- PA1 = mức 1 (cao)
- PA8 = mức 1 (cao)
- Các chân khác = mức 0 (thấp)
Cấu hình GPIO input STM32f103RCT6
Các bước cấu hình GPIO Input cho STM32F103
- Bật clock GPIOA
- Bật clock GPIOC
- Cấu hình PC1 (KEY0):
- MODE1 = 0 (input)
- CNF1 = 2 (pull-up)
- ODR1 = 1 (pull-up)
(RM0008 – Section 9.2.1, trang 171)
Code cấu hình GPIO input PC1
1 2 3 4 5 6 7 8 | void GPIO_Button_Config(void) { // Enable GPIOC clock (RM0008, Section 7.3.7, page 113) RCC->APB2ENR.IOPCEN = 1; // Configure PC1 as input with pull-up GPIOC->CRL.MODE1 = 0; // Input mode GPIOC->CRL.CNF1 = 2; // Pull-up/pull-down GPIOC->ODR.ODR1 = 1; // Enable pull-up } |
1 2 3 4 5 6 7 8 | void LED_Toggle(void) { // Toggle PA8 state if (GPIOA->ODR.ODR8) { GPIOA->BSRR.BR8 = 1; // Set low } else { GPIOA->BSRR.BS8 = 1; // Set high } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 | int main(void) { SystemClock_Config(); GPIO_LED_Config(); GPIO_Button_Config(); while (1) { // Check if PC1 is low (button pressed) if (!((GPIOC->IDR.reg >> 1) & 1U)) { LED_Toggle(); Delay_Approx(30); // Debounce delay 0.03s } } } |
Github link: Download code chương trình cấu hình GPIO Input STM32f103RCT6
Giải thích đọc GPIO input bằng IDR
Giải thích:
GPIOC->IDR.reglà thanh ghi Input Data Register của port C- Dịch phải
>> 1để lấy bit 1 (PC1) & 1Uđể lọc lấy 1 bit- Dấu
!đảo ngược logic (do input pull-up)
IDR đọc trạng thái input (RM0008 – Section 9.2.3, trang 172)
Chống dội nút nhấn (Debounce) bằng Delay
Hàm tạo trễ khoảng 30ms (0.03 giây).
Mục đích:
- Chống dội nút nhấn (debounce)
- Tránh LED đổi trạng thái nhiều lần do nhiễu khi nhấn nút
Trong thời gian delay 30ms, dù nút còn dội hay không, chương trình không phản ứng.
Vấn đề của cách chống dội đơn giản
Chương trình chống dội trên có thể hoạt động không chính xác:
- Nút nhấn cơ học không thay đổi tín hiệu ngay lập tức
- Khi nhấn hoặc nhả, tín hiệu dao động trong vài ms đến vài chục ms
Nhấn nút thật:
Tín hiệu thực tế:
Hạn chế:
- Trong 30ms delay, nếu nút vẫn bị dội → LED có thể toggle thêm
- Nhấn nhanh hơn 30ms → bị bỏ qua
- Không kiểm tra cạnh → giữ nút vẫn bị toggle liên tục
Bạn hãy thử tìm cách cải thiện code, để tránh trường hợp giả nhấn khi thả nút nha ?
Chương trình chống dội cải thiện (Dò cạnh + debounce)
- Dùng biến
lastKeyStateđể phát hiện cạnh - Chỉ toggle khi từ 1 → 0
- Chỉ toggle 1 lần cho mỗi lần nhấn
- Không toggle khi giữ nút
- Lọc nhiễu tốt cả khi nhấn và nhả
Kết luận
Qua bài viết này, bạn đã nắm được cách lập trình STM32F103 thanh ghi để cấu hình GPIO input STM32F103RCT6, đọc nút nhấn bằng GPIO_IDR và điều khiển LED thông qua GPIO_ODR/BSRR. Việc kết hợp dò cạnh và chống dội giúp chương trình hoạt động ổn định, đúng thực tế hơn so với cách delay đơn giản.
Đây là nền tảng rất quan trọng khi học cấu hình GPIO input STM32F103RCT6 theo hướng bare-metal.
Bài viết tiếp theo tôi sẽ giới thiệu các phương pháp chống dội nút nhấn bằng phần mềm khác.
📘 Tham chiếu: RM0008 – Section 9.2 (trang 171–175)
