본문 바로가기
코스웨어/11년 내장형하드웨어

[내장형]박춘우_2011년 9월 20일 화요일 Daily Report

by 알 수 없는 사용자 2011. 9. 21.
728x90
반응형
[ ARM ]

ADC를 이용한 빛센서

센서라고 부르는 것은 보통 센서와 센서 모듈을 통칭해서 부른다. 저렴한 센서에는 센서 자체만 존재하지만 값 비싼 센서에는 칩까지 포함되어 있다. 이를 센서 모듈이라고 부른다. 다음과 같이 칩이 들어 있는 초음파 센서 모듈이라고한다.



빛 센서가 빛의 세기를 측정한 값을 ADC 를 통해 받아서 하이퍼터미널 또는 캐릭터 LCD 로 출력해보자. 빛센서에 대해 간단히 알아보자. 실습에 사용할 빛센서는 다음과 같이 생겼다.



빛 센서는 그림에서와 같이 양쪽 다리의 길이가 같다. 보통 양쪽 다리의 길이가 같으면 무극성이다. 무극성이라는 말은 방향성이 없다는 말이다. 저항이 무극성을 가지는 소자이다. 빛 센서는 빛의 세기에 따라 전압이 달라진다. 빛의 세기가 강하면 전압이 높아진다. 이것은 가변저항을 사용하기 때문이다. 센서가 감지하는 빛의 세기가 가장 크면 저항은 0 이될 것이다. 
이를 정리하면, 결국 Vcc(전압)을 센서가 빛을 측정하여 가변저항에 의해 빛의 세기에 해당되는 전압으로 변경시켜준다. 결국 변경된 전압을 가지고 빛의 세기를 측정해야한다.  
센서는 실제로 온도나 빛 등을 직접 측정하는 것이 아니라 전압을 통해 측정한다. 따라서 해당 전압에 빛의 밝기나 온도를 변환해야한다. 값 비싼 센서에는 제품에 대한 변환식들이 제공된다. 하지만 저렴한 센서의 경우는 사용자가 직접 테스트를 하여 변환식을 구해야한다.
우리는 저렴한 센서이니깐 직접 구해야 한다는 것 !! ㅠㅠ

빛 센서에서 해당되는 전압을 ARM 에서 받아서 제어를 해야한다. 하지만 전압 크기가 얼마인지 알 수가 없다. 이를 측정해주는 것이 ADC 장치가 ARM 에서 제공한다. 이 ADC 를 통해서 빛 센서가 주는 전압값을 10 비트 또는 8 비트 분해능을 통해 해당 전압에 맞도록 비교하고 변환식을 통해 출력하면된다.



ADC 는 AD0 ~ AD7 라인을 사용하며 AD0 ~ AD3 라인은 병렬 I/O 와 겸용으로 사용한다. AD4 ~ AD7 은 ADC 전용라인이다. 따라서 예제에서는 AD7 라인과 10 bit 분해능으로 실습하였다.

ADC 를 어떻게 제어해야하는지 간단하게 절차로 나누면 다음과 같다.

1. ADC 를 사용하기 위한 초기화를 한다.

2. 아날로그를 디지털로 변환 작업과 변환값을 출력한다.

ADC 를 동작하기 위한 전체적인 소스 코드는 다음과 같다. 헤더 파일을 먼저 살펴보면, 헤더 파일에는 ADC 에 사용하는 레지스터들을 정의한다.

< adc.h >

ADC 를 사용하기 위한 초기화와 설정 부분의 소스코드를 살펴보자.

<adc.c>

main 함수에서는 초기화를 한 후 PC 의 하이퍼터미널을 통해 키를 입력하면 빛 센서에서 얻어 온 빛 센서를 하이퍼터미널과 캐릭터 LCD 로 출력한다.

<main.c>
 
간단한 두 가지의 절차를 세부적으로 살펴보자.

1. ADC 를 초기화를 해야하므로 초기화를 해줄 함수를 구현한다.

void Adc_Init(void)
{
	// 전원 공급
	PMC_PCER = 1 << ADC;
	// ADC 리셋 
	ADC_CR = 1 << SWRST;
	// 채널 선택 7번 
	ADC_CHER = 1 << ADC_CH7;
	// 프리스케일 (4 MHz) 및 분해능 (10 bit) 설정
	ADC_MR = (5 << PRESCAL) | (0 << LOWRES);
	
	return ;
}

1) 초기화를 하기위해서 전원 공급을 한다.

ADC 는 주변 장치 ID 번호가 4 이므로 해당 번호에 전원을 공급한다. 하지만 직접 테스트 해본 결과 전원 공급을 하지 않아도 동작하였다. 그 이유는 ADC 에는 로직회로가 적고 아날로그 회로는 슬립 모드를 사용할 수 있기 때문에 전력관리 제어기 PMC 와 관계없이 동작한다. 다시 말해 정상 모드에서는 ADC 에 항상 클록이 공급되고 슬립 모드에서는 클록이 차단된다.

2) ADC 내부를 하드웨어 리셋한 것처럼 리셋을 해준다.

3) ADC 를 사용하기위한 채널을 선택한다. 7 번을 선택하였다.

4) ADC 에 사용하기 위한 클럭 주파수를 선택하고 분해능을 설정한다.

ADC 클럭 주파수는 ADC_MR 레지스터의 PRESCAL 에 설정하며 클럭 주파수를 구하는 공식은 다음과 같다.

MCK / ((PRESCAL + 1) * 2)

이 때 10 비트 분해능을 사용할 때는 ADC 클럭 주파수는 5 MHz 이하를 사용해야 하고, 8 비트 분해능을 사용할 때는 8 MHz 이하를 사용해야 한다. 4 MHz 를 사용하기 위해서 PRESCAL 에 5 를 넣어준다.


2. 아날로그를 디지털로 변환 작업과 변환값을 출력한다.

unsigned char* Adc_Run7(void)
{
	static unsigned int uiResult;
	static unsigned char ucBuff[] = "0000\n\r";

	// ADC 시작 
	ADC_CR = 1 << START;

	// A/D 변환 완료 시 까지 ARM 대기 (Polling 방식) 
	while( 0 == ((1<< EOC7) & (ADC_SR)) ); 

	// A/D 변환 값 저장
	uiResult = ADC_CDR[7];
	
	// A/D 변환 값을 아스키 코드로 저장 
	ucBuff[0] = '0' + ((uiResult%10000)/1000);	// 천의 자리수
	ucBuff[1] = '0' + ((uiResult%1000)/100);	// 백의 자리수 
	ucBuff[2] = '0' + ((uiResult%100)/10);	// 십의 자리수
	ucBuff[3] = '0' + (uiResult%10);		// 일의 자리수
		
	return ucBuff;
}

1) A/D 변환을 시작한다.

2) A/D 변환이 완료 될 때까지 대기한다.

A/D 변환은 ARM 코어의 속도보다 많이 느리기 때문에 변환이 끝날 때까지 기다려줘야한다. 또한 변환된 결과값으로 출력을 해야하기 때문에 결과값이 나올 때까지 기다릴 필요가 있다. 따라서 기다리기 위해서는 풀링 방식과 인터럽트 방식이 있는데, 그중 풀링 방식을 사용한다. ADC_SR 레지스터의 필드 EOCx 에 1 이 들어올 때까지 대기한다. EOCx 1 로 변경되면 A/D 변환이 완료되었음을 의미하고 0 일때는 변환이 완료되지 않았음을 의미한다. 7번 채널을 사용하므로 EOC7 을 확인해야 한다.

3) A/D 변환된 값을 저장한다.

변환이 완료되면 변환된 값을 저장한다. ADC_CDR 레지스터를 배열로 사용하였다. 이는 헤더 파일에 살펴보면 ADC_CDR0 레지스터의 주소값으로 define 되어있음을 확인할 수 있다.

4) A/D 변환된 값을 해당 변환식을 통해 빛의 세기를 출력한다.

빛의 세기를 출력하기 위해 변환식을 만들거나 주어진 변환식을 통해 빛의 세기를 출력해야하지만 저렴한 센서는 변환식을 찾아서 만들어야한다. 이 작업은 쉬운일이 아니므로 A/D 변환된 값을 출력하도록 하였다. A/D 변환값은 10 bit 분해능을 사용했기 때문에 빛의 기에 따라 0 ~ 1023 까지의 값이 출력된다.


다음은 하이퍼터미널에 출력한 결과이다.




캐릭터 LCD 에 출력한 결과이다. 빛 센서가 아무것도 가리지 않은 채 측정하니 최고값이 나왔다.



물체로 빛을 어느 정도 가렸을 때 값이 줄어든 것을 알 수 있다.







[ WinAPI ]

체크 박스

