/* * 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 * * 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 #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 %" PRIu32 ",\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])); } }