DSP

TMS320C6748 을 활용한 DSP _ 인터럽트 (LAB2)

알 수 없는 사용자 2019. 9. 19. 03:57
반응형

<이 내용은 강원대학교 전자공학과 실시간 신호처리 과목에서 진행된 내용을 기반으로 작성되었습니다.>

 

 

LAB1 에서는 계속해서 버튼이 눌렸는지를 검사하였다. 하지만 이 방식은 cpu의 리소스를 매우 많이 먹기 때문에 매우 안 좋은 방식이다. 따라서 인터럽트를 사용하는 방식을 통해 조금 더 향상된 프로그램을 만들어보자.

 

보드에서 인터럽트가 어떻게 작용하는지 아는 것은 매우 중요하다. 세상에는 수많은 인터럽트가 있다. 이중에서 우리 보드는 128개의 인터럽트 소스를 규정하고 있다. 그리고 규정된 128개의 인터럽트 소스 중에서 12개를 받아들일 수 있다.

 

 맨 처음 C6000계열이 나왔을 때 12개의 인터럽트를 규정 할 수 있게 만들었는데 이 부분은 변경하기 매우 어려운 부분이라 그대로 사용할 수 있는 인터럽트는 12개로 고정 되었다.(요청 할 수 있는 인터럽트가 128개다.)

 

서비스 할 수 있는 인터럽트는 12개뿐 이므로 각각의 용도를 정확하게 정해놓지 않았다. , 사용자가 128개중 12개를 골라 쓸 수 있게 해놓은 것이다. (소스는 128개인데 서비스를 규정 할 수 있는 것은 12개 밖에 없다.) 그리고 우리는 이것을 직접 매칭시켜야 한다. (매칭시키지 않는다면 default12개가 규정되어져 있다.)

 

인터럽트가 발생하면 (12개중 하나가 발생하면) IFR이라는 flag register (12bit) 발생한 인터럽트의 레지스터가 1로 바뀐다.

 

interrupt Selector resister에서 셀렉트를 해놓고 인터럽트가 발생하면 IFR의 해당 부분에 1로 세팅한다. 그 뒤 IER 1로 되어있어야만 (인터럽트를 받을 지 결정하는 것) + GIE(bit. 이걸 0으로 하면 모든 인터럽트에 대한 거절. 전체 컨트롤) 를 통과하여 vector table에 도달한다.

 

대부분의 마이크로 프로세서가 인터럽트를 이런 식으로 진행한다.

이러한 과정에 따라서 interrupts vector table이 중요하다. 벡터테이블이란 인터럽트가 발생하였을 때 그 인터럽트와 11로 매칭되는 것이다. 이건 하드웨어적으로 매칭이 된다. 인터럽트가 발생되었을 때 원하는 인터럽트 서비스 루틴을 실행하기 위해서 사용된다.

 

벡터테이블에 어드레스를 적으면 그 주소로 가라는 이야기이다. (32bit) 근데 command를 적는 경우도 있다. 이럴 경우에는 영역을 좀 넓게 준다. (32byte) 8개의 명령어를 적을 수 있는 공간이다. 이렇게 해둔 이유는 이게 더 융튱성이 있다. 아주 간단한 명령어인 경우 아예 인터럽트 벡터 테이블 내에서 일을 수행해 버릴 수 있다. 번거롭게 점프를 뛰지 않아도 되는 것이다. 하지만 대부분의 경우 명령어 8개로는 부족하기 때문에 점프 명령어가 들어간다.

벡터 테이블은 메모리맵에 default로 크기가 규정이 되어있다. 하지만 바꿀 수도 있다. 앞으로 소개할 코드에서 벡터테이블을 설정하는 코드를 보게 될 것이다.

 

인터럽트를 만들 때는 인터럽트 서비스루틴을 작성 한 뒤 인터럽트 벡터에다가 어드레스를 적거나 점프 어드레스 명령어를 넣어야 한다. 이 과정을 인터럽트 설치라고 명명한다.

 

인터럽트가 다 수행 된 후 IFR은 자동으로 지워져 0이 된다. 대부분의 시스템들은 자동으로 지워준다. IFR만 컨트롤 해주면 인터럽트가 발생하는 것이기 때문에 인위적으로 프로그램 상에서 인터럽트를 발생시킬 수도 있다.

그럼 이제 코드를 통해 인터럽트를 설정하고 설치하는 과정을 살펴보도록 하자.

ConfigInterrupt( void )는 어떤 인터럽트를 사용할 지 고르는 함수이다. 사용하겠다고 선언한 인터럽트들을 살펴보면 타이머, 오디오, DMA, UART, 뱅크2, realtimeclk들을 골랐다. 6개만 사용하므로 6개를 더 쓸 수 있지만 나머지는 쓰지 않을 것이다.

 

