Lập trình STM32 thanh ghi cấu hình GPIO Output STM32F103RCT6

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)

Thanh ghi port configuration register low (GPIOx_CRL) (x=A..G) cho lập trình STM32 thanh ghi cấu hình GPIO output STM32F103RCT6
Thanh ghi port configuration register low (GPIOx_CRL) (x=A..G)
// 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)

Thanh ghi port configuration register high (GPIOx_CRH) (x=A..G) cho lập trình STM32 thanh ghi cấu hình Output
Thanh ghi port configuration register high (GPIOx_CRH) (x=A..G)
// 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)

Thanh ghi port output data register (GPIOx_ODR) (x=A..G)
Thanh ghi port output data register (GPIOx_ODR) (x=A..G)
// 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)

Thanh ghi port bit set/reset register (GPIOx_BSRR) (x=A..G) cho hướng dẫn lập trình STM32 thanh ghi
Thanh ghi port bit set/reset register (GPIOx_BSRR) (x=A..G)
// 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

Địa chỉ GPIO port A, B, C, D, E, F, G STM32f103
Địa chỉ GPIO port A, B, C, D, E, F, G STM32f103

Define struct GPIO

// 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

#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 GPIO_CRL STM32F103 cho lập trình thanh ghi STM32
Thanh ghi GPIO_CRL STM32F103

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

// 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 GPIO_CRH cho lập trình STM32 thanh ghi cấu hình Output STM32F103RCT6
Thanh ghi GPIO_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

// 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

MODEyCNFyÝ nghĩa
0000Analog 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.
0001Floating 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ở.
0010Input 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).
0011Reserved: 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
01Output 10 MHz: Dùng cho tín hiệu chậm.
10Output 2 MHz: Dùng cho ứng dụng không cần tốc độ cao, tiết kiệm điện.
11Output 50 MHz: Dùng cho tín hiệu nhanh (SPI, PWM, UART…).

Chế độ Output – CNF

CNFÝ nghĩa
00General 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.
01General 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.
10Alternate 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…)
11Alternate 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)

void Delay_Approx(uint32_t ms) {
    // Approximate delay using loop, ~1us per iteration at 72MHz
    volatile uint32_t i;
    for (i = 0; i < ms * 1000; i++);
}

Trong bài viết hướng dẫn lập trình STM32 thanh ghi cấu hình GPIO Ouput, chúng ta sẽ dùng hàm Delay_Approx như một delay không chính xác. Những bài viết sau, tôi sẽ hướng dẫn bạn dùng timer, systick timer để viết hàm delay.

Cấu hình GPIO cho LED

void GPIO_LED_Config(void)
{
    // Enable GPIOA clock (RM0008, Section 7.3.7, page 113)
    RCC->APB2ENR.IOPAEN = 1;

    // Configure PA8 as output, push-pull, 2MHz
    GPIOA->CRH.MODE8 = 2;  // 2MHz output
    GPIOA->CRH.CNF8 = 0;   // General purpose push-pull
    GPIOA->ODR.ODR8 = 1;   // Set the initialize value as High (turn off LED)
    
    // Let try to config LED1 (PD02) as Output push pull, 2MHz
    // Enter your code here

}

Điều khiển LED bằng BSRR

void LED_On(void)
{
    // Set PA8 low to turn on LED (active low)
    GPIOA->BSRR.BR8 = 1;    
}

void LED_Off(void)
{
    // Set PA8 high to turn off LED
    GPIOA->BSRR.BS8 = 1;    
}

void LED_On_LED1(void)
{
    // Set PD2 low to turn on LED (active low)
    // Enter your code here
}

void LED_Off_LED1(void)
{
    // Set PD2 high to turn off LED
    // Enter your code here
}

Hàm main

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 MODECNF 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.

Viết một bình luận

This site uses Akismet to reduce spam. Learn how your comment data is processed.