GPIF를 사용한 Bulk In

출처 : http://muosys.egloos.com/221117

 

회로도

clip_image002

전체적인 모습

clip_image003

세부모습1

clip_image004

세부모습2

clip_image005

UniHigh Firmware v2.7
UniHigh App v2.7

v2.7 Application은 기본적으로 v2.6과 동일하다.
Overlapped I/O에 관련된 코드오류를 바로잡은 것이 다른 점이다.

실험방법은 이전 v2.6의 실험방법과 동일하다.

Firmware v2.7은
v2.6의 8051 코어가 Bulk IN을 수행하던 부분을 GPIF가 대신하도록 바꾼 것이다.

구체적으로는 TD_Poll()의 마지막에 기존의 같은 기능을 하던 부분을 들어내고, 다음의 코드를 추가한 것이다.

if( bConfigured )
{
    if( GPIFTRIG & 0×80 ) // GPIF is Idle
    {
        if( !bGPIFStart )
        {
            GPIFTRIG = GPIFTRIGRD | GPIF_EP6;
            bGPIFStart = TRUE;
        }
        else
       {
            INPKTEND = 0×06;
            bGPIFStart = FALSE;
        }
    }
}

v1.7의 코드를 약간 수정하여 작성한 부분이다.
Firmwarw v2.7에 오류가 있어서 디버깅 중이다.
(Bulk IN이 한번만 수행됨)
본좌를 똥통에서 건져줄 행자의 구원의 손길을 기다린다.
이상.

두번째 Bulk In

출처 : http://muosys.egloos.com/203067

오늘은 Bulk IN transfer를 실험해 보자.

이전 강의 “Bulk IN 실험”을 참조하시라.

우선 아래와 같은 회로를 꾸미자.
이전 강의(Bulk IN 실험)의 회로와 약간 다르니, 이전 강의의 회로도를 참조해서 실험하면서 왜 안되나요? 라고 물으면 대략 즐.

clip_image002

바로 전 실험을 하면서 이미 꾸며져 있는 회로에 Parallel Port와 인터페이스 할 배선만 추가하면 된다.
전체사진

clip_image003

세부사진1(Parallel Port 쪽)

clip_image004

세부사진2(LED 쪽)

clip_image005

세부사진3(버튼 쪽)

clip_image006

펌웨어는 v2.5에 v1.6을 추가하는 형식으로 수정하였다.
Unihigh Firmware v2.6

어플리케이션 소스
Unihigh App v2.6
실행파일

새로 추가된 (맴버) 함수는 다음과 같다.

OnButtonBulkIn()
BulkInThread(…)
ReadDataArrived( … )
UpdateListBoxScroll(…)

리소스에 새로 추가된 에디트 박스에 읽을 바이트 수를 써 넣고서, “Read” 버튼을 누르면 OnButtonBulkIn() 함수가 호출된다.

OnButtonBulkIn() 함수는 데이터를 기다리는 동안 메인 쓰레드를 홀딩시키지 않기 위해 BulkInThread(…)를 생성 한 후 리턴된다.

BulkInThread(…)에서는 ReadFile(…)을 이용해서 디바이스로부터 데이터를 읽어오는데, 여기서 이전 쓰레드(GetMsgThread)와 다른 점이 있다면
WaitForSingleObject( stOverlapped.hEvent, dwWaitTime );
이전에는 두번째 인자에 INFINITE를 주었었는데, 이번에는 dwWaitTime로 유한한 시간(1분)을 준다.
즉 “Read” 명령을 주고 나서 1분내에 그 ReadFile이 리턴되지 않으면 에러 메시지를 출력하도록 되어 있다.

성공적으로든 아니면 에러가 나든 BulkInThread(…)를 마치면 ReadDataArrived(…)가 호출되는데, 여기서 받아온 데이터를 ListBox에 출력한다.
그리고 데이터를 위한 버퍼를 해제한다.

