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 for | Có | Thấp |
| SysTick | Không | Cao |
| HAL_Delay() | Không | Trung bình-Cao |
Cấu hình STM32CubeMX
Thiết lập clock tree là bước quan trọng:
- Kích hoạt HSE (External oscillator)
- Cấu hình PLL với hệ số chia thích hợp
- 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
}