개발유틸리티

[스크랩] Excel에서 통신포트사용하기

EP 기술연구소 2013. 7. 3. 19:00

Generic RS232 Communicator V.05

'엑셀에서 RS232기기 연결/제어하기' 시리즈의 마지막 편으로 Generic RS232 Communicator V.05를 업로드합니다.

Generic_RS232_Communicator_Ver05.xls

이번 버젼은 아래 포스트에서 설명한 Win32Comm을 사용하여 외부 ActiveX 모듈이 필요하지 않습니다. 자세한 내용은 아래의 포스트를 참조하시기 바랍니다. 이번 버젼에서는 01/02 버젼의 간단한 인터페이스를 되살리고, 필요없는 부분은 다 삭제해 버렸습니다. 버튼도 세 개에서 두 개로 줄여버렸습니다. 필요한 부분은 사용자들께서 직접 추가/수정하여 사용하시기 바랍니다. 사용방법은 이전버젼과 대동소이하지만 다시 한번 설명합니다.

1. RS232기기를 케이블을 이용해 PC에 연결합니다. USB-RS232 컨버터를 사용하셔도 됩니다.

2. 엑셀을 띄우고, 화일을 엽니다. 매크로를 사용할 지 묻는 질문에 '예'를 선택해 주세요.

3. Serial Port Settings라고 되어 있는 곳에 통신조건을 기입합니다. Port부분은 COM1~COM4 중 실제로 연결된 포트를 선택하시고, 만일 USB-RS232 컨버터를 사용하셨을 경우에는 컨버터의 설정과 동일한 포트로 지정해야 합니다. 다른 통신조건은 접속하고자 하는 기기에 따라 다르므로 해당 기기의 메뉴얼을 참조하셔서 설정해 주시기 바랍니다.

4. Command & Data Format이라고 되어 있는 부분을 설정합니다. AddToCommand는 보낼 문자열에 붙일 제어문자입니다. 대부분의 기기가 CR, LF 또는 CR+LF를 이용하여 문장이 끝났음을 표시합니다. 어떤 문자를 붙여야 되는지는 해당기기의 메뉴얼을 참조하시기 바랍니다. RemoveCR/LF는 받은 문자열을 그대로 셀에 넣을지 설정하는 곳입니다. 문자열에 섞여들어 온 CR, LF 등의 제어문자을 그대로 셀에 넣어 버리면 기능상의 문제는 없지만 셀높이가 늘어나 엑셀화면이 지저분해집니다.

5. 오른쪽의 Commands라고 써진 컬럼에 기기에 보낼 명령어를 기입합니다. 보낼 명령어는 기기 및 여러분이 하고자 하는 작업에 따라 다르므로 기기 메뉴얼을 참조하세요. 디폴트로 써있는 "Q"는 제가 쓰고 있는 전자저울의 값을 읽어오는 명령어로서 여러분의 기기와는 아무 상관없는 것입니다. 모든 스텝의 명령어가 반드시 같아야 할 필요는 없습니다. 두 가지 동작을 교대로 반복한다던지, 어떤 값을 읽어온 후 엑셀의 if()함수를 써서 다음 스텝의 명령을 결정한다든지 하는 것도 가능합니다.

6. Time(s)라고 써있는 컬럼에 각 스텝의 시작시간을 기입합니다. 시간은 0.1초 단위로 자유로이 설정가능합니다. 기본으로 100 밀리초 주기로 체크하므로 0.1초 이하 단위의 설정을 해도 의미가 없습니다. 1주기 범위 내에서 시간지터(jitter)가 다소 있을 수 있음에 유념하시기 바랍니다. 만일 0.1초 이하의 시간지터나 초고속 처리를 원하시면 매크로 내부의 TIMER_INTERVAL(=100)을 더 작은 값으로 줄이시기 바랍니다. 그래도 윈도우즈 타이머 자체의 지터가 있으므로 정밀도의 한계가 있습니다. 정말 정밀한 시간간격 제어를 원하시면 아예 루프 내에서 GetTickCount() 함수를 이용해 제어하시든지, 외부 모듈을 이용하셔야 할 겁니다.

7. Program Variables의 EndStep은 어느 스텝까지 실행하고 멈출 것인지 결정하는 곳입니다. 적절한 값을 넣으시기 바랍니다. 레코딩이 시작된 후에도 이 값은 변경할 수 있습니다.

모든 설정이 끝나셨으면 [Record] 버튼을 눌러보시기 바랍니다. 포트의 문제가 없으면 스텝컬럼의 색이 바뀌면서 프로그램이 진행하는 것을 보실 수 있습니다. [Record] 버튼을 다시 한번 누르시면 프로그램의 진행이 멈춥니다. 포트를 열 수 없다는 메세지가 뜰 경우, 해당 포트가 존재하지 않거나 아니면 다른 프로그램에서 사용하고 있는 겁니다. 만일 Responses 부분에 아무런 변화가 없으면 기기가 꺼져 있거나, 연결에 문제가 있을 가능성이 높습니다. 읽을 수 없는 쓰레기 값이라도 뭔가 들어오면 연결은 확립된 겁니다. 통신설정이나 제어문자 설정 등이 올바른지 다시 한번 체크해 보세요. 연결은 잘 됐는데 원하는 동작을 하지 않는 경우는 명령을 잘못 설정한 것일테니 메뉴얼을 보고 적절한 명령을 써주시면 됩니다.

