📚 Điều kiện tiên quyết

Để tiếp thu bài học này tốt nhất, bạn nên ôn lại:

Trong bài học trước, chúng ta đã lập trình cho MCU nhấp nháy LED bằng cách sử dụng vòng lặp trễ vô dụng delay() để đốt thời gian của CPU. Cách tiếp cận này khiến CPU bị block hoàn toàn — nó không thể làm bất kỳ việc gì khác trong lúc chờ đợi, kể cả việc đọc nút nhấn hay xử lý khẩn cấp.

Để giải quyết bài toán đa nhiệm và đáp ứng tức thời trong các hệ thống nhúng thực tế, hai vũ khí quan trọng nhất là Ngắt phần cứng (Interrupts)Bộ định thời (Timers). Bài học cuối cùng này sẽ phân tích chi tiết cơ chế hoạt động của chúng và hướng dẫn bạn ghép nối một hệ thống điều khiển LED thông minh (Smart LED Station) hoàn chỉnh — dự án Capstone kết hợp toàn bộ kiến thức của cả series.

1. Cơ chế Ngắt phần cứng (Hardware Interrupts)

1.1 Polling (Truy vấn vòng) vs Interrupt (Ngắt)

Hãy tưởng tượng bạn đang đợi một bưu phẩm giao đến nhà:

  • Polling (Truy vấn vòng): Cứ 5 phút bạn lại chạy ra mở cửa kiểm tra xem shipper đến chưa. Việc này tốn cực kỳ nhiều công sức (CPU chu kỳ máy) và bạn không thể làm việc khác tập trung.
  • Interrupt (Ngắt): Bạn ngồi làm việc bình thường. Khi shipper đến, họ bấm chuông (tín hiệu ngắt). Bạn dừng việc đang làm, ra nhận hàng (thực thi hàm ngắt), rồi quay lại bàn làm việc tiếp tục công việc trước đó.

Trong MCU, ngắt là một tín hiệu phần cứng từ các ngoại vi bên ngoài (như nút nhấn nhấn xuống) hoặc bên trong (như bộ định thời báo tràn) yêu cầu CPU tạm dừng chương trình chính ngay lập tức để chuyển sang xử lý một tác vụ ưu tiên cao gọi là ISR.

Quy trình xử lý ngắt của CPU diễn ra như sau:

  1. Một sự kiện ngắt xảy ra (ví dụ: chân INT0 nhận cạnh xuống từ nút nhấn).
  2. CPU hoàn thành lệnh hiện tại đang thực thi.
  3. CPU tự động lưu địa chỉ của lệnh tiếp theo (Program Counter) vào vùng nhớ ngăn xếp (Stack).
  4. CPU tra cứu bảng Vector ngắt (Interrupt Vector Table) để tìm địa chỉ của hàm ISR tương ứng và nhảy tới đó thực thi.
  5. Khi kết thúc ISR (lệnh RETI - Return from Interrupt), CPU khôi phục địa chỉ từ Stack và quay lại chạy chương trình chính bình thường.

1.2 Cạm bẫy dội phím cơ học (Contact Bounce) & Giải pháp lọc thông thấp RC

Khi bạn nhấn một nút cơ học, các tiếp điểm kim loại bên trong không tiếp xúc hoàn hảo ngay lập tức. Do tính chất đàn hồi, chúng sẽ nảy và chạm vào nhau nhiều lần trong khoảng $1\text{ ms} - 10\text{ ms}$ trước khi ổn định.

Hiện tượng này gọi là Dội phím (Contact Bounce). Đối với mắt người thì nút nhấn như ăn ngay lập tức, nhưng đối với CPU chạy hàng triệu lệnh mỗi giây, nó sẽ nhìn thấy hàng chục lần bật/tắt liên tục và kích hoạt ISR ngắt ngoài hàng chục lần chỉ cho một lần nhấn nút.

Hiện tượng dội phím cơ học và giải pháp lọc thông thấp RC Dội phím thô (Raw Switch): Nảy/Dội (1-10ms) Kích hoạt nhiều ngắt rác! Sau khi qua mạch lọc RC: Đường cong nạp tụ mượt mà 1 lần kích ngắt sạch! R C Tới chân INT0

