void pmu_power_on(void) { uint8_t i; int8_t ret; pmu_regulator_t *reg; /* if somehow this gets called twice, bail early on */ if (is_booting) return; else if (is_on) return; else state = BOOT; /* reset the fpga */ pmu_reset_fpga(true); fpga_init(); for (i = 0; i < ARRAY_SIZE(boot_order); i++) { reg = boot_order[i]; /* if regulator set a on/off function, call it */ if (reg->ops->set_regulator) { ret = pmu_set_regulator(reg, true); if (ret) { pmu_error = reg->error_code; goto fail_regulators; } } /* if regulator set a set_voltage function, call it */ if (reg->ops->set_voltage && reg->voltage) { ret = reg->ops->set_voltage(reg, reg->voltage); if (ret) { pmu_error = reg->error_code; goto fail_regulators; } } /* if we got here, this means all is well */ reg->powered = true; } /* enable the usb clock */ io_set_pin(USB_CLK_EN); _delay_ms(PMU_USB_CLK_WAIT); io_set_pin(FTDI_RESETn); _delay_ms(PMU_FTDI_WAIT); /* power for the fpga should be up now, let it run */ pmu_reset_fpga(false); state = ON; return; fail_regulators: /* TODO: Turn of stuff again in reverse order */ return; }
bool ltc3675_init(ltc3675_reg_helper_fn helper) { if (helper) _ltc3675_reg_helper = helper; else _ltc3675_reg_helper = _ltc3675_default_reg_helper; #ifdef HARDWIRE_ENABLE io_output_pin(PWR_EN1); io_output_pin(PWR_EN2); io_output_pin(PWR_EN3); io_output_pin(PWR_EN4); io_output_pin(PWR_EN5); #endif // HARDWIRE_ENABLE /* io_output_pin(PWR_SDA); io_output_pin(PWR_SCL); // Must remain HIGH when idle io_set_pin(PWR_SDA); io_set_pin(PWR_SCL); */ #ifdef I2C_REWORK i2c_init_ex(PWR_SDA, PWR_SCL, _ltc3675_pull_up); #endif // I2C_REWORK io_input_pin(PWR_IRQ); #if !defined(DEBUG) && !defined(ATTINY88_DIP) io_set_pin(PWR_IRQ); // Enable pull-up for Open Drain #endif // DEBUG io_input_pin(WAKEUP); io_set_pin(WAKEUP); // Enable pull-up for Open Drain io_input_pin(ONSWITCH_DB); io_set_pin(ONSWITCH_DB); // Enable pull-up for Open Drain io_input_pin(PWR_RESET); io_set_pin(PWR_RESET); // Enable pull-up for Open Drain _ltc3675_clear_irq(); // Clear old interrupt - state might have changed (e.g. undervoltage might have been resolved) if (i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, LTC3675_REG_IRQB_MASK, 0xFF, _ltc3675_pull_up) == false) // Any PGOOD fault will pull IRQB low return false; if (i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, LTC3675_REG_UVOT, 0x70, _ltc3675_pull_up) == false) // 3.4V UV return false; if (ltc3675_has_interrupt()) _ltc3675_handle_irq(); // Non-maskable: // UV warning threshold (default): 2.7V // Over temp warning threshold (default): 10 degrees below return true; }
static void pmu_reset_fpga(bool delay) { io_clear_pin(PS_POR); io_clear_pin(PS_SRST); if (delay) _delay_ms(PMU_FPGA_RESET_DELAY); io_set_pin(PS_POR); io_set_pin(PS_SRST); }
static bool _i2c_read_byte_ex(io_pin_t sda, io_pin_t scl, uint8_t* value, bool pull_up) { // Assumes: // SDA output is LOW // SCL output is LOW io_input_pin(sda); if (pull_up) io_set_pin(sda); // OK to leave line floating for a moment (better not to drive as slave will be pulling it to ground) (*value) = 0x00; for (uint8_t i = 0; i < 8; ++i) { // if (pull_up) // io_set_pin(scl); // [Not ideal with pull-up] io_input_pin(scl); // Release HIGH if (pull_up) io_set_pin(scl); I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD); #ifdef I2C_ALLOW_CLOCK_STRETCH uint8_t retries = I2C_DEFAULT_MAX_BUS_RETRIES; while (io_test_pin(scl) == false) // Clock stretch requested? { I2C_DELAY(I2C_DEFAULT_BUS_WAIT); if (--retries == 0) { debug_log_ex("I2C:R "); debug_log_hex(scl); debug_blink_rev(5); return false; } } #endif // I2C_ALLOW_CLOCK_STRETCH (*value) |= ((io_test_pin(sda) ? 0x1 : 0x0) << (7 - i)); // MSB first if (pull_up) io_clear_pin(scl); io_output_pin(scl); // Drive LOW (not ideal with pull-up) // if (pull_up) // io_clear_pin(scl); I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD); } // Not necessary to ACK since it's only this one byte return true; }
void debug_init() { #ifdef ENABLE_SERIAL io_set_pin(SERIAL_DEBUG); io_output_pin(SERIAL_DEBUG); #endif // ENABLE_SERIAL }
/* static void _i2c_stop(io_pin_t sda, io_pin_t scl) { _i2c_stop_ex(sda, scl, false); } *//* static void _i2c_abort_safe_ex(io_pin_t pin, bool pull_up) { if (io_is_output(pin)) { if (io_is_pin_set(pin)) // This is bad - hope no slave is pulling down the line { io_input_pin(pin); // Pull-up already enabled if (pull_up == false) io_clear_pin(pin); // Doing this after changing direction ensures the line is not brought down } else // Currently pulling line down { io_input_pin(pin); // Hi-Z if (pull_up) // There will be a moment where the line will float (better than driving the line though...) { io_set_pin(pin); } } } else // Already an input { if (pull_up) { io_set_pin(pin); // Enable pull-ups } else { io_clear_pin(pin); // Disable pull-ups } } // Normally: pin will be Hi-Z input // With internal pull-up: pin will be input with pull-up enabled } */ static void _i2c_abort_safe(io_pin_t pin, bool pull_up) { if (pull_up == false) io_clear_pin(pin); // Should never be output/HIGH, could be input/<was outputting HIGH> so disable pull-ups io_input_pin(pin); if (pull_up) io_set_pin(pin); // Enable pull-up }
int8_t ltc3675_init(void) { uint8_t id; int8_t ret; ret = ltc3675_read(LTC3675_REG_LED_CONFIG, &id); if (ret) return ret; button.pmu_button.ops = <c3675_pmu_button_ops; button.onswitch_last_state = io_test_pin(ONSWITCH_DB); /* setup the input pins with pull-up for open drain */ io_input_pin(PWR_IRQ); io_set_pin(PWR_IRQ); io_input_pin(WAKEUP); io_set_pin(WAKEUP); io_input_pin(ONSWITCH_DB); io_set_pin(ONSWITCH_DB); io_input_pin(PWR_RESET); io_set_pin(PWR_RESET); /* clear the old interrupts */ ltc3675_clear_interrupts(); /* setup interrupt masks on chip to be notified of any faults */ ret = ltc3675_write(LTC3675_REG_IRQB_MASK, 0xff); if (ret) goto fail_i2c_write_mask; /* program warning @ 3.4V */ ret = ltc3675_write(LTC3675_REG_UVOT, 0x70); if (ret) goto fail_i2c_write_uvot; pmu_register_button(&button.pmu_button); return 0; fail_i2c_write_uvot: fail_i2c_write_mask: return ret; }
void tps54478_init(bool enable) { /* enable pull-up for open drain */ io_input_pin(CORE_PGOOD); io_set_pin(CORE_PGOOD); tps54478_set_regulator(NULL, enable); io_clear_pin(CORE_PWR_EN); }
/** * Initialize the ST7565 */ void st7565_init(void) { io_set_pin_dir(ST7565_RST_PORT,ST7565_RST_PIN, IO_DIR_OUT); io_set_pin_dir(ST7565_RS_PORT,ST7565_RS_PIN, IO_DIR_OUT); io_set_pin(ST7565_RST_PORT,ST7565_RST_PIN, 1); tick_wait_ms(10); io_set_pin(ST7565_RST_PORT,ST7565_RST_PIN, 0); tick_wait_ms(10); io_set_pin(ST7565_RST_PORT,ST7565_RST_PIN, 1); tick_wait_ms(10); spi_set_baud(ST7565_DRV, ST7565_BAUD); spi_cs_en(ST7565_DRV); st7565_command(CMD_SET_BIAS_7); st7565_command(CMD_SET_ADC_NORMAL); st7565_command(CMD_SET_COM_NORMAL); st7565_command(CMD_SET_DISP_START_LINE); st7565_command(CMD_SET_POWER_CONTROL | 0x4); tick_wait_ms(50); st7565_command(CMD_SET_POWER_CONTROL | 0x6); tick_wait_ms(50); st7565_command(CMD_SET_POWER_CONTROL | 0x7); tick_wait_ms(10); st7565_command(CMD_SET_RESISTOR_RATIO | 0x6); st7565_command(CMD_DISPLAY_ON); st7565_command(CMD_SET_ALLPTS_NORMAL); /*Set brightness*/ st7565_command(CMD_SET_VOLUME_FIRST); st7565_command(CMD_SET_VOLUME_SECOND | (0x18 & 0x3f)); spi_cs_dis(ST7565_DRV); memset(lcd_fb, 0x00, sizeof(lcd_fb)); }
void debug_init() { io_output_pin(DEBUG_1); io_output_pin(DEBUG_2); io_enable_pin(DEBUG_1, true); io_enable_pin(DEBUG_2, true); #ifdef ENABLE_SERIAL io_set_pin(SERIAL_DEBUG); io_output_pin(SERIAL_DEBUG); #endif // ENABLE_SERIAL }
void _debug_log_ex(const char* message, bool new_line) { if (message[0] == '\0') return; pmc_mask_irqs(true); do { _serial_tx_char(*message); } while (*(++message) != '\0'); if (new_line) _serial_tx_char('\n'); io_set_pin(SERIAL_DEBUG); pmc_mask_irqs(false); }
void debug_log_ex_P(const char* message, bool new_line) { char c = pgm_read_byte(message); if (c == '\0') return; pmc_mask_irqs(true); do { _serial_tx_char(c); c = pgm_read_byte(++message); } while (c != '\0'); if (new_line) _serial_tx_char('\n'); io_set_pin(SERIAL_DEBUG); pmc_mask_irqs(false); }
/** * Write a command to the ST7565 * @param cmd the command */ static void st7565_command(uint8_t cmd) { io_set_pin(ST7565_RS_PORT, ST7565_RS_PIN, ST7565_CMD_MODE); spi_xchg(ST7565_DRV, &cmd, NULL, sizeof(cmd)); }
static bool _i2c_stop_ex(io_pin_t sda, io_pin_t scl, bool pull_up) { // Assumes: // SCL is output & LOW // SDA is input (Hi-Z, or pull-up enabled) // Assuming pull-up already enabled //if (pull_up) // io_set_pin(sda); bool result = true; // SDA should be HIGH after ACK has been clocked away // bool skip_drive = false; uint8_t retries = 0; while (io_test_pin(sda) == false) { if (retries == I2C_DEFAULT_MAX_ACK_RETRIES) { debug_log_ex("I2C:STP ", false); debug_log_hex(sda); debug_blink_rev(4); // skip_drive = true; result = false; break; // SDA is being held low?! } ++retries; I2C_DELAY(I2C_DEFAULT_RETRY_DELAY); } // STOP condition // if ((pull_up == false) || (skip_drive)) io_clear_pin(sda); // Don't tri-state if internal pull-up is used // //else // // Pin will now be driven, but having checked SDA is HIGH above means slave's SDA should be Open Collector (i.e. it won't blow up) io_output_pin(sda); // Drive LOW // if (pull_up) // io_clear_pin(sda); /////////////////////////////////// // if (pull_up) // io_set_pin(scl); // Don't tri-state if internal pull-up is used. Line will be driven, but assuming this is the only master on the clock line (i.e. no one else will pull it low). io_input_pin(scl); if (pull_up) io_set_pin(scl); I2C_DELAY(I2C_DEFAULT_STOP_TIME); /////////////////////////////////// // if ((pull_up) && (skip_drive == false)) // io_set_pin(sda); // Don't tri-state if internal pull-up is used io_input_pin(sda); // if ((pull_up) && (skip_drive)) io_set_pin(sda); I2C_DELAY(I2C_DEFAULT_BUS_FREE_TIME); return result; }
bool i2c_read2_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value, bool pull_up) { if (_i2c_start_ex(sda, scl, pull_up) == false) return false; if (_i2c_write_byte_ex(sda, scl, addr & ~0x01, pull_up) == false) { #ifdef I2C_EXTRA_DEBUGGING //debug_log_ex("R21:", false); debug_log("R21"); //debug_log_hex(addr); #endif // I2C_EXTRA_DEBUGGING goto i2c_read2_fail; } if (_i2c_write_byte_ex(sda, scl, subaddr, pull_up) == false) { #ifdef I2C_EXTRA_DEBUGGING //debug_log_ex("R22:", false); debug_log("R22"); //debug_log_hex(subaddr); #endif // I2C_EXTRA_DEBUGGING goto i2c_read2_fail; } io_input_pin(scl); if (pull_up) io_set_pin(scl); I2C_DELAY(I2C_DEFAULT_BUS_WAIT); if (_i2c_start_ex(sda, scl, pull_up) == false) { return false; } if (_i2c_write_byte_ex(sda, scl, addr | 0x01, pull_up) == false) { #ifdef I2C_EXTRA_DEBUGGING //debug_log_ex("R23:", false); debug_log("R23"); //debug_log_hex(addr); #endif // I2C_EXTRA_DEBUGGING goto i2c_read2_fail; } if (_i2c_read_byte_ex(sda, scl, value, pull_up) == false) { #ifdef I2C_EXTRA_DEBUGGING //debug_log_ex("R24:", false); debug_log("R24"); //debug_log_hex(*value); #endif // I2C_EXTRA_DEBUGGING goto i2c_read2_fail; } if (_i2c_stop_ex(sda, scl, pull_up) == false) { #ifdef I2C_EXTRA_DEBUGGING debug_log("R25"); #endif // I2C_EXTRA_DEBUGGING } return true; i2c_read2_fail: _i2c_abort_ex(sda, scl, pull_up); return false; }
/* static void _i2c_abort(io_pin_t sda, io_pin_t scl) { _i2c_abort_ex(sda, scl, false); } */ static bool _i2c_write_byte_ex(io_pin_t sda, io_pin_t scl, uint8_t value, bool pull_up) { // Assumes: // SDA output is LOW // SCL output is LOW for (uint8_t i = 0; i < 8; ++i) { bool b = ((value & (0x01 << (7 - i))) != 0x00); // MSB first if (b) { if (pull_up) { // io_set_pin(sda); // This is bad (will drive line for a moment), but more stable than letting line float io_input_pin(sda); io_set_pin(sda); } else io_input_pin(sda); // Release HIGH if (io_test_pin(sda) == false) { debug_log("I2C:WR "); debug_log_hex(sda); debug_blink_rev(1); return false; } } else { if (pull_up) { // if (io_is_output(sda)) io_clear_pin(sda); // else // { io_output_pin(sda); // [This is bad (will drive line for a moment), but more stable than letting line float] // io_clear_pin(sda); // } } else { io_enable_pin(sda, false); io_output_pin(sda); // Drive LOW } } /////////////////////////////// io_input_pin(scl); // Release HIGH if (pull_up) io_set_pin(scl); I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD); #ifdef I2C_ALLOW_CLOCK_STRETCH uint8_t retries = I2C_DEFAULT_MAX_BUS_RETRIES; while (io_test_pin(scl) == false) // Clock stretch requested? { I2C_DELAY(I2C_DEFAULT_BUS_WAIT); if (--retries == 0) { io_input_pin(sda); // Release HIGH if (pull_up) io_set_pin(sda); debug_log_ex("I2C:STRTCH ", false); debug_log_hex(scl); debug_blink_rev(2); return false; } } #endif // I2C_ALLOW_CLOCK_STRETCH if (pull_up) io_clear_pin(scl); io_output_pin(scl); // Drive LOW I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD); } io_input_pin(sda); // Release HIGH if (pull_up) io_set_pin(sda); // Assuming letting line float won't confuse slave when pulling line LOW for ACK I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD); uint8_t retries = 0; while ((_i2c_disable_ack_check == false) && (io_test_pin(sda))) { if (retries == I2C_DEFAULT_MAX_ACK_RETRIES) { debug_log_ex("I2C:ACK ", false); debug_log_hex_ex(sda, false); debug_log_hex(value); debug_blink_rev(3); return false; // Will abort and not release bus - done by caller } ++retries; I2C_DELAY(I2C_DEFAULT_RETRY_DELAY); } // Clock away acknowledge // if (pull_up) // io_set_pin(scl); io_input_pin(scl); // Release HIGH if (pull_up) io_set_pin(scl); I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD); if (pull_up) io_clear_pin(scl); io_output_pin(scl); // Drive LOW // if (pull_up) // io_clear_pin(scl); I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD); return true; }
int8_t ltc294x_init(ltc294x_model_t model) { uint8_t val; int8_t ret; /* make input, set pullup */ io_input_pin(FG_ALn_CC); io_set_pin(FG_ALn_CC); gauge.pmu_gauge.ops = <c294x_pmu_gauge_ops; ret = ltc294x_read(LTC294X_REG_STATUS, &val); if (ret) goto fail_i2c_read; val &= LTC294X_CHIP_ID_MASK; val >>= LTC294X_CHIP_ID_SHIFT; if (val != model) goto fail_id; /* set ACR to 0, this allows for calibrating by * completely discharging the battery once */ ltc294x_set_charge(0x0010); /* set low threshold to 10% assuming full is 0xeae4... */ ltc294x_write16(LTC294X_REG_LOW_THRESH_MSB, 0x1794); gauge.charge_lo_thesh = 0x1794; /* set high threshold for temperature to 85 C */ ltc294x_write16(LTC294X_REG_TEMP_THRESH_HI, 0x98); gauge.temp_thresh_high = 0x9800; /* set low threshold for temperature to -2 C */ ltc294x_write16(LTC294X_REG_TEMP_THRESH_LO, 0x74); gauge.temp_thresh_low = 0x7400; ret = ltc294x_read(LTC294X_REG_CONTROL, &val); if (ret) goto fail_i2c_read; /* enable automatic temp conversion */ val &= ~LTC294X_ADC_MODE_MASK; val |= 0x3 << LTC294X_ADC_MODE_SHIFT; /* set prescaler to 16 */ val &= ~LTC294X_PRESCALER_MASK; val |= 0x4 << LTC294X_PRESCALER_SHIFT; /* disable IRQ mode */ val &= ~LTC294X_ALCC_CFG_MASK; val |= 0x0 << LTC294X_ALCC_CFG_SHIFT; ret = ltc294x_write(LTC294X_REG_CONTROL, val); pmu_register_gauge(&gauge.pmu_gauge); return 0; fail_id: fail_i2c_read: return ret; }
/** * Write data to the ST7565 * @param data the data */ static void st7565_data(uint8_t data) { io_set_pin(ST7565_RS_PORT, ST7565_RS_PIN, ST7565_DATA_MODE); spi_xchg(ST7565_DRV, &data, NULL, sizeof(data)); }
int8_t pmu_init(void) { int8_t ret; bool battery_present; state = OFF; /* make panic button an input */ io_input_pin(PANICn); panic_last = io_test_pin(PANICn); /* make the LED outputs */ io_output_pin(CHARGE); io_output_pin(POWER_LED); /* initialize the ADC, so we can sense the battery */ adc_init(); /* initialize TPS54478 for core power */ tps54478_init(true); /* wiggle USB and FTDI pins */ io_input_pin(USB_RESETn); io_output_pin(FTDI_RESETn); io_output_pin(USB_CLK_EN); io_input_pin(FTDI_CBUS3); /* make OVERTEMP input pin */ io_input_pin(OVERTEMP); /* initialize the charger */ ret = bq2419x_init(); if (ret) goto fail_bq2419x; /* wait a sec */ _delay_ms(1000); /* wdt setup */ cli(); WDTCSR |= BIT(WDCE) | BIT(WDE); WDTCSR = BIT(WDIE); sei(); /* see if we got a battery */ battery_present = pmu_battery_present(); battery_present_last = battery_present; if (battery_present) { last_full_charge = eeprom_get_last_full(); ret = ltc294x_init(LTC294X_MODEL_2942); } if (ret) return ret; ret = ltc3675_init(); if (ret) goto fail_ltc3675; /* need to hold them low until power is stable */ io_output_pin(PS_POR); io_output_pin(PS_SRST); io_clear_pin(PS_POR); io_clear_pin(PS_SRST); /* TODO: Not sure if needed */ io_input_pin(AVR_RESET); /* TODO: This will probably need to change */ io_input_pin(AVR_IRQ); io_set_pin(AVR_IRQ); // enable pull-up ? /* configure and enable interrupts */ interrupt_init(); /* initialize the timers */ timer0_init(); timer1_init(); state = OFF; return 0; fail_ltc3675: fail_bq2419x: return -1; }