체크 박스는 참과 거짓의 진위적인 선택을 입력받을 때 주로 사용한다. 체크 박스는 차일드 윈도우이며, 체크 마크를 표시하는 조그만 사각형과 체크 박스의 의미를 설명하는 짧은 문자열로 구성되어있다. 체크 박스를 만드는 방법은 CreateWindow 함수를 통해 "button" 클래스를 인자를 사용하여 생성한다. 스타일의 인자에는 체크 박스에 맞는 스타일을 설정해야한다. 체크 박스의 스타일의 종류를 알아보자.

체크 박스의 스타일은 다음과 같다.

BS_CHECKBOX

두 가지 상태를 가지는 수동 체크 박스

BS_3STATE

세 가지 상태를 가지는 수동 체크 박스

BS_AUTOCHECKBOX

두 가지 상태를 가지는 자동 체크 박스

BS_AUTO3STATE

세 가지 상태를 가지는 자동 체크 박스


체크 박스의 스타일은 크게 2 가지 형태로 나뉜다.

1. 옵션의 개수에 따른 스타일

두 가지 상태를 가지는 체크 박스는 선택 / 비선택 둘 중에 하나의 상태를 가진다.

세 가지 상태를 가지는 체크 박스는 선택 / 비선택 외에도 Grayed 라는 제 3 의 상태를 가진다. Grayed 는 알수 없음 (결정할 수 없음) 이라는 의미를 가진다.

2. 동작 방법에 따른 스타일

수동 체크 박스는 선택 / 비선택 상태를 부모 윈도우가 직접 바꾸어야한다. 체크 박스의 통지 코드가 전달될 때 체크 박스의 상태와 프로그램의 여러 가지 상태를 알아보고 직접 토글시켜야 한다. 따라서 체크 박스의 상태가 변경될 때마다 어떤 처리를 해야하거나 체크 조건이 복잡한 경우일 때 사용한다.

자동 체크 박스는 스스로 체크 상태를 바꾼다. 따라서 별도의 코드를 작성하지 않아도 되며 필요할 때 상태를 조사하여 사용하는 경우에 사용한다.


체크 박스의 간단한 예제를 살펴보자. 화면에 사각형을 하나 그리고 체크 박스의 상태에 따라 타원으로 그리도록 하는 예제이다.

< MsgProc.h >


< main.cpp >


< MsgProc.cpp >


소스코드를 분석해보자. 우선 main.c 파일에 WinMain 함수를 살펴보자.

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance
					 , LPSTR lpszCmdParam, int nCmdShow)
	...

	// 윈도우의 배경색을 컨트롤 배경색으로 지정 
	WndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
	
	...
}

WinMain 함수의 일부분이다. 여기서 WndClass 를 등록하는 곳에서 배경색을 지정하는 부분에 COLOR_BTNFACE 를 사용하였다. 이는 시스템이 사용하는 색을 사용하는 것이다. 그런데 코드에는 +1 을 주고 있다. 이 의미는 배경색을 컨트롤의 색상과 일치 시키도록 설정하는 것이다.

MsgProc.cpp 파일을 살펴보자

LRESULT OnCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
{	
	/* 체크 박스 생성 */

	// 두 가지 상태를 가지는 수동 체크 박스, 컨트롤 ID 는 0 
	c1 = CreateWindow(TEXT("button"), TEXT("Draw Ellipse?"), WS_CHILD | WS_VISIBLE |
		BS_CHECKBOX, 20, 20, 160, 25, hWnd, (HMENU)0, g_hInst, NULL);
	// 두 가지 상태를 가지는 자동 체크 박스, 컨트롤 ID 는 1
	c2 = CreateWindow(TEXT("button"), TEXT("Good Bye Message?"), WS_CHILD | WS_VISIBLE |
		BS_AUTOCHECKBOX, 20, 50, 160, 25, hWnd, (HMENU)1, g_hInst, NULL);
	// 세 가지 상태를 가지는 수동 체크 박스, 컨트롤 ID 는 2 
	c3 = CreateWindow(TEXT("button"), TEXT("3State"), WS_CHILD | WS_VISIBLE |
		BS_3STATE, 20, 80, 160, 25, hWnd, (HMENU)2, g_hInst, NULL);
	// 세 가지 상태를 가지는 자동 체크 박스, 컨트롤 ID 는 3 
	c4 = CreateWindow(TEXT("button"), TEXT("Auto 3State"), WS_CHILD | WS_VISIBLE |
		BS_AUTO3STATE, 20, 110, 160, 25, hWnd, (HMENU)3, g_hInst, NULL);
	
	return 0;
}

