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 | ||
| 68
by Tim Marston added frame reset code and inited minor mode flavours on mode activation | 31 | * the fan's SENSE (tachometer) pin connected to pin 2 on the | 
| 32 | Arduino. | |
| 23
by edam renamed code directories and updated the comments in the code | 33 | |
| 68
by Tim Marston added frame reset code and inited minor mode flavours on mode activation | 34 | * the pins 4 to 13 on the Arduino should directly drive an LED (the | 
| 23
by edam renamed code directories and updated the comments in the code | 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 | 
| 68
by Tim Marston added frame reset code and inited minor mode flavours on mode activation | 40 | LEDs that turn on and 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 | ||
| 68
by Tim Marston added frame reset code and inited minor mode flavours on mode activation | 44 | * A DS1307 remote clock is connected via I2C on analogue pins 4 and 5. | 
| 35
by edam initialise from real-time clock; updated Makefile | 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 | ||
| 68
by Tim Marston added frame reset code and inited minor mode flavours on mode activation | 53 | * a PC fan actually sends 2 tachometer pulses per revolution, so the | 
| 23
by edam renamed code directories and updated the comments in the code | 54 | software skips every other one. This means that the clock may | 
| 55 | appear upside-down if started with the propeller in the wrong | |
| 68
by Tim Marston added frame reset code and inited minor mode flavours on mode activation | 56 | position. You will need to experiment to discover the position that | 
| 23
by edam renamed code directories and updated the comments in the code | 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 | ||
| 59
by edam removed ulibc, fixed button, added text rendering | 78 | #include "config.h" | 
| 58
by edam removed Bounce library and updated/fixed new code | 79 | #include "button.h" | 
| 56
by edam updated software to include drawing abstraction infrastructure | 80 | #include "time.h" | 
| 64
by Tim Marston added support for eclipse project and converted to a manual Makefile | 81 | #include "Arduino.h" | 
| 87
by edam switched back to using classes for modes | 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" | |
| 82
by Tim Marston added info mode, a '/' to the fonts and cleaned up a couple of bits | 86 | #include "modes/info_mode.h" | 
| 87
by edam switched back to using classes for modes | 87 | #include "modes/test_pattern_mode.h" | 
| 71
by Tim Marston added time set mode, made text renderer's buffer auto reset/output | 88 | #include "text.h" | 
| 89 | #include "text_renderer.h" | |
| 90 | #include "common.h" | |
| 16
by edam finished first revision of propeller-clock code (can display clock and test); added Bounce library | 91 | |
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 92 | //_____________________________________________________________________________ | 
| 93 | // data | |
| 94 | ||
| 95 | // when non-zero, the time (in microseconds) of a new fan pulse that | |
| 96 | // has just occurred, which means that segment drawing needs to be | |
| 97 | // restarted | |
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 98 | static unsigned long _new_pulse_at = 0; | 
| 11
by Dan added initial propeller clock code | 99 | |
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 100 | // the time (in microseconds) when the last fan pulse occurred | 
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 101 | static unsigned long _last_pulse_at = 0; | 
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 102 | |
| 103 | // duration (in microseconds) that a segment should be displayed | |
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 104 | static unsigned long _segment_step = 0; | 
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 105 | |
| 106 | // remainder after divisor and a tally of the remainders for each segment | |
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 107 | static unsigned long _segment_step_sub_step = 0; | 
| 108 | static unsigned long _segment_step_sub = 0; | |
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 109 | |
| 58
by edam removed Bounce library and updated/fixed new code | 110 | // the button | 
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 111 | static Button _button( 3 ); | 
| 112 | ||
| 87
by edam switched back to using classes for modes | 113 | // major modes | 
| 114 | static MajorMode *_modes[ 3 ]; | |
| 115 | ||
| 116 | // current major mode | |
| 117 | static int _mode = 0; | |
| 58
by edam removed Bounce library and updated/fixed new code | 118 | |
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 119 | //_____________________________________________________________________________ | 
| 120 | // code | |
| 121 | ||
| 59
by edam removed ulibc, fixed button, added text rendering | 122 | // perform button events | 
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 123 | void do_button_events() | 
| 16
by edam finished first revision of propeller-clock code (can display clock and test); added Bounce library | 124 | {
 | 
| 59
by edam removed ulibc, fixed button, added text rendering | 125 | // loop through pending events | 
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 126 | while( int event = _button.get_event() ) | 
| 59
by edam removed ulibc, fixed button, added text rendering | 127 | 	{
 | 
| 128 | switch( event ) | |
| 129 | 		{
 | |
| 130 | case 1: | |
| 131 | // short press | |
| 87
by edam switched back to using classes for modes | 132 | _modes[ _mode ]->press(); | 
| 59
by edam removed ulibc, fixed button, added text rendering | 133 | break; | 
| 134 | case 2: | |
| 135 | // long press | |
| 87
by edam switched back to using classes for modes | 136 | _modes[ _mode ]->long_press(); | 
| 59
by edam removed ulibc, fixed button, added text rendering | 137 | break; | 
| 138 | case 3: | |
| 139 | // looooong press (change major mode) | |
| 87
by edam switched back to using classes for modes | 140 | _modes[ _mode ]->deactivate(); | 
| 141 | if( !_modes[ ++_mode ] ) _mode = 0; | |
| 142 | _modes[ _mode ]->activate(); | |
| 59
by edam removed ulibc, fixed button, added text rendering | 143 | break; | 
| 144 | } | |
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 145 | } | 
| 146 | } | |
| 147 | ||
| 148 | ||
| 16
by edam finished first revision of propeller-clock code (can display clock and test); added Bounce library | 149 | // draw a display segment | 
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 150 | void draw_next_segment( bool reset ) | 
| 16
by edam finished first revision of propeller-clock code (can display clock and test); added Bounce library | 151 | {
 | 
| 53
by edam conrtol segment number from one place and reverse the order the segments are drawn (backwards clock!) | 152 | // keep track of segment | 
| 153 | #if CLOCK_FORWARD | |
| 54
by edam changed 12-tick to a double tick, added CLOCK_SHIFT to align face and fixed hour-hand | 154 | static int segment = ( NUM_SEGMENTS - CLOCK_SHIFT ) % NUM_SEGMENTS; | 
| 155 | 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!) | 156 | #else | 
| 54
by edam changed 12-tick to a double tick, added CLOCK_SHIFT to align face and fixed hour-hand | 157 | static int segment = NUM_SEGMENTS - 1 - CLOCK_SHIFT; | 
| 158 | 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!) | 159 | #endif | 
| 160 | ||
| 87
by edam switched back to using classes for modes | 161 | // reset the text renderer's buffer | 
| 71
by Tim Marston added time set mode, made text renderer's buffer auto reset/output | 162 | TextRenderer::reset_buffer(); | 
| 163 | ||
| 87
by edam switched back to using classes for modes | 164 | if( reset ) | 
| 165 | 	{
 | |
| 166 | _modes[ _mode ]->draw_reset(); | |
| 71
by Tim Marston added time set mode, made text renderer's buffer auto reset/output | 167 | |
| 168 | // tell the text services we're starting a new frame | |
| 169 | Text::draw_reset(); | |
| 68
by Tim Marston added frame reset code and inited minor mode flavours on mode activation | 170 | } | 
| 171 | ||
| 56
by edam updated software to include drawing abstraction infrastructure | 172 | // draw | 
| 87
by edam switched back to using classes for modes | 173 | _modes[ _mode ]->draw( segment ); | 
| 174 | ||
| 175 | // TODO: remove this hack | |
| 86
by Tim Marston various tweaks, a (failed) attempt to fix text reset bug and added TODO | 176 | Text::post_draw(); | 
| 53
by edam conrtol segment number from one place and reverse the order the segments are drawn (backwards clock!) | 177 | |
| 87
by edam switched back to using classes for modes | 178 | // draw text rednerer's buffer | 
| 71
by Tim Marston added time set mode, made text renderer's buffer auto reset/output | 179 | TextRenderer::output_buffer(); | 
| 180 | ||
| 53
by edam conrtol segment number from one place and reverse the order the segments are drawn (backwards clock!) | 181 | #if CLOCK_FORWARD | 
| 54
by edam changed 12-tick to a double tick, added CLOCK_SHIFT to align face and fixed hour-hand | 182 | 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!) | 183 | #else | 
| 54
by edam changed 12-tick to a double tick, added CLOCK_SHIFT to align face and fixed hour-hand | 184 | 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!) | 185 | #endif | 
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 186 | } | 
| 187 | ||
| 188 | ||
| 189 | // calculate time constants when a new pulse has occurred | |
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 190 | void calculate_segment_times() | 
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 191 | {
 | 
| 192 | // check for overflows, and only recalculate times if there isn't | |
| 193 | // one (if there is, we'll just go with the last pulse's times) | |
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 194 | if( _new_pulse_at > _last_pulse_at ) | 
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 195 | 	{
 | 
| 196 | // new segment stepping times | |
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 197 | unsigned long delta = _new_pulse_at - _last_pulse_at; | 
| 198 | _segment_step = delta / NUM_SEGMENTS; | |
| 199 | _segment_step_sub = 0; | |
| 200 | _segment_step_sub_step = delta % NUM_SEGMENTS; | |
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 201 | } | 
| 202 | ||
| 203 | // now we have dealt with this pulse, save the pulse time and | |
| 204 | // clear new_pulse_at, ready for the next pulse | |
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 205 | _last_pulse_at = _new_pulse_at; | 
| 206 | _new_pulse_at = 0; | |
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 207 | } | 
| 208 | ||
| 209 | ||
| 210 | // wait until it is time to draw the next segment or a new pulse has | |
| 211 | // occurred | |
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 212 | void wait_till_end_of_segment( bool reset ) | 
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 213 | {
 | 
| 214 | static unsigned long end_time = 0; | |
| 215 | ||
| 216 | // handle reset | |
| 217 | if( reset ) | |
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 218 | end_time = _last_pulse_at; | 
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 219 | |
| 220 | // work out the time that this segment should be displayed until | |
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 221 | end_time += _segment_step; | 
| 222 | _segment_step_sub += _segment_step_sub_step; | |
| 223 | 	if( _segment_step_sub >= NUM_SEGMENTS ) {
 | |
| 224 | _segment_step_sub -= NUM_SEGMENTS; | |
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 225 | end_time++; | 
| 226 | } | |
| 227 | ||
| 228 | // wait | |
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 229 | while( micros() < end_time && !_new_pulse_at ); | 
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 230 | } | 
| 231 | ||
| 232 | ||
| 82
by Tim Marston added info mode, a '/' to the fonts and cleaned up a couple of bits | 233 | // ISR to handle the pulses from the fan's tachometer | 
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 234 | void fan_pulse_handler() | 
| 16
by edam finished first revision of propeller-clock code (can display clock and test); added Bounce library | 235 | {
 | 
| 236 | // the fan actually sends two pulses per revolution. These pulses | |
| 237 | // may not be exactly evenly distributed around the rotation, so | |
| 238 | // we can't recalculate times on every pulse. Instead, we ignore | |
| 239 | // every other pulse so timings are based on a complete rotation. | |
| 240 | static bool ignore = true; | |
| 241 | ignore = !ignore; | |
| 242 | if( !ignore ) | |
| 243 | 	{
 | |
| 244 | // set a new pulse time | |
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 245 | _new_pulse_at = micros(); | 
| 16
by edam finished first revision of propeller-clock code (can display clock and test); added Bounce library | 246 | } | 
| 247 | } | |
| 248 | ||
| 249 | ||
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 250 | // main setup | 
| 251 | void setup() | |
| 252 | {
 | |
| 82
by Tim Marston added info mode, a '/' to the fonts and cleaned up a couple of bits | 253 | // set up an interrupt handler on pin 2 to notice fan pulses | 
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 254 | attachInterrupt( 0, fan_pulse_handler, RISING ); | 
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 255 | digitalWrite( 2, HIGH ); | 
| 256 | ||
| 257 | // set up output pins (4 to 13) for the led array | |
| 258 | for( int a = 4; a < 14; a++ ) | |
| 259 | pinMode( a, OUTPUT ); | |
| 260 | ||
| 16
by edam finished first revision of propeller-clock code (can display clock and test); added Bounce library | 261 | // set up mode-switch button on pin 3 | 
| 262 | pinMode( 3, INPUT ); | |
| 42
by edam lengthened button debounce time and turned on the pull-up resistor (oops!) | 263 | digitalWrite( 3, HIGH ); | 
| 62
by edam moved some stuf round, created a re-usable pool of message buffers, genericised "modes" for messages | 264 | 	static int event_times[] = { 5, 500, 4000, 0 };
 | 
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 265 | _button.set_event_times( event_times ); | 
| 58
by edam removed Bounce library and updated/fixed new code | 266 | |
| 68
by Tim Marston added frame reset code and inited minor mode flavours on mode activation | 267 | // initialise RTC | 
| 66
by Tim Marston removed time singleton, not cause it saved much space, but cause i don't want singletons in this project! | 268 | Time::init(); | 
| 269 | ||
| 76
by edam switch button to no interim presses during settings mode; added NVRAM support | 270 | // init text renderer | 
| 271 | TextRenderer::init(); | |
| 272 | ||
| 87
by edam switched back to using classes for modes | 273 | // reset text | 
| 274 | Text::reset(); | |
| 275 | leds_off(); | |
| 276 | ||
| 277 | static SwitcherMajorMode switcher; | |
| 278 | static SettingsMajorMode settings; | |
| 279 | ||
| 280 | // add major modes | |
| 281 | int mode = 0; | |
| 282 | _modes[ mode++ ] = &switcher; | |
| 283 | _modes[ mode++ ] = &settings; | |
| 284 | _modes[ mode ] = 0; | |
| 285 | ||
| 286 | // activate the current major mode | |
| 287 | _modes[ _mode ]->activate(); | |
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 288 | } | 
| 289 | ||
| 290 | ||
| 291 | // main loop | |
| 11
by Dan added initial propeller clock code | 292 | void loop() | 
| 293 | {
 | |
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 294 | // if there has been a new pulse, we'll be resetting the display | 
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 295 | bool reset = _new_pulse_at? true : false; | 
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 296 | |
| 59
by edam removed ulibc, fixed button, added text rendering | 297 | // update button | 
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 298 | _button.update(); | 
| 59
by edam removed ulibc, fixed button, added text rendering | 299 | |
| 16
by edam finished first revision of propeller-clock code (can display clock and test); added Bounce library | 300 | // only do this stuff at the start of a display cycle, to ensure | 
| 301 | // that no state changes mid-display | |
| 302 | if( reset ) | |
| 303 | 	{
 | |
| 59
by edam removed ulibc, fixed button, added text rendering | 304 | // calculate segment times | 
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 305 | calculate_segment_times(); | 
| 16
by edam finished first revision of propeller-clock code (can display clock and test); added Bounce library | 306 | |
| 307 | // keep track of time | |
| 66
by Tim Marston removed time singleton, not cause it saved much space, but cause i don't want singletons in this project! | 308 | Time::update(); | 
| 59
by edam removed ulibc, fixed button, added text rendering | 309 | |
| 310 | // perform button events | |
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 311 | do_button_events(); | 
| 16
by edam finished first revision of propeller-clock code (can display clock and test); added Bounce library | 312 | } | 
| 313 | ||
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 314 | // draw this segment | 
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 315 | draw_next_segment( reset ); | 
| 13
by edam updated propeller-clock code, added GPL text and renamed fan-test | 316 | |
| 317 | // wait till it's time to draw the next segment | |
| 65
by Tim Marston removed most OOP/inheritance crap, saved loads of space! | 318 | wait_till_end_of_segment( reset ); | 
| 11
by Dan added initial propeller clock code | 319 | } |