diff options
Diffstat (limited to 'firmware/e300/rev_b/main.c')
-rw-r--r-- | firmware/e300/rev_b/main.c | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/firmware/e300/rev_b/main.c b/firmware/e300/rev_b/main.c new file mode 100644 index 000000000..1e065ffef --- /dev/null +++ b/firmware/e300/rev_b/main.c @@ -0,0 +1,385 @@ +/* + * Copyright 2009 Ettus Research LLC + */ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <avr/io.h> +#include <util/delay.h> +#include <avr/sleep.h> +#include <avr/interrupt.h> + +#include "global.h" +#include "power.h" +#include "debug.h" +#include "error.h" +#include "ltc3675.h" +#ifdef CHARGER_TI +#include "bq24190.h" +#else +#include "ltc4155.h" +#endif // CHARGER_TI + +#define AUTO_POWER_ON + +#define INITIAL_DELAY 250 // ms + +FUSES = { // FIXME: & FUSE_CKSEL1 for low power 128 kHz clock + .low = (FUSE_CKSEL0 & FUSE_SUT0 & FUSE_CKDIV8), // Internal 8MHz Oscillator, Slowly rising power (start-up time), Divide Clock by 8 + .high = (FUSE_EESAVE & FUSE_SPIEN), // Save EEPROM between flashes // FIXME: Leave SPIEN for programming enabled? +}; // Not using watchdog as it runs during sleep and consumes power + +volatile STATE _state; + +/* + - Main/shared variables must be volatile + - Port pins are tri-stated on reset + * AVR_IRQ PD(5) + - Enable pull-ups on all O.D. outputs from regulator chip + * PS_POR/SRST should be driven HIGH by ATTiny? + - AVR_RESET -> RESET pin - don't configure fuse (this would disable this functionality and prohibit serial programming) + * Ship-and-store mode for charge controller? + * cli before I2C calls + * PS_TX + - en5-clk, en2-data + * Instruction following SEI is executed before interrupts + * LTC3675 real-time status doesn't contain UV/OT + * LTC3675 PGOOD -> power down (no point in checking blink state) + * On WALL, use TX, on battery use OTG switcher + * PRR - Power Reduction Register (p40) + - 100% -> 50% battery charge limit + * Check latched status for UV/OT in 3675 + * If blink error reset, get latest charge status from 4155 + * Fix UV status check from 3675/4155 as they currently share the same error + * Use charger termination at 8hr or Vc/x + * Check PGood on all regs after power on before setting powered=true + * Re-init 4155 on soft-power on + - Re-set 3A limit in 4155 after external power connection + - Removing power when running on battery, 4155GO 0xA0 - but WALL has been removed + - Why is charger reporting Constant Current when power is removed + * ltc3675_is_power_button_depressed: check if any reg is on, otherwise value will be invalid + * When e.g. 3.3V doesn't come up, blink code is correctly 4 but there's a very short blink before re-starting the sequence + - Vprog<Vc/x +*/ + +bool pmc_mask_irqs(bool mask) +{ + if (_state.interrupts_enabled == false) + return false; + + if (mask) + { + if (_state.interrupt_depth == 0) + cli(); + ++_state.interrupt_depth; + } + else + { + if (_state.interrupt_depth == 0) + return false; + + --_state.interrupt_depth; + if (_state.interrupt_depth == 0) + sei(); + } + + return true; +} + +int main(void) +{ + _delay_ms(INITIAL_DELAY); + + /////////////////////////////////////////////////////////////////////////// + + memset((void*)&_state, 0x00, sizeof(STATE)); + + debug_init(); + debug_blink(1); + + //debug_log("#"); // Will not boot if this is 21 chars long?! + debug_log("Hello world"); + + set_sleep_mode(SLEEP_MODE_PWR_DOWN); // SLEEP_MODE_PWR_SAVE combination is documented as Reserved + +//ltc4155_dump(); + + // FIXME: Init as SPI slave (FPGA is master) + + // 8-bit timer for blinking errors on charge LED + TCCR0A = _BV(CTC0); // CTC mode + OCR0A = 244; // 250ms with 1024 prescale + TIMSK0 = _BV(OCIE0A); // Enable CTC on Timer 0 + + bool init_result = power_init(); + debug_log_ex("Init", false); + _debug_log(init_result ? "+" : "-"); + debug_blink(2); + //debug_blink_rev(6); + + /////////////////////////////////// +#ifdef AUTO_POWER_ON + power_on(); // Turn on immediately. Need to de-press power button to turn off. + debug_log("Power"); + debug_blink(3); + //debug_blink_rev(10); + + //debug_wait(); + +//ltc4155_dump(); +#endif // AUTO_POWER_ON + _state.interrupts_enabled = true; + sei(); // Enable interrupts + + asm("nop"); + + _state.wake_up = false; // This will fire the first time the regs are turned on + + bool one_more = false; + + while (true) + { + one_more = false; +#ifdef CHARGER_TI + if (_state.bq24190_irq) + { + bq24190_handle_irq(); + + _state.bq24190_irq = false; + } +#else + if ((_state.ltc4155_irq)/* || ltc4155_has_interrupt()*/) // [Don't know why PCINT ISR misses LTC4155 IRQ on power up, so double-check state of line] + { + ltc4155_handle_irq(); +//ltc4155_dump(); + _state.ltc4155_irq = false; + } +#endif // !CHARGER_TI + if (_state.core_power_bad) // FIXME: Check whether it's supposed to be on + { + if (power_is_subsys_on(PS_FPGA)) + { + _delay_ms(1); // Seeing weird 120us drop in PGOOD during boot from flash (no apparent drop in 1.0V though) + + if (tps54478_is_power_good() == false) + { + debug_log("ML:FPGA!"); + + //power_off(); + _state.power_off = true; + + /*while (_state.wake_up == false) + { + blink_error_sequence(1); + }*/ + pmc_set_blink_error(BlinkError_FPGA_Power); // [If a blink error was set in power_off, this will supercede it] + } + } + + _state.core_power_bad = false; + } + + if ((_state.ltc3675_irq)/* || ltc3675_has_interrupt()*/) // This is fired on initial power up + { + debug_log("ML:3675+"); + + ltc3675_handle_irq(); + + if (ltc3675_is_power_good(ltc3675_get_last_status()) == false) + { + debug_log("ML:3675!"); + + //power_off(); + _state.power_off = true; + } + + _state.ltc3675_irq = false; + } + + if (_state.power_off) + { + debug_log("ML:Off.."); + + power_off(); + + _state.power_off = false; + _state.wake_up = false; + } + else if (_state.wake_up) + { + _delay_ms(1); // Tapping 3.1 ohm load ing 4155 in dev setup causes transient on this line and causes power on sequence to begin again + + //if (_state.powered == false) // Don't check in case button is held long enough to force LTC3675 shutdown (will not change 'powered' value) + if (ltc3675_is_waking_up()) + { + debug_log("ML:On.."); + + power_on(); + } + + _state.wake_up = false; + } + + // Check to see if the error state has resolved itself at the end of each sequence of the current blink error + + if ((_state.blink_error != BlinkError_None) && (_state.blink_last_loop != _state.blink_loops)) + { + // [Check IRQs periodically] + + bool ltc3675_use_last_status = false; + /*if (ltc3675_has_interrupt()) + { + //debug_set(IO_PB(6), ((_state.blink_loops % 2) == 0)); + ltc3675_use_last_status = true; + ltc3675_handle_irq(); + }*/ + + /////////////////////////// + + switch (_state.blink_error) + { + case BlinkError_LTC3675_UnderVoltage: + case BlinkError_LTC3675_OverTemperature: + case BlinkError_DRAM_Power: + case BlinkError_3_3V_Peripherals_Power: + case BlinkError_1_8V_Peripherals_Power: + case BlinkError_TX_Power: + if (((ltc3675_use_last_status) && (ltc3675_status_to_error(ltc3675_get_last_status()) != BlinkError_None)) || + ((ltc3675_use_last_status == false) && (ltc3675_check_status() != BlinkError_None))) + break; + debug_log("BE:3675-"); + goto cancel_blink_error; + case BlinkError_FPGA_Power: + if (tps54478_is_power_good() == false) + break; + debug_log("BE:FPGA-"); + goto cancel_blink_error; + default: +cancel_blink_error: + //debug_set(IO_PB(7), true); + pmc_set_blink_error(BlinkError_None); + } + + //////////////////////////////////// + + // More periodic checks + // Need to do this has some interrupts are on PCINT, and while GIE is disabled, might change & change back + // E.g. LTC3675 IRQ due to UV, reset IRQ, re-asserts UV +#ifndef CHARGER_TI + if (ltc4155_has_interrupt()) + { + debug_log("BE:4155"); + + _state.ltc4155_irq = true; + one_more = true; + } +#endif // !CHARGER_TI + if (ltc3675_has_interrupt()) + { + debug_log("BE:3675"); + + _state.ltc3675_irq = true; + one_more = true; + } + + if (power_is_subsys_on(PS_FPGA)) + { + if (tps54478_is_power_good() == false) + { + debug_log("BE:FPGA!"); + + _state.core_power_bad = true; + one_more = true; + } + } + + //////////////////////////////////// + + _state.blink_last_loop = _state.blink_loops; + } + + //if (_state.timers_running == false) + if ((_state.active_timers == 0) && (one_more == false)) + { + debug_log("^"); + sleep_mode(); + debug_log("$"); + } + } + + return 0; +} + +uint8_t pmc_get_blink_error(void) +{ + return _state.blink_error; +} + +void pmc_set_blink_error(uint8_t count) +{ + if ((_state.blink_error != BlinkError_None) && (count /*> _state.blink_error*/!= BlinkError_None)) // [Prioritise] Always keep first sequence running + return; + else if (_state.blink_error == count) // Don't restart if the same + return; + + if (count == BlinkError_None) + { + debug_log("BLNK-"); + _state.blink_stop = true; + return; + } + + //char msg[25]; + //sprintf(msg, "Blink code = %i\n", count); + //debug_log(msg); + debug_log_ex("BLNK ", false); + debug_log_byte(count); + + _state.blink_error = count; + _state.blink_loops = 0; + _state.blink_last_loop = 0; + _state.blinker_state = 0; + _state.blink_stop = false; + + charge_set_led(false); + + TCNT0 = 0; + if ((TCCR0A & 0x07) == 0x00) // Might already be active with existing error + _state.active_timers++; + TCCR0A |= 0x05; // Start with 1024 prescale +} + +ISR(TIMER0_COMPA_vect) // Blink the sequence, and leave one slot at the beginning and end where the LED is off so one can get a sense of how many blinks occurred +{ + pmc_mask_irqs(true); + + if (_state.blinker_state < (2 * _state.blink_error + 1)) + charge_set_led((_state.blinker_state % 2) == 1); + + _state.blinker_state++; + + if (_state.blinker_state == (2 * _state.blink_error + 1 + 1)) + { + _state.blinker_state = 0; + + if (_state.blink_stop) + { + if ((TCCR0A & 0x07) != 0x00) + _state.active_timers--; + TCCR0A &= ~0x07; + + _state.blink_error = BlinkError_None; + + debug_log("BLNK."); + } + else + { + _state.blink_loops++; + } + } + + pmc_mask_irqs(false); +} |