multiple ADC channels

General discussions about V-USB, our firmware-only implementation of a low speed USB device on Atmel's AVR microcontrollers
Post Reply
Niikhawod
Posts: 5
Joined: Sat Jun 08, 2013 11:29 pm

multiple ADC channels

Post by Niikhawod » Sat Jun 08, 2013 11:33 pm

Hello,

i am trying to make a USB-hid device with a atmega328p and i want to use multiple ADC channels.
i have looked at the examples on the main site of objective develpment, but i still can't get it to work propperly.
i have managed to get two channels to work but al other channels just have somekind of noise.

Here is the code.

Code: Select all

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <avr/pgmspace.h>
#include <stdlib.h>


#define F_CPU 20000000L
#include <util/delay.h>
#include "usbdrv.h"
#include "oddebug.h"



const PROGMEM char usbHidReportDescriptor[77] = {
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x15, 0x00,                    // LOGICAL_MINIMUM (0)
    0x09, 0x04,                    // USAGE (Joystick)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x05, 0x02,                    //   USAGE_PAGE (Simulation Controls)
    0x09, 0xbb,                    //   USAGE (Throttle)
    0x15, 0x81,                    //   LOGICAL_MINIMUM (-127)
    0x25, 0x7f,                    //   LOGICAL_MAXIMUM (127)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0x05, 0x01,                    //   USAGE_PAGE (Generic Desktop)
    0x09, 0x01,                    //   USAGE (Pointer)
    0xa1, 0x00,                    //   COLLECTION (Physical)
    0x09, 0x30,                    //     USAGE (X)
    0x09, 0x31,                    //     USAGE (Y)
    0x95, 0x02,                    //     REPORT_COUNT (2)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0xc0,                          //   END_COLLECTION
    0x09, 0x39,                    //   USAGE (Hat switch)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x03,                    //   LOGICAL_MAXIMUM (3)
    0x35, 0x00,                    //   PHYSICAL_MINIMUM (0)
    0x46, 0x0e, 0x01,              //   PHYSICAL_MAXIMUM (270)
    0x65, 0x14,                    //   UNIT (Eng Rot:Angular Pos)
    0x75, 0x04,                    //   REPORT_SIZE (4)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0x05, 0x09,                    //   USAGE_PAGE (Button)
    0x19, 0x01,                    //   USAGE_MINIMUM (Button 1)
    0x29, 0x04,                    //   USAGE_MAXIMUM (Button 4)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x95, 0x04,                    //   REPORT_COUNT (4)
    0x55, 0x00,                    //   UNIT_EXPONENT (0)
    0x65, 0x00,                    //   UNIT (None)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0xc0                           // END_COLLECTION
};




typedef struct{
    char   throttle;
    char    x;
    char    y;
    char    buttons;
}report_t;

static report_t joystick_report;
static uchar    idleRate;


usbMsgLen_t usbFunctionSetup(uint8_t data[8])
{
   usbRequest_t    *rq = (void *)data;

   
   if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){    /* class request type */
      if(rq->bRequest == USBRQ_HID_GET_REPORT){  /* wValue: ReportType (highbyte), ReportID (lowbyte) */
         usbMsgPtr = (void *)&joystick_report;
         return sizeof(joystick_report);
            
            }
            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;
}

int input(void)
{
      //ADCSRA |= 1<<ADSC;
      char pins = 0, pinInv;
      pins = PINB;
      pinInv = ~pins;
      //int throttle = (ADCH + 128);
      //uint8_t throttle = ;      
      //uint8_t x = ;
      //uint8_t y = ;
      
            
      
      //joystick_report.x = (x + 128);
      //joystick_report.y = (y + 128);
      joystick_report.buttons = pinInv << 2;
}

static void hardwareInit(void)
{   
   PORTB = 0xff;   /* activate all pull-ups */
   DDRB = 0;       /* all pins input */
   DDRC = 0;       /* all pins input */
   PORTD = 0xfa;   /* 1111 1010 bin: activate pull-ups except on USB lines */   
   
   uchar   i;
   wdt_enable(WDTO_1S);
   usbDeviceDisconnect();  /* enforce re-enumeration, do this while interrupts are disabled! */
   
   i = 0;
    while(--i){             /* fake USB disconnect for > 250 ms */
        wdt_reset();
        _delay_ms(1);
    }
   
   usbDeviceConnect();
   

   
}

int main(void)
{

   hardwareInit();
   usbInit();   
   sei();

   
   
   for(;;)
   {
      wdt_reset();
      usbPoll();
      input();
      if(usbInterruptIsReady()){
            /* called after every poll of the interrupt endpoint */
         
            usbSetInterrupt((void *)&joystick_report, sizeof(joystick_report));
        }
      
   }
}

ISR(ADC_vect) {
   
   
   //confige ADC      
   //enable prescaler - determent by internal/ external clock 20,000,000/128
   ADCSRA |= ((1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0));
   //8-bit
   ADMUX |= 1<<ADLAR;
   //set voltage reference
   ADMUX |= 1<<REFS0;         //0110 0000 60
   //Select input
   //ADMUX |= ((0<<MUX3) | (1<<MUX2) | (0<<MUX1) | (1<<MUX0));
   //enable interrupts function in ADC
   //ADCSRA |= 1<<ADIE;
   //enable global interrupts
   sei();
   //turn on the ADC features
   ADCSRA |= 1<<ADEN;
   //start the first conversion
   ADCSRA |= 1<<ADSC;
    
      ADCSRA |= 1<<ADSC;
      
     //ADMUX |= ch;   
    // ADCSRA |= (1<<ADSC);
     //while (!(ADCSRA & (1<<ADIF)));
    // ADCSRA |= (1<<ADIF);
    // return ADC;
  }

ulao
Rank 4
Rank 4
Posts: 481
Joined: Mon Aug 25, 2008 8:45 pm

Re: multiple ADC channels

Post by ulao » Tue Jun 11, 2013 6:18 pm

here is how I do it.

//voltage reader.
#define ADC_VREF_TYPE 0x40
static int read_adc(unsigned char adc_input)
{
ADMUX=adc_input|ADC_VREF_TYPE;
_delay_us(150);
ADCSRA|=0x40; // Start the AD conversion
while ((ADCSRA & 0x10)==0);// Wait for complete
ADCSRA|=0x10;
return ADCW;
}

reportBuffer[1]= (read_adc(0));//pc0
reportBuffer[2]= (read_adc(1))*-1;//pc1

I can not help you with noise that is ore then likely your issue.

Niikhawod
Posts: 5
Joined: Sat Jun 08, 2013 11:29 pm

Re: multiple ADC channels

Post by Niikhawod » Wed Jun 12, 2013 8:40 pm

ulao wrote:here is how I do it.

//voltage reader.
#define ADC_VREF_TYPE 0x40
static int read_adc(unsigned char adc_input)
{
ADMUX=adc_input|ADC_VREF_TYPE;
_delay_us(150);
ADCSRA|=0x40; // Start the AD conversion
while ((ADCSRA & 0x10)==0);// Wait for complete
ADCSRA|=0x10;
return ADCW;
}

reportBuffer[1]= (read_adc(0));//pc0
reportBuffer[2]= (read_adc(1))*-1;//pc1

I can not help you with noise that is ore then likely your issue.


