Compare commits
18 commits
flash_conf
...
main
Author | SHA1 | Date | |
---|---|---|---|
Thomas Kolb | e6891aed78 | ||
Thomas Kolb | 19735ee550 | ||
Thomas Kolb | e8dff1f017 | ||
Thomas Kolb | bd08440584 | ||
Thomas Kolb | 2cedcb712a | ||
Thomas Kolb | 1153da6fc2 | ||
Thomas Kolb | a140b3ade6 | ||
Thomas Kolb | 5090b7de4a | ||
Thomas Kolb | 4d4cc41b46 | ||
Thomas Kolb | 430b7c73d0 | ||
Thomas Kolb | 1cbe0ce410 | ||
Thomas Kolb | 5c6497fb82 | ||
Thomas Kolb | d7b8c90cb4 | ||
Thomas Kolb | c807acec11 | ||
Thomas Kolb | 07dd91ecc2 | ||
Thomas Kolb | 055186180f | ||
Thomas Kolb | 3dcf412a02 | ||
Thomas Kolb | 2f0f6a01f2 |
35
src/addon_io.c
Normal file
35
src/addon_io.c
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#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;
|
||||||
|
}
|
14
src/addon_io.h
Normal file
14
src/addon_io.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#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
Normal file
284
src/bmp280.c
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
#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
Normal file
18
src/bmp280.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#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
|
77
src/bmp280_comp.c
Normal file
77
src/bmp280_comp.c
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
/* 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);
|
||||||
|
}
|
23
src/bmp280_comp.h
Normal file
23
src/bmp280_comp.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#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,29 +2,33 @@
|
||||||
|
|
||||||
#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[] = {
|
static const char *CHARGE_STATE_TEXT[CHARGE_NUM_STATES] = {
|
||||||
"WAIT_CHARGEPUMP",
|
"WAIT_CHARGEPUMP",
|
||||||
"INITIAL",
|
"INITIAL",
|
||||||
"INITIAL_HOLD",
|
"INITIAL_HOLD",
|
||||||
"TRANSITION",
|
"TRANSITION",
|
||||||
"FLOAT",
|
"FLOAT",
|
||||||
"SLEEP",
|
"SLEEP",
|
||||||
"HIGH_TEMPERATURE"
|
"HIGH_INTERNAL_TEMPERATURE",
|
||||||
|
"LOW_EXTERNAL_TEMPERATURE"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *DISCHARGE_STATE_TEXT[] = {
|
static const char *DISCHARGE_STATE_TEXT[DISCHARGE_NUM_STATES] = {
|
||||||
"WAIT_CHARGEPUMP",
|
"WAIT_CHARGEPUMP",
|
||||||
"OK",
|
"OK",
|
||||||
"VOLTAGE_LOW",
|
"VOLTAGE_LOW",
|
||||||
"OVERCURRENT"
|
"OVERCURRENT",
|
||||||
|
"OVERCURRENT_DELAY"
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum ChargeState charge_state;
|
static enum ChargeState charge_state;
|
||||||
|
@ -52,13 +56,19 @@ 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,
|
||||||
|
@ -86,9 +96,15 @@ static enum ChargeState control_solar_charging(
|
||||||
solar_switch_onoff_duration = 0;
|
solar_switch_onoff_duration = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// temperature limit
|
// internal temperature limit: prevent overheating of the power transistors.
|
||||||
if(meas->avg_temperature > internal_temperature_limit) {
|
if(meas->avg_temperature > internal_temperature_limit) {
|
||||||
return CHARGE_HIGH_TEMPERATURE;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// low-current limit (go to sleep at night)
|
// low-current limit (go to sleep at night)
|
||||||
|
@ -195,7 +211,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_TEMPERATURE;
|
charge_state = CHARGE_HIGH_INTERNAL_TEMPERATURE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -217,7 +233,7 @@ static void solar_fsm_update(uint64_t uptime_ms, struct MeasurementResult *meas)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
case CHARGE_HIGH_TEMPERATURE:
|
case CHARGE_HIGH_INTERNAL_TEMPERATURE:
|
||||||
if(charge_state_entered) {
|
if(charge_state_entered) {
|
||||||
power_switch_solar_off();
|
power_switch_solar_off();
|
||||||
}
|
}
|
||||||
|
@ -228,6 +244,27 @@ 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;
|
||||||
|
@ -261,7 +298,20 @@ 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)) {
|
||||||
discharge_state = DISCHARGE_OVERCURRENT;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(meas->avg_u_bat < u_bat_load_off) {
|
if(meas->avg_u_bat < u_bat_load_off) {
|
||||||
|
@ -278,17 +328,36 @@ 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:
|
||||||
// Battery voltage is too low, so keep the load switched off
|
// Current limit reached
|
||||||
if(discharge_state_entered) {
|
if(discharge_state_entered) {
|
||||||
power_switch_load_off();
|
power_switch_load_off();
|
||||||
}
|
}
|
||||||
|
|
||||||
// no way out except reset
|
// 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;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -329,8 +398,13 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -381,7 +455,8 @@ 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_TEMPERATURE))
|
|| (charge_state == CHARGE_HIGH_INTERNAL_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)));
|
||||||
}
|
}
|
||||||
|
@ -390,7 +465,8 @@ 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_TEMPERATURE:
|
case CHARGE_HIGH_INTERNAL_TEMPERATURE:
|
||||||
|
case CHARGE_LOW_EXTERNAL_TEMPERATURE:
|
||||||
case CHARGE_WAIT_CHARGEPUMP:
|
case CHARGE_WAIT_CHARGEPUMP:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,10 @@ enum ChargeState
|
||||||
CHARGE_TRANSITION,
|
CHARGE_TRANSITION,
|
||||||
CHARGE_FLOAT,
|
CHARGE_FLOAT,
|
||||||
CHARGE_SLEEP,
|
CHARGE_SLEEP,
|
||||||
CHARGE_HIGH_TEMPERATURE
|
CHARGE_HIGH_INTERNAL_TEMPERATURE,
|
||||||
|
CHARGE_LOW_EXTERNAL_TEMPERATURE,
|
||||||
|
|
||||||
|
CHARGE_NUM_STATES
|
||||||
};
|
};
|
||||||
|
|
||||||
enum DischargeState
|
enum DischargeState
|
||||||
|
@ -23,7 +26,10 @@ 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
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#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)
|
||||||
{
|
{
|
||||||
|
@ -26,6 +27,9 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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_U_BAT_REGULATION_CORRIDOR != -1);
|
&& (FLASH_CONFIG_DEEPSLEEP_DURATION != 0xFFFFFFFF);
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,27 +63,47 @@ 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 + 0x0130))
|
#define FLASH_CONFIG_U_BAT_LOAD_ON (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0138))
|
||||||
/* 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 + 0x0134))
|
#define FLASH_CONFIG_U_BAT_LOAD_OFF (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x013c))
|
||||||
|
|
||||||
/* 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 + 0x0138))
|
#define FLASH_CONFIG_LOAD_CURRENT_LIMIT_MA (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0140))
|
||||||
|
|
||||||
/* 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 + 0x013C))
|
#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))
|
||||||
|
|
||||||
/* 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 + 0x0140))
|
#define FLASH_CONFIG_MIN_CHARGE_PUMP_EXCESS_VOLTAGE (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0148))
|
||||||
|
|
||||||
/* 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 + 0x0144))
|
#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))
|
||||||
|
|
||||||
|
|
||||||
/* Measurement Averaging:
|
/* Measurement Averaging:
|
||||||
|
@ -99,21 +119,23 @@ 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 + 0x0148))
|
#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 + 0x014C))
|
#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 + 0x0150))
|
#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 + 0x0154))
|
#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 + 0x0158))
|
#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 + 0x015C))
|
#define FLASH_CONFIG_AVG_ALPHA_TEMP (*(int32_t*)(FLASH_CONFIG_BASE_PTR + 0x0164))
|
||||||
|
|
||||||
|
|
||||||
/* 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 + 0x0160))
|
#define FLASH_CONFIG_DEEPSLEEP_DELAY (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x0168))
|
||||||
|
|
||||||
/* Deep sleep duration (in seconds). */
|
/* Deep sleep duration (in seconds). */
|
||||||
#define FLASH_CONFIG_DEEPSLEEP_DURATION (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x0164))
|
#define FLASH_CONFIG_DEEPSLEEP_DURATION (*(uint32_t*)(FLASH_CONFIG_BASE_PTR + 0x016c))
|
||||||
|
|
||||||
|
/* FIXME: next free memory location: 0x178. Update when adding a value! */
|
||||||
|
|
||||||
|
|
||||||
/* Functions */
|
/* Functions */
|
||||||
|
|
171
src/i2c_dma.c
Normal file
171
src/i2c_dma.c
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
#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();
|
||||||
|
}
|
22
src/i2c_dma.h
Normal file
22
src/i2c_dma.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#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,12 +16,19 @@
|
||||||
#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)
|
||||||
|
@ -170,6 +177,18 @@ 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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,6 +227,19 @@ 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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,6 +265,29 @@ 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;
|
||||||
|
@ -250,6 +305,7 @@ 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();
|
||||||
|
@ -259,6 +315,12 @@ 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");
|
||||||
|
@ -298,6 +360,9 @@ 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);
|
||||||
|
|
||||||
|
@ -306,19 +371,24 @@ int main(void)
|
||||||
charge_control_update(timebase_ms, &meas_data);
|
charge_control_update(timebase_ms, &meas_data);
|
||||||
|
|
||||||
// deep sleep control
|
// deep sleep control
|
||||||
if(charge_control_is_idle()) {
|
if(bmp280_state != BMP280_MEASURING) { // general blockers
|
||||||
if(!charge_control_was_idle) {
|
if(charge_control_is_idle()) {
|
||||||
charge_control_was_idle = true;
|
if(!charge_control_was_idle) {
|
||||||
charge_control_idle_since = timebase_ms;
|
charge_control_was_idle = true;
|
||||||
} else {
|
charge_control_idle_since = timebase_ms;
|
||||||
// charge control already idle
|
} else {
|
||||||
if((timebase_ms - charge_control_idle_since) > FLASH_CONFIG_DEEPSLEEP_DELAY) {
|
// charge control already idle
|
||||||
low_power_mode(FLASH_CONFIG_DEEPSLEEP_DURATION);
|
if((timebase_ms - charge_control_idle_since) > FLASH_CONFIG_DEEPSLEEP_DELAY) {
|
||||||
charge_control_was_idle = false;
|
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 {
|
||||||
|
charge_control_was_idle = false;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
charge_control_was_idle = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#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
|
||||||
|
@ -94,15 +93,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(CAL_FACTOR_U_BAT), FXP_FROM_INT(1000));
|
fxp_div(FXP_FROM_INT(FLASH_CONFIG_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(CAL_FACTOR_U_SOLAR), FXP_FROM_INT(1000));
|
fxp_div(FXP_FROM_INT(FLASH_CONFIG_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(CAL_FACTOR_U_SW), FXP_FROM_INT(1000));
|
fxp_div(FXP_FROM_INT(FLASH_CONFIG_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(CAL_FACTOR_I_SOLAR), FXP_FROM_INT(1000));
|
fxp_div(FXP_FROM_INT(FLASH_CONFIG_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(CAL_FACTOR_I_LOAD), FXP_FROM_INT(1000));
|
fxp_div(FXP_FROM_INT(FLASH_CONFIG_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));
|
||||||
|
|
17
src/pinout.h
17
src/pinout.h
|
@ -43,4 +43,21 @@
|
||||||
#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
|
||||||
|
|
|
@ -56,13 +56,21 @@ 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: 24000
|
U_BAT_LOAD_OFF: 24500
|
||||||
|
|
||||||
# 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
|
||||||
|
@ -71,6 +79,15 @@ 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
|
||||||
|
|
|
@ -56,6 +56,14 @@ 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
|
||||||
|
|
||||||
|
|
|
@ -29,20 +29,24 @@ 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,
|
||||||
"U_BAT_LOAD_ON": 0x0130,
|
"EXTERNAL_TEMPERATURE_LIMIT": 0x0130,
|
||||||
"U_BAT_LOAD_OFF": 0x0134,
|
"EXTERNAL_TEMPERATURE_RECOVERY": 0x0134,
|
||||||
"LOAD_CURRENT_LIMIT_MA": 0x0138,
|
"U_BAT_LOAD_ON": 0x0138,
|
||||||
"LOAD_CURRENT_INRUSH_TIME": 0x013C,
|
"U_BAT_LOAD_OFF": 0x013c,
|
||||||
"MIN_CHARGE_PUMP_EXCESS_VOLTAGE": 0x0140,
|
"LOAD_CURRENT_LIMIT_MA": 0x0140,
|
||||||
"LOAD_ON_DELAY": 0x0144,
|
"LOAD_CURRENT_INRUSH_TIME": 0x0144,
|
||||||
"AVG_ALPHA_I_SOLAR": 0x0148,
|
"MIN_CHARGE_PUMP_EXCESS_VOLTAGE": 0x0148,
|
||||||
"AVG_ALPHA_I_LOAD": 0x014C,
|
"LOAD_ON_DELAY": 0x014c,
|
||||||
"AVG_ALPHA_U_BAT": 0x0150,
|
"AVG_ALPHA_I_SOLAR": 0x0150,
|
||||||
"AVG_ALPHA_U_SW": 0x0154,
|
"AVG_ALPHA_I_LOAD": 0x0154,
|
||||||
"AVG_ALPHA_U_SOLAR": 0x0158,
|
"AVG_ALPHA_U_BAT": 0x0158,
|
||||||
"AVG_ALPHA_TEMP": 0x015C,
|
"AVG_ALPHA_U_SW": 0x015c,
|
||||||
"DEEPSLEEP_DELAY": 0x0160,
|
"AVG_ALPHA_U_SOLAR": 0x0160,
|
||||||
"DEEPSLEEP_DURATION": 0x0164,
|
"AVG_ALPHA_TEMP": 0x0164,
|
||||||
|
"DEEPSLEEP_DELAY": 0x0168,
|
||||||
|
"DEEPSLEEP_DURATION": 0x016c,
|
||||||
|
"FLASH_CONFIG_OVERLOAD_DELAY_TIME": 0x0170,
|
||||||
|
"FLASH_CONFIG_OVERLOAD_RETRY_TIME": 0x0174
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,7 +67,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])
|
||||||
|
|
Loading…
Reference in a new issue