Notice
Recent Posts
Recent Comments
Today
Total
04-25 00:04
Archives
관리 메뉴

Jeongchul Kim

3. C 프로그램에서 하드웨어 접근 방법 본문

Embedded Linux

3. C 프로그램에서 하드웨어 접근 방법

김 정출 2015. 12. 22. 13:00

3. C 프로그램에서 하드웨어 접근 방법

C 프로그래밍에서 하드웨어 접근 방법

1. 프로세서에서 주변기기는 메모리다.

프로세서는 LED, UART, 버튼 등 주변 입출력 장치를 단순히 메모리로 처리한다.

입출력 장치를 제어하기 위한 값들을 보내거나 받을 때, 메모리에 값을 읽고 쓰는 것과 동일하게 처리한다. 프로세서는 외부에 있는 주변기기로부터 데이터를 받거나 보내는 통로가 필요한데, 이것이 입출력포트이다. 그러므로 C프로그램에서 주변기기를 제어하기 위해서는 먼저 입출력 포트가 사용할 메모리를 지정하고, 주변기기로 데이터를 쓰거나, 읽기 위해서는 주변기기가 사용하는 포트에 할당된 메모리에 읽고, 쓰기를 한다.


2. Memory mapped I/O와 I/O mapped I/O

프로세서에 따라서 주변 기기가 사용하는 메모리가 정해져 있을 수도 있고, 정해있지 않을 수도 있는데 이에 따라 memory mapped I/O와 I/O mapped I/O로 나뉜다.

> memory mapped I/O

입출력 장치가 사용하는 메모리가 따로 정해있지 않다.

프로그램에서 입출력장치가 사용할 메모리를 지정하여 사용한다.


> I/O mapped I/O

입출력 장치가 사용하는 메모리가 따로 정해있다.

프로그램은 정해있는 메모리를 사용해야 한다.


> 데이터 시트(data sheet)

보드를 구성하는 하드웨어와 프로세서를 제어하기 위한 정보들을 담고 있는 다큐먼트 파일로

보드나 프로세서 공급업체에서 제공한다. 즉 개발을 위한 참고자료이다.



하드웨어 제어 방법

1. 데이터 시트

입출력 장치를 제어하기 위해서는 회로도와 데이터 시트와 같은 참고자료가 필요하다.

데이터 시트는 프로세서 사용 설명서로 프로세서 메뉴얼이다. 프로세서에서 어떠한 입출력 포트를 제공하고, 어떻게 사용해야 하는 가와 같은 설명들이 나열되어있다. 입출력 포트 뿐 아니라 ARM 명령어 셋, 버스, LCD, UART등 프로세서를 사용하는데 필요한 정보들이 상세히 나온다. 입출력 포트에 어떤 장치들을 연결할 것인가는 시스템에 따라서 달라진다. 그러므로 회로도를 참고하여 어떤 장치들이 어떤 포트에 연결되어 있는가를 확인해야 한다.

※ 첫 번째 표는 레지스터의 이름과 사용되는 메모리 위치에 대한 정보가 담겨있다.

• GPECON : 모드 설정 레지스터. 32비트 입출력 포트는 입력, 출력, 특수 모드로 설정할 수 있다.

모드의 종류가 3가지이기 때문에 포트 하나당 2 비트를 사용한다. 모드설정은 표에 나온 값을 참고하여 설정한다.

• GPEDAT : 데이터 레지스터. 15비트 데이터를 입력하거나, 출력하는 레지스터로 값은 0, 1로 한 비트이기 때문에 포트 하나 당 한 비트 사용.

• GPEUP : 풀업 설정 레지스터.

• Reserved : 비상 시를 위해 여분으로 예약


※ 두 번째 표에서 포트 별 CON 레지스터의 정보가 나와있다. 예를 들어 GPE15번 포트는 CON 레지스터 [31:30] 비트를 사용함을 의미한다.


