]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/pwmled.c
delete pwm_off(), use pwm_set instead
[bike-lights.git] / firmware / pwmled.c
1 #include <avr/io.h>
2
3 #include "lights.h"
4
5 static unsigned char pwm_vals[N_PWMLEDS*N_PWMLED_MODES];
6 static unsigned char adc_vals[N_PWMLEDS*N_PWMLED_MODES] = {
7         /* pwmled0 */
8         0x04, 0x14, 0x24, 0x38,
9         /* pwmled1 */
10         0x04, 0x14, 0x24, 0x38,
11         /* pwmled2 */
12         0x0c, 0x24, 0x48, 0x90,
13 };
14
15 // TODO: maybe convert this to bitmask to simplify pwmled_needs_adc() ?
16 static unsigned char pwmled_state[N_PWMLEDS];
17 #define ST_DISABLED 0
18 #define ST_PROBING  1
19 #define ST_OFF      2
20 #define ST_ON       3
21
22 static unsigned char pwmled_mode[N_PWMLEDS];
23 static unsigned char pwmled_mode_set[N_PWMLEDS];
24
25 static unsigned char pwm_probes[N_PWMLEDS];
26
27 static void start_probing(unsigned char n)
28 {
29         pwmled_state[n] = ST_PROBING;
30         pwm_set(n, 0);
31         pwm_probes[n] = 0;
32 }
33
34 void pwmled_init()
35 {
36         unsigned char i;
37
38         for (i = 0; i < N_PWMLEDS*N_PWMLED_MODES; i++) {
39                 pwm_vals[i] = 0;
40                 pwmled_mode[i] = 0;
41                 pwmled_mode_set[i] = 0;
42         }
43
44         for (i = 0; i < N_PWMLEDS; i++) {
45                 start_probing(i);
46         }
47 }
48
49 unsigned char pwmled_needs_adc(unsigned char n)
50 {
51         unsigned char st = pwmled_state[n];
52         if (st == ST_PROBING || st == ST_ON)
53                 return 1;
54         else
55                 return 0;
56 }
57
58 unsigned char pwmled_enabled(unsigned char n)
59 {
60         unsigned char st = pwmled_state[n];
61         if (st == ST_OFF || st == ST_ON)
62                 return 1;
63         else
64                 return 0;
65 }
66
67 void pwmled_set_mode(unsigned char n, unsigned char mode)
68 {
69         if (!pwmled_enabled(n))
70                 return;
71
72 #if 0
73         log_byte(0xF8);
74         log_byte(n);
75         log_byte(mode);
76 #endif
77
78         if (mode == 0) {
79                 pwm_off(n);
80                 pwmled_state[n] = ST_OFF;
81                 return;
82         }
83
84         if (mode <= N_PWMLED_MODES) {
85                 unsigned char pwmval;
86                 mode--;
87                 pwmval = pwm_vals[n*N_PWMLED_MODES+mode];
88                 pwm_set(n, pwmval);
89 #if 0
90                 log_byte(pwmval);
91 #endif
92                 pwmled_state[n] = ST_ON;
93                 pwmled_mode[n] = mode;
94                 pwmled_mode_set[n] = 1;
95         }
96 }
97
98 static void inline probing_adc(unsigned char n, uint16_t adcval)
99 {
100         unsigned char need_bigger = 0, i;
101         unsigned char *pwm_p = &pwm_vals[n*N_PWMLED_MODES];
102         unsigned char *adc_p = &adc_vals[n*N_PWMLED_MODES];
103         unsigned char pwm = pwm_probes[n];
104
105 #if 0
106         log_byte(0xF4);
107         log_byte(n);
108         log_word(adcval);
109 #endif
110
111         if (adcval > 0x100 // Too high
112                 || (pwm == 0 && adcval > 0) // non-zero voltage with zero PWM
113                 ) {
114                 pwm_off(n);
115                 pwmled_state[n] = ST_DISABLED;
116                 log_byte(0xF0);
117                 log_byte(n);
118                 log_word(adcval);
119                 return;
120         }
121
122         for (i = 0; i < N_PWMLED_MODES; i++, pwm_p++, adc_p++) {
123                 uint16_t adc = *adc_p;
124                 if (adc >= adcval) {
125                         *pwm_p = pwm;
126                         need_bigger = 1;
127                 }
128         }
129
130 #if 0
131         if ((n == 1 && pwm > 0x35) || adcval != 0) {
132                 log_byte(n);
133                 log_byte(0xF3);
134                 log_byte(pwm);
135                 log_word(adcval);
136         }
137 #endif
138
139         if (!need_bigger) { // successfully probed
140                 pwm_off(n);
141                 // pwm_set(n, 0);
142                 pwmled_state[n] = ST_OFF;
143                 log_byte(0xF1);
144                 log_byte(n);
145
146                 return;
147         }
148
149         if (pwm >= 0xE0) { // over the maximum!
150                 pwm_off(n);
151                 pwmled_state[n] = ST_DISABLED;
152                 log_byte(0xF2);
153                 log_byte(n);
154                 // pwm_set(n, 0);
155                 return;
156         }
157
158         // try to increase
159         pwm++;
160         pwm_probes[n] = pwm;
161         pwm_set(n, pwm);
162 }
163
164 // Feedback loop
165 static void inline on_adc(unsigned char n, uint16_t adcval)
166 {
167         unsigned char mode = pwmled_mode[n];
168         uint16_t adc_exp     =  adc_vals[n*N_PWMLED_MODES+mode];
169         unsigned char *pwm_p = &pwm_vals[n*N_PWMLED_MODES+mode];
170         uint16_t old_pwm     = *pwm_p;
171         uint16_t new_pwm = old_pwm;
172
173 #if 0
174         log_byte(0xF5);
175         log_byte(n);
176         log_word(adcval);
177 #endif
178
179         if (pwmled_mode_set[n]) { // ignore the first reading
180                 pwmled_mode_set[n] = 0;
181                 return;
182         }
183
184         // FIXME: running average?
185         if (2*adcval > 5*adc_exp) { // >2.5x expected, lower significantly
186                 new_pwm = 2*old_pwm/3;
187         } else if (3*adcval > 4*adc_exp) { // >1.33x expected, lower a bit
188                 new_pwm = old_pwm - 1;
189         } else if (4*adcval < 3*adc_exp) { // 0.75x expected, raise a bit
190                 new_pwm = old_pwm + 1;
191         }
192
193         // FIXME: better disconnect detection
194         if (new_pwm > 0xE0) { // disconnected?
195                 new_pwm = 0xE0;
196         }
197         if (new_pwm < 2) { // short-circuit?
198                 new_pwm = 2;
199         }
200
201         if (new_pwm != old_pwm) {
202                 *pwm_p = new_pwm;
203                 pwm_set(n, new_pwm);
204 #if 0
205                 log_byte(0xF9);
206                 log_byte(new_pwm);
207 #endif
208         }
209 }
210
211 void pwmled_adc(unsigned char n, uint16_t adcval)
212 {
213         unsigned char i, probing;
214         switch (pwmled_state[n]) {
215         case ST_PROBING:
216                 probing_adc(n, adcval);
217
218 #if 1
219                 probing = 0;
220                 for (i = 0; i < N_PWMLEDS; i++)
221                         if (pwmled_state[i] == ST_PROBING)
222                                 probing = 1;
223
224                 if (!probing) {
225                         for (i = 0; i < N_PWMLEDS; i++)
226                                 log_byte(pwmled_state[i]);
227                                 
228                         for (i = 0; i < N_PWMLEDS*N_PWMLED_MODES; i++)
229                                 log_byte(pwm_vals[i]);
230                         log_flush();
231                         log_set_state(4);
232                 }
233 #endif
234                 
235                 return;
236         case ST_ON:
237                 on_adc(n, adcval);
238                 return;
239         // WTF am I doing in this function then? Maybe recently switched off?
240         }
241 }