Sau khi làm quen với ngắt ở ngày 4, thì hôm nay, chúng ta sẽ tiếp tục tìm hiểu về một khái niệm cốt lõi của các hệ thống nhúng thời gian thực. Như đã biết, ngắt GPIO hay ngắt ngoài giúp vi điều khiển phản ứng với sự kiện bên ngoài một cách chính xác mà không tốn tài nguyên ngồi kiểm tra liên tục thông qua Polling thì ở ngày 5, ta sẽ làm việc với Timer, chức năng giúp vi điều khiển phản ứng với các sự kiện nội tại trong chip mà không phải chờ liên tục.
Về bản chất, khi dùng sleep(), usleep() chính là sử dụng vòng lặp, căn bản trong hai hàm này, vi điều khiển sẽ lặp trong đó đến khi đúng bằng thời gian thiết lập trên tham số truyền vào. Rõ ràng, khi kẹt trong vòng lặp, bạn không thể làm việc khác. Tất nhiên, khi bạn đi làm thì rõ ràng tư bản sẽ không muốn bạn dành cả ngày chỉ để làm một việc như vậy =))). Do đó, ta cần một giải pháp để giải quyết công việc một cách “multitask” hơn. Timer được sinh ra dành cho trường hợp này.
Khi cấu hình timer ta sẽ quan tâm các thông số sau
– Bit Width: Độ rộng thanh ghi timer, trên CC1312R7 sẽ có 2 chế độ là 16 hoặc 32 bit
– Count Mode: One shot (đếm 1 lần, ngắt, không tự bật lại) và Periodic (Chu kỳ)
– Frequency và Prescaler: Dùng để tính toán thời gian dựa trên xung nhịp hệ thống. System default clock 48MHz
Thực hành: Nháy đèn led với chu kì 1s dùng timer thay vì sleep()
Bước 1: Cấu hình sysconfig, ở mục TI Drivers, ta chọn Timer. Đừng tick vào GPTimer, vì hiện t tại highlevel SDK TiDriver không còn sử dụng hàm link với GPTimer, nếu muốn sử dụng ở level thấp hơn, hãy sử dụng driverlib.

Bước 2: Đặt tên Name: Timer_0. Timer Type (Bit Width) chọn 32 bits. Piority để mặc định. Đồng thời cấu hình LED_RED GPIO như các bài trước
Bước 3: Viết code
Trước khi viết code, ta cần tính toán một số thông tin sau. Vì tần số xung Clock là 48MHz, tức là trong 1s, vi điều khiển có thể đếm được tương ứng 48MHz, mà ta cần nháy led với chu kì 1s, tức là mỗi 0.5s led sẽ đổi trạng thái 1 lần, do đó ta cần sự kiện ngắt tương ứng với khoảng thời gian đổi trạng thái trên tức là 48*0.5 = 24MHz lần đếm. Bằng bộ prescale, ta có thể set nhiều mức thời gian khác nhau. Tuy nhiên cách này dùng để tính toán khi ta dựa trên xung clock. Ti Driver đã hỗ trợ chúng ta hàm dựa trên us, rất tiện lợi.
Dưới đây là chương trình mẫu
#include <unistd.h>
#include <stdint.h>
#include <stddef.h>
/* Driver Header files */
#include <ti/drivers/GPIO.h>
#include <ti/drivers/Timer.h>
// #include <ti/drivers/I2C.h>
// #include <ti/drivers/SPI.h>
// #include <ti/drivers/Watchdog.h>
/* Driver configuration */
#include "ti_drivers_config.h"
/*
* ======== mainThread ========
* =
*/
void ToggleLed()
{
GPIO_toggle(LED_RED);
}
void *mainThread(void *arg0)
{
GPIO_init();
Timer_init();
Timer_Handle handle;
Timer_Params params;
Timer_Params_init(¶ms);
params.periodUnits = Timer_PERIOD_US;
params.period = 500000;
params.timerMode = Timer_CONTINUOUS_CALLBACK;
params.timerCallback = ToggleLed;
handle = Timer_open(Timer_0, ¶ms);
Timer_start(handle);
while(1)
{
}
}
Giải thích code:
#include <ti/drivers/Timer.h> Ta cần include thư viện Timer của Ti driver
Timer_init(); khởi tạo Timer
Khởi tạo các biến gồm biến xử lí handle thuộc lớp Timer_Handle để xử lí hàm và biến tham số params thuộc lớp Timer_Params để lưu các tham số thiết lập cho timer
Timer_Params_init(¶ms); Nạp tham số của biến params vào Timer
params.periodUnits = Timer_PERIOD_US; Chọn đơn vị chu kì, ở đây ta chọn us thì vi điều khiển sẽ tiến hành đếm theo us (micro giây), ngoài ra còn có các cách đếm khác như Timer_PERIOD_HZ (tính dựa theo tần số) và Timer_PERIOD_COUNT (tính dựa trên counter xung nhịp)
Ví dụ: Với chu kì 0.5s
Dùng Timer_PERIOD_US: Driver sẽ tự động lấy xung nhịp hệ thống Driver sẽ tự động lấy xung nhịp hệ thống chia cho 1 triệu để ra số tick cần thiết. Bạn không cần quan tâm chip chạy bao nhiêu MHz. Chỉ cần nhập period vào là ra được chu kì mong muốn
Dùng Timer_PERIOD_HZ: Ở đây ta quy định số lần timer chạy trong 1 giây, ví dụ mỗi 0.5s đảo trạng thái 1 lần, tức là 1s đảo trạng thái 2 lần, -> tần số = 2Hz. Lúc này ta set period = 2. Ở cách này, sai số sẽ lớn khi chu kì là số lẻ
Dùng Timer_PERIOD_COUNTS: Cách này sẽ đếm số xung clock của vi điều khiển. CC1312R7 chạy Clock 48MHz (48 000 000 xung/s), ta muốn 0.5s tức là cần đếm 1 nửa số xung tức 24 000 000 như vậy period lúc này sẽ là 24 000 000
params.timerMode = Timer_CONTINUOUS_CALLBACK; Dùng để đếm theo chu kì, sau khi tràn sẽ tự đặt lại timer và đếm lại từ đầu.
handle = Timer_open(Timer_0, ¶ms); Mở timer với các tham số thiết lập
Timer_start(handle); Khởi động timer
Ngoài ra nếu muốn ngừng timer, có thể sử dụng Timer_stop(handle);