Bare metal C Ch08 SysTick - Hongjayeong-02/Basic_SoC_implementation GitHub Wiki

Ch08. SysTick

목표
빈 loop delay 대신 Cortex-M4 core의 SysTick timer를 사용해 millisecond 단위 delay를 구현한다.


1. SysTick이란?

SysTick은 STM32 vendor peripheral이 아니라 ARM Cortex-M core에 포함된 system timer다.


2. SysTick Register

CMSIS | 역할 -- | -- SysTick->CTRL | Enable / Clock source / Count flag SysTick->LOAD | Reload value SysTick->VAL | Current counter SysTick->CALIB | Calibration

SysTick은 24-bit down counter다.


3. 주요 CTRL Bit

bit 0  ENABLE
bit 1  TICKINT
bit 2  CLKSOURCE
bit 16 COUNTFLAG

이번 실습은 polling 방식이므로 TICKINT는 사용하지 않는다.


4. 1 ms Reload 계산

Project 기록의 system clock은 16 MHz다.

16,000,000 × 0.001
= 16,000 cycles

따라서:

LOAD = 16000 - 1
     = 15999

5. COUNTFLAG Polling

while ((SysTick->CTRL & CTRL_COUNTFLAG) == 0UL)
{
}

Notion에서는 COUNTFLAG를 읽으면 clear되는 SysTick 특성을 핵심 포인트로 정리했다.


6. 실제 프로젝트 구조

Ch08_systick/
├── Inc/
│   ├── gpio.h
│   └── systick.h
└── Src/
    ├── gpio.c
    ├── systick.c
    └── main.c

7. 전체 코드

Inc/gpio.h

#ifndef GPIO_H
#define GPIO_H

#include <stdbool.h>

/* ---- LED (PB0, LD1 녹색) ---- */ void led_init(void); void led_on(void); void led_off(void);

/* ---- Push Button (PC13, B1) ---- */ void button_init(void); bool get_btn_state(void);

void led_toggle(void); /* Ch08에서 추가 */

#endif /* GPIO_H */

Src/gpio.c

#include "stm32f4xx.h"
#include "gpio.h"

#define LED_PIN 0U /* PB0 = LD1 (녹색) / #define BTN_PIN 13U / PC13 = B1 */

void led_init(void) { RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;

GPIOB-&gt;MODER &amp;= ~(0x3UL &lt;&lt; (LED_PIN * 2));
GPIOB-&gt;MODER |=  (0x1UL &lt;&lt; (LED_PIN * 2));

}

void led_on(void) { GPIOB->BSRR = (1UL << LED_PIN); /* Set */ }

void led_off(void) { GPIOB->BSRR = (1UL << (LED_PIN + 16)); /* Reset */ }

void button_init(void) { RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;

GPIOC-&gt;MODER &amp;= ~(0x3UL &lt;&lt; (BTN_PIN * 2));  /* Input */
GPIOC-&gt;PUPDR &amp;= ~(0x3UL &lt;&lt; (BTN_PIN * 2));  /* No pull (보드가 자체 처리) */

}

bool get_btn_state(void) { return (GPIOC->IDR & (1UL << BTN_PIN)) != 0U; /* active-HIGH */ }

void led_toggle(void) { GPIOB->ODR ^= (1UL << 0); /* PB0 = LD1 */ }

Inc/systick.h

#ifndef SYSTICK_H_
#define SYSTICK_H_

#include <stdint.h> #include "stm32f4xx.h"

void systick_msec_delay(uint32_t delay);

#endif /* SYSTICK_H_ */

Src/systick.c

#include "systick.h"

#define CTRL_ENABLE (1UL << 0) #define CTRL_CLKSRC (1UL << 2) #define CTRL_COUNTFLAG (1UL << 16)

/* 코어클럭 16MHz(기본 HSI) 기준 1ms당 클럭 사이클 수 */ #define ONE_MSEC_LOAD 16000UL

void systick_msec_delay(uint32_t delay) { SysTick->LOAD = ONE_MSEC_LOAD - 1UL; SysTick->VAL = 0UL; SysTick->CTRL = CTRL_CLKSRC; SysTick->CTRL |= CTRL_ENABLE;

for (uint32_t i = 0; i &lt; delay; i++)
{
    while ((SysTick-&gt;CTRL &amp; CTRL_COUNTFLAG) == 0UL) { }
}

SysTick-&gt;CTRL = 0UL;

}

Src/main.c

#include "gpio.h"
#include "systick.h"

int main(void) { led_init();

while (1)
{
    systick_msec_delay(500);
    led_toggle();
}

}


8. systick_msec_delay() 동작

LOAD = 15999
│
▼
VAL = 0
│
▼
CLKSOURCE 설정
│
▼
ENABLE
│
▼
COUNTFLAG polling
│
├─ 1 ms 완료
│
└─ delay 횟수만큼 반복
│
▼
SysTick disable

9. 왜 for 안에 while이 있는가?

systick_msec_delay(500);

은 1 ms 완료를 500번 반복해서 500 ms delay를 만든다.


10. led_toggle()이 ODR을 쓰는 이유

BSRR에는 toggle 기능이 없으므로:

GPIOB->ODR ^= (1UL << 0);

으로 PB0 bit를 XOR toggle한다.


11. Hardware 흐름

Cortex-M4 Core
│
▼
SysTick 24-bit Counter
│
▼
1 ms COUNTFLAG
│
▼
500회 반복
│
▼
led_toggle()
│
▼
GPIOB ODR bit0
│
▼
LD1 Toggle

12. 실습 기록

Notion 기록:

text = 764 bytes
data = 0 bytes
bss  = 2560 bytes
warning 없음

13. 핵심 정리

  • SysTick은 Cortex-M core peripheral이다.

  • 16 MHz에서 1 ms는 16,000 cycle이다.

  • Reload value는 16000 - 1을 사용한다.

  • COUNTFLAG polling으로 delay를 구현한다.

  • GPIO/TIM 같은 STM32 peripheral과 달리 SysTick은 ARM core 공통 기능이다.

⚠️ **GitHub.com Fallback** ⚠️