X-Git-Url: http://git.rrze.uni-erlangen.de/gitweb/?p=moodlight.git;a=blobdiff_plain;f=ledpwm.c;fp=ledpwm.c;h=d4aa5e298c46edb4c2752ff74aad95f53e0e267d;hp=b9ef73cb8dcc15e83c03ad71da83034a2d703ba6;hb=4307b86abbc1664bf3de89518962491a9e491653;hpb=e001c0863c4fee6b80e36464cdbd05b0c8ce25a9 diff --git a/ledpwm.c b/ledpwm.c index b9ef73c..d4aa5e2 100644 --- a/ledpwm.c +++ b/ledpwm.c @@ -1,107 +1,100 @@ -/* $Id: ledpwm.c,v 1.3 2010/07/10 07:34:51 simimeie Exp $ +/* $Id: ledpwm.c,v 1.4 2010/07/25 20:40:44 simimeie Exp $ * Functions for led brightness control via PWM (pulse width modulation). */ #include +#include #include "ledpwm.h" -/* Select between Phase correct PWM and Fast PWM mode. - * setting this to 1 selects phase correct mode, everything else is Fast PWM. */ -#define PHASECORRECTPWM 1 - /* - * Our hardware connections are as follows: - * Red -> PD6 / OC0A - * Green -> PD5 / OC0B - * Blue -> PD3 / OC2B + * Our hardware connections on the first prototype are as follows: + * Red -> PD6 (OC0A) + * Green -> PD5 (OC0B) + * Blue -> PD3 (OC2B) CHANGED umgeloetet durch Julian jetzt PD7 + * In the next hardware revision this will be: + * Red -> PC3 + * Green -> PC2 + * Blue -> PC1 */ +#define LEDPORT PORTD +#define LEDDDR DDRD +#define LEDPINR 6 +#define LEDPING 5 +#define LEDPINB 7 + 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; -void ledpwm_init(void) -{ - /* Set OC2B, OC0A and OC0B to output */ - DDRD |= _BV(3) | _BV(5) | _BV(6); - /* Set compare output mode for OC2B in timer counter control register: */ -#if (PHASECORRECTPWM == 1) - /* select phase correct PWM mode (1). WGM2 bit is 0 so we don't need to touch TCCR2B. */ - TCCR2A |= _BV(COM2B1) | _BV(WGM20); -#else /* not PHASECORRECTPWM */ - /* set output at BOTTOM (=0), clear it on compare match. - * select fast PWM mode 3. WGM2 bit is 0 so we don't need to touch TCCR2B. */ - TCCR2A |= _BV(COM2B1) | _BV(WGM20) /*| _BV(WGM21)*/; -#endif /* PHASECORRECTPWM */ - /* select clock source: cpu clock without prescaler. */ - TCCR2B |= _BV(CS20); - /* Default brightness: PWM 0 */ - OCR2B = 0; - /* Set compare output mode for OC0A and OC0B in timer counter control register: */ -#if (PHASECORRECTPWM == 1) - /* select phase correct PWM mode (1). WGM2 bit is 0 so we don't need to touch TCCR0B. */ - TCCR0A |= _BV(COM0A1) | _BV(COM0B1) | _BV(WGM00); -#else /* not PHASECORRECTPWM */ - /* set output at BOTTOM (=0), clear it on compare match. - * select fast PWM mode 3. WGM2 bit is 0 so we don't need to touch TCCR0B. */ - TCCR0A |= _BV(COM0A1) | _BV(COM0B1) | _BV(WGM00) | _BV(WGM01); -#endif /* PHASECORRECTPWM */ - /* select clock source: i/o clock without prescaler. */ - TCCR0B |= _BV(CS20); - /* Default brightness: PWM 0 */ - OCR0A = 0; - OCR0B = 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; + uint8_t i; + for (i = 0; i < 3; i++) { + if ((ledpwm_val[i] > ledpwm_curoreg) && (ledpwm_val[i] <= nextval)) { + nextval = ledpwm_val[i]; + } + } + OCR1AH = nextval >> 8; + OCR1AL = nextval & 0xff; + ledpwm_curoreg = nextval; } -/* In none-phase-correct PWM mode, This function does some mapping of - * the values: - * 0 really turns the LED off. - * 1 is an PWM value of 0, which still lets the LED glimmer. - * 2 to 254 are mapped to PWM 3 to 253 (always one less!) - * 255 is mapped to PWM 255. - * So there is no way to set PWM 254, but you cannot visually see the - * difference to 253 or 255 anyways... - * In phase-correct PWM mode there does not need to be a mapping, 0 means off. - */ -void ledpwm_setled(uint8_t led, uint8_t val) +/* 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); + } + if (ledpwm_val[LEDPWM_GREENLED] > 0) { + LEDPORT |= _BV(LEDPING); + } + if (ledpwm_val[LEDPWM_BLUELED] > 0) { + LEDPORT |= _BV(LEDPINB); + } + ledpwm_curoreg = 0; + ledpwm_programnextstep(); +} + +/* Called on compare match */ +ISR(TIMER1_COMPA_vect) { -#if (PHASECORRECTPWM == 1) - switch (led) { - case LEDPWM_REDLED: OCR0A = val; break; - case LEDPWM_GREENLED: OCR0B = val; break; - case LEDPWM_BLUELED: OCR2B = val; break; - }; -#else /* not PHASECORRECTPWM */ - if (val == 0) { - switch (led) { - case LEDPWM_REDLED: DDRD &= (uint8_t)~_BV(6); - break; - case LEDPWM_GREENLED: DDRD &= (uint8_t)~_BV(5); - break; - case LEDPWM_BLUELED: DDRD &= (uint8_t)~_BV(3); - break; - }; - } else { - val -= 1; - if (val == 254) { - val = 255; - } - switch (led) { - case LEDPWM_REDLED: DDRD |= _BV(6); - OCR0A = val; - break; - case LEDPWM_GREENLED: DDRD |= _BV(5); - OCR0B = val; - break; - case LEDPWM_BLUELED: DDRD |= _BV(3); - OCR2B = val; - break; - }; + if (ledpwm_val[LEDPWM_REDLED] <= ledpwm_curoreg) { + LEDPORT &= (uint8_t)~_BV(LEDPINR); + } + if (ledpwm_val[LEDPWM_GREENLED] <= ledpwm_curoreg) { + LEDPORT &= (uint8_t)~_BV(LEDPING); + } + if (ledpwm_val[LEDPWM_BLUELED] <= ledpwm_curoreg) { + LEDPORT &= (uint8_t)~_BV(LEDPINB); } -#endif /* PHASECORRECTPWM */ + ledpwm_programnextstep(); +} + +void ledpwm_init(void) { + /* Set our Port Pins to output */ + LEDDDR |= _BV(LEDPINR) | _BV(LEDPING) | _BV(LEDPINB); + /* DO NOT initialize TIMER1 frequency and overflow interrupt. + * timers.c already does that. */ + /* Enable TIMER1 output compare interrupt A */ + TIMSK1 |= _BV(OCIE1A); + ledpwm_programnextstep(); +} + +void ledpwm_setled(uint8_t led, uint16_t val) { + if (led > 2) { return; /* ignore invalid values */ } + ledpwm_val[led] = val; + ledpwm_programnextstep(); } void ledpwm_set(uint8_t red, uint8_t green, uint8_t blue, uint8_t br) { - ledpwm_setled(LEDPWM_REDLED, (((uint16_t)red * br) / 255)); - ledpwm_setled(LEDPWM_GREENLED,(((uint16_t)green * br) / 255)); - ledpwm_setled(LEDPWM_BLUELED, (((uint16_t)blue * br) / 255)); + ledpwm_val[LEDPWM_REDLED] = red * br; + ledpwm_val[LEDPWM_GREENLED] = green * br; + ledpwm_val[LEDPWM_BLUELED] = blue * br; + ledpwm_programnextstep(); }