Bài 4: Lập trình Ngắt Ngoài STM32 EXTI

Ngắt ngoài STM32 EXTI

Bài này chúng ta sẽ nói về lập trình ngắt ngoài STM32 (EXTI): ngắt là gì, cách hoạt động của ngắt, ngắt ngoài là gì và cách lập trình ngắt ngoài trên STM32.

Bài 4 trong Serie Học lập trình STM32 từ A tới Z

Ngắt (Interrupts) là gì?

Hãy tưởng tượng MCU chính là bạn. Sau khi làm các công việc khởi tạo hàng ngày như thức dậy, ăn sáng, vệ sinh cá nhân… Đó là các hàm được viết ở đầu main().

Bạn đi đến công ty và làm các công việc trong while(1), cứ như vậy lặp đi lặp lại. Quá trình này trong lập trình gọi là Polling, việc nào đến trước làm trước, đến sau làm sau và không có sự ưu tiên việc nào.
Trong quá trình làm việc, bỗng bạn nhận được nhiệm vụ từ sếp là “Đi gặp khách hàng”. Công việc này phải ưu tiên hoàn thành trước, làm xong rồi mới quay lại làm tiếp công việc thường ngày.

Sự kiện này trong lập trình gọi là Ngắt (Interrupts).

Trong lập trình hiện nay, hiếm có chương trình nào không sử dụng ngắt, vì có những dữ liệu phải được xử lý tức thì, nếu không sẽ dẫn đến sai hoặc mất dữ liệu.

Các ngắt đều có 1 trị số priority (mức ưu tiên) cho phép người lập trình chọn xử lý ngắt nào trước nếu xảy ra ngắt chồng nhau.
Lấy ví dụ: trong khi bạn đang gặp khách hàng lại nhận được tin “vợ đẻ”. Nếu sự kiện “vợ đẻ” có priority cao hơn “Đi gặp khách hàng” thì chắc chắn bạn sẽ đi đón vợ đẻ trước, sau đó mới đi gặp khách hàng. Và tất nhiên việc gặp khách hàng sẽ chậm hơn 1 chút, đúng không nào.

Trong STM32, việc quản lý các ngắt được điều khiển bằng bộ NVIC, tạm dịch là bộ điều khiển ngắt lồng nhau.

Bộ điều khiển ngắt lồng nhau NVIC trong STM32

Thông số của NVIC như sau:

  • Bao gồm 68 ngắt (chưa gồm 16 ngắt hệ thống)
  • Lập trình được 16 mức ưu tiên ngắt (sử dụng 4 bit)
  • Độ trễ thấp
  • Quản lý năng lượng tiêu hao

Các vector ngắt được trình bày trong phần 10.1.2 của Reference Manual.

H1 1

Ngắt ngoài STM32 EXTI

EXTI (External Interrupts) tạm dịch là ngắt ngoài hay ngắt sự kiện bên ngoài. Ngắt EXTI được kích hoạt khi có sự kiện từ bên ngoài tác động vào chân EXTI đó. Sự kiện đó phải phù hợp với điều kiện ngắt đã cấu hình thì ngắt ngoài mới xảy ra.

Ngắt ngoài STM32 được mô tả như sau:

Các tham số (Main Features):

  • Kích hoạt độc lập trên mỗi dòng ngắt (Line Interrupts)
  • Truy cập đến từng bit trong mỗi dòng ngắt
  • Tạo ra tối đa 20 sự kiện/ngắt
  • Phát hiện được cả những xung có độ rộng nhỏ hơn chu kì xung nhịp của APB2 (APB2 là bus cấp xung cho GPIO)

Các bạn tham khảo thêm phần Block diagram ở mục 10.2.1 và 10.2.2.

H2 2

Các Line ngắt ngoài được tổ chức như sau:

H3 1

Các chân PA0, PB0,…, PG0 dùng chung line EXTI0. Tiếp tục như vậy đến EXTI15, chúng ta có 16 line ngắt cho GPIO.

4 line ngắt ngoài đặc biệt là:

  • EXTI line 16: dùng cho PVD output
  • EXTI line 17: dùng cho sự kiện báo thức (Alarm) của bộ RTC
  • EXTI line 18: dùng cho sự kiện thức dậy của USB
  • EXTI line 19: dùng cho sự kiện thức dậy của ngoại vi Ethernet

Cấu hình Ngắt ngoài STM32 trên CubeMX

Trong bài này chúng ta sẽ cấu hình 3 nút nhấn ở các chân PA0, PA1, PB3, sau đó phân biệt các sự kiện đó trong ngắt rồi điều khiển led:

PA0: Led sáng

PA1: Led tắt

PB3: Led nhấp nháy

Các bạn mở CubeMX lên, chọn chip STM32F103C8 và tạo project.

Nếu các bạn chưa biết tạo thì đọc lại Bài 3 nhé.

Trong SYS chọn Debug: Serial Wire, RCC giữ nguyên nhé.

Lưu ý: Chân PB3 mặc định là chân JTDO của chuẩn nạp JTAG. Nếu để Debug là No Debug hoặc JTAG thì PB3 không dùng làm GPIO được, nhấn nút sẽ không có ngắt. Chọn Serial Wire thì PB3, PB4, PA15 mới được giải phóng.

Trong GPIO chọn các chân PA0, PA1, PB3 là GPIO_EXTI.

H4

Trong mục Configuration có các lựa chọn sau:

GPIO mode:

  • External Interrupt Mode Rising edge: ngắt ngoài chế độ phát hiện sườn lên
  • External Interrupt Mode Falling edge: ngắt ngoài chế độ phát hiện sườn xuống
  • External Interrupt Mode Rising/Falling: ngắt cả sườn lên và sườn xuống
  • External Event Mode Rising edge: sự kiện ngoài phát hiện sườn lên
  • External Event Mode Falling edge: sự kiện ngoài phát hiện sườn xuống
  • External Event Mode Rising/Falling: sự kiện cả sườn lên và sườn xuống

Trong bài này chúng ta sẽ chọn chế độ ngắt phát hiện sườn xuống: External Interrupt Mode Falling edge.

GPIO Pull: có dùng trở kéo lên hoặc kéo xuống không. Ta chọn trở kéo lên Pull-up. Như vậy khi chưa nhấn, chân ở mức 1, khi nhấn nút chân bị kéo về GND tạo ra một sườn xuống.

User Label không cần sử dụng.

H5 1

Trong tab NVIC, tick vào các ô Enable để bật các ngắt đó lên.

H6 1

Chọn PC13 là Output. Sau đó chuyển sang tab Project Manager đặt tên và Gen code.

H7 2

Cấu hình Ngắt ngoài STM32 trên Keil C

Trong Keil C các bạn nhấn F7 để IDE build chương trình mới tạo ra.

Mở file stm32f1xx_it.c sẽ thấy 3 hàm phục vụ ngắt được sinh ra, đó là:

EXTI0_IRQHandler, EXTI1_IRQHandler, EXTI3_IRQHandler

Các bạn nhấn chuột phải vào tên hàm HAL được gọi bên trong và chọn Go To Definition để tới nơi viết chương trình phục vụ ngắt đó.

H8 1

Trong hàm phục vụ ngắt sẽ có 1 câu lệnh kiểm tra cờ ngắt của chân GPIO_Pin truyền vào.

Nếu đã xảy ra ngắt, hàm sẽ xóa cờ ngắt để sẵn sàng cho lần ngắt tiếp theo.

Sau đó gọi tới hàm Callback.

H9 1

Các bạn chuyển qua tab Functions, chọn file stm32f1xx_hal_gpio.c và tìm đến hàm:

HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)

Copy cả hàm đó và bỏ từ khóa __weak đi.

H10 1

Paste vào phía trên hàm main() (trong khoảng USER CODE BEGIN 0) và viết code vào trong hàm này như sau:

H11 1

Giải thích: vì mọi ngắt ngoài tại các Line khác nhau đều gọi tới cùng một hàm Callback, chỉ khác tham số truyền vào là chân nào. Vậy nên chúng ta so sánh chân truyền vào với các chân tương ứng Line 0, 1, 3 để xác định ngắt xảy ra ở đâu.

Sau đó gọi hàm bật tắt led theo đúng yêu cầu bên trên.

Tiếp đó build bằng F7 và nạp code vào mạch bằng F8.

Nối 3 nút nhấn vào 3 chân PA0, PA1, PB3, đầu còn lại của nút nối xuống GND như sơ đồ sau:

Sơ đồ đấu nối ngắt ngoài STM32 EXTI: 3 nút nhấn nối PA0, PA1, PB3 xuống GND trên Blue Pill, LED PC13 có sẵn trên board

Sau đó nhấn từng nút, bạn sẽ thấy led thay đổi tương ứng với ngắt tại chân đó.

Lưu ý: Mỗi Line chỉ sử dụng được 1 chân. Ví dụ Line 0 đã dùng chân PA0 thì PB0 sẽ không thể sử dụng làm chân ngắt nữa.

