mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-19 22:35:23 +03:00
Improve speed of LED STRIP code.
Shaved about 150us each strip update. This commit leaves in some timing trace code for the CC3D target, the results of which can be seen in the debug variables for the sections being timed. A later commit will remove/disable the timing code.
This commit is contained in:
parent
36dac872b8
commit
e847a6347d
9 changed files with 189 additions and 15 deletions
|
@ -20,9 +20,40 @@
|
||||||
#define UNUSED(x) (void)(x)
|
#define UNUSED(x) (void)(x)
|
||||||
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
|
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
|
||||||
|
|
||||||
|
#ifdef UNIT_TEST
|
||||||
|
#define STATIC_UNIT_TESTED // make visible to unit test
|
||||||
|
#define UNIT_TESTED
|
||||||
|
#else
|
||||||
|
#define STATIC_UNIT_TESTED static
|
||||||
|
#define UNIT_TESTED
|
||||||
|
#endif
|
||||||
|
|
||||||
//#define SOFT_I2C // enable to test software i2c
|
//#define SOFT_I2C // enable to test software i2c
|
||||||
|
|
||||||
#ifndef __CC_ARM
|
#ifndef __CC_ARM
|
||||||
#define REQUIRE_CC_ARM_PRINTF_SUPPORT
|
#define REQUIRE_CC_ARM_PRINTF_SUPPORT
|
||||||
#define REQUIRE_PRINTF_LONG_SUPPORT
|
#define REQUIRE_PRINTF_LONG_SUPPORT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern int16_t debug[4];
|
||||||
|
|
||||||
|
#ifdef DEBUG_SECTION_TIMES
|
||||||
|
extern uint32_t sectionTimes[2][4];
|
||||||
|
|
||||||
|
#define TIME_SECTION_BEGIN(index) { \
|
||||||
|
extern uint32_t sectionTimes[2][4]; \
|
||||||
|
sectionTimes[0][index] = micros(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TIME_SECTION_END(index) { \
|
||||||
|
extern uint32_t sectionTimes[2][4]; \
|
||||||
|
sectionTimes[1][index] = micros(); \
|
||||||
|
debug[index] = sectionTimes[1][index] - sectionTimes[0][index]; \
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define TIME_SECTION_BEGIN(index) {}
|
||||||
|
#define TIME_SECTION_END(index) {}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,12 @@
|
||||||
*/
|
*/
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
|
#include "build_config.h"
|
||||||
|
|
||||||
#include "common/color.h"
|
#include "common/color.h"
|
||||||
#include "common/colorconversion.h"
|
#include "common/colorconversion.h"
|
||||||
#include "drivers/light_ws2811strip.h"
|
#include "drivers/light_ws2811strip.h"
|
||||||
|
@ -75,6 +78,7 @@ void setStripColors(const hsvColor_t *colors)
|
||||||
|
|
||||||
void ws2811LedStripInit(void)
|
void ws2811LedStripInit(void)
|
||||||
{
|
{
|
||||||
|
memset(&ledStripDMABuffer, 0, WS2811_DMA_BUFFER_SIZE);
|
||||||
ws2811LedStripHardwareInit();
|
ws2811LedStripHardwareInit();
|
||||||
ws2811UpdateStrip();
|
ws2811UpdateStrip();
|
||||||
}
|
}
|
||||||
|
@ -84,10 +88,22 @@ bool isWS2811LedStripReady(void)
|
||||||
return !ws2811LedDataTransferInProgress;
|
return !ws2811LedDataTransferInProgress;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t dmaBufferOffset;
|
STATIC_UNIT_TESTED uint16_t dmaBufferOffset;
|
||||||
static int16_t ledIndex;
|
static int16_t ledIndex;
|
||||||
|
|
||||||
void updateLEDDMABuffer(uint8_t componentValue)
|
#define USE_FAST_DMA_BUFFER_IMPL
|
||||||
|
#ifdef USE_FAST_DMA_BUFFER_IMPL
|
||||||
|
|
||||||
|
STATIC_UNIT_TESTED void fastUpdateLEDDMABuffer(rgbColor24bpp_t *color)
|
||||||
|
{
|
||||||
|
uint32_t grb = (color->rgb.g << 16) | (color->rgb.r << 8) | (color->rgb.b);
|
||||||
|
|
||||||
|
for (int8_t index = 23; index >= 0; index--) {
|
||||||
|
ledStripDMABuffer[dmaBufferOffset++] = (grb & (1 << index)) ? BIT_COMPARE_1 : BIT_COMPARE_0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
STATIC_UNIT_TESTED void updateLEDDMABuffer(uint8_t componentValue)
|
||||||
{
|
{
|
||||||
uint8_t bitIndex;
|
uint8_t bitIndex;
|
||||||
|
|
||||||
|
@ -95,15 +111,16 @@ void updateLEDDMABuffer(uint8_t componentValue)
|
||||||
{
|
{
|
||||||
if ((componentValue << bitIndex) & 0x80 ) // data sent MSB first, j = 0 is MSB j = 7 is LSB
|
if ((componentValue << bitIndex) & 0x80 ) // data sent MSB first, j = 0 is MSB j = 7 is LSB
|
||||||
{
|
{
|
||||||
ledStripDMABuffer[dmaBufferOffset] = 17; // compare value for logical 1
|
ledStripDMABuffer[dmaBufferOffset] = BIT_COMPARE_1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ledStripDMABuffer[dmaBufferOffset] = 9; // compare value for logical 0
|
ledStripDMABuffer[dmaBufferOffset] = BIT_COMPARE_0; // compare value for logical 0
|
||||||
}
|
}
|
||||||
dmaBufferOffset++;
|
dmaBufferOffset++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This method is non-blocking unless an existing LED update is in progress.
|
* This method is non-blocking unless an existing LED update is in progress.
|
||||||
|
@ -128,20 +145,17 @@ void ws2811UpdateStrip(void)
|
||||||
{
|
{
|
||||||
rgb24 = hsvToRgb24(&ledColorBuffer[ledIndex]);
|
rgb24 = hsvToRgb24(&ledColorBuffer[ledIndex]);
|
||||||
|
|
||||||
|
#ifdef USE_FAST_DMA_BUFFER_IMPL
|
||||||
|
fastUpdateLEDDMABuffer(rgb24);
|
||||||
|
#else
|
||||||
updateLEDDMABuffer(rgb24->rgb.g);
|
updateLEDDMABuffer(rgb24->rgb.g);
|
||||||
updateLEDDMABuffer(rgb24->rgb.r);
|
updateLEDDMABuffer(rgb24->rgb.r);
|
||||||
updateLEDDMABuffer(rgb24->rgb.b);
|
updateLEDDMABuffer(rgb24->rgb.b);
|
||||||
|
#endif
|
||||||
|
|
||||||
ledIndex++;
|
ledIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add needed delay at end of byte cycle, pulsewidth = 0
|
|
||||||
while(dmaBufferOffset < WS2811_DMA_BUFFER_SIZE)
|
|
||||||
{
|
|
||||||
ledStripDMABuffer[dmaBufferOffset] = 0;
|
|
||||||
dmaBufferOffset++;
|
|
||||||
}
|
|
||||||
|
|
||||||
ws2811LedDataTransferInProgress = 1;
|
ws2811LedDataTransferInProgress = 1;
|
||||||
ws2811LedStripDMAEnable();
|
ws2811LedStripDMAEnable();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,12 @@
|
||||||
#define WS2811_BITS_PER_LED 24
|
#define WS2811_BITS_PER_LED 24
|
||||||
#define WS2811_DELAY_BUFFER_LENGTH 42 // for 50us delay
|
#define WS2811_DELAY_BUFFER_LENGTH 42 // for 50us delay
|
||||||
|
|
||||||
#define WS2811_DMA_BUFFER_SIZE (WS2811_BITS_PER_LED * WS2811_LED_STRIP_LENGTH + WS2811_DELAY_BUFFER_LENGTH) // number of bytes needed is #LEDs * 24 bytes + 42 trailing bytes)
|
#define WS2811_DATA_BUFFER_SIZE (WS2811_BITS_PER_LED * WS2811_LED_STRIP_LENGTH)
|
||||||
|
|
||||||
|
#define WS2811_DMA_BUFFER_SIZE (WS2811_DATA_BUFFER_SIZE + WS2811_DELAY_BUFFER_LENGTH) // number of bytes needed is #LEDs * 24 bytes + 42 trailing bytes)
|
||||||
|
|
||||||
|
#define BIT_COMPARE_1 17 // timer compare value for logical 1
|
||||||
|
#define BIT_COMPARE_0 9 // timer compare value for logical 0
|
||||||
|
|
||||||
void ws2811LedStripInit(void);
|
void ws2811LedStripInit(void);
|
||||||
|
|
||||||
|
|
|
@ -216,7 +216,6 @@ static void ppmEdgeCallback(uint8_t port, captureCompare_t capture)
|
||||||
|
|
||||||
#define MAX_MISSED_PWM_EVENTS 10
|
#define MAX_MISSED_PWM_EVENTS 10
|
||||||
|
|
||||||
extern uint16_t debug[4];
|
|
||||||
static void pwmOverflowCallback(uint8_t port, captureCompare_t capture)
|
static void pwmOverflowCallback(uint8_t port, captureCompare_t capture)
|
||||||
{
|
{
|
||||||
UNUSED(capture);
|
UNUSED(capture);
|
||||||
|
|
|
@ -725,8 +725,11 @@ void updateLedStrip(void)
|
||||||
static uint8_t warningState = 0;
|
static uint8_t warningState = 0;
|
||||||
static uint8_t warningFlags;
|
static uint8_t warningFlags;
|
||||||
|
|
||||||
|
TIME_SECTION_BEGIN(3);
|
||||||
|
|
||||||
// LAYER 1
|
// LAYER 1
|
||||||
|
|
||||||
|
TIME_SECTION_BEGIN(0);
|
||||||
applyLedModeLayer();
|
applyLedModeLayer();
|
||||||
applyLedThrottleLayer();
|
applyLedThrottleLayer();
|
||||||
|
|
||||||
|
@ -781,7 +784,14 @@ void updateLedStrip(void)
|
||||||
#ifdef USE_LED_ANIMATION
|
#ifdef USE_LED_ANIMATION
|
||||||
applyLedAnimationLayer();
|
applyLedAnimationLayer();
|
||||||
#endif
|
#endif
|
||||||
|
TIME_SECTION_END(0);
|
||||||
|
|
||||||
|
TIME_SECTION_BEGIN(2);
|
||||||
ws2811UpdateStrip();
|
ws2811UpdateStrip();
|
||||||
|
TIME_SECTION_END(2);
|
||||||
|
|
||||||
|
TIME_SECTION_END(3);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool parseColor(uint8_t index, char *colorConfig)
|
bool parseColor(uint8_t index, char *colorConfig)
|
||||||
|
|
|
@ -68,6 +68,9 @@
|
||||||
|
|
||||||
#include "build_config.h"
|
#include "build_config.h"
|
||||||
|
|
||||||
|
#ifdef DEBUG_SECTION_TIMES
|
||||||
|
uint32_t sectionTimes[2][4];
|
||||||
|
#endif
|
||||||
extern uint32_t previousTime;
|
extern uint32_t previousTime;
|
||||||
|
|
||||||
#ifdef SOFTSERIAL_LOOPBACK
|
#ifdef SOFTSERIAL_LOOPBACK
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
|
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define DEBUG_SECTION_TIMES
|
||||||
|
|
||||||
#define LED0_GPIO GPIOB
|
#define LED0_GPIO GPIOB
|
||||||
#define LED0_PIN Pin_3 // PB3 (LED)
|
#define LED0_PIN Pin_3 // PB3 (LED)
|
||||||
#define LED0_PERIPHERAL RCC_APB2Periph_GPIOB
|
#define LED0_PERIPHERAL RCC_APB2Periph_GPIOB
|
||||||
|
|
|
@ -29,7 +29,7 @@ OBJECT_DIR = ../../obj/test
|
||||||
CPPFLAGS += -isystem $(GTEST_DIR)/inc
|
CPPFLAGS += -isystem $(GTEST_DIR)/inc
|
||||||
|
|
||||||
# Flags passed to the C++ compiler.
|
# Flags passed to the C++ compiler.
|
||||||
CXXFLAGS += -g -Wall -Wextra -pthread -ggdb -O0
|
CXXFLAGS += -g -Wall -Wextra -pthread -ggdb -O0 -DUNIT_TEST
|
||||||
|
|
||||||
# All tests produced by this Makefile. Remember to add new tests you
|
# All tests produced by this Makefile. Remember to add new tests you
|
||||||
# created to the list.
|
# created to the list.
|
||||||
|
@ -39,7 +39,8 @@ TESTS = \
|
||||||
gps_conversion_unittest \
|
gps_conversion_unittest \
|
||||||
telemetry_hott_unittest \
|
telemetry_hott_unittest \
|
||||||
rc_controls_unittest \
|
rc_controls_unittest \
|
||||||
ledstrip_unittest
|
ledstrip_unittest \
|
||||||
|
ws2811_unittest
|
||||||
|
|
||||||
# All Google Test headers. Usually you shouldn't change this
|
# All Google Test headers. Usually you shouldn't change this
|
||||||
# definition.
|
# definition.
|
||||||
|
@ -172,3 +173,17 @@ ledstrip_unittest :$(OBJECT_DIR)/io/ledstrip.o $(OBJECT_DIR)/ledstrip_unittest.o
|
||||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $(OBJECT_DIR)/$@
|
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $(OBJECT_DIR)/$@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$(OBJECT_DIR)/drivers/light_ws2811strip.o : $(USER_DIR)/drivers/light_ws2811strip.c $(USER_DIR)/drivers/light_ws2811strip.h $(GTEST_HEADERS)
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(TEST_CFLAGS) -c $(USER_DIR)/drivers/light_ws2811strip.c -o $@
|
||||||
|
|
||||||
|
$(OBJECT_DIR)/ws2811_unittest.o : $(TEST_DIR)/ws2811_unittest.cc \
|
||||||
|
$(USER_DIR)/drivers/light_ws2811strip.h $(GTEST_HEADERS)
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(TEST_CFLAGS) -c $(TEST_DIR)/ws2811_unittest.cc -o $@
|
||||||
|
|
||||||
|
ws2811_unittest :$(OBJECT_DIR)/drivers/light_ws2811strip.o $(OBJECT_DIR)/ws2811_unittest.o $(OBJECT_DIR)/gtest_main.a
|
||||||
|
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $(OBJECT_DIR)/$@
|
||||||
|
|
||||||
|
|
||||||
|
|
95
src/test/unit/ws2811_unittest.cc
Normal file
95
src/test/unit/ws2811_unittest.cc
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Cleanflight.
|
||||||
|
*
|
||||||
|
* Cleanflight is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Cleanflight is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "build_config.h"
|
||||||
|
|
||||||
|
#include "common/color.h"
|
||||||
|
|
||||||
|
#include "drivers/light_ws2811strip.h"
|
||||||
|
|
||||||
|
#include "unittest_macros.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
STATIC_UNIT_TESTED extern uint16_t dmaBufferOffset;
|
||||||
|
|
||||||
|
STATIC_UNIT_TESTED void fastUpdateLEDDMABuffer(rgbColor24bpp_t *color);
|
||||||
|
STATIC_UNIT_TESTED void updateLEDDMABuffer(uint8_t componentValue);
|
||||||
|
|
||||||
|
TEST(WS2812, updateDMABuffer) {
|
||||||
|
// given
|
||||||
|
rgbColor24bpp_t color1 = {0xFF,0xAA,0x55};
|
||||||
|
|
||||||
|
// and
|
||||||
|
dmaBufferOffset = 0;
|
||||||
|
|
||||||
|
// when
|
||||||
|
#if 0
|
||||||
|
updateLEDDMABuffer(color1.rgb.g);
|
||||||
|
updateLEDDMABuffer(color1.rgb.r);
|
||||||
|
updateLEDDMABuffer(color1.rgb.b);
|
||||||
|
#else
|
||||||
|
fastUpdateLEDDMABuffer(&color1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// then
|
||||||
|
EXPECT_EQ(24, dmaBufferOffset);
|
||||||
|
|
||||||
|
// and
|
||||||
|
uint8_t byteIndex = 0;
|
||||||
|
|
||||||
|
EXPECT_EQ(BIT_COMPARE_1, ledStripDMABuffer[(byteIndex * 8) + 0]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_0, ledStripDMABuffer[(byteIndex * 8) + 1]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_1, ledStripDMABuffer[(byteIndex * 8) + 2]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_0, ledStripDMABuffer[(byteIndex * 8) + 3]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_1, ledStripDMABuffer[(byteIndex * 8) + 4]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_0, ledStripDMABuffer[(byteIndex * 8) + 5]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_1, ledStripDMABuffer[(byteIndex * 8) + 6]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_0, ledStripDMABuffer[(byteIndex * 8) + 7]);
|
||||||
|
byteIndex++;
|
||||||
|
|
||||||
|
EXPECT_EQ(BIT_COMPARE_1, ledStripDMABuffer[(byteIndex * 8) + 0]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_1, ledStripDMABuffer[(byteIndex * 8) + 1]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_1, ledStripDMABuffer[(byteIndex * 8) + 2]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_1, ledStripDMABuffer[(byteIndex * 8) + 3]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_1, ledStripDMABuffer[(byteIndex * 8) + 4]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_1, ledStripDMABuffer[(byteIndex * 8) + 5]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_1, ledStripDMABuffer[(byteIndex * 8) + 6]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_1, ledStripDMABuffer[(byteIndex * 8) + 7]);
|
||||||
|
byteIndex++;
|
||||||
|
|
||||||
|
EXPECT_EQ(BIT_COMPARE_0, ledStripDMABuffer[(byteIndex * 8) + 0]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_1, ledStripDMABuffer[(byteIndex * 8) + 1]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_0, ledStripDMABuffer[(byteIndex * 8) + 2]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_1, ledStripDMABuffer[(byteIndex * 8) + 3]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_0, ledStripDMABuffer[(byteIndex * 8) + 4]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_1, ledStripDMABuffer[(byteIndex * 8) + 5]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_0, ledStripDMABuffer[(byteIndex * 8) + 6]);
|
||||||
|
EXPECT_EQ(BIT_COMPARE_1, ledStripDMABuffer[(byteIndex * 8) + 7]);
|
||||||
|
byteIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
rgbColor24bpp_t* hsvToRgb24(const hsvColor_t *c) {
|
||||||
|
UNUSED(c);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ws2811LedStripHardwareInit(void) {}
|
||||||
|
void ws2811LedStripDMAEnable(void) {}
|
Loading…
Add table
Add a link
Reference in a new issue