#include<windows.h>
#define ID_WRITE 100
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
DWORD WINAPI MechaRead(LPVOID NotUse);
HINSTANCE g_hInst;
HWND HWndMain;
LPCTSTR lpszClass=TEXT("메카트로닉스");
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance
,LPSTR lpszCmdParam,int nCmdShow)
{
HWND hWnd;
MSG Message;
WNDCLASS WndClass;
g_hInst = hInstance;
WndClass.cbClsExtra=0;
WndClass.cbWndExtra=0;
WndClass.hbrBackground=(HBRUSH)(COLOR_BTNFACE+1);
WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
WndClass.hInstance=hInstance;
WndClass.lpfnWndProc=WndProc;
WndClass.lpszClassName=lpszClass;
WndClass.lpszMenuName=NULL;
WndClass.style=CS_HREDRAW | CS_VREDRAW;
RegisterClass(&WndClass);
hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL,(HMENU)NULL,hInstance,NULL);
ShowWindow(hWnd,nCmdShow);
while(GetMessage(&Message,NULL,0,0)){
TranslateMessage(&Message);
DispatchMessage(&Message);
}
return (int)Message.wParam;
}
//위 까지는 기본적인 API 메인 부분이다
HWND PrintWnd,WriteWnd,WriteWnd2;//각종 컨트롤들의 제어를 위한 핸들러
HANDLE hComm;//통신을 위해 사용할 포트 핸들러를 저장할 변수
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
DWORD dwWritten;//포트를 열고 읽을 때 사용할 변수로 얼마나 읽고 썼는지를 담을 변수
DCB sPState;//시리얼 통신포트 설정을 위한 구조체
const char *ucpData = "TEST ";//테스트용으로 사용할 전송 메시지
DWORD ThreadID;//스레드 생성시 얻어지는 아이디
static HANDLE hThread;//스레드 핸들러
COMMTIMEOUTS cTime; //타임아웃 구조체(통신의 지연시간 대기시간 등등)
TCHAR Buff[256];//버퍼로 사용할 공간
int len;//문자열의 길이를 담을 변수
switch(iMessage)
{
case WM_CREATE:
HWndMain = hWnd;
//전송이나 수신에 사용할 에디터창을 생성하고 핸들러를 리턴 받고있다.
CreateWindow(TEXT("static"),TEXT("전송받은 문자"),WS_CHILD|WS_VISIBLE,30,10,100,25,hWnd,(HMENU)-1,g_hInst,NULL);
CreateWindow(TEXT("static"),TEXT("수신한 문자"),WS_CHILD|WS_VISIBLE,260,10,100,25,hWnd,(HMENU)-1,g_hInst,NULL);
CreateWindow(TEXT("static"),TEXT("수신할 문자"),WS_CHILD|WS_VISIBLE,330,260,100,25,hWnd,(HMENU)-1,g_hInst,NULL);
PrintWnd = CreateWindow(TEXT("edit"), NULL, WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_AUTOVSCROLL|WS_BORDER,
20, 40, 200, 200, hWnd, (HMENU)-1, g_hInst, NULL);
WriteWnd2 = CreateWindow(TEXT("edit"), NULL, WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_AUTOVSCROLL|WS_BORDER,
260, 40, 200, 200, hWnd, (HMENU)-1, g_hInst, NULL);
WriteWnd = CreateWindow(TEXT("edit"), NULL, WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_AUTOVSCROLL|WS_BORDER,
20,260, 300, 25, hWnd, (HMENU)ID_WRITE, g_hInst, NULL);
//포트를 열고 핸들러를 리턴 받는다.
hComm = CreateFile(TEXT("COM7"),GENERIC_READ | GENERIC_WRITE, 0 , NULL, OPEN_EXISTING, 0,0);
//오픈시 에러를 검출하고 있다. 에러라면 INVALID_HANDLE_VALUE(-1)이 리턴 된다.
if(INVALID_HANDLE_VALUE == hComm)
{
MessageBox(hWnd, TEXT("포트 열 수 없음"), TEXT("ERROR"), MB_OK);
DestroyWindow(hWnd);
return 0;
}
//4096 입력 버퍼 크기, 3096 츨력 버퍼 크기
if(0 == SetupComm(hComm, 4096, 3096)) {
MessageBox(hWnd, TEXT("버퍼 설정 에러"), TEXT("ERROR"), MB_OK);
DestroyWindow(hWnd);
return 0;
}
//모든 출력을 중단하고 출력 버퍼 클리어
if( 0 == PurgeComm(hComm, PURGE_TXABORT | PURGE_TXCLEAR)) {
MessageBox(hWnd, TEXT("버퍼 초기화 에러"), TEXT("ERROR"), MB_OK);
DestroyWindow(hWnd);
return 0;
}
//포트설정 구조체의 크기
sPState.DCBlength = sizeof(sPState);
//시리얼 포트 상태를 조사한다.
if(0 == GetCommState(hComm, &sPState)) {
MessageBox(hWnd, TEXT("시리얼 상태 읽기 에러"), TEXT("ERROR"), MB_OK);
DestroyWindow(hWnd);
return 0;
}
//타임아웃 구조체를 설정 하고 있다.
//한문자를 수신한후 다음 문자가 수신될때까지 기다리는 시간 이시간이 지나면 버퍼를 리턴한다.
cTime.ReadIntervalTimeout = MAXDWORD;
cTime.ReadTotalTimeoutMultiplier = 0;//한문자를 수신하는데 걸리는 시간
//읽을 바이트수에 ReadTotalTimeoutMultiplier 값을 곱한 값에 ReadTotalTimeoutConstant값을 더해
//총 타임아웃시간을 구할수 있다.
cTime.ReadTotalTimeoutConstant = 0;
cTime.WriteTotalTimeoutMultiplier = 0;
cTime.WriteTotalTimeoutConstant = 0;
SetCommTimeouts(hComm, &cTime);
//보우율,전송비트,페리티모드,스탑비트등을 설정 한다.
sPState.BaudRate = CBR_115200;
sPState.ByteSize = 8;
sPState.Parity = NOPARITY;
sPState.StopBits = ONESTOPBIT;
//설정한 포트 값을 적용 시킨다.
if( 0 == SetCommState(hComm, &sPState)) //시리얼 포트 상태를 조사
{
MessageBox(hWnd, TEXT("시리얼 상태 설정 에러"), TEXT("ERROR"), MB_OK);
DestroyWindow(hWnd);
return 0;
}
//스레트를 생성한다. MechaRead 함수는 아래 정의 되어 있는 수신용 함수다
hThread = CreateThread(NULL, 0, MechaRead, NULL, 0, &ThreadID);
return 0;
case WM_PAINT:
hdc = BeginPaint(hWnd,&ps);
EndPaint(hWnd,&ps);
return 0;
case WM_COMMAND:
switch(LOWORD(wParam))//컨트롤에서 메시지가 오면
{
case ID_WRITE://전송 부분의 컨트롤의 메시지였다면
switch(HIWORD(wParam))
{
case EN_CHANGE://에디트창의 문자열에 변화가 있었을 경우
len = GetWindowText(WriteWnd,Buff,sizeof(Buff));//에디트창의 문자열을 읽어 들인다
if (Buff[len-1]=='\n')//문자열의 마지막이 엔터일때까지 기다리다 엔터라면 전송을 시작한다.
{
Buff[len] = '\0';
WriteFile(hComm, Buff,lstrlen(Buff),&dwWritten,0);//버퍼의 내용을 전송한다.
if (!dwWritten)
{
MessageBox(hWnd,TEXT("전송 실패"),TEXT("실패"),MB_OK);
}
SetWindowText(WriteWnd,TEXT(""));//전송후 에디트창의 값을 리셋한다.
//전송후 전송한 메시지를 다른 에디터창에 나타낸다.
SendMessage(WriteWnd2, EM_REPLACESEL, (WPARAM)TRUE,(LPARAM)Buff);
}
break;
}
break;
}
return 0;
case WM_LBUTTONDOWN:
//마우스 왼쪽 클릭시 테스트용 메시지를 전송한다.
WriteFile(hComm, ucpData, (DWORD)strlen(ucpData),&dwWritten,0);
return 0;
case WM_DESTROY:
CloseHandle(hThread);//생성한 스레드는 반드시 닫아준다
CloseHandle(hComm);//오픈한 포트 역시 닫아 주어야 한다.
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}
//스레드 함수의 원형은 반드시 아래와 같아야 한다.
DWORD WINAPI MechaRead(LPVOID NotUse)
{
TCHAR tt[3]={};
DWORD dwRead;
while(1)
{
Sleep(50);//스레드 함수에서 과도한 CPU의 점유를 막기위해 잠시 잠들어 있게 한다.
//포트를 통해 전송된 문자를 읽어 들인다. 읽어들인 문자가 없어도 블러킹 되어 있지 않고 리턴한다.
//위에서 타임아웃 구조체를 설정해 주었기 때문에 블러킹 되지 않는것이다.
ReadFile(hComm, tt,2,&dwRead,NULL);
if(0 != dwRead)//전송된 문자가 있었다면
{
//에디터창에 수신된 문자열을 출력한다.
SendMessage(PrintWnd, EM_REPLACESEL, (WPARAM)TRUE,(LPARAM)tt);
}
}
}