--- /dev/null
+/*
+ * File: buttons.c
+ *
+ * Button control
+ */
+
+#include <htc.h>
+#include "btn.h"
+#include "tmr.h"
+#include "task.h"
+
+/* All the buttons are on PORTB. RB0 is the push button.
+ * RB4 and RB5 are the rocker switch positions left and right, respectively.
+ */
+#define ALLMASK 0b00110001
+
+/* Initialize the button module */
+void btn_init()
+{
+ nWPUEN = 0; /* enable weak pull-ups on PortB */
+ TRISB |= ALLMASK; /* all button pins as inputs */
+ ANSELB &= ~ALLMASK; /* turn off ADC inputs on button pins */
+ IOCBF &= ~ALLMASK; /* clear pin interrupt flags */
+ IOCIE = 1; /* enable interrupt-on-change */
+ GIE = 1; /* enable global interrupts */
+}
+
+void btn_isr()
+{
+ if (IOCIF) {
+ if (IOCBF0) {
+ _btn_pbdis();
+ tmr_start(TMR_BTN_PB, 1);
+ }
+ if (IOCBF4) {
+ _btn_rsdis();
+ tmr_start(TMR_BTN_RS, 1);
+ }
+ if (IOCBF5) {
+ _btn_rsdis();
+ tmr_start(TMR_BTN_RS, 1);
+ }
+ }
+
+ if (tmr_fired(TMR_BTN_PB))
+ _task_post(TASK_BTN_PB);
+ if (tmr_fired(TMR_BTN_RS))
+ _task_post(TASK_BTN_RS);
+}
+
--- /dev/null
+/*
+ * File: btn.h
+ *
+ * Generic button module. Understands the rocker switch and push button
+ * present on the rgb lamp board. Implements switch debounce in software.
+ *
+ * When a switch is enabled, a state change triggers the ISR. The ISR
+ * posts a timer task to wait a debounce period. When the timer fires,
+ * the ISR posts a task indicating a state change on the button. The
+ * user code, in its implementation of the task, should read the current
+ * state and take an action assuming that the state is different than it
+ * was before.
+ */
+
+#ifndef _BTN_H
+#define _BTN_H
+
+#include <htc.h>
+
+enum {
+ BTN_PB_DOWN = 0,
+ BTN_PB_UP,
+
+ BTN_RS_OFF = 0,
+ BTN_RS_LEFT,
+ BTN_RS_RIGHT,
+};
+
+/* Read the current state of the pushbutton */
+#define btn_pb() (RB0)
+
+/* Read the current state of the rocker switch */
+#define btn_rs() ((RB4 + ((unsigned char)RB5 << 1)) % 3)
+
+/* Enable the pushbutton from ISR */
+#define _btn_pben() do { \
+ IOCBP0 = 1; IOCBN0 = 1; IOCBF0 = 0; \
+ } while (0)
+
+/* Disable the pushbutton from ISR */
+#define _btn_pbdis() do { \
+ IOCBP0 = 0; IOCBN0 = 0; IOCBF0 = 0; \
+ } while (0)
+
+/* Enable the rocker switch from ISR */
+#define _btn_rsen() do { \
+ IOCBP4 = 1; IOCBN4 = 1; IOCBF4 = 0; \
+ IOCBP5 = 1; IOCBN5 = 1; IOCBF5 = 0; \
+ } while (0)
+
+/* Disable the rocker switch from ISR */
+#define _btn_rsdis() do { \
+ IOCBP4 = 0; IOCBN4 = 0; IOCBF4 = 0; \
+ IOCBP5 = 0; IOCBN5 = 0; IOCBF5 = 0; \
+ } while (0)
+
+/* Enable the pushbutton from user code */
+#define btn_pben() do { \
+ ndi(); \
+ _btn_pben(); \
+ nei(); \
+ } while (0)
+
+/* Disable the pushbutton from user code */
+#define btn_pbdis() do { \
+ ndi(); \
+ _btn_pbdis(); \
+ nei(); \
+ } while (0)
+
+/* Enable the rocker switch from user code */
+#define btn_rsen() do { \
+ ndi(); \
+ _btn_rsen(); \
+ nei(); \
+ } while (0)
+
+/* Disable the rocker switch from user code */
+#define btn_rsdis() do { \
+ ndi(); \
+ _btn_rsdis(); \
+ nei(); \
+ } while (0)
+
+/* Initialize the button module */
+void btn_init();
+
+/* This function must be called from the user interrupt function. */
+void btn_isr();
+
+#endif
+++ /dev/null
-/*
- * File: buttons.c
- *
- * Button control
- */
-
-
-#include <htc.h>
-#include "buttons.h"
-#include "tmr.h"
-
-void buttons_sleep()
-{
- /* Turn off global interrupts. Don't need to call the ISR */
- GIE = 0;
-
- /* If the rocker is on, sleep until it is placed in the off position */
- IOCBN4 = 0; IOCBP4 = 1;
- IOCBN5 = 0; IOCBP5 = 1;
- while ((buttons_read() & (IN_ROCKERA | IN_ROCKERB))) {
- IOCIE = 1;
- SLEEP();
- IOCIE = 0;
- tmr_mwait(50); /* debounce */
- }
-
- /* Now that the rocker is off, sleep until it is turned on */
- IOCBN4 = 1; IOCBP4 = 0; IOCBF4 = 0;
- IOCBN5 = 1; IOCBP5 = 0; IOCBF5 = 0;
- if ((buttons_read() & (IN_ROCKERA | IN_ROCKERB)) == 0) {
- IOCIE = 1;
- SLEEP();
- IOCIE = 0;
- }
-
- /* Turn global interrupts back on to activate the ISR. */
- GIE = 0;
-}
+++ /dev/null
-/*
- * File: buttons.h
- *
- * Button control
- */
-
-
-#ifndef _BUTTONS_H
-#define _BUTTONS_H
-
-#include <htc.h>
-
-#define IN_MASK 0b00110001
-#define IN_ROCKERA 0b00100000
-#define IN_ROCKERB 0b00010000
-#define IN_PUSHBTN 0b00000001
-
-/* Initialize the buttons. RB0 is the push button. RB4 and RB5 are the
- * rocker switch positions.
- */
-#define buttons_init() do { \
- nWPUEN = 0; /* enable weak pull-ups on PortB */ \
- TRISB |= IN_MASK; \
- ANSELB &= ~IN_MASK; \
-} while (0)
-
-/* Read all the buttons at once, returning a mask of IN_xxx bits */
-#define buttons_read() ((PORTB ^ IN_MASK) & IN_MASK)
-
-/* Return the rocker button position as a mask of IN_xxx bits */
-#define buttons_on() ((PORTB ^ IN_MASK) & (IN_ROCKERA | IN_ROCKERB))
-
-/* Return the pushbutton state */
-#define buttons_push() ((PORTB ^ IN_MASK) & IN_PUSHBTN)
-
-/* Sleep until the rocker is in one of the non-off positions */
-void buttons_sleep();
-
-#endif
#include <htc.h>
#include "tmr.h"
+#include "btn.h"
+#include "task.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();
+ btn_isr();
+
+ if (tmr_fired(TMR_AUTO_OFFON))
+ _task_post(TASK_AUTO_OFFON);
+ if (tmr_fired(TMR_FADE))
+ _task_post(TASK_FADE);
+ if (tmr_fired(TMR_INCOLOR))
+ _task_post(TASK_INCOLOR);
+#if 0
+ if (tmr_fired(TMR_DIM))
+ _task_post(TMR_DIM);
+#endif
}
#include <stdlib.h>
#include "picinit.h"
#include "unused.h"
-#include "buttons.h"
+#include "btn.h"
#include "rgb.h"
#include "tmr.h"
#include "adc_random.h"
+#include "task.h"
#if 0
#define AUTO_OFF_COUNT 549316UL /* 5 hrs in 32.768 ms units */
#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)
#define rand_u16() ((rand() << 8) + rand_u8())
#define rand_incolor_steps(s) (min_incolor_steps[s & 3] + \
int increment;
signed char remainder;
} led_t;
-#define INIT_LED { 0, 0, 0 }
/* The index of all step arrays is the speed variable */
#if 1
const static int range_fade_steps[4] = { 1, 1, 1, 1 };
#endif
-int main(void)
+led_t red;
+led_t grn;
+led_t blu;
+led_t wht;
+bit on;
+unsigned char speed;
+int fade_steps;
+
+void start_fade()
+{
+ /* RGB PWM values are 8 bits, but computations are done in 15
+ * (leaving room for a sign bit), so leds_set() uses >>7 to convert
+ * to PWM values.
+ */
+ int newr, newg, newb, neww;
+
+ /* New RGB values; all zero is not a valid option */
+ do {
+ newr = rand();
+ newg = rand();
+ newb = rand();
+ neww = rand();
+ } while (newr == 0 && newg == 0 && newb == 0 && neww == 0);
+
+ /* Random # of steps to reach the new color */
+ fade_steps = rand_fade_steps(speed);
+
+ /* Compute increment per fade step, and remainder, for each led */
+ red.increment = (newr - red.value) / fade_steps;
+ red.remainder = newr - (red.value + red.increment * fade_steps);
+ grn.increment = (newg - grn.value) / fade_steps;
+ grn.remainder = newg - (grn.value + grn.increment * fade_steps);
+ blu.increment = (newb - blu.value) / fade_steps;
+ blu.remainder = newb - (blu.value + blu.increment * fade_steps);
+ wht.increment = (neww - wht.value) / fade_steps;
+ wht.remainder = neww - (wht.value + wht.increment * fade_steps);
+
+ /* Start the fade timer */
+ tmr_startPeriodic(TMR_FADE, 1); /* 32.768 msec */
+}
+
+void turnOn()
+{
+ dbgpin_high();
+ on = 1;
+ rgb_on();
+ red.value = 0;
+ grn.value = 0;
+ blu.value = 0;
+ wht.value = 0;
+ start_fade();
+}
+
+void turnOff()
+{
+ /* Event on to off, either by switch or auto-off timer */
+ tmr_stop(TMR_INCOLOR);
+ tmr_stop(TMR_FADE);
+ rgb_off();
+ dbgpin_low();
+ on = 0;
+}
+
+void pb_task()
{
- led_t red = INIT_LED;
- led_t grn = INIT_LED;
- led_t blu = INIT_LED;
- led_t wht = INIT_LED;
- unsigned char speed = 0;
- unsigned incolor_steps;
- int fade_steps;
- unsigned char buttons, last_buttons;
-
- pic_init();
- unused_init();
- buttons_init();
- rgb_init();
- tmr_init();
- dbgpin_init();
-
- srand((adc_random() << 8) + adc_random());
- reset_steps();
- if (buttons_on())
- rgb_on();
-
- dbgpin_high();
- last_buttons = 0;
- buttons = buttons_read();
- while (1) {
- /* Wait for an event. Timer firing or button state change.
- * When we have a crystal, we can sleep instead. Also, soon we
- * can transition button state changes to the ISR.
- */
- dbgpin_low();
- while (!tmr_events() && last_buttons == buttons) {
- last_buttons = buttons;
- buttons = buttons_read();
- }
- dbgpin_high();
-
- if (((buttons & IN_ROCKERB) && !tmr_on(TMR_AUTO_OFFON)) ||
- !(last_buttons & (IN_ROCKERB | IN_ROCKERA))) {
- /* Event: off to on */
- tmr_startPeriodic(TMR_AUTO_OFFON, AUTO_OFF_COUNT);
- tmr_startPeriodic(TMR_FADE, 1); /* 32.768 msec */
-
- } else if (((buttons & IN_ROCKERB) && tmr_fired(TMR_AUTO_OFFON)) ||
- (!(buttons & (IN_ROCKERA | IN_ROCKERB)))) {
- /* Event on to off, either by switch or auto-off timer */
- rgb_off();
- dbgpin_low();
- tmr_stop(TMR_AUTO_OFFON);
- tmr_stop(TMR_FADE);
- buttons_sleep();
- dbgpin_high();
- rgb_on();
- red.value = 0;
- grn.value = 0;
- blu.value = 0;
- wht.value = 0;
- reset_steps();
- tmr_startPeriodic(TMR_FADE, 1); /* 32.768 msec */
-
- } else if (tmr_fired(TMR_FADE)) {
- /* Other actions, driven by the fade timer for now */
-
- /* Crappy way to detect rising edges to change state of speed var */
- if (!(speed & 4) && (buttons & IN_PUSHBTN))
- speed |= 4;
- else if ((speed & 4) && !(buttons & IN_PUSHBTN)) {
- speed = (speed + 1) & ~4;
- reset_steps();
- }
-
- if (fade_steps) {
- /* Continue the in-progress fade */
- fade_steps--;
- red.value += red.increment;
- grn.value += grn.increment;
- blu.value += blu.increment;
- wht.value += wht.increment;
- if (fade_steps == 0) {
- red.value += red.remainder;
- grn.value += grn.remainder;
- blu.value += blu.remainder;
- wht.value += wht.remainder;
- }
- leds_set(red, grn, blu, wht);
-
- } else if (--incolor_steps == 0) {
- /* RGB PWM values are 8 bits, but computations are done in 15
- * (leaving room for a sign bit), so leds_set() uses >>7 to convert
- * to PWM values.
- */
- int newr, newg, newb, neww;
-
- /* New RGB values; all zero is not a valid option */
- do {
- newr = rand();
- newg = rand();
- newb = rand();
- neww = rand();
- } while (newr == 0 && newg == 0 && newb == 0 && neww == 0);
-
- /* Next incolor and fade steps */
- incolor_steps = rand_incolor_steps(speed);
- fade_steps = rand_fade_steps(speed);
-
- /* Compute increment and remainder for each led */
- red.increment = (newr - red.value) / fade_steps;
- red.remainder = newr - (red.value + red.increment * fade_steps);
- grn.increment = (newg - grn.value) / fade_steps;
- grn.remainder = newg - (grn.value + grn.increment * fade_steps);
- blu.increment = (newb - blu.value) / fade_steps;
- blu.remainder = newb - (blu.value + blu.increment * fade_steps);
- wht.increment = (neww - wht.value) / fade_steps;
- wht.remainder = neww - (wht.value + wht.increment * fade_steps);
- }
- }
+ if (btn_pb() == BTN_PB_UP) {
+ speed = (speed + 1) & ~4;
+ fade_steps = 0;
}
- return 0;
+ btn_pben();
}
+
+void rs_task()
+{
+ switch (btn_rs()) {
+ case BTN_RS_OFF:
+ tmr_stop(TMR_AUTO_OFFON);
+ turnOff();
+ break;
+ case BTN_RS_RIGHT:
+ tmr_start(TMR_AUTO_OFFON, AUTO_OFF_COUNT);
+ /* fall through */
+ case BTN_RS_LEFT:
+ turnOn();
+ break;
+ }
+ btn_rsen();
+}
+
+void fade_task()
+{
+ red.value += red.increment;
+ grn.value += grn.increment;
+ blu.value += blu.increment;
+ wht.value += wht.increment;
+ if (--fade_steps == 0) {
+ /* This is the last fade step. Finalize the RGB led states and decide how
+ * long to stay in this color.
+ */
+ red.value += red.remainder;
+ grn.value += grn.remainder;
+ blu.value += blu.remainder;
+ wht.value += wht.remainder;
+ tmr_stop(TMR_FADE);
+ tmr_start(TMR_INCOLOR, rand_incolor_steps(speed));
+ }
+ leds_set(red, grn, blu, wht);
+}
+
+void auto_onoff_task()
+{
+ if (on) {
+ turnOff();
+ if (btn_rs() == BTN_RS_RIGHT)
+ tmr_start(TMR_AUTO_OFFON, AUTO_OFF_COUNT);
+ } else /* off */ {
+ turnOn();
+ if (btn_rs() == BTN_RS_RIGHT)
+ tmr_start(TMR_AUTO_OFFON, AUTO_ON_COUNT);
+ }
+}
+
+void user_boot()
+{
+ dbgpin_high();
+ srand((adc_random() << 8) + adc_random());
+ pb_task();
+ rs_task();
+}
+
+void user_tasks(unsigned char block)
+{
+ task_id_t tid;
+
+ while ((tid = task_get(block))) {
+ switch (tid) {
+ case TASK_BTN_PB: /* pushbutton state change */
+ pb_task();
+ break;
+ case TASK_BTN_RS: /* rocker switch state change */
+ rs_task();
+ break;
+ case TASK_FADE: /* fade timer has fired */
+ fade_task();
+ break;
+ case TASK_INCOLOR: /* in-color timer has fired */
+ start_fade();
+ break;
+ case TASK_AUTO_OFFON: /* auto on/off timer has fired */
+ auto_onoff_task();
+ break;
+ }
+ }
+}
+
+int main(void)
+{
+ pic_init();
+ unused_init();
+ btn_init();
+ rgb_init();
+ tmr_init();
+ task_init();
+ dbgpin_init();
+
+#if 0
+ /* Execute tasks until the queue empties */
+ user_tasks(0);
+#endif
+
+#if 0
+ /* Software initialization */
+#endif
+
+#if 0
+ /* Execute tasks until the queue empties */
+ user_tasks(0);
+#endif
+
+ /* Run user boot code */
+ user_boot();
+
+ /* Process tasks forever */
+ user_tasks(1);
+}
+
--- /dev/null
+/*
+ * File: task.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 "task.h"
+#include "isr.h"
+#include "bit.h"
+
+unsigned long _task_ids;
+task_id_t _task_bitno;
+
+bit task_check()
+{
+ unsigned char r;
+
+ ndi();
+ r = _task_ids != 0;
+ nei();
+ return r != 0;
+}
+
+task_id_t task_get(unsigned char block)
+{
+ task_id_t t = 0;
+ unsigned long ids;
+
+ do {
+ ndi();
+ ids = _task_ids;
+ nei();
+ if (ids) {
+ for (unsigned char i = 0; t == 0 && i < TASK_COUNT; i++) {
+ if (ids & (1UL << _task_bitno))
+ t = _task_bitno;
+ _task_bitno = (_task_bitno + 1) % TASK_COUNT;
+ }
+ }
+#if 0 /* Not until we have a crystal and can wake from sleep via tmr module */
+ else
+ SLEEP();
+#endif
+ } while (t == 0 && block == 1);
+ return t;
+}
+
+void task_post(task_id_t t)
+{
+ ndi();
+ _task_post(t);
+ nei();
+}
+
--- /dev/null
+/*
+ * File: task.h
+ *
+ * Generic task module. Allows ISR or user code to post tasks that can
+ * then later be executed by calling task_get(). Implements an option
+ * to put the CPU into lowest power mode if task_get() blocks.
+ *
+ * The user code must call task_isr() from the interrupt function. It then
+ * will call task functions to activate and query timers. For example:
+ *
+ * void interrupt isr()
+ * {
+ * task_post(TASK_ID);
+ * }
+ *
+ * void main()
+ * {
+ * task_id_t tid;
+ *
+ * task_init();
+ * while ((tid = task_get(1))) {
+ * switch (tid) {
+ * case TASK_ID_SOMETHING:
+ * something_task();
+ * break;
+ * }
+ * }
+ * }
+ */
+
+#ifndef _TASK_H
+#define _TASK_H
+
+#include "task_defs.h"
+#include "bit.h"
+
+typedef unsigned char task_id_t;
+
+/* Do not use directly; only for the 'inline' functions below */
+extern unsigned long _task_ids;
+extern task_id_t _task_bitno;
+
+/* Post task t from ISR */
+#define _task_post(t) do { _task_ids |= 1UL << t; } while (0)
+
+/* Initialize the task subsystem */
+#define task_init() do { _task_ids = 0; _task_bitno = 0; } while (0)
+
+/* Returns non-zero if one or more tasks are posted. Does not block. */
+bit task_check();
+
+/* Returns the next posted task. If the task list is empty, return zero if the
+ * argument is set to zero. Else if the argument is non-zero, block in low power
+ * mode until a task is posted.
+ *
+ * This function should never be called from an ISR.
+ */
+task_id_t task_get(unsigned char block);
+
+/* Post task t from user code */
+void task_post(task_id_t t);
+
+#endif
--- /dev/null
+/*
+ * File: task_defs.h
+ *
+ * User defines this file to create the tasks it wishes. This is a
+ * generic template.
+ */
+
+
+#ifndef _TASK_DEFS_H
+#define _TASK_DEFS_H
+
+/* Define the tasks in use. Last enum, TASK_COUNT, is used by the
+ * tmr module to know the number of timers and allocate resources
+ * accordingly.
+ *
+ * Note: The number of tasks is limited to the number of bits in the
+ * _task_ids variable in task.c.
+ */
+enum {
+ TASK_BTN_PB = 0,
+ TASK_BTN_RS,
+ TASK_FADE,
+ TASK_INCOLOR,
+ TASK_AUTO_OFFON,
+
+ TASK_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
* }
*/
-
#ifndef _TMR_H
#define _TMR_H
* accordingly.
*/
enum {
- TMR_AUTO_OFFON = 0,
+ TMR_BTN_PB = 0,
+ TMR_BTN_RS,
+ TMR_AUTO_OFFON,
TMR_FADE,
- TMR_CHANGE,
- TMR_DIM,
- TMR_BTN,
- TMR_ROCKER,
+ TMR_INCOLOR,
+ //TMR_DIM,
TMR_COUNT
};