X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?p=bike-lights.git;a=blobdiff_plain;f=firmware%2Fambient.c;h=3ae15d3c0e97080e1ac56957f06b67a883275305;hp=21c2a66d721ad54f647c96463cc5b9218d6fb06a;hb=dc4a8b74e6bd5ecfd139fabbb1f5bd8dad3b624e;hpb=69ef9776904e9f3018d340a794cf8427bdc728ff diff --git a/firmware/ambient.c b/firmware/ambient.c index 21c2a66..3ae15d3 100644 --- a/firmware/ambient.c +++ b/firmware/ambient.c @@ -1,50 +1,199 @@ #include +#include #include "lights.h" -static uint16_t ambient_val; -volatile unsigned char ambient_zone; -static unsigned char ambient_zone_set; +#define AMBIENT_FAST_SHIFT 2 // running avg of ADC vals +#define AMBIENT_SLOW_SHIFT 6 // 1 << AMBIENT_SLOW_SHIFT times slower -static uint16_t ambient_zones[] = { - 0x0b70, 0x0b80, 0x1000, 0x1800, 0x2800, 0x2f80, 0xffff +static uint16_t ambient_fast, ambient_slow; +volatile unsigned char ambient_zone, ambient_shadow; +static unsigned char ambient_min, ambient_max, ambient_drop; + +/* logging */ +#define AMBIENT_LOG_SIZE 256 +static unsigned char ambient_log_offset_stored EEMEM; +static unsigned char ambient_log_offset; +static unsigned char ambient_log[AMBIENT_LOG_SIZE] EEMEM; + +/* My photodiode reads 0x00C5 .. 0x033B */ +typedef struct { + uint16_t lo, hi; // TODO: consider changing this to bytes +} ambient_zone_t; + +/* + * Note: these have to be sorted, starting with 0, ending with 0xFFFF + * and having small overlaps in order to provide a bit of hysteresis. + */ +static ambient_zone_t ambient_zones[N_AMBIENT_ZONES] = { +#ifdef PAVLINA + { 0x0000, 0x02d0 }, // dark + { 0x02b0, 0x0318 }, // evening + { 0x0308, 0x032c }, // dawn + { 0x0324, 0xffff }, // day +#else + { 0x0000, 0x0250 }, // dark + { 0x0230, 0x02e8 }, // evening + { 0x02d0, 0x0302 }, // dawn + { 0x02fc, 0xffff }, // day +#endif }; -#define N_AMBIENT_ZONES (sizeof(ambient_zones)/sizeof(ambient_zones[0])) -void ambient_init() +#define SHADOW_DROP_LIMIT 0x20 // in ADC units (0..0x3ff) + +void init_ambient() +{ + ambient_slow = 0; + ambient_fast = 0; + ambient_max = 0; + ambient_min = 0xFF; + ambient_zone = 1; + ambient_drop = 0; + ambient_shadow = 0; + + ambient_log_offset = eeprom_read_byte(&ambient_log_offset_stored); + + if (ambient_log_offset == AMBIENT_LOG_SIZE) + ambient_log_offset = 0; // start over +} + +void susp_ambient() +{ + unsigned char stored_offset; + + ambient_log_min_max(); + + stored_offset = eeprom_read_byte(&ambient_log_offset_stored); + if (stored_offset != ambient_log_offset) + eeprom_write_byte(&ambient_log_offset_stored, + ambient_log_offset); +} + +void ambient_log_min_max() +{ + if (ambient_log_offset >= AMBIENT_LOG_SIZE - 1) + ambient_log_offset = 0; // start over + + ambient_max -= ambient_min; + // ambient_max >>= 2; + if (ambient_max > 15) + ambient_max = 15; + + ambient_drop >>= 2; + if (ambient_drop > 15) + ambient_drop = 15; + + eeprom_write_byte(&ambient_log[ambient_log_offset++], ambient_min); + eeprom_write_byte(&ambient_log[ambient_log_offset++], + (ambient_max << 4) | ambient_drop); + + ambient_min = 0xFF; + ambient_max = 0; + ambient_drop = 0; +} + +static inline void ambient_zone_changed() { - ambient_val = 0; - ambient_zone = 0; - ambient_zone_set = 0; + pwmled_select_brightness(); + pattern_reload(); } -void ambient_zone_changed() +static unsigned char val_to_zone(uint16_t ambient_val) { - log_byte(0xab); - log_byte(ambient_zone); - log_word(ambient_val); - log_flush(); + unsigned char new_zone = ambient_zone; + + if (new_zone >= N_AMBIENT_ZONES) + new_zone = N_AMBIENT_ZONES-1; + + while (ambient_zones[new_zone].lo > ambient_val) + new_zone--; + + while (ambient_zones[new_zone].hi < ambient_val) + new_zone++; + + return new_zone; } void ambient_adc(uint16_t adcval) { - unsigned char newzone; - - if (!ambient_zone_set) - ambient_val = adcval << 4; - else // running sum - ambient_val += adcval - (ambient_val >> 4); - - newzone = 0; - while (newzone < N_AMBIENT_ZONES-1 - && ambient_zones[newzone] < ambient_val) - newzone++; - - // TODO: implement hysteresis? - if (!ambient_zone_set || newzone != ambient_zone) { - ambient_zone = newzone; - ambient_zone_set = 1; - // ambient_zone_changed(); + unsigned char new_zone, user_zone, new_shadow = 0; + unsigned char byte_fast, byte_slow, drop; + uint16_t fast_10bit, slow_10bit; + + // running avg - shorter timespan + ambient_fast += adcval - (ambient_fast >> AMBIENT_FAST_SHIFT); + + // running avg - longer timespan + fast_10bit = ambient_fast >> (AMBIENT_FAST_SHIFT + AMBIENT_ADC_SHIFT); + ambient_slow += fast_10bit - (ambient_slow >> AMBIENT_SLOW_SHIFT); + + // ambient zones are governed by shorter timespan by default + new_zone = val_to_zone(fast_10bit); + + slow_10bit = ambient_slow >> AMBIENT_SLOW_SHIFT; + + if ((new_zone > ambient_zone) + || (new_zone > 1 && new_zone == ambient_zone - 1)) { + // but change to the neighbouring zone is governed by _slow, + // except to the darkest zone, where we want fast reaction. + new_zone = val_to_zone(slow_10bit); + } + + // user_param ambient zone override + if ((user_zone = get_user_param(0)) > 0) + new_zone = user_zone - 1; + +#if 0 // ignore shadow-entering code altogether for now + // are we entering the shadow? + if (!user_zone && new_zone < ambient_zone + && ambient_zone >= 2 && slow_10bit > fast_10bit + && slow_10bit - fast_10bit >= SHADOW_DROP_LIMIT) { + // we are entering the shadow + new_shadow = 0x30; + } + + if (ambient_shadow) { + if (new_shadow) { + new_zone = ambient_zone; // don't change while entering shadow + ambient_shadow = new_shadow; // update the timeout + } else { + ambient_shadow--; + if (!ambient_shadow) { // leaving the shadow + ambient_zone_changed(); + } + } + } else if (new_shadow) { + ambient_shadow = new_shadow; // set up the timeout + new_zone = ambient_zone; // don't change while entering shadow + ambient_zone_changed(); // notify others the first time + } +#endif + + // TODO: maybe use these values instead of 10-bit? + byte_fast = fast_10bit >> 2; + byte_slow = slow_10bit >> 2; + + if (byte_slow > byte_fast) { + drop = byte_slow - byte_fast; + if (drop > ambient_drop) + ambient_drop = drop; + } + + if (ambient_min > byte_slow) + ambient_min = byte_slow; + + if (ambient_max < byte_slow) + ambient_max = byte_slow; + + if (new_zone != ambient_zone) { + ambient_zone = new_zone; +#if 0 + log_byte(0xab); + log_byte(ambient_zone); + log_word(adcval); + log_flush(); +#endif + ambient_zone_changed(); } }