mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
9efd47952e
Point out that automatic backend selection is performed, when no backend is selected.
257 lines
7.9 KiB
C
257 lines
7.9 KiB
C
/*
|
|
* Copyright 2019 Marian Buschsieweke
|
|
*
|
|
* 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_ws281x WS2812/SK6812 RGB LED (NeoPixel)
|
|
* @ingroup drivers_actuators
|
|
* @brief Driver for the WS2812 or the SK6812 RGB LEDs sold as NeoPixel
|
|
*
|
|
* # Summary
|
|
*
|
|
* The WS2812 or SK6812 RGB LEDs, or more commonly known as NeoPixels, can be
|
|
* chained so that a single data pin of the MCU can control an arbitrary number
|
|
* of RGB LEDs. This driver supports both the WS2812/SK6812 and the WS2812b
|
|
* LEDs.
|
|
*
|
|
* # Support
|
|
*
|
|
* The protocol to communicate with the WS281x is custom, so no hardware
|
|
* implementations can be used. Hence, the protocol needs to be bit banged in
|
|
* software. As the timing requirements are too strict to do this using
|
|
* the platform independent APIs for accessing @ref drivers_periph_gpio and
|
|
* @ref sys_xtimer, platform specific implementations of @ref ws281x_write are
|
|
* needed.
|
|
*
|
|
* ## ATmega
|
|
*
|
|
* A bit banging implementation for ATmegas clocked at 8MHz and at 16MHz is
|
|
* provided. Boards clocked at any other core frequency are not supported.
|
|
* (But keep in mind that most (all?) ATmega MCUs do have an internal 8MHz
|
|
* oscillator, that could be enabled by changing the fuse settings.)
|
|
*
|
|
* @warning On 8MHz ATmegas, only pins at GPIO ports B, C, and D are supported.
|
|
* (On 16MHz ATmegas, any pin is fine.)
|
|
*
|
|
* ## ESP32
|
|
*
|
|
* The ESP32 implementation is frequency independent, as frequencies above 80MHz
|
|
* are high enough to support bit banging without assembly.
|
|
*
|
|
* ## Native/VT100
|
|
*
|
|
* The native (VT100) implementation writes the LED state to the console.
|
|
*
|
|
* ### Usage
|
|
*
|
|
* Add the following to your `Makefile`:
|
|
*
|
|
* * Auto-selecting the backend:
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Makefile
|
|
* USEMODULE += ws281x
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* This will automatically pull in one of the backends supported by your board.
|
|
* In case multiple backends apply and the automatic selection does not pick
|
|
* your preferred backend, you can manually pick your preferred backend as
|
|
* described below.
|
|
*
|
|
* * the ATmega backend:
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Makefile
|
|
* USEMODULE += ws281x_atmega
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* * the ESP32 backend:
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Makefile
|
|
* USEMODULE += ws281x_esp32
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* * the native/VT100 backend:
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Makefile
|
|
* USEMODULE += ws281x_vt100
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief WS2812/SK6812 RGB LED Driver
|
|
*
|
|
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
|
*/
|
|
|
|
#ifndef WS281X_H
|
|
#define WS281X_H
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "color.h"
|
|
#include "periph/gpio.h"
|
|
#include "ws281x_backend.h"
|
|
#include "ws281x_constants.h"
|
|
#include "xtimer.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/**
|
|
* @brief The number of bytes to allocate in the data buffer per LED
|
|
*/
|
|
#define WS281X_BYTES_PER_DEVICE (3U)
|
|
|
|
/**
|
|
* @brief Struct to hold initialization parameters for a WS281x RGB LED
|
|
*/
|
|
typedef struct {
|
|
/**
|
|
* @brief A statically allocated data buffer storing the state of the LEDs
|
|
*
|
|
* @pre Must be sized `numof * WS281X_BYTES_PER_DEVICE` bytes
|
|
*/
|
|
uint8_t *buf;
|
|
uint16_t numof; /**< Number of chained RGB LEDs */
|
|
gpio_t pin; /**< GPIO connected to the data pin of the first LED */
|
|
} ws281x_params_t;
|
|
|
|
/**
|
|
* @brief Device descriptor of a WS281x RGB LED chain
|
|
*/
|
|
typedef struct {
|
|
ws281x_params_t params; /**< Parameters of the LED chain */
|
|
} ws281x_t;
|
|
|
|
#if defined(WS281X_HAVE_INIT) || defined(DOXYGEN)
|
|
/**
|
|
* @brief Initialize an WS281x RGB LED chain
|
|
*
|
|
* @param dev Device descriptor to initialize
|
|
* @param params Parameters to initialize the device with
|
|
*
|
|
* @retval 0 Success
|
|
* @retval -EINVAL Invalid argument
|
|
* @retval -EIO Failed to initialize the data GPIO pin
|
|
*/
|
|
int ws281x_init(ws281x_t *dev, const ws281x_params_t *params);
|
|
#else
|
|
static inline int ws281x_init(ws281x_t *dev, const ws281x_params_t *params) {
|
|
dev->params = *params;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @brief Writes the color data of the user supplied buffer
|
|
*
|
|
* @param dev Device descriptor of the LED chain to write to
|
|
* @param buf Buffer to write
|
|
* @param size Size of the buffer in bytes
|
|
*
|
|
* @pre Before the transmission starts @ref ws281x_prepare_transmission is
|
|
* called
|
|
* @post At the end of the transmission @ref ws281x_end_transmission is
|
|
* called
|
|
*
|
|
* This function can be used to drive a huge number of LEDs with small data
|
|
* buffers. However, after the return of this function the next chunk should
|
|
* be send within a few microseconds to avoid accidentally sending the end of
|
|
* transmission signal.
|
|
*
|
|
* Usage:
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
|
|
* uint8_t chunk[CHUNK_SIZE];
|
|
* ws281x_prepare_transmission(ws281x_dev);
|
|
* while (more_chunks_available()) {
|
|
* prepare_chunk(chunk);
|
|
* ws281x_write_buffer(ws281x_dev, chunk, sizeof(chunk));
|
|
* }
|
|
* ws281x_end_transmission(dev);
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*/
|
|
void ws281x_write_buffer(ws281x_t *dev, const void *buf, size_t size);
|
|
|
|
#if defined(WS281X_HAVE_PREPARE_TRANSMISSION) || defined(DOXYGEN)
|
|
/**
|
|
* @brief Sets up everything needed to write data to the WS281X LED chain
|
|
*
|
|
* @param dev Device descriptor of the LED chain to write to
|
|
*/
|
|
void ws281x_prepare_transmission(ws281x_t *dev);
|
|
#else
|
|
static inline void ws281x_prepare_transmission(ws281x_t *dev)
|
|
{
|
|
(void)dev;
|
|
}
|
|
#endif
|
|
|
|
#if defined(WS281X_HAVE_END_TRANSMISSION) || defined(DOXYGEN)
|
|
/**
|
|
* @brief Ends the transmission to the WS2812/SK6812 LED chain
|
|
*
|
|
* @param dev Device descriptor of the LED chain to write to
|
|
*
|
|
* Does any cleanup the backend needs after sending data. In the simplest case
|
|
* it simply waits for 80µs to send the end of transmission signal.
|
|
*/
|
|
void ws281x_end_transmission(ws281x_t *dev);
|
|
#else
|
|
static inline void ws281x_end_transmission(ws281x_t *dev)
|
|
{
|
|
(void)dev;
|
|
xtimer_usleep(WS281X_T_END_US);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @brief Sets the color of an LED in the given data buffer
|
|
*
|
|
* @param dest Buffer to set the color in
|
|
* @param index The index of the LED to set the color of
|
|
* @param color The new color to apply
|
|
*
|
|
* @warning This change will not become active until @ref ws281x_write is
|
|
* called
|
|
*/
|
|
void ws281x_set_buffer(void *dest, uint16_t index, color_rgb_t color);
|
|
|
|
/**
|
|
* @brief Sets the color of an LED in the chain in the internal buffer
|
|
*
|
|
* @param dev Device descriptor of the LED chain to modify
|
|
* @param index The index of the LED to set the color of
|
|
* @param color The new color to apply
|
|
*
|
|
* @warning This change will not become active until @ref ws281x_write is
|
|
* called
|
|
*/
|
|
static inline void ws281x_set(ws281x_t *dev, uint16_t index, color_rgb_t color)
|
|
{
|
|
ws281x_set_buffer(dev->params.buf, index, color);
|
|
}
|
|
|
|
/**
|
|
* @brief Writes the internal buffer to the LED chain
|
|
*
|
|
* @param dev Device descriptor of the LED chain to write to
|
|
*
|
|
* @note This function implicitly calls @ref ws281x_end_transmission
|
|
* @see ws281x_set
|
|
*/
|
|
static inline void ws281x_write(ws281x_t *dev)
|
|
{
|
|
ws281x_prepare_transmission(dev);
|
|
ws281x_write_buffer(dev, dev->params.buf,
|
|
(size_t)dev->params.numof * WS281X_BYTES_PER_DEVICE);
|
|
ws281x_end_transmission(dev);
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* WS281X_H */
|
|
/** @} */
|