occasional transmission errors with 16.5MHz RC oscillator

General discussions about V-USB, our firmware-only implementation of a low speed USB device on Atmel's AVR microcontrollers
iphi
Rank 2
Rank 2
Posts: 68
Joined: Mon Jun 25, 2007 11:37 am

occasional transmission errors with 16.5MHz RC oscillator

Post by iphi » Wed Feb 06, 2008 11:41 am

Hello,

I have implemented a little USB device on an ATTINY45 based on the 16.5MHz driver found in the EasyLogger project.

Basically it's working but I do occasionally experience transmission errors, e.g. the Product string or Manufacturer string cannot be read once in a while.

Any ideas or suggestions on this issue?

Regards, Tom

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

Post by christian » Wed Feb 06, 2008 1:18 pm

My first guess would be that the oscillator is not calibrated precisely enough or that it's not stable enough. Oscillator stability depends on the stability of the supply voltage.

iphi
Rank 2
Rank 2
Posts: 68
Joined: Mon Jun 25, 2007 11:37 am

Post by iphi » Wed Feb 06, 2008 3:45 pm

Hi Christian,

thanks for your quick reply.
This may well be. I don't use a stabilized supply voltage so far.

Question:
Easylogger firmware searches a calibration value in the EEPROM at startup. How does it get there?
Will the driver modify a calibration from the USB timing results? Is this PLL pure software or is a retuning of the clock oscillator involved?

Regards, Tom

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

Post by christian » Wed Feb 06, 2008 3:53 pm

It is your responsibility to write a calibration value to the EEPROM. We don't have an algorithm for that yet, but I'm sure you can come up with something.

The PLL is in software only: It executes NOP instructions to get in sync with the sender. The OSCCAL value is not touched for that.

iphi
Rank 2
Rank 2
Posts: 68
Joined: Mon Jun 25, 2007 11:37 am

Post by iphi » Thu Feb 07, 2008 2:05 pm

Hi Christian,

you were correct. The problem is caused by an oscillator frequency offset.
I have made the following experiment:

I have added some code so I could read and modify OSCCAL via USB.
After program startup I read a value of 0x5d.
Next I changed it until USB connection failed.
USB connection worked for OSCCAL values between 0x59 and 0x5d. So my device is actually running at the very edge of the possible OSCCAL values. Setting it into the middle of the range all transmission problems are gone.

I would like to do some kind of selfcalibration. I am not experienced enough to extract info from the driver itself on whether the clock is too fast or too slow. Do you see another possibility to determine the clock frequency at startup? I guess, only the PC USB signals can serve as time standards.

Regards, Tom

P.S.
Could one make use of the 1ms USB time frame?

iphi
Rank 2
Rank 2
Posts: 68
Joined: Mon Jun 25, 2007 11:37 am

Post by iphi » Thu Feb 07, 2008 2:57 pm

Hi Christian,

one more thing:
In the Easylogger example, the watchdog timer is quite useless if the EEPROM doesn't contain an initial calibration value if my understanding is correct: If the watchdog fires and the program restarts, the clock oscillator will be tuned away again from the already detuned value and no connection can be obtained.

Do you agree?

Regards, Tom

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

Post by christian » Thu Feb 07, 2008 6:07 pm

Regarding self calibration: It's not possible within the time constraints of the driver to store away a "too slow" or "too fast" bit.

The best method would be to watch the 1ms pulses at D- shortly after connecting USB and calibrate with that. Note that the host will try to query the descriptor and if it fails, it stops sending these pulses.

Regarding OSCCAL and watchdog: As far as I understand the data sheet, any RESET (including watchdog) restores the factory OSCCAL value. But I may be wrong with this assumption.

iphi
Rank 2
Rank 2
Posts: 68
Joined: Mon Jun 25, 2007 11:37 am

Post by iphi » Sun Feb 10, 2008 11:42 pm

christian wrote:Regarding OSCCAL and watchdog: As far as I understand the data sheet, any RESET (including watchdog) restores the factory OSCCAL value. But I may be wrong with this assumption.


You are correct. Now that my watchdog works I could test. OSCCAL is restored to factory values for any reset.

Regards, Thomas

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

Post by christian » Thu Feb 28, 2008 11:53 pm

Just for your information: We have released EasyLogger 2008-02-28 today. This version adds self-calibration of the RC oscillator.

In principle, this calibration algorithm can be used to calibrate the RC oscillator directly to 12 MHz. However, the precision is not high enough for the 12 MHz module (because it does not have a software PLL) and 12 MHz is outside the specified range of OSCCAL values. It's still an interesting field for experiments...

