From: R. Steve McKown Date: Sat, 10 Dec 2011 02:26:02 +0000 (-0700) Subject: Convert to task form X-Git-Tag: 1.0~33 X-Git-Url: https://oss.titaniummirror.com/gitweb?p=rgblamp.git;a=commitdiff_plain;h=d7221e9aeb82c97fbbff18963ef69a300b3d0cd3 Convert to task form Barely working. Will drive the lamp the wrong way if started with the rocker in an on position. --- diff --git a/btn.c b/btn.c new file mode 100644 index 0000000..7b3c75e --- /dev/null +++ b/btn.c @@ -0,0 +1,50 @@ +/* + * File: buttons.c + * + * Button control + */ + +#include +#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); +} + diff --git a/btn.h b/btn.h new file mode 100644 index 0000000..857ec6f --- /dev/null +++ b/btn.h @@ -0,0 +1,91 @@ +/* + * 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 + +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 diff --git a/buttons.c b/buttons.c deleted file mode 100644 index 486fd58..0000000 --- a/buttons.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * File: buttons.c - * - * Button control - */ - - -#include -#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; -} diff --git a/buttons.h b/buttons.h deleted file mode 100644 index 63791ac..0000000 --- a/buttons.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * File: buttons.h - * - * Button control - */ - - -#ifndef _BUTTONS_H -#define _BUTTONS_H - -#include - -#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 diff --git a/isr.c b/isr.c index f466c55..7829145 100644 --- a/isr.c +++ b/isr.c @@ -7,6 +7,8 @@ #include #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() */ @@ -14,4 +16,16 @@ 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 } diff --git a/main.c b/main.c index 46fe66a..637879f 100644 --- a/main.c +++ b/main.c @@ -29,10 +29,11 @@ #include #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 */ @@ -41,8 +42,6 @@ #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] + \ @@ -67,7 +66,6 @@ typedef struct { 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 @@ -82,123 +80,189 @@ const static int min_fade_steps[4] = { 64, 32, 16, 8 }; 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); +} + diff --git a/task.c b/task.c new file mode 100644 index 0000000..a348924 --- /dev/null +++ b/task.c @@ -0,0 +1,58 @@ +/* + * 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 +#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(); +} + diff --git a/task.h b/task.h new file mode 100644 index 0000000..f95d715 --- /dev/null +++ b/task.h @@ -0,0 +1,63 @@ +/* + * 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 diff --git a/task_defs.h b/task_defs.h new file mode 100644 index 0000000..2a43168 --- /dev/null +++ b/task_defs.h @@ -0,0 +1,42 @@ +/* + * 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 diff --git a/tmr.h b/tmr.h index 5e55431..d748e94 100644 --- a/tmr.h +++ b/tmr.h @@ -33,7 +33,6 @@ * } */ - #ifndef _TMR_H #define _TMR_H diff --git a/tmr_defs.h b/tmr_defs.h index 830da3e..ea94e9c 100644 --- a/tmr_defs.h +++ b/tmr_defs.h @@ -14,12 +14,12 @@ * 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 };