Page 1 of 1

Communication problem

Posted: Thu Mar 01, 2012 9:19 pm
by Micha
I'm working on a project using V-USB and have a problem with communication with the host (Win XP, C#). Every report ID is working except one that is not transmitted properly. That contains an uint32_t and an uint16_t. The uint32_t represents the seconds of a software RTC and the uint16_t the milliseconds of the same RTC.

When I read that report with my host application it works fine up to 429 seconds, but when the RTC reaches 430 seconds I read 0. Then it counts up to 429 again, again wrapping around to 0 and so on. The problem stays the same when I change the starting value, so I assume it's not an issue that appears when the device ran for more than 429 seconds.

On PB1 a LED is connected that shall light when 429 seconds have passed. This LED turns on as it should and stays on afterwards until I disconnect (pull the USB-plug) and reconnect the device. So I assume the content of the variable is higher than 429, but I still read 0.

Does somebody have an idea what could cause that behaviour?

Down below you can find the code which I reduced to a minimum, but the problem still remains.

Code: Select all

#include <avr/io.h>
#include <avr/wdt.h>          /* watchdog */
#include <avr/interrupt.h>    /* for sei() */
#include <util/delay.h>       /* for _delay_ms() */
#include <string.h>           /* for memcpy */

#include <avr/pgmspace.h>     /* required by usbdrv.h */
#include "usbdrv.h"           /* v-usb driver */

#include "configUSBIRRemoteReceiver.h" /* USB-IRRR config file */

#ifndef F_CPU
   #error F_CPU unknown
#endif

#ifdef USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH
   #undef USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH
#endif
#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH    134

#define F_INTERRUPTS                            10000

#define CPU_CYCLES_BETWEEN_INTERRUPTS  (F_CPU/F_INTERRUPTS)

/* const for V-USB feature report ID handling */
enum ReportID {
   noReport             = 0,
   NewIRCodeAvailable   = 1,
   ReadPowerOnEnabled   = 2,
   ReadTrainedIRCode    = 3,
   SetPowerOnEnabled    = 4,
   SetTrainedIRCode     = 5,
   repIRPollingTime     = 6,
   ReadIrmpVersion      = 7,
   repMinRepeats        = 8,

// keep some spare report IDs for future use of USB-IRRR
   repCurrentTime       = 50,
   repXtalFrequency     = 51,
   repWakeupTime        = 52,
   repWakeupTimeSpan    = 53,
   repPowerOffIRCode    = 55,
};

/* ------------------------------------------------------------------------- */
/* ----------------------------- USB interface ----------------------------- */
/* ------------------------------------------------------------------------- */
PROGMEM char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = {
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x0b, 0x01, 0x00, 0x00, 0xff,  // USAGE (Vendor Defined Page 1:Vendor Usage 1)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x85, NewIRCodeAvailable,      //   REPORT_ID
    0x95, 0x06,                    //   REPORT_COUNT (6)
    0x09, 0x00,                    //   USAGE (Undefined)
    0x82, 0x00, 0x01,              //   INPUT (Data,Ary,Abs,Buf)
    0x85, ReadPowerOnEnabled,      //   REPORT_ID
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
    0x85, ReadTrainedIRCode,       //   REPORT_ID
    0x95, 0x06,                    //   REPORT_COUNT (6)
    0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
    0x85, SetPowerOnEnabled,       //   REPORT_ID
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
    0x85, SetTrainedIRCode,        //   REPORT_ID
    0x95, 0x06,                    //   REPORT_COUNT (6)
    0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
    0x85, repIRPollingTime,        //   REPORT_ID
    0x95, 0x02,                    //   REPORT_COUNT (2)
    0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
    0x85, ReadIrmpVersion,         //   REPORT_ID
    0x95, 0x0A,                    //   REPORT_COUNT (10)   // works only when reading from device
    0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
    0x85, repMinRepeats,           //   REPORT_ID
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
    0x85, repCurrentTime,          //   REPORT_ID           // Current date
    0x95, 0x07,                    //   REPORT_COUNT (6)    // time in seconds (element 1-4) and msec (element 5&6)
    0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
    0x85, repXtalFrequency,        //   REPORT_ID           // XTAL frequency
    0x95, 0x04,                    //   REPORT_COUNT (4)    // Data format: uint32/Hz
    0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
    0x85, repWakeupTime,           //   REPORT_ID           // Wakeup Date and time
    0x95, 0x04,                    //   REPORT_COUNT (4)    // time in seconds
    0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
    0x85, repWakeupTimeSpan,       //   REPORT_ID           // span of wakeup time
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
    0x85, repPowerOffIRCode,       //   REPORT_ID           // IR code for shutting the PC down
    0x95, 0x06,                    //   REPORT_COUNT (6)
    0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
    0xc0                           // END_COLLECTION
};  // don't forget to update USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH when you change this array


