7 Bộ định thời SysTick 
Giới thiệu về bộ định thời SysTick 
SysTick là một bộ định thời đơn giản, hoạt động như bộ đếm ngược 24-bit với khả năng tự động nạp lại. Nó có thể được sử dụng như một bộ đếm tiêu chuẩn, và có thể ngăn chặn việc tạo ra các ngắt không mong muốn trong hệ thống.
- Bộ xử lý Cortex-M4 được tích hợp sẵn bộ định thời SysTick, vì vậy tất cả vi điều khiển sử dụng nhân M4 đều có bộ định thời này.
- Điều này giúp việc chuyển đổi mã nguồn giữa các thiết bị khác nhau trở nên dễ dàng hơn, đặc biệt trong các ứng dụng hệ điều hành nhúng.
Ứng dụng:
- SysTick thường được sử dụng trong hệ điều hành thời gian thực (RTOS) để tạo nhịp hệ thống (tick timer), cung cấp "nhịp tim" cho việc lập lịch tác vụ.
- Nhờ tính phổ biến trên mọi vi điều khiển dùng nhân M4, việc sử dụng SysTick giúp tránh được những rắc rối thường gặp khi dùng bộ định thời thông thường trong quá trình porting phần mềm.
Tần số xung nhịp 
- RCU (Reset and Clock Unit) cấp xung nhịp cho SysTick bằng cách chia xung AHB (HCLK) (Advanced High-performance Bus - Hardwired Clock ) cho 8.
- Tuy nhiên, có thể lựa chọn giữa xung HCLK hoặc HCLK/8 làm nguồn clock cho SysTick thông qua thanh ghi điều khiển.
Nguyên lý hoạt động 
- Khi bạn gán giá trị khởi đầu cho SysTick và bật nó lên: - Mỗi chu kỳ clock trôi qua sẽ làm giảm giá trị đếm đi 1 đơn vị.
- Khi giá trị đếm giảm về 0, SysTick sẽ: - Tự động nạp lại giá trị ban đầu.
- Tiếp tục đếm.
- Đặt cờ COUNTFLAG về mức 1.
- Nếu ngắt được bật, sẽ kích hoạt ngắt SysTick.
 
 
Bộ Thanh ghi của Bộ Định thời SysTick 
Các thanh ghi liên quan đến bộ định thời SysTick:
- Thanh ghi điều khiển và trạng thái (SysTick_CTRL)- Dùng để bật/tắt SysTick, chọn nguồn xung nhịp, và kiểm tra trạng thái hiện tại của bộ định thời.
 
- Thanh ghi giá trị nạp lại (SysTick_LOAD)- Dùng để cấu hình giá trị nạp lại, tức là số chu kỳ xung nhịp mà bộ định thời sẽ đếm từ khi bắt đầu đến khi về 0.
- Ví dụ: nếu bạn muốn tạo ngắt mỗi 1ms, bạn sẽ tính toán giá trị nạp phù hợp với tốc độ xung nhịp.
 
- Thanh ghi giá trị hiện tại (SysTick_VAL)- Cho phép đọc giá trị đếm hiện tại của bộ định thời.
- Ghi giá trị bất kỳ vào thanh ghi này sẽ xóa giá trị hiện tại và khởi động lại bộ đếm.
 
- Thanh ghi hiệu chuẩn (SysTick_CALIB)- Cung cấp một giá trị hiệu chuẩn sẵn có, giúp bạn tạo ngắt với chu kỳ cố định mà không phụ thuộc vào tần số xung nhịp hệ thống.
 
Tất cả các thanh ghi trên đều được định nghĩa sẵn trong file
core_cm4.h(bạn có thể tra trong thư mục CMSIS). Tham khảo hình minh họa bên dướiDưới đây là phần mã
Cđược trích xuất từ hình ảnh và kèm theo bản dịch tiếng Việt song song:
/**
  * @brief  Structure type to access the System Timer (SysTick).
  *         Cấu trúc để truy cập Bộ định thời hệ thống (SysTick).
  */
