From: R. Steve McKown Date: Thu, 8 Dec 2011 21:34:04 +0000 (-0700) Subject: Incorporate new tmr and isr code X-Git-Tag: 1.0~38 X-Git-Url: https://oss.titaniummirror.com/gitweb?p=rgblamp.git;a=commitdiff_plain;h=5fa3659d382d2055d1bdb1c06b6b82dcfc68bb95 Incorporate new tmr and isr code --- diff --git a/bit.h b/bit.h new file mode 100644 index 0000000..fa3c03d --- /dev/null +++ b/bit.h @@ -0,0 +1,20 @@ +/* + * 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 diff --git a/isr.c b/isr.c new file mode 100644 index 0000000..f466c55 --- /dev/null +++ b/isr.c @@ -0,0 +1,17 @@ +/* + * File: isr.c + * + * Interrupt handling routines + */ + + +#include +#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(); +} diff --git a/isr.h b/isr.h new file mode 100644 index 0000000..7b1ede0 --- /dev/null +++ b/isr.h @@ -0,0 +1,36 @@ +/* + * File: isr.h + * + * Interrupt handling routines + */ + +#ifndef _ISR_H +#define _ISR_H + +#include + +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 diff --git a/main.c b/main.c index fbf88d3..65cff55 100644 --- a/main.c +++ b/main.c @@ -31,10 +31,16 @@ #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) @@ -52,8 +58,9 @@ 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; @@ -90,7 +97,7 @@ int main(void) unused_init(); buttons_init(); rgb_init(); - timer_init(); + tmr_init(); dbgpin_init(); srand((adc_random() << 8) + adc_random()); @@ -99,13 +106,14 @@ int main(void) 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. @@ -174,7 +182,7 @@ int main(void) 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(); } } diff --git a/picinit.c b/picinit.c index 882aaaa..4274e2a 100644 --- a/picinit.c +++ b/picinit.c @@ -13,7 +13,7 @@ __CONFIG(LVP_OFF); 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); diff --git a/picinit.h b/picinit.h index 1925003..e2a50cf 100644 --- a/picinit.h +++ b/picinit.h @@ -7,7 +7,7 @@ #ifndef _PICINIT_H #define _PICINIT_H -#define _XTAL_FREQ 2000000 +#define _XTAL_FREQ 4000000 void pic_init(); diff --git a/rgb.c b/rgb.c index 13c36da..8525442 100644 --- a/rgb.c +++ b/rgb.c @@ -14,9 +14,9 @@ void rgb_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. */ diff --git a/rgb.h b/rgb.h index 663e189..d24b41b 100644 --- a/rgb.h +++ b/rgb.h @@ -8,6 +8,8 @@ #ifndef _RGB_H #define _RGB_H +#include "isr.h" + /* Initialize the RGB LED assembly. Outputs are zero, PWM is off. */ void rgb_init(); @@ -19,10 +21,12 @@ void rgb_off(); /* 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 diff --git a/timer.c b/timer.c deleted file mode 100644 index 0975cb8..0000000 --- a/timer.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * File: timer.c - * - * Timer 0 + Timer 1 for timekeeping - */ - - -#include -#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); -} diff --git a/timer.h b/timer.h deleted file mode 100644 index 20e0c8b..0000000 --- a/timer.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 diff --git a/tmr.c b/tmr.c new file mode 100644 index 0000000..fec23f9 --- /dev/null +++ b/tmr.c @@ -0,0 +1,155 @@ +/* + * 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 +#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); +} diff --git a/tmr.h b/tmr.h new file mode 100644 index 0000000..716ff65 --- /dev/null +++ b/tmr.h @@ -0,0 +1,105 @@ +/* + * 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 diff --git a/tmr_defs.h b/tmr_defs.h new file mode 100644 index 0000000..34ffda5 --- /dev/null +++ b/tmr_defs.h @@ -0,0 +1,39 @@ +/* + * 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