Kit STM32F4 Discovery – Bài 3: Timer hệ thống System Timer

0
5177

Giới thiệu

Trên tất cả các vi điều khiển ARM Cortex-M, bao gồm STM32F4, có tích hợp một timer nhỏ, gọi là timer hệ thống (System Timer), gọi tắt là SysTick. Timer này được tích hợp như một phần của bộ xử lý ngắt lồng nhau NVIC và có thể khởi tạo SysTick Exception (exception type #15). Bản chất của Systick là một timer  24-bit, thực hiện việc đếm xuống, nghĩa là nó thực hiện việc đếm từ một giá trị reload nào đó xuống đến 0 sau đó nạp lại giá trị reload trước khi lặp lại việc đếm xuống. Giá trị reload luôn nhỏ hơn 224. Nguồn tạo xung nhịp cho SysTick có thể là tần số xung nhịp hệ thống SYSCLC hoặc tần số xung nhịp hệ thống chia cho 8 (SYSCLK/8).

Trong các hệ điều hành hiện đại cần có một ngắt có tính chu kỳ để nhân hệ điều hành có thể gọi đến thường xuyên, chẳng hạn để phục vụ cho việc quản lý tác vụ hoặc chuyển ngữ cảnh. SysTick chính là công cụ được dùng để cung cấp xung nhịp cho hệ điều hành hoặc để tạo ra ngắt có tính chu kỳ phục vụ cho các tác vụ được lập lịch cũng như cho bất cứ công việc nào khác cần đến ngắt chu kỳ.

Sở dĩ SysTick được thiết kế ngay bên trong CPU của Cortex-M là để tạo ra tính khả chuyển cho phần mềm. Chính nhờ việc tất cả các vi xử lý Cortex-M có chung SysTick timer mà hệ điều hành được viết cho vi điều khiển Cortex-M này có thể chạy trên vi điều khiển Cortex-M khác. Khi không cần đến hệ điều hành nhúng cho ứng dụng, SysTick vẫn có thể được dùng như một thiết bị ngoại vi định thời đơn giản để khởi tạo ngắt chu kỳ, khởi tạo hàm delay hoặc dùng đo đếm thời gian. Để lập trình với SysTick chúng ta cần tác động lên các thanh ghi của timer này. Đối với ARM Cortex-M4, có 4 thanh ghi cho SysTick như sau:

Địa chỉKý hiệu trong CMSIS-CoreThanh ghi
0xE000E010SysTick->CTRLSysTick Control and Status Register
0xE000E014SysTick->LOADSysTick Reload Value Register
0xE000E018SysTick->VALSysTick Current Value Register
0xE000E01CSysTick->CALIBSysTick Calibration Register

Để khởi tạo ngắt SysTick có tính chu kỳ, cách đơn giản nhất là dùng hàm CMSIS-Core có tên là “SysTick_Config”:

uint32_t SysTick_Config(uint32_t ticks);

Hàm này được khai báo trong file core_cm4.h như sau:

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{

/* Reload value impossible */
if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);     
       
/* set reload register */
SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      

/* set Priority for Systick Interrupt */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  

/* Load the SysTick Counter Value */
SysTick->VAL   = 0;                                          

/* Enable SysTick IRQ and SysTick Timer */
SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                    

/* Function successful */
return (0);		
}

Hàm SysTick_Config() cho phép thiết lập khoảng ngắt cho SysTick là “ticks” lần đếm, cho phép bộ đếm sử dụng bộ tạo xung của CPU và cho phép xảy ra ngắt SysTick với độ ưu tiên thấp nhất.
Ví dụ, tần số làm việc của bộ tạo xung nhịp hệ thống là 168MHz, chúng ta muốn tần suất xảy ra ngắt SysTick là 1 KHz (1000 lần / 1s) hay mỗi ms xảy ra ngắt 1 lần, chúng ta gọi hàm như sau:

SysTick_Config(SystemCoreClock / 1000);

Biến “SystemCoreClock” cần được thiết lập giá trị đúng là 168×106.
Nói cách khác, chúng ta có thể gọi trực tiếp như sau:

SysTick_Config(168000); // 1680MHz / 1000 = 168000

Hàm “SysTick_Handler(void)” khi đó sẽ tự động được gọi mỗi ms một lần.
Nếu giá trị tham số truyền vào cho hàm SysTick_Config không vừa với thanh ghi 24-bit reload value (lớn hơn 0xFFFFFF), hàm SysTick_Config trả về giá trị 1, ngược lại trả về giá trị 0.
Dưới đây là ví dụ ứng dụng SysTick để khởi tạo hàm delay:

Ví dụ

/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"

/* Private typedef -----------------------------------------------------------*/
GPIO_InitTypeDef  GPIO_InitStructure;

/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static __IO uint32_t TimingDelay;
/* Private function prototypes -----------------------------------------------*/
void Delay(__IO uint32_t nTime);
/* Private functions ---------------------------------------------------------*/

/**
  * @brief  GPIO pin toggle program
  * @param  None
  * @retval None
  */
int main(void)
{

  SystemInit();

  /* GPIOD Periph clock enable */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

  /* Configure PD12, PD13, PD14 and PD15 in output pushpull mode */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOD, &GPIO_InitStructure);

  if (SysTick_Config(SystemCoreClock / 1000))
  {
    /* Capture error */
    while (1);
  }

  while (1)	
  {
    /* PD12 to be toggled */
    GPIO_SetBits(GPIOD, GPIO_Pin_12);

    /* Insert delay */
    Delay(500);

    /* PD13 to be toggled */
    GPIO_SetBits(GPIOD, GPIO_Pin_13);

    /* Insert delay */
    Delay(500);

    /* PD14 to be toggled */
    GPIO_SetBits(GPIOD, GPIO_Pin_14);

    /* Insert delay */
    Delay(500);

    /* PD15 to be toggled */
    GPIO_SetBits(GPIOD, GPIO_Pin_15);

    /* Insert delay */
    Delay(500);

    GPIO_ResetBits(GPIOD, GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);

    /* Insert delay */
    Delay(1000);
  }
}

/**
  * @brief  Inserts a delay time.
  * @param  nTime: specifies the delay time length, in milliseconds.
  * @retval None
  */
void Delay(__IO uint32_t nTime)
{
  TimingDelay = nTime;

  while(TimingDelay != 0);
}

/**
  * @brief  Decrements the TimingDelay variable.
  * @param  None
  * @retval None
  */
void SysTick_Handler(void)
{
  if (TimingDelay != 0x00)
  {
    TimingDelay--;
  }
}

Hàm SysTick_Handler(void) là hàm phục vụ ngắt SysTick. Theo cài đặt trên thì cứ 1ms ngắt sẽ xảy ra 1 lần, mỗi lần ngắt xảy ra hàm phục vụ ngắt sẽ được gọi tự động. Với nội dung hàm Delay() và SysTick_Handler() như trên, ban đầu TimingDelay sẽ nhận giá trị tương ứng với số ms cần delay, mỗi 1ms sẽ xảy ra ngắt 1 lần và khi ngắt xảy ra nó sẽ giảm biến TimingDelay 1 đơn vị. Như vậy lời gọi hàm Delay(n) sẽ tương ứng với thực hiện giữ chậm n (ms).

LEAVE A REPLY

Please enter your comment!
Please enter your name here