UpdateListBoxScroll(…)은 리스트 박스의 스크롤을 갱신하기 위한 함수로서 USB 강좌에서 설명할 성질의 것은 아니므로 패쓰.

자. 이제 테스트를 해보자.
이전 강의(Bulk IN 실험)에 썼던 Parallel.exe를 다시 줏어오자.

펌웨어를 UniHigh 보드에 다운로드 시키고,

다음은 Parallel.exe를 실행시켜 놓고 나서, (물론 컴터에 패러렐 포트는 꽂혀 있어야 한다.) 다운로드 할 데이터 파일(data.bin)을 지정해 준다.

그리고 나서, Unihigh App v2.6를 실행시키고 “Read Byte” 에디트 박스에 Parallel.exe에 나타난 데이터의 크기(377, 즉, 우리가 읽어올 데이터의 크기)를 입력한다.
그리고, “Read” 버튼을 누른다.

이제 기다리면 되느냐?
아니다.
Parallel.exe의 “Transfer” 버튼을 눌러줘야 패러렐 포트를 통해 UniHigh 보드로 데이터가 날아가고, UniHigh 보드는 받은 데이터를 Bulk In USB 파이프를 통해 호스트로 전송한다.

clip_image007

EEPROM에 펌웨어 구워넣기

출처 : http://muosys.egloos.com/198883

 

오늘은 잠깐 곁가지로 새서, EEPROM에 펌웨어를 구워보자.

이제까지 우리는 작성한 펌웨어를 EZ-USB Control Panel을 써서 CY7C68013으로 다운로드 해 왔다.
하지만 이러한 방법은 개발할 때에나 쓸 수 있는 것이고, USB장치를 사용할 때마다 사용자더러 이렇게 번거로운 절차를 행하라고 시킬 수는 없는 일 아닌가?

따라서 펌웨어를 EEPROM에 구워 넣고, 장치를 USB 포트에 끼워 넣으면 바로 우리가 코딩한 펌웨어에 따라 동작하는 USB 디바이스로 인식되게끔 만들어 보자.

먼저 EEPROM의 시작번지 첫 두 바이트에 0xFF 0xFF를 써 주어야 한다.
요 짓을 왜 하느냐 하면 아래에 설명하겠지만, EEPROM 이미지(xxx.iic 파일)를 EEEPROM(24LC64)에 써 넣는 일도 EZ-USB Control Panel을 사용해서 하는데, 만약 첫 두 바이트가 0xC0 0xXX 요렇게 이미 세팅되어 있다면 EZ-USB Control Panel이 ‘아! EEPROM이 이미 프로그램 되어 있구나’ 하고 알아차리고는 덮어쓰기를 거부해 버린다.
따라서 EEPROM을 굽기 앞서 이 두 바이트를 지워주는 절차로서 “0xFF 0xFF”를 써주는 것이다.

자 어떻게 하는냐?
EEPROM이 소켓에 잘 끼워져 있는지 확인부터 하시라.
끼워져 있는가?
그럼 빼시라. ㅋㅋㅋ
(Tip. 처음 새 EEPROM을 끼울 때에는 접촉불량이 생길 수 있으므로, 그냥 세네번 끼웠다 뺐다를 반복해 주시라. 다리가 휘지 않도록 조심 하면서. 핀셋으로 하면 캡숑 좋다.)

EEPROM을 “뺀” 상태에서 UniHigh 보드를 USB 포트에 꼽는다.
그리고 나서 EEPROM을 끼워라.

이제 EZ-USB Contrrol Panel을 연다.
먼저 EZ-USB Contrrol Panel의 “Download” 버튼을 누른다.

clip_image002

C:\Cypress\USB\Examples\FX2\Vend_ax 폴더의 Vend_Ax.hex를 선택해서 다운로드한다.

clip_image004

아래와 같이 세팅하고 “Vend Req” 버튼을 누른다.

clip_image006

