/elec/propeller-clock

To get this branch, use:
bzr branch http://bzr.ed.am/elec/propeller-clock
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
1
/*
27 by edam
updated propeller clock code for arduino-1.0 and fixed a compiler error
2
 * propeller-clock.ino
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
3
 *
27 by edam
updated propeller clock code for arduino-1.0 and fixed a compiler error
4
 * 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
5
 *
6
 * This file is part of propeller-clock (hereafter referred to as "this
29 by edam
corrected URL and removed scematic from src
7
 * 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
8
 * information.
9
 *
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.
14
 *
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.
19
 *
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/>.
22
 */
23
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
24
/******************************************************************************
25
23 by edam
renamed code directories and updated the comments in the code
26
Set up:
27
28
 * a PC fan is wired up to a 12V power supply
29
30
 * the fan's SENSE (tachiometer) pin connected to pin 2 on the
31
   arduino.
32
33
 * the pins 4 to 13 on the arduino should directly drive an LED (the
34
   LED on pin 4 is in the centre of the clock face and the LED on pin
35
   13 is at the outside.
36
37
 * 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
38
   used to indirectly drive a transistor which in turn drives several
39
   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
40
41
 * a button should be attached to pin 3 that grounds it when pressed.
42
35 by edam
initialise from real-time clock; updated Makefile
43
 * A DS1307 remote clock is connected via I2C on analog pins 4 and 5.
44
23 by edam
renamed code directories and updated the comments in the code
45
Implementation details:
46
35 by edam
initialise from real-time clock; updated Makefile
47
 * for a schematic, see ../project/propeller-clock.sch.
23 by edam
renamed code directories and updated the comments in the code
48
49
 * the timing of the drawing of the clock face is recalculated with
50
   every rotation of the propeller.
51
    
52
 * a PC fan actually sends 2 tachiometer pulses per revolution, so the
53
   software skips every other one. This means that the clock may
54
   appear upside-down if started with the propeller in the wrong
55
   position. You will need to experiment to dicsover the position that
56
   the propeller must be in when starting the clock.
57
    
58
Usage instructions:
59
60
 * pressing the button cycles between variations of the current
61
   display mode.
62
  
63
 * pressing and holding the button for a second cycles between display
64
   modes (e.g., analogue and digital).
65
66
 * pressing and holding the button for 5 seconds enters "time set"
67
   mode. In this mode, the following applies:
68
    - the field that is being set flashes
69
    - pressing the button increments the field currently being set
70
    - pressing and holding the button for a second cycles through the
71
      fields that can be set
35 by edam
initialise from real-time clock; updated Makefile
72
    - pressing and holding the button for 5 seconds sets the time and
73
      exits "time set" mode
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
74
75
******************************************************************************/
76
77
78
#include <Bounce.h>
35 by edam
initialise from real-time clock; updated Makefile
79
#include <DS1307.h>
80
#include <Wire.h>
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
81
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
82
//_____________________________________________________________________________
83
//                                                                         data
84
85
86
// when non-zero, the time (in microseconds) of a new fan pulse that
87
// has just occurred, which means that segment drawing needs to be
88
// restarted
11 by Dan
added initial propeller clock code
89
static unsigned long new_pulse_at = 0;
90
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
91
// the time (in microseconds) when the last fan pulse occurred
92
static unsigned long last_pulse_at = 0;
93
94
// duration (in microseconds) that a segment should be displayed
95
static unsigned long segment_step = 0;
96
97
// remainder after divisor and a tally of the remainders for each segment
98
static unsigned long segment_step_sub_step = 0;
99
static unsigned long segment_step_sub = 0;
100
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
101
// flag to indicate that the drawing mode should be cycled to the next one
102
static bool inc_draw_mode = false;
103
104
// a bounce-managed button
105
static Bounce button( 3, 5 );
106
107
// the time
108
static int time_hours = 0;
109
static int time_minutes = 0;
110
static int time_seconds = 0;
111
15 by edam
moved schematic and Makefile to propeller-clock dir and updated Makefile for Arduino Pro Mini w/ Atmel 168 board
112
// number of segments in a full display (rotation) is 60 (one per
113
// second) times the desired number of sub-divisions of a second
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
114
#define NUM_SECOND_SEGMENTS 5
115
#define NUM_SEGMENTS ( 60 * NUM_SECOND_SEGMENTS )
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
116
117
//_____________________________________________________________________________
118
//                                                                         code
119
120
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
121
// check for button presses
122
void checkButtons()
123
{
124
	// update buttons
125
	button.update();
126
127
	// notice button presses
128
	if( button.risingEdge() )
129
		inc_draw_mode = true;
130
}
131
132
133
// keep track of time
134
void trackTime()
135
{
136
	// previous time and any carried-over milliseconds
137
	static unsigned long last_time = millis();
138
	static unsigned long carry = 0;
139
140
	// how many milliseonds have elapsed since we last checked?
141
	unsigned long next_time = millis();
142
	unsigned long delta = next_time - last_time + carry;
143
144
	// update the previous time and carried-over milliseconds
145
	last_time = next_time;
146
	carry = delta % 1000;
147
148
	// add the seconds that have passed to the time
149
	time_seconds += delta / 1000;
150
	while( time_seconds >= 60 ) {
151
		time_seconds -= 60;
152
		time_minutes++;
153
		if( time_minutes >= 60 ) {
154
			time_minutes -= 60;
155
			time_hours++;
156
			if( time_hours >= 24 )
157
				time_hours -= 24;
158
		}
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
159
	}
160
}
161
162
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
163
// draw a segment for the test display
164
void drawNextSegment_test( bool reset )
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
165
{
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
166
	// keep track of segment
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
167
	static unsigned int segment = 0;
168
	if( reset ) segment = 0;
169
	segment++;
170
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
171
	// turn on inside and outside LEDs
172
	digitalWrite( 4, HIGH );
173
	digitalWrite( 13, HIGH );
174
175
	// display segment number in binary across in the inside LEDs,
176
	// with the LED on pin 12 showing the least-significant bit
177
	for( int a = 0; a < 8; a++ )
178
		digitalWrite( 12 - a, ( ( segment >> a ) & 1 )? HIGH : LOW );
179
}
180
181
182
// draw a segment for the time display
183
void drawNextSegment_time( bool reset )
184
{
185
	static unsigned int second = 0;
186
	static unsigned int segment = 0;
187
188
	// handle display reset
189
	if( reset ) {
190
		second = 0;
191
		segment = 0;
192
	}
193
194
	// what needs to be drawn?
17 by Dan
fixed display wdth for hands on clock face
195
	bool draw_tick = !segment && second % 5 == 0;
196
	bool draw_second = !segment && second == time_seconds;
27 by edam
updated propeller clock code for arduino-1.0 and fixed a compiler error
197
	bool draw_minute = !segment && second == time_minutes;
198
	bool draw_hour = !segment && second == time_hours;
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
199
200
	// set the LEDs
201
	digitalWrite( 13, HIGH );
202
	digitalWrite( 12, draw_tick || draw_minute );
203
	for( int a = 10; a <= 11; a++ )
204
		digitalWrite( a, draw_minute || draw_second );
205
	for( int a = 4; a <= 9; a++ )
206
		digitalWrite( 10, draw_minute | draw_second || draw_hour );
207
208
	// inc position
209
	if( ++segment >= NUM_SECOND_SEGMENTS ) {
210
		segment = 0;
211
		second++;
212
	}
213
}
214
215
216
// draw a display segment
217
void drawNextSegment( bool reset )
218
{
219
	static int draw_mode = 0;
220
221
 	// handle mode switch requests
222
	if( reset && inc_draw_mode ) {
223
		inc_draw_mode = false;
224
		draw_mode++;
225
		if( draw_mode >= 2 )
226
			draw_mode = 0;
227
	}
228
229
	// draw the segment
230
	switch( draw_mode ) {
231
	case 0: drawNextSegment_test( reset ); break;
232
	case 1: drawNextSegment_time( reset ); break;
233
	}
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
234
}
235
236
237
// calculate time constants when a new pulse has occurred
238
void calculateSegmentTimes()
239
{
240
	// check for overflows, and only recalculate times if there isn't
241
	// one (if there is, we'll just go with the last pulse's times)
242
	if( new_pulse_at > last_pulse_at )
243
	{
244
		// new segment stepping times
245
		unsigned long delta = new_pulse_at - last_pulse_at;
246
		segment_step = delta / NUM_SEGMENTS;
247
		segment_step_sub = 0;
248
		segment_step_sub_step = delta % NUM_SEGMENTS;
249
	}
250
251
	// now we have dealt with this pulse, save the pulse time and
252
	// clear new_pulse_at, ready for the next pulse
253
	last_pulse_at = new_pulse_at;
254
	new_pulse_at = 0;
255
}
256
257
258
// wait until it is time to draw the next segment or a new pulse has
259
// occurred
260
void waitTillNextSegment( bool reset )
261
{
262
	static unsigned long end_time = 0;
263
264
	// handle reset
265
	if( reset )
266
		end_time = last_pulse_at;
267
268
	// work out the time that this segment should be displayed until
269
	end_time += segment_step;
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
270
	segment_step_sub += segment_step_sub_step;
271
	if( segment_step_sub >= NUM_SEGMENTS ) {
272
		segment_step_sub -= NUM_SEGMENTS;
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
273
		end_time++;
274
	}
275
276
	// wait
277
	while( micros() < end_time && !new_pulse_at );
278
}
279
280
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
281
// ISR to handle the pulses from the fan's tachiometer
282
void fanPulseHandler()
283
{
284
	// the fan actually sends two pulses per revolution. These pulses
285
	// may not be exactly evenly distributed around the rotation, so
286
	// we can't recalculate times on every pulse. Instead, we ignore
287
	// every other pulse so timings are based on a complete rotation.
288
	static bool ignore = true;
289
	ignore = !ignore;
290
	if( !ignore )
291
	{
292
		// set a new pulse time
293
		new_pulse_at = micros();
294
	}
295
}
296
297
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
298
// main setup
299
void setup()
300
{
301
	// set up an interrupt handler on pin 2 to nitice fan pulses
302
	attachInterrupt( 0, fanPulseHandler, RISING );
303
	digitalWrite( 2, HIGH );
304
  
305
	// set up output pins (4 to 13) for the led array
306
	for( int a = 4; a < 14; a++ )
307
		pinMode( a, OUTPUT );
308
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
309
	// set up mode-switch button on pin 3
310
	pinMode( 3, INPUT );
311
35 by edam
initialise from real-time clock; updated Makefile
312
	// get the time from the real-time clock
313
	int rtc_data[ 7 ];
314
	RTC.get( rtc_data, true );
315
	time_hours = rtc_data[ DS1307_HR ];
316
	time_minutes = rtc_data[ DS1307_MIN ];
317
	time_seconds = rtc_data[ DS1307_SEC ];
318
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
319
	// serial comms
320
	Serial.begin( 9600 );
321
}
322
323
324
// main loop
11 by Dan
added initial propeller clock code
325
void loop()
326
{
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
327
	// if there has been a new pulse, we'll be resetting the display
328
	bool reset = new_pulse_at? true : false;
329
16 by edam
finished first revision of propeller-clock code (can display clock and test); added Bounce library
330
	// only do this stuff at the start of a display cycle, to ensure
331
	// that no state changes mid-display
332
	if( reset )
333
	{
334
		// check buttons
335
		checkButtons();
336
337
		// keep track of time
338
		trackTime();
339
	}
340
13 by edam
updated propeller-clock code, added GPL text and renamed fan-test
341
	// draw this segment
342
	drawNextSegment( reset );
343
344
	// do we need to recalculate segment times?
345
	if( reset )
346
		calculateSegmentTimes();
347
348
	// wait till it's time to draw the next segment
349
	waitTillNextSegment( reset );
11 by Dan
added initial propeller clock code
350
}