NVIC – Nested vectored interrupt controller là bộ điều khiển xử lý ngắt có trong MCU STM32F103C8T6. Việc lập trình sử dụng ngắt là một kĩ năng quan trọng khi các bạn lập trìnhh vi điều khiển, nếu không có ngắt thì chương trình sẽ thực hiện theo một trình tự từ trên xuống dưới, ngắt giúp chương trình xử lý theo sự việc, đáp ứng được các sự kiện như sự thay đổi mức logic từ 1 chân vi điều khiển (ngắt ngoài), nhận một kí tự (ngắt nhận UART)…. Trong bài viết này, mình sẽ trình bày về ngắt ngoài (external interrupt) của vi điều khiển STM3232F103C8T6.

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

Một số thông số tính năng chính của NVIC:

  • 16 mức ưu tiên có thể lập trình được.
  • Độ trễ thấp (xảy ra ngắt cực kì nhanh).
  • Có quản lí năng lượng cho vector ngắt.
  • Có các thanh ghi điều khiển quá trình ngắt.
  • 68 vector ngắt

Ngắt ngoài nằm trong 1 phần của NVIC. Mỗi EXTI – interrupt/event controller có thể được lập trình chọn loại sự kiện/ ngắt, chọn cạnh lên, cạnh xuống hoặc cả 2, sắp xếp mức ưu tiên ngắt.

Một số tính năng chính của ngắt ngoài:

  • Kích hoạt độc lập và mask cho mỗi line sự kiện/ngắt.
  • Có bit trạng thái (status) riêng cho mỗi line ngắt.
  • Có thể có tối đa 20 sự kiện/ ngắt, tham khảo thêm trong reference manual.
  • Kiểm tra tín hiệu ngoài có độ rộng xung nhỏ hơn clock trên APB2.

Sơ đồ khối của các khối điều khiển ngắt ngoài:

Cấu hình với thư viện chuẩn của ST. Có 2 loại ngắt ngoài chính đó là ngắt ngoài trên các chân điều khiển ở dạng thông thường và ngắt ngoài trên các ứng dụng như : PVD, RTC, USB, Ethernet.

Ngắt ngoài của chip STM32F103 bao gồm có 16 line:

Ở đây chúng ta có thể thấy chip STM32F103C8 gồm có 16 Line ngắt riêng biệt.

Ví dụ:

  •  Line0 sẽ chung cho tất cả chân Px0 ở tất cả các Port, với x là tên của Port A, B…
  •  Line0 nếu chúng ta đã chọn chân PA0 (chân 0 ở port A) làm chân ngắt thì tất cả các chân 0 ở các Port khác không được khai báo làm chân ngắt ngoài nữa
  • Line1 nếu chúng ta chọn chân PB1 là chân ngắt thì tất cả chân 1 ở các Port khác không được khai báo làm chân ngắt nữa.

Tiếp theo các Line ngắt sẽ được phân vào các Vector ngắt tương ứng. Các Line ngắt của chip STM32F103 được phân bố vào các vector ngắt như sau:

Các Line0, Line1, Line2, Line3, Line4 sẽ được phân vào các vector ngắt riêng biệt EXTI0, EXTI1, EXTI2, EXTI3, EXTI4, còn từ Line5->Line9 sẽ được phân vào vector ngắt EXTI9_5, Line10->Line15 được phân vào vecotr EXTI15_10.

Một số thanh ghi quan trọng:

1.EXTI_IMR – Interrupt mask register: Thanh ghi này cài đặt cho phép có yêu cầu ngắt trên Line tương ứng. (cho phép ngắt)

2. EXTI_RTSR – Rising trigger selection register: Thanh ghi này được sử dụng để cấu hình chọn sườn lên làm tín hiệu kích hoạt ngắt.

3. EXTI_FTSR – Falling trigger selection register: Thanh ghi này được sử dụng để cấu hình chọn sườn xuống làm tín hiệu kích hoạt ngắt

4. EXTI_SWIER – Software interrupt even register: Thanh ghi này cho phép kích hoạt Line ngát tương tứng bằng phần mềm. 

5. EXTI_PR – Pending register:  Đây là thanh ghi chờ xử lý ngắt, khi có yêu cầu ngắt được tạo ra trên một Line ngắt thì bit tương tứng của thanh ghi này được bật lên cho đến khi ngắt này được xử lý. Nhiều trước hợp có sự thay đổi sườn tín hiệu tạo ra yêu cầu ngắt nhưng ngắt không được thực thi như: độ ưu tiên thấp, chưa cho phép ngắt toàn cục.

Bảng mức độ ưu tiên ngắt NVIC:

