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
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); |
|
}
|
|
|