Skip to content

Commit

Permalink
drivers/ltc4150: Allow tracking last minute charge
Browse files Browse the repository at this point in the history
Implemented an example `ltc4150_recorder_t` implementation as a proof of concept
for the recorder API.
  • Loading branch information
maribu committed Jan 28, 2019
1 parent fa0d08a commit db0c66e
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 16 deletions.
67 changes: 66 additions & 1 deletion drivers/include/ltc4150.h
Expand Up @@ -185,7 +185,7 @@ typedef struct {
* @brief `NULL` or a `NULL`-terminated array of data recorders
* @pre If not `NULL`, the last element of the array must be `NULL`
*/
ltc4150_recorder_t **recorders;
const ltc4150_recorder_t **recorders;
/**
* @brief `NULL` or an array of the user defined data for each recorder
* @pre If @see ltc4150_params_t::recorders is not `NULL`, this must point
Expand All @@ -207,6 +207,35 @@ struct ltc4150_dev {
uint32_t discharged; /**< # of pulses for discharging (POL=low) */
};

/**
* @brief Data structure used by @ref ltc4150_last_minute
*/
typedef struct {
uint32_t last_rotate_sec; /**< Time stamp of the last ring "rotation" */
/**
* @brief Pulses in charging direction recorded in the last minute
*/
uint16_t charged;
/**
* @brief Pulses in discharging direction recorded in the last minute
*/
uint16_t discharged;
/**
* @brief Ring-buffer to store charge information in 10 sec resolution
*/
uint8_t buf_charged[7];
/**
* @brief As above, but in discharging direction
*/
uint8_t buf_discharged[7];
uint8_t ring_pos; /**< Position in the ring buffer */
} ltc4150_last_minute_data_t;

/**
* @brief Records the charge transferred within the last minute using
*/
extern const ltc4150_recorder_t ltc4150_last_minute;

/**
* @brief Initialize the LTC4150 driver
*
Expand Down Expand Up @@ -275,6 +304,42 @@ int ltc4150_charge(ltc4150_dev_t *dev, uint32_t *charged, uint32_t *discharged);
*/
int ltc4150_avg_current(ltc4150_dev_t *dev, int16_t *dest);

/**
* @brief Get the measured charge in the last minute
*
* @param dev The LTC4150 device to read data from
* @param data The data recorded by @ref ltc4150_last_minute
* @param[out] charged The charge transferred in charging direction
* @param[out] discharged The charge transferred in discharging direction
*
* @retval 0 Success
* @retval -EINVAL Called with an invalid argument
*
* @warning The returned data may be outdated up to ten seconds
*
* Passing `NULL` for `charged` or `discharged` is allowed, if only one
* information is of interest.
*/
int ltc4150_last_minute_charge(ltc4150_dev_t *dev,
ltc4150_last_minute_data_t *data,
uint32_t *charged, uint32_t *discharged);

/**
* @brief Convert the raw data (# pulses) acquired by the LTC4150 device to
* charge information in millicoulomb
* @note This function will make writing data recorders (see
* @ref ltc4150_recorder_t) easier, but is not intended for end users
*
* @param dev LTC4150 device the data was received from
* @param[out] charged Charge in charging direction is stored here
* @param[out] discharged Charge in discharging direction is stored here
* @param[in] raw_charged Number of pulses in charging direction
* @param[in] raw_discharged Number of pulses in discharging direction
*/
void ltc4150_pulses2c(const ltc4150_dev_t *dev,
uint32_t *charged, uint32_t *discharged,
uint32_t raw_charged,
uint32_t raw_discharged);
#ifdef __cplusplus
}
#endif
Expand Down
20 changes: 5 additions & 15 deletions drivers/ltc4150/ltc4150.c
Expand Up @@ -139,20 +139,10 @@ int ltc4150_shutdown(ltc4150_dev_t *dev)
return 0;
}

