17aea8ef |
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 | } |