Nếu CPU là bộ não thì Timer (Bộ định thời) chính là “nhịp tim” của hệ thống nhúng. Mọi tác vụ từ việc tạo ra một khoảng trễ chính xác micro giây, đo tần số tín hiệu đến việc điều khiển tốc độ động cơ qua xung PWM đều dựa trên ngoại vi này. Trong bài học hôm nay, chúng ta sẽ cùng khám phá kiến trúc sâu bên trong của Timer trên STM32 và cách cấu hình để tạo ra các ứng dụng thực tế.
🎯 Mục tiêu học tập
- Hiểu nguyên lý hoạt động của Counter và các bộ chia tần Prescaler.
- Phân biệt các loại Timer trên STM32: Basic, General Purpose và Advanced.
- Làm chủ các chế độ đếm: Up-counting, Down-counting và Center-aligned.
- Nắm vững khái niệm Watchdog Timer (WDT) – “Người gác cổng” của hệ thống.
- Thực hành tạo xung định thời và xử lý ngắt Timer trên Kit STM32F4/F7.
1. Tổng quan về kiến trúc Timer và Counter
Bản chất của Timer là một bộ đếm (Counter). Nó nhận xung nhịp từ hệ thống (Clock), đi qua một bộ chia tần để tăng giá trị của thanh ghi đếm sau mỗi khoảng thời gian xác định.
a) Các thành phần cốt lõi
- Prescaler (PSC): Bộ chia tần số clock đầu vào. Nếu clock hệ thống là 100MHz và PSC = 99, tần số đếm của Timer sẽ là 1MHz (mỗi 1µs tăng 1 đơn vị).
- Counter Register (TIMx_CNT): Thanh ghi chứa giá trị hiện tại của bộ đếm.
- Auto-Reload Register (TIMx_ARR): Giá trị ngưỡng. Khi CNT đạt đến ARR, một sự kiện “tràn” (Update Event) sẽ xảy ra, reset bộ đếm và có thể tạo ra ngắt.
b) Phân loại Timer trên STM32
| Loại Timer |
Đặc điểm & Chức năng |
| Basic Timer (TIM6, TIM7) |
Đơn giản nhất, chỉ dùng để tạo cơ sở thời gian (Time base) hoặc kích hoạt DAC. |
| General Purpose (TIM2-TIM5, TIM9-TIM14) |
Hỗ trợ PWM, Input Capture (đo tần số), Output Compare. Phổ biến nhất trong ứng dụng. |
| Advanced Timer (TIM1, TIM8) |
Đầy đủ tính năng, hỗ trợ thêm các tính năng chuyên dụng cho điều khiển động cơ (Dead-time, Break input). |
2. Các chế độ đếm của Timer
Tùy theo ứng dụng, chúng ta có thể cấu hình cách thức mà bộ đếm hoạt động:
- Up-counting: Đếm từ
0 lên ARR. Khi đạt ARR, nó quay về 0 (Tràn trên – Overflow).
- Down-counting: Đếm từ
ARR xuống 0. Khi về 0, nó quay lại ARR (Tràn dưới – Underflow).
- Center-aligned: Đếm từ
0 lên ARR rồi lại đếm xuống 0. Chế độ này thường dùng trong các thuật toán điều khiển vector cho động cơ để giảm nhiễu hài.
3. Watchdog Timer (WDT) – Người gác cổng hệ thống
Trong thực tế, chương trình có thể bị “treo” do lỗi phần mềm hoặc nhiễu điện từ. Watchdog Timer đóng vai trò như một bộ đếm ngược.
- Chương trình của bạn phải định kỳ “nuôi” (reset) Watchdog trước khi nó đếm về 0.
- Nếu hệ thống bị treo, lệnh “nuôi” không được thực hiện, Watchdog sẽ đếm hết và tự động Reset toàn bộ vi điều khiển để khôi phục hoạt động.
4. Thực hành: Tạo ngắt định thời 1µs với Timer 6
Chúng ta sẽ cấu hình Timer 6 (Basic Timer) trên STM32F746 để tạo ra một sự kiện ngắt mỗi 1 micro giây.
Bước 1: Tính toán thông số
Giả sử Bus APB1 cấp clock cho Timer là 108 MHz.
- Để có độ phân giải 1µs, chúng ta cần tần số đếm sau bộ chia là 18 MHz (Ví dụ trong tài liệu chọn tỷ lệ chia là 6).
- Prescaler (PSC): 6 – 1 = 5 (Vì thanh ghi PSC bắt đầu từ 0).
- Counter Period (ARR): Để đạt 1µs tại tần số 18MHz, ta cần 18 nhịp đếm. Vậy ARR = 18 – 1 = 17.
Bước 2: Cấu hình trên STM32CubeMX
- Bật TIM6, tích chọn Activated.
- Nhập
Prescaler = 5 và Counter Period = 17.
- Trong tab NVIC Settings, Enable “TIM6 global interrupt”.
- Nhấn Generate Code.
Bước 3: Viết mã nguồn xử lý ngắt
Trong file main.c, bạn cần khởi động Timer ở chế độ ngắt:
/* Khởi động Timer 6 và cho phép ngắt cập nhật */
HAL_TIM_Base_Start_IT(&htim6);
/* Hàm Callback xử lý khi Timer đếm xong chu kỳ (Update Event) */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM6)
{
// Đảo trạng thái chân PB6 để quan sát xung trên Oscilloscope
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_6);
}
}
💡 Mẹo nhỏ: Khi đo xung bằng Oscilloscope tại chân PB6, bạn sẽ thấy một tín hiệu vuông với chu kỳ 2µs (vì 1µs đảo trạng thái 1 lần). Nếu xung không chuẩn, hãy kiểm tra lại cấu hình Clock Tree trong CubeMX.
5. Ứng dụng quan trọng: Tạo xung PWM
PWM (Pulse Width Modulation) là kỹ thuật thay đổi độ rộng xung để điều khiển mức điện áp trung bình.
- Tần số (Frequency): Xác định bởi
ARR.
- Độ rộng xung (Duty Cycle): Xác định bởi thanh ghi so sánh
CCR (Capture Compare Register).
Nếu CNT < CCR, chân ngõ ra ở mức CAO. Nếu CNT >= CCR, chân ngõ ra ở mức THẤP. Bằng cách thay đổi giá trị CCR, chúng ta có thể làm LED sáng dần hoặc điều khiển tốc độ động cơ.
🚀 Bài tập về nhà buổi 18
- Định thời: Cấu hình Timer để tạo ngắt mỗi 1ms. Trong ngắt, hãy tăng một biến đếm
uint32_t tick_count. Đây là cách xây dựng hàm getTick() tự chế.
- PWM: Sử dụng General Purpose Timer (ví dụ TIM3) để tạo xung PWM tần số 1kHz. Viết code để LED sáng dần từ 0% lên 100% trong 2 giây rồi lặp lại.
- Watchdog: Kích hoạt IWDG (Independent Watchdog) với thời gian timeout 2 giây. Trong vòng lặp
while(1), hãy thử thêm một lệnh while(button_pressed());. Chuyện gì xảy ra nếu bạn giữ nút nhấn lâu hơn 2 giây?
📝 Tóm tắt: Timer không chỉ để đếm thời gian. Việc làm chủ các thanh ghi PSC, ARR và hiểu rõ cơ chế ngắt/PWM là nền tảng để bạn xử lý các bài toán điều khiển phức tạp. Hãy nhớ: Độ chính xác của Timer phụ thuộc hoàn toàn vào cấu hình Clock hệ thống.
“Trong thế giới nhúng, thời gian là tiền bạc, và Timer là chiếc đồng hồ chính xác nhất bạn có.”
Gợi ý bài tiếp theo: Chế Độ Counter: Khi Timer Trở Thành Bộ Đếm Sự Kiện.