-
Notifications
You must be signed in to change notification settings - Fork 0
/
osctune.h
109 lines (94 loc) · 4.76 KB
/
osctune.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/* 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 $
*/
/*
This file has been modified for GemmaBoot, by Adafruit Industries
Copyright (c) 2013 Adafruit Industries
All rights reserved.
GemmaBoot is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
GemmaBoot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with GemmaBoot. If not, see
<http://www.gnu.org/licenses/>.
*/
/*
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