받은 데이타의 포맷은 기기에 따라 다릅니다. 가끔은 원하는 데이타(예를 들면 중량) 뿐만 아니라 다른 데이타(시간 등)이 함께 들어오는 경우도 있습니다. 이런 경우 인접한 셀에 필요한 데이타를 추출하는 엑셀함수식(mid, value 등)을 미리 만들어 놓으면 편리합니다. 다량의 데이타가 한꺼번에 들어오는 경우도 있는데, 참고로 엑셀은 한 셀에 8KB의 문자열 밖에 저장하지 못합니다. 그 이상의 데이타는 매크로 레벨에서 처리해 줘야 합니다.

[Clear] 버튼을 누르시면 받아놓은 데이터를 다 지울지 확인하는 메시지가 뜹니다. '예'를 선택하시면 삭 지워버리고, 프로그램 설정을 초기화합니다. 데이터를 저장하시고 싶으시면 웍시트를 하나 더 만들어, 거기다 복사해 저장해 두시면 됩니다.

맺음말

그동안 많은 분들께서 호응해 주시기에 제 전공과 전혀 관계없는 주제에 대해 많은 시간을 할애해 이런저런 업데이트 했습니다만, 점점 재미가 없어지네요. 혹시 이 매크로를 이용하신다면 어떤 용도로 어떻게 사용하시는지 알려주시면 무척 기쁘겠습니다. 저는 일단 RS232는 일단락 짓고 다른 재미있는 걸 찾아볼까 합니다. 제 글을 찾아주신 모든 분들께 감사드립니다.

by stal | 2007/10/03 18:12 | 메카트로닉스 | 트랙백 | 덧글(20)

Win32Comm - RS232 connection module for VBA

ActiveX 방식의 문제점들

그동안 IO Control ActivX 모듈을 써서 RS232 기기와 연결하는 엑셀 매크로 프로그램들을 여러 개 만들어 쓰고 있었습니다. 그런데 이 방식은 엑셀 외부의 모듈을 사용하기 때문에 매크로를 사용하려면 미리 ActiveX 모듈을 설치해야 하고, 경우에 따라서는(다 그런지는 모르겠지만 제 동료들이 쓰는 일본어 윈도우즈 및 일본어 엑셀에서는) 외부 모듈의 참조순서를 바꿔 줘야 하는 등 여러가지 번잡한 문제가 있어서 다른 사람들과 공유해서 쓰는 것이 쉽지 않았습니다. 일일이 직접 설치해 줘야 하고 문제가 생기면 고쳐줘야 했거든요.

최근에 만든 엑셀 매크로 프로그램을 여러 나라(일본, 대만, 미국) 공장 및 실험실에 배포해야 할 일이 생겼는데 위에서 말한 문제 때문에 그대로 배포할 수가 없었습니다. 엑티브엑스 모듈이야 그냥 설치화일 주고 미리 깔으라고 하면 된다지만 외부모듈의 참조순서를 바꿔주는 것은 사용자들한테 직접 하라고 하기가 영 껄끄러운 것이었습니다. 저 자신이 해도 일본어 엑셀이나 중국어 엑셀에서는 메뉴 뒤져가며 고치는 것이 꽤 번잡한 일이었거든요. 사용자들마다 깔려있는 프로그램들이 다르기 때문에 기본순서도 다 다릅니다. 그래서 액티브엑스 방식이 아닌 DLL 방식으로 만들어 공개해 놓은 것이 없는지 찾던 중에 EasyComm을 발견하게 된 겁니다. EasyComm은 외부 모듈을 전혀 사용하지 않고, 윈도우즈 운영체제가 기본적으로 갖고 있는 kernel32.dll 내의 Win32 API만을 이용하여 시리얼 포트를 연결하여 읽고 쓸 수 있게 해 줍니다. 저도 지식이 짧아서 VBA로 Win32 API를 직접 호출할 수 있다는 걸 EasyComm 내부를 뜯어보고 나서야 알게 되었습니다.

API 호출방식

API(Application Programming Interface)라는 것이 뭐냐 하면 운영체제의 여러 기능을 일반 프로그램에서 이용할 수 있도록 열어놓은 통로라고 생각하시면 됩니다. 일반 응용프로그램들은 대개 자신이 열고자 하는 파일이 하드디스크의 몇 번째 섹터에 들어있는지 알 필요가 없습니다. 그냥 운영체제한테 파일이름을 주고 열어달라고 하면 운영체제가 다 알아서 해줍니다. 모든 운영체제는(심지어 도스에서도) 이렇게 일반 프로그램들을 위해 파일 입출력, 화면 입출력, 프로세스 제어, 메모리 관리 등의 서비스를 해 주는데 이를 API라 부르기도 하고, 시스템 호출이라고도 하고, 도스에서는 BIOS(Basic Input Output Service)라고 하기도 했습니다. 윈도우즈95 이후의 32비트 윈도우즈 버젼들은 이런 서비스를 Win32 API라고 통칭합니다. 프로그래머는 자신이 이용하고자 하는 기능을 해주는 API함수를 찾아서 이를 호출하기만 하면 됩니다. C나 베이직 등의 고급언어에서 기본으로 제공하는 화일, 화면제어, 메모리 등의 관련 함수와 기능들도 사실 내부적으로는 대부분 API함수들을 이용하고 있습니다.

그래도 맘에 들지 않는...

VBA로 Win32API를 호출하여 모든 걸 할 수 있다는 걸 알게 된 저는 엄청 기뻤고, 급히 EasyComm 메뉴얼을 번역해 아래의 포스트에 올려놓았습니다. 그런데 막상 EasyComm을 써서 그동안 짜놓은 프로그램들을 바꾸려니 역시 걸리적거리는 것이 있었습니다. 이건 기능의 문제라기 보다는 사실 취향의 문제인데요...

1. 대부분의 기능들이 함수가 아닌 프로퍼티로 되어 있다.
키노시타씨가 왜 함수를 만들지 않고, 프로퍼티만으로 입출력 기능을 구현하려 했는지 잘 모르겠습니다. 프로퍼티는 한번에 단 하나의 입출력만 가능합니다. 필요한 변수들을 한꺼번에 세팅할 수 없기 때문에 귀찮게 여러 줄을 써야 합니다.

2. 에러처리가 번잡하다.
프로퍼티(특히 쓰기 작업)로는 입출력 에러 발생여부를 직접 리턴할 수가 없으므로, 각 입출력 코드 뒤에 에러체크 루틴을 일일이 넣을 수 밖에 없는데 그러면 코드가 아주 더~리해 집니다. 물론 에러 캐치 방식을 써도 됩니다만 다른 나라에서 사용할 수 있도록 일본어(또는 제가 번역해 놓은 한글)로 설정해 놓은 에러코드 및 설명을 일일이 영문으로 바꾸려니 그것도 쉬운 작업이 아닙니다.

3. 내겐 필요없는 기능이 너무 많다.
깔끔하고 간결한 기능를 선호하는 저로서는 주렁주렁 덧붙여진 기능들이 사실 번거롭기만 합니다.

위에 나열한 그다지 중요하지 않는 이유들 때문에 저는 EasyComm을 재구성해서, 제 입맛에 맞게 모든 기능을 함수 형태로 바꾸기로 했습니다. EasyComm 내의 중요한 입출력 부분만 가져오고, RS232와 관련없는 것들이나 에러핸들링은 그냥 다 빼버렸습니다. 결과적으로 ecDef와 ec 두 모듈을 합쳐 4,000 라인이 넘던 코드를 350 라인으로 줄여 Win32Comm 모듈 하나로 만들어 버렸습니다. 그래도 실질적인 RS232관련 입출력 기능은 모두 살아 있습니다. 그만큼 EasyComm에 군살이 많았다는 뜻이죠.

Win32Comm 사용법  Win32Comm.bas

위의 텍스트 화일을 다운로드(14 kB)해서 저장하신 후, 엑셀의 VB 에디터(alt-F11)에서 [파일]-[가져오기] 메뉴를 선택해서 다운로드한 파일을 임포트하세요. 그러면 모듈 트리에 Win32Comm이라는 모듈이 나타날 겁니다. 그러면 사용준비가 끝난 겁니다. 간단하죠? 이제 자신의 프로그램 코드를 작성하시면 됩니다.

다음은 코드 작성예로서 Command1 버튼이 눌려지면 COM1 포트를 열어 그 핸들을 A1 셀에 저장한 후, A2 셀의 내용을 읽어 포트에 전송합니다. Command2 버튼이 눌려지면 시리얼포트에 들어온 값을 A3 셀에 집어넣고 포트를 닫는 작업을 수행합니다.

Private Sub CommandButton1_Click()             ' 포트 열어 문자열 보내기 프로시져
    [a1] = CommOpen("com1", "4800, e, 7, 1")  ' 4800 bps, even parity, 7 data, 1 stop으로 COM1을 오픈. 핸들은 A1셀에 저장
    If [a1] = 0 Then                                        ' 에러일 경우 메세지
        MsgBox "Cannot open COM1.", , "Error"
        Exit Sub
    End If
    CommWrite [a1], CStr([a2])                       ' 문자열 보내기
End Sub


Private Sub CommandButton2_Click()              ' 문자열을 받아들인 후 포트 닫기 프로시져
    If [a1] = 0 Then Exit Sub                            ' 아직 포트가 열려있지 않으면 exit
    [a3] = CommReadStr([a1])                        ' 포트에서 문자열을 읽어 A3셀에 저장.
    CommClose [a1]                                      ' 포트 닫기
    [a1] = 0                                                    ' 이전 핸들값 삭제
End Sub

포트를 열 때 핸드쉐이크(default=none)나 버퍼크기(default=1024 bytes)는 CommSetFlowControl 및 CommSetBuffers 함수를 이용하여 세밀하게 설정할 수 있습니다. 자세한 사용법은 아래의 함수 설명 부분을 참조하세요.