typedef struct
{
  __IO uint32_t CTRL;   /*!< Offset: 0x000 (R/W)  SysTick Control and Status Register 
                             Thanh ghi điều khiển và trạng thái SysTick */
  __IO uint32_t LOAD;   /*!< Offset: 0x004 (R/W)  SysTick Reload Value Register 
                             Thanh ghi giá trị nạp lại của SysTick */
  __IO uint32_t VAL;    /*!< Offset: 0x008 (R/W)  SysTick Current Value Register 
                             Thanh ghi giá trị hiện tại của SysTick */
  __I  uint32_t CALIB;  /*!< Offset: 0x00C (R/ )  SysTick Calibration Register 
                             Thanh ghi hiệu chuẩn SysTick */
} SysTick_Type;Giải thích nhanh:
__IO: có thể đọc và ghi (Input/Output).
__I: chỉ đọc (Input).
SysTick_Type: cấu trúc đại diện cho các thanh ghi liên quan đến SysTick, thường dùng trong việc cấu hình delay, ngắt định kỳ...
Gợi ý: SysTick_Config là một hàm tiện ích do CMSIS cung cấp, giúp bạn thiết lập nhanh chu kỳ ngắt mà không cần thao tác trực tiếp với từng thanh ghi.
Dưới đây là phần code C được trích xuất từ hình ảnh, kèm bản dịch song ngữ:
/**
  * @brief  System Tick Configuration
  *         Cấu hình bộ định thời SysTick
  *
  * The function initializes the System Timer and its interrupt, and starts the System Tick Timer.
  * Counter is in free running mode to generate periodic interrupts.
  * Hàm này khởi tạo bộ định thời hệ thống và ngắt của nó, sau đó khởi động bộ định thời SysTick.
  * Bộ đếm sẽ chạy tự do để tạo ra các ngắt định kỳ.
  *
  * @param [in]  ticks  Number of ticks between two interrupts.
  *                     Số lượng chu kỳ giữa hai ngắt.
  *
  * @return      0 Function succeeded.  (0: thành công)
  * @return      1 Function failed.     (1: thất bại)
  *
  * @note  When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the 
  *        function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b> 
  *        must contain a vendor-specific implementation of this function.
  *        Khi biến <b>__Vendor_SysTickConfig</b> được đặt là 1, thì hàm <b>SysTick_Config</b> sẽ không được sử dụng.
  *        Khi đó, tập tin <b><i>device</i>.h</b> phải chứa định nghĩa riêng từ nhà sản xuất cho hàm này.
  */
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible 
                                                             Giá trị nạp lại không hợp lệ */
  SysTick->LOAD  = ticks - 1;                              /* set reload register 
                                                             thiết lập giá trị nạp lại */
  NVIC_SetPriority (SysTick_IRQn, (1 << __NVIC_PRIO_BITS) - 1); /* set Priority for Systick Interrupt 
                                                                   đặt mức ưu tiên cho ngắt SysTick */
  SysTick->VAL   = 0;                                     /* Load the SysTick Counter Value 
                                                             nạp giá trị bộ đếm SysTick */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;              /* Enable SysTick IRQ and SysTick Timer 
                                                             bật ngắt và bộ định thời SysTick */
  return (0);                                             /* Function successful 
                                                             Hàm thực thi thành công */
}Các Hàm Liên Quan Đến Bộ Định Thời SysTick 
Khi sử dụng bộ định thời SysTick, bạn sẽ thường dùng các hàm sau:
1. SysTick_Config() 
- Đây là hàm chính để cấu hình thời gian định kỳ cho SysTick.
- Bạn chỉ cần truyền vào giá trị số chu kỳ xung đếm (ví dụ: số chu kỳ tương ứng với 1ms).
- Hàm này sẽ: - Thiết lập giá trị nạp lại (LOAD)
- Xóa bộ đếm (VAL)
- Bật ngắt
- Bật bộ định thời
 
- Thiết lập giá trị nạp lại (
2. SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource) 
- Dùng để chọn nguồn xung nhịp cho SysTick: - HCLK: tốc độ đầy đủ của hệ thống
- HCLK/8: giảm tốc độ xuống 1/8
 
