From 46875049363ff26540ab0a9f7a1bbb2349b18f58 Mon Sep 17 00:00:00 2001 From: chrysn Date: Wed, 30 Sep 2020 23:55:29 +0200 Subject: [PATCH] drivers/saul/auto_init: Support bool and percent for dimmers Booleans make sense for dimmers when accessed from a Sith application that deals in absolutes. Percent values are also a widespread interpretation of brightness levels, and are thus supported as well. The bit arithmetic makes sure that the arithmetic operation value / 100 * saul_pwm_resolution is done efficiently (by expression as a multiplication followed by shifting) and accurately (by maximizing the number of usable bits) while still being flexible both with respect to integer sizes and to changes of saul_pwm_resolution. Co-authored-by: Marian Buschsieweke --- drivers/saul/pwm_saul.c | 54 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/drivers/saul/pwm_saul.c b/drivers/saul/pwm_saul.c index ca2db36859..0a84425e45 100644 --- a/drivers/saul/pwm_saul.c +++ b/drivers/saul/pwm_saul.c @@ -22,6 +22,42 @@ #include "phydat.h" #include "periph/pwm.h" #include "saul/periph.h" +#include "bitarithm.h" + +/** + * Find factor and shiftback such that for each value entry in the phydat, the + * resulting PWM duty cycle would be (value * factor) >> shiftback. + */ +static int extract_scaling(const phydat_t *state, int *factor, int *shiftback) +{ + if (state->scale != 0) { + return -ECANCELED; + } + + /** Number of bits i by which we can shift the calculation (value * (255 << i)/100) >> i + * to get a better result than value * 2 (which would otherwise happen in integers) */ + int shift100 = bitarithm_msb(INT_MAX) - bitarithm_msb(saul_pwm_resolution); + + switch (state->unit) { + case UNIT_UNDEF: + case UNIT_NONE: + *factor = 1; + *shiftback = 0; + break; + case UNIT_BOOL: + *factor = saul_pwm_resolution; + *shiftback = 0; + break; + case UNIT_PERCENT: + *factor = ((int)saul_pwm_resolution << shift100) / 100; + *shiftback = shift100; + break; + default: + return -ECANCELED; + } + + return 0; +} static inline void setchan(const saul_pwm_channel_t *chan, uint16_t value) { @@ -34,7 +70,14 @@ static int write_dimmer(const void *dev, phydat_t *state) { const saul_pwm_dimmer_params_t *p = dev; - setchan(&p->channel, state->val[0]); + int factor, shiftback; + + int err = extract_scaling(state, &factor, &shiftback); + if (err < 0) { + return err; + } + + setchan(&p->channel, (state->val[0] * factor) >> shiftback); return 3; } @@ -48,8 +91,15 @@ static int write_rgb(const void *dev, phydat_t *state) { const saul_pwm_rgb_params_t *p = dev; + int factor, shiftback; + + int err = extract_scaling(state, &factor, &shiftback); + if (err < 0) { + return err; + } + for (int i = 0; i < 3; ++i) { - setchan(&p->channels[i], state->val[i]); + setchan(&p->channels[i], (state->val[i] * factor) >> shiftback); } return 3; }