입력 기다리기

CommRead 혹은 CommWrite계열의 입출력 함수를 호출하는 경우, 각 함수에서는 입출력 작업을 운영체제에 의뢰한 후 하드웨어적인 작업이 끝날 때까지 기다리지 않고 바로 리턴합니다. 원하는 문자열이 다 들어올 때까지 기다리는 기능은 갖고 있지 않습니다. 이런 부분은 포트에 문자가 몇 개 들어왔는지는 체크해 볼 수 있는 CommNumRcv 함수 등을 이용해 사용자가 직접 작성해 줘야 합니다. 일반적으로 입력을 기다리는 방법은 루프를 써서 계속 체크하는 방법(폴링이라고 합니다), 주기적으로 입력버퍼에 들어온 것이 있는지 체크하는 방법 그리고 이벤트(혹은 인터럽트) 방식이 있는데 Win32 API는 이벤트 방식을 지원하지 않습니다.

만일 루프를 써서 기다리는 방법을 쓰신다면 반드시 루프 내에 DoEvents 를 넣어 두세요. 안 그러면 문자열이 들어올 때까지 엑셀 전체가 무응답 상태가 되어 버립니다. 별도의 쓰레드를 만들면 이런 문제를 피할 수 있는데 VBA에서 쓰레드를 만드는 방법이 있는지는 모르겠습니다. 어쩌다 어떤 블로그에서 Win32 API로 VBA 쓰레드를 만들어 보려고 시도하는 것을 본 적이 있는데 결국 실패하더군요.  

'예
CommWrite [a1], [a2]         ' 포트로 문자열 전송. [a1]은 포트 핸들, [a2]는 기기명령어를 담고 있다고 가정.
Do While CommNumRcv([a1]) = 0   ' 포트에 들어온 문자가 없으면 기다림
    DoEvents                                  ' 무한루프 때문에 엑셀이 멈춰버리는 것을 방지
Loop
[a3] = CommReadStr([a1])             ' 입력된 문자열을 [a3]에 저장

때로는 루프를 쓰는 것보다 타이머를 이용해 주기적으로 문자열이 들어왔는지 체크하는 것이 더 유용할 수도 있습니다. 타이머를 이용하는 가장 쉬운 방법은 엑셀 내장 함수인 Application.OnTime을 쓰는 것입니다. 이 함수는 지정된 매크로를 지정된 시간에 호출하도록 설정하는데 쓰이는데, 콜백프로시져 끝부분에 자기 자신을 다시 콜백하도록 해놓으면 주기적인 작업을 할 수 있습니다. 콜백프로시져는 반드시 모듈레벨이어야 하고 시트별로 저장된 매크로는 부를 수 없습니다.

'예. Module1 내에 작성
Public Sub CheckInput()
        If [a1] = 0 Then Exit Sub              ' [a1]에 포트 핸들이 저장되어 있다고 가정. 만일 0이면 exit
        If CommNumRcv([a1]) > 0 Then  '  포트에 들어온 문자열이 있으면 
            'your handling routine              ' 필요한 작업을 합니다.
        End If
        Application.OnTime Now + TimeValue("00:00:01"), "CheckInput"    ' 1초 후에 다시 체크하도록 설정
End Sub

프로그램 어디선가 CheckInput을 한번 실행한 이후로는 스스로 계속 돌아갑니다. 그 Application.OnTime 이 편하긴 한데 시간설정의 최소 단위가 1초로 다소 길다는 점, 호출시마다 커서 모양이 화살표로 바뀐다는 점 그리고 어쩌다 시간설정을 잘못 했을 경우 이미 닫아놓았던 파일도 그 시간이 되면 자동으로 다시 열릴 수 있다는 등의 부작용이 있습니다. 보다 빠르고 부작용이 없이 타이머를 쓰는 방법도 있는데 그건 역시 Win32 API 기능을 불러다 쓰는 것입니다. 이것 역시 모듈레벨에서 만들어야 합니다. 먼저 표준모듈(Module1)을 만들고, 입력을 체크하고 필요한 작업을 수행하는 프로시져를 만듭니다.

Sub CheckInput()
    On Error Resume Next  ' 에러가 발생하면 무시하고 다음 라인으로 넘어갑니다. 고속루틴에서는 필수.

    If [a1] = 0 Then Exit Sub                           ' [a1]에 포트 핸들이 저장되어 있다고 가정. 만일 0이면 exit
    If CommNumRcv([a1]) = 0 Then Exit Sub   '  포트에 들어온 문자열이 없으면 exit
        'your handling routine                           ' 여기에 필요한 작업을 합니다.
End Sub

그 다음에는 모듈의 윗부분에 다음과 같이 Win32 타이머 API를 이용하겠다는 선언을 합니다.

Public Declare Function SetTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Public Declare Function KillTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long) As Long

그리고나면 모듈뿐만 아니라 시트레벨에서도 필요할 때 아무 때나 다음과 같이 타이머를 켜거나, 끌 수 있습니다. SetTimer는 Application.OnTime과 달리, 콜백 프로시져를 한번만 부르는 것이 아니라 KillTimer를 부르기 전까지는 지정된 시간간격으로 계속 콜백한다는 것에 주의하시기 바랍니다.

