diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index bdd5b6da17903dc36e6b96b3b198d89a7f0dac98..77d899f5e20a23d047a33370d5f22d3b02c9614c 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -94,6 +94,11 @@ ifneq (,$(filter lm75a,$(USEMODULE))) USEMODULE += xtimer endif +ifneq (,$(filter lpd8808,$(USEMODULE))) + USEMODULE += color + FEATURES_REQUIRED += periph_gpio +endif + ifneq (,$(filter ltc4150,$(USEMODULE))) USEMODULE += xtimer endif diff --git a/drivers/Makefile.include b/drivers/Makefile.include index 330ed55bb5e50c40ea84dd9b6271f90abc254946..667b05794d0b156b1007abe671ca0f096145d34b 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -67,3 +67,6 @@ endif ifneq (,$(filter cc2420,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/cc2420/include endif +ifneq (,$(filter lpd8808,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/lpd8808/include +endif diff --git a/drivers/include/lpd8808.h b/drivers/include/lpd8808.h new file mode 100644 index 0000000000000000000000000000000000000000..7cabdfa3cade74ca8b71db8a04b43504d1fce333 --- /dev/null +++ b/drivers/include/lpd8808.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2016 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. + */ + +/** + * @defgroup drivers_lpd8808 LPD8808 based LED Strip + * @ingroup drivers + * @brief Driver for LPD8808 based LED strips + * + * LPD8808 based LED strips consist of a number of LEDs driven by LPD8808 chips. + * In these strips, each LED can be controlled individually. For this, every two + * LEDs are driven by a LPD8808 chip, which are chained and form a long shift + * register. To control a certain LED, the target color value needs to be + * shifted to the LEDs position on the strip. + * + * This driver implementation does not buffer the current values for each LED. + * It expects the application to take care of this. + * + * @{ + * @file + * @brief Interface definition for the LPD8808 LED strip driver + * + * @author Hauke Petersen <hauke.petersen@fu-berlin.de> + */ + +#ifndef LPD8808_H +#define LPD8808_H + +#include "color.h" +#include "periph/gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Parameters needed for configuration + */ +typedef struct { + int led_cnt; /**< number of LEDs on the strip */ + gpio_t pin_clk; /**< pin connected to the strip's clock signal */ + gpio_t pin_dat; /**< pin connected to the strip's data signal */ +} lpd8808_params_t; + +/** + * @brief Device descriptor, same as the configuration parameters + */ +typedef lpd8808_params_t lpd8808_t; + +/** + * @brief Initialize the given LPD8808 based LED strip + * + * @param[out] dev device to initialize + * @param[in] params parameters used for initialization + * + * @return 0 on success + * @return <0 on error + */ +int lpd8808_init(lpd8808_t *dev, const lpd8808_params_t *params); + +/** + * @brief Set the color value of each LED on the strip + * + * This function sets the color value of each LED on the strip by shifting out + * the corresponding color values one after the other. The function expects an + * array of @p color_rgb_t values of the same length as LEDs on the strip. + * + * @param[in] dev device to load color values to + * @param[in] vals array of color values, MUST be of same length as LEDs on + * the strip + */ +void lpd8808_load_rgb(lpd8808_t *dev, color_rgb_t vals[]); + +#ifdef __cplusplus +} +#endif + +#endif /* LPD8808_H */ +/** @} */ diff --git a/drivers/lpd8808/Makefile b/drivers/lpd8808/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..48422e909a47d7cd428d10fa73825060ccc8d8c2 --- /dev/null +++ b/drivers/lpd8808/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/lpd8808/include/lpd8808_params.h b/drivers/lpd8808/include/lpd8808_params.h new file mode 100644 index 0000000000000000000000000000000000000000..51dc1654a996d404678c1cb2d965a763b0f8c9f2 --- /dev/null +++ b/drivers/lpd8808/include/lpd8808_params.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 Hauke Petersen <devel@haukepetersen.de> + * + * 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_lpd8808 + * + * @{ + * @file + * @brief Default configuration for LPD8808 based LED strips + * + * @author Hauke Petersen <hauke.petersen@fu-berlin.de> + */ + +#ifndef LPD8808_PARAMS_H +#define LPD8808_PARAMS_H + +#include "board.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Set default configuration parameters for the LPD8808 driver + * @{ + */ +#ifndef LPD8808_PARAM_LED_CNT +#define LPD8808_PARAM_LED_CNT (64) /* most have 64 per meter... */ +#endif +#ifndef LPD8808_PARAM_PIN_CLK +#define LPD8808_PARAM_PIN_CLK (GPIO_PIN(0, 0)) +#endif +#ifndef LPD8808_PARAM_PIN_DAT +#define LPD8808_PARAM_PIN_DAT (GPIO_PIN(0, 1)) +#endif + +#define LPD8808_PARAMS_DEFAULT {.led_cnt = LPD8808_PARAM_LED_CNT, \ + .pin_clk = LPD8808_PARAM_PIN_CLK, \ + .pin_dat = LPD8808_PARAM_PIN_DAT } +/**@}*/ + +/** + * @brief LPD8808 parameter allocation + */ +static const lpd8808_params_t lpd8808_params[] = +{ +#ifdef LPD8808_PARAMS_BOARD + LPD8808_PARAMS_BOARD, +#else + LPD8808_PARAMS_DEFAULT, +#endif +}; + +#ifdef __cplusplus +} +#endif + +#endif /* LPD8808_PARAMS_H */ +/** @} */ diff --git a/drivers/lpd8808/lpd8808.c b/drivers/lpd8808/lpd8808.c new file mode 100644 index 0000000000000000000000000000000000000000..dbe3d0095dff0c2848a7dffa398f622429d996d9 --- /dev/null +++ b/drivers/lpd8808/lpd8808.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 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 lpd8808 + * @{ + * + * @file + * @brief LPD8808 based LED strip driver implementation + * + * @author Hauke Petersen <mail@haukepetersen.de> + * + * @} + */ + +#include <string.h> + +#include "lpd8808.h" + +/** + * @brief Shift a single byte to the strip + * + * @param[in] dev device to use + * @param[in] d byte to shift out + */ +static void put_byte(lpd8808_t *dev, uint8_t d) +{ + for (int i = 0; i < 8; i++) { + gpio_write(dev->pin_dat, d & 0x80); + gpio_set(dev->pin_clk); + gpio_clear(dev->pin_clk); + d <<= 1; + } +} + +/** + * @brief Flush the previous input + * + * LPD8808 based strips need to be flushed after loading values for each LED. + * This is done by feeding the strip with one zero byte for every 32 LEDs on + * the strip. + * + * @param[in] dev device to flush + */ +static void flush(lpd8808_t *dev) +{ + for (int i = 0; i < ((dev->led_cnt + 31) / 32); i++) { + put_byte(dev, 0); + } +} + +int lpd8808_init(lpd8808_t *dev, const lpd8808_params_t *params) +{ + memcpy(dev, params, sizeof(lpd8808_params_t)); + + /* initialize pins */ + gpio_init(dev->pin_dat, GPIO_OUT); + gpio_init(dev->pin_clk, GPIO_OUT); + flush(dev); + + return 0; +} + +void lpd8808_load_rgb(lpd8808_t *dev, color_rgb_t vals[]) +{ + for (int i = 0; i < dev->led_cnt; i++) { + put_byte(dev, ((vals[i].g >> 1) | 0x80)); + put_byte(dev, ((vals[i].r >> 1) | 0x80)); + put_byte(dev, ((vals[i].b >> 1) | 0x80)); + } + flush(dev); +} diff --git a/tests/driver_lpd8808/Makefile b/tests/driver_lpd8808/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..e4e6e26e6fd78ec8d9308727d10e61d4dae55ba9 --- /dev/null +++ b/tests/driver_lpd8808/Makefile @@ -0,0 +1,7 @@ +APPLICATION = driver_lpd8808 +include ../Makefile.tests_common + +USEMODULE += lpd8808 +USEMODULE += xtimer + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_lpd8808/README.md b/tests/driver_lpd8808/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f2a1cbc9ab5b8858b276161bc4a946f9b5dfa578 --- /dev/null +++ b/tests/driver_lpd8808/README.md @@ -0,0 +1,14 @@ +# About +This test application is made for verification of the LPD8808 LED strip driver. + +# Usage +Connect a LPD8808 based LED strip to a board of your choice, build, and flash +this application. You should see a light running up and down your strip, +slowly changing it's color. + +You might need to adjust the default parameters (number of LEDs on the strip and +pin configuration). You can do this simply by pre-setting the `CFLAGS` +environment variable, e.g.: +``` +$ CFLAGS="-DLPD8808_PARAM_LED_CNT=78"" make all +``` diff --git a/tests/driver_lpd8808/main.c b/tests/driver_lpd8808/main.c new file mode 100644 index 0000000000000000000000000000000000000000..ac7285c1add7358376a0b6691309f7bc3d536038 --- /dev/null +++ b/tests/driver_lpd8808/main.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2016 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 tests + * @{ + * + * @file + * @brief Test application for the LPD8808 LED strip driver + * + * Refer to the accompanying README.md for more information. + * + * @author Hauke Petersen <hauke.petersen@fu-berlin.de> + * + * @} + */ + +#include <string.h> +#include <stdio.h> + +#include "xtimer.h" + +#include "lpd8808.h" +#include "lpd8808_params.h" + +/** + * @brief Switch to the next LED every 10ms + */ +#define STEP (10 * 1000U) + +/** + * @brief Allocate the device descriptor + */ +static lpd8808_t dev; + +/** + * @brief Allocate a color_rgb_t struct for each LED on the strip + */ +static color_rgb_t leds[LPD8808_PARAM_LED_CNT]; + + +int main(void) +{ + uint32_t now = xtimer_now(); + int pos = 0; + int step = 1; + color_hsv_t col = { 0.0, 1.0, 1.0 }; + + /* initialize all LED color values to black (off) */ + memset(leds, 0, sizeof(color_rgb_t) * LPD8808_PARAM_LED_CNT); + + /* initialize the driver */ + lpd8808_init(&dev, &lpd8808_params[0]); + + while (1) { + + /* change the active color by running around the hue circle */ + col.h += 1.0; + if (col.h > 360.0) { + col.h = 0; + } + + /* set the active LED to the active color value */ + memset(&leds[pos], 0, sizeof(color_rgb_t)); + pos += step; + color_hsv2rgb(&col, &leds[pos]); + + /* apply the values to the LED strip */ + lpd8808_load_rgb(&dev, leds); + + /* switch direction once reaching an end of the strip */ + if ((pos == (LPD8808_PARAM_LED_CNT - 1)) || (pos == 0)) { + step *= -1; + } + + xtimer_periodic_wakeup(&now, STEP); + } + + return 0; +}