rn42/suart.S \ | rn42/suart.S \ | ||||
rn42/rn42.c \ | rn42/rn42.c \ | ||||
rn42/rn42_task.c \ | rn42/rn42_task.c \ | ||||
rn42/battery.c \ | |||||
rn42/main.c | rn42/main.c | ||||
OPT_DEFS += -DPROTOCOL_RN42 | OPT_DEFS += -DPROTOCOL_RN42 |
#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); | |||||
ADCSRA |= (1<<ADEN); | |||||
} | |||||
// 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; | |||||
// MCP73831:STAT | |||||
// HiZ: Shutdown/No Battery | |||||
// Low: Charging | |||||
// Hi: 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)); | |||||
return charging; | |||||
} | |||||
// Returns voltage in mV | |||||
uint16_t battery_voltage(void) | |||||
{ | |||||
volatile uint16_t bat; | |||||
//ADCSRA |= (1<<ADEN); | |||||
// discard first result | |||||
ADCSRA |= (1<<ADSC); | |||||
while (ADCSRA & (1<<ADSC)) ; | |||||
bat = ADC; | |||||
// discard second result | |||||
ADCSRA |= (1<<ADSC); | |||||
while (ADCSRA & (1<<ADSC)) ; | |||||
bat = ADC; | |||||
ADCSRA |= (1<<ADSC); | |||||
while (ADCSRA & (1<<ADSC)) ; | |||||
bat = ADC; | |||||
//ADCSRA &= ~(1<<ADEN); | |||||
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; | |||||
} | |||||
} |
#ifndef POWER_H | |||||
#define POWER_H | |||||
#include <stdint.h> | |||||
#include <stdbool.h> | |||||
typedef enum { | |||||
FULL_CHARGED, | |||||
CHARGING, | |||||
DISCHARGING, | |||||
LOW_VOLTAGE, | |||||
} battery_status_t; | |||||
typedef enum { | |||||
LED_CHARGER = 0, | |||||
LED_ON, | |||||
LED_OFF, | |||||
LED_TOGGLE, | |||||
} battery_led_t; | |||||
/* Battery API */ | |||||
void battery_init(void); | |||||
void battery_led(battery_led_t val); | |||||
bool battery_charging(void); | |||||
uint16_t battery_voltage(void); | |||||
battery_status_t battery_status(void); | |||||
#define BATTERY_VOLTAGE_LOW_LIMIT 3500 | |||||
#define BATTERY_VOLTAGE_LOW_RECOVERY 3700 | |||||
// ADC offset:16, resolution:5mV | |||||
#define BATTERY_ADC_OFFSET 16 | |||||
#define BATTERY_ADC_RESOLUTION 5 | |||||
#endif |
#include "print.h" | #include "print.h" | ||||
#include "timer.h" | #include "timer.h" | ||||
#include "command.h" | #include "command.h" | ||||
#include "battery.h" | |||||
static bool config_mode = false; | static bool config_mode = false; | ||||
static bool force_usb = false; | static bool force_usb = false; | ||||
} | } | ||||
} | } | ||||
static void battery_adc_init(void) | |||||
{ | |||||
ADMUX = (1<<REFS1) | (1<<REFS0); // Ref:2.56V band-gap, Input:ADC0(PF0) | |||||
ADCSRA = (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // Prescale:128 16MHz/128=125KHz | |||||
ADCSRA |= (1<<ADEN); // enable ADC | |||||
} | |||||
static uint16_t battery_adc(void) | |||||
{ | |||||
volatile uint16_t bat; | |||||
ADCSRA |= (1<<ADEN); | |||||
// discard first result | |||||
ADCSRA |= (1<<ADSC); | |||||
while (ADCSRA & (1<<ADSC)) ; | |||||
bat = ADC; | |||||
// discard second result | |||||
ADCSRA |= (1<<ADSC); | |||||
while (ADCSRA & (1<<ADSC)) ; | |||||
bat = ADC; | |||||
ADCSRA |= (1<<ADSC); | |||||
while (ADCSRA & (1<<ADSC)) ; | |||||
bat = ADC; | |||||
ADCSRA &= ~(1<<ADEN); | |||||
return bat; | |||||
} | |||||
static void battery_led(bool on) | |||||
{ | |||||
if (on) { | |||||
DDRF |= (1<<5); | |||||
PORTF &= ~(1<<5); // Low | |||||
} else { | |||||
DDRF &= ~(1<<5); | |||||
PORTF &= ~(1<<5); // HiZ | |||||
} | |||||
} | |||||
static bool battery_charging(void) | |||||
{ | |||||
// MCP73831:STAT | |||||
// Hi-Z: Shutdown/No Battery | |||||
// Low: Charging | |||||
// Hi: Charged | |||||
DDRF &= ~(1<<5); | |||||
PORTF |= (1<<5); | |||||
return PINF&(1<<5) ? false : true; | |||||
} | |||||
void rn42_task_init(void) | void rn42_task_init(void) | ||||
{ | { | ||||
battery_adc_init(); | |||||
// battery charging(HiZ) | |||||
DDRF &= ~(1<<5); | |||||
PORTF &= ~(1<<5); | |||||
battery_init(); | |||||
} | } | ||||
void rn42_task(void) | void rn42_task(void) | ||||
} | } | ||||
} | } | ||||
/* Battery monitor */ | |||||
/* Low voltage alert */ | |||||
if (battery_status() == LOW_VOLTAGE) { | |||||
battery_led(LED_ON); | |||||
} else { | |||||
battery_led(LED_CHARGER); | |||||
} | |||||
/* Connection monitor */ | /* Connection monitor */ | ||||
if (rn42_linked()) { | if (rn42_linked()) { | ||||
xprintf("config_mode: %X\n", config_mode); | xprintf("config_mode: %X\n", config_mode); | ||||
xprintf("VBUS: %X\n", USBSTA&(1<<VBUS)); | xprintf("VBUS: %X\n", USBSTA&(1<<VBUS)); | ||||
xprintf("battery_charging: %X\n", battery_charging()); | xprintf("battery_charging: %X\n", battery_charging()); | ||||
xprintf("battery_status: %X\n", battery_status()); | |||||
return true; | return true; | ||||
case KC_B: | case KC_B: | ||||
// battery monitor | // battery monitor | ||||
t = timer_read32()/1000; | t = timer_read32()/1000; | ||||
b = battery_adc(); | |||||
xprintf("BAT: %umV(%04X)\t", (b-16)*5, b); | |||||
b = battery_voltage(); | |||||
xprintf("BAT: %umV\t", b); | |||||
xprintf("%02u:", t/3600); | xprintf("%02u:", t/3600); | ||||
xprintf("%02u:", t%3600/60); | xprintf("%02u:", t%3600/60); | ||||
xprintf("%02u\n", t%60); | xprintf("%02u\n", t%60); |