occasional transmission errors with 16.5MHz RC oscillator
occasional transmission errors with 16.5MHz RC oscillator
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
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
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
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
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?
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?
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
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
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.
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.
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
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...
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...
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:
3. I have copied the following code into my application:
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
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
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
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