]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/pwmled.c
pwmled: more logging
[bike-lights.git] / firmware / pwmled.c
1 #include <avr/io.h>
2
3 #include "lights.h"
4
5 static uint16_t pwm_vals[N_PWMLEDS*N_PWMLED_MODES];
6 static uint16_t pwm_max[N_PWMLEDS] = {
7         2*PWM_MAX/3,
8         PWM_MAX - (PWM_MAX >> 4), // step-up
9         2*PWM_MAX/3
10 };
11
12 #define PWMLED2_TESTING_WITH_350MA_LED
13
14 #define SENSE_MOHM      33      /* 0.033 Ohm */
15 #define MA_MOHM_GAIN_TO_ADC(ma, mohm, gain) (\
16         ((unsigned long)(ma))*(mohm) /* voltage at sensing resistor in uV */ \
17         /(1100000UL/gain/1024UL)     /* voltage of ADC reading == 1 */ \
18 )
19 static uint16_t adc_max[N_PWMLEDS] = {
20         MA_MOHM_GAIN_TO_ADC( 400, SENSE_MOHM, 20),
21         MA_MOHM_GAIN_TO_ADC(  30, SENSE_MOHM, 20),
22 #ifdef PWMLED2_TESTING_WITH_350MA_LED
23         MA_MOHM_GAIN_TO_ADC( 400, SENSE_MOHM,  1)
24 #else
25         MA_MOHM_GAIN_TO_ADC(2500, SENSE_MOHM,  1)
26 #endif
27 };
28 static uint16_t adc_vals[N_PWMLEDS*N_PWMLED_MODES] = {
29         /* pwmled0 */
30         MA_MOHM_GAIN_TO_ADC(  20, SENSE_MOHM, 20),
31         MA_MOHM_GAIN_TO_ADC(  50, SENSE_MOHM, 20),
32         MA_MOHM_GAIN_TO_ADC( 100, SENSE_MOHM, 20),
33         MA_MOHM_GAIN_TO_ADC( 350, SENSE_MOHM, 20),
34         /* pwmled1 */
35         MA_MOHM_GAIN_TO_ADC(   5, SENSE_MOHM, 20),
36         MA_MOHM_GAIN_TO_ADC(  12, SENSE_MOHM, 20),
37         MA_MOHM_GAIN_TO_ADC(  16, SENSE_MOHM, 20),
38         MA_MOHM_GAIN_TO_ADC(  20, SENSE_MOHM, 20),
39         /* pwmled2 */
40 #ifdef PWMLED2_TESTING_WITH_350MA_LED
41         MA_MOHM_GAIN_TO_ADC( 100, SENSE_MOHM,  1),
42         MA_MOHM_GAIN_TO_ADC( 140, SENSE_MOHM,  1),
43         MA_MOHM_GAIN_TO_ADC( 250, SENSE_MOHM,  1),
44         MA_MOHM_GAIN_TO_ADC( 350, SENSE_MOHM,  1),
45 #else
46         MA_MOHM_GAIN_TO_ADC( 150, SENSE_MOHM,  1),
47         MA_MOHM_GAIN_TO_ADC( 350, SENSE_MOHM,  1),
48         MA_MOHM_GAIN_TO_ADC( 700, SENSE_MOHM,  1),
49         MA_MOHM_GAIN_TO_ADC(2400, SENSE_MOHM,  1),
50 #endif
51 };
52
53 // TODO: maybe convert this to bitmask to simplify pwmled_needs_adc() ?
54 static unsigned char pwmled_state[N_PWMLEDS];
55 #define ST_DISABLED 0
56 #define ST_PROBING  1
57 #define ST_OFF      2
58 #define ST_ON       3
59
60 static unsigned char pwmled_mode[N_PWMLEDS];
61 static unsigned char pwmled_mode_set[N_PWMLEDS];
62
63 static uint16_t pwm_probes[N_PWMLEDS];
64 static int16_t differences[N_PWMLEDS];
65
66 static void start_probing(unsigned char n)
67 {
68         pwmled_state[n] = ST_PROBING;
69         pwm_set(n, 0);
70         pwm_probes[n] = 0;
71 }
72
73 void pwmled_init()
74 {
75         unsigned char i;
76
77         for (i = 0; i < N_PWMLEDS*N_PWMLED_MODES; i++) {
78                 pwm_vals[i] = 0;
79                 pwmled_mode[i] = 0;
80                 pwmled_mode_set[i] = 0;
81         }
82
83         for (i = 0; i < N_PWMLEDS; i++) {
84                 differences[i] = 0;
85                 start_probing(i);
86         }
87 }
88
89 unsigned char pwmled_enabled(unsigned char n)
90 {
91         unsigned char st = pwmled_state[n];
92         if (st == ST_OFF || st == ST_ON)
93                 return 1;
94         else
95                 return 0;
96 }
97
98 void pwmled_set_mode(unsigned char n, unsigned char mode)
99 {
100         if (!pwmled_enabled(n))
101                 return;
102
103 #if 0
104         log_byte(0xF8);
105         log_byte(n);
106         log_byte(mode);
107 #endif
108         if (mode > 0 && mode <= N_PWMLED_MODES) {
109                 uint16_t pwmval;
110                 mode--;
111                 pwmval = pwm_vals[n*N_PWMLED_MODES+mode];
112                 pwm_set(n, pwmval);
113 #if 0
114                 log_byte(pwmval);
115 #endif
116                 pwmled_state[n] = ST_ON;
117                 pwmled_mode[n] = mode;
118                 pwmled_mode_set[n] = 1;
119                 differences[n] = 0;
120         } else {
121                 pwm_off(n);
122                 pwmled_state[n] = ST_OFF;
123         }
124 }
125
126 static void inline probing_adc(unsigned char n, uint16_t adcval)
127 {
128         unsigned char need_bigger = 0, i;
129         uint16_t *pwm_p = &pwm_vals[n*N_PWMLED_MODES];
130         uint16_t *adc_p = &adc_vals[n*N_PWMLED_MODES];
131         uint16_t pwm = pwm_probes[n];
132
133 #if 0
134         log_byte(0xF4);
135         log_byte(n);
136         log_word(adcval);
137 #endif
138
139         if (adcval > adc_max[n] // Too high
140                 || (pwm == 0 && adcval > 2) // non-zero voltage with zero PWM
141                 ) {
142                 pwm_off(n);
143                 pwmled_state[n] = ST_DISABLED;
144                 log_byte(0xF0);
145                 log_byte(n);
146                 log_word(adcval);
147                 return;
148         }
149
150         for (i = 0; i < N_PWMLED_MODES; i++, pwm_p++, adc_p++) {
151                 uint16_t adc = *adc_p;
152                 if (adc >= adcval) {
153                         *pwm_p = pwm;
154                         need_bigger = 1;
155                 }
156         }
157
158 #if 0
159         if ((n == 1 && pwm > 0x35) || adcval != 0) {
160                 log_byte(n);
161                 log_byte(0xF3);
162                 log_byte(pwm);
163                 log_word(adcval);
164         }
165 #endif
166
167         if (!need_bigger) { // successfully probed
168                 pwm_off(n);
169                 // pwm_set(n, 0);
170                 pwmled_state[n] = ST_OFF;
171                 log_byte(0xF1);
172                 log_byte(n);
173
174                 return;
175         }
176
177         if (pwm >= pwm_max[n]) { // over the maximum!
178                 pwm_off(n);
179                 pwmled_state[n] = ST_DISABLED;
180                 log_byte(0xF2);
181                 log_byte(n);
182                 // pwm_set(n, 0);
183                 return;
184         }
185
186         // try to increase
187         pwm++;
188         pwm_probes[n] = pwm;
189         pwm_set(n, pwm);
190 }
191
192 // Feedback loop
193 static void inline on_adc(unsigned char n, uint16_t adcval)
194 {
195         unsigned char mode = pwmled_mode[n];
196         uint16_t adc_exp   =  adc_vals[n*N_PWMLED_MODES+mode];
197         uint16_t *pwm_p    = &pwm_vals[n*N_PWMLED_MODES+mode];
198         int16_t old_pwm    = *pwm_p;
199         int16_t new_pwm    = old_pwm;
200
201 #if 0
202         log_byte(0xF5);
203         log_byte(n);
204         log_word(adcval);
205 #endif
206
207
208         if (pwmled_mode_set[n]) { // ignore the first reading
209                 pwmled_mode_set[n] = 0;
210                 return;
211         }
212
213         differences[n] += adcval;
214         differences[n] -= adc_exp;
215
216         if (differences[n] > 16)
217                 new_pwm -= 2;
218         else if (differences[n] > 4)
219                 new_pwm--;
220         else if (differences[n] < -16)
221                 new_pwm += 2;
222         else if (differences[n] < -4)
223                 new_pwm++;
224         // new_pwm -= differences[n] >> 3;
225
226         if (new_pwm == old_pwm)
227                 return;
228
229         differences[n] = 0;
230
231         if (new_pwm > (int16_t)pwm_max[n]) {
232                 // FIXME: disconnected?
233                 log_byte(0xE1);
234                 log_byte(n);
235                 log_word(new_pwm);
236                 log_word(adcval);
237                 log_word(jiffies);
238                 pwmled_state[n] = ST_DISABLED;
239                 pwm_off(n);
240                 return;
241         }
242
243         if (new_pwm < 1) {
244                 // FIXME: short-circuit?
245                 new_pwm = 1;
246         }
247
248         *pwm_p = new_pwm;
249         pwm_set(n, new_pwm);
250
251         if (jiffies > 500 && n == 1) {
252                 log_byte(adcval & 0xFF);
253                 log_byte(new_pwm);
254         }
255 }
256
257 void pwmled_adc(unsigned char n, uint16_t adcval)
258 {
259         unsigned char i, probing;
260
261         switch (pwmled_state[n]) {
262         case ST_PROBING:
263                 probing_adc(n, adcval);
264 #if 1
265                 probing = 0;
266                 for (i = 0; i < N_PWMLEDS; i++)
267                         if (pwmled_state[i] == ST_PROBING)
268                                 probing = 1;
269
270                 if (!probing) {
271                         log_word(0x5555);
272                         log_word(jiffies);
273                         for (i = 0; i < N_PWMLEDS; i++)
274                                 log_byte(pwmled_state[i]);
275                                 
276                         for (i = 0; i < N_PWMLEDS*N_PWMLED_MODES; i++)
277                                 log_word(pwm_vals[i]);
278                         log_flush();
279                         log_set_state(4);
280                         for (i = 0; i < N_PWMLEDS; i++)
281                                 led_set_pattern(i, mode1_pattern);
282                 }
283 #endif
284                 
285                 return;
286         case ST_ON:
287                 on_adc(n, adcval);
288                 return;
289         // WTF am I doing in this function then? Maybe recently switched off?
290         }
291 }