]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/pattern.c
braking is handled behind the patterns inside pattern.c
[bike-lights.git] / firmware / pattern.c
1 #include <avr/io.h>
2 #include <stdlib.h> // for NULL
3
4 #include "lights.h"
5
6 static unsigned char led_counters[N_LEDS];
7 static pattern_t *led_patterns[N_LEDS];
8
9 static unsigned char fibonacci[8] = {
10         0, 1, 2, 3, 5, 8, 13, 21,
11 };
12
13 static pattern_t boot_pattern[] = {
14         { 1, D_5 },
15         { 0, D_5 },
16         { 1, D_3 },
17         { 0, D_3 },
18         { 1, D_2 },
19         { 0, D_2 },
20         { 1, D_1 },
21         { 0, D_1 },
22         { 1, D_1 },
23         { 0, D_1 },
24         { 1, D_1 },
25         { 0, D_1 },
26         { 1, D_1 },
27         { 0, D_1 },
28         { 1, D_8 },
29         { 0, D_8 },
30         PATTERN_END
31 };
32
33 static pattern_t pattern_num[] = {
34         { 0, D_5 },
35         { 1, D_1 }, /* 10 */
36         { 0, D_5 },
37         { 1, D_1 }, /*  9 */
38         { 0, D_5 },
39         { 1, D_1 }, /*  8 */
40         { 0, D_5 },
41         { 1, D_1 }, /*  7 */
42         { 0, D_5 },
43         { 1, D_1 }, /*  6 */
44         { 0, D_5 },
45         { 1, D_1 }, /*  5 */
46         { 0, D_5 },
47         { 1, D_1 }, /*  4 */
48         { 0, D_5 },
49         { 1, D_1 }, /*  3 */
50         { 0, D_5 },
51         { 1, D_1 }, /*  2 */
52         { 0, D_5 },
53         { 1, D_1 }, /*  1 */
54         { 0, D_13 },
55         PATTERN_END
56 };
57
58 static pattern_t pattern_invnum[] = {
59         { 1, D_5 },
60         { 0, D_1 }, /* 10 */
61         { 1, D_5 },
62         { 0, D_1 }, /*  9 */
63         { 1, D_5 },
64         { 0, D_1 }, /*  8 */
65         { 1, D_5 },
66         { 0, D_1 }, /*  7 */
67         { 1, D_5 },
68         { 0, D_1 }, /*  6 */
69         { 1, D_5 },
70         { 0, D_1 }, /*  5 */
71         { 1, D_5 },
72         { 0, D_1 }, /*  4 */
73         { 1, D_5 },
74         { 0, D_1 }, /*  3 */
75         { 1, D_5 },
76         { 0, D_1 }, /*  2 */
77         { 1, D_5 },
78         { 0, D_1 }, /*  1 */
79         { 1, D_13 },
80         PATTERN_END
81 };
82
83 pattern_t off_pattern[] = {
84         { 0, D_1 },
85         PATTERN_END
86 };
87
88 /*
89  * This is tricky: we use a single pattern for all three pwmleds,
90  * but on some occasions, we want to be able to modify only a single
91  * pwmled status without affecting other outputs. For example, during
92  * braking, we want to modify only the rear pwmled status. We don't
93  * use a separate "braking" pattern for every other pattern used, but instead
94  * we change the pwmled0 status regardless of the original value when
95  * braking. The rule is the following:
96  * - if during braking the pwmled2 (front) is at mode 2, we switch it to
97  *   mode 3 (which has the same target current) to avoid flicker.
98  * - if pwmled0 (rear) is off, we set it to mode 2
99  *      (if it is with mode 1, we keep it at mode 1)
100  * TODO: something similar should be done for the "entering the dark area"
101  *      condition, where we want to switch pwmled2 (front) on, to mode 2.
102  */
103 void pwmleds_update_mode()
104 {
105         unsigned char mode, mode0, mode1, mode2;
106
107         mode = led_patterns[0]->mode;
108
109         mode0 = mode & 3;
110         mode1 = (mode >> 2) & 1;
111         mode2 = (mode >> 3) & 3;
112
113         if (braking) {
114                 if (!mode0)
115                         mode0 = 2;
116                 if (mode2 == 2)
117                         mode2 = 3;
118         }
119
120         pwmled_set_mode(0, mode0);
121         pwmled_set_mode(1, mode1);
122         pwmled_set_mode(2, mode2);
123 }
124
125 void led_set_pattern(unsigned char n, pattern_t *pattern)
126 {
127         if (!pattern)
128                 pattern = off_pattern;
129
130         led_patterns[n] = pattern;
131
132         led_counters[n] = fibonacci[pattern->duration_fib];
133
134         if (n == 0) {
135                 pwmleds_update_mode();
136         } else if (n < N_LEDS) {
137                 gpio_set(n - 1, pattern->mode);
138         }
139 }
140
141 void init_pattern()
142 {
143         unsigned char i;
144
145         for (i = 0; i < N_LEDS; i++)
146                 led_set_pattern(i, NULL);
147
148         led_set_pattern(N_ILLUM_LED, boot_pattern);
149 }
150
151 pattern_t *number_pattern(unsigned char num, unsigned char inv)
152 {
153         if (num >= 10)
154                 num = 10;
155
156         if (inv) {
157                 return pattern_invnum
158                         + sizeof(pattern_invnum)/sizeof(pattern_t)
159                         - 2 - 2*num;
160         } else {
161                 return pattern_num
162                         + sizeof(pattern_num)/sizeof(pattern_t)
163                         - 2 - 2*num;
164         }
165 }
166
167 static pattern_t *pattern_select(unsigned char n)
168 {
169         switch(n) {
170         case 0: return pwmled_pattern_select();
171         case N_STATUS_LED: return status_led_pattern_select();
172         case N_ILLUM_LED:  return illumination_led_pattern_select();
173         case N_LASER_LED:  return laser_pattern_select();
174         default: return NULL;
175         }
176 }
177
178 void pattern_reload()
179 {
180         unsigned char i;
181
182         for (i = 0; i < N_LEDS; i++)
183                 led_set_pattern(i, pattern_select(i));
184 }
185
186 static void inline pattern_finished(unsigned char n)
187 {
188         unsigned char i;
189
190         led_patterns[n] = NULL;
191
192         if (n == 0) {
193                 led_set_pattern(0, pattern_select(0));
194         } else if (n == N_STATUS_LED) {
195                 if (!led_patterns[N_ILLUM_LED])
196                         led_set_pattern(N_ILLUM_LED,
197                                 pattern_select(N_ILLUM_LED));
198         } else if (n == N_ILLUM_LED) {
199                 if (!led_patterns[N_STATUS_LED])
200                         led_set_pattern(N_STATUS_LED,
201                                 pattern_select(N_STATUS_LED));
202         } else {
203                 led_set_pattern(n, pattern_select(n));
204         }
205 }
206
207 void patterns_next_tick()
208 {
209         unsigned char i;
210
211         for (i = 0; i < N_LEDS; i++) {
212                 if (!led_patterns[i]) {
213                         pattern_finished(i);
214                         continue;
215                 }
216
217                 if (--led_counters[i] == 0) {
218                         pattern_t *p = led_patterns[i];
219                         p++;
220                         if (p->duration_fib == 0) { // END
221                                 /* Keep the last state, wait for others */
222                                 pattern_finished(i);
223                                 continue;
224                         }
225                         led_set_pattern(i, p);
226                 }
227
228         }
229 }
230