4
* Copyright (C) 2011 Tim Marston <edam@waxworlds.org>
6
* This file is part of propeller-clock (hereafter referred to as "this
7
* program"). See http://ed.am/software/arduino/propeller-clock for more
10
* This program is free software: you can redistribute it and/or modify
11
* it under the terms of the GNU Lesser General Public License as published
12
* by the Free Software Foundation, either version 3 of the License, or
13
* (at your option) any later version.
15
* This program is distributed in the hope that it will be useful,
16
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
* GNU Lesser General Public License for more details.
20
* You should have received a copy of the GNU Lesser General Public License
21
* along with this program. If not, see <http://www.gnu.org/licenses/>.
24
//_____________________________________________________________________________
28
// when non-zero, the time (in microseconds) of a new fan pulse that
29
// has just occurred, which means that segment drawing needs to be
31
static unsigned long new_pulse_at = 0;
33
// the time (in microseconds) when the last fan pulse occurred
34
static unsigned long last_pulse_at = 0;
36
// duration (in microseconds) that a segment should be displayed
37
static unsigned long segment_step = 0;
39
// remainder after divisor and a tally of the remainders for each segment
40
static unsigned long segment_step_sub_step = 0;
41
static unsigned long segment_step_sub = 0;
43
// number of segments in a full display (rotation) is 60 (one per
44
// second) times the desired number of sub-divisions of a second
45
#define NUM_SEGMENTS ( 60 * 5 )
48
//_____________________________________________________________________________
52
// ISR to handle the pulses from the fan's tachiometer
53
void fanPulseHandler()
55
// the fan actually sends two pulses per revolution. These pulses
56
// may not be exactly evenly distributed around the rotation, so
57
// we can't recalculate times on every pulse. Instead, we ignore
58
// every other pulse so timings are based on a complete rotation.
59
static bool ignore = true;
63
// set a new pulse time
64
new_pulse_at = micros();
69
// draw a particular segment
70
void drawNextSegment( bool reset )
72
static unsigned int segment = 0;
73
if( reset ) segment = 0;
76
for( int a = 0; a < 10; a++ )
77
digitalWrite( a + 4, ( ( segment >> a ) & 1 )? HIGH : LOW );
81
// calculate time constants when a new pulse has occurred
82
void calculateSegmentTimes()
84
// check for overflows, and only recalculate times if there isn't
85
// one (if there is, we'll just go with the last pulse's times)
86
if( new_pulse_at > last_pulse_at )
88
// new segment stepping times
89
unsigned long delta = new_pulse_at - last_pulse_at;
90
segment_step = delta / NUM_SEGMENTS;
92
segment_step_sub_step = delta % NUM_SEGMENTS;
95
// now we have dealt with this pulse, save the pulse time and
96
// clear new_pulse_at, ready for the next pulse
97
last_pulse_at = new_pulse_at;
102
// wait until it is time to draw the next segment or a new pulse has
104
void waitTillNextSegment( bool reset )
106
static unsigned long end_time = 0;
110
end_time = last_pulse_at;
112
// work out the time that this segment should be displayed until
113
end_time += segment_step;
114
semgment_step_sub += semgment_step_sub_step;
115
if( semgment_step_sub >= NUM_SEGMENTS ) {
116
semgment_step_sub -= NUM_SEGMENTS;
121
while( micros() < end_time && !new_pulse_at );
128
// set up an interrupt handler on pin 2 to nitice fan pulses
129
attachInterrupt( 0, fanPulseHandler, RISING );
130
digitalWrite( 2, HIGH );
132
// set up output pins (4 to 13) for the led array
133
for( int a = 4; a < 14; a++ )
134
pinMode( a, OUTPUT );
137
Serial.begin( 9600 );
144
// if there has been a new pulse, we'll be resetting the display
145
bool reset = new_pulse_at? true : false;
148
drawNextSegment( reset );
150
// do we need to recalculate segment times?
152
calculateSegmentTimes();
154
// wait till it's time to draw the next segment
155
waitTillNextSegment( reset );