Hiểu và lập trình ngắt ngoài STM32F411

Với phương pháp polling, mọi câu lệnh đều được viết trong hàm main() và while(1) và các khối lệnh đảm nhận một chức năng của một hệ thống sẽ được thực thi một cách tuần tự bằng cách kiểm tra các biến trạng thái, điều kiện. Phương pháp này phù hợp cho các ứng dụng đơn giản không có sự phân biệt về mức độ ưu tiên giữa các nhiệm vụ trong ứng dụng đó.
Với Interrupt, các chương trình phục vụ ngắt (ISR) sẽ được thực thi khi các sự kiện tương ứng đã được khai báo trước xảy ra, tốc độ đáp ứng nhanh hơn so với phương pháp polling, vi điều khiển sẽ được sử dụng một cách hiệu quả hơn.

[HỌC ONLINE: LẬP TRÌNH VI ĐIỀU KHIỂN STM32, VI XỬ LÝ ARM CORTEX – M]

Bài viết này sẽ cung cấp các bạn cơ sở lý thuyết về ngắt ngoài trên STM32F411 và hướng dẫn thực hành sử dụng phần mềm CubeMX và KeilC.

I. Các chức năng chính của khối External Interrupt
– Khối External Interrupt bao gồm 23 bộ phát hiện sườn để tạo ra yêu cầu ngắt, người sử dụng có thể
 cấu hình lựa chọn sườn ngắt (sườn lên, sườn xuống hoặc cả 2) để kích hoạt ngắt. Mỗi line ngắt đều có thể cấu hình cho phép tạo yêu cầu ngắt hoặc không một cách độc lập. Khi có yêu cầu ngắt thì người sử dụng có thể giám sát được trạng thái của line ngắt.

II. Hiểu về line ngắt
Vi điều khiển STM32F411VET6 trên board STM32F411DISCOVERY có 81 chân GPIO được chia thành 16 line ngắt ngoài theo sơ đồ bên dưới. Mỗi line ngắt là tập hợp các chân có cùng thứ tự trên các Port khác nhau. Ví dụ line 0 (EXTI0) là tập hợp các chân PA0, PB0, PC0, PD0, PE0 và PH0.


5 line ngắt ngoài còn lại được kết nối với các tính năng sau (Các bạn chưa cần quan tâm bây giờ)
– EXTI16 được kết nối với khối tính năng PVD ouput
– EXTI17 được kết nối với khối tính năng RTC Alarm event
– EXTI18 được kết nối với khối tính năng USB OTG FS Wakeup event
– EXTI21 được kết nối với khối tính năng RTC Tamper and TamStamp events
– EXTI22 được kết nối với khối tính năng RTC Wakeup event

III. Mô tả hoạt động của ngắt
– Để tạo ra ngắt, line ngắt cần được cấu hình chọn sườn ngắt thông qua hai thanh ghi Rising trigger selection (nếu muốn chọn sườn lên làm tín hiệu kích hoạt ngắt) và Falling trigger slection (nếu muốn chọn sườn xuống làm tín hiệu kích hoạt ngắt), sau đó bằng cách set bit tương ứng của line ngắt trong thanh ghi Interrupt Mask để cho phép tạo yêu cầu ngắt.
– Khi có sự thay đổi sườn tín hiệu tương ứng xảy ra trên line đã cấu hình ngắt thì một yêu cầu ngắt (Interrupt Request) được tạo ra. Lúc này bit tương ứng với line ngắt trên thanh ghi Pending Request được set lên 1 cho đến khi yêu cầu ngắt này được khối NVIC chấp nhận thực thi. (Có thể hiểu đơn giản: khối NVIC được thiết kế trong lõi ARM – Cortex M4 để thực hiện việc quản lý các yêu cầu ngắt và quá trình thực thi ngắt).

IV. Các thanh ghi cần quan tâm của khối EXTI
Mặc dù hiện tại bộ thư viện HAL giúp người dùng lập trình cho vi điều khiển STM32 mà không cần phải hiểu chi tiết phần cứng của vi điều khiển đến các thanh ghi, tuy nhiên trong 1 số trường hợp như debug hay can thiệp xuống lớp dưới của thư viện, tác động trực tiếp vào 1 số thanh ghi thì các bạn cần hiểu một số thanh ghi cơ bản sau trong khối EXTI.
– Thanh ghi chọn sườn xuống: FTSR có 32 bit, trong đó sử dụng 16 bit thấp để cấu hình chọn sườn xuống cho line ngắt tương ứng với các chân GPIO.