SetTimer Application.hWnd, 3100, 200, AddressOf CheckInput 
KillTimer Application.hWnd, 3100  

위의 예에서 3100은 타이머ID입니다. 여러 개의 타이머가 있을 때 구분하기 위해 씁니다. 아무 값이나 자신이 정해서 쓰면 됩니다. 200은 타이머 간격을 밀리초 단위로 설정한 것입니다. 매 0.2초마다 콜백 프로시져가 불려져서 포트에 들어온 데이타가 있는지 체크합니다. 데이타가 있으면 CommReadBytes(바이너리 데이타 읽어오기) 또는 CommReadStr(아스키 문자열 데이타 읽어오기) 함수를 이용해 데이타를 읽어와 처리하면 됩니다. 

Win32Comm_Example.xls

위 엑셀 파일은 위에서 설명한 포트 여닫기, 읽고 쓰기, 타이머 사용법을 실제 화일로 만든 것입니다. 예제일 뿐이므로 실용적이지는 않습니다. 타이머는 포트가 열려 있을 때 버튼을 눌러야 동작하고, 포트를 닫으면 저절로 멈춥니다. 콜백이 일어나고 있음을 확인하기 위하여 A4 셀의 값을 하나씩 증가시킬 뿐 실제로 시리얼 포트 관련 작업은 하지 않습니다.

Win32Comm 함수 설명

CommOpen 함수

Public Function CommOpen(PortName As String, Optional Setting As String = "9600, n, 8, 1") As Long
PortName: 열고자 하는 포트이름. "COM1", "COM2" 등
Setting: 통신조건. 문법은 DOS의 mode 명령어와 동일합니다. 기본값은 9600 bps, no parity, 8 data bit, 1 stop bit 입니다.
            mode 명령어 문법
            baudrate=1200, 2400, 4800, 9600, 14400, 28800, ... , 119200 중 하나. 저는 9600이랑 4800밖에 안 써봐서..
            parity=no, even, odd, mark, space 중 하나
            data=5,6,7,8 중 하나
            stop=1,2
반환값: 성공=화일핸들, 실패=0. 화일핸들은 다른 함수들을 호출할 때 항상 첫번째 인수로 써야 합니다.
예1) [a1] = CommOpen "COM1", "9600, n, 8, 1"              '약식 표기에서는 순서를 지켜야 합니다.
예2) [a1] = CommOpen "COM1", "parity=n stop=1 data=8 baud=9600"   '변수이름을 지정하면 순서는 관계없습니다.


CommClose 함수

Public Function CommClose(Handle As Long) As Long
Handle: 포트 핸들. CommOpen의 반환값
반환값: 성공<>0, 실패=0
예) 
Dim hPort as Long
hPort = CommOpen("COM1", "9600, n, 8, 1")
...
CommClose hPort

CommSetBuffer 함수
Public Function CommSetBuffers(Handle As Long, InBufferSize As Long, OutBufferSize As Long) As Long
Handle: 포트 핸들. CommOpen의 반환값
InBufferSize/OutBufferSize: 입력 및 출력버퍼의 크기. 바이트 단위. 디폴트=1024
반환값: 성공<>0, 실패=0
예) 
Dim hPort as Long
hPort = CommOpen("COM1", "9600, n, 8, 1") ' 포트 오픈
CommSetBuffers hPort, 4096, 1024    ' 입력버퍼 4KB, 출력버퍼 1KB

CommSetFlowControl 함수

Public Function CommSetFlowControl(Handle As Long, Flow As String, _
        Optional xonChar As Byte = &H11, Optional xoffChar As Byte = &H13, _
        Optional xonlim As Integer = 128, Optional xofflim As Integer = 128) As Long
Handle: 포트 핸들. CommOpen의 반환값
Flow: "No", "XonXoff", "RTS", "DTR" 중 하나. 사실 첫 글자만 구분하므로 "N", "X", "R", "D" 만 써도 됩니다. 디폴트="N"
xonChar/xoffChar: xon/off 방식 흐름제어에 이용할 문자들입니다. 지정하지 않으면 디폴트 문자들로 지정됩니다.
xonLim/xoffLim: xon/off 방식 흐름제어시 xon/xoff를 개시할 쓰기버퍼의 잔량입니다.
반환값: 성공<>0, 실패=0
예) 
Dim hPort as Long
hPort = CommOpen("COM1", "9600, n, 8, 1") ' 포트 오픈
CommSetFlowControl hPort, "X"                  ' Xon/Xoff 흐름제어, 제어문자 및 버퍼임계선은 디폴트 사용

CommWrite 함수

Public Function CommWrite(Handle As Long, ByteData As Variant, Optional length As Long = 0) As Long

Handle: 포트 핸들. CommOpen의 반환값
ByteData: 보낼 데이타. Byte, Integer, Long, Single, Double의 경우 0-255의 범위로 truncation됩니다.
                실수형은 정수형으로 반올림됩니다. 배열은 각 개별항이 truncation 및 반올림되어 전송됩니다.
                엑셀의 문자열(유니코드)는 ANSI코드로 변환된 후 전송됩니다.
