summaryrefslogtreecommitdiff
path: root/keyboards/hhkb/rn42/battery.c
blob: c0c82bb80ae34b4dce3d14217773f63e01723305 (plain)
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;
    }
}