2014-09-03 20:30:00 +00:00
# include <avr/io.h>
# include <util/delay.h>
# include "battery.h"
/*
* Battery
*/
void battery_init ( void )
{
// blink
battery_led ( LED_ON ) ; _delay_ms ( 500 ) ;
battery_led ( LED_OFF ) ; _delay_ms ( 500 ) ;
battery_led ( LED_ON ) ; _delay_ms ( 500 ) ;
battery_led ( LED_OFF ) ; _delay_ms ( 500 ) ;
// 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 ) ;
2014-12-02 13:47:59 +00:00
// digital input buffer disable(24.9.5)
2014-12-02 17:17:54 +00:00
DIDR0 = ( 1 < < ADC0D ) | ( 1 < < ADC4D ) | ( 1 < < ADC7D ) ;
2014-12-02 13:47:59 +00:00
DIDR1 = ( 1 < < AIN0D ) ;
DIDR2 = ( 1 < < ADC8D ) | ( 1 < < ADC9D ) | ( 1 < < ADC11D ) | ( 1 < < ADC12D ) | ( 1 < < ADC13D ) ;
2014-09-19 21:12:49 +00:00
// ADC disable voltate divider(PF4)
DDRF | = ( 1 < < 4 ) ;
PORTF & = ~ ( 1 < < 4 ) ;
2014-09-03 20:30:00 +00:00
}
// 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 ;
2014-09-04 08:08:23 +00:00
// 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
2014-09-03 20:30:00 +00:00
// 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 ) ) ;
2014-09-04 08:08:23 +00:00
// 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?
2014-09-03 20:30:00 +00:00
return charging ;
}
// Returns voltage in mV
uint16_t battery_voltage ( void )
{
2014-09-19 21:12:49 +00:00
// ADC disable voltate divider(PF4)
DDRF | = ( 1 < < 4 ) ;
PORTF | = ( 1 < < 4 ) ;
2014-09-03 20:30:00 +00:00
volatile uint16_t bat ;
2014-12-02 17:08:27 +00:00
ADCSRA | = ( 1 < < ADEN ) ;
2014-12-03 05:22:10 +00:00
_delay_ms ( 1 ) ; // wait for charging S/H capacitance
2014-09-03 20:30:00 +00:00
ADCSRA | = ( 1 < < ADSC ) ;
while ( ADCSRA & ( 1 < < ADSC ) ) ;
bat = ADC ;
2014-12-02 17:08:27 +00:00
ADCSRA & = ~ ( 1 < < ADEN ) ;
2014-09-03 20:30:00 +00:00
2014-09-19 21:12:49 +00:00
// ADC disable voltate divider(PF4)
DDRF | = ( 1 < < 4 ) ;
PORTF & = ~ ( 1 < < 4 ) ;
2014-09-03 20:30:00 +00:00
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 ;
}
}