OS를 만듭시다. 어때요~ 참 쉽죠? (13)

나빌레라의 이미지

OS. 영어로 풀어서 Operating System. 한자어로 번역해서 운영체제. 이것을 만든다고 하면 사람들은 굉장히 어렵게 생각한다. 리눅스나 윈도우같은 OS를 비교대상으로 본다면 OS를 만드는것은 정말 어렵고 힘들고, 개인이 만들기엔 어쩌면 불가능에 가까운 도전일지도 모른다. 하지만 OS의 기본 개념들은 대학교 학부과정에서 가르칠 정도로 이미 보편화 되어 있고 그 개념 자체들은 그다지 어렵지 않다. 개념 구현을 중심으로 동작하는 것 자체에 의미를 둔 OS를 만드는 것은 어쩌면 도전 해 볼만 한 가치가 있는 시도가 아닐까.

목차


1회
2회
3회
4회
5회
6회
7회
8회
9회
10회
11회
12회

13. 외부인터럽트핸들링

우리는 이지보드에 OS를 만들고 있다. 그런데 뭔가 심심하다. 임베디드OS라면 무언가 임베디드장비에 들어가는 OS이고 그렇다면 분명히 외부 입력을 받아 상호 작용해야 한다. 쉽게 생각해서 MP3플레이어같은거만 생각해도 play버튼이 있지 않은가. 이번 강좌에서는 바로 그 버튼을 보드 외부에 붙이고, 이를 외부 인터럽트로 인식해서 처리 해 보도록 하겠다.

보통 MCU에 보면 외부핀이 각자 목적을 가지고 있고(USART, JTAG ... 등) 또한 동시에 같은 핀을 GPIO로 쓸 수 있다. GPIO 란 General Purpose Input Output 의 약자이다. 범용입출력핀이다. 해당 핀을 표시하는 레지스터비트를 0/1 로 세팅하여 Logic low 나 Logic high 전압이 출력되거나 혹은 전압입력을 받아서 프로세서 내부에서 0 이나 1의 값을 받는다. 외부로 부터 값을 받아들일 수 있다는 뜻이다.

보드에 달려 있는 USB포트, 이더넷컨트롤러칩, SDRAM, Flash, USART포트등등 전부다 MCU의 핀에 연결되어 전압의 높고 낮음으로 0과 1을 표현할 뿐이다. 그중에서 Externel Interrupt 라는 것이 있다. 직역하면 외부 인터럽트. 인터럽트가 외부에서 발생한다는 의미이다. 다시 기억을 되 살려 OS timer 를 설명했을때의 ICPR (Interrupt Controller Pending Register)의 각 비트별 정의를 보자. 8 번트가 GPIO0번핀에 대한 edge detect 비트, 9번비트가 GPIO1번핀에 대한 edge detect 핀이다.

전산관련 학과라면 논리회로 과목을 들었을 것이다. 그때 나오는 용어중에 falling edge, rising edge 라는 말이 나온다. falling edge 는 전압이 logic high 에서 logic low 로 떨어지는 그 순간을 말하는 것이고, rising edge 는 그 반대로 전압이 logic low 에서 logic high 로 올라가는 그 순간을 말한다. edge detect 라는 것은 falling edge 이건 rising edge 이건 레지스터의 세팅에 따라서 전앞의 변화에 따른 edge 발생시에 인터럽트 컨트롤러에서 이를 알아내어 인터럽트를 발생시킨다는 뜻이다.

자! 다시 데이터시트를 봐야 할 시간이 왔다. OS timer 구현할때 보았던 데이터시트를 다시 브라우저와 함께 띄우고 본 강좌를 읽어 주시기 바란다. 왠만하면 데이터시트의 각 표와 그림들을 캡춰해서 첨부하고 싶지만 타이핑하기도 힘에 겨운데 그림까지 따다 붙일려니 팍팍 체력저하가 느껴진다. 그러니 양해 해 주시기 바란다. 이번 강좌에서 봐야 할 부분은 4.1 챕터 이다. 모두 pdf 뷰어에서 찾아주시기 바란다. 챕터 제목은 General-Purpose I/O 이다.