이제 24LC64의 첫 두 바이트가 지워졌다.

이제 리셋버튼을 누르거나 USB 포트에서 분리 후 다시 장착해서 장치관리자를 들여다 보자.

clip_image007

위와 같이 나오면 EEPROM이 잘 초기화 된 것이다.

이제 EEPROM을 프로그램 할 차례이다.

EZ-USB Contrrol Panel의 “EEPROM” 버튼을 누른다.

clip_image009

굽고자 하는 펌웨어의 iic파일을 선택해 준다.

clip_image011

확인을 누르면 프로그램이 한 10초정도 응답이 없다가, 다 구워지면 커서가 깜빡거린다.

clip_image013

리셋버튼을 누르거나 USB 포트에서 분리 후 다시 장착해서 장치관리자를 들여다 보자.
우리가 짠 펌웨어대로 장치가 나타날 것이다.

이제는 우리가 만든 USB 디바이스를 아무 컴터에나 꼽기만 하면 인식이 될 것이다.
(물론 드라이버가 깔려있거나, 깔아야 하고, 테스트 프로그램도 있어야 하지만…)

사족.
펌웨어 소스를 빌드한 결과물은 hex파일이고, EEPROM이미지는 iic파일이다.
hex파일을 iic파일로 바꾸어주는 툴은 c:\cypress\usb\bin 폴더의 hex2bix.exe이다.

이 hex2bix.exe를 사용한 예는 Keil u-Vision으로 예제 프로젝트를 열고, ( 왼쪽 창에서 “Target 1”이 선택된 상태에서) 메뉴의 Project->Options for Target “Target 1”을 선택한 후에 “Output” 탭을 선택하면 아래와 같이 볼 수 있다.

clip_image014

UniHigh 펌웨어와 BulkLoop 펌웨어의 세팅을 비교해 보시라.
hex2bix를 실행시킬 때 파라메터가 어덯게 다른지.
디바이스 디스크립터의 VID와 PID를 바꾸면 이 파라메터도 같이 바꾸어 주어야 한다.

Bulk Out Pipe(PIPE 01)를 통해 디바이스로 데이터 전송하기

출처 : http://muosys.egloos.com/196351

 

오늘은 Bulk Out Endpoint를 통해 호스트로부터 데이터를 디바이스로 전송하는 것을 연습해 볼 차례이다.

Bulk Endpoint로 데이터 날리기를 참조하여 회로를 꾸미고, 아래의 소스들을 다운로드 받아 설치하자.
참고적으로 다가 얘기하자면 이미 구성되어 있는 회로(Port A.7의 LED, Port A.6의 버튼 )는 떼어내지 말고, 그대로 두고서 Port B에 8개의 LED를 추가로 설치하길 바란다.

clip_image001

UniHigh Firmware v2.5
UniHigh Application v2.5
data.bin <- 전송할 데이터를 담고 있는 파일(이전의 파일과 동일한 파일)

만약 버튼(Port A.6)을 떼어내고 실험하고자 한다면, 펌웨어의 TD_Poll()함수에서 다음의 부분을 주석처리 해줘야 한다.
if(PA6 == 1 && !bButtonMask)

이전 강좌들과 마찬가지로 펌웨어는 unihigh.sys 드라이버와 붙도록 VID, PID만 바꾼 것이다.

UniHigh Application v2.5를 살펴보면 Bulk Out transfer를 위해 버튼 두 개와 에디트 박스 하나를 리소스에 추가 했다.

“File Select” 버튼은 디바이스로 전송할 파일을 선택 하기 위한 대화상자를 열기 위한 버튼이며, 눌렸을 때는 OnButtonSelectFile() 멤버함수가 호출된다.

에디트 박스는 선택된 파일의 경로와 이름을 나타내기 위한 것이며,

