diff --git a/src/charge_control.c b/src/charge_control.c index 516223a..5f1d6a0 100644 --- a/src/charge_control.c +++ b/src/charge_control.c @@ -84,7 +84,7 @@ static enum ChargeState control_solar_charging( } // temperature limit - if(meas->temperature > internal_temperature_limit) { + if(meas->avg_temperature > internal_temperature_limit) { return CHARGE_HIGH_TEMPERATURE; } @@ -92,7 +92,7 @@ static enum ChargeState control_solar_charging( if((time_in_state > SLEEP_STATE_DELAY) && (current_switch_state == true) && (solar_switch_onoff_duration > SLEEP_SWITCH_DELAY) - && (meas->i_solar < sleep_solar_current)) { + && (meas->avg_i_solar < sleep_solar_current)) { return CHARGE_SLEEP; } @@ -232,9 +232,8 @@ static void load_fsm_update(uint64_t uptime_ms, struct MeasurementResult *meas) power_switch_load_on(); } - if(meas->i_load > load_current_limit) { - // TODO: maybe only check this 10 ms after load is switched on - // to allow for inrush current? + if((meas->i_load > load_current_limit) + && (discharge_time_in_state > LOAD_CURRENT_INRUSH_TIME)) { discharge_state = DISCHARGE_OVERCURRENT; } @@ -352,8 +351,10 @@ void charge_control_update(uint64_t uptime_ms, struct MeasurementResult *meas) bool charge_control_is_idle(void) { - return ((charge_state == CHARGE_SLEEP) - && (discharge_state == DISCHARGE_VOLTAGE_LOW)); + return (((charge_state == CHARGE_SLEEP) + || (charge_state == CHARGE_HIGH_TEMPERATURE)) + && ((discharge_state == DISCHARGE_VOLTAGE_LOW) + || (discharge_state == DISCHARGE_OVERCURRENT))); } diff --git a/src/config.h b/src/config.h index 8ac166b..7676c5b 100644 --- a/src/config.h +++ b/src/config.h @@ -7,7 +7,7 @@ #define U_BAT_REGULATION_CORRIDOR 100 /* Initial charge battery voltage threshold (in mV). */ -#define U_BAT_INITIAL_FULL 28800 // stop charging if battery voltage reaches this threshold +#define U_BAT_INITIAL_FULL 28600 // stop charging if battery voltage reaches this threshold /* Transition to floating voltage levels after this time (in ms). */ #define INITIAL_CHARGE_HOLD_TIME 3600000 @@ -51,6 +51,10 @@ /* Current at which the overload protection triggers (in mA). */ #define LOAD_CURRENT_LIMIT_MA 10000 +/* Inrush tolerance time (in ms). Overload protection is not enforced for this + * time after load power-on. */ +#define LOAD_CURRENT_INRUSH_TIME 10 + /* Minimum voltage that the charge pump must produce above U_bat before any * power FET is switched on (in mV). */ #define MIN_CHARGE_PUMP_EXCESS_VOLTAGE 10000 @@ -59,6 +63,27 @@ #define LOAD_ON_DELAY 10000 +/* Measurement Averaging: + * Alpha is specified in units of 1/1000. 1000 means that only the latest + * value is relevant, 0 means that the measurement has no influence. The latter + * is useless. + * + * The formula to calculate the next averaged value avg from a measurement meas is: + * avg[k] = meas * (alpha/1000) + avg[k-1] * (1 - alpha/1000) + * + * For overload protection (battery voltage, load current), the latest values + * are always used. + * */ + +/* Averaging factor for load current. */ +#define AVG_ALPHA_I_SOLAR 10 +#define AVG_ALPHA_I_LOAD 10 +#define AVG_ALPHA_U_BAT 100 +#define AVG_ALPHA_U_SW 100 +#define AVG_ALPHA_U_SOLAR 100 +#define AVG_ALPHA_TEMP 10 + + /* Generic configuration */ /* Time (in ms) to stay active in idle state before entering deep sleep. */ diff --git a/src/main.c b/src/main.c index 4e8812d..580e7a5 100644 --- a/src/main.c +++ b/src/main.c @@ -77,8 +77,8 @@ static void update_leds(uint64_t uptime_ms, struct MeasurementResult *meas_data) static uint64_t charge_pulse_until = 0; static uint64_t discharge_pulse_until = 0; - charge_in_mAs = fxp_add(charge_in_mAs, meas_data->i_solar); - charge_out_mAs = fxp_add(charge_out_mAs, meas_data->i_load); + charge_in_mAs = fxp_add(charge_in_mAs, meas_data->avg_i_solar); + charge_out_mAs = fxp_add(charge_out_mAs, meas_data->avg_i_load); if(charge_in_mAs > FXP_FROM_INT(1000)) { led_chplex_on(LED_CHPLEX_IDX_CHARGE_PULSE); @@ -156,6 +156,38 @@ static void report_status(struct MeasurementResult *meas_data) } +static void report_averaged(struct MeasurementResult *meas_data) +{ + char number[FXP_STR_MAXLEN]; + + rs485_enqueue("AVGD:"); + + fxp_format(meas_data->avg_u_bat, number, 3); + rs485_enqueue(number); + rs485_enqueue(":"); + + fxp_format(meas_data->avg_u_solar, number, 3); + rs485_enqueue(number); + rs485_enqueue(":"); + + fxp_format(meas_data->avg_u_sw, number, 3); + rs485_enqueue(number); + rs485_enqueue(":"); + + fxp_format(meas_data->avg_i_solar, number, 3); + rs485_enqueue(number); + rs485_enqueue(":"); + + fxp_format(meas_data->avg_i_load, number, 3); + rs485_enqueue(number); + rs485_enqueue(":"); + + fxp_format(meas_data->avg_temperature, number, 2); + rs485_enqueue(number); + rs485_enqueue("\n"); +} + + static void low_power_mode(uint32_t duration_sec) { // stop the systick counter for reliable deep sleep @@ -230,6 +262,11 @@ int main(void) report_status(&meas_data); } + // Send the averaged measurement data from the last cycle. + if(timebase_ms % 500 == 20) { + report_averaged(&meas_data); + } + measurement_wait_for_completion(); measurement_finalize(&meas_data); diff --git a/src/measurement.c b/src/measurement.c index dd112fe..713e8cb 100644 --- a/src/measurement.c +++ b/src/measurement.c @@ -8,12 +8,27 @@ #include "measurement.h" #include "calibration.h" +#include "config.h" #define ADC_NUM_CHANNELS 6 static volatile int16_t adc_values[ADC_NUM_CHANNELS]; static fxp_t calibration_factors[ADC_NUM_CHANNELS-1]; // all except temperature; filled in measurement_init() +fxp_t avg_alpha_i_solar; +fxp_t avg_alpha_i_load; +fxp_t avg_alpha_u_bat; +fxp_t avg_alpha_u_sw; +fxp_t avg_alpha_u_solar; +fxp_t avg_alpha_temp; + +fxp_t avg_alpha_i_solar_inv; +fxp_t avg_alpha_i_load_inv; +fxp_t avg_alpha_u_bat_inv; +fxp_t avg_alpha_u_sw_inv; +fxp_t avg_alpha_u_solar_inv; +fxp_t avg_alpha_temp_inv; + /* Temperature sensor calibration value address */ #define TEMP110_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7C2)) #define TEMP30_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7B8)) @@ -89,6 +104,23 @@ void measurement_init(void) calibration_factors[ANALOG_INPUT_I_LOAD] = fxp_div(FXP_FROM_INT(CAL_FACTOR_I_LOAD), FXP_FROM_INT(1000)); + // Convert and precalculate coefficients for exponential averaging + avg_alpha_i_solar = fxp_div(FXP_FROM_INT(AVG_ALPHA_I_SOLAR), FXP_FROM_INT(1000)); + avg_alpha_i_load = fxp_div(FXP_FROM_INT(AVG_ALPHA_I_LOAD), FXP_FROM_INT(1000)); + avg_alpha_u_bat = fxp_div(FXP_FROM_INT(AVG_ALPHA_U_BAT), FXP_FROM_INT(1000)); + avg_alpha_u_sw = fxp_div(FXP_FROM_INT(AVG_ALPHA_U_SW), FXP_FROM_INT(1000)); + avg_alpha_u_solar = fxp_div(FXP_FROM_INT(AVG_ALPHA_U_SOLAR), FXP_FROM_INT(1000)); + avg_alpha_temp = fxp_div(FXP_FROM_INT(AVG_ALPHA_TEMP), FXP_FROM_INT(1000)); + + // Inverse (1 - alpha) exponential averaging coefficients + avg_alpha_i_solar_inv = fxp_sub(FXP_FROM_INT(1), avg_alpha_i_solar); + avg_alpha_i_load_inv = fxp_sub(FXP_FROM_INT(1), avg_alpha_i_load); + avg_alpha_u_bat_inv = fxp_sub(FXP_FROM_INT(1), avg_alpha_u_bat); + avg_alpha_u_sw_inv = fxp_sub(FXP_FROM_INT(1), avg_alpha_u_sw); + avg_alpha_u_solar_inv = fxp_sub(FXP_FROM_INT(1), avg_alpha_u_solar); + avg_alpha_temp_inv = fxp_sub(FXP_FROM_INT(1), avg_alpha_temp); + + // Prepare the ADC adc_power_off(ADC1); // enable the temperature sensor @@ -173,4 +205,35 @@ void measurement_finalize(struct MeasurementResult *result) calibration_factors[ANALOG_INPUT_I_LOAD]); result->temperature = calc_temperature(adc_values[5]); + + /* calculate exponentially averaged values */ + result->avg_u_bat = + fxp_add( + fxp_mult(avg_alpha_u_bat, result->u_bat), + fxp_mult(avg_alpha_u_bat_inv, result->avg_u_bat)); + + result->avg_u_solar = + fxp_add( + fxp_mult(avg_alpha_u_solar, result->u_solar), + fxp_mult(avg_alpha_u_solar_inv, result->avg_u_solar)); + + result->avg_u_sw = + fxp_add( + fxp_mult(avg_alpha_u_sw, result->u_sw), + fxp_mult(avg_alpha_u_sw_inv, result->avg_u_sw)); + + result->avg_i_solar = + fxp_add( + fxp_mult(avg_alpha_i_solar, result->i_solar), + fxp_mult(avg_alpha_i_solar_inv, result->avg_i_solar)); + + result->avg_i_load = + fxp_add( + fxp_mult(avg_alpha_i_load, result->i_load), + fxp_mult(avg_alpha_i_load_inv, result->avg_i_load)); + + result->avg_temperature = + fxp_add( + fxp_mult(avg_alpha_temp, result->temperature), + fxp_mult(avg_alpha_temp_inv, result->avg_temperature)); } diff --git a/src/measurement.h b/src/measurement.h index 1999266..602f15b 100644 --- a/src/measurement.h +++ b/src/measurement.h @@ -11,6 +11,14 @@ struct MeasurementResult fxp_t i_solar; // in Ampere fxp_t i_load; // in Ampere fxp_t temperature; // in degrees Celsius + + // exponentially averaged versions of the above + fxp_t avg_u_bat; + fxp_t avg_u_solar; + fxp_t avg_u_sw; + fxp_t avg_i_solar; + fxp_t avg_i_load; + fxp_t avg_temperature; }; void measurement_init(void);