#include <avr/io.h> #include <util/delay.h> #include "battery.h" /* * Battery */ void battery_init(void) { // blink battery_led(LED_ON); _delay_ms(100); battery_led(LED_OFF); _delay_ms(100); battery_led(LED_ON); _delay_ms(100); battery_led(LED_OFF); _delay_ms(100); // LED indicates charger status battery_led(LED_CHARGER); // ADC setting for voltage monitor // Ref:2.56V band-gap, Input:ADC0(PF0), Prescale:128(16MHz/128=125KHz) ADMUX = (1<<REFS1) | (1<<REFS0); ADCSRA = (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // digital input buffer disable(24.9.5) DIDR0 = (1<<ADC0D) | (1<<ADC4D) | (1<<ADC7D); DIDR1 = (1<<AIN0D); DIDR2 = (1<<ADC8D) | (1<<ADC9D) | (1<<ADC11D) | (1<<ADC12D) | (1<<ADC13D); // ADC disable voltate divider(PF4) DDRF |= (1<<4); PORTF &= ~(1<<4); } // Indicator for battery void battery_led(battery_led_t val) { if (val == LED_TOGGLE) { // Toggle LED DDRF |= (1<<5); PINF |= (1<<5); } else if (val == LED_ON) { // On overriding charger status DDRF |= (1<<5); PORTF &= ~(1<<5); } else if (val == LED_OFF) { // Off overriding charger status DDRF |= (1<<5); PORTF |= (1<<5); } else { // Display charger status DDRF &= ~(1<<5); PORTF &= ~(1<<5); } } bool battery_charging(void) { if (!(USBSTA&(1<<VBUS))) return false; // Charger Status: // MCP73831 MCP73832 LTC4054 Status // Hi-Z Hi-Z Hi-Z Shutdown/No Battery // Low Low Low Charging // Hi Hi-Z Hi-Z Charged // preserve last register status uint8_t ddrf_prev = DDRF; uint8_t portf_prev = PORTF; // Input with pullup DDRF &= ~(1<<5); PORTF |= (1<<5); _delay_ms(1); bool charging = PINF&(1<<5) ? false : true; // restore last register status DDRF = (DDRF&~(1<<5)) | (ddrf_prev&(1<<5)); PORTF = (PORTF&~(1<<5)) | (portf_prev&(1<<5)); // TODO: With MCP73831 this can not get stable status when charging. // LED is powered from PSEL line(USB or Lipo) // due to weak low output of STAT pin? // due to pull-up'd via resitor and LED? return charging; } // Returns voltage in mV uint16_t battery_voltage(void) { // ADC disable voltate divider(PF4) DDRF |= (1<<4); PORTF |= (1<<4); volatile uint16_t bat; ADCSRA |= (1<<ADEN); _delay_ms(1); // wait for charging S/H capacitance ADCSRA |= (1<<ADSC); while (ADCSRA & (1<<ADSC)) ; bat = ADC; ADCSRA &= ~(1<<ADEN); // ADC disable voltate divider(PF4) DDRF |= (1<<4); PORTF &= ~(1<<4); return (bat - BATTERY_ADC_OFFSET) * BATTERY_ADC_RESOLUTION; } static bool low_voltage(void) { static bool low = false; uint16_t v = battery_voltage(); if (v < BATTERY_VOLTAGE_LOW_LIMIT) { low = true; } else if (v > BATTERY_VOLTAGE_LOW_RECOVERY) { low = false; } return low; } battery_status_t battery_status(void) { if (USBSTA&(1<<VBUS)) { /* powered */ return battery_charging() ? CHARGING : FULL_CHARGED; } else { /* not powered */ return low_voltage() ? LOW_VOLTAGE : DISCHARGING; } }