Có hai loại ưu tiên ngắt khác nhau trên MCU STM32F103C8T6 đó là Preemption Priorities và Sub Priorities:
– Mặc định thì ngắt nào có Preemtion Priority cao hơn thì sẽ được thực hiện trước.
– Khi nào 2 ngắt có cùng một mức Preemption Priority thì ngắt nào có Sub Priority cao hơn thì ngắt đó được thực hiện trước.
– Còn trường hợp 2 ngắt có cùng mức Preemption và Sub Priority luôn thì ngắt nào đến trước được thực hiện trước.

THỰC HÀNH:

Tiếp theo chúng ta sẽ thực hành project ngắt ngoài trên chip STM32F103C8T6 với CubeMX. Ở ví dụ này, chúng ta sẽ sử dụng chân PA0 tương ứng với Line0 – vector EXTI0 và PA6 tương ứng Line6 – vector EXTI5_9 để thực hành. Khai báo thêm chân Led PC13, khi ngắt ngoài ở chân PA0 thì chúng ta sẽ tắt Led, khi ngắt ngoài ở chân PA6 thì chúng ta sẽ bật Led

Bước 1:

Khởi tạo project CubeMX, sau đó chọn cổng nạp dữ liệu Serial Wire

Bước 2:

Chọn chân PC13 là chân GPIO_Output

Bước 3:

Chọn chân PA0 là chân GPIO_EXTI0, chúng ta cũng làm tương tự đối với chân PA6 là chân GPIO_EXTI6

Bước 4:

Tiếp theo, click vào System Core sau đó click vào GPIO. Lúc này sẽ hiện ra một bảng Pin Configuration. Click vào lần lượt các chân PA0 và PA6 để cấu hình:

GPIO_Mode: External Interrupt Mode with Falling edge trigger detection

GPIO_Pullup/Pull-down: Pull-up

Bước 5:

Tiếp theo các bạn click vào NVIC, ở đây chúng ta có thể thấy:

Piority Group: 4bits for pre-emtion piority 0 bit for subpiority, ở đây mặc định ban đầu CubeMX đã set nhóm ưu tiên ngắt là ở nhóm 4 gồm 2 trường preempiority, subpiority, với 16 giá trị preempiority và 0 giá trị preempiority.

Chúng ta tít chọn để bật ngắt cho EXTI0 và EXTI5_9, mặc định ban đầu preempiority và subpiority của ngắt vector ngắt này đều bằng 0, nghĩa là có mức độ ưu tiên như nhau. Chúng ta có thể cài đặt lại mức độ ưu tiên của từng vector ngắt tùy theo mục đích sử dụng. Các vector ngắt nào có preempiority cao hơn thì mức độ ưu tiên thấp hơn và ngược lại các vector ngắt nào có subpiority cao hơn thì mức độ ưu tiên cao hơn.

Bước 6:

Cấu hình project và sinh code.

Bước 7:

Các bạn click vào Function, 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) ở đây chúng ta thấy rằng đã khởi tạo chân PA0,PA6, ngắt khi có cạnh xuống, chân này ban đầu được kéo lên nguồn.

Bật ngắt trên 2 vector EXTI0, EXTI5_9

Bước 8:

Ở hàm HAL_NVIC_Setpriority(), chúng có thể thấy thông số đầu tiên là vector, thông số thứ 2 là mức độ ưu tiên của preemprioity, thứ 3 là mức độ ưu tiên của subpriority. Mặc định ban đầu 2 thông số này của 2 vector ngắt set bằng 0 tương ứng với mức độ ưu tiên của 2 vector ngắt EXTI0, EXTI5_9 bằng nhau.

Bước 9:

Tiếp theo chúng ta khởi tạo hàm void HAL_GPIO_EXTI_Callback(){}

Bước 10:

Ở trong hàm này, mình sẽ có 2 lệnh điều kiện if() để phân luồng ngắt kiểm tra rằng ngắt hiện tại đang sinh ra ở chân nào.

If(GPIO_Pin == GPIO_PIN_0)

If(GPIO_Pin == GPIO_PIN_6)

Bước 11:

Sau khi đã phân luồng ngắt được rồi, tiếp theo ở trong hàm kiểm tra chân nào đang ngắt:

+ PA0 mình sẽ cho tắt Led PC13

+ PA6 mình sẽ cho bật Led PC13

Tiếp theo các bạn biên dịch và nạp code vào chip STM32F103C8T6.

Chúc các bạn thành công!

Xem thêm: Tổng hợp các bài hướng dẫn Lập trình vi điều khiển STM32 tại đây.

Nhóm Tapit ARM R&D