버튼입력을 받아보자

인터럽트 모드 트렌스퍼에서 인터럽트의 의미가 “끼여들기”라기 보다는 “가끔”이라는 것은 이미 언급했었다.
인터럽트 엔드포인트 디스크립터를 보면 Polling Time이라는 항목이 있는데 여기에 세팅된 시간 간격으로 호스트가 디바이스에게 전송할 데이터가 있는지 정기적으로 물어 보게 된다.

USB의 구조가 호스트-슬레이브의 구조이므로, 디바이스가 호스트에게 전송할 무언가가 있다고 해서 디바이스 맘대로 호스트를 호출 할 수 없다.
노예(Slave)는 항상 주인님(Host)이 물어볼 때만 대답할 수 있다.
따라서 디바이스가 호스트에게 데이터를 보내야 할 필요가 있다면 호스트가 정기적으로 디바이스에게 보낼 데이터가 있는지를 물어보도록 만들어야 한다.
이런 때 쓰이는 Transfer Mode가 Interrupt Transfer Mode이다.

디바이스의 버튼이 눌린 것을 어플리케이션에 알리고자 한다면, 인터럽트 엔드포인트를 사용하는 것이 적절 할 것이다.

소스코드 : UniHigh1.4.zip

먼저 Endpoint Descriptor를 추가하자.

;; Endpoint Descriptor
db DSCR_ENDPNT_LEN ;; Descriptor length
db DSCR_ENDPNT ;; Descriptor type
db 81H ;; Endpoint number(1), and direction(IN)
db ET_INT ;; Endpoint type
db 40H ;; Maximun packet size (LSB)
db 00H ;; Max packect size (MSB)
db 64H ;; Polling interval(100ms)

Maximum Packet Size는 64byte로 설정 하고 있는데, High Speed에서는 1024byte, Full Speed에서는 64byte, Low Speed에서는 8byte까지 설정 할 수 있다.
그러나 우리가 인터럽트 엔드포인트로 쓸 EP1의 버퍼사이즈가 64byte이므로 위와 같이 설정해 주었다.
(T.R.M. Page 1-23의 그림 참조)
물론 High Speed의 1024byte의 인터럽트 트렌스퍼를 사용하고 싶다면 EP2나 EP6를 이용하면 된다.

Polling Interval은 1~255까지 설정 할 수 있고, 단위는 mSec이다.

위의 디스크립터를 High Speed Config Descriptor와 Full Speed Config Descriptor에 추가해 주고, 각각의 Interface Descriptor의 Number of end points 필드도 1로 세팅해 주어라.

본좌가 메시지를 EP1을 통해 호스트로 날리기 위한 함수를 하나 추가 했다.

void SendMessage( BYTE cMessage, WORD wParam )
{
while((EP1INCS & bmEPBUSY)); //wait until EP1IN Endpoint available

EP1INBUF[0] = cMessage;
EP1INBUF[1] = LSB(wParam);
EP1INBUF[2] = MSB(wParam);

EP1INBC = 3;
}

EP0에서 데이터를 날리기 위해 EP0BCH/L을 썻던 것과 마찬가지로 EP1에서는 EP1INBC를 쓰면 된다.

이 루틴은 비단 버튼이 눌릴 때만이 아니라, 디버깅 용도로도 유용하다.
EZ-USB Control Panel을 이용하는 지금이야 디버깅 용도로 사용하기가 좀 번거롭지만, 앞으로 우리가 어플리케이션을 코딩하게 되면 이 루틴을 사용해 디바이스의 상태를 계속 USB포트를 통해 우리에게 보여 줄 수 있다.
그래서 본좌가 “WORD wParam”인자를 추가로 확보해 둔 것이다.
버튼 눌린 상태만 알고자 한다면 “BYTE cMessage”로도 충분한 데 말이다.

그 외에 Timer2를 사용하기 위해 TD_Init에 초기화 코드를 추가하고, Fw.c에 timer2_isr() Interrupt Service Routine을 추가 한 다음 TD_Poll()에 버튼입력을 체크하는 루틴을 추가 했다.

타이머는 버튼이 한 번 눌리면 일정시간 동안은 버튼 입력을 무시하도록 하는데 사용했다.
버튼이 눌릴 때 노이즈를 걸러내기 위해 세 번쯤 포트의 레벨 상태를 검사하는 루틴은 이 코드에서는 추가하지 않았다.
행자들이 추가 해 보시든가 하시라.

회로도는 아래와 같다.
아래 사진에서는 본좌는 점선으로 표시된 저항은 안 달았다.
사실 버튼입력 받을 때 이렇게 회로를 꾸며야 맞는지도 잘 모르겠다.
(본좌 하드웨어하는 넘이 아니므로 행자들이 이해 하시라.)
하지만 잘 작동하니까 대~충 맞다고 치자.
본좌처럼 점선 안의 저항을 생략하면 스위치가 눌릴 때 VCC와 GND가 쇼트되므로 별로 좋을 것 같지는 않다.

clip_image001

clip_image002

EZ-USB Control Panel로 버튼 입력을 받으려면, 펌웨어를 다운로드 한 다음에 “Get Pipe”버튼을 눌러주어 Pipe0(Endpoint 0)이 나타나는 지를 확인하고, “Bulk/Int”버튼을 누르면 된다.
잠시 어플리케이션이 응답이 없는 것처럼 보일 것이다.
디바이스로부터 응답을 기다리는데, 아직 버튼이 안 눌렸으므로 디바이스에서 응답을 안 하여서 마냥 기다리는 것이다.

clip_image004

나중에 우리의 어플리케이션을 짤 때, 이러한 상황을 피하기 위해서는 버튼을 체크하는 루틴은 다른 쓰레드에서 돌려주어야 한다.
안 그럼 이 경우처럼 프로그램의 메인 쓰레드가 디바이스로부터 응답이 있을 때까지 얼어 있게 된다.

자 이제 버튼을 눌러보자.
버튼 눌림이 표시 될 것이다.

그럼 버튼 먼저 누르고, “Bulk/Int”버튼을 눌러보자.
바로 응답이 올 것이다.
버튼이 눌렸다는 데이터가 FX2 버퍼에서 대기하고 있다가, 호스트가 요청하자마자 바로 보내기 때문에 즉각적인 응답이 나타난 것이다.

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중

%d 블로거가 이것을 좋아합니다: