|
본좌. 한 이틀간 회로와 납땜 얘기로 변죽만 울렸다.
오늘은 바로 핵심으로 돌진한다. 이제까지 desc.a51 파일과 fw.c 파일을 디볐는데, 실질적으로 bulkloop 프로젝트의 핵심은 bullkloop.c 파일에 있다. 이전에 이미 언급했던 것처럼 그 중에서도 핵심은 TD_Init(), DR_VendorCmnd(), TD_Poll() 이 세 함수가 되겠다. 주말에 술 마시느라 기억력을 고스란히 술잔에 반납하신 행자들을 위해 본좌가 졸라리 싫어하는 리바이벌을 함 하자면, TD_Init()은 우리가 필요한 초기화 루틴을 넣으라고 FrameWork님께서 마련해 놓으신 자리이고, DR_VendorCmnd()은 Default Control Endpoint( Endpoint 0 )를 통해 호스트로부터 오는 명령 중에서 FrameWork님께서 처리하지 않는 우리만의 명령을 처리하기 위한 루틴을 위한 함수이고, TD_Poll()은 Poll이란 단어의 뉘앙스에서 팍 느껴지듯이 (안 느껴지남?) 루프를 돌면서 계속 할 일이 있는지 체크하다가 할 일이 생기면 처리하는 그런 코드를 담은 함수가 되겠다. 여기가 바로 bulkloop의 핵심이다. 잠시 딴길로 빠지자면, 이 bulkloop의 예에서처럼 polling으로 일을 처리하는 코드는 처음에 짜기는 편하지만 프로세서가 처리해야 할 일이 많아지기 시작하면 이런 “마이크로”프로세서에서는 쥐약이다. 우리가 쓰는 컴퓨터처럼 운영체제가 멀티프로세싱을 지원하는 것도 아니어서 한가지 작업을 하는데 시간을 많이 잡아먹어 버리면 그 뒤에 처리를 기다리는 Time Critical한 작업들도 전부 다 지연되어버린다. 따라서 인터럽트 우선순위를 잘 이용한 Interrupt driven 방식으로 코드를 짜기를 권한다. Bulkloop는 예제이니 그냥 그런가 보다 하고 넘어가자. TD_Init()과 DR_VendorCmnd()는 후에 밟아보기로 하고, 오늘은 바로 본론(TD_Poll() )으로 들어간다. Bulkloop가 호스트가 보내는 데이터를 FIFO에 저장했다가 호스트가 데이터를 요구하면 FIFO의 데이터를 차례로 꺼내주는 예제이니만큼 여기에 이런 기능이 구현되어 있을 것이다. if(!(EP2468STAT & bmEP2EMPTY)) 첫 번째 if 문은 Endpoint 2로 들어오는 데이터를 Endpoint 6로 내보내는 코드이고, 두 번째 if 문은 Endpoint 4로 들어오는 데이터를 Endpoint 8로 내보내는 코드이다. 두 코드는 데이터를 IN/OUT하는 엔드포인트만 다를 뿐, 하는 일은 똑 같으므로 첫 번째 if문만 설명하도록 하겠다. 먼저 if( !( EP2468STAT & bmEP2EMPTY ) ) 자 통빡을 굴려보자. 데굴데굴 EP2468STAT은 무슨 레지스터 같고, bmEP2EMPTY는 읽어보니 EP2(엔드포인트 투)가 비었다는 걸 나타내는 것 같다. 통빡들 다 굴리셨남? 다 굴렸으면 데이터쉬트 찾아볼 차례다. 다들 T.R.M(Technical Reference Manual)을 뒤비시라. 본좌는 예전에 뒤볐었기 때문에 그냥 혼자 진도 나간다. 만일 EP2의 버퍼가 비어있다면 ( EP2468STAT & bmEP2EMPTY ) == TRUE 일 것이고 따라서 ( !( EP2468STAT & bmEP2EMPTY ) ) == FALSE일 것이다. 고로 if문을 수행하지 않는다. 정리하면 EP2의 버퍼가 비어있다면 if문을 수행하지 않는다는 말이다. 바꿔 말하면 EP2의 버퍼가 비어있지 않다면 if문으로 들어가 뭔 짓거리를 한다는 말쌈이다. EP2의 버퍼가 비어있지 않을 때 어떤 짓거리를 하는지 따라 들어가 보자. if(!(EP2468STAT & bmEP6FULL)) 어라~! 비슷한게 또 나왔네? 이번엔 막바로 설명하면 EP6의 버퍼가 꽉차있지 않다면 if문으로 들어가 뭔 짓거리를 한다는 말쌈이다. 두 if 문을 합쳐서 생각해보면 (데이터를 받는) EP2의 FIFO에 뭔가가 들어왔고, (데이터를 보내는) EP6의 FIFO가 꽉 차서 더 이상 (호스트로 ) 데이터를 보낼 수 없지 않다면 이 짓거리를 한다. {라는 말쌈이 되겠다. APTR1H, APTR1L과 AUTOPTRH2, AUTOPTRL2는 각각 Auto Pointer1과 Auto Pointer2를 위한 레지스터가 되겠다. 이 오토포인터가 뭐시기냐 하면 보통 버퍼에서 데이터를 한 바이트(워드)식 불러다가 처리 하고자 할때에는 다음과 같은 절차를 반복하게 된다. 1. 포인터를 버퍼의 맨 앞을 가리키도록 초기화한다. 2. 포인터가 가리키는 버퍼의 주소에 있는 데이터를 가져다가 지지고 볶는다. 3. 포인터를 하나 증가시킨다. 4. 원하는 만큼 2,3번을 반복한다. 그런데 Auto pointer를 쓰게 되면 3번의 절차를 생략해도 버퍼에서 데이터를 가져올 때마다 포인터가 자동으로 증가한다. 그래서 우리는 1. 포인터를 버퍼의 맨 앞을 가리키도록 초기화한다. 2. 포인터가 가리키는 버퍼의 주소에 있는 데이터를 가져다가 지지고 볶는다. 3. 원하는 만큼 2번을 반복한다. 요렇게만 해도 된다는 거다. 쫌 편해 보이지 않는가? 아님 말고. 물론 버퍼에서 데이터를 가져올 때마다 포인터를 감소시킬 수도 있고, 물론 버퍼에서 데이터를 가져올 때마다 포인터를 증가시킬 수도 있고, 물론 버퍼에 데이터를 쓸 때마다 포인터를 감소시킬 수도 있고, 물론 버퍼에 데이터를 쓸 때마다 포인터를 증가시킬 수도 있다. 그건 Auto pointer를 어떻게 세팅하는가에 따라, 그때그때~ 달라여~. ![]() Auto pointer에 대해 자세한 것은 나중에 살피기로 하고, 다시 진도 나가자. APTR1H = MSB( &EP2FIFOBUF ); 오토포인터1은 EP2 FIFO 버퍼의 시작주소를 가리키도록, 오토포인터2은 EP6 FIFO 버퍼의 시작주소를 가리키도록, 초기화 하고 있다. count = (EP2BCH << 8) + EP2BCL; count라는 WORD형( 2 byte ) 변수의 상위바이트에는 EP2BCH를 하위 바이트에는 EP2BCL가 들어가도록 우겨넣고 있다. EP2BCH/EP2BCL가 뭔지 머리 떼고, 꼬리 떼고 다짜고짜 설명하면 호스트로부터 EP2 버퍼에 데이터가 얼마만큼 들어온 지를 나타내는 레지스터가 되겠다. (EP2 Byte Count High/Low) 호스트가 데이터를 우리 디바이스에게 날리면 FX2는 그 데이터를 받아서 FIFO에 차곡차곡 넣은 다음 들어온 데이터가 얼마만큼인지를 EP2BCH/EP2BCL에 써 넣는다. 그리고 나서 요 앞서 보았던 EP2468STAT 레지스터에 딸랑~딸랑~ EP2에 데이터 들어왔어요 하고 해당 비트(bmEP2EMPTY)를 세팅하는 것이다. FX2 졸라 착하지 않은가? 우리는 USB 트렌젝션(transaction)이 어떻게 이루어지는지 아직 알지도 못하는데, FX2가 다 알아서 해버린다. 그 다음 for( i = 0x0000; i < count; i++ ) 들어온 데이터 바이트 수 만큼 뺑뺑이를 돌리는데, 어랍쇼? EXTAUTODAT2는 뭐고, EXTAUTODAT1는 또 뭐야? 바로 앞서 설명한 Auto pointer가 가리키는 버퍼의 데이터가 되시겠다. C에서의 참조연산자 “*”라고 생각하시면 딱이다. *Autopointer2 == EXTAUTODAT2 Autopointer2는 EP6 FIFO Buffer의 시작번지로 초기화하고, Autopointer1는 EP2 FIFO Buffer의 시작번지로 초기화 한 거 기억 하시남? 기억 안 나면 언능 앞에 가서 보고 오셔~. 루프를 한번 돌 때마다 Autopointer2, Autopointer1은 각 버퍼의 처음 주소부터 시작해서 자동으로 1씩 증가 할 것이고, EXTAUTODAT”x”는 Autopointer”x”가 가리키는 번지의 데이터이므로 For 루프안에서 하는 일은 결국 EP2 버퍼의 데이터를 EP6로 고스란히 옮기는 거이 되겠다. 그리고 나서 EP6BCH = EP2BCH; SYNCDELAY; EP6BCL = EP2BCL; SYNCDELAY; SYNCDELAY;는 일단 생까자. 뭔 일을 하는 명령어는 아니다. 그럼 이렇게 된다. EP6BCH = EP2BCH; EP6BCL = EP2BCL; 보라 EP2의 EPxBCH/L를 EP6의 그것에 고스란히 복사하고 있다. 얼랄라리오. 아까는 EPxBCH/L가 받은 데이터의 바이트수가 들어있는 레지스터 라메? 하시는 행자분께 박수를. 암 생각없는 행자는 1분간 olo잡고 반성. EP”x”BCH/L에서 “x”가 OUT Endpoint를 나타낸다면, 본좌가 앞서 설명한게 맞고, EP”x”BCH/L에서 “x”가 IN Endpoint를 나타낸다면, 호스트에게 보낼 바이트 수를 저장하는 레지스터가 된다. 그때 그때 다른 쓰임세 되겠다. 왜 그러냐고 묻지 마라. Cypress에서 그렇게 만들었을 뿐이다. (아울러 IN transfer가 디바이스->호스트로 데이터가 전송된다는 것도 한번 더 상기시켜 드린다. - host 입장에서 바라보기) 그러니까 정리해보면 EP2로 받은 데이터를 고대로 EP6의 버퍼에 카피하고, EP6BCH/L을 기입함으로써 “나 이만큼 데이터를 날릴 꺼야” 라고 장전까지 마친 상태가 여기까지이다. -to be continue- 한 줄 남았는데 마침. 왠지 낼이 기대 되지 않는가? -.-;;
|
카테고리
메모장
UniHigh(FX2) Q&A WDM 드라이버(Driver) USB 개발 모듈(module) 장치(Device) High Speed 자작 강좌(강의) 전문가 CYPRESS CY7C68013 FTDI PHILIPS 최근 등록된 덧글
재미있게 쓰셨네요. 도움..
by 김찬 at 11/23 EP2CFG = 0xA2; S.. by 감사~ㅋ at 06/26 하드웨어 구매는 어떻게.. by 하니 at 06/22 강좌 감사합니다 by 하니 at 06/22 쉽게 설명해 주셔서 이해.. by 윤여준 at 05/13 전원부 회로에서 R4 100K.. by 푸른날개 at 02/26 머리가 한계를 느끼기 .. by 어리버리 관절염 at 02/19 검색하다 우연히 들렀습.. by bongpal2 at 02/01 감사합니다. 자료 정말 .. by 정태우 at 11/30 olo 강의 볼때마다 잡는.. by 까불이 at 10/11 skin by 이글루스 | |||