Trong các bài học trước, chúng ta đã làm quen với phương pháp Polling (vòng lặp kiểm tra). Tuy nhiên, trong các hệ thống nhúng hiện đại đòi hỏi tính thời gian thực (Real-time) và đa nhiệm, Polling bộc lộ điểm yếu chết người là làm lãng phí 100% tài nguyên CPU chỉ để “chờ đợi”. Bài viết này sẽ giúp bạn làm chủ Ngắt (Interrupt), RTOS và DMA – ba “vũ khí” hạng nặng để tối ưu hóa hiệu năng hệ thống.
🎯 Mục tiêu học tập nâng cao
- Phân tích sâu cơ chế Context Switching trong lập trình Ngắt.
- Hiểu cách RTOS quản lý đa nhiệm và khi nào cần sử dụng nó.
- Nắm vững kiến trúc DMA Controller và các chế độ truyền dữ liệu đỉnh cao.
- Thực hành cấu hình Ngắt ngoài (EXTI) trên STM32 từ CubeMX đến xử lý mã nguồn trong Keil MDK.
1. Kỹ thuật lập trình Ngắt (Interrupt) – Phản xạ tức thì
Ngắt là cơ chế cho phép phần cứng gửi tín hiệu IRQ (Interrupt Request) để thông báo cho CPU khi có một sự kiện khẩn cấp xảy ra.
a) Cơ chế hoạt động: Từ IRQ đến ISR
Khi một yêu cầu ngắt được chấp nhận, CPU sẽ thực hiện quy trình sau:
- Tạm dừng chương trình hiện tại (Main loop).
- Lưu trạng thái các thanh ghi vào Stack (Context Saving).
- Tra cứu địa chỉ hàm xử lý ngắt trong Vector Table.
- Thực thi ISR (Interrupt Service Routine).
- Khôi phục trạng thái cũ và tiếp tục chạy Main loop (Context Restore).
| Tiêu chí |
Phương pháp Polling |
Phương pháp Interrupt |
| Tài nguyên CPU |
Luôn bận rộn 100%. |
Chỉ bận khi có sự kiện xảy ra. |
| Tốc độ đáp ứng |
Phụ thuộc vào độ dài của vòng lặp. |
Gần như tức thì. |
| Tiết kiệm điện |
Không hiệu quả. |
Tốt (Kết hợp chế độ Sleep). |
2. Hệ điều hành thời gian thực (RTOS) – Quản lý đa nhiệm
RTOS (Real-Time Operating System) không phải là một “phép thuật” giúp CPU chạy nhanh hơn, mà là một bộ quản lý (Scheduler) thông minh.
- Đa nhiệm (Multitasking): RTOS chia CPU thành các “lát cắt” thời gian nhỏ cho từng Task.
- Ưu tiên (Priority): Đảm bảo Task quan trọng nhất luôn được thực thi trước.
- FreeRTOS: Là bản phân phối phổ biến nhất trong giới nhúng, giúp đơn giản hóa các chương trình phức tạp, cho phép các tiến trình chạy song song (về mặt logic).
Lưu ý: Chỉ sử dụng RTOS khi hệ thống có quá nhiều tác vụ phức tạp, vì RTOS sẽ chiếm dụng thêm một phần RAM và Flash của vi điều khiển.
3. DMA (Direct Memory Access) – Đường truyền dữ liệu siêu tốc
DMA là một khối phần cứng độc lập cho phép trao đổi dữ liệu trực tiếp giữa Ngoại vi và Bộ nhớ hoặc Bộ nhớ và Bộ nhớ mà hoàn toàn không đi qua CPU.
Các tính năng nổi bật của bộ DMA trên STM32:
- Đa kênh: 12 kênh độc lập (7 kênh DMA1, 5 kênh DMA2) cho phép xử lý nhiều luồng dữ liệu cùng lúc.
- Mức độ ưu tiên: Có 4 mức (Very High, High, Medium, Low) có thể lập trình được.
- Kích thước linh hoạt: Truyền theo Byte (8-bit), Half-word (16-bit) hoặc Word (32-bit).
- Cờ báo hiệu: Hỗ trợ các ngắt như Transfer Complete (TC), Half Transfer (HT), và Error (TE).
Chế độ hoạt động:
- Normal Mode: Truyền xong một lượng dữ liệu định trước là dừng. Thường dùng cho các lệnh gửi dữ liệu UART cố định.
- Circular Mode: Hoạt động như một Ring Buffer. Khi truyền đến cuối vùng nhớ, DMA tự động quay lại bắt đầu từ đầu. Cực kỳ hiệu quả khi làm việc với ADC (đọc cảm biến liên tục) hoặc xử lý Audio.
4. Thực hành: Cấu hình EXTI (External Interrupt) trên STM32
Bước 1: Cấu hình trên STM32CubeMX
- Pinout: Chọn chân PA0 (Nút nhấn) là
GPIO_EXTI0. Chọn chân PB6 (LED) là GPIO_Output.
- Clock Configuration: Để đạt hiệu năng cao nhất, cấu hình tần số Max (VD: 216MHz cho F7).
- NVIC Settings: Tìm đến dòng “EXTI line0 interrupt” và tích vào ô Enabled.
- Project Manager: Chọn Toolchain là MDK-ARM (KeilC) và nhấn Generate Code.
Bước 2: Hiểu luồng code xử lý ngắt
Trong thư viện HAL, khi có ngắt xảy ra, hệ thống sẽ thực hiện theo chuỗi:
EXTI0_IRQHandler (trong stm32f7xx_it.c)
↓
HAL_GPIO_EXTI_IRQHandler
↓
HAL_GPIO_EXTI_Callback (Hàm bạn sẽ viết code)
/* * Hàm Callback xử lý ngắt ngoài
* Được gọi tự động bởi thư viện HAL sau khi đã clear các cờ ngắt
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_0) // Kiểm tra ngắt đến từ chân Pin 0
{
// Thực hiện đảo trạng thái LED
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_6);
}
}
/* Trong hàm main */
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init(); // Trong này đã cấu hình EXTI và NVIC
while (1)
{
// Chương trình nền có thể ngủ hoặc làm việc khác
HAL_Delay(1000);
}
}
5. Hướng dẫn nạp code và Debug chuyên nghiệp
Để chương trình chạy đúng, việc nạp code cần tuân thủ các bước cấu hình mạch nạp ST-LINK V2 trong KeilC:
- Kết nối: Nối 4 chân (3.3V, GND, SWDIO, SWCLK) từ mạch nạp sang JTAG header của KIT.
- Settings: Trong mục Debug -> Settings, chọn SW Port thay vì JTAG. Nếu hiện ID CODE, chip đã kết nối thành công.
- Flash Download: Tích chọn Reset and Run. Điều này giúp chip tự động chạy chương trình ngay sau khi thanh tiến trình nạp đạt 100%.
🚀 Bài tập thực hành
Yêu cầu: Kết hợp Ngắt và DMA.
- Cấu hình UART ở chế độ DMA truyền dữ liệu.
- Mỗi khi nhấn nút (dùng Ngắt EXTI), hãy kích hoạt DMA gửi chuỗi ký tự “AIoT – Button Pressed!” lên terminal máy tính.
- Sử dụng biến đếm để hiển thị số lần nhấn nút trong chuỗi gửi lên.
📝 Tóm tắt: Làm chủ Ngắt giúp hệ thống phản ứng nhanh, làm chủ DMA giúp hệ thống truyền tin tốc độ cao mà CPU vẫn rảnh rỗi. Đây là những kỹ thuật tách biệt một kỹ sư thực thụ với một người mới bắt đầu.
“CPU là bộ não, nhưng DMA và Ngắt là hệ thần kinh giúp cơ thể nhúng hoạt động nhịp nhàng.”
Gợi ý bài tiếp theo: Timer – Từ cơ bản đến nâng cao: Định thời, Ngắt Timer và Ứng dụng tạo xung PWM.