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