Nội Dung Bài Viết
Bài viết này hướng dẫn chi tiết cách lập trình STM32 thanh ghi để cấu hình GPIO Output STM32F103RCT6, đồng thời thực hiện chương trình nhấp nháy LED (blink LED) sử dụng delay vòng lặp đơn giản. Đây là bước cơ bản trong lập trình bare-metal, giúp bạn hiểu sâu về thanh ghi GPIO mà không cần thư viện HAL.
Define struct thanh ghi GPIO cho lập trình STM32 thanh ghi cấu hình GPIO Output STM32F103
Dựa trên RM0008, Section 9.2 (trang 171-177):
Thanh ghi port configuration register low (GPIOx_CRL) (x=A..G)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // Thanh ghi Cấu hình thấp GPIO (GPIO Configuration Low Register) typedef union { struct { // Structure for bit fields __IOM uint32_t MODE0 : 2; // Bit 0-1: Pin 0 mode __IOM uint32_t CNF0 : 2; // Bit 2-3: Pin 0 configuration __IOM uint32_t MODE1 : 2; // Bit 4-5: Pin 1 mode __IOM uint32_t CNF1 : 2; // Bit 6-7: Pin 1 configuration __IOM uint32_t MODE2 : 2; // Bit 8-9: Pin 2 mode __IOM uint32_t CNF2 : 2; // Bit 10-11: Pin 2 configuration __IOM uint32_t MODE3 : 2; // Bit 12-13: Pin 3 mode __IOM uint32_t CNF3 : 2; // Bit 14-15: Pin 3 configuration __IOM uint32_t MODE4 : 2; // Bit 16-17: Pin 4 mode __IOM uint32_t CNF4 : 2; // Bit 18-19: Pin 4 configuration __IOM uint32_t MODE5 : 2; // Bit 20-21: Pin 5 mode __IOM uint32_t CNF5 : 2; // Bit 22-23: Pin 5 configuration __IOM uint32_t MODE6 : 2; // Bit 24-25: Pin 6 mode __IOM uint32_t CNF6 : 2; // Bit 26-27: Pin 6 configuration __IOM uint32_t MODE7 : 2; // Bit 28-29: Pin 7 mode __IOM uint32_t CNF7 : 2; // Bit 30-31: Pin 7 configuration }; __IOM uint32_t reg; // Access entire register } GPIO_CRL_Type; |
Thanh ghi port configuration register high (GPIO_x_CRH) (x=A..G)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // Thanh ghi Cấu hình cao GPIO (GPIO Configuration High Register) typedef union { struct { // Structure for bit fields __IOM uint32_t MODE8 : 2; // Bit 0-1: Pin 8 mode __IOM uint32_t CNF8 : 2; // Bit 2-3: Pin 8 configuration __IOM uint32_t MODE9 : 2; // Bit 4-5: Pin 9 mode __IOM uint32_t CNF9 : 2; // Bit 6-7: Pin 9 configuration __IOM uint32_t MODE10 : 2; // Bit 8-9: Pin 10 mode __IOM uint32_t CNF10 : 2; // Bit 10-11: Pin 10 configuration __IOM uint32_t MODE11 : 2; // Bit 12-13: Pin 11 mode __IOM uint32_t CNF11 : 2; // Bit 14-15: Pin 11 configuration __IOM uint32_t MODE12 : 2; // Bit 16-17: Pin 12 mode __IOM uint32_t CNF12 : 2; // Bit 18-19: Pin 12 configuration __IOM uint32_t MODE13 : 2; // Bit 20-21: Pin 13 mode __IOM uint32_t CNF13 : 2; // Bit 22-23: Pin 13 configuration __IOM uint32_t MODE14 : 2; // Bit 24-25: Pin 14 mode __IOM uint32_t CNF14 : 2; // Bit 26-27: Pin 14 configuration __IOM uint32_t MODE15 : 2; // Bit 28-29: Pin 15 mode __IOM uint32_t CNF15 : 2; // Bit 30-31: Pin 15 configuration }; __IOM uint32_t reg; // Access entire register } GPIO_CRH_Type; |
Thanh ghi port output data register (GPIOx_ODR) (x=A..G)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // Thanh ghi Dữ liệu Output GPIO (GPIO Output Data Register) typedef union { struct { // Structure for bit fields __IOM uint32_t ODR0 : 1; // Bit 0: Output data for pin 0 __IOM uint32_t ODR1 : 1; // Bit 1: Output data for pin 1 __IOM uint32_t ODR2 : 1; // Bit 2: Output data for pin 2 __IOM uint32_t ODR3 : 1; // Bit 3: Output data for pin 3 __IOM uint32_t ODR4 : 1; // Bit 4: Output data for pin 4 __IOM uint32_t ODR5 : 1; // Bit 5: Output data for pin 5 __IOM uint32_t ODR6 : 1; // Bit 6: Output data for pin 6 __IOM uint32_t ODR7 : 1; // Bit 7: Output data for pin 7 __IOM uint32_t ODR8 : 1; // Bit 8: Output data for pin 8 __IOM uint32_t ODR9 : 1; // Bit 9: Output data for pin 9 __IOM uint32_t ODR10 : 1; // Bit 10: Output data for pin 10 __IOM uint32_t ODR11 : 1; // Bit 11: Output data for pin 11 __IOM uint32_t ODR12 : 1; // Bit 12: Output data for pin 12 __IOM uint32_t ODR13 : 1; // Bit 13: Output data for pin 13 __IOM uint32_t ODR14 : 1; // Bit 14: Output data for pin 14 __IOM uint32_t ODR15 : 1; // Bit 15: Output data for pin 15 uint32_t reserved : 16; // Bit 16-31: Reserved }; __IOM uint32_t reg; // Access entire register } GPIO_ODR_Type; |
Thanh ghi port bit set/reset register (GPIOx_BSRR) (x=A..G)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | // Thanh ghi Set/Reset Bit GPIO (GPIO Bit Set/Reset Register) typedef union { struct { // Structure for bit fields __OM uint32_t BS0 : 1; // Bit 0: Set pin 0 __OM uint32_t BS1 : 1; // Bit 1: Set pin 1 __OM uint32_t BS2 : 1; // Bit 2: Set pin 2 __OM uint32_t BS3 : 1; // Bit 3: Set pin 3 __OM uint32_t BS4 : 1; // Bit 4: Set pin 4 __OM uint32_t BS5 : 1; // Bit 5: Set pin 5 __OM uint32_t BS6 : 1; // Bit 6: Set pin 6 __OM uint32_t BS7 : 1; // Bit 7: Set pin 7 __OM uint32_t BS8 : 1; // Bit 8: Set pin 8 __OM uint32_t BS9 : 1; // Bit 9: Set pin 9 __OM uint32_t BS10 : 1; // Bit 10: Set pin 10 __OM uint32_t BS11 : 1; // Bit 11: Set pin 11 __OM uint32_t BS12 : 1; // Bit 12: Set pin 12 __OM uint32_t BS13 : 1; // Bit 13: Set pin 13 __OM uint32_t BS14 : 1; // Bit 14: Set pin 14 __OM uint32_t BS15 : 1; // Bit 15: Set pin 15 __OM uint32_t BR0 : 1; // Bit 16: Reset pin 0 __OM uint32_t BR1 : 1; // Bit 17: Reset pin 1 __OM uint32_t BR2 : 1; // Bit 18: Reset pin 2 __OM uint32_t BR3 : 1; // Bit 19: Reset pin 3 __OM uint32_t BR4 : 1; // Bit 20: Reset pin 4 __OM uint32_t BR5 : 1; // Bit 21: Reset pin 5 __OM uint32_t BR6 : 1; // Bit 22: Reset pin 6 __OM uint32_t BR7 : 1; // Bit 23: Reset pin 7 __OM uint32_t BR8 : 1; // Bit 24: Reset pin 8 __OM uint32_t BR9 : 1; // Bit 25: Reset pin 9 __OM uint32_t BR10 : 1; // Bit 26: Reset pin 10 __OM uint32_t BR11 : 1; // Bit 27: Reset pin 11 __OM uint32_t BR12 : 1; // Bit 28: Reset pin 12 __OM uint32_t BR13 : 1; // Bit 29: Reset pin 13 __OM uint32_t BR14 : 1; // Bit 30: Reset pin 14 __OM uint32_t BR15 : 1; // Bit 31: Reset pin 15 }; __IOM uint32_t reg; // Access entire register } GPIO_BSRR_Type; |
Những thanh ghi IDR, BRR, LCKR chưa được nghĩa struct. Bạn hãy định nghĩa nó.
Sơ đồ map thanh ghi GPIO

