+/* $Id: ledpwm.c,v 1.1 2010/06/26 19:08:18 simimeie Exp $
+ * Functions for led brightness control via PWM (pulse width modulation).
+ */
+
+#include <avr/io.h>
+#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
+ */
+void ledpwm_init(void)
+{
+ /* Set OC2B, OC0A and OC0B to output */
+ DDRD |= _BV(PD3) | _BV(PD5) | _BV(PD6);
+ /* 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;
+}
+
+/* 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)
+{
+#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(PD6);
+ break;
+ case LEDPWM_GREENLED: DDRD &= (uint8_t)~_BV(PD5);
+ break;
+ case LEDPWM_BLUELED: DDRD &= (uint8_t)~_BV(PD3);
+ break;
+ };
+ } else {
+ val -= 1;
+ if (val == 254) {
+ val = 255;
+ }
+ switch (led) {
+ case LEDPWM_REDLED: DDRD |= _BV(PD6);
+ OCR0A = val;
+ break;
+ case LEDPWM_GREENLED: DDRD |= _BV(PD5);
+ OCR0B = val;
+ break;
+ case LEDPWM_BLUELED: DDRD |= _BV(PD3);
+ OCR2B = val;
+ break;
+ };
+ }
+#endif /* PHASECORRECTPWM */
+}