diff options
Diffstat (limited to 'firmware/libs-device/osccal.c')
| -rw-r--r-- | firmware/libs-device/osccal.c | 178 | 
1 files changed, 149 insertions, 29 deletions
| diff --git a/firmware/libs-device/osccal.c b/firmware/libs-device/osccal.c index 939d5c3..8debe49 100644 --- a/firmware/libs-device/osccal.c +++ b/firmware/libs-device/osccal.c @@ -8,49 +8,169 @@   */  #include <avr/io.h> - +#include <avr/interrupt.h>  #ifndef uchar  #define uchar   unsigned char  #endif +int usbMeasureFrameLengthDecreasing(int); +  /* ------------------------------------------------------------------------- */  /* ------------------------ Oscillator Calibration ------------------------- */  /* ------------------------------------------------------------------------- */  /* 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. + * a USB RESET.  + * + *  + * Optimized version by cpldcpu@gmail.com, Nov 3rd 2013. + * + * Benefits: + *	  - Codesize reduced by 54 bytes. + *    - Improved robustness due to removing timeout from frame length measurement and  + *	    inserted NOP after OSCCAL writes. + * + * Changes: + *    - The new routine performs a combined binary and neighborhood search + *      in a single loop. + *      Note that the neighborhood search is necessary due to the quasi-monotonic  + *      nature of OSCCAL. (See Atmel application note AVR054). + *	  - Inserted NOP after writes to OSCCAL to avoid CPU errors during oscillator + *      stabilization.  + *    - Implemented new routine to measure frame time "usbMeasureFrameLengthDecreasing". + *		This routine takes the target time as a parameter and returns the deviation. + *	  - usbMeasureFrameLengthDecreasing measures in multiples of 5 cycles and is thus + *	    slighly more accurate. + *	  - usbMeasureFrameLengthDecreasing does not support time out anymore. The original + *	    implementation returned zero in case of time out, which would have caused the old + *      calibrateOscillator() implementation to increase OSSCAL to 255, effictively + *      overclocking and most likely crashing the CPU. The new implementation will enter + *		an infinite loop when no USB activity is encountered. The user program should + *      use the watchdog to escape from situations like this. +  *  + * This routine will work both on controllers with and without split OSCCAL range. + * The first trial value is 128 which is the lowest value of the upper OSCCAL range + * on Attiny85 and will effectively limit the search to the upper range, unless the + * RC oscillator frequency is unusually high. Under normal operation, the highest  + * tested frequency setting is 192. This corresponds to ~20 Mhz core frequency and  + * is still within spec for a 5V device.   */ +  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; +	uchar       step, trialValue, optimumValue; +	int         x, targetValue; +	uchar		optimumDev; +	uchar		i,xl; +	 +	targetValue = (unsigned)((double)F_CPU * 999e-6 / 5.0 + 0.5);  /* Time is measured in multiples of 5 cycles. Target is 0.999µs */ +    optimumDev = 0xff;    +  //  optimumValue = OSCCAL;  +	step=64; +	trialValue = 128; +	 +	cli(); // disable interrupts +	 +	/* +		Performs seven iterations of a binary search (stepwidth decreasing9 +		with three additional steps of a neighborhood search (step=1, trialvalue will oscillate around target value to find optimum) +	*/ + +	for(i=0; i<10; i++){ +		OSCCAL = trialValue; +		asm volatile(" NOP"); +	 +		x = usbMeasureFrameLengthDecreasing(targetValue); + +		if(x < 0)             /* frequency too high */ +		{ +			trialValue -= step; +			xl=(uchar)-x; +		} +		else                  /* frequency too low */ +		{ +			trialValue += step;			 +			xl=(uchar)x; +		} +		 +		/* +			Halve stepwidth to perform binary search. Logical oring with 1 to ensure step is never equal to zero.  +			This results in a neighborhood search with stepwidth 1 after binary search is finished.			 +			Once the neighbourhood search stage is reached, x will be smaller than +-255, hence more code can be +			saved by only working with the lower 8 bits. +		*/ +		 +		step >>= 1; +		 +		if (step==0) +		{ +			step=1;			 +			if(xl <= optimumDev){ +				optimumDev = xl; +				optimumValue = OSCCAL; +			} +		} + +	} + +	OSCCAL = optimumValue; +	asm volatile(" NOP"); +	 +	sei(); // enable interrupts  } + +void    calibrateOscillator_old(void) +{ +	uchar       step, trialValue, optimumValue; +	int         x, optimumDev, targetValue; +	uchar		i; +	 +	targetValue = (unsigned)((double)F_CPU * 999e-6 / 5.0 + 0.5);  /* Time is measured in multiples of 5 cycles. Target is 0.999µs */ +    optimumDev = 0x7f00;   // set to high positive value +    optimumValue = OSCCAL;  +	step=64; +	trialValue = 128; +	 +	/* +		Performs seven iterations of a binary search (stepwidth decreasing9 +		with three additional steps of a neighborhood search (step=1, trialvalue will oscillate around target value to find optimum) +	*/ + +	for(i=0; i<10; i++){ +		OSCCAL = trialValue; +		asm volatile(" NOP"); +	 +		x = usbMeasureFrameLengthDecreasing(targetValue); + +		if(x < 0)             /* frequency too high */ +		{ +			trialValue -= step; +			x = -x; +		} +		else                  /* frequency too low */ +		{ +			trialValue += step;			 +		} +		 +		/* +			Halve stepwidth to perform binary search. Logical oring with 1 to ensure step is never equal to zero.  +			This results in a neighborhood search with stepwidth 1 after binary search is finished.			 +		*/ +		 +		step >>= 1; +		step |=1;	 + +		if(x < optimumDev){ +			optimumDev = x; +			optimumValue = OSCCAL; +		} +	} + +	OSCCAL = optimumValue; +	asm volatile(" NOP"); +} +  /*  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 | 
