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