SysTick trên Tiva C là gì?

Cortex-M4 được tích hợp một timer hệ thống tên là SysTick, nó cung cấp một bộ đếm lùi 24 bit đơn giản với có chế điều khiển mềm dẻo. Bộ đếm có thể được sử dụng theo nhiều cách, ví dụ:

  • Đồng hồ bấm giờ cho RTOS có thể lập trình được
  • Bộ đếm báo động tốc độ cao dùng xung nhịp của hệ thống
  • Một bộ đếm đơn giản đo thời gian hoàn thành hoặc thời gian đã sử dụng

Tận dụng SysTick cho bạn thêm một timer để làm các công việc định thời đơn giản.

Hàm API SysTick trong thư viện TivaWare

Đặt chu kì cho SysTick, giá trị Period nằm trong khoảng từ 1 đến 2^24 do thanh ghi STRELOAD chứa giá trị này là thanh ghi 32bit tuy nhiên bit 24 đến 31 không được sử dụng (cũng không biết tại sao nsx làm vậy :D )

void SysTickPeriodSet(uint32_t ui32Period)

Ngoài ra còn có các hàm cần khai báo lần lượt là:

void SysTickIntRegister(void (*pfnHandler)(void)): đăng ký hàm được gọi khi ngắt SysTick xảy ra, trong ví dụ bên dưới là hàm void SysTick_ISR().

void SysTickIntEnable(void): cho phép ngắt SysTick.

void SysTickEnable(void): cho phép counter của SysTick bắt đầu chạy.

Bên dưới là code mẫu chớp tắt LED: LED đỏ chu kì 1 giây, LED xanh dương chu kì 2 giây và LED xanh lá chu kì 6 giây. Riêng LED xanh lá chớp tắt theo kiểu dùng hàm delay truyền thống, đặt trong vòng while, 2 LED còn lại hoàn toàn không bị ảnh hưởng do được điều khiển thông qua ngắt SysTick.

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/systick.h"

volatile uint32_t time = 0;
uint32_t RedLedTime = 0, BlueLedTime = 0;

void SysTick_ISR()
{
    time++;
    if ((RedLedTime + 500) < time)
     {
         GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, ~GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_1));
		 // Đọc giá trị PF1 từ thanh ghi, đảo bit (~) sau đó ghi trở lại.
         RedLedTime = time;
     }
     if ((BlueLedTime + 1000) < time)
      {
          GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, ~GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_2));
          BlueLedTime = time;
      }
}

int main(void)
{
    // 80MHz
    SysCtlClockSet(SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ);

    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    SysCtlDelay(10);

    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3);
	
    SysTickPeriodSet(SysCtlClockGet()/1000);
    SysTickIntRegister(&SysTick_ISR);
    SysTickIntEnable();
    SysTickEnable();

	while(1)
	 {
		 SysCtlDelay(SysCtlClockGet());
		 GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, ~GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_3));
	 }
}

Ở đây mình lấy period bằng tần số clock chia cho 1000, tương ứng với 1ms mỗi lần ngắt SysTick xảy ra, biến time sẽ lưu thời gian (tính theo ms) từ lúc code bắt đầu chạy. Biến time có độ lớn là 32bit, để tràn biến này, TM4C123GH6PM phải chạy khoảng 49 ngày.

Cách tính period:

Trong tài liệu “TivaWare™ Peripheral Driver Library for C Series User’s Guide (Rev. E)” có mô tả hàm void SysTickPeriodSet(uint32_t ui32Period) như sau:

“This function sets the rate at which the SysTick counter wraps, which equates to the number of processor clocks between interrupts.”

Giá trị uint32_t ui32Period sẽ bằng với số chu kì lệnh của vi xử lý giữa các lần ngắt. Với tần số 80MHz, mỗi chu kì lệnh mất (10^9)/(8*10^7) = 12.5ns. Ví dụ mình cần ngắt SysTick xảy ra mỗi 1ms 1 lần thì số period cần đưa vào là 10^6/12.5 = 8*10^4 = SysCtlClockGet()/1000.