Compare commits
No commits in common. "main" and "flash_config" have entirely different histories.
main
...
flash_conf
|
@ -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;
|
||||
}
|
|
@ -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
|
284
src/bmp280.c
284
src/bmp280.c
|
@ -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;
|
||||
}
|
18
src/bmp280.h
18
src/bmp280.h
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -2,33 +2,29 @@
|
|||
|
||||
#include <fxp.h>
|
||||
|
||||
#include "bmp280.h"
|
||||
#include "power_switch.h"
|
||||
#include "measurement.h"
|
||||
#include "charge_pump.h"
|
||||
#include "rs485.h"
|
||||
#include "flash_config.h"
|
||||
#include "addon_io.h"
|
||||
|
||||
#include "charge_control.h"
|
||||
|
||||
static const char *CHARGE_STATE_TEXT[CHARGE_NUM_STATES] = {
|
||||
static const char *CHARGE_STATE_TEXT[] = {
|
||||
"WAIT_CHARGEPUMP",
|
||||
"INITIAL",
|
||||
"INITIAL_HOLD",
|
||||
"TRANSITION",
|
||||
"FLOAT",
|
||||
"SLEEP",
|
||||
"HIGH_INTERNAL_TEMPERATURE",
|
||||
"LOW_EXTERNAL_TEMPERATURE"
|
||||
"HIGH_TEMPERATURE"
|
||||
};
|
||||
|
||||
static const char *DISCHARGE_STATE_TEXT[DISCHARGE_NUM_STATES] = {
|
||||
static const char *DISCHARGE_STATE_TEXT[] = {
|
||||
"WAIT_CHARGEPUMP",
|
||||
"OK",
|
||||
"VOLTAGE_LOW",
|
||||
"OVERCURRENT",
|
||||
"OVERCURRENT_DELAY"
|
||||
"OVERCURRENT"
|
||||
};
|
||||
|
||||
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 load_current_limit;
|
||||
static fxp_t load_current_limit_delay;
|
||||
|
||||
static fxp_t internal_temperature_limit;
|
||||
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_excess_voltage;
|
||||
|
||||
static uint32_t overload_retry_time;
|
||||
|
||||
|
||||
static enum ChargeState control_solar_charging(
|
||||
fxp_t corridor_high,
|
||||
|
@ -96,15 +86,9 @@ static enum ChargeState control_solar_charging(
|
|||
solar_switch_onoff_duration = 0;
|
||||
}
|
||||
|
||||
// internal temperature limit: prevent overheating of the power transistors.
|
||||
// temperature limit
|
||||
if(meas->avg_temperature > internal_temperature_limit) {
|
||||
return CHARGE_HIGH_INTERNAL_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;
|
||||
return CHARGE_HIGH_TEMPERATURE;
|
||||
}
|
||||
|
||||
// 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
|
||||
if(meas->temperature > internal_temperature_limit) {
|
||||
charge_state = CHARGE_HIGH_INTERNAL_TEMPERATURE;
|
||||
charge_state = CHARGE_HIGH_TEMPERATURE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -233,7 +217,7 @@ static void solar_fsm_update(uint64_t uptime_ms, struct MeasurementResult *meas)
|
|||
break;
|
||||
|
||||
|
||||
case CHARGE_HIGH_INTERNAL_TEMPERATURE:
|
||||
case CHARGE_HIGH_TEMPERATURE:
|
||||
if(charge_state_entered) {
|
||||
power_switch_solar_off();
|
||||
}
|
||||
|
@ -244,27 +228,6 @@ static void solar_fsm_update(uint64_t uptime_ms, struct MeasurementResult *meas)
|
|||
}
|
||||
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:
|
||||
// unknown state
|
||||
break;
|
||||
|
@ -298,20 +261,7 @@ static void load_fsm_update(uint64_t uptime_ms, struct MeasurementResult *meas)
|
|||
|
||||
if((meas->i_load > load_current_limit)
|
||||
&& (discharge_time_in_state > FLASH_CONFIG_LOAD_CURRENT_INRUSH_TIME)) {
|
||||
if(load_current_limit_delay == 0) {
|
||||
// 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 it’s stable and
|
||||
// reset the retry time delay.
|
||||
overload_retry_time = FLASH_CONFIG_OVERLOAD_RETRY_TIME;
|
||||
discharge_state = DISCHARGE_OVERCURRENT;
|
||||
}
|
||||
|
||||
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
|
||||
if((meas->avg_u_bat > u_bat_load_on)
|
||||
&& (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;
|
||||
} else if(discharge_time_in_state >= FLASH_CONFIG_OVERLOAD_DELAY_TIME) {
|
||||
// switch off immediately
|
||||
power_switch_load_off();
|
||||
discharge_state = DISCHARGE_OVERCURRENT;
|
||||
}
|
||||
break;
|
||||
|
||||
case DISCHARGE_OVERCURRENT:
|
||||
// Current limit reached
|
||||
// Battery voltage is too low, so keep the load switched off
|
||||
if(discharge_state_entered) {
|
||||
power_switch_load_off();
|
||||
}
|
||||
|
||||
// Overload recovery
|
||||
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;
|
||||
}
|
||||
// no way out except reset
|
||||
break;
|
||||
|
||||
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_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_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)
|
||||
{
|
||||
return (((charge_state == CHARGE_SLEEP)
|
||||
|| (charge_state == CHARGE_HIGH_INTERNAL_TEMPERATURE)
|
||||
|| (charge_state == CHARGE_LOW_EXTERNAL_TEMPERATURE))
|
||||
|| (charge_state == CHARGE_HIGH_TEMPERATURE))
|
||||
&& ((discharge_state == DISCHARGE_VOLTAGE_LOW)
|
||||
|| (discharge_state == DISCHARGE_OVERCURRENT)));
|
||||
}
|
||||
|
@ -465,8 +390,7 @@ bool charge_control_is_idle(void)
|
|||
bool charge_control_is_charge_blocked(void)
|
||||
{
|
||||
switch(charge_state) {
|
||||
case CHARGE_HIGH_INTERNAL_TEMPERATURE:
|
||||
case CHARGE_LOW_EXTERNAL_TEMPERATURE:
|
||||
case CHARGE_HIGH_TEMPERATURE:
|
||||
case CHARGE_WAIT_CHARGEPUMP:
|
||||
return true;
|
||||
|
||||
|
|
|
@ -15,10 +15,7 @@ enum ChargeState
|
|||
CHARGE_TRANSITION,
|
||||
CHARGE_FLOAT,
|
||||
CHARGE_SLEEP,
|
||||
CHARGE_HIGH_INTERNAL_TEMPERATURE,
|
||||
CHARGE_LOW_EXTERNAL_TEMPERATURE,
|
||||
|
||||
CHARGE_NUM_STATES
|
||||
CHARGE_HIGH_TEMPERATURE
|
||||
};
|
||||
|
||||
enum DischargeState
|
||||
|
@ -26,10 +23,7 @@ enum DischargeState
|
|||
DISCHARGE_WAIT_CHARGEPUMP,
|
||||
DISCHARGE_OK,
|
||||
DISCHARGE_VOLTAGE_LOW,
|
||||
DISCHARGE_OVERCURRENT,
|
||||
DISCHARGE_OVERCURRENT_DELAY,
|
||||
|
||||
DISCHARGE_NUM_STATES
|
||||
DISCHARGE_OVERCURRENT
|
||||
};
|
||||
|
||||
// Error flags
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include <libopencm3/stm32/rcc.h>
|
||||
|
||||
#include "clock.h"
|
||||
#include "libopencm3/stm32/f0/rcc.h"
|
||||
|
||||
void init_clock(void)
|
||||
{
|
||||
|
@ -27,9 +26,6 @@ void init_clock(void)
|
|||
|
||||
// DMA1 is used for ADC data transfer
|
||||
rcc_periph_clock_enable(RCC_DMA1);
|
||||
|
||||
// I2C1 is used for communication with the BMP280
|
||||
rcc_periph_clock_enable(RCC_I2C1);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
bool flash_config_is_present(void)
|
||||
{
|
||||
return (FLASH_CONFIG_CAL_FACTOR_U_BAT != 0xFFFF)
|
||||
&& (FLASH_CONFIG_DEEPSLEEP_DURATION != 0xFFFFFFFF);
|
||||
&& (FLASH_CONFIG_U_BAT_REGULATION_CORRIDOR != -1);
|
||||
}
|
||||
|
|
|
@ -63,47 +63,27 @@ extern uint8_t __conf_start;
|
|||
/* Resume operation below this temperature (in units of 0.1 °C). */
|
||||
#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 */
|
||||
|
||||
/* 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). */
|
||||
#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). */
|
||||
#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
|
||||
* time after load power-on. */
|
||||
#define FLASH_CONFIG_LOAD_CURRENT_INRUSH_TIME (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x0144))
|
||||
|
||||
/* 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))
|
||||
#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 + 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). */
|
||||
#define FLASH_CONFIG_LOAD_ON_DELAY (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x014c))
|
||||
|
||||
/* 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))
|
||||
#define FLASH_CONFIG_LOAD_ON_DELAY (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x0144))
|
||||
|
||||
|
||||
/* Measurement Averaging:
|
||||
|
@ -119,23 +99,21 @@ extern uint8_t __conf_start;
|
|||
* */
|
||||
|
||||
/* 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_LOAD (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0154))
|
||||
#define FLASH_CONFIG_AVG_ALPHA_U_BAT (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0158))
|
||||
#define FLASH_CONFIG_AVG_ALPHA_U_SW (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x015c))
|
||||
#define FLASH_CONFIG_AVG_ALPHA_U_SOLAR (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0160))
|
||||
#define FLASH_CONFIG_AVG_ALPHA_TEMP (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0164))
|
||||
#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 + 0x0168))
|
||||
#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 + 0x016c))
|
||||
|
||||
/* FIXME: next free memory location: 0x178. Update when adding a value! */
|
||||
#define FLASH_CONFIG_DEEPSLEEP_DURATION (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x0164))
|
||||
|
||||
|
||||
/* Functions */
|
||||
|
|
171
src/i2c_dma.c
171
src/i2c_dma.c
|
@ -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();
|
||||
}
|
|
@ -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
|
92
src/main.c
92
src/main.c
|
@ -16,19 +16,12 @@
|
|||
#include "power_switch.h"
|
||||
#include "measurement.h"
|
||||
#include "deepsleep.h"
|
||||
#include "bmp280.h"
|
||||
#include "addon_io.h"
|
||||
|
||||
#include "pinout.h"
|
||||
#include "flash_config.h"
|
||||
|
||||
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 */
|
||||
static void init_systick(int freq)
|
||||
|
@ -177,18 +170,6 @@ static void report_status(struct MeasurementResult *meas_data)
|
|||
rs485_enqueue(":");
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
|
@ -227,19 +208,6 @@ static void report_averaged(struct MeasurementResult *meas_data)
|
|||
rs485_enqueue(":");
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
//uint32_t cpuload = 0;
|
||||
|
@ -305,7 +250,6 @@ int main(void)
|
|||
init_clock();
|
||||
init_rtc();
|
||||
|
||||
addon_io_init();
|
||||
rs485_init();
|
||||
charge_pump_init();
|
||||
power_switch_init();
|
||||
|
@ -315,12 +259,6 @@ int main(void)
|
|||
led_chplex_init();
|
||||
led_chplex_on(LED_CHPLEX_IDX_SOLAR_ON);
|
||||
|
||||
if(bmp280_init()) {
|
||||
bmp280_state = BMP280_IDLE;
|
||||
} else {
|
||||
bmp280_state = BMP280_NOT_PRESENT;
|
||||
}
|
||||
|
||||
init_systick(1000);
|
||||
|
||||
rs485_enqueue("LNSC-2420 v" VERSION " initialized.\n");
|
||||
|
@ -360,9 +298,6 @@ int main(void)
|
|||
report_averaged(&meas_data);
|
||||
}
|
||||
|
||||
// update BMP280
|
||||
handle_bmp280(timebase_ms);
|
||||
|
||||
measurement_wait_for_completion();
|
||||
measurement_finalize(&meas_data);
|
||||
|
||||
|
@ -371,24 +306,19 @@ int main(void)
|
|||
charge_control_update(timebase_ms, &meas_data);
|
||||
|
||||
// deep sleep control
|
||||
if(bmp280_state != BMP280_MEASURING) { // general blockers
|
||||
if(charge_control_is_idle()) {
|
||||
if(!charge_control_was_idle) {
|
||||
charge_control_was_idle = true;
|
||||
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;
|
||||
}
|
||||
}
|
||||
if(charge_control_is_idle()) {
|
||||
if(!charge_control_was_idle) {
|
||||
charge_control_was_idle = true;
|
||||
charge_control_idle_since = timebase_ms;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "pinout.h"
|
||||
|
||||
#include "measurement.h"
|
||||
#include "calibration.h"
|
||||
#include "flash_config.h"
|
||||
|
||||
#define ADC_NUM_CHANNELS 6
|
||||
|
@ -93,15 +94,15 @@ void measurement_init(void)
|
|||
|
||||
// Convert calibration factors to fixed-point numbers for direct use
|
||||
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] =
|
||||
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] =
|
||||
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] =
|
||||
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] =
|
||||
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
|
||||
avg_alpha_i_solar = fxp_div(FXP_FROM_INT(FLASH_CONFIG_AVG_ALPHA_I_SOLAR), FXP_FROM_INT(1000));
|
||||
|
|
17
src/pinout.h
17
src/pinout.h
|
@ -43,21 +43,4 @@
|
|||
#define RS485_TX_PIN GPIO6
|
||||
#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
|
||||
|
|
|
@ -56,21 +56,13 @@ config:
|
|||
# Resume operation below this temperature (in units of 0.1 °C).
|
||||
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
|
||||
|
||||
# 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: 24500
|
||||
U_BAT_LOAD_OFF: 24000
|
||||
|
||||
# Current at which the overload protection triggers (in mA).
|
||||
LOAD_CURRENT_LIMIT_MA: 10000
|
||||
|
@ -79,15 +71,6 @@ config:
|
|||
# time after load power-on.
|
||||
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
|
||||
# power FET is switched on (in mV).
|
||||
MIN_CHARGE_PUMP_EXCESS_VOLTAGE: 10000
|
||||
|
|
|
@ -56,14 +56,6 @@ config:
|
|||
# Resume operation below this temperature (in units of 0.1 °C).
|
||||
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
|
||||
|
||||
|
|
|
@ -29,24 +29,20 @@ CONFIG_KEY_TO_OFFSET = {
|
|||
"SLEEP_SWITCH_DELAY": 0x0124,
|
||||
"INTERNAL_TEMPERATURE_LIMIT": 0x0128,
|
||||
"INTERNAL_TEMPERATURE_RECOVERY": 0x012C,
|
||||
"EXTERNAL_TEMPERATURE_LIMIT": 0x0130,
|
||||
"EXTERNAL_TEMPERATURE_RECOVERY": 0x0134,
|
||||
"U_BAT_LOAD_ON": 0x0138,
|
||||
"U_BAT_LOAD_OFF": 0x013c,
|
||||
"LOAD_CURRENT_LIMIT_MA": 0x0140,
|
||||
"LOAD_CURRENT_INRUSH_TIME": 0x0144,
|
||||
"MIN_CHARGE_PUMP_EXCESS_VOLTAGE": 0x0148,
|
||||
"LOAD_ON_DELAY": 0x014c,
|
||||
"AVG_ALPHA_I_SOLAR": 0x0150,
|
||||
"AVG_ALPHA_I_LOAD": 0x0154,
|
||||
"AVG_ALPHA_U_BAT": 0x0158,
|
||||
"AVG_ALPHA_U_SW": 0x015c,
|
||||
"AVG_ALPHA_U_SOLAR": 0x0160,
|
||||
"AVG_ALPHA_TEMP": 0x0164,
|
||||
"DEEPSLEEP_DELAY": 0x0168,
|
||||
"DEEPSLEEP_DURATION": 0x016c,
|
||||
"FLASH_CONFIG_OVERLOAD_DELAY_TIME": 0x0170,
|
||||
"FLASH_CONFIG_OVERLOAD_RETRY_TIME": 0x0174
|
||||
"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,
|
||||
}
|
||||
|
||||
|
||||
|
@ -67,7 +63,7 @@ if __name__ == "__main__":
|
|||
|
||||
for key, offset in CONFIG_KEY_TO_OFFSET.items():
|
||||
value = config['config'][key]
|
||||
enc_bytes = struct.pack('<i', value)
|
||||
enc_bytes = struct.pack('<I', value)
|
||||
output.puts(BASE_ADDR + offset, enc_bytes)
|
||||
|
||||
output.write_hex_file(sys.argv[2])
|
||||
|
|
Loading…
Reference in a new issue