/* The following variables store the status of the current data transfer */
static uchar   currentAddress    = 0;
static uchar   bytesRemaining    = 0;
enum ReportID  DoWriteReport     = noReport;
enum ReportID  DoReadReport      = noReport;


/* global variables */
static uint32_t   CurrentTime          = 415;
static uint16_t   InterruptTicks       = 0;  // count how often timer ISR was executed each second
static int16_t    ClockDeviation       = 0;

/* reply buffer should be big enough to cover also the irmp version ! */
static uchar      replyBuf[16];


/* ------------------------------------------------------------------------- */
/* main functions for irmp                                                   */
/* ------------------------------------------------------------------------- */
void init_timer1 (void)
{
   /* IR polling timer */
   TCCR1B  = (1 << WGM12) | (1 << CS10);     // switch CTC Mode on, set prescaler to 1

   // may adjust IR polling rate here to optimize IR receiving:
   OCR1A   =  (F_CPU / F_INTERRUPTS) - 1;    // compare value: 1/15000 of CPU frequency

   // enable Timer1 for IR polling
   #if defined (__AVR_ATmega8__) || defined (__AVR_ATmega16__) || defined (__AVR_ATmega32__) \
    || defined (__AVR_ATmega64__) || defined (__AVR_ATmega162__)
      TIMSK = 1 << OCIE1A;       // Timer1A ISR activate
   #else
      TIMSK1  = 1 << OCIE1A;     // Timer1A ISR activate
   #endif   // __AVR...
}


/*-----------------------------------------------------------------------------------------------------------------
 * init all io pins of the AVR, first all to input with pullups. then config USB and output pin
 *---------------------------------------------------------------------------------------------------------------*/
void init_io(void)
{
   /* USB pins */
   USBOUT ^= _BV(USB_CFG_DMINUS_BIT) | _BV(USB_CFG_DPLUS_BIT);    /* deactivate pull-ups on USB lines */
   
   PORTB &= ~ (1 << PB1);
   DDRB = (1 << PB1);
}
 

/*-----------------------------------------------------------------------------------------------------------------
 * timer 1 compare handler, should be called every 1/10000 sec
 *---------------------------------------------------------------------------------------------------------------*/
void TIMER1_COMPA_vect(void) __attribute__((interrupt));
void TIMER1_COMPA_vect(void)
{
   static int16_t TimeError = 0;    // RTC error compensation

   // RTC error correction
   if ( TimeError >= CPU_CYCLES_BETWEEN_INTERRUPTS )        // RTC too fast
   {
      //InterruptTicks += 0  // add zero ticks
      TimeError -= CPU_CYCLES_BETWEEN_INTERRUPTS;
   }
   else if ( TimeError <= -CPU_CYCLES_BETWEEN_INTERRUPTS )  // RTC too slow
   {
      InterruptTicks += 2; // add two ticks
      TimeError += CPU_CYCLES_BETWEEN_INTERRUPTS;     
   }
   else
   {
      InterruptTicks += 1; // add one ticks
   }

   // compute time every second
   if ( InterruptTicks >= F_INTERRUPTS )  // one second has passed
   {
      CurrentTime++;                      // add one second
      TimeError += ClockDeviation;        // accumulate deviation
      InterruptTicks -= F_INTERRUPTS;     // subtract number of interrupts per second
   }
}


/* ------------------------------------------------------------------------- */
/* usbFunctionRead() is called when the host requests a chunk of data from
 * the device. For more information see the documentation in usbdrv/usbdrv.h.
 */
