Compare commits

..

No commits in common. "main" and "flash_config" have entirely different histories.

19 changed files with 63 additions and 930 deletions

View file

@ -1,35 +0,0 @@
#include <libopencm3/stm32/gpio.h>
#include "pinout.h"
#include "addon_io.h"
static const uint32_t OUTPUT_LIST[2] = {ADDON_ISO_IO_OUT1, ADDON_ISO_IO_OUT2};
void addon_io_init(void)
{
// Isolated outputs configuration: output, initially low = off
gpio_clear(ADDON_ISO_IO_PORT, ADDON_ISO_IO_OUT1 | ADDON_ISO_IO_OUT2);
gpio_mode_setup(ADDON_ISO_IO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, ADDON_ISO_IO_OUT1 | ADDON_ISO_IO_OUT2);
// Isolated inputs configuration: input, no pull resistors
gpio_mode_setup(ADDON_ISO_IO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, ADDON_ISO_IO_IN);
}
void addon_io_iso_out_on(uint8_t idx)
{
gpio_set(ADDON_ISO_IO_PORT, OUTPUT_LIST[idx]);
}
void addon_io_iso_out_off(uint8_t idx)
{
gpio_clear(ADDON_ISO_IO_PORT, OUTPUT_LIST[idx]);
}
bool addon_io_read_iso_in(void)
{
return gpio_get(ADDON_ISO_IO_PORT, ADDON_ISO_IO_IN) != 0;
}

View file

@ -1,14 +0,0 @@
#ifndef ADDON_IO_H
#define ADDON_IO_H
#include <stdint.h>
#include <stdbool.h>
void addon_io_init(void);
void addon_io_iso_out_on(uint8_t idx);
void addon_io_iso_out_off(uint8_t idx);
bool addon_io_read_iso_in(void);
#endif // ADDON_IO_H

View file

@ -1,284 +0,0 @@
#include <libopencm3/stm32/i2c.h>
#include <libopencm3/stm32/gpio.h>
#include "i2c_dma.h"
#include "pinout.h"
#include "bmp280_comp.h"
#include "bmp280.h"
#define BMP280_7BIT_ADDR 0x76
typedef enum {
READY,
SEND_REQUEST,
WAIT_MEASUREMENT_COMPLETE,
READ_RESULTS,
COMPENSATE_TEMPERATURE,
COMPENSATE_PRESSURE,
ERROR // set by the I²C callback in case of errors/timeout
} bmp280_readout_state_t;
static bmp280_readout_state_t m_readout_state;
// calibration value cache, shared with bmp280_comp.c
extern uint16_t dig_T1;
extern int16_t dig_T2;
extern int16_t dig_T3;
extern uint16_t dig_P1;
extern int16_t dig_P2;
extern int16_t dig_P3;
extern int16_t dig_P4;
extern int16_t dig_P5;
extern int16_t dig_P6;
extern int16_t dig_P7;
extern int16_t dig_P8;
extern int16_t dig_P9;
static fxp_t m_last_temperature = 0;
static fxp_t m_last_pressure = 0;
static bool m_measurements_valid = false;
static int32_t m_temp_raw;
static int32_t m_press_raw;
static void cb_i2c_dma(i2c_dma_evt_t evt, const uint8_t *rdata, size_t rlen);
bool bmp280_init(void)
{
m_measurements_valid = false;
// configure pins for I2C1
gpio_set_af(BMP280_I2C_PORT, GPIO_AF4, BMP280_I2C_SCL | BMP280_I2C_SDA);
gpio_mode_setup(BMP280_I2C_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, BMP280_I2C_SCL | BMP280_I2C_SDA);
// Set up I²C
i2c_reset(I2C1);
i2c_set_speed(I2C1, i2c_speed_sm_100k, 48);
i2c_peripheral_enable(I2C1);
// Set up the Chip
uint8_t wdata[2];
uint8_t rdata[32];
uint8_t wsize, rsize;
// check for chip presence by accessing the chip id register (not a proper read!)
i2c_set_7bit_address(I2C1, BMP280_7BIT_ADDR);
i2c_set_write_transfer_dir(I2C1);
i2c_set_bytes_to_transfer(I2C1, 1);
i2c_enable_autoend(I2C1);
i2c_send_start(I2C1);
uint32_t timeout = 1000000;
while(--timeout && !i2c_transmit_int_status(I2C1)); // wait for the address transfer to complete
i2c_send_data(I2C1, 0xD0);
if((timeout == 0) || i2c_nack(I2C1)) {
// something went wrong during communication
goto err_out;
}
// read the chip ID
wsize = 0;
wdata[wsize++] = 0xD0;
rsize = 1;
i2c_transfer7(I2C1, BMP280_7BIT_ADDR, wdata, wsize, rdata, rsize);
if(rdata[0] != 0x58) {
// unexpected chip id
goto err_out;
}
// read calibration data
wsize = 0;
wdata[wsize++] = 0x88; // first calibration data register of first block
rsize = 0xA1 + 1 - 0x88;
i2c_transfer7(I2C1, BMP280_7BIT_ADDR, wdata, wsize, rdata, rsize);
dig_T1 = (((uint16_t)rdata[ 1]) << 8) | rdata[ 0]; // 0x88, 0x89
dig_T2 = (((int16_t)(int8_t)rdata[ 3]) << 8) | rdata[ 2]; // 0x8A, 0x8B
dig_T3 = (((int16_t)(int8_t)rdata[ 5]) << 8) | rdata[ 4]; // 0x8C, 0x8D
dig_P1 = (((uint16_t)rdata[ 7]) << 8) | rdata[ 6]; // 0x8E, 0x8F
dig_P2 = (((int16_t)(int8_t)rdata[ 9]) << 8) | rdata[ 8]; // 0x90, 0x91
dig_P3 = (((int16_t)(int8_t)rdata[11]) << 8) | rdata[10]; // 0x92, 0x93
dig_P4 = (((int16_t)(int8_t)rdata[13]) << 8) | rdata[12]; // 0x94, 0x95
dig_P5 = (((int16_t)(int8_t)rdata[15]) << 8) | rdata[14]; // 0x96, 0x97
dig_P6 = (((int16_t)(int8_t)rdata[17]) << 8) | rdata[16]; // 0x98, 0x99
dig_P7 = (((int16_t)(int8_t)rdata[19]) << 8) | rdata[18]; // 0x9A, 0x9B
dig_P8 = (((int16_t)(int8_t)rdata[21]) << 8) | rdata[20]; // 0x9C, 0x9D
dig_P9 = (((int16_t)(int8_t)rdata[23]) << 8) | rdata[22]; // 0x9E, 0x9F
i2c_dma_init(cb_i2c_dma);
return true;
err_out:
return false;
}
static void trigger_read_status_register(void)
{
uint8_t wdata[1];
uint8_t wsize = 0, rsize;
wdata[wsize++] = 0xF3; // status register
rsize = 1;
i2c_dma_start_transfer7(I2C1, BMP280_7BIT_ADDR, wdata, wsize, rsize);
}
static void trigger_read_results(void)
{
uint8_t wdata[1];
uint8_t wsize = 0, rsize;
wdata[wsize++] = 0xF7;
rsize = 8;
i2c_dma_start_transfer7(I2C1, BMP280_7BIT_ADDR, wdata, wsize, rsize);
}
static void cb_i2c_dma(i2c_dma_evt_t evt, const uint8_t *rdata, size_t rlen)
{
(void)rlen;
switch(evt) {
case I2C_DMA_EVT_TRANSFER_COMPLETE:
switch(m_readout_state) {
case SEND_REQUEST:
m_readout_state = WAIT_MEASUREMENT_COMPLETE;
trigger_read_status_register();
break;
case WAIT_MEASUREMENT_COMPLETE:
if((rdata[0] & 0x09) != 0) {
// either "measuring" or "im_update" is active, so measurement is not complete yet.
// try again:
trigger_read_status_register();
} else {
m_readout_state = READ_RESULTS;
trigger_read_results();
}
break;
case READ_RESULTS:
m_press_raw =
((int32_t)rdata[0] << 12) // press_msb (0xF7)
| ((int32_t)rdata[1] << 4) // press_lsb (0xF8)
| ((int32_t)rdata[2] >> 4); // press_xlsb (0xF9)
m_temp_raw =
((int32_t)rdata[3] << 12) // temp_msb (0xFA)
| ((int32_t)rdata[4] << 4) // temp_lsb (0xFB)
| ((int32_t)rdata[5] >> 4); // temp_xlsb (0xFC)
m_readout_state = COMPENSATE_TEMPERATURE;
break;
default:
// should never happen
while(1);
break;
}
break;
case I2C_DMA_EVT_TIMEOUT:
case I2C_DMA_EVT_NAK:
m_readout_state = ERROR;
break;
}
}
bool bmp280_loop(void)
{
bool retval = false;
switch(m_readout_state) {
case READY:
// nothing to do in this state
break;
case SEND_REQUEST:
case WAIT_MEASUREMENT_COMPLETE:
case READ_RESULTS:
// handled in cb_i2c_dma exclusively
break;
case COMPENSATE_TEMPERATURE:
m_last_temperature = bmp280_comp_temperature(m_temp_raw);
m_readout_state = COMPENSATE_PRESSURE;
break;
case COMPENSATE_PRESSURE:
m_last_pressure = bmp280_comp_pressure(m_press_raw);
m_readout_state = READY;
m_measurements_valid = true;
retval = true; // measurement complete
break;
case ERROR:
m_readout_state = READY;
m_measurements_valid = false;
retval = true; // measurement cycle terminated
break;
}
if(m_readout_state != READY) {
i2c_dma_loop();
}
return retval;
}
bool bmp280_start_measurement(void)
{
uint8_t wdata[2];
uint8_t wsize = 0;
if(m_readout_state != READY) {
return false; // still busy
}
wdata[wsize++] = 0xF4; // measurement control register
wdata[wsize++] = (0x01 << 5) | (0x01 << 2) | 0x01; // pressure x1, temp x1, forced mode
i2c_dma_start_transfer7(I2C1, BMP280_7BIT_ADDR, wdata, wsize, 0);
m_readout_state = SEND_REQUEST;
return true;
}
bool bmp280_are_measurements_valid(void)
{
return m_measurements_valid;
}
fxp_t bmp280_get_temperature(void)
{
return m_last_temperature;
}
fxp_t bmp280_get_pressure(void)
{
return m_last_pressure;
}

