diff --git a/radio/src/boards/generic_stm32/module_ports.cpp b/radio/src/boards/generic_stm32/module_ports.cpp index f36b80833a1..80622c8e5a7 100644 --- a/radio/src/boards/generic_stm32/module_ports.cpp +++ b/radio/src/boards/generic_stm32/module_ports.cpp @@ -22,6 +22,7 @@ #include "hal/module_port.h" #include "stm32_serial_driver.h" #include "stm32_softserial_driver.h" +#include "stm32_dma.h" #include "module_ports.h" #include "board.h" @@ -86,7 +87,7 @@ static_assert(__STM32_PULSE_IS_TIMER_CHANNEL_SUPPORTED(INTMODULE_TIMER_Channel), "Unsupported timer channel"); // Make sure the DMA channel is supported -static_assert(__STM32_PULSE_IS_DMA_STREAM_SUPPORTED(INTMODULE_TIMER_DMA_STREAM), +static_assert(__STM32_IS_DMA_STREAM_SUPPORTED(INTMODULE_TIMER_DMA_STREAM), "Unsupported DMA stream"); #if !defined(INTMODULE_TIMER_DMA_IRQHandler) @@ -198,7 +199,7 @@ static_assert(__STM32_PULSE_IS_TIMER_CHANNEL_SUPPORTED(EXTMODULE_TIMER_Channel), "Unsupported timer channel"); // Make sure the DMA channel is supported -static_assert(__STM32_PULSE_IS_DMA_STREAM_SUPPORTED(EXTMODULE_TIMER_DMA_STREAM_LL), +static_assert(__STM32_DMA_IS_STREAM_SUPPORTED(EXTMODULE_TIMER_DMA_STREAM_LL), "Unsupported DMA stream"); #if !defined(EXTMODULE_TIMER_DMA_IRQHandler) diff --git a/radio/src/boards/generic_stm32/rgb_leds.cpp b/radio/src/boards/generic_stm32/rgb_leds.cpp new file mode 100644 index 00000000000..78739b59e16 --- /dev/null +++ b/radio/src/boards/generic_stm32/rgb_leds.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) EdgeTx + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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. + */ + +#include "stm32_pulse_driver.h" +#include "rgb_leds.h" +#include "hal.h" +#include "opentx.h" + +#if defined(LED_STRIP_GPIO) + +#include "stm32_ws2812.h" +#include "stm32_dma.h" + +#if !defined(SIMU) +#include +#include +#endif + +extern const stm32_pulse_timer_t _led_timer; + +static TimerHandle_t rgbLedTimer = nullptr; +static StaticTimer_t rgbLedTimerBuffer; + +void rgbSetLedColor(uint8_t led, uint8_t r, uint8_t g, uint8_t b) +{ + ws2812_set_color(led, r, g, b); +} + +void rgbLedColorApply() +{ + ws2812_update(&_led_timer);; +} + +static void rgbLedTimerCb(TimerHandle_t xTimer) +{ + (void)xTimer; + + if (!isFunctionActive(FUNCTION_RGBLED)) { + for (uint8_t i = 0; i < LED_STRIP_LENGTH; i++) { + rgbSetLedColor(i, 0, 0, 0); + } + } + ws2812_update(&_led_timer); +} + +void rgbLedStart() +{ + if (!rgbLedTimer) { + rgbLedTimer = + xTimerCreateStatic("rgbLed", LED_STRIP_REFRESH_PERIOD / RTOS_MS_PER_TICK, pdTRUE, (void*)0, + rgbLedTimerCb, &rgbLedTimerBuffer); + } + + if (rgbLedTimer) { + if( xTimerStart( rgbLedTimer, 0 ) != pdPASS ) { + /* The timer could not be set into the Active state. */ + } + } +} + +void rgbLedStop() +{ + if (rgbLedTimer) { + if( xTimerStop( rgbLedTimer, 5 / RTOS_MS_PER_TICK ) != pdPASS ) { + /* The timer could not be stopped. */ + } + } +} + +const stm32_pulse_timer_t _led_timer = { + .GPIOx = LED_STRIP_GPIO, + .GPIO_Pin = LED_STRIP_GPIO_PIN_DATA, + .GPIO_Alternate = LED_STRIP_GPIO_PIN_AF, + .TIMx = LED_STRIP_TIMER, + .TIM_Freq = LED_STRIP_TIMER_FREQ, + .TIM_Channel = LED_STRIP_TIMER_CHANNEL, + .TIM_IRQn = (IRQn_Type)-1, + .DMAx = LED_STRIP_TIMER_DMA, + .DMA_Stream = LED_STRIP_TIMER_DMA_STREAM, + .DMA_Channel = LED_STRIP_TIMER_DMA_CHANNEL, + .DMA_IRQn = LED_STRIP_TIMER_DMA_IRQn, + .DMA_TC_CallbackPtr = nullptr, +}; + +// Make sure the timer channel is supported +static_assert(__STM32_PULSE_IS_TIMER_CHANNEL_SUPPORTED(LED_STRIP_TIMER_CHANNEL), + "Unsupported timer channel"); + +// Make sure the DMA channel is supported +static_assert(__STM32_DMA_IS_STREAM_SUPPORTED(LED_STRIP_TIMER_DMA_STREAM), + "Unsupported DMA stream"); + +#if !defined(LED_STRIP_TIMER_DMA_IRQHandler) + #error "Missing LED_STRIP_TIMER_DMA_IRQHandler definition" +#endif + +extern "C" void LED_STRIP_TIMER_DMA_IRQHandler() +{ + ws2812_dma_isr(&_led_timer); +} + +#endif diff --git a/radio/src/boards/generic_stm32/rgb_leds.h b/radio/src/boards/generic_stm32/rgb_leds.h new file mode 100644 index 00000000000..535d1c556ca --- /dev/null +++ b/radio/src/boards/generic_stm32/rgb_leds.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) EdgeTx + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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. + */ + +#pragma once + +void rgbLedStart(); +void rgbLedStop(); +void rgbSetLedColor(unsigned char, unsigned char, unsigned char, unsigned char); +void rgbLedColorApply(); + +// void boardInitRGBLed(); diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index 08ea06e26d3..05432b7fd5c 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -596,6 +596,7 @@ enum Functions { FUNC_DISABLE_TOUCH, FUNC_SET_SCREEN, #endif + FUNC_RGB_LED, #if defined(DEBUG) FUNC_TEST, // should remain the last before MAX as not added in Companion #endif @@ -675,6 +676,16 @@ enum ModelOverridableEnable { OVERRIDE_ON }; +// List of modes available for RGB leds +enum FunctionRgbLedsParams { + FUNC_RGBLEDS_LUA, + FUNC_RGBLEDS_WHITE, + FUNC_RGBLEDS_BLUE, + FUNC_RGBLEDS_RED, + FUNC_RGBLEDS_YELLOW, + FUNC_RGBLEDS_GREEN, + FUNC_RGBLEDS_MAX SKIP = FUNC_RGBLEDS_GREEN +}; #define SELECTED_THEME_NAME_LEN 26 #endif // _DATACONSTANTS_H_ diff --git a/radio/src/functions.cpp b/radio/src/functions.cpp index e3da70f9efb..281e75cb09a 100644 --- a/radio/src/functions.cpp +++ b/radio/src/functions.cpp @@ -21,6 +21,7 @@ #include "opentx.h" #include "switches.h" +#include "boards/generic_stm32/rgb_leds.h" #if defined(COLORLCD) void setRequestedMainView(uint8_t view); @@ -411,6 +412,47 @@ void evalFunctions(const CustomFunctionData * functions, CustomFunctionsContext } break; +#if defined(LED_STRIP_GPIO) + case FUNC_RGB_LED: + newActiveFunctions |= (1u << FUNCTION_RGBLED); + + switch (CFN_PARAM(cfn)) { + case FUNC_RGBLEDS_LUA: + // color values are set using LUA + break; + + case FUNC_RGBLEDS_WHITE: + for (uint8_t i = 0; i < LED_STRIP_LENGTH; i++) { + rgbSetLedColor(i, 50, 50, 50); + } + break; + + case FUNC_RGBLEDS_BLUE: + for (uint8_t i = 0; i < LED_STRIP_LENGTH; i++) { + rgbSetLedColor(i, 0, 0, 50); + } + break; + + case FUNC_RGBLEDS_RED: + for (uint8_t i = 0; i < LED_STRIP_LENGTH; i++) { + rgbSetLedColor(i, 50, 0, 0); + } + break; + + case FUNC_RGBLEDS_YELLOW: + for (uint8_t i = 0; i < LED_STRIP_LENGTH; i++) { + rgbSetLedColor(i, 50, 50, 0); + } + break; + + case FUNC_RGBLEDS_GREEN: + for (uint8_t i = 0; i < LED_STRIP_LENGTH; i++) { + rgbSetLedColor(i, 0, 50, 0); + } + break; + } + break; +#endif #if defined(PXX2) case FUNC_RACING_MODE: if (isRacingModeEnabled()) { @@ -531,6 +573,8 @@ const char* funcGetLabel(uint8_t func) case FUNC_SET_SCREEN: return STR_SF_SET_SCREEN; #endif + case FUNC_RGB_LED: + return STR_SF_RGBLEDS; #if defined(DEBUG) case FUNC_TEST: return STR_SF_TEST; diff --git a/radio/src/gui/128x64/model_special_functions.cpp b/radio/src/gui/128x64/model_special_functions.cpp index 94920244757..b9ddf69f5eb 100644 --- a/radio/src/gui/128x64/model_special_functions.cpp +++ b/radio/src/gui/128x64/model_special_functions.cpp @@ -354,6 +354,13 @@ void menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomF INCDEC_ENABLE_CHECK(isSourceAvailable); } } + else if (func == FUNC_RGB_LED) { + val_max = FUNC_RGBLEDS_MAX; + lcdDrawTextAtIndex(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, STR_FUNCRGBLEDS, val_displayed, attr); + if (active) { + CFN_PARAM(cfn) = CHECK_INCDEC_PARAM(event, val_displayed, val_min, val_max); + } + } #if defined(SDCARD) else if (func == FUNC_LOGS) { val_min = SD_LOGS_PERIOD_MIN; diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 694749e7cf7..ba56bdde10a 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -585,6 +585,7 @@ bool isAssignableFunctionAvailable(int function, CustomFunctionData * functions) #endif #if !defined(HAPTIC) case FUNC_HAPTIC: + return false; #endif #if !defined(DANGEROUS_MODULE_FUNCTIONS) case FUNC_RANGECHECK: @@ -595,6 +596,10 @@ bool isAssignableFunctionAvailable(int function, CustomFunctionData * functions) case FUNC_PLAY_SCRIPT: return false; #endif +#if !defined(LED_STRIP_GPIO) + case FUNC_RGB_LED: + return false; +#endif default: return true; diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 02dc9de1df2..cf24f33daa4 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -32,6 +32,10 @@ #include "hal/rotary_encoder.h" #include "switches.h" #include "input_mapping.h" +#if defined(LED_STRIP_GPIO) +#include "boards/generic_stm32/rgb_leds.h" +#endif + #if defined(LIBOPENUI) #include "libopenui.h" @@ -2771,6 +2775,33 @@ static int luaGetTrainerStatus(lua_State * L) return 1; } +#if defined(LED_STRIP_GPIO) \ +/*luadoc +@function setRgbLedColor(id, rvalue, bvalue, cvalue) + +@param id: integer identifying a led in the led chain + +@param rvalue: interger, value of red channel + +@param gvalue: interger, value of green channel + +@param bvalue: interger, value of blue channel + +@status current Introduced in 2.9 +*/ + +static int luaSetRgbLedColor(lua_State * L) +{ + uint8_t id = luaL_checkunsigned(L, 1); + uint8_t r = luaL_checkunsigned(L, 2); + uint8_t g = luaL_checkunsigned(L, 3); + uint8_t b = luaL_checkunsigned(L, 4); + + rgbSetLedColor(id, r, g, b); + + return 1; +} +#endif #define KEY_EVENTS(xxx, yyy) \ { "EVT_"#xxx"_FIRST", LRO_NUMVAL(EVT_KEY_FIRST(yyy)) }, \ @@ -2858,6 +2889,9 @@ LROT_BEGIN(etxlib, NULL, 0) LROT_FUNCENTRY( getSourceIndex, luaGetSourceIndex ) LROT_FUNCENTRY( getSourceName, luaGetSourceName ) LROT_FUNCENTRY( sources, luaSources ) +#if defined(LED_STRIP_GPIO) + LROT_FUNCENTRY(setRgbLedColor, luaSetRgbLedColor ) +#endif LROT_END(etxlib, NULL, 0) LROT_BEGIN(etxcst, NULL, 0) @@ -3194,7 +3228,9 @@ LROT_BEGIN(etxcst, NULL, 0) LROT_NUMENTRY( PLAY_NOW, PLAY_NOW ) LROT_NUMENTRY( PLAY_BACKGROUND, PLAY_BACKGROUND ) LROT_NUMENTRY( TIMEHOUR, TIMEHOUR ) - +#if defined(LED_STRIP_GPIO) + LROT_NUMENTRY( LED_STRIP_LENGTH, LED_STRIP_LENGTH ) +#endif LROT_NUMENTRY( UNIT_RAW, UNIT_RAW ) LROT_NUMENTRY( UNIT_VOLTS, UNIT_VOLTS ) LROT_NUMENTRY( UNIT_AMPS, UNIT_AMPS ) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 219f9b9e562..eeebc2a22f3 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -19,6 +19,11 @@ * GNU General Public License for more details. */ +#if !defined(SIMU) +#include "stm32_ws2812.h" +#include "boards/generic_stm32/rgb_leds.h" +#endif + #include "opentx.h" #include "io/frsky_firmware_update.h" #include "hal/adc_driver.h" @@ -1610,6 +1615,10 @@ void opentxInit() resetBacklightTimeout(); +#if defined(LED_STRIP_GPIO) && !defined(SIMU) + rgbLedStart(); +#endif + pulsesStart(); WDG_ENABLE(WDG_DURATION); } diff --git a/radio/src/opentx.h b/radio/src/opentx.h index aa5c075cf0e..9d9104176ed 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -668,6 +668,9 @@ enum FunctionsActive { #if defined(HARDWARE_TOUCH) FUNCTION_DISABLE_TOUCH, #endif +#if defined(LED_STRIP_GPIO) + FUNCTION_RGBLED, +#endif }; #define VARIO_FREQUENCY_ZERO 700/*Hz*/ diff --git a/radio/src/storage/yaml/yaml_datastructs_128x64.cpp b/radio/src/storage/yaml/yaml_datastructs_128x64.cpp index cd48c94e699..3791c43c92c 100644 --- a/radio/src/storage/yaml/yaml_datastructs_128x64.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_128x64.cpp @@ -81,6 +81,7 @@ const struct YamlIdStr enum_Functions[] = { { FUNC_BACKLIGHT, "BACKLIGHT" }, { FUNC_SCREENSHOT, "SCREENSHOT" }, { FUNC_RACING_MODE, "RACING_MODE" }, + { FUNC_RGB_LED, "RGB_LED" }, { 0, NULL } }; const struct YamlIdStr enum_TimerModes[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_funcs.cpp b/radio/src/storage/yaml/yaml_datastructs_funcs.cpp index 35b47e53f61..64f37e55684 100644 --- a/radio/src/storage/yaml/yaml_datastructs_funcs.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_funcs.cpp @@ -1302,6 +1302,11 @@ static const char* const _func_sound_lookup[] = { "SciF","Robt","Chrp","Tada","Crck","Alrm" }; +static const char* const _func_rgbled_lookup[] = { + "LUA","White","Blue","Red","Yellow", + "Green" +}; + static const char* const _adjust_gvar_mode_lookup[] = { "Cst", "Src", "GVar", "IncDec" }; @@ -1402,6 +1407,16 @@ static void r_customFn(void* user, uint8_t* data, uint32_t bitoffs, } break; + case FUNC_RGB_LED: + // find "," and cut val_len + for (unsigned i=0; i < DIM(_func_rgbled_lookup); i++) { + if (!strncmp(_func_rgbled_lookup[i],val,l_sep)) { + CFN_PARAM(cfn) = i; + break; + } + } + break; + case FUNC_PLAY_TRACK: case FUNC_BACKGND_MUSIC: case FUNC_PLAY_SCRIPT: @@ -1608,6 +1623,12 @@ static bool w_customFn(void* user, uint8_t* data, uint32_t bitoffs, if (!wf(opaque, str, strlen(str))) return false; break; + case FUNC_RGB_LED: + // Lua, WHite, Blue, Red, Yellow, Green + str = _func_rgbled_lookup[CFN_PARAM(cfn)]; + if (!wf(opaque, str, strlen(str))) return false; + break; + case FUNC_PLAY_TRACK: case FUNC_BACKGND_MUSIC: case FUNC_PLAY_SCRIPT: diff --git a/radio/src/storage/yaml/yaml_datastructs_nv14.cpp b/radio/src/storage/yaml/yaml_datastructs_nv14.cpp index 2417bfe1592..d30a8274563 100644 --- a/radio/src/storage/yaml/yaml_datastructs_nv14.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_nv14.cpp @@ -83,6 +83,7 @@ const struct YamlIdStr enum_Functions[] = { { FUNC_RACING_MODE, "RACING_MODE" }, { FUNC_DISABLE_TOUCH, "DISABLE_TOUCH" }, { FUNC_SET_SCREEN, "SET_SCREEN" }, + { FUNC_RGB_LED, "RGB_LED" }, { 0, NULL } }; const struct YamlIdStr enum_ZoneOptionValueEnum[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_t20.cpp b/radio/src/storage/yaml/yaml_datastructs_t20.cpp index 3cb3d757193..f2fd39ac3d0 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t20.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t20.cpp @@ -81,6 +81,7 @@ const struct YamlIdStr enum_Functions[] = { { FUNC_BACKLIGHT, "BACKLIGHT" }, { FUNC_SCREENSHOT, "SCREENSHOT" }, { FUNC_RACING_MODE, "RACING_MODE" }, + { FUNC_RGB_LED, "RGB_LED" }, { 0, NULL } }; const struct YamlIdStr enum_TimerModes[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_tpro.cpp b/radio/src/storage/yaml/yaml_datastructs_tpro.cpp index 18458320d54..592d7684db4 100644 --- a/radio/src/storage/yaml/yaml_datastructs_tpro.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_tpro.cpp @@ -81,6 +81,7 @@ const struct YamlIdStr enum_Functions[] = { { FUNC_BACKLIGHT, "BACKLIGHT" }, { FUNC_SCREENSHOT, "SCREENSHOT" }, { FUNC_RACING_MODE, "RACING_MODE" }, + { FUNC_RGB_LED, "RGB_LED" }, { 0, NULL } }; const struct YamlIdStr enum_TimerModes[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_x10.cpp b/radio/src/storage/yaml/yaml_datastructs_x10.cpp index 54ba13f5ffa..1da82423a35 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x10.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x10.cpp @@ -83,6 +83,7 @@ const struct YamlIdStr enum_Functions[] = { { FUNC_RACING_MODE, "RACING_MODE" }, { FUNC_DISABLE_TOUCH, "DISABLE_TOUCH" }, { FUNC_SET_SCREEN, "SET_SCREEN" }, + { FUNC_RGB_LED, "RGB_LED" }, { 0, NULL } }; const struct YamlIdStr enum_ZoneOptionValueEnum[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_x12s.cpp b/radio/src/storage/yaml/yaml_datastructs_x12s.cpp index 54ba13f5ffa..1da82423a35 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x12s.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x12s.cpp @@ -83,6 +83,7 @@ const struct YamlIdStr enum_Functions[] = { { FUNC_RACING_MODE, "RACING_MODE" }, { FUNC_DISABLE_TOUCH, "DISABLE_TOUCH" }, { FUNC_SET_SCREEN, "SET_SCREEN" }, + { FUNC_RGB_LED, "RGB_LED" }, { 0, NULL } }; const struct YamlIdStr enum_ZoneOptionValueEnum[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_x9d.cpp b/radio/src/storage/yaml/yaml_datastructs_x9d.cpp index d06903e3aa8..6b0e01723c2 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9d.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9d.cpp @@ -81,6 +81,7 @@ const struct YamlIdStr enum_Functions[] = { { FUNC_BACKLIGHT, "BACKLIGHT" }, { FUNC_SCREENSHOT, "SCREENSHOT" }, { FUNC_RACING_MODE, "RACING_MODE" }, + { FUNC_RGB_LED, "RGB_LED" }, { 0, NULL } }; const struct YamlIdStr enum_TimerModes[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_x9e.cpp b/radio/src/storage/yaml/yaml_datastructs_x9e.cpp index 86e2aeef5f8..d1854516a93 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9e.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9e.cpp @@ -81,6 +81,7 @@ const struct YamlIdStr enum_Functions[] = { { FUNC_BACKLIGHT, "BACKLIGHT" }, { FUNC_SCREENSHOT, "SCREENSHOT" }, { FUNC_RACING_MODE, "RACING_MODE" }, + { FUNC_RGB_LED, "RGB_LED" }, { 0, NULL } }; const struct YamlIdStr enum_TimerModes[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_x9lite.cpp b/radio/src/storage/yaml/yaml_datastructs_x9lite.cpp index a3a4dd488d5..a3b3d4ef52b 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9lite.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9lite.cpp @@ -81,6 +81,7 @@ const struct YamlIdStr enum_Functions[] = { { FUNC_BACKLIGHT, "BACKLIGHT" }, { FUNC_SCREENSHOT, "SCREENSHOT" }, { FUNC_RACING_MODE, "RACING_MODE" }, + { FUNC_RGB_LED, "RGB_LED" }, { 0, NULL } }; const struct YamlIdStr enum_TimerModes[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_xlites.cpp b/radio/src/storage/yaml/yaml_datastructs_xlites.cpp index a27f02d078b..bfc72b21beb 100644 --- a/radio/src/storage/yaml/yaml_datastructs_xlites.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_xlites.cpp @@ -81,6 +81,7 @@ const struct YamlIdStr enum_Functions[] = { { FUNC_BACKLIGHT, "BACKLIGHT" }, { FUNC_SCREENSHOT, "SCREENSHOT" }, { FUNC_RACING_MODE, "RACING_MODE" }, + { FUNC_RGB_LED, "RGB_LED" }, { 0, NULL } }; const struct YamlIdStr enum_TimerModes[] = { diff --git a/radio/src/targets/common/arm/stm32/stm32_dma.h b/radio/src/targets/common/arm/stm32/stm32_dma.h index 9a1af355932..9e94a882ddc 100644 --- a/radio/src/targets/common/arm/stm32/stm32_dma.h +++ b/radio/src/targets/common/arm/stm32/stm32_dma.h @@ -24,13 +24,25 @@ #include #include "stm32_hal_ll.h" +#define __STM32_DMA_IS_STREAM_SUPPORTED(stream) \ + ((stream) == LL_DMA_STREAM_1 || (stream) == LL_DMA_STREAM_3 || \ + (stream) == LL_DMA_STREAM_5 || (stream) == LL_DMA_STREAM_6 || \ + (stream) == LL_DMA_STREAM_7) + inline static bool stm32_dma_check_tc_flag(DMA_TypeDef* DMAx, uint32_t DMA_Stream) { + if (!LL_DMA_IsEnabledIT_TC(DMAx, DMA_Stream)) + return false; + switch(DMA_Stream) { case LL_DMA_STREAM_1: if (!LL_DMA_IsActiveFlag_TC1(DMAx)) return false; LL_DMA_ClearFlag_TC1(DMAx); break; + case LL_DMA_STREAM_3: + if (!LL_DMA_IsActiveFlag_TC3(DMAx)) return false; + LL_DMA_ClearFlag_TC3(DMAx); + break; case LL_DMA_STREAM_5: if (!LL_DMA_IsActiveFlag_TC5(DMAx)) return false; LL_DMA_ClearFlag_TC5(DMAx); @@ -48,4 +60,35 @@ inline static bool stm32_dma_check_tc_flag(DMA_TypeDef* DMAx, uint32_t DMA_Strea return true; } +inline static bool stm32_dma_check_ht_flag(DMA_TypeDef* DMAx, uint32_t DMA_Stream) +{ + if (!LL_DMA_IsEnabledIT_HT(DMAx, DMA_Stream)) + return false; + + switch(DMA_Stream) { + case LL_DMA_STREAM_1: + if (!LL_DMA_IsActiveFlag_HT1(DMAx)) return false; + LL_DMA_ClearFlag_HT1(DMAx); + break; + case LL_DMA_STREAM_3: + if (!LL_DMA_IsActiveFlag_HT3(DMAx)) return false; + LL_DMA_ClearFlag_HT3(DMAx); + break; + case LL_DMA_STREAM_5: + if (!LL_DMA_IsActiveFlag_HT5(DMAx)) return false; + LL_DMA_ClearFlag_HT5(DMAx); + break; + case LL_DMA_STREAM_6: + if (!LL_DMA_IsActiveFlag_HT6(DMAx)) return false; + LL_DMA_ClearFlag_HT6(DMAx); + break; + case LL_DMA_STREAM_7: + if (!LL_DMA_IsActiveFlag_HT7(DMAx)) return false; + LL_DMA_ClearFlag_HT7(DMAx); + break; + } + + return true; +} + void stm32_dma_enable_clock(DMA_TypeDef* DMAx); diff --git a/radio/src/targets/common/arm/stm32/stm32_pulse_driver.cpp b/radio/src/targets/common/arm/stm32/stm32_pulse_driver.cpp index eefdf53497b..c895e7eeb78 100644 --- a/radio/src/targets/common/arm/stm32/stm32_pulse_driver.cpp +++ b/radio/src/targets/common/arm/stm32/stm32_pulse_driver.cpp @@ -35,8 +35,48 @@ // - DMA IRQ prio configurable (now 7) // - Timer IRQ prio configurable (now 7) -void stm32_pulse_init(const stm32_pulse_timer_t* tim, uint32_t freq) +static void init_dma_arr_mode(const stm32_pulse_timer_t* tim) { + // re-init DMA stream + LL_DMA_DeInit(tim->DMAx, tim->DMA_Stream); + + LL_DMA_InitTypeDef dmaInit; + LL_DMA_StructInit(&dmaInit); + + // Direction + dmaInit.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + + // Source + dmaInit.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + + // Destination + dmaInit.PeriphOrM2MSrcAddress = CONVERT_PTR_UINT(&tim->TIMx->ARR); + dmaInit.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + + // Data width + if (IS_TIM_32B_COUNTER_INSTANCE(tim->TIMx)) { + dmaInit.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dmaInit.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + } else { + dmaInit.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD; + dmaInit.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD; + } + + dmaInit.Channel = tim->DMA_Channel; + dmaInit.Priority = LL_DMA_PRIORITY_VERYHIGH; + + LL_DMA_Init(tim->DMAx, tim->DMA_Stream, &dmaInit); +} + +int stm32_pulse_init(const stm32_pulse_timer_t* tim, uint32_t freq) +{ + // check if timer is free first (clock disabled) + if (stm32_timer_is_clock_enabled(tim->TIMx)) return -1; + + // .. and GPIO pin is not used + uint32_t pin_mode = LL_GPIO_GetPinMode(tim->GPIOx, tim->GPIO_Pin); + if (pin_mode != LL_GPIO_MODE_INPUT) return -1; + if (tim->DMA_TC_CallbackPtr) { memset(tim->DMA_TC_CallbackPtr, 0, sizeof(stm32_pulse_dma_tc_cb_t)); } @@ -61,24 +101,30 @@ void stm32_pulse_init(const stm32_pulse_timer_t* tim, uint32_t freq) stm32_timer_enable_clock(tim->TIMx); LL_TIM_Init(tim->TIMx, &timInit); - if (tim->DMAx) { - // Enable DMA IRQ + if (tim->DMAx && (int32_t)tim->DMA_IRQn >= 0) { + init_dma_arr_mode(tim); NVIC_EnableIRQ(tim->DMA_IRQn); NVIC_SetPriority(tim->DMA_IRQn, 7); } - // Enable timer IRQ - NVIC_EnableIRQ(tim->TIM_IRQn); - NVIC_SetPriority(tim->TIM_IRQn, 7); + if ((int32_t)tim->TIM_IRQn >= 0) { + NVIC_EnableIRQ(tim->TIM_IRQn); + NVIC_SetPriority(tim->TIM_IRQn, 7); + } + + return 0; } void stm32_pulse_deinit(const stm32_pulse_timer_t* tim) { // Disable IRQs - if (tim->DMAx) { + if ((int32_t)tim->DMA_IRQn >= 0) { NVIC_DisableIRQ(tim->DMA_IRQn); } - NVIC_DisableIRQ(tim->TIM_IRQn); + + if ((int32_t)tim->TIM_IRQn >= 0) { + NVIC_DisableIRQ(tim->TIM_IRQn); + } if (tim->DMAx) { LL_DMA_DeInit(tim->DMAx, tim->DMA_Stream); @@ -100,6 +146,25 @@ void stm32_pulse_deinit(const stm32_pulse_timer_t* tim) LL_GPIO_Init(tim->GPIOx, &pinInit); } +static inline bool _is_complementary_channel(uint32_t channel) +{ + return (channel == LL_TIM_CHANNEL_CH1N) || (channel == LL_TIM_CHANNEL_CH2N) || + (channel == LL_TIM_CHANNEL_CH3N); +} + +static const uint32_t _base_channel[] = { + LL_TIM_CHANNEL_CH1, LL_TIM_CHANNEL_CH1, + LL_TIM_CHANNEL_CH2, LL_TIM_CHANNEL_CH2, + LL_TIM_CHANNEL_CH3, LL_TIM_CHANNEL_CH3, + LL_TIM_CHANNEL_CH4 +}; + +static inline uint32_t _get_base_channel(uint32_t channel) +{ + auto idx = TIM_GET_CHANNEL_INDEX(channel); + return _base_channel[idx]; +} + void stm32_pulse_config_output(const stm32_pulse_timer_t* tim, bool polarity, uint32_t ocmode, uint32_t cmp_val) { @@ -109,14 +174,14 @@ void stm32_pulse_config_output(const stm32_pulse_timer_t* tim, bool polarity, ocInit.OCMode = ocmode; ocInit.CompareValue = cmp_val; - uint32_t channel = tim->TIM_Channel; - if (tim->TIM_Channel != LL_TIM_CHANNEL_CH1N) { + uint32_t channel = _get_base_channel(tim->TIM_Channel); + bool comp_ch = _is_complementary_channel(tim->TIM_Channel); + if (!comp_ch) { ocInit.OCState = LL_TIM_OCSTATE_ENABLE; ocInit.OCNState = LL_TIM_OCSTATE_DISABLE; } else { ocInit.OCState = LL_TIM_OCSTATE_DISABLE; ocInit.OCNState = LL_TIM_OCSTATE_ENABLE; - channel = LL_TIM_CHANNEL_CH1; } uint32_t ll_polarity; @@ -126,7 +191,7 @@ void stm32_pulse_config_output(const stm32_pulse_timer_t* tim, bool polarity, ll_polarity = LL_TIM_OCPOLARITY_LOW; } - if (tim->TIM_Channel != LL_TIM_CHANNEL_CH1N) { + if (!comp_ch) { ocInit.OCPolarity = ll_polarity; } else { ocInit.OCNPolarity = ll_polarity; @@ -138,7 +203,6 @@ void stm32_pulse_config_output(const stm32_pulse_timer_t* tim, bool polarity, if (IS_TIM_BREAK_INSTANCE(tim->TIMx)) { LL_TIM_EnableAllOutputs(tim->TIMx); } - } void stm32_pulse_set_polarity(const stm32_pulse_timer_t* tim, bool polarity) @@ -173,9 +237,8 @@ bool stm32_pulse_if_not_running_disable(const stm32_pulse_timer_t* tim) static void set_compare_reg(const stm32_pulse_timer_t* tim, uint32_t val) { - switch(tim->TIM_Channel){ + switch(_get_base_channel(tim->TIM_Channel)){ case LL_TIM_CHANNEL_CH1: - case LL_TIM_CHANNEL_CH1N: LL_TIM_OC_SetCompareCH1(tim->TIMx, val); break; case LL_TIM_CHANNEL_CH2: @@ -190,36 +253,42 @@ static void set_compare_reg(const stm32_pulse_timer_t* tim, uint32_t val) } } +void stm32_pulse_set_period(const stm32_pulse_timer_t* tim, uint32_t period) +{ + LL_TIM_SetAutoReload(tim->TIMx, period - 1); +} + void stm32_pulse_set_cmp_val(const stm32_pulse_timer_t* tim, uint32_t cmp_val) { set_compare_reg(tim, cmp_val); } -static void set_oc_mode(const stm32_pulse_timer_t* tim, uint32_t ocmode) +void stm32_pulse_start(const stm32_pulse_timer_t* tim) +{ + LL_TIM_EnableCounter(tim->TIMx); +} + +void stm32_pulse_stop(const stm32_pulse_timer_t* tim) { - uint32_t channel = tim->TIM_Channel; - if (channel == LL_TIM_CHANNEL_CH1N) - channel = LL_TIM_CHANNEL_CH1; + LL_TIM_DisableCounter(tim->TIMx); +} +static void set_oc_mode(const stm32_pulse_timer_t* tim, uint32_t ocmode) +{ + uint32_t channel = _get_base_channel(tim->TIM_Channel); LL_TIM_OC_SetMode(tim->TIMx, channel, ocmode); } void stm32_pulse_wait_for_completed(const stm32_pulse_timer_t* tim) { - uint32_t channel = tim->TIM_Channel; - if (channel == LL_TIM_CHANNEL_CH1N) - channel = LL_TIM_CHANNEL_CH1; - + uint32_t channel = _get_base_channel(tim->TIM_Channel); while(LL_TIM_IsEnabledCounter(tim->TIMx) && LL_TIM_OC_GetMode(tim->TIMx, channel) != LL_TIM_OCMODE_FORCED_INACTIVE); } static void force_start_level(const stm32_pulse_timer_t* tim) { - uint32_t channel = tim->TIM_Channel; - if (channel == LL_TIM_CHANNEL_CH1N) - channel = LL_TIM_CHANNEL_CH1; - + uint32_t channel = _get_base_channel(tim->TIM_Channel); uint32_t mode = LL_TIM_OC_GetMode(tim->TIMx, channel); LL_TIM_OC_SetMode(tim->TIMx, channel, LL_TIM_OCMODE_FORCED_ACTIVE); LL_TIM_OC_SetMode(tim->TIMx, channel, mode); @@ -232,55 +301,19 @@ void stm32_pulse_start_dma_req(const stm32_pulse_timer_t* tim, // Re-configure timer output set_compare_reg(tim, cmp_val); set_oc_mode(tim, ocmode); - - // re-init DMA stream - LL_DMA_DeInit(tim->DMAx, tim->DMA_Stream); - LL_DMA_InitTypeDef dmaInit; - LL_DMA_StructInit(&dmaInit); - - // Direction - dmaInit.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - - // Source - dmaInit.MemoryOrM2MDstAddress = CONVERT_PTR_UINT(pulses); - dmaInit.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - - // Destination - dmaInit.PeriphOrM2MSrcAddress = CONVERT_PTR_UINT(&tim->TIMx->ARR); - dmaInit.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - - // Data width - if (IS_TIM_32B_COUNTER_INSTANCE(tim->TIMx)) { - // TODO: try using 16-bit source as well - dmaInit.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dmaInit.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - } else { - dmaInit.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD; - dmaInit.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD; - } - - dmaInit.NbData = length; - dmaInit.Channel = tim->DMA_Channel; - dmaInit.Priority = LL_DMA_PRIORITY_VERYHIGH; - - LL_DMA_Init(tim->DMAx, tim->DMA_Stream, &dmaInit); + LL_DMA_SetDataLength(tim->DMAx, tim->DMA_Stream, length); + LL_DMA_SetMemoryAddress(tim->DMAx, tim->DMA_Stream, (uint32_t)pulses); // Enable TC IRQ LL_DMA_EnableIT_TC(tim->DMAx, tim->DMA_Stream); - if (ocmode == LL_TIM_OCMODE_PWM1) { - // preloads first period for PWM) - LL_TIM_SetCounter(tim->TIMx, 0x00); + // Reset counter + LL_TIM_SetCounter(tim->TIMx, 0x00); + + // only on PWM (preloads the first period) + if (ocmode == LL_TIM_OCMODE_PWM1) LL_TIM_GenerateEvent_UPDATE(tim->TIMx); - } else { - // Reset counter close to overflow - if (IS_TIM_32B_COUNTER_INSTANCE(tim->TIMx)) { - LL_TIM_SetCounter(tim->TIMx, 0xFFFFFFFF); - } else { - LL_TIM_SetCounter(tim->TIMx, 0xFFFF); - } - } LL_TIM_EnableDMAReq_UPDATE(tim->TIMx); LL_DMA_EnableStream(tim->DMAx, tim->DMA_Stream); diff --git a/radio/src/targets/common/arm/stm32/stm32_pulse_driver.h b/radio/src/targets/common/arm/stm32/stm32_pulse_driver.h index 1197f00d307..f32858edafb 100644 --- a/radio/src/targets/common/arm/stm32/stm32_pulse_driver.h +++ b/radio/src/targets/common/arm/stm32/stm32_pulse_driver.h @@ -50,7 +50,7 @@ struct stm32_pulse_timer_t { stm32_pulse_dma_tc_cb_t* DMA_TC_CallbackPtr; }; -void stm32_pulse_init(const stm32_pulse_timer_t* tim, uint32_t freq); +int stm32_pulse_init(const stm32_pulse_timer_t* tim, uint32_t freq); void stm32_pulse_deinit(const stm32_pulse_timer_t* tim); void stm32_pulse_config_input(const stm32_pulse_timer_t* tim); @@ -62,8 +62,13 @@ void stm32_pulse_set_polarity(const stm32_pulse_timer_t* tim, bool polarity); bool stm32_pulse_get_polarity(const stm32_pulse_timer_t* tim); +void stm32_pulse_set_period(const stm32_pulse_timer_t* tim, uint32_t period); + void stm32_pulse_set_cmp_val(const stm32_pulse_timer_t* tim, uint32_t cmp_val); +void stm32_pulse_start(const stm32_pulse_timer_t* tim); +void stm32_pulse_stop(const stm32_pulse_timer_t* tim); + void stm32_pulse_wait_for_completed(const stm32_pulse_timer_t* tim); // return true if it could be disabled without interrupting a pulse train, false otherwise @@ -81,9 +86,6 @@ void stm32_pulse_tim_update_isr(const stm32_pulse_timer_t* tim); #define __STM32_PULSE_IS_TIMER_CHANNEL_SUPPORTED(ch) \ ((ch) == LL_TIM_CHANNEL_CH1 || (ch) == LL_TIM_CHANNEL_CH1N || \ - (ch) == LL_TIM_CHANNEL_CH2 || (ch) == LL_TIM_CHANNEL_CH3 || \ + (ch) == LL_TIM_CHANNEL_CH2 || (ch) == LL_TIM_CHANNEL_CH2N || \ + (ch) == LL_TIM_CHANNEL_CH3 || (ch) == LL_TIM_CHANNEL_CH3N || \ (ch) == LL_TIM_CHANNEL_CH4) - -#define __STM32_PULSE_IS_DMA_STREAM_SUPPORTED(stream) \ - ((stream) == LL_DMA_STREAM_1 || (stream) == LL_DMA_STREAM_5 || \ - (stream) == LL_DMA_STREAM_7) diff --git a/radio/src/targets/common/arm/stm32/stm32_timer.cpp b/radio/src/targets/common/arm/stm32/stm32_timer.cpp index 11c1b08b6c2..f8ff6f93ebd 100644 --- a/radio/src/targets/common/arm/stm32/stm32_timer.cpp +++ b/radio/src/targets/common/arm/stm32/stm32_timer.cpp @@ -54,3 +54,23 @@ void stm32_timer_disable_clock(TIM_TypeDef *TIMx) LL_APB2_GRP1_DisableClock(LL_APB2_GRP1_PERIPH_TIM8); } } + +bool stm32_timer_is_clock_enabled(TIM_TypeDef *TIMx) +{ + if (TIMx == TIM1) { + return LL_APB2_GRP1_IsEnabledClock(LL_APB2_GRP1_PERIPH_TIM1) != 0; + } else if (TIMx == TIM2) { + return LL_APB1_GRP1_IsEnabledClock(LL_APB1_GRP1_PERIPH_TIM2) != 0; + } else if (TIMx == TIM3) { + return LL_APB1_GRP1_IsEnabledClock(LL_APB1_GRP1_PERIPH_TIM3) != 0; + } else if (TIMx == TIM4) { + return LL_APB1_GRP1_IsEnabledClock(LL_APB1_GRP1_PERIPH_TIM4) != 0; + } else if (TIMx == TIM5) { + return LL_APB1_GRP1_IsEnabledClock(LL_APB1_GRP1_PERIPH_TIM5) != 0; + } else if (TIMx == TIM8) { + return LL_APB2_GRP1_IsEnabledClock(LL_APB2_GRP1_PERIPH_TIM8) != 0; + } + + // not supported + return false; +} diff --git a/radio/src/targets/common/arm/stm32/stm32_timer.h b/radio/src/targets/common/arm/stm32/stm32_timer.h index e597fd1a530..c7c13796740 100644 --- a/radio/src/targets/common/arm/stm32/stm32_timer.h +++ b/radio/src/targets/common/arm/stm32/stm32_timer.h @@ -25,3 +25,5 @@ void stm32_timer_enable_clock(TIM_TypeDef *TIMx); void stm32_timer_disable_clock(TIM_TypeDef *TIMx); + +bool stm32_timer_is_clock_enabled(TIM_TypeDef *TIMx); diff --git a/radio/src/targets/common/arm/stm32/stm32_ws2812.cpp b/radio/src/targets/common/arm/stm32/stm32_ws2812.cpp new file mode 100644 index 00000000000..827313c2d2c --- /dev/null +++ b/radio/src/targets/common/arm/stm32/stm32_ws2812.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (C) EdgeTx + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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. + */ + +#include "stm32_ws2812.h" +#include "stm32_dma.h" + +#if defined(DEBUG_WS2812) + // LED_STRIP_DEBUG_GPIO && LED_STRIP_DEBUG_GPIO_PIN + #include "hal.h" +#endif + +#include "definitions.h" + +#include + +// Pixel values +static uint8_t _led_colors[WS2812_BYTES_PER_LED * WS2812_MAX_LEDS]; + +// Timer used +// static const stm32_pulse_timer_t* _led_timer; + +// LED strip length +static uint8_t _led_strip_len; + +// DMA buffer contains data for 2 LEDs and is filled +// half by half on DMA HT and TC IRQs +#define WS2821_DMA_BUFFER_HALF_LEN (WS2812_BYTES_PER_LED * 8) +#define WS2821_DMA_BUFFER_LEN (WS2821_DMA_BUFFER_HALF_LEN * 2) + +#define WS2812_FREQ 800000UL // 800 kHz +#define WS2812_TIMER_PERIOD 20UL +#define WS2812_ONE (3 * WS2812_TIMER_PERIOD / 4) +#define WS2812_ZERO (1 * WS2812_TIMER_PERIOD / 4) +#define WS2812_DMA_IRQ_PRIO 3 + +// Debug facility +#if defined(LED_STRIP_DEBUG_GPIO) && defined(LED_STRIP_DEBUG_GPIO_PIN) + +#define WS2812_DBG_INIT _led_dbg_init() + +#define WS2812_DBG_HIGH \ + LL_GPIO_SetOutputPin(LED_STRIP_DEBUG_GPIO, LED_STRIP_DEBUG_GPIO_PIN) + +#define WS2812_DBG_LOW \ + LL_GPIO_ResetOutputPin(LED_STRIP_DEBUG_GPIO, LED_STRIP_DEBUG_GPIO_PIN) + +static void _led_dbg_init() { + LL_GPIO_InitTypeDef pinInit{0}; + pinInit.Mode = LL_GPIO_MODE_OUTPUT; + pinInit.Pin = LED_STRIP_DEBUG_GPIO_PIN; + LL_GPIO_Init(LED_STRIP_DEBUG_GPIO, &pinInit); + WS2812_DBG_LOW; +} + +#else // LED_STRIP_DEBUG_GPIO && LED_STRIP_DEBUG_GPIO_PIN + +#define WS2812_DBG_INIT +#define WS2812_DBG_HIGH +#define WS2812_DBG_LOW + +#endif + +#if defined(STM32_SUPPORT_32BIT_TIMERS) +typedef uint32_t led_timer_value_t; +#else +typedef uint16_t led_timer_value_t; +#endif + +// DMA buffer contains pulses for 2 LED at a time +// (allows for refill at HT and TC) +static led_timer_value_t _led_dma_buffer[WS2821_DMA_BUFFER_LEN] __DMA; + +static uint8_t _led_seq_cnt; + +static void _fill_byte(uint8_t c, led_timer_value_t* dma_buffer) +{ + for (int i = 0; i < 8; i++) { + dma_buffer[i] = c & 1 ? WS2812_ONE : WS2812_ZERO; + c >>= 1; + } +} + +static void _fill_pulses(const uint8_t* colors, led_timer_value_t* dma_buffer, uint32_t len) +{ + for (uint32_t i = 0; i < len; i++) { + _fill_byte(*colors, dma_buffer); + dma_buffer += 8; + colors++; + } +} + +static inline uint32_t _calc_offset(uint8_t tc) +{ + return tc * WS2821_DMA_BUFFER_HALF_LEN; +} + +static void _update_dma_buffer(const stm32_pulse_timer_t* tim, uint8_t tc) +{ + WS2812_DBG_HIGH; + if (_led_seq_cnt < _led_strip_len) { + + auto idx = WS2812_BYTES_PER_LED * _led_seq_cnt; + auto offset = _calc_offset(tc); + _fill_pulses(&_led_colors[idx], &_led_dma_buffer[offset], WS2812_BYTES_PER_LED); + _led_seq_cnt++; + + } else if(_led_seq_cnt < _led_strip_len + WS2812_TRAILING_RESET) { + + // no need to reset the buffer after 2 cycles + if (_led_seq_cnt < _led_strip_len + 2) { + auto offset = _calc_offset(tc); + auto size = WS2821_DMA_BUFFER_HALF_LEN * sizeof(led_timer_value_t); + memset(&_led_dma_buffer[offset], 0, size); + } + _led_seq_cnt++; + + } else { + + LL_DMA_DisableIT_TC(tim->DMAx, tim->DMA_Stream); + LL_DMA_DisableIT_HT(tim->DMAx, tim->DMA_Stream); + LL_DMA_DisableStream(tim->DMAx, tim->DMA_Stream); + LL_TIM_CC_DisableChannel(tim->TIMx, tim->TIM_Channel); + } + WS2812_DBG_LOW; +} + +void ws2812_dma_isr(const stm32_pulse_timer_t* tim) +{ + if (stm32_dma_check_ht_flag(tim->DMAx, tim->DMA_Stream)) { + _update_dma_buffer(tim, 0); + } + + if (stm32_dma_check_tc_flag(tim->DMAx, tim->DMA_Stream)) { + _update_dma_buffer(tim, 1); + } +} + +static void _led_set_dma_periph_addr(const stm32_pulse_timer_t* tim) +{ + volatile uint32_t* cmp_reg = nullptr; + switch(tim->TIM_Channel) { + case LL_TIM_CHANNEL_CH1: + case LL_TIM_CHANNEL_CH1N: + cmp_reg = &tim->TIMx->CCR1; + break; + case LL_TIM_CHANNEL_CH2: + cmp_reg = &tim->TIMx->CCR2; + break; + case LL_TIM_CHANNEL_CH3: + cmp_reg = &tim->TIMx->CCR3; + break; + case LL_TIM_CHANNEL_CH4: + cmp_reg = &tim->TIMx->CCR4; + break; + } + + LL_DMA_SetPeriphAddress(tim->DMAx, tim->DMA_Stream, (uint32_t)cmp_reg); +} + +static void _init_timer(const stm32_pulse_timer_t* tim) +{ + stm32_pulse_init(tim, WS2812_FREQ * WS2812_TIMER_PERIOD); + stm32_pulse_config_output(tim, true, LL_TIM_OCMODE_PWM1, 0); + LL_TIM_SetAutoReload(tim->TIMx, WS2812_TIMER_PERIOD - 1); + + // pulse driver uses DMA to ARR, but we need CCRx + _led_set_dma_periph_addr(tim); + + LL_DMA_SetMode(tim->DMAx, tim->DMA_Stream, LL_DMA_MODE_CIRCULAR); + LL_DMA_SetDataLength(tim->DMAx, tim->DMA_Stream, WS2821_DMA_BUFFER_LEN); + LL_DMA_SetMemoryAddress(tim->DMAx, tim->DMA_Stream, (uint32_t)_led_dma_buffer); + + // we need to use a higher prio to avoid having + // issues with some other things used during boot + NVIC_SetPriority(tim->DMA_IRQn, WS2812_DMA_IRQ_PRIO); +} + +void ws2812_init(const stm32_pulse_timer_t* timer, uint8_t strip_len) +{ + WS2812_DBG_INIT; + + memset(_led_colors, 0, sizeof(_led_colors)); + memset(_led_dma_buffer, 0, sizeof(_led_dma_buffer)); + + if (strip_len <= WS2812_MAX_LEDS) { + _led_strip_len = strip_len; + } else { + _led_strip_len = WS2812_MAX_LEDS; + } + + _init_timer(timer); +} + +void ws2812_set_color(uint8_t led, uint8_t r, uint8_t g, uint8_t b) +{ + if (led >= _led_strip_len) return; + + uint8_t* pixel = &_led_colors[led * WS2812_BYTES_PER_LED]; + pixel[0] = g; + pixel[1] = r; + pixel[2] = b; +} + +void ws2812_update(const stm32_pulse_timer_t* tim) +{ + WS2812_DBG_HIGH; + if (!stm32_pulse_if_not_running_disable(tim)) return; + + _led_seq_cnt = 0; + memset(_led_dma_buffer, 0, sizeof(_led_dma_buffer)); + + LL_DMA_EnableIT_HT(tim->DMAx, tim->DMA_Stream); + LL_DMA_EnableIT_TC(tim->DMAx, tim->DMA_Stream); + LL_DMA_EnableStream(tim->DMAx, tim->DMA_Stream); + + LL_TIM_EnableDMAReq_UPDATE(tim->TIMx); + LL_TIM_CC_EnableChannel(tim->TIMx, tim->TIM_Channel); + LL_TIM_EnableCounter(tim->TIMx); + + WS2812_DBG_LOW; +} diff --git a/radio/src/targets/common/arm/stm32/stm32_ws2812.h b/radio/src/targets/common/arm/stm32/stm32_ws2812.h new file mode 100644 index 00000000000..cc332303811 --- /dev/null +++ b/radio/src/targets/common/arm/stm32/stm32_ws2812.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) EdgeTx + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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. + */ + +#pragma once + +#include "stm32_pulse_driver.h" + +// RGB +#define WS2812_BYTES_PER_LED 3 +#define WS2812_MAX_LEDS 8 + +// Number of LED periods used for trailing reset +#define WS2812_TRAILING_RESET 10 + +void ws2812_init(const stm32_pulse_timer_t* timer, uint8_t strip_len); +void ws2812_update(const stm32_pulse_timer_t* timer); +void ws2812_dma_isr(const stm32_pulse_timer_t* timer); + +void ws2812_set_color(uint8_t led, uint8_t r, uint8_t g, uint8_t b); diff --git a/radio/src/targets/simu/led_driver.cpp b/radio/src/targets/simu/led_driver.cpp index ede5cc2c61d..34d1c77a873 100644 --- a/radio/src/targets/simu/led_driver.cpp +++ b/radio/src/targets/simu/led_driver.cpp @@ -29,3 +29,5 @@ void ledOff() {} void fsLedOn(uint8_t) {} void fsLedOff(uint8_t) {} bool getFSLedState(uint8_t) { return false;} +void rgbSetLedColor(unsigned char, unsigned char, unsigned char, unsigned char) {} +void rgbLedColorApply() {} diff --git a/radio/src/targets/taranis/board.cpp b/radio/src/targets/taranis/board.cpp index 9c17cbb7195..e27b812fb31 100644 --- a/radio/src/targets/taranis/board.cpp +++ b/radio/src/targets/taranis/board.cpp @@ -19,6 +19,8 @@ * GNU General Public License for more details. */ +#include "stm32_ws2812.h" + #include "hal/adc_driver.h" #include "hal/switch_driver.h" #include "hal/module_port.h" @@ -27,6 +29,7 @@ #include "boards/generic_stm32/module_ports.h" #include "boards/generic_stm32/intmodule_heartbeat.h" #include "boards/generic_stm32/analog_inputs.h" +#include "boards/generic_stm32/rgb_leds.h" #include "debug.h" #include "rtc.h" @@ -228,6 +231,16 @@ void boardInit() init1msTimer(); usbInit(); +#if defined(LED_STRIP_GPIO) + extern const stm32_pulse_timer_t _led_timer; + + ws2812_init(&_led_timer, LED_STRIP_LENGTH); + for (uint8_t i = 0; i < LED_STRIP_LENGTH; i++) { + ws2812_set_color(i, 0, 0, 50); + } + ws2812_update(&_led_timer); +#endif + #if defined(DEBUG) serialInit(SP_AUX1, UART_MODE_DEBUG); #endif diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index d9ce70ba9be..65b8e135bb1 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -45,6 +45,7 @@ ISTR(VMLTPX2); ISTR(VCSWFUNC); ISTR(VFSWRESET); ISTR(FUNCSOUNDS); +ISTR(FUNCRGBLEDS); ISTR(VTELEMUNIT); ISTR(GPSFORMAT); ISTR(VTELEMSCREENTYPE); @@ -187,6 +188,7 @@ const char STR_SF_SET_TIMER[] = TR_SF_SET_TIMER; const char STR_SF_SWITCH[] = TR_SF_SWITCH; const char STR_SF_TRAINER[] = TR_SF_TRAINER; const char STR_SF_VARIO[] = TR_SF_VARIO; +const char STR_SF_RGBLEDS[] = TR_SF_RGBLEDS; const char STR_SF_VOLUME[] = TR_SF_VOLUME; const char STR_SF_RACING_MODE[] = TR_SF_RACING_MODE; const char STR_SF_SAFETY[] = TR_SF_SAFETY; diff --git a/radio/src/translations.h b/radio/src/translations.h index 5d7c0daa0b0..e7f5db88486 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -156,6 +156,7 @@ extern const char* const STR_VMLTPX2[]; extern const char* const STR_VCSWFUNC[]; extern const char* const STR_VFSWRESET[]; extern const char* const STR_FUNCSOUNDS[]; +extern const char* const STR_FUNCRGBLEDS[]; extern const char* const STR_VTELEMUNIT[]; extern const char* const STR_GPSFORMAT[]; extern const char* const STR_VTELEMSCREENTYPE[]; @@ -310,6 +311,7 @@ extern const char STR_SF_SET_SCREEN[]; extern const char STR_SF_SWITCH[]; extern const char STR_SF_TRAINER[]; extern const char STR_SF_VARIO[]; +extern const char STR_SF_RGBLEDS[]; extern const char STR_SF_VOLUME[]; extern const char STR_SF_RACING_MODE[]; extern const char STR_SF_SCREENSHOT[]; diff --git a/radio/src/translations/cn.h b/radio/src/translations/cn.h index 74eca2d69bb..866723f63d0 100644 --- a/radio/src/translations/cn.h +++ b/radio/src/translations/cn.h @@ -91,6 +91,7 @@ #define TR_SF_FAILSAFE "设置失控保护" #define TR_SF_RANGE_CHECK "测距模式" #define TR_SF_MOD_BIND "模块对频" +#define TR_SF_RGBLEDS "RGB leds" #define TR_SOUND "播放声音" #define TR_PLAY_TRACK "播放音频文件" @@ -122,6 +123,7 @@ #define TR_VFSWRESET TR_FSW_RESET_TIMERS,TR("全部","飞行"),TR_FSW_RESET_TELEM +#define TR_FUNCRGBLEDS "LUA","White","Blue","Red","Yellow","Green" #define TR_FUNCSOUNDS TR("Bp1","Beep1"),TR("Bp2","Beep2"),TR("Bp3","Beep3"),TR("Wrn1","Warn1"),TR("Wrn2","Warn2"),TR("Chee","Cheep"),TR("Rata","Ratata"),"Tick",TR("Sirn","Siren"),"Ring",TR("SciF","SciFi"),TR("Robt","Robot"),TR("Chrp","Chirp"),"Tada",TR("Crck","Crickt"),TR("Alrm","AlmClk") #define LENGTH_UNIT_IMP "ft" diff --git a/radio/src/translations/cz.h b/radio/src/translations/cz.h index bd593437f5c..76e431706a4 100644 --- a/radio/src/translations/cz.h +++ b/radio/src/translations/cz.h @@ -105,6 +105,7 @@ #define TR_SF_FAILSAFE "Nastavit Failsafe" #define TR_SF_RANGE_CHECK "Kontrola dosahu" #define TR_SF_MOD_BIND "ModuleBind" +#define TR_SF_RGBLEDS "RGB leds" #define TR_SOUND TR3("\200\201Zvuk", "\200\201Zvuk", "Hrát zvuk") #define TR_PLAY_TRACK TR3("\200\201Stopa", "\200\201Stopa", "Přehrát wav") @@ -134,6 +135,7 @@ #endif #define TR_VFSWRESET TR_FSW_RESET_TIMERS,"Vše",TR_FSW_RESET_TELEM +#define TR_FUNCRGBLEDS "LUA","White","Blue","Red","Yellow","Green" #define TR_FUNCSOUNDS TR("Píp1","Pípnutí1"),TR("Píp2","Pípnutí2"),TR("Píp3","Pípnutí3"),TR("Var1","Varování1"),TR("Var2","Varování2"),TR("Chee","Cheep"),TR("Rata", "Ratata"),"Tick",TR("Sirn","Siréna"),"Ring",TR("SciF","SciFi"),TR("Robt","Robot"),TR("Chrp","Chirp"),"Tada",TR("Crck","Crickt"),TR("Alrm","AlmClk") #define LENGTH_UNIT_IMP "ft" diff --git a/radio/src/translations/da.h b/radio/src/translations/da.h index ce333305ce3..0b8aa13a100 100644 --- a/radio/src/translations/da.h +++ b/radio/src/translations/da.h @@ -103,6 +103,7 @@ #define TR_SF_FAILSAFE "SetFejlsikr" #define TR_SF_RANGE_CHECK "AfstandCheck" #define TR_SF_MOD_BIND "ModuleBind" +#define TR_SF_RGBLEDS "RGB leds" #define TR_SOUND "Spil lyd" #define TR_PLAY_TRACK TR("Afspil lyd", "Afspil lydfil") @@ -134,6 +135,7 @@ #define TR_VFSWRESET TR_FSW_RESET_TIMERS,TR("Alle","Flight"),TR_FSW_RESET_TELEM +#define TR_FUNCRGBLEDS "LUA","White","Blue","Red","Yellow","Green" #define TR_FUNCSOUNDS TR("Bi1","Bip1"),TR("Bi2","Bip2"),TR("Bi3","Bi3"),TR("Adv1","Advarsel1"),TR("Adv2","Advarsel2"),TR("Chee","Cheep"),TR("Rata","Ratata"),"Tick",TR("Sirn","Siren"),"Ring",TR("SciF","SciFi"),TR("Robt","Robot"),TR("Chrp","Chirp"),"Tada",TR("Crck","Crickt"),TR("Alrm","AlmClk") #define LENGTH_UNIT_IMP "ft" diff --git a/radio/src/translations/de.h b/radio/src/translations/de.h index caee9f528b7..fce638cd8c3 100644 --- a/radio/src/translations/de.h +++ b/radio/src/translations/de.h @@ -100,7 +100,8 @@ #define TR_SF_FAILSAFE "SetFailsafe" #define TR_SF_RANGE_CHECK "RangeCheck" #define TR_SF_MOD_BIND "ModuleBind" - +#define TR_SF_RGBLEDS "RGB leds" + #define TR_SOUND "Spiel Töne" #define TR_PLAY_TRACK "Sag Text" #define TR_PLAY_VALUE "Sag Wert" @@ -130,6 +131,7 @@ #define TR_VFSWRESET TR_FSW_RESET_TIMERS,"All",TR_FSW_RESET_TELEM +#define TR_FUNCRGBLEDS "LUA","White","Blue","Red","Yellow","Green" #define TR_FUNCSOUNDS TR("Bp1","Piep1"),TR("Bp2","Piep2"),TR("Bp3","Piep3"),TR("Wrn1","Warn1"),TR("Wrn2","Warn2"),TR("Chee","Cheep"),TR("Rata","Ratata"),"Tick",TR("Sirn","Siren"),"Ring",TR("SciF","SciFi"),TR("Robt","Robot"),TR("Chrp","Chirp"),"Tada",TR("Crck","Crickt"),TR("Alrm","AlmClk") #define LENGTH_UNIT_IMP "ft" diff --git a/radio/src/translations/en.h b/radio/src/translations/en.h index af8a0fe7fd9..69c19a21afd 100644 --- a/radio/src/translations/en.h +++ b/radio/src/translations/en.h @@ -96,7 +96,8 @@ #define TR_SF_FAILSAFE "SetFailsafe" #define TR_SF_RANGE_CHECK "RangeCheck" #define TR_SF_MOD_BIND "ModuleBind" - +#define TR_SF_RGBLEDS "RGB leds" + #define TR_SOUND "Play Sound" #define TR_PLAY_TRACK "Play Track" #define TR_PLAY_VALUE TR("Play Val","Play Value") @@ -132,6 +133,7 @@ #define TR_VFSWRESET TR_FSW_RESET_TIMERS,TR("All","Flight"),TR_FSW_RESET_TELEM +#define TR_FUNCRGBLEDS "LUA","White","Blue","Red","Yellow","Green" #define TR_FUNCSOUNDS TR("Bp1","Beep1"),TR("Bp2","Beep2"),TR("Bp3","Beep3"),TR("Wrn1","Warn1"),TR("Wrn2","Warn2"),TR("Chee","Cheep"),TR("Rata","Ratata"),"Tick",TR("Sirn","Siren"),"Ring",TR("SciF","SciFi"),TR("Robt","Robot"),TR("Chrp","Chirp"),"Tada",TR("Crck","Crickt"),TR("Alrm","AlmClk") #define LENGTH_UNIT_IMP "ft" diff --git a/radio/src/translations/es.h b/radio/src/translations/es.h index cbec8d6edf0..34e6f2a1417 100644 --- a/radio/src/translations/es.h +++ b/radio/src/translations/es.h @@ -98,6 +98,7 @@ #define TR_SF_FAILSAFE "Failsafe" #define TR_SF_RANGE_CHECK "CheckRango" #define TR_SF_MOD_BIND "Enl.módulo" +#define TR_SF_RGBLEDS "RGB leds" #define TR_SOUND "Oir sonido" #define TR_PLAY_TRACK "Oir pista" @@ -128,6 +129,7 @@ #define TR_VFSWRESET TR_FSW_RESET_TIMERS,TR("Todo","Vuelo"),TR_FSW_RESET_TELEM +#define TR_FUNCRGBLEDS "LUA","White","Blue","Red","Yellow","Green" #define TR_FUNCSOUNDS TR("Bp1","Beep1"),TR("Bp2","Beep2"),TR("Bp3","Beep3"),TR("Avs1","Aviso1"),TR("Avs2","Aviso2"),TR("Chee","Cheep"),TR("Rata","Ratata"),"Tick",TR("Sirn","Siren"),"Ring",TR("SciF","SciFi"),TR("Robt","Robot"),TR("Chrp","Chirp"),"Tada",TR("Crck","Crickt"),TR("Alrm","AlmClk") #define LENGTH_UNIT_IMP "ft" diff --git a/radio/src/translations/fi.h b/radio/src/translations/fi.h index bf74d88be93..2211f5457ec 100644 --- a/radio/src/translations/fi.h +++ b/radio/src/translations/fi.h @@ -103,6 +103,7 @@ #define TR_SF_FAILSAFE "SetFailsafe" #define TR_SF_RANGE_CHECK "RangeCheck" #define TR_SF_MOD_BIND "ModuleBind" +#define TR_SF_RGBLEDS "RGB leds" #define TR_SOUND "Play Sound" #define TR_PLAY_TRACK "Play Track" @@ -133,6 +134,7 @@ #define TR_VFSWRESET TR_FSW_RESET_TIMERS,"All",TR_FSW_RESET_TELEM +#define TR_FUNCRGBLEDS "LUA","White","Blue","Red","Yellow","Green" #define TR_FUNCSOUNDS TR("Bp1","Beep1"),TR("Bp2","Beep2"),TR("Bp3","Beep3"),TR("Wrn1","Warn1"),TR("Wrn2","Warn2"),TR("Chee","Cheep"),TR("Rata","Ratata"),"Tick",TR("Sirn","Siren"),"Ring",TR("SciF","SciFi"),TR("Robt","Robot"),TR("Chrp","Chirp"),"Tada",TR("Crck","Crickt"),TR("Alrm","AlmClk") diff --git a/radio/src/translations/fr.h b/radio/src/translations/fr.h index 70dccd99bbd..bebaedbc401 100644 --- a/radio/src/translations/fr.h +++ b/radio/src/translations/fr.h @@ -102,6 +102,7 @@ #define TR_SF_FAILSAFE "Régle Failsafe" #define TR_SF_RANGE_CHECK "Test Port." #define TR_SF_MOD_BIND "Bind" +#define TR_SF_RGBLEDS "RGB leds" #define TR_SOUND "Jouer son" #define TR_PLAY_TRACK "Jouer fichier" @@ -131,6 +132,7 @@ #endif #define TR_VFSWRESET TR_FSW_RESET_TIMERS, "Tout", TR_FSW_RESET_TELEM +#define TR_FUNCRGBLEDS "LUA","White","Blue","Red","Yellow","Green" #define TR_FUNCSOUNDS TR("Bp1","Beep1"),TR("Bp2","Beep2"),TR("Bp3","Beep3"),TR("Wrn1","Warn1"),TR("Wrn2","Warn2"),TR("Chee","Cheep"),TR("Rata","Ratata"),"Tick",TR("Sirn","Siren"),"Ring",TR("SciF","SciFi"),TR("Robt","Robot"),TR("Chrp","Chirp"),"Tada",TR("Crck","Crickt"),TR("Alrm","AlmClk") diff --git a/radio/src/translations/he.h b/radio/src/translations/he.h index 1cfde313d06..271eb3abc72 100644 --- a/radio/src/translations/he.h +++ b/radio/src/translations/he.h @@ -185,6 +185,7 @@ #define TR_VFSWRESET TR_FSW_RESET_TIMERS,TR("All","Flight"),TR_FSW_RESET_TELEM +#define TR_FUNCRGBLEDS "LUA","White","Blue","Red","Yellow","Green" #define TR_FUNCSOUNDS TR("Bp1","Beep1"),TR("Bp2","Beep2"),TR("Bp3","Beep3"),TR("Wrn1","Warn1"),TR("Wrn2","Warn2"),TR("Chee","Cheep"),TR("Rata","Ratata"),"Tick",TR("Sirn","Siren"),"Ring",TR("SciF","SciFi"),TR("Robt","Robot"),TR("Chrp","Chirp"),"Tada",TR("Crck","Crickt"),TR("Alrm","AlmClk") #define LENGTH_UNIT_IMP "ft" diff --git a/radio/src/translations/it.h b/radio/src/translations/it.h index 85d9d3d1199..a21f5dffa03 100644 --- a/radio/src/translations/it.h +++ b/radio/src/translations/it.h @@ -101,6 +101,7 @@ #define TR_SF_FAILSAFE "SetFailsafe" #define TR_SF_RANGE_CHECK "RangeCheck" #define TR_SF_MOD_BIND "BindModulo" +#define TR_SF_RGBLEDS "RGB leds" #define TR_SOUND "Suona" #define TR_PLAY_TRACK "Suona Traccia" @@ -131,6 +132,7 @@ #define TR_VFSWRESET TR_FSW_RESET_TIMERS,TR("All","Tutto"),TR_FSW_RESET_TELEM +#define TR_FUNCRGBLEDS "LUA","White","Blue","Red","Yellow","Green" #define TR_FUNCSOUNDS TR("Bp1","Beep1"),TR("Bp2","Beep2"),TR("Bp3","Beep3"),TR("Wrn1","Warn1"),TR("Wrn2","Warn2"),TR("Chee","Cheep"),TR("Rata","Ratata"),"Tick",TR("Sirn","Siren"),"Ring",TR("SciF","SciFi"),TR("Robt","Robot"),TR("Chrp","Chirp"),"Tada",TR("Crck","Crickt"),TR("Alrm","AlmClk") #define LENGTH_UNIT_IMP "ft" diff --git a/radio/src/translations/jp.h b/radio/src/translations/jp.h index 2dd967f9c2c..d232d5dc6e3 100644 --- a/radio/src/translations/jp.h +++ b/radio/src/translations/jp.h @@ -96,6 +96,7 @@ #define TR_SF_FAILSAFE "フェイルセーフ" #define TR_SF_RANGE_CHECK "レンジチェック" #define TR_SF_MOD_BIND "モジュールバインド" +#define TR_SF_RGBLEDS "RGB leds" #define TR_SOUND "サウンド再生" #define TR_PLAY_TRACK "音源再生" @@ -127,6 +128,7 @@ #define TR_VFSWRESET TR_FSW_RESET_TIMERS,TR("すべて","飛行時"),TR_FSW_RESET_TELEM +#define TR_FUNCRGBLEDS "LUA","White","Blue","Red","Yellow","Green" #define TR_FUNCSOUNDS TR("Bp1","Beep1"),TR("Bp2","Beep2"),TR("Bp3","Beep3"),TR("Wrn1","Warn1"),TR("Wrn2","Warn2"),TR("Chee","Cheep"),TR("Rata","Ratata"),"Tick",TR("Sirn","Siren"),"Ring",TR("SciF","SciFi"),TR("Robt","Robot"),TR("Chrp","Chirp"),"Tada",TR("Crck","Crickt"),TR("Alrm","AlmClk") #define LENGTH_UNIT_IMP "ft" diff --git a/radio/src/translations/nl.h b/radio/src/translations/nl.h index 7fd646559af..68f2ce5e729 100644 --- a/radio/src/translations/nl.h +++ b/radio/src/translations/nl.h @@ -98,6 +98,7 @@ #define TR_SF_FAILSAFE "SetFailsafe" #define TR_SF_RANGE_CHECK "RangeCheck" #define TR_SF_MOD_BIND "ModuleBind" +#define TR_SF_RGBLEDS "RGB leds" #define TR_SOUND "Geluid" #define TR_PLAY_TRACK "Play Track" @@ -129,6 +130,7 @@ #define TR_VFSWRESET TR_FSW_RESET_TIMERS,TR("All","Vliegdata"),TR_FSW_RESET_TELEM +#define TR_FUNCRGBLEDS "LUA","White","Blue","Red","Yellow","Green" #define TR_FUNCSOUNDS TR("Bp1","Beep1"),TR("Bp2","Beep2"),TR("Bp3","Beep3"),TR("Wrn1","Warn1"),TR("Wrn2","Warn2"),TR("Chee","Cheep"),TR("Rata","Ratata"),"Tick",TR("Sirn","Siren"),"Ring",TR("SciF","SciFi"),TR("Robt","Robot"),TR("Chrp","Chirp"),"Tada",TR("Crck","Crickt"),TR("Alrm","AlmClk") #define LENGTH_UNIT_IMP "ft" diff --git a/radio/src/translations/pl.h b/radio/src/translations/pl.h index 725a24f69c4..d88940b18c4 100644 --- a/radio/src/translations/pl.h +++ b/radio/src/translations/pl.h @@ -97,6 +97,7 @@ #define TR_SF_FAILSAFE "SetFailsafe" #define TR_SF_RANGE_CHECK "RangeCheck" #define TR_SF_MOD_BIND "ModuleBind" +#define TR_SF_RGBLEDS "RGB leds" #define TR_SOUND "GrajDźwięk" #define TR_PLAY_TRACK "GrajŚcieżk" @@ -126,6 +127,7 @@ #endif #define TR_VFSWRESET TR_FSW_RESET_TIMERS,TR("All","Flight"),TR_FSW_RESET_TELEM +#define TR_FUNCRGBLEDS "LUA","White","Blue","Red","Yellow","Green" #define TR_FUNCSOUNDS TR("Bp1","Beep1"),TR("Bp2","Beep2"),TR("Bp3","Beep3"),TR("Ost1","Ostrz1"),TR("Ost2","Ostrz2"),TR("Chee","Cheep"),TR("Rata","Ratata"),"Tick",TR("Syre","Syrena"),TR("Dzwo","Dzwone"),TR("Crck","Krytcz"),TR("Alrm","AlmZeg") #define LENGTH_UNIT_IMP "ft" diff --git a/radio/src/translations/pt.h b/radio/src/translations/pt.h index 3d7c6c4c7c7..517957a27a2 100644 --- a/radio/src/translations/pt.h +++ b/radio/src/translations/pt.h @@ -99,6 +99,7 @@ #define TR_SF_FAILSAFE "DefFailsafe" #define TR_SF_RANGE_CHECK "RangeCheck" #define TR_SF_MOD_BIND "BindModulo" +#define TR_SF_RGBLEDS "RGB leds" #define TR_SOUND "Tocar Som" #define TR_PLAY_TRACK "Tocar Trilha" @@ -135,6 +136,7 @@ #define TR_VFSWRESET TR_FSW_RESET_TIMERS,TR("All","Flight"),TR_FSW_RESET_TELEM +#define TR_FUNCRGBLEDS "LUA","White","Blue","Red","Yellow","Green" #define TR_FUNCSOUNDS TR("Bp1","Beep1"),TR("Bp2","Beep2"),TR("Bp3","Beep3"),TR("Wrn1","Warn1"),TR("Wrn2","Warn2"),TR("Chee","Cheep"),TR("Rata","Ratata"),"Tick",TR("Sirn","Siren"),"Ring",TR("SciF","SciFi"),TR("Robt","Robot"),TR("Chrp","Chirp"),"Tada",TR("Crck","Crickt"),TR("Alrm","AlmClk") #define LENGTH_UNIT_IMP "ft" diff --git a/radio/src/translations/se.h b/radio/src/translations/se.h index bb45aa02527..7b18c0facf3 100644 --- a/radio/src/translations/se.h +++ b/radio/src/translations/se.h @@ -107,6 +107,7 @@ #define TR_SF_FAILSAFE "Sätt failsafe" #define TR_SF_RANGE_CHECK "Range check" #define TR_SF_MOD_BIND "Bind modul" +#define TR_SF_RGBLEDS "RGB leds" #define TR_SOUND "Spela ljud" #define TR_PLAY_TRACK "Spela upp" @@ -137,6 +138,7 @@ #endif #define TR_VFSWRESET TR_FSW_RESET_TIMERS,"Alla",TR_FSW_RESET_TELEM +#define TR_FUNCRGBLEDS "LUA","White","Blue","Red","Yellow","Green" #define TR_FUNCSOUNDS TR("Bp1","Beep1"),TR("Bp2","Beep2"),TR("Bp3","Beep3"),TR("Wrn1","Warn1"),TR("Wrn2","Warn2"),TR("Chee","Cheep"),TR("Rata","Ratata"),"Tick",TR("Sirn","Siren"),"Ring",TR("SciF","SciFi"),TR("Robt","Robot"),TR("Chrp","Chirp"),"Tada",TR("Crck","Crickt"),TR("Alrm","AlmClk") diff --git a/radio/src/translations/tw.h b/radio/src/translations/tw.h index af3ff5ad082..6dce7c8f29f 100644 --- a/radio/src/translations/tw.h +++ b/radio/src/translations/tw.h @@ -97,6 +97,7 @@ #define TR_SF_FAILSAFE "設置失控保護" #define TR_SF_RANGE_CHECK "測距模式" #define TR_SF_MOD_BIND "模塊對頻" +#define TR_SF_RGBLEDS "RGB leds" #define TR_SOUND "播放聲音" #define TR_PLAY_TRACK "播放音頻文件" @@ -127,6 +128,7 @@ #define TR_VFSWRESET TR_FSW_RESET_TIMERS,TR("全部","飛行"),TR_FSW_RESET_TELEM +#define TR_FUNCRGBLEDS "LUA","White","Blue","Red","Yellow","Green" #define TR_FUNCSOUNDS TR("Bp1","Beep1"),TR("Bp2","Beep2"),TR("Bp3","Beep3"),TR("Wrn1","Warn1"),TR("Wrn2","Warn2"),TR("Chee","Cheep"),TR("Rata","Ratata"),"Tick",TR("Sirn","Siren"),"Ring",TR("SciF","SciFi"),TR("Robt","Robot"),TR("Chrp","Chirp"),"Tada",TR("Crck","Crickt"),TR("Alrm","AlmClk") #define LENGTH_UNIT_IMP "ft"