Cấu hình SysTick trên STM32CubeMX để tạo độ trễ chính xác

Nguyên lý hoạt động của bộ đếm SysTick

SysTick là bộ đếm 24-bit tích hợp trong nhân Cortex-M, hoạt động như bộ định thời hệ thống với các đặc điểm:

  • Đếm lùi từ giá trị nạp lại (LOAD)
  • Tự động tải lại giá trị khi về 0
  • Nguồn xung: HCLK hoặc HCLK/8

Ví dụ cấu hình tại 72MHz với nguồn HCLK:

Thời_gian_mỗi_xung = 1 / 72,000,000 ≈ 13.89ns
SysTick->LOAD = 71999;  // Cho 1ms
SysTick->VAL = 0;
SysTick->CTRL = 0x07;   // Kích hoạt | Ngắt | HCLK

So sánh phương pháp tạo độ trễ

Phương phápẢnh hưởng tối ưuĐộ chính xác
Vòng lặp forThấp
SysTickKhôngCao
HAL_Delay()KhôngTrung bình-Cao

Cấu hình STM32CubeMX

Thiết lập clock tree là bước quan trọng:

  1. Kích hoạt HSE (External oscillator)
  2. Cấu hình PLL với hệ số chia thích hợp
  3. Xác minh giá trị SystemCoreClock
RCC_OscInitTypeDef cauhinh_osc = {0};
RCC_ClkInitTypeDef cauhinh_clock = {0};

cauhinh_osc.OscillatorType = RCC_OSCILLATORTYPE_HSE;
cauhinh_osc.HSEState = RCC_HSE_ON;
cauhinh_osc.PLL.PLLState = RCC_PLL_ON;
cauhinh_osc.PLL.PLLSource = RCC_PLLSOURCE_HSE;
cauhinh_osc.PLL.PLLM = 8;
cauhinh_osc.PLL.PLLN = 192;
cauhinh_osc.PLL.PLLP = RCC_PLLP_DIV2;
HAL_RCC_OscConfig(&cauhinh_osc);

cauhinh_clock.ClockType = RCC_CLOCKTYPE_SYSCLK;
cauhinh_clock.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
cauhinh_clock.AHBCLKDivider = RCC_SYSCLK_DIV2;
HAL_RCC_ClockConfig(&cauhinh_clock, FLASH_LATENCY_3);

Thiết lập SysTick trong CubeMX

  • Kích hoạt ngắt SysTick (System Core > SysTick)
  • Đặt độ ưu tiên ngắt (NVIC Settings) ở mức 3-4
  • Sử dụng callback HAL_SYSTICK_Callback cho tác vụ định kỳ

Triển khai hàm độ trễ tùy chỉnh

Khởi tạo hệ số:

uint32_t heso_us;

void KhoiTaoDoTre(void) {
    heso_us = SystemCoreClock / 1000000U;
}

Hàm delay micro giây:

void Tre_us(uint32_t us) {
    uint32_t batdau = SysTick->VAL;
    uint32_t dem = us * heso_us;
    
    while(dem > 0) {
        uint32_t hientai = SysTick->VAL;
        uint32_t chenhlech;
        
        if(hientai <= batdau) {
            chenhlech = batdau - hientai;
        } else {
            chenhlech = batdau + (0xFFFFFF - hientai) + 1;
        }
        
        if(chenhlech >= heso_us) {
            dem--;
            batdau = hientai;
        }
    }
}

Hàm delay mili giây:

void Tre_ms(uint32_t ms) {
    for(uint32_t i=0; i<ms; i++) {
        Tre_us(1000);
    }
}

Xử lý vấn đề thực tế

Chống tối ưu hóa trình biên dịch:

__attribute__((optimize("O0")))
void Tre_us(uint32_t us) {
    // Nội dung hàm
}

Sử dụng DWT cho độ chính xác cao:

void Tre_us_DWT(uint32_t us) {
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
    DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
    
    uint32_t batdau = DWT->CYCCNT;
    uint32_t chu_ky = us * (SystemCoreClock / 1000000);
    
    while((DWT->CYCCNT - batdau) < chu_ky);
}

Bộ định thời mềm cho hệ thống

typedef struct {
    uint32_t thoigian;
    uint32_t khoang;
    void (*ham)(void);
    uint8_t kichhoat;
} DinhThiMem;

DinhThiMem dinhthi[5];

void BatDinhThi(uint8_t id, uint32_t ms, void(*ham)(void)) {
    dinhthi[id].thoigian = HAL_GetTick() + ms;
    dinhthi[id].ham = ham;
    dinhthi[id].kichhoat = 1;
}

void XuLyDinhThi(void) {
    for(uint8_t i=0; i<5; i++) {
        if(dinhthi[i].kichhoat && HAL_GetTick() >= dinhthi[i].thoigian) {
            dinhthi[i].ham();
            dinhthi[i].kichhoat = 0;
        }
    }
}

Tích hợp với RTOS

Xử lý ngắt SysTick trong môi trường RTOS:

void SysTick_Handler(void) {
    HAL_IncTick();
    osSystickHandler();  // Cho FreeRTOS
}

Thẻ: STM32 SysTick STM32CubeMX HAL delay

Đăng vào ngày 4 tháng 6 lúc 02:26