본문 바로가기
Embedded SW/[NXP] Embedded SW Project

[NXP] 08. Timer Interrupt를 사용해 보자 (NXP 32 bit MCU)

by 방구석 임베디드 2021. 5. 7.
반응형

앞에서 포스팅한 글에서 LED를 주기적으로 깜빡여 보았습니다.

하지만, 정확한 시간에 깜빡이지는 못했습니다.

지금부터 타이머 인터럽트를 이용하여, 1초 주기로 LED를 깜빡여 보도록 하겠습니다.

MPC5643L에는 PIT라는 주변기기(Peripheral)가 있습니다.

reference manual 1149 page를 보면 아래와 같은 블럭도를 확인하실수 있습니다.

이 모듈은 우리가 설정한 clock Counter에 도달을 하면 인터럽트를 발생 시키는 모듈입니다.

여기서 아셔야 할 부분은 3가지 입니다.

1) Clock이란 무엇인가?

2) 인터럽트랑 무엇인가?

3) 타이머는 무엇이고 Clock Counter란 무엇인가?

1) Clock에 대해서 제가 아는 부분을 말씀드리겠습니다.

우선 우리가 사용하는 보드를 보면 아래 그림과 같이 40MHz 외부 오실레이터가 달려 있습니다.

회로를 보면 아래와 같습니다.

이 아래이미지는 구글에서 캡처한건데 오실로스코프로 측정하면 아래와 같이 40MHz의 주파수를 가지는

아래 모양의이미를 확인할수 있습니다.

MCU는 이 입력값을 얻어서 Clock으로 사용합니다.

MCU의 동기로 맞추어 사용합니다.

이 Clock에 맞추어 instruction을 읽어드리고 해석해서 수행하고

다른 주변기기들과도 동기를 맞춥니다.

하지만 일반적으로 Clock은 외부 오실레이터로 받는 40MHz를 그대로 사용하지 않습니다.

MPC5643L의 경우에는 최대 System clock을 120MHz로 사용할수 있기 때문에

PLL이라는 장치를 통해 40MHz를 120MHz로 변조하여 이 Clock을 이용하여 사용합니다.

높은 System clock을 사용할수록, 똑같은 함수를 돌리더라도 수행시간이 적게걸리니 더 빠른 성능을 확보할수 있습니다.

제가 첨부한 프로젝트에는 80MHz로 System Clock을 사용합니다.

그리고 PIT 모듈은 아래 그림과 같이 변조된 80MHz를4로 나눈값 20MHZ로 사용합니다.

그렇다면 이제 우리가 사용할 PIT 모듈은 20MHz의 clock을 Counter한다는 것을 예상하실수 있을 겁니다.

2) 인터럽트에 대해서 간단히 설명하겠습니다.

아래 코드를 잠시 살펴 보도록 하겠습니다. CPU는 아래와 같이

초기화를 끝내고 while구문에 무한반복해서 counter값을 증가시키는 일을 합니다.

CPU는 계속 본연의 일을 할것입니다.

그런데, 여기서 인터럽트가 발생하게 되면 CPU가 그것을 물리적으로 인지하도록 하드웨어를 설계해 놓았습니다.

그렇다면 CPU는 지금 일하고 이는 Program counter 주소를 멈추고

현재의 모든 레지스터 정보를 스택에 저장하고

인터럽트 서비스 함수로 이동하여 함수동작을 마치고

다시 스택에 저장한 레지스터 정보를 가지고

다시 coutner를 증가시키는 위의 일로 돌아오게 됩니다.

위의 그림과 같이 예를들어 1ms만큼 PIT 타이머의 counter가 차게 되면 PIT는 인터럽트 Controller에게

요청을 하고

인터럽트 Controller는 PIT 말고 다른 주변기기들의 인터럽트 우선순위를 고려한뒤 CPU에게 요청합니다.

이 모든것이 하드웨어로 만들어져 있습니다.

그렇다면 CPU는 자신의 일을 멈추고

PIT인터럽트가 발생하면 이 함수를 수행하라고 미리 지정한 함수를수행하고

다시 아래의 빨강박스 로 PC(Program Counter) 주소를 옮기고 코드를 수행합니다.

MCU는 이렇게 주변기기들의 요청을 긴급하게 수행하며

주변기기들과의 약속된 동작을 타이밍을 맞추어 수행합니다.

3) 타이머에 대해서 말씀드리도록 하겠습니다.

MPC5643L은 여러가지 타이머를 가지고 있습니다. (eTimer, PIT...)

그중 PIT를 설명드리면

