- add LED control with PWM
[moodlight.git] / ledpwm.c
diff --git a/ledpwm.c b/ledpwm.c
new file mode 100644 (file)
index 0000000..dfc26d6
--- /dev/null
+++ b/ledpwm.c
@@ -0,0 +1,96 @@
+/* $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 */
+}
This page took 0.038265 seconds and 4 git commands to generate.