diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 69076f2b6..a951d29b3 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -4,12 +4,14 @@ on: push: branches: - '2.4' + - 'main' paths-ignore: - '**.md' - '.github/ISSUE_TEMPLATE/**' pull_request: branches: - '2.4' + - 'main' paths-ignore: - '**.md' - '.github/ISSUE_TEMPLATE/**' diff --git a/CMakeLists.txt b/CMakeLists.txt index 80c365e64..8b14b9256 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ project(OpenTX) set(VERSION_MAJOR "2") -set(VERSION_MINOR "4") +set(VERSION_MINOR "5") set(VERSION_REVISION "0") set(VERSION_SUFFIX $ENV{OPENTX_VERSION_SUFFIX}) set(VERSION_FAMILY ${VERSION_MAJOR}.${VERSION_MINOR}) @@ -47,7 +47,7 @@ today(DATE) now(TIME) git_id(GIT_STR) -message(STATUS "OpenTX ${VERSION} @ ${GIT_STR}") +message(STATUS "EdgeTX ${VERSION} @ ${GIT_STR}") # options shared by all targets diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index 5c17f8723..6ace554bf 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -672,6 +672,22 @@ PACK(struct ModelData { return thrTrimSw + MIXSRC_FIRST_TRIM; } } + + void setThrottleStickTrimSource(int16_t src) + { + // The order here is TERA, so that 0 (default) means Throttle + switch (src) { + case MIXSRC_TrimThr: + thrTrimSw = 0; + break; + case MIXSRC_TrimRud: + thrTrimSw = 2; + break; + default: + thrTrimSw = src - MIXSRC_FIRST_TRIM; + break; + } + } }); /* diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 9a5f4bd69..8ac5fc51f 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -631,12 +631,13 @@ void menuModelSetup(event_t event) case ITEM_MODEL_SETUP_THROTTLE_TRACE: { lcdDrawTextAlignedLeft(y, STR_TTRACE); - if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, g_model.thrTraceSrc, NUM_POTS+NUM_SLIDERS+MAX_OUTPUT_CHANNELS); - uint8_t idx = g_model.thrTraceSrc + MIXSRC_Thr; - if (idx > MIXSRC_Thr) - idx += 1; - if (idx >= MIXSRC_FIRST_POT+NUM_POTS+NUM_SLIDERS) - idx += MIXSRC_CH1 - MIXSRC_FIRST_POT - NUM_POTS - NUM_SLIDERS; + if (attr) + CHECK_INCDEC_MODELVAR_ZERO_CHECK( + event, g_model.thrTraceSrc, + NUM_POTS + NUM_SLIDERS + MAX_OUTPUT_CHANNELS, + isThrottleSourceAvailable); + + uint8_t idx = throttleSource2Source(g_model.thrTraceSrc); drawSource(MODEL_SETUP_2ND_COLUMN, y, idx, attr); break; } diff --git a/radio/src/gui/212x64/model_setup.cpp b/radio/src/gui/212x64/model_setup.cpp index 5b187a545..2bc541987 100644 --- a/radio/src/gui/212x64/model_setup.cpp +++ b/radio/src/gui/212x64/model_setup.cpp @@ -606,12 +606,13 @@ void menuModelSetup(event_t event) case ITEM_MODEL_SETUP_THROTTLE_TRACE: { lcdDrawTextAlignedLeft(y, STR_TTRACE); - if (attr) CHECK_INCDEC_MODELVAR_ZERO_CHECK(event, g_model.thrTraceSrc, NUM_POTS+NUM_SLIDERS+MAX_OUTPUT_CHANNELS, isThrottleSourceAvailable); - uint8_t idx = g_model.thrTraceSrc + MIXSRC_Thr; - if (idx > MIXSRC_Thr) - idx += 1; - if (idx >= MIXSRC_FIRST_POT+NUM_POTS+NUM_SLIDERS) - idx += MIXSRC_CH1 - MIXSRC_FIRST_POT - NUM_POTS - NUM_SLIDERS; + if (attr) + CHECK_INCDEC_MODELVAR_ZERO_CHECK( + event, g_model.thrTraceSrc, + NUM_POTS + NUM_SLIDERS + MAX_OUTPUT_CHANNELS, + isThrottleSourceAvailable); + + uint8_t idx = throttleSource2Source(g_model.thrTraceSrc); drawSource(MODEL_SETUP_2ND_COLUMN, y, idx, attr); break; } diff --git a/radio/src/gui/colorlcd/CMakeLists.txt b/radio/src/gui/colorlcd/CMakeLists.txt index 367430657..b5aea580f 100644 --- a/radio/src/gui/colorlcd/CMakeLists.txt +++ b/radio/src/gui/colorlcd/CMakeLists.txt @@ -77,6 +77,7 @@ endmacro(add_gui_src) if(LUA) add_gui_src(standalone_lua.cpp) + add_gui_src(model_mixer_scripts.cpp) endif() if(HELI) diff --git a/radio/src/gui/colorlcd/colors.h b/radio/src/gui/colorlcd/colors.h index 93c83152c..a670fde42 100644 --- a/radio/src/gui/colorlcd/colors.h +++ b/radio/src/gui/colorlcd/colors.h @@ -27,6 +27,9 @@ // TODO common code, not in target enum LcdColorIndex { + // this one first for when colour is not set + DEFAULT_COLOR_INDEX, + ALARM_COLOR_INDEX, BARGRAPH1_COLOR_INDEX, BARGRAPH2_COLOR_INDEX, @@ -38,7 +41,6 @@ enum LcdColorIndex CURVE_CURSOR_COLOR_INDEX, CUSTOM_COLOR_INDEX, DEFAULT_BGCOLOR_INDEX, - DEFAULT_COLOR_INDEX, DISABLE_COLOR_INDEX, EDIT_MARKER_COLOR_INDEX, FIELD_BGCOLOR_INDEX, diff --git a/radio/src/gui/colorlcd/draw_functions.cpp b/radio/src/gui/colorlcd/draw_functions.cpp index a0530c2b0..e08966048 100644 --- a/radio/src/gui/colorlcd/draw_functions.cpp +++ b/radio/src/gui/colorlcd/draw_functions.cpp @@ -416,6 +416,12 @@ void drawSensorCustomValue(BitmapBuffer * dc, coord_t x, coord_t y, uint8_t sens } } +// libopemui defines TIMOUR as zero +#if !TIMHOUR +#undef TIMEHOUR +#define TIMEHOUR 0x2000 +#endif + void drawTimer(BitmapBuffer * dc, coord_t x, coord_t y, int32_t tme, LcdFlags flags) { char str[LEN_TIMER_STRING]; @@ -437,7 +443,8 @@ void drawSourceCustomValue(BitmapBuffer * dc, coord_t x, coord_t y, source_t sou } else if (source >= MIXSRC_FIRST_TIMER || source == MIXSRC_TX_TIME) { // TODO if (value < 0) flags |= BLINK|INVERS; - // TODO drawTimer(dc, x, y, value, flags); + if (source == MIXSRC_TX_TIME) flags |= TIMEHOUR; + drawTimer(dc, x, y, value, flags); } else if (source == MIXSRC_TX_VOLTAGE) { dc->drawNumber(x, y, value, flags|PREC1); diff --git a/radio/src/gui/colorlcd/getset_helpers.h b/radio/src/gui/colorlcd/getset_helpers.h index a13695433..7d7c930dc 100644 --- a/radio/src/gui/colorlcd/getset_helpers.h +++ b/radio/src/gui/colorlcd/getset_helpers.h @@ -36,7 +36,7 @@ #define GET_SET_DEFAULT(value) GET_DEFAULT(value), SET_DEFAULT(value) #define GET_SET_INVERTED(value) GET_INVERTED(value), SET_INVERTED(value) #define GET_SET_VALUE_WITH_OFFSET(value, offset) GET_VALUE_WITH_OFFSET(value, offset), SET_VALUE_WITH_OFFSET(value, offset) -#define GET_SET_WITH_OFFSET(value, offset) GET_VALUE_WITH_OFFSET(value, offset), SET_VALUE_WITH_OFFSET(value, offset) +#define GET_SET_WITH_OFFSET(value, offset) GET_VALUE_WITH_OFFSET(value, offset), SET_VALUE_WITH_OFFSET(value, offset) #define GET_SET_BF(value, offset, bits) GET_VALUE_WITH_BF(value, offset, bits), SET_VALUE_WITH_BF(value, offset, bits) #endif // _GETSET_HELPERS_H_ diff --git a/radio/src/gui/colorlcd/layouts/trims.cpp b/radio/src/gui/colorlcd/layouts/trims.cpp index 312d7adbf..1336522a1 100644 --- a/radio/src/gui/colorlcd/layouts/trims.cpp +++ b/radio/src/gui/colorlcd/layouts/trims.cpp @@ -25,7 +25,7 @@ void MainViewTrim::checkEvents() { Window::checkEvents(); int8_t stickIndex = CONVERT_MODE(idx); - int8_t newValue = getTrimValue(mixerCurrentFlightMode, stickIndex); + int newValue = getTrimValue(mixerCurrentFlightMode, stickIndex); if (value != newValue) { value = newValue; invalidate(); @@ -43,6 +43,10 @@ void MainViewHorizontalTrim::paint(BitmapBuffer * dc) trimMin = TRIM_MIN; trimMax = TRIM_MAX; } + + int16_t dispVal = value; + if (dispVal < trimMin) dispVal = trimMin; + if (dispVal > trimMax) dispVal = trimMax; // Trim line dc->drawSolidFilledRect( @@ -50,7 +54,7 @@ void MainViewHorizontalTrim::paint(BitmapBuffer * dc) width() - TRIM_SQUARE_SIZE + 1, TRIM_LINE_WIDTH, DEFAULT_COLOR); // Trim square - coord_t x = divRoundClosest((width() - TRIM_SQUARE_SIZE) * (value - trimMin), + coord_t x = divRoundClosest((width() - TRIM_SQUARE_SIZE) * (dispVal - trimMin), trimMax - trimMin); drawTrimSquare(dc, x, 0, (value < TRIM_MIN || value > TRIM_MAX) @@ -85,15 +89,20 @@ void MainViewVerticalTrim::paint(BitmapBuffer * dc) trimMax = TRIM_MAX; } + int16_t dispVal = value; + if (dispVal < trimMin) dispVal = trimMin; + if (dispVal > trimMax) dispVal = trimMax; + // Trim line dc->drawSolidFilledRect((width() - TRIM_LINE_WIDTH) / 2, TRIM_SQUARE_SIZE / 2, TRIM_LINE_WIDTH, height() - TRIM_SQUARE_SIZE + 1, DEFAULT_COLOR); // Trim square - coord_t y = height() - TRIM_SQUARE_SIZE - - divRoundClosest((height() - TRIM_SQUARE_SIZE) * (value - trimMin), - trimMax - trimMin); + coord_t y = + height() - TRIM_SQUARE_SIZE - + divRoundClosest((height() - TRIM_SQUARE_SIZE) * (dispVal - trimMin), + trimMax - trimMin); drawTrimSquare(dc, 0, y, (value < TRIM_MIN || value > TRIM_MAX) ? HIGHLIGHT_COLOR /* TODO add a color */ diff --git a/radio/src/gui/colorlcd/menu_model.cpp b/radio/src/gui/colorlcd/menu_model.cpp index 3d19a8a41..1b9f028d2 100644 --- a/radio/src/gui/colorlcd/menu_model.cpp +++ b/radio/src/gui/colorlcd/menu_model.cpp @@ -29,6 +29,7 @@ #include "model_gvars.h" #include "model_curves.h" #include "model_logical_switches.h" +#include "model_mixer_scripts.h" #include "special_functions.h" #include "model_telemetry.h" @@ -47,5 +48,8 @@ ModelMenu::ModelMenu(): addTab(new ModelGVarsPage()); addTab(new ModelLogicalSwitchesPage()); addTab(new SpecialFunctionsPage(g_model.customFn)); +#if defined(LUA) + addTab(new ModelMixerScriptsPage()); +#endif addTab(new ModelTelemetryPage()); } diff --git a/radio/src/gui/colorlcd/model_inputs.cpp b/radio/src/gui/colorlcd/model_inputs.cpp index a28b1e0dd..2157ac2b3 100644 --- a/radio/src/gui/colorlcd/model_inputs.cpp +++ b/radio/src/gui/colorlcd/model_inputs.cpp @@ -56,20 +56,19 @@ void copyExpo(uint8_t source, uint8_t dest, int8_t input) pauseMixerCalculations(); ExpoData sourceExpo; memcpy(&sourceExpo, expoAddress(source), sizeof(ExpoData)); - ExpoData * expo = expoAddress(dest); - - if(input == PASTE_AFTER) { - memmove(expo+2, expo+1, (MAX_EXPOS-(source+1))*sizeof(ExpoData)); - memcpy(expo+1, &sourceExpo, sizeof(ExpoData)); - (expo+1)->chn = (expo)->chn; - } - else if(input == PASTE_BEFORE) { - memmove(expo+1, expo, (MAX_EXPOS-(source+1))*sizeof(ExpoData)); + ExpoData *expo = expoAddress(dest); + size_t trailingExpos = MAX_EXPOS - (dest + 1); + if (input == PASTE_AFTER) { + trailingExpos--; + memmove(expo + 2, expo + 1, trailingExpos * sizeof(ExpoData)); + memcpy(expo + 1, &sourceExpo, sizeof(ExpoData)); + (expo + 1)->chn = (expo)->chn; + } else if (input == PASTE_BEFORE) { + memmove(expo + 1, expo, trailingExpos * sizeof(ExpoData)); memcpy(expo, &sourceExpo, sizeof(ExpoData)); - expo->chn = (expo+1)->chn; - } - else { - memmove(expo+1, expo, (MAX_EXPOS-(source+1))*sizeof(ExpoData)); + expo->chn = (expo + 1)->chn; + } else { + memmove(expo + 1, expo, trailingExpos * sizeof(ExpoData)); memcpy(expo, &sourceExpo, sizeof(ExpoData)); expo->chn = input; } diff --git a/radio/src/gui/colorlcd/model_logical_switches.cpp b/radio/src/gui/colorlcd/model_logical_switches.cpp index cbe2d968d..ea91b8550 100644 --- a/radio/src/gui/colorlcd/model_logical_switches.cpp +++ b/radio/src/gui/colorlcd/model_logical_switches.cpp @@ -144,14 +144,14 @@ class LogicalSwitchEditPage: public Page new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V1); auto timer = new NumberEdit(logicalSwitchOneWindow, grid.getFieldSlot(), -128, 122, GET_SET_DEFAULT(cs->v1)); timer->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { - dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, lswTimerValue(value), flags | PREC1); + drawTimer(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, flags); }); grid.nextLine(); new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V2); timer = new NumberEdit(logicalSwitchOneWindow, grid.getFieldSlot(), -128, 122, GET_SET_DEFAULT(cs->v2)); timer->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { - dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, lswTimerValue(value), flags | PREC1); + drawTimer(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, flags); }); grid.nextLine(); } diff --git a/radio/src/gui/colorlcd/model_mixer_scripts.cpp b/radio/src/gui/colorlcd/model_mixer_scripts.cpp new file mode 100644 index 000000000..0e0fd012a --- /dev/null +++ b/radio/src/gui/colorlcd/model_mixer_scripts.cpp @@ -0,0 +1,314 @@ +/* + * 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 "opentx.h" + +#include "model_mixer_scripts.h" +#include "dataconstants.h" +#include "lua/lua_api.h" + +#include "translations.h" +#include "menus.h" +#include "libopenui.h" + +#include + +#define SET_DIRTY() storageDirty(EE_MODEL) + +class ScriptEditWindow : public Page { + public: + explicit ScriptEditWindow(uint8_t idx) : + Page(ICON_MODEL_LUA_SCRIPTS), + idx(idx) + { + buildBody(&body); + buildHeader(&header); + } + + protected: + uint8_t idx; + bool update = false; + + void checkEvents() override + { + if (update) { + TRACE("rebuilding ScriptEditWindow..."); + rebuildBody(&body); + update = false; + } + // note: 'update' is set from Page::checkEvents() + Page::checkEvents(); + } + + void buildHeader(Window * window) + { + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_MENUCUSTOMSCRIPTS, 0, FOCUS_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + std::string("LUA") + std::to_string(idx + 1), 0, FOCUS_COLOR); + } + + void buildBody(FormWindow * window, bool focusScript = false) + { + FormGridLayout grid; + grid.spacer(PAGE_PADDING); + + ScriptData* sd = &(g_model.scriptsData[idx]); + + // Filename + new StaticText(window, grid.getLabelSlot(), STR_SCRIPT); + auto fc = new FileChoice( + window, grid.getFieldSlot(), SCRIPTS_MIXES_PATH, SCRIPTS_EXT, + LEN_SCRIPT_FILENAME, + [=]() { return std::string(sd->file, LEN_SCRIPT_FILENAME); }, + [=](std::string newValue) { + strncpy(sd->file, newValue.c_str(), LEN_SCRIPT_FILENAME); + if (newValue.empty()) { memset((void*)sd, 0, sizeof(ScriptData)); } + storageDirty(EE_MODEL); + LUA_LOAD_MODEL_SCRIPT(idx); // async reload... + update = true; + }, true); + grid.nextLine(); + + // Custom name + new StaticText(window, grid.getLabelSlot(), STR_NAME); + new ModelTextEdit(window, grid.getFieldSlot(), sd->name, sizeof(sd->name)); + grid.nextLine(); + + // scriptInputsOutputs + ScriptInputsOutputs& sio = scriptInputsOutputs[idx]; + + if (sio.inputsCount > 0) { + new Subtitle(window, grid.getLineSlot(), STR_INPUTS); + grid.nextLine(); + + auto gInputs = + new FormGroup(window, grid.getFieldSlot(), + FORM_BORDER_FOCUS_ONLY | PAINT_CHILDREN_FIRST); + GridLayout inputsGrid(gInputs); + + for (int i = 0; i < sio.inputsCount; i++) { + ScriptInput& si = sio.inputs[i]; + new StaticText(window, grid.getLabelSlot(true), si.name); + grid.nextLine(); + if (si.type == INPUT_TYPE_VALUE) { + new NumberEdit(gInputs, inputsGrid.getSlot(), si.min - si.def, + si.max - si.def, + GET_SET_WITH_OFFSET(sd->inputs[i].value, si.def)); + } else { + new SourceChoice(gInputs, inputsGrid.getSlot(), 0, + MIXSRC_LAST_TELEM, + GET_SET_DEFAULT(sd->inputs[i].source)); + } + inputsGrid.nextLine(); + } + gInputs->setHeight(inputsGrid.getWindowHeight()); + } + + if (sio.outputsCount > 0) { + new Subtitle(window, grid.getLabelSlot(), STR_OUTPUTS); + grid.nextLine(); + + auto gOutputs = + new FormGroup(window, grid.getLineSlot(), + FORM_BORDER_FOCUS_ONLY | PAINT_CHILDREN_FIRST); + FormGridLayout outputsGrid(gOutputs->width()); + + for (int i = 0; i < sio.outputsCount; i++) { + ScriptOutput* so = &(sio.outputs[i]); + new DynamicText(gOutputs, outputsGrid.getLabelSlot(), [=]() { + char s[16]; + getSourceString(s, + MIXSRC_FIRST_LUA + (idx * MAX_SCRIPT_OUTPUTS) + i); + return std::string(s, sizeof(s) - 1); + }); + new DynamicNumber(gOutputs, outputsGrid.getFieldSlot(), + [=]() { return calcRESXto1000(so->value); }); + + outputsGrid.nextLine(); + } + gOutputs->setHeight(outputsGrid.getWindowHeight()); + grid.addWindow(gOutputs); + } + + window->setInnerHeight(grid.getWindowHeight()); + if (focusScript) { fc->setFocus(); } + } + + void rebuildBody(FormWindow * window) + { + coord_t scrollPosition = window->getScrollPositionY(); + window->clear(); + buildBody(window); + window->setScrollPositionY(scrollPosition); + } +}; + +constexpr char SCRIPT_STATUS_ERROR[] = "(error)"; +constexpr char SCRIPT_STATUS_KILLED[] = "(killed)"; + +class ScriptLineButton : public Button +{ + public: + ScriptLineButton(FormGroup* parent, const rect_t& rect, + const ScriptData& scriptData, + const ScriptInternalData* runtimeData) : + Button(parent, rect), scriptData(scriptData), runtimeData(runtimeData) + { + } + + void paint(BitmapBuffer* dc) override + { + LcdFlags textColor = DEFAULT_COLOR; + LcdFlags bgColor = FIELD_BGCOLOR; + + dc->drawSolidFilledRect(0, 0, width(), height(), bgColor); + + if (runtimeData) { + coord_t x = 2*FIELD_PADDING_LEFT; + coord_t y = FIELD_PADDING_TOP; + + x = dc->drawSizedText(x, y, scriptData.name, sizeof(scriptData.name), textColor); + x += 4*FIELD_PADDING_LEFT; + + dc->drawSizedText(x, y, scriptData.file, sizeof(scriptData.file), textColor); + + x = width() - 2*FIELD_PADDING_LEFT; + y = FIELD_PADDING_TOP; + textColor |= RIGHT; + + switch (runtimeData->state) { + case SCRIPT_SYNTAX_ERROR: + dc->drawSizedText(x, y, SCRIPT_STATUS_ERROR, + sizeof(SCRIPT_STATUS_ERROR), textColor); + break; + case SCRIPT_KILLED: + dc->drawSizedText(x, y, SCRIPT_STATUS_KILLED, + sizeof(SCRIPT_STATUS_KILLED), textColor); + break; + default: + dc->drawNumber(x, y, runtimeData->instructions, textColor, 0, nullptr, "%"); + break; + } + } + + // bounding rect + if (hasFocus()) + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + else + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); + } + + protected: + const ScriptData& scriptData; + const ScriptInternalData* runtimeData; +}; + +ModelMixerScriptsPage::ModelMixerScriptsPage() : + PageTab(STR_MENUCUSTOMSCRIPTS, ICON_MODEL_LUA_SCRIPTS) +{ +} + +void ModelMixerScriptsPage::rebuild(FormWindow * window, int8_t focusIdx) +{ + coord_t scrollPosition = window->getScrollPositionY(); + window->clear(); + build(window, focusIdx); + window->setScrollPositionY(scrollPosition); +} + +void ModelMixerScriptsPage::build(FormWindow * window, int8_t focusIdx) +{ + FormGridLayout grid; + grid.spacer(PAGE_PADDING); + grid.setLabelWidth(66); + + int8_t scriptIdx = 0; + for (int8_t idx = 0; idx < MAX_SCRIPTS; idx++) { + + ScriptInternalData* runtimeData = nullptr; + ScriptData &sd = g_model.scriptsData[idx]; + + if (sd.file[0] != '\0') { + runtimeData = &(scriptInternalData[scriptIdx++]); + } + + // LUAx label + auto txt = new StaticText(window, grid.getLabelSlot(), + std::string("LUA") + std::to_string(idx + 1), + BUTTON_BACKGROUND, CENTERED); + + Button* button = new ScriptLineButton(window, grid.getFieldSlot(), sd, runtimeData); + + button->setPressHandler([=]() -> uint8_t { + Menu* menu = new Menu(window); + menu->addLine(STR_EDIT, [=]() { editLine(window, idx); }); + + if (runtimeData != nullptr) { + menu->addLine(STR_RESET, [=]() { + memset((void*)&sd, 0, sizeof(sd)); + // TODO: anything else? reload scripts??? + storageDirty(EE_MODEL); + rebuild(window, idx); + }); + return 0; + } + return 0; + }); + + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); + + if (focusIdx == idx) { + button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); + } + + txt->setHeight(button->height()); + grid.spacer(button->height() + 5); + } + + grid.nextLine(); + window->setInnerHeight(grid.getWindowHeight()); +} + +void ModelMixerScriptsPage::editLine(FormWindow * window, uint8_t idx) +{ + Window::clearFocus(); + Window * editWindow = new ScriptEditWindow(idx); + editWindow->setCloseHandler([=]() { + rebuild(window, idx); + }); +} diff --git a/radio/src/gui/colorlcd/model_mixer_scripts.h b/radio/src/gui/colorlcd/model_mixer_scripts.h new file mode 100644 index 000000000..8486a193c --- /dev/null +++ b/radio/src/gui/colorlcd/model_mixer_scripts.h @@ -0,0 +1,39 @@ +/* + * 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 "tabsgroup.h" + +class ModelMixerScriptsPage: public PageTab { + public: + ModelMixerScriptsPage(); + + virtual void build(FormWindow * window) override + { + build(window, 0); + } + + protected: + void build(FormWindow * window, int8_t focusIdx); + void rebuild(FormWindow * window, int8_t focusIdx); + void editLine(FormWindow * window, uint8_t idx); +}; diff --git a/radio/src/gui/colorlcd/model_mixes.cpp b/radio/src/gui/colorlcd/model_mixes.cpp index a6fae37a9..1e67b5656 100644 --- a/radio/src/gui/colorlcd/model_mixes.cpp +++ b/radio/src/gui/colorlcd/model_mixes.cpp @@ -89,7 +89,7 @@ class MixEditWindow : public Page // Mix name new StaticText(window, grid.getLabelSlot(), STR_MIXNAME); - new RadioTextEdit(window, grid.getFieldSlot(), mix->name, + new ModelTextEdit(window, grid.getFieldSlot(), mix->name, sizeof(mix->name)); grid.nextLine(); @@ -531,24 +531,22 @@ void copyMix(uint8_t source, uint8_t dest, int8_t ch) pauseMixerCalculations(); MixData sourceMix; memcpy(&sourceMix, mixAddress(source), sizeof(MixData)); - MixData * mix = mixAddress(dest); - if(ch == PASTE_AFTER) { - memmove(mix+2, mix+1, (MAX_MIXERS-(source+1))*sizeof(MixData)); - memcpy(mix+1, &sourceMix, sizeof(MixData)); - (mix+1)->destCh = (mix)->destCh; - } - else if(ch == PASTE_BEFORE) { - memmove(mix+1, mix, (MAX_MIXERS-(source+1))*sizeof(MixData)); + MixData *mix = mixAddress(dest); + size_t trailingMixes = MAX_MIXERS - (dest + 1); + if (ch == PASTE_AFTER) { + trailingMixes--; + memmove(mix + 2, mix + 1, trailingMixes * sizeof(MixData)); + memcpy(mix + 1, &sourceMix, sizeof(MixData)); + (mix + 1)->destCh = (mix)->destCh; + } else if (ch == PASTE_BEFORE) { + memmove(mix + 1, mix, trailingMixes * sizeof(MixData)); memcpy(mix, &sourceMix, sizeof(MixData)); - mix->destCh = (mix+1)->destCh; - } - else { - memmove(mix+1, mix, (MAX_MIXERS-(source+1))*sizeof(MixData)); + mix->destCh = (mix + 1)->destCh; + } else { + memmove(mix + 1, mix, trailingMixes * sizeof(MixData)); memcpy(mix, &sourceMix, sizeof(MixData)); - mix->destCh = ch; + mix->destCh = ch; } - - //memmove(mix + 1, mix, (MAX_MIXERS - (dest + 1)) * sizeof(MixData)); resumeMixerCalculations(); storageDirty(EE_MODEL); } diff --git a/radio/src/gui/colorlcd/model_outputs.cpp b/radio/src/gui/colorlcd/model_outputs.cpp index bbc42f58c..020a46546 100644 --- a/radio/src/gui/colorlcd/model_outputs.cpp +++ b/radio/src/gui/colorlcd/model_outputs.cpp @@ -60,7 +60,7 @@ class OutputEditWindow : public Page { // Name new StaticText(window, grid.getLabelSlot(), STR_NAME); - new RadioTextEdit(window, grid.getFieldSlot(), output->name, sizeof(output->name)); + new ModelTextEdit(window, grid.getFieldSlot(), output->name, sizeof(output->name)); grid.nextLine(); // Offset @@ -130,7 +130,7 @@ class OutputLineButton : public Button { LcdFlags bgColor = FIELD_BGCOLOR; dc->drawSolidFilledRect(0, 0, width(), height(), bgColor); - + // first line dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, output->min - 1000, PREC1 | textColor); diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index 8ecb3ffa7..f73600b10 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -245,7 +245,8 @@ class ModelCategoryPageBody : public FormWindow modelslist.setCurrentModel(model); modelslist.setCurrentCategory(category); - update(); + this->onEvent(EVT_KEY_FIRST(KEY_EXIT)); + checkAll(); }); } menu->addLine(STR_CREATE_MODEL, getCreateModelAction()); diff --git a/radio/src/gui/colorlcd/model_setup.cpp b/radio/src/gui/colorlcd/model_setup.cpp index 75033cc05..f045fabfc 100644 --- a/radio/src/gui/colorlcd/model_setup.cpp +++ b/radio/src/gui/colorlcd/model_setup.cpp @@ -630,22 +630,42 @@ class ModuleWindow : public FormGroup { // Multi type (CUSTOM, brand A, brand B,...) int multiRfProto = g_model.moduleData[moduleIdx].getMultiProtocol(); - rfChoice = new Choice(this, grid.getFieldSlot(g_model.moduleData[moduleIdx].multi.customProto ? 3 : 2, 0), STR_MULTI_PROTOCOLS, MODULE_SUBTYPE_MULTI_FIRST, MODULE_SUBTYPE_MULTI_LAST, - GET_DEFAULT(multiRfProto), - [=](int32_t newValue) { - g_model.moduleData[moduleIdx].setMultiProtocol(newValue); - g_model.moduleData[moduleIdx].subType = 0; - if (mmSubProtocol != nullptr) mmSubProtocol->invalidate(); - resetMultiProtocolsOptions(moduleIdx); - SET_DIRTY(); - update(); - rfChoice->setFocus(SET_FOCUS_DEFAULT); - }); + rfChoice = new Choice( + this, + grid.getFieldSlot( + g_model.moduleData[moduleIdx].multi.customProto ? 3 : 2, 0), + STR_MULTI_PROTOCOLS, MODULE_SUBTYPE_MULTI_FIRST, + MODULE_SUBTYPE_MULTI_LAST, GET_DEFAULT(multiRfProto), + [=](int32_t newValue) { + g_model.moduleData[moduleIdx].setMultiProtocol(newValue); + g_model.moduleData[moduleIdx].subType = 0; + if (mmSubProtocol != nullptr) mmSubProtocol->invalidate(); // always null ??? + resetMultiProtocolsOptions(moduleIdx); + SET_DIRTY(); + update(); + rfChoice->setFocus(SET_FOCUS_DEFAULT);// really valid ??? + }); - // Subtype (D16, DSMX,...) - const mm_protocol_definition * pdef = getMultiProtocolDefinition(g_model.moduleData[moduleIdx].getMultiProtocol()); - if (pdef->maxSubtype > 0) - mmSubProtocol = new Choice(this, grid.getFieldSlot(2, 1), pdef->subTypeString, 0, pdef->maxSubtype,GET_SET_DEFAULT(g_model.moduleData[moduleIdx].subType)); + if (MULTIMODULE_HAS_SUBTYPE(moduleIdx)) { + // Subtype (D16, DSMX,...) + const mm_protocol_definition *pdef = getMultiProtocolDefinition( + g_model.moduleData[moduleIdx].getMultiProtocol()); + if (pdef->maxSubtype > 0) { + mmSubProtocol = new Choice( + this, grid.getFieldSlot(2, 1), pdef->subTypeString, 0, + pdef->maxSubtype, + [=]() { return g_model.moduleData[moduleIdx].subType; }, + [=](int16_t newValue) { + g_model.moduleData[moduleIdx].subType = newValue; + resetMultiProtocolsOptions(moduleIdx); + SET_DIRTY(); + // update(); ??? how to set focus properly ??? + update(); + //mmSubProtocol->setFocus(SET_FOCUS_DEFAULT); + }); + + } + } grid.nextLine(); // Multimodule status @@ -664,46 +684,122 @@ class ModuleWindow : public FormGroup { new StaticText(this, grid.getFieldSlot(), statusText); }*/ - // Multi optional feature row const uint8_t multi_proto = g_model.moduleData[moduleIdx].getMultiProtocol(); - if (pdef->optionsstr) { - grid.nextLine(); - new StaticText(this, grid.getLabelSlot(true), pdef->optionsstr); - if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { - auto edit = new NumberEdit(this, grid.getFieldSlot(2,0), 50, 400, - GET_DEFAULT(50 + 5 * g_model.moduleData[moduleIdx].multi.optionValue), - SET_VALUE(g_model.moduleData[moduleIdx].multi.optionValue, (newValue- 50) / 5)); - edit->setStep(5); - } - else if (multi_proto == MODULE_SUBTYPE_MULTI_OLRS) { - new NumberEdit(this, grid.getFieldSlot(2,0), -1, 7, GET_SET_DEFAULT(g_model.moduleData[moduleIdx].multi.optionValue)); - } - else { - new NumberEdit(this, grid.getFieldSlot(2,0), -128, 127, GET_SET_DEFAULT(g_model.moduleData[moduleIdx].multi.optionValue)); + if (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx)) { + // Multi optional feature row + const char *title = getMultiOptionTitleStatic(moduleIdx); + if (title != nullptr) { + grid.nextLine(); + new StaticText(this, grid.getLabelSlot(true), title); - //Show RSSI next to RF Freq Fine Tune - if (getMultiOptionTitle(moduleIdx) == STR_MULTI_RFTUNE) - { - new DynamicNumber(this, grid.getFieldSlot(2, 1), [] { - return (int)TELEMETRY_RSSI();}, 0, "RSSI: ", " db"); + // int optionValue = + // g_model.moduleData[moduleIdx].multi.optionValue; + int8_t min, max; + getMultiOptionValues(multi_proto, min, max); + + if (title == STR_MULTI_RFPOWER) { + new Choice(this, grid.getFieldSlot(2, 0), STR_MULTI_POWER, 0, 15, + GET_SET_DEFAULT( + g_model.moduleData[moduleIdx].multi.optionValue)); + } else if (title == STR_MULTI_TELEMETRY) { + new Choice(this, grid.getFieldSlot(2, 0), + STR_MULTI_TELEMETRY_MODE, min, max, + GET_SET_DEFAULT( + g_model.moduleData[moduleIdx].multi.optionValue)); + } else if (title == STR_MULTI_WBUS) { + new Choice(this, grid.getFieldSlot(2, 0), STR_MULTI_WBUS_MODE, 0, + 1, + GET_SET_DEFAULT( + g_model.moduleData[moduleIdx].multi.optionValue)); + } else if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { + auto edit = new NumberEdit( + this, grid.getFieldSlot(2, 0), 50, 400, + GET_DEFAULT( + 50 + 5 * g_model.moduleData[moduleIdx].multi.optionValue), + SET_VALUE(g_model.moduleData[moduleIdx].multi.optionValue, + (newValue - 50) / 5)); + edit->setStep(5); + } else if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { + new CheckBox( + this, grid.getFieldSlot(2, 0), + [=]() { + return g_model.moduleData[moduleIdx].multi.optionValue & + 0x01; + }, + [=](int16_t newValue) { + g_model.moduleData[moduleIdx].multi.optionValue = + (g_model.moduleData[moduleIdx].multi.optionValue & + 0xFE) + + newValue; + }); + } else { + if (min == 0 && max == 1) { + new CheckBox( + this, grid.getFieldSlot(2, 0), + GET_SET_DEFAULT( + g_model.moduleData[moduleIdx].multi.optionValue)); + } else { + new NumberEdit( + this, grid.getFieldSlot(2, 0), -128, 127, + GET_SET_DEFAULT( + g_model.moduleData[moduleIdx].multi.optionValue)); + + // Show RSSI next to RF Freq Fine Tune + if (title == STR_MULTI_RFTUNE) { + new DynamicNumber( + this, grid.getFieldSlot(2, 1), + [] { return (int)TELEMETRY_RSSI(); }, 0, "RSSI: ", " db"); + } + } } } } - - // Bind on power up grid.nextLine(); - new StaticText(this, grid.getLabelSlot(true),g_model.moduleData[moduleIdx].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2 ? STR_MULTI_DSM_AUTODTECT : STR_MULTI_AUTOBIND); - new CheckBox(this, grid.getFieldSlot(), GET_SET_DEFAULT(g_model.moduleData[moduleIdx].multi.autoBindMode)); + + if (multi_proto == MODULE_SUBTYPE_MULTI_DSM2) { + + const char* servoRates[] = { "22ms", "11ms" }; + + new StaticText(this, grid.getLabelSlot(true), STR_MULTI_SERVOFREQ); + new Choice( + this, grid.getFieldSlot(), servoRates, 0, 1, + [=]() { + return (g_model.moduleData[moduleIdx].multi.optionValue & 0x02) >> 1; + }, + [=](int16_t newValue) { + g_model.moduleData[moduleIdx].multi.optionValue = + (g_model.moduleData[moduleIdx].multi.optionValue & 0xFD) + + (newValue << 1); + }); + } else { + // Bind on power up + new StaticText(this, grid.getLabelSlot(true), STR_MULTI_AUTOBIND); + new CheckBox(this, grid.getFieldSlot(), + GET_SET_DEFAULT( + g_model.moduleData[moduleIdx].multi.autoBindMode)); + } // Low power mode grid.nextLine(); new StaticText(this, grid.getLabelSlot(true), STR_MULTI_LOWPOWER); - new CheckBox(this, grid.getFieldSlot(), GET_SET_DEFAULT(g_model.moduleData[moduleIdx].multi.lowPowerMode)); + new CheckBox( + this, grid.getFieldSlot(), + GET_SET_DEFAULT(g_model.moduleData[moduleIdx].multi.lowPowerMode)); // Disable telemetry grid.nextLine(); new StaticText(this, grid.getLabelSlot(true), STR_DISABLE_TELEM); - new CheckBox(this, grid.getFieldSlot(), GET_SET_DEFAULT(g_model.moduleData[moduleIdx].multi.disableMapping)); + new CheckBox(this, grid.getFieldSlot(), + GET_SET_DEFAULT( + g_model.moduleData[moduleIdx].multi.disableTelemetry)); + + if (MULTI_DISABLE_CHAN_MAP_ROW_STATIC(moduleIdx) != HIDDEN_ROW) { + // Disable channel mapping + grid.nextLine(); + new StaticText(this, grid.getLabelSlot(true), STR_DISABLE_CH_MAP); + new CheckBox(this, grid.getFieldSlot(), + GET_SET_DEFAULT(g_model.moduleData[moduleIdx].multi.disableMapping)); + } } #endif #if defined(AFHDS3) @@ -1021,15 +1117,16 @@ void ModelSetupPage::build(FormWindow * window) // Model name new StaticText(window, grid.getLabelSlot(), STR_MODELNAME); - auto text = new RadioTextEdit(window, grid.getFieldSlot(), g_model.header.name, sizeof(g_model.header.name), - 0, MODEL_NAME_EXTRA_CHARS); + auto text = + new ModelTextEdit(window, grid.getFieldSlot(), g_model.header.name, + sizeof(g_model.header.name), 0, MODEL_NAME_EXTRA_CHARS); text->setChangeHandler([=] { - modelslist.load(); - auto model = modelslist.getCurrentModel(); - if (model) { - model->setModelName(g_model.header.name); - } - SET_DIRTY(); + modelslist.load(); + auto model = modelslist.getCurrentModel(); + if (model) { + model->setModelName(g_model.header.name); + } + SET_DIRTY(); }); grid.nextLine(); @@ -1139,7 +1236,17 @@ void ModelSetupPage::build(FormWindow * window) // Throttle source new StaticText(window, grid.getLabelSlot(true), STR_TTRACE); - new SourceChoice(window, grid.getFieldSlot(), 0, MIXSRC_LAST_CH, GET_SET_DEFAULT(g_model.thrTraceSrc)); + auto sc = new SourceChoice( + window, grid.getFieldSlot(), 0, MIXSRC_LAST_CH, + [=]() { return throttleSource2Source(g_model.thrTraceSrc); }, + [=](int16_t src) { + int16_t val = source2ThrottleSource(src); + if (val >= 0) { + g_model.thrTraceSrc = val; + SET_DIRTY(); + } + }); + sc->setAvailableHandler(isThrottleSourceAvailable); grid.nextLine(); // Throttle trim @@ -1149,7 +1256,10 @@ void ModelSetupPage::build(FormWindow * window) // Throttle trim source new StaticText(window, grid.getLabelSlot(true), STR_TTRIM_SW); - new SourceChoice(window, grid.getFieldSlot(), 0, NUM_TRIMS - 1, GET_SET_DEFAULT( g_model.thrTrimSw)); + new SourceChoice( + window, grid.getFieldSlot(), MIXSRC_FIRST_TRIM, MIXSRC_LAST_TRIM, + [=]() { return g_model.getThrottleStickTrimSource(); }, + [=](int16_t src) { g_model.setThrottleStickTrimSource(src); }); grid.nextLine(); } diff --git a/radio/src/gui/colorlcd/view_statistics.h b/radio/src/gui/colorlcd/view_statistics.h index f9632e628..5305dbca5 100644 --- a/radio/src/gui/colorlcd/view_statistics.h +++ b/radio/src/gui/colorlcd/view_statistics.h @@ -32,19 +32,19 @@ class DebugInfoNumber : public Window public: DebugInfoNumber(Window * parent, const rect_t & rect, std::function numberHandler, LcdFlags textFlags = 0, const char * prefix = nullptr, const char * suffix = nullptr): Window(parent, rect, 0, textFlags), - numberHandler(std::move(numberHandler)), prefix(prefix), suffix(suffix) { - coord_t prefixSize = getTextWidth(prefix, 0, HEADER_COLOR | FONT(XS)); - new StaticText(this, {0, 0, prefixSize, rect.h}, prefix, 0, HEADER_COLOR | FONT(XS)); - new DynamicNumber(this, {prefixSize, 0, rect.w - prefixSize, rect.h}, [] { - return menusStack.available(); - }); + coord_t prefixSize = 0; + if (prefix != nullptr) { + prefixSize = getTextWidth(prefix, 0, HEADER_COLOR | FONT(XS)); + new StaticText(this, {0, 0, prefixSize, rect.h}, prefix, 0, HEADER_COLOR | FONT(XS)); + } + new DynamicNumber(this, {prefixSize, 0, rect.w - prefixSize, rect.h}, + numberHandler); } protected: - std::function numberHandler; const char * prefix; const char * suffix; }; diff --git a/radio/src/gui/colorlcd/view_text.h b/radio/src/gui/colorlcd/view_text.h index e73938ef1..aa7b598f7 100644 --- a/radio/src/gui/colorlcd/view_text.h +++ b/radio/src/gui/colorlcd/view_text.h @@ -75,13 +75,13 @@ class ViewTextWindow : public Page } } - void paint(BitmapBuffer* dc) + void paint(BitmapBuffer* dc) override { Page::paint(dc); drawVerticalScrollbar(dc); } #endif - virtual void checkEvents(); + void checkEvents() override; #if defined(DEBUG_WINDOWS) std::string getName() const override { return "ViewTextWindow"; }; @@ -123,4 +123,4 @@ bool isInSetup; }; }; -void readModelNotes(); \ No newline at end of file +void readModelNotes(); diff --git a/radio/src/gui/colorlcd/widgets/outputs.cpp b/radio/src/gui/colorlcd/widgets/outputs.cpp index 71c634562..3a79eef6f 100644 --- a/radio/src/gui/colorlcd/widgets/outputs.cpp +++ b/radio/src/gui/colorlcd/widgets/outputs.cpp @@ -75,7 +75,7 @@ class OutputsWidget: public Widget dc->drawSizedText(barLft + 23, barTop, g_model.limitData[curChan - 1].name, sizeof(g_model.limitData[curChan - 1].name), FONT(XS) | DEFAULT_COLOR | LEFT); } else { - drawSource(dc, barLft + 1, barTop, curChan, FONT(XS) | DEFAULT_COLOR | LEFT); + drawSource(dc, barLft + 1, barTop, MIXSRC_FIRST_CH+curChan-1, FONT(XS) | DEFAULT_COLOR | LEFT); } } return lastChan - 1; @@ -111,7 +111,7 @@ class OutputsWidget: public Widget lastRefresh = now; invalidate(); } - + invalidate(); } diff --git a/radio/src/gui/colorlcd/widgets/timer.cpp b/radio/src/gui/colorlcd/widgets/timer.cpp index 389303acb..eb2c0db2a 100644 --- a/radio/src/gui/colorlcd/widgets/timer.cpp +++ b/radio/src/gui/colorlcd/widgets/timer.cpp @@ -21,6 +21,12 @@ #include "opentx.h" #include "widgets_container_impl.h" +// libopemui defines TIMOUR as zero +#ifdef TIMEHOUR +#undef TIMEHOUR +#define TIMEHOUR 0x2000 +#endif + class TimerWidget: public Widget { public: diff --git a/radio/src/gui/colorlcd/widgets/value.cpp b/radio/src/gui/colorlcd/widgets/value.cpp index 6c8821a2a..e2ed19f9a 100644 --- a/radio/src/gui/colorlcd/widgets/value.cpp +++ b/radio/src/gui/colorlcd/widgets/value.cpp @@ -86,30 +86,45 @@ class ValueWidget: public Widget if (timerState.val < 0) { color = ALARM_COLOR; } - drawSource(dc, NUMBERS_PADDING, 2, field, color); - drawSource(dc, NUMBERS_PADDING + 1, 3, field, COLOR2FLAGS(BLACK)); - drawTimer(dc, xValue, yValue, abs(timerState.val), attrValue | FONT(XL) | color); - } - - if (field == MIXSRC_TX_TIME) { - drawTimer(dc, xValue, yValue, getValue(MIXSRC_TX_TIME), - attrValue | FONT(XL) | color); - } - - if (field >= MIXSRC_FIRST_TELEM) { - TelemetryItem & telemetryItem = telemetryItems[(field - MIXSRC_FIRST_TELEM) / 3]; - if (!telemetryItem.isAvailable() || telemetryItem.isOld()) { - color = TEXT_DISABLE_COLOR; + if (persistentData->options[2].value.boolValue) { + drawSource(dc, xLabel + 1, yLabel + 1, field, attrLabel | COLOR2FLAGS(BLACK)); + drawTimer(dc, xValue + 1, yValue + 1, abs(timerState.val), + attrValue | FONT(STD) | COLOR2FLAGS(BLACK)); } - } + drawSource(dc, xLabel, yLabel, field, attrLabel | color); + drawTimer(dc, xValue, yValue, abs(timerState.val), + attrValue | FONT(STD) | color); - if (persistentData->options[2].value.boolValue) { - drawSource(dc,xLabel + 1, yLabel + 1, field, attrLabel | COLOR2FLAGS(BLACK)); - drawSourceValue(dc, xValue + 1, yValue + 1, field, attrValue | COLOR2FLAGS(BLACK)); - } + } else if (field == MIXSRC_TX_TIME) { + int32_t tme = getValue(MIXSRC_TX_TIME); + if (persistentData->options[2].value.boolValue) { + drawSource(dc, xLabel + 1, yLabel + 1, field, COLOR2FLAGS(BLACK)); + drawTimer(dc, xValue + 1, yValue + 1, tme, + attrValue | FONT(STD) | COLOR2FLAGS(BLACK)); + } + drawSource(dc, xLabel, yLabel, field, attrLabel | color); + drawTimer(dc, xValue, yValue, tme, + attrValue | FONT(STD) | color); + } else { - drawSource(dc, xLabel, yLabel, field, attrLabel | color); - drawSourceValue(dc, xValue, yValue, field, attrValue | color); + if (field >= MIXSRC_FIRST_TELEM) { + TelemetryItem& telemetryItem = + telemetryItems[(field - MIXSRC_FIRST_TELEM) / 3]; + if (!telemetryItem.isAvailable() || telemetryItem.isOld()) { + color = TEXT_DISABLE_COLOR; + } + } + + if (persistentData->options[2].value.boolValue) { + drawSource(dc, xLabel + 1, yLabel + 1, field, + attrLabel | COLOR2FLAGS(BLACK)); + drawSourceValue(dc, xValue + 1, yValue + 1, field, + attrValue | COLOR2FLAGS(BLACK)); + } + + drawSource(dc, xLabel, yLabel, field, attrLabel | color); + drawSourceValue(dc, xValue, yValue, field, attrValue | color); + } } void checkEvents() override diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index d243d154f..058c20765 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -442,12 +442,15 @@ bool isSwitch2POSWarningStateAvailable(int state) // return isSwitchAvailable(swtch, TimersContext); //} -bool isThrottleSourceAvailable(int source) +bool isThrottleSourceAvailable(int src) { - if (source >= THROTTLE_SOURCE_FIRST_POT && source < THROTTLE_SOURCE_FIRST_POT+NUM_POTS+NUM_SLIDERS && !IS_POT_SLIDER_AVAILABLE(POT1+source-THROTTLE_SOURCE_FIRST_POT)) - return false; - else - return true; +#if !defined(LIBOPENUI) + src = throttleSource2Source(src); +#endif + return isSourceAvailable(src) && + ((src == MIXSRC_Thr) || + ((src >= MIXSRC_FIRST_POT) && (src <= MIXSRC_LAST_POT)) || + ((src >= MIXSRC_FIRST_CH) && (src <= MIXSRC_LAST_CH))); } bool isLogicalSwitchFunctionAvailable(int function) @@ -1097,6 +1100,13 @@ const mm_protocol_definition *getMultiProtocolDefinition (uint8_t protocol) #endif #if defined(MULTIMODULE) +const char * getMultiOptionTitleStatic(uint8_t moduleIdx) +{ + const uint8_t multi_proto = g_model.moduleData[moduleIdx].getMultiProtocol(); + const mm_protocol_definition * pdef = getMultiProtocolDefinition(multi_proto); + return pdef->optionsstr; +} + const char * getMultiOptionTitle(uint8_t moduleIdx) { MultiModuleStatus &status = getMultiModuleStatus(moduleIdx); @@ -1107,10 +1117,7 @@ const char * getMultiOptionTitle(uint8_t moduleIdx) } return mm_options_strings::options[status.optionDisp]; } - else { - const uint8_t multi_proto = g_model.moduleData[moduleIdx].getMultiProtocol(); - const mm_protocol_definition * pdef = getMultiProtocolDefinition(multi_proto); - return pdef->optionsstr; - } + + return getMultiOptionTitleStatic(moduleIdx); } #endif diff --git a/radio/src/gui/gui_common.h b/radio/src/gui/gui_common.h index 46efa21d0..e59b5df57 100644 --- a/radio/src/gui/gui_common.h +++ b/radio/src/gui/gui_common.h @@ -220,6 +220,21 @@ inline uint8_t IF_ALLOW_RACING_MODE(int) #endif #if defined(MULTIMODULE) +inline uint8_t MULTI_DISABLE_CHAN_MAP_ROW_STATIC(uint8_t moduleIdx) +{ + if (!isModuleMultimodule(moduleIdx)) + return HIDDEN_ROW; + + uint8_t protocol = g_model.moduleData[moduleIdx].getMultiProtocol(); + if (protocol < MODULE_SUBTYPE_MULTI_LAST) { + const mm_protocol_definition * pdef = getMultiProtocolDefinition(protocol); + if (pdef->disable_ch_mapping) + return 0; + } + + return HIDDEN_ROW; +} + inline uint8_t MULTI_DISABLE_CHAN_MAP_ROW(uint8_t moduleIdx) { if (!isModuleMultimodule(moduleIdx)) @@ -230,14 +245,7 @@ inline uint8_t MULTI_DISABLE_CHAN_MAP_ROW(uint8_t moduleIdx) return status.supportsDisableMapping() == true ? 0 : HIDDEN_ROW; } - uint8_t protocol = g_model.moduleData[moduleIdx].getMultiProtocol(); - if (protocol < MODULE_SUBTYPE_MULTI_LAST) { - const mm_protocol_definition * pdef = getMultiProtocolDefinition(protocol); - if (pdef->disable_ch_mapping) - return 0; - } - - return HIDDEN_ROW; + return MULTI_DISABLE_CHAN_MAP_ROW_STATIC(moduleIdx); } inline bool isMultiProtocolSelectable(int protocol) @@ -355,6 +363,7 @@ inline uint8_t MODULE_OPTION_ROW(uint8_t moduleIdx) { void editStickHardwareSettings(coord_t x, coord_t y, int idx, event_t event, LcdFlags flags, uint8_t old_editMode); +const char * getMultiOptionTitleStatic(uint8_t moduleIdx); const char *getMultiOptionTitle(uint8_t moduleIdx); const char * writeScreenshot(); diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 2cbf3e113..4d7a4e0f6 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -374,7 +374,8 @@ bool luaFindFieldByName(const char * name, LuaField & field, unsigned int flags) for (int i = 0; i < MAX_TELEMETRY_SENSORS; i++) { if (isTelemetryFieldAvailable(i)) { const char* sensorName = g_model.telemetrySensors[i].label; - if ((len <= TELEM_LABEL_LEN) && !strncmp(sensorName, name, len)) { + int len = strnlen(sensorName, TELEM_LABEL_LEN); + if (!strncmp(sensorName, name, len)) { if (name[len] == '\0') { field.id = MIXSRC_FIRST_TELEM + 3 * i; field.desc[0] = '\0'; diff --git a/radio/src/model_init.cpp b/radio/src/model_init.cpp index 1a4b8ce2e..202784a9f 100644 --- a/radio/src/model_init.cpp +++ b/radio/src/model_init.cpp @@ -77,7 +77,7 @@ void setDefaultGVars() #endif } -void setVendorSpecificModelDefaults() +void setVendorSpecificModelDefaults(uint8_t id) { #if defined(FRSKY_RELEASE) g_model.moduleData[INTERNAL_MODULE].type = IS_PXX2_INTERNAL_ENABLED() ? MODULE_TYPE_ISRM_PXX2 : MODULE_TYPE_XJT_PXX1; @@ -132,7 +132,7 @@ void setModelDefaults(uint8_t id) memset(&g_model, 0, sizeof(g_model)); applyDefaultTemplate(); - setVendorSpecificModelDefaults(); + setVendorSpecificModelDefaults(id); strAppendUnsigned(strAppend(g_model.header.name, STR_MODEL), id + 1, 2); diff --git a/radio/src/myeeprom.h b/radio/src/myeeprom.h index 438989eb0..ac9f9d460 100644 --- a/radio/src/myeeprom.h +++ b/radio/src/myeeprom.h @@ -229,32 +229,6 @@ enum SwashType { #include "pulses/modules_constants.h" -enum ThrottleSources { - THROTTLE_SOURCE_THR, - THROTTLE_SOURCE_FIRST_POT, -#if defined(PCBX9E) - THROTTLE_SOURCE_F1 = THROTTLE_SOURCE_FIRST_POT, - THROTTLE_SOURCE_F2, - THROTTLE_SOURCE_F3, - THROTTLE_SOURCE_F4, - THROTTLE_SOURCE_S1, - THROTTLE_SOURCE_S2, - THROTTLE_SOURCE_LS, - THROTTLE_SOURCE_RS, -#elif defined(PCBTARANIS) - THROTTLE_SOURCE_S1 = THROTTLE_SOURCE_FIRST_POT, - THROTTLE_SOURCE_S2, - THROTTLE_SOURCE_S3, - THROTTLE_SOURCE_LS, - THROTTLE_SOURCE_RS, -#else - THROTTLE_SOURCE_P1 = THROTTLE_SOURCE_FIRST_POT, - THROTTLE_SOURCE_P2, - THROTTLE_SOURCE_P3, -#endif - THROTTLE_SOURCE_CH1, -}; - enum DisplayTrims { DISPLAY_TRIMS_NEVER, diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index d2a5aa00c..b792d6ab1 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -1007,21 +1007,26 @@ void checkTrims() else #endif { - if (before>TRIM_MIN && after<=TRIM_MIN) { + int16_t tMax = g_model.extendedTrims ? TRIM_EXTENDED_MAX : TRIM_MAX; + int16_t tMin = g_model.extendedTrims ? TRIM_EXTENDED_MIN : TRIM_MIN; + if (before > tMin && after <= tMin) { beepTrim = true; AUDIO_TRIM_MIN(); killEvents(event); } - else if (before=TRIM_MAX) { + else if (before < tMax && after >= tMax) { beepTrim = true; AUDIO_TRIM_MAX(); killEvents(event); } - if ((beforeTRIM_MAX) || (before>after && after tMax) || // increasing over tMax + (before > after && after < tMin)) { // decreasing under tMin + after = before; } + // Clip at hard limits if (after < TRIM_EXTENDED_MIN) { after = TRIM_EXTENDED_MIN; } @@ -1590,7 +1595,8 @@ void instantTrim() } } if (addTrim && abs(delta) >= INSTANT_TRIM_MARGIN) { - int16_t trim = limit(TRIM_EXTENDED_MIN, (delta + trims[stick]) / 2, TRIM_EXTENDED_MAX); + int16_t trim = limit( + TRIM_EXTENDED_MIN, (delta + trims[stick]) / 2, TRIM_EXTENDED_MAX); setTrimValue(trimFlightMode, stick, trim); } } diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index 779771c04..d1f067d53 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -285,6 +285,8 @@ void enablePulsesExternalModule(uint8_t protocol) extmoduleSerialStart(); mixerSchedulerSetPeriod(EXTERNAL_MODULE, MULTIMODULE_PERIOD); #endif + getMultiModuleStatus(EXTERNAL_MODULE).failsafeChecked = false; + getMultiModuleStatus(EXTERNAL_MODULE).flags = 0; break; #endif @@ -309,7 +311,7 @@ void enablePulsesExternalModule(uint8_t protocol) #if defined(AFHDS3) case PROTOCOL_CHANNELS_AFHDS3: extmodulePulsesData.afhds3.init(EXTERNAL_MODULE); - extmoduleSerialStart(/*AFHDS3_BAUDRATE, AFHDS3_COMMAND_TIMEOUT * 2000, false*/); + extmoduleSerialStart(); mixerSchedulerSetPeriod(EXTERNAL_MODULE, AFHDS3_COMMAND_TIMEOUT * 1000 /* us */); break; #endif @@ -491,6 +493,8 @@ static void enablePulsesInternalModule(uint8_t protocol) intmodulePulsesData.multi.initFrame(); intmoduleSerialStart(MULTIMODULE_BAUDRATE, true, USART_Parity_Even, USART_StopBits_2, USART_WordLength_9b); mixerSchedulerSetPeriod(INTERNAL_MODULE, MULTIMODULE_PERIOD); + getMultiModuleStatus(INTERNAL_MODULE).failsafeChecked = false; + getMultiModuleStatus(INTERNAL_MODULE).flags = 0; break; #endif diff --git a/radio/src/storage/storage_common.cpp b/radio/src/storage/storage_common.cpp index 439032e74..0a7c81564 100644 --- a/radio/src/storage/storage_common.cpp +++ b/radio/src/storage/storage_common.cpp @@ -58,6 +58,7 @@ void preModelLoad() #endif #if defined(HARDWARE_EXTERNAL_MODULE) stopPulsesExternalModule(); + RTOS_WAIT_MS(200); #endif stopTrainer(); diff --git a/radio/src/strhelpers.cpp b/radio/src/strhelpers.cpp index 48bb7a8af..f5d1d5917 100644 --- a/radio/src/strhelpers.cpp +++ b/radio/src/strhelpers.cpp @@ -89,6 +89,16 @@ int zchar2str(char * dest, const char * src, int size) return size+1; } +int strnlen(const char *src, int max_size) +{ + for (int i=0; i 0) { diff --git a/radio/src/strhelpers.h b/radio/src/strhelpers.h index de39c8867..1b25ab682 100644 --- a/radio/src/strhelpers.h +++ b/radio/src/strhelpers.h @@ -30,6 +30,7 @@ char char2lower(char c); int8_t char2zchar(char c); void str2zchar(char *dest, const char *src, int size); int zchar2str(char *dest, const char *src, int size); +int strnlen(const char *src, int max_size); char * strAppend(char * dest, const char * source, int len = 0); char * strAppendUnsigned(char * dest, uint32_t value, uint8_t digits = 0, uint8_t radix = 10); diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index c01057e9c..be80a40ce 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -238,7 +238,7 @@ set(TARGET_SRC ) if(HARDWARE_TOUCH) - set(TOUCH_DRIVER tp_gt911.cpp) + set(TOUCH_DRIVER tp_gt911.cpp i2c_driver.cpp) add_definitions(-DHARDWARE_TOUCH) endif() diff --git a/radio/src/targets/horus/extmodule_driver.cpp b/radio/src/targets/horus/extmodule_driver.cpp index 9565e900c..3f963a4bc 100644 --- a/radio/src/targets/horus/extmodule_driver.cpp +++ b/radio/src/targets/horus/extmodule_driver.cpp @@ -326,19 +326,21 @@ void extmoduleSendNextFrame() #if defined(EXTMODULE_USART) && defined(EXTMODULE_TX_INVERT_GPIO) extmoduleSendBuffer(extmodulePulsesData.afhds3.getData(), extmodulePulsesData.afhds3.getSize()); #else + if (EXTMODULE_TIMER_DMA_STREAM->CR & DMA_SxCR_EN) + return; + const uint16_t* dataPtr = extmodulePulsesData.afhds3.getData(); uint32_t dataSize = extmodulePulsesData.afhds3.getSize(); - //Peripheral data size - //we know that afdhds3 uses 2 byte values! - uint32_t dmaSize = (EXTMODULE_TIMER_DMA_SIZE & (DMA_SxCR_PSIZE_0 | DMA_SxCR_PSIZE_1)) | DMA_SxCR_MSIZE_0; - - EXTMODULE_TIMER->CCR2 = dataPtr[dataSize -1] - 4000; // 2mS in advance EXTMODULE_TIMER_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA - EXTMODULE_TIMER_DMA_STREAM->CR |= EXTMODULE_TIMER_DMA_CHANNEL | DMA_SxCR_DIR_0 | DMA_SxCR_MINC | dmaSize | DMA_SxCR_PL_0 | DMA_SxCR_PL_1; + EXTMODULE_TIMER_DMA_STREAM->CR |= EXTMODULE_TIMER_DMA_CHANNEL | DMA_SxCR_DIR_0 | DMA_SxCR_MINC | DMA_SxCR_PSIZE_0 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PL_0 | DMA_SxCR_PL_1; EXTMODULE_TIMER_DMA_STREAM->PAR = CONVERT_PTR_UINT(&EXTMODULE_TIMER->ARR); EXTMODULE_TIMER_DMA_STREAM->M0AR = CONVERT_PTR_UINT(dataPtr); - EXTMODULE_TIMER_DMA_STREAM->NDTR = (uint32_t)dataSize; + EXTMODULE_TIMER_DMA_STREAM->NDTR = dataSize; EXTMODULE_TIMER_DMA_STREAM->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // Enable DMA + + // re-init timer + EXTMODULE_TIMER->EGR = TIM_PSCReloadMode_Immediate; + EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; #endif } break; diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index 8d7be0da5..fd1605892 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -728,7 +728,7 @@ #define I2C_SCL_GPIO_PinSource GPIO_PinSource8 #define I2C_SDA_GPIO_PinSource GPIO_PinSource9 #endif -#define I2C_SPEED 400000 +#define I2C_CLK_RATE 400000 // Haptic #define HAPTIC_PWM diff --git a/radio/src/targets/horus/i2c_driver.cpp b/radio/src/targets/horus/i2c_driver.cpp new file mode 100644 index 000000000..1bd9b403d --- /dev/null +++ b/radio/src/targets/horus/i2c_driver.cpp @@ -0,0 +1,1145 @@ +/* + * 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. + * + * The content of this file partially stems from STM32F4 HAL by STMicroelectronics. + */ + +#include "opentx.h" +#include "i2c_driver.h" + +uint32_t GetTickCountMS(void) +{ + static uint32_t tickcount = 0; + tickcount++; + return (tickcount >> 11); // division with 2048, roughly to get milliseconds +} + +/* I2C MSP Initialization + * This function configures the hardware resources used in this example + * @param hi2c: I2C handle pointer + * @retval None + */ +void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c) +{ + GPIO_InitTypeDef GPIO_InitStruct = {0}; + if (I2C_GPIO == GPIOA) + __HAL_RCC_GPIOA_CLK_ENABLE(); + else if (I2C_GPIO == GPIOB) + __HAL_RCC_GPIOB_CLK_ENABLE(); + else if (I2C_GPIO == GPIOC) + __HAL_RCC_GPIOC_CLK_ENABLE(); + else if (I2C_GPIO == GPIOH) + __HAL_RCC_GPIOH_CLK_ENABLE(); + else + TRACE("I2C ERROR: HAL_I2C_MspInit() I2C_GPIO misconfiguration"); + + GPIO_PinAFConfig(I2C_GPIO, I2C_SCL_GPIO_PinSource, I2C_GPIO_AF); + GPIO_PinAFConfig(I2C_GPIO, I2C_SDA_GPIO_PinSource, I2C_GPIO_AF); + + GPIO_InitStruct.GPIO_Pin = I2C_SCL_GPIO_PIN | I2C_SDA_GPIO_PIN; + GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; + GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(I2C_GPIO, &GPIO_InitStruct); + + /* Peripheral clock enable */ + if (I2C == I2C1) + __HAL_RCC_I2C1_CLK_ENABLE(); + else if (I2C == I2C2) + __HAL_RCC_I2C2_CLK_ENABLE(); + else if (I2C == I2C3) + __HAL_RCC_I2C3_CLK_ENABLE(); + else + TRACE("I2C ERROR: HAL_I2C_MspInit() I2C misconfiguration"); +} + +/* De-initializes the GPIOx peripheral registers to their default reset values. + * @param GPIOx where x can be (A..K) to select the GPIO peripheral for STM32F429X device or + * x can be (A..I) to select the GPIO peripheral for STM32F40XX and STM32F427X devices. + * @param GPIO_Pin specifies the port bit to be written. + * This parameter can be one of GPIO_PIN_x where x can be (0..15). + * @retval None + */ +void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin) +{ + uint32_t position; + uint32_t ioposition = 0x00U; + uint32_t iocurrent = 0x00U; + uint32_t tmp = 0x00U; + + /* Check the parameters */ + assert_param(IS_GPIO_ALL_INSTANCE(GPIOx)); + + /* Configure the port pins */ + for(position = 0U; position < GPIO_NUMBER; position++) + { + /* Get the IO position */ + ioposition = 0x01U << position; + /* Get the current IO position */ + iocurrent = (GPIO_Pin) & ioposition; + + if(iocurrent == ioposition) + { + /*------------------------- EXTI Mode Configuration --------------------*/ + tmp = SYSCFG->EXTICR[position >> 2U]; + tmp &= (0x0FU << (4U * (position & 0x03U))); + if(tmp == ((uint32_t)(GPIO_GET_INDEX(GPIOx)) << (4U * (position & 0x03U)))) + { + /* Clear EXTI line configuration */ + EXTI->IMR &= ~((uint32_t)iocurrent); + EXTI->EMR &= ~((uint32_t)iocurrent); + + /* Clear Rising Falling edge configuration */ + EXTI->RTSR &= ~((uint32_t)iocurrent); + EXTI->FTSR &= ~((uint32_t)iocurrent); + + /* Configure the External Interrupt or event for the current IO */ + tmp = 0x0FU << (4U * (position & 0x03U)); + SYSCFG->EXTICR[position >> 2U] &= ~tmp; + } + + /*------------------------- GPIO Mode Configuration --------------------*/ + /* Configure IO Direction in Input Floating Mode */ + GPIOx->MODER &= ~(GPIO_MODER_MODER0 << (position * 2U)); + + /* Configure the default Alternate Function in current IO */ + GPIOx->AFR[position >> 3U] &= ~(0xFU << ((uint32_t)(position & 0x07U) * 4U)) ; + + /* Deactivate the Pull-up and Pull-down resistor for the current IO */ + GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPDR0 << (position * 2U)); + + /* Configure the default value IO Output Type */ + GPIOx->OTYPER &= ~(GPIO_OTYPER_OT_0 << position) ; + + /* Configure the default value for IO Speed */ + GPIOx->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2U)); + } + } +} + +/* I2C MSP De-Initialization + * This function freeze the hardware resources used in this example + * @param hi2c: I2C handle pointer + * @retval None + */ +void HAL_I2C_MspDeInit(I2C_HandleTypeDef* hi2c) +{ + if(hi2c->Instance==I2C1) + { + __HAL_RCC_I2C1_CLK_DISABLE(); + } + else + { + if(hi2c->Instance==I2C2) + { + __HAL_RCC_I2C2_CLK_DISABLE(); + if(hi2c->Instance==I2C3) + __HAL_RCC_I2C3_CLK_DISABLE(); + else + TRACE("I2C ERROR: HAL_I2C_MspDeInit() I2C misconfiguration"); + } + } + + GPIO_InitTypeDef GPIO_InitStructure; + + /* Configure the default Alternate Function in current IO */ + GPIO_PinAFConfig(I2C_GPIO, I2C_SCL_GPIO_PinSource, 0); + GPIO_PinAFConfig(I2C_GPIO, I2C_SDA_GPIO_PinSource, 0); + + GPIO_InitStructure.GPIO_Pin = I2C_SCL_GPIO_PIN | I2C_SDA_GPIO_PIN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; /* Configure a low value for IO Speed */ + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; /* Configure IO Direction in Input Floating Mode */ + GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; /* Leave the configuration to Open Drain */ + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; /* Deactivate the Pull-up and Pull-down resistor for the current IO */ + GPIO_Init(I2C_GPIO, &GPIO_InitStructure); +} + +/* Initializes the I2C according to the specified parameters + * in the HAL_I2C_InitTypeDef and initialize the associated handle. + * @param hi2c Pointer to a I2C_HandleTypeDef structure that contains + * the configuration information for the specified I2C. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_I2C_Init(I2C_HandleTypeDef *hi2c) +{ + uint32_t freqrange; + uint32_t pclk1; + + /* Check the I2C handle allocation */ + if (hi2c == NULL) + { + return HAL_ERROR; + } + + /* Check the parameters */ + assert_param(IS_I2C_ALL_INSTANCE(hi2c->Instance)); + assert_param(IS_I2C_CLOCK_SPEED(hi2c->Init.ClockSpeed)); + assert_param(IS_I2C_DUTY_CYCLE(hi2c->Init.DutyCycle)); + assert_param(IS_I2C_OWN_ADDRESS1(hi2c->Init.OwnAddress1)); + assert_param(IS_I2C_ADDRESSING_MODE(hi2c->Init.AddressingMode)); + assert_param(IS_I2C_DUAL_ADDRESS(hi2c->Init.DualAddressMode)); + assert_param(IS_I2C_OWN_ADDRESS2(hi2c->Init.OwnAddress2)); + assert_param(IS_I2C_GENERAL_CALL(hi2c->Init.GeneralCallMode)); + assert_param(IS_I2C_NO_STRETCH(hi2c->Init.NoStretchMode)); + + if (hi2c->State == HAL_I2C_STATE_RESET) + { + /* Allocate lock resource and initialize it */ + hi2c->Lock = HAL_UNLOCKED; + + /* Init the low level hardware : GPIO, CLOCK, NVIC */ + HAL_I2C_MspInit(hi2c); + } + + hi2c->State = HAL_I2C_STATE_BUSY; + + /* Disable the selected I2C peripheral */ + __HAL_I2C_DISABLE(hi2c); + + /*Reset I2C*/ + hi2c->Instance->CR1 |= I2C_CR1_SWRST; + hi2c->Instance->CR1 &= ~I2C_CR1_SWRST; + + /* Get PCLK1 frequency */ + RCC_ClocksTypeDef rccClocks; + RCC_GetClocksFreq(&rccClocks); + pclk1 = rccClocks.PCLK1_Frequency; + + /* Check the minimum allowed PCLK1 frequency */ + if (I2C_MIN_PCLK_FREQ(pclk1, hi2c->Init.ClockSpeed) == 1U) + { + return HAL_ERROR; + } + + /* Calculate frequency range */ + freqrange = I2C_FREQRANGE(pclk1); + + /*---------------------------- I2Cx CR2 Configuration ----------------------*/ + /* Configure I2Cx: Frequency range */ + MODIFY_REG(hi2c->Instance->CR2, I2C_CR2_FREQ, freqrange); + + /*---------------------------- I2Cx TRISE Configuration --------------------*/ + /* Configure I2Cx: Rise Time */ + MODIFY_REG(hi2c->Instance->TRISE, I2C_TRISE_TRISE, I2C_RISE_TIME(freqrange, hi2c->Init.ClockSpeed)); + + /*---------------------------- I2Cx CCR Configuration ----------------------*/ + /* Configure I2Cx: Speed */ + MODIFY_REG(hi2c->Instance->CCR, (I2C_CCR_FS | I2C_CCR_DUTY | I2C_CCR_CCR), I2C_SPEED(pclk1, hi2c->Init.ClockSpeed, hi2c->Init.DutyCycle)); + + /*---------------------------- I2Cx CR1 Configuration ----------------------*/ + /* Configure I2Cx: Generalcall and NoStretch mode */ + MODIFY_REG(hi2c->Instance->CR1, (I2C_CR1_ENGC | I2C_CR1_NOSTRETCH), (hi2c->Init.GeneralCallMode | hi2c->Init.NoStretchMode)); + + /*---------------------------- I2Cx OAR1 Configuration ---------------------*/ + /* Configure I2Cx: Own Address1 and addressing mode */ + MODIFY_REG(hi2c->Instance->OAR1, (I2C_OAR1_ADDMODE | I2C_OAR1_ADD8_9 | I2C_OAR1_ADD1_7 | I2C_OAR1_ADD0), (hi2c->Init.AddressingMode | hi2c->Init.OwnAddress1)); + + /*---------------------------- I2Cx OAR2 Configuration ---------------------*/ + /* Configure I2Cx: Dual mode and Own Address2 */ + MODIFY_REG(hi2c->Instance->OAR2, (I2C_OAR2_ENDUAL | I2C_OAR2_ADD2), (hi2c->Init.DualAddressMode | hi2c->Init.OwnAddress2)); + + /* Enable the selected I2C peripheral */ + __HAL_I2C_ENABLE(hi2c); + + hi2c->ErrorCode = HAL_I2C_ERROR_NONE; + hi2c->State = HAL_I2C_STATE_READY; + hi2c->PreviousState = I2C_STATE_NONE; + hi2c->Mode = HAL_I2C_MODE_NONE; + + return HAL_OK; +} + +/* DeInitialize the I2C peripheral. + * @param hi2c Pointer to a I2C_HandleTypeDef structure that contains + * the configuration information for the specified I2C. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_I2C_DeInit(I2C_HandleTypeDef *hi2c) +{ + /* Check the I2C handle allocation */ + if (hi2c == NULL) + { + return HAL_ERROR; + } + + /* Check the parameters */ + assert_param(IS_I2C_ALL_INSTANCE(hi2c->Instance)); + + hi2c->State = HAL_I2C_STATE_BUSY; + + /* Disable the I2C Peripheral Clock */ + __HAL_I2C_DISABLE(hi2c); + + /* DeInit the low level hardware: GPIO, CLOCK, NVIC */ + HAL_I2C_MspDeInit(hi2c); + + hi2c->ErrorCode = HAL_I2C_ERROR_NONE; + hi2c->State = HAL_I2C_STATE_RESET; + hi2c->PreviousState = I2C_STATE_NONE; + hi2c->Mode = HAL_I2C_MODE_NONE; + + /* Release Lock */ + __HAL_UNLOCK(hi2c); + + return HAL_OK; +} + +/* Configures I2C Analog noise filter. + * @param hi2c pointer to a I2C_HandleTypeDef structure that contains + * the configuration information for the specified I2Cx peripheral. + * @param AnalogFilter new state of the Analog filter. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_I2CEx_ConfigAnalogFilter(I2C_HandleTypeDef *hi2c, uint32_t AnalogFilter) +{ + /* Check the parameters */ + assert_param(IS_I2C_ALL_INSTANCE(hi2c->Instance)); + assert_param(IS_I2C_ANALOG_FILTER(AnalogFilter)); + + if (hi2c->State == HAL_I2C_STATE_READY) + { + hi2c->State = HAL_I2C_STATE_BUSY; + + /* Disable the selected I2C peripheral */ + __HAL_I2C_DISABLE(hi2c); + + /* Reset I2Cx ANOFF bit */ + hi2c->Instance->FLTR &= ~(I2C_FLTR_ANOFF); + + /* Disable the analog filter */ + hi2c->Instance->FLTR |= AnalogFilter; + + __HAL_I2C_ENABLE(hi2c); + + hi2c->State = HAL_I2C_STATE_READY; + + return HAL_OK; + } + else + { + return HAL_BUSY; + } +} + +/* Configures I2C Digital noise filter. + * @param hi2c pointer to a I2C_HandleTypeDef structure that contains + * the configuration information for the specified I2Cx peripheral. + * @param DigitalFilter Coefficient of digital noise filter between 0x00 and 0x0F. + * @retval HAL status + */ +HAL_StatusTypeDef HAL_I2CEx_ConfigDigitalFilter(I2C_HandleTypeDef *hi2c, uint32_t DigitalFilter) +{ + uint16_t tmpreg = 0; + + /* Check the parameters */ + assert_param(IS_I2C_ALL_INSTANCE(hi2c->Instance)); + assert_param(IS_I2C_DIGITAL_FILTER(DigitalFilter)); + + if (hi2c->State == HAL_I2C_STATE_READY) + { + hi2c->State = HAL_I2C_STATE_BUSY; + + /* Disable the selected I2C peripheral */ + __HAL_I2C_DISABLE(hi2c); + + /* Get the old register value */ + tmpreg = hi2c->Instance->FLTR; + + /* Reset I2Cx DNF bit [3:0] */ + tmpreg &= ~(I2C_FLTR_DNF); + + /* Set I2Cx DNF coefficient */ + tmpreg |= DigitalFilter; + + /* Store the new register value */ + hi2c->Instance->FLTR = tmpreg; + + __HAL_I2C_ENABLE(hi2c); + + hi2c->State = HAL_I2C_STATE_READY; + + return HAL_OK; + } + else + { + return HAL_BUSY; + } +} + +/* This function handles I2C Communication Timeout. + * @param hi2c Pointer to a I2C_HandleTypeDef structure that contains + * the configuration information for I2C module + * @param Flag specifies the I2C flag to check. + * @param Status The new Flag status (SET or RESET). + * @param Timeout Timeout duration + * @param Tickstart Tick start value + * @retval HAL status + */ +static HAL_StatusTypeDef I2C_WaitOnFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Flag, FlagStatus Status, uint32_t Timeout, uint32_t Tickstart) +{ + /* Wait until flag is set */ + while (I2C_GetFlagStatus(hi2c->Instance, Flag) == Status) + { + /* Check for the Timeout */ + if (Timeout != HAL_MAX_DELAY) + { + if (((GetTickCountMS() - Tickstart) > Timeout) || (Timeout == 0U)) + { + hi2c->PreviousState = I2C_STATE_NONE; + hi2c->State = HAL_I2C_STATE_READY; + hi2c->Mode = HAL_I2C_MODE_NONE; + hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT; + + /* Process Unlocked */ + __HAL_UNLOCK(hi2c); + + return HAL_ERROR; + } + } + } + return HAL_OK; +} + +/* This function handles I2C Communication Timeout for Master addressing phase. + * @param hi2c Pointer to a I2C_HandleTypeDef structure that contains + * the configuration information for I2C module + * @param Flag specifies the I2C flag to check. + * @param Timeout Timeout duration + * @param Tickstart Tick start value + * @retval HAL status + */ +static HAL_StatusTypeDef I2C_WaitOnMasterAddressFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Flag, uint32_t Timeout, uint32_t Tickstart) +{ + while (I2C_GetFlagStatus(hi2c->Instance, Flag) == RESET) + { + if (I2C_GetFlagStatus(hi2c->Instance, I2C_FLAG_AF) == SET) + { + /* Generate Stop */ + SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP); + + /* Clear AF Flag */ + __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_AF); + + hi2c->PreviousState = I2C_STATE_NONE; + hi2c->State = HAL_I2C_STATE_READY; + hi2c->Mode = HAL_I2C_MODE_NONE; + hi2c->ErrorCode |= HAL_I2C_ERROR_AF; + + /* Process Unlocked */ + __HAL_UNLOCK(hi2c); + + return HAL_ERROR; + } + + /* Check for the Timeout */ + if (Timeout != HAL_MAX_DELAY) + { + if (((GetTickCountMS() - Tickstart) > Timeout) || (Timeout == 0U)) + { + hi2c->PreviousState = I2C_STATE_NONE; + hi2c->State = HAL_I2C_STATE_READY; + hi2c->Mode = HAL_I2C_MODE_NONE; + hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT; + + /* Process Unlocked */ + __HAL_UNLOCK(hi2c); + + return HAL_ERROR; + } + } + } + return HAL_OK; +} + +/* hi2c Pointer to a I2C_HandleTypeDef structure that contains + * the configuration information for I2C module + * @param DevAddress Target device address: The device 7 bits address value + * in datasheet must be shifted to the left before calling the interface + * @param Timeout Timeout duration + * @param Tickstart Tick start value + * @retval HAL status + */ +static HAL_StatusTypeDef I2C_MasterRequestWrite(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint32_t Timeout, uint32_t Tickstart) +{ + /* Declaration of temporary variable to prevent undefined behavior of volatile usage */ + uint32_t CurrentXferOptions = hi2c->XferOptions; + + /* Generate Start condition if first transfer */ + if ((CurrentXferOptions == I2C_FIRST_AND_LAST_FRAME) || (CurrentXferOptions == I2C_FIRST_FRAME) || (CurrentXferOptions == I2C_NO_OPTION_FRAME)) + { + /* Generate Start */ + SET_BIT(hi2c->Instance->CR1, I2C_CR1_START); + } + else if (hi2c->PreviousState == I2C_STATE_MASTER_BUSY_RX) + { + /* Generate ReStart */ + SET_BIT(hi2c->Instance->CR1, I2C_CR1_START); + } + else + { + /* Do nothing */ + } + + /* Wait until SB flag is set */ + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_SB, RESET, Timeout, Tickstart) != HAL_OK) + { + if (READ_BIT(hi2c->Instance->CR1, I2C_CR1_START) == I2C_CR1_START) + { + hi2c->ErrorCode = HAL_I2C_WRONG_START; + } + return HAL_TIMEOUT; + } + + if (hi2c->Init.AddressingMode == I2C_ADDRESSINGMODE_7BIT) + { + /* Send slave address */ + hi2c->Instance->DR = I2C_7BIT_ADD_WRITE(DevAddress); + } + else + { + /* Send header of slave address */ + hi2c->Instance->DR = I2C_10BIT_HEADER_WRITE(DevAddress); + + /* Wait until ADD10 flag is set */ + if (I2C_WaitOnMasterAddressFlagUntilTimeout(hi2c, I2C_FLAG_ADD10, Timeout, Tickstart) != HAL_OK) + { + return HAL_ERROR; + } + + /* Send slave address */ + hi2c->Instance->DR = I2C_10BIT_ADDRESS(DevAddress); + } + + /* Wait until ADDR flag is set */ + if (I2C_WaitOnMasterAddressFlagUntilTimeout(hi2c, I2C_FLAG_ADDR, Timeout, Tickstart) != HAL_OK) + { + return HAL_ERROR; + } + + return HAL_OK; +} + +/* This function handles Acknowledge failed detection during an I2C Communication. + * @param hi2c Pointer to a I2C_HandleTypeDef structure that contains + * the configuration information for the specified I2C. + * @retval HAL status + */ +static HAL_StatusTypeDef I2C_IsAcknowledgeFailed(I2C_HandleTypeDef *hi2c) +{ + if ( I2C_GetFlagStatus(hi2c->Instance, I2C_FLAG_AF) == SET) + { + /* Clear NACKF Flag */ + __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_AF); + + hi2c->PreviousState = I2C_STATE_NONE; + hi2c->State = HAL_I2C_STATE_READY; + hi2c->Mode = HAL_I2C_MODE_NONE; + hi2c->ErrorCode |= HAL_I2C_ERROR_AF; + + /* Process Unlocked */ + __HAL_UNLOCK(hi2c); + + return HAL_ERROR; + } + return HAL_OK; +} + +/* This function handles I2C Communication Timeout for specific usage of TXE flag. + * @param hi2c Pointer to a I2C_HandleTypeDef structure that contains + * the configuration information for the specified I2C. + * @param Timeout Timeout duration + * @param Tickstart Tick start value + * @retval HAL status + */ +static HAL_StatusTypeDef I2C_WaitOnTXEFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Timeout, uint32_t Tickstart) +{ + while ( I2C_GetFlagStatus(hi2c->Instance, I2C_FLAG_TXE) == RESET) + { + /* Check if a NACK is detected */ + if (I2C_IsAcknowledgeFailed(hi2c) != HAL_OK) + { + return HAL_ERROR; + } + + /* Check for the Timeout */ + if (Timeout != HAL_MAX_DELAY) + { + if (((GetTickCountMS() - Tickstart) > Timeout) || (Timeout == 0U)) + { + hi2c->PreviousState = I2C_STATE_NONE; + hi2c->State = HAL_I2C_STATE_READY; + hi2c->Mode = HAL_I2C_MODE_NONE; + hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT; + + /* Process Unlocked */ + __HAL_UNLOCK(hi2c); + + return HAL_ERROR; + } + } + } + return HAL_OK; +} + +/* This function handles I2C Communication Timeout for specific usage of BTF flag. + * @param hi2c Pointer to a I2C_HandleTypeDef structure that contains + * the configuration information for the specified I2C. + * @param Timeout Timeout duration + * @param Tickstart Tick start value + * @retval HAL status + */ +static HAL_StatusTypeDef I2C_WaitOnBTFFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Timeout, uint32_t Tickstart) +{ + while ( I2C_GetFlagStatus(hi2c->Instance, I2C_FLAG_BTF) == RESET) + { + /* Check if a NACK is detected */ + if (I2C_IsAcknowledgeFailed(hi2c) != HAL_OK) + { + return HAL_ERROR; + } + + /* Check for the Timeout */ + if (Timeout != HAL_MAX_DELAY) + { + if (((GetTickCountMS() - Tickstart) > Timeout) || (Timeout == 0U)) + { + hi2c->PreviousState = I2C_STATE_NONE; + hi2c->State = HAL_I2C_STATE_READY; + hi2c->Mode = HAL_I2C_MODE_NONE; + hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT; + + /* Process Unlocked */ + __HAL_UNLOCK(hi2c); + + return HAL_ERROR; + } + } + } + return HAL_OK; +} + +/* Transmits in master mode an amount of data in blocking mode. + * @param hi2c Pointer to a I2C_HandleTypeDef structure that contains + * the configuration information for the specified I2C. + * @param DevAddress Target device address: The device 7 bits address value + * in datasheet must be shifted to the left before calling the interface + * @param pData Pointer to data buffer + * @param Size Amount of data to be sent + * @param Timeout Timeout duration + * @retval HAL status + */ +HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout) +{ + /* Init tickstart for timeout management*/ + uint32_t tickstart = GetTickCountMS(); + + if (hi2c->State == HAL_I2C_STATE_READY) + { + /* Wait until BUSY flag is reset */ + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG, tickstart) != HAL_OK) + { + return HAL_BUSY; + } + + /* Process Locked */ + __HAL_LOCK(hi2c); + + /* Check if the I2C is already enabled */ + if ((hi2c->Instance->CR1 & I2C_CR1_PE) != I2C_CR1_PE) + { + /* Enable I2C peripheral */ + __HAL_I2C_ENABLE(hi2c); + } + + /* Disable Pos */ + CLEAR_BIT(hi2c->Instance->CR1, I2C_CR1_POS); + + hi2c->State = HAL_I2C_STATE_BUSY_TX; + hi2c->Mode = HAL_I2C_MODE_MASTER; + hi2c->ErrorCode = HAL_I2C_ERROR_NONE; + + /* Prepare transfer parameters */ + hi2c->pBuffPtr = pData; + hi2c->XferCount = Size; + hi2c->XferSize = hi2c->XferCount; + hi2c->XferOptions = I2C_NO_OPTION_FRAME; + + /* Send Slave Address */ + if (I2C_MasterRequestWrite(hi2c, DevAddress, Timeout, tickstart) != HAL_OK) + { + return HAL_ERROR; + } + + /* Clear ADDR flag */ + __HAL_I2C_CLEAR_ADDRFLAG(hi2c); + + while (hi2c->XferSize > 0U) + { + /* Wait until TXE flag is set */ + if (I2C_WaitOnTXEFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK) + { + if (hi2c->ErrorCode == HAL_I2C_ERROR_AF) + { + /* Generate Stop */ + SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP); + } + return HAL_ERROR; + } + + /* Write data to DR */ + hi2c->Instance->DR = *hi2c->pBuffPtr; + + /* Increment Buffer pointer */ + hi2c->pBuffPtr++; + + /* Update counter */ + hi2c->XferCount--; + hi2c->XferSize--; + + if (( I2C_GetFlagStatus(hi2c->Instance, I2C_FLAG_BTF) == SET) && (hi2c->XferSize != 0U)) + { + /* Write data to DR */ + hi2c->Instance->DR = *hi2c->pBuffPtr; + + /* Increment Buffer pointer */ + hi2c->pBuffPtr++; + + /* Update counter */ + hi2c->XferCount--; + hi2c->XferSize--; + } + + /* Wait until BTF flag is set */ + if (I2C_WaitOnBTFFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK) + { + if (hi2c->ErrorCode == HAL_I2C_ERROR_AF) + { + /* Generate Stop */ + SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP); + } + return HAL_ERROR; + } + } + + /* Generate Stop */ + SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP); + + hi2c->State = HAL_I2C_STATE_READY; + hi2c->Mode = HAL_I2C_MODE_NONE; + + /* Process Unlocked */ + __HAL_UNLOCK(hi2c); + + return HAL_OK; + } + else + { + return HAL_BUSY; + } +} + +/* Master sends target device address for read request. + * @param hi2c Pointer to a I2C_HandleTypeDef structure that contains + * the configuration information for I2C module + * @param DevAddress Target device address: The device 7 bits address value + * in datasheet must be shifted to the left before calling the interface + * @param Timeout Timeout duration + * @param Tickstart Tick start value + * @retval HAL status + */ +static HAL_StatusTypeDef I2C_MasterRequestRead(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint32_t Timeout, uint32_t Tickstart) +{ + /* Declaration of temporary variable to prevent undefined behavior of volatile usage */ + uint32_t CurrentXferOptions = hi2c->XferOptions; + + /* Enable Acknowledge */ + SET_BIT(hi2c->Instance->CR1, I2C_CR1_ACK); + + /* Generate Start condition if first transfer */ + if ((CurrentXferOptions == I2C_FIRST_AND_LAST_FRAME) || (CurrentXferOptions == I2C_FIRST_FRAME) || (CurrentXferOptions == I2C_NO_OPTION_FRAME)) + { + /* Generate Start */ + SET_BIT(hi2c->Instance->CR1, I2C_CR1_START); + } + else if (hi2c->PreviousState == I2C_STATE_MASTER_BUSY_TX) + { + /* Generate ReStart */ + SET_BIT(hi2c->Instance->CR1, I2C_CR1_START); + } + else + { + /* Do nothing */ + } + + /* Wait until SB flag is set */ + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_SB, RESET, Timeout, Tickstart) != HAL_OK) + { + if (READ_BIT(hi2c->Instance->CR1, I2C_CR1_START) == I2C_CR1_START) + { + hi2c->ErrorCode = HAL_I2C_WRONG_START; + } + return HAL_TIMEOUT; + } + + if (hi2c->Init.AddressingMode == I2C_ADDRESSINGMODE_7BIT) + { + /* Send slave address */ + hi2c->Instance->DR = I2C_7BIT_ADD_READ(DevAddress); + } + else + { + /* Send header of slave address */ + hi2c->Instance->DR = I2C_10BIT_HEADER_WRITE(DevAddress); + + /* Wait until ADD10 flag is set */ + if (I2C_WaitOnMasterAddressFlagUntilTimeout(hi2c, I2C_FLAG_ADD10, Timeout, Tickstart) != HAL_OK) + { + return HAL_ERROR; + } + + /* Send slave address */ + hi2c->Instance->DR = I2C_10BIT_ADDRESS(DevAddress); + + /* Wait until ADDR flag is set */ + if (I2C_WaitOnMasterAddressFlagUntilTimeout(hi2c, I2C_FLAG_ADDR, Timeout, Tickstart) != HAL_OK) + { + return HAL_ERROR; + } + + /* Clear ADDR flag */ + __HAL_I2C_CLEAR_ADDRFLAG(hi2c); + + /* Generate Restart */ + SET_BIT(hi2c->Instance->CR1, I2C_CR1_START); + + /* Wait until SB flag is set */ + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_SB, RESET, Timeout, Tickstart) != HAL_OK) + { + if (READ_BIT(hi2c->Instance->CR1, I2C_CR1_START) == I2C_CR1_START) + { + hi2c->ErrorCode = HAL_I2C_WRONG_START; + } + return HAL_TIMEOUT; + } + + /* Send header of slave address */ + hi2c->Instance->DR = I2C_10BIT_HEADER_READ(DevAddress); + } + + /* Wait until ADDR flag is set */ + if (I2C_WaitOnMasterAddressFlagUntilTimeout(hi2c, I2C_FLAG_ADDR, Timeout, Tickstart) != HAL_OK) + { + return HAL_ERROR; + } + + return HAL_OK; +} + +/* This function handles I2C Communication Timeout for specific usage of RXNE flag. + * @param hi2c Pointer to a I2C_HandleTypeDef structure that contains + * the configuration information for the specified I2C. + * @param Timeout Timeout duration + * @param Tickstart Tick start value + * @retval HAL status + */ +static HAL_StatusTypeDef I2C_WaitOnRXNEFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Timeout, uint32_t Tickstart) +{ + + while ( I2C_GetFlagStatus(hi2c->Instance, I2C_FLAG_RXNE) == RESET) + { + /* Check if a STOPF is detected */ + if ( I2C_GetFlagStatus(hi2c->Instance, I2C_FLAG_STOPF) == SET) + { + /* Clear STOP Flag */ + __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_STOPF); + + hi2c->PreviousState = I2C_STATE_NONE; + hi2c->State = HAL_I2C_STATE_READY; + hi2c->Mode = HAL_I2C_MODE_NONE; + hi2c->ErrorCode |= HAL_I2C_ERROR_NONE; + + /* Process Unlocked */ + __HAL_UNLOCK(hi2c); + + return HAL_ERROR; + } + + /* Check for the Timeout */ + if (((GetTickCountMS() - Tickstart) > Timeout) || (Timeout == 0U)) + { + hi2c->PreviousState = I2C_STATE_NONE; + hi2c->State = HAL_I2C_STATE_READY; + hi2c->Mode = HAL_I2C_MODE_NONE; + hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT; + + /* Process Unlocked */ + __HAL_UNLOCK(hi2c); + + return HAL_ERROR; + } + } + return HAL_OK; +} + +/* Receives in master mode an amount of data in blocking mode. + * @param hi2c Pointer to a I2C_HandleTypeDef structure that contains + * the configuration information for the specified I2C. + * @param DevAddress Target device address: The device 7 bits address value + * in datasheet must be shifted to the left before calling the interface + * @param pData Pointer to data buffer + * @param Size Amount of data to be sent + * @param Timeout Timeout duration + * @retval HAL status + */ +HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout) +{ + /* Init tickstart for timeout management*/ + uint32_t tickstart = GetTickCountMS(); + + if (hi2c->State == HAL_I2C_STATE_READY) + { + /* Wait until BUSY flag is reset */ + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG, tickstart) != HAL_OK) + { + return HAL_BUSY; + } + + /* Process Locked */ + __HAL_LOCK(hi2c); + + /* Check if the I2C is already enabled */ + if ((hi2c->Instance->CR1 & I2C_CR1_PE) != I2C_CR1_PE) + { + /* Enable I2C peripheral */ + __HAL_I2C_ENABLE(hi2c); + } + + /* Disable Pos */ + CLEAR_BIT(hi2c->Instance->CR1, I2C_CR1_POS); + + hi2c->State = HAL_I2C_STATE_BUSY_RX; + hi2c->Mode = HAL_I2C_MODE_MASTER; + hi2c->ErrorCode = HAL_I2C_ERROR_NONE; + + /* Prepare transfer parameters */ + hi2c->pBuffPtr = pData; + hi2c->XferCount = Size; + hi2c->XferSize = hi2c->XferCount; + hi2c->XferOptions = I2C_NO_OPTION_FRAME; + + /* Send Slave Address */ + if (I2C_MasterRequestRead(hi2c, DevAddress, Timeout, tickstart) != HAL_OK) + { + return HAL_ERROR; + } + + if (hi2c->XferSize == 0U) + { + /* Clear ADDR flag */ + __HAL_I2C_CLEAR_ADDRFLAG(hi2c); + + /* Generate Stop */ + SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP); + } + else if (hi2c->XferSize == 1U) + { + /* Disable Acknowledge */ + CLEAR_BIT(hi2c->Instance->CR1, I2C_CR1_ACK); + + /* Clear ADDR flag */ + __HAL_I2C_CLEAR_ADDRFLAG(hi2c); + + /* Generate Stop */ + SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP); + } + else if (hi2c->XferSize == 2U) + { + /* Disable Acknowledge */ + CLEAR_BIT(hi2c->Instance->CR1, I2C_CR1_ACK); + + /* Enable Pos */ + SET_BIT(hi2c->Instance->CR1, I2C_CR1_POS); + + /* Clear ADDR flag */ + __HAL_I2C_CLEAR_ADDRFLAG(hi2c); + } + else + { + /* Enable Acknowledge */ + SET_BIT(hi2c->Instance->CR1, I2C_CR1_ACK); + + /* Clear ADDR flag */ + __HAL_I2C_CLEAR_ADDRFLAG(hi2c); + } + + while (hi2c->XferSize > 0U) + { + if (hi2c->XferSize <= 3U) + { + /* One byte */ + if (hi2c->XferSize == 1U) + { + /* Wait until RXNE flag is set */ + if (I2C_WaitOnRXNEFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK) + { + return HAL_ERROR; + } + + /* Read data from DR */ + *hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR; + + /* Increment Buffer pointer */ + hi2c->pBuffPtr++; + + /* Update counter */ + hi2c->XferSize--; + hi2c->XferCount--; + } + /* Two bytes */ + else if (hi2c->XferSize == 2U) + { + /* Wait until BTF flag is set */ + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BTF, RESET, Timeout, tickstart) != HAL_OK) + { + return HAL_ERROR; + } + + /* Generate Stop */ + SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP); + + /* Read data from DR */ + *hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR; + + /* Increment Buffer pointer */ + hi2c->pBuffPtr++; + + /* Update counter */ + hi2c->XferSize--; + hi2c->XferCount--; + + /* Read data from DR */ + *hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR; + + /* Increment Buffer pointer */ + hi2c->pBuffPtr++; + + /* Update counter */ + hi2c->XferSize--; + hi2c->XferCount--; + } + /* 3 Last bytes */ + else + { + /* Wait until BTF flag is set */ + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BTF, RESET, Timeout, tickstart) != HAL_OK) + { + return HAL_ERROR; + } + + /* Disable Acknowledge */ + CLEAR_BIT(hi2c->Instance->CR1, I2C_CR1_ACK); + + /* Read data from DR */ + *hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR; + + /* Increment Buffer pointer */ + hi2c->pBuffPtr++; + + /* Update counter */ + hi2c->XferSize--; + hi2c->XferCount--; + + /* Wait until BTF flag is set */ + if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BTF, RESET, Timeout, tickstart) != HAL_OK) + { + return HAL_ERROR; + } + + /* Generate Stop */ + SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP); + + /* Read data from DR */ + *hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR; + + /* Increment Buffer pointer */ + hi2c->pBuffPtr++; + + /* Update counter */ + hi2c->XferSize--; + hi2c->XferCount--; + + /* Read data from DR */ + *hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR; + + /* Increment Buffer pointer */ + hi2c->pBuffPtr++; + + /* Update counter */ + hi2c->XferSize--; + hi2c->XferCount--; + } + } + else + { + /* Wait until RXNE flag is set */ + if (I2C_WaitOnRXNEFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK) + { + return HAL_ERROR; + } + + /* Read data from DR */ + *hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR; + + /* Increment Buffer pointer */ + hi2c->pBuffPtr++; + + /* Update counter */ + hi2c->XferSize--; + hi2c->XferCount--; + + if ( I2C_GetFlagStatus(hi2c->Instance, I2C_FLAG_BTF) == SET) + { + /* Read data from DR */ + *hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR; + + /* Increment Buffer pointer */ + hi2c->pBuffPtr++; + + /* Update counter */ + hi2c->XferSize--; + hi2c->XferCount--; + } + } + } + + hi2c->State = HAL_I2C_STATE_READY; + hi2c->Mode = HAL_I2C_MODE_NONE; + + /* Process Unlocked */ + __HAL_UNLOCK(hi2c); + + return HAL_OK; + } + else + { + return HAL_BUSY; + } +} diff --git a/radio/src/targets/horus/i2c_driver.h b/radio/src/targets/horus/i2c_driver.h new file mode 100644 index 000000000..30bb9ffda --- /dev/null +++ b/radio/src/targets/horus/i2c_driver.h @@ -0,0 +1,385 @@ +/* + * 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. + * + * The content of this file partially stems from STM32F4 HAL by STMicroelectronics. + */ + + +#pragma once + +#include + +#define __IO volatile /*!< Defines 'read / write' permissions */ + +#define GPIO_NUMBER 16U + +#define GPIO_GET_INDEX(__GPIOx__) (uint8_t)(((__GPIOx__) == (GPIOA))? 0U :\ + ((__GPIOx__) == (GPIOB))? 1U :\ + ((__GPIOx__) == (GPIOC))? 2U :\ + ((__GPIOx__) == (GPIOD))? 3U :\ + ((__GPIOx__) == (GPIOE))? 4U :\ + ((__GPIOx__) == (GPIOF))? 5U :\ + ((__GPIOx__) == (GPIOG))? 6U :\ + ((__GPIOx__) == (GPIOH))? 7U :\ + ((__GPIOx__) == (GPIOI))? 8U :\ + ((__GPIOx__) == (GPIOJ))? 9U : 10U) + +#define HAL_MAX_DELAY 0xFFFFFFFFU + +/* AHB1 Peripheral Clock Enable Disable + * @brief Enable or disable the AHB1 peripheral clock. + * @note After reset, the peripheral clock (used for registers read/write access) + * is disabled and the application software has to enable this clock before + * using it. + */ +#define __HAL_RCC_GPIOA_CLK_ENABLE() do { \ + __IO uint32_t tmpreg = 0x00U; \ + SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\ + /* Delay after an RCC peripheral clock enabling */ \ + tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\ + UNUSED(tmpreg); \ + } while(0U) +#define __HAL_RCC_GPIOB_CLK_ENABLE() do { \ + __IO uint32_t tmpreg = 0x00U; \ + SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOBEN);\ + /* Delay after an RCC peripheral clock enabling */ \ + tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOBEN);\ + UNUSED(tmpreg); \ + } while(0U) +#define __HAL_RCC_GPIOC_CLK_ENABLE() do { \ + __IO uint32_t tmpreg = 0x00U; \ + SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN);\ + /* Delay after an RCC peripheral clock enabling */ \ + tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN);\ + UNUSED(tmpreg); \ + } while(0U) +#define __HAL_RCC_GPIOH_CLK_ENABLE() do { \ + __IO uint32_t tmpreg = 0x00U; \ + SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOHEN);\ + /* Delay after an RCC peripheral clock enabling */ \ + tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOHEN);\ + UNUSED(tmpreg); \ + } while(0U) + +#define __HAL_RCC_GPIOA_CLK_DISABLE() (RCC->AHB1ENR &= ~(RCC_AHB1ENR_GPIOAEN)) +#define __HAL_RCC_GPIOB_CLK_DISABLE() (RCC->AHB1ENR &= ~(RCC_AHB1ENR_GPIOBEN)) +#define __HAL_RCC_GPIOC_CLK_DISABLE() (RCC->AHB1ENR &= ~(RCC_AHB1ENR_GPIOCEN)) +#define __HAL_RCC_GPIOH_CLK_DISABLE() (RCC->AHB1ENR &= ~(RCC_AHB1ENR_GPIOHEN)) + +/* APB1 Peripheral Clock Enable Disable + * @brief Enable or disable the Low Speed APB (APB1) peripheral clock. + * @note After reset, the peripheral clock (used for registers read/write access) + * is disabled and the application software has to enable this clock before + * using it. + */ +#define __HAL_RCC_I2C1_CLK_ENABLE() do { \ + __IO uint32_t tmpreg = 0x00U; \ + SET_BIT(RCC->APB1ENR, RCC_APB1ENR_I2C1EN);\ + /* Delay after an RCC peripheral clock enabling */ \ + tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_I2C1EN);\ + UNUSED(tmpreg); \ + } while(0U) +#define __HAL_RCC_I2C2_CLK_ENABLE() do { \ + __IO uint32_t tmpreg = 0x00U; \ + SET_BIT(RCC->APB1ENR, RCC_APB1ENR_I2C2EN);\ + /* Delay after an RCC peripheral clock enabling */ \ + tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_I2C2EN);\ + UNUSED(tmpreg); \ + } while(0U) +#define __HAL_RCC_I2C3_CLK_ENABLE() do { \ + __IO uint32_t tmpreg = 0x00U; \ + SET_BIT(RCC->APB1ENR, RCC_APB1ENR_I2C3EN);\ + /* Delay after an RCC peripheral clock enabling */ \ + tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_I2C3EN);\ + UNUSED(tmpreg); \ + } while(0U) +#define __HAL_RCC_I2C1_CLK_DISABLE() (RCC->APB1ENR &= ~(RCC_APB1ENR_I2C1EN)) +#define __HAL_RCC_I2C2_CLK_DISABLE() (RCC->APB1ENR &= ~(RCC_APB1ENR_I2C2EN)) +#define __HAL_RCC_I2C3_CLK_DISABLE() (RCC->APB1ENR &= ~(RCC_APB1ENR_I2C3EN)) + +#define __HAL_LOCK(__HANDLE__) \ + do{ \ + if((__HANDLE__)->Lock == HAL_LOCKED) \ + { \ + return HAL_BUSY; \ + } \ + else \ + { \ + (__HANDLE__)->Lock = HAL_LOCKED; \ + } \ + }while (0U) + +#define __HAL_UNLOCK(__HANDLE__) \ + do{ \ + (__HANDLE__)->Lock = HAL_UNLOCKED; \ + }while (0U) + + +/* Enable the specified I2C peripheral. + * @param __HANDLE__ specifies the I2C Handle. + * @retval None + */ +#define __HAL_I2C_ENABLE(__HANDLE__) SET_BIT((__HANDLE__)->Instance->CR1, I2C_CR1_PE) + +/* Disable the specified I2C peripheral. + * @param __HANDLE__ specifies the I2C Handle. + * @retval None + */ +#define __HAL_I2C_DISABLE(__HANDLE__) CLEAR_BIT((__HANDLE__)->Instance->CR1, I2C_CR1_PE) + +// I2C Private Constants +#define I2C_FLAG_MASK 0x0000FFFFU +#define I2C_MIN_PCLK_FREQ_STANDARD 2000000U /*!< 2 MHz */ +#define I2C_MIN_PCLK_FREQ_FAST 4000000U /*!< 4 MHz */ + +#define I2C_TIMEOUT_FLAG 35U /*!< Timeout 35 ms */ +#define I2C_TIMEOUT_BUSY_FLAG 25U /*!< Timeout 25 ms */ +#define I2C_TIMEOUT_STOP_FLAG 5U /*!< Timeout 5 ms */ +#define I2C_NO_OPTION_FRAME 0xFFFF0000U /*!< XferOptions default value */ + +// I2C Private Macros +#define I2C_MIN_PCLK_FREQ(__PCLK__, __SPEED__) (((__SPEED__) <= 100000U) ? ((__PCLK__) < I2C_MIN_PCLK_FREQ_STANDARD) : ((__PCLK__) < I2C_MIN_PCLK_FREQ_FAST)) +#define I2C_CCR_CALCULATION(__PCLK__, __SPEED__, __COEFF__) (((((__PCLK__) - 1U)/((__SPEED__) * (__COEFF__))) + 1U) & I2C_CCR_CCR) +#define I2C_FREQRANGE(__PCLK__) ((__PCLK__)/1000000U) +#define I2C_RISE_TIME(__FREQRANGE__, __SPEED__) (((__SPEED__) <= 100000U) ? ((__FREQRANGE__) + 1U) : ((((__FREQRANGE__) * 300U) / 1000U) + 1U)) +#define I2C_SPEED_STANDARD(__PCLK__, __SPEED__) ((I2C_CCR_CALCULATION((__PCLK__), (__SPEED__), 2U) < 4U)? 4U:I2C_CCR_CALCULATION((__PCLK__), (__SPEED__), 2U)) +#define I2C_SPEED_FAST(__PCLK__, __SPEED__, __DUTYCYCLE__) (((__DUTYCYCLE__) == I2C_DUTYCYCLE_2)? I2C_CCR_CALCULATION((__PCLK__), (__SPEED__), 3U) : (I2C_CCR_CALCULATION((__PCLK__), (__SPEED__), 25U) | I2C_DUTYCYCLE_16_9)) +#define I2C_SPEED(__PCLK__, __SPEED__, __DUTYCYCLE__) (((__SPEED__) <= 100000U)? (I2C_SPEED_STANDARD((__PCLK__), (__SPEED__))) : \ + ((I2C_SPEED_FAST((__PCLK__), (__SPEED__), (__DUTYCYCLE__)) & I2C_CCR_CCR) == 0U)? 1U : \ + ((I2C_SPEED_FAST((__PCLK__), (__SPEED__), (__DUTYCYCLE__))) | I2C_CCR_FS)) +#define I2C_7BIT_ADD_WRITE(__ADDRESS__) ((uint8_t)((__ADDRESS__) & (uint8_t)(~I2C_OAR1_ADD0))) +#define I2C_7BIT_ADD_READ(__ADDRESS__) ((uint8_t)((__ADDRESS__) | I2C_OAR1_ADD0)) + +#define I2C_10BIT_ADDRESS(__ADDRESS__) ((uint8_t)((uint16_t)((__ADDRESS__) & (uint16_t)0x00FF))) +#define I2C_10BIT_HEADER_WRITE(__ADDRESS__) ((uint8_t)((uint16_t)((uint16_t)(((uint16_t)((__ADDRESS__) & (uint16_t)0x0300)) >> 7) | (uint16_t)0x00F0))) +#define I2C_10BIT_HEADER_READ(__ADDRESS__) ((uint8_t)((uint16_t)((uint16_t)(((uint16_t)((__ADDRESS__) & (uint16_t)0x0300)) >> 7) | (uint16_t)(0x00F1)))) + +#define I2C_MEM_ADD_MSB(__ADDRESS__) ((uint8_t)((uint16_t)(((uint16_t)((__ADDRESS__) & (uint16_t)0xFF00)) >> 8))) +#define I2C_MEM_ADD_LSB(__ADDRESS__) ((uint8_t)((uint16_t)((__ADDRESS__) & (uint16_t)0x00FF))) + +// I2C duty cycle in fast mode +#define I2C_DUTYCYCLE_2 0x00000000U +#define I2C_DUTYCYCLE_16_9 I2C_CCR_DUTY + +// I2C addressing mode +#define I2C_ADDRESSINGMODE_7BIT 0x00004000U +#define I2C_ADDRESSINGMODE_10BIT (I2C_OAR1_ADDMODE | 0x00004000U) + +// I2C dual addressing mode +#define I2C_DUALADDRESS_DISABLE 0x00000000U +#define I2C_DUALADDRESS_ENABLE I2C_OAR2_ENDUAL + +// I2C general call addressing mode +#define I2C_GENERALCALL_DISABLE 0x00000000U +#define I2C_GENERALCALL_ENABLE I2C_CR1_ENGC + +// I2C nostretch mode +#define I2C_NOSTRETCH_DISABLE 0x00000000U +#define I2C_NOSTRETCH_ENABLE I2C_CR1_NOSTRETCH + +// I2C Error Code definition +#define HAL_I2C_ERROR_NONE 0x00000000U /*!< No error */ +#define HAL_I2C_ERROR_BERR 0x00000001U /*!< BERR error */ +#define HAL_I2C_ERROR_ARLO 0x00000002U /*!< ARLO error */ +#define HAL_I2C_ERROR_AF 0x00000004U /*!< AF error */ +#define HAL_I2C_ERROR_OVR 0x00000008U /*!< OVR error */ +#define HAL_I2C_ERROR_DMA 0x00000010U /*!< DMA transfer error */ +#define HAL_I2C_ERROR_TIMEOUT 0x00000020U /*!< Timeout Error */ +#define HAL_I2C_ERROR_SIZE 0x00000040U /*!< Size Management error */ +#define HAL_I2C_ERROR_DMA_PARAM 0x00000080U /*!< DMA Parameter Error */ +#define HAL_I2C_WRONG_START 0x00000200U /*!< Wrong start Error */ + +// I2C XferOptions definition +#define I2C_FIRST_FRAME 0x00000001U +#define I2C_FIRST_AND_NEXT_FRAME 0x00000002U +#define I2C_NEXT_FRAME 0x00000004U +#define I2C_FIRST_AND_LAST_FRAME 0x00000008U +#define I2C_LAST_FRAME_NO_STOP 0x00000010U +#define I2C_LAST_FRAME 0x00000020U + +// I2C states +#define I2C_STATE_MSK ((uint32_t)((uint32_t)((uint32_t)HAL_I2C_STATE_BUSY_TX | (uint32_t)HAL_I2C_STATE_BUSY_RX) & (uint32_t)(~((uint32_t)HAL_I2C_STATE_READY)))) /*!< Mask State define, keep only RX and TX bits */ +#define I2C_STATE_NONE ((uint32_t)(HAL_I2C_MODE_NONE)) /*!< Default Value */ +#define I2C_STATE_MASTER_BUSY_TX ((uint32_t)(((uint32_t)HAL_I2C_STATE_BUSY_TX & I2C_STATE_MSK) | (uint32_t)HAL_I2C_MODE_MASTER)) /*!< Master Busy TX, combinaison of State LSB and Mode enum */ +#define I2C_STATE_MASTER_BUSY_RX ((uint32_t)(((uint32_t)HAL_I2C_STATE_BUSY_RX & I2C_STATE_MSK) | (uint32_t)HAL_I2C_MODE_MASTER)) /*!< Master Busy RX, combinaison of State LSB and Mode enum */ +#define I2C_STATE_SLAVE_BUSY_TX ((uint32_t)(((uint32_t)HAL_I2C_STATE_BUSY_TX & I2C_STATE_MSK) | (uint32_t)HAL_I2C_MODE_SLAVE)) /*!< Slave Busy TX, combinaison of State LSB and Mode enum */ +#define I2C_STATE_SLAVE_BUSY_RX ((uint32_t)(((uint32_t)HAL_I2C_STATE_BUSY_RX & I2C_STATE_MSK) | (uint32_t)HAL_I2C_MODE_SLAVE)) /*!< Slave Busy RX, combinaison of State LSB and Mode enum */ + +// I2C Analog Filter +#define I2C_ANALOGFILTER_ENABLE 0x00000000U +#define I2C_ANALOGFILTER_DISABLE I2C_FLTR_ANOFF + +/* Clears the I2C ADDR pending flag. + * @param __HANDLE__ specifies the I2C Handle. + * This parameter can be I2C where x: 1, 2, or 3 to select the I2C peripheral. + * @retval None + */ +#define __HAL_I2C_CLEAR_ADDRFLAG(__HANDLE__) \ + do{ \ + __IO uint32_t tmpreg = 0x00U; \ + tmpreg = (__HANDLE__)->Instance->SR1; \ + tmpreg = (__HANDLE__)->Instance->SR2; \ + UNUSED(tmpreg); \ + } while(0) + +/* Clears the I2C pending flags which are cleared by writing 0 in a specific bit. + * @param __HANDLE__ specifies the I2C Handle. + * @param __FLAG__ specifies the flag to clear. + * This parameter can be any combination of the following values: + * @arg I2C_FLAG_OVR: Overrun/Underrun flag (Slave mode) + * @arg I2C_FLAG_AF: Acknowledge failure flag + * @arg I2C_FLAG_ARLO: Arbitration lost flag (Master mode) + * @arg I2C_FLAG_BERR: Bus error flag + * @retval None + */ +#define __HAL_I2C_CLEAR_FLAG(__HANDLE__, __FLAG__) ((__HANDLE__)->Instance->SR1 = (uint16_t)(~((__FLAG__) & I2C_FLAG_MASK))) + +// HAL Status structures definition +typedef enum +{ + HAL_OK = 0x00U, + HAL_ERROR = 0x01U, + HAL_BUSY = 0x02U, + HAL_TIMEOUT = 0x03U +} HAL_StatusTypeDef; + +// HAL lock structures definition +typedef enum +{ + HAL_UNLOCKED = 0x00U, + HAL_LOCKED = 0x01U +} HAL_LockTypeDef; + +/* HAL State structure definition + * HAL I2C State value coding follow below described bitmap : + * b7-b6 Error information + * 00 : No Error + * 01 : Abort (Abort user request on going) + * 10 : Timeout + * 11 : Error + * b5 Peripheral initialization status + * 0 : Reset (Peripheral not initialized) + * 1 : Init done (Peripheral initialized and ready to use. HAL I2C Init function called) + * b4 (not used) + * x : Should be set to 0 + * b3 + * 0 : Ready or Busy (No Listen mode ongoing) + * 1 : Listen (Peripheral in Address Listen Mode) + * b2 Intrinsic process state + * 0 : Ready + * 1 : Busy (Peripheral busy with some configuration or internal operations) + * b1 Rx state + * 0 : Ready (no Rx operation ongoing) + * 1 : Busy (Rx operation ongoing) + * b0 Tx state + * 0 : Ready (no Tx operation ongoing) + * 1 : Busy (Tx operation ongoing) + */ +typedef enum +{ + HAL_I2C_STATE_RESET = 0x00U, /*!< Peripheral is not yet Initialized */ + HAL_I2C_STATE_READY = 0x20U, /*!< Peripheral Initialized and ready for use */ + HAL_I2C_STATE_BUSY = 0x24U, /*!< An internal process is ongoing */ + HAL_I2C_STATE_BUSY_TX = 0x21U, /*!< Data Transmission process is ongoing */ + HAL_I2C_STATE_BUSY_RX = 0x22U, /*!< Data Reception process is ongoing */ + HAL_I2C_STATE_LISTEN = 0x28U, /*!< Address Listen Mode is ongoing */ + HAL_I2C_STATE_BUSY_TX_LISTEN = 0x29U, /*!< Address Listen Mode and Data Transmission + process is ongoing */ + HAL_I2C_STATE_BUSY_RX_LISTEN = 0x2AU, /*!< Address Listen Mode and Data Reception + process is ongoing */ + HAL_I2C_STATE_ABORT = 0x60U, /*!< Abort user request ongoing */ + HAL_I2C_STATE_TIMEOUT = 0xA0U, /*!< Timeout state */ + HAL_I2C_STATE_ERROR = 0xE0U /*!< Error */ +} HAL_I2C_StateTypeDef; + +/* HAL Mode structure definition + * HAL I2C Mode value coding follow below described bitmap : + * b7 (not used) + * x : Should be set to 0 + * b6 + * 0 : None + * 1 : Memory (HAL I2C communication is in Memory Mode) + * b5 + * 0 : None + * 1 : Slave (HAL I2C communication is in Slave Mode) + * b4 + * 0 : None + * 1 : Master (HAL I2C communication is in Master Mode) + * b3-b2-b1-b0 (not used) + * xxxx : Should be set to 0000 + */ +typedef enum +{ + HAL_I2C_MODE_NONE = 0x00U, /*!< No I2C communication on going */ + HAL_I2C_MODE_MASTER = 0x10U, /*!< I2C communication is in Master Mode */ + HAL_I2C_MODE_SLAVE = 0x20U, /*!< I2C communication is in Slave Mode */ + HAL_I2C_MODE_MEM = 0x40U /*!< I2C communication is in Memory Mode */ +} HAL_I2C_ModeTypeDef; + +// I2C configuration structure definition +typedef struct +{ + uint32_t ClockSpeed; /*!< Specifies the clock frequency. + This parameter must be set to a value lower than 400kHz */ + uint32_t DutyCycle; /*!< Specifies the I2C fast mode duty cycle. + This parameter can be a value of @ref I2C_duty_cycle_in_fast_mode */ + uint32_t OwnAddress1; /*!< Specifies the first device own address. + This parameter can be a 7-bit or 10-bit address. */ + uint32_t AddressingMode; /*!< Specifies if 7-bit or 10-bit addressing mode is selected. + This parameter can be a value of @ref I2C_addressing_mode */ + uint32_t DualAddressMode; /*!< Specifies if dual addressing mode is selected. + This parameter can be a value of @ref I2C_dual_addressing_mode */ + uint32_t OwnAddress2; /*!< Specifies the second device own address if dual addressing mode is selected + This parameter can be a 7-bit address. */ + uint32_t GeneralCallMode; /*!< Specifies if general call mode is selected. + This parameter can be a value of @ref I2C_general_call_addressing_mode */ + uint32_t NoStretchMode; /*!< Specifies if nostretch mode is selected. + This parameter can be a value of @ref I2C_nostretch_mode */ +} HAL_I2C_InitTypeDef; // WARNING: renamed from I2C_InitTypeDef due to conflict + +// I2C handle structure definition +typedef struct +{ + I2C_TypeDef *Instance; /*!< I2C registers base address */ + HAL_I2C_InitTypeDef Init; /*!< I2C communication parameters (MOD!) */ + uint8_t *pBuffPtr; /*!< Pointer to I2C transfer buffer */ + uint16_t XferSize; /*!< I2C transfer size */ + __IO uint16_t XferCount; /*!< I2C transfer counter */ + __IO uint32_t XferOptions; /*!< I2C transfer options */ + __IO uint32_t PreviousState; /*!< I2C communication Previous state and mode + context for internal usage */ + //DMA_HandleTypeDef *hdmatx; /*!< I2C Tx DMA handle parameters */ + //DMA_HandleTypeDef *hdmarx; /*!< I2C Rx DMA handle parameters */ + HAL_LockTypeDef Lock; /*!< I2C locking object */ + __IO HAL_I2C_StateTypeDef State; /*!< I2C communication state */ + __IO HAL_I2C_ModeTypeDef Mode; /*!< I2C communication mode */ + __IO uint32_t ErrorCode; /*!< I2C Error code */ + __IO uint32_t Devaddress; /*!< I2C Target device address */ + __IO uint32_t Memaddress; /*!< I2C Target memory address */ + __IO uint32_t MemaddSize; /*!< I2C Target memory address size */ + __IO uint32_t EventCount; /*!< I2C Event counter */ +} I2C_HandleTypeDef; + +HAL_StatusTypeDef HAL_I2C_Init(I2C_HandleTypeDef *hi2c); +HAL_StatusTypeDef HAL_I2C_DeInit(I2C_HandleTypeDef *hi2c); +HAL_StatusTypeDef HAL_I2CEx_ConfigAnalogFilter(I2C_HandleTypeDef *hi2c, uint32_t AnalogFilter); +HAL_StatusTypeDef HAL_I2CEx_ConfigDigitalFilter(I2C_HandleTypeDef *hi2c, uint32_t DigitalFilter); +HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout); +HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout); diff --git a/radio/src/targets/horus/tp_gt911.cpp b/radio/src/targets/horus/tp_gt911.cpp index 8dd01b49b..667df4b50 100644 --- a/radio/src/targets/horus/tp_gt911.cpp +++ b/radio/src/targets/horus/tp_gt911.cpp @@ -19,6 +19,7 @@ */ #include "opentx.h" +#include "i2c_driver.h" #include "tp_gt911.h" #include "touch.h" @@ -409,6 +410,8 @@ struct TouchData touchData; uint16_t touchGT911fwver = 0; uint32_t touchGT911hiccups = 0; +I2C_HandleTypeDef hi2c1; + static void TOUCH_AF_ExtiStop(void) { SYSCFG_EXTILineConfig(TOUCH_INT_EXTI_PortSource, TOUCH_INT_EXTI_PinSource1); @@ -484,267 +487,75 @@ void TOUCH_AF_INT_Change(void) GPIO_Init(TOUCH_INT_GPIO, &GPIO_InitStructure); } -void I2C_Init() +void I2C_Init_Radio(void) { TRACE("I2C Init"); - I2C_DeInit(I2C); - - GPIO_InitTypeDef GPIO_InitStructure; - I2C_InitTypeDef I2C_InitStructure; - I2C_InitStructure.I2C_ClockSpeed = I2C_SPEED; - I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; - I2C_InitStructure.I2C_OwnAddress1 = 0x00; - I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; - I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; - I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; - I2C_Init(I2C, &I2C_InitStructure); - I2C_Cmd(I2C, ENABLE); - - GPIO_PinAFConfig(I2C_GPIO, I2C_SCL_GPIO_PinSource, I2C_GPIO_AF); - GPIO_PinAFConfig(I2C_GPIO, I2C_SDA_GPIO_PinSource, I2C_GPIO_AF); - - GPIO_InitStructure.GPIO_Pin = I2C_SCL_GPIO_PIN | I2C_SDA_GPIO_PIN; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; - GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_Init(I2C_GPIO, &GPIO_InitStructure); + hi2c1.Instance = I2C; + hi2c1.Init.ClockSpeed = I2C_CLK_RATE; + hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_16_9; + hi2c1.Init.OwnAddress1 = 0; + hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; + hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; + hi2c1.Init.OwnAddress2 = 0; + hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; + hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; + if (HAL_I2C_Init(&hi2c1) != HAL_OK) + { + TRACE("I2C ERROR: HAL_I2C_Init() failed"); + } + // Configure Analogue filter + if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK) + { + TRACE("I2C ERROR: HAL_I2CEx_ConfigAnalogFilter() failed"); + } + // Configure Digital filter + if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK) + { + TRACE("I2C ERROR: HAL_I2CEx_ConfigDigitalFilter() failed"); + } } -bool I2C_WaitFlag(uint32_t flag) +bool I2C_GT911_WriteRegister(uint16_t reg, uint8_t * buf, uint8_t len) { - uint32_t timeout = I2C_TIMEOUT_MAX; - while (!I2C_GetFlagStatus(I2C, flag)) { - if ((timeout--) == 0) return false; - } - return true; -} + uint8_t uAddrAndBuf[258]; + uAddrAndBuf[0] = (uint8_t)((reg & 0xFF00) >> 8); + uAddrAndBuf[1] = (uint8_t)(reg & 0x00FF); -bool I2C_WaitEvent(uint32_t event) -{ - uint32_t timeout = I2C_TIMEOUT_MAX; - while (!I2C_CheckEvent(I2C, event)) { - if ((timeout--) == 0) return false; - } - return true; -} - -bool I2C_WaitEventCleared(uint32_t event) -{ - uint32_t timeout = I2C_TIMEOUT_MAX; - while (I2C_CheckEvent(I2C, event)) { - if ((timeout--) == 0) return false; - } - return true; -} - -uint8_t I2C_GT911_WriteRegister(uint16_t reg, uint8_t * buf, uint8_t len) -{ - if (!I2C_WaitEventCleared(I2C_FLAG_BUSY)) - { - TRACE("I2C WRITE ERROR: busy timeout"); - return false; - } - - I2C_GenerateSTART(I2C, ENABLE); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_MODE_SELECT)) - { - TRACE("I2C WRITE ERROR: could not issue start"); - return false; - } - - I2C_Send7bitAddress(I2C, GT_CMD_WR, I2C_Direction_Transmitter); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) - { - TRACE("I2C WRITE ERROR: could not send device address"); - return false; - } - - I2C_SendData(I2C, (uint8_t)((reg & 0xFF00) >> 8)); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTING)) - { - TRACE("I2C WRITE ERROR: could not send register high address"); - return false; - } - - I2C_SendData(I2C, (uint8_t)(reg & 0x00FF)); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTING)) - { - TRACE("I2C WRITE ERROR: could not send register low address"); - return false; - } - - /* While there is data to be written */ - while (len--) { - I2C_SendData(I2C, *buf); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTING)) + if (len > 0) { - TRACE("I2C WRITE ERROR: could not send data with %u bytes remaining", len+1 ); - return false; + for (int i = 0;i < len;i++) + { + uAddrAndBuf[i + 2] = buf[i]; + } } - buf++; - } - if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) - { - TRACE("I2C WRITE ERROR: waiting for BTF failed."); - return false; - } - - I2C_GenerateSTOP(I2C, ENABLE); - return true; + if (HAL_I2C_Master_Transmit(&hi2c1, GT911_I2C_ADDR << 1, uAddrAndBuf, len + 2, 100) != HAL_OK) + { + TRACE("I2C ERROR: WriteRegister failed"); + return false; + } + return true; } -bool I2C_GT911_ReadRegister(u16 reg, uint8_t * buf, uint8_t len) +bool I2C_GT911_ReadRegister(uint16_t reg, uint8_t * buf, uint8_t len) { - if (!I2C_WaitEventCleared(I2C_FLAG_BUSY)) - { - TRACE("I2C READ ERROR: busy timeout"); - return false; - } + uint8_t uRegAddr[2]; + uRegAddr[0] = (uint8_t)((reg & 0xFF00) >> 8); + uRegAddr[1] = (uint8_t)(reg & 0x00FF); - I2C_GenerateSTART(I2C, ENABLE); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_MODE_SELECT)) - { - TRACE("I2C READ ERROR: could not issue first step start"); - return false; - } - - I2C_Send7bitAddress(I2C, GT_CMD_WR, I2C_Direction_Transmitter); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) - { - TRACE("I2C READ ERROR: could not send first step device address"); - return false; - } - - I2C_SendData(I2C, (uint8_t)((reg & 0xFF00) >> 8)); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTING)) - { - TRACE("I2C READ ERROR: could not send register high address"); - return false; - } - - I2C_SendData(I2C, (uint8_t)(reg & 0x00FF)); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) - { - TRACE("I2C READ ERROR: could not send register low address"); - return false; - } - - // According to GP911 data sheet, repeated start is not listed, so we do full stop. - // See chapter 6.1.c, page 12, diagram for reading data - I2C_GenerateSTOP(I2C, ENABLE); - if (!I2C_WaitEventCleared(I2C_FLAG_BUSY)) - { - TRACE("I2C READ ERROR: timeout waiting for first stop"); - return false; - } - - // Enable Acknowledgement, clear POS flag - I2C_AcknowledgeConfig(I2C, ENABLE); - I2C_NACKPositionConfig(I2C, I2C_NACKPosition_Current); - - I2C_GenerateSTART(I2C, ENABLE); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_MODE_SELECT)) - { - TRACE("I2C READ ERROR: could not issue second step start"); - return false; - } - - I2C_Send7bitAddress(I2C, GT_CMD_RD, I2C_Direction_Receiver); - if (!I2C_WaitEvent(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) - { - TRACE("I2C READ ERROR: could not send second step device address"); - return false; - } - - if (len == 1) { - // Clear Ack bit - I2C_AcknowledgeConfig(I2C, DISABLE); - - // EV6_1 -- must be atomic -- Clear ADDR, generate STOP - __disable_irq(); - (void) I2C->SR2; - I2C_GenerateSTOP(I2C,ENABLE); - __enable_irq(); - - // Receive data EV7 - if(!I2C_WaitFlag(I2C_FLAG_RXNE)) { - TRACE("I2C READ ERROR: timeout waiting for RxNE"); - I2C_Init(); - return false; - } - - *buf++ = I2C_ReceiveData(I2C); - } - else if (len == 2) { - - // Set POS flag - I2C_NACKPositionConfig(I2C, I2C_NACKPosition_Next); - - // EV6_1 -- must be atomic and in this order - __disable_irq(); - (void) I2C->SR2; // Clear ADDR flag - I2C_AcknowledgeConfig(I2C, DISABLE); // Clear Ack bit - __enable_irq(); - - // EV7_3 -- Wait for BTF, program stop, read data twice - if (!I2C_WaitFlag(I2C_FLAG_BTF)) { - TRACE("I2C READ ERROR: timeout waiting for BTF"); - I2C_Init(); - return false; - } - - __disable_irq(); - I2C_GenerateSTOP(I2C,ENABLE); - *buf++ = I2C->DR; - __enable_irq(); - - *buf++ = I2C->DR; - } - else { - - (void) I2C->SR2; // Clear ADDR flag - while (len-- != 3) { - // EV7 -- cannot guarantee 1 transfer completion time, wait for BTF - // instead of RXNE - if (!I2C_WaitFlag(I2C_FLAG_BTF)) { - TRACE("I2C READ ERROR: timeout waiting for BTF"); - I2C_Init(); + if (HAL_I2C_Master_Transmit(&hi2c1, GT911_I2C_ADDR << 1, uRegAddr, 2, 10) != HAL_OK) + { + TRACE("I2C ERROR: ReadRegister write reg address failed"); return false; - } - - *buf++ = I2C_ReceiveData(I2C); } - // Data N-2 in DR, data N-1 in shift register, - // SCL stretched low until data N-2 is read - if (!I2C_WaitFlag(I2C_FLAG_BTF)) { - TRACE("I2C READ ERROR: timeout waiting for BTF"); - I2C_Init(); - return false; + if (HAL_I2C_Master_Receive(&hi2c1, GT911_I2C_ADDR << 1, buf, len, 100) != HAL_OK) + { + TRACE("I2C ERROR: ReadRegister read reg address failed"); + return false; } - - I2C_AcknowledgeConfig(I2C, DISABLE); // clear ack bit - *buf++ = I2C_ReceiveData(I2C); // receive byte N-2 - - // Wait for BTF, program stop, read data twice - if (!I2C_WaitFlag(I2C_FLAG_BTF)) { - TRACE("I2C READ ERROR: timeout waiting for BTF"); - I2C_Init(); - return false; - } - - __disable_irq(); - I2C_GenerateSTOP(I2C,ENABLE); - *buf++ = I2C->DR; - __enable_irq(); - - *buf++ = I2C->DR; - } - - return true; + return true; } bool I2C_GT911_SendConfig(void) @@ -791,7 +602,7 @@ bool touchPanelInit(void) TRACE("Touchpanel init start ..."); TOUCH_AF_GPIOConfig(); //SET RST=OUT INT=OUT INT=LOW - I2C_Init(); + I2C_Init_Radio(); TPRST_LOW(); TPINT_HIGH(); @@ -827,7 +638,7 @@ bool touchPanelInit(void) } TRACE("Chip config Ver:%x", tmp[0]); - if (tmp[0] <= GT911_CFG_NUMER) //Config ver + if (tmp[0] < GT911_CFG_NUMER) //Config ver { TRACE("Sending new config %d", GT911_CFG_NUMER); if (!I2C_GT911_SendConfig()) @@ -864,6 +675,23 @@ bool touchPanelInit(void) } } +bool I2C_ReInit(void) +{ + TRACE("I2C ReInit"); + touchPanelDeInit(); + if (HAL_I2C_DeInit(&hi2c1) != HAL_OK) + TRACE("I2C ReInit - I2C DeInit failed"); + + // If DeInit fails, try to re-init anyway + if (!touchPanelInit()) + { + TRACE("I2C ReInit - touchPanelInit failed"); + return false; + } + return true; +} + + #if defined(SIMU) || defined(SEMIHOSTING) || defined(DEBUG) static const char* event2str(uint8_t ev) { @@ -888,10 +716,10 @@ void touchPanelRead() { uint8_t state = 0; - // if (!touchEventOccured) - // return; + if (!touchEventOccured) + return; - // touchEventOccured = false; + touchEventOccured = false; uint32_t startReadStatus = RTOS_GET_MS(); do { @@ -899,8 +727,8 @@ void touchPanelRead() //ledRed(); touchGT911hiccups++; TRACE("GT911 I2C read XY error"); - touchPanelDeInit(); - touchPanelInit(); + if (!I2C_ReInit()) + TRACE("I2C ReInit failed"); return; } @@ -921,8 +749,8 @@ void touchPanelRead() //ledRed(); touchGT911hiccups++; TRACE("GT911 I2C data read error"); - touchPanelDeInit(); - touchPanelInit(); + if (!I2C_ReInit()) + TRACE("I2C ReInit failed"); return; } if (touchState.event == TE_NONE || touchState.event == TE_UP || diff --git a/radio/src/targets/horus/tp_gt911.h b/radio/src/targets/horus/tp_gt911.h index 0651dc1b6..d0fe6e903 100644 --- a/radio/src/targets/horus/tp_gt911.h +++ b/radio/src/targets/horus/tp_gt911.h @@ -30,28 +30,29 @@ extern bool touchPanelInit(); void touchPanelRead(); bool touchPanelEventOccured(); -#define GT911_TIMEOUT 3 // 3ms +#define GT911_TIMEOUT 3 // 3ms #define GT911_MAX_TP 5 -#define GT911_CFG_NUMER 0x6C +#define GT911_CFG_NUMER 0x6D //I2C -#define GT_CMD_WR 0X28 -#define GT_CMD_RD 0X29 -#define I2C_TIMEOUT_MAX 1000 +#define GT911_I2C_ADDR 0x14 +//#define GT_CMD_WR 0x28 +//#define GT_CMD_RD 0x29 +//#define I2C_TIMEOUT_MAX 1000 -//GT9147 -#define GT_CTRL_REG 0X8040 -#define GT_CFGS_REG 0X8047 -#define GT_CHECK_REG 0X80FF -#define GT_PID_REG 0X8140 +//GT911 +#define GT_CTRL_REG 0x8040 +#define GT_CFGS_REG 0x8047 +#define GT_CHECK_REG 0x80FF +#define GT_PID_REG 0x8140 -#define GT_GSTID_REG 0X814E -#define GT_TP1_REG 0X8150 -#define GT_TP2_REG 0X8158 -#define GT_TP3_REG 0X8160 -#define GT_TP4_REG 0X8168 -#define GT_TP5_REG 0X8170 +#define GT_GSTID_REG 0x814E +#define GT_TP1_REG 0x8150 +#define GT_TP2_REG 0x8158 +#define GT_TP3_REG 0x8160 +#define GT_TP4_REG 0x8168 +#define GT_TP5_REG 0x8170 #define GT911_READ_XY_REG 0x814E #define GT911_CLEARBUF_REG 0x814E diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index 27d68d9c0..e25ed35c5 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -1968,7 +1968,7 @@ #define I2C_SDA_GPIO_PinSource GPIO_PinSource7 #define I2C_ADDRESS_VOLUME 0x5C #endif -#define I2C_SPEED 400000 +#define I2C_CLK_RATE 400000 #define I2C_ADDRESS_EEPROM 0xA2 #define I2C_FLASH_PAGESIZE 64 diff --git a/radio/src/targets/taranis/i2c_driver.cpp b/radio/src/targets/taranis/i2c_driver.cpp index 2620312ba..24d15a2c0 100644 --- a/radio/src/targets/taranis/i2c_driver.cpp +++ b/radio/src/targets/taranis/i2c_driver.cpp @@ -37,7 +37,7 @@ void i2cInit() GPIO_ResetBits(I2C_WP_GPIO, I2C_WP_GPIO_PIN); I2C_InitTypeDef I2C_InitStructure; - I2C_InitStructure.I2C_ClockSpeed = I2C_SPEED; + I2C_InitStructure.I2C_ClockSpeed = I2C_CLK_RATE; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 = 0x00; I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; diff --git a/radio/src/tests/transparency_CN_480x272.png b/radio/src/tests/transparency_CN_480x272.png index 92083a206..a6ee82449 100644 Binary files a/radio/src/tests/transparency_CN_480x272.png and b/radio/src/tests/transparency_CN_480x272.png differ diff --git a/radio/src/tests/transparency_EN_480x272.png b/radio/src/tests/transparency_EN_480x272.png index a1cc5987b..301584068 100644 Binary files a/radio/src/tests/transparency_EN_480x272.png and b/radio/src/tests/transparency_EN_480x272.png differ diff --git a/radio/src/thirdparty/libopenui b/radio/src/thirdparty/libopenui index 014e87b9f..980959608 160000 --- a/radio/src/thirdparty/libopenui +++ b/radio/src/thirdparty/libopenui @@ -1 +1 @@ -Subproject commit 014e87b9fc5dd345a3619f375b84c5e9747a398f +Subproject commit 980959608920417990b7f9aeb1ffa7d4d78096b7 diff --git a/radio/src/timers.cpp b/radio/src/timers.cpp index 0ee289e78..c3d336b1f 100644 --- a/radio/src/timers.cpp +++ b/radio/src/timers.cpp @@ -174,3 +174,22 @@ void evalTimers(int16_t throttle, uint8_t tick10ms) } } } + +int16_t throttleSource2Source(int16_t thrSrc) +{ + if (thrSrc == 0) return (int16_t)MIXSRC_Thr; + if (--thrSrc < NUM_POTS + NUM_SLIDERS) + return (int16_t)(thrSrc + MIXSRC_FIRST_POT); + return (int16_t)(thrSrc - (NUM_POTS + NUM_SLIDERS) + MIXSRC_FIRST_CH); +} + +int16_t source2ThrottleSource(int16_t src) +{ + if (src == MIXSRC_Thr) + return 0; + else if (src <= MIXSRC_LAST_POT) + return src - MIXSRC_FIRST_POT + 1; + else if (src <= MIXSRC_LAST_CH) + return src - MIXSRC_FIRST_CH + NUM_POTS + NUM_SLIDERS + 1; + return -1; +} diff --git a/radio/src/timers.h b/radio/src/timers.h index fb5f7a4be..5226414a0 100644 --- a/radio/src/timers.h +++ b/radio/src/timers.h @@ -62,4 +62,7 @@ static inline tmr10ms_t get_tmr10ms() return g_tmr10ms; } +int16_t throttleSource2Source(int16_t thrSrc); +int16_t source2ThrottleSource(int16_t src); + #endif // _TIMERS_H_ diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index de1c9e9cb..7b99fb514 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -166,7 +166,7 @@ #define TR_CSWEQUAL "a=x\0 " #define LEN_VCSWFUNC "\005" -#define TR_VCSWFUNC "---\0 " TR_CSWEQUAL "a~x\0 ""a>x\0 ""ax""|a|b\0 ""ax\0 ""ax""|a|b\0 ""a