Blackbox device type 'file' (SITL) considered working when file handler is available
[inav.git] / src / main / drivers / light_ws2811strip.c
blobfe5f405d032ddf2f4e0f75a247f45184ce79b970
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 #include "config/parameter_group_ids.h"
47 #include "fc/settings.h"
48 #include "fc/runtime_config.h"
50 #define WS2811_PERIOD (WS2811_TIMER_HZ / WS2811_CARRIER_HZ)
51 #define WS2811_BIT_COMPARE_1 ((WS2811_PERIOD * 2) / 3)
52 #define WS2811_BIT_COMPARE_0 (WS2811_PERIOD / 3)
54 PG_REGISTER_WITH_RESET_TEMPLATE(ledPinConfig_t, ledPinConfig, PG_LEDPIN_CONFIG, 0);
56 PG_RESET_TEMPLATE(ledPinConfig_t, ledPinConfig,
57 .led_pin_pwm_mode = SETTING_LED_PIN_PWM_MODE_DEFAULT
60 static DMA_RAM timerDMASafeType_t ledStripDMABuffer[WS2811_DMA_BUFFER_SIZE];
62 static IO_t ws2811IO = IO_NONE;
63 static TCH_t * ws2811TCH = NULL;
64 static bool ws2811Initialised = false;
65 static bool pwmMode = false;
67 static hsvColor_t ledColorBuffer[WS2811_LED_STRIP_LENGTH];
69 void setLedHsv(uint16_t index, const hsvColor_t *color)
71 ledColorBuffer[index] = *color;
74 void getLedHsv(uint16_t index, hsvColor_t *color)
76 *color = ledColorBuffer[index];
79 void setLedValue(uint16_t index, const uint8_t value)
81 ledColorBuffer[index].v = value;
84 void scaleLedValue(uint16_t index, const uint8_t scalePercent)
86 ledColorBuffer[index].v = ((uint16_t)ledColorBuffer[index].v * scalePercent / 100);
89 void setStripColor(const hsvColor_t *color)
91 uint16_t index;
92 for (index = 0; index < WS2811_LED_STRIP_LENGTH; index++) {
93 setLedHsv(index, color);
97 void setStripColors(const hsvColor_t *colors)
99 uint16_t index;
100 for (index = 0; index < WS2811_LED_STRIP_LENGTH; index++) {
101 setLedHsv(index, colors++);
105 bool ledConfigureDMA(void) {
106 /* Compute the prescaler value */
107 uint8_t period = WS2811_TIMER_HZ / WS2811_CARRIER_HZ;
109 timerConfigBase(ws2811TCH, period, WS2811_TIMER_HZ);
110 timerPWMConfigChannel(ws2811TCH, 0);
112 return timerPWMConfigChannelDMA(ws2811TCH, ledStripDMABuffer, sizeof(ledStripDMABuffer[0]), WS2811_DMA_BUFFER_SIZE);
115 void ledConfigurePWM(void) {
116 timerConfigBase(ws2811TCH, 100, WS2811_TIMER_HZ );
117 timerPWMConfigChannel(ws2811TCH, 0);
118 timerPWMStart(ws2811TCH);
119 timerEnable(ws2811TCH);
120 pwmMode = true;
123 void ws2811LedStripInit(void)
125 const timerHardware_t * timHw = timerGetByTag(IO_TAG(WS2811_PIN), TIM_USE_ANY);
127 if (!(timHw->usageFlags & TIM_USE_LED)) { // Check if it has not been reassigned
128 timHw = timerGetByUsageFlag(TIM_USE_LED); // Get first pin marked as LED
131 if (timHw == NULL) {
132 return;
135 ws2811TCH = timerGetTCH(timHw);
136 if (ws2811TCH == NULL) {
137 return;
140 ws2811IO = IOGetByTag(timHw->tag); //IOGetByTag(IO_TAG(WS2811_PIN));
141 IOInit(ws2811IO, OWNER_LED_STRIP, RESOURCE_OUTPUT, 0);
142 IOConfigGPIOAF(ws2811IO, IOCFG_AF_PP_FAST, timHw->alternateFunction);
144 if (ledPinConfig()->led_pin_pwm_mode == LED_PIN_PWM_MODE_LOW) {
145 ledConfigurePWM();
146 *timerCCR(ws2811TCH) = 0;
147 } else if (ledPinConfig()->led_pin_pwm_mode == LED_PIN_PWM_MODE_HIGH) {
148 ledConfigurePWM();
149 *timerCCR(ws2811TCH) = 100;
150 } else {
151 if (!ledConfigureDMA()) {
152 // If DMA failed - abort
153 ws2811Initialised = false;
154 return;
157 // Zero out DMA buffer
158 memset(&ledStripDMABuffer, 0, sizeof(ledStripDMABuffer));
159 if ( ledPinConfig()->led_pin_pwm_mode == LED_PIN_PWM_MODE_SHARED_HIGH ) {
160 ledStripDMABuffer[WS2811_DMA_BUFFER_SIZE-1] = 255;
162 ws2811Initialised = true;
164 ws2811UpdateStrip();
168 bool isWS2811LedStripReady(void)
170 return !timerPWMDMAInProgress(ws2811TCH);
173 STATIC_UNIT_TESTED uint16_t dmaBufferOffset;
174 static int16_t ledIndex;
176 STATIC_UNIT_TESTED void fastUpdateLEDDMABuffer(rgbColor24bpp_t *color)
178 uint32_t grb = (color->rgb.g << 16) | (color->rgb.r << 8) | (color->rgb.b);
180 for (int8_t index = 23; index >= 0; index--) {
181 ledStripDMABuffer[WS2811_DELAY_BUFFER_LENGTH + dmaBufferOffset++] = (grb & (1 << index)) ? WS2811_BIT_COMPARE_1 : WS2811_BIT_COMPARE_0;
186 * This method is non-blocking unless an existing LED update is in progress.
187 * it does not wait until all the LEDs have been updated, that happens in the background.
189 void ws2811UpdateStrip(void)
191 static rgbColor24bpp_t *rgb24;
193 // don't wait - risk of infinite block, just get an update next time round
194 if (pwmMode || timerPWMDMAInProgress(ws2811TCH)) {
195 return;
198 dmaBufferOffset = 0; // reset buffer memory index
199 ledIndex = 0; // reset led index
201 // fill transmit buffer with correct compare values to achieve
202 // correct pulse widths according to color values
203 while (ledIndex < WS2811_LED_STRIP_LENGTH)
205 rgb24 = hsvToRgb24(&ledColorBuffer[ledIndex]);
206 fastUpdateLEDDMABuffer(rgb24);
207 ledIndex++;
210 // Initiate hardware transfer
211 if (!ws2811Initialised || !ws2811TCH) {
212 return;
215 timerPWMPrepareDMA(ws2811TCH, WS2811_DMA_BUFFER_SIZE);
216 timerPWMStartDMA(ws2811TCH);
219 //value
220 void ledPinStartPWM(uint16_t value) {
221 if (ws2811TCH == NULL) {
222 return;
225 if ( !pwmMode ) {
226 timerPWMStopDMA(ws2811TCH);
227 //FIXME: implement method to release DMA
228 ws2811TCH->dma->owner = OWNER_FREE;
230 ledConfigurePWM();
232 *timerCCR(ws2811TCH) = value;
235 void ledPinStopPWM(void) {
236 if (ws2811TCH == NULL || !pwmMode ) {
237 return;
240 if ( ledPinConfig()->led_pin_pwm_mode == LED_PIN_PWM_MODE_HIGH ) {
241 *timerCCR(ws2811TCH) = 100;
242 return;
243 } else if ( ledPinConfig()->led_pin_pwm_mode == LED_PIN_PWM_MODE_LOW ) {
244 *timerCCR(ws2811TCH) = 0;
245 return;
247 pwmMode = false;
249 if (!ledConfigureDMA()) {
250 ws2811Initialised = false;
255 #endif