🌱 STM32 - 6. Lập trình Thanh Ghi Ngoại vi GPIO STM32 - Blinked LED

🌱 STM32 - 6. Lập trình Thanh Ghi Ngoại vi GPIO STM32 - Blinked LED

    Ví dụ kinh điển khi thực hành GPIO ở bất cứ dòng vi điều khiển nào đó là nút bấm điều khiển LED, giúp người học hiểu về các cấu hình Input và Output trong một vi điều khiển. Với STM32, chúng ta còn 2 chế độ: Alternate Function sẽ được giới thiệu trong các ngoại vi khác, Analog sẽ được giới thiệu trong bài về ADC. Vì vậy, bài viết này mình sẽ lấy ví dụ với User Button (GPIOC.13)User LED (GPIOA.5) trên Board NUCLEO STM32F401.

STM32F401 NUCLEO Board

Hình 1: Board STM32F401 NUCLEO với các chân GPIO

Điều kiện cần thiết

    Trước khi cấu hình cho ngoại vi GPIO, cần định nghĩa các thanh ghi và quan trọng nhất là cấu hình cấp xung Clock cho ngoại vi, ở đây là GPIOA và GPIOC. Cấu hình này sử dụng thanh ghi AHB1_ENR. (Mình mặc định dùng nguồn cấp clock là HSI). Để học tốt bài này, các bạn cần nắm được kỹ thuật Bitwise.

👉 Define địa chỉ các thanh ghi cần thiết

#define RCC_BASE        0x40023800
#define GPIOA_BASE      0x40020000
#define GPIOC_BASE      0x40020800

#define RCC_AHB1ENR     (*(volatile unsigned int *)(RCC_BASE + 0x30))
#define GPIOA_MODER     (*(volatile unsigned int *)(GPIOA_BASE + 0x00))
#define GPIOA_OTYPER    (*(volatile unsigned int *)(GPIOA_BASE + 0x04))
#define GPIOA_OSPEEDR   (*(volatile unsigned int *)(GPIOA_BASE + 0x08))
#define GPIOA_ODR       (*(volatile unsigned int *)(GPIOA_BASE + 0x14))
#define GPIOC_MODER     (*(volatile unsigned int *)(GPIOC_BASE + 0x00))
#define GPIOC_PUPDR     (*(volatile unsigned int *)(GPIOC_BASE + 0x0C))
#define GPIOC_IDR       (*(volatile unsigned int *)(GPIOC_BASE + 0x10))

👉 Tiếp theo, cấu hình các thanh ghi. Các bạn có nhiều cách, nhưng phổ biến nhất là sử dụng Struct để quản lý. Dưới đây là Struct cho thanh ghi RCC, các bạn cũng có thể làm tương tự với GPIOA và GPIOC (Tự làm để hiểu thêm nhé 😂).

typedef struct {
    volatile unsigned int CR;         // 0x00
    volatile unsigned int PLLCFGR;    // 0x04
    volatile unsigned int CFGR;       // 0x08
    volatile unsigned int CIR;        // 0x0C
    volatile unsigned int AHB1RSTR;   // 0x10
    volatile unsigned int AHB2RSTR;   // 0x14
    volatile unsigned int reserved0[2];
    volatile unsigned int APB1RSTR;   // 0x20
    volatile unsigned int APB2RSTR;   // 0x24
    volatile unsigned int reserved1[2];
    volatile unsigned int AHB1ENR;    // 0x30
    volatile unsigned int AHB2ENR;    // 0x34
} RCC_t;

#define RCC ((RCC_t *)RCC_BASE)

👉 Cấu hình Clock cho GPIOA và GPIOC

RCC_AHB1ENR |= (1 << 0); // Enable clock for GPIOA
RCC_AHB1ENR |= (1 << 2); // Enable clock for GPIOC

Tham khảo Reference Manual - thanh ghi AHB1_ENR để cấu hình cấp clock cho 2 ngoại vi này.

Bit 0: GPIOAEN - GPIOA clock enable
Bit 2: GPIOCEN - GPIOC clock enable

👉 Cấu hình User LED (GPIOA.5) = OUTPUT

    Để cấu hình được GPIO ở 2 chế độ INPUT và OUTPUT, các bạn cần xem lại bài STM32 - 5. GPIO RegistersSTM32 - 4. GPIO Overview để nắm được các thanh ghi và chế độ của ngoại vi GPIO trong STM32.

    Với việc điều khiển LED, ta thường sử dụng mode Push-Pull. Với chế độ Output, cần cài đặt thêm tốc độ bằng thanh ghi OSPEEDR, ở đây chọn Medium Speed.

GPIOA_MODER &= ~(3 << 10);   // Clear bits 10, 11
GPIOA_MODER |= (1 << 10);    // Set PA5 as Output (01)
GPIOA_OTYPER &= ~(1 << 5);   // PA5 as Push-Pull (0)
GPIOA_OSPEEDR &= ~(3 << 10); // Clear bits 10, 11
GPIOA_OSPEEDR |= (1 << 10);  // Medium Speed (01)

👉 Cấu hình User Button (GPIOC.13) = INPUT-PULLUP

    Các bit của thanh ghi GPIOx_MODER mặc định là 0 nên GPIOC mặc định ở chế độ Input. Để cấu hình Pull-up cho chân PC13, ta cần tác động vào bit[26]bit[27] của thanh ghi GPIOx_PUPDR.

GPIOC_PUPDR &= ~(3 << 26); // Clear bits 26, 27
GPIOC_PUPDR |= (1 << 26);  // Set Pull-up for PC13 (01)

👉 Chương trình chính "Bấm nút đảo LED"

    Để kiểm tra nút bấm, chúng ta kiểm tra bit thứ 13 trên thanh ghi GPIOx_IDR. Nếu bit này bằng 0 tức là nút bấm được nhấn. Để điều khiển LED, chúng ta sử dụng bit thứ 5 trên thanh ghi GPIOx_ODR. Ở đây, để đảo LED, chúng ta dùng phép XOR.

int main(void)
{
    // Cấu hình Clock, LED, Button ở đây (như trên)

    while (1)
    {
        if (!(GPIOC_IDR & (1 << 13)))
        {
            // Nếu nút bấm được nhấn (PC13 = 0)
            GPIOA_ODR ^= (1 << 5);      // Đảo trạng thái LED (PA5)
            while (!(GPIOC_IDR & (1 << 13))); // Chờ thả nút
        }
    }
}

User LED trên board STM32F401 NUCLEO
User LED trên board STM32F401 NUCLEO

    Trên đây là ví dụ cơ bản về LED và Button để các bạn nắm được nguyên lý Input/Output cơ bản của ngoại vi GPIO. Các bạn nên luyện tập thêm để hiểu rõ hơn về chúng.

>>>>>> Follow ngay <<<<<<

Để nhận được những bài học miễn phí mới nhất nhé 😊
Chúc các bạn học tập tốt 😊

6 Nhận xét

  1. Nguyễn Đức Hảilúc 21:24 6 tháng 9, 2021

    Tuyệt vời quá anh ơi.Chi tiết quá

    Trả lờiXóa
  2. hay quá anh, code từ tầng dưới này code lên mới ngộ ra nhiều thứ anh ạ!

    Trả lờiXóa
  3. Giá như biết blog này của anh sớm hơn

    Trả lờiXóa
Mới hơn Cũ hơn
//