From: simimeie Date: Sun, 25 Jul 2010 20:40:44 +0000 (+0000) Subject: preliminary support for 16 bit pwm. The 16 bit counter ist still used for X-Git-Url: http://git.rrze.uni-erlangen.de/gitweb/?p=moodlight.git;a=commitdiff_plain;h=4307b86abbc1664bf3de89518962491a9e491653 preliminary support for 16 bit pwm. The 16 bit counter ist still used for precise timing info as well (e.g. for ir decoding). pwm support still is a little buggy - e.g. when values are too close together. --- diff --git a/console.c b/console.c index 8dff9f4..d884017 100644 --- a/console.c +++ b/console.c @@ -1,4 +1,4 @@ -/* $Id: console.c,v 1.4 2010/07/24 20:55:48 simimeie Exp $ +/* $Id: console.c,v 1.5 2010/07/25 20:40:44 simimeie Exp $ * Functions for a serial console. */ @@ -349,6 +349,7 @@ ISR(USART_RX_vect) { saveeepromsettings(); console_printpgm_noirq_P(PSTR("Settings written to EEPROM."));*/ } else if (strcmp_P(inputbuf, PSTR("status")) == 0) { + uint8_t v; console_printpgm_noirq_P(PSTR("Current status:\r\n")); console_printpgm_noirq_P(PSTR("red = ")); console_printhex8_noirq(ledpwm_re); @@ -358,34 +359,38 @@ ISR(USART_RX_vect) { console_printhex8_noirq(ledpwm_bl); console_printpgm_noirq_P(PSTR(", brightness = ")); console_printhex8_noirq(ledpwm_bri); + console_printpgm_noirq_P(PSTR("\r\nvalues ")); + for (v = 0; v < 3; v++) { + console_printhex8_noirq(ledpwm_val[v] >> 8); + console_printhex8_noirq(ledpwm_val[v] & 0xff); + console_printpgm_noirq_P(PSTR(" ")); + } } else if (strncmp_P(inputbuf, PSTR("r "), 2) == 0) { uint8_t v; v = strtoul(&inputbuf[2], NULL, 0); ledpwm_re = v; - ledpwm_setled(LEDPWM_REDLED, (((uint16_t)ledpwm_re * ledpwm_bri) / 255)); + ledpwm_setled(LEDPWM_REDLED, ((uint16_t)ledpwm_re * ledpwm_bri)); console_printpgm_noirq_P(PSTR("RED value set to 0x")); console_printhex8_noirq(v); } else if (strncmp_P(inputbuf, PSTR("g "), 2) == 0) { uint8_t v; v = strtoul(&inputbuf[2], NULL, 0); ledpwm_gr = v; - ledpwm_setled(LEDPWM_GREENLED, (((uint16_t)ledpwm_gr * ledpwm_bri) / 255)); + ledpwm_setled(LEDPWM_GREENLED, ((uint16_t)ledpwm_gr * ledpwm_bri)); console_printpgm_noirq_P(PSTR("GREEN value set to 0x")); console_printhex8_noirq(v); } else if (strncmp_P(inputbuf, PSTR("b "), 2) == 0) { uint8_t v; v = strtoul(&inputbuf[2], NULL, 0); ledpwm_bl = v; - ledpwm_setled(LEDPWM_BLUELED, (((uint16_t)ledpwm_bl * ledpwm_bri) / 255)); + ledpwm_setled(LEDPWM_BLUELED, ((uint16_t)ledpwm_bl * ledpwm_bri)); console_printpgm_noirq_P(PSTR("BLUE value set to 0x")); console_printhex8_noirq(v); } else if (strncmp_P(inputbuf, PSTR("bri "), 2) == 0) { uint8_t v; v = strtoul(&inputbuf[4], NULL, 0); ledpwm_bri = v; - ledpwm_setled(LEDPWM_REDLED, (((uint16_t)ledpwm_re * ledpwm_bri) / 255)); - ledpwm_setled(LEDPWM_GREENLED, (((uint16_t)ledpwm_gr * ledpwm_bri) / 255)); - ledpwm_setled(LEDPWM_BLUELED, (((uint16_t)ledpwm_bl * ledpwm_bri) / 255)); + ledpwm_set(ledpwm_re, ledpwm_gr, ledpwm_bl, ledpwm_bri); console_printpgm_noirq_P(PSTR("brightness set to 0x")); console_printhex8_noirq(v); } else if (strcmp_P(inputbuf, PSTR("rfstatus")) == 0) { 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(); } diff --git a/ledpwm.h b/ledpwm.h index 6d81319..b7065fd 100644 --- a/ledpwm.h +++ b/ledpwm.h @@ -1,24 +1,28 @@ -/* $Id: ledpwm.h,v 1.3 2010/07/10 07:34:51 simimeie Exp $ +/* $Id: ledpwm.h,v 1.4 2010/07/25 20:40:44 simimeie Exp $ * Functions for led brightness control via PWM (pulse width modulation). */ #ifndef _LEDPWM_H_ #define _LEDPWM_H_ -#define LEDPWM_REDLED 1 -#define LEDPWM_GREENLED 2 -#define LEDPWM_BLUELED 3 +#define LEDPWM_REDLED 0 +#define LEDPWM_GREENLED 1 +#define LEDPWM_BLUELED 2 extern uint8_t ledpwm_re; extern uint8_t ledpwm_gr; extern uint8_t ledpwm_bl; extern uint8_t ledpwm_bri; +/* Temporary for debugging - these should be internal */ +extern volatile uint16_t ledpwm_val[3]; +extern volatile uint16_t ledpwm_curoreg; + /* Init PWM (pins and timers) */ void ledpwm_init(void); /* Set brightness for a certain LED */ -void ledpwm_setled(uint8_t led, uint8_t val); +void ledpwm_setled(uint8_t led, uint16_t val); /* Set brightness for all LEDs */ void ledpwm_set(uint8_t red, uint8_t green, uint8_t blue, uint8_t br); diff --git a/main.c b/main.c index 3e66f1a..05d4b6b 100644 --- a/main.c +++ b/main.c @@ -1,4 +1,4 @@ -/* $Id: main.c,v 1.7 2010/07/24 20:55:48 simimeie Exp $ +/* $Id: main.c,v 1.8 2010/07/25 20:40:44 simimeie Exp $ * Main file for the HaWo moodlight. * This is the main file that glues it all together. It also contains all * functionality that is too small to require an extra file. @@ -166,9 +166,7 @@ int main(void) irstate = 0; break; }; - ledpwm_setled(LEDPWM_REDLED, (((uint16_t)ledpwm_re * ledpwm_bri) / 255)); - ledpwm_setled(LEDPWM_GREENLED, (((uint16_t)ledpwm_gr * ledpwm_bri) / 255)); - ledpwm_setled(LEDPWM_BLUELED, (((uint16_t)ledpwm_bl * ledpwm_bri) / 255)); + ledpwm_set(ledpwm_re, ledpwm_gr, ledpwm_bl, ledpwm_bri); } /* i++; console_printhex8(i); diff --git a/timers.c b/timers.c index 754db06..eb2f2fe 100644 --- a/timers.c +++ b/timers.c @@ -1,4 +1,4 @@ -/* $Id: timers.c,v 1.1 2010/06/27 22:18:26 simimeie Exp $ +/* $Id: timers.c,v 1.2 2010/07/25 20:40:44 simimeie Exp $ * Functions for timing. * This mainly allows you to get a timestamp value */ @@ -10,12 +10,13 @@ /* Internal ticks */ static volatile uint16_t ticks = 0; -/* FIXME TCNT1H TCNT1L */ +void ledpwm_TIMER1OVF_hook(void); /* Executes every 65536 cpu cycles when the timer overflows */ ISR(TIMER1_OVF_vect) { ticks++; + ledpwm_TIMER1OVF_hook(); } uint16_t getticks(void)