“Transfer” 버튼은 앞서 “File Select” 버튼에 의해 선택된 파일을 디바이스로 전송하기 위한 버튼이다.
이 버튼이 눌리면 OnButtonBulkOutTranfer() 멤버함수가 호출된다.
우리가 중점적으로 들여다 보아야 할 곳이 바로 여기다.

이전에는 디바이스와 통신하기 위해 DeviceIoControl을 사용했는데,
여기서는 WriteFile을 사용하고 있다.

hFile = OpenFile( BULKOUT_PIPE );
if( INVALID_HANDLE_VALUE != hFile )
{
    bRet = WriteFile( hFile, pcBuffer, m_dwTransferLength, &dwBytesReturned, NULL );
    CloseHandle( hFile );
}

OpenFile(CreateFile())로 디바이스의 핸들을 얻고, WriteFile로 디바이스로 데이터를 전송하며, CloseFile로 핸들을 반환한다.

DeviceIoControl이 WriteFile로 바뀐 것 밖에 없다.

앞서 강의에서도 언급했듯이 보통 DeviceIoControl은 적은 양의 데이터를 읽거나 쓸 때, 특히 한번에 읽고 쓰려고 할 때 사용되고, ReadFile, WriteFile은 많은 양의 데이터를 한 방향으로 전송할 때 사용된다.

일반적으로 그렇게들 쓴다는 얘기이고, 어떤 함수를 언제 어떻게 사용하느냐는 어플리케이션 개발자와 WDM 드라이버 개발자가 상의해서 정하기 나름이다.

이제 그만.

clip_image002

IN Setup & 버튼(메시지) 받기

출처 : http://muosys.egloos.com/193127

 

오늘은 Default Control Pipe를 이용하는 마지막 예제를 디벼보고, 버튼입력을 받는 것을 연습해 보자.

그 전에 잠깐. 이전에 다운 받은 예제 중에 UniHigh Application v2.1/v2.2내의 OnButtonLEDBllink()함수에서 파라메터(sizeof(PCONTROL_REQUEST))가 잘못 사용되었다.
이를 수정하여 다시 올렸으니 다운 받으시던지 아니면 그냥 아래의 정정된 예제를 참조하시든지 하시라.

이전 강의 Control Endpoint의 DATA stage 활용 - IN편에서는 IN Data Stage를 이용해서 디바이스로부터 호스트로 데이터를 전송하는 것을 설명하였었다.

이 예제들을 실험하기 위해서는 드라이버를 또 업데이트 해 주어야 한다.
바로 이전 강의를 참조하여 드라이버를 아래의 것으로 업데이트 해 주자.
UniHigh Driver v2.4

UniHigh App 소스코드의 Unihighusr.h에 보면 추가된 control code들을 볼 수 있다.

UniHigh Application v2.3 - Setup Stage를 이용한 IN Tranfer
UniHigh Application v2.4 - Button & Message 받기(IN Interrupt Transfer)

UniHigh Firmware v2.3
UniHigh Firmware v2.4

v2.4는 v2.3의 코드에 새 코드를 추가한 것이므로, v2.4만 다운받아도 된다.

v2.3은 이전 v1.3예제처럼 어플리케이션으로부터 두 값을 입력 받아서 이 값들의 곱을 FX2에서 계산 한 다음 IN Data Stage에 실어 다시 어플리케이션으로 값을 반환하는 예제이다.

OnButtonCalculate() 함수에서 모든 처리가 일어나는데, 이전의 DeicceIoControl의 사용과는 다른 점이 데이터를 받기 위해 다섯 번째와 여섯 번째 인자, 즉, LPVOID lpOutBuffer,와 DWORD nOutBufferSize,를 세팅해 주고 있다는 것이다.

그 이외에는 이전과 특별히 다른 점이 없으므로 행자들이 직접 코드를 디벼보면 별 어려움 없이 이해가 될 것이다.

다음은 디바이스에 있는 버튼이 눌렸을 때, 이를 어플리케이션으로 알리는 v2.4예제.
마찬가지로 UniHigh1.4.zip을 unihigh.sys에 맞추어 바꾼 것이다.

