diff --git a/.gitignore b/.gitignore index fd691c5..f8f3e6b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ # Vim swap files .*.sw? + +# generated config HEX files +utils/*.hex diff --git a/Makefile b/Makefile index 914eb14..9865c8d 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ LDFLAGS+=--static \ -nostartfiles -Wl,--gc-sections \ -mthumb -mcpu=cortex-m0 -mthumb -mfloat-abi=soft -# the LD script +# the LD script (RAM-only does not work because the code is too large) #LDFLAGS+=-Tldscripts/lnsc-2420-$(BUILD).ld LDFLAGS+=-Tldscripts/lnsc-2420-release.ld diff --git a/ldscripts/lnsc-2420-debug.ld b/ldscripts/lnsc-2420-debug.ld index 097d4ca..541b106 100644 --- a/ldscripts/lnsc-2420-debug.ld +++ b/ldscripts/lnsc-2420-debug.ld @@ -7,6 +7,7 @@ MEMORY { /*rom (rx) : ORIGIN = 0x08000000, LENGTH = 64K*/ + conf (r) : ORIGIN = 0x08007c00, LENGTH = 1K ram (rwx) : ORIGIN = 0x20000000, LENGTH = 4K } @@ -96,6 +97,10 @@ SECTIONS . = ALIGN(4); end = .; + + .conf : { + __conf_start = .; + } >conf } PROVIDE(_stack = ORIGIN(ram) + LENGTH(ram)); diff --git a/ldscripts/lnsc-2420-release.ld b/ldscripts/lnsc-2420-release.ld index 0b7c6ba..09fd4ca 100644 --- a/ldscripts/lnsc-2420-release.ld +++ b/ldscripts/lnsc-2420-release.ld @@ -3,10 +3,18 @@ /* Define memory regions. */ MEMORY { - rom (rx) : ORIGIN = 0x08000000, LENGTH = 32K + rom (rx) : ORIGIN = 0x08000000, LENGTH = 31K + conf (r) : ORIGIN = 0x08007c00, LENGTH = 1K ram (rwx) : ORIGIN = 0x20000000, LENGTH = 4K } /* Include the common ld script. */ INCLUDE cortex-m-generic.ld +SECTIONS +{ + .conf : { + __conf_start = .; + . = ALIGN(4); + } >conf +} diff --git a/src/calibration.h b/src/calibration.h index e0523cb..c8fbc75 100644 --- a/src/calibration.h +++ b/src/calibration.h @@ -6,10 +6,24 @@ * than the actual voltage, you have to scale by 1.02 and therefore specify * 1020 in this list. */ +/* Values for the device at the B26 tower */ + +#if 0 #define CAL_FACTOR_U_BAT 994 #define CAL_FACTOR_U_SOLAR 997 #define CAL_FACTOR_U_SW 996 #define CAL_FACTOR_I_SOLAR 1015 #define CAL_FACTOR_I_LOAD 1000 +#endif + +/* Values for the development device */ + +#if 1 +#define CAL_FACTOR_U_BAT 1012 +#define CAL_FACTOR_U_SOLAR 1015 +#define CAL_FACTOR_U_SW 1006 +#define CAL_FACTOR_I_SOLAR 3980 +#define CAL_FACTOR_I_LOAD 1000 +#endif #endif // CALIBRATION_H diff --git a/src/charge_control.c b/src/charge_control.c index e719cfa..3b8a13a 100644 --- a/src/charge_control.c +++ b/src/charge_control.c @@ -6,7 +6,7 @@ #include "measurement.h" #include "charge_pump.h" #include "rs485.h" -#include "config.h" +#include "flash_config.h" #include "charge_control.h" @@ -92,9 +92,9 @@ static enum ChargeState control_solar_charging( } // low-current limit (go to sleep at night) - if((time_in_state > SLEEP_STATE_DELAY) + if((time_in_state > FLASH_CONFIG_SLEEP_STATE_DELAY) && (current_switch_state == true) - && (solar_switch_onoff_duration > SLEEP_SWITCH_DELAY) + && (solar_switch_onoff_duration > FLASH_CONFIG_SLEEP_SWITCH_DELAY) && (meas->avg_i_solar < sleep_solar_current)) { return CHARGE_SLEEP; } @@ -152,20 +152,20 @@ static void solar_fsm_update(uint64_t uptime_ms, struct MeasurementResult *meas) } // time limit for initial hold charging - if(charge_time_in_state > INITIAL_CHARGE_HOLD_TIME) { + if(charge_time_in_state > FLASH_CONFIG_INITIAL_CHARGE_HOLD_TIME) { charge_state = CHARGE_TRANSITION; } break; case CHARGE_TRANSITION: - if(charge_time_in_state < INITIAL_TO_FLOAT_TRANSITION_TIME) { + if(charge_time_in_state < FLASH_CONFIG_INITIAL_TO_FLOAT_TRANSITION_TIME) { // dynamically adjust thresholds fxp_t u_bat_full = fxp_add(u_bat_initial_full, fxp_mult( fxp_sub(u_bat_float_full, u_bat_initial_full), - fxp_div(charge_time_in_state, INITIAL_TO_FLOAT_TRANSITION_TIME))); + fxp_div(charge_time_in_state, FLASH_CONFIG_INITIAL_TO_FLOAT_TRANSITION_TIME))); fxp_t u_bat_low = fxp_sub(u_bat_full, u_bat_regulation_corridor); @@ -260,7 +260,7 @@ static void load_fsm_update(uint64_t uptime_ms, struct MeasurementResult *meas) } if((meas->i_load > load_current_limit) - && (discharge_time_in_state > LOAD_CURRENT_INRUSH_TIME)) { + && (discharge_time_in_state > FLASH_CONFIG_LOAD_CURRENT_INRUSH_TIME)) { discharge_state = DISCHARGE_OVERCURRENT; } @@ -277,7 +277,7 @@ static void load_fsm_update(uint64_t uptime_ms, struct MeasurementResult *meas) // Can only switch on again after a specific amount of time has passed if((meas->avg_u_bat > u_bat_load_on) - && (discharge_time_in_state > LOAD_ON_DELAY)) { + && (discharge_time_in_state > FLASH_CONFIG_LOAD_ON_DELAY)) { discharge_state = DISCHARGE_OK; } break; @@ -307,30 +307,30 @@ void charge_control_init(void) discharge_state_entered = true; /* calculate thresholds */ - u_bat_regulation_corridor = fxp_div(FXP_FROM_INT(U_BAT_REGULATION_CORRIDOR), + u_bat_regulation_corridor = fxp_div(FXP_FROM_INT(FLASH_CONFIG_U_BAT_REGULATION_CORRIDOR), FXP_FROM_INT(1000)); - u_bat_initial_full = fxp_div(FXP_FROM_INT(U_BAT_INITIAL_FULL), FXP_FROM_INT(1000)); + u_bat_initial_full = fxp_div(FXP_FROM_INT(FLASH_CONFIG_U_BAT_INITIAL_FULL), FXP_FROM_INT(1000)); u_bat_initial_low = fxp_sub(u_bat_initial_full, u_bat_regulation_corridor); - u_bat_initial_hold_cancel = fxp_div(FXP_FROM_INT(U_BAT_INITIAL_HOLD_CANCEL), FXP_FROM_INT(1000)); + u_bat_initial_hold_cancel = fxp_div(FXP_FROM_INT(FLASH_CONFIG_U_BAT_INITIAL_HOLD_CANCEL), FXP_FROM_INT(1000)); - u_bat_float_full = fxp_div(FXP_FROM_INT(U_BAT_FLOAT_FULL), FXP_FROM_INT(1000)); + u_bat_float_full = fxp_div(FXP_FROM_INT(FLASH_CONFIG_U_BAT_FLOAT_FULL), FXP_FROM_INT(1000)); u_bat_float_low = fxp_sub(u_bat_float_full, u_bat_regulation_corridor); - min_charge_pump_excess_voltage = fxp_div(FXP_FROM_INT(MIN_CHARGE_PUMP_EXCESS_VOLTAGE), + min_charge_pump_excess_voltage = fxp_div(FXP_FROM_INT(FLASH_CONFIG_MIN_CHARGE_PUMP_EXCESS_VOLTAGE), FXP_FROM_INT(1000)); - u_bat_load_on = fxp_div(FXP_FROM_INT(U_BAT_LOAD_ON), FXP_FROM_INT(1000)); - u_bat_load_off = fxp_div(FXP_FROM_INT(U_BAT_LOAD_OFF), FXP_FROM_INT(1000)); + u_bat_load_on = fxp_div(FXP_FROM_INT(FLASH_CONFIG_U_BAT_LOAD_ON), FXP_FROM_INT(1000)); + u_bat_load_off = fxp_div(FXP_FROM_INT(FLASH_CONFIG_U_BAT_LOAD_OFF), FXP_FROM_INT(1000)); - load_current_limit = fxp_div(FXP_FROM_INT(LOAD_CURRENT_LIMIT_MA), FXP_FROM_INT(1000)); + load_current_limit = fxp_div(FXP_FROM_INT(FLASH_CONFIG_LOAD_CURRENT_LIMIT_MA), FXP_FROM_INT(1000)); - internal_temperature_limit = fxp_div(FXP_FROM_INT(INTERNAL_TEMPERATURE_LIMIT), FXP_FROM_INT(10)); - internal_temperature_recovery = fxp_div(FXP_FROM_INT(INTERNAL_TEMPERATURE_RECOVERY), FXP_FROM_INT(10)); + internal_temperature_limit = fxp_div(FXP_FROM_INT(FLASH_CONFIG_INTERNAL_TEMPERATURE_LIMIT), FXP_FROM_INT(10)); + internal_temperature_recovery = fxp_div(FXP_FROM_INT(FLASH_CONFIG_INTERNAL_TEMPERATURE_RECOVERY), FXP_FROM_INT(10)); - sleep_solar_current = fxp_div(FXP_FROM_INT(SLEEP_SOLAR_CURRENT), FXP_FROM_INT(1000)); - sleep_solar_excess_voltage = fxp_div(FXP_FROM_INT(SLEEP_SOLAR_EXCESS_VOLTAGE), FXP_FROM_INT(1000)); + sleep_solar_current = fxp_div(FXP_FROM_INT(FLASH_CONFIG_SLEEP_SOLAR_CURRENT), FXP_FROM_INT(1000)); + sleep_solar_excess_voltage = fxp_div(FXP_FROM_INT(FLASH_CONFIG_SLEEP_SOLAR_EXCESS_VOLTAGE), FXP_FROM_INT(1000)); } diff --git a/src/config.h b/src/config.h deleted file mode 100644 index 794488e..0000000 --- a/src/config.h +++ /dev/null @@ -1,98 +0,0 @@ -#ifndef CONFIG_H -#define CONFIG_H - -/* Thresholds for charging control */ - -/* Battery regulation corridor width (in mV). */ -#define U_BAT_REGULATION_CORRIDOR 100 - -/* Initial charge battery voltage threshold (in mV). */ -#define U_BAT_INITIAL_FULL 28600 // stop charging if battery voltage reaches this threshold - -/* Cancel initial charge voltage hold below this battery voltage (in mV). */ -#define U_BAT_INITIAL_HOLD_CANCEL 27000 - -/* Transition to floating voltage levels after this time (in ms). */ -#define INITIAL_CHARGE_HOLD_TIME 1800000 - -/* Duration of the transistion from initial charging to float (in ms). */ -#define INITIAL_TO_FLOAT_TRANSITION_TIME 600000 - -/* Float charge battery voltage threshold (in mV). */ -#define U_BAT_FLOAT_FULL 27600 // stop charging if battery voltage reaches this threshold - -/* Minimum voltage difference to U_bat that the solar panels must produce - * before charging is resumed after it was switched off (in mV). */ -#define SLEEP_SOLAR_EXCESS_VOLTAGE 1000 - -/* Minimum charge current required before charging is stopped to save power at - * the charge pump (in mA). */ -#define SLEEP_SOLAR_CURRENT 1 - -/* Delay between state change and sleep state check (in ms). */ -#define SLEEP_STATE_DELAY 100 - -/* Delay between charging switch state change and sleep state check(in ms). */ -#define SLEEP_SWITCH_DELAY 1000 - -/* Maximum allowed microcontroller temperature (in units of 0.1 °C). If this - * temperature is exceeded, charging is stopped. The load is kept on. Do not - * set this too high as the heat has to propagate from the power MOSFETs. */ -#define INTERNAL_TEMPERATURE_LIMIT 500 - -/* Resume operation below this temperature (in units of 0.1 °C). */ -#define INTERNAL_TEMPERATURE_RECOVERY 450 - - -/* Thresholds for load control */ - -/* Voltage above which the load is turned on (in mV). */ -#define U_BAT_LOAD_ON 27000 -/* Voltage below which the load is turned off (in mV). */ -#define U_BAT_LOAD_OFF 24000 - -/* 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 - -/* The minimum time the load must be off before it can be switched on again (in ms). */ -#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. */ -#define DEEPSLEEP_DELAY 1000 - -/* Deep sleep duration (in seconds). */ -#define DEEPSLEEP_DURATION 10 - -#endif // CONFIG_H diff --git a/src/flash_config.c b/src/flash_config.c new file mode 100644 index 0000000..365769b --- /dev/null +++ b/src/flash_config.c @@ -0,0 +1,7 @@ +#include "flash_config.h" + +bool flash_config_is_present(void) +{ + return (FLASH_CONFIG_CAL_FACTOR_U_BAT != 0xFFFF) + && (FLASH_CONFIG_U_BAT_REGULATION_CORRIDOR != -1); +} diff --git a/src/flash_config.h b/src/flash_config.h new file mode 100644 index 0000000..24dfde0 --- /dev/null +++ b/src/flash_config.h @@ -0,0 +1,123 @@ +#ifndef FLASH_CONFIG_H +#define FLASH_CONFIG_H + +#include +#include + +extern uint8_t __conf_start; + +#define FLASH_CONFIG_BASE_PTR ((uint8_t*)(&__conf_start)) + +/***** Calibration factors *****/ + +/* Calibration factor defined here are divided by 1000 to determine the actual + * scaling factor. Therefore, if the voltage determined by the firmware is 2% + * lower than the actual voltage, you have to scale by 1.02 and therefore + * specify 1020 in this list. */ + +#define FLASH_CONFIG_CAL_FACTOR_U_BAT (*(uint16_t*)(FLASH_CONFIG_BASE_PTR + 0x0000)) +#define FLASH_CONFIG_CAL_FACTOR_U_SOLAR (*(uint16_t*)(FLASH_CONFIG_BASE_PTR + 0x0002)) +#define FLASH_CONFIG_CAL_FACTOR_U_SW (*(uint16_t*)(FLASH_CONFIG_BASE_PTR + 0x0004)) +#define FLASH_CONFIG_CAL_FACTOR_I_SOLAR (*(uint16_t*)(FLASH_CONFIG_BASE_PTR + 0x0006)) +#define FLASH_CONFIG_CAL_FACTOR_I_LOAD (*(uint16_t*)(FLASH_CONFIG_BASE_PTR + 0x0008)) + +/***** General configuration *****/ + +/* Battery regulation corridor width (in mV). */ +#define FLASH_CONFIG_U_BAT_REGULATION_CORRIDOR (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0100)) + +/* Initial charge battery voltage threshold (in mV). */ +#define FLASH_CONFIG_U_BAT_INITIAL_FULL (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0104)) + +/* Cancel initial charge voltage hold below this battery voltage (in mV). */ +#define FLASH_CONFIG_U_BAT_INITIAL_HOLD_CANCEL (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0108)) + +/* Transition to floating voltage levels after this time (in ms). */ +#define FLASH_CONFIG_INITIAL_CHARGE_HOLD_TIME (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x010C)) + +/* Duration of the transistion from initial charging to float (in ms). */ +#define FLASH_CONFIG_INITIAL_TO_FLOAT_TRANSITION_TIME (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x0110)) + +/* Float charge battery voltage threshold (in mV). */ +#define FLASH_CONFIG_U_BAT_FLOAT_FULL (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0114)) + +/* Minimum voltage difference to U_bat that the solar panels must produce + * before charging is resumed after it was switched off (in mV). */ +#define FLASH_CONFIG_SLEEP_SOLAR_EXCESS_VOLTAGE (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0118)) + +/* Minimum charge current required before charging is stopped to save power at + * the charge pump (in mA). */ +#define FLASH_CONFIG_SLEEP_SOLAR_CURRENT (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x011C)) + +/* Delay between state change and sleep state check (in ms). */ +#define FLASH_CONFIG_SLEEP_STATE_DELAY (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x0120)) + +/* Delay between charging switch state change and sleep state check(in ms). */ +#define FLASH_CONFIG_SLEEP_SWITCH_DELAY (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x0124)) + +/* Maximum allowed microcontroller temperature (in units of 0.1 °C). If this + * temperature is exceeded, charging is stopped. The load is kept on. Do not + * set this too high as the heat has to propagate from the power MOSFETs. */ +#define FLASH_CONFIG_INTERNAL_TEMPERATURE_LIMIT (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0128)) + +/* Resume operation below this temperature (in units of 0.1 °C). */ +#define FLASH_CONFIG_INTERNAL_TEMPERATURE_RECOVERY (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x012C)) + + +/* Thresholds for load control */ + +/* Voltage above which the load is turned on (in mV). */ +#define FLASH_CONFIG_U_BAT_LOAD_ON (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0130)) +/* Voltage below which the load is turned off (in mV). */ +#define FLASH_CONFIG_U_BAT_LOAD_OFF (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0134)) + +/* Current at which the overload protection triggers (in mA). */ +#define FLASH_CONFIG_LOAD_CURRENT_LIMIT_MA (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0138)) + +/* Inrush tolerance time (in ms). Overload protection is not enforced for this + * time after load power-on. */ +#define FLASH_CONFIG_LOAD_CURRENT_INRUSH_TIME (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x013C)) + +/* Minimum voltage that the charge pump must produce above U_bat before any + * power FET is switched on (in mV). */ +#define FLASH_CONFIG_MIN_CHARGE_PUMP_EXCESS_VOLTAGE (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0140)) + +/* The minimum time the load must be off before it can be switched on again (in ms). */ +#define FLASH_CONFIG_LOAD_ON_DELAY (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x0144)) + + +/* 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 FLASH_CONFIG_AVG_ALPHA_I_SOLAR (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0148)) +#define FLASH_CONFIG_AVG_ALPHA_I_LOAD (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x014C)) +#define FLASH_CONFIG_AVG_ALPHA_U_BAT (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0150)) +#define FLASH_CONFIG_AVG_ALPHA_U_SW (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0154)) +#define FLASH_CONFIG_AVG_ALPHA_U_SOLAR (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0158)) +#define FLASH_CONFIG_AVG_ALPHA_TEMP (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x015C)) + + +/* Generic configuration */ + +/* Time (in ms) to stay active in idle state before entering deep sleep. */ +#define FLASH_CONFIG_DEEPSLEEP_DELAY (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x0160)) + +/* Deep sleep duration (in seconds). */ +#define FLASH_CONFIG_DEEPSLEEP_DURATION (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x0164)) + + +/* Functions */ + +bool flash_config_is_present(void); + +#endif // FLASH_CONFIG_H diff --git a/src/main.c b/src/main.c index 1e1ed76..cb5ed9e 100644 --- a/src/main.c +++ b/src/main.c @@ -19,7 +19,7 @@ #include "bmp280.h" #include "pinout.h" -#include "config.h" +#include "flash_config.h" volatile int wait_frame = 1; @@ -43,6 +43,16 @@ static void init_systick(int freq) } +static void config_err_blink_code(uint64_t timebase_ms) +{ + if(timebase_ms % 500 < 250) { + led_chplex_mask(0x3F); // all on + } else { + led_chplex_mask(0x00); // all off + } +} + + static bool ledtest(uint64_t timebase_ms) { if(timebase_ms == 0) { @@ -326,9 +336,14 @@ int main(void) ledtest_done = ledtest(timebase_ms); led_chplex_periodic(); } else if(!startup_done) { - charge_pump_start(); + if(flash_config_is_present()) { + charge_pump_start(); - startup_done = true; + startup_done = true; + } else { + config_err_blink_code(timebase_ms); + led_chplex_periodic(); + } } else { measurement_start(); @@ -366,8 +381,8 @@ int main(void) charge_control_idle_since = timebase_ms; } else { // charge control already idle - if((timebase_ms - charge_control_idle_since) > DEEPSLEEP_DELAY) { - low_power_mode(DEEPSLEEP_DURATION); + if((timebase_ms - charge_control_idle_since) > FLASH_CONFIG_DEEPSLEEP_DELAY) { + low_power_mode(FLASH_CONFIG_DEEPSLEEP_DURATION); charge_control_was_idle = false; } } diff --git a/src/measurement.c b/src/measurement.c index 713e8cb..e91b303 100644 --- a/src/measurement.c +++ b/src/measurement.c @@ -8,7 +8,7 @@ #include "measurement.h" #include "calibration.h" -#include "config.h" +#include "flash_config.h" #define ADC_NUM_CHANNELS 6 static volatile int16_t adc_values[ADC_NUM_CHANNELS]; @@ -55,7 +55,7 @@ static fxp_t calc_temperature(uint16_t adc_val) static fxp_t adc_val_to_pin_voltage(uint16_t adc_val) { return fxp_div( - fxp_mult(FXP_FROM_INT(adc_val), fxp_div(FXP_FROM_INT(33), FXP_FROM_INT(10))), + fxp_mult(FXP_FROM_INT((int32_t)adc_val), fxp_div(FXP_FROM_INT(33), FXP_FROM_INT(10))), FXP_FROM_INT(4096)); } @@ -105,12 +105,12 @@ void measurement_init(void) 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)); + avg_alpha_i_solar = fxp_div(FXP_FROM_INT(FLASH_CONFIG_AVG_ALPHA_I_SOLAR), FXP_FROM_INT(1000)); + avg_alpha_i_load = fxp_div(FXP_FROM_INT(FLASH_CONFIG_AVG_ALPHA_I_LOAD), FXP_FROM_INT(1000)); + avg_alpha_u_bat = fxp_div(FXP_FROM_INT(FLASH_CONFIG_AVG_ALPHA_U_BAT), FXP_FROM_INT(1000)); + avg_alpha_u_sw = fxp_div(FXP_FROM_INT(FLASH_CONFIG_AVG_ALPHA_U_SW), FXP_FROM_INT(1000)); + avg_alpha_u_solar = fxp_div(FXP_FROM_INT(FLASH_CONFIG_AVG_ALPHA_U_SOLAR), FXP_FROM_INT(1000)); + avg_alpha_temp = fxp_div(FXP_FROM_INT(FLASH_CONFIG_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); diff --git a/utils/config_b26.yaml b/utils/config_b26.yaml new file mode 100644 index 0000000..8175032 --- /dev/null +++ b/utils/config_b26.yaml @@ -0,0 +1,108 @@ +# Values for the device at the B26 tower + +calibration: + # Calibration factor defined here are divided by 1000 to determine the actual + # scaling factor. Therefore, if the voltage determined by the firmware is 2% + # lower than the actual voltage, you have to scale by 1.02 and therefore + # specify 1020 in this list. + CAL_FACTOR_U_BAT: 994 + CAL_FACTOR_U_SOLAR: 997 + CAL_FACTOR_U_SW: 996 + CAL_FACTOR_I_SOLAR: 1015 + CAL_FACTOR_I_LOAD: 1000 + +config: + # Thresholds for charging control + + # Battery regulation corridor width (in mV). + U_BAT_REGULATION_CORRIDOR: 100 + + # Initial charge battery voltage threshold (in mV). + # stop charging if battery voltage reaches this threshold + U_BAT_INITIAL_FULL: 28600 + + # Cancel initial charge voltage hold below this battery voltage (in mV). + U_BAT_INITIAL_HOLD_CANCEL: 27000 + + # Transition to floating voltage levels after this time (in ms). + INITIAL_CHARGE_HOLD_TIME: 1800000 + + # Duration of the transistion from initial charging to float (in ms). + INITIAL_TO_FLOAT_TRANSITION_TIME: 600000 + + # Float charge battery voltage threshold (in mV). + # stop charging if battery voltage reaches this threshold + U_BAT_FLOAT_FULL: 27600 + + # Minimum voltage difference to U_bat that the solar panels must produce + # before charging is resumed after it was switched off (in mV). + SLEEP_SOLAR_EXCESS_VOLTAGE: 1000 + + # Minimum charge current required before charging is stopped to save power at + # the charge pump (in mA). + SLEEP_SOLAR_CURRENT: 1 + + # Delay between state change and sleep state check (in ms). + SLEEP_STATE_DELAY: 60000 + + # Delay between charging switch state change and sleep state check(in ms). + SLEEP_SWITCH_DELAY: 1000 + + # Maximum allowed microcontroller temperature (in units of 0.1 °C). If this + # temperature is exceeded, charging is stopped. The load is kept on. Do not + # set this too high as the heat has to propagate from the power MOSFETs. + INTERNAL_TEMPERATURE_LIMIT: 500 + + # Resume operation below this temperature (in units of 0.1 °C). + INTERNAL_TEMPERATURE_RECOVERY: 450 + + + # Thresholds for load control + + # Voltage above which the load is turned on (in mV). + U_BAT_LOAD_ON: 27000 + # Voltage below which the load is turned off (in mV). + U_BAT_LOAD_OFF: 24000 + + # Current at which the overload protection triggers (in mA). + LOAD_CURRENT_LIMIT_MA: 10000 + + # Inrush tolerance time (in ms). Overload protection is not enforced for this + # time after load power-on. + 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). + MIN_CHARGE_PUMP_EXCESS_VOLTAGE: 10000 + + # The minimum time the load must be off before it can be switched on again (in ms). + 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. + AVG_ALPHA_I_SOLAR: 10 + AVG_ALPHA_I_LOAD: 10 + AVG_ALPHA_U_BAT: 100 + AVG_ALPHA_U_SW: 100 + AVG_ALPHA_U_SOLAR: 100 + AVG_ALPHA_TEMP: 10 + + + # Generic configuration + + # Time (in ms) to stay active in idle state before entering deep sleep. + DEEPSLEEP_DELAY: 1000 + + # Deep sleep duration (in seconds). + DEEPSLEEP_DURATION: 10 diff --git a/utils/config_dev.yaml b/utils/config_dev.yaml new file mode 100644 index 0000000..8a65a4d --- /dev/null +++ b/utils/config_dev.yaml @@ -0,0 +1,108 @@ +# Values for the development device + +calibration: + # Calibration factor defined here are divided by 1000 to determine the actual + # scaling factor. Therefore, if the voltage determined by the firmware is 2% + # lower than the actual voltage, you have to scale by 1.02 and therefore + # specify 1020 in this list. + CAL_FACTOR_U_BAT: 1012 + CAL_FACTOR_U_SOLAR: 1015 + CAL_FACTOR_U_SW: 1006 + CAL_FACTOR_I_SOLAR: 3980 + CAL_FACTOR_I_LOAD: 1000 + +config: + # Thresholds for charging control + + # Battery regulation corridor width (in mV). + U_BAT_REGULATION_CORRIDOR: 100 + + # Initial charge battery voltage threshold (in mV). + # stop charging if battery voltage reaches this threshold + U_BAT_INITIAL_FULL: 28600 + + # Cancel initial charge voltage hold below this battery voltage (in mV). + U_BAT_INITIAL_HOLD_CANCEL: 27000 + + # Transition to floating voltage levels after this time (in ms). + INITIAL_CHARGE_HOLD_TIME: 1800000 + + # Duration of the transistion from initial charging to float (in ms). + INITIAL_TO_FLOAT_TRANSITION_TIME: 600000 + + # Float charge battery voltage threshold (in mV). + # stop charging if battery voltage reaches this threshold + U_BAT_FLOAT_FULL: 27600 + + # Minimum voltage difference to U_bat that the solar panels must produce + # before charging is resumed after it was switched off (in mV). + SLEEP_SOLAR_EXCESS_VOLTAGE: 1000 + + # Minimum charge current required before charging is stopped to save power at + # the charge pump (in mA). + SLEEP_SOLAR_CURRENT: 1 + + # Delay between state change and sleep state check (in ms). + SLEEP_STATE_DELAY: 60000 + + # Delay between charging switch state change and sleep state check(in ms). + SLEEP_SWITCH_DELAY: 1000 + + # Maximum allowed microcontroller temperature (in units of 0.1 °C). If this + # temperature is exceeded, charging is stopped. The load is kept on. Do not + # set this too high as the heat has to propagate from the power MOSFETs. + INTERNAL_TEMPERATURE_LIMIT: 500 + + # Resume operation below this temperature (in units of 0.1 °C). + INTERNAL_TEMPERATURE_RECOVERY: 450 + + + # Thresholds for load control + + # Voltage above which the load is turned on (in mV). + U_BAT_LOAD_ON: 27000 + # Voltage below which the load is turned off (in mV). + U_BAT_LOAD_OFF: 24000 + + # Current at which the overload protection triggers (in mA). + LOAD_CURRENT_LIMIT_MA: 10000 + + # Inrush tolerance time (in ms). Overload protection is not enforced for this + # time after load power-on. + 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). + MIN_CHARGE_PUMP_EXCESS_VOLTAGE: 10000 + + # The minimum time the load must be off before it can be switched on again (in ms). + 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. + AVG_ALPHA_I_SOLAR: 10 + AVG_ALPHA_I_LOAD: 10 + AVG_ALPHA_U_BAT: 100 + AVG_ALPHA_U_SW: 100 + AVG_ALPHA_U_SOLAR: 100 + AVG_ALPHA_TEMP: 10 + + + # Generic configuration + + # Time (in ms) to stay active in idle state before entering deep sleep. + DEEPSLEEP_DELAY: 1000 + + # Deep sleep duration (in seconds). + DEEPSLEEP_DURATION: 10 diff --git a/utils/config_to_hex.py b/utils/config_to_hex.py new file mode 100755 index 0000000..f96949c --- /dev/null +++ b/utils/config_to_hex.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 + +import sys +import struct + +import yaml +from intelhex import IntelHex + +BASE_ADDR = 0x08007C00 + +CALIBRATION_KEY_TO_OFFSET = { + "CAL_FACTOR_U_BAT": 0x0000, + "CAL_FACTOR_U_SOLAR": 0x0002, + "CAL_FACTOR_U_SW": 0x0004, + "CAL_FACTOR_I_SOLAR": 0x0006, + "CAL_FACTOR_I_LOAD": 0x0008, + } + +CONFIG_KEY_TO_OFFSET = { + "U_BAT_REGULATION_CORRIDOR": 0x0100, + "U_BAT_INITIAL_FULL": 0x0104, + "U_BAT_INITIAL_HOLD_CANCEL": 0x0108, + "INITIAL_CHARGE_HOLD_TIME": 0x010C, + "INITIAL_TO_FLOAT_TRANSITION_TIME": 0x0110, + "U_BAT_FLOAT_FULL": 0x0114, + "SLEEP_SOLAR_EXCESS_VOLTAGE": 0x0118, + "SLEEP_SOLAR_CURRENT": 0x011C, + "SLEEP_STATE_DELAY": 0x0120, + "SLEEP_SWITCH_DELAY": 0x0124, + "INTERNAL_TEMPERATURE_LIMIT": 0x0128, + "INTERNAL_TEMPERATURE_RECOVERY": 0x012C, + "U_BAT_LOAD_ON": 0x0130, + "U_BAT_LOAD_OFF": 0x0134, + "LOAD_CURRENT_LIMIT_MA": 0x0138, + "LOAD_CURRENT_INRUSH_TIME": 0x013C, + "MIN_CHARGE_PUMP_EXCESS_VOLTAGE": 0x0140, + "LOAD_ON_DELAY": 0x0144, + "AVG_ALPHA_I_SOLAR": 0x0148, + "AVG_ALPHA_I_LOAD": 0x014C, + "AVG_ALPHA_U_BAT": 0x0150, + "AVG_ALPHA_U_SW": 0x0154, + "AVG_ALPHA_U_SOLAR": 0x0158, + "AVG_ALPHA_TEMP": 0x015C, + "DEEPSLEEP_DELAY": 0x0160, + "DEEPSLEEP_DURATION": 0x0164, + } + + +if __name__ == "__main__": + output = IntelHex() + + if len(sys.argv) < 3: + print(f"usage: {sys.argv[0]} ") + sys.exit(1) + + with open(sys.argv[1], 'r') as configfile: + config = yaml.safe_load(configfile) + + for key, offset in CALIBRATION_KEY_TO_OFFSET.items(): + value = config['calibration'][key] + enc_bytes = struct.pack('