Skip to content

14 Bật Đèn Bằng Ngắt Ngoài Từ Nút Nhấn

Quy Trình Cấu Hình

Khi sử dụng chức năng ngắt ngoài (External Interrupt) của GPIO, ta thường thực hiện theo các bước sau:

  1. Bật clock
  2. Cấu hình chế độ GPIO
  3. Kích hoạt ngắt tại NVIC và cấu hình mức ưu tiên
  4. Cấu hình kết nối EXTI với chân GPIO
  5. Bật EXTI và xóa cờ ngắt
  6. Viết hàm xử lý ngắt (ISR)

Trong STM32, bất kỳ chân nào cũng có thể được cấu hình làm nguồn kích ngắt ngoài. Trên bo mạch phát triển, nút nhấn KEY_UP được kết nối với chân PA0, vì vậy ta có thể sử dụng ngắt ngoài trên PA0 để điều khiển đèn.

Bật Clock

Bật clock cho GPIOA:

c
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

Bật clock cho SYSCFG (cấu hình EXTI):

c
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

Cấu Hình Chế Độ GPIO

Cấu hình chân nút nhấn:

c
GPIO_InitTypeDef GPIO_InitStructure;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;

GPIO_Init(GPIOA, &GPIO_InitStructure);

Giải thích từng dòng:

  1. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; → Cấu hình chân số 0 (pin 0) là chân cần sử dụng.
  2. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; → Thiết lập chế độ hoạt động của chân là Input (nhập dữ liệu).
  3. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; → Cấu hình điện trở kéo xuống (pull-down) để đảm bảo rằng khi không nhấn nút, chân sẽ ở mức logic thấp (LOW), tránh trạng thái treo.
  4. GPIO_Init(GPIOA, &GPIO_InitStructure); → Áp dụng cấu hình trên cho chân PA0 – chính là chân kết nối với nút KEY_UP trên bo mạch.

Cấu hình ngắt GPIO

c
NVIC_InitTypeDef   NVIC_InitStructure;
EXTI_InitTypeDef   EXTI_InitStructure;

SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);

EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);

NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

Chức năng tương ứng của từng dòng ngắt (EXTI line) được thể hiện trong hình dưới đây.

img

Viết Hàm Xử Lý Ngắt (ISR)

Sau khi đã kích hoạt ngắt, nếu có sự kiện xảy ra (ví dụ nút nhấn), chương trình sẽ tự động nhảy đến hàm xử lý ngắt tương ứng để thực hiện.

Để xử lý đúng, ta cần viết hàm xử lý ngắt theo đúng tên quy định. Tên hàm này được định nghĩa sẵn trong file khởi động startup_stm32f40xx.s — là bảng vector ngắt của chip.

Dưới đây là ví dụ trích đoạn từ file startup, thể hiện một số tên hàm ngắt:

img

Trong hàm xử lý ngắt, ta cần kiểm tra xem cờ ngắt của dòng EXTI có được kích hoạt hay không.

ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);

Đây là hàm dùng để kiểm tra cờ ngắt.

  • Tham số truyền vào là số dòng EXTI (ví dụ EXTI_Line0)
  • Hàm trả về một giá trị kiểu FlagStatus với 2 trạng thái:
    • SET: Cờ ngắt đang được kích hoạt (đã xảy ra ngắt)
    • RESET: Không có ngắt

Lưu ý: Sau khi xử lý xong trong ISR, bắt buộc phải xóa cờ ngắt, nếu không thì ngắt sẽ liên tục được kích hoạt lại.

Hàm xử lý ngắt ví dụ (cho EXTI0 / chân PA0):

c
void EXTI0_IRQHandler(void)
{
    // Kiểm tra xem EXTI Line0 có cờ ngắt hay không
    if (EXTI_GetITStatus(EXTI_Line0) == SET)
    {
        // Đảo trạng thái đèn LED
        if (flag)
        {
            GPIO_SetBits(GPIOB, GPIO_Pin_2);
            flag = 0;
        }
        else
        {
            GPIO_ResetBits(GPIOB, GPIO_Pin_2);
            flag = 1;
        }

        printf("Interrupt Triggered!\r\n");

        // Xóa cờ ngắt EXTI Line0 để chờ lần ngắt tiếp theo
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}

Hiện Tượng Thí Nghiệm

Mã nguồn của chương này nằm trong:

https://github.com/leybme/stm32f407_resource/tree/main/C03%20Code/Code%20Sample/007LED%20control%20with%20button%20using%20external%20interrupt

Sau khi nạp chương trình vào bo mạch:

  • Nhấn rồi thả nút → LED sẽ bật
  • Nhấn rồi thả lần nữa → LED sẽ tắt
  • Cứ như vậy, mỗi lần nhấn và thả nút, LED sẽ đổi trạng thái bật/tắt luân phiên.

Đồng thời, mỗi lần nhấn và thả nút sẽ in ra chuỗi "Key!!" qua UART, như hình minh họa.

img