🌱 STM32 - 19. Ví Dụ Sử Dụng Systick Timer Trong STM32
Ở post trước, mình đã giới thiệu với các bạn về Systick Timer - Overview & Registers, post này sẽ lấy một ví dụ điển hình để các bạn có thể sử dụng Systick.
👉 Tham khảo tài liệu Core Cortex M4 Device Generic User Guide, chúng ta có hẳn một phần Hints & Tips, mà không cần tham khảo ông nào trên mạng cả 😅
👉 Để cấu hình cho Systick Timer, chúng ta sẽ làm theo 3 bước trên:
- Đặt giá trị đếm ban đầu - Reload Value bằng thanh ghi SYST_RVR.
- Xóa giá trị đếm hiện tại trên thanh ghi SYST_CVR.
- Cấu hình hoạt động cho Systick Timer bằng thanh ghi Control SYST_CSR:
- Chọn nguồn cấp xung Clock cho Systick bằng Bit[2] - CLKSOURCE.
- Cho phép ngắt Systick (nếu sử dụng ngắt) bằng Bit[1] - TICKINT.
- Cho phép bộ đếm Systick hoạt động bằng Bit[0] - ENABLE.
👉 Example - myHAL_Delay()
Ví dụ này mình sẽ sử dụng ngắt Systick để tạo 2 hàm delay (delay ms và delay us). Ý tưởng giống như hàm HAL_Delay nhưng đơn giản hơn.
Vậy đầu tiên mình cần tính toán về Clock, hãy tính làm sao cho mỗi lần Systick Timer tràn, thì chúng ta đã đếm được 1us. Mình sẽ triển khai tính toán trên Vi điều khiển STM32F401 của mình như sau:
💚 Đầu tiên cấu hình Clock
Sử dụng Internal Clock HSI (Xem bài RCC và cấu hình Clock tại đây):
Mình chọn tần số clock là 16MHz và bus AHB cấp cho Systick cũng mang tần số này.
💚 Define các thanh ghi Systick
Các bạn có thể tham khảo tài liệu Core để xem địa chỉ các thanh ghi của Systick, ở post trước về Systick Registers mình cũng đã nói qua.
💚 Cấu hình cho Systick Timer
Mình sẽ chọn Clock Source cho Systick là External Clock, tức là AHB/8 = 16MHz/8 = 2MHz. Như vậy, mỗi một chu kỳ xung clock sẽ là 1/2MHz = 0.5us. Vậy để đếm được 1us, chúng ta chỉ cần 2 chu kỳ clock là đủ.
⇒ Cần nạp thanh ghi Reload giá trị là 2 - 1 = 1 (vì đếm từ N-1 xuống 0). Sau đó chúng ta cho phép ngắt Systick bằng thanh ghi SYST_CSR.
💚 Cấu hình cho hàm Delay
Cho phép bộ đếm chạy và bắt đầu đếm số lần tràn của Systick Timer bằng chương trình phục vụ ngắt SysTick_Handler(). Dưới đây là đoạn code hoàn chỉnh:
#include <stdint.h>
#define RCC_BASE 0x40023800
#define SYSTICK_BASE 0xE000E010
// RCC
#define RCC_CR (*(volatile uint32_t *)(RCC_BASE + 0x00))
#define RCC_CFGR (*(volatile uint32_t *)(RCC_BASE + 0x08))
// Systick
#define SYST_CSR (*(volatile uint32_t *)(SYSTICK_BASE + 0x00))
#define SYST_RVR (*(volatile uint32_t *)(SYSTICK_BASE + 0x04))
#define SYST_CVR (*(volatile uint32_t *)(SYSTICK_BASE + 0x08))
// RCC
#define RCC_CR_HSION (1 << 0)
#define RCC_CR_HSIRDY (1 << 1)
#define RCC_CFGR_SW_HSI (0 << 0)
#define RCC_CFGR_HPRE_DIV1 (0 << 4)
// SYST_CSR
#define SYST_CSR_COUNTFLAG (1 << 16)
#define SYST_CSR_CLKSOURCE (1 << 2)
#define SYST_CSR_TICKINT (1 << 1)
#define SYST_CSR_ENABLE (1 << 0)
// Global Variable to count the Systick Overflow
volatile uint32_t usTicks = 0;
// Clock Configuration
void SystemClock_Config(void) {
RCC_CR |= RCC_CR_HSION; // Enable HSI
while (!(RCC_CR & RCC_CR_HSIRDY)); // Wait for HSI ready
RCC_CFGR &= ~(RCC_CFGR_SW_HSI); // SYSCLK = HSI = 16 MHz
RCC_CFGR &= ~(RCC_CFGR_HPRE_DIV1); // AHB = 16 MHz
}
// Systick Initialization
void SysTick_Init(void) {
SYST_RVR = 1; // Reload = 2 - 1 (1us with clock 2MHz)
SYST_CVR = 0; //
SYST_CSR = 0; // Clear old configuration
SYST_CSR |= SYST_CSR_TICKINT; // Enable Systick Interrupt
SYST_CSR &= ~SYST_CSR_CLKSOURCE; // Clock = AHB/8 = 2MHz
SYST_CSR |= SYST_CSR_ENABLE; // Enable Systick
}
// Systick Interrupt Handler
void SysTick_Handler(void) {
if (usTicks > 0) {
usTicks--;
}
if (usTicks == 0) {
SYST_CSR &= ~SYST_CSR_ENABLE; // Disable Systick after Done
}
}
// Hàm delay microsecond
void Delay_us(uint32_t us) {
usTicks = us * 2; // Each us need 2 tick
SYST_CVR = 0; // Reset counter
SYST_CSR |= SYST_CSR_ENABLE; // Enable Systick
while (usTicks); // Wait for Counting
}
// Delay millisecond
void Delay_ms(uint32_t ms) {
Delay_us(ms * 1000); // 1ms = 1000us
}
int main(void) {
SystemClock_Config();
SysTick_Init();
while (1) {
Delay_ms(1000); // Delay 1s
// Add Your Application Code (Example: Blinked LED)
}
}
💬 Ở đây mỗi tick chúng ta sẽ nhảy vào SysTick_Handler(). Mỗi microsecond sẽ tương ứng là 2 tick. Khi nhảy vào ISR, chúng ta sẽ giảm tick này đi, cho đến khi usTicks này bằng 0 thì dừng và disable Systick.
💬 Có hàm Delay_us rồi thì hàm Delay_ms cũng rất đơn giản, 1ms = 1000us.
➤ Các bạn có thể DOWNLOAD Code Tại đây!
>>>>>> Follow ngay <<<<<<<
Để nhận được những bài học miễn phí mới nhất nhé 😊
Chúc các bạn học tập tốt 😊
dạ anh cho em hỏi ở hàm Systick_Handler() chỗ while(usTisks); mình truyền usTicks bằng 1000 thì làm sao để chương trình thoát được vòng while đó ạ
Trả lờiXóaà em thấy cái hàm ở dưới r ạ, nãy em k để ý. cảm ơn anh ạ
Trả lờiXóamình gọi hàm Systick_Handler() ở trong vòng while(usTisks); đúng không ạ
Trả lờiXóaKhông nhé, Systick_Handler() là hàm phục vụ ngắt và sẽ được thực thi khi ngắt Systick xảy ra
Xóadạ anh cho em hỏi làm sao để chương trình gọi hàm Systick_Handler() khi có ngắt xảy ra vậy ạ
Trả lờiXóaEm tìm hiểu về flow của ngắt nhé, khi ngắt xảy ra CPU sẽ tìm tới vector table và lấy được địa chỉ của hàm Systick_Handler
Xóa