diff --git a/drivers/include/ltc4150.h b/drivers/include/ltc4150.h index e51962524bf17588229b9b229a27b67713e5cc66..53ba6a4952a833c345018557cc43809caeb876c1 100644 --- a/drivers/include/ltc4150.h +++ b/drivers/include/ltc4150.h @@ -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 @@ -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 * @@ -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 diff --git a/drivers/ltc4150/ltc4150.c b/drivers/ltc4150/ltc4150.c index a702aaef6f8e62b44e2d1c9101ae3fc0f342d10d..ba9a44bb5b939d85a23ac6eac3f327b9d6f5f24f 100644 --- a/drivers/ltc4150/ltc4150.c +++ b/drivers/ltc4150/ltc4150.c @@ -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; @@ -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; } diff --git a/drivers/ltc4150/ltc4150_last_minute.c b/drivers/ltc4150/ltc4150_last_minute.c new file mode 100644 index 0000000000000000000000000000000000000000..2fbf1b77903b4006f6e4dd3fb53e5ce0ee38769b --- /dev/null +++ b/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; +}