Để khử dội phím, chúng ta có hai phương án:

  • Chống dội bằng phần cứng (Hardware Debounce): Cho tín hiệu qua mạch lọc thông thấp RC (Rơ-le/Nút nhấn $\rightarrow$ Điện trở $R$ $\rightarrow$ Tụ điện $C$ mắc song song xuống GND). Hằng số thời gian $\tau = R \cdot C$ (thường chọn $10\text{ k}\Omega$ và $100\text{ nF} \Rightarrow \tau = 1\text{ ms}$) hoạt động như một bộ giảm chấn làm trơn toàn bộ các xung nhọn dội phím tần số cao.
  • Chống dội bằng phần mềm (Software Debounce): Khi phát hiện ngắt kích hoạt, kiểm tra lại trạng thái chân sau một khoảng thời gian chờ ngắn ($20\text{ ms} - 50\text{ ms}$) để xác nhận phím nhấn thực sự ổn định trước khi xử lý logic.

Tra cứu chi tiết về cách cấu hình ngắt ngoài và quản lý tài nguyên ngắt tại Datasheet ATmega328P (Microchip).

2. Bộ định thời (Timer) và Bộ chia tần (Prescaler)

2.1 Tại sao cần bộ định thời phần cứng?

Trong Bài 15, để tạo trễ ta dùng vòng lặp trống bắt CPU chờ. Điều này lãng phí chu kỳ máy. Bộ định thời (Timer) là một khối phần cứng đếm xung nhịp độc lập nằm bên ngoài CPU. Nó hoạt động như một đồng hồ đếm tự động: cứ sau một số chu kỳ clock hệ thống nhất định, thanh ghi đếm (ví dụ TCNT0) sẽ tự cộng thêm 1 đơn vị, hoàn toàn không tốn tài nguyên xử lý của CPU.

2.2 Bộ chia tần (Prescaler) và cách hoạt động

Nếu clock hệ thống của vi điều khiển AVR chạy ở tần số $16\text{ MHz}$, nghĩa là tụ thạch anh dao động 16,000,000 lần mỗi giây. Một Timer 8-bit chỉ có thể đếm tối đa từ $0$ đến $255$. Nếu đếm trực tiếp xung clock hệ thống, thời gian tối đa để tụ đếm tràn chỉ là:

$$t_{max} = \frac{256}{16 \times 10^6\text{ Hz}} = 16\ \mu\text{s}$$

Khoảng thời gian này quá ngắn để tạo các ứng dụng thực tế (như nhấp nháy LED chu kỳ $1\text{ giây}$). Để kéo dài thời gian đếm, người ta đưa xung nhịp qua một bộ chia tần gọi là Prescaler. Prescaler sẽ chia tần số clock hệ thống xuống các tỉ lệ như $8, 64, 256, 1024$ trước khi đưa vào bộ đếm.

Nếu chọn Prescaler $= 1024$, tần số cấp cho Timer chỉ còn:

$$f_{timer} = \frac{16\text{ MHz}}{1024} = 15625\text{ Hz}$$

Mỗi bước đếm của Timer lúc này tốn $\frac{1}{15625\text{ Hz}} = 64\ \mu\text{s}$. Thời gian đếm tràn 8-bit tăng lên thành $64\ \mu\text{s} \times 256 \approx 16.38\text{ ms}$. Nếu sử dụng Timer 16-bit (đếm tới $65535$), thời gian tràn tối đa sẽ lên tới hơn $4.19\text{ giây}$.

💡 Công thức tính tần số ngắt Timer khi đếm Compare Match

Để tạo ra một ngắt định thời với tần số ngõ ra mong muốn $f_{target}$ ở chế độ Clear Timer on Compare Match (CTC), ta nạp giá trị vào thanh ghi so sánh $OCR0A$ theo công thức:

$$OCR0A = \frac{f_{osc}}{2 \cdot \text{Prescaler} \cdot f_{target}} - 1$$

Trong đó $f_{osc}$ là tần số clock hệ thống ($16\text{ MHz}$). Giá trị $OCR0A$ tính ra bắt buộc phải là một số nguyên dương nhỏ hơn $256$ đối với Timer 8-bit.

Ví dụ 1: Tính toán bộ chia tần và giá trị so sánh để tạo ngắt 1 kHz

Đề bài: Sử dụng Timer 8-bit trên vi điều khiển chạy thạch anh $16\text{ MHz}$, hãy tính giá trị cần nạp vào thanh ghi so sánh $OCR0A$ và chọn Prescaler thích hợp để tạo ra ngắt định thời có tần số $f = 1\text{ kHz}$ (chu kỳ $1\text{ ms}$).

Lời giải

1. Thử với bộ chia tần Prescaler $= 64$:

$$OCR0A = \frac{16 \times 10^6\text{ Hz}}{1000\text{ Hz} \cdot 64} - 1 = 250 - 1 = 249$$

2. Kiểm tra tính hợp lệ: Giá trị $249$ là số nguyên và nhỏ hơn $256$, do đó hoàn toàn phù hợp để nạp vào thanh ghi so sánh 8-bit.