Lưu ý: Nút nhấn cơ khí bị rung (bounce) khi nhấn, nên 1 lần nhấn có thể sinh ra vài lần ngắt liên tiếp. Với chế độ Led nhấp nháy các bạn sẽ dễ thấy hiện tượng này, muốn chắc chắn thì nên thêm tụ chống rung 100nF song song nút nhấn hoặc lọc thời gian giữa 2 lần ngắt trong Callback.

Kết

Ngắt ngoài STM32 được sử dụng rất rộng rãi, nhất là trong các ứng dụng Low Power. Khi đó MCU sẽ rơi vào trạng thái Sleep, khi có tác động từ bên ngoài, ngắt ngoài sẽ xảy ra và đánh thức MCU hoạt động trở lại. Hẹn gặp lại các bạn trong bài tiếp theo nhé!

 

5/5 - (6 bình chọn)

16 những suy nghĩ trên “Bài 4: Lập trình Ngắt Ngoài STM32 EXTI”

  1. Đức nói:

    Em làm theo anh nhưng khi mô phỏng hàm Hal_Delay(100) trong ngắt gây ra lỗi treo chương trình. Nếu bỏ hàm delay thì chương trình chạy được. Em đang làm sai ở đâu phải không a?

  2. Đức nói:

    Em làm theo hướng dẫn ở trên, nhưng khi có ngắt line 3 thì chương trình nhảy vào ngắt và không thoát ra được. Khi bỏ hết hàm delay trong chương trình ngắt line 3 thì chương trình chạy bình thường a

  3. Thanh Hoàng nói:

    em làm đúng như anh hướng dẫn nhưng khi Buil thì báo lỗi “of undeclared identifier LED_GPIO_Port” là sao ạ???

    • Huy nói:

      Anh ơi cho em hỏi trong hàm xử lý ngắt ngoài khi hàm xử lý đấy chưa thực hiện xong chương trình ngắt của nó thì em nhấm thêm nút nhấn một lần nữa thì có hiện tượng gì xảy ra ạ.Cái event nhấn nút thứ 2 sẽ chuyển vào pending hay sẽ bị hủy ạ. Mong a giải đáp giúp em ạ

  4. Huy nói:

    Anh ơi cho em hỏi trong hàm xử lý ngắt ngoài khi hàm xử lý đấy chưa thực hiện xong chương trình ngắt của nó thì em nhấm thêm nút nhấn một lần nữa thì có hiện tượng gì xảy ra ạ.Cái event nhấn nút thứ 2 sẽ chuyển vào pending hay sẽ bị hủy ạ. Mong a giải đáp giúp em ạ

  5. nguyên nói:

    Sinh viên khai báo thêm 2 button sử dụng
    ngắt ngoài. Button 1 khi bấm sẽ thực hiện ngắt và đếm lên, button 2 khi bấm sẽ thực hiện
    đếm xuống. Giá trị hiển thị trên led từ 0-99..anh giải code giúp em với ạ!!!1

  6. Đức nói:

    Trong hàm ngắt ngoài chúng ta không nên sử dụng hàm hal_delay() vì khi đó chương trình sẽ bị treo trong vòng lặp vô hạn mn có thể tìm hiểu thêm tại:https://khuenguyencreator.com/hal_delay-voi-systick-va-delay-us-voi-timer-tren-stm32/
    để xử lý việc này chúng ta có thể có hai cách: 1 là xét mức độ ưu tiên cho ngắt ngoài(cách này không hiệu quả và khó quản lý ưu tiên ngắt) và 2 là có thể sử dụng một tín hiệu cho hàm ngắt sau đó lôi ra hàm while trong main để có thể tha hồ delay().
    uint8_t a = 0;
    void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
    {
     if(GPIO_Pin == GPIO_PIN_3)
    {
    a = 1;
    }
    }
    sang hàm while(1) trong main()
    while (1)
     {
      /* USER CODE END WHILE */

      /* USER CODE BEGIN 3 */
    if(a == 0)
    {
    HAL_GPIO_TogglePin(led1_GPIO_Port, led1_Pin);
    HAL_Delay(200);
    }else
    {
    a = 0;
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_RESET);
    for(int i = 0; i <= 9 ; i++)
    {
    HAL_GPIO_TogglePin(led2_GPIO_Port,led2_Pin);
    HAL_Delay(1000);
    }
    HAL_GPIO_WritePin (GPIOB, GPIO_PIN_5,GPIO_PIN_RESET);
    }
    }
    trên đây là một vài chia sẻ của e một người mới hk được cho mn đi sau. cảm ơn mn nếu có cách hay hơn mn hay share nhé!!

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *