USB-joystick Atmega 16

General discussions about V-USB, our firmware-only implementation of a low speed USB device on Atmel's AVR microcontrollers
Post Reply
WaaZaa

USB-joystick Atmega 16

Post by WaaZaa » Mon Nov 19, 2007 4:17 pm

Hello all,

I am busy trying to create an USB joystick with the atmega16. For this I want to use AVR-USB. The big problem is, however, that I cannot get any examples of AVR-USB to work. I tried the C64keys, HIDkeys, but they wont work. I did set the

Code: Select all

# MCU name
MCU = atmega16


But, for some reason it will not work.

I also added resistors to my D- line, as you can see in my scheme.

Image


Strange thing is, that it did work with the all-done mjoy16 code I found on mindaugas website, but I want to edit some things, but I cant since there is no source released of this.

Anyone have a clue whats going on?

[/img]

sprhawk
Posts: 11
Joined: Sun Oct 28, 2007 5:52 am

Post by sprhawk » Wed Nov 21, 2007 5:30 am

Maybe it's the problem of USB initializing...

I was working on it for a long time.

Try to change them according to your own schematic.

christian
Objective Development
Objective Development
Posts: 1443
Joined: Thu Nov 09, 2006 11:46 am

Post by christian » Mon Nov 26, 2007 3:24 pm

Please verify that the definitions in usbconfig.h and the initialization in main() match your hardware. On the hardware side, you should probably consider adding series resistors and 3.6 V zener diodes to limit the bus voltage to 3.3 V. Not all hosts tolerate 5 V on D+ and D-.

WaaZaa
Posts: 4
Joined: Wed Dec 05, 2007 11:30 am

Post by WaaZaa » Thu Dec 06, 2007 11:15 am

I got the initialization working finally.. So I'm guessing the hardware side should be allright. I did add a resistor and measured the D lines. D- = 3.3V
D+ = 0V.. For some reason I am getting a hunch the 0V is not right for the D+? Should this be switched around?

*crosspost, wanted to post it here*

I am trying to get reports sent to the PC, but the avr doesn't seem to be sending anything.. For the record, I am using an atmega16, but that shouldn't change anything..

It's strange because the device is recognized by windows, gamecontrollers shows this:
Image
I'm guessing this means the enumeration is going correct, but the interrupts after are not received correctly?

I also added a usbtrace log on my webserver, the device desciption it gives and a list of transfers received and sent. This is a cycle which keeps repeating itself.. http://www.waazaa.net/usb/

I'm just adding my code here..

Code: Select all

/**********************Global Variables****************************************/
/* Originally used as a mask for the modifier bits, but now also
   used for other x -> 2^x conversions (lookup table). */
const char modmask[8] PROGMEM = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
uchar  adcData[8] ={0,0,0,0,0,0,0,0}; //int to store the AD conversion results in.
uchar newDataFound;
uchar   updateNeeded = 0;

/* The ReportBuffer contains the USB report sent to the PC */
static uchar reportBuffer[8] = {0,0,0,0,0,0,0,0};    /* buffer for HID reports */
static uchar idleRate;           /* in 4 ms units */


/* Device serial number (length is defined in usbconfig.h) */
int usbCfgSerialNumberStringDescriptor[USB_CFG_SERIAL_NUMBER_LENGTH+1] PROGMEM =
{
    USB_STRING_DESCRIPTOR_HEADER(USB_CFG_SERIAL_NUMBER_LENGTH),
    '1', '3',
};