Define struct GPIO
1 2 3 4 5 6 7 8 9 10 | // Bản đồ thanh ghi GPIO (9.5 GPIO and AFIO register maps- Trang 194) typedef struct { GPIO_CRL_Type CRL; // 0x00: Cấu hình thấp GPIO_CRH_Type CRH; // 0x04: Cấu hình cao uint32_t IDR; // 0x08: Dữ liệu input GPIO_ODR_Type ODR; // 0x0C: Dữ liệu output GPIO_BSRR_Type BSRR; // 0x10: Set/Reset bit uint32_t BRR; // 0x14: Reset bit uint32_t LCKR; // 0x18: Khóa cấu hình } GPIO_TypeDef; |
Define địa chỉ cho GPIOA, GPIOB, GPIOC
1 2 3 | #define GPIOA ((GPIO_TypeDef *)0x40010800UL) #define GPIOB ((GPIO_TypeDef *)0x40010C00UL) #define GPIOC ((GPIO_TypeDef *)0x40011000UL) |
Ý nghĩa thanh ghi GPIO
- CRL/CRH: Cấu hình chế độ (MODE: 0=input, 1-3=output với tốc độ khác nhau) và cấu hình (CNF: 0=push-pull, 2=input pull-up/down; Section 9.2.1/9.2.2, trang 171–172).
- ODR: Đặt trạng thái output (Section 9.2.4, trang 173). Các bit có thể đọc/viết, với set/reset nguyên tử qua BSRR.
- BSRR: Set/reset bit một cách nguyên tử (Section 9.2.5, trang 173), tránh vấn đề read-modify-write trong môi trường đa luồng.
Cấu hình GPIO Output STM32F103RCT6 và lập trình LED bật/tắt với delay không chính xác
Thanh ghi GPIOx_CRL

Thanh ghi GPIOx_CRL dùng để cấu hình chân 0 đến chân 7 của các port từ A đến G.
Mỗi chân được cấu hình bởi hai trường bit:
- MODE[1:0]
- CNF[1:0]
Ví dụ: Cấu hình PC1 làm input pull-up
1 2 3 4 | // MODE = 00 (input), CNF = 10 (input pull-up/pull-down) GPIOC->CRL &= ~(0xF << (1 * 4)); // Xóa 4 bit cấu hình của PC1 GPIOC->CRL |= (0x8 << (1 * 4)); // 1000b = CNF=10, MODE=00 GPIOC->ODR |= (1 << 1); // Kéo lên (pull-up) |
Thanh ghi GPIOx_CRH

Thanh ghi GPIOx_CRH dùng để cấu hình chân 8 đến chân 15 của các port từ A đến G.
Mỗi chân cũng được cấu hình bởi:
- MODE[1:0]
- CNF[1:0]
Ví dụ: Cấu hình PA8 làm output push-pull, tốc độ 2 MHz
1 2 3 | // MODE = 10 (2 MHz output), CNF = 00 (push-pull) GPIOA->CRH &= ~(0xF << ((8 - 8) * 4)); // Xóa 4 bit cấu hình của PA8 (bit đầu CRH) GPIOA->CRH |= (0x2 << ((8 - 8) * 4)); // 0010b = MODE=10, CNF=00 |
Bảng ý nghĩa MODE và CNF
Chế độ Input
| MODEy | CNFy | Ý nghĩa |
|---|---|---|
| 00 | 00 | Analog input: Chân được dùng làm ngõ vào tương tự (analog input) – thường dùng cho ADC, DAC. Không có điện trở kéo lên/kéo xuống. |
| 00 | 01 | Floating input (mặc định sau reset): Chân ở chế độ ngõ vào số nhưng không có điện trở kéo lên/kéo xuống. Trạng thái chân có thể dao động không ổn định nếu để hở. |
| 00 | 10 | Input pull-up / pull-down: Khi ở chế độ này, bạn có thể cấu hình điện trở nội kéo lên hoặc kéo xuống. Bit trong ODR sẽ quyết định kéo lên (ODR=1) hay kéo xuống (ODR=0). |
| 00 | 11 | Reserved: Không sử dụng |
Ở chế độ pull-up / pull-down, bit ODR quyết định kéo lên (ODR=1) hay kéo xuống (ODR=0).
Chế độ Output – MODE
| MODE | Ý nghĩa |
|---|---|
| 01 | Output 10 MHz: Dùng cho tín hiệu chậm. |
| 10 | Output 2 MHz: Dùng cho ứng dụng không cần tốc độ cao, tiết kiệm điện. |
| 11 | Output 50 MHz: Dùng cho tín hiệu nhanh (SPI, PWM, UART…). |
Chế độ Output – CNF
| CNF | Ý nghĩa |
|---|---|
| 00 | General purpose output push-pull: Chân output có thể kéo lên 3.3V hoặc kéo xuống GND. Đây là kiểu phổ biến nhất khi điều khiển LED, relay, IC logic. |
| 01 | General purpose output open-drain: Chân chỉ có thể kéo xuống GND, khi thả ra thì ở trạng thái hở (high-Z). Cần điện trở kéo lên bên ngoài. |
| 10 | Alternate function output push-pull: Dùng khi chân đảm nhận chức năng ngoại vi (ví dụ UART TX, SPI SCK, PWM…) |
| 11 | Alternate function output open-drain: Dùng cho ngoại vi nhưng chỉ kéo xuống GND, không kéo lên (ví dụ I²C). |
Hướng dẫn cấu hình GPIO Output STM32F103RCT6 (PA8)
- Cấu hình PA8 (LED0) làm output
- Bật/tắt LED bằng thanh ghi
- Tạo delay bằng vòng lặp (không chính xác ~1s)
Cấu hình GPIO Output
- Bật clock GPIOA:
RCC->APB2ENR.IOPAEN = 1 - PA8:
- MODE8 = 2 (Output 2 MHz)
- CNF8 = 0 (Push-pull, Section 9.2.2, trang 172)
Code cấu hình GPIO PA8 Output
Hàm delay (không chính xác)
Cấu hình GPIO cho LED
Điều khiển LED bằng BSRR
Hàm main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int main(void) { SystemClock_Config(); // Initialize system clock GPIO_LED_Config(); while (1) { // Toggle PA8 LED_On(); // LED_Off_LED1(); Delay_Approx(1000); // ~1s delay LED_Off(); // LED_On_LED1(); Delay_Approx(1000); } } |
Github link: Download code cấu hình GPIO Output Bật Tắt LED0 (PA08)
Giải thích
- BSRR cho phép set/reset (Section 9.2.5, trang 173) bit GPIO atomic, an toàn hơn so với ghi trực tiếp ODR
- Delay bằng vòng lặp không chính xác, phụ thuộc:
- Tần số hệ thống
- Compiler optimization
Tham chiếu
- RM0008 – Reference Manual
- Section 9.2 GPIO (trang 171–175)
- Section 9.2.5 BSRR Register
Kết luận
Qua bài viết này, bạn đã nắm được cách lập trình STM32 thanh ghi để cấu hình output STM32F103RCT6, từ việc sử dụng các thanh ghi GPIOx_CRL, GPIOx_CRH cho đến điều khiển chân GPIO bằng BSRR. Việc cấu hình đúng MODE và CNF giúp chân GPIO hoạt động chính xác theo yêu cầu, đặc biệt trong các ứng dụng điều khiển LED.
Bên cạnh đó, ví dụ bật/tắt LED với hàm delay vòng lặp cho thấy hạn chế của phương pháp delay không chính xác, do phụ thuộc vào tần số hệ thống và tối ưu của compiler. Đây là điểm cần lưu ý khi lập trình STM32 bằng thanh ghi, nhất là trong các ứng dụng yêu cầu thời gian chính xác.
Tóm lại, việc hiểu rõ cách cấu hình output STM32F103RCT6 ở mức thanh ghi là nền tảng quan trọng, giúp bạn chủ động hơn trong việc tối ưu hiệu năng, kiểm soát phần cứng và phát triển các ứng dụng nhúng trên STM32 một cách hiệu quả và ổn định.
