From 5fa3659d382d2055d1bdb1c06b6b82dcfc68bb95 Mon Sep 17 00:00:00 2001 From: "R. Steve McKown" Date: Thu, 8 Dec 2011 14:34:04 -0700 Subject: [PATCH] Incorporate new tmr and isr code --- bit.h | 20 +++++++ isr.c | 17 ++++++ isr.h | 36 +++++++++++++ main.c | 24 ++++++--- picinit.c | 2 +- picinit.h | 2 +- rgb.c | 4 +- rgb.h | 4 ++ timer.c | 41 -------------- timer.h | 53 ------------------ tmr.c | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tmr.h | 105 ++++++++++++++++++++++++++++++++++++ tmr_defs.h | 39 ++++++++++++++ 13 files changed, 396 insertions(+), 106 deletions(-) create mode 100644 bit.h create mode 100644 isr.c create mode 100644 isr.h delete mode 100644 timer.c delete mode 100644 timer.h create mode 100644 tmr.c create mode 100644 tmr.h create mode 100644 tmr_defs.h 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 -- 2.39.2