length: 데이타가 배열/문자열일 경우 보낼 데이타 개수입니다. 0 이면 모든 데이타를 전송합니다(디폴트).
반환값: 출력버퍼로 보낸 데이타 갯수. 전송성공 <> 0,  전송실패=0.
예) 
Dim hPort as Long
hPort = CommOpen("COM1", "9600, n, 8, 1") ' 포트 오픈
CommWrite hPort, "Test"

CommWriteRTS / CommWriteDTR 함수

Public Function CommWriteRTS(Handle As Long, level As Long) As Long
Public Function CommWriteDTR(Handle As Long, level As Long) As Long

Handle: 포트 핸들. CommOpen의 반환값
level:  각 라인의 설정값. 0 이면 reset(마이너스 레벨), 0 이 아니면 set(플러스 레벨)
반환값: 성공 <> 0,  실패=0.
예) 
Dim hPort as Long
hPort = CommOpen("COM1", "9600, n, 8, 1") ' 포트 오픈
CommWriteDTR hPort, 1                             ' set DTR

나중에 CommSetFlowControl함수를 써서 핸드쉐이크를 설정하면 CommWriteRTS/CommWriteDTR로 설정해 놓은 라인상태는 무효화될 수 있으니 주의하세요.
CommNumRcv 함수

Public Function CommNumRcv(Handle As Long) As Long

Handle: 포트 핸들. CommOpen의 반환값
반환값: 입력버퍼에 들어온 문자 갯수. 에러나 no data일 경우 0.
예) 
Dim hPort as Long
hPort = CommOpen("COM1", "9600, n, 8, 1") ' 포트 오픈
CommWrite hPort, Cstr([a2]) + Chr(13)        ' A2 cell의 내용을 기기로 전송
Do While CommNumRcv(hPort)=0                ' 문자가 들어올 때까지 기다림
    DoEvents                                              ' 엑셀 freeze 방지
Loop
[a3] = CommReadStr(hPort)                        ' 들어온 문자열을 A3 셀에 저장
CommReadBytes 함수

Public Function CommReadBytes(Handle As Long, Optional length As Long = 0) As Byte()

Handle: 포트 핸들. CommOpen의 반환값
length: 읽어올 문자의 개수. 0(default)이거나, CommNumRcv보다 클 경우 버퍼내의 모든 문자를 읽어옵니다.
반환값: 에러나 no data일 경우 인덱스가 0 to 0인 배열을, 데이타가 있을 경우 1 부터 시작하는 배열을 반환합니다.
예) 
Dim bdata() as Byte
bdata = CommReadBytes(hPort, 1)     ' 입력버퍼의 데이타 중 1 바이트만 읽어 옵니다.
If Ubound(bdata) Then 
    Msgbox "No data or error"          ' 반환된 배열의 인덱스 상한이 0이면 no data 또는 에러
Else
    [a3] = Cstr(bdata(1))                  ' 아스키 값을 A3 셀에 표시합니다.
End If

CommReadBytes 함수는 바이너리 데이타를 처리하기 위해 만든 것으로 일반적인 아스키 문자열을 읽어올 경우에는 아래의 CommReadStr 함수가 더 편리합니다.
CommReadStr 함수

Public Function CommReadStr(Handle As Long, Optional length As Long = 0) As String

Handle: 포트 핸들. CommOpen의 반환값
length: 읽어올 문자의 개수. 0(default)이거나, CommNumRcv보다 클 경우 버퍼내의 모든 문자를 읽어옵니다.
반환값: 버퍼 내의 데이타를 문자열로서 반환합니다. 에러나 no data일 경우 빈 문자열("")을 반환합니다.
예)  CommNumRcv 예제 참조

CommReadStr 함수는 바이너리 데이타, 특히 아스키값이 0인 데이타를 제대로 처리하지 못합니다. 윈도우즈 및 엑셀 내부에서 0을 문자열 종료로 인식하기 때문입니다. 게다가 CommReadStr함수는 자동으로 ANSI 코드를 유니코드로 변환합니다. 바이너리 데이타를 원형 그대로 입력받기 위해서는 위에서 설명한 CommReadBytes 함수를 이용하세요.
CommReadCTS / CommReadDSR / CommReadDCD / CommReadRI 함수

Public Function CommReadCTS(Handle As Long) As Long
Public Function CommReadDSR(Handle As Long) As Long
Public Function CommReadDCD(Handle As Long) As Long
Public Function CommReadRI(Handle As Long) As Long

Handle: 포트 핸들. CommOpen의 반환값
반환값: 플러스 레벨=+1, 마이너스 레벨=-1, 에러=0.

RS232의 신호라인 각각의 현재 상태를 읽어옵니다. 리턴값 0은 마이너스 레벨이 아니라, 에러를 뜻함에 주의하세요. 너무 단순해서 예는 생략합니다.
CommClear / CommClearRx / CommClearTx 함수

Public Function CommClear(Handle As Long) As Long
Public Function CommClearRx(Handle As Long) As Long
Public Function CommClearTx(Handle As Long) As Long

