+/* vetinari's clock
+ *
+ * This is an accurate time-keeping clock which has irregular second
+ * hand ticks.
+ * This is achieved by counting seconds properly using an external
+ * 32.768 kHz crystal and calling an interrupt every 1/512 of a second
+ * We further separate this into 1/32 second intervals and offset the
+ * actual movement of the second hand by between 0 and 15 1/32s.
+ * Also we then occasionally skip 1 or 2 ticks and catch them up later
+ * my moving the hand the necessary additional number of ticks as part
+ * of the next tick performed.
+ */
+
#include <msp430.h>
#include <legacymsp430.h>
#include <stdint.h>
#include <stdlib.h>
-//#define LED0 BIT0 /* P1.0 */
#define CLKP BIT6 /* P1.6 */
#define CLKN BIT7 /* P1.7 */
+static unsigned long next = 0;
+static void my_srand(unsigned long seed)
+{
+ next = seed;
+}
+static unsigned int
+my_rand()
+{
+ next = next * 1103515245 + 12345;
+ return (unsigned int)next;
+}
+
static void
setup(void)
{
- //P1DIR |= LED0 | CLKP | CLKN; /* set P1.0,P1.1,P1.2 as output */
- //P1OUT |= LED0 | CLKP | CLKN; /* set P1.0,P1.1,P1.2 low */
P1DIR = 0xff; /* set P1.* to output for reduced power consumption */
P1OUT = CLKP | CLKN;
{
/* disable the watchdog */
WDTCTL = WDTPW + WDTHOLD;
-
+
/* external 32.768kHz crystal */
BCSCTL1 |= DIVA_3; /* ACLK/8 */
BCSCTL3 |= XCAP_3; /* enable 12.5pF internal capacitance */
-
- srand(0xf00d);
+
+ srand(2);
+ my_srand(0xdeadbeef);
setup();
/* enable global interrupts */
eint();
-
+
while (1) {
/* do nothing forever, timer interrupts drive the leds */
/* enter low power mode 3 with interrupts enabled) */
}
}
-enum State {State_Wait, State_Delay, State_Driving};
+static void
+energize_coil()
+{
+ static int coil_mode = 0;
+ P1OUT |= (coil_mode ? CLKP : CLKN);
+ P1OUT &= ~(coil_mode ? CLKN : CLKP);
+ coil_mode = !coil_mode;
+}
+
+static void
+deenergize_coil()
+{
+ P1OUT &= ~(CLKP | CLKN);
+}
+
+enum State {
+ State_Wait, State_BeginTick, State_EndTick,
+ State_SkipTick, State_BeginDouble, State_NextDouble,
+ State_SkipTwo, State_BeginTriple, State_NextTriple,
+};
enum State state = State_Wait;
-static uint8_t count = 0;
-static int8_t offset = 0;
-static uint8_t pin_pos = CLKP;
-static uint8_t pin_neg = CLKN;
+static uint8_t count = 0, offset = 0;
interrupt(TIMER0_A0_VECTOR)
TIMERA0_ISR(void)
TACCTL0 &= ~CCIFG;
++count;
- /* if driving the stepper coil, stop after 32ms (1 interrupt) */
- if (state == State_Driving) {
- state = State_Wait;
- P1OUT &= ~(CLKP | CLKN);
- }
-
if (count == 32) {
count = 0;
- offset = 1 + (rand() & 0x0f);
- state = State_Delay;
+ int skip = ((my_rand() % 3) == 0) ? 1 : 0;
+ switch (state) {
+ case State_SkipTwo:
+ state = State_BeginTriple;
+ break;
+ case State_SkipTick:
+ state = skip ? State_SkipTwo : State_BeginDouble;
+ break;
+ default:
+ offset = 1 + (my_rand() % 15);
+ state = skip ? State_SkipTick : State_BeginTick;
+ break;
+ }
}
- if (state == State_Delay) {
- --offset;
- if (offset == 0) {
- state = State_Driving;
- P1OUT |= pin_pos;
- P1OUT &= ~pin_neg;
- /* swap the pins for the next tick */
- uint8_t t = pin_pos;
- pin_pos = pin_neg;
- pin_neg = t;
+ /* if driving the stepper coil, stop after 32ms (1 interrupt) */
+ switch (state) {
+ case State_SkipTick:
+ case State_SkipTwo: {
+ break;
+ }
+ case State_BeginTriple: {
+ energize_coil();
+ state = State_NextTriple;
+ break;
+ }
+ case State_NextTriple: {
+ deenergize_coil();
+ state = State_BeginDouble;
+ break;
+ }
+ case State_BeginDouble: {
+ energize_coil();
+ state = State_NextDouble;
+ break;
+ }
+ case State_NextDouble: {
+ deenergize_coil();
+ state = State_BeginTick;
+ break;
+ }
+ case State_BeginTick: {
+ --offset;
+ if (offset == 0) {
+ energize_coil();
+ state = State_EndTick;
+ }
+ break;
+ }
+ case State_EndTick: {
+ deenergize_coil();
+ state = State_Wait;
+ break;
}
+ case State_Wait:
+ break;
}
}