2. 입출력 장치 제어

1. LED를 GPE 14번 포트에 연결했을 때의 예
아래 그림과 같이 포트 하나에 GPECON은 2 비트, GPEDAT는 1 비트가 지정된다.

현재 제어하려고 하는 LED는 GPE14번 포트에 연결되어 있으므로 GPECON 레지스터 29, 28번 비트를, GPEDAT 레지스터 13번 비트를 사용한다.


2. 레지스터들이 사용할 메모리를 지정한다.

데이터 시트에 GPECON 레지스터의 위치는 0x56000040, GPEDAT 레지스터의 위치는 0x56000044이다. //GPECON 메모리 할당

int *gpecon=(int *)0x56000040;

//GPEDAT 메모리 할당

short *gpedat=(short *)0x56000044;

과 같이 선언할 수 있다. 하지만 이러한 방법도 가능하다.


#define GPECON (*(volatile unsigned int *)0x56000040)

#define GPEDAT (*(volatile unsigned short *)0x56000044)
위의 선언 방식은 포인터 변수를 사용한 방법이고, 아래의 선언 방식은 포인터 없이 메모리 직접 접근하는 방법이다.


3. 모드 설정

포트를 입력 모드로 사용할지, 출력 모드로 사용할 지를 설정한다.

모드 설정은 GPECON 레지스터에서 한다.

LED를 제어하기 위한 값을 프로세서에서 LED로 보내주어야 하므로 출력 모드로 지정한다. 프로세서를 기준으로 데이터가 들어오면 입력, 나가면 출력이다.

GPE14번의 모드 설정은 GPECON[29:28]에서 하고(아래의 데이터 시트 참조), 출력 모드로 설정해야 한다. 출력 모드는 01이다.

GPECON &= ~(0x1<<29); //29번 비트를 0으로

GPECON |= (0x1<<28); //28번 비트를 1로


4. 데이터 읽고, 쓰기

장치 제어에 필요한 데이터를 읽거나 쓴다.

데이터는 GPEDAT 레지스터에서 읽고 쓴다.

GPE14번 포트는 GPEDAT[13]을 사용한다.

LED를 켜려면 0을, 끄려면 1을 써야한다. LED의 전원을 프로세서가 아닌 외부에서 받아오기때문이다.

//LED 켜기

GPEDAT &= ~(0x1<<13); //13번 비트를 0으로


//LED 끄기

GPEDAT |= (0x1<<13); //13번 비트를 1로


코드의 대부분이 비트연산이다. GPECON레지스터의 전체 크기는 32비트이다. 이중 LED가 사용한 비트는 29, 28번 2 비트 뿐이다. 다시 말해 다른 비트들은 다른 장치들이 사용하고 있다는 의미이다. 그러므로 하나의 장치를 제어할 때 레지스터의 다른 비트 들의 값이 변경되어서는 안되고 특정 비트만 변경되어야 한다.


입출력 장치 신호 처리 방법

1.폴링 Polling

• 폴링은 입출력장치에서 신호가 발생했는가를 반복적으로 소프트웨어 측면에서 확인하는 방법이다.

• GPE13번 포트에 스위치를 연결하였다. 이 스위치 누를 때 마다 LED가 켜지도록 하려고 한다.

스위치를 누르면 GPEDAT에 1이 써지고, 스위치를 놓으면 0이 써진다. 이는 데이터가 프로세서 외부에서 들어오므로 스위치는 입력 장치이다. 이를 제어하는 방법은 다음과 같다.

• GPECON을 입력 모드로 설정

• GPEDAT의 데이터를 읽어온다.

• 읽어온 데이터가 1이면 LED를 켜고 읽어온 데이터가 0이면 LED를 끈다.


코드

//레지스터 지정

#define GPECON (*(volatile unsigned int *)0x56000040)

#define GPEDAT (*(volatile unsigned short *)0x56000044)


//모드 설정(input)