iphi
Rank 2
Rank 2
Posts: 68
Joined: Mon Jun 25, 2007 11:37 am

Post by iphi » Fri Feb 29, 2008 9:06 am

Great! I'll try this!

Thomas

iphi
Rank 2
Rank 2
Posts: 68
Joined: Mon Jun 25, 2007 11:37 am

Post by iphi » Sun Mar 16, 2008 7:31 pm

Hello Christian,

I have tried your OSCCAL auto-calibration code. In easylogger it works fine, but I can't get it running in my own application.

Here is what I did:

1. I have updated the drivers directory usbdrv with the latest drivers, recompiled my project and flashed it. Works fine.
2. I have edited my old USBConfig.h and added the following lines:

Code: Select all

#ifndef __ASSEMBLER__
extern void usbEventResetReady(void);
#endif
#define USB_RESET_HOOK(isReset)             if(!isReset){usbEventResetReady();}
/* This macro is a hook if you need to know when an USB RESET occurs. It has
 * one parameter which distinguishes between the start of RESET state and its
 * end.
 */
#define USB_CFG_HAVE_MEASURE_FRAME_LENGTH   1
/* define this macro to 1 if you want the function usbMeasureFrameLength()
 * compiled in. This function can be used to calibrate the AVR's RC oscillator.
 */


3. I have copied the following code into my application:

Code: Select all

static void calibrateOscillator(void)
{
uchar       step = 128;
uchar       trialValue = 0, optimumValue;
int         x, optimumDev, targetValue = (unsigned)(1499 * (double)F_CPU / 10.5e6 + 0.5);

    /* do a binary search: */
    do{
        OSCCAL = trialValue + step;
        x = usbMeasureFrameLength();    /* proportional to current real frequency */
        if(x < targetValue)             /* frequency still too low */
            trialValue += step;
        step >>= 1;
    }while(step > 0);
    /* We have a precision of +/- 1 for optimum OSCCAL here */
    /* now do a neighborhood search for optimum value */
    optimumValue = trialValue;
    optimumDev = x; /* this is certainly far away from optimum */
    for(OSCCAL = trialValue - 1; OSCCAL <= trialValue + 1; OSCCAL++){
        x = usbMeasureFrameLength() - targetValue;
        if(x < 0)
            x = -x;
        if(x < optimumDev){
            optimumDev = x;
            optimumValue = OSCCAL;
        }
    }
    OSCCAL = optimumValue;
}
/*
Note: This calibration algorithm may try OSCCAL values of up to 192 even if
the optimum value is far below 192. It may therefore exceed the allowed clock
frequency of the CPU in low voltage designs!
You may replace this search algorithm with any other algorithm you like if
you have additional constraints such as a maximum CPU clock.
For version 5.x RC oscillators (those with a split range of 2x128 steps, e.g.
ATTiny25, ATTiny45, ATTiny85), it may be useful to search for the optimum in
both regions.
*/

void    usbEventResetReady(void)
{
    calibrateOscillator();
    eeprom_write_byte(0, OSCCAL);   /* store the calibrated value in EEPROM */
}

I have left my main() untouched for this next step.

The code compiles fine, but the device won't connect.

What am I doing wrong?

Regards, Thomas

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

Post by christian » Sun Mar 16, 2008 11:27 pm

If you use interrupts in your main code, enclose the call to calibrateOscillator() in cli() and sei(). Since timing is measured by counting CPU cycles, no interrupts may occur.

iphi
Rank 2
Rank 2
Posts: 68
Joined: Mon Jun 25, 2007 11:37 am

Post by iphi » Sun Mar 16, 2008 11:42 pm

I don't use interrupts in my application. Also, I don't see the code write any value into the EEPROM.

It looks as if the AVR undergoes continuous resets. Any other idea what could go wrong?

Did I miss out on anything else inside the usbconfig.h?

Regards, Thomas

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

Post by christian » Mon Mar 17, 2008 12:28 pm

This might be watchdog resets. Calibration takes a while. If your watchdog is too fast, it will cause a reset.

iphi
Rank 2
Rank 2
Posts: 68
Joined: Mon Jun 25, 2007 11:37 am

Post by iphi » Mon Mar 17, 2008 5:08 pm

Hi Christian,

thanks for this hint. It was the watchdog firing resets during calibration.
Now everything works fine.

BTW:
The auto-calibration works very well and subjectively does not require any time. Therefore I suggest not to store the CSCCAL value in the EEPROM and reuse it but to recalibrate on every reset. This way ageing drifts and temperature drifts can be compensated.

Thank you very much for this great new feature!

Regards, Thomas

Post Reply