--- /dev/null
+/*
+ * File: bits.h
+ *
+ * Bit handling macros
+ */
+
+
+#ifndef _BIT_H
+#define _BIT_H
+
+/* bit_set() and bit_clr() are converted to single assembly instructions.
+ * Not sure yet about bit_get and bit_toggle.
+ */
+
+#define bit_get(var, bitno) ((var) & (1UL << (bitno)))
+#define bit_set(var, bitno) ((var) |= 1UL << (bitno))
+#define bit_clr(var, bitno) ((var) &= ~(1UL << (bitno)))
+#define bit_toggle(var, bitno) ((var) ^= (1UL << (bitno))
+
+#endif
--- /dev/null
+/*
+ * File: isr.c
+ *
+ * Interrupt handling routines
+ */
+
+
+#include <htc.h>
+#include "tmr.h"
+
+bit isr_gie; /* Used to store the state of GIE for nested ndi()/nei() */
+unsigned char isr_di; /* Count of nested ndi() */
+
+void interrupt isr()
+{
+ tmr_isr();
+}
--- /dev/null
+/*
+ * File: isr.h
+ *
+ * Interrupt handling routines
+ */
+
+#ifndef _ISR_H
+#define _ISR_H
+
+#include <htc.h>
+
+extern bit isr_gie;
+extern unsigned char isr_di;
+
+/* Nested disable interrupts inline function, for use outside ISR */
+#define ndi() \
+ do { \
+ if (isr_di++ == 0) \
+ isr_gie = GIE; \
+ if (isr_gie) \
+ di(); \
+ } while (0)
+
+/* Nested enable interrupts inline function, for use outside ISR */
+#define nei() \
+ do { \
+ if (--isr_di == 0) \
+ if (isr_gie) { \
+ isr_gie = 0; \
+ ei(); \
+ } \
+ } while (0)
+
+void interrupt isr();
+
+#endif
#include "unused.h"
#include "buttons.h"
#include "rgb.h"
-#include "timer.h"
+#include "tmr.h"
#include "adc_random.h"
-#define AUTO_OFF_COUNT 549316UL /* 5 hrs in 32.768 ms units */
+#if 0
+#define AUTO_OFF_COUNT 549316UL /* 5 hrs in 32.768 ms units */
+#define AUTO_ON_COUNT 2087402UL /* 19 hrs in 32.768 ms units */
+#else
+#define AUTO_OFF_COUNT 1831 /* 1 minute in 32.768 ms units */
+#define AUTO_ON_COUNT 3662 /* 2 minutes in 32.768 ms units */
+#endif
#define reset_steps() do { incolor_steps = 1; fade_steps = 0; } \
while (0)
#define rand_u8() (rand() & 0xff)
RA2 = 0; \
TRISA2 = 0; \
} while (0)
-#define dbgpin_high() RA2 = 1;
-#define dbgpin_low() RA2 = 0;
+#define dbgpin_high() (RA2 = 1)
+#define dbgpin_low() (RA2 = 0)
+#define dbgpin_toggle() (RA2 = (LATA2 == 0) ? 1 : 0)
typedef struct {
int value;
unused_init();
buttons_init();
rgb_init();
- timer_init();
+ tmr_init();
dbgpin_init();
srand((adc_random() << 8) + adc_random());
rgb_on();
dbgpin_high();
+ tmr_startPeriodic(TMR_FADE, 1); /* 32.768 msec */
while (1) {
unsigned char buttons = buttons_read();
if ((buttons & IN_ROCKERB) && auto_off == 0)
- auto_off = AUTO_OFF_COUNT;
+ tmr_startPeriodic(TMR_AUTO_OFFON, AUTO_OFF_COUNT);
- if (((buttons & IN_ROCKERB) && auto_off && --auto_off == 0) ||
+ if ((buttons & IN_ROCKERB) && tmr_fired(TMR_AUTO_OFFON) ||
(!(buttons & (IN_ROCKERA | IN_ROCKERB)))) {
/* Sleep when auto-off time has expired or if rocker switch is
* turned off.
wht.remainder = neww - (wht.value + wht.increment * fade_steps);
}
dbgpin_low();
- timer_owait(); /* wait 32 ms since last return from last call() */
+ while (!tmr_fired(TMR_FADE));
dbgpin_high();
}
}
void pic_init()
{
/* Setting below must match _XTAL_FREQ in picinit.h */
- OSCCON = 0b01100000;
+ OSCCON = 0b01101000;
/* OSCSTAT.HFIOFL is set when oscillator is locked (accurate within 2%) */
while (!HFIOFL);
#ifndef _PICINIT_H
#define _PICINIT_H
-#define _XTAL_FREQ 2000000
+#define _XTAL_FREQ 4000000
void pic_init();
/* Initialize rgb
* CCP1 on RB3, CCP2 on RA7, CCP3 on RA3, CCP4 on RA4
* - Fosc = 8 MHz, 4 MHz, 2 MHz
- * - Prescale = 4, 4, 1
+ * - Prescale = 4, 1, 1
* - PRx value = 0xff
- * = f(rgb) = 1.95 kHz, 0.98 kHz, 1.95 kHz
+ * = f(rgb) = 1.95 kHz, 3.90 kHz, 1.95 kHz
*/
/* Set rgb Rxn pins as outputs. */
#ifndef _RGB_H
#define _RGB_H
+#include "isr.h"
+
/* Initialize the RGB LED assembly. Outputs are zero, PWM is off. */
void rgb_init();
/* Set a PWM value for each color LED. 0=off, 255=full on. */
#define rgb_set(red, grn, blu, wht) do { \
+ ndi(); /* FIXME: doesn't seem to fix flicker */ \
CCPR1L = (red); \
CCPR2L = (grn); \
CCPR3L = (blu); \
CCPR4L = (wht); \
+ nei(); \
} while (0)
#endif
+++ /dev/null
-/*
- * File: timer.c
- *
- * Timer 0 + Timer 1 for timekeeping
- */
-
-
-#include <htc.h>
-#include "timer.h"
-
-void timer_uwait(unsigned us)
-{
- unsigned t0 = TMR0;
-
- TMR0IF = 0;
- while (us >= 32768) {
- timer_owait();
- us -= 32768;
- }
- while (us >= 16384) {
- timer_cwait(128);
- us -= 16384;
- }
- timer_cwait(us / 128);
-}
-
-void timer_mwait(unsigned ms)
-{
- unsigned t0 = TMR0;
-
- TMR0IF = 0;
- while (ms >= 32) {
- timer_owait();
- ms -= 32;
- }
- while (ms >= 16) {
- timer_cwait(128);
- ms -= 16;
- }
- timer_cwait(ms * 8);
-}
+++ /dev/null
-/*
- * File: timer.h
- *
- * Timer 0 + Timer 1 for timekeeping
- */
-
-
-#ifndef _TIMER_H
-#define _TIMER_H
-
-/* Timer 0 with 1:256 prescale given Fosc (_XTAL_FREQ).
- * Timer 0 is clocked by Fosc/4.
- *
- * Fosc Overflow, ms
- * ------- ----------
- * 32 MHz 8.192
- * 16 MHz 16.384
- * 8 MHz 32.768
- * 4 MHz 65.536
- * 2 MHz 131.072
- * 1 MHz 262.144
- * 500 KHz 524.288
- * 250 KHz 1,048.576
- * 125 KHz 2,097.152
- */
-
-/* Configure Timer0 to overflow every 32 msec. Adjust with
- * Fosc as set in picinit.[ch]. At 2 MHz, prescale is 1:64.
- */
-#define timer_init() \
- do { \
- /* TMR0CS = 0, PSA = 0, PS = 0b101 */ \
- OPTION_REG = (OPTION_REG & 0b11010000) + 0b0101; \
- } while (0)
-
-/* Wait for the timer to overflow. This is 32 msec from the last overflow. */
-#define timer_owait() \
- { \
- while (!TMR0IF); \
- TMR0IF = 0; \
- } while (0)
-
-/* Wait for c clocks, 0 <= c < 128. */
-#define timer_cwait(c) \
- { \
- unsigned t0 = TMR0; \
- while ((unsigned)(TMR0 - t0) <= c); /* cast prevents integral promotion */ \
- } while (0)
-
-void timer_uwait(unsigned us);
-void timer_mwait(unsigned ms);
-
-#endif
--- /dev/null
+/*
+ * File: tmr.c
+ *
+ * Generic timer module. Currently uses Timer0 to generate ticks every
+ * 32 ms. Later will use Timer1, a crystal, and optionally a compare
+ * module to be able to generate ticks and wake up from sleep.
+ */
+
+
+#include <htc.h>
+#include "tmr.h"
+#include "isr.h"
+#include "bit.h"
+
+persistent tmr_time_t _tmr_ticks;
+static tmr_time_t _tmr_t0[TMR_COUNT];
+static tmr_time_t _tmr_elapsed[TMR_COUNT];
+static tmr_bitno_t _tmr_on;
+static tmr_bitno_t _tmr_periodic;
+static tmr_bitno_t _tmr_fired;
+
+/* Timer0 with 1:256 prescale given Fosc (_XTAL_FREQ).
+ * Timer0 is clocked by Fosc/4.
+ *
+ * Fosc Overflow, ms
+ * ------- ----------
+ * 32 MHz 8.192
+ * 16 MHz 16.384
+ * 8 MHz 32.768
+ * 4 MHz 65.536
+ * 2 MHz 131.072
+ * 1 MHz 262.144
+ * 500 KHz 524.288
+ * 250 KHz 1,048.576
+ * 125 KHz 2,097.152
+ */
+
+void tmr_init()
+{
+ /* Configure Timer0 to overflow every 32 msec. Adjust with
+ * Fosc as set in picinit.[ch]. At 4 MHz, prescale is 1:128.
+ * TMR0CS = 0, PSA = 0, PS = 0b110
+ */
+ OPTION_REG = (OPTION_REG & 0b11010000) + 0b0110;
+ TMR0IF = 0;
+ TMR0IE = 1;
+ GIE = 1;
+}
+
+void tmr_start(tmr_bitno_t t, tmr_time_t elapsed)
+{
+ ndi();
+ bit_set(_tmr_on, t);
+ bit_clr(_tmr_periodic, t);
+ bit_clr(_tmr_fired, t);
+ _tmr_t0[t] = _tmr_ticks;
+ _tmr_elapsed[t] = elapsed;
+ nei();
+}
+
+void tmr_startAt(tmr_bitno_t t, tmr_time_t t0, tmr_time_t elapsed)
+{
+ ndi();
+ bit_set(_tmr_on, t);
+ bit_clr(_tmr_periodic, t);
+ bit_clr(_tmr_fired, t);
+ _tmr_t0[t] = t0 + elapsed;
+ nei();
+}
+
+void tmr_startPeriodic(tmr_bitno_t t, tmr_time_t elapsed)
+{
+ ndi();
+ bit_set(_tmr_on, t);
+ bit_set(_tmr_periodic, t);
+ bit_clr(_tmr_fired, t);
+ _tmr_t0[t] = _tmr_ticks + elapsed;
+ _tmr_elapsed[t] = elapsed;
+ nei();
+}
+
+void tmr_startPeriodicAt(tmr_bitno_t t, tmr_time_t t0, tmr_time_t elapsed)
+{
+ ndi();
+ bit_set(_tmr_on, t);
+ bit_set(_tmr_periodic, t);
+ bit_clr(_tmr_fired, t);
+ _tmr_t0[t] = t0 + elapsed;
+ _tmr_elapsed[t] = elapsed;
+ nei();
+}
+
+bit tmr_fired(tmr_bitno_t t)
+{
+ /* FIXME: if called from ISR ndi()/nei() is not required */
+ static unsigned char last_fired;
+ unsigned char fired;
+
+ ndi();
+ fired = bit_get(_tmr_fired, t) != 0;
+ if (fired)
+ bit_clr(_tmr_fired, t);
+ nei();
+ return fired;
+}
+
+void tmr_isr()
+{
+ if (TMR0IF) {
+ TMR0IF = 0;
+ _tmr_ticks++;
+ for (tmr_bitno_t t = 0; t < TMR_COUNT; t++) {
+ if (_tmr_ticks - _tmr_t0[t] <= 0) {
+ bit_set(_tmr_fired, t);
+ if (bit_get(_tmr_periodic, t))
+ _tmr_t0[t] += _tmr_elapsed[t];
+ else
+ bit_clr(_tmr_on, t);
+ }
+ }
+ }
+}
+
+/* Wait for a specific timer value t */
+#define tmr_wait(t) while (TMR0 != t);
+
+void tmr_uwait(unsigned us)
+{
+ unsigned t0 = TMR0;
+
+ while (us >= 32768) {
+ tmr_wait(t0);
+ us -= 32768;
+ }
+ while (us >= 16384) {
+ tmr_cwait(128);
+ us -= 16384;
+ }
+ tmr_cwait(us / 128);
+}
+
+void tmr_mwait(unsigned ms)
+{
+ unsigned t0 = TMR0;
+
+ while (ms >= 32) {
+ tmr_wait(t0);
+ ms -= 32;
+ }
+ while (ms >= 16) {
+ tmr_cwait(128);
+ ms -= 16;
+ }
+ tmr_cwait(ms * 8);
+}
--- /dev/null
+/*
+ * File: tmr.h
+ *
+ * Generic timer module. Currently uses Timer0 to generate ticks every
+ * 32 ms. Later will use Timer1, a crystal, and optionally a compare
+ * module to be able to generate ticks and wake up from sleep.
+ *
+ * The user code must call tmr_isr() from the interrupt function. It then
+ * will call tmr functions to activate and query timers. For example:
+ *
+ * void interrupt isr()
+ * {
+ * tmr_isr();
+ *
+ * // ISR may take actions based on timers being fired.
+ * if (tmr_fired(TMR_SOMESUCH)) {
+ * // do something that is critically time important
+ * }
+ * }
+ *
+ * void main()
+ * {
+ * tmr_init();
+ * tmr_startPeriodic(TMR_SOMESUCH, 1000);
+ * tmr_startPeriodic(TMR_ANOTHER, 500);
+ *
+ * while (1) {
+ * if (tmr_fired(TMR_ANOTHER)) {
+ * // Do stuff that doesn't have to be in the ISR
+ * }
+ * SLEEP();
+ * }
+ * }
+ */
+
+
+#ifndef _TMR_H
+#define _TMR_H
+
+#include "tmr_defs.h"
+#include "isr.h"
+#include "bit.h"
+
+/* Only access when in ISR or if interrupts are disabled */
+extern persistent tmr_time_t _tmr_ticks;
+
+/* Initialize the tmr subsystem */
+void tmr_init();
+
+/* Return non-zero if the timer is on */
+/* FIXME: this may not be atomic WRT ISR */
+#define tmr_on(t) (bit_get(_tmr_on, (t)))
+
+/* Return non-zero if the timer is periodic */
+/* FIXME: this may not be atomic WRT ISR */
+#define tmr_periodic(t) (bit_get(_tmr_periodic, (t)))
+
+/* Start a timer, expecting it to fire in elapsed ticks */
+void tmr_start(tmr_bitno_t t, tmr_time_t elapsed);
+
+/* Start a timer, expecting it to fire in elapsed ticks from t0 */
+void tmr_startAt(tmr_bitno_t t, tmr_time_t t0, tmr_time_t elapsed);
+
+/* Start a periodic timer, expecting it to fire every elapsed ticks */
+void tmr_startPeriodic(tmr_bitno_t t, tmr_time_t elapsed);
+
+/* Start a periodic timer, expecting it to fire every elapsed ticks */
+void tmr_startPeriodicAt(tmr_bitno_t t, tmr_time_t t0, tmr_time_t elapsed);
+
+/* Stop a timer */
+#define tmr_stop(t) \
+ do { \
+ ndi(); \
+ bit_clr(_tmr_on, (t)); \
+ nei(); \
+ } while(0)
+
+/* Return 1 if the timer has fired, resetting the fired bit */
+bit tmr_fired(tmr_bitno_t t);
+
+/* Return the current number of timer ticks, for use outside ISR */
+#define tmr_time(ptr_time) \
+ do { \
+ ndi(); \
+ *(ptr_time) = _tmr_ticks; \
+ nei(); \
+ } while (0)
+
+/* Used in the ISR to update timer */
+void tmr_isr();
+
+/* Wait for c clocks, 0 <= c < 128. */
+#define tmr_cwait(c) \
+ do { \
+ unsigned t0 = TMR0; \
+ while ((unsigned)(TMR0 - t0) <= c); /* cast prevents integral promotion */ \
+ } while (0)
+
+/* Wait for a number of us. This is pretty accurate. */
+void tmr_uwait(unsigned us);
+
+/* Wait for a number of ms. Each 32 ms actually waits about 32,768 us */
+void tmr_mwait(unsigned ms);
+
+#endif
--- /dev/null
+/*
+ * File: tmr_defs.h
+ *
+ * User defines this file to create the timers it wishes. This is a
+ * generic template.
+ */
+
+
+#ifndef _TMR_DEFS_H
+#define _TMR_DEFS_H
+
+/* Define the timers to use. Last enum, TIMER_COUNT, is used by the
+ * tmr module to know the number of timers and allocate resources
+ * accordingly.
+ */
+enum {
+ TMR_AUTO_ONOFF = 0,
+ TMR_FADE,
+ TMR_DIM,
+ TMR_BTN,
+ TMR_ROCKER,
+
+ TMR_COUNT
+};
+
+/* The tmr module can support unsigned and unsigned long tmr_time_t.
+ * tmr_time_t is the timer value, which is incremented by one for
+ * each tick using Timer0. When transitioned to Timer1, it will be
+ * the value of TMR1, likely extended to a larger width in software.
+ */
+typedef unsigned long tmr_time_t;
+
+/* The tmr module can support unsigned char, unsigned, and unsigned long
+ * tmr_bit_t. The number of bits in tmr_bit_t must be >= TIMER_COUNT, as
+ * this type allocates a bit to each virtual timer.
+ */
+typedef unsigned char tmr_bitno_t;
+
+#endif