Timer là một ngoại vi được ứng dụng rất nhiều trong lập trình nhúng. Trong STM32 có khá nhiều Timer, đa số là 16 bit và chúng có thể làm việc một cách độc lập với nhau.
Trong bài này chúng ta sẽ tìm hiểu Timer STM32 được sử dụng như thế nào, lập trình Timer STM32 điều khiển chớp tắt led không dùng delay.
Bài 5 trong Serie Lập trình STM32 từ A tới Z
Timer là gì?
Timer dịch đơn giản là người ghi giờ, bộ hẹn giờ.
Ví dụ khi bạn cần thức dậy sau 8 tiếng ngủ, bạn sử dụng một bộ hẹn giờ đếm ngược hoặc đếm xuôi đủ 8 tiếng, chuông sẽ kêu và bạn thức dậy.
Hoặc khi chạy 100m, người trọng tài chính là Timer để đo thời gian bạn chạy hết 100m đó.
Trong lập trình, Timer là một khối độc lập, có tác dụng tạo ra các sự kiện hoặc ngắt để kích hoạt các ngoại vi khác hoạt động, hoặc đo thời gian của 1 tín hiệu đầu vào nào đó.
Trong STM32F103C8 có 4 bộ Timer, trong đó:
- Timer 1: là bộ Advanced-control Timer hay bộ Timer điều khiển nâng cao, có nhiều chức năng nhất
- 3 bộ Timer thông dụng (General-purpose) là Timer 2, 3, 4: có chức năng tương tự nhau và độc lập với nhau
Trong bài này chúng ta sẽ làm việc với Timer 2, các chức năng đều tương tự với các Timer khác.
Các chức năng chính của Timer STM32
Các chức năng chính của Timer STM32 bao gồm:
– Thanh ghi 16 bit đếm lên, đếm xuống, đếm lên/xuống, tự nạp lại (auto-reload)
– Bộ chia tần 16 bit để chia tần số từ APB (giá trị chia từ 1 – 65536)
– 4 kênh độc lập trên mỗi Timer cho các chức năng:
+ Input Capture
+ Output Compare
+ PWM
+ One Pulse
– Đồng bộ hóa với các mạch tạo tín hiệu bên ngoài để kết hợp nhiều bộ Timer với nhau
– Ngắt/DMA được sinh ra khi có các sự kiện:
+ Cập nhật (Update): tràn Counter, khởi tạo Counter (bởi phần mềm hoặc kích hoạt internal/external trigger)
+ Sự kiện kích hoạt (Trigger): bắt đầu đếm, dừng đếm, khởi tạo bộ đếm hoặc đếm bởi internal/external trigger
+ Input Capture: bắt xung đầu vào
+ Output Compare: so sánh xung đầu ra
– Hỗ trợ điều khiển Encoder và Hall-sensor
– Đầu vào kích hoạt cho đồng hồ bên ngoài hoặc quản lý theo chu kì
Chi tiết các bạn tham khảo mục 15.2 trong Reference Manual.

Trong bài này chúng ta sẽ cấu hình Timer 2 ở chế độ Time-base unit.
Mô tả chức năng này như sau:
– Time-base unit là chế độ chính của Timer, bao gồm các chế độ đếm: lên, xuống hoặc cả lên và xuống
– Xung Clock được chia bởi bộ chia tần Prescaler để lấy thời gian thích hợp cho mỗi lần đếm
– Các thanh ghi quản lý bao gồm:
+ Counter Register (TIMx_CNT): lưu giá trị đếm
+ Prescaler Register (TIMx_PSC): lưu giá trị chia từ tần số cơ sở cấp cho Timer để tạo ra tần số thích hợp
+ Auto-Reload Register (TIMx_ARR): lưu giá trị đích đếm lên hoặc đếm xuống
– Thanh ghi Auto-Reload sẽ được nạp trước khi Timer hoạt động, và có thể nạp lại trong khi Timer hoạt động, trước khi một sự kiện cập nhật xảy ra (UEV).
– Bộ đếm sẽ hoạt động khi bit CEN của thanh ghi TIMx_CR1 được bật
Chi tiết các bạn tham khảo mục 15.3.1.

