#include #include // for NULL #include "lights.h" static unsigned char led_counters[N_LEDS]; static pattern_t *led_patterns[N_LEDS]; static unsigned char fibonacci[8] = { 0, 1, 2, 3, 5, 8, 13, 21, }; static pattern_t boot_pattern[] = { { 1, D_5 }, { 0, D_5 }, { 1, D_3 }, { 0, D_3 }, { 1, D_2 }, { 0, D_2 }, { 1, D_1 }, { 0, D_1 }, { 1, D_1 }, { 0, D_1 }, { 1, D_1 }, { 0, D_1 }, { 1, D_1 }, { 0, D_1 }, { 1, D_8 }, { 0, D_8 }, PATTERN_END }; static pattern_t pattern_num[] = { { 0, D_5 }, { 1, D_1 }, /* 10 */ { 0, D_5 }, { 1, D_1 }, /* 9 */ { 0, D_5 }, { 1, D_1 }, /* 8 */ { 0, D_5 }, { 1, D_1 }, /* 7 */ { 0, D_5 }, { 1, D_1 }, /* 6 */ { 0, D_5 }, { 1, D_1 }, /* 5 */ { 0, D_5 }, { 1, D_1 }, /* 4 */ { 0, D_5 }, { 1, D_1 }, /* 3 */ { 0, D_5 }, { 1, D_1 }, /* 2 */ { 0, D_5 }, { 1, D_1 }, /* 1 */ { 0, D_13 }, PATTERN_END }; static pattern_t pattern_invnum[] = { { 1, D_5 }, { 0, D_1 }, /* 10 */ { 1, D_5 }, { 0, D_1 }, /* 9 */ { 1, D_5 }, { 0, D_1 }, /* 8 */ { 1, D_5 }, { 0, D_1 }, /* 7 */ { 1, D_5 }, { 0, D_1 }, /* 6 */ { 1, D_5 }, { 0, D_1 }, /* 5 */ { 1, D_5 }, { 0, D_1 }, /* 4 */ { 1, D_5 }, { 0, D_1 }, /* 3 */ { 1, D_5 }, { 0, D_1 }, /* 2 */ { 1, D_5 }, { 0, D_1 }, /* 1 */ { 1, D_13 }, PATTERN_END }; pattern_t off_pattern[] = { { 0, D_1 }, PATTERN_END }; /* * This is tricky: we use a single pattern for all three pwmleds, * but on some occasions, we want to be able to modify only a single * pwmled status without affecting other outputs. For example, during * braking, we want to modify only the rear pwmled status. We don't * use a separate "braking" pattern for every other pattern used, but instead * we change the pwmled0 status regardless of the original value when * braking. The rule is the following: * - if during braking the pwmled2 (front) is at mode 2, we switch it to * mode 3 (which has the same target current) to avoid flicker. * - if pwmled0 (rear) is off, we set it to mode 2 * (if it is with mode 1, we keep it at mode 1) * TODO: something similar should be done for the "entering the dark area" * condition, where we want to switch pwmled2 (front) on, to mode 2. */ void pwmleds_update_mode() { unsigned char mode, mode0, mode1, mode2; mode = led_patterns[0]->mode; mode0 = mode & 3; mode1 = (mode >> 2) & 1; mode2 = (mode >> 3) & 3; if (err_flags.braking && !err_flags.err_battery) { mode0 = 2; if (mode2 == 2) mode2 = 3; } pwmled_set_mode(0, mode0); pwmled_set_mode(1, mode1); pwmled_set_mode(2, mode2); } void led_set_pattern(unsigned char n, pattern_t *pattern) { if (!pattern) pattern = off_pattern; led_patterns[n] = pattern; led_counters[n] = fibonacci[pattern->duration_fib]; if (n == 0) { pwmleds_update_mode(); } else if (n < N_LEDS) { gpio_set(n - 1, pattern->mode); } } void init_pattern() { unsigned char i; for (i = 0; i < N_LEDS; i++) led_set_pattern(i, NULL); led_set_pattern(N_ILLUM_LED, boot_pattern); } pattern_t *number_pattern(unsigned char num, unsigned char inv) { if (num >= 10) num = 10; if (inv) { return pattern_invnum + sizeof(pattern_invnum)/sizeof(pattern_t) - 2 - 2*num; } else { return pattern_num + sizeof(pattern_num)/sizeof(pattern_t) - 2 - 2*num; } } static pattern_t *pattern_select(unsigned char n) { switch(n) { case 0: return pwmled_pattern_select(); case N_STATUS_LED: return status_led_pattern_select(); case N_ILLUM_LED: return illumination_led_pattern_select(); case N_LASER_LED: return laser_pattern_select(); default: return NULL; } } void pattern_reload() { unsigned char i; for (i = 0; i < N_LEDS; i++) led_set_pattern(i, pattern_select(i)); } static void inline pattern_finished(unsigned char n) { unsigned char i; led_patterns[n] = NULL; if (n == 0) { led_set_pattern(0, pattern_select(0)); } else if (n == N_STATUS_LED) { if (!led_patterns[N_ILLUM_LED]) led_set_pattern(N_ILLUM_LED, pattern_select(N_ILLUM_LED)); } else if (n == N_ILLUM_LED) { if (!led_patterns[N_STATUS_LED]) led_set_pattern(N_STATUS_LED, pattern_select(N_STATUS_LED)); } else { led_set_pattern(n, pattern_select(n)); } } void patterns_next_tick() { unsigned char i; for (i = 0; i < N_LEDS; i++) { if (!led_patterns[i]) { pattern_finished(i); continue; } if (--led_counters[i] == 0) { pattern_t *p = led_patterns[i]; p++; if (p->duration_fib == 0) { // END /* Keep the last state, wait for others */ pattern_finished(i); continue; } led_set_pattern(i, p); } } }