i have added the code and now its not reporting anything at all. :(
the device wil disconect and reconnect when i add the ADC interrupt.


i have added these lines:
declaration of reportBuffer

Code: Select all

static uchar    reportBuffer[8];

added ADC setup to the hardwareInit.

Code: Select all

   
        ADCSRA |= ((1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0));
   ADMUX |= 1<<ADLAR;
   ADCSRA |= (1<<ADEN);

then comes the read_adc

Code: Select all

static int read_adc(unsigned char adc_input)
{
   ADMUX=adc_input|ADC_VREF_TYPE;
   _delay_us(150);
   ADCSRA|=0x40; // Start the AD conversion
   while ((ADCSRA & 0x10)==0);// Wait for complete
   ADCSRA|=0x10;
   return ADCW;

}


and last i have the input.

Code: Select all

unsigned char input()
{
   reportBuffer[1]= (read_adc(0));//pc0
   reportBuffer[2]= (read_adc(1))*-1;//pc1
}


complete code.

Code: Select all

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <avr/pgmspace.h>
#include <stdlib.h>


#define F_CPU 20000000L
#include <util/delay.h>
#include "usbdrv.h"
#include "oddebug.h"
//voltage reader.
#define ADC_VREF_TYPE 1<<REFS0 //0x40


static int read_adc(unsigned char adc_input);

const PROGMEM char usbHidReportDescriptor[77] = {
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x15, 0x00,                    // LOGICAL_MINIMUM (0)
    0x09, 0x04,                    // USAGE (Joystick)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x05, 0x02,                    //   USAGE_PAGE (Simulation Controls)
    0x09, 0xbb,                    //   USAGE (Throttle)
    0x15, 0x81,                    //   LOGICAL_MINIMUM (-127)
    0x25, 0x7f,                    //   LOGICAL_MAXIMUM (127)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0x05, 0x01,                    //   USAGE_PAGE (Generic Desktop)
    0x09, 0x01,                    //   USAGE (Pointer)
    0xa1, 0x00,                    //   COLLECTION (Physical)
    0x09, 0x30,                    //     USAGE (X)
    0x09, 0x31,                    //     USAGE (Y)
    0x95, 0x02,                    //     REPORT_COUNT (2)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0xc0,                          //   END_COLLECTION
    0x09, 0x39,                    //   USAGE (Hat switch)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x03,                    //   LOGICAL_MAXIMUM (3)
    0x35, 0x00,                    //   PHYSICAL_MINIMUM (0)
    0x46, 0x0e, 0x01,              //   PHYSICAL_MAXIMUM (270)
    0x65, 0x14,                    //   UNIT (Eng Rot:Angular Pos)
    0x75, 0x04,                    //   REPORT_SIZE (4)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0x05, 0x09,                    //   USAGE_PAGE (Button)
    0x19, 0x01,                    //   USAGE_MINIMUM (Button 1)
    0x29, 0x04,                    //   USAGE_MAXIMUM (Button 4)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x95, 0x04,                    //   REPORT_COUNT (4)
    0x55, 0x00,                    //   UNIT_EXPONENT (0)
    0x65, 0x00,                    //   UNIT (None)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0xc0                           // END_COLLECTION
};


static uchar    reportBuffer[8];
static uchar    idleRate;


usbMsgLen_t usbFunctionSetup(uint8_t data[8])
{
   usbRequest_t    *rq = (void *)data;

   
   if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){    /* class request type */
      if(rq->bRequest == USBRQ_HID_GET_REPORT){  /* wValue: ReportType (highbyte), ReportID (lowbyte) */
         usbMsgPtr = (void *)&reportBuffer;
         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;
}



static void hardwareInit(void)
{   
   PORTB = 0xff;   /* activate all pull-ups */
   DDRB = 0;       /* all pins input */
   DDRC = 0;       /* all pins input */
   PORTD = 0xfa;   /* 1111 1010 bin: activate pull-ups except on USB lines */   
   

   
   uchar   i;
   wdt_enable(WDTO_1S);
   usbDeviceDisconnect();  /* enforce re-enumeration, do this while interrupts are disabled! */
   
   i = 0;
    while(--i){             /* fake USB disconnect for > 250 ms */
        wdt_reset();
        _delay_ms(1);
    }
   
   usbDeviceConnect();
   
   ADCSRA |= ((1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0));
   ADMUX |= 1<<ADLAR;
   ADCSRA |= (1<<ADEN);

   
}

static int read_adc(unsigned char adc_input)
{
   ADMUX=adc_input|ADC_VREF_TYPE;
   _delay_us(150);
   ADCSRA|=0x40; // Start the AD conversion
   while ((ADCSRA & 0x10)==0);// Wait for complete
   ADCSRA|=0x10;
   return ADCW;

}

   


unsigned char input()
{
   reportBuffer[1]= (read_adc(0));//pc0
   reportBuffer[2]= (read_adc(1))*-1;//pc1
   reportBuffer[3]= 128;
}

int main(void)
{

   hardwareInit();
   usbInit();   
   sei();

   
   
   for(;;)
   {
      wdt_reset();
      usbPoll();
      input();
      
      if(usbInterruptIsReady()){
            /* called after every poll of the interrupt endpoint */
         
            usbSetInterrupt((void *)&reportBuffer, sizeof(reportBuffer));
        }
      
   }
}

ulao
Rank 4
Rank 4
Posts: 481
Joined: Mon Aug 25, 2008 8:45 pm

Re: multiple ADC channels

Post by ulao » Wed Jun 12, 2013 8:53 pm

the only thing I can think of that would give you a disconnect is a infinite loop. This also is a good question for avrfreeks, I dont think the disconnects are v-usb related. I would just debug it and see if your are putting the code in to a long loop.

ps: why is wdt_reset() in the main loop, are you using that for debug?
od and where is the 0 report?
did you mean:
reportBuffer[0]= (read_adc(0));//pc0
reportBuffer[1]= (read_adc(1))*-1;//pc1
reportBuffer[2]= 128;

or
joystick_report.x (read_adc(0));//pc0
joystick_report.x (read_adc(1))*-1;//pc1

never copy from source ;) !!! I skip 0 for my own reasons.

Niikhawod
Posts: 5
Joined: Sat Jun 08, 2013 11:29 pm

Re: multiple ADC channels

Post by Niikhawod » Wed Jun 12, 2013 9:18 pm

if i take wdt_reset(); out of the main loop the device wil reset itself every second. its there in case the code stops working, and then the device wil restart/reconnect itself.

ulao
Rank 4
Rank 4
Posts: 481
Joined: Mon Aug 25, 2008 8:45 pm

Re: multiple ADC channels

Post by ulao » Wed Jun 12, 2013 9:33 pm

ok ,what about the report, did you fix that? Should fix the "i have added the code and now its not reporting anything at all."

I dont use a reset like that in my loop this is all I do.

Code: Select all

 
for (;;)
{
   getdata();
   while ( !usbInterruptIsReady () ) { usbPoll(); }
   usbSetInterrupt( (void *) &reportBuffer, sizeof(reportBuffer));
}

using the while may work better for you.

Niikhawod
Posts: 5
Joined: Sat Jun 08, 2013 11:29 pm

Re: multiple ADC channels

Post by Niikhawod » Wed Jun 12, 2013 9:58 pm

i have put back the typedef struct, and used joystick_report.x and now it works, but in windows the axis goed to 127 and then jumps to -127 and it does this a few times before it centers on 0. :?

ulao
Rank 4
Rank 4
Posts: 481
Joined: Mon Aug 25, 2008 8:45 pm

Re: multiple ADC channels

Post by ulao » Wed Jun 12, 2013 10:29 pm

yeah that is because your data reader needs calibrating.

example

reportBuffer[1]= (read_adc(0)/3 + 200);//pc0 : this is some extreme math

You have to now do your own math. Maybe just - 50 from the result ( only a guess ) you'll get it.

Niikhawod
Posts: 5
Joined: Sat Jun 08, 2013 11:29 pm

Re: multiple ADC channels

Post by Niikhawod » Wed Jun 12, 2013 11:04 pm

final code for the adc is now: joystick_report.x= (read_adc(4))/4-128;
all is now properly working :).

thx for the help.

Post Reply