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.
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
)
81 for (index
= 0; index
< WS2811_LED_STRIP_LENGTH
; index
++) {
82 setLedHsv(index
, color
);
86 void setStripColors(const hsvColor_t
*colors
)
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
);
102 ws2811TCH
= timerGetTCH(timHw
);
103 if (ws2811TCH
== NULL
) {
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;
123 // Zero out DMA buffer
124 memset(&ledStripDMABuffer
, 0, sizeof(ledStripDMABuffer
));
125 ws2811Initialised
= true;
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
)) {
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
);
172 // Initiate hardware transfer
173 if (!ws2811Initialised
|| !ws2811TCH
) {
177 timerPWMPrepareDMA(ws2811TCH
, WS2811_DMA_BUFFER_SIZE
);
178 timerPWMStartDMA(ws2811TCH
);