mirror of
https://github.com/google/pebble.git
synced 2025-06-14 13:43:12 +00:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
53
src/fw/drivers/battery/battery_adc_conversion.c
Normal file
53
src/fw/drivers/battery/battery_adc_conversion.c
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "drivers/battery.h"
|
||||
|
||||
uint32_t battery_convert_reading_to_millivolts(ADCVoltageMonitorReading reading,
|
||||
uint32_t numerator, uint32_t denominator) {
|
||||
// The result from the ADC is 0-1.8V, but scaled into a 12bit number. That means a value of
|
||||
// zero indicates 0V and a value of 4095 (2^12 - 1) indicates a reading of 1.8V.
|
||||
|
||||
// The ADC is only capable of measuring between 0 and 1.8V, so we expect the thing providing
|
||||
// a voltage to the monitor pin to be scaling it in some way. This scaling factor is captured
|
||||
// in the numerator and denominator arguments.
|
||||
|
||||
// Therefore, whatever 12-bit number we read from the ADC needs to be converted to a voltage by
|
||||
// multiplying it by 1.8/4095, and then further scaled into it's final voltage by multiplying by
|
||||
// numerator and dividing by the denominator.
|
||||
|
||||
// Finally, our reading contains a sum of many readings from both the monitor pin as well as
|
||||
// an internal 1.2V reference voltage. The reason for this is that these pins will have noise
|
||||
// on them and we can assume that any ripple on the mon rail will also occur on the 1.2V internal
|
||||
// reference voltage. So, we can measure the voltage synchronously on both ADCs and then
|
||||
// calculate a relative voltage. Therefore, the actual monitor voltage can be estimated by
|
||||
// calculating 1800 * (vmon_mv_sum / (vref_mv_sum * 1800 / 1200) or
|
||||
// 1800 * (vmon_mv_sum * 1200) / (vref_mv_sum * 1800).
|
||||
|
||||
// Convert from 12-bit to millivolts by multiplying by 1800/4095 which is the same as 40/91
|
||||
const uint32_t vref_mv_sum = reading.vref_total * 40 / 91;
|
||||
const uint32_t vmon_mv_sum = reading.vmon_total * 40 / 91;
|
||||
|
||||
// Use the reference voltage to convert a single smoothed out mv reading.
|
||||
// Multiply vmon/vref * 2/3 to find a percentage of the full scale and then multiply it back
|
||||
// by 1800 to get back to mV.
|
||||
const uint32_t millivolts = ((vmon_mv_sum * 1800 * 2) / (vref_mv_sum * 3));
|
||||
|
||||
// Finally, hit it with the scaling factors.
|
||||
const uint32_t scaled_millivolts = millivolts * numerator / denominator;
|
||||
|
||||
return scaled_millivolts;
|
||||
}
|
57
src/fw/drivers/battery/battery_common.c
Normal file
57
src/fw/drivers/battery/battery_common.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "drivers/battery.h"
|
||||
|
||||
#include "board/board.h"
|
||||
#include "drivers/voltage_monitor.h"
|
||||
#include "drivers/gpio.h"
|
||||
#include "drivers/periph_config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static bool s_charging_forced_disable = false;
|
||||
|
||||
ADCVoltageMonitorReading battery_read_voltage_monitor(void) {
|
||||
VoltageReading info;
|
||||
voltage_monitor_read(VOLTAGE_MONITOR_BATTERY, &info);
|
||||
return (ADCVoltageMonitorReading) {
|
||||
.vref_total = info.vref_total,
|
||||
.vmon_total = info.vmon_total,
|
||||
};
|
||||
}
|
||||
|
||||
bool battery_charge_controller_thinks_we_are_charging(void) {
|
||||
if (s_charging_forced_disable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return battery_charge_controller_thinks_we_are_charging_impl();
|
||||
}
|
||||
|
||||
bool battery_is_usb_connected(void) {
|
||||
if (s_charging_forced_disable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return battery_is_usb_connected_impl();
|
||||
}
|
||||
|
||||
void battery_force_charge_enable(bool charging_enabled) {
|
||||
s_charging_forced_disable = !charging_enabled;
|
||||
|
||||
battery_set_charge_enable(charging_enabled);
|
||||
}
|
67
src/fw/drivers/battery/battery_pmic.c
Normal file
67
src/fw/drivers/battery/battery_pmic.c
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "drivers/battery.h"
|
||||
#include "drivers/gpio.h"
|
||||
#include "drivers/periph_config.h"
|
||||
#include "drivers/pmic.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
void battery_init(void) {
|
||||
// pmic_init() needs to happen, but I think it will happen elsewhere first
|
||||
// It may be okay to just init the pmic here again
|
||||
return;
|
||||
}
|
||||
|
||||
int battery_get_millivolts(void) {
|
||||
if (!pmic_enable_battery_measure()) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Failed to enable battery measure. "
|
||||
"Battery voltage reading will be bogus.");
|
||||
}
|
||||
ADCVoltageMonitorReading info = battery_read_voltage_monitor();
|
||||
pmic_disable_battery_measure();
|
||||
|
||||
PBL_ASSERTN(BOARD_CONFIG_POWER.battery_vmon_scale.denominator);
|
||||
return battery_convert_reading_to_millivolts(info,
|
||||
BOARD_CONFIG_POWER.battery_vmon_scale.numerator,
|
||||
BOARD_CONFIG_POWER.battery_vmon_scale.denominator);
|
||||
}
|
||||
|
||||
bool battery_charge_controller_thinks_we_are_charging_impl(void) {
|
||||
return pmic_is_charging();
|
||||
}
|
||||
|
||||
bool battery_is_usb_connected_impl(void) {
|
||||
return pmic_is_usb_connected();
|
||||
}
|
||||
|
||||
void battery_set_charge_enable(bool charging_enabled) {
|
||||
pmic_set_charger_state(charging_enabled);
|
||||
}
|
||||
|
||||
// TODO
|
||||
// This is my understanding from Figure 9 of the datasheet:
|
||||
// Charger off -> Pre charge -> Fast Charge (constant current) ->
|
||||
// Fast Charge (constant voltage) -> Maintain Charge -> Maintain Charge Done
|
||||
//
|
||||
// The Pre Charge and Charge Termination currents are programmed via I2C
|
||||
// The Fast Charge current is determined by Rset
|
||||
//
|
||||
// There doesn't seem to be a way to change the current in constant current mode
|
||||
void battery_set_fast_charge(bool fast_charge_enabled) {
|
||||
return;
|
||||
}
|
245
src/fw/drivers/battery/battery_tintin.c
Normal file
245
src/fw/drivers/battery/battery_tintin.c
Normal file
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "drivers/battery.h"
|
||||
|
||||
#include "drivers/exti.h"
|
||||
#include "drivers/gpio.h"
|
||||
#include "board/board.h"
|
||||
#include "drivers/otp.h"
|
||||
#include "drivers/periph_config.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
#define STM32F2_COMPATIBLE
|
||||
#include <mcu.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "kernel/events.h"
|
||||
#include "services/common/system_task.h"
|
||||
#include "services/common/new_timer/new_timer.h"
|
||||
#include "os/tick.h"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "timers.h"
|
||||
|
||||
|
||||
static const uint32_t USB_CONN_DEBOUNCE_MS = 400;
|
||||
static TimerID s_debounce_timer_handle = TIMER_INVALID_ID;
|
||||
static bool s_debounced_is_usb_connected = false;
|
||||
|
||||
static bool battery_is_usb_connected_raw(void);
|
||||
|
||||
static void battery_vusb_interrupt_handler(bool *should_context_switch);
|
||||
|
||||
static void battery_conn_debounce_callback(void* data) {
|
||||
s_debounced_is_usb_connected = battery_is_usb_connected_raw();
|
||||
|
||||
if (!s_debounced_is_usb_connected) {
|
||||
// disconnection event
|
||||
// - put the watch charger into a sane state
|
||||
// - disable fast-charge and re-enable the charger
|
||||
battery_set_charge_enable(true);
|
||||
battery_set_fast_charge(false);
|
||||
}
|
||||
|
||||
PebbleEvent event = {
|
||||
.type = PEBBLE_BATTERY_CONNECTION_EVENT,
|
||||
.battery_connection = {
|
||||
.is_connected = s_debounced_is_usb_connected,
|
||||
}
|
||||
};
|
||||
|
||||
event_put(&event);
|
||||
}
|
||||
|
||||
static bool board_has_chg_fast() {
|
||||
return BOARD_CONFIG_POWER.chg_fast.gpio != 0;
|
||||
}
|
||||
|
||||
static bool board_has_chg_en() {
|
||||
return BOARD_CONFIG_POWER.chg_en.gpio != 0;
|
||||
}
|
||||
|
||||
// These are the guts of battery_set_charge_enable(), called when we already have periph_config_acquire_lock
|
||||
static void prv_battery_set_charge_enable(bool charging_enabled) {
|
||||
if (board_has_chg_en()) {
|
||||
gpio_use(BOARD_CONFIG_POWER.chg_en.gpio);
|
||||
|
||||
GPIO_WriteBit(BOARD_CONFIG_POWER.chg_en.gpio, BOARD_CONFIG_POWER.chg_en.gpio_pin, charging_enabled?Bit_SET:Bit_RESET);
|
||||
|
||||
gpio_release(BOARD_CONFIG_POWER.chg_en.gpio);
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Charging:%s", charging_enabled?"enabled":"disabled");
|
||||
}
|
||||
}
|
||||
|
||||
// These are the guts of battery_set_fast_charge(), called when we already have periph_config_acquire_lock
|
||||
static void prv_battery_set_fast_charge(bool fast_charge_enabled) {
|
||||
if (board_has_chg_fast()) {
|
||||
gpio_use(BOARD_CONFIG_POWER.chg_fast.gpio);
|
||||
|
||||
GPIO_WriteBit(BOARD_CONFIG_POWER.chg_fast.gpio, BOARD_CONFIG_POWER.chg_fast.gpio_pin, fast_charge_enabled?Bit_RESET:Bit_SET);
|
||||
|
||||
gpio_release(BOARD_CONFIG_POWER.chg_fast.gpio);
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Fastcharge %s", fast_charge_enabled?"enabled":"disabled");
|
||||
}
|
||||
}
|
||||
|
||||
void battery_init(void) {
|
||||
s_debounce_timer_handle = new_timer_create();
|
||||
|
||||
periph_config_acquire_lock();
|
||||
gpio_use(BOARD_CONFIG_POWER.vusb_stat.gpio);
|
||||
gpio_use(BOARD_CONFIG_POWER.chg_stat.gpio);
|
||||
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
GPIO_StructInit(&GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
|
||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
|
||||
GPIO_InitStructure.GPIO_Pin = BOARD_CONFIG_POWER.vusb_stat.gpio_pin;
|
||||
GPIO_Init(BOARD_CONFIG_POWER.vusb_stat.gpio, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = BOARD_CONFIG_POWER.chg_stat.gpio_pin;
|
||||
GPIO_Init(BOARD_CONFIG_POWER.chg_stat.gpio, &GPIO_InitStructure);
|
||||
|
||||
if (board_has_chg_fast() || board_has_chg_en()) {
|
||||
// Initialize PD2 as the sensor enable
|
||||
GPIO_StructInit(&GPIO_InitStructure);
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
|
||||
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
|
||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
||||
|
||||
if (board_has_chg_fast()) {
|
||||
gpio_use(BOARD_CONFIG_POWER.chg_fast.gpio);
|
||||
GPIO_InitStructure.GPIO_Pin = BOARD_CONFIG_POWER.chg_fast.gpio_pin;
|
||||
GPIO_Init(BOARD_CONFIG_POWER.chg_fast.gpio, &GPIO_InitStructure);
|
||||
prv_battery_set_fast_charge(false);
|
||||
gpio_release(BOARD_CONFIG_POWER.chg_fast.gpio);
|
||||
}
|
||||
|
||||
if (board_has_chg_en()) {
|
||||
gpio_use(BOARD_CONFIG_POWER.chg_en.gpio);
|
||||
GPIO_InitStructure.GPIO_Pin = BOARD_CONFIG_POWER.chg_en.gpio_pin;
|
||||
GPIO_Init(BOARD_CONFIG_POWER.chg_en.gpio, &GPIO_InitStructure);
|
||||
prv_battery_set_charge_enable(true);
|
||||
gpio_release(BOARD_CONFIG_POWER.chg_en.gpio);
|
||||
}
|
||||
}
|
||||
|
||||
if (BOARD_CONFIG_POWER.has_vusb_interrupt) {
|
||||
periph_config_release_lock();
|
||||
|
||||
exti_configure_pin(BOARD_CONFIG_POWER.vusb_exti, ExtiTrigger_RisingFalling,
|
||||
battery_vusb_interrupt_handler);
|
||||
exti_enable(BOARD_CONFIG_POWER.vusb_exti);
|
||||
|
||||
periph_config_acquire_lock();
|
||||
} else {
|
||||
// TODO: Start polling vusb_stat
|
||||
}
|
||||
|
||||
gpio_release(BOARD_CONFIG_POWER.vusb_stat.gpio);
|
||||
gpio_release(BOARD_CONFIG_POWER.chg_stat.gpio);
|
||||
|
||||
periph_config_release_lock();
|
||||
|
||||
if (BOARD_CONFIG_POWER.has_vusb_interrupt) {
|
||||
// Prime the debounced state.
|
||||
s_debounced_is_usb_connected = battery_is_usb_connected_raw();
|
||||
}
|
||||
}
|
||||
|
||||
bool battery_charge_controller_thinks_we_are_charging_impl(void) {
|
||||
periph_config_acquire_lock();
|
||||
gpio_use(BOARD_CONFIG_POWER.chg_stat.gpio);
|
||||
bool state = !GPIO_ReadInputDataBit(BOARD_CONFIG_POWER.chg_stat.gpio, BOARD_CONFIG_POWER.chg_stat.gpio_pin);
|
||||
gpio_release(BOARD_CONFIG_POWER.chg_stat.gpio);
|
||||
periph_config_release_lock();
|
||||
return state;
|
||||
}
|
||||
|
||||
static bool battery_is_usb_connected_raw(void) {
|
||||
periph_config_acquire_lock();
|
||||
gpio_use(BOARD_CONFIG_POWER.vusb_stat.gpio);
|
||||
bool state = !GPIO_ReadInputDataBit(BOARD_CONFIG_POWER.vusb_stat.gpio, BOARD_CONFIG_POWER.vusb_stat.gpio_pin);
|
||||
gpio_release(BOARD_CONFIG_POWER.vusb_stat.gpio);
|
||||
periph_config_release_lock();
|
||||
return state;
|
||||
}
|
||||
|
||||
bool battery_is_usb_connected_impl(void) {
|
||||
if (BOARD_CONFIG_POWER.has_vusb_interrupt) {
|
||||
return s_debounced_is_usb_connected;
|
||||
} else {
|
||||
return battery_is_usb_connected_raw();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This callback gets installed by DBG_SERIAL_FREERTOS_IRQHandler() using system_task_add_callback_from_isr().
|
||||
// It is used to start up our timer since doing so from an ISR is not allowed.
|
||||
static void prv_start_timer_sys_task_callback(void* data) {
|
||||
new_timer_start(s_debounce_timer_handle, USB_CONN_DEBOUNCE_MS, battery_conn_debounce_callback, NULL, 0 /*flags*/);
|
||||
}
|
||||
|
||||
static void battery_vusb_interrupt_handler(bool *should_context_switch) {
|
||||
// Start the timer from a system task callback - not allowed to do so from an ISR
|
||||
system_task_add_callback_from_isr(prv_start_timer_sys_task_callback, NULL, should_context_switch);
|
||||
}
|
||||
|
||||
int battery_get_millivolts(void) {
|
||||
ADCVoltageMonitorReading info = battery_read_voltage_monitor();
|
||||
|
||||
// Apologies for the madness numbers.
|
||||
// The previous implementation had some approximations in it. The battery voltage is scaled
|
||||
// down by a pair of resistors (750k at the top, 470k at the bottom), resulting in a required
|
||||
// scaling of (75 + 47) / 47 or roughly 2.56x, but the previous implementation also required
|
||||
// fudging the numbers a bit in order to approximate for leakage current (a 73/64 multiple
|
||||
// was arbitrarily increased to 295/256). In order to match this previous arbitrary scaling
|
||||
// I've chosen new numbers that provide 2.62x scaling, which is the previous 2.56x with the
|
||||
// same amount of fudging applied.
|
||||
|
||||
return battery_convert_reading_to_millivolts(info, 3599, 1373);
|
||||
}
|
||||
|
||||
extern void command_sim_battery_connection(const char *bool_str) {
|
||||
bool value = atoi(bool_str);
|
||||
|
||||
PebbleEvent event = {
|
||||
.type = PEBBLE_BATTERY_CONNECTION_EVENT,
|
||||
.battery_connection = {
|
||||
.is_connected = value,
|
||||
}
|
||||
};
|
||||
event_put(&event);
|
||||
}
|
||||
|
||||
void battery_set_charge_enable(bool charging_enabled) {
|
||||
periph_config_acquire_lock();
|
||||
prv_battery_set_charge_enable(charging_enabled);
|
||||
periph_config_release_lock();
|
||||
}
|
||||
|
||||
void battery_set_fast_charge(bool fast_charge_enabled) {
|
||||
periph_config_acquire_lock();
|
||||
prv_battery_set_fast_charge(fast_charge_enabled);
|
||||
periph_config_release_lock();
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue