/elec/propeller-clock

To get this branch, use:
bzr branch http://bzr.ed.am/elec/propeller-clock

« back to all changes in this revision

Viewing changes to src/propeller-clock.cc

  • Committer: Tim Marston
  • Date: 2012-03-10 01:01:54 UTC
  • Revision ID: tim@ed.am-20120310010154-lv041mt4275k5jxo
removed most OOP/inheritance crap, saved loads of space!

Show diffs side-by-side

added added

removed removed

1
 
/* -*- mode: c++; compile-command: "make"; -*- */
 
1
/* -*- mode: c++; compile-command: "BOARD=pro5v make"; -*- */
2
2
/*
3
3
 * propeller-clock.ino
4
4
 *
28
28
 
29
29
 * a PC fan is wired up to a 12V power supply
30
30
 
31
 
 * the fan's SENSE (tachometer) pin connected to pin 2 on the
32
 
   Arduino.
 
31
 * the fan's SENSE (tachiometer) pin connected to pin 2 on the
 
32
   arduino.
33
33
 
34
 
 * the pins 4 to 13 on the Arduino should directly drive an LED (the
 
34
 * the pins 4 to 13 on the arduino should directly drive an LED (the
35
35
   LED on pin 4 is in the centre of the clock face and the LED on pin
36
36
   13 is at the outside.
37
37
 
38
38
 * if a longer hand (and a larger clock face) is desired, pin 4 can be
39
39
   used to indirectly drive a transistor which in turn drives several
40
 
   LEDs that turn on and off in unison in the centre of the clock.
 
40
   LEDs that turn on anf off in unison in the centre of the clock.
41
41
 
42
42
 * a button should be attached to pin 3 that grounds it when pressed.
43
43
 
44
 
 * A DS1307 remote clock is connected via I2C on analogue pins 4 and 5.
 
44
 * A DS1307 remote clock is connected via I2C on analog pins 4 and 5.
45
45
 
46
46
Implementation details:
47
47
 
50
50
 * the timing of the drawing of the clock face is recalculated with
51
51
   every rotation of the propeller.
52
52
    
53
 
 * a PC fan actually sends 2 tachometer pulses per revolution, so the
 
53
 * a PC fan actually sends 2 tachiometer pulses per revolution, so the
54
54
   software skips every other one. This means that the clock may
55
55
   appear upside-down if started with the propeller in the wrong
56
 
   position. You will need to experiment to discover the position that
 
56
   position. You will need to experiment to dicsover the position that
57
57
   the propeller must be in when starting the clock.
58
58
    
59
59
Usage instructions:
79
79
#include "button.h"
80
80
#include "time.h"
81
81
#include "Arduino.h"
82
 
#include "modes/switcher_major_mode.h"
83
 
#include "modes/settings_major_mode.h"
84
 
#include "modes/analogue_clock_mode.h"
85
 
#include "modes/digital_clock_mode.h"
86
 
#include "modes/info_mode.h"
87
 
#include "modes/test_pattern_mode.h"
88
 
#include "text.h"
89
 
#include "text_renderer.h"
90
 
#include "common.h"
 
82
#include "analogue_clock.h"
 
83
#include "digital_clock.h"
 
84
#include "test_pattern.h"
91
85
 
92
86
//_____________________________________________________________________________
93
87
//                                                                         data
110
104
// the button
111
105
static Button _button( 3 );
112
106
 
113
 
// major modes
114
 
static MajorMode *_modes[ 3 ];
115
 
 
116
 
// current major mode
117
 
static int _mode = 0;
118
 
 
119
 
// interupt handler's "ignore every other" flag
120
 
static bool _pulse_ignore = true;
 
107
// modes
 
108
static int _major_mode = 0;
 
109
static int _minor_mode = 0;
 
110
 
 
111
#define MAIN_MODE_IDX 0
 
112
 
 
113
#define ANALOGUE_CLOCK_IDX 0
 
114
#define DIGITAL_CLOCK_IDX 1
 
115
#define TEST_PATTERN_IDX 2
121
116
 
122
117
//_____________________________________________________________________________
123
118
//                                                                         code
124
119
 
 
120
 
 
121
// activate the current minor mode
 
122
void activate_minor_mode()
 
123
{
 
124
        switch( _minor_mode ) {
 
125
        case DIGITAL_CLOCK_IDX: digital_clock_activate(); break;
 
126
        }
 
127
}
 
128
 
125
129
// perform button events
126
130
void do_button_events()
127
131
{
132
136
                {
133
137
                case 1:
134
138
                        // short press
135
 
                        _modes[ _mode ]->press();
 
139
                        switch( _major_mode ) {
 
140
                        case MAIN_MODE_IDX:
 
141
                                switch( _minor_mode ) {
 
142
                                case ANALOGUE_CLOCK_IDX: analogue_clock_press(); break;
 
143
                                case DIGITAL_CLOCK_IDX: digital_clock_press(); break;
 
144
                                }
 
145
                                break;
 
146
                        }
136
147
                        break;
 
148
 
137
149
                case 2:
138
150
                        // long press
139
 
                        _modes[ _mode ]->long_press();
 
151
                        switch( _major_mode ) {
 
152
                        case MAIN_MODE_IDX:
 
153
                                if( ++_minor_mode >= 3 )
 
154
                                        _minor_mode = 0;
 
155
                                switch( _minor_mode ) {
 
156
                                case DIGITAL_CLOCK_IDX: digital_clock_activate(); break;
 
157
                                }
 
158
                                break;
 
159
                        }
140
160
                        break;
 
161
 
141
162
                case 3:
142
163
                        // looooong press (change major mode)
143
 
                        _modes[ _mode ]->deactivate();
144
 
                        if( !_modes[ ++_mode ] ) _mode = 0;
145
 
                        _modes[ _mode ]->activate();
146
 
                        break;
147
 
                case 4:
148
 
                        // switch display upside-down
149
 
                        _pulse_ignore = !_pulse_ignore;
 
164
                        if( ++_major_mode > 0 )
 
165
                                _major_mode = 0;
 
166
                        switch( _major_mode ) {
 
167
                        case MAIN_MODE_IDX: _minor_mode = 0; break;
 
168
                        }
 
169
                        activate_minor_mode();
150
170
                        break;
151
171
                }
152
172
        }
157
177
void draw_next_segment( bool reset )
158
178
{
159
179
        // keep track of segment
160
 
        static int segment = 0;
161
180
#if CLOCK_FORWARD
 
181
        static int segment = ( NUM_SEGMENTS - CLOCK_SHIFT ) % NUM_SEGMENTS;
162
182
        if( reset ) segment = ( NUM_SEGMENTS - CLOCK_SHIFT ) % NUM_SEGMENTS;
163
183
#else
 
184
        static int segment = NUM_SEGMENTS - 1 - CLOCK_SHIFT;
164
185
        if( reset ) segment = NUM_SEGMENTS - 1 - CLOCK_SHIFT;
165
186
#endif
166
187
 
167
 
        // reset the text renderer's buffer
168
 
        TextRenderer::reset_buffer();
169
 
 
170
 
        if( reset )
171
 
        {
172
 
                _modes[ _mode ]->draw_reset();
173
 
 
174
 
                // tell the text services we're starting a new frame
175
 
                Text::draw_reset();
176
 
        }
177
 
 
178
188
        // draw
179
 
        _modes[ _mode ]->draw( segment );
180
 
 
181
 
        // draw text
182
 
        Text::draw( segment );
183
 
 
184
 
        // draw text rednerer's buffer
185
 
        TextRenderer::output_buffer();
 
189
        switch( _major_mode ) {
 
190
        case MAIN_MODE_IDX:
 
191
                switch( _minor_mode ) {
 
192
                case ANALOGUE_CLOCK_IDX: analogue_clock_draw( segment ); break;
 
193
                case DIGITAL_CLOCK_IDX: digital_clock_draw( segment ); break;
 
194
                case TEST_PATTERN_IDX: test_pattern_draw( segment ); break;
 
195
                }
 
196
                break;
 
197
        }
186
198
 
187
199
#if CLOCK_FORWARD
188
200
        if( ++segment >= NUM_SEGMENTS ) segment = 0;
236
248
}
237
249
 
238
250
 
239
 
// ISR to handle the pulses from the fan's tachometer
 
251
// ISR to handle the pulses from the fan's tachiometer
240
252
void fan_pulse_handler()
241
253
{
242
254
        // the fan actually sends two pulses per revolution. These pulses
243
255
        // may not be exactly evenly distributed around the rotation, so
244
256
        // we can't recalculate times on every pulse. Instead, we ignore
245
257
        // every other pulse so timings are based on a complete rotation.
246
 
        _pulse_ignore = !_pulse_ignore;
247
 
        if( !_pulse_ignore )
 
258
        static bool ignore = true;
 
259
        ignore = !ignore;
 
260
        if( !ignore )
248
261
        {
249
262
                // set a new pulse time
250
263
                _new_pulse_at = micros();
255
268
// main setup
256
269
void setup()
257
270
{
258
 
        // set up an interrupt handler on pin 2 to notice fan pulses
 
271
        // set up an interrupt handler on pin 2 to nitice fan pulses
259
272
        attachInterrupt( 0, fan_pulse_handler, RISING );
260
273
        digitalWrite( 2, HIGH );
261
274
  
266
279
        // set up mode-switch button on pin 3
267
280
        pinMode( 3, INPUT );
268
281
        digitalWrite( 3, HIGH );
269
 
        static int event_times[] = { 10, 500, 2000, 4000, 0 };
 
282
        static int event_times[] = { 5, 500, 4000, 0 };
270
283
        _button.set_event_times( event_times );
271
284
 
272
 
        // initialise RTC
273
 
        Time::load_time();
274
 
 
275
 
        // init text renderer
276
 
        TextRenderer::init();
277
 
 
278
 
        // reset text
279
 
        Text::reset();
280
 
        leds_off();
281
 
 
282
 
        static SwitcherMajorMode switcher;
283
 
        static SettingsMajorMode settings( _button );
284
 
 
285
 
        // add major modes
286
 
        int mode = 0;
287
 
        _modes[ mode++ ] = &switcher;
288
 
        _modes[ mode++ ] = &settings;
289
 
        _modes[ mode ] = 0;
290
 
 
291
 
        // activate the current major mode
292
 
        _modes[ _mode ]->activate();
 
285
        // activate the minor mode
 
286
        switch( _major_mode ) {
 
287
        case MAIN_MODE_IDX: activate_minor_mode(); break;
 
288
        }
293
289
}
294
290
 
295
291
 
310
306
                calculate_segment_times();
311
307
 
312
308
                // keep track of time
313
 
                Time::update();
 
309
                Time &time = Time::get_instance();
 
310
                time.update();
314
311
 
315
312
                // perform button events
316
313
                do_button_events();