Handle: 포트 핸들. CommOpen의 반환값
반환값: 성공<>0, 에러=0.

입출력 버퍼를 모두 혹은 각각 초기화합니다. 너무 단순해서 예는 생략합니다.
맺음말

Win32Comm은 EasyComm을 기반으로 수정한 것입니다. 저 역시  EasyComm의 라이센스 정책에 따라 Win32Comm을 프리웨어로 공개합니다. 아무나 용도에 관계없이 자유로이 사용하실 수 있습니다. 단 Win32Comm을 사용하다 발생한 손실에 대해서는 아무런 책임도 지지 않습니다. Win32Comm은 상업용 어플리케이션의 일부로서 포함할 수 있지만, 단독으로 판매해서는 안 됩니다.

그동안 ActiveX를 사용해 오던 매크로 프로그램을, Win32Comm을 써서 바꿔봤는데 아무 이상없이 잘 작동하고 있습니다. 하지만 제가 사용하는 작동조건이 몇 개 안 되기 때문에 다른 사용조건에서는 에러가 날 수도 있습니다. 그런 부분을 알려주시면 가능한 범위 내에서 업데이트하도록 하겠습니다. 그리고, Generic RS232 Communicator도 조만간 Win32Comm용으로 업데이트하도록 하겠습니다. 아무쪼록 제 작은 노력이 여러분의 작업에 도움이 되었길 바랍니다.

by stal | 2007/09/29 02:48 | 메카트로닉스 | 트랙백 | 덧글(1)

VBA로 작성된 RS232 연결모듈 EasyComm 1.87

ActiveX를 사용하지 않고, 순전히 VBA로만 작성된 RS232 연결모듈인 EasyComm 1.87를 소개하고자 합니다. 이 모듈은 일본의 K. Kinoshita 씨가 작성한 것으로 프리웨어로서 그 이용과 배포가 자유롭습니다. 원래는 일본어로 쓰여진 것인데 제가 한국어로 바꿨습니다. 한국어판의 번역과 배포는 원작자의 허락을 구한 것임을 알려드립니다. 저작권 관련 사항은 압축화일 내에 있는 readme.txt의 내용을 참조하시기 바랍니다.

아래에 두 개의 압축화일이 있는데 하나는 원래의 일본어판 모듈이고, 다른 하나는 제가 한국어로 번역한 것입니다. 한국어판은 화일이름 끝에 Kr을 붙여 구분하였습니다.

한국어판 압축을 풀면 4개의 화일이 나옵니다.

- readmeKr.txt        간략한 소개와 저작권 관련내용
- ecManualKr.xls    메뉴얼
- ecKr.bas              EasyComm의 주 모듈  
- ecDefKr.bas         Win32 API 인터페이스용 함수 및 구조체 선언모듈

사용하실 때는 VB 에디터에서 ecKr.bas와 ecDefKr.bas 두 개의 모듈만  임포트하시면 됩니다. 자세한 사용법은 메뉴얼을 참조하시기 바랍니다.

ecKr.bas를 번역할 때 실수로 '써넣기'를 '읽어오기'로 전부 바꿔쓰기 해버려 이상하게 되어버린 곳이 꽤 있는데 일일이 찾아 고치지 못했습니다. 그리고, 번역을 끝내고 Excel에서 문법에러가 없는 것만 확인한 상태로서, 아직 동작테스트까지는 안 해 봤습니다. 조만간 실제 코딩작업을 해가면서 오류가 발견될 때마다 수정해 나갈 생각입니다. 아무쪼록 잘 사용하시고 오류나 개선사항이 있으면 원작자나 제게 알려주시면 고맙겠습니다.

by stal | 2007/09/16 02:56 | 메카트로닉스 | 트랙백(1) | 덧글(2)

곧 업데이트합니다.

일본 출장 왔다가 현금을 다 써버려 주말에 밖에 못 나가고 호텔에서 우울하게 보내다가 Excel에서 RS232연결하는 방법을 구글에서 한번 검색해 봤습니다. 무료! 라고 써있는 건 거의 미끼고, 실제로는 무료가 거의 없더군요. 그러던 중 어떤 일본 프로그래머가 공개한 버젼을 보게 되었습니다. ActiveX가 아니고, 순전히 Win32 call로만 짠 모듈이더군요. 오옷!!! 이거 왕건이입니다.

주말 내내 메뉴얼 번역해서 한국어판 완성했습니다. 그리고, 원작자한테 한국어판 메뉴얼 공개해도 되겠냐고 메일보냈는데 오늘 허락메일 받았습니다. 그리고, 아직 공개하지 않은 최신판까지 첨부해 보냈더군요. 이번 주말에 번역 끝내서 올릴 생각입니다.

Communicator도 다시 업데이트 해야겠지요. 일단 연결모듈부터 공유하고, 나중에 communicator도 손 볼 생각입니다. 뭐.. 제 블로그 한두번 들러보신 분은 아시겠지만 그게 언제쯤 될 건지는 순전히 제 맘대로입니다.. 흐흐흐..

by stal | 2007/09/13 00:29 | 트랙백 | 덧글(0)

Generic RS232 Communicator V.03

