]> git.donarmstrong.com Git - qmk_firmware.git/blob - keyboards/hhkb/rn42/battery.c
add hhkb bluetooth functionality (rn42) (#2693)
[qmk_firmware.git] / keyboards / hhkb / rn42 / battery.c
1 #include <avr/io.h>
2 #include <util/delay.h>
3 #include "battery.h"
4
5
6 /*
7  * Battery
8  */
9 void battery_init(void)
10 {
11     // blink
12     battery_led(LED_ON);  _delay_ms(100);
13     battery_led(LED_OFF); _delay_ms(100);
14     battery_led(LED_ON);  _delay_ms(100);
15     battery_led(LED_OFF); _delay_ms(100);
16     // LED indicates charger status
17     battery_led(LED_CHARGER);
18
19     // ADC setting for voltage monitor
20     // Ref:2.56V band-gap, Input:ADC0(PF0), Prescale:128(16MHz/128=125KHz)
21     ADMUX = (1<<REFS1) | (1<<REFS0);
22     ADCSRA = (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
23     // digital input buffer disable(24.9.5)
24     DIDR0 = (1<<ADC0D) | (1<<ADC4D) | (1<<ADC7D);
25     DIDR1 = (1<<AIN0D);
26     DIDR2 = (1<<ADC8D) | (1<<ADC9D) | (1<<ADC11D) | (1<<ADC12D) | (1<<ADC13D);
27
28     // ADC disable voltate divider(PF4)
29     DDRF  |=  (1<<4);
30     PORTF &= ~(1<<4);
31 }
32
33 // Indicator for battery
34 void battery_led(battery_led_t val)
35 {
36     if (val == LED_TOGGLE) {
37         // Toggle LED
38         DDRF  |=  (1<<5);
39         PINF  |=  (1<<5);
40     } else if (val == LED_ON) {
41         // On overriding charger status
42         DDRF  |=  (1<<5);
43         PORTF &= ~(1<<5);
44     } else if (val == LED_OFF) {
45         // Off overriding charger status
46         DDRF  |=  (1<<5);
47         PORTF |=  (1<<5);
48     } else {
49         // Display charger status
50         DDRF  &= ~(1<<5);
51         PORTF &= ~(1<<5);
52     }
53 }
54
55 bool battery_charging(void)
56 {
57     if (!(USBSTA&(1<<VBUS))) return false;
58
59     // Charger Status:
60     //   MCP73831   MCP73832   LTC4054  Status
61     //   Hi-Z       Hi-Z       Hi-Z     Shutdown/No Battery
62     //   Low        Low        Low      Charging
63     //   Hi         Hi-Z       Hi-Z     Charged
64
65     // preserve last register status
66     uint8_t ddrf_prev  = DDRF;
67     uint8_t portf_prev = PORTF;
68
69     // Input with pullup
70     DDRF  &= ~(1<<5);
71     PORTF |=  (1<<5);
72     _delay_ms(1);
73     bool charging = PINF&(1<<5) ? false : true;
74
75     // restore last register status
76     DDRF  = (DDRF&~(1<<5))  | (ddrf_prev&(1<<5));
77     PORTF = (PORTF&~(1<<5)) | (portf_prev&(1<<5));
78
79     // TODO: With MCP73831 this can not get stable status when charging.
80     // LED is powered from PSEL line(USB or Lipo)
81     // due to weak low output of STAT pin?
82     // due to pull-up'd via resitor and LED?
83     return charging;
84 }
85
86 // Returns voltage in mV
87 uint16_t battery_voltage(void)
88 {
89     // ADC disable voltate divider(PF4)
90     DDRF  |=  (1<<4);
91     PORTF |=  (1<<4);
92
93     volatile uint16_t bat;
94     ADCSRA |= (1<<ADEN);
95     _delay_ms(1);   // wait for charging S/H capacitance
96
97     ADCSRA |= (1<<ADSC);
98     while (ADCSRA & (1<<ADSC)) ;
99     bat = ADC;
100
101     ADCSRA &= ~(1<<ADEN);
102
103     // ADC disable voltate divider(PF4)
104     DDRF  |=  (1<<4);
105     PORTF &= ~(1<<4);
106
107     return (bat - BATTERY_ADC_OFFSET) * BATTERY_ADC_RESOLUTION;
108 }
109
110 static bool low_voltage(void) {
111     static bool low = false;
112     uint16_t v = battery_voltage();
113     if (v < BATTERY_VOLTAGE_LOW_LIMIT) {
114         low = true;
115     } else if (v > BATTERY_VOLTAGE_LOW_RECOVERY) {
116         low = false;
117     }
118     return low;
119 }
120
121 battery_status_t battery_status(void)
122 {
123     if (USBSTA&(1<<VBUS)) {
124         /* powered */
125         return battery_charging() ? CHARGING : FULL_CHARGED;
126     } else {
127         /* not powered */
128         return low_voltage() ? LOW_VOLTAGE : DISCHARGING;
129     }
130 }