Nhìn vào mô tả này chúng ta có thể viết ra các bước để Timer hoạt động như sau:
- Khởi tạo bộ Timer với xung Clock thích hợp (tất cả các ngoại vi đều phải có bước này)
- Ghi giá trị cho bộ chia tần Prescaler
- Ghi giá trị cho thanh ghi Auto-reload
- Bật Timer cho hoạt động
- Xử lý các sự kiện xảy ra như tràn…
Các bước cơ bản là vậy, sau đây mình sẽ hướng dẫn các bạn cấu hình trên CubeMX.
Cấu hình Timer STM32 chế độ Time Base
Đề bài của chúng ta như sau: sử dụng Timer STM32, cụ thể là Timer 2, nhấp nháy led mỗi 500ms một lần.
Các bạn mở phần mềm, chọn chip STM32F103C8, cấu hình SYS Debug: Serial Wire.
Chuyển qua tab Clock Configuration, chúng ta chọn thạch anh nội 8MHz. Vì xung Clock của Timer 2 được cấp bởi bộ APB1 nên xung Internal Clock của Timer có tần số là 8MHz.

Chi tiết về cách cấu hình Clock các bạn xem Bảng 1 mục 3.1.

Trong tab Timers các bạn chọn TIM2. Clock Source chọn Internal Clock.
Trong bảng Parameter Settings:
Prescaler chọn 8000 để đếm mỗi 1ms, vì 8MHz/8000 = 1kHz => T = 1ms
Counter Mode: Up hay Down đều được
Counter Period: 499, đếm từ 0 đến 499 là 500 lần 1ms, ta sẽ được 500ms
Auto-reload preload: Enable
Các thông số còn lại giữ nguyên.
Lưu ý: Bộ chia thực tế bằng giá trị thanh ghi PSC + 1, Counter Period cũng vậy (ARR + 1). Muốn chia đúng 8000 thì các bạn điền 8000-1 vào ô Prescaler (CubeMX cho phép gõ cả biểu thức). Điền 8000 thì sai số chỉ 0.0125%, với bài nháy led không ảnh hưởng gì, nhưng khi đo thời gian chính xác thì phải để ý chỗ này nhé.

Chọn PC13 là Output, đặt label LED.

Trong NVIC tick vào TIM2 global interrupt để bật ngắt cho Timer 2.

Sau đó đặt tên Project, chọn Toolchain MDK-ARM V5 và nhấn Generate Code.

Lập trình Timer STM32 chớp tắt led
Mở Project, nhấn Build (F7).
Trong file stm32f1xx_it.c đã có hàm thực thi ngắt Timer 2, các bạn nhấn chuột phải vào hàm HAL_TIM_IRQHandler(&htim2); chọn Go To Definition.

Tại đây các bạn nhấn Ctrl+F, tìm từ “update”, nhấn Find Next sẽ thấy phần xử lý ngắt Update Event.
Nhấn chuột phải vào hàm HAL_TIM_PeriodElapsedCallback(htim); chọn Go To Definition để tìm tới nơi viết hàm đó.
Đây là hàm được gọi về khi có sự kiện ngắt Update xảy ra.

Copy hàm đó dán vào phía trên hàm main() trong main.c, nhớ bỏ từ khóa __weak đi nhé.
Sau đó trong hàm này chúng ta viết thêm lệnh đảo trạng thái led như sau:

Trước while(1) ta khởi chạy Timer kèm ngắt bằng câu lệnh sau:

Lưu ý: Phải dùng hàm HAL_TIM_Base_Start_IT() có đuôi _IT. Nếu dùng HAL_TIM_Base_Start() thì Timer vẫn đếm nhưng không sinh ngắt, hàm Callback không bao giờ được gọi và led sẽ đứng im.
Sau đó build, nạp chương trình và xem kết quả, led PC13 sẽ nháy đều đặn mỗi 500ms mà vòng while(1) vẫn trống trơn, đúng không nào.
Kết
Timer là một ngoại vi không thể thiếu trong lập trình nhúng. Timer STM32 ngoài việc dùng để hẹn giờ, còn được sử dụng để đo thời gian của các xung, tạo ra các xung có tần số khác nhau mà bài sau chúng ta sẽ đề cập. Hẹn gặp lại các bạn trong bài tiếp theo nhé!


Sao em làm đến bước go to define HAL_TIM_IRQHandler(&htim2); nó lại báo lỗi cái này chưa đc define vậy à 🙁 em ktra các bước trên làm đúng hết r mà 🙁
À thôi anh ơi :V em biết sao bị vậy e 😛
Nhấn F7 để Build trc nhé
anh ơi, như vậy là bị sao, em cx bị
bị sao em?
Hay, cảm ơn ad.
A ơi sao e làm chạy được đèn led PC13 sáng nhưng nó lại không nhấp nháy ạ