허접한 프로그램에 관심을 가져주신 많은 분들께 다시 한번 감사드리며, Generic RS232 Communicator V.03 버젼을 공개합니다. 각 분야의 사용자들께서 요구하시는 기능이 상당히 다양한데, 제가 보기에 공통된 부분만 뽑아 필요한 기능을 선정했습니다. 하지만 범용성을 최대한 유지하다보니 개별 응용분야에 맞지 않는 부분이 당연히 있을 겁니다. 이해바랍니다. 변경된 점은 다음과 같습니다.

1. 단일 반복 명령어에 최적화
이전 버젼은 다양한 명령을 사용자가 매번 자유롭게 바꿀 수 있도록 유연성에 초점을 맞췄습니다만 이로 인해 시간간격이 초 단위 밖에 안 되는 단점이 있었습니다. 많은 분들께서 밀리초 단위의 빠른 속도를 필요로 하시기에 시간간격을 밀리초 단위로 바꾸고,  동일명령어만을 반복적으로 전송하도록 수정했습니다. 유연성을 상당히 희생한 수정이었는데, 여전히 다른 명령어를 보내고 싶은 경우가 있을 것 같아 이는 별도의 버튼으로 처리할 수 있도록 했습니다.

2. 선택 기록 가능
이전 버젼에서는 모든 데이타가 곧바로 셀로 들어가도록 했는데, 지켜보다가 필요할 때만 저장되도록 하고 싶다는 분이 계셔서 일단 하나의 데이타 셀만을 업데이트 하도록 바꿨습니다. 유저는 필요에 따라 그 값을 수동저장하거나 혹은 연속적으로 자동저장할 수 있습니다.

간단한 설명과 사용법입니다.
설정부:
    Work Variables: 매크로에서 사용하는 변수들입니다. 사용자가 바꾸실 필요는 별로 없을 것 같습니다.
    Program Settings:
        interval: 명령 전송 간격을 설정합니다. 밀리초 단위로 설정가능합니다.
        ReadCmd: 반복전송할 명령어입니다. 대개 측정값을 읽어오기 위한 것이겠지요. 연결기기의 메뉴얼을 참조하세요. 위에 써있는 Q는 어떤 전자저울에서 중량을 읽어오기 위한 것입니다.
        Cmd1 & Cmd2: 반복전송할 명령어 외에 가끔씩 보낼 필요가 있는 명령어는 여기에 적어 놓으세요. 상단의 [Cmd1], [Cmd2] 버튼을 누르시면 기기로 각 명령어를 전송합니다.
        AddToCmd: 명령어의 끝에 붙여줄 문자입니다. 대개 CR(carriage return, ascii 13) 또는 LF(line feed, ascii 10)인데, 연결할 기기의 메뉴얼을 보시면 뭘 써야 하는지 나와 있을 겁니다.
        RespEnd: 기기에서 전송하는 답신 문자열의 끝에 들어가는 문자를 설정하는 곳입니다. 역시 CR 또는 LF를 설정할 수 있는데, 이 문자가 들어오면 매크로가 데이타를 업데이트합니다. 
    Serial Port Setting: 이전 버젼과 동일합니다.

버튼:
    [Connect]: 시리얼 포트를 열고, 닫습니다. 다른 버튼을 누를 때 필요한 경우 자동으로 이 버튼도 같이 눌러집니다.
    [Read]: 반복적으로 ReadCmd 셀에 저장된 명령어를 보내고, 값을 읽어 옵니다. Response 및 Data 셀가 업데이트 됩니다.[Record] 버튼을 누르면 자동으로 같이 눌러집니다.
    [Record]: 들어오는 데이타를 시간과 함께 data section에 자동으로 연속저장합니다. 
    [Write1P]: 현재값 한 포인트만 저장합니다. 버튼을 누를 때 한번만 동작합니다.
    [Clear]: 확인 메세지가 뜨고 이 때 [예]를 누르면 데이타 섹션에 저장된 값들을 모두 지웁니다.

데이타 셀들:
    Response: 기기에서 보내온 문자열 원본입니다.
    Data: 유저가 엑셀함수를 써서 response 문자열로부터 필요한 데이타를 추출할 수 있도록 한 셀입니다. 원본 문자열 그대로를 사용하신다면 그냥 =e10 또는 =response라고 적어주시면 됩니다.

데이타 섹션:
    레코드가 들어가는 곳입니다. 기록이 모두 끝난 후 이곳의 데이타를 다른 화일로 옮겨 저장하시면 됩니다. 매크로 동작은 밀리초 단위로 하지만 시간기록은 초 단위로 밖에 안 됩니다. 엑셀의 시간 포맷이 초 단위 밖에 지원하지 않기 때문입니다. 밀리초 단위가 정 필요한 경우 그냥 시간간격에 라인넘버 곱해서 사용하시면 될 겁니다.

맺음말:
아시다시피 블로그를 자주 들르는 편이 아니라 업데이트 자주 못 합니다. 질문이나 제안이 있으시면 덧글로 남겨주시면 고맙겠습니다.

generic_rs232_communicator_ver.03.xls
출처 : robot story
글쓴이 : 달팽이 원글보기
메모 :