diff options
Diffstat (limited to 'keyboards/hhkb/rn42/battery.c')
-rw-r--r-- | keyboards/hhkb/rn42/battery.c | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/keyboards/hhkb/rn42/battery.c b/keyboards/hhkb/rn42/battery.c new file mode 100644 index 0000000000..c0c82bb80a --- /dev/null +++ b/keyboards/hhkb/rn42/battery.c @@ -0,0 +1,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; + } +} |