마음 속 하늘은 항상 맑음~!

AVR Serial/HID의 간단한 설명. 본문

하드웨어

AVR Serial/HID의 간단한 설명.

파르셀수스 2014. 10. 22. 11:31

 HID장치를 간단하게 시리얼 통신처럼 이용할 수 있는 방법은 CDC ACM 방법이 운영체제가 막아서 생기는 문제는 해결할 수 있다. 이 방법이 꼭 장점이 강한 것은 아니지만, 간단한 테스트를 위한 하드웨어에서의 응용은 나쁘지 않을거라고 생각된다.


 기본적으로 HID의 사용되는 프로그래밍 방법은 같고 단지 주의할 점만 존재한다. 호스트 컴퓨터에서 장치로 USB통해 정보는 받는 부분에 control전송과 인터럽트 전송을 모두 거치는 usbFunctionWriteOut을 쓰는데, 이것은 별로 문제가 되지 않는다. 단지 가끔 NULL데이터를 보내므로 펌웨어에서 체크를 하면 된다. 그리고 데이터의 길이가 버퍼의 길이와 맞추는 작업도 필요하다. 이것은 각각의 구현에 따른 변경이 다르므로 다른 게시물에 업로드한 소스를 확인하면 대충 이해하기 쉽다.


 가장 크게 주의할 점은 바로 HID 레포트 정보의 선언으로 장치에서 호스트 컴퓨터로 보내는 크기는 8바이트가 되어야 한다. 그렇지 않으면 데이터가 전혀 전송이 되지 않는다. 호스트 컴퓨터에서 장치로 전송되는 크기는 크게 문제가 없지만 64이하로 만들어주는 것이 좋다.


PROGMEM const char usbHidReportDescriptor[] = {
  0x06, 0xa0, 0xff, // USAGE_PAGE (Vendor Defined Page 1)
  0x09, 0x01,       // USAGE (Vendor Usage 1)
  0xa1, 0x01,       // COLLECTION (Application)

  // Input Report
  0x09, 0x02,       // Usage ID - vendor defined
  0x15, 0x00,       // Logical Minimum (0)
  0x26, 0xFF, 0x00, // Logical Maximum (255)
  0x75, 0x08,       // Report Size (8 bits)
  0x95, 0x08,       // Report Count (8 fields), must be 8
  0x81, 0x02,       // Input (Data, Variable, Absolute)

  // Output report
  0x09, 0x03,       // Usage ID - vendor defined
  0x15, 0x00,       // Logical Minimum (0)
  0x26, 0xFF, 0x00, // Logical Maximum (255)
  0x75, 0x08,       // Report Size (8 bits)
  0x95, 0x16,       // Report Count (16 fields)
  0x91, 0x02,       // Output (Data, Variable, Absolute)

  0xc0              // END_COLLECTION
};


 기본적인 레포트의 선언은 위와 같은데, 항상 명칭은 호스트 컴퓨터를 중심으로 표현된다. Input는 장치에서 컴퓨터로 보내는 것으로 Report Size와 Report Count는 반드시 8바이트가 되어야 한다. 하드웨어 지원이 된다면 이런 제한이 없다고 하는데 하드웨어 USB 지원이 안되는 AVR은 저속 장치로 인식되는 탓에 어쩔 수 없다.


 Output 레포트의 크기는 변화를 줘도 아무런 문제가 없다.


 레포트 선언은 이렇게 간단하고, v-usb의 선언에서 데이터 전송을 위한 부분을 처리하도록 작성하는데 몇개의 함수만 만들어주면 간단히 끝난다.


usbMsgLen_t usbFunctionSetup(uchar data[8])
{
    return 0;
}

uchar usbFunctionRead(uchar *data, uchar len)
{
    return 0;
}