Kết luận: Cấu hình Timer chạy với bộ chia tần Prescaler $= 64$ và nạp giá trị $249$ vào thanh ghi $OCR0A$. Mỗi khi Timer đếm từ $0$ lên $249$ (tốn đúng $1\text{ ms}$), một ngắt so sánh sẽ được kích hoạt.

3. Tạo xung PWM bằng phần cứng (Hardware PWM)

Ở Bài 14, chúng ta tạo xung PWM bằng cách sử dụng IC 555 analog hoặc lập trình mềm (bật chân HIGH, chạy vòng lặp delay, hạ chân LOW). Cách tạo xung bằng phần mềm cực kỳ không chính xác vì CPU chỉ cần bị ngắt bởi tác vụ khác là độ rộng xung sẽ bị méo ngay lập tức.

Hardware PWM giải quyết triệt để vấn đề này. Bản thân khối Timer trong MCU có các cổng logic so sánh ngõ ra chuyên dụng liên kết trực tiếp với chân vật lý (như chân OC0A / OC0B):

  • Bộ đếm $TCNT0$ tăng dần liên tục từ $0$ đến $255$.
  • Thanh ghi so sánh ngõ ra $OCR0A$ lưu giá trị ngưỡng mong muốn (tương ứng với Duty Cycle).
  • Khi $TCNT0 < OCR0A$: phần cứng tự động kéo chân OC0A lên mức HIGH.
  • Khi $TCNT0 \ge OCR0A$: phần cứng tự động kéo chân OC0A xuống mức LOW.
  • Chu kỳ lặp lại liên tục và chạy hoàn toàn tự động bằng phần cứng. CPU chỉ cần ghi đè trị số vào $OCR0A$ một lần duy nhất khi muốn thay đổi độ sáng LED hoặc tốc độ động cơ, sau đó có thể ngủ hoặc xử lý việc khác.
🕳️ Cạm bẫy: Cố gắng cập nhật OCR0A liên tục trong vòng lặp cực nhanh

Nhiều người mới lập trình thường viết code liên tục cập nhật giá trị OCR0A trong vòng lặp while(1) không có trễ. Việc ghi đè giá trị so sánh giữa chu kỳ đếm của Timer có thể làm mất sự kiện Compare Match, dẫn đến chân ngõ ra bị treo ở mức cao hoặc mức thấp ngoài ý muốn (hiện tượng glitch). Giải pháp là cấu hình chế độ Double Buffering (phần cứng tự động cập nhật giá trị mới từ thanh ghi đệm vào thanh ghi hoạt động của OCR0A chỉ tại thời điểm Timer đếm chạm đỉnh hoặc đáy).

4. Dự án tổng hợp cuối series (Smart LED Station)

Để khép lại lộ trình Điện Tử & Vi Mạch, chúng ta sẽ thiết kế một hệ thống điều khiển công suất LED thông minh (Smart LED Station) kết hợp tất cả các khối linh kiện đã học từ các bài học trước:

  1. Khối nguồn xoay chiều: Điện lưới hạ áp qua máy biến áp (Bài 6) xuống $12\text{V AC}$.
  2. Khối chỉnh lưu & Lọc: Cầu đi-ốt chỉnh lưu toàn chu kỳ kết hợp tụ lọc hóa $1000\ \mu\text{F}$ (Bài 7) biến áp AC thành nguồn $15\text{V DC}$ nhấp nháy mượt.
  3. Khối ổn áp tuyến tính: Sử dụng IC ổn áp 7805 (Bài 7) hạ áp từ nguồn DC xuống $5\text{V DC}$ ổn định để nuôi vi điều khiển MCU.
  4. Khối vi điều khiển: MCU đọc trạng thái nút nhấn kích ngắt ngoài qua chân INT0 (Bài 16). Mỗi lần nhấn nút, ngắt được kích hoạt để thay đổi các chế độ hoạt động (Mode).
  5. Khối công suất: Chân Hardware PWM của MCU (OC0A) điều khiển cực Gate của N-Channel MOSFET IRF540N (Bài 9) đóng ngắt tải dòng lớn.
  6. Khối tải tiêu thụ: Dải LED siêu sáng đấu nối tiếp với điện trở hạn dòng (Bài 2).
Sơ đồ khối nguyên lý hệ thống Smart LED Station Nguồn AC Chỉnh lưu + Lọc 12V DC LM7805 Ổn áp 5V 5V DC MCU ATmega328P Button INT0 PWM (OC0A) IRF540N MOSFET LED Array Nguồn công suất 12V DC

5. Sơ đồ mạch nguyên lý của bộ thử nghiệm giả lập (Simulator Circuit)

Để hiểu rõ các tín hiệu hiển thị trên Oscilloscope ảo của trình giả lập bên dưới, đây là sơ đồ đấu nối phần cứng chi tiết của hệ thống:

  • Mạch ngắt ngõ vào (INT0 / PD2): Nút nhấn cơ học được kết nối nối tiếp với nguồn $5\text{V}$. Chân ngắt ngoài INT0 được kéo xuống đất (GND) thông qua một điện trở kéo xuống (pull-down) $R_{pd} = 10\ \text{k}\Omega$ để đảm bảo trạng thái ổn định LOW ($0\text{V}$) khi không nhấn nút. Tụ lọc chống dội $C_f = 100\text{ nF}$ mắc song song với điện trở tạo thành bộ lọc thông thấp RC phần cứng.
  • Mạch công suất ngõ ra (OC0A / PD6): Chân phát xung PWM cứng (OC0A) của vi điều khiển được nối vào cực Gate của MOSFET kênh N (IRF540N) qua điện trở hạn dòng $100\ \Omega$. MOSFET đóng vai trò là một khóa điện tử tốc độ cao điều khiển nguồn điện $12\text{V}$ chạy qua dải đèn LED công suất lớn.
Sơ đồ chi tiết ngõ vào INT0 và ngõ ra PWM (OC0A) 5V R_pd 10k Cf 100nF GND ATmega328P PD2 (INT0) PD6 (OC0A) 100R MOSFET LED Array 12V

5. Smart LED & MCU Virtual Lab: Trình giả lập ngắt & định thời

Trình giả lập bên dưới mô phỏng hoạt động thời gian thực của vi điều khiển. Bạn có thể chọn cơ chế chống dội phím (Debounce) để quan sát sự méo dạng điện áp tại ngõ vào và xem cách ngắt ngoài INT0 chuyển đổi chu kỳ làm việc PWM cấp cho LED.

MCU Registers

TCNT0
0x00
OCR0A
0x00
EIFR
0x00
Phím tắt: Bấm Spacebar để kích
Kênh 1 (Vàng): Điện áp chân ngắt INT0 Kênh 2 (Xanh): Xung PWM ngõ ra OC0A

Mã lệnh thực thi C / ISR Handler

Mode: TẮT Duty: 0%
🛠️ Dự án Thực hành: Tự thiết kế & Lắp ráp phần cứng thật

Bạn muốn tự tay xây dựng một thiết bị điện tử chạy được ngoài đời thực? Hãy bước vào trang thực hành đặc biệt:

Xem Bài Thực Hành: Mạch LED Ma Trận & Thiết Kế Phần Cứng →

6. Trắc nghiệm kiểm tra

Câu 1: Tại sao ngắt Hardware PWM lại vượt trội hơn hoàn toàn so với Software PWM tự tạo bằng vòng lặp delay?

Đáp án đúng: Vì hoạt động bằng phần cứng độc lập giúp xung chính xác và không tốn CPU. Khối so sánh của Timer tự động lật chân OC0A khi thanh ghi đếm TCNT0 chạm ngưỡng OCR0A hoàn toàn bằng mạch điện lý cứng, không cần CPU can thiệp chạy dòng lệnh nào, đảm bảo xung ổn định tuyệt đối.

Câu 2: Nếu một vi điều khiển chạy xung nhịp clock hệ thống là $8\text{ MHz}$, sử dụng Timer 8-bit và chọn bộ chia tần Prescaler $= 256$. Tần số đếm của bộ đếm Timer lúc này là bao nhiêu?

Đáp án đúng: 31250 Hz. Tần số đếm của Timer bằng tần số clock hệ thống chia cho hệ số chia tần Prescaler: $f_{timer} = \frac{8 \times 10^6}{256} = 31250\text{ Hz}$. Mỗi bước đếm tốn $\frac{1}{31250} = 32\ \mu\text{s}$.

Câu 3: Hiện tượng dội phím cơ học (Contact Bounce) nếu không được xử lý chống dội bằng phần cứng hoặc phần mềm sẽ gây ra hậu quả gì cho mạch kích ngắt ngoài?

Đáp án đúng: Kích hoạt ngắt liên tiếp gây nhảy loạn chế độ. Xung gai nhiễu từ tiếp điểm cơ học dao động nhanh làm chân ngắt ngoài INT0 liên tiếp nhận các sườn xung kích hoạt, CPU sẽ liên tục gọi ISR xử lý, dẫn đến nhảy chế độ sai lệch.