mirror of
https://github.com/sjlongland/cluster-powerctl.git
synced 2025-09-13 20:13:16 +10:00
powerctl: Re-work controller logic.
This commit is contained in:
parent
dd0f371b7c
commit
03161f8d8e
118
README.md
118
README.md
@ -1,11 +1,12 @@
|
||||
Power controller for solar powered personal cloud
|
||||
=================================================
|
||||
|
||||
This firmware is intended to control the charging of a battery bank that
|
||||
powers a small computer cluster running a private cloud system. The
|
||||
idea is to try to keep the battery within a small range of voltages,
|
||||
charging from solar or mains based chargers as necessary to top the
|
||||
battery back up.
|
||||
This firmware is intended to control the charging of a battery bank that powers
|
||||
a small computer cluster running a private cloud system. The system has two
|
||||
charging sources: mains power and solar power.
|
||||
|
||||
The chargers are assumed to have a logic-level signal which, when pulled low,
|
||||
shuts down the relevant charger.
|
||||
|
||||
Circuit description
|
||||
-------------------
|
||||
@ -13,20 +14,22 @@ Circuit description
|
||||
An ATTiny24A microcontroller runs from a 5V rail derived from the
|
||||
battery. It uses its internal RC oscillator running at 1MHz.
|
||||
|
||||
Connected to PB0 and PB1 are the two MOSFETs that turn on the input
|
||||
sources, one for solar (PB1), the other for a mains charger (PB0).
|
||||
Connected to PB0 and PB1 are the two transistors that turn on the input
|
||||
sources, one for solar (PB1), the other for a mains charger (PB0). These are
|
||||
NPN bi-polar junction transistors. When the base is turned on via PB0 or PB1,
|
||||
this allows current to flow from the collector to ground, effectively pulling
|
||||
the relevant logic level output low.
|
||||
|
||||
The source pins of these connect to the battery positive input, so when
|
||||
the FET is on, power may flow from that source to the battery.
|
||||
|
||||
A third MOSFET connects to PB2, this is PWM-controlled to manage some
|
||||
cooling fans. The internal temperature sensor is used to decide whether
|
||||
the fans should be on or off, and at what speed.
|
||||
Connected to PB2 is a third NPN transistor which is wired to a P-channel
|
||||
MOSFET, such that turning the transistor on also turns on that MOSFET. This
|
||||
is PWM-controlled to manage some cooling fans. The internal temperature
|
||||
sensor is used to decide whether the fans should be on or off, and at what
|
||||
speed.
|
||||
|
||||
Connecting to each input, and to the battery, are separate voltage
|
||||
dividers, comprising of a 1.5kOhm and 100Ohm resistors. These divide
|
||||
the input voltage by 16, and the divided voltage is fed into the ADC
|
||||
pins PA0 (mains), PA1 (solar) and PA2 (battery).
|
||||
pins PA1 (solar) and PA2 (battery).
|
||||
|
||||
LEDs connect to PA7, PA6, PA5, PA4 and PA3 to 0v. ICSP is shared with the
|
||||
LEDs.
|
||||
@ -34,18 +37,17 @@ LEDs.
|
||||
GPIOs
|
||||
-----
|
||||
|
||||
PB0: Mains MOSFET (active HIGH)
|
||||
PB1: Solar MOSFET (active HIGH)
|
||||
PB0: Mains enable (active LOW)
|
||||
PB1: Solar enable (active LOW)
|
||||
PB2: Fan MOSFET (active HIGH)
|
||||
PB3: nRESET
|
||||
PA7: Temperature Low LED (active HIGH)
|
||||
PA6: Temperature High LED (active HIGH) + ICSP MOSI
|
||||
PA5: Battery Voltage High LED (active HIGH) + ICSP MISO
|
||||
PA4: Warning LED (active high) + ICSP SCK + Debug Tx
|
||||
PA5: Mains Floating LED (active HIGH) + ICSP MISO
|
||||
PA4: Mains Charging LED (active high) + ICSP SCK + Debug Tx
|
||||
PA3: Battery Voltage Good LED (active HIGH)
|
||||
PA2: Analogue Input: Battery voltage
|
||||
PA1: Analogue Input: Solar voltage
|
||||
PA0: Analogue Input: Mains voltage
|
||||
|
||||
Firmware description
|
||||
--------------------
|
||||
@ -64,48 +66,60 @@ loop. If DEBUG is enabled, "INIT xx" will be displayed, where xx is the
|
||||
value of the MCUSR register.
|
||||
|
||||
Two counters are decremented by the Timer 1 overflow interrupt.
|
||||
`led_timeout` causes the main loop to update the state of the LEDs when
|
||||
it hits zero, and `adc_timeout` triggers a new ADC capture run when it
|
||||
reaches zero.
|
||||
`t_second` causes the main loop to decrement each one-second timer, `t_adc`
|
||||
causes the ADC state machine to advance one tick and checkt the state of the
|
||||
channels.
|
||||
|
||||
The three analogue inputs and temperature sensor are scanned when
|
||||
`adc_timeout` reaches zero, then the state analysed. The battery
|
||||
voltage is compared to the previous reading to dissern if the battery is
|
||||
charging, discharging or holding steady.
|
||||
The two analogue inputs and temperature sensor are scanned when
|
||||
`t_adc` reaches zero, then the state analysed.
|
||||
|
||||
If this state has not changed, a battery state counter increments,
|
||||
otherwise it is reset.
|
||||
The charger is in one of four states:
|
||||
* initialisation
|
||||
* solar
|
||||
* charging from mains
|
||||
* floating on mains
|
||||
|
||||
The current state of the FETs is checked. Three states are valid:
|
||||
- Idle state: all FETs off
|
||||
- Mains charge: MAINS FET turned on
|
||||
- Solar charge: SOLAR FET turned on
|
||||
On power up, we enter the initialisation state. In this state, we wait for
|
||||
the first ADC readings to arrive before deciding on whether we run from mains
|
||||
power or solar. During this time, all power inputs are inhibited.
|
||||
|
||||
IF statements at this point compare the battery voltage to the
|
||||
thresholds, and decide whether to switch voltage or not.
|
||||
If either the battery or solar input are below minimum thresholds, the mains
|
||||
charger is turned on and we enter the "charging from mains" state.
|
||||
Otherwise the solar input is used and we enter the "solar" state.
|
||||
|
||||
In the "solar" state, we monitor the battery voltage. If it drops below the
|
||||
minimum voltage, we switch to the "charging from mains" state.
|
||||
|
||||
In the "charging from mains" state, we monitor the battery charging progress.
|
||||
Upon reaching the high voltage threshold, we switch to the "floating on mains"
|
||||
state.
|
||||
|
||||
In the "floating on mains" state, we wait a minimum of one hour for the
|
||||
mains charger to finish its cycle. If the battery drops below the
|
||||
high-voltage threshold, we move back to the "charging from mains" state.
|
||||
|
||||
Once an hour has elapsed in the floating state, if the solar input is above
|
||||
the minimum threshold, we turn off the mains charger and switch to the "solar"
|
||||
state.
|
||||
|
||||
LED indications
|
||||
---------------
|
||||
|
||||
The LEDs have the following meanings:
|
||||
|
||||
Warning LED (PA4; DEBUG undefined):
|
||||
- Off: No warning condition
|
||||
- Flashing: Battery below critical threshold or temperature above
|
||||
maximum threshold
|
||||
- On: Not used
|
||||
|
||||
When in DEBUG mode, this LED may flicker with serial activity, and will
|
||||
remain ON when idle.
|
||||
|
||||
Temperature LEDs (PA6, PA7):
|
||||
- PA7 On, PA6 Off: Temperature below minimum threshold
|
||||
- PA7 Flashing, PA6 Off: Temperature above minimum threshold
|
||||
- PA7 Off, PA6 Flashing: Temperature above maximum threshold
|
||||
- Other states: not used
|
||||
- PA7 Off, PA6 Off: Temperature below minimum threshold
|
||||
- PA7 On, PA6 Off: Temperature between thresholds
|
||||
- PA7 Off, PA6 On: Temperature above maximum threshold
|
||||
|
||||
Battery LEDs (PA5, PA3):
|
||||
- PA3 Flashing, PA5 Off: Battery below low threshold
|
||||
- PA3 Off, PA5 Flashing: Battery above high threshold
|
||||
- PA3 On, PA5 Off: Battery is in "good" range (between low and high)
|
||||
- Other states: not used
|
||||
Battery State LED (PA3):
|
||||
- PA3 Off: Battery is below minimum voltage
|
||||
- PA3 On: Battery is above minimum voltage
|
||||
|
||||
Mains Charger State LEDs (PA4, PA5):
|
||||
- PA4 Off, PA5 Off: Mains supply is off
|
||||
- PA4 On, PA5 Off: Mains supply is charging the battery
|
||||
- PA4 Off, PA5 On: Mains supply is floating the battery
|
||||
|
||||
Note that in debug mode, PA4 instead becomes the serial TX line, and so will
|
||||
remain on in the idle state, and will flicker with serial activity.
|
||||
|
8
board.h
8
board.h
@ -22,15 +22,15 @@
|
||||
/* LEDs */
|
||||
#define LED_TEMP_LOW (1 << 7)
|
||||
#define LED_TEMP_HIGH (1 << 6)
|
||||
#define LED_BATT_HIGH (1 << 5)
|
||||
#define LED_WARNING (1 << 4)
|
||||
#define LED_BATT_FLT (1 << 5)
|
||||
#define LED_BATT_CHG (1 << 4)
|
||||
#define LED_BATT_GOOD (1 << 3)
|
||||
#define LED_PORT PORTA
|
||||
#define LED_PORT_DDR_REG DDRA
|
||||
#define LED_PORT_DDR_VAL ( LED_TEMP_LOW \
|
||||
| LED_TEMP_HIGH \
|
||||
| LED_BATT_HIGH \
|
||||
| LED_WARNING \
|
||||
| LED_BATT_FLT \
|
||||
| LED_BATT_CHG \
|
||||
| LED_BATT_GOOD )
|
||||
|
||||
/* MOSFETs */
|
||||
|
428
powerctl.c
428
powerctl.c
@ -38,36 +38,22 @@
|
||||
)
|
||||
|
||||
/* --- Thresholds --- */
|
||||
#define V_CH_ADC ADC_READ(V_CH_MV)
|
||||
#define V_H_ADC ADC_READ(V_H_MV)
|
||||
#define V_L_ADC ADC_READ(V_L_MV)
|
||||
#define V_CL_ADC ADC_READ(V_CL_MV)
|
||||
#define V_DELTA_ADC ADC_READ(V_DELTA_MV)
|
||||
#define V_SOL_MIN_ADC ADC_READ(V_SOL_MIN_MV)
|
||||
|
||||
/* --- Timeouts --- */
|
||||
#define T_LED_TICKS TIMER_TICKS(T_LED_MS)
|
||||
#define T_ADC_TICKS TIMER_TICKS(T_ADC_MS)
|
||||
|
||||
#define STATE_DIS_CHECK (0) /*!< Check voltage in discharge state */
|
||||
#define STATE_DIS_WAIT (1) /*!< Wait in discharge state */
|
||||
#define STATE_CHG_CHECK (2) /*!< Check voltage in charging state */
|
||||
#define STATE_CHG_WAIT (3) /*!< Wait in charging state */
|
||||
#define STATE_INIT (0) /*!< Initial start-up state */
|
||||
#define STATE_SOLAR (1) /*!< Running from solar */
|
||||
#define STATE_MAINS_CHG (2) /*!< Charging from mains */
|
||||
#define STATE_MAINS_FLT (3) /*!< Floating on mains */
|
||||
|
||||
/*!
|
||||
* Charger state machine state. We have four states we can be in.
|
||||
*/
|
||||
static volatile uint8_t charger_state = STATE_DIS_CHECK;
|
||||
|
||||
#define SRC_NONE (0) /*!< Turn off all chargers */
|
||||
#define SRC_SOLAR (1) /*!< Turn on solar charger */
|
||||
#define SRC_MAINS (2) /*!< Turn on mains charger */
|
||||
#define SRC_ALT (3) /*!< Alternate to *other* source,
|
||||
* valid for select_src only.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Charging source.
|
||||
*/
|
||||
static volatile uint8_t charge_source = SRC_NONE;
|
||||
static volatile uint8_t charger_state = STATE_INIT;
|
||||
|
||||
/*!
|
||||
* For state machine, the last state of the ADC MUX so we know whether
|
||||
@ -76,16 +62,15 @@ static volatile uint8_t charge_source = SRC_NONE;
|
||||
*/
|
||||
static volatile uint8_t last_admux = 0;
|
||||
|
||||
/*!
|
||||
* For state machine, determines what the battery was one sample ago so
|
||||
* we know if it's charging, discharging, or remaining static. ADC units.
|
||||
*/
|
||||
static volatile uint16_t v_bl_adc = 0;
|
||||
|
||||
/*!
|
||||
* Current reading of the battery voltage in ADC units.
|
||||
*/
|
||||
static volatile uint16_t v_bn_adc = 0;
|
||||
static volatile uint16_t v_bat_adc = 0;
|
||||
|
||||
/*!
|
||||
* Current reading of the solar voltage in ADC units.
|
||||
*/
|
||||
static volatile uint16_t v_sol_adc = 0;
|
||||
|
||||
/*!
|
||||
* Current reading of the internal temperature sensor in ADC units.
|
||||
@ -103,9 +88,9 @@ static volatile uint16_t t_second = 0;
|
||||
static volatile uint16_t t_adc = 0;
|
||||
|
||||
/*!
|
||||
* How long before we change LED states?
|
||||
* Float timeout
|
||||
*/
|
||||
static volatile uint16_t t_led = 0;
|
||||
static volatile uint16_t t_float = 0;
|
||||
|
||||
/*!
|
||||
* Fan kick-start timeout
|
||||
@ -113,19 +98,9 @@ static volatile uint16_t t_led = 0;
|
||||
static volatile uint8_t t_fan = 0;
|
||||
|
||||
/*!
|
||||
* Charger timeout
|
||||
* ADC readings taken?
|
||||
*/
|
||||
static volatile uint8_t t_charger = T_LF_S;
|
||||
|
||||
/*!
|
||||
* Charger warning timeout
|
||||
*/
|
||||
static volatile uint8_t t_cwarn = 0;
|
||||
|
||||
/*!
|
||||
* Are we presently in a warning state?
|
||||
*/
|
||||
static volatile uint8_t charger_warning = 0;
|
||||
static volatile uint8_t adc_checked = 0;
|
||||
|
||||
/* Debug messages */
|
||||
#ifdef DEBUG
|
||||
@ -162,190 +137,99 @@ static inline void uart_tx_bool(const char* msg, uint8_t val) {
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Enter charger warning state. This indicates that the battery
|
||||
* *should* be charging, but isn't due to insufficient input current from
|
||||
* the charger.
|
||||
* Switch to charging from mains power.
|
||||
*/
|
||||
static inline void enter_warning() {
|
||||
if (charger_warning)
|
||||
LED_PORT |= LED_WARNING;
|
||||
else
|
||||
charger_warning = 1;
|
||||
|
||||
/* Reset our timer */
|
||||
t_cwarn = T_CWARN_S;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Leave the charger warning state. This indicates the charger has left
|
||||
* the charging state or the battery has begun charging.
|
||||
*/
|
||||
static inline void exit_warning() {
|
||||
charger_warning = 0;
|
||||
t_cwarn = 0;
|
||||
LED_PORT &= ~LED_WARNING;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Switch between chargers. This is does a "break-before-make" switchover
|
||||
* of charging sources to switch from mains to solar, solar to mains, or to
|
||||
* switch from charging to discharging mode. It expressly forbids turning
|
||||
* both chargers on simultaneously.
|
||||
*
|
||||
* Added is the ability to just alternate between sources.
|
||||
*/
|
||||
static void select_src(uint8_t src) {
|
||||
if (src == SRC_ALT) {
|
||||
if (charge_source == SRC_SOLAR)
|
||||
src = SRC_MAINS;
|
||||
else
|
||||
src = SRC_SOLAR;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
uart_tx_str(STR_SELECT_SRC);
|
||||
#endif
|
||||
switch(src) {
|
||||
case SRC_SOLAR:
|
||||
static void enter_mains_chg(void) {
|
||||
/* Enable mains power */
|
||||
FET_PORT &= ~FET_MAINS;
|
||||
FET_PORT |= FET_SOLAR;
|
||||
charge_source = SRC_SOLAR;
|
||||
#ifdef DEBUG
|
||||
uart_tx_str(STR_SRC_SOLAR);
|
||||
#endif
|
||||
break;
|
||||
case SRC_MAINS:
|
||||
FET_PORT &= ~FET_SOLAR;
|
||||
FET_PORT |= FET_MAINS;
|
||||
charge_source = SRC_MAINS;
|
||||
#ifdef DEBUG
|
||||
uart_tx_str(STR_SRC_MAINS);
|
||||
#endif
|
||||
break;
|
||||
case SRC_NONE:
|
||||
default:
|
||||
FET_PORT &= ~FET_SRC_MASK;
|
||||
charge_source = SRC_NONE;
|
||||
#ifdef DEBUG
|
||||
uart_tx_str(STR_SRC_NONE);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
uart_tx_str(STR_NL);
|
||||
#endif
|
||||
|
||||
/* Indicate via LEDs */
|
||||
LED_PORT |= LED_BATT_CHG;
|
||||
LED_PORT &= ~LED_BATT_FLT;
|
||||
|
||||
/* Enter state */
|
||||
charger_state = STATE_MAINS_CHG;
|
||||
}
|
||||
|
||||
static void discharge_check() {
|
||||
/* Decide when we should do our next check */
|
||||
#ifdef DEBUG
|
||||
uart_tx_str(STR_DIS); uart_tx_str(STR_CHK);
|
||||
uart_tx_bool(STR_V_BN_GE_V_H, v_bn_adc >= V_H_ADC);
|
||||
#endif
|
||||
if (v_bn_adc >= V_H_ADC)
|
||||
t_charger = T_LF_S;
|
||||
else
|
||||
t_charger = T_HF_S;
|
||||
|
||||
/* Snapshot the current battery voltage */
|
||||
v_bl_adc = v_bn_adc;
|
||||
|
||||
/* Exit state */
|
||||
#ifdef DEBUG
|
||||
uart_tx_bool(STR_V_BN_GT_V_L, v_bn_adc > V_L_ADC);
|
||||
#endif
|
||||
if (v_bn_adc > V_L_ADC)
|
||||
charger_state = STATE_DIS_WAIT;
|
||||
else
|
||||
charger_state = STATE_CHG_CHECK;
|
||||
}
|
||||
|
||||
static void discharge_wait() {
|
||||
#ifdef DEBUG
|
||||
uart_tx_str(STR_DIS); uart_tx_str(STR_WAIT);
|
||||
uart_tx_bool(STR_V_BN_LE_V_CL, v_bn_adc <= V_CL_ADC);
|
||||
#endif
|
||||
if (v_bn_adc <= V_CL_ADC)
|
||||
/* Expire timer */
|
||||
t_charger = 0;
|
||||
|
||||
/* Exit state if timer is expired */
|
||||
#ifdef DEBUG
|
||||
uart_tx_bool(STR_T_CHARGER, !t_charger);
|
||||
#endif
|
||||
if (!t_charger)
|
||||
charger_state = STATE_DIS_CHECK;
|
||||
}
|
||||
|
||||
static void charge_check() {
|
||||
#ifdef DEBUG
|
||||
uart_tx_str(STR_CHG); uart_tx_str(STR_CHK);
|
||||
uart_tx_bool(STR_V_BN_LE_V_CL, v_bn_adc <= V_CL_ADC);
|
||||
#endif
|
||||
/* Still need to charge, when should we next check? */
|
||||
if (v_bn_adc <= V_CL_ADC)
|
||||
t_charger = T_HF_S;
|
||||
else
|
||||
t_charger = T_LF_S;
|
||||
|
||||
/* Critically high voltage check */
|
||||
if (v_bn_adc >= V_CH_ADC) {
|
||||
/* We must stop now! */
|
||||
select_src(SRC_NONE);
|
||||
charger_state = STATE_DIS_CHECK;
|
||||
charger_warning = 0;
|
||||
t_cwarn = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (charge_source == SRC_NONE) {
|
||||
/* Not yet charging, switch to primary source */
|
||||
select_src(SRC_SOLAR);
|
||||
/* As we have just started charging, reset warning timer */
|
||||
exit_warning();
|
||||
} else if (v_bn_adc <= (v_bl_adc + V_DELTA_ADC)) {
|
||||
/* Check for high voltage threshold, are we there yet? */
|
||||
#ifdef DEBUG
|
||||
uart_tx_bool(STR_V_BN_GE_V_H, v_bn_adc >= V_H_ADC);
|
||||
#endif
|
||||
if (v_bn_adc >= V_H_ADC) {
|
||||
/* We are done now */
|
||||
select_src(SRC_NONE);
|
||||
exit_warning();
|
||||
return;
|
||||
} else if (!t_cwarn) {
|
||||
if (charger_warning) {
|
||||
/*
|
||||
* Situation still not improving,
|
||||
* switch sources.
|
||||
/*!
|
||||
* Switch to floating on mains power.
|
||||
*/
|
||||
select_src(SRC_ALT);
|
||||
}
|
||||
/* Reset our warning timer */
|
||||
enter_warning();
|
||||
}
|
||||
} else if (!t_cwarn) {
|
||||
/* Things are improving, reset warning if set. */
|
||||
exit_warning();
|
||||
static void enter_mains_float(void) {
|
||||
/* Reset timer */
|
||||
t_float = T_FLOAT_S;
|
||||
|
||||
/* Indicate via LEDs */
|
||||
LED_PORT &= ~LED_BATT_CHG;
|
||||
LED_PORT |= LED_BATT_FLT;
|
||||
|
||||
/* Enter state */
|
||||
charger_state = STATE_MAINS_FLT;
|
||||
}
|
||||
|
||||
v_bl_adc = v_bn_adc;
|
||||
charger_state = STATE_CHG_WAIT;
|
||||
/*!
|
||||
* Switch to running on solar.
|
||||
*/
|
||||
static void enter_solar(void) {
|
||||
/* Inhibit mains */
|
||||
FET_PORT |= FET_MAINS;
|
||||
|
||||
/* Indicate via LEDs */
|
||||
LED_PORT &= ~(LED_BATT_FLT | LED_BATT_CHG);
|
||||
|
||||
/* Enter state */
|
||||
charger_state = STATE_SOLAR;
|
||||
}
|
||||
|
||||
static void charge_wait() {
|
||||
#ifdef DEBUG
|
||||
uart_tx_str(STR_CHG); uart_tx_str(STR_WAIT);
|
||||
uart_tx_bool(STR_V_BN_GE_V_CH, v_bn_adc >= V_CH_ADC);
|
||||
#endif
|
||||
if (v_bn_adc >= V_CH_ADC)
|
||||
/* Expire timer */
|
||||
t_charger = 0;
|
||||
/*!
|
||||
* Checks at start-up
|
||||
*/
|
||||
static void init_check(void) {
|
||||
/* Wait until we have our first readings from the ADC */
|
||||
if (!adc_checked)
|
||||
return;
|
||||
|
||||
#ifdef DEBUG
|
||||
uart_tx_bool(STR_T_CHARGER, !t_charger);
|
||||
#endif
|
||||
if (!t_charger)
|
||||
charger_state = STATE_CHG_CHECK;
|
||||
if ((v_bat_adc < V_L_ADC) || (v_sol_adc < V_SOL_MIN_ADC))
|
||||
/* Battery/solar is low, begin charging */
|
||||
enter_mains_chg();
|
||||
else
|
||||
/* Run from solar */
|
||||
enter_solar();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Checks whilst running on solar
|
||||
*/
|
||||
static void solar_check(void) {
|
||||
if (v_bat_adc < V_L_ADC) {
|
||||
/* Move to mains power */
|
||||
enter_mains_chg();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Checks whilst charging from mains
|
||||
*/
|
||||
static void mains_chg_check(void) {
|
||||
if (v_bat_adc >= V_H_ADC) {
|
||||
/* We've reached the float voltage */
|
||||
enter_mains_float();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Checks whilst floating on mains
|
||||
*/
|
||||
static void mains_float_check(void) {
|
||||
if (v_bat_adc < V_H_ADC) {
|
||||
/* We've regressed, go back to charging state! */
|
||||
enter_mains_chg();
|
||||
return;
|
||||
} else if ((!t_float) && (v_sol_adc >= V_SOL_MIN_ADC)) {
|
||||
/* Solar can take it from here */
|
||||
enter_solar();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -358,7 +242,7 @@ int main(void) {
|
||||
|
||||
/* Configure MOSFETs */
|
||||
FET_PORT_DDR_REG = FET_PORT_DDR_VAL;
|
||||
FET_PORT = 0;
|
||||
FET_PORT = FET_MAINS | FET_SOLAR;
|
||||
|
||||
/* Turn on ADC and timers */
|
||||
PRR &= ~((1 << PRTIM0) | (1 << PRTIM1) | (1 << PRADC));
|
||||
@ -400,49 +284,8 @@ int main(void) {
|
||||
/* One second passed, tick down the 1-second timers. */
|
||||
if (!t_second) {
|
||||
t_second = TIMER_FREQ;
|
||||
if (t_charger)
|
||||
t_charger--;
|
||||
if (t_cwarn)
|
||||
t_cwarn--;
|
||||
}
|
||||
|
||||
if (t_adc)
|
||||
t_adc--;
|
||||
|
||||
if (!t_led) {
|
||||
if (v_bn_adc <= V_CL_ADC) {
|
||||
/* Battery is critically low */
|
||||
LED_PORT &= ~LED_BATT_HIGH;
|
||||
LED_PORT ^= LED_BATT_GOOD;
|
||||
} else if (v_bn_adc <= V_L_ADC) {
|
||||
/* Battery is low */
|
||||
LED_PORT &= ~(LED_BATT_HIGH|LED_BATT_GOOD);
|
||||
} else if (v_bn_adc <= V_H_ADC) {
|
||||
/* Battery is in "good" range */
|
||||
LED_PORT &= ~LED_BATT_HIGH;
|
||||
LED_PORT |= LED_BATT_GOOD;
|
||||
} else if (v_bn_adc <= V_CH_ADC) {
|
||||
/* Battery is above "high" threshold */
|
||||
LED_PORT |= LED_BATT_HIGH;
|
||||
LED_PORT &= ~LED_BATT_GOOD;
|
||||
} else {
|
||||
/* Battery is critically high */
|
||||
LED_PORT ^= LED_BATT_HIGH;
|
||||
LED_PORT &= ~LED_BATT_GOOD;
|
||||
}
|
||||
|
||||
if (temp_adc < TEMP_MIN) {
|
||||
LED_PORT |= LED_TEMP_LOW;
|
||||
LED_PORT &= ~LED_TEMP_HIGH;
|
||||
} else if (temp_adc < TEMP_MAX) {
|
||||
LED_PORT ^= LED_TEMP_LOW;
|
||||
LED_PORT &= ~LED_TEMP_HIGH;
|
||||
} else {
|
||||
LED_PORT &= ~LED_TEMP_LOW;
|
||||
LED_PORT ^= LED_TEMP_HIGH;
|
||||
}
|
||||
|
||||
t_led = T_LED_TICKS;
|
||||
if (t_float)
|
||||
t_float--;
|
||||
}
|
||||
|
||||
if (!t_adc) {
|
||||
@ -451,6 +294,28 @@ int main(void) {
|
||||
|
||||
while(ADCSRA & (1 << ADEN));
|
||||
|
||||
/* Temperature LED control */
|
||||
if (temp_adc < TEMP_MIN) {
|
||||
LED_PORT |= LED_TEMP_LOW;
|
||||
LED_PORT &= ~LED_TEMP_HIGH;
|
||||
} else if (temp_adc < TEMP_MAX) {
|
||||
LED_PORT |= (LED_TEMP_LOW | LED_TEMP_HIGH);
|
||||
} else {
|
||||
LED_PORT &= ~LED_TEMP_LOW;
|
||||
LED_PORT |= LED_TEMP_HIGH;
|
||||
}
|
||||
|
||||
/*
|
||||
* The "SOLAR" FET is no longer fitted, so this is more
|
||||
* an indication of whether we consider solar to be
|
||||
* "good enough". In short, it's just controlling the
|
||||
* LED where the MOSFET was now.
|
||||
*/
|
||||
if (v_sol_adc < V_SOL_MIN_ADC)
|
||||
FET_PORT |= FET_SOLAR;
|
||||
else
|
||||
FET_PORT &= ~FET_SOLAR;
|
||||
|
||||
/* Fan control */
|
||||
if (t_fan) {
|
||||
/* Kick-start mode */
|
||||
@ -475,22 +340,29 @@ int main(void) {
|
||||
OCR0A = 0;
|
||||
}
|
||||
|
||||
/* Battery state LED control */
|
||||
if (v_bat_adc <= V_L_ADC) {
|
||||
LED_PORT &= ~LED_BATT_GOOD;
|
||||
} else {
|
||||
LED_PORT |= LED_BATT_GOOD;
|
||||
}
|
||||
|
||||
/* Charger control */
|
||||
switch (charger_state) {
|
||||
case STATE_DIS_CHECK:
|
||||
discharge_check();
|
||||
case STATE_INIT:
|
||||
init_check();
|
||||
break;
|
||||
case STATE_DIS_WAIT:
|
||||
discharge_wait();
|
||||
case STATE_SOLAR:
|
||||
solar_check();
|
||||
break;
|
||||
case STATE_CHG_CHECK:
|
||||
charge_check();
|
||||
case STATE_MAINS_CHG:
|
||||
mains_chg_check();
|
||||
break;
|
||||
case STATE_CHG_WAIT:
|
||||
charge_wait();
|
||||
case STATE_MAINS_FLT:
|
||||
mains_float_check();
|
||||
break;
|
||||
default:
|
||||
charger_state = STATE_DIS_CHECK;
|
||||
charger_state = STATE_INIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -505,8 +377,8 @@ ISR(TIM1_COMPA_vect) {
|
||||
if (t_second)
|
||||
t_second--;
|
||||
|
||||
if (t_led)
|
||||
t_led--;
|
||||
if (t_adc)
|
||||
t_adc--;
|
||||
}
|
||||
|
||||
ISR(ADC_vect) {
|
||||
@ -519,20 +391,14 @@ ISR(ADC_vect) {
|
||||
ADCSRA |= (1 << ADSC);
|
||||
break;
|
||||
case ADC_MUX_BATT:
|
||||
v_bn_adc = adc;
|
||||
#if 0
|
||||
/* Not being used for now */
|
||||
v_bat_adc = adc;
|
||||
ADMUX = ADC_MUX_SOLAR;
|
||||
ADCSRA |= (1 << ADSC);
|
||||
break;
|
||||
case ADC_MUX_SOLAR:
|
||||
adc_solar = adc;
|
||||
ADMUX = ADC_MUX_MAINS;
|
||||
ADCSRA |= (1 << ADSC);
|
||||
break;
|
||||
case ADC_MUX_MAINS:
|
||||
adc_mains = adc;
|
||||
#endif
|
||||
v_sol_adc = adc;
|
||||
/* Once we get here, we've done a full cycle */
|
||||
adc_checked = 1;
|
||||
default:
|
||||
ADMUX = ADC_MUX_TEMP;
|
||||
ADCSRA &= ~(1 << ADEN);
|
||||
|
@ -28,11 +28,6 @@
|
||||
*/
|
||||
#define T_ADC_MS (250)
|
||||
|
||||
/*!
|
||||
* How long before we change LED states? Milliseconds.
|
||||
*/
|
||||
#define T_LED_MS (150)
|
||||
|
||||
/*
|
||||
* Temperature ranges and fan PWM settings
|
||||
*/
|
||||
@ -57,53 +52,29 @@
|
||||
|
||||
/* --- Thresholds --- */
|
||||
|
||||
/*!
|
||||
* Critically high battery voltage. Exceeding this voltage could damage
|
||||
* the battery or the equipment downstream of it.
|
||||
*/
|
||||
#define V_CH_MV (15500)
|
||||
|
||||
/*!
|
||||
* High battery voltage. If we reach this voltage and the charger
|
||||
* stops, just switch to discharge mode, consider the job done.
|
||||
*/
|
||||
#define V_H_MV (14000)
|
||||
#define V_H_MV (14400)
|
||||
|
||||
/*!
|
||||
* Low battery voltage. If the voltage dips to or below this level, we
|
||||
* should turn the charger on.
|
||||
*/
|
||||
#define V_L_MV (13800)
|
||||
#define V_L_MV (12400)
|
||||
|
||||
/*!
|
||||
* Critically low battery voltage. If we reach this level, we need to
|
||||
* urgently turn the charger on and need to be ready to switch sources
|
||||
* in a hurry if the chosen source isn't charging.
|
||||
* Solar minimum voltage. If the solar is below this threshold, we
|
||||
* consider it too low to reliably charge the system.
|
||||
*/
|
||||
#define V_CL_MV (11800)
|
||||
|
||||
/*!
|
||||
* Battery minimum charge step. This is the amount of charge we expect
|
||||
* the battery to have increased by before we consider flagging a
|
||||
* warning.
|
||||
*/
|
||||
#define V_DELTA_MV (50)
|
||||
#define V_SOL_MIN_MV (18000)
|
||||
|
||||
/* --- Timeouts --- */
|
||||
|
||||
/*!
|
||||
* High frequency polling period in seconds.
|
||||
* How long do we remain on the mains charger after reaching V_H_MV?
|
||||
*/
|
||||
#define T_HF_S (15)
|
||||
|
||||
/*!
|
||||
* Low frequency polling period in seconds.
|
||||
*/
|
||||
#define T_LF_S (60)
|
||||
|
||||
/*!
|
||||
* How long before we consider a lack of voltage increase a warning?
|
||||
*/
|
||||
#define T_CWARN_S (10)
|
||||
#define T_FLOAT_S (3600)
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user