void usbFunctionWriteOut(uchar *data, uchar len)
{
uchar i,c;
    if(len>0) {
        if( (*data==0x0d) || (*data==0x0a) )
            return;
        if(len>HIDSERIAL_INBUFFER_SIZE) len=HIDSERIAL_INBUFFER_SIZE;
        i=0;
        while(i<len) {
            c=data[i];
            inBuffer[i]=c;
            i++;
            if(c==0) break;
        }
        received=1;
        recv_len=i;
    }
}


 usbFunctionSetup은 컴퓨터에 장치의 정보들을 설정하고 주고 받거나 컨트롤 전송을 위해 쓰이는데, 이 구현에서는 따로 크게 코딩할 부분이 없다.


 usbFunctionRead는 장치로 데이터를 보내는 방법을 위한 것으로 마찬가지로 컨트롤 전송을 위한 것으로 인터럽트 전송을 하는 이 방법에서는 쓸모가 없다.


 usbFunctionWriteOut은 호스트 컴퓨터에서 장치로 보내는 데이터를 처리할 때 쓰이는데, 이곳에서 장치의 버퍼에 데이터를 저장하면 된다. 설명에는 컨트롤 전송도 이 함수를 통해 데이터를 받을 수 있다고 되어 있다. 데이터가 다 전송되었다는 것을 확인할 방법을 구현하는게 좋다.


int main(void)
{
    odDebugInit();
    hardwareInit();
    usbInit();

    sei();
    for(;;){    /* main event loop */
        wdt_reset();
        usbPoll();

        if( usbInterruptIsReady() && (received != 0) )
          Translate_buffer(inBuffer);

...

    if(iwptr>0) {
        rx_buf[iwptr]=0;
        usbSetInterrupt(rx_buf,8);
        if(iwptr>8) {
            while(!usbInterruptIsReady()) {
                wdt_reset();
                usbPoll();
            }
            usbSetInterrupt(rx_buf+8,8);
        }
        iwptr=0;
    }

    }
...

    return 0;
}


 이렇게 간단히 선언하고 위와 같이 usbPoll()과 usbInterruptIsReady() 함수로 계속 상태를 확인하고 데이터의 전송에는 usbSetInterrupt(data, 8)과 같이 사용하면 된다. 8로 고정된 이유는 이미 설명했다. 만약 데이터가 길다면 usbPoll()과 usbInterruptIsReady()로 상태를 체크한 후에 usbSetInterrupt(...,8)로 다시 보내주면 된다. 8바이트씩 보내는 것이라 계속 보내준다면 크게 길이의 제한은 없다고 생각된다.


그리고 usbconfig.h의 내용들은 대충 이렇다.

...

#define USB_CFG_HAVE_INTRIN_ENDPOINT    1

#define USB_CFG_HAVE_INTROUT_ENDPOINT   1 

#define USB_CFG_IMPLEMENT_FN_WRITEOUT   1

...

#define USB_CFG_INTERFACE_CLASS     3       /* HID class */
#define USB_CFG_INTERFACE_SUBCLASS  0     
#define USB_CFG_INTERFACE_PROTOCOL  0    

...

#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH    34

...



 특별한 것은 없는 방법론이기 때문에 그냥 따라하면 끝이다. 탐구를 하고 싶다면 삽질을 해봐도 좋을 일이다. =_=;



cdcio.2009-07-15-serhid-4313.zip


추가적인 비트 쓰기/읽기 기능의 추가.

portb.7 portb.1 [  읽기 msb, 첫번째는 데이터, 두번째는 클럭.

portb.0 portb.3 ]  읽기 lsb,

ff portb.1 portb.0 > 쓰기 lsb

ff portb.2. portb1 / 쓰기 msb ( 터미널 오류로 다른 문자를 사용 )



cdcio.2009-07-15-serhid-addfuncs1.zip


'하드웨어' 카테고리의 다른 글

12F675를 이용한 ADC를 가져오기.  (0) 2014.11.21
간단한 승압 회로.  (0) 2014.10.29
AVR Serial HID 구현.  (0) 2014.10.21
USB CDC는 막힌 듯?  (0) 2014.10.20
USBTiny 실험.  (0) 2014.10.17