Merge remote-tracking branch 'upstream/master' into abo_RTH_sanity_fix
[inav.git] / src / main / drivers / light_ws2811strip.c
blob718c96ca8a9934c0710e4c6a8b8a558bdc49fe0e
1 /*
2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
20 * "Note that the timing on the WS2812/WS2812B LEDs has changed as of batches from WorldSemi
21 * manufactured made in October 2013, and timing tolerance for approx 10-30% of parts is very small.
22 * Recommendation from WorldSemi is now: 0 = 400ns high/850ns low, and 1 = 850ns high, 400ns low"
24 * Currently the timings are 0 = 350ns high/800ns and 1 = 700ns high/650ns low.
27 #include <stdbool.h>
28 #include <stdint.h>
29 #include <string.h>
31 #include <platform.h>
33 #ifdef USE_LED_STRIP
35 #include "build/build_config.h"
36 #include "build/debug.h"
38 #include "common/color.h"
39 #include "common/colorconversion.h"
41 #include "drivers/dma.h"
42 #include "drivers/io.h"
43 #include "drivers/timer.h"
44 #include "drivers/light_ws2811strip.h"
46 #define WS2811_PERIOD (WS2811_TIMER_HZ / WS2811_CARRIER_HZ)
47 #define WS2811_BIT_COMPARE_1 ((WS2811_PERIOD * 2) / 3)
48 #define WS2811_BIT_COMPARE_0 (WS2811_PERIOD / 3)
50 static DMA_RAM timerDMASafeType_t ledStripDMABuffer[WS2811_DMA_BUFFER_SIZE];
52 static IO_t ws2811IO = IO_NONE;
53 static TCH_t * ws2811TCH = NULL;
54 static bool ws2811Initialised = false;
56 static hsvColor_t ledColorBuffer[WS2811_LED_STRIP_LENGTH];
58 void setLedHsv(uint16_t index, const hsvColor_t *color)
60 ledColorBuffer[index] = *color;
63 void getLedHsv(uint16_t index, hsvColor_t *color)
65 *color = ledColorBuffer[index];
68 void setLedValue(uint16_t index, const uint8_t value)
70 ledColorBuffer[index].v = value;
73 void scaleLedValue(uint16_t index, const uint8_t scalePercent)
75 ledColorBuffer[index].v = ((uint16_t)ledColorBuffer[index].v * scalePercent / 100);
78 void setStripColor(const hsvColor_t *color)
80 uint16_t index;
81 for (index = 0; index < WS2811_LED_STRIP_LENGTH; index++) {
82 setLedHsv(index, color);
86 void setStripColors(const hsvColor_t *colors)
88 uint16_t index;
89 for (index = 0; index < WS2811_LED_STRIP_LENGTH; index++) {
90 setLedHsv(index, colors++);
94 void ws2811LedStripInit(void)
96 const timerHardware_t * timHw = timerGetByTag(IO_TAG(WS2811_PIN), TIM_USE_ANY);
98 if (timHw == NULL) {
99 return;
102 ws2811TCH = timerGetTCH(timHw);
103 if (ws2811TCH == NULL) {
104 return;
107 /* Compute the prescaler value */
108 uint8_t period = WS2811_TIMER_HZ / WS2811_CARRIER_HZ;
110 ws2811IO = IOGetByTag(IO_TAG(WS2811_PIN));
111 IOInit(ws2811IO, OWNER_LED_STRIP, RESOURCE_OUTPUT, 0);
112 IOConfigGPIOAF(ws2811IO, IOCFG_AF_PP_FAST, timHw->alternateFunction);
114 timerConfigBase(ws2811TCH, period, WS2811_TIMER_HZ);
115 timerPWMConfigChannel(ws2811TCH, 0);
117 // If DMA failed - abort
118 if (!timerPWMConfigChannelDMA(ws2811TCH, ledStripDMABuffer, sizeof(ledStripDMABuffer[0]), WS2811_DMA_BUFFER_SIZE)) {
119 ws2811Initialised = false;
120 return;
123 // Zero out DMA buffer
124 memset(&ledStripDMABuffer, 0, sizeof(ledStripDMABuffer));
125 ws2811Initialised = true;
127 ws2811UpdateStrip();
130 bool isWS2811LedStripReady(void)
132 return !timerPWMDMAInProgress(ws2811TCH);
135 STATIC_UNIT_TESTED uint16_t dmaBufferOffset;
136 static int16_t ledIndex;
138 STATIC_UNIT_TESTED void fastUpdateLEDDMABuffer(rgbColor24bpp_t *color)
140 uint32_t grb = (color->rgb.g << 16) | (color->rgb.r << 8) | (color->rgb.b);
142 for (int8_t index = 23; index >= 0; index--) {
143 ledStripDMABuffer[dmaBufferOffset++] = (grb & (1 << index)) ? WS2811_BIT_COMPARE_1 : WS2811_BIT_COMPARE_0;
148 * This method is non-blocking unless an existing LED update is in progress.
149 * it does not wait until all the LEDs have been updated, that happens in the background.
151 void ws2811UpdateStrip(void)
153 static rgbColor24bpp_t *rgb24;
155 // don't wait - risk of infinite block, just get an update next time round
156 if (timerPWMDMAInProgress(ws2811TCH)) {
157 return;
160 dmaBufferOffset = 0; // reset buffer memory index
161 ledIndex = 0; // reset led index
163 // fill transmit buffer with correct compare values to achieve
164 // correct pulse widths according to color values
165 while (ledIndex < WS2811_LED_STRIP_LENGTH)
167 rgb24 = hsvToRgb24(&ledColorBuffer[ledIndex]);
168 fastUpdateLEDDMABuffer(rgb24);
169 ledIndex++;
172 // Initiate hardware transfer
173 if (!ws2811Initialised || !ws2811TCH) {
174 return;
177 timerPWMPrepareDMA(ws2811TCH, WS2811_DMA_BUFFER_SIZE);
178 timerPWMStartDMA(ws2811TCH);
181 #endif