/**
* @brief Convert the raw data (# pulses) acquired by the LTC4150 device to
* charge information in millicoulomb
*
* @param dev LTC4150 device the data was received from
* @param[out] charged Charge in charging direction is stored here
* @param[out] discharged Charge in discharging direction is stored here
* @param[in] raw_charged Number of pulses in charging direction
* @param[in] raw_discharged Number of pulses in discharging direction
*/
static void get_coulomb(const ltc4150_dev_t *dev,
uint32_t *charged, uint32_t *discharged,
uint32_t raw_charged,
uint32_t raw_discharged)
void ltc4150_pulses2c(const ltc4150_dev_t *dev,
uint32_t *charged, uint32_t *discharged,
uint32_t raw_charged,
uint32_t raw_discharged)
{
uint64_t tmp;

Expand Down Expand Up @@ -180,7 +170,7 @@ int ltc4150_charge(ltc4150_dev_t *dev, uint32_t *charged, uint32_t *discharged)
}

gpio_irq_disable(dev->params.interrupt);
get_coulomb(dev, charged, discharged, dev->charged, dev->discharged);
ltc4150_pulses2c(dev, charged, discharged, dev->charged, dev->discharged);
gpio_irq_enable(dev->params.interrupt);
return 0;
}
Expand Down
95 changes: 95 additions & 0 deletions drivers/ltc4150/ltc4150_last_minute.c
@@ -0,0 +1,95 @@
/*
* Copyright 2019 Otto-von-Guericke-Universität Magdeburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @ingroup drivers_ltc4150
* @{
*
* @file
* @brief Track the drawn charged of the last minute
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* @}
*/

#include <errno.h>
#include <string.h>

#include "ltc4150.h"
#include "xtimer.h"

static void init_or_reset(ltc4150_dev_t *dev, uint64_t now_usec, void *arg);
static void pulse(ltc4150_dev_t *dev, ltc4150_dir_t dir, uint64_t now_usec,
void *arg);

const ltc4150_recorder_t ltc4150_last_minute = {
.reset = init_or_reset,
.pulse = pulse,
};

static void init_or_reset(ltc4150_dev_t *dev, uint64_t now_usec, void *arg)
{
(void)dev;
ltc4150_last_minute_data_t *data = arg;

memset(data, 0, sizeof(ltc4150_last_minute_data_t));
data->last_rotate_sec = now_usec / US_PER_SEC;
}

static void update_ringbuffer(ltc4150_last_minute_data_t *data,
uint64_t now_usec)
{
uint32_t now_sec = (now_usec / US_PER_SEC);

/* Note: This expression should be correct even when time overflows */
while (now_sec - data->last_rotate_sec > 10) {
data->last_rotate_sec += 10;
data->charged += data->buf_charged[data->ring_pos];
data->discharged += data->buf_discharged[data->ring_pos];
if (++data->ring_pos >= sizeof(data->buf_charged)/sizeof(data->buf_charged[0])) {
data->ring_pos = 0;
}
data->charged -= data->buf_charged[data->ring_pos];
data->discharged -= data->buf_discharged[data->ring_pos];
data->buf_charged[data->ring_pos] = 0;
data->buf_discharged[data->ring_pos] = 0;
}
}

static void pulse(ltc4150_dev_t *dev, ltc4150_dir_t dir, uint64_t now_usec,
void *arg)
{
(void)dev;
ltc4150_last_minute_data_t *data = arg;
update_ringbuffer(data, now_usec);

switch (dir) {
case LTC4150_CHARGE:
data->buf_charged[data->ring_pos]++;
break;
default:
case LTC4150_DISCHARGE:
data->buf_discharged[data->ring_pos]++;
break;
}
}

int ltc4150_last_minute_charge(ltc4150_dev_t *dev,
ltc4150_last_minute_data_t *d,
uint32_t *charged, uint32_t *discharged)
{
if (!dev || !d) {
return -EINVAL;
}

gpio_irq_disable(dev->params.interrupt);
update_ringbuffer(d, xtimer_now_usec64());
ltc4150_pulses2c(dev, charged, discharged, d->charged, d->discharged);
gpio_irq_enable(dev->params.interrupt);

return 0;
}

0 comments on commit db0c66e

Please sign in to comment.