pxa255 칩은 85개의 핀을 GPIO 로 사용할 수 있다. 물론 레지스터를 어떻게 세팅하느냐에 따라 다른 용도로 사용 할 수도 있다. GPIO 핀이 input 으로 세팅되었을 경우 GPIO핀은 모두 외부인터럽트로 사용 할 수도 있다.

GPIO는 85개나 되고 각 핀을 GPIO로도 쓸수 있고 특수목적으로도 쓸수 있는등 하나의 핀을 가지고 설정 할 수 있는 폭이 넓은 만큼 관여하는 레지스터의 종류도 많다. pxa255칩의 데이터 시트에는 이 레지스터들에 대한 설명이 친절하게 잘 나와 있다. 펌웨어 개발자이건 임베디드 OS 개발자이건 RTOS 개발자이건 간에, 임베디드 환경에서 무언가를 개발하는 개발자는 누가 뭐래도 데이터시트를 잘 읽고 이해 해야 한다. 물론 필자도 노력하는 중이긴 하지만 데이터시트를 꼼꼼히 읽고 챙기는게 쉬운일은 아니다.

그래도 읽자. 읽어야 만들 수 있다. :) 실제 우리가 필요로 하는 것은 GPIO 핀이 input 모드인 상태에서 들어오는 input 을 Externel Interrupt 로 잡아서 인터럽트컨트롤러를 통해 해당 핀의 입력을 잡아내는 것이다. 그렇다면 GPIO 핀의 Output 관련 레지스터의 내용은 알 필요가 없다. 또한 인터럽트가 발생했는지가 중요한것이기 때문에 GPIO 핀의 값을 읽거나 쓰는 동작에 관련된 레지스터들도 알 필요가 없다. 이점을 염두에 두고 가벼운 마음으로 데이터 시트를 읽어 내려가도록 하자.

4.1.1 장에 보면 pxa255 가 제공하는 GPIO관련 레지스터들에 대해서 개략적인 설명이 나와 있다. 먼저 GPDR(GPIO Pin Direction Register) 가 나온다. 뭐하는 녀석인지는 이름만 봐도 알것 같다. 해당 핀의 방향을 입력(input)으로 할지 출력(output)으로 할지를 결정하는 녀석이다.

핀의 방향을 출력으로 할경우에는 GPSR(GPIO Pin Output Set Register) 에 해당핀과 바인딩되는 비트에 1을 주어서 전압 logic high 를 출력하게 할 수 있고, GPCR(GPIO Pin Output Clear Register) 를 세팅해서 logic low 전압을 출력하게 할 수 있다. 클리어 레지스터는 해당핀이 입력이던 출력이던 관계없이 핀의 값을 클리어 하는데 사용된다고 한다.

GPIO핀을 입력모드로 사용하면 GPLR(GPIO Pin Level Register)를 이용해서 해당 핀의 레벨값을 읽을 수 있다. 그리고 GRER(GPIO Rising Edge Detect Enable Register) 과 GFER(GPIO Falling Edge Detect Enable Register) 를 세팅하면 해당 핀의 전압값이 Rising Edge 혹은 Falling Edge 가 될때 마다 GEDR(GPIO Edge Detect Register)에 Edge Detect 상태가 기록된다. 이 Edge Detect 기능이 비로소 인터럽트를 감지하는데에 사용된다. 고로 이번 강좌에서 가장 중요한 내용이라고 할 수 있겠다.

그 아래로도 Sleep Mode 어쩌구 하면서 설명이 조금 더 나오는데 나빌눅스를 만드는데 크게 필요한 내용은 아니다. 넘어가자. 그냥 간단하게 요약하면 GPIO0번 부터 15번까지는 pxa255칩의 sleep mode 를 깨우는 (wake-up) 역할을 한다는 것이다. GPIO0~15번에는 입력이 들어가면 알아서 sleep mode 도 해제 되는가 보다.

4.1.2절은 GPIO의 각 핀이 어떻게 사용되는지 목록이 나와 있다. 앞에서 설명했듯이 보통 MCU는 하나의 핀이 두개이상의 역할을 하게 되어 있고, 이것은 pxa255도 마찬가지 이다. GPIO핀은 85개가 있다고 했다. 참 많다. 우리는 그중 하나만 골라서 쓰면 된다. 근데 한가지 염두 할점은 이지보드는 pxa255칩의 거의 모든 신호선을 다 연결했다는 점이다.(그래서 맘에 든다.) 바꾸어 말하면 거의 모든 GPIO핀을 GPIO가 아니라 다른 특수목적 핀으로 사용했다는 말이다.

