/elec/propeller-clock

To get this branch, use:
bzr branch http://bzr.ed.am/elec/propeller-clock
43 by edam
added phantom button press test and temporarily disabled pin 4 (that drives the PNP transistor)
1
/* -*- mode: c++; compile-command: "BOARD=pro5v make"; -*- */
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
2
/*
27 by edam
updated propeller clock code for arduino-1.0 and fixed a compiler error
3
 * propeller-clock.ino
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
4
 *
27 by edam
updated propeller clock code for arduino-1.0 and fixed a compiler error
5
 * Copyright (C) 2011 Tim Marston <tim@ed.am> and Dan Marston.
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
6
 *
7
 * This file is part of propeller-clock (hereafter referred to as "this
29 by edam
corrected URL and removed scematic from src
8
 * program"). See http://ed.am/dev/software/arduino/propeller-clock for more
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
9
 * information.
10
 *
11
 * This program is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU Lesser General Public License as published
13
 * by the Free Software Foundation, either version 3 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU Lesser General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Lesser General Public License
22
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23
 */
24
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
25
/******************************************************************************
26
23 by edam
renamed code directories and updated the comments in the code
27
Set up:
28
29
 * a PC fan is wired up to a 12V power supply
30
31
 * the fan's SENSE (tachiometer) pin connected to pin 2 on the
32
   arduino.
33
34
 * the pins 4 to 13 on the arduino should directly drive an LED (the
35
   LED on pin 4 is in the centre of the clock face and the LED on pin
36
   13 is at the outside.
37
38
 * if a longer hand (and a larger clock face) is desired, pin 4 can be
35 by edam
initialise from real-time clock; updated Makefile
39
   used to indirectly drive a transistor which in turn drives several
40
   LEDs that turn on anf off in unison in the centre of the clock.
23 by edam
renamed code directories and updated the comments in the code
41
42
 * a button should be attached to pin 3 that grounds it when pressed.
43
35 by edam
initialise from real-time clock; updated Makefile
44
 * A DS1307 remote clock is connected via I2C on analog pins 4 and 5.
45
23 by edam
renamed code directories and updated the comments in the code
46
Implementation details:
47
35 by edam
initialise from real-time clock; updated Makefile
48
 * for a schematic, see ../project/propeller-clock.sch.
23 by edam
renamed code directories and updated the comments in the code
49
50
 * the timing of the drawing of the clock face is recalculated with
51
   every rotation of the propeller.
52
    
53
 * a PC fan actually sends 2 tachiometer pulses per revolution, so the
54
   software skips every other one. This means that the clock may
55
   appear upside-down if started with the propeller in the wrong
56
   position. You will need to experiment to dicsover the position that
57
   the propeller must be in when starting the clock.
58
    
59
Usage instructions:
60
61
 * pressing the button cycles between variations of the current
62
   display mode.
63
  
64
 * pressing and holding the button for a second cycles between display
65
   modes (e.g., analogue and digital).
66
67
 * pressing and holding the button for 5 seconds enters "time set"
68
   mode. In this mode, the following applies:
69
    - the field that is being set flashes
70
    - pressing the button increments the field currently being set
71
    - pressing and holding the button for a second cycles through the
72
      fields that can be set
35 by edam
initialise from real-time clock; updated Makefile
73
    - pressing and holding the button for 5 seconds sets the time and
74
      exits "time set" mode
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
75
76
******************************************************************************/
77
58 by edam
removed Bounce library and updated/fixed new code
78
#include "button.h"
56 by edam
updated software to include drawing abstraction infrastructure
79
#include "config.h"
80
#include "time.h"
81
#include "mode_switcher.h"
82
#include "drawer.h"
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
83
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
84
//_____________________________________________________________________________
85
//                                                                         data
86
87
88
// when non-zero, the time (in microseconds) of a new fan pulse that
89
// has just occurred, which means that segment drawing needs to be
90
// restarted
11 by Dan
added initial propeller clock code
91
static unsigned long new_pulse_at = 0;
92
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
93
// the time (in microseconds) when the last fan pulse occurred
94
static unsigned long last_pulse_at = 0;
95
96
// duration (in microseconds) that a segment should be displayed
97
static unsigned long segment_step = 0;
98
99
// remainder after divisor and a tally of the remainders for each segment
100
static unsigned long segment_step_sub_step = 0;
101
static unsigned long segment_step_sub = 0;
102
58 by edam
removed Bounce library and updated/fixed new code
103
// the button
56 by edam
updated software to include drawing abstraction infrastructure
104
static Button button( 3 );
54 by edam
changed 12-tick to a double tick, added CLOCK_SHIFT to align face and fixed hour-hand
105
58 by edam
removed Bounce library and updated/fixed new code
106
// major mode
107
static int major_mode = 0;
108
109
// major modes
110
static std::vector< MajorMode * > major_modes;
111
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
112
//_____________________________________________________________________________
113
//                                                                         code
114
115
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
116
// check for button presses
117
void checkButtons()
118
{
119
	// update buttons
56 by edam
updated software to include drawing abstraction infrastructure
120
	int event = button.update();
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
121
56 by edam
updated software to include drawing abstraction infrastructure
122
	// handle any events
123
	switch( event ) {
124
	case 1:
58 by edam
removed Bounce library and updated/fixed new code
125
		major_modes[ major_mode ]->short_press();
126
		break;
127
	case 2:
128
		major_modes[ major_mode ]->long_press();
129
		break;
130
	case 3:
131
		if( ++major_mode >= major_modes.size() )
132
			major_mode = 0;
133
		major_modes[ major_mode ]->activate();
56 by edam
updated software to include drawing abstraction infrastructure
134
		break;
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
135
	}
136
}
137
138
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
139
// draw a display segment
140
void drawNextSegment( bool reset )
141
{
53 by edam
conrtol segment number from one place and reverse the order the segments are drawn (backwards clock!)
142
	// keep track of segment
143
#if CLOCK_FORWARD
54 by edam
changed 12-tick to a double tick, added CLOCK_SHIFT to align face and fixed hour-hand
144
	static int segment = ( NUM_SEGMENTS - CLOCK_SHIFT ) % NUM_SEGMENTS;
145
	if( reset ) segment = ( NUM_SEGMENTS - CLOCK_SHIFT ) % NUM_SEGMENTS;
53 by edam
conrtol segment number from one place and reverse the order the segments are drawn (backwards clock!)
146
#else
54 by edam
changed 12-tick to a double tick, added CLOCK_SHIFT to align face and fixed hour-hand
147
	static int segment = NUM_SEGMENTS - 1 - CLOCK_SHIFT;
148
	if( reset ) segment = NUM_SEGMENTS - 1 - CLOCK_SHIFT;
53 by edam
conrtol segment number from one place and reverse the order the segments are drawn (backwards clock!)
149
#endif
150
56 by edam
updated software to include drawing abstraction infrastructure
151
	// draw
58 by edam
removed Bounce library and updated/fixed new code
152
	Drawer &drawer = major_modes[ major_mode ]->get_drawer();
56 by edam
updated software to include drawing abstraction infrastructure
153
	if( reset ) drawer.draw_reset();
154
	drawer.draw( segment );
53 by edam
conrtol segment number from one place and reverse the order the segments are drawn (backwards clock!)
155
156
#if CLOCK_FORWARD
54 by edam
changed 12-tick to a double tick, added CLOCK_SHIFT to align face and fixed hour-hand
157
	if( ++segment >= NUM_SEGMENTS ) segment = 0;
53 by edam
conrtol segment number from one place and reverse the order the segments are drawn (backwards clock!)
158
#else
54 by edam
changed 12-tick to a double tick, added CLOCK_SHIFT to align face and fixed hour-hand
159
	if( --segment < 0 ) segment = NUM_SEGMENTS - 1;
53 by edam
conrtol segment number from one place and reverse the order the segments are drawn (backwards clock!)
160
#endif
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
161
}
162
163
164
// calculate time constants when a new pulse has occurred
165
void calculateSegmentTimes()
166
{
167
	// check for overflows, and only recalculate times if there isn't
168
	// one (if there is, we'll just go with the last pulse's times)
169
	if( new_pulse_at > last_pulse_at )
170
	{
171
		// new segment stepping times
172
		unsigned long delta = new_pulse_at - last_pulse_at;
173
		segment_step = delta / NUM_SEGMENTS;
174
		segment_step_sub = 0;
175
		segment_step_sub_step = delta % NUM_SEGMENTS;
176
	}
177
178
	// now we have dealt with this pulse, save the pulse time and
179
	// clear new_pulse_at, ready for the next pulse
180
	last_pulse_at = new_pulse_at;
181
	new_pulse_at = 0;
182
}
183
184
185
// wait until it is time to draw the next segment or a new pulse has
186
// occurred
187
void waitTillNextSegment( bool reset )
188
{
189
	static unsigned long end_time = 0;
190
191
	// handle reset
192
	if( reset )
193
		end_time = last_pulse_at;
194
195
	// work out the time that this segment should be displayed until
196
	end_time += segment_step;
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
197
	segment_step_sub += segment_step_sub_step;
198
	if( segment_step_sub >= NUM_SEGMENTS ) {
199
		segment_step_sub -= NUM_SEGMENTS;
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
200
		end_time++;
201
	}
202
203
	// wait
204
	while( micros() < end_time && !new_pulse_at );
205
}
206
207
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
208
// ISR to handle the pulses from the fan's tachiometer
209
void fanPulseHandler()
210
{
211
	// the fan actually sends two pulses per revolution. These pulses
212
	// may not be exactly evenly distributed around the rotation, so
213
	// we can't recalculate times on every pulse. Instead, we ignore
214
	// every other pulse so timings are based on a complete rotation.
215
	static bool ignore = true;
216
	ignore = !ignore;
217
	if( !ignore )
218
	{
219
		// set a new pulse time
220
		new_pulse_at = micros();
221
	}
222
}
223
224
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
225
// main setup
226
void setup()
227
{
228
	// set up an interrupt handler on pin 2 to nitice fan pulses
229
	attachInterrupt( 0, fanPulseHandler, RISING );
230
	digitalWrite( 2, HIGH );
231
  
232
	// set up output pins (4 to 13) for the led array
233
	for( int a = 4; a < 14; a++ )
234
		pinMode( a, OUTPUT );
235
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
236
	// set up mode-switch button on pin 3
237
	pinMode( 3, INPUT );
42 by edam
lengthened button debounce time and turned on the pull-up resistor (oops!)
238
	digitalWrite( 3, HIGH );
56 by edam
updated software to include drawing abstraction infrastructure
239
	button.add_event_at( 5, 1 );
240
	button.add_event_at( 1000, 2 );
241
	button.add_event_at( 4000, 3 );
35 by edam
initialise from real-time clock; updated Makefile
242
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
243
	// serial comms
244
	Serial.begin( 9600 );
58 by edam
removed Bounce library and updated/fixed new code
245
246
	// set up major modes
247
	static ModeSwitcher mode_switcher;
248
	major_modes.push_back( &mode_switcher );
249
	major_modes[ 0 ]->activate();
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
250
}
251
252
253
// main loop
11 by Dan
added initial propeller clock code
254
void loop()
255
{
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
256
	// if there has been a new pulse, we'll be resetting the display
257
	bool reset = new_pulse_at? true : false;
258
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
259
	// only do this stuff at the start of a display cycle, to ensure
260
	// that no state changes mid-display
261
	if( reset )
262
	{
263
		// check buttons
264
		checkButtons();
265
266
		// keep track of time
56 by edam
updated software to include drawing abstraction infrastructure
267
		Time &time = Time::get_instance();
268
		time.update();
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
269
	}
270
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
271
	// draw this segment
272
	drawNextSegment( reset );
273
274
	// do we need to recalculate segment times?
275
	if( reset )
276
		calculateSegmentTimes();
277
278
	// wait till it's time to draw the next segment
279
	waitTillNextSegment( reset );
11 by Dan
added initial propeller clock code
280
}