View file

@ -1,18 +0,0 @@
#ifndef BMP280_H
#define BMP280_H
#include <stdbool.h>
#include <stdint.h>
#include <fxp.h>
bool bmp280_init(void);
bool bmp280_loop(void); // returns true when measurement cycle has terminated (successfully or with error)
bool bmp280_start_measurement(void);
bool bmp280_are_measurements_valid(void);
fxp_t bmp280_get_temperature(void);
fxp_t bmp280_get_pressure(void);
#endif // BMP280_H

View file

@ -1,77 +0,0 @@
/* BMP280 value compensation code, from the datasheet. */
#include "bmp280_comp.h"
// calibration value cache. Values are set externally in bmp280.c
uint16_t dig_T1;
int16_t dig_T2;
int16_t dig_T3;
uint16_t dig_P1;
int16_t dig_P2;
int16_t dig_P3;
int16_t dig_P4;
int16_t dig_P5;
int16_t dig_P6;
int16_t dig_P7;
int16_t dig_P8;
int16_t dig_P9;
typedef int32_t BMP280_S32_t;
typedef uint32_t BMP280_U32_t;
typedef int64_t BMP280_S64_t;
// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.
// t_fine carries fine temperature as global value
BMP280_S32_t t_fine;
static BMP280_S32_t bmp280_compensate_T_int32(BMP280_S32_t adc_T)
{
BMP280_S32_t var1, var2, T;
var1 = ((((adc_T>>3) - ((BMP280_S32_t)dig_T1<<1))) * ((BMP280_S32_t)dig_T2)) >> 11;
var2 = (((((adc_T>>4) - ((BMP280_S32_t)dig_T1)) * ((adc_T>>4) - ((BMP280_S32_t)dig_T1))) >> 12) * ((BMP280_S32_t)dig_T3)) >> 14;
t_fine = var1 + var2;
T = (t_fine * 5 + 128) >> 8;
return T;
}
// Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
// Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa
static BMP280_U32_t bmp280_compensate_P_int64(BMP280_S32_t adc_P)
{
BMP280_S64_t var1, var2, p;
var1 = ((BMP280_S64_t)t_fine) - 128000;
var2 = var1 * var1 * (BMP280_S64_t)dig_P6;
var2 = var2 + ((var1*(BMP280_S64_t)dig_P5)<<17);
var2 = var2 + (((BMP280_S64_t)dig_P4)<<35);
var1 = ((var1 * var1 * (BMP280_S64_t)dig_P3)>>8) + ((var1 * (BMP280_S64_t)dig_P2)<<12);
var1 = (((((BMP280_S64_t)1)<<47)+var1))*((BMP280_S64_t)dig_P1)>>33;
if (var1 == 0) {
return 0; // avoid exception caused by division by zero
}
p = 1048576-adc_P;
p = (((p<<31)-var2)*3125)/var1;
var1 = (((BMP280_S64_t)dig_P9) * (p>>13) * (p>>13)) >> 25;
var2 = (((BMP280_S64_t)dig_P8) * p) >> 19;
p = ((p + var1 + var2) >> 8) + (((BMP280_S64_t)dig_P7)<<4);
return (BMP280_U32_t)p;
}
fxp_t bmp280_comp_temperature(int32_t adc)
{
int32_t compensated = bmp280_compensate_T_int32(adc);
return ((fxp_t)compensated << POINTPOS) / 100L;
}
fxp_t bmp280_comp_pressure(int32_t adc)
{
uint32_t compensated = bmp280_compensate_P_int64(adc);
return (fxp_t)(((uint64_t)compensated << (POINTPOS - 8L)) / 100);
}