Table 4-1 이 나오기 전에 한단락의 설명이 있다. 가볍게 읽어보자. GPIO는 선택적으로 동작을 세팅해서 쓸 수 있고, 하나의 핀은 동시에 두가지 역할을 할 수 없다. 그리고 GPIO0번 핀은 sleep mode 를 깨우는데 사용되므로 다른 Alternate Function 이 없다고 한다. 그럼 그냥 속편하게 이번 강좌에서 사용할 외부 인터럽트 핀으로는 GPIO0번 핀을 사용하도록 하겠다. 어차피 원리는 같으므로 다른 핀을 사용할때도 과정은 비슷할 것이다.

다른 Alternate Function 이 없는 GPIO0을 사용하기로 결정 하였으니 Table 4-1은 볼 필요 없다. 건너 뛰자.

pxa255칩의 GPIO는 85개, 32비트 ARM칩 1워드에 넣을 수 있는 비트값은 총 32비트. 그러므로 레지스터 한개로 세팅 할 수 있는 GPIO핀은 최대 32개. 즉, 85개의 GPIO핀을 전부 세팅하려면 최소 3개의 레지스터에 나눠서 세팅을 해야 한다. 이것을 설명한것이 데이터시트의 4.1.3절이다. Table 4-2 에 내용이 나와 있다. GPDR하나만 예를 들어 설명하자면 GPIO18번 핀은 GPDR0의 18번째 비트를 세팅해서 설정하는 것이고, GPIO32번 핀은 GPDR1의 0번째 비트를 세팅해서 설정한다. 이런 뜻이다. 이번강좌에서는 GPIO0번핀을 사용할것이기 때문에 모두 첫번째 레지스터(GPLR0, GPSR0, GPCR0, GPDR0 ...)을 사용한다. 물론 첫번째군에 속하는 레지스터들도 전부 다 사용하지도 않는다. 필요한것만 사용한다.

4.1.3.1 절은 GPLR레지스터이다. GPLR레지스터는 input 모드에서 값을 읽기위해 사용한다고 하였다. pxa255에 센서나 입력장치등을 달아서 값을 읽을때 사용한다. 본강좌의 목적은 외부 인터럽트이므로 핀의 값이 무어인지는 그닥 상관하지 않는다. 물론 나빌눅스의 디바이스드라이버 단에서 GPLR레지스터의 특정비트를 폴링하고 있다가 값이 변하면 내부에서 인터럽트를 발생시키는 방식으로 만들 수도 있다. 하지만 이번 강좌에서는 다루지 않는다. 즉, 이번 강좌에서는 GPLR레지스터도 사용하지 않는다.

4.1.3.2 절은 GPDR레지스터이다. GPIO핀을 input으로 할지 output으로 할지 결정하는 레지스터 이다. GPIO0번핀은 GPDR0 레지스터의 0번비트이다. 비트의 값을 0으로 하면 input 모드이고 1로 하면 output 모드이다. 우리는 GPDR0 레지스터의 0번 비트를 0으로 하면 되겠다.

4.1.3.3 절은 GPSR레지스터와 GPCR레지스터이다. GPIO핀이 output모드일때 핀에 logic high 전압을 인가하기 위해 사용한다. GPCR레지스터와 함께 사용하여 LED나, LCD 혹은 모터등의 출력을 하기 위해 사용한다. 이번 강좌에서는 input모드의 외부인터럽트를 사용하기 위함이므로 역시 사용하지 않는다.

4.1.3.4 절은 GRER, GFER 레지스터 설명이다. GPIO핀이 인터럽트로 동작하게 하는데 필요한 레지스터이기 때문에 매우 중요하다. 두 레지스터가 하는 일은 간단하다. GRER에 비트가 세팅되면 GPIO핀이 rising edge 일때 GEDR의 같은 비트에 값이 셋되고, 반대로 GFER에 비트가 세팅되면 GPIO핀이 falling edge일때 GEDR에 값이 셋 된다.

그럼 여기서 Rising edge, Falling edge가 무엇인가.. 하시는 분이 계실런지도 모르겠다. 전기,전자등 하드웨어 관련 분들은 당연히 아시는 개념일 터이고 소프트웨어쪽 만 하신 분들은 모르실 수도 있다. 그냥 간단하게만 설명하겠다.

