drivers/saul/auto_init: Add PWM for LEDs
In analogy to the existing GPIO mappings, this provides (write-only) SAUL entries for PWM'd LEDs in a single-LED (as SAUL_ACT_DIMMER) and an RGB (as SAUL_ACT_RGB_LED) mode. Co-authored-by: Marian Buschsieweke <marian.buschsieweke@ovgu.de>
This commit is contained in:
parent
bdf82eddc6
commit
d196c7c4a6
@ -49,7 +49,9 @@ configurations. e.g:
|
||||
specific pin connections to a LCD screen, radio, etc.). Some boards might also
|
||||
define optimized `XTIMER_%` values (e.g. @ref XTIMER_BACKOFF).
|
||||
- `gpio_params.h`: if the board supports @ref drivers_saul "SAUL" then its
|
||||
@ref saul_gpio_params_t is defined here.
|
||||
@ref saul_gpio_params_t is defined here. (Analogously, a `adc_params.h` can
|
||||
contain a @ref saul_adc_params_t, and `pwm_params.h` a @ref
|
||||
saul_pwm_rgb_params_t and a @ref saul_pwm_dimmer_params_t).
|
||||
- other: other specific headers needed by one `BOARD`
|
||||
|
||||
@note Header files do not need to be defined in `include/`, but if defined
|
||||
|
||||
@ -154,6 +154,10 @@ ifneq (,$(filter saul_gpio,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_gpio
|
||||
endif
|
||||
|
||||
ifneq (,$(filter saul_pwm,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_pwm
|
||||
endif
|
||||
|
||||
ifneq (,$(filter saul,$(USEMODULE)))
|
||||
USEMODULE += phydat
|
||||
endif
|
||||
|
||||
@ -27,6 +27,10 @@
|
||||
#include "periph/adc.h"
|
||||
#endif /* MODULE_SAUL_ADC */
|
||||
|
||||
#if MODULE_SAUL_PWM || DOXYGEN
|
||||
#include "periph/pwm.h"
|
||||
#endif /* MODULE_SAUL_PWM */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -63,6 +67,115 @@ typedef struct {
|
||||
} saul_adc_params_t;
|
||||
#endif /* MODULE_SAUL_ADC */
|
||||
|
||||
#if MODULE_SAUL_PWM || DOXYGEN
|
||||
/**
|
||||
* @brief Resolution of SAUL mapped PWMs
|
||||
*/
|
||||
static const uint16_t saul_pwm_resolution = 255;
|
||||
|
||||
/**
|
||||
* @brief SAUL PWM parameters
|
||||
*/
|
||||
typedef enum {
|
||||
SAUL_PWM_REGULAR = (0 << 0), /**< Physical values are proportional to
|
||||
average voltage levels (ie. LEDs are in
|
||||
active-high, anode driven) */
|
||||
SAUL_PWM_INVERTED = (1 << 0), /**< Physical values are inverted from
|
||||
average voltage levels (ie. LEDs are in
|
||||
active-low, cathode driven) */
|
||||
} saul_pwm_flags_t;
|
||||
|
||||
/**
|
||||
* @brief Single PWM channel exposed via SAUL
|
||||
*
|
||||
* This does never need to be declared on its own, but is used inisde @ref
|
||||
* saul_pwm_dimmer_params_t and @ref saul_pwm_rgb_params_t structs.
|
||||
*/
|
||||
typedef struct {
|
||||
pwm_t dev; /**< PWM device backing this entry */
|
||||
uint8_t channel; /**< Channel on the PWM device */
|
||||
saul_pwm_flags_t flags; /**< Configuration flags */
|
||||
} saul_pwm_channel_t;
|
||||
|
||||
/** @brief Default value for @ref SAUL_PWM_FREQ */
|
||||
#define SAUL_PWM_FREQ_DEFAULT 1000
|
||||
|
||||
/**
|
||||
* @brief Define the PWM frequency for LEDs
|
||||
*
|
||||
* This frequency is requested from the PWM driver. As the per @ref pwm_init,
|
||||
* the actual frequency may be lower, and the SAUL wrapper does not place a
|
||||
* limit there.
|
||||
*
|
||||
* Frequencies of above 200Hz usually give a smooth visual experience. The
|
||||
* higher 1kHz is picked as a default as some devices can't go that low with
|
||||
* their timer.
|
||||
*
|
||||
* This is typically set in the board's `pwm_params.h`.
|
||||
*/
|
||||
/* This is not applied here as it would later need to be undef'd; actual
|
||||
* application of the default happens in auto_init_saul_pwm.c */
|
||||
#if DOXYGEN
|
||||
#define SAUL_PWM_FREQ SAUL_PWM_FREQ_DEFAULT
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Suppress saul_pwm's dimmer generation
|
||||
*
|
||||
* This can be defined in `pwm_params.h` if the saul_pwm module is used, but no
|
||||
* dimmers (and only RGB LEDs) are in use. Then, no @ref saul_pwm_dimmer_params
|
||||
* needs to be set.
|
||||
*/
|
||||
#if DOXYGEN
|
||||
#define SAUL_PWM_NO_DIMMER
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWM channels mapped to dimmer-style registration entries
|
||||
*
|
||||
* This is used to define a `static const saul_pwm_dimmer_params_t
|
||||
* saul_pwm_dimer_params[]` in a board's `pwm_params.h` for use by the saul_pwm
|
||||
* module. If the module is used but only RGB LEDs are present, a @ref
|
||||
* SAUL_PWM_NO_DIMMER define can be set instead.
|
||||
*/
|
||||
typedef struct {
|
||||
const char *name; /**< Name of the device connected to this
|
||||
channel */
|
||||
saul_pwm_channel_t channel; /**< Full channel description (device, channel)
|
||||
along with flags that indicate whether high
|
||||
PWM values are dark or bright*/
|
||||
} saul_pwm_dimmer_params_t;
|
||||
|
||||
/**
|
||||
* @brief Suppress saul_pwm's RGB LED generation
|
||||
*
|
||||
* This can be defined in `pwm_params.h` if the saul_pwm module is used, but no
|
||||
* RGB LEDs (and only dimmers) are in use. Then, no @ref saul_pwm_rgb_params_t
|
||||
* needs to be set.
|
||||
*/
|
||||
#if DOXYGEN
|
||||
#define SAUL_PWM_NO_RGB
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWM channels mapped to RGB LED registration entries
|
||||
*
|
||||
* This is used to define a `static const saul_pwm_rgb_params_t
|
||||
* saul_pwm_rgb_params[]` in a board's `pwm_params.h` for use by the saul_pwm
|
||||
* module. If the module is used but only dimmers are present, a @ref
|
||||
* SAUL_PWM_NO_RGB define can be set instead.
|
||||
*/
|
||||
typedef struct {
|
||||
const char *name; /**< Name of the device connected to these
|
||||
channels */
|
||||
saul_pwm_channel_t channels[3]; /**< Full channel description (device, channel)
|
||||
along with flags that indicate whether high
|
||||
PWM values are dark or bright*/
|
||||
} saul_pwm_rgb_params_t;
|
||||
|
||||
|
||||
#endif /* MODULE_SAUL_PWM */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -6,5 +6,8 @@ endif
|
||||
ifneq (,$(filter saul_adc,$(USEMODULE)))
|
||||
SRC += adc_saul.c
|
||||
endif
|
||||
ifneq (,$(filter saul_pwm,$(USEMODULE)))
|
||||
SRC += pwm_saul.c
|
||||
endif
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
|
||||
186
drivers/saul/init_devs/auto_init_saul_pwm.c
Normal file
186
drivers/saul/init_devs/auto_init_saul_pwm.c
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Freie Universität Berlin
|
||||
*
|
||||
* 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 sys_auto_init_saul
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Auto initialization of PWM pins directly mapped to SAUL reg
|
||||
*
|
||||
* @author Christian Amsüss <chrysn@fsfe.org>
|
||||
*
|
||||
* When this module is used, any PWM device assigned inside the configuration
|
||||
* structs inside `pwm_params.h` in the @ref saul_pwm_dimmer_params_t and @ref
|
||||
* saul_pwm_rgb_params_t is initialized at startup for 8-bit dimming at about
|
||||
* 1kHz, and the indicated channels are exposed via SAUL.
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include "saul_reg.h"
|
||||
#include "saul/periph.h"
|
||||
#include "pwm_params.h"
|
||||
#include "periph/pwm.h"
|
||||
|
||||
#if !defined(SAUL_PWM_FREQ)
|
||||
#define SAUL_PWM_FREQ SAUL_PWM_FREQ_DEFAULT
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Define the number of configured dimmers
|
||||
*/
|
||||
#ifndef SAUL_PWM_NO_DIMMER
|
||||
#define SAUL_PWM_DIMMER_NUMOF ARRAY_SIZE(saul_pwm_dimmer_params)
|
||||
#else
|
||||
#define SAUL_PWM_DIMMER_NUMOF 0
|
||||
static const saul_pwm_dimmer_params_t saul_pwm_dimmer_params[0];
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Define the number of configured RGB LEDs
|
||||
*/
|
||||
#ifndef SAUL_PWM_NO_RGB
|
||||
#define SAUL_PWM_RGB_NUMOF ARRAY_SIZE(saul_pwm_rgb_params)
|
||||
#else
|
||||
#define SAUL_PWM_RGB_NUMOF 0
|
||||
static const saul_pwm_rgb_params_t saul_pwm_rgb_params[0];
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Memory for the registry RGB LED entries
|
||||
*/
|
||||
/* The static variable will be unused in the 0 case and thus not emitted. */
|
||||
static saul_reg_t saul_reg_entries_rgb[SAUL_PWM_RGB_NUMOF];
|
||||
|
||||
/**
|
||||
* @brief Memory for the registry dimmer entries
|
||||
*/
|
||||
/* The static variable will be unused in the 0 case and thus not emitted. */
|
||||
static saul_reg_t saul_reg_entries_dimmer[SAUL_PWM_DIMMER_NUMOF];
|
||||
|
||||
/**
|
||||
* @brief Reference to the driver for single-channel dimmers
|
||||
*/
|
||||
extern saul_driver_t dimmer_saul_driver;
|
||||
|
||||
/**
|
||||
* @brief Reference to the driver for RGB LEDs
|
||||
*/
|
||||
extern saul_driver_t rgb_saul_driver;
|
||||
|
||||
/**
|
||||
* Configure a PWM driver for LED output (1kHz, 8bit)
|
||||
*/
|
||||
static int configure(pwm_t dev)
|
||||
{
|
||||
LOG_DEBUG("[auto_init_saul] initializing PWM %u for LED operation,", dev);
|
||||
uint32_t freq = pwm_init(dev, PWM_LEFT, SAUL_PWM_FREQ, saul_pwm_resolution);
|
||||
LOG_DEBUG(" actual frequency %lu,\n", freq);
|
||||
return freq != 0 ? 0 : -ENOTSUP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the PWM driver at the given index (inside saul_pwm_dimmer_params,
|
||||
* overflowing into saul_pwm_rgb_params) unless that device came up previously,
|
||||
* in which case the function returns without any action.
|
||||
* */
|
||||
static int configure_on_first_use(pwm_t dev, unsigned index)
|
||||
{
|
||||
/* Work around -Werror=type-limits that would otherwise trigger */
|
||||
unsigned dimmer_numof = SAUL_PWM_DIMMER_NUMOF;
|
||||
for (unsigned i = 0; i < dimmer_numof; ++i) {
|
||||
pwm_t currentdev = saul_pwm_dimmer_params[i].channel.dev;
|
||||
if (currentdev == dev) {
|
||||
if (i == index) {
|
||||
return configure(dev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Work around -Werror=type-limits that would otherwise trigger */
|
||||
unsigned rgb_numof = SAUL_PWM_RGB_NUMOF;
|
||||
for (unsigned i = 0; i < rgb_numof; ++i) {
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
unsigned index = SAUL_PWM_DIMMER_NUMOF + i * 3 + j;
|
||||
pwm_t currentdev = saul_pwm_rgb_params[i].channels[j].dev;
|
||||
if (currentdev == dev) {
|
||||
if (i == index) {
|
||||
return configure(dev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
void auto_init_saul_pwm(void)
|
||||
{
|
||||
/* Work around -Werror=type-limits that would otherwise trigger */
|
||||
unsigned dimmer_numof = SAUL_PWM_DIMMER_NUMOF;
|
||||
for (unsigned i = 0; i < dimmer_numof; i++) {
|
||||
const saul_pwm_dimmer_params_t *p = &saul_pwm_dimmer_params[i];
|
||||
|
||||
LOG_DEBUG("[auto_init_saul] initializing dimmer #%u\n", i);
|
||||
|
||||
saul_reg_entries_dimmer[i].dev = (void*)p;
|
||||
saul_reg_entries_dimmer[i].name = p->name;
|
||||
saul_reg_entries_dimmer[i].driver = &dimmer_saul_driver;
|
||||
|
||||
int err = configure_on_first_use(p->channel.dev, i);
|
||||
if (err != 0) {
|
||||
LOG_ERROR(
|
||||
"[auto_init_saul] Error initializing device for dimmer #%u\n",
|
||||
i);
|
||||
/* not `continue`ing: we could run into this on a non-first use and
|
||||
* then we couldn't break either */
|
||||
}
|
||||
/* set initial dark state */
|
||||
phydat_t s;
|
||||
s.val[0] = 0;
|
||||
saul_reg_entries_dimmer[i].driver->write(p, &s);
|
||||
/* add to registry */
|
||||
saul_reg_add(&(saul_reg_entries_dimmer[i]));
|
||||
}
|
||||
|
||||
/* Work around -Werror=type-limits that would otherwise trigger */
|
||||
unsigned rgb_numof = SAUL_PWM_RGB_NUMOF;
|
||||
for (unsigned i = 0; i < rgb_numof; i++) {
|
||||
const saul_pwm_rgb_params_t *p = &saul_pwm_rgb_params[i];
|
||||
|
||||
LOG_DEBUG("[auto_init_saul] initializing RGB #%u\n", i);
|
||||
|
||||
saul_reg_entries_rgb[i].dev = (void*)p;
|
||||
saul_reg_entries_rgb[i].name = p->name;
|
||||
saul_reg_entries_rgb[i].driver = &rgb_saul_driver;
|
||||
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
unsigned index = SAUL_PWM_DIMMER_NUMOF + i * 3 + j;
|
||||
int err = configure_on_first_use(p->channels[j].dev, index);
|
||||
if (err != 0) {
|
||||
LOG_ERROR(
|
||||
"[auto_init_saul] Error initializing device for RGB #%u/%u\n",
|
||||
i, j);
|
||||
/* not `continue`ing: we could run into this on a non-first use and
|
||||
* then we couldn't break either */
|
||||
}
|
||||
}
|
||||
/* set initial dark state */
|
||||
phydat_t s;
|
||||
s.val[0] = 0;
|
||||
s.val[1] = 0;
|
||||
s.val[2] = 0;
|
||||
saul_reg_entries_rgb[i].driver->write(p, &s);
|
||||
/* add to registry */
|
||||
saul_reg_add(&(saul_reg_entries_rgb[i]));
|
||||
}
|
||||
}
|
||||
@ -35,6 +35,10 @@ void saul_init_devs(void)
|
||||
extern void auto_init_gpio(void);
|
||||
auto_init_gpio();
|
||||
}
|
||||
if (IS_USED(MODULE_SAUL_PWM)) {
|
||||
extern void auto_init_saul_pwm(void);
|
||||
auto_init_saul_pwm();
|
||||
}
|
||||
if (IS_USED(MODULE_SAUL_NRF_TEMPERATURE)) {
|
||||
extern void auto_init_nrf_temperature(void);
|
||||
auto_init_nrf_temperature();
|
||||
|
||||
61
drivers/saul/pwm_saul.c
Normal file
61
drivers/saul/pwm_saul.c
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Freie Universität Berlin
|
||||
*
|
||||
* 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_saul
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief SAUL wrapper for PWM pins
|
||||
*
|
||||
* @author Christian Amsüss <chrysn@fsfe.org>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "saul.h"
|
||||
#include "phydat.h"
|
||||
#include "periph/pwm.h"
|
||||
#include "saul/periph.h"
|
||||
|
||||
static inline void setchan(const saul_pwm_channel_t *chan, uint16_t value)
|
||||
{
|
||||
pwm_set(chan->dev,
|
||||
chan->channel,
|
||||
(chan->flags & SAUL_PWM_INVERTED) ? saul_pwm_resolution - value : value);
|
||||
}
|
||||
|
||||
static int write_dimmer(const void *dev, phydat_t *state)
|
||||
{
|
||||
const saul_pwm_dimmer_params_t *p = dev;
|
||||
|
||||
setchan(&p->channel, state->val[0]);
|
||||
return 3;
|
||||
}
|
||||
|
||||
const saul_driver_t dimmer_saul_driver = {
|
||||
.read = saul_notsup,
|
||||
.write = write_dimmer,
|
||||
.type = SAUL_ACT_DIMMER
|
||||
};
|
||||
|
||||
static int write_rgb(const void *dev, phydat_t *state)
|
||||
{
|
||||
const saul_pwm_rgb_params_t *p = dev;
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
setchan(&p->channels[i], state->val[i]);
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
const saul_driver_t rgb_saul_driver = {
|
||||
.read = saul_notsup,
|
||||
.write = write_rgb,
|
||||
.type = SAUL_ACT_LED_RGB
|
||||
};
|
||||
@ -104,6 +104,7 @@ PSEUDOMODULES += saul_adc
|
||||
PSEUDOMODULES += saul_default
|
||||
PSEUDOMODULES += saul_gpio
|
||||
PSEUDOMODULES += saul_nrf_temperature
|
||||
PSEUDOMODULES += saul_pwm
|
||||
PSEUDOMODULES += scanf_float
|
||||
PSEUDOMODULES += sched_cb
|
||||
PSEUDOMODULES += semtech_loramac_rx
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user