View file

@ -1,23 +0,0 @@
#ifndef BMP280_COMP_H
#define BMP280_COMP_H
#include <stdint.h>
#include <fxp.h>
/*!@brief Calculate temperature from BMP280 raw sensor value.
* @returns The temperature in °C.
*/
fxp_t bmp280_comp_temperature(int32_t adc);
/*!@brief Calculate relative humidity from BMP280 raw sensor value.
* @returns The relative humidity in %.
*/
fxp_t bmp280_comp_humidity(int32_t adc);
/*!@brief Calculate pressure from BMP280 raw sensor value.
* @returns The pressure in hPa.
*/
fxp_t bmp280_comp_pressure(int32_t adc);
#endif // BMP280_COMP_H

View file

@ -2,33 +2,29 @@
#include <fxp.h> #include <fxp.h>
#include "bmp280.h"
#include "power_switch.h" #include "power_switch.h"
#include "measurement.h" #include "measurement.h"
#include "charge_pump.h" #include "charge_pump.h"
#include "rs485.h" #include "rs485.h"
#include "flash_config.h" #include "flash_config.h"
#include "addon_io.h"
#include "charge_control.h" #include "charge_control.h"
static const char *CHARGE_STATE_TEXT[CHARGE_NUM_STATES] = { static const char *CHARGE_STATE_TEXT[] = {
"WAIT_CHARGEPUMP", "WAIT_CHARGEPUMP",
"INITIAL", "INITIAL",
"INITIAL_HOLD", "INITIAL_HOLD",
"TRANSITION", "TRANSITION",
"FLOAT", "FLOAT",
"SLEEP", "SLEEP",
"HIGH_INTERNAL_TEMPERATURE", "HIGH_TEMPERATURE"
"LOW_EXTERNAL_TEMPERATURE"
}; };
static const char *DISCHARGE_STATE_TEXT[DISCHARGE_NUM_STATES] = { static const char *DISCHARGE_STATE_TEXT[] = {
"WAIT_CHARGEPUMP", "WAIT_CHARGEPUMP",
"OK", "OK",
"VOLTAGE_LOW", "VOLTAGE_LOW",
"OVERCURRENT", "OVERCURRENT"
"OVERCURRENT_DELAY"
}; };
static enum ChargeState charge_state; static enum ChargeState charge_state;
@ -56,19 +52,13 @@ static fxp_t u_bat_load_on;
static fxp_t u_bat_load_off; static fxp_t u_bat_load_off;
static fxp_t load_current_limit; static fxp_t load_current_limit;
static fxp_t load_current_limit_delay;
static fxp_t internal_temperature_limit; static fxp_t internal_temperature_limit;
static fxp_t internal_temperature_recovery; static fxp_t internal_temperature_recovery;
static fxp_t external_temperature_limit;
static fxp_t external_temperature_recovery;
static fxp_t sleep_solar_current; static fxp_t sleep_solar_current;
static fxp_t sleep_solar_excess_voltage; static fxp_t sleep_solar_excess_voltage;
static uint32_t overload_retry_time;
static enum ChargeState control_solar_charging( static enum ChargeState control_solar_charging(
fxp_t corridor_high, fxp_t corridor_high,
@ -96,15 +86,9 @@ static enum ChargeState control_solar_charging(
solar_switch_onoff_duration = 0; solar_switch_onoff_duration = 0;
} }
// internal temperature limit: prevent overheating of the power transistors. // temperature limit
if(meas->avg_temperature > internal_temperature_limit) { if(meas->avg_temperature > internal_temperature_limit) {
return CHARGE_HIGH_INTERNAL_TEMPERATURE; return CHARGE_HIGH_TEMPERATURE;
}
// external temperature limit: prevent charging the battery if temperature is too low.
if(bmp280_are_measurements_valid() &&
bmp280_get_temperature() < external_temperature_limit) {
return CHARGE_LOW_EXTERNAL_TEMPERATURE;
} }
// low-current limit (go to sleep at night) // low-current limit (go to sleep at night)
@ -211,7 +195,7 @@ static void solar_fsm_update(uint64_t uptime_ms, struct MeasurementResult *meas)
// temperature limit // temperature limit
if(meas->temperature > internal_temperature_limit) { if(meas->temperature > internal_temperature_limit) {
charge_state = CHARGE_HIGH_INTERNAL_TEMPERATURE; charge_state = CHARGE_HIGH_TEMPERATURE;
break; break;
} }
break; break;
@ -233,7 +217,7 @@ static void solar_fsm_update(uint64_t uptime_ms, struct MeasurementResult *meas)
break; break;
case CHARGE_HIGH_INTERNAL_TEMPERATURE: case CHARGE_HIGH_TEMPERATURE:
if(charge_state_entered) { if(charge_state_entered) {
power_switch_solar_off(); power_switch_solar_off();
} }
@ -244,27 +228,6 @@ static void solar_fsm_update(uint64_t uptime_ms, struct MeasurementResult *meas)
} }
break; break;
case CHARGE_LOW_EXTERNAL_TEMPERATURE:
if(charge_state_entered) {
power_switch_solar_off();
// switch on the heater via the isolated I/O addon board
addon_io_iso_out_on(0);
}
// this state can only be entered if the BMP280 measurement is valid, so
// no need to check it again here.
if(bmp280_get_temperature() > external_temperature_recovery) {
charge_state = CHARGE_WAIT_CHARGEPUMP;
// switch the heater off again when this state is left
addon_io_iso_out_off(0);
break;
}
break;
default: default:
// unknown state // unknown state
break; break;
@ -298,20 +261,7 @@ static void load_fsm_update(uint64_t uptime_ms, struct MeasurementResult *meas)
if((meas->i_load > load_current_limit) if((meas->i_load > load_current_limit)
&& (discharge_time_in_state > FLASH_CONFIG_LOAD_CURRENT_INRUSH_TIME)) { && (discharge_time_in_state > FLASH_CONFIG_LOAD_CURRENT_INRUSH_TIME)) {
if(load_current_limit_delay == 0) { discharge_state = DISCHARGE_OVERCURRENT;
// switch off immediately
power_switch_load_off();
discharge_state = DISCHARGE_OVERCURRENT;
} else {
discharge_state = DISCHARGE_OVERCURRENT_DELAY;
}
}
if((overload_retry_time != FLASH_CONFIG_OVERLOAD_RETRY_TIME) &&
(discharge_time_in_state > 300000)) {
// overload did not trigger for 5 minutes, so we assume its stable and
// reset the retry time delay.
overload_retry_time = FLASH_CONFIG_OVERLOAD_RETRY_TIME;
} }
if(meas->avg_u_bat < u_bat_load_off) { if(meas->avg_u_bat < u_bat_load_off) {
@ -328,36 +278,17 @@ 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 // Can only switch on again after a specific amount of time has passed
if((meas->avg_u_bat > u_bat_load_on) if((meas->avg_u_bat > u_bat_load_on)
&& (discharge_time_in_state > FLASH_CONFIG_LOAD_ON_DELAY)) { && (discharge_time_in_state > FLASH_CONFIG_LOAD_ON_DELAY)) {
discharge_state = DISCHARGE_WAIT_CHARGEPUMP;
}
break;
case DISCHARGE_OVERCURRENT_DELAY:
if(meas->i_load < load_current_limit) {
// current recovered
discharge_state = DISCHARGE_OK; discharge_state = DISCHARGE_OK;
} else if(discharge_time_in_state >= FLASH_CONFIG_OVERLOAD_DELAY_TIME) {
// switch off immediately
power_switch_load_off();
discharge_state = DISCHARGE_OVERCURRENT;
} }
break; break;
case DISCHARGE_OVERCURRENT: case DISCHARGE_OVERCURRENT:
// Current limit reached // Battery voltage is too low, so keep the load switched off
if(discharge_state_entered) { if(discharge_state_entered) {
power_switch_load_off(); power_switch_load_off();
} }
// Overload recovery // no way out except reset
if(discharge_time_in_state >= overload_retry_time) {
// double the overload retry time for the next turn if it is less than 7 days
if(overload_retry_time < (7*24*3600*1000)) {
overload_retry_time *= 2;
}
discharge_state = DISCHARGE_WAIT_CHARGEPUMP;
}
break; break;
default: default:
@ -398,13 +329,8 @@ void charge_control_init(void)
internal_temperature_limit = fxp_div(FXP_FROM_INT(FLASH_CONFIG_INTERNAL_TEMPERATURE_LIMIT), 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)); internal_temperature_recovery = fxp_div(FXP_FROM_INT(FLASH_CONFIG_INTERNAL_TEMPERATURE_RECOVERY), FXP_FROM_INT(10));
external_temperature_limit = fxp_div(FXP_FROM_INT(FLASH_CONFIG_EXTERNAL_TEMPERATURE_LIMIT), FXP_FROM_INT(10));
external_temperature_recovery = fxp_div(FXP_FROM_INT(FLASH_CONFIG_EXTERNAL_TEMPERATURE_RECOVERY), FXP_FROM_INT(10));
sleep_solar_current = fxp_div(FXP_FROM_INT(FLASH_CONFIG_SLEEP_SOLAR_CURRENT), 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)); sleep_solar_excess_voltage = fxp_div(FXP_FROM_INT(FLASH_CONFIG_SLEEP_SOLAR_EXCESS_VOLTAGE), FXP_FROM_INT(1000));
overload_retry_time = FLASH_CONFIG_OVERLOAD_RETRY_TIME;
} }
@ -455,8 +381,7 @@ void charge_control_update(uint64_t uptime_ms, struct MeasurementResult *meas)
bool charge_control_is_idle(void) bool charge_control_is_idle(void)
{ {
return (((charge_state == CHARGE_SLEEP) return (((charge_state == CHARGE_SLEEP)
|| (charge_state == CHARGE_HIGH_INTERNAL_TEMPERATURE) || (charge_state == CHARGE_HIGH_TEMPERATURE))
|| (charge_state == CHARGE_LOW_EXTERNAL_TEMPERATURE))
&& ((discharge_state == DISCHARGE_VOLTAGE_LOW) && ((discharge_state == DISCHARGE_VOLTAGE_LOW)
|| (discharge_state == DISCHARGE_OVERCURRENT))); || (discharge_state == DISCHARGE_OVERCURRENT)));
} }
@ -465,8 +390,7 @@ bool charge_control_is_idle(void)
bool charge_control_is_charge_blocked(void) bool charge_control_is_charge_blocked(void)
{ {
switch(charge_state) { switch(charge_state) {
case CHARGE_HIGH_INTERNAL_TEMPERATURE: case CHARGE_HIGH_TEMPERATURE:
case CHARGE_LOW_EXTERNAL_TEMPERATURE:
case CHARGE_WAIT_CHARGEPUMP: case CHARGE_WAIT_CHARGEPUMP:
return true; return true;

View file

@ -15,10 +15,7 @@ enum ChargeState
CHARGE_TRANSITION, CHARGE_TRANSITION,
CHARGE_FLOAT, CHARGE_FLOAT,
CHARGE_SLEEP, CHARGE_SLEEP,
CHARGE_HIGH_INTERNAL_TEMPERATURE, CHARGE_HIGH_TEMPERATURE
CHARGE_LOW_EXTERNAL_TEMPERATURE,
CHARGE_NUM_STATES
}; };
enum DischargeState enum DischargeState
@ -26,10 +23,7 @@ enum DischargeState
DISCHARGE_WAIT_CHARGEPUMP, DISCHARGE_WAIT_CHARGEPUMP,
DISCHARGE_OK, DISCHARGE_OK,
DISCHARGE_VOLTAGE_LOW, DISCHARGE_VOLTAGE_LOW,
DISCHARGE_OVERCURRENT, DISCHARGE_OVERCURRENT
DISCHARGE_OVERCURRENT_DELAY,
DISCHARGE_NUM_STATES
}; };
// Error flags // Error flags