Rising Edge는 전압레벨이 logic low 에서 logic high 로 올라가는 그 순간을 말한다. 본강좌 그림을 안그리려 했으나, 어쩔 수 없다 잠시 그려 보겠다.

               +--------------
               |
               |
---------------+
               ↑
           바로 이순간

위그림에서 표현한 바로 저때이다. 필자도 하드웨어는 심도있게 공부해본적이 없어서 명쾌한 설명은 못하겠지만, 예를 들어 보자면 GPIO핀에 full-down 저항을 연결하고 이어서 스위치를 vcc에 연결해 놓아, 평소에 스위치가 없을때는 logic low 전압에 계속 핀에 걸려 있다가, 스위치를 누르면 GPIO핀에 logic high가 걸리면 바로 그 순간 pxa255의 edge detect unit은 이를 알아차리고 GEDR에 비트를 세팅한다는 것이다.

그렇다면 Falling Edge는 정확히 반대이다. logic high 에서 logic low로 전압레벨이 변하는 순간을 말한다.

-------------------+
                   | 
                   |
                   +------------------
                  ↑
               바로 이순간

이는 반대로 핀에 full-up 저항이 묶여 있고 GND를 향해 스위치가 연결되어 있을때, 스위치가 눌리지 않으면 계속 logic high전압이 인가 되어 있다가 스위치를 누르는 순간 logic low가 되면서 그 순간의 falling edge를 알아차리고 GEDR에 비트를 세팅한다.

필자는 이지보드의 메뉴얼을 보고 이지보드 양옆에 있는 커넥터중 U1커넥터의 65번 핀 (이 핀이 pxa255의 GP0번과 연결되어 있다.)과 5번핀 (3.3V 입력)을 full-up 저항으로 연결하고, 이어서 같이 스위치를 연결한다음 스위치의 반대편은 6번핀(접지)로 연결하여, 평소에는 logic high 전압이 GPIO0번핀에 인가되고 있다가, 스위치를 누르는 순간 falling edge가 발생하여 이를 인지해서 인터럽트를 발생시키고 이것을 커널 내부에서 핸들링 할 생각이다.

그래서 GRER0레지스터의 0번 비트는 0으로 하여 Rising Edge Detect 는 Disable 시키고 GFER0의 0번 비트를 1로 하여 Falling Edge Detect를 Enable 하여 사용할 것이다.

4.1.3.5 절은 GEDR의 설명이다. GEDR은 GRER이나 GFER을 세팅한 핀에 해당하는 Edge Detect가 발생하면 비트가 1로 세팅되는 레지스터이다. 그중 GPIO0과 GPIO1에 해당하는 Edge Detect 가 발생한다면 각각 ICPR의 8번 9번비트에 값이 세팅되어 직접 인터럽트가 발생하게 되고, GPIO2~GPIO84까지는 10번 비트를 공통적으로 세팅하고 2차적으로 GEDR레지스터를 검사해서 어떤 인터럽트가 발생했는지를 봐야 한다. 이번 회에서는 GPIO0번을 사용할것이므로 ICPR의 8번비트만 신경 쓰면된다.

GEDR레지스터는 읽기/쓰기가 모두 가능한 레지스터 이다. 읽기 일때는 해당 비트에 1이 있으면 Edge Detect가 된것이다. 쓰기 일때는 0을 쓰는 것은 아무런 역할을 하지 않고 1을 쓰게 되면 해당 Edge Detect 비트 필드를 클리어 한다. 이것은 OS timer를 작성했을 때 OSSR 레지스터에 1을 세팅해서 인터럽트 비트를 클리어 하는 것과 같은 이치이다.