/* USB report descriptor (length is defined in usbconfig.h)*/
const char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH]
  PROGMEM = {
     0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x15, 0x00,                    // LOGICAL_MINIMUM (0)
    0x09, 0x04,                    // USAGE (Joystick)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x09, 0x01,                    //   USAGE (Pointer)
    0xa1, 0x00,                    //   COLLECTION (Physical)
    0x95, 0x06,                    //     REPORT_COUNT (6)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x15, 0x80,                    //     LOGICAL_MINIMUM (-128)
    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
    0x35, 0x00,                    //     PHYSICAL_MINIMUM (0)
    0x46, 0xff, 0x00,              //     PHYSICAL_MAXIMUM (255)
    0x09, 0x31,                    //     USAGE (Y)
    0x09, 0x30,                    //     USAGE (X)
    0x09, 0x32,                    //     USAGE (Z)
    0x09, 0x33,                    //     USAGE (Rx)
    0x09, 0x34,                    //     USAGE (Ry)
    0x09, 0x35,                    //     USAGE (Rz)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x95, 0x08,                    //     REPORT_COUNT (8)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x35, 0x00,                    //     PHYSICAL_MINIMUM (0)
    0x45, 0x01,                    //     PHYSICAL_MAXIMUM (1)
    0xc0,                          // END_COLLECTION
    0x05, 0x09,                    //     USAGE_PAGE (Button)
    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
    0x29, 0x08,                    //     USAGE_MAXIMUM (Button 8)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0xc0                           //     END_COLLECTION                 
};

/**********************||Global Variables||****************************************/




static void hardwareInit(void) {
  uchar   i, j;

   /* 1101 1000 bin: activate pull-ups except on USB lines
    *
    * USB signals are on bit 0 and 2.
    *
    * */
   PORTD = 0xf8;   

   /* Usb pin are init as outputs */
   DDRD = 0x01 | 0x04;   

   
   j = 0;
   while(--j){     /* USB Reset by device only required on Watchdog Reset */
      i = 0;
      while(--i); /* delay >10ms for USB reset */
   }
   DDRD = 0x00;    /* 0000 0000 bin: remove USB reset condition */
   
   /* configure timer 0 for a rate of 12M/(1024 * 256) = 45.78 Hz (~22ms) */
   TCCR0 = 5;      /* timer 0 prescaler: 1024 */

   TCCR2 = (1<<WGM21)|(1<<CS22)|(1<<CS21)|(1<<CS20);
   OCR2 = 196; // for 60 hz
}


//Initialize the AD converter.
void Init_ADC( void )
{
   //          enable     *      prescaler = 128       *
    ADCSRA  = (1<<ADEN)|(1<<ADPS2)|(0<<ADPS1)|(1<<ADPS0);
}


uchar   usbFunctionSetup(uchar data[8]) {
  usbRequest_t *rq = (void *)data;
  usbMsgPtr = reportBuffer;
  if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){    /* class request type */
    if(rq->bRequest == USBRQ_HID_GET_REPORT){  /* wValue: ReportType (highbyte), ReportID (lowbyte) */
      /* we only have one report type, so don't look at wValue */
      return sizeof(reportBuffer);
    }else if(rq->bRequest == USBRQ_HID_GET_IDLE){
      usbMsgPtr = &idleRate;
      return 1;
    }else if(rq->bRequest == USBRQ_HID_SET_IDLE){
      idleRate = rq->wValue.bytes[1];
    }
  }else{
    /* no vendor specific requests implemented */
  }
   return 0;
}


 
void inDecoderPoll(void)
{
   
   for (unsigned int i = 0; i < 8; i++)   
   {
       
      ADMUX = i;                 // select ADC channel
      ADMUX |= (0<<REFS1)|(1<<REFS0)|(1<<ADLAR);
      ADCSRA |= (1<<ADSC);         // start conversion
        while (ADCSRA & (1<<ADSC)){;} // wait for result (fast, not a problem for USB driver)
        adcData[i] = ADCH;          // save to data buffer
      
    }
    // set new data flag
    newDataFound = 1;
}

void buildReport(void)
{
   for (unsigned int i =0; i<7; i++)
   {
          //just using channel 7 value for now.
      reportBuffer[i+1] = adcData[7];
   }
}

void testMeth(void)
{
   
   if(usbInterruptIsReady())
   {
      // led on
      PORTB = 0x00;
      DDRB = 0x01;
   }
   else
   {
      //led off.
      DDRB = 0x00;
   }

}



int   main(void) {
     
      
   wdt_enable(WDTO_2S); /* Enable watchdog timer 2s */
   hardwareInit(); /* Initialize hardware (I/O) */
   Init_ADC();

   usbInit(); /* Initialize USB stack processing */
   sei(); /* Enable global interrupts */
      
   
   for(;;)
   {   /* Main loop */
      wdt_reset(); /* Reset the watchdog */
      usbPoll(); /* Poll the USB stack */
      
      
      inDecoderPoll();
   
      /* If an update is needed, send the report */
      if(usbInterruptIsReady())
      {
            
            buildReport();
            testMeth();
            usbSetInterrupt(reportBuffer, sizeof(reportBuffer));
      }
   
   }
   return 0;
}

