Implemented the logic to detect and read the charge controller status pins using two different policies :

- the first one (also used right now) is using interrupts on the two lines
- the second one was doing some polling using a timer interrupt to check the controller status
This commit is contained in:
anschrammh 2023-03-26 23:17:20 +02:00
parent 9f574f0486
commit 214e174ffc
2 changed files with 167 additions and 22 deletions

View File

@ -6,19 +6,107 @@
#include "wm_timer.h"
#include "wm_pwm.h"
#define INTERRUPT_POLICY (0)
#define POLL_POLICY (1)
#define BATTERY_CONTROLLER_STATUS_DETECTION_POLICY INTERRUPT_POLICY
/* Battery voltage and ADC */
static int8_t _adc_offset = 0;
/* Vibration motor timer */
static uint8_t _vibration_motor_timer_id = WM_TIMER_ID_INVALID;
/* Battery charge controller status */
static battery_controller_status_e _battery_fsm = BATTERY_CONTROLLER_STATUS_DISCHARGING;
static BatteryControllerStatusChangeCb_t _BatteryControllerStatusChangeCb = NULL;
static void vibration_motor_timer_irq_cb(void *p)
{
(void)p;
tls_pwm_stop(VIBRATION_MOTOR_PWM_CHANNEL);
tls_gpio_cfg(VIBRATION_MOTOR_ENABLE, WM_GPIO_DIR_OUTPUT, WM_GPIO_ATTR_FLOATING);
tls_gpio_write(VIBRATION_MOTOR_ENABLE, 0);
APP_LOG_DEBUG("Vibration stopped");
}
#if BATTERY_CONTROLLER_STATUS_DETECTION_POLICY == POLL_POLICY
static uint8_t _battery_status_timer_id = WM_TIMER_ID_INVALID;
static void battery_status_timer_irq_cb(void *p)
{
bool charging = tls_gpio_read(BATTERY_CONTROLLER_CHARGING_STATUS) == 0;
bool charged = tls_gpio_read(BATTERY_CONTROLLER_CHARGED_STATUS) == 0;
battery_controller_status_e old_battery_status = _battery_fsm;
if(charging && !charged)
_battery_fsm = BATTERY_CONTROLLER_STATUS_CHARGING;
else if(!charging && charged)
_battery_fsm = BATTERY_CONTROLLER_STATUS_CHARGED;
else if (!charging && !charged)
_battery_fsm = BATTERY_CONTROLLER_STATUS_DISCHARGING;
else
_battery_fsm = BATTERY_CONTROLLER_STATUS_ERROR;
if(old_battery_status != _battery_fsm)
{
// Let's call a user registered callback here
APP_LOG_INFO("Battery status changed from %s to %s",
battery_controller_status_2_str(old_battery_status),
battery_controller_status_2_str(_battery_fsm));
if(_BatteryControllerStatusChangeCb) _BatteryControllerStatusChangeCb(old_battery_status, _battery_fsm);
}
}
#elif BATTERY_CONTROLLER_STATUS_DETECTION_POLICY == INTERRUPT_POLICY
static void battery_controller_irq_cb(void *p)
{
enum tls_io_name gpio_pin = (enum tls_io_name)p;
if(BATTERY_CONTROLLER_CHARGING_STATUS == gpio_pin)
{
tls_clr_gpio_irq_status(BATTERY_CONTROLLER_CHARGING_STATUS);
}
if(BATTERY_CONTROLLER_CHARGED_STATUS == gpio_pin)
{
tls_clr_gpio_irq_status(BATTERY_CONTROLLER_CHARGED_STATUS);
}
bool charging = tls_gpio_read(BATTERY_CONTROLLER_CHARGING_STATUS) == 0;
bool charged = tls_gpio_read(BATTERY_CONTROLLER_CHARGED_STATUS) == 0;
battery_controller_status_e old_battery_status = _battery_fsm;
switch(old_battery_status)
{
case BATTERY_CONTROLLER_STATUS_CHARGING:
if(charged) _battery_fsm = BATTERY_CONTROLLER_STATUS_CHARGED;
else if(!charged && !charging) _battery_fsm = BATTERY_CONTROLLER_STATUS_DISCHARGING;
break;
case BATTERY_CONTROLLER_STATUS_CHARGED:
if(!charged && !charging) _battery_fsm = BATTERY_CONTROLLER_STATUS_DISCHARGING;
break;
case BATTERY_CONTROLLER_STATUS_DISCHARGING:
if(charging) _battery_fsm = BATTERY_CONTROLLER_STATUS_CHARGING;
break;
default:
_battery_fsm = BATTERY_CONTROLLER_STATUS_DISCHARGING;
break;
}
if(old_battery_status != _battery_fsm)
{
// Let's call a user registered callback here
APP_LOG_INFO("Battery status changed from %s to : %s, IRQ : %u",
battery_controller_status_2_str(old_battery_status),
battery_controller_status_2_str(_battery_fsm),
gpio_pin);
if(_BatteryControllerStatusChangeCb) _BatteryControllerStatusChangeCb(old_battery_status, _battery_fsm);
}
}
#endif
static void watch_peripherals_io_init(void)
{
/* We initialize the ADC input as well as the gpio used to enabled the voltage divider bridge */
@ -31,8 +119,34 @@ static void watch_peripherals_io_init(void)
tls_gpio_write(VIBRATION_MOTOR_ENABLE, 0);
/* We initialize the pins used to read the battery controller IC charging and charged statuses */
tls_gpio_cfg(BATTERY_CONTROLLER_CHARGING_STATUS, WM_GPIO_DIR_INPUT, WM_GPIO_ATTR_FLOATING);
tls_gpio_cfg(BATTERY_CONTROLLER_CHARGED_STATUS, WM_GPIO_DIR_INPUT, WM_GPIO_ATTR_FLOATING);
tls_gpio_cfg(BATTERY_CONTROLLER_CHARGING_STATUS, WM_GPIO_DIR_INPUT, WM_GPIO_ATTR_FLOATING);
#if BATTERY_CONTROLLER_STATUS_DETECTION_POLICY == INTERRUPT_POLICY
tls_gpio_isr_register(BATTERY_CONTROLLER_CHARGED_STATUS, &(battery_controller_irq_cb), (int*) BATTERY_CONTROLLER_CHARGED_STATUS);
tls_gpio_irq_enable(BATTERY_CONTROLLER_CHARGED_STATUS, WM_GPIO_IRQ_TRIG_DOUBLE_EDGE); // Enabled when level is changing edge
tls_gpio_isr_register(BATTERY_CONTROLLER_CHARGING_STATUS, &(battery_controller_irq_cb), (int*) BATTERY_CONTROLLER_CHARGING_STATUS);
tls_gpio_irq_enable(BATTERY_CONTROLLER_CHARGING_STATUS, WM_GPIO_IRQ_TRIG_DOUBLE_EDGE); // Enabled when level is changing edge
#endif
}
#ifndef CASE_RETURN_STR
#define CASE_RETURN_STR(const) case const: return #const;
#endif
const char *battery_controller_status_2_str(battery_controller_status_e status)
{
switch(status)
{
CASE_RETURN_STR(BATTERY_CONTROLLER_STATUS_CHARGING)
CASE_RETURN_STR(BATTERY_CONTROLLER_STATUS_DISCHARGING)
CASE_RETURN_STR(BATTERY_CONTROLLER_STATUS_CHARGED)
CASE_RETURN_STR(BATTERY_CONTROLLER_STATUS_ERROR)
default:
return "Unknown Battery Status";
}
}
void watch_peripherals_init(int8_t adcOffset)
@ -43,7 +157,7 @@ void watch_peripherals_init(int8_t adcOffset)
/* Prevent multiple init call !*/
if(WM_TIMER_ID_INVALID == _vibration_motor_timer_id)
{
/* Let's initialize the timer w'll use to stop the vibration motor */
/* Let's initialize the timer we'll use to stop the vibration motor */
struct tls_timer_cfg vibration_motor_timer_cfg =
{
.arg = NULL,
@ -55,8 +169,32 @@ void watch_peripherals_init(int8_t adcOffset)
_vibration_motor_timer_id = tls_timer_create(&vibration_motor_timer_cfg);
if(WM_TIMER_ID_INVALID == _vibration_motor_timer_id)
APP_LOG_ERROR("Failed to create timer");
APP_LOG_ERROR("Failed to create vibration motor timer");
}
#if BATTERY_CONTROLLER_STATUS_DETECTION_POLICY == POLL_POLICY
if(WM_TIMER_ID_INVALID == _battery_status_timer_id)
{
struct tls_timer_cfg battery_status_timer_cfg =
{
.arg = NULL,
.callback = &(battery_status_timer_irq_cb),
.unit = TLS_TIMER_UNIT_MS,
.is_repeat = true,
.timeout = 200
};
_battery_status_timer_id = tls_timer_create(&battery_status_timer_cfg);
if(WM_TIMER_ID_INVALID == _battery_status_timer_id)
APP_LOG_ERROR("Failed to create battery status timer");
else
tls_timer_start(_battery_status_timer_id);
}
#endif
}
void watch_peripherals_register_battery_controller_status_change_cb(BatteryControllerStatusChangeCb_t BatteryControllerStatusChangeCb)
{
_BatteryControllerStatusChangeCb = BatteryControllerStatusChangeCb;
}
uint16_t watch_peripherals_get_battery_voltage(battery_unit_e unit)
@ -100,22 +238,11 @@ uint8_t battery_voltage_to_percentage(uint16_t voltage_in_mV)
battery_controller_status_e watch_peripherals_get_battery_controller_status(void)
{
bool charging = tls_gpio_read(BATTERY_CONTROLLER_CHARGING_STATUS) == 0;
bool charged = tls_gpio_read(BATTERY_CONTROLLER_CHARGED_STATUS) == 0;
if(charging && !charged)
return BATTERY_CHARGING;
else if(!charging && charged)
return BATTERY_CHARGED;
else if (!charging && !charged)
return BATTERY_DISCHARGING;
else
return BATTERY_ERROR;
return _battery_fsm;
}
void watch_peripherals_vibrate(uint8_t strength, uint32_t durationMs)
{
APP_LOG_DEBUG("Vibration started");
/* No need to do anything if the duration is 0 or if the strength is 0 */
if(!strength || !durationMs) return;
/* We start the timer which will stop the vibration after durationMs time */

View File

@ -15,12 +15,22 @@ typedef enum battery_unit
typedef enum battery_controller_status
{
BATTERY_CHARGING = 0,
BATTERY_CHARGED,
BATTERY_DISCHARGING,
BATTERY_ERROR
BATTERY_CONTROLLER_STATUS_DISCHARGING = 0,
BATTERY_CONTROLLER_STATUS_CHARGING,
BATTERY_CONTROLLER_STATUS_CHARGED,
BATTERY_CONTROLLER_STATUS_ERROR
} battery_controller_status_e;
typedef void (*BatteryControllerStatusChangeCb_t)(battery_controller_status_e old, battery_controller_status_e new);
/**
* @brief Helper function to easily print the battery status enumeration name
*
* @param status the enumeration value for which to get the name
* @return const char* a string representing the enumeration value
*/
const char *battery_controller_status_2_str(battery_controller_status_e status);
/**
* @brief Inits the watch peripherals driver.
* This must be called before using the API.
@ -29,6 +39,14 @@ typedef enum battery_controller_status
*/
void watch_peripherals_init(int8_t adcOffset);
/**
* @brief Registers the user provided callback function to notify the battery controller status change event
* @note This function should be as short as possible, do not call LVGL functions in it.
*
* @param BatteryStatusChangeCb a pointer to the function having the right definition.
*/
void watch_peripherals_register_battery_controller_status_change_cb(BatteryControllerStatusChangeCb_t BatteryStatusChangeCb);
/**
* @brief Reads and returns the current battery voltage in mV or it's corresponding capacity in percentage.
* This function takes into account the adcOffset parameter provided in the @ref watch_peripherals_init
@ -50,9 +68,9 @@ uint8_t battery_voltage_to_percentage(uint16_t voltage_in_mV);
/**
* @brief Returns the current battery controller IC status.
* It can be one of the following
* It can be one of the following :
*
* @return battery_controller_status_e
* @return battery_controller_status_e BATTERY_CHARGING, BATTERY_CHARGED, BATTERY_DISCHARGING and BATTERY_ERROR.
*/
battery_controller_status_e watch_peripherals_get_battery_controller_status(void);