]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/pattern.c
pattern.c: alternative brake handling
[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 && !battery_critical) {
114                 if (mode0) {
115                         mode0 = 2;
116                         if (mode2 == 2)
117                                 mode2 = 3;
118                 } else {
119                         mode0 = 1;
120                 }
121         }
122
123         pwmled_set_mode(0, mode0);
124         pwmled_set_mode(1, mode1);
125         pwmled_set_mode(2, mode2);
126 }
127
128 void led_set_pattern(unsigned char n, pattern_t *pattern)
129 {
130         if (!pattern)
131                 pattern = off_pattern;
132
133         led_patterns[n] = pattern;
134
135         led_counters[n] = fibonacci[pattern->duration_fib];
136
137         if (n == 0) {
138                 pwmleds_update_mode();
139         } else if (n < N_LEDS) {
140                 gpio_set(n - 1, pattern->mode);
141         }
142 }
143
144 void init_pattern()
145 {
146         unsigned char i;
147
148         for (i = 0; i < N_LEDS; i++)
149                 led_set_pattern(i, NULL);
150
151         led_set_pattern(N_ILLUM_LED, boot_pattern);
152 }
153
154 pattern_t *number_pattern(unsigned char num, unsigned char inv)
155 {
156         if (num >= 10)
157                 num = 10;
158
159         if (inv) {
160                 return pattern_invnum
161                         + sizeof(pattern_invnum)/sizeof(pattern_t)
162                         - 2 - 2*num;
163         } else {
164                 return pattern_num
165                         + sizeof(pattern_num)/sizeof(pattern_t)
166                         - 2 - 2*num;
167         }
168 }
169
170 static pattern_t *pattern_select(unsigned char n)
171 {
172         switch(n) {
173         case 0: return pwmled_pattern_select();
174         case N_STATUS_LED: return status_led_pattern_select();
175         case N_ILLUM_LED:  return illumination_led_pattern_select();
176         case N_LASER_LED:  return laser_pattern_select();
177         default: return NULL;
178         }
179 }
180
181 void pattern_reload()
182 {
183         unsigned char i;
184
185         for (i = 0; i < N_LEDS; i++)
186                 led_set_pattern(i, pattern_select(i));
187 }
188
189 static void inline pattern_finished(unsigned char n)
190 {
191         unsigned char i;
192
193         led_patterns[n] = NULL;
194
195         if (n == 0) {
196                 led_set_pattern(0, pattern_select(0));
197         } else if (n == N_STATUS_LED) {
198                 if (!led_patterns[N_ILLUM_LED])
199                         led_set_pattern(N_ILLUM_LED,
200                                 pattern_select(N_ILLUM_LED));
201         } else if (n == N_ILLUM_LED) {
202                 if (!led_patterns[N_STATUS_LED])
203                         led_set_pattern(N_STATUS_LED,
204                                 pattern_select(N_STATUS_LED));
205         } else {
206                 led_set_pattern(n, pattern_select(n));
207         }
208 }
209
210 void patterns_next_tick()
211 {
212         unsigned char i;
213
214         for (i = 0; i < N_LEDS; i++) {
215                 if (!led_patterns[i]) {
216                         pattern_finished(i);
217                         continue;
218                 }
219
220                 if (--led_counters[i] == 0) {
221                         pattern_t *p = led_patterns[i];
222                         p++;
223                         if (p->duration_fib == 0) { // END
224                                 /* Keep the last state, wait for others */
225                                 pattern_finished(i);
226                                 continue;
227                         }
228                         led_set_pattern(i, p);
229                 }
230
231         }
232 }
233