PIT는 주기적으로 인터럽트를 발생시키는 모듈로서

아까 말씀드린 20MHz의 Clock을 input으로 사용합니다.

다시 말씀드려서 그 Clock에 맞추어 동기화되어 사용되는 모듈입니다.

그리고 PIT는 그 Clock을 내부적으로 Counter해서 저장하는 레지스터를 가지고 있습니다.

위의 초기화 코드를 보면

20000의 counter가 찾을때 인터럽트를 발생시키라는 내용이 보입니다.

20000/20000000 = 1/1000

1ms를 의미합니다.

즉 1ms마다 인터럽트를 발생시키라는 설정이라고 할수 있습니다.

지금까지 1)~3) 내용을 정말 속성으로 정리해 보았습니다.

그렇다면 이제 PIT모듈을 이용하여 1ms마다 인터럽트를 발생시키는 초기화 모듈을 설명해 보도록 하겠습니다.

그리고 인터럽트에 어떻게 연결해서 사용하는지도 정리해 보도록 하겠습니다.

위에 있었던 초기화 코드를 잠시 살펴 보도록 하겠습니다.

PIT.PITMCR.B.MDIS = 1U; /*Clock for Timers is disable*/

 

먼저 PIT 모듈을 Disable 하는 것부터 시작을 합니다.

PIT.CH[0].LDVAL.R = 20000U;

그리고 나서 Count down할 값을 넣어줍니다. 이값은 점점 20MHz Clock을 input로 사용하여 Counter가 감소할 것이고 0이 되면 인터럽트를 발생시킬 것입니다.

PIT.CH[0].TCTRL.B.TIE = 1U;

PIT.CH[0].TCTRL.B.TEN = 1U;

그리고 나서 이 타이머 모듈의 인터럽트를 Enable 하고 타이머를 동작시키도록 설정을 합니다.

PIT.CH[0].TFLG.R = 1U;

인터럽트 Flag를 1로 설정합니다.

그리고 이것은 나중에 인터럽트가 발생하면 인터럽트 서비스 루틴 함수가 실행될것이고

그때 동일하게 1이라는 값을 써주어 Clear 시켜줄것입니다.

참 신기했던것은 0을 써주는 것이 아니라 1을 써주어 Clear 시켜 준다는 내용입니다. ㅎㅎ

지금부터는 인터럽트 설정입니다.

우선순위를 아래와 같이 1로 설정하였습니다.

INTC.PSR[59].R = 0x01u;

그리고 나서, PIT 모듈을 Enable 해줍니다.

PIT.PITMCR.B.MDIS = 0U; /*Clock for Timers is Enable*/

여기까지가 PIT의 초기화 내용입니다.

그렇다면 INTCISRVEC_table.c 파일에서

아래 DrvPit0ISR 함수를 등록시켜 줍니다.

저는 미리 위와 같은 함수를 만들었습니다.

PIT 인터럽트가 발생하게 되면 인터럽트 Controller는 59번에 등록된 함수를 동작하도록 CPU에게 요청할것입니다. 물론 이러한 것들은 이미 하드웨어로 설계되어 있습니다.

따라서 우리는 59번 항목에 수행할 함수의 주소를 적어 놓습니다.

왜 59번일까요?

미리 지정이 되어 있습니다.

59번에 아래와 같이 할당이 되어 있음을 확인 할 수 있습니다.

이제 어느정도 설계가 되었으니 확인을 해보도록 하겠습니다.

1ms가 되어 아래와 같이

인터럽트 서비스 루틴 함수가 실행이 되었습니다.

왼쪽은 c 소스이고 오른쪽은 어셈블리 소스입니다.

그러면 이제 어떻게 main 함수로 다시 복귀하는지 살펴 보도록 하겠습니다.

ISR(Interrupt Sevice Routine) 함수가 끝나면 아래와 같이

이전 CPU 레지스터를 복귀하는 작업이 일어나고

다시 기존의 PC(Program Counter)로 이동하는 과정이 어셈블리 코드로 표현이 되어 있습니다.

아래와 같이 다시 main 함수로 돌아 왔습니다.

이러한 인터럽트 관련 내용을 이해하시면 다른 주변기기들(CAN, ADC, UART, DMA 등등)의 인터럽트 동작을 이해하시는데에도 도움이 도실 것입니다.

이제 1ms마다 증가하는 counter를 이용하여

1초마다 LED를 점등하도록 코드를 구현하였습니다.

 

예제 코드를 돌려보시면 잘 동작하는것을 확인하실수 있습니다!

 

반응형

댓글