de0e300b |
1 | /* $Id: ledpwm.c,v 1.3 2010/07/10 07:34:51 simimeie Exp $ |
17aea8ef |
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 |
ddf1553f |
11 | |
17aea8ef |
12 | /* |
13 | * Our hardware connections are as follows: |
14 | * Red -> PD6 / OC0A |
15 | * Green -> PD5 / OC0B |
16 | * Blue -> PD3 / OC2B |
17 | */ |
ddf1553f |
18 | |
19 | uint8_t ledpwm_re = 0xff, ledpwm_gr = 0xff, ledpwm_bl = 0xff; |
20 | uint8_t ledpwm_bri = 128; |
21 | |
17aea8ef |
22 | void ledpwm_init(void) |
23 | { |
24 | /* Set OC2B, OC0A and OC0B to output */ |
de0e300b |
25 | DDRD |= _BV(3) | _BV(5) | _BV(6); |
17aea8ef |
26 | /* Set compare output mode for OC2B in timer counter control register: */ |
27 | #if (PHASECORRECTPWM == 1) |
28 | /* select phase correct PWM mode (1). WGM2 bit is 0 so we don't need to touch TCCR2B. */ |
29 | TCCR2A |= _BV(COM2B1) | _BV(WGM20); |
30 | #else /* not PHASECORRECTPWM */ |
31 | /* set output at BOTTOM (=0), clear it on compare match. |
32 | * select fast PWM mode 3. WGM2 bit is 0 so we don't need to touch TCCR2B. */ |
33 | TCCR2A |= _BV(COM2B1) | _BV(WGM20) /*| _BV(WGM21)*/; |
34 | #endif /* PHASECORRECTPWM */ |
35 | /* select clock source: cpu clock without prescaler. */ |
36 | TCCR2B |= _BV(CS20); |
37 | /* Default brightness: PWM 0 */ |
38 | OCR2B = 0; |
39 | /* Set compare output mode for OC0A and OC0B in timer counter control register: */ |
40 | #if (PHASECORRECTPWM == 1) |
41 | /* select phase correct PWM mode (1). WGM2 bit is 0 so we don't need to touch TCCR0B. */ |
42 | TCCR0A |= _BV(COM0A1) | _BV(COM0B1) | _BV(WGM00); |
43 | #else /* not PHASECORRECTPWM */ |
44 | /* set output at BOTTOM (=0), clear it on compare match. |
45 | * select fast PWM mode 3. WGM2 bit is 0 so we don't need to touch TCCR0B. */ |
46 | TCCR0A |= _BV(COM0A1) | _BV(COM0B1) | _BV(WGM00) | _BV(WGM01); |
47 | #endif /* PHASECORRECTPWM */ |
48 | /* select clock source: i/o clock without prescaler. */ |
49 | TCCR0B |= _BV(CS20); |
50 | /* Default brightness: PWM 0 */ |
51 | OCR0A = 0; |
52 | OCR0B = 0; |
53 | } |
54 | |
55 | /* In none-phase-correct PWM mode, This function does some mapping of |
56 | * the values: |
57 | * 0 really turns the LED off. |
58 | * 1 is an PWM value of 0, which still lets the LED glimmer. |
59 | * 2 to 254 are mapped to PWM 3 to 253 (always one less!) |
60 | * 255 is mapped to PWM 255. |
61 | * So there is no way to set PWM 254, but you cannot visually see the |
62 | * difference to 253 or 255 anyways... |
63 | * In phase-correct PWM mode there does not need to be a mapping, 0 means off. |
64 | */ |
65 | void ledpwm_setled(uint8_t led, uint8_t val) |
66 | { |
67 | #if (PHASECORRECTPWM == 1) |
68 | switch (led) { |
69 | case LEDPWM_REDLED: OCR0A = val; break; |
70 | case LEDPWM_GREENLED: OCR0B = val; break; |
71 | case LEDPWM_BLUELED: OCR2B = val; break; |
72 | }; |
73 | #else /* not PHASECORRECTPWM */ |
74 | if (val == 0) { |
75 | switch (led) { |
de0e300b |
76 | case LEDPWM_REDLED: DDRD &= (uint8_t)~_BV(6); |
17aea8ef |
77 | break; |
de0e300b |
78 | case LEDPWM_GREENLED: DDRD &= (uint8_t)~_BV(5); |
17aea8ef |
79 | break; |
de0e300b |
80 | case LEDPWM_BLUELED: DDRD &= (uint8_t)~_BV(3); |
17aea8ef |
81 | break; |
82 | }; |
83 | } else { |
84 | val -= 1; |
85 | if (val == 254) { |
86 | val = 255; |
87 | } |
88 | switch (led) { |
de0e300b |
89 | case LEDPWM_REDLED: DDRD |= _BV(6); |
17aea8ef |
90 | OCR0A = val; |
91 | break; |
de0e300b |
92 | case LEDPWM_GREENLED: DDRD |= _BV(5); |
17aea8ef |
93 | OCR0B = val; |
94 | break; |
de0e300b |
95 | case LEDPWM_BLUELED: DDRD |= _BV(3); |
17aea8ef |
96 | OCR2B = val; |
97 | break; |
98 | }; |
99 | } |
100 | #endif /* PHASECORRECTPWM */ |
101 | } |
de0e300b |
102 | |
103 | void ledpwm_set(uint8_t red, uint8_t green, uint8_t blue, uint8_t br) { |
104 | ledpwm_setled(LEDPWM_REDLED, (((uint16_t)red * br) / 255)); |
105 | ledpwm_setled(LEDPWM_GREENLED,(((uint16_t)green * br) / 255)); |
106 | ledpwm_setled(LEDPWM_BLUELED, (((uint16_t)blue * br) / 255)); |
107 | } |