- add LED control with PWM
[moodlight.git] / ledpwm.c
1 /* $Id: ledpwm.c,v 1.1 2010/06/26 19:08:18 simimeie Exp $
2  * Functions for led brightness control via PWM (pulse width modulation).
3  */
4
5 #include <avr/io.h>
6 #include "ledpwm.h"
7
8 /* Select between Phase correct PWM and Fast PWM mode.
9  * setting this to 1 selects phase correct mode, everything else is Fast PWM. */
10 #define PHASECORRECTPWM 1
11 /*
12  * Our hardware connections are as follows:
13  *  Red -> PD6 / OC0A
14  *  Green -> PD5 / OC0B
15  *  Blue -> PD3 / OC2B
16  */
17 void ledpwm_init(void)
18 {
19   /* Set OC2B, OC0A and OC0B to output */
20   DDRD |= _BV(PD3) | _BV(PD5) | _BV(PD6);
21   /* Set compare output mode for OC2B in timer counter control register: */
22 #if (PHASECORRECTPWM == 1)
23   /* select phase correct PWM mode (1). WGM2 bit is 0 so we don't need to touch TCCR2B. */
24   TCCR2A |= _BV(COM2B1) | _BV(WGM20);
25 #else  /* not PHASECORRECTPWM */
26   /* set output at BOTTOM (=0), clear it on compare match.
27    * select fast PWM mode 3. WGM2 bit is 0 so we don't need to touch TCCR2B. */
28   TCCR2A |= _BV(COM2B1) | _BV(WGM20) /*| _BV(WGM21)*/;
29 #endif /* PHASECORRECTPWM */
30   /* select clock source: cpu clock without prescaler. */
31   TCCR2B |= _BV(CS20);
32   /* Default brightness: PWM 0 */
33   OCR2B = 0;
34   /* Set compare output mode for OC0A and OC0B in timer counter control register: */
35 #if (PHASECORRECTPWM == 1)
36   /* select phase correct PWM mode (1). WGM2 bit is 0 so we don't need to touch TCCR0B. */
37   TCCR0A |= _BV(COM0A1) | _BV(COM0B1) | _BV(WGM00);
38 #else  /* not PHASECORRECTPWM */
39   /* set output at BOTTOM (=0), clear it on compare match.
40    * select fast PWM mode 3. WGM2 bit is 0 so we don't need to touch TCCR0B. */
41   TCCR0A |= _BV(COM0A1) | _BV(COM0B1) | _BV(WGM00) | _BV(WGM01);
42 #endif /* PHASECORRECTPWM */
43   /* select clock source: i/o clock without prescaler. */
44   TCCR0B |= _BV(CS20);
45   /* Default brightness: PWM 0 */
46   OCR0A = 0;
47   OCR0B = 0;
48 }
49
50 /* In none-phase-correct PWM mode, This function does some mapping of
51  * the values:
52  * 0 really turns the LED off.
53  * 1 is an PWM value of 0, which still lets the LED glimmer.
54  * 2 to 254 are mapped to PWM 3 to 253 (always one less!)
55  * 255 is mapped to PWM 255.
56  * So there is no way to set PWM 254, but you cannot visually see the
57  * difference to 253 or 255 anyways...
58  * In phase-correct PWM mode there does not need to be a mapping, 0 means off.
59  */
60 void ledpwm_setled(uint8_t led, uint8_t val)
61 {
62 #if (PHASECORRECTPWM == 1)
63   switch (led) {
64   case LEDPWM_REDLED:   OCR0A = val; break;
65   case LEDPWM_GREENLED: OCR0B = val; break;
66   case LEDPWM_BLUELED:  OCR2B = val; break;
67   };
68 #else /* not PHASECORRECTPWM */
69   if (val == 0) {
70     switch (led) {
71     case LEDPWM_REDLED:   DDRD &= (uint8_t)~_BV(PD6);
72                           break;
73     case LEDPWM_GREENLED: DDRD &= (uint8_t)~_BV(PD5);
74                           break;
75     case LEDPWM_BLUELED:  DDRD &= (uint8_t)~_BV(PD3);
76                           break;
77     };
78   } else {
79     val -= 1;
80     if (val == 254) {
81       val = 255;
82     }
83     switch (led) {
84     case LEDPWM_REDLED:   DDRD |= _BV(PD6);
85                           OCR0A = val;
86                           break;
87     case LEDPWM_GREENLED: DDRD |= _BV(PD5);
88                           OCR0B = val;
89                           break;
90     case LEDPWM_BLUELED:  DDRD |= _BV(PD3);
91                           OCR2B = val;
92                           break;
93     };
94   }
95 #endif /* PHASECORRECTPWM */
96 }
This page took 0.046942 seconds and 3 git commands to generate.