]>
Commit | Line | Data |
---|---|---|
b7a7cffc | 1 | /* $Id: ledpwm.c,v 1.6 2010/08/08 18:09:40 simimeie Exp $ |
17aea8ef | 2 | * Functions for led brightness control via PWM (pulse width modulation). |
3 | */ | |
4 | ||
5 | #include <avr/io.h> | |
4307b86a | 6 | #include <avr/interrupt.h> |
17aea8ef | 7 | #include "ledpwm.h" |
8 | ||
17aea8ef | 9 | /* |
4307b86a | 10 | * Our hardware connections on the first prototype are as follows: |
11 | * Red -> PD6 (OC0A) | |
12 | * Green -> PD5 (OC0B) | |
13 | * Blue -> PD3 (OC2B) CHANGED umgeloetet durch Julian jetzt PD7 | |
14 | * In the next hardware revision this will be: | |
15 | * Red -> PC3 | |
16 | * Green -> PC2 | |
17 | * Blue -> PC1 | |
17aea8ef | 18 | */ |
ddf1553f | 19 | |
4307b86a | 20 | #define LEDPORT PORTD |
21 | #define LEDDDR DDRD | |
22 | #define LEDPINR 6 | |
23 | #define LEDPING 5 | |
24 | #define LEDPINB 7 | |
25 | ||
ddf1553f | 26 | uint8_t ledpwm_re = 0xff, ledpwm_gr = 0xff, ledpwm_bl = 0xff; |
27 | uint8_t ledpwm_bri = 128; | |
4307b86a | 28 | /* Internal */ |
29 | volatile uint16_t ledpwm_val[3] = { 0x8000, 0x8000, 0x8000 }; | |
b7a7cffc | 30 | typedef struct { |
31 | uint16_t atcntval; /* at this counter value */ | |
32 | uint8_t orval; /* or this value onto the output */ | |
33 | } ledpwm_microprog; | |
34 | ledpwm_microprog ledpwm_mprogs[2][10]; | |
35 | volatile uint8_t ledpwm_activemprog = 0; | |
36 | volatile ledpwm_microprog * ledpwm_amprogptr = &ledpwm_mprogs[0][0]; | |
37 | ||
ddf1553f | 38 | |
4307b86a | 39 | /* To do a 16-bit write, the high byte must be written before the low byte. |
40 | * For a 16-bit read, the low byte must be read before the high byte */ | |
b7a7cffc | 41 | |
42 | static inline uint16_t getnextminimum(uint16_t curmin) { | |
43 | uint16_t res = 0xffff; | |
44 | uint8_t i; uint8_t found = 0; | |
4307b86a | 45 | for (i = 0; i < 3; i++) { |
b7a7cffc | 46 | if ((ledpwm_val[i] > curmin) && (ledpwm_val[i] <= res)) { |
47 | res = ledpwm_val[i]; | |
48 | found = 1; | |
4307b86a | 49 | } |
50 | } | |
b7a7cffc | 51 | if (found) { |
52 | return res; | |
53 | } else { | |
54 | return 0; /* We already had the maximum, no higher values there (EOL) */ | |
6c58993d | 55 | } |
17aea8ef | 56 | } |
57 | ||
b7a7cffc | 58 | static void ledpwm_recalculateprogram(void) { |
59 | uint8_t nextprog = !ledpwm_activemprog; | |
60 | uint8_t i = 0; | |
61 | uint16_t nextminimum = 0; | |
62 | uint8_t orval; | |
63 | ||
64 | do { | |
65 | orval = 0; | |
66 | if (ledpwm_val[LEDPWM_REDLED] > nextminimum) { | |
67 | orval |= _BV(LEDPINR); | |
68 | } | |
69 | if (ledpwm_val[LEDPWM_GREENLED] > nextminimum) { | |
70 | orval |= _BV(LEDPING); | |
71 | } | |
72 | if (ledpwm_val[LEDPWM_BLUELED] > nextminimum) { | |
73 | orval |= _BV(LEDPINB); | |
74 | } | |
75 | ledpwm_mprogs[nextprog][i].atcntval = nextminimum; | |
76 | ledpwm_mprogs[nextprog][i].orval = orval; | |
77 | /* now calculate nextminimum */ | |
78 | nextminimum = getnextminimum(nextminimum); | |
79 | i++; | |
80 | } while (nextminimum > 0); | |
81 | while (i < 10) { /* Fill the rest with dummys that cause no harm */ | |
82 | ledpwm_mprogs[nextprog][i].atcntval = 0; | |
83 | ledpwm_mprogs[nextprog][i].orval = 0; | |
84 | i++; | |
4307b86a | 85 | } |
b7a7cffc | 86 | /* activate the freshly calculated microprogram */ |
87 | ledpwm_activemprog = nextprog; | |
88 | return; | |
89 | } | |
90 | ||
91 | static inline void programnextpwmstep(void) { | |
92 | uint16_t curcnt; | |
93 | curcnt = TCNT1L; | |
94 | curcnt |= (uint16_t)TCNT1H << 8; | |
95 | while (((curcnt + 40) > ledpwm_amprogptr->atcntval) && (ledpwm_amprogptr->atcntval > 0)) { | |
96 | /* Something to do in the next few CPU cycles, so wait for it! */ | |
97 | if (curcnt >= ledpwm_amprogptr->atcntval) { /* Execute! */ | |
98 | LEDPORT = (LEDPORT & (uint8_t)~(_BV(LEDPINR) | _BV(LEDPING) | _BV(LEDPINB))) | ledpwm_amprogptr->orval; | |
99 | ledpwm_amprogptr++; | |
100 | } | |
101 | curcnt = TCNT1L; | |
102 | curcnt |= (uint16_t)TCNT1H << 8; | |
4307b86a | 103 | } |
b7a7cffc | 104 | /* Now program the overflow interrupt */ |
105 | if (ledpwm_amprogptr->atcntval > 0) { | |
106 | uint16_t nextval = ledpwm_amprogptr->atcntval; | |
107 | OCR1AH = nextval >> 8; | |
108 | OCR1AL = nextval & 0xff; | |
4307b86a | 109 | } |
b7a7cffc | 110 | } |
111 | ||
112 | /* This gets called from timers.c which holds the 'main' (interrupt) | |
113 | * handler for the timer1 overflow. */ | |
114 | void ledpwm_TIMER1OVF_hook(void) { | |
115 | /* (Re-)Start microprogram */ | |
116 | ledpwm_amprogptr = &ledpwm_mprogs[ledpwm_activemprog][0]; | |
117 | LEDPORT = (LEDPORT & (uint8_t)~(_BV(LEDPINR) | _BV(LEDPING) | _BV(LEDPINB))) | ledpwm_amprogptr->orval; | |
118 | ledpwm_amprogptr++; | |
119 | programnextpwmstep(); | |
4307b86a | 120 | } |
121 | ||
122 | /* Called on compare match */ | |
123 | ISR(TIMER1_COMPA_vect) | |
17aea8ef | 124 | { |
b7a7cffc | 125 | LEDPORT = (LEDPORT & (uint8_t)~(_BV(LEDPINR) | _BV(LEDPING) | _BV(LEDPINB))) | ledpwm_amprogptr->orval; |
126 | ledpwm_amprogptr++; | |
127 | programnextpwmstep(); | |
4307b86a | 128 | } |
129 | ||
130 | void ledpwm_init(void) { | |
131 | /* Set our Port Pins to output */ | |
132 | LEDDDR |= _BV(LEDPINR) | _BV(LEDPING) | _BV(LEDPINB); | |
133 | /* DO NOT initialize TIMER1 frequency and overflow interrupt. | |
134 | * timers.c already does that. */ | |
135 | /* Enable TIMER1 output compare interrupt A */ | |
136 | TIMSK1 |= _BV(OCIE1A); | |
b7a7cffc | 137 | ledpwm_recalculateprogram(); |
4307b86a | 138 | } |
139 | ||
140 | void ledpwm_setled(uint8_t led, uint16_t val) { | |
141 | if (led > 2) { return; /* ignore invalid values */ } | |
142 | ledpwm_val[led] = val; | |
b7a7cffc | 143 | ledpwm_recalculateprogram(); |
17aea8ef | 144 | } |
de0e300b | 145 | |
146 | void ledpwm_set(uint8_t red, uint8_t green, uint8_t blue, uint8_t br) { | |
4307b86a | 147 | ledpwm_val[LEDPWM_REDLED] = red * br; |
148 | ledpwm_val[LEDPWM_GREENLED] = green * br; | |
149 | ledpwm_val[LEDPWM_BLUELED] = blue * br; | |
b7a7cffc | 150 | ledpwm_recalculateprogram(); |
de0e300b | 151 | } |