69 lines
2.6 KiB
C
69 lines
2.6 KiB
C
|
/* Name: osccal.c
|
||
|
* Author: Christian Starkjohann
|
||
|
* Creation Date: 2008-04-10
|
||
|
* Tabsize: 4
|
||
|
* Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
|
||
|
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
|
||
|
*/
|
||
|
|
||
|
#include <avr/io.h>
|
||
|
|
||
|
#ifndef uchar
|
||
|
#define uchar unsigned char
|
||
|
#endif
|
||
|
|
||
|
/* ------------------------------------------------------------------------- */
|
||
|
/* ------------------------ Oscillator Calibration ------------------------- */
|
||
|
/* ------------------------------------------------------------------------- */
|
||
|
|
||
|
/* This is a "C" implementation. You can customize it to your needs easily.
|
||
|
* If you want smaller code size, there is an improved version in the
|
||
|
* micronucleous project. See
|
||
|
* https://github.com/micronucleus/micronucleus/blob/master/firmware/osccalASM.S
|
||
|
*/
|
||
|
|
||
|
/* Calibrate the RC oscillator. Our timing reference is the Start Of Frame
|
||
|
* signal (a single SE0 bit) repeating every millisecond immediately after
|
||
|
* a USB RESET. We first do a binary search for the OSCCAL value and then
|
||
|
* optimize this value with a neighboorhod search.
|
||
|
*/
|
||
|
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.
|
||
|
*/
|