X-Git-Url: http://git.rrze.uni-erlangen.de/gitweb/?p=moodlight.git;a=blobdiff_plain;f=ledpwm.c;h=30472f4f3800eec7c48f03a575195cb7637448d6;hp=dc8b7bddf5e894308dd4c061d72a5139271861d3;hb=b7a7cffc73b28738142834116f4617ac85fc92f1;hpb=6c58993de6a9677e0503810e2481f0ecf97e6640 diff --git a/ledpwm.c b/ledpwm.c index dc8b7bd..30472f4 100644 --- a/ledpwm.c +++ b/ledpwm.c @@ -1,4 +1,4 @@ -/* $Id: ledpwm.c,v 1.5 2010/08/08 17:06:11 simimeie Exp $ +/* $Id: ledpwm.c,v 1.6 2010/08/08 18:09:40 simimeie Exp $ * Functions for led brightness control via PWM (pulse width modulation). */ @@ -27,65 +27,104 @@ uint8_t ledpwm_re = 0xff, ledpwm_gr = 0xff, ledpwm_bl = 0xff; uint8_t ledpwm_bri = 128; /* Internal */ volatile uint16_t ledpwm_val[3] = { 0x8000, 0x8000, 0x8000 }; -volatile uint16_t ledpwm_curoreg = 0; +typedef struct { + uint16_t atcntval; /* at this counter value */ + uint8_t orval; /* or this value onto the output */ +} ledpwm_microprog; +ledpwm_microprog ledpwm_mprogs[2][10]; +volatile uint8_t ledpwm_activemprog = 0; +volatile ledpwm_microprog * ledpwm_amprogptr = &ledpwm_mprogs[0][0]; + /* To do a 16-bit write, the high byte must be written before the low byte. * For a 16-bit read, the low byte must be read before the high byte */ -static void ledpwm_programnextstep(void) { - uint16_t nextval = 0xffff; - uint16_t tmp1 = ledpwm_curoreg; /* Copy so the compiler can place it into - * a register despite the volatile. */ - uint8_t i; + +static inline uint16_t getnextminimum(uint16_t curmin) { + uint16_t res = 0xffff; + uint8_t i; uint8_t found = 0; for (i = 0; i < 3; i++) { - if ((ledpwm_val[i] > tmp1) && (ledpwm_val[i] <= nextval)) { - nextval = ledpwm_val[i]; + if ((ledpwm_val[i] > curmin) && (ledpwm_val[i] <= res)) { + res = ledpwm_val[i]; + found = 1; } } - /* tmp1 now reused */ - tmp1 = TCNT1L; - tmp1 |= (uint16_t)TCNT1H << 8; - if ((tmp1 + 30) > nextval) { /* 30 cycles is more or less a guess. - * This needs to be set to the number of cycles the code takes from - * here until the iret. */ - nextval = tmp1 + 30; + if (found) { + return res; + } else { + return 0; /* We already had the maximum, no higher values there (EOL) */ } - OCR1AH = nextval >> 8; - OCR1AL = nextval & 0xff; - ledpwm_curoreg = nextval; } -/* This gets called from timers.c which holds the 'main' (interrupt) - * handler for the timer1 overflow. */ -void ledpwm_TIMER1OVF_hook(void) { - /* Turn on all LEDs */ - if (ledpwm_val[LEDPWM_REDLED] > 0) { - LEDPORT |= _BV(LEDPINR); +static void ledpwm_recalculateprogram(void) { + uint8_t nextprog = !ledpwm_activemprog; + uint8_t i = 0; + uint16_t nextminimum = 0; + uint8_t orval; + + do { + orval = 0; + if (ledpwm_val[LEDPWM_REDLED] > nextminimum) { + orval |= _BV(LEDPINR); + } + if (ledpwm_val[LEDPWM_GREENLED] > nextminimum) { + orval |= _BV(LEDPING); + } + if (ledpwm_val[LEDPWM_BLUELED] > nextminimum) { + orval |= _BV(LEDPINB); + } + ledpwm_mprogs[nextprog][i].atcntval = nextminimum; + ledpwm_mprogs[nextprog][i].orval = orval; + /* now calculate nextminimum */ + nextminimum = getnextminimum(nextminimum); + i++; + } while (nextminimum > 0); + while (i < 10) { /* Fill the rest with dummys that cause no harm */ + ledpwm_mprogs[nextprog][i].atcntval = 0; + ledpwm_mprogs[nextprog][i].orval = 0; + i++; } - if (ledpwm_val[LEDPWM_GREENLED] > 0) { - LEDPORT |= _BV(LEDPING); + /* activate the freshly calculated microprogram */ + ledpwm_activemprog = nextprog; + return; +} + +static inline void programnextpwmstep(void) { + uint16_t curcnt; + curcnt = TCNT1L; + curcnt |= (uint16_t)TCNT1H << 8; + while (((curcnt + 40) > ledpwm_amprogptr->atcntval) && (ledpwm_amprogptr->atcntval > 0)) { + /* Something to do in the next few CPU cycles, so wait for it! */ + if (curcnt >= ledpwm_amprogptr->atcntval) { /* Execute! */ + LEDPORT = (LEDPORT & (uint8_t)~(_BV(LEDPINR) | _BV(LEDPING) | _BV(LEDPINB))) | ledpwm_amprogptr->orval; + ledpwm_amprogptr++; + } + curcnt = TCNT1L; + curcnt |= (uint16_t)TCNT1H << 8; } - if (ledpwm_val[LEDPWM_BLUELED] > 0) { - LEDPORT |= _BV(LEDPINB); + /* Now program the overflow interrupt */ + if (ledpwm_amprogptr->atcntval > 0) { + uint16_t nextval = ledpwm_amprogptr->atcntval; + OCR1AH = nextval >> 8; + OCR1AL = nextval & 0xff; } - ledpwm_curoreg = 0; - ledpwm_programnextstep(); +} + +/* This gets called from timers.c which holds the 'main' (interrupt) + * handler for the timer1 overflow. */ +void ledpwm_TIMER1OVF_hook(void) { + /* (Re-)Start microprogram */ + ledpwm_amprogptr = &ledpwm_mprogs[ledpwm_activemprog][0]; + LEDPORT = (LEDPORT & (uint8_t)~(_BV(LEDPINR) | _BV(LEDPING) | _BV(LEDPINB))) | ledpwm_amprogptr->orval; + ledpwm_amprogptr++; + programnextpwmstep(); } /* Called on compare match */ ISR(TIMER1_COMPA_vect) { - uint8_t orval = 0x00; - if (ledpwm_val[LEDPWM_REDLED] > ledpwm_curoreg) { - orval |= _BV(LEDPINR); - } - if (ledpwm_val[LEDPWM_GREENLED] > ledpwm_curoreg) { - orval |= _BV(LEDPING); - } - if (ledpwm_val[LEDPWM_BLUELED] > ledpwm_curoreg) { - orval |= _BV(LEDPINB); - } - LEDPORT = (LEDPORT & (uint8_t)~(_BV(LEDPINR) | _BV(LEDPING) | _BV(LEDPINB))) | orval; - ledpwm_programnextstep(); + LEDPORT = (LEDPORT & (uint8_t)~(_BV(LEDPINR) | _BV(LEDPING) | _BV(LEDPINB))) | ledpwm_amprogptr->orval; + ledpwm_amprogptr++; + programnextpwmstep(); } void ledpwm_init(void) { @@ -95,15 +134,18 @@ void ledpwm_init(void) { * timers.c already does that. */ /* Enable TIMER1 output compare interrupt A */ TIMSK1 |= _BV(OCIE1A); + ledpwm_recalculateprogram(); } void ledpwm_setled(uint8_t led, uint16_t val) { if (led > 2) { return; /* ignore invalid values */ } ledpwm_val[led] = val; + ledpwm_recalculateprogram(); } void ledpwm_set(uint8_t red, uint8_t green, uint8_t blue, uint8_t br) { ledpwm_val[LEDPWM_REDLED] = red * br; ledpwm_val[LEDPWM_GREENLED] = green * br; ledpwm_val[LEDPWM_BLUELED] = blue * br; + ledpwm_recalculateprogram(); }