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