2 * Author: cpldcpu@gmail.com
3 * Creation Date: 2013-11-3
5 * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
8 /* Calibrate the RC oscillator. Our timing reference is the Start Of Frame
9 * signal (a single SE0 bit) repeating every millisecond immediately after
14 * - Codesize reduced by 90 bytes.
15 * - Improved robustness due to removing timeout from frame length measurement and
16 * inserted NOP after OSCCAL writes.
19 * - The new routine performs a combined binary and neighborhood search
21 * Note that the neighborhood search is necessary due to the quasi-monotonic
22 * nature of OSCCAL. (See Atmel application note AVR054).
23 * - Inserted NOP after writes to OSCCAL to avoid CPU errors during oscillator
25 * - Implemented new routine to measure frame time "usbMeasureFrameLengthDecreasing".
26 * This routine takes the target time as a parameter and returns the deviation.
27 * - usbMeasureFrameLengthDecreasing measures in multiples of 5 cycles and is thus
28 * slighly more accurate.
29 * - usbMeasureFrameLengthDecreasing does not support time out anymore. The original
30 * implementation returned zero in case of time out, which would have caused the old
31 * calibrateOscillator() implementation to increase OSSCAL to 255, effictively
32 * overclocking and most likely crashing the CPU. The new implementation will enter
33 * an infinite loop when no USB activity is encountered. The user program should
34 * use the watchdog to escape from situations like this.
36 * This routine will work both on controllers with and without split OSCCAL range.
37 * The first trial value is 128 which is the lowest value of the upper OSCCAL range
38 * on Attiny85 and will effectively limit the search to the upper range, unless the
39 * RC oscillator frequency is unusually high. Under normal operation, the highest
40 * tested frequency setting is 192. This corresponds to ~20 Mhz core frequency and
41 * is still within spec for a 5V device.
45 #define __SFR_OFFSET 0 /* used by avr-libc's register definitions */
46 #include "./usbdrv/usbdrv.h" /* for common defs */
48 #ifdef __IAR_SYSTEMS_ASM__
49 /* Register assignments for usbMeasureFrameLengthDecreasing on IAR cc */
50 /* Calling conventions on IAR:
51 * First parameter passed in r16/r17, second in r18/r19 and so on.
52 * Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer)
53 * Result is passed in r16/r17
54 * In case of the "tiny" memory model, pointers are only 8 bit with no
55 * padding. We therefore pass argument 1 as "16 bit unsigned".
70 #else /* __IAR_SYSTEMS_ASM__ */
71 /* Register assignments for usbMeasureFrameLength on gcc */
72 /* Calling conventions on gcc:
73 * First parameter passed in r24/r25, second in r22/23 and so on.
74 * Callee must preserve r1-r17, r28/r29
75 * Result is passed in r24/r25
88 ; extern void calibrateOscillatorASM(void);
90 .global calibrateOscillatorASM
91 calibrateOscillatorASM:
96 in try, OSCCAL ; calibration start value
97 ldi stp, 64 ; initial step width
98 ldi i, 10 ; 10 iterations
105 ; Delay values = F_CPU * 999e-6 / 5 + 0.5
107 #if (F_CPU == 16500000)
108 ldi cnt16L, lo8(3297)
109 ldi cnt16H, hi8(3297)
110 #elif (F_CPU == 12800000)
111 ldi cnt16L, lo8(2557)
112 ldi cnt16H, hi8(2557)
114 #error "calibrateOscillatorASM: no delayvalues defined for this F_CPU setting"
117 usbCOWaitStrobe: ; first wait for D- == 0 (idle strobe)
118 sbic USBIN, USBMINUS ;
119 rjmp usbCOWaitStrobe ;
120 usbCOWaitIdle: ; then wait until idle again
121 sbis USBIN, USBMINUS ;1 wait for D- == 1
122 rjmp usbCOWaitIdle ;2
124 sbiw cnt16,1 ;[0] [5]
125 sbic USBIN, USBMINUS ;[2]
126 rjmp usbCOWaitLoop ;[3]
128 sbrs cnt16H, 7 ;delay overflow?
129 rjmp usbCOclocktoolow
132 rjmp usbCOclocktoohigh
137 brne usbCOnoneighborhoodsearch
139 brcs usbCOnoimprovement
144 usbCOnoneighborhoodsearch:
162 /* ------------------------------------------------------------------------- */
163 /* ------ Original C Implementation of improved calibrateOscillator -------- */
164 /* ---------------------- for Reference only ----------------------------- */
165 /* ------------------------------------------------------------------------- */
168 void calibrateOscillator(void)
170 uchar step, trialValue, optimumValue;
175 targetValue = (unsigned)((double)F_CPU * 999e-6 / 5.0 + 0.5); /* Time is measured in multiples of 5 cycles. Target is 0.999µs */
177 // optimumValue = OSCCAL;
181 cli(); // disable interrupts
184 Performs seven iterations of a binary search (stepwidth decreasing9
185 with three additional steps of a neighborhood search (step=1, trialvalue will oscillate around target value to find optimum)
190 asm volatile(" NOP");
192 x = usbMeasureFrameLengthDecreasing(targetValue);
194 if(x < 0) /* frequency too high */
199 else /* frequency too low */
206 Halve stepwidth to perform binary search. At step=1 the mode changes to neighbourhood search.
207 Once the neighbourhood search stage is reached, x will be smaller than +-255, hence more code can be
208 saved by only working with the lower 8 bits.
213 if (step==0) // Enter neighborhood search mode
216 if(xl <= optimumDev){
218 optimumValue = OSCCAL;
223 OSCCAL = optimumValue;
224 asm volatile(" NOP");
226 sei(); // enable interrupts