#include <avr/io.h>
+#include <avr/eeprom.h>
#include "lights.h"
-static uint16_t ambient_val;
+#define AMBIENT_VAL_SHIFT 2
+static uint16_t ambient_val, ambient_val16;
volatile unsigned char ambient_zone;
-static unsigned char ambient_zone_set;
+static unsigned char ambient_min, ambient_max, ambient_16drop;
-static uint16_t ambient_zones[] = {
- 0x0c00, 0x0d00, 0x1000, 0x1800, 0x2800, 0x2f80, 0xffff
+/* logging */
+#define AMBIENT_LOG_SIZE 128
+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;
+} 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[] = {
+ { 0x0000 , 0x0270<<AMBIENT_VAL_SHIFT }, // dark
+ { 0x0260<<AMBIENT_VAL_SHIFT, 0x02e0<<AMBIENT_VAL_SHIFT }, // evening
+ { 0x02d0<<AMBIENT_VAL_SHIFT, 0x0306<<AMBIENT_VAL_SHIFT }, // dawn
+ { 0x0302<<AMBIENT_VAL_SHIFT, 0xffff }, // day
};
#define N_AMBIENT_ZONES (sizeof(ambient_zones)/sizeof(ambient_zones[0]))
void init_ambient()
{
ambient_val = 0;
- ambient_zone = 0;
- ambient_zone_set = 0;
+ ambient_val16 = 0;
+ ambient_max = 0;
+ ambient_min = 0xFF;
+ ambient_zone = 1;
+ ambient_16drop = 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 ambient_zone_changed()
+void susp_ambient()
{
- log_byte(0xab);
- log_byte(ambient_zone);
- log_word(ambient_val);
- log_flush();
+ 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)
+ return;
+
+ // eeprom_write_byte(&ambient_log[ambient_log_offset++], ambient_min);
+ eeprom_write_byte(&ambient_log[ambient_log_offset++], ambient_max);
+ eeprom_write_byte(&ambient_log[ambient_log_offset++], ambient_16drop);
+
+ ambient_min = 0xFF;
+ ambient_max = 0;
+ ambient_16drop = 0;
+}
+static inline void ambient_zone_changed()
+{
+ pwmled_select_brightness();
pattern_reload();
}
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 old_zone = ambient_zone;
+ unsigned char byte_val, byte_val16;
+
+ ambient_val += adcval - (ambient_val
+ >> (AMBIENT_VAL_SHIFT - AMBIENT_ADC_SHIFT));
+
+ while (ambient_zones[ambient_zone].lo > ambient_val)
+ ambient_zone--;
+
+ while (ambient_zones[ambient_zone].hi < ambient_val)
+ ambient_zone++;
+
+ byte_val = ambient_val >> (2 + AMBIENT_VAL_SHIFT - AMBIENT_ADC_SHIFT);
+
+ ambient_val16 += byte_val - (ambient_val16 >> 4);
+ byte_val16 = ambient_val16 >> 4;
+
+ if (byte_val16 > byte_val) {
+ byte_val16 -= byte_val;
+ if (byte_val16 > ambient_16drop)
+ ambient_16drop = byte_val16;
+ }
+
+ if (ambient_min > byte_val)
+ ambient_min = byte_val;
+
+ if (ambient_max < byte_val)
+ ambient_max = byte_val;
+ if (old_zone != ambient_zone) {
+#if 0
+ log_byte(0xab);
+ log_byte(ambient_zone);
+ log_word(adcval);
+ log_flush();
+#endif
+ ambient_zone_changed();
}
}