ADS v1.2 사용법
● 기존 프로젝트 열기
첫 화면...비주얼 스튜디오와 비슷한 사용자 인터페이스를 갖고 있고 비슷한 메뉴구성이다. (비슷함)
타이틀바에 있는 Warrior라는 단어를 보니 왠지 강해보인다.
기존 프로젝트를 열어야 하니 메뉴 File - Open을 클릭하거나 Ctrl키와 O키를 동시에 누른다.
다른 프로그램들도 이와같은 단축키를 쓰니 알아 두면 편리할 것이다.
파일을 탐색할 수 있는 대화상자가 나오고 프로젝트파일인 mcp확장자로 끝나는 Xads.mcp를 열어 보자.
그럼 열릴 것이다.
처음 시작이니 프로젝트가 없을 것이다. 그러니 새로운 프로젝트를 만들어 보자.
● 새로운 프로젝트 생성
메뉴 File - New를 클릭하여 새로운 프로젝트를 생성하자.
단축키가 조금 복잡하고 일반적으로 쓰는 Ctrl + N과 다르다.
우리는 프로젝트를 생성해야 하니 Project탭을 선택한 후 프로젝트가 담길 폴더를 선택하기 위해 Set…버튼을 클릭한다.
적당한 폴더를 선택하고 저장을 클릭한다.
(폴더 생성 체크박스가 있으니 특정 프로젝트 폴더만 지정하면 프로젝트명에 따라 폴더가 생성되니 편리하다.)
처음이니까 First라는 이름으로 프로젝트를 만들어 보자.
확인 클릭…
내부에 새로운 First.mcp라는 이름의 창이 떳다.
뭔가 단순해 보인다.
새로운 그룹을 생성하기 위해 하얀부분에서 오른 클릭을 한 후 Create Group을 클릭한다.
(왜 그룹을 생성해야 하는지는 아직은 모르겠다. 여러 파일들을 관리하기 쉽도록 하기 위한 것일까?)
그룹의 이름을 적어 주고 OK버튼을 클릭한다.
이름이 Cstartup이니 ARM프로세서 기동을 위한 소스코드들이 들어가겠지?
폴더아이콘 모양이 생겼다.
그 위에서 오른 클릭하여 파일들을 추가하자. 우선 Cstartup.s파일을 추가한다.
Debug폴더에는 디버그정보가 포함된 실행파일이 생성되고 최적화를 적게함,
Release폴더에는 디버그정보가 없이 최적화를 많이 함.
DebugRel폴더에는 디버그정보와 최적화를 중간정보 처리한 실행파일이 있음.
VS2005에서도 이와같이 디버그와 릴리즈 두 가지 실행파일이 출력되는 점이 비슷하다.
보통 개발단계에선 디버그할 일이 많으니 디버그만 체크하고 빠르게 빌드하는게 좋을 듯 함.
그러나 이번은 처음이니까 모두 다 체크하자.
Cstartup그룹에 Cstartup.s파일이 등록되었다.
두 개의 파일을 더 추가해야 되니 상기와 같은(오른 우클릭-추가) 방법으로 추가해 보자.
retarget,c
Cstartup_SAM7.c
상기 두 개의 파일들을 추가하자.
마찬가지로 모두 체크하고 OK버튼을 클릭.
main()가 들어가는 실행파일을 등록하기 위해 이번엔 하얀부분에서 오른클릭-파일추가하자.
윤교수님책에는 다른 예제파일이 추가되었으나,
나는 프로젝트명하고 똑같이 했다.
마찬가지로 모두 체크하고 OK버튼 클릭...
● 프로젝트 환경 설정
빌드(컴파일)환경설정을 하기 위해 메뉴 – Edit – DebugRel Settings를 클릭한다.
첫 번째 Target Settings에서,
Post-linker항목을 ARM fromELF로 설정한다.
이제부터 나오는 스크린샷에 빨간색 사각형표시가 되지 않은 부분은 건들지 말고 디폴트 상태로 그냥 두세요.
ARM Assembler에서,
Floating Point항목을 No floating point로 바꾼다.
실수연산을 하지 않겠다는 뜻인가 보다.
ARM C Compiler에서,
Target and Source탭은 건들 것이 없으나 어떤 건지 대충살펴볼것.
실수연산은 하는 것 같고 리틀엔디안방식으로 메모리에 값들의 바이트순서를 낮은 번지부터 하고,
대부분의 기능이 ANSI C표준을 따른다.
Debug/Opt탭 (디버그/최적화)
디버그 컨트롤과 최적화옵션들을 상기와 같이 선택한다.
All은 최적화를 최대한 한다는 것이고,
For time은 코드실행속도를 빠르게 한다는 것이다. (Flash메모리 용량이 부족하면 For space를 선택)
ARM Linker에서,
Output탭,
RO Base는 Read Only 메모리의 시작주소로 AT91SAM7S256의 경우 0번지부터 시작한다.
최초 실행시 Remap이 되어 있지 않으므로 0번지는 Flash메모리가 맵핑되어 있다.
RW Base는 Read Write 메모리의 시작주소로 AT91SAM7S256의 경우 0x00200000번지~0x0020FFFF번지까지 SRAM이다.
(64KBytes의 저장공간)
아래의 Equivalent Command Line은 상기의 라디오버튼과 텍스트박스, 체크박스를 바꾸면 자동으로 완성이 되니..
건들지 말자. 옵션을 추가하고 싶은데 선택하는 부분이 없을 때만 다루자.
아직 초보니까 건들 일은 없을 것이다.
Layout탭에서,
Object/Symbol텍스트박스에 Cstartup.o파일이름을 넣자.
Section에는 reset을 입력하자.
(왜 넣는지는 당연히 모른다. 링크단계에서 부트코드가 필요하니 하는 것 같다.)
Listings탭에서,
Image map을 체크하고,
List file텍스트박스에 프로젝트명하고 같은 First.map을 기입하자.
책에는 Test.map이라 되어 있으니 따라해서 똑같이 기입한다.
Give Information on항목은 map파일에 어떤 정보를 담을 것인가 선택하는 항목같다.
(잘 몰라유)
ARM fromELF에서,
출력파일포맷을 바이너리 파일로 하자.
클릭해보면 모토롤라 32bit HEX, 인텔HEX등 다른 여러 파일 포맷이 나오는데,
HEX파일은 16진수를 문자로 바꾼 텍스트파일이다.
● 편집창 설정
텍스트편집창을 보기 좋게 바꾸기 위해,
메뉴 – Edit - Preferences를 클릭한다.
들여쓰기를 할 때 보통 공백문자8칸이니 Tab을 8로 바꾼다.
디폴트는 9로 되어 있어 소스코드를 열어 보면 지저분하게 텍스트가 배치되어 읽기 어렵게 된다.
나는 Tab을 4로 하고 싶은데 윤교수님 소스코드는 8로 되어 있으니 그대로 따르자.
이상 프로젝트를 생성하여 파일을 등록하고 빌드(컴파일) 환경설정을 하였다.
● 컴파일
프로젝트 파일들을 볼 수 있는 창에서 Make아이콘을 클릭하니..
정상적으로 컴파일이 되어 프로젝트폴더\First_Data\DebugRel폴더에 First.bin파일이 생성되었다.
D:\ARM_project\20110831_BUILD\First\First_Data\DebugRel
F5키를 누르니 상기와 같이 1개의 경고메세지만 뜨고 컴파일된 후에 시뮬레이터 비슷한 창이 하나 더 떳다.
AT91SAM7S256 인터럽트
● 인터럽트란?
※ 인터럽트1과 인터럽트2는 서로 다른 인터럽트라는 것이고 타이머/카운터, ADC등 어떤 인터럽트라도 해당된다.
인터럽트가 발생하면 IRQ예외가 발생하여 본 프로그램을 잠시 중지하고 벡터테이블에 있는 주소로 이동한다.
인터럽트 소스를 찾아 해당하는 함수의 시작주소로 이동하여 기능을 수행하고 다시 본 프로그램으로 돌아간다.
외부에서 들어오는 입력은 언제 들어 올지 예측할 수 없기에 이런 인터럽트라는 기능을 사용해 처리를 한다.
ATmega128 수업 때 배운 타이머/카운터로 주기적으로 특정 기능을 하거나,
UART(시리얼통신)에서 언제 수신될지 모르는 데이터(?) 패킷(?)을 받기 위해 무한히 대기하는 것 보단..
수신버퍼가 찼을 때 처리하면 좋겠다.
이렇게 인터럽트는 매우 유용한 기능이고 중요하니 꼭 알아가자.
● cstartup.S 예외처리부분
1: /* Exception vectors (should be a branch to be detected as a valid code by the rom */
2: _exception_vectors:
3: reset_vector:
4: ldr pc, =reset_handler
5: undef_vector:
6: b undef_vector /* Undefined Instruction */
7: swi_vector:
8: b swi_vector /* Software Interrupt */
9: pabt_vector:
10: b pabt_vector /* Prefetch Abort */
11: dabt_vector:
12: b dabt_vector /* Data Abort */
13: rsvd_vector:
14: b rsvd_vector /* reserved */
15: irq_vector:
16: b irq_handler /* IRQ : read the AIC */
17: fiq_vector:
18: /*------------------------------------------------------------------------------
19: *- Function : fiq_handler
20: *- Treatments : FIQ Interrupt Handler.
21: *- Called Functions :
22: *------------------------------------------------------------------------------*/
23: fiq_handler:
24: b fiq_handler
62행부터 시작되는 Exception vectors만 잘라냈다.
각각 레이블들이 어떤 의미를 갖는지 알아보자.
잘 보면 reset_vector와 irq_vector를 제외한 나머지 예외가 발생하였을 경우 아무런 처리도 하지 않고 제자리에서 점프하여 정지한다.
● reset_handler
AT91SAM7S256 MCU에 전원을 인가하여 ARM이 기동하거나 reset입력(NRST에 Low레벨신호 –> High레벨신호)이 들어 왔을 경우,
CU는 IP에 들어 있는 0번지부터 명령을 읽어와 해독하여 해당 기능을 실행한다.
4: ldr pc, =reset_handler
Load register명령으로 reset_handler가 시작되는 주소를 program counter(instruction pointer)에 넣는다.
프로그램카운터에 주소가 들어가면 다음 수행할 명령이 그곳에 있으니 그곳으로 프로그램 흐름이 바뀐다.
우선순위를 보면 리셋이 최상위이다. 그러니 다른 예외와 동시에 발생하면 무시하고 리셋부터 처리한다.
리셋 예외에서는 리턴하여 되돌아가는 않으니 main.c파일을 작성할 때 while(1); 문을 마지막에 추가하는 것 같다.
● undef_vector
ARM 마이크로프로세서가 보조 프로세서 명령을 실행할 때는 보조 프로세서로부터 이 명령을 실행할 수 있다는 확인 신호를 기다리게 되는데, 만약 보조 프로세서에서 이런 응답이 오지 않으면 ARM은 정의 되지 않은 명령 예외를 발생한다. 이것은 하드웨어적으로 존재하지 않는 보조 프로세서를 소프트웨어적으로 예뮬레이션할 때 사용될 수 있다.
너무 어려운데…이해가 안 되 그대로 적음.
멀티 프로세스인가? 아니면 실수처리용 Co프로세서를 이야기하는 것인가?
● swi_vector
윈도우API에서 메시지와 비슷한 것.
● pabt_vector
이것은 ARM 마이크로프로세서의 메모리 시스템으로부터 신호를 받아 발생되는 예외로서, 패치된 명령이 유효하지 않거나 유효하지 않은 명령을 패치하려고 시도하면 Prefetch Abort 예외를 발생.
즉, 엉뚱한 명령을 읽어 번역할 수 없는 상태인데...좋은 기능이라 생각한다.
이런 기능이 없는 MCU에서 IP가 Code영역이 아닌 stack영역을 가리킨다고 생각하면 끔찍한 결과를 초래하겠지..
● dabt_vector
메모리 시스템으로부터 신호를 받아 발생되는 예외로서,
로드나 스토어 명령으로 엑세스된 데이터가 유효하지 않으면 Data Abort예외발생.
● rsvd_vector
AT91SAM7S256에는 없는 예외로 업그레이드판에는 회로가 추가될 수 있겠다.
● irq_vector
ARM 마이크로프로세서에 외부로부터 IRQ 신호가 입력되면 발생하는 예외로서,
CPSR레지스터에서 I = 0으로 설정된 경우에만 발생.
만약 FIQ예외가 처리 중 일 때는 IRQ 예외가 무시된다.
ARM외부이니 AT91SAM7S256 MCU 내부에 들어있는 주변회로(H/W)에서 발생하는 인터럽트 요청을 처리하는 예외이다.
(예: 타이머/카운터, PWM제어기, USART, SSC, SPI, TWI, UDP, ADC등등)
각 소스(발생원 = 주변회로)에 맞는 인터럽트벡터로 점프하고 전후처리도 함께 한다.
● fiq_vector
ARM마이크로프로세서에 외부로부터 FIQ신호가 입력되면 발생하는 예외로서, CPSR레지스터에서 F = 0으로 설정된 경우에만 발생.
CPSR레지스터가 인터럽트를 빠르게 처리할지를 결정하는 구나..
다른 예외와 다르게 분기명령을 쓰지 않는다.
23: fiq_handler:
24: b fiq_handler
24행의 분기명령은 제자리 점프로 아무 일도 하지 않는 것처럼 보인다.
우리가 받은 스타트업 코드는 초보자에게 부담이 되지 않게 수정된 코드로 모든 기능을 사용할 수는 없다.
아무튼 멀리 분기할 필요 없이 바로 다음에 코드가 있으므로 수행시간을 절약하여 빠른 처리를 할 수 있다.
보다 빠른 처리를 위해 전용레지스터(R8_fiq ~ R12_fiq)를 사용.
● 인터럽트 소스 입력단의 구조
인터럽트 제어기는 각 인터럽트 소스의 동작을 개별적으로 제어할 수 있다. 내부 인터럽트의 경우는 상기의 그림과 같이 AIC_SMRx레지스터를 사용하여 레벨 감지 방식과 에지트리거 방식 중에서 하나를 설정할 수 있다.
(그림이 이상하고 윤교수님 책도 조금 이상하다 후에 정오표를 보고 수정해야겠다.)
windows API
● 두 개의 타이머 (windows API정복 page.103)
1: // winAPI정복 page.103 예제: TwoTimer
2: // 2011년 8월 31일 작성
3:
4: /* 윈도우즈 프로그래밍 순서 WinMain()
5:
6: WndClass정의
7: ↓
8: 클래스를 등록
9: ↓
10: 메모리상에 윈도우 생성
11: ↓
12: 윈도우를 화면에 출력
13: ↓
14: 메시지 루프 (반복) 여기서 텍스트 출력할 것.
15: */
16:
17: #include <windows.h> // 모든 API함수들의 원형과 사용하는 상수들이 정의되어 있음.
18:
19: LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //메시지처리함수
20: void MyTextOut(HDC hdc, int x, int y, LPCTSTR Text);
21:
22: HINSTANCE g_hlnst; //핸들 인스턴스
23: LPCTSTR lpszClass = TEXT("TwoTimer"); //윈도우 제목으로 사용되는 const char * (문자열)
24:
25: /***************************************************************************************************************************
26: WinMain은 엔트리 포인트(시작함수)
27: APIENTRY는 윈도우즈 표준호출규약 _stdcall(없어도 무방함)
28:
29: WinMain의 인자들...
30: ① hInstance: 프로그램의 인스턴스 핸들
31: ② hPrevInstance: 바로 앞에 실행된 현재 프로그램의 인스턴스 핸들로 16비트와 호환성위한 것이니 무시하자. Win32는 NULL이다.
32: ③ lpszCmdParam: 명령행으로 입력된 프로그램의 인수 (콘솔app에서 argv), 보통 실행직후에 열 파일의 경로가 전달됨.
33: ④ nCmdShow: 프로그램이 실행될 형태이며 최소화, 보통 모양 등이 전달된다.
34: ***************************************************************************************************************************/
35: int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
36: {
37: HWND hWnd;
38: MSG Message;
39: WNDCLASS WndClass;
40: g_hlnst = hInstance;
41:
42: WndClass.cbClsExtra = 0;
43: WndClass.cbWndExtra = 0;
44: WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
45: WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
46: WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
47: WndClass.hInstance = hInstance;
48: WndClass.lpfnWndProc = WndProc;
49: WndClass.lpszClassName = lpszClass;
50: WndClass.lpszMenuName = NULL;
51: WndClass.style = CS_HREDRAW | CS_VREDRAW;
52: RegisterClass(&WndClass);
53:
54:
55: hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW,
56: //CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
57: 200, 200, 500, 200,
58: NULL, (HMENU)NULL, hInstance, NULL);
59:
60: ShowWindow(hWnd, nCmdShow);
61:
62: while(GetMessage(&Message, NULL, 0, 0)) {
63: TranslateMessage(&Message);
64: DispatchMessage(&Message);
65: }
66:
67: return (int)Message.wParam;
68: }
69:
70: LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
71: {
72: HDC hdc;
73: PAINTSTRUCT ps;
74: SYSTEMTIME st;
75: static TCHAR sTime[128];
76: static RECT rt = {100, 100, 500, 120};
77: static int iCount = 0;
78: TCHAR sBuff[10];
79:
80: switch(iMessage) {
81: case WM_CREATE:
82: SetTimer(hWnd, 1, 1000, NULL); //타이머 ID = 1, 1000ms후에 WM_TIMER 메시지발생.
83: SetTimer(hWnd, 2, 5000, NULL); //타이머 ID = 2, 5000ms후에 WM_TIMER 메시지발생.
84: SendMessage(hWnd, WM_TIMER, 1, 0); //WM_TIMER이벤트(ID = 1) 강제발생.
85: SendMessage(hWnd, WM_TIMER, 2, 0); //WM_TIMER이벤트(ID = 2) 강제발생.
86: return 0;
87:
88: case WM_TIMER:
89: switch(wParam) {
90: case 1: //타이머 ID 1
91: GetLocalTime(&st);
92: wsprintf(sTime, TEXT("%d년 %d월 %d일 지금 시간은 %d:%d:%d입니다."),
93: st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
94: InvalidateRect(hWnd, &rt, TRUE);
95: break;
96:
97: case 2: //타이머 ID 2
98: MessageBeep(0);
99: wsprintf(sBuff, "%d", iCount);
100: SetWindowText(hWnd, TEXT(sBuff));
101: ++iCount;
102: break;
103: }
104: return 0;
105:
106: case WM_PAINT:
107: hdc = BeginPaint(hWnd, &ps);
108: MyTextOut(hdc, 100, 100, sTime);
109: EndPaint(hWnd, &ps);
110: return 0;
111:
112: case WM_DESTROY:
113: KillTimer(hWnd, 1);
114: KillTimer(hWnd, 2);
115: PostQuitMessage(0);
116: return 0;
117: }
118:
119: return(DefWindowProc(hWnd, iMessage, wParam, lParam));
120: }
121:
122: void MyTextOut(HDC hdc, int x, int y, LPCTSTR Text)
123: {
124: TextOut(hdc, x, y, Text, strlen(Text));
125: }
82: SetTimer(hWnd, 1, 1000, NULL); //타이머 ID = 1, 1000ms후에 WM_TIMER 메시지발생.
83: SetTimer(hWnd, 2, 5000, NULL); //타이머 ID = 2, 5000ms후에 WM_TIMER 메시지발생.
84: SendMessage(hWnd, WM_TIMER, 1, 0); //WM_TIMER이벤트(ID = 1) 강제발생.
SetTimer()로 현재 윈도우에 타이머 ID 1번과 2번을 설정함.
ID 1번은 1000ms(1초)마다,
ID 2번은 5000ms(5초)마다 WM_TIMRE WM_TIMER 메시지가 발생한다.
SendMessage()로 타이머 ID 1번만 최초실행시(WM_CREATE) 메시지를 강제로 발생시켜 ID 1번에 해당하는 기능을 수행하도록함.
85: SendMessage(hWnd, WM_TIMER, 2, 0); //WM_TIMER이벤트(ID = 2) 강제발생.
추가로 2번 타이머도 실행시 바로 메시지가 발생할 수 있도록함.
88: case WM_TIMER:
89: switch(wParam) {
90: case 1: //타이머 ID 1
97: case 2: //타이머 ID 2
WM_TIMER메시지가 발생한 후 wParam에 들어 있는 값을 확인해 보면 어느 타이머가 알려 왔는지 알 수 있다.
98: MessageBeep(0);
99: wsprintf(sBuff, "%d", iCount);
100: SetWindowText(hWnd, TEXT(sBuff));
101: ++iCount;
MessageBeep()는 사운드코덱(사운드카드)를 통해 소리를 발생시키는 함수로 0을 인자로 넣으면 띵~ 하는 소리가 난다.
이어폰이 없으면 확인하기 어려우니 아래와 같이,
wsprintf()로 정수형 변수의 값을 서식화해 SetWindowText()로 윈도우의 타이틀바에 그 값이 표시되도록 한다.
★ Win32환경에서 만들 수 있는 타이머의 게수 개수에는 제한이 없지만 그렇다고 많이 만들면 느려진다.
● 백그라운드 작업 (windows API page.105)
지속적인 작업을 하기 위해 보통 무한루프를 돌린다.
그러나 윈도우즈와 같은 멀티태스킹환경에서는 이런 방식을 사용해서는 안 된다. 왜냐하면 한 프로그램이 제어권을 독점하고 있어서는 안 되며 다른 프로그램도 실행시간을 가져야 하기 때문이다.
CPU를 독점하는 이런 무한 루프를 작성해서는 안 되며 반드시 메시지가 전달되었을 때 에 한해 필요한 작업을 해야 한다.
<예제코드>
1: // winAPI정복 page.105 예제: RandGrp
2: // 2011년 8월 31일 작성
3:
4: /* 윈도우즈 프로그래밍 순서 WinMain()
5:
6: WndClass정의
7: ↓
8: 클래스를 등록
9: ↓
10: 메모리상에 윈도우 생성
11: ↓
12: 윈도우를 화면에 출력
13: ↓
14: 메시지 루프 (반복) 여기서 텍스트 출력할 것.
15: */
16:
17: #include <windows.h> // 모든 API함수들의 원형과 사용하는 상수들이 정의되어 있음.
18:
19: LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //메시지처리함수
20: void MyTextOut(HDC hdc, int x, int y, LPCTSTR Text);
21:
22: HINSTANCE g_hlnst; //핸들 인스턴스
23: LPCTSTR lpszClass = TEXT("RandGrp"); //윈도우 제목으로 사용되는 const char * (문자열)
24:
25: /***************************************************************************************************************************
26: WinMain은 엔트리 포인트(시작함수)
27: APIENTRY는 윈도우즈 표준호출규약 _stdcall(없어도 무방함)
28:
29: WinMain의 인자들...
30: ① hInstance: 프로그램의 인스턴스 핸들
31: ② hPrevInstance: 바로 앞에 실행된 현재 프로그램의 인스턴스 핸들로 16비트와 호환성위한 것이니 무시하자. Win32는 NULL이다.
32: ③ lpszCmdParam: 명령행으로 입력된 프로그램의 인수 (콘솔app에서 argv), 보통 실행직후에 열 파일의 경로가 전달됨.
33: ④ nCmdShow: 프로그램이 실행될 형태이며 최소화, 보통 모양 등이 전달된다.
34: ***************************************************************************************************************************/
35: int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
36: {
37: HWND hWnd;
38: MSG Message;
39: WNDCLASS WndClass;
40: g_hlnst = hInstance;
41:
42: WndClass.cbClsExtra = 0;
43: WndClass.cbWndExtra = 0;
44: WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
45: WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
46: WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
47: WndClass.hInstance = hInstance;
48: WndClass.lpfnWndProc = WndProc;
49: WndClass.lpszClassName = lpszClass;
50: WndClass.lpszMenuName = NULL;
51: WndClass.style = CS_HREDRAW | CS_VREDRAW;
52: RegisterClass(&WndClass);
53:
54:
55: hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW,
56: //CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
57: 200, 200, 500, 400,
58: NULL, (HMENU)NULL, hInstance, NULL);
59:
60: ShowWindow(hWnd, nCmdShow);
61:
62: while(GetMessage(&Message, NULL, 0, 0)) {
63: TranslateMessage(&Message);
64: DispatchMessage(&Message);
65: }
66:
67: return (int)Message.wParam;
68: }
69:
70: LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
71: {
72: HDC hdc;
73: int i;
74:
75: switch(iMessage) {
76: case WM_CREATE:
77: SetTimer(hWnd, 1, 50, NULL); //타이머 ID = 1, 50ms후에 WM_TIMER 메시지발생.
78: return 0;
79:
80: case WM_TIMER:
81: hdc = GetDC(hWnd);
82:
83: for(i = 0 ; i < 1000 ; ++i) { //좌표(0~499, 0~399)에 알록달록 점을 1000개 그린다.
84: SetPixel(hdc, rand() % 500, rand() % 400,
85: RGB(rand() % 256, rand() % 256, rand() % 256));
86: }
87: ReleaseDC(hWnd, hdc);
88: return 0;
89:
90: case WM_LBUTTONDOWN: //왼쪽 클릭시 마우스 좌표에 반지름이 10인 원을 그린다.
91: hdc = GetDC(hWnd);
92: Ellipse(hdc, LOWORD(lParam) - 10, HIWORD(lParam) - 10,
93: LOWORD(lParam) + 10, HIWORD(lParam) + 10);
94: ReleaseDC(hWnd, hdc);
95: return 0;
96:
97: case WM_DESTROY:
98: KillTimer(hWnd, 1);
99: PostQuitMessage(0);
100: return 0;
101: }
102:
103: return(DefWindowProc(hWnd, iMessage, wParam, lParam));
104: }
105:
106: void MyTextOut(HDC hdc, int x, int y, LPCTSTR Text)
107: {
108: TextOut(hdc, x, y, Text, strlen(Text));
109: }
<실행화면>
80: case WM_TIMER:
83: for(i = 0 ; i < 1000 ; ++i) { //좌표(0~499, 0~399)에 알록달록 점을 1000개 그린다.
90: case WM_LBUTTONDOWN: //왼쪽 클릭시 마우스 좌표에 반지름이 10인 원을 그린다.
타이머를 돌려 WM_TIMER메시지를 받아 50ms마다 랜덤좌표에 알록달록 점을 1000개 그린다.
타이머는 항상 돌아 화면 점을 뿌린다. 점을 뿌리는 도중에 마우스메시지를 받아 다른 일도 처리할 수 있다.
스타크래프투게임에서 SVC가 미네랄을 계속 채집하여 나르는 일은 타이머메시지에 넣으면 되겠다.
● 콜백함수
SetTimer()의 네 번째 인수는 TIMERPROC lpTimerFunc이라고 되어 있는데 이 인수는 타이머 프로시저 함수의 포인터를 가리킨다.
이 인수가 NULL로 되어 있을 경우 첫 번째 인수로 지정된 hWnd로 WM_TIMER메시지가 전달되지만 이 인수에 타이머 함수가 지정되었을 경우는 매 시간마다 이 함수가 대신 호출된다.
골백함수의 원형은 아래와 같다.
void CALLBACK TimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime); |
콜백함수의 이름은 상기와 같이 TimerProc가 아니어도 되나 SetTimer() 호출 시 네 번째 인수에 이름과는 같아야 한다.
그리고 인자들의 type은 꼭 같아야 한다.
hWnd는 타이머를 소유한 윈도우의 핸들이고,
uMsg는 WM_TIMER,
idEvent는 타이머 ID,
dwTIme은 윈도우즈가 실행된 후의 경과시간이다.
이 함수의 인수들은 잘 사용되지 않으므로 구체적으로 알 필요는 없다. 알고 싶어~
<예제코드>
1: // winAPI정복 page.108 예제: Callback
2: // 2011년 8월 31일 작성
3:
4: /* 윈도우즈 프로그래밍 순서 WinMain()
5:
6: WndClass정의
7: ↓
8: 클래스를 등록
9: ↓
10: 메모리상에 윈도우 생성
11: ↓
12: 윈도우를 화면에 출력
13: ↓
14: 메시지 루프 (반복) 여기서 텍스트 출력할 것.
15: */
16:
17: #include <windows.h> // 모든 API함수들의 원형과 사용하는 상수들이 정의되어 있음.
18:
19: LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //메시지처리함수
20: void MyTextOut(HDC hdc, int x, int y, LPCTSTR Text);
21:
22: HINSTANCE g_hlnst; //핸들 인스턴스
23: LPCTSTR lpszClass = TEXT("Callback"); //윈도우 제목으로 사용되는 const char * (문자열)
24:
25: /***************************************************************************************************************************
26: WinMain은 엔트리 포인트(시작함수)
27: APIENTRY는 윈도우즈 표준호출규약 _stdcall(없어도 무방함)
28:
29: WinMain의 인자들...
30: ① hInstance: 프로그램의 인스턴스 핸들
31: ② hPrevInstance: 바로 앞에 실행된 현재 프로그램의 인스턴스 핸들로 16비트와 호환성위한 것이니 무시하자. Win32는 NULL이다.
32: ③ lpszCmdParam: 명령행으로 입력된 프로그램의 인수 (콘솔app에서 argv), 보통 실행직후에 열 파일의 경로가 전달됨.
33: ④ nCmdShow: 프로그램이 실행될 형태이며 최소화, 보통 모양 등이 전달된다.
34: ***************************************************************************************************************************/
35: int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
36: {
37: HWND hWnd;
38: MSG Message;
39: WNDCLASS WndClass;
40: g_hlnst = hInstance;
41:
42: WndClass.cbClsExtra = 0;
43: WndClass.cbWndExtra = 0;
44: WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
45: WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
46: WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
47: WndClass.hInstance = hInstance;
48: WndClass.lpfnWndProc = WndProc;
49: WndClass.lpszClassName = lpszClass;
50: WndClass.lpszMenuName = NULL;
51: WndClass.style = CS_HREDRAW | CS_VREDRAW;
52: RegisterClass(&WndClass);
53:
54:
55: hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW,
56: //CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
57: 200, 200, 500, 400,
58: NULL, (HMENU)NULL, hInstance, NULL);
59:
60: ShowWindow(hWnd, nCmdShow);
61:
62: while(GetMessage(&Message, NULL, 0, 0)) {
63: TranslateMessage(&Message);
64: DispatchMessage(&Message);
65: }
66:
67: return (int)Message.wParam;
68: }
69:
70: //운영체제가 호출하는 CALLBACK함수 (타이머메시지처리), 윈도우핸들,
71: void CALLBACK TimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
72: {
73: HDC hdc;
74: int i;
75:
76: hdc = GetDC(hWnd);
77: for(i = 0 ; i < 1000 ; ++i) { //알록달록 랜덤좌표에 점 1000개
78: SetPixel(hdc, rand() % 500, rand() % 400,
79: RGB(rand() % 256, rand() % 256, rand() %256));
80: }
81: ReleaseDC(hWnd, hdc);
82:
83: return ;
84: }
85:
86:
87: //운영체제가 호출하는 CALLBACK함수 (메시지 처리)
88: LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
89: {
90: HDC hdc;
91: int i;
92:
93: switch(iMessage) {
94: case WM_CREATE:
95: SetTimer(hWnd, 1, 50, TimerProc); //타이머 ID = 1, 50ms후에 TimerProc()를 OS가 자동호출.
96: //SendMessage(hWnd, WM_TIMER, 1, 0); //강제 함수호출은 안 됨.
97: return 0;
98:
99: case WM_LBUTTONDOWN:
100: hdc = GetDC(hWnd);
101: Ellipse(hdc, LOWORD(lParam) - 10, HIWORD(lParam) - 10,
102: LOWORD(lParam) + 10, HIWORD(lParam) + 10);
103: ReleaseDC(hWnd, hdc);
104: return 0;
105:
106: case WM_DESTROY:
107: KillTimer(hWnd, 1);
108: PostQuitMessage(0);
109: return 0;
110: }
111:
112: return(DefWindowProc(hWnd, iMessage, wParam, lParam));
113: }
114:
115: void MyTextOut(HDC hdc, int x, int y, LPCTSTR Text)
116: {
117: TextOut(hdc, x, y, Text, strlen(Text));
118: }
<실행화면>
94: case WM_CREATE:
95: SetTimer(hWnd, 1, 50, TimerProc); //타이머 ID = 1, 50ms후에 TimerProc()를 OS가 자동호출.
96: //SendMessage(hWnd, WM_TIMER, 1, 0); //강제 함수호출은 안 됨.
WM_CREATE메시지로 최초실행시 SetTimer()로 타이머를 돌리는데 4번째 인수로 NULL이 아닌,
TImerProc()의 이름을 넣어 콜백함수로 등록하였다.
콜백함수가 등록되면 운영체제는 윈도우로 WM_TIMER 메시지로 보내는 대신 이 함수를 주기적으로 호출한다.
70: //운영체제가 호출하는 CALLBACK함수 (타이머메시지처리), 윈도우핸들,
71: void CALLBACK TimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
콜백함수 내부에서는 RandGrp.cpp와 같이 (0~499, 0~399)의 좌표에 알록달록 점을 1000개 찍는 일을 한다.
이와 같이 콜백함수로 처리하는 방법과 RandGrp.cpp와 같이 WM_TIMER에서 처리는 방법의 차이점은,
WM_TIMER메시지는 다른 메시지가 있을 경우 실행 순서에 밀려 늦게 호출되는 경우가 있지만,
콜백함수를 사용하면 정확한 시간에 호출된다는 점이다. 그래서 정확도를 요하는 작업은 타이머 메시지보다는 콜백함수를 사용하는 것이 더 좋다고 되어 있다. 하지만 Win32 환경에서는 사실상 별 차이가 없다.
콜백함수는 어떻게 하면 WM_CREATE메시지에서 호출되게 할 수 있을까? 궁금하네…
★ 콜백함수란?
일반적으로 API함수들은 운영체제가 제공하며 프로그램에서는 이 함수들을 호출해서 운영체제의 서비스를 받는다.
예를 들어 도스의 시스템 콜 함수를 호출하여 디스크 입출력을 한다든가 윈도우즈의 TextOut()를 호출하여 문자열을 출력하는 경우가 이에 해당한다. 응용 프로그램이 운영체제에 내장도니 함수를 호출하여 원하는 작업을 하는 것이다.
반면 콜백함수는 응용 프로그램이 제공하며 운영체제가 필요할 때 호출하는 함수이다.
호출되는 방향이 거꾸로 되었기 때문에 콜백(CALLBACK)이라고 부르는 것이다.
'코스웨어 > 11년 내장형하드웨어' 카테고리의 다른 글
[내장형]최남식-2011년9월5일 일일보고서 (11) | 2011.09.06 |
---|---|
[내장형]윤민석-2011년 9월 2일 일일보고서 (10) | 2011.09.02 |
AT91SAM7S의 USART(DBGU) (0) | 2011.09.02 |
[내장형]이성재 2011년 9월1일 일일보고서 (15) | 2011.09.01 |
[내장형]이상만_2011년 8월 30일_일일보고서 (9) | 2011.08.30 |
[내장형]윤병도_2011년 8월 26일_일일보고서 (11) | 2011.08.26 |
[내장형]이영진_2011년 8월 24일_일일보고서 (10) | 2011.08.24 |
[내장형]백길남_2011년 8월 23일_일일보고서 (10) | 2011.08.23 |