프로그램이 실행되면 WM_CREATE 메시지를 한번 발생하고 위의 함수를 호출한다. 체크 박스를 각각 4 개 생성한다. 생성하는 체크박스 마다 스타일이 조금씩 차이가 나며 그 차이는 주석을 살펴보면 된다. 또한 컨트롤 ID 를 각각 다른 값으로 설정한다. 생성한 체크 박스의 핸들 값을 c1, c2, c3, c4 에 저장한다. 체크 박스를 생성하면 다음과 같이 나타난다. 물론 위의 소스 만으로 출력되는 것은 아님을 유의하자.




컨트롤은 자신에게 어떠한 변화가 있을 때마다 부모 윈도우로 메시지를 보낸다. 이 메시지를 통지 메시지라 한다. 반대로 부모 윈도우가 체크 박스의 현재 상태를 알아보거나 상태를 바꾸려고 한다면 차일드 윈도우 (컨트롤) 로 메시지를 보낸다. 간단하게 정리를 하자면 다음과 같다.

통지 메시지

차일드 윈도우가 부모 윈도우에게 보내는 보고 메시지

ex> BN_CLICKED

일반 메시지

부모 윈도우가 차일드 윈도우에게 어떠한 지시를 내리기 위한 명령 메시지

ex> BM_GETCHECK, BM_SETCHECK


메시지의 종류는 컨트롤마다 다르다. 체크 박스의 경우에는 사용자가 마우스로 클릭을 하면 차일드 윈도우가 마우스 클릭이 되었음을 부모 윈도우에게 통지 메시지 BN_CLICKED 메시지를 보낸다. 그 후에 부모 윈도우는 차일드 윈도우인 체크 박스에게 지시를 내리기 위해 다음과 같은 메시지를 보낼 수 있다.

BM_GETCHECK

체크 박스가 현재 체크되어 있는 상태인지 조사

wParam, lParam 은 사용하지 않는다.

체크 상태를 리턴

BM_SETCHECK

체크 박스의 체크 상태를 변경

wParam 에 변경할 체크 상태를 지정


MB_GETCHECK 에 의해 리턴되는 값은 다음과 같다.

BST_UNCHECKED

0

현재 체크되어 있지 않다.

BST_CHECKED

1

현재 체크되어 있다.

BST_INDETERMINATE

2

현재 Grayed 상태이다.


위의 방법을 이용하여 만약 컨트롤 ID 가 0 인 체크 박스가 클릭되었다면, 체크 박스가 통지 메시지 BN_CLICKED 를 WM_COMMAND 메시지를 통해 부모 윈도우에 전달된다. WM_COMMAND 메시지를 처리하는 함수가 호출되며, 예제에서 호출된 함수는 다음과 같이 구성되어 있다.

LRESULT OnCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
	switch(LOWORD(wParam))
	{	
		// 두 가지 상태를 가지는 수동 체크 박스, 컨트롤 ID : 0 
		case 0 :
			// 체크 해제 상태인 경우 
			if( SendMessage(c1, BM_GETCHECK, 0, 0) == BST_UNCHECKED )
			{
				// 체크 표시 상태로 변경
				SendMessage(c1, BM_SETCHECK, BST_CHECKED, 0);
				// 원을 그리도록 변수값 변경
				bEllipse = TRUE;
			}
			// 체크 표시 상태인 경우
			else
			{
				// 체크 해제 상태로 설정
				SendMessage(c1, BM_SETCHECK, BST_UNCHECKED, 0);
				// 사각형을 그리도록 변수값 변경
				bEllipse = FALSE;
			}
			// 화면 지우기, WM_PAINT 메시지 보냄
			InvalidateRect(hWnd, NULL, TRUE);

			break;
                     ...

위의 함수에서 체크 박스에게 지시를 내리면된다. SendMessage 함수를 통해 지시를 내리며 일반 메시지이다. 예제에서는 부모 윈도우가 BM_GETCHECK 메시지를 컨트롤 핸들 변수 c1 인 체크 박스에 지시를 내려 현재 상태를 조사한 후 그에 해당하는 새로운 상태로 지시를 내린다. 과정은 다음과 같다.

1. 현재 체크 박스의 상태를 조사한다.

2. 현재 체크 해제 상태 ( BST_UNCHECKED ) 라면 체크 표시 상태 ( BST_CHECKED ) 로 변경하도록 명령하여  wParam 에 전달하고, bEllipse 변수값을 TRUE 로 설정한다.

3. 현재 체크 표시 상태 ( BST_CHECKED ) 라면 체크 해제 상태 ( BST_UNCHECKED ) 로 변경하도록 명령하여 wParam 에 전달하고, bEllipse 변수값을 FALSE 로 설정한다.

4. InvalidateRect 함수를 호출하여 WM_PAINT 메시지를 발생시켜 bEllipse 변수의 값에 따라 타원 또는 사각형을 그린다.



사용자가 c1 체크 박스를 클릭 할 때마다 체크 박스의 상태가 토글되면서 화면에 타원과 사각형이 바뀐다. 체크 박스를 클릭 하면 다음과 같이 사각형이 타원으로 변경된다.




컨트롤 ID 가 2 인 체크 박스를 살펴보자. 이 체크 박스는 BS_3STATE 스타일로 생성되었다. 따라서 3 가지의 상태를 가진다. 이 체크 박스에 마우스로 클릭을 하게되면 BN_CLICKED 통지 메시지를 부모 윈도우에 전달된다. 역시 WM_COMMAND 메시지를 처리하는 함수가 호출 된다.

                      ...

		// 세 가지 상태를 가지는 수동 체크 박스, 컨트롤 ID : 2 
		case 2 :
			// 체크 해체 상태인 경우
			if( SendMessage(c3, BM_GETCHECK, 0, 0) == BST_UNCHECKED )
			{
				// 체크 표시 상태로 변경
				SendMessage(c3, BM_SETCHECK, BST_CHECKED, 0);
			}
			// Grayed 상태인 경우
			else if( SendMessage(c3, BM_GETCHECK, 0, 0) == BST_INDETERMINATE )
			{
				// 체크 해제 상태로 변경
				SendMessage(c3, BM_SETCHECK, BST_UNCHECKED, 0);
			}
			// 체크 표시 상태인 경우
			else
			{
				// Grayed 상태로 변경
				SendMessage(c3, BM_SETCHECK, BST_INDETERMINATE, 0);
			}

			break;
	}

	return 0;
}