4.1.3.6 절은 Alternate Function 을 지정해주는 GAFR_L, GAFR_U 레지스터 이다. 하나의 GPIO핀이 가지는 기능은 여러개라고 데이터 시트에 나와 있다. 예를 들자면 GPIOn번핀은 기능 4개를 모두 가지고 있다고 한다. 그러면 어떤레지스터에는 이 GPIOn번핀이 어떤역할을 해야 하는지에 대한 표시가 있어야 한다. 0은 그냥 GPIO, 1은 첫번째 기능, 2는 두번째 기능, 3은 세번째 기능, 4는 네번째기능 이런식으로 값을 주어 설정을 해주어야 한다. 하지만 비트 한개로는 0과 1 밖에 표현하지 못한다. 4까지 표현할려면 두개의 비트를 써야 한다. 그래서 GAFR은 GAFR_L (GAFR Lower) 과 GAFR_U (GAFR Upper) 두개의 레지스터를 사용하여, 그전에는 하나의 레지스터로 32개씩의 GPIO핀에 대한 설정을 하였으나, GAFR 은 각각 _L 과 _U 로 나누어 16개씩의 GPIO핀에 대한 설정을 한다. 한 GPIO핀이 두개씩의 비트를 사용하기 때문에 당연히 저렇게 구성했을 것이다. 인텔의 엔지니어들은 생각보다 상식적인 사람들이다. :)

앞서 설명했듯이, GPIO0 는 sleep mode 와 관련하여 기능이 예약되어 있기 때문에 다른 Alternate Function 이 없다. 그러므로 GAFR 레지스터에서도 세팅할것이 없다. 특수목적 핀이 아니므로 무조건 GPIO 이다. 그러므로 GPIO0핀에 대한 레지스터 세팅비트가 있는 GAFR0_L 레지스터의 0번 1번 레지스터는 00 으로 세팅하면 될 것이다.

이제 이지보드에 외부 스위치를 붙여 나빌눅스에서 핸들링 하기 위한 준비는 모두 끝났다. 위에 문장으로 외부 스위치를 어떻게 연결했는지를 서술했는데, 상상력이 뛰어난 독자이거나 관련 전공자가 아니라면 어떤식으로 연결했는지 상상하기 힘들 것이다. 필자 그림을 첨부하지 않는다고 공언하였으니 그림은 첨부하지 않고 텍스트로 어떻게 한번 그려 보겠다. :)

                                 O  이지보드 U1 커넥터 5번핀 (3.3V 입력)
                                 |
                                 |
                                                         저항
                                 |
                                 |                        
                                 *---------------- 이지보드 U1 커넥터 65번핀 (GPIO0)
                                 |
                                 |
                 +---------------+
                 |               
                /0               
               /                 
              /                  
             /                   
                 0               
                 |               
                 +---------------+
                                 |
                                 |
                              -------
                                ---   이지보드 U1 커넥터 6번핀 (접지)
                                 -

저렇게 연결하면 된다. 이제 하드웨어 의존적인 설명은 끝났다. 나빌눅스 커널에 직접 코딩을 할 차례이다. 기나긴 설명에 비해서 실제 코드는 몇줄 되지 않는다. 코딩하는 것 자체에 대한 독자들의 부담을 최대한 줄이기 위해서 코드를 간단하게 작성하려다 보니 사전 지식을 스터디 하는데는 긴 시간이 필요하지만 실제 코드는 막상 몇줄 되지는 않는다. 그렇다면 코드를 보자, 수정할 파일은 navilnux.c 하나 이다.

#include <navilnux.h>
 
extern Navil_task_mng taskmng;
 
Navil_free_task *navilnux_current;
Navil_free_task *navilnux_next;
Navil_free_task dummyTCB;
int navilnux_current_index;
 
void scheduler(void)
{
    navilnux_current_index++;
    navilnux_current_index %= (taskmng.max_task_id + 1);
 
    navilnux_next = &taskmng.free_task_pool[navilnux_current_index];
    navilnux_current = navilnux_next;
}
 
void swiHandler(unsigned int syscallnum)
{
    printf("system call %d\n", syscallnum);
}
 
void irqHandler(void)
{
    if( (ICIP&(1<<27)) != 0 ){
        OSSR = OSSR_M1;
        OSMR1 = OSCR + 3686400;
 
        scheduler();
    } 
 
    if ( (ICIP&(1<<8)) != 0 ){
        GEDR0 = 1;
        printf("Switch Push!!\n");
    }
}
 
void os_timer_init(void)
{
    ICCR = 0x00;
 
    ICMR |= (1 << 27);      
    ICLR &= ~(1 << 27);    
 
    OSCR = 0;
    OSMR1 = OSCR + 3686400; 
 
    OSSR = OSSR_M1;
}
 
void os_timer_start(void)
{
    OIER |= (1<<1);
    OSSR = OSSR_M1;
}
 
void gpio0_init(void)
{
    GPDR0 &= ~( 1 << 0 );
    GAFR0_L &= ~( 0x03 );
 
    GRER0 &= ~( 1 << 0 );
    GFER0 |= ( 1 << 0 );
 
    ICMR |= ( 1 << 8 );
    ICLR &= ~( 1 << 8 );
}
 
void irq_enable(void)
{
    __asm__("msr    cpsr_c,#0x40|0x13");
}
 
void irq_disable(void)
{
    __asm__("msr    cpsr_c,#0xc0|0x13");
}
 
int sched_init(void)
{
    if(taskmng.max_task_id < 0){
        return -1;
    }
 
    navilnux_current = &dummyTCB;
    navilnux_next = &taskmng.free_task_pool[0];
    navilnux_current_index = -1;
 
    return 0;
}
 
void navilnux_init(void)
{
    mem_init();
    task_init();
 
    os_timer_init();
    gpio0_init();
 
    os_timer_start();
}
 
int main(void)
{
    navilnux_init();
    navilnux_user();
 
    if(sched_init() < 0){
        printf("Kernel Pannic!\n");
        return -1;
    }
 
    int i;
    for(i = 0 ; i <= taskmng.max_task_id ; i++){
        printf("TCB : TASK%d - init PC(%p) \t init SP(%p)\n", i+1,
                    taskmng.free_task_pool[i].context_pc,
                    taskmng.free_task_pool[i].context_sp);
    }
 
    printf("REAL func TASK1 : %p\n", user_task_1);
    printf("REAL func TASK2 : %p\n", user_task_2);
    printf("REAL func TASK3 : %p\n", user_task_3);
 
    irq_enable();
 
    while(1){
        msleep(1000);
    }
 
    return 0;
}	

수정된 부분은 gpio0_init() 함수가 새로 생겼고, gpio0_init() 함수를 호출하는 부분이 navilnux_init() 함수에 추가되었고, GPIO0 를 통해 들어오는 인터럽트를 처리하는 부분이 irqHandler() 에 추가 되었다. 하나씩 살펴보도록 하겠다.

먼저 새로 추가한 gpio0_init() 함수이다.

void gpio0_init(void)
{
    GPDR0 &= ~( 1 << 0 );
    GAFR0_L &= ~( 0x03 );
 
    GRER0 &= ~( 1 << 0 );
    GFER0 |= ( 1 << 0 );
 
    ICMR |= ( 1 << 8 );
    ICLR &= ~( 1 << 8 );
}

GPIO0 번핀은 input 으로 동작한다 그러므로 GPDR0의 0번 비트만 0으로 바꾸어야 한다. 그래서 0번비트만 0으로 and 연산하여 해당 비트를 클리어 하였다. 이어서 GPIO0는 무조건 GPIO기능만 한다고 하였으므로 역시 0번 1번비트를 0으로 클리어 해주어야 한다. 그래서 0x03 에 bit not 연산을 하여 해당 비트만 0 으로 바꾼다음 GAFR0_L 레지스터와 and 연산하여 0번 1번 비트를 클리어 하였다. 다음으로 Rising Edge Detect 는 비활성화 하고 Falling Edge Detect 는 활성화 하기 위해 GRER0 의 0번비트는 클리어, GFER0의 0번 비트는 세팅 하였다. 여기까지 GPIO관련 레지스터의 세팅이 끝났다. 위에서 그렇게 길게 설명한 내용이 달랑 네줄의 코드로 끝났다. 무언가 허무한듯 싶지만 원래 이렇다.

그리고 인터럽트 컨트롤러 관련 레지스터로 Edge Detect 레지스터를 세팅하여 GEDR에 비트가 세팅되면 GPIO0 는 ICPR의 8번, GPIO1은 ICPR의 9번 비트에 비트가 세팅된다고 하였다. 우리는 GPIO0만 사용하므로 ICMR의 8번 비트에 1을 세팅해서 일단 마스크를 풀어주고 ICLR 의 8번 비트를 0으로 클리어 하여 해당 인터럽트를 IRQ 모드로 받아들이도록 세팅 하였다.

함수를 추가 하였으므로 나빌눅스의 각 부분 초기화 함수를 통합해서 처리하는 navilnux_init() 함수에 gpio0_init() 함수를 호출하는 부분을 추가 한다.

void navilnux_init(void)
{
    mem_init();
    task_init();
 
    os_timer_init();
    gpio0_init();
 
    os_timer_start();
}

OS timer 가 동작하고 나면 스케줄러가 동작하기 때문에 OS timer 를 동작하기 전에 gpio0_init() 함수를 먼저 호출 하였다. 이거 한줄이다. 다음은 irqHandler() 의 수정 사항이다.

void irqHandler(void)
{
    if( (ICIP&(1<<27)) != 0 ){
        OSSR = OSSR_M1;
        OSMR1 = OSCR + 3686400;
 
        scheduler();
    } 
 
    if ( (ICIP&(1<<8)) != 0 ){
        GEDR0 = 1;
        printf("Switch Push!!\n");
    }
}

위쪽에 OS timer 인터럽트에 대한 처리에 추가 하여 ICIP 의 8번 비트, 즉 GPIO0 번 핀에 대한 인터럽트 처리 구문이 if 문으로 작성되어 있다. GEDR0 의 0번 비트에 1을 써서 Edge Detect 비트를 클리어 하고, 인터럽트가 발생했음을 알리는 printf() 문장이 있다. 이부분은 이후에 핸들러 벡터 부분을 추가 할때 수정 할 것이다.

파일을 추가하지 않았으므로 Makefile 은 수정하지 않아도 된다. 그럼 가벼운 마음으로 make 명령을 쳐 보자. 성공적으로 빌드가 될것이다. 빌드된 이미지를 이지보드에 다운로드 하고서 부팅을 해보자 그리고 스위치를 눌러 보자. 그럼 스위치를 누르는 순간 아래와 같은 메시지가 출력 될 것이다.

TCB : TASK1 - init PC(a000bcd0)          init SP(a04ffffc)                      
TCB : TASK2 - init PC(a000bd2c)          init SP(a05ffffc)                      
TCB : TASK3 - init PC(a000bd88)          init SP(a06ffffc)                      
REAL func TASK1 : a000bcd0                                                      
REAL func TASK2 : a000bd2c                                                      
REAL func TASK3 : a000bd88                                                      
Switch Push!!                                                                   
TASK1 - a:a04fffe8      b:a04fffe4      c:a04fffe0                              
TASK1 - a:a04fffe8      b:a04fffe4      c:a04fffe0                              
TASK1 - a:a04fffe8      b:a04fffe4      c:a04fffe0                              
TASK1 - a:a04fffe8      b:a04fffe4      c:a04fffe0                              
TASK1 - a:a04fffe8      b:a04fffe4      c:a04fffe0                              
Switch Push!!                                                                   
TASK1 - a:a04fffe8      b:a04fffe4      c:a04fffe0                              
Switch Push!!                                                                   
Switch Push!!                                                                   
Switch Push!!                                                                   
Switch Push!!                                                                   
TASK1 - a:a04fffe8      b:a04fffe4      c:a04fffe0                              
TASK1 - a:a04fffe8      b:a04fffe4      c:a04fffe0

스위치를 1초가 가기 전에 여러번 누르면 여러번 찍힌다. 잘 동작한다. 빨리 만들고 확실하게 확인하기 위해 GPIO에 스위치를 연결하였으나 같은 원리를 이용하여 센서 등으로 부터 값을 받을 수도 있고, 반대로 캐릭터LCD나 LED등으로 출력을 내 보낼 수도 있을 것이다.

이번회는 pxa255 의 레지스터 설명이 들어가고 간단하게 나마 하드웨어적인 설명도 들어가서 내용이 조금 길어 졌다. 지금까지 강좌중에 가장 긴 회차가 아닌가 싶다. 다음회에는 드디어 시스템콜을 추가하는 레이어를 작성해 보도록 하겠다. 역시나 이번회에 작업내용을 담은 파일을 첨부하오니 모쪼록 공부에 도움이 되시기 바란다.

이 글은 http://raonlife.com/navilera/blog/view/87/에 동시 연재 됩니다.

File attachments: 
첨부파일 크기
파일 navilnux_chap13.tgz73.79 KB

댓글

paeksj98의 이미지

벌써 13회 차까지 오셨네요....

잘보고 있습니다...^^

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.