#include #include "lights.h" static uint16_t pwm_vals[N_PWMLEDS*N_PWMLED_MODES]; static uint16_t pwm_max[N_PWMLEDS] = { 0x70, 0x70, 0xF0 }; static uint16_t adc_max[N_PWMLEDS] = { 0x70, 0x70, 0xF0 }; static uint16_t adc_vals[N_PWMLEDS*N_PWMLED_MODES] = { /* pwmled0 */ 0x04, 0x14, 0x24, 0x38, /* pwmled1 */ 0x04, 0x14, 0x24, 0x38, /* pwmled2 */ 0x0c, 0x24, 0x48, 0x90, }; // TODO: maybe convert this to bitmask to simplify pwmled_needs_adc() ? static unsigned char pwmled_state[N_PWMLEDS]; #define ST_DISABLED 0 #define ST_PROBING 1 #define ST_OFF 2 #define ST_ON 3 static unsigned char pwmled_mode[N_PWMLEDS]; static unsigned char pwmled_mode_set[N_PWMLEDS]; static uint16_t pwm_probes[N_PWMLEDS]; static void start_probing(unsigned char n) { pwmled_state[n] = ST_PROBING; pwm_set(n, 0); pwm_probes[n] = 0; } void pwmled_init() { unsigned char i; for (i = 0; i < N_PWMLEDS*N_PWMLED_MODES; i++) { pwm_vals[i] = 0; pwmled_mode[i] = 0; pwmled_mode_set[i] = 0; } for (i = 0; i < N_PWMLEDS; i++) { start_probing(i); } } unsigned char pwmled_needs_adc(unsigned char n) { unsigned char st = pwmled_state[n]; if (st == ST_PROBING || st == ST_ON) return 1; else return 0; } unsigned char pwmled_enabled(unsigned char n) { unsigned char st = pwmled_state[n]; if (st == ST_OFF || st == ST_ON) return 1; else return 0; } void pwmled_set_mode(unsigned char n, unsigned char mode) { if (!pwmled_enabled(n)) return; #if 0 log_byte(0xF8); log_byte(n); log_byte(mode); #endif if (mode == 0) { pwm_off(n); pwmled_state[n] = ST_OFF; return; } if (mode <= N_PWMLED_MODES) { uint16_t pwmval; mode--; pwmval = pwm_vals[n*N_PWMLED_MODES+mode]; pwm_set(n, pwmval); #if 0 log_byte(pwmval); #endif pwmled_state[n] = ST_ON; pwmled_mode[n] = mode; pwmled_mode_set[n] = 1; } } static void inline probing_adc(unsigned char n, uint16_t adcval) { unsigned char need_bigger = 0, i; uint16_t *pwm_p = &pwm_vals[n*N_PWMLED_MODES]; uint16_t *adc_p = &adc_vals[n*N_PWMLED_MODES]; uint16_t pwm = pwm_probes[n]; #if 0 log_byte(0xF4); log_byte(n); log_word(adcval); #endif if (adcval > adc_max[n] // Too high || (pwm == 0 && adcval > 0) // non-zero voltage with zero PWM ) { pwm_off(n); pwmled_state[n] = ST_DISABLED; log_byte(0xF0); log_byte(n); log_word(adcval); return; } for (i = 0; i < N_PWMLED_MODES; i++, pwm_p++, adc_p++) { uint16_t adc = *adc_p; if (adc >= adcval) { *pwm_p = pwm; need_bigger = 1; } } #if 0 if ((n == 1 && pwm > 0x35) || adcval != 0) { log_byte(n); log_byte(0xF3); log_byte(pwm); log_word(adcval); } #endif if (!need_bigger) { // successfully probed pwm_off(n); // pwm_set(n, 0); pwmled_state[n] = ST_OFF; log_byte(0xF1); log_byte(n); return; } if (pwm >= pwm_max[n]) { // over the maximum! pwm_off(n); pwmled_state[n] = ST_DISABLED; log_byte(0xF2); log_byte(n); // pwm_set(n, 0); return; } // try to increase pwm++; pwm_probes[n] = pwm; pwm_set(n, pwm); } // Feedback loop static void inline on_adc(unsigned char n, uint16_t adcval) { unsigned char mode = pwmled_mode[n]; uint16_t adc_exp = adc_vals[n*N_PWMLED_MODES+mode]; uint16_t *pwm_p = &pwm_vals[n*N_PWMLED_MODES+mode]; uint16_t old_pwm = *pwm_p; uint16_t new_pwm = old_pwm; #if 0 log_byte(0xF5); log_byte(n); log_word(adcval); #endif if (pwmled_mode_set[n]) { // ignore the first reading pwmled_mode_set[n] = 0; return; } // FIXME: running average? if (2*adcval > 5*adc_exp) { // >2.5x expected, lower significantly new_pwm = 2*old_pwm/3; } else if (3*adcval > 4*adc_exp) { // >1.33x expected, lower a bit new_pwm = old_pwm - 1; } else if (4*adcval < 3*adc_exp) { // 0.75x expected, raise a bit new_pwm = old_pwm + 1; } // FIXME: better disconnect detection if (new_pwm > pwm_max[n]) { // FIXME: disconnected? new_pwm = pwm_max[n]; } if (new_pwm < 2) { // short-circuit? new_pwm = 2; } if (new_pwm != old_pwm) { *pwm_p = new_pwm; pwm_set(n, new_pwm); #if 0 log_byte(0xF9); log_byte(new_pwm); #endif } } void pwmled_adc(unsigned char n, uint16_t adcval) { unsigned char i, probing; switch (pwmled_state[n]) { case ST_PROBING: probing_adc(n, adcval); #if 1 probing = 0; for (i = 0; i < N_PWMLEDS; i++) if (pwmled_state[i] == ST_PROBING) probing = 1; if (!probing) { for (i = 0; i < N_PWMLEDS; i++) log_byte(pwmled_state[i]); for (i = 0; i < N_PWMLEDS*N_PWMLED_MODES; i++) log_word(pwm_vals[i]); log_flush(); log_set_state(4); } #endif return; case ST_ON: on_adc(n, adcval); return; // WTF am I doing in this function then? Maybe recently switched off? } }