본문 바로가기
Robot/MCU

Cortex-M3 Peripheral : SysTick Timer

by lifeseed 2013. 9. 26.

※ 아래의 내용은 직접 작성한 내용이며, 경어를 사용하지 않았습니다.

읽으시는동안 불편하시더라도 이해 부탁드립니다.

그리고 테스트를 위한 보드는 http://oroca.org 에서 공동구매한 stm32 smart robot board를 사용하였습니다.

 

0. 시작하기 전에

Cortex-M3 는 공통적으로 사용된다고 생각되는 Peripheral 을 Core에서 제공한다.

Nested Vectored Interrupt Controller

System Control Block

System Timer (SysTick)

그리고 마지막으로 Memory Protection Unit인데, MPU는 Optional로 지원이 되지 않는 Chipset도 있다.

 

이와 관련해서 참조해야할 문서가 있다.

ARM에서 제공하는 Cortex-M3 Device Generic User Guide와 Cortex-M3 Techenical Reference Manual 이다.

그리고 ARM에서 제공하는 기능들을 ST에서 정리하여 배포하는 Cortex-M3 Programming Manual이 있다.

 

이 강좌에서는 가장 짧고 쉽게 접근할 수 있는 System Timer에 대해 알아보도록 하자.

 

1. Register Map Table

ARM Cortex-M3 Device Generic User Guide는 다음과 같이 속성만 표시하며, 세부 설명은 따로 설명하고 있다.

 

 

다음에 보여지는 Register Map Table은 Cortex-M3 Programming Manual 에 수록된 내용이다.

 

두개의 MAP을 비교하면 Register Name이 다르고, 세부 Bit 내용을 ST의 문서에는 표시하고 있다.

 

어쨌든 System Timer는 굉장히 Simple하게 3개의 R/W Register 설정만으로 Control이 가능하다.

 

2. Timer 구동 방법

Timer를 구동시키는 순서는 다음과 같다.

1) LOAD Register에 Timer Period를 위한 Tick Count를 설정한다.

2) VAL 값을 0으로 초기화 시킨다.

3) CTRL 값을 설정하여 Timer를 Enable시킨다.

 

여기까지 하면 VAL에 기록되는 Tick Count값이 증가하고 LOAD에 기록된 값과 같아 지는 순간 Timer가 완료되었다고 알려준다.

 

이제 각 Register에 대한 내용을 위 설정 순서대로 살펴보도록 하자.

A. LOAD (0xE000 E014)

- 사용자가 설정하는 Timer Period Tick Count로 24Bit 사용가능하다. 즉 최대 값은 0x00FFFFFF 이된다.

Clock값이 72MHz라고 가정하면 233ms가 Timer 만료를 알려주는 최대 주기 시간이 된다.

 

 

B. VAL (0xE000 E018)

- System Counter의 현재 Tick Count값이다. 앞서 언급한 바와 같이 이 값은 Timer가 Enable 되어 있을 때 계속 증가하며, LOAD와 같은 값을 가지는 순간 Timer 만료 신호가 발생된다.

 

C. CTRL (0xE000 E010)

- Timer Control Register로 입력 Clock설정, Interrupt 사용 유무, 그리고 Timer의 동작 유무를 Control하며, Timer 만료여부를 확인할 수있는 1 Bit Status값을 가진다.

 - CTRL의 Bit Description

COUNTFLAG (16번째 Bit) : 0으로 되어 있다가 Timer가 만료되면 1로 설정된다. Interrupt를 사용하지 않을 경우 Timer 만료를 확인 할 수 있는 값이다.

CLKSOURCE(3번째 Bit) : Timer의 Tick Count를 증가 시키는 입력 Clock을 AHB 혹은 AHB/8 중에서 선택할 수 있다.

이를 이용하는 이유는 조금 긴시간의 Timer를 동작시키기 위해 사용된다.

72MHz 기준으로 최대값을 설정했을 때 AHB클럭을 사용할 경우 최대시간이 233ms (16777215 / 72000000) 가 된다. 그러나 AHB/8을 사용하면 최대 시간이 1.864s 까지 늘일 수 있다.

TICKINT(2번째 Bit) : Timer 만료시 Interrupt를 발생시킬지 여부를 결정한다. Interrupt를 사용할 경우 Timer 만료시 Vector Handler의 16번째 등록된 SysTick_Handler 함수가 호출된다.

ENABLE(1번째 Bit) : 가장 중요한 Timer 동작을 결정한다.

 

 

3. Timer 구동 예제

자.. 여기까지 봤으면 이제는 실제로 Timer를 동작시켜볼 차례다.

예제1)

Interrupt를 사용하지 않고 1ms 를 기다리기.

-> AHB가 72MHz 라고 가정하고 AHB/8 클럭을 SysTick Count 입력으로 사용하면 1msec는 9000 Tick이된다. 

 

*(volatile unsigned int *)0xE000E014 = 9000;

*(volatile unsigned int *)0xE000E018 = 0;

*(volatile unsigned int *)0xE000E010 = 0x1;    // Interrupt Disable, ClockSource : AHB/8

do {

   temp = *(volatile unsigned int *)0xE000E010 ;

} while((temp&0x1) && !(temp&(1<<16)));

*(volatile unsigned int *)0xE000E010 = 0;

 

즉 Timer를 구동시키고 Control Flag가 1이 될 때까지 기다리면된다.

 

예제2)

1msec 마다 Interrupt를 발생시켜기

역시 Clock Source는 0으로 설정하여 AHB/8 값을 쓰는 걸로 가정한다.

그리고 SysTick_Handler 라는 이름의 함수 주소가 Vector Table 16번째 등록되어 있다고 가정한다. 

unsigned int sys_msec = 0;

 

void SysTick_Handler(void)

{

     sys_msec++;  

}

 

void main(void)

{

     . . . . . . 

    *(volatile unsigned int *)0xE000E014 = 9000;

    *(volatile unsigned int *)0xE000E018 = 0;

    *(volatile unsigned int *)0xE000E010 = 0x3;    // Interrupt Enable, ClockSource : AHB/8

    while(1)

    {

         if(sys_msec%1000 == 0 )

              printf("%d sec\n", (sys_msec/1000));

    }

 

물론 main함수 앞부분엔 클럭설정을 필요한 기본적인 초기화코드가 필요하며, printf를 수행하기 위해 UART가 초기화 되어 있어야 한다.

결과는 1초마다 시스템이 동작되고 있는동안의 시간을 출력하게 될 것이다.

 

4. CMSIS

ARM에서 제공하는 Library인 CMSIS에서는 Ver3.20을 기준으로 Core_cm3.h 에 다음과 같은 함수 및 Register 구조체를 제공한다.

 

 

SysTick은 다음과 같이 정의되어 있다.

#define SysTick             ((SysTick_Type   *)     SysTick_BASE  )

 

여기서 SysTick_BASE 는 당연히 0xE000E010 값을 가지며, SysTick_Type은 구조체로 각각 4byte 옵셋을 가진다.

 

즉 SysTick->LOAD = 0xFFFF; 은

*(volatile unsigned int *) 0xE000E014 = 0xFFFF; 과 같은 의미이다.