View file

@ -1,7 +1,6 @@
#include <libopencm3/stm32/rcc.h> #include <libopencm3/stm32/rcc.h>
#include "clock.h" #include "clock.h"
#include "libopencm3/stm32/f0/rcc.h"
void init_clock(void) void init_clock(void)
{ {
@ -27,9 +26,6 @@ void init_clock(void)
// DMA1 is used for ADC data transfer // DMA1 is used for ADC data transfer
rcc_periph_clock_enable(RCC_DMA1); rcc_periph_clock_enable(RCC_DMA1);
// I2C1 is used for communication with the BMP280
rcc_periph_clock_enable(RCC_I2C1);
} }

View file

@ -3,5 +3,5 @@
bool flash_config_is_present(void) bool flash_config_is_present(void)
{ {
return (FLASH_CONFIG_CAL_FACTOR_U_BAT != 0xFFFF) return (FLASH_CONFIG_CAL_FACTOR_U_BAT != 0xFFFF)
&& (FLASH_CONFIG_DEEPSLEEP_DURATION != 0xFFFFFFFF); && (FLASH_CONFIG_U_BAT_REGULATION_CORRIDOR != -1);
} }

View file

@ -63,47 +63,27 @@ extern uint8_t __conf_start;
/* Resume operation below this temperature (in units of 0.1 °C). */ /* Resume operation below this temperature (in units of 0.1 °C). */
#define FLASH_CONFIG_INTERNAL_TEMPERATURE_RECOVERY (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x012C)) #define FLASH_CONFIG_INTERNAL_TEMPERATURE_RECOVERY (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x012C))
/* Minimum allowed external (BMP280) temperature (in units of 0.1 °C). If the
* temperature falls below this value, charging is stopped to prevent damage to
* the battery. */
#define FLASH_CONFIG_EXTERNAL_TEMPERATURE_LIMIT (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0130))
/* Resume operation above this temperature (in units of 0.1 °C). */
#define FLASH_CONFIG_EXTERNAL_TEMPERATURE_RECOVERY (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0134))
/* Thresholds for load control */ /* Thresholds for load control */
/* Voltage above which the load is turned on (in mV). */ /* Voltage above which the load is turned on (in mV). */
#define FLASH_CONFIG_U_BAT_LOAD_ON (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0138)) #define FLASH_CONFIG_U_BAT_LOAD_ON (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0130))
/* Voltage below which the load is turned off (in mV). */ /* Voltage below which the load is turned off (in mV). */
#define FLASH_CONFIG_U_BAT_LOAD_OFF (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x013c)) #define FLASH_CONFIG_U_BAT_LOAD_OFF (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0134))
/* Current at which the overload protection triggers (in mA). */ /* Current at which the overload protection triggers (in mA). */
#define FLASH_CONFIG_LOAD_CURRENT_LIMIT_MA (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0140)) #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 /* Inrush tolerance time (in ms). Overload protection is not enforced for this
* time after load power-on. */ * time after load power-on. */
#define FLASH_CONFIG_LOAD_CURRENT_INRUSH_TIME (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x0144)) #define FLASH_CONFIG_LOAD_CURRENT_INRUSH_TIME (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x013C))
/* 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 + 0x0144))
/* Minimum voltage that the charge pump must produce above U_bat before any /* Minimum voltage that the charge pump must produce above U_bat before any
* power FET is switched on (in mV). */ * power FET is switched on (in mV). */
#define FLASH_CONFIG_MIN_CHARGE_PUMP_EXCESS_VOLTAGE (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0148)) #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). */ /* 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 + 0x014c)) #define FLASH_CONFIG_LOAD_ON_DELAY (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x0144))
/* Overload delay time (in ms). If load current is too high for this duration,
* load is switched permanently off. */
#define FLASH_CONFIG_OVERLOAD_DELAY_TIME (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x0170))
/* Overload retry time (in ms). Load is switched on again after this time if
* overload condition has triggered. */
#define FLASH_CONFIG_OVERLOAD_RETRY_TIME (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x0174))
/* Measurement Averaging: /* Measurement Averaging:
@ -119,23 +99,21 @@ extern uint8_t __conf_start;
* */ * */
/* Averaging factor for load current. */ /* Averaging factor for load current. */
#define FLASH_CONFIG_AVG_ALPHA_I_SOLAR (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0150)) #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 + 0x0154)) #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 + 0x0158)) #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 + 0x015c)) #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 + 0x0160)) #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 + 0x0164)) #define FLASH_CONFIG_AVG_ALPHA_TEMP (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x015C))
/* Generic configuration */ /* Generic configuration */
/* Time (in ms) to stay active in idle state before entering deep sleep. */ /* Time (in ms) to stay active in idle state before entering deep sleep. */
#define FLASH_CONFIG_DEEPSLEEP_DELAY (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x0168)) #define FLASH_CONFIG_DEEPSLEEP_DELAY (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x0160))
/* Deep sleep duration (in seconds). */ /* Deep sleep duration (in seconds). */
#define FLASH_CONFIG_DEEPSLEEP_DURATION (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x016c)) #define FLASH_CONFIG_DEEPSLEEP_DURATION (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x0164))
/* FIXME: next free memory location: 0x178. Update when adding a value! */
/* Functions */ /* Functions */