펌웨어는 이전과 마찬가지로 VID, PID만 바뀌었고, 회로도 이전 강의(버튼입력을 받아보자)과 같게 꾸미면 된다.
그 회로에서는 앞서 사용하던 LED 하나가 빠져 있는데, LED를 그냥 두고, 버튼회로만 추가해도 된다.

새로운 드라이버(UniHigh Driver v2.4)는 행자들이 이미 설치했을 것이고, 쫌 설명을 해야 할 것은 UniHigh Application v2.4이다.

기억을 거슬러 올라가서, 이전 강의(버튼입력을 받아보자)에서 우리가 버튼 입력을 받기 위해서 펌웨어 다운로드 -> Get Pipe버튼 누름 -> Pipe 0 선택 -> “Bulk/Int”버튼을 누름 이런 절차를 밟았다.
그런데 -> “Bulk/Int”버튼을 누르면 우리가 UniHigh 디바이스에 달린 버튼을 눌러서, 그 메시지가 EZ-USB Control Panel에 도착하기 전까지 어플리케이션이 잠시 얼어있는 것처럼 보이는데, 그 이유는 어플리케이션의 메인 쓰레드에서 버튼입력을 기다리기 때문이라고 설명 했었다.
기억 나시남?

버튼 입력을 받기 위해 메인 쓰레드를 홀딩 시킬 수는 없으므로, 우리는 이 기능을 위한 쓰레드를 하나 더 생성해야 한다.
UniHigh Application v2.4 프로젝트를 열어 보시라.
UniHighDlg.cpp의 마지막 부분에 있는 GetMsgThread(…)가 바로 버튼(메시지)입력을 받기 위한 쓰레드 본체이다.

OnInitDialog()에서 쓰레드를 시작하고,
m_bMsgThreadWorking = TRUE;
m_pGetMsgThread = AfxBeginThread( GetMsgThread, (LPVOID)this );

프로그램이 종료될 때 호출되는 OnClose에서 쓰레드를 종료한다.
m_bMsgThreadWorking = FALSE;

그리고, CUniHighDlg의 멤버함수인 MsgArrived는 디바이스로부터 버튼입력(메시지)이 도착했을 때 GetMsgThread에서 호출하는 함수이다.

GetMsgThread 내부를 살펴보면 이전의 DeviceIoControl과는 다르게 마지막 인자에 OVERLAPPED 구조체의 주소를 주고 있다.

이것은 이 쓰레드가 메시지가 도착하기 전까지는 pending 되어 있다가, 메시지가 도착했을 때, 이를 알려줄 이벤트를 위한 것이다.

Windows 프로그램을 잘 모르는 행자들를 위해 자세히 설명하려니 양도 만만치 않은게 본 강좌가 윈도우 프로그래밍강좌로 변질 될 것 같고, Windows 프로그램을 잘 아는 행자들을 위해 설렁설렁 설명하려니 다 아는거 리바이벌 밖에 더 되겠낭?

따라서 이 쓰레드 함수의 내부에 대한 설명은 생략한다. ㅋㅋㅋ
난 Windows 프로그램을 잘 모르는데 그래도 꼭 알아야 되겠다 라는 행자들은 Overlapped I/O에 관해 데브파이의 Visual C++ “강좌와 Tip”란을 디벼 보시거나 구글링 하시라.

UniHigh Application v2.4는 종료 시 쓰레드 땜에 메모리 leak이 날지도 모르겠다.
나중에 시간 나면 잡고, 안 나면 말자.
테스트하는 데는 별 지장 없다.

UniHigh Firmware v2.4를 살펴 보면 이전에 본좌가 얘기 했듯이 SendMessage 함수를 디버깅 용도로 사용한 예가 있다.

TD_Poll()함수를 잘 찾아 보시라.
이제 디버깅을 위한 LED 깜빡 신공으로부터 해방이닷.