Trong CMSIS, bạn có thể thấy một số phiên bản dùng tên tương đương là
systick_clksource_set().
Cấu Hình SysTick 
- Trong file board.c, bạn sẽ thấy hàmboard_init().
- Đây là hàm khởi tạo toàn bộ hệ thống, bao gồm cả cấu hình cho SysTick.
- Như hình minh họa (nếu có), hàm này đã thiết lập các thanh ghi SysTick một cách trực tiếp — bạn có thể sử dụng ngay mà không cần viết lại.
🔧 Lưu ý:
board_init()được thiết kế sẵn để bạn không cần loay hoay cấu hình thủ công, rất tiện khi bắt đầu dự án.
/**
 * This function will initial stm32 board.
 * Hàm này sẽ khởi tạo bo mạch STM32.
 */
 void board_init(void)
 {
    /* NVIC Configuration */
    #define NVIC_VTOR_MASK         0x3FFFFF80  // Mặt nạ địa chỉ cho bảng vector
    #ifdef VECT_TAB_RAM
        /* Set the Vector Table base location at 0x10000000 */
        // Thiết lập địa chỉ cơ sở của bảng vector tại 0x10000000 (RAM)
        SCB->VTOR = (0x10000000 & NVIC_VTOR_MASK);
    #else /* VECT_TAB_FLASH */
        /* Set the Vector Table base location at 0x08000000 */
        // Thiết lập địa chỉ cơ sở của bảng vector tại 0x08000000 (FLASH)
        SCB->VTOR = (0x08000000 & NVIC_VTOR_MASK);
    #endif
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);  // Cấu hình nguồn xung nhịp cho SysTick là HCLK
    SysTick->LOAD = 0xFFFF;  // Nạp giá trị đếm (65535) vào thanh ghi LOAD
    SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;  // Bật SysTick và bắt đầu đếm
    // RCC_ClocksTypeDef rcc;
    // RCC_GetClocksFreq(&rcc);  // Đọc tần số xung nhịp hệ thống (bị comment)
 }Sử Dụng Bộ Định Thời SysTick 
Trong file board.c, bạn sẽ thấy hàm:
void delay_ms(uint32_t _ms)
//Định nghĩa của delay_ms được thể hiện dưới đây
void delay_ms(uint32_t _ms) {
    while (_ms--) {
        SysTick->VAL = 0;                      // Clear current value
        SysTick->LOAD = SystemCoreClock / 1000 - 1;  // 1 ms interval {168000000 / 1000 - 1 = 168000 - 1 = 16799}
        SysTick->CTRL = 0x5;                   // Enable timer, no interrupt
        while (!(SysTick->CTRL & (1 << 16)));  // Wait for COUNTFLAG
        SysTick->CTRL = 0;                     // Stop timer
    }
}- Tham số _mslà thời gian cần delay tính theo mili giây (ms).- Ví dụ: - delay_ms(1)→ trễ 1 mili giây
- delay_ms(1000)→ trễ 1 giây
 
 
- Ví dụ: 
Ngoài ra còn có hàm:
void delay_us(uint32_t _us)- Dùng để delay theo micro giây (µs), cú pháp sử dụng cũng tương tự.
Lưu ý: Đây là delay dạng blocking (chặn) – tức là trong thời gian delay, CPU sẽ bị giữ và không làm được việc khác. Điều này tiêu tốn tài nguyên hệ thống, vì vậy nên tránh sử dụng trong các chương trình phức tạp hoặc đa nhiệm.
Thực Hành Nháy Đèn LED Với SysTick 
Sau khi đã học cách sử dụng SysTick, chúng ta sẽ dùng nó để làm LED nháy với chu kỳ 1 giây.
Ý tưởng: 
- Đặt chân LED ở mức cao (sáng)
- Gọi delay_ms(1000)
- Đặt chân LED ở mức thấp (tắt)
- Gọi delay_ms(1000)
- Lặp lại liên tục trong vòng lặp while(1)
Mã nguồn ví dụ: 
Có sẵn trong thư mục sau được chia sẻ qua Github
https://github.com/leybme/stm32f407_resource/tree/main/C03%20Code/Code%20Sample/003LED%20blinking%20with%20SysTick%20timerhttps://download-directory.github.io/ Dùng link này để tải thư mục trên.
Kết quả: 
- Sau khi nạp chương trình vào bo mạch, LED2 sẽ nháy sáng 1 giây, tắt 1 giây, lặp lại liên tục.