/* * ws281x.c * * The MIT License. * Created on: 14.07.2017 * Author: Mateusz Salamon * www.msalamon.pl * mateusz@msalamon.pl */ /* Includes */ #include /* 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); }