From c6b12b287746ac950f4efcd1e5e872f5fe5acfe2 Mon Sep 17 00:00:00 2001 From: cpldcpu <cpldcpu@gmail.com> Date: Sun, 15 Dec 2013 21:16:32 +0100 Subject: clean up osccal --- firmware/Makefile | 4 +- firmware/libs-device/Readme.txt | 22 ---- firmware/libs-device/osccal.c | 183 ------------------------------- firmware/libs-device/osccal.h | 57 ---------- firmware/libs-device/osccalASM.S | 228 --------------------------------------- firmware/libs-device/osctune.h | 88 --------------- firmware/osccal.h | 57 ++++++++++ firmware/osccalASM.S | 228 +++++++++++++++++++++++++++++++++++++++ firmware/osccalASM.o | Bin 0 -> 1684 bytes 9 files changed, 287 insertions(+), 580 deletions(-) delete mode 100644 firmware/libs-device/Readme.txt delete mode 100644 firmware/libs-device/osccal.c delete mode 100644 firmware/libs-device/osccal.h delete mode 100644 firmware/libs-device/osccalASM.S delete mode 100644 firmware/libs-device/osctune.h create mode 100644 firmware/osccal.h create mode 100644 firmware/osccalASM.S create mode 100644 firmware/osccalASM.o (limited to 'firmware') diff --git a/firmware/Makefile b/firmware/Makefile index 0905d0a..9da5be6 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -149,12 +149,12 @@ CC = avr-gcc DEFINES = -DBOOTLOADER_ADDRESS=0x$(BOOTLOADER_ADDRESS) #-DDEBUG_LEVEL=2 # Remove the -fno-* options when you use gcc 3, it does not understand them # -CFLAGS = -g2 -nostartfiles -ffunction-sections -fdata-sections -fpack-struct -Wall -Os -fno-inline-small-functions -fno-move-loop-invariants -fno-tree-scev-cprop -I. -Ilibs-device -mmcu=$(DEVICE) -DF_CPU=$(F_CPU) $(DEFINES) +CFLAGS = -g2 -nostartfiles -ffunction-sections -fdata-sections -fpack-struct -Wall -Os -fno-inline-small-functions -fno-move-loop-invariants -fno-tree-scev-cprop -I. -mmcu=$(DEVICE) -DF_CPU=$(F_CPU) $(DEFINES) LDFLAGS = -Wl,--relax,--section-start=.text=$(BOOTLOADER_ADDRESS),-Map=main.map,--section-start=.zerotable=0 OBJECTS = crt1.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o -OBJECTS += libs-device/osccalASM.o +OBJECTS += osccalASM.o # symbolic targets: all: main.hex diff --git a/firmware/libs-device/Readme.txt b/firmware/libs-device/Readme.txt deleted file mode 100644 index 76518dc..0000000 --- a/firmware/libs-device/Readme.txt +++ /dev/null @@ -1,22 +0,0 @@ -This is the Readme file for the libs-device directory. This directory contains -code snippets which may be useful for USB device firmware. - - -WHAT IS INCLUDED IN THIS DIRECTORY? -=================================== - -osccal.c and osccal.h - This module contains a function which calibrates the AVR's built-in RC - oscillator based on the USB frame clock. See osccal.h for a documentation - of the API. - -osctune.h - This header file contains a code snippet for usbconfig.h. With this code, - you can keep the AVR's internal RC oscillator in sync with the USB frame - clock. This is a continuous synchronization, not a single calibration at - USB reset as with osccal.c above. Please note that this code works only - if D- is wired to the interrupt, not D+. - ----------------------------------------------------------------------------- -(c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH. -http://www.obdev.at/ diff --git a/firmware/libs-device/osccal.c b/firmware/libs-device/osccal.c deleted file mode 100644 index 8debe49..0000000 --- a/firmware/libs-device/osccal.c +++ /dev/null @@ -1,183 +0,0 @@ -/* 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) - * This Revision: $Id: osccal.c 762 2009-08-12 17:10:30Z cs $ - */ - -#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. - * - * - * 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, 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 -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. -*/ diff --git a/firmware/libs-device/osccal.h b/firmware/libs-device/osccal.h deleted file mode 100644 index af37a43..0000000 --- a/firmware/libs-device/osccal.h +++ /dev/null @@ -1,57 +0,0 @@ -/* Name: osccal.h - * Author: Christian Starkjohann - * Creation Date: 2008-04-10 - * Changes 2013-11-04 cpldcpu@gmail.com - * Tabsize: 4 - * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH - * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) - */ - -/* -General Description: -This module contains a function which calibrates the AVR's internal RC -oscillator so that the CPU runs at F_CPU (F_CPU is a macro which must be -defined when the module is compiled, best passed in the compiler command -line). The time reference is the USB frame clock of 1 kHz available -immediately after a USB RESET condition. Timing is done by counting CPU -cycles, so all interrupts must be disabled while the calibration runs. -The size optimized assembler implementation includes its own implementation -of usbMeasureFrameLength. Therefore USB_CFG_HAVE_MEASURE_FRAME_LENGTH should -be set to 0 to avoid including unused code sections. It is recommended to call -calibrateOscillatorASM() from the reset hook in usbconfig.h by including osccal.h: - -#include "osccal.h" - -This routine is an alternative to the continuous synchronization described -in osctune.h. - -Algorithm used: See osccalASM.x - -Limitations: -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! -Precision depends on the OSCCAL vs. frequency dependency of the oscillator. -Typical precision for an ATMega168 (derived from the OSCCAL vs. F_RC diagram -in the data sheet) should be in the range of 0.4%. Only the 12.8 MHz and -16.5 MHz versions of V-USB (with built-in receiver PLL) can tolerate this -deviation! All other frequency modules require at least 0.2% precision. -*/ - -#ifndef __OSCCAL_H_INCLUDED__ -#define __OSCCAL_H_INCLUDED__ - -#ifndef __ASSEMBLER__ - void calibrateOscillatorASM(void); -# define USB_RESET_HOOK(resetStarts) if(!resetStarts){ calibrateOscillatorASM();} -# define USB_CFG_HAVE_MEASURE_FRAME_LENGTH 0 -#endif -/* This function calibrates the RC oscillator so that the CPU runs at F_CPU. - * It MUST be called immediately after the end of a USB RESET condition! - * Disable all interrupts during the call! - * It is recommended that you store the resulting value in EEPROM so that a - * good guess value is available after the next reset. - */ - - -#endif /* __OSCCAL_H_INCLUDED__ */ diff --git a/firmware/libs-device/osccalASM.S b/firmware/libs-device/osccalASM.S deleted file mode 100644 index 9a317f1..0000000 --- a/firmware/libs-device/osccalASM.S +++ /dev/null @@ -1,228 +0,0 @@ -/* Name: osccalASM.S - * Author: cpldcpu@gmail.com - * Creation Date: 2013-11-3 - * Tabsize: 4 - * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) - */ - -/* 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. - * - * - * Benefits: - * - Codesize reduced by 90 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. - */ - - -#define __SFR_OFFSET 0 /* used by avr-libc's register definitions */ -#include "./usbdrv/usbdrv.h" /* for common defs */ - -#ifdef __IAR_SYSTEMS_ASM__ -/* Register assignments for usbMeasureFrameLengthDecreasing on IAR cc */ -/* Calling conventions on IAR: - * First parameter passed in r16/r17, second in r18/r19 and so on. - * Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer) - * Result is passed in r16/r17 - * In case of the "tiny" memory model, pointers are only 8 bit with no - * padding. We therefore pass argument 1 as "16 bit unsigned". - */ - -//Untested - -# define i r20 -# define opV r19 -# define opD r18 -# define try r21 -# define stp r22 - -# define cnt16L r30 -# define cnt16H r31 - - -#else /* __IAR_SYSTEMS_ASM__ */ -/* Register assignments for usbMeasureFrameLength on gcc */ -/* Calling conventions on gcc: - * First parameter passed in r24/r25, second in r22/23 and so on. - * Callee must preserve r1-r17, r28/r29 - * Result is passed in r24/r25 - */ - -# define i r20 -# define opV r19 -# define opD r18 -# define try r27 -# define stp r26 -# define cnt16L r24 -# define cnt16H r25 -#endif -# define cnt16 cnt16L - -; extern void calibrateOscillatorASM(void); - -.global calibrateOscillatorASM -calibrateOscillatorASM: - - cli - ldi opD, 255 - - ldi try, 128 ; calibration start value - ldi stp, 64 ; initial step width - ldi i, 10 ; 10 iterations - -usbCOloop: - - out OSCCAL, try - nop - - ; Delay values = F_CPU * 999e-6 / 5 + 0.5 - -#if (F_CPU == 16500000) - ldi cnt16L, lo8(3297) - ldi cnt16H, hi8(3297) -#elif (F_CPU == 12800000) - ldi cnt16L, lo8(2557) - ldi cnt16H, hi8(2557) -#else - #error "calibrateOscillatorASM: no delayvalues defined for this F_CPU setting" -#endif - -usbCOWaitStrobe: ; first wait for D- == 0 (idle strobe) - sbic USBIN, USBMINUS ; - rjmp usbCOWaitStrobe ; -usbCOWaitIdle: ; then wait until idle again - sbis USBIN, USBMINUS ;1 wait for D- == 1 - rjmp usbCOWaitIdle ;2 -usbCOWaitLoop: - sbiw cnt16,1 ;[0] [5] - sbic USBIN, USBMINUS ;[2] - rjmp usbCOWaitLoop ;[3] - - sbrs cnt16H, 7 ;delay overflow? - rjmp usbCOclocktoolow - sub try, stp - neg cnt16L - rjmp usbCOclocktoohigh -usbCOclocktoolow: - add try, stp -usbCOclocktoohigh: - lsr stp - brne usbCOnoneighborhoodsearch - cp opD, cnt16L - brcs usbCOnoimprovement - in opV, OSCCAL - mov opD, cnt16L -usbCOnoimprovement: - ldi stp, 1 -usbCOnoneighborhoodsearch: - subi i, 1 - brne usbCOloop - - out OSCCAL, opV - nop - sei - ret - -#undef i -#undef opV -#undef opD -#undef try -#undef stp -#undef cnt16 -#undef cnt16L -#undef cnt16H - -/* ------------------------------------------------------------------------- */ -/* ------ Original C Implementation of improved calibrateOscillator -------- */ -/* ---------------------- for Reference only ----------------------------- */ -/* ------------------------------------------------------------------------- */ - -#if 0 -void calibrateOscillator(void) -{ - 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. At step=1 the mode changes to neighbourhood search. - 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) // Enter neighborhood search mode - { - step=1; - if(xl <= optimumDev){ - optimumDev = xl; - optimumValue = OSCCAL; - } - } - } - - OSCCAL = optimumValue; - asm volatile(" NOP"); - - sei(); // enable interrupts -} -#endif \ No newline at end of file diff --git a/firmware/libs-device/osctune.h b/firmware/libs-device/osctune.h deleted file mode 100644 index c751648..0000000 --- a/firmware/libs-device/osctune.h +++ /dev/null @@ -1,88 +0,0 @@ -/* Name: osctune.h - * Author: Christian Starkjohann - * Creation Date: 2008-10-18 - * Tabsize: 4 - * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH - * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) - * This Revision: $Id: osctune.h 692 2008-11-07 15:07:40Z cs $ - */ - -/* -General Description: -This file is declared as C-header file although it is mostly documentation -how the RC oscillator can be kept in sync to the USB frame rate. The code -shown here must be added to usbconfig.h or this header file is included from -there. This code works only if D- is wired to the interrupt, not D+!!! - -This is an alternative to the osccal routine in osccal.c. It has the advantage -that the synchronization is done continuously and that it has more compact -code size. The disadvantages are slow synchronization (it may take a while -until the driver works), that messages immediately after the SOF pulse may be -lost (and need to be retried by the host) and that the interrupt is on D- -contrary to most examples. - -You may want to store a good calibration value in EEPROM for the next startup. -You know that the calibration value is good when the first USB message is -received. Do not store the value on every received message because the EEPROM -has a limited endurance. - -Notes: -(*) You must declare the global character variable "lastTimer0Value" in your -main code. - -(*) Timer 0 must be free running (not written by your code) and the prescaling -must be consistent with the TIMER0_PRESCALING define. - -(*) Good values for Timer 0 prescaling depend on how precise the clock must -be tuned and how far away from the default clock rate the target clock is. -For precise tuning, choose a low prescaler factor, for a broad range of tuning -choose a high one. A prescaler factor of 64 is good for the entire OSCCAL -range and allows a precision of better than +/-1%. A prescaler factor of 8 -allows tuning to slightly more than +/-6% of the default frequency and is -more precise than one step of OSCCAL. It is therefore not suitable to tune an -8 MHz oscillator to 12.5 MHz. - -Thanks to Henrik Haftmann for the idea to this routine! -*/ - -#define TIMER0_PRESCALING 64 /* must match the configuration for TIMER0 in main */ -#define TOLERATED_DEVIATION_PPT 5 /* max clock deviation before we tune in 1/10 % */ -/* derived constants: */ -#define EXPECTED_TIMER0_INCREMENT ((F_CPU / (1000 * TIMER0_PRESCALING)) & 0xff) -#define TOLERATED_DEVIATION (TOLERATED_DEVIATION_PPT * F_CPU / (1000000 * TIMER0_PRESCALING)) - -#ifdef __ASSEMBLER__ -macro tuneOsccal - push YH ;[0] - in YL, TCNT0 ;[2] - lds YH, lastTimer0Value ;[3] - sts lastTimer0Value, YL ;[5] - sub YL, YH ;[7] time passed since last frame - subi YL, EXPECTED_TIMER0_INCREMENT ;[8] -#if OSCCAL > 0x3f /* outside I/O addressable range */ - lds YH, OSCCAL ;[6] -#else - in YH, OSCCAL ;[6] assembler modle uses __SFR_OFFSET == 0 -#endif - cpi YL, TOLERATED_DEVIATION + 1 ;[10] - brmi notTooHigh ;[11] - subi YH, 1 ;[12] clock rate was too high -; brcs tuningOverflow ; optionally check for overflow - rjmp osctuneDone ;[13] -notTooHigh: - cpi YL, -TOLERATED_DEVIATION ;[13] - brpl osctuneDone ;[14] not too low - inc YH ;[15] clock rate was too low -; breq tuningOverflow ; optionally check for overflow -osctuneDone: -#if OSCCAL > 0x3f /* outside I/O addressable range */ - sts OSCCAL, YH ;[12-13] store tuned value -#else - out OSCCAL, YH ;[12-13] store tuned value -#endif -tuningOverflow: - pop YH ;[17] - endm ;[19] max number of cycles -#endif - -#define USB_SOF_HOOK tuneOsccal diff --git a/firmware/osccal.h b/firmware/osccal.h new file mode 100644 index 0000000..af37a43 --- /dev/null +++ b/firmware/osccal.h @@ -0,0 +1,57 @@ +/* Name: osccal.h + * Author: Christian Starkjohann + * Creation Date: 2008-04-10 + * Changes 2013-11-04 cpldcpu@gmail.com + * Tabsize: 4 + * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +/* +General Description: +This module contains a function which calibrates the AVR's internal RC +oscillator so that the CPU runs at F_CPU (F_CPU is a macro which must be +defined when the module is compiled, best passed in the compiler command +line). The time reference is the USB frame clock of 1 kHz available +immediately after a USB RESET condition. Timing is done by counting CPU +cycles, so all interrupts must be disabled while the calibration runs. +The size optimized assembler implementation includes its own implementation +of usbMeasureFrameLength. Therefore USB_CFG_HAVE_MEASURE_FRAME_LENGTH should +be set to 0 to avoid including unused code sections. It is recommended to call +calibrateOscillatorASM() from the reset hook in usbconfig.h by including osccal.h: + +#include "osccal.h" + +This routine is an alternative to the continuous synchronization described +in osctune.h. + +Algorithm used: See osccalASM.x + +Limitations: +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! +Precision depends on the OSCCAL vs. frequency dependency of the oscillator. +Typical precision for an ATMega168 (derived from the OSCCAL vs. F_RC diagram +in the data sheet) should be in the range of 0.4%. Only the 12.8 MHz and +16.5 MHz versions of V-USB (with built-in receiver PLL) can tolerate this +deviation! All other frequency modules require at least 0.2% precision. +*/ + +#ifndef __OSCCAL_H_INCLUDED__ +#define __OSCCAL_H_INCLUDED__ + +#ifndef __ASSEMBLER__ + void calibrateOscillatorASM(void); +# define USB_RESET_HOOK(resetStarts) if(!resetStarts){ calibrateOscillatorASM();} +# define USB_CFG_HAVE_MEASURE_FRAME_LENGTH 0 +#endif +/* This function calibrates the RC oscillator so that the CPU runs at F_CPU. + * It MUST be called immediately after the end of a USB RESET condition! + * Disable all interrupts during the call! + * It is recommended that you store the resulting value in EEPROM so that a + * good guess value is available after the next reset. + */ + + +#endif /* __OSCCAL_H_INCLUDED__ */ diff --git a/firmware/osccalASM.S b/firmware/osccalASM.S new file mode 100644 index 0000000..9a317f1 --- /dev/null +++ b/firmware/osccalASM.S @@ -0,0 +1,228 @@ +/* Name: osccalASM.S + * Author: cpldcpu@gmail.com + * Creation Date: 2013-11-3 + * Tabsize: 4 + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + */ + +/* 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. + * + * + * Benefits: + * - Codesize reduced by 90 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. + */ + + +#define __SFR_OFFSET 0 /* used by avr-libc's register definitions */ +#include "./usbdrv/usbdrv.h" /* for common defs */ + +#ifdef __IAR_SYSTEMS_ASM__ +/* Register assignments for usbMeasureFrameLengthDecreasing on IAR cc */ +/* Calling conventions on IAR: + * First parameter passed in r16/r17, second in r18/r19 and so on. + * Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer) + * Result is passed in r16/r17 + * In case of the "tiny" memory model, pointers are only 8 bit with no + * padding. We therefore pass argument 1 as "16 bit unsigned". + */ + +//Untested + +# define i r20 +# define opV r19 +# define opD r18 +# define try r21 +# define stp r22 + +# define cnt16L r30 +# define cnt16H r31 + + +#else /* __IAR_SYSTEMS_ASM__ */ +/* Register assignments for usbMeasureFrameLength on gcc */ +/* Calling conventions on gcc: + * First parameter passed in r24/r25, second in r22/23 and so on. + * Callee must preserve r1-r17, r28/r29 + * Result is passed in r24/r25 + */ + +# define i r20 +# define opV r19 +# define opD r18 +# define try r27 +# define stp r26 +# define cnt16L r24 +# define cnt16H r25 +#endif +# define cnt16 cnt16L + +; extern void calibrateOscillatorASM(void); + +.global calibrateOscillatorASM +calibrateOscillatorASM: + + cli + ldi opD, 255 + + ldi try, 128 ; calibration start value + ldi stp, 64 ; initial step width + ldi i, 10 ; 10 iterations + +usbCOloop: + + out OSCCAL, try + nop + + ; Delay values = F_CPU * 999e-6 / 5 + 0.5 + +#if (F_CPU == 16500000) + ldi cnt16L, lo8(3297) + ldi cnt16H, hi8(3297) +#elif (F_CPU == 12800000) + ldi cnt16L, lo8(2557) + ldi cnt16H, hi8(2557) +#else + #error "calibrateOscillatorASM: no delayvalues defined for this F_CPU setting" +#endif + +usbCOWaitStrobe: ; first wait for D- == 0 (idle strobe) + sbic USBIN, USBMINUS ; + rjmp usbCOWaitStrobe ; +usbCOWaitIdle: ; then wait until idle again + sbis USBIN, USBMINUS ;1 wait for D- == 1 + rjmp usbCOWaitIdle ;2 +usbCOWaitLoop: + sbiw cnt16,1 ;[0] [5] + sbic USBIN, USBMINUS ;[2] + rjmp usbCOWaitLoop ;[3] + + sbrs cnt16H, 7 ;delay overflow? + rjmp usbCOclocktoolow + sub try, stp + neg cnt16L + rjmp usbCOclocktoohigh +usbCOclocktoolow: + add try, stp +usbCOclocktoohigh: + lsr stp + brne usbCOnoneighborhoodsearch + cp opD, cnt16L + brcs usbCOnoimprovement + in opV, OSCCAL + mov opD, cnt16L +usbCOnoimprovement: + ldi stp, 1 +usbCOnoneighborhoodsearch: + subi i, 1 + brne usbCOloop + + out OSCCAL, opV + nop + sei + ret + +#undef i +#undef opV +#undef opD +#undef try +#undef stp +#undef cnt16 +#undef cnt16L +#undef cnt16H + +/* ------------------------------------------------------------------------- */ +/* ------ Original C Implementation of improved calibrateOscillator -------- */ +/* ---------------------- for Reference only ----------------------------- */ +/* ------------------------------------------------------------------------- */ + +#if 0 +void calibrateOscillator(void) +{ + 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. At step=1 the mode changes to neighbourhood search. + 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) // Enter neighborhood search mode + { + step=1; + if(xl <= optimumDev){ + optimumDev = xl; + optimumValue = OSCCAL; + } + } + } + + OSCCAL = optimumValue; + asm volatile(" NOP"); + + sei(); // enable interrupts +} +#endif \ No newline at end of file diff --git a/firmware/osccalASM.o b/firmware/osccalASM.o new file mode 100644 index 0000000..2a829d4 Binary files /dev/null and b/firmware/osccalASM.o differ -- cgit v1.2.3