역시 마찬가지로 부모 윈도우가 BM_GETCHECK 메시지를 컨트롤 핸들 변수 c3 인 체크 박스에 지시를 내려 현재 상태를 조사한 후 그에 해당하는 새로운 상태로 지시를 내린다. 과정은 다음과 같다.

1. 현재 체크 박스의 상태를 조사한다.

2. 현재 체크 해제 상태 ( BST_UNCHECKED ) 라면 체크 표시 상태 ( BST_CHECKED ) 로 변경하도록 명령하여 wParam 에 전달
한다.

3. 현재 Grayed 상태 ( BST_INDETERMINATE ) 라면 체크 해제 상태 ( BST_UNCHECKED ) 로 변경하도록 명령하여 wParam 에 전달한다.

4. 현재 체크 표시 상태 ( BST_CHECKED ) 라면 Grayed 상태 ( BST_INDETERMINATE ) 로 변경하도록 명령하여 wParam 에 전달하고, bEllipse 변수값을 FALSE 로 설정한다.


사용자가 클릭 할때마다 체크 -> Grayed 상태 -> 체크 해제 순으로 바뀐다. 별 다른 동작이 없으므로 체크 상태에 따른 변화는 없다. 3 번째 체크 박스가 Grayed 상태를 보여준다.



여기서 컨트롤 ID 3 인 그림에서 4 번째 체크 박스는 스타일이 세 가지 상태를 가지는 자동 체크 박스이므로 별도의 코드를 작성을 하지 않아도 박스가 스스로 상태를 변경한다.

마지막으로 컨트롤 ID 1 인 체크 박스에 대해 알아보자. 이 체크 박스는 종료시에 사용된다. 종료 메시지인 WM_DESTROY 발생하면 다음 예제의 함수가 호출된다. 

LRESULT OnDestroy(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
	// 두 가지 상태를 가지는 자동 체크 박스, 컨트롤 ID 는 1
	// 체크 표시 상태인 경우 
	if( SendMessage(c2, BM_GETCHECK, 0, 0) == BST_CHECKED)
	{
		// 메시지 박스 출력
		MessageBox(hWnd, TEXT("Good bye"), TEXT("Check"), MB_OK);
	}

	PostQuitMessage(0);

	return 0;
}

BM_GETCHECK 메시지를 보내서 체크 상태를 조사하여 체크 표시 상태일 때 메시지 박스를 출력하고 프로그램을 종료한다. 체크 해제 상태일 때는 그냥 프로그램을 종료한다. 이 체크 박스에 마우스를 클릭하여 체크를 표시하면 다음과 같다. 



종료를 하면 다음과 같이 메시지 박스를 출력한다.



728x90