uchar usbFunctionRead(uchar *data, uchar len)
{
   if(len > bytesRemaining)
      len = bytesRemaining;

   if ( DoReadReport == repCurrentTime )
   {
      uint16_t temp;
      cli();                                                  // don't allow any interrupts here
      temp = InterruptTicks / (F_INTERRUPTS / 1000);          // scale InterruptTicks to msec
      memcpy(&data[1], &CurrentTime, sizeof(CurrentTime));    // copy data to buffer
      memcpy(&data[5], &temp, sizeof(InterruptTicks));        // copy data to buffer
      sei();
   }

   currentAddress += len;
   bytesRemaining -= len;
   return len;
}


/* ------------------------------------------------------------------------- */
/* usbFunctionWrite() is called when the host sends a chunk of data to the
 * device. For more information see the documentation in usbdrv/usbdrv.h.
 */
uchar usbFunctionWrite(uchar *data, uchar len)
{
   if(bytesRemaining == 0)
      return 1;               /* end of transfer */
   if(len > bytesRemaining)
      len = bytesRemaining;

   if ( DoWriteReport == repCurrentTime )
   {
/*      cli();                                          // don't allow any interrupts here
      memcpy(&CurrentTime, &data[1], sizeof(CurrentTime)); // update CurrentTime with received data
      memcpy(&InterruptTicks, &data[5], sizeof(InterruptTicks)); // update Int.Ticks with received data
      InterruptTicks = InterruptTicks * (F_INTERRUPTS / 1000); // scale InterruptsTicks
      sei();
*/   }
   
   currentAddress += len;
   bytesRemaining -= len;
   return bytesRemaining == 0;                                          /* return 1 if this was the last chunk */
}


/* ------------------------------------------------------------------------- */
usbMsgLen_t usbFunctionSetup(uchar data[8])
{
   usbRequest_t   *rq = (void *)data;

   if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS)        // HID class request
   {
/* ----------------------------------------------------------- GET REPORT (device -> host) -- */
      if(rq->bRequest == USBRQ_HID_GET_REPORT)              // wValue: ReportType (highbyte), ReportID (lowbyte)
      {
         usbMsgPtr = replyBuf;

         memcpy(&replyBuf[0], &rq->wValue.bytes[0], sizeof(uchar));     // copy report id

         if(rq->wValue.bytes[0] == repCurrentTime)                 /* Report: Current Time */
         {
            bytesRemaining = sizeof(CurrentTime) + sizeof(InterruptTicks) + sizeof(uchar);
            DoReadReport = repCurrentTime;
            return USB_NO_MSG;
         }
      }
/* ----------------------------------------------------------- SET REPORT (host -> device) -- */
      else if(rq->bRequest == USBRQ_HID_SET_REPORT)
      {
         if(rq->wValue.bytes[0] == repCurrentTime)           /* Report: Current Date */
         {
/*            bytesRemaining = sizeof(CurrentTime) + sizeof(InterruptTicks);
            DoWriteReport = repCurrentTime;
            return USB_NO_MSG;
*/         }
      }
   }
   else
   {
      // ignore vendor type requests, we don't use any
   }
   return 0;
}


/* ------------------------------------------------------------------------- */
/* main function
*/
int main(void)
{
   uchar i = 0;

   wdt_disable();             // disable watchdog

   init_io();
   init_timer1();             // initialize timer

   usbDeviceDisconnect();     /* enforce re-enumeration, do this while interrupts are disabled! */
   while(--i){                /* fake USB disconnect for > 500 ms */
      _delay_ms(5);
   }
   usbDeviceConnect();

   usbInit();                 // initialize v-usb
   sei();                     // enable global int
   
   for(;;)  /* main event loop */
   {
      uint32_t temptime;
      
      usbPoll();              // do a USB poll

      cli();
      temptime = CurrentTime;
      sei();
      
      if (temptime > 429)
      {
         PORTB |= (1 << PB1);
      }
      else
      {
         PORTB &= ~(1 << PB1);
      }
      
      _delay_ms(255);
   }
   return 0;
}