You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

213 lines
4.9 KiB

/*
* ws281x.c
*
* The MIT License.
* Created on: 14.07.2017
* Author: Mateusz Salamon
* www.msalamon.pl
* mateusz@msalamon.pl
*/
/* Includes */
#include <ws281x.h>
/* Definitions */
#define WS281X_MAX (16U)
#define ZERO (0b11000000)
#define ONE (0b11111000)
/* Macros */
#define ws281x_return_if_fail(cond) if(!(cond)) { return; }
#define ws281x_return_val_if_fail(cond, val) if(!(cond)) { return (val); }
#define ws281x_return_null_if_fail(cond) ws281x_return_val_if_fail((cond), NULL)
/* Data Structures */
struct ws281x_s {
ws281x_type_t type;
SPI_HandleTypeDef *hspi;
ws281x_color_t *leds;
int32_t num_leds;
int32_t curr_led;
uint8_t wordlen;
uint8_t buf[64]; /* enough to hold even/odd 32-bit words */
uint8_t reset;
};
/* Private Constants */
/* Public Global Variables */
/* Private Global Variables */
static ws281x_t ws281x[WS281X_MAX];
static int32_t ws281x_count = 0;
static uint8_t wordlen[WS281X_TYPE_END] = {
24, /* WS281X_TYPE_WS2811 */
24, /* WS281X_TYPE_WS2812*/
24, /* WS281X_TYPE_WS2812B */
24, /* WS281X_TYPE_SK6812 */
32, /* WS281X_TYPE_SK6812RGBW */
24 /* WS281X_TYPE_WS2813B */
};
/* Function Prototypes */
static ws281x_t *ws281x_lookup(SPI_HandleTypeDef *hspi);
static void ws281x_update_half(ws281x_t *ws281x, uint8_t even);
/* Function Definitions */
/**
* \brief Creates a new `ws281x_t` instance.
*
* This function initializes a new `ws281x_t` object which is controlled by
* a SPI peripheral and DMA.
*
* \param type The type of addressable LED(s).
* \param hspi Pointer to the SPI device handle. The `hspi` handle must be
* unique for each obect initialized.
* \param leds Pointer to an array of `ws281x_color_t` which holds all of the
* LED color information.
* \param num_leds The number of LEDs to control.
*
* \return Pointer to the created ws281x_t object, NULL on failure.
*/
ws281x_t *ws281x_init(ws281x_type_t type, SPI_HandleTypeDef *hspi, ws281x_color_t *leds, int32_t num_leds)
{
ws281x_return_null_if_fail(ws281x_count < WS281X_MAX);
ws281x_return_null_if_fail(type < WS281X_TYPE_END);
ws281x_return_null_if_fail(hspi);
ws281x_return_null_if_fail(leds);
ws281x_return_null_if_fail(num_leds > 0);
ws281x_return_null_if_fail(ws281x_lookup(hspi) == NULL);
ws281x[ws281x_count].hspi = hspi;
ws281x[ws281x_count].type = type;
ws281x[ws281x_count].leds = leds;
ws281x[ws281x_count].num_leds = num_leds;
ws281x[ws281x_count].curr_led = 0;
ws281x[ws281x_count].wordlen = wordlen[type];
ws281x[ws281x_count].reset = 0;
return &ws281x[ws281x_count++];
}
/**
* \brief Refreshes the LED(s).
*
* All of the LEDs in the string are sent the current color information.
*
* \param ws281x Pointer to a `ws281x_t` object.
*/
void ws281x_refresh(ws281x_t *ws281x)
{
uint8_t i;
ws281x_return_if_fail(ws281x);
ws281x->curr_led = 0;
ws281x->reset = 1;
for(i = 0; i < ws281x->wordlen * 2; i++)
{
ws281x->buf[i] = 0x00;
}
HAL_SPI_Transmit_DMA(ws281x->hspi, ws281x->buf, ws281x->wordlen * 2);
while(HAL_DMA_GetState(ws281x->hspi->hdmatx) != HAL_DMA_STATE_READY);
}
/**
* \brief Sets an LED color.
*
* This function sets the color of the LED at index `led_id`.
*
* \param ws281x Pointer to a `ws281x_t` object.
* \param led_id Index of the LED which is to have its color assigned.
* \param color A ws281x_color_t object which contains the color information
* to be assigned.
*
* \return `led_id` on success, -1 on failure.
*/
int32_t ws281x_set_led(ws281x_t *ws281x, uint16_t led_id, ws281x_color_t color)
{
ws281x_return_val_if_fail(ws281x, -1);
ws281x_return_val_if_fail(led_id < ws281x->num_leds, -1);
ws281x->leds[led_id] = color;
return led_id;
}
/* Private Function Definitions */
static ws281x_t *ws281x_lookup(SPI_HandleTypeDef *hspi)
{
int32_t i;
ws281x_return_val_if_fail(hspi, NULL);
ws281x_return_val_if_fail(ws281x_count > 0, NULL);
for(i = 0; i < ws281x_count; i++)
{
if(ws281x[i].hspi == hspi)
{
return &ws281x[i];
}
}
return NULL;
}
static void ws281x_update_half(ws281x_t *ws281x, uint8_t even)
{
uint8_t i = even ? ws281x->wordlen : 0;
int8_t j;
if(ws281x->curr_led > ws281x->num_leds)
{
HAL_SPI_DMAStop(ws281x->hspi);
}
else
{
for(j = ws281x->wordlen - 1; j >= 0; j--)
{
if((ws281x->leds[ws281x->curr_led].u32 & (1 << j)))
{
ws281x->buf[i] = ONE;
}
else
{
ws281x->buf[i] = ZERO;
}
i++;
}
ws281x->curr_led++;
}
}
/* HAL Callbacks */
void HAL_SPI_TxHalfCpltCallback(SPI_HandleTypeDef *hspi)
{
ws281x_t *ws281x = ws281x_lookup(hspi);
uint8_t i;
ws281x_return_if_fail(ws281x);
if(ws281x->reset)
{
for(i = 0; i < ws281x->wordlen; i++)
{
ws281x->buf[i] = 0x00;
}
ws281x->reset = 0;
}
else /* odd LEDs -- 1, 3, 5, ... */
{
ws281x_update_half(ws281x, 0);
}
}
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
ws281x_t *ws281x = ws281x_lookup(hspi);
ws281x_return_if_fail(ws281x);
/* even LEDs -- 2, 4, 6, ... */
ws281x_update_half(ws281x, 1);
}