Add a new top-level for general utilities, like utility functions, linear range API, time utils, etc. This category should be used by stuff that is not strictly bound to the OS, like are OS services. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
342 lines
8.7 KiB
C
342 lines
8.7 KiB
C
/*
|
|
* Copyright (C) 2022, Nordic Semiconductor ASA
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#ifndef INCLUDE_ZEPHYR_SYS_LINEAR_RANGE_H_
|
|
#define INCLUDE_ZEPHYR_SYS_LINEAR_RANGE_H_
|
|
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <zephyr/sys/util.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/**
|
|
* @defgroup linear_range Linear Range
|
|
* @ingroup utilities
|
|
*
|
|
* The linear range API maps values in a linear range to a range index. A linear
|
|
* range can be fully defined by four parameters:
|
|
*
|
|
* - Minimum value
|
|
* - Step value
|
|
* - Minimum index value
|
|
* - Maximum index value
|
|
*
|
|
* For example, in a voltage regulator, supported voltages typically map to a
|
|
* register index value like this:
|
|
*
|
|
* - 1000uV: 0x00
|
|
* - 1250uV: 0x01
|
|
* - 1500uV: 0x02
|
|
* - ...
|
|
* - 3000uV: 0x08
|
|
*
|
|
* In this case, we have:
|
|
*
|
|
* - Minimum value: 1000uV
|
|
* - Step value: 250uV
|
|
* - Minimum index value: 0x00
|
|
* - Maximum index value: 0x08
|
|
*
|
|
* A linear range may also be constant, that is, step set to zero.
|
|
*
|
|
* It is often the case where the same device has discontinuous linear ranges.
|
|
* The API offers utility functions to deal with groups of linear ranges as
|
|
* well.
|
|
*
|
|
* Implementation uses fixed-width integers. Range is limited to [INT32_MIN,
|
|
* INT32_MAX], while number of indices is limited to UINT16_MAX.
|
|
*
|
|
* Original idea borrowed from Linux.
|
|
* @{
|
|
*/
|
|
|
|
/** @brief Linear range. */
|
|
struct linear_range {
|
|
/** Minimum value. */
|
|
int32_t min;
|
|
/** Step value. */
|
|
uint32_t step;
|
|
/** Minimum index (must be <= maximum index). */
|
|
uint16_t min_idx;
|
|
/** Maximum index (must be >= minimum index). */
|
|
uint16_t max_idx;
|
|
};
|
|
|
|
/**
|
|
* @brief Initializer for @ref linear_range.
|
|
*
|
|
* @param _min Minimum value in range.
|
|
* @param _step Step value.
|
|
* @param _min_idx Minimum index.
|
|
* @param _max_idx Maximum index.
|
|
*/
|
|
#define LINEAR_RANGE_INIT(_min, _step, _min_idx, _max_idx) \
|
|
{ \
|
|
.min = (_min), \
|
|
.step = (_step), \
|
|
.min_idx = (_min_idx), \
|
|
.max_idx = (_max_idx), \
|
|
}
|
|
|
|
/**
|
|
* @brief Obtain the number of values representable in a linear range.
|
|
*
|
|
* @param[in] r Linear range instance.
|
|
*
|
|
* @return Number of ranges representable by @p r.
|
|
*/
|
|
static inline uint32_t linear_range_values_count(const struct linear_range *r)
|
|
{
|
|
return r->max_idx - r->min_idx + 1U;
|
|
}
|
|
|
|
/**
|
|
* @brief Obtain the number of values representable by a group of linear ranges.
|
|
*
|
|
* @param[in] r Array of linear range instances.
|
|
* @param r_cnt Number of linear range instances.
|
|
*
|
|
* @return Number of ranges representable by the @p r group.
|
|
*/
|
|
static inline uint32_t linear_range_group_values_count(
|
|
const struct linear_range *r, size_t r_cnt)
|
|
{
|
|
uint32_t values = 0U;
|
|
|
|
for (size_t i = 0U; i < r_cnt; i++) {
|
|
values += linear_range_values_count(&r[i]);
|
|
}
|
|
|
|
return values;
|
|
}
|
|
|
|
/**
|
|
* @brief Obtain the maximum value representable by a linear range.
|
|
*
|
|
* @param[in] r Linear range instance.
|
|
*
|
|
* @return Maximum value representable by @p r.
|
|
*/
|
|
static inline int32_t linear_range_get_max_value(const struct linear_range *r)
|
|
{
|
|
return r->min + (int32_t)(r->step * (r->max_idx - r->min_idx));
|
|
}
|
|
|
|
/**
|
|
* @brief Obtain value given a linear range index.
|
|
*
|
|
* @param[in] r Linear range instance.
|
|
* @param idx Range index.
|
|
* @param[out] val Where value will be stored.
|
|
*
|
|
* @retval 0 If successful
|
|
* @retval -EINVAL If index is out of range.
|
|
*/
|
|
static inline int linear_range_get_value(const struct linear_range *r,
|
|
uint16_t idx, int32_t *val)
|
|
{
|
|
if ((idx < r->min_idx) || (idx > r->max_idx)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
*val = r->min + (int32_t)(r->step * (idx - r->min_idx));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Obtain value in a group given a linear range index.
|
|
*
|
|
* @param[in] r Array of linear range instances.
|
|
* @param r_cnt Number of linear range instances.
|
|
* @param idx Range index.
|
|
* @param[out] val Where value will be stored.
|
|
*
|
|
* @retval 0 If successful
|
|
* @retval -EINVAL If index is out of range.
|
|
*/
|
|
static inline int linear_range_group_get_value(const struct linear_range *r,
|
|
size_t r_cnt, uint16_t idx,
|
|
int32_t *val)
|
|
{
|
|
int ret = -EINVAL;
|
|
|
|
for (size_t i = 0U; (ret != 0) && (i < r_cnt); i++) {
|
|
ret = linear_range_get_value(&r[i], idx, val);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Obtain index given a value.
|
|
*
|
|
* If the value falls outside the range, the nearest index will be stored and
|
|
* -ERANGE returned. That is, if the value falls below or above the range, the
|
|
* index will take the minimum or maximum value, respectively. For constant
|
|
* ranges, the minimum index will be returned.
|
|
*
|
|
* @param[in] r Linear range instance.
|
|
* @param val Value.
|
|
* @param[out] idx Where index will be stored.
|
|
*
|
|
* @retval 0 If value falls within the range.
|
|
* @retval -ERANGE If the value falls out of the range.
|
|
*/
|
|
static inline int linear_range_get_index(const struct linear_range *r,
|
|
int32_t val, uint16_t *idx)
|
|
{
|
|
if (val < r->min) {
|
|
*idx = r->min_idx;
|
|
return -ERANGE;
|
|
}
|
|
|
|
if (val > linear_range_get_max_value(r)) {
|
|
*idx = r->max_idx;
|
|
return -ERANGE;
|
|
}
|
|
|
|
if (r->step == 0U) {
|
|
*idx = r->min_idx;
|
|
} else {
|
|
*idx = r->min_idx + DIV_ROUND_UP((uint32_t)(val - r->min),
|
|
r->step);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Obtain index in a group given a value.
|
|
*
|
|
* This function works the same way as linear_range_get_index(), but considering
|
|
* all ranges in the group.
|
|
*
|
|
* @param[in] r Linear range instances.
|
|
* @param r_cnt Number of linear range instances.
|
|
* @param val Value.
|
|
* @param[out] idx Where index will be stored.
|
|
*
|
|
* @retval 0 If value falls within the range group.
|
|
* @retval -ERANGE If the value falls out of the range group.
|
|
* @retval -EINVAL If input is not valid (i.e. zero groups).
|
|
*/
|
|
static inline int linear_range_group_get_index(const struct linear_range *r,
|
|
size_t r_cnt, int32_t val,
|
|
uint16_t *idx)
|
|
{
|
|
for (size_t i = 0U; i < r_cnt; i++) {
|
|
if ((val > linear_range_get_max_value(&r[i])) &&
|
|
(i < (r_cnt - 1U))) {
|
|
continue;
|
|
}
|
|
|
|
return linear_range_get_index(&r[i], val, idx);
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
/**
|
|
* @brief Obtain index given a window of values.
|
|
*
|
|
* If the window of values does not intersect with the range, -EINVAL will be
|
|
* returned. If intersection is partial (any of the window egdes does not
|
|
* intersect), the nearest index will be stored and -ERANGE returned.
|
|
*
|
|
* @param[in] r Linear range instance.
|
|
* @param val_min Minimum window value.
|
|
* @param val_max Maximum window value.
|
|
* @param[out] idx Where index will be stored.
|
|
*
|
|
* @retval 0 If a valid index is found within linear range.
|
|
* @retval -ERANGE If the given window of values falls partially out of the
|
|
* linear range.
|
|
* @retval -EINVAL If the given window of values does not intersect with the
|
|
* linear range or if they are too narrow.
|
|
*/
|
|
static inline int linear_range_get_win_index(const struct linear_range *r,
|
|
int32_t val_min, int32_t val_max,
|
|
uint16_t *idx)
|
|
{
|
|
int32_t r_max = linear_range_get_max_value(r);
|
|
|
|
if ((val_max < r->min) || (val_min > r_max)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (val_min < r->min) {
|
|
*idx = r->min_idx;
|
|
return -ERANGE;
|
|
}
|
|
|
|
if (val_max > r_max) {
|
|
*idx = r->max_idx;
|
|
return -ERANGE;
|
|
}
|
|
|
|
if (r->step == 0U) {
|
|
*idx = r->min_idx;
|
|
return 0;
|
|
}
|
|
|
|
*idx = r->min_idx + DIV_ROUND_UP((uint32_t)(val_min - r->min), r->step);
|
|
if ((r->min + r->step * (*idx - r->min_idx)) > val_max) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Obtain index in a group given a value that must be within a window of
|
|
* values.
|
|
*
|
|
* This function works the same way as linear_range_get_win_index(), but
|
|
* considering all ranges in the group.
|
|
*
|
|
* @param[in] r Linear range instances.
|
|
* @param r_cnt Number of linear range instances.
|
|
* @param val_min Minimum window value.
|
|
* @param val_max Maximum window value.
|
|
* @param[out] idx Where index will be stored.
|
|
*
|
|
* @retval 0 If a valid index is found within linear range group.
|
|
* @retval -ERANGE If the given window of values falls partially out of the
|
|
* linear range group.
|
|
* @retval -EINVAL If the given window of values does not intersect with the
|
|
* linear range group, if they are too narrow, or if input is invalid (i.e.
|
|
* zero groups).
|
|
*/
|
|
static inline int linear_range_group_get_win_index(const struct linear_range *r,
|
|
size_t r_cnt,
|
|
int32_t val_min,
|
|
int32_t val_max,
|
|
uint16_t *idx)
|
|
{
|
|
for (size_t i = 0U; i < r_cnt; i++) {
|
|
if (val_min > linear_range_get_max_value(&r[i])) {
|
|
continue;
|
|
}
|
|
|
|
return linear_range_get_win_index(&r[i], val_min, val_max, idx);
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* INCLUDE_ZEPHYR_SYS_LINEAR_RANGE_H_ */
|