1 // Copyright 2009 Olivier Gillet.
2 //
3 // Author: Olivier Gillet (ol.gillet@gmail.com)
4 //
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program. If not, see <http://www.gnu.org/licenses/>.
15 //
16 // -----------------------------------------------------------------------------
17 //
18 // Real time clock.
19
20 #ifndef AVRLIB_TIME_H_
21 #define AVRLIB_TIME_H_
22
23 #include <avr/delay.h>
24
25 #include "avrlib/base.h"
26
27 namespace avrlib {
28
29 uint32_t milliseconds();
30 uint32_t Delay(uint32_t delay);
31
32 #define ConstantDelay(x) _delay_ms((x))
33
34 void InitClock();
35
36 const uint32_t microseconds_per_timer0_overflow =
37 (64 * 256) / (F_CPU / 1000000L);
38 const uint32_t milliseconds_increment =
39 microseconds_per_timer0_overflow / 1000;
40
41 const uint32_t fractional_increment = (
42 microseconds_per_timer0_overflow % 1000) >> 3;
43
44 const uint8_t fractional_max = 1000 >> 3;
45
46 // The timer count is stored as an union instead of a mere uint32_t because we
47 // need access to the individual 16-bit parts of the value.
48 extern volatile LongWord timer0_milliseconds;
49
50 extern uint8_t timer0_fractional;
51
TickSystemClock()52 inline void TickSystemClock() {
53 // Compile-time optimization: with a 20Mhz clock rate, milliseconds_increment
54 // is always null, so we have to increment it only when there's a
55 // fractional overflow!
56 if (milliseconds_increment) {
57 timer0_milliseconds.value += milliseconds_increment;
58 }
59 timer0_fractional += fractional_increment;
60 if (timer0_fractional >= fractional_max) {
61 timer0_fractional -= fractional_max;
62 // The next lines are equivalent to: ++timer0_fractional. Why am I not
63 // using ++timer0_fractional? The reason is in the way gcc compiles this.
64 // 32-bits values are always loaded into contiguous registers. This code is
65 // called from an ISR, so this means 4 contiguous registers are going to
66 // be pushed/popped in the ISR. This costs 4 pairs of push/pops (16 cycles).
67 // On the other hand, this weird implementation only requires 2 adjacent
68 // registers, and they are probably already used for something else in the
69 // ISR. There's no free lunch, though: this code is less efficient than
70 // a++. However, when it is called every 16th or 32th entry in an ISR, the
71 // time saved by avoiding the extra push/pops makes it a better choice.
72 //
73 // Rule: when you *occasionnally* do something complicated from within an
74 // ISR, the code doing the complicated thing should really try to minimize
75 // the number of registers it uses, even if it takes more cycles to do
76 // the work.
77 ++timer0_milliseconds.words[0];
78 if (timer0_milliseconds.words[0] == 0) {
79 ++timer0_milliseconds.words[1];
80 }
81 }
82 }
83
84 } // namespace avrlib
85
86 #endif // AVRLIB_TIME_H_
87