christian
Objective Development
Objective Development
Posts: 1443
Joined: Thu Nov 09, 2006 11:46 am

Post by christian » Thu Dec 06, 2007 6:11 pm

Sorry, I don't have the time to go through your code and descriptors, but some comments:

It's OK that D+ is at 0 V and D- at 3.3 V. This is the idle status when no data is transferred. Since data is transferred in bursts, you see the bus idle most of the time.

If the device is recognized by Windows, the driver level should be OK. You should look out for errors in the descriptors (configuration descriptor, endpoint descriptors, HID Report descriptor). There are tools for Linux, Mac OS X and Windows to analyze the descriptors (but I don't know the names, I use USB Prober on the Mac).

Guest

Post by Guest » Mon Dec 10, 2007 6:03 pm

These descriptors all seem right to me.. I used the most basic HID descriptor this time, but it still does not work.

It recognises the new descriptor in windows, so I guess this is allright.

All I can think of is that the interrupts are not triggered / handled correctly. While testing, the

Code: Select all

usbInterruptIsReady()
is always false.. This would indicate there is something wrong there right? Unless it is going to fast for me too see.

The descriptor I'm using now is like I said, very basic:

Code: Select all

char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] 
  PROGMEM = {
   0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x04,                    // USAGE (Joystick)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x26, 0xfe, 0x00,              //     LOGICAL_MAXIMUM (254)
    0x09, 0x30,                    //     USAGE (X)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0xc0                           // END_COLLECTION               
};


I'm currently building the report like this:

Code: Select all

if(adcData[0] > 125) adcData[0] = 125;
   if(adcData[0] < 1) adcData[0] = 1;
   usbReport[0] = adcData[0];

with adcData beeing the AD converter values. I figured with the extra checks I make sure it does not exceed the 8 bit.

then usbReport[] is sent to the host via:

Code: Select all

if(usbInterruptIsReady())
      {            
            usbSetInterrupt(usbReport, sizeof(usbReport));
      }


All that the usb protocol analyzer says is this:

Image

Which once again makes me think there is something wrong with the interrupt, since the interrupt transfer return unsuccesfull...

Any help will be appreciated ;)

christian
Objective Development
Objective Development
Posts: 1443
Joined: Thu Nov 09, 2006 11:46 am

Post by christian » Mon Dec 10, 2007 6:33 pm

If usbInterruptIsReady() is never true, then the host does not poll the interrupt endpoint. There are two possilbe causes for this:
(1) The endpoint is not declared correctly in the configuration descriptor, or
(2) the device did not respond when the host polled the interrupt endpoint and the host therefore stalled it.

If you have other interrupts on the device which delay INT0 for more than a couple of cycles, (2) is very likely. If the host polls, but the USB driver's interrupt routine can't be called soon enough, the poll fails and the interrupt endpoint is stalled by the host. It effectively stops polling until you re-open the device.

WaaZaa
Posts: 4
Joined: Wed Dec 05, 2007 11:30 am

Post by WaaZaa » Tue Dec 11, 2007 4:23 pm

Finally found out what it was..

the usbReport[] was still sized at 8 elements, while I only wanted to receive 1 byte. I'm guessing this caused a stall at the host sized, because of bogus data beeing sent after the interrupt and the first byte beeing sent.

At any rate, thanks for the help! Maybe I'll be back for some more problems later ;)

christian
Objective Development
Objective Development
Posts: 1443
Joined: Thu Nov 09, 2006 11:46 am

Post by christian » Tue Dec 11, 2007 6:10 pm

So you confused the operating system with a report which did not match the Report Descriptor.

In any case: I'm glad you found the cause!

Guest

Post by Guest » Wed Dec 12, 2007 9:31 am

That was it indeed.

At first I figured the other 7 bytes would be discarded, because I figured if it was not filled, it would be 0 and thus NULL / nothing.

But I guess a logical 0 != nothing if you're transmitting it.

Thinking error of mine ;)

Post Reply