blob: c0c82bb80ae34b4dce3d14217773f63e01723305 (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
#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;
}
}
|