View file

@ -1,171 +0,0 @@
#include <string.h>
#include <libopencm3/stm32/i2c.h>
#include <libopencm3/stm32/dma.h>
#include "i2c_dma.h"
#define DMA_CHANNEL_TX DMA_CHANNEL2
#define DMA_CHANNEL_RX DMA_CHANNEL3
#define TIMEOUT_COUNTER_VALUE 100 // milliseconds
static uint8_t m_wdata[2];
static uint8_t m_wlen;
static uint8_t m_rdata[32];
static uint8_t m_rlen;
static uint8_t m_addr;
static uint32_t m_i2c;
static uint32_t m_timeout_counter;
typedef enum {
IDLE,
SEND_DATA,
RECEIVE_DATA
} i2c_dma_state_t;
static i2c_dma_state_t m_state;
static i2c_dma_callback_t m_callback;
static void setup_txdma(void)
{
dma_channel_reset(DMA1, DMA_CHANNEL_TX);
dma_set_priority(DMA1, DMA_CHANNEL_TX, DMA_CCR_PL_LOW);
dma_set_memory_size(DMA1, DMA_CHANNEL_TX, DMA_CCR_MSIZE_8BIT);
dma_set_peripheral_size(DMA1, DMA_CHANNEL_TX, DMA_CCR_PSIZE_8BIT);
dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL_TX);
dma_set_read_from_memory(DMA1, DMA_CHANNEL_TX);
dma_set_peripheral_address(DMA1, DMA_CHANNEL_TX, (uint32_t) &I2C_TXDR(m_i2c));
// send m_wlen bytes from m_wdata
dma_set_memory_address(DMA1, DMA_CHANNEL_TX, (uint32_t)m_wdata);
dma_set_number_of_data(DMA1, DMA_CHANNEL_TX, m_wlen);
dma_enable_channel(DMA1, DMA_CHANNEL_TX);
i2c_enable_txdma(m_i2c);
}
static void setup_rxdma(void)
{
dma_channel_reset(DMA1, DMA_CHANNEL_RX);
dma_set_priority(DMA1, DMA_CHANNEL_RX, DMA_CCR_PL_LOW);
dma_set_memory_size(DMA1, DMA_CHANNEL_RX, DMA_CCR_MSIZE_8BIT);
dma_set_peripheral_size(DMA1, DMA_CHANNEL_RX, DMA_CCR_PSIZE_8BIT);
dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL_RX);
dma_set_read_from_peripheral(DMA1, DMA_CHANNEL_RX);
dma_set_peripheral_address(DMA1, DMA_CHANNEL_RX, (uint32_t) &I2C_RXDR(m_i2c));
// receive m_rlen bytes and store them in m_rdata
dma_set_memory_address(DMA1, DMA_CHANNEL_RX, (uint32_t)m_rdata);
dma_set_number_of_data(DMA1, DMA_CHANNEL_RX, m_rlen);
dma_enable_channel(DMA1, DMA_CHANNEL_RX);
i2c_enable_rxdma(m_i2c);
}
void i2c_dma_init(i2c_dma_callback_t callback)
{
m_callback = callback;
m_state = IDLE;
}
void i2c_dma_loop(void)
{
switch(m_state) {
case IDLE:
// nothing to do
return;
case SEND_DATA:
//if(i2c_transfer_complete(m_i2c)) { // does not work for some reason :(
if(dma_get_interrupt_flag(DMA1, DMA_CHANNEL_TX, DMA_TCIF)) {
dma_clear_interrupt_flags(DMA1, DMA_CHANNEL_TX, DMA_TCIF);
if(m_rlen) {
m_state = RECEIVE_DATA;
i2c_set_7bit_address(m_i2c, m_addr);
i2c_set_read_transfer_dir(m_i2c);
i2c_set_bytes_to_transfer(m_i2c, m_rlen);
i2c_send_start(m_i2c);
i2c_enable_autoend(m_i2c); // important to do it after start() for a proper repeated start!
setup_rxdma();
} else {
// no RX requested => we are done here
m_state = IDLE;
m_callback(I2C_DMA_EVT_TRANSFER_COMPLETE, NULL, 0);
}
}
if(I2C_ISR(m_i2c) & I2C_ISR_NACKF) {
// there should be no NACK during the transfer => abort
I2C_ICR(m_i2c) |= I2C_ICR_NACKCF; // clear the interrupt flag
m_state = IDLE;
m_callback(I2C_DMA_EVT_NAK, NULL, 0);
}
break;
case RECEIVE_DATA:
//if(i2c_transfer_complete(m_i2c)) { // does not work for some reason :(
if(dma_get_interrupt_flag(DMA1, DMA_CHANNEL_RX, DMA_TCIF)) {
dma_clear_interrupt_flags(DMA1, DMA_CHANNEL_RX, DMA_TCIF);
m_state = IDLE;
m_callback(I2C_DMA_EVT_TRANSFER_COMPLETE, m_rdata, m_rlen);
}
if(I2C_ISR(m_i2c) & I2C_ISR_NACKF) {
// there should be no NACK during the transfer => abort
I2C_ICR(m_i2c) |= I2C_ICR_NACKCF; // clear the interrupt flag
m_state = IDLE;
m_callback(I2C_DMA_EVT_NAK, NULL, 0);
}
break;
}
m_timeout_counter--;
if(m_timeout_counter == 0) {
m_callback(I2C_DMA_EVT_TIMEOUT, NULL, 0);
}
}
void i2c_dma_start_transfer7(uint32_t i2c, uint8_t addr, const uint8_t *w, size_t wn, size_t rn)
{
m_wlen = wn;
m_rlen = rn;
memcpy(m_wdata, w, wn);
m_state = SEND_DATA;
m_i2c = i2c;
m_addr = addr;
i2c_set_7bit_address(i2c, addr);
i2c_set_write_transfer_dir(i2c);
i2c_set_bytes_to_transfer(i2c, wn);
if(rn) {
i2c_disable_autoend(i2c); // prepare repeated start
} else {
i2c_enable_autoend(i2c); // stop condition after write transfer
}
m_timeout_counter = TIMEOUT_COUNTER_VALUE;
i2c_send_start(i2c); // start the transfer. The rest is handled by the DMA.
setup_txdma();
}