– Thanh ghi chọn sườn lên: RTSR có 32 bit, trong đó sử dụng 16 bit thấp để cấu hình chọn sườn lên cho line ngắt tương ứng với các chân GPIO.

Nếu cấu hình chọn cả sườn lên và sườn xuống cho 1 line thì yêu cầu ngắt sẽ được tạo ra trong cả 2 trường hợp xuất hiện sườn lên và sườn xuống.

Thanh ghi cho phép ngắt: IMR, bit tương ứng của thanh ghi được set lên 1 thì line ngắt tương ứng được cho phép sinh ra yêu cầu ngắt:

Thanh ghi chờ phục vụ ngắt – PR: Khi yêu cầu ngắt của 1 line được tạo ra, thì bit tương ứng trên thanh ghi Pending được set lên 1 cho đến khi yêu cầu ngắt được NVIC cho phép thực thi thì bit sẽ được clear về 0. Người dùng cũng có thể clear về 0 bằng cách thực hiện lệnh ghi 1 vào bit tương ứng của thanh ghi này:

V. Thực hành ngắt ngoài với nút nhấn.
Phần cuối của bài viết này là hướng dẫn các bạn thực hành lập trình ngắt ngoài sử dụng kit STM32F411DISCOVERY để phát hiện sườn lên của nút nhấn PA0, từ đó điều khiển đèn LED tại pin PD12. Phần mềm STM32CubeMX sẽ được sử dụng để thực hiện cấu hình và sinh code. Keil C IDE sẽ được sử dụng để lập trình hoàn thiện chương trình và nạp code. 

Bước 1: Tại ô “Part Number Search” gõ dòng chip STM32F411VE cũng là dòng chip trên Kit phát triển STM32F411DISCOVERY. Kích chọn dòng STM32F411VETx và sau đó chọn “Start Project”.

Bước 2: Chọn Serial Wire để nạp code

Bước 3: Click chuột phải vào chân PD12 sau đó tích vào ô GPIO_Output

Bước 4: Chọn chân PA0 là chân GPIO_EXTI0.

Vì sơ đồ nguyên lý của nút nhấn  PA0 trên kit STM32F411DISCOVERY đã thiết kế sẵn trở kéo xuống nên trong CUBEMX ta chỉ cần  cấu hình No pull-up No pull-down tại chân này và chọn sườn lên làm tín hiệu kích hoạt ngắt. 

Bước 5: Chuyển qua tab NVIC và tick chọn ô Enabled, cho phép xảy ra ngắt tại line 0.

Để thay đổi mức độ ưu tiên của các line ngắt đang sử dụng, các bạn vào ô NVIC ở System Core. Ở đây chúng ta có 5 nhóm Priority Group khác nhau để phân chia số bit cho pre-emption priority và subpriority. Ý nghĩa của 2 loại ưu tiên ngắt này như sau:
– Ngắt nào có Preemption Priority cao hơn thì sẽ được ưu tiên thực hiện trước.
– Nếu các ngắt có cùng Preemtion Priority thì ngắt nào có Sub Priority cao hơn thì sẽ được thực hiện trước.
– Nếu các ngắt có cùng Preemtion Priority và Sub Priority thì ngắt nào đến trước sẽ được phục vụ trước.
Chi tiết xem ở đây.

Bước 6: Setting project và sinh code

Bước 7: Click chọn Open Project và chuyển qua tab Keil C.

Ở mục Function góc ngoài cùng bên phải, các bạn click vào phần mở rộng main.c. Ở đây chúng ta có thể xem tất cả các hàm hiện có trong file main. Click vào MX_GPIO_Init(void) để xem lại tất cả những cấu hình GPIO mà bạn đã khởi tạo ở phần mềm CubeMX.

Ở hàm HAL_NVIC_Setpriority(), chúng có thể thấy thông số đầu tiên là EXTI Line Interrupt, thông số thứ 2 là mức độ ưu tiên của pre-empriority, thứ 3 là mức độ ưu tiên của subpriority.

Bước 8: Ở file main.c, chúng ta khởi tạo hàm void HAL_GPIO_EXTI_Callback() như sau:

Ở trong hàm này, lệnh if(GPIO_Pin == GPIO_PIN_0) để phân luồng ngắt, kiểm tra rằng ngắt hiện tại đang sinh ra ở chân nào. Mỗi lần xảy ra ngắt tại chân GPIO0, led PD12 sẽ đảo trạng thái, sau đó chờ đến khi nút nhấn được thả trong 1 khoảng thời gian time out rồi xóa pending ngắt, tránh tình trạng nhảy vào hàm ngắt nhiều lần khi nhấn nút.

TAPIT ARM R&D