티스토리 뷰

심화

ISR(Interrupt Service Routine)

Just4Fun 2016. 10. 2. 19:43

입문 과정중에 인터럽트의 개념과 간단한 사용법, 그리고 인터럽트가 발생하는 시점부터 어떤 절차를 거쳐 인터럽트가 종료되는지 설명하였다.

이번 글에서는 Atmel Studio에서 제공하는 인터럽트 코드를 사용하지 않고, 프로그래머가 직접 인터럽트 서비스 코드를 만드는 방법에 대해서 설명하도록 하겠다.


이번 글을 위한 프로젝트는 앞에서 설명한 startup 프로젝트를 그대로 사용하도록 하겠다.


즉, startup.S 코드는 다음과 같다.


그리고, main.c 파일은 다음과 같이 시작하도록 하겠다.


물론, startup 코드를 직접 만들었으므로 링커 스크립트 파일도 프로젝트에 추가하여야 하고, 컴파일러에서 제공하는 crt0 코드를 사용하지 않겠다고 설정해 주어야 한다.


프로그램이 제대로 실행되는지 확인해 보자.


startup 코드와 회로 구성이 문제 없이 잘 동작되는것을 확인하였으면 본격적으로 ISR 코드를 만들어 보기로 하겠다.


먼저 startup.S를 다음과 같이 수정한다.

그리고, 링커 스크립트 파일에서 ENTRY(_start)라고 되어 있는 줄은 삭제하고, .text 섹션 안을 다음과 같이 KEEP() 명령을 추가한다.


이렇게만 수정한 후 프로젝트를 rebuild 하고, map 파일과 lss파일을 열어 어떤 변화가 생겼는지 확인해 본다.

__vector라는 심볼이 .vectors 섹션에 포함되어 있고, _start 심볼은 이제 .text섹션으로 옮겨진것을 볼 수 있을 것이다.


이제, ATmega328P에 맞는 벡터 테이블을 만들어 보도록 하겠다.


다음과 같은 두개의 매크로 코드를 만든다.


위의 매크로를 이용하여 벡터테이블을 완성한다.

이렇게 하면 ATmega328P에서 제공하는 1 ~25까지의 인터럽트를 처리하는 테이블을 만들수 있다.


map 파일을 열어 보면 다음과 같은것을 볼 수 있다.


reset 벡터를 제외한 25개의 ISR 시작점이 생성된 것을 볼 수 있다.

lss 파일을 열어보면 좀더 자세한 내용을 볼 수 있다.


예를 들어 첫번째 인터럽트인 INT0(External Interrupt Request 0) 인터럽트가 발생하면 CPU는 무조건 0x04번지로 점프한다.  0x04번지에는 irq_1 심볼이 있는 0x68번지로 점프하라는 명령어가 있으므로 0x68로 점프하게 된다.

0x68번지에는 r24 레지스터 안에 들어있는 값을 스택에 저장하는 코드가 들어있다.  그런다음 r24에 벡터 번호에 해당되는 1을 넣어주는 명령어가 있다. 

나머지 벡터들도 모두 동일한 과정을 거쳐 r24에 해당 벡터 번호를 넣어주는 것을 알 수 있다.


각각의 인터럽트에 대해서 테이블이 제대로 만들어 진것을 확인하였으므로 이들을 처리하는 공통 인터럽트 코드를 만들어 보겠다.


alloc_irq 매크로에 인터럽트를 처리하도록 process_irq함수를 부르는 코드를 추가한다.

r24에 벡터 번호를 넣어준 다음 process_irq로 점프하게 된다. 따라서 process_irq 함수를 만들어야 한다.


process_irq 함수를 _start 다음에 이어서 추가하였다.

20번째 줄에 보면 isr_handler라는 함수를 불러서 발생한 인터럽트에 맞는 처리를 하는데, 그전에  isr_handler에서 사용하게 될지도 모르는 레지스터들에 대해서 스택에 저장해 둔다.  AVR에는 r0 ~ r31까지의 32개의 레지스터가 있지만 입문과정에서 인터럽트를 설명할 때 왜 모든 레지스터를 스택에 저장하지 않고 일부만 저장해도 되는지 그 이유를 설명하였다.


isr_handler에서 인터럽터에 따른 처리를 수행완료하면 저장해 두었던 레지스터들의 값들을 스택에서 불러와서 원래대로 복구하게 된다.

마지막으로 "reti" 명령으로 인터럽트가 발생하는 시점에 수행중이던 원래의 위치로 돌아가게 된다.


이제, isr_handler 코드를 만들어 보겠다.

isr.c 파일에 다음과 같은 코드를 만든다.

인터럽트 벡터 테이블 만들때 r24에 벡터 번호를 넣어 주었기 때문에, isr_handler로 들어오는 인자는 벡터 번호가 된다.  따라서 isr_handler에서는 벡터 번호를 보고 해당 인터럽트를 처리하면 된다.


인터럽트를 사용하고자 할 때에는 register_isr_func()를 이용하여 인터럽트 핸들러 함수를 등록한다.  그럼 실제로 인터럽트가 발생하여 isr_handler() 함수가 불리워지면 isr[] 배열에서 callback 함수를 찾아서 해당 인터럽트를 처리하게 된다.


이로써 ISR관련 코드는 모두 완성 되었다.  실제로 제대로 동작하는지 몇개의 인터럽트를 발생하여 시험해 보도록 하겠다.


먼저 1초마다 한번씩 주기적으로 인터럽트를 발생 시키기 위하여 16비트 타이머를 설정해 보도록 하겠다.



setup() 함수에서 init_timer()를 불러서 타이머를 초기화 한 후 CPU에서 인터럽트를 발생 시키도록 하기 위하여 sei() 를 불러준다.


모든 코드를 적용하여 프로그램을 실행 시키면 다음과 같은 화면을 볼 수 있다.

1초마다 한번씩 타이머 인터럽트가 발생하고, 이에 대한 타이머 인터럽트 핸들러에서는 타이머 인터럽트가 몇번 발생했는지 그 회수를 출력해주고 있다.


다른 인터럽트에 대해서도 register_isr_func() 함수를 이용하여 핸들러를 등록하고 제대로 인터럽트가 불리워 지는지 확인해 보도록 한다.


isr.zip



심화 과정 목차

'심화' 카테고리의 다른 글

megaOS - 0. 시작하기 전에  (0) 2016.10.16
RTOS - Real Time Operating System  (2) 2016.10.15
startup.S  (5) 2016.10.01
crt0  (0) 2016.10.01
Linker Script - 링커 스크립트  (3) 2016.10.01
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함