View file

@ -1,22 +0,0 @@
#ifndef I2C_DMA_H
#define I2C_DMA_H
#include <stdint.h>
#include <stddef.h>
typedef enum
{
I2C_DMA_EVT_TRANSFER_COMPLETE,
I2C_DMA_EVT_NAK,
I2C_DMA_EVT_TIMEOUT,
} i2c_dma_evt_t;
typedef void (*i2c_dma_callback_t)(i2c_dma_evt_t evt, const uint8_t *rdata, size_t rlen);
void i2c_dma_init(i2c_dma_callback_t callback);
void i2c_dma_loop(void);
void i2c_dma_start_transfer7(uint32_t i2c, uint8_t addr, const uint8_t *w, size_t wn, size_t rn);
#endif // I2C_DMA_H

View file

@ -16,19 +16,12 @@
#include "power_switch.h" #include "power_switch.h"
#include "measurement.h" #include "measurement.h"
#include "deepsleep.h" #include "deepsleep.h"
#include "bmp280.h"
#include "addon_io.h"
#include "pinout.h" #include "pinout.h"
#include "flash_config.h" #include "flash_config.h"
volatile int wait_frame = 1; volatile int wait_frame = 1;
static enum {
BMP280_NOT_PRESENT,
BMP280_IDLE,
BMP280_MEASURING,
} bmp280_state;
/* Set up systick to fire freq times per second */ /* Set up systick to fire freq times per second */
static void init_systick(int freq) static void init_systick(int freq)
@ -177,18 +170,6 @@ static void report_status(struct MeasurementResult *meas_data)
rs485_enqueue(":"); rs485_enqueue(":");
rs485_enqueue(charge_control_is_discharge_blocked() ? "1" : "0"); rs485_enqueue(charge_control_is_discharge_blocked() ? "1" : "0");
if(bmp280_state != BMP280_NOT_PRESENT
&& bmp280_are_measurements_valid()) {
rs485_enqueue(":");
fxp_format(bmp280_get_temperature(), number, 2);
rs485_enqueue(number);
rs485_enqueue(":");
fxp_format(bmp280_get_pressure(), number, 2);
rs485_enqueue(number);
}
rs485_enqueue("\n"); rs485_enqueue("\n");
} }
@ -227,19 +208,6 @@ static void report_averaged(struct MeasurementResult *meas_data)
rs485_enqueue(":"); rs485_enqueue(":");
rs485_enqueue(charge_control_is_discharge_blocked() ? "1" : "0"); rs485_enqueue(charge_control_is_discharge_blocked() ? "1" : "0");
if(bmp280_state != BMP280_NOT_PRESENT
&& bmp280_are_measurements_valid()) {
rs485_enqueue(":");
fxp_format(bmp280_get_temperature(), number, 2);
rs485_enqueue(number);
rs485_enqueue(":");
fxp_format(bmp280_get_pressure(), number, 2);
rs485_enqueue(number);
}
rs485_enqueue("\n"); rs485_enqueue("\n");
} }
@ -265,29 +233,6 @@ static void low_power_mode(uint32_t duration_sec)
} }
static void handle_bmp280(uint64_t timebase_ms)
{
switch(bmp280_state) {
case BMP280_NOT_PRESENT:
// do nothing
break;
case BMP280_IDLE:
if(timebase_ms % 1000 == 137) {
bmp280_start_measurement();
bmp280_state = BMP280_MEASURING;
}
break;
case BMP280_MEASURING:
if(timebase_ms % 10 == 0 && bmp280_loop()) {
bmp280_state = BMP280_IDLE;
}
break;
}
}
int main(void) int main(void)
{ {
//uint32_t cpuload = 0; //uint32_t cpuload = 0;
@ -305,7 +250,6 @@ int main(void)
init_clock(); init_clock();
init_rtc(); init_rtc();
addon_io_init();
rs485_init(); rs485_init();
charge_pump_init(); charge_pump_init();
power_switch_init(); power_switch_init();
@ -315,12 +259,6 @@ int main(void)
led_chplex_init(); led_chplex_init();
led_chplex_on(LED_CHPLEX_IDX_SOLAR_ON); led_chplex_on(LED_CHPLEX_IDX_SOLAR_ON);
if(bmp280_init()) {
bmp280_state = BMP280_IDLE;
} else {
bmp280_state = BMP280_NOT_PRESENT;
}
init_systick(1000); init_systick(1000);
rs485_enqueue("LNSC-2420 v" VERSION " initialized.\n"); rs485_enqueue("LNSC-2420 v" VERSION " initialized.\n");
@ -360,9 +298,6 @@ int main(void)
report_averaged(&meas_data); report_averaged(&meas_data);
} }
// update BMP280
handle_bmp280(timebase_ms);
measurement_wait_for_completion(); measurement_wait_for_completion();
measurement_finalize(&meas_data); measurement_finalize(&meas_data);
@ -371,24 +306,19 @@ int main(void)
charge_control_update(timebase_ms, &meas_data); charge_control_update(timebase_ms, &meas_data);
// deep sleep control // deep sleep control
if(bmp280_state != BMP280_MEASURING) { // general blockers if(charge_control_is_idle()) {
if(charge_control_is_idle()) { if(!charge_control_was_idle) {
if(!charge_control_was_idle) { charge_control_was_idle = true;
charge_control_was_idle = true; charge_control_idle_since = timebase_ms;
charge_control_idle_since = timebase_ms;
} else {
// charge control already idle
if((timebase_ms - charge_control_idle_since) > FLASH_CONFIG_DEEPSLEEP_DELAY) {
low_power_mode(FLASH_CONFIG_DEEPSLEEP_DURATION);
charge_control_was_idle = false;
// correct the time base after deep sleep
timebase_ms += FLASH_CONFIG_DEEPSLEEP_DURATION * 1000;
}
}
} else { } else {
charge_control_was_idle = false; // charge control already idle
if((timebase_ms - charge_control_idle_since) > FLASH_CONFIG_DEEPSLEEP_DELAY) {
low_power_mode(FLASH_CONFIG_DEEPSLEEP_DURATION);
charge_control_was_idle = false;
}
} }
} else {
charge_control_was_idle = false;
} }
} }

View file

@ -7,6 +7,7 @@
#include "pinout.h" #include "pinout.h"
#include "measurement.h" #include "measurement.h"
#include "calibration.h"
#include "flash_config.h" #include "flash_config.h"
#define ADC_NUM_CHANNELS 6 #define ADC_NUM_CHANNELS 6
@ -93,15 +94,15 @@ void measurement_init(void)
// Convert calibration factors to fixed-point numbers for direct use // Convert calibration factors to fixed-point numbers for direct use
calibration_factors[ANALOG_INPUT_U_BAT] = calibration_factors[ANALOG_INPUT_U_BAT] =
fxp_div(FXP_FROM_INT(FLASH_CONFIG_CAL_FACTOR_U_BAT), FXP_FROM_INT(1000)); fxp_div(FXP_FROM_INT(CAL_FACTOR_U_BAT), FXP_FROM_INT(1000));
calibration_factors[ANALOG_INPUT_U_SOLAR] = calibration_factors[ANALOG_INPUT_U_SOLAR] =
fxp_div(FXP_FROM_INT(FLASH_CONFIG_CAL_FACTOR_U_SOLAR), FXP_FROM_INT(1000)); fxp_div(FXP_FROM_INT(CAL_FACTOR_U_SOLAR), FXP_FROM_INT(1000));
calibration_factors[ANALOG_INPUT_U_SW] = calibration_factors[ANALOG_INPUT_U_SW] =
fxp_div(FXP_FROM_INT(FLASH_CONFIG_CAL_FACTOR_U_SW), FXP_FROM_INT(1000)); fxp_div(FXP_FROM_INT(CAL_FACTOR_U_SW), FXP_FROM_INT(1000));
calibration_factors[ANALOG_INPUT_I_SOLAR] = calibration_factors[ANALOG_INPUT_I_SOLAR] =
fxp_div(FXP_FROM_INT(FLASH_CONFIG_CAL_FACTOR_I_SOLAR), FXP_FROM_INT(1000)); fxp_div(FXP_FROM_INT(CAL_FACTOR_I_SOLAR), FXP_FROM_INT(1000));
calibration_factors[ANALOG_INPUT_I_LOAD] = calibration_factors[ANALOG_INPUT_I_LOAD] =
fxp_div(FXP_FROM_INT(FLASH_CONFIG_CAL_FACTOR_I_LOAD), FXP_FROM_INT(1000)); fxp_div(FXP_FROM_INT(CAL_FACTOR_I_LOAD), FXP_FROM_INT(1000));
// Convert and precalculate coefficients for exponential averaging // Convert and precalculate coefficients for exponential averaging
avg_alpha_i_solar = fxp_div(FXP_FROM_INT(FLASH_CONFIG_AVG_ALPHA_I_SOLAR), FXP_FROM_INT(1000)); avg_alpha_i_solar = fxp_div(FXP_FROM_INT(FLASH_CONFIG_AVG_ALPHA_I_SOLAR), FXP_FROM_INT(1000));

View file

@ -43,21 +43,4 @@
#define RS485_TX_PIN GPIO6 #define RS485_TX_PIN GPIO6
#define RS485_RX_PIN GPIO7 #define RS485_RX_PIN GPIO7
/*** Expansion connector signals ***/
/* BMP280 I²C */
#define BMP280_I2C_PORT GPIOA
#define BMP280_I2C_SCL GPIO9
#define BMP280_I2C_SDA GPIO10
/* Isolated inputs and outputs on I/O addon board */
#define ADDON_ISO_IO_PORT GPIOA
#define ADDON_ISO_IO_OUT1 GPIO5
#define ADDON_ISO_IO_OUT2 GPIO6
#define ADDON_ISO_IO_IN GPIO7
#endif // PINOUT_H #endif // PINOUT_H

View file

@ -56,21 +56,13 @@ config:
# Resume operation below this temperature (in units of 0.1 °C). # Resume operation below this temperature (in units of 0.1 °C).
INTERNAL_TEMPERATURE_RECOVERY: 450 INTERNAL_TEMPERATURE_RECOVERY: 450
# Minimum allowed external (BMP280) temperature (in units of 0.1 °C). If the
# temperature falls below this value, charging is stopped to prevent damage to
# the battery.
EXTERNAL_TEMPERATURE_LIMIT: -30
# Resume operation above this temperature (in units of 0.1 °C).
EXTERNAL_TEMPERATURE_RECOVERY: 0
# Thresholds for load control # Thresholds for load control
# Voltage above which the load is turned on (in mV). # Voltage above which the load is turned on (in mV).
U_BAT_LOAD_ON: 27000 U_BAT_LOAD_ON: 27000
# Voltage below which the load is turned off (in mV). # Voltage below which the load is turned off (in mV).
U_BAT_LOAD_OFF: 24500 U_BAT_LOAD_OFF: 24000
# Current at which the overload protection triggers (in mA). # Current at which the overload protection triggers (in mA).
LOAD_CURRENT_LIMIT_MA: 10000 LOAD_CURRENT_LIMIT_MA: 10000
@ -79,15 +71,6 @@ config:
# time after load power-on. # time after load power-on.
LOAD_CURRENT_INRUSH_TIME: 10 LOAD_CURRENT_INRUSH_TIME: 10
# Overload delay time (in ms). If load current is too high for this duration,
# load is switched permanently off.
FLASH_CONFIG_OVERLOAD_DELAY_TIME: 10
# Overload retry time (in ms). Load is switched on again after this time if
# overload condition has triggered. If overload immediately triggers again,
# this time is doubled.
FLASH_CONFIG_OVERLOAD_RETRY_TIME: 10000
# Minimum voltage that the charge pump must produce above U_bat before any # Minimum voltage that the charge pump must produce above U_bat before any
# power FET is switched on (in mV). # power FET is switched on (in mV).
MIN_CHARGE_PUMP_EXCESS_VOLTAGE: 10000 MIN_CHARGE_PUMP_EXCESS_VOLTAGE: 10000

View file

@ -56,14 +56,6 @@ config:
# Resume operation below this temperature (in units of 0.1 °C). # Resume operation below this temperature (in units of 0.1 °C).
INTERNAL_TEMPERATURE_RECOVERY: 450 INTERNAL_TEMPERATURE_RECOVERY: 450
# Minimum allowed external (BMP280) temperature (in units of 0.1 °C). If the
# temperature falls below this value, charging is stopped to prevent damage to
# the battery.
EXTERNAL_TEMPERATURE_LIMIT: 250
# Resume operation above this temperature (in units of 0.1 °C).
EXTERNAL_TEMPERATURE_RECOVERY: 270
# Thresholds for load control # Thresholds for load control

View file

@ -29,24 +29,20 @@ CONFIG_KEY_TO_OFFSET = {
"SLEEP_SWITCH_DELAY": 0x0124, "SLEEP_SWITCH_DELAY": 0x0124,
"INTERNAL_TEMPERATURE_LIMIT": 0x0128, "INTERNAL_TEMPERATURE_LIMIT": 0x0128,
"INTERNAL_TEMPERATURE_RECOVERY": 0x012C, "INTERNAL_TEMPERATURE_RECOVERY": 0x012C,
"EXTERNAL_TEMPERATURE_LIMIT": 0x0130, "U_BAT_LOAD_ON": 0x0130,
"EXTERNAL_TEMPERATURE_RECOVERY": 0x0134, "U_BAT_LOAD_OFF": 0x0134,
"U_BAT_LOAD_ON": 0x0138, "LOAD_CURRENT_LIMIT_MA": 0x0138,
"U_BAT_LOAD_OFF": 0x013c, "LOAD_CURRENT_INRUSH_TIME": 0x013C,
"LOAD_CURRENT_LIMIT_MA": 0x0140, "MIN_CHARGE_PUMP_EXCESS_VOLTAGE": 0x0140,
"LOAD_CURRENT_INRUSH_TIME": 0x0144, "LOAD_ON_DELAY": 0x0144,
"MIN_CHARGE_PUMP_EXCESS_VOLTAGE": 0x0148, "AVG_ALPHA_I_SOLAR": 0x0148,
"LOAD_ON_DELAY": 0x014c, "AVG_ALPHA_I_LOAD": 0x014C,
"AVG_ALPHA_I_SOLAR": 0x0150, "AVG_ALPHA_U_BAT": 0x0150,
"AVG_ALPHA_I_LOAD": 0x0154, "AVG_ALPHA_U_SW": 0x0154,
"AVG_ALPHA_U_BAT": 0x0158, "AVG_ALPHA_U_SOLAR": 0x0158,
"AVG_ALPHA_U_SW": 0x015c, "AVG_ALPHA_TEMP": 0x015C,
"AVG_ALPHA_U_SOLAR": 0x0160, "DEEPSLEEP_DELAY": 0x0160,
"AVG_ALPHA_TEMP": 0x0164, "DEEPSLEEP_DURATION": 0x0164,
"DEEPSLEEP_DELAY": 0x0168,
"DEEPSLEEP_DURATION": 0x016c,
"FLASH_CONFIG_OVERLOAD_DELAY_TIME": 0x0170,
"FLASH_CONFIG_OVERLOAD_RETRY_TIME": 0x0174
} }
@ -67,7 +63,7 @@ if __name__ == "__main__":
for key, offset in CONFIG_KEY_TO_OFFSET.items(): for key, offset in CONFIG_KEY_TO_OFFSET.items():
value = config['config'][key] value = config['config'][key]
enc_bytes = struct.pack('<i', value) enc_bytes = struct.pack('<I', value)
output.puts(BASE_ADDR + offset, enc_bytes) output.puts(BASE_ADDR + offset, enc_bytes)
output.write_hex_file(sys.argv[2]) output.write_hex_file(sys.argv[2])