ICR = 0xFFF0;     파워를 리셋하면 모든 레지스터들이 초기화 된다. 하지만 개발중에는 코드 업로드로 인한 리셋에 의해서 초기화 되지 않는 메모리도 있을 수 있기 때문에 초기화 시켜주는 것이 좋다.

 

레지스터 중에 IFR은 인터럽트가 발생하였을 때 1로 바뀌는 레지스터이다. 이를 이용하여 소프트웨어적으로 IFR을 건드리게 되면 충돌이 발생 할 수 있다. 하드웨어적인 이벤트와 소프트웨어적인 설정이 충돌이 날 수도 있기 때문에 ISR(set), ICR(clear)를 통해서 IFR에 인터럽트 셋 레지스터를 통해 세팅하거나 0으로 클리어 하는 방식을 사용한다.

 

인터럽트중에는 유저 인터럽트도 있지만(maskable interrupt) nonmaskable 인터럽트도 있다. 이건 막을 수 없다. 무조건 받아지는 인터럽트이다. 이건 항상 인에이블 시켜 놔야 한다. IER이 바로 그것이다.

보통 0~3번까지는 시스템 인터럽트이다. 유저 인터럽트는 4번에서 15번까지이다.

 

void InstallISR(Uint32 int_num, void (*c_int)(void))

이 함수는 인터럽트 벡터 테이블에다가 함수의 포인터를 넘기는 기능을 담당한다. 즉 해당 인터럽트 위치에 기계어 코드를 넣어서 점프하게 만들어 놨다.

 

벡터테이블은 default 0번지부터 인데 위치가 바뀌었다면  ISTP = (unsigned int)intvecTable; 이 줄을 통해서 벡터테이블의 위치를 찾는다. 만약 위치를 바꾸고 싶다면 ISTP를 이용해서 위치를 바꿀 수 있다. 인터럽트 벡터 테이블은 속도가 빠른 메모리에 넣어두는 것이 좋다. 그래서 전에 설명한 링크커맨드 파일에서 해당 벡터 테이블의 영역을 설정하였다.

벡터테이블의 위치는 1024byte의 배수가 되는 곳에 위치시킬 수 있다. 그리고 벡터테이블은 512바이트의 크기를 지니고 있다. 하나당 32바이트씩 총 16개가 있다. (시스템 4+ 유저 12) 따라서 L2RAM에 무작위로 위치를 배정하되, 그 크기는 512이고 시작번지는 1024의 배수로 시작되도록 할당을 하였다. 그 뒤, intvecTable이라는 변수에 시작번지가 들어가게 된다. 이 때, 벡터라고 하는 섹션은 실제로 생성되지 않는다. 실체가 없다. 코드가 생성되는 것도 아니고 이름만 주는 것이다. 메인문을 한번 보자.

 

, 인터럽트의 기본적인 설정이 모두 끝났다. 벡터테이블도 만들었고, 인터럽트 설치도 하였다.

저기에서 GPIO_PUSHBUTTON_ISR 은 새롭게 정의한 함수이다.

이런 식으로 정의되어있다. 이 때, 뱅크 2에서 4, 5번 두 개가 버튼이다. 현재 두 개의 버튼 모두 뱅크2에 있기 때문에 인터럽트는 둘 다 뱅크2에서 발생한다.

뱅크 안에 있는 16개의 핀은 모두 동일한 인터럽트를 발생시키지만 뱅크 안에 레지스터가 하나 있어서 어떤 핀이었는지 기록해둔다. 바로 status레지스터다. 만약 각각의 버튼에 대한 동작을 하고 싶다면 그런 과정을 거쳐야 할 것이다. 그 과정을 통해 각각의 버튼에 대한 동작을 하게 만든 예시 코드는 다음과 같다.

앗 그런데 밑에 이상한 함수 하나가 혹시 보이는가?

 

메인문에서 while 문을 통해 돌리던 LED_Toggle을 타이머를 통해 작동하도록 바꾼 것이다. 해당 보드는 타이머가 2개 제공된다. 바로 64bit 타이머 2개이다. 초기에는 16비트 32비트 이렇게 나오다가 클럭 수가 빨라지다 보니까 최근 들어 16비트는 의미가 없어졌다. 물론 외부클럭을 사용하면 의미가 있겠지만 요즘은 내부클럭이 너무 높아서 64비트는 되어야 긴 시간도 카운트 할 수 있다. 64비트는 엄청 긴 시간이 필요할 때 쓰기 때문에 이걸 32비트 2개로 나눠서 사용할 수 있다. 그리고 타이머는 인터럽트 4번에 연결되어있다.

 

이제 모든 기능을 인터럽트화 시켰다. 메인문을 보면 훨씬 깔끔해 진 것을 볼 수 있다.

반응형