#include #include "lights.h" static uint16_t pwm_vals[N_PWMLEDS*N_PWMLED_MODES]; static uint16_t pwm_max[N_PWMLEDS] = { PWM_MAX/2, PWM_MAX - (PWM_MAX >> 4), // step-up PWM_MAX/2 }; #define PWMLED2_TESTING_WITH_350MA_LED #define SENSE_MOHM 33 /* 0.033 Ohm */ #define MA_MOHM_GAIN_TO_ADC(ma, mohm, gain) (\ ((unsigned long)(ma))*(mohm) /* voltage at sensing resistor in uV */ \ /(1100000UL/gain/1024UL) /* voltage of ADC reading == 1 */ \ ) static uint16_t adc_max[N_PWMLEDS] = { MA_MOHM_GAIN_TO_ADC( 400, SENSE_MOHM, 20), MA_MOHM_GAIN_TO_ADC( 30, SENSE_MOHM, 20), #ifdef PWMLED2_TESTING_WITH_350MA_LED MA_MOHM_GAIN_TO_ADC( 400, SENSE_MOHM, 1) #else MA_MOHM_GAIN_TO_ADC(2500, SENSE_MOHM, 1) #endif }; static uint16_t adc_vals[N_PWMLEDS*N_PWMLED_MODES] = { /* pwmled0 */ MA_MOHM_GAIN_TO_ADC( 20, SENSE_MOHM, 20), MA_MOHM_GAIN_TO_ADC( 50, SENSE_MOHM, 20), MA_MOHM_GAIN_TO_ADC( 100, SENSE_MOHM, 20), MA_MOHM_GAIN_TO_ADC( 350, SENSE_MOHM, 20), /* pwmled1 */ MA_MOHM_GAIN_TO_ADC( 5, SENSE_MOHM, 20), MA_MOHM_GAIN_TO_ADC( 12, SENSE_MOHM, 20), MA_MOHM_GAIN_TO_ADC( 16, SENSE_MOHM, 20), MA_MOHM_GAIN_TO_ADC( 20, SENSE_MOHM, 20), /* pwmled2 */ #ifdef PWMLED2_TESTING_WITH_350MA_LED MA_MOHM_GAIN_TO_ADC( 100, SENSE_MOHM, 1), MA_MOHM_GAIN_TO_ADC( 140, SENSE_MOHM, 1), MA_MOHM_GAIN_TO_ADC( 250, SENSE_MOHM, 1), MA_MOHM_GAIN_TO_ADC( 350, SENSE_MOHM, 1), #else MA_MOHM_GAIN_TO_ADC( 150, SENSE_MOHM, 1), MA_MOHM_GAIN_TO_ADC( 350, SENSE_MOHM, 1), MA_MOHM_GAIN_TO_ADC( 700, SENSE_MOHM, 1), MA_MOHM_GAIN_TO_ADC(2400, SENSE_MOHM, 1), #endif }; // 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 int16_t differences[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++) { differences[i] = 0; start_probing(i); } } 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 && 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; differences[n] = 0; } else { pwm_off(n); pwmled_state[n] = ST_OFF; } } 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]; int16_t old_pwm = *pwm_p; int16_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; } differences[n] += adcval; differences[n] -= adc_exp; if (differences[n] > 16) new_pwm -= 2; else if (differences[n] > 4) new_pwm--; else if (differences[n] < -16) new_pwm += 2; else if (differences[n] < -4) new_pwm++; // new_pwm -= differences[n] >> 3; if (new_pwm == old_pwm) return; differences[n] = 0; if (new_pwm > (int16_t)pwm_max[n]) { // FIXME: disconnected? new_pwm = pwm_max[n]; } if (new_pwm < 1) { // FIXME: short-circuit? new_pwm = 1; } *pwm_p = new_pwm; pwm_set(n, new_pwm); if (jiffies > 500 && n == 1) { log_byte(adcval & 0xFF); log_byte(new_pwm); } } 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? } }