GPECON &= ~(0x3<<26); //26, 27번 비트를 0으로 클리어. input 모드는 00


while(1){

//스위치가 눌렸는지 무한 반복으로 확인

if( GPEDAT & (0x1<<12) ) //12번 비트가 1이면

GPEDAT &= ~(0x1<<13); //LED 켜기

else

GPEDAT |= (0x1<<13); // LED 끄기


//위의 로직을 삼항 연산자(?)를 활용한 코드

// GPEDAT & (0x1<<12) ? GPEDAT &= ~(0x1<<13) : GPEDAT |= (0x1<<13);

}













2. 인터럽트(Interrupt) 처리

인터럽트란?

프로세서 외부의 장치들이 프로세서 사용을 요청하기 위해서 프로세서에 보내는 신호이다.

인터럽트 처리는 프로세서 외부에서 발생한 사건을 하드웨어적으로 처리하는 방법이다. 속도가 빠르다.

앞에서 살펴본 폴링 방법은 소프트웨어로 사건이 발생 했는가 는 반복적으로 확인하지만, 인터럽트는 사건이 발생했음을 하드웨어적으로 확인하고 처리한다.
예를 들어ARM프로세서의 경우 매 인스트럭션 실행 후 상태레지스터의 인터럽트 비트를 확인하여 인터럽트가 발생했는가를 확인하고, 만약 인터럽트가 발생했다면 인터럽트 벡터를 참조하여 서비스 루틴을 실행한다.

인터럽트를 사용하는 방법은 프로세서마다 다르지만, 큰 맥락은 비슷하다.

다음은 SC32440프로세서를 기준으로 인터럽트 처리 과정을 나열한 것이다.

• 인터럽트 허용/불허 설정

• 인터럽트 서비스 루틴 등록

• 인터럽트 서비스 루틴 구현


1. 인터럽트 허용/불허 설정

인터럽트의 종류는 INTMOD 레지스터에 정의 되어있고, 이 순서대로 INTMSK 레지스터의 비트를 사용한다. 예를 들어 스위치의 순서가 10번째라고 하면 10번째 비트를 1로 설정하면 불허 0으로 설정하면 인터럽트가 허용된다.


2. 인터럽트 서비스 루틴 등록

인터럽트 벡터에 서비스 루틴 함수를 등록한다.

인터럽트 벡터란 인터럽트가 발생하면 어느 주소로 분기해야 하는지에 대한 정보가 있는 테이블이다. (*(unsigned *)(_isr_address+0x10)) = isr_fp;  //isr_fp는 서비스 루틴 함수의 주소


3. 인터럽트 서비스 루틴 구현

인터럽트 서비스 루틴에서는 다음의 작업을 한다.

• 인터럽트 불허

• 해당 인터럽트 발생 정보 클리어

• 서비스 수행

• 인터럽트 허용


인터럽트 서비스 루틴은 함수와 동일하게 만들어진다. 단 함수 앞에 __irq를 붙여 서비스루틴임을 표시한다. 함수 호출은 프로그램코드 어디에서도 찾을 수 없다. 인터럽트 발생시 하드웨어적으로 호출되므로 코드상으로는 나타나지 않는다.



3. DMA(Direct Memory Access)

DMA는 프로세서의 개입 없이 주변 장치가 메모리에 데이터를 읽고 쓸 수 있는 기능을 말한다.

프로세서의 개입이 없으므로 전체적인 성능을 높일 수 있다. 예를 들어 UART통신을 통해 데이터를 주고 받거나, LCD화면에 이미지를 로드할 때 데이터 하나하나를 전송할 때마다 프로세서 명령을 따른다면 다른 명령은 처리할 수 없을 정도로 처리량이 많을 것이다. 이럴 때 DMA를 사용하면 주변 장치가 프로세서 명령 없이 처리하므로 프로세서는 다른 일을 처리 할 수 있게 된다.


Comments