diff --git a/CMakeLists.txt b/CMakeLists.txt index 69c809f6e..011d767c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ endif() set(CMAKE_COLOR_MAKEFILE ON) set(CMAKE_CXX_STANDARD 11) +add_definitions(-D_GLIBCXX_USE_C99=1) # proper to_string definition set(RADIO_DIRECTORY ${PROJECT_SOURCE_DIR}/radio) set(RADIO_SRC_DIRECTORY ${RADIO_DIRECTORY}/src) diff --git a/cmake/Bitmaps.cmake b/cmake/Bitmaps.cmake index f5281c0d3..df61adde4 100644 --- a/cmake/Bitmaps.cmake +++ b/cmake/Bitmaps.cmake @@ -7,18 +7,19 @@ macro(add_bitmaps_target targetname filter width format) add_custom_command( OUTPUT ${target} COMMAND ${PYTHON_EXECUTABLE} ${RADIO_DIRECTORY}/util/img2lbm.py ${bitmap} ${target} ${width} ${format} ${ARGN} - DEPENDS ${bitmap} + DEPENDS ${bitmap} ${RADIO_DIRECTORY}/util/img2lbm.py ) list(APPEND bitmaps_files ${target}) endforeach() add_custom_target(${targetname} DEPENDS ${bitmaps_files}) -endmacro(add_bitmaps_target) +endmacro() -macro(add_truetype_font_target radio name font size offset) - set(target ${RADIO_SRC_DIRECTORY}/fonts/${radio}/font_${name}) - add_custom_target(ttf_${radio}_${name} +macro(add_truetype_font_target name font size offset) + set(target ${RADIO_SRC_DIRECTORY}/fonts/truetype/font_${name}) + add_custom_target(truetype_font_${name} COMMAND ${PYTHON_EXECUTABLE} ${RADIO_DIRECTORY}/util/font2png.py ${font} ${size} ${offset} ${target} WORKING_DIRECTORY ${RADIO_SRC_DIRECTORY} + DEPENDS ${RADIO_DIRECTORY}/util/font2png.py ) - set(ttf_${radio}_fonts_targets ${ttf_${radio}_fonts_targets} ttf_${radio}_${name}) -endmacro(add_truetype_font_target) + set(truetype_fonts_targets ${truetype_fonts_targets} truetype_font_${name}) +endmacro() diff --git a/radio/sdcard/horus/THEMES/Darkblue/X10.bmp b/radio/sdcard/horus/THEMES/Darkblue/X10.bmp index 32a730cde..24893bad0 100755 Binary files a/radio/sdcard/horus/THEMES/Darkblue/X10.bmp and b/radio/sdcard/horus/THEMES/Darkblue/X10.bmp differ diff --git a/radio/sdcard/horus/THEMES/Darkblue/X10S.bmp b/radio/sdcard/horus/THEMES/Darkblue/X10S.bmp old mode 100644 new mode 100755 index 221e25ec4..e27932eef Binary files a/radio/sdcard/horus/THEMES/Darkblue/X10S.bmp and b/radio/sdcard/horus/THEMES/Darkblue/X10S.bmp differ diff --git a/radio/sdcard/horus/THEMES/Darkblue/mask_radio_tools.png b/radio/sdcard/horus/THEMES/Darkblue/mask_radio_tools.png new file mode 100755 index 000000000..711d499de Binary files /dev/null and b/radio/sdcard/horus/THEMES/Darkblue/mask_radio_tools.png differ diff --git a/radio/sdcard/horus/THEMES/FrSky/X10.bmp b/radio/sdcard/horus/THEMES/FrSky/X10.bmp new file mode 100755 index 000000000..24893bad0 Binary files /dev/null and b/radio/sdcard/horus/THEMES/FrSky/X10.bmp differ diff --git a/radio/sdcard/horus/THEMES/FrSky/X10S.bmp b/radio/sdcard/horus/THEMES/FrSky/X10S.bmp new file mode 100755 index 000000000..e27932eef Binary files /dev/null and b/radio/sdcard/horus/THEMES/FrSky/X10S.bmp differ diff --git a/radio/sdcard/horus/THEMES/Default/asterisk.bmp b/radio/sdcard/horus/THEMES/FrSky/asterisk.bmp similarity index 100% rename from radio/sdcard/horus/THEMES/Default/asterisk.bmp rename to radio/sdcard/horus/THEMES/FrSky/asterisk.bmp diff --git a/radio/sdcard/horus/THEMES/Default/background.png b/radio/sdcard/horus/THEMES/FrSky/background.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/background.png rename to radio/sdcard/horus/THEMES/FrSky/background.png diff --git a/radio/sdcard/horus/THEMES/Default/busy.bmp b/radio/sdcard/horus/THEMES/FrSky/busy.bmp similarity index 100% rename from radio/sdcard/horus/THEMES/Default/busy.bmp rename to radio/sdcard/horus/THEMES/FrSky/busy.bmp diff --git a/radio/sdcard/horus/THEMES/Default/horus.bmp b/radio/sdcard/horus/THEMES/FrSky/horus.bmp similarity index 100% rename from radio/sdcard/horus/THEMES/Default/horus.bmp rename to radio/sdcard/horus/THEMES/FrSky/horus.bmp diff --git a/radio/sdcard/horus/THEMES/Default/mask_currentmenu_bg.png b/radio/sdcard/horus/THEMES/FrSky/mask_currentmenu_bg.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_currentmenu_bg.png rename to radio/sdcard/horus/THEMES/FrSky/mask_currentmenu_bg.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_currentmenu_dot.png b/radio/sdcard/horus/THEMES/FrSky/mask_currentmenu_dot.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_currentmenu_dot.png rename to radio/sdcard/horus/THEMES/FrSky/mask_currentmenu_dot.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_currentmenu_shadow.png b/radio/sdcard/horus/THEMES/FrSky/mask_currentmenu_shadow.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_currentmenu_shadow.png rename to radio/sdcard/horus/THEMES/FrSky/mask_currentmenu_shadow.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_menu_model.png b/radio/sdcard/horus/THEMES/FrSky/mask_menu_model.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_menu_model.png rename to radio/sdcard/horus/THEMES/FrSky/mask_menu_model.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_menu_radio.png b/radio/sdcard/horus/THEMES/FrSky/mask_menu_radio.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_menu_radio.png rename to radio/sdcard/horus/THEMES/FrSky/mask_menu_radio.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_menu_stats.png b/radio/sdcard/horus/THEMES/FrSky/mask_menu_stats.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_menu_stats.png rename to radio/sdcard/horus/THEMES/FrSky/mask_menu_stats.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_menu_theme.png b/radio/sdcard/horus/THEMES/FrSky/mask_menu_theme.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_menu_theme.png rename to radio/sdcard/horus/THEMES/FrSky/mask_menu_theme.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_model_curves.png b/radio/sdcard/horus/THEMES/FrSky/mask_model_curves.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_model_curves.png rename to radio/sdcard/horus/THEMES/FrSky/mask_model_curves.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_model_flight_modes.png b/radio/sdcard/horus/THEMES/FrSky/mask_model_flight_modes.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_model_flight_modes.png rename to radio/sdcard/horus/THEMES/FrSky/mask_model_flight_modes.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_model_gvars.png b/radio/sdcard/horus/THEMES/FrSky/mask_model_gvars.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_model_gvars.png rename to radio/sdcard/horus/THEMES/FrSky/mask_model_gvars.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_model_heli.png b/radio/sdcard/horus/THEMES/FrSky/mask_model_heli.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_model_heli.png rename to radio/sdcard/horus/THEMES/FrSky/mask_model_heli.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_model_inputs.png b/radio/sdcard/horus/THEMES/FrSky/mask_model_inputs.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_model_inputs.png rename to radio/sdcard/horus/THEMES/FrSky/mask_model_inputs.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_model_logical_switches.png b/radio/sdcard/horus/THEMES/FrSky/mask_model_logical_switches.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_model_logical_switches.png rename to radio/sdcard/horus/THEMES/FrSky/mask_model_logical_switches.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_model_lua_scripts.png b/radio/sdcard/horus/THEMES/FrSky/mask_model_lua_scripts.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_model_lua_scripts.png rename to radio/sdcard/horus/THEMES/FrSky/mask_model_lua_scripts.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_model_mixer.png b/radio/sdcard/horus/THEMES/FrSky/mask_model_mixer.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_model_mixer.png rename to radio/sdcard/horus/THEMES/FrSky/mask_model_mixer.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_model_outputs.png b/radio/sdcard/horus/THEMES/FrSky/mask_model_outputs.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_model_outputs.png rename to radio/sdcard/horus/THEMES/FrSky/mask_model_outputs.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_model_setup.png b/radio/sdcard/horus/THEMES/FrSky/mask_model_setup.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_model_setup.png rename to radio/sdcard/horus/THEMES/FrSky/mask_model_setup.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_model_special_functions.png b/radio/sdcard/horus/THEMES/FrSky/mask_model_special_functions.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_model_special_functions.png rename to radio/sdcard/horus/THEMES/FrSky/mask_model_special_functions.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_model_telemetry.png b/radio/sdcard/horus/THEMES/FrSky/mask_model_telemetry.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_model_telemetry.png rename to radio/sdcard/horus/THEMES/FrSky/mask_model_telemetry.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_monitor.png b/radio/sdcard/horus/THEMES/FrSky/mask_monitor.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_monitor.png rename to radio/sdcard/horus/THEMES/FrSky/mask_monitor.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_monitor_channels1.png b/radio/sdcard/horus/THEMES/FrSky/mask_monitor_channels1.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_monitor_channels1.png rename to radio/sdcard/horus/THEMES/FrSky/mask_monitor_channels1.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_monitor_channels2.png b/radio/sdcard/horus/THEMES/FrSky/mask_monitor_channels2.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_monitor_channels2.png rename to radio/sdcard/horus/THEMES/FrSky/mask_monitor_channels2.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_monitor_channels3.png b/radio/sdcard/horus/THEMES/FrSky/mask_monitor_channels3.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_monitor_channels3.png rename to radio/sdcard/horus/THEMES/FrSky/mask_monitor_channels3.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_monitor_channels4.png b/radio/sdcard/horus/THEMES/FrSky/mask_monitor_channels4.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_monitor_channels4.png rename to radio/sdcard/horus/THEMES/FrSky/mask_monitor_channels4.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_monitor_gvars.png b/radio/sdcard/horus/THEMES/FrSky/mask_monitor_gvars.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_monitor_gvars.png rename to radio/sdcard/horus/THEMES/FrSky/mask_monitor_gvars.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_monitor_inver.png b/radio/sdcard/horus/THEMES/FrSky/mask_monitor_inver.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_monitor_inver.png rename to radio/sdcard/horus/THEMES/FrSky/mask_monitor_inver.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_monitor_lockch.png b/radio/sdcard/horus/THEMES/FrSky/mask_monitor_lockch.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_monitor_lockch.png rename to radio/sdcard/horus/THEMES/FrSky/mask_monitor_lockch.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_monitor_logsw.png b/radio/sdcard/horus/THEMES/FrSky/mask_monitor_logsw.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_monitor_logsw.png rename to radio/sdcard/horus/THEMES/FrSky/mask_monitor_logsw.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_monitor_mixes1.png b/radio/sdcard/horus/THEMES/FrSky/mask_monitor_mixes1.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_monitor_mixes1.png rename to radio/sdcard/horus/THEMES/FrSky/mask_monitor_mixes1.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_monitor_mixes2.png b/radio/sdcard/horus/THEMES/FrSky/mask_monitor_mixes2.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_monitor_mixes2.png rename to radio/sdcard/horus/THEMES/FrSky/mask_monitor_mixes2.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_mplex_add.png b/radio/sdcard/horus/THEMES/FrSky/mask_mplex_add.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_mplex_add.png rename to radio/sdcard/horus/THEMES/FrSky/mask_mplex_add.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_mplex_multi.png b/radio/sdcard/horus/THEMES/FrSky/mask_mplex_multi.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_mplex_multi.png rename to radio/sdcard/horus/THEMES/FrSky/mask_mplex_multi.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_mplex_replace.png b/radio/sdcard/horus/THEMES/FrSky/mask_mplex_replace.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_mplex_replace.png rename to radio/sdcard/horus/THEMES/FrSky/mask_mplex_replace.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_opentx.png b/radio/sdcard/horus/THEMES/FrSky/mask_opentx.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_opentx.png rename to radio/sdcard/horus/THEMES/FrSky/mask_opentx.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_opentx_testmode.png b/radio/sdcard/horus/THEMES/FrSky/mask_opentx_testmode.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_opentx_testmode.png rename to radio/sdcard/horus/THEMES/FrSky/mask_opentx_testmode.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_radio_calibration.png b/radio/sdcard/horus/THEMES/FrSky/mask_radio_calibration.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_radio_calibration.png rename to radio/sdcard/horus/THEMES/FrSky/mask_radio_calibration.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_radio_global_functions.png b/radio/sdcard/horus/THEMES/FrSky/mask_radio_global_functions.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_radio_global_functions.png rename to radio/sdcard/horus/THEMES/FrSky/mask_radio_global_functions.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_radio_hardware.png b/radio/sdcard/horus/THEMES/FrSky/mask_radio_hardware.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_radio_hardware.png rename to radio/sdcard/horus/THEMES/FrSky/mask_radio_hardware.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_radio_sd_browser.png b/radio/sdcard/horus/THEMES/FrSky/mask_radio_sd_browser.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_radio_sd_browser.png rename to radio/sdcard/horus/THEMES/FrSky/mask_radio_sd_browser.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_radio_setup.png b/radio/sdcard/horus/THEMES/FrSky/mask_radio_setup.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_radio_setup.png rename to radio/sdcard/horus/THEMES/FrSky/mask_radio_setup.png diff --git a/radio/sdcard/horus/THEMES/FrSky/mask_radio_tools.png b/radio/sdcard/horus/THEMES/FrSky/mask_radio_tools.png new file mode 100755 index 000000000..711d499de Binary files /dev/null and b/radio/sdcard/horus/THEMES/FrSky/mask_radio_tools.png differ diff --git a/radio/sdcard/horus/THEMES/Default/mask_radio_trainer.png b/radio/sdcard/horus/THEMES/FrSky/mask_radio_trainer.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_radio_trainer.png rename to radio/sdcard/horus/THEMES/FrSky/mask_radio_trainer.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_radio_version.png b/radio/sdcard/horus/THEMES/FrSky/mask_radio_version.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_radio_version.png rename to radio/sdcard/horus/THEMES/FrSky/mask_radio_version.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_sbar_mixer.png b/radio/sdcard/horus/THEMES/FrSky/mask_sbar_mixer.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_sbar_mixer.png rename to radio/sdcard/horus/THEMES/FrSky/mask_sbar_mixer.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_sbar_output.png b/radio/sdcard/horus/THEMES/FrSky/mask_sbar_output.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_sbar_output.png rename to radio/sdcard/horus/THEMES/FrSky/mask_sbar_output.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_sbar_to.png b/radio/sdcard/horus/THEMES/FrSky/mask_sbar_to.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_sbar_to.png rename to radio/sdcard/horus/THEMES/FrSky/mask_sbar_to.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_stats_analogs.png b/radio/sdcard/horus/THEMES/FrSky/mask_stats_analogs.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_stats_analogs.png rename to radio/sdcard/horus/THEMES/FrSky/mask_stats_analogs.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_stats_debug.png b/radio/sdcard/horus/THEMES/FrSky/mask_stats_debug.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_stats_debug.png rename to radio/sdcard/horus/THEMES/FrSky/mask_stats_debug.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_stats_lua.png b/radio/sdcard/horus/THEMES/FrSky/mask_stats_lua.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_stats_lua.png rename to radio/sdcard/horus/THEMES/FrSky/mask_stats_lua.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_stats_throttle_graph.png b/radio/sdcard/horus/THEMES/FrSky/mask_stats_throttle_graph.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_stats_throttle_graph.png rename to radio/sdcard/horus/THEMES/FrSky/mask_stats_throttle_graph.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_stats_timers.png b/radio/sdcard/horus/THEMES/FrSky/mask_stats_timers.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_stats_timers.png rename to radio/sdcard/horus/THEMES/FrSky/mask_stats_timers.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_stats_value.png b/radio/sdcard/horus/THEMES/FrSky/mask_stats_value.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_stats_value.png rename to radio/sdcard/horus/THEMES/FrSky/mask_stats_value.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_switches.png b/radio/sdcard/horus/THEMES/FrSky/mask_switches.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_switches.png rename to radio/sdcard/horus/THEMES/FrSky/mask_switches.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_textline_curve.png b/radio/sdcard/horus/THEMES/FrSky/mask_textline_curve.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_textline_curve.png rename to radio/sdcard/horus/THEMES/FrSky/mask_textline_curve.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_textline_delay.png b/radio/sdcard/horus/THEMES/FrSky/mask_textline_delay.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_textline_delay.png rename to radio/sdcard/horus/THEMES/FrSky/mask_textline_delay.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_textline_delayslow.png b/radio/sdcard/horus/THEMES/FrSky/mask_textline_delayslow.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_textline_delayslow.png rename to radio/sdcard/horus/THEMES/FrSky/mask_textline_delayslow.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_textline_fm.png b/radio/sdcard/horus/THEMES/FrSky/mask_textline_fm.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_textline_fm.png rename to radio/sdcard/horus/THEMES/FrSky/mask_textline_fm.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_textline_label.png b/radio/sdcard/horus/THEMES/FrSky/mask_textline_label.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_textline_label.png rename to radio/sdcard/horus/THEMES/FrSky/mask_textline_label.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_textline_slow.png b/radio/sdcard/horus/THEMES/FrSky/mask_textline_slow.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_textline_slow.png rename to radio/sdcard/horus/THEMES/FrSky/mask_textline_slow.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_textline_switch.png b/radio/sdcard/horus/THEMES/FrSky/mask_textline_switch.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_textline_switch.png rename to radio/sdcard/horus/THEMES/FrSky/mask_textline_switch.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_theme_add_view.png b/radio/sdcard/horus/THEMES/FrSky/mask_theme_add_view.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_theme_add_view.png rename to radio/sdcard/horus/THEMES/FrSky/mask_theme_add_view.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_theme_setup.png b/radio/sdcard/horus/THEMES/FrSky/mask_theme_setup.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_theme_setup.png rename to radio/sdcard/horus/THEMES/FrSky/mask_theme_setup.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_theme_view1.png b/radio/sdcard/horus/THEMES/FrSky/mask_theme_view1.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_theme_view1.png rename to radio/sdcard/horus/THEMES/FrSky/mask_theme_view1.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_theme_view2.png b/radio/sdcard/horus/THEMES/FrSky/mask_theme_view2.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_theme_view2.png rename to radio/sdcard/horus/THEMES/FrSky/mask_theme_view2.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_theme_view3.png b/radio/sdcard/horus/THEMES/FrSky/mask_theme_view3.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_theme_view3.png rename to radio/sdcard/horus/THEMES/FrSky/mask_theme_view3.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_theme_view4.png b/radio/sdcard/horus/THEMES/FrSky/mask_theme_view4.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_theme_view4.png rename to radio/sdcard/horus/THEMES/FrSky/mask_theme_view4.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_theme_view5.png b/radio/sdcard/horus/THEMES/FrSky/mask_theme_view5.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_theme_view5.png rename to radio/sdcard/horus/THEMES/FrSky/mask_theme_view5.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_theme_view6.png b/radio/sdcard/horus/THEMES/FrSky/mask_theme_view6.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_theme_view6.png rename to radio/sdcard/horus/THEMES/FrSky/mask_theme_view6.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_theme_view7.png b/radio/sdcard/horus/THEMES/FrSky/mask_theme_view7.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_theme_view7.png rename to radio/sdcard/horus/THEMES/FrSky/mask_theme_view7.png diff --git a/radio/sdcard/horus/THEMES/Default/mask_theme_view8.png b/radio/sdcard/horus/THEMES/FrSky/mask_theme_view8.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/mask_theme_view8.png rename to radio/sdcard/horus/THEMES/FrSky/mask_theme_view8.png diff --git a/radio/sdcard/horus/THEMES/Default/modelsel/icon_boat1.png b/radio/sdcard/horus/THEMES/FrSky/modelsel/icon_boat1.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/modelsel/icon_boat1.png rename to radio/sdcard/horus/THEMES/FrSky/modelsel/icon_boat1.png diff --git a/radio/sdcard/horus/THEMES/Default/modelsel/icon_boat2.png b/radio/sdcard/horus/THEMES/FrSky/modelsel/icon_boat2.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/modelsel/icon_boat2.png rename to radio/sdcard/horus/THEMES/FrSky/modelsel/icon_boat2.png diff --git a/radio/sdcard/horus/THEMES/Default/modelsel/icon_default.png b/radio/sdcard/horus/THEMES/FrSky/modelsel/icon_default.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/modelsel/icon_default.png rename to radio/sdcard/horus/THEMES/FrSky/modelsel/icon_default.png diff --git a/radio/sdcard/horus/THEMES/Default/modelsel/icon_favorites.png b/radio/sdcard/horus/THEMES/FrSky/modelsel/icon_favorites.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/modelsel/icon_favorites.png rename to radio/sdcard/horus/THEMES/FrSky/modelsel/icon_favorites.png diff --git a/radio/sdcard/horus/THEMES/Default/modelsel/icon_plane1.png b/radio/sdcard/horus/THEMES/FrSky/modelsel/icon_plane1.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/modelsel/icon_plane1.png rename to radio/sdcard/horus/THEMES/FrSky/modelsel/icon_plane1.png diff --git a/radio/sdcard/horus/THEMES/Default/modelsel/icon_plane2.png b/radio/sdcard/horus/THEMES/FrSky/modelsel/icon_plane2.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/modelsel/icon_plane2.png rename to radio/sdcard/horus/THEMES/FrSky/modelsel/icon_plane2.png diff --git a/radio/sdcard/horus/THEMES/Default/modelsel/icon_plane3.png b/radio/sdcard/horus/THEMES/FrSky/modelsel/icon_plane3.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/modelsel/icon_plane3.png rename to radio/sdcard/horus/THEMES/FrSky/modelsel/icon_plane3.png diff --git a/radio/sdcard/horus/THEMES/Default/modelsel/mask_iconback.png b/radio/sdcard/horus/THEMES/FrSky/modelsel/mask_iconback.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/modelsel/mask_iconback.png rename to radio/sdcard/horus/THEMES/FrSky/modelsel/mask_iconback.png diff --git a/radio/sdcard/horus/THEMES/Default/modelsel/mask_modelname.png b/radio/sdcard/horus/THEMES/FrSky/modelsel/mask_modelname.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/modelsel/mask_modelname.png rename to radio/sdcard/horus/THEMES/FrSky/modelsel/mask_modelname.png diff --git a/radio/sdcard/horus/THEMES/Default/modelsel/mask_modelqty.png b/radio/sdcard/horus/THEMES/FrSky/modelsel/mask_modelqty.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/modelsel/mask_modelqty.png rename to radio/sdcard/horus/THEMES/FrSky/modelsel/mask_modelqty.png diff --git a/radio/sdcard/horus/THEMES/Default/modelsel/mask_moveback.png b/radio/sdcard/horus/THEMES/FrSky/modelsel/mask_moveback.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/modelsel/mask_moveback.png rename to radio/sdcard/horus/THEMES/FrSky/modelsel/mask_moveback.png diff --git a/radio/sdcard/horus/THEMES/Default/modelsel/mask_moveico.png b/radio/sdcard/horus/THEMES/FrSky/modelsel/mask_moveico.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/modelsel/mask_moveico.png rename to radio/sdcard/horus/THEMES/FrSky/modelsel/mask_moveico.png diff --git a/radio/sdcard/horus/THEMES/Default/modelsel/mask_sdfree.png b/radio/sdcard/horus/THEMES/FrSky/modelsel/mask_sdfree.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/modelsel/mask_sdfree.png rename to radio/sdcard/horus/THEMES/FrSky/modelsel/mask_sdfree.png diff --git a/radio/sdcard/horus/THEMES/Default/question.bmp b/radio/sdcard/horus/THEMES/FrSky/question.bmp similarity index 100% rename from radio/sdcard/horus/THEMES/Default/question.bmp rename to radio/sdcard/horus/THEMES/FrSky/question.bmp diff --git a/radio/sdcard/horus/THEMES/Default/shutdown.bmp b/radio/sdcard/horus/THEMES/FrSky/shutdown.bmp similarity index 100% rename from radio/sdcard/horus/THEMES/Default/shutdown.bmp rename to radio/sdcard/horus/THEMES/FrSky/shutdown.bmp diff --git a/radio/sdcard/horus/THEMES/Default/sleep.bmp b/radio/sdcard/horus/THEMES/FrSky/sleep.bmp similarity index 100% rename from radio/sdcard/horus/THEMES/Default/sleep.bmp rename to radio/sdcard/horus/THEMES/FrSky/sleep.bmp diff --git a/radio/sdcard/horus/THEMES/Default/stick_background.png b/radio/sdcard/horus/THEMES/FrSky/stick_background.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/stick_background.png rename to radio/sdcard/horus/THEMES/FrSky/stick_background.png diff --git a/radio/sdcard/horus/THEMES/Default/stick_pointer.png b/radio/sdcard/horus/THEMES/FrSky/stick_pointer.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/stick_pointer.png rename to radio/sdcard/horus/THEMES/FrSky/stick_pointer.png diff --git a/radio/sdcard/horus/THEMES/Default/thumb.bmp b/radio/sdcard/horus/THEMES/FrSky/thumb.bmp similarity index 100% rename from radio/sdcard/horus/THEMES/Default/thumb.bmp rename to radio/sdcard/horus/THEMES/FrSky/thumb.bmp diff --git a/radio/sdcard/horus/THEMES/Default/topleft.png b/radio/sdcard/horus/THEMES/FrSky/topleft.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/topleft.png rename to radio/sdcard/horus/THEMES/FrSky/topleft.png diff --git a/radio/sdcard/horus/THEMES/Default/trackp_background.png b/radio/sdcard/horus/THEMES/FrSky/trackp_background.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/trackp_background.png rename to radio/sdcard/horus/THEMES/FrSky/trackp_background.png diff --git a/radio/sdcard/horus/THEMES/Default/wizard/background.png b/radio/sdcard/horus/THEMES/FrSky/wizard/background.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/wizard/background.png rename to radio/sdcard/horus/THEMES/FrSky/wizard/background.png diff --git a/radio/sdcard/horus/THEMES/Default/wizard/itembg.png b/radio/sdcard/horus/THEMES/FrSky/wizard/itembg.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/wizard/itembg.png rename to radio/sdcard/horus/THEMES/FrSky/wizard/itembg.png diff --git a/radio/sdcard/horus/THEMES/Default/wizard/pgdn.png b/radio/sdcard/horus/THEMES/FrSky/wizard/pgdn.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/wizard/pgdn.png rename to radio/sdcard/horus/THEMES/FrSky/wizard/pgdn.png diff --git a/radio/sdcard/horus/THEMES/Default/wizard/pgup.png b/radio/sdcard/horus/THEMES/FrSky/wizard/pgup.png similarity index 100% rename from radio/sdcard/horus/THEMES/Default/wizard/pgup.png rename to radio/sdcard/horus/THEMES/FrSky/wizard/pgup.png diff --git a/radio/sdcard/nv14/CROSSFIRE/crossfire.lua b/radio/sdcard/nv14/CROSSFIRE/crossfire.lua new file mode 100644 index 000000000..6606f0aaa --- /dev/null +++ b/radio/sdcard/nv14/CROSSFIRE/crossfire.lua @@ -0,0 +1,121 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### +local devices = { } +local lineIndex = 1 +local pageOffset = 0 + +local function createDevice(id, name) + local device = { + id = id, + name = name, + timeout = 0 + } + return device +end + +local function getDevice(name) + for i=1, #devices do + if devices[i].name == name then + return devices[i] + end + end + return nil +end + +local function parseDeviceInfoMessage(data) + local id = data[2] + local name = "" + local i = 3 + while data[i] ~= 0 do + name = name .. string.char(data[i]) + i = i + 1 + end + local device = getDevice(name) + if device == nil then + device = createDevice(id, name) + devices[#devices + 1] = device + end + local time = getTime() + device.timeout = time + 3000 -- 30s + if lineIndex == 0 then + lineIndex = 1 + end +end + +local devicesRefreshTimeout = 0 +local function refreshNext() + local command, data = crossfireTelemetryPop() + if command == nil then + local time = getTime() + if time > devicesRefreshTimeout then + devicesRefreshTimeout = time + 100 -- 1s + crossfireTelemetryPush(0x28, { 0x00, 0xEA }) + end + elseif command == 0x29 then + parseDeviceInfoMessage(data) + end +end + +local function selectDevice(step) + lineIndex = 1 + ((lineIndex + step - 1 + #devices) % #devices) +end + +-- Init +local function init() + lineIndex = 0 + pageOffset = 0 +end + +NoCross = { 110, LCD_H - 28, "Waiting for Crossfire devices...", TEXT_COLOR + INVERS + BLINK } + +-- Main +local function run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_EXIT_BREAK then + return 2 + elseif event == EVT_ROT_LEFT then + selectDevice(1) + elseif event == EVT_ROT_RIGHT then + selectDevice(-1) + end + + lcd.clear() + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5,"CROSSFIRE SETUP", MENU_TITLE_COLOR) + + + if #devices == 0 then + lcd.drawText(NoCross[1],NoCross[2],NoCross[3],NoCross[4]) + else + for i=1, #devices do + local attr = (lineIndex == i and INVERS or 0) + if event == EVT_ROT_BREAK and attr == INVERS then + crossfireTelemetryPush(0x28, { devices[i].id, 0xEA }) + return "device.lua" + end + lcd.drawText(5, i*22+10, devices[i].name, attr) + end + end + + refreshNext() + + return 0 +end + +return { init=init, run=run } diff --git a/radio/sdcard/nv14/CROSSFIRE/device.lua b/radio/sdcard/nv14/CROSSFIRE/device.lua new file mode 100644 index 000000000..811f01847 --- /dev/null +++ b/radio/sdcard/nv14/CROSSFIRE/device.lua @@ -0,0 +1,533 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### +local deviceId = 0 +local deviceName = "" +local lineIndex = 0 +local pageOffset = 0 +local edit = false +local charIndex = 1 +local fieldPopup +local fieldTimeout = 0 +local fieldId = 1 +local fieldChunk = 0 +local fieldData = {} +local fields = {} + +local function getField(line) + local counter = 1 + for i = 1, #fields do + local field = fields[i] + if not field.hidden then + if counter < line then + counter = counter + 1 + else + return field + end + end + end +end + +local function initLineIndex() + lineIndex = 0 + for i = 1, #fields do + local field = getField(i) + if field and field.type ~= 11 and field.type ~= 12 and field.name ~= nil then + lineIndex = i + break + end + end +end + +-- Change display attribute to current field +local function incrField(step) + local field = getField(lineIndex) + if field.type == 10 then + local byte = 32 + if charIndex <= #field.value then + byte = string.byte(field.value, charIndex) + step + end + if byte < 32 then + byte = 32 + elseif byte > 122 then + byte = 122 + end + if charIndex <= #field.value then + field.value = string.sub(field.value, 1, charIndex-1) .. string.char(byte) .. string.sub(field.value, charIndex+1) + else + field.value = field.value .. string.char(byte) + end + else + local min, max = 0, 0 + if field.type <= 5 then + min = field.min + max = field.max + step = field.step * step + elseif field.type == 9 then + min = 0 + max = #field.values - 1 + end + if (step < 0 and field.value > min) or (step > 0 and field.value < max) then + field.value = field.value + step + end + end +end + +-- Select the next or previous editable field +local function selectField(step) + local newLineIndex = lineIndex + local field + repeat + newLineIndex = newLineIndex + step + if newLineIndex == 0 then + newLineIndex = #fields + elseif newLineIndex == 1 + #fields then + newLineIndex = 1 + pageOffset = 0 + end + field = getField(newLineIndex) + until newLineIndex == lineIndex or (field and field.type ~= 11 and field.name) + lineIndex = newLineIndex + if lineIndex > 11 + pageOffset then -- NOTE: increased from 7 to 11 to allow 11 lines in Horus display + pageOffset = lineIndex - 11 -- NOTE: increased from 7 to 11 to allow 11 lines in Horus display + elseif lineIndex <= pageOffset then + pageOffset = lineIndex - 1 + end +end + +local function split(str) + local t = {} + local i = 1 + for s in string.gmatch(str, "([^;]+)") do + t[i] = s + i = i + 1 + end + return t +end + +local function fieldGetString(data, offset) + local result = "" + while data[offset] ~= 0 do + result = result .. string.char(data[offset]) + offset = offset + 1 + end + offset = offset + 1 + return result, offset +end + +local function parseDeviceInfoMessage(data) + local offset + deviceId = data[2] + deviceName, offset = fieldGetString(data, 3) + local fields_count = data[offset+12] + for i=1, fields_count do + fields[i] = { name=nil } + end +end + +local function fieldGetValue(data, offset, size) + local result = 0 + for i=0, size-1 do + result = bit32.lshift(result, 8) + data[offset + i] + end + return result +end + +local function fieldUnsignedLoad(field, data, offset, size) + field.value = fieldGetValue(data, offset, size) + field.min = fieldGetValue(data, offset+size, size) + field.max = fieldGetValue(data, offset+2*size, size) + field.default = fieldGetValue(data, offset+3*size, size) + field.unit, offset = fieldGetString(data, offset+4*size) + field.step = 1 +end + +local function fieldUnsignedToSigned(field, size) + local bandval = bit32.lshift(0x80, (size-1)*8) + field.value = field.value - bit32.band(field.value, bandval) * 2 + field.min = field.min - bit32.band(field.min, bandval) * 2 + field.max = field.max - bit32.band(field.max, bandval) * 2 + field.default = field.default - bit32.band(field.default, bandval) * 2 +end + + local function fieldSignedLoad(field, data, offset, size) + fieldUnsignedLoad(field, data, offset, size) + fieldUnsignedToSigned(field, size) +end + +local function fieldIntSave(index, value, size) + local frame = { deviceId, 0xEA, index } + for i=size-1, 0, -1 do + frame[#frame + 1] = (bit32.rshift(value, 8*i) % 256) + end + crossfireTelemetryPush(0x2D, frame) +end + +local function fieldUnsignedSave(field, size) + local value = field.value + fieldIntSave(field.id, value, size) +end + +local function fieldSignedSave(field, size) + local value = field.value + if value < 0 then + value = bit32.lshift(0x100, (size-1)*8) + value + end + fieldIntSave(field.id, value, size) +end + +local function fieldIntDisplay(field, y, attr) + -- lcd.drawNumber(140, y, field.value, LEFT + attr) -- NOTE: original code getLastPos not available in Horus + -- lcd.drawText(lcd.getLastPos(), y, field.unit, attr) -- NOTE: original code getLastPos not available in Horus + lcd.drawText(140, y, field.value .. field.unit, attr) -- NOTE: Concenated fields instead of get lastPos +end + +-- UINT8 +local function fieldUint8Load(field, data, offset) + fieldUnsignedLoad(field, data, offset, 1) +end + +local function fieldUint8Save(field) + fieldUnsignedSave(field, 1) +end + +-- INT8 +local function fieldInt8Load(field, data, offset) + fieldSignedLoad(field, data, offset, 1) +end + +local function fieldInt8Save(field) + fieldSignedSave(field, 1) +end + +-- UINT16 +local function fieldUint16Load(field, data, offset) + fieldUnsignedLoad(field, data, offset, 2) +end + +local function fieldUint16Save(field) + fieldUnsignedSave(field, 2) +end + +-- INT16 +local function fieldInt16Load(field, data, offset) + fieldSignedLoad(field, data, offset, 2) +end + +local function fieldInt16Save(field) + fieldSignedSave(field, 2) +end + +-- FLOAT +local function fieldFloatLoad(field, data, offset) + field.value = fieldGetValue(data, offset, 4) + field.min = fieldGetValue(data, offset+4, 4) + field.max = fieldGetValue(data, offset+8, 4) + field.default = fieldGetValue(data, offset+12, 4) + fieldUnsignedToSigned(field, 4) + field.prec = data[offset+16] + if field.prec > 2 then + field.prec = 2 + end + field.step = fieldGetValue(data, offset+17, 4) + field.unit, offset = fieldGetString(data, offset+21) +end + +local function fieldFloatDisplay(field, y, attr) + local attrnum + if field.prec == 1 then + attrnum = LEFT + attr + PREC1 + elseif field.prec == 2 then + attrnum = LEFT + attr + PREC2 + else + attrnum = LEFT + attr + end + -- lcd.drawNumber(140, y, field.value, attrnum) -- NOTE: original code getLastPos not available in Horus + -- lcd.drawText(lcd.getLastPos(), y, field.unit, attr) -- NOTE: original code getLastPos not available in Horus + lcd.drawText(140, y, field.value .. field.unit, attr) -- NOTE: Concenated fields instead of get lastPos +end + +local function fieldFloatSave(field) + fieldUnsignedSave(field, 4) +end + +-- TEXT SELECTION +local function fieldTextSelectionLoad(field, data, offset) + local values + values, offset = fieldGetString(data, offset) + if values ~= "" then + field.values = split(values) + end + field.value = data[offset] + field.min = data[offset+1] + field.max = data[offset+2] + field.default = data[offset+3] + field.unit, offset = fieldGetString(data, offset+4) +end + +local function fieldTextSelectionSave(field) + crossfireTelemetryPush(0x2D, { deviceId, 0xEA, field.id, field.value }) +end + +local function fieldTextSelectionDisplay(field, y, attr) + -- lcd.drawText(140, y, field.values[field.value+1], attr) -- NOTE: original code getLastPos not available in Horus + -- lcd.drawText(lcd.getLastPos(), y, field.unit, attr) -- NOTE: original code getLastPos not available in Horus + lcd.drawText(140, y, field.values[field.value+1] .. field.unit, attr) -- NOTE: Concenated fields instead of get lastPos +end + +-- STRING +local function fieldStringLoad(field, data, offset) + field.value, offset = fieldGetString(data, offset) + if #data >= offset then + field.maxlen = data[offset] + end +end + +local function fieldStringSave(field) + local frame = { deviceId, 0xEA, field.id } + for i=1, string.len(field.value) do + frame[#frame + 1] = string.byte(field.value, i) + end + frame[#frame + 1] = 0 + crossfireTelemetryPush(0x2D, frame) +end + +local function fieldStringDisplay(field, y, attr) + if edit == true and attr then + -- lcd.drawText(140, y, field.value, FIXEDWIDTH) -- NOTE: FIXEDWIDTH unknown.... + -- lcd.drawText(134+6*charIndex, y, string.sub(field.value, charIndex, charIndex), FIXEDWIDTH + attr) -- NOTE: FIXEDWIDTH unknown.... + lcd.drawText(140, y, field.value, attr) + lcd.drawText(134+6*charIndex, y, string.sub(field.value, charIndex, charIndex), attr) + else + lcd.drawText(140, y, field.value, attr) + end +end + +local function fieldCommandLoad(field, data, offset) + field.status = data[offset] + field.timeout = data[offset+1] + field.info, offset = fieldGetString(data, offset+2) + if field.status < 2 or field.status > 3 then + fieldPopup = nil + end +end + +local function fieldCommandSave(field) + if field.status == 0 then + field.status = 1 + crossfireTelemetryPush(0x2D, { deviceId, 0xEA, field.id, field.status }) + fieldPopup = field + fieldTimeout = getTime() + field.timeout + end +end + +local function fieldCommandDisplay(field, y, attr) + lcd.drawText(1, y, field.name, attr) + if field.info ~= "" then + lcd.drawText(140, y, "[" .. field.info .. "]") + end +end + +local functions = { + { load=fieldUint8Load, save=fieldUint8Save, display=fieldIntDisplay }, + { load=fieldInt8Load, save=fieldInt8Save, display=fieldIntDisplay }, + { load=fieldUint16Load, save=fieldUint16Save, display=fieldIntDisplay }, + { load=fieldInt16Load, save=fieldInt16Save, display=fieldIntDisplay }, + nil, + nil, + nil, + nil, + { load=fieldFloatLoad, save=fieldFloatSave, display=fieldFloatDisplay }, + { load=fieldTextSelectionLoad, save=fieldTextSelectionSave, display=fieldTextSelectionDisplay }, + { load=fieldStringLoad, save=fieldStringSave, display=fieldStringDisplay }, + nil, + { load=fieldStringLoad, save=fieldStringSave, display=fieldStringDisplay }, + { load=fieldCommandLoad, save=fieldCommandSave, display=fieldCommandDisplay }, +} + +local function parseParameterInfoMessage(data) + if data[2] ~= deviceId or data[3] ~= fieldId then + fieldData = {} + fieldChunk = 0 + return + end + local field = fields[fieldId] + local chunks = data[4] + for i=5, #data do + fieldData[#fieldData + 1] = data[i] + end + if chunks > 0 then + fieldChunk = fieldChunk + 1 + else + fieldChunk = 0 + field.id = fieldId + field.parent = fieldData[1] + field.type = fieldData[2] % 128 + field.hidden = (bit32.rshift(fieldData[2], 7) == 1) + local name, i = fieldGetString(fieldData, 3) + if name ~= "" then + local indent = "" + local parent = field.parent + while parent ~= 0 do + indent = indent .. " " + parent = fields[parent].parent + end + field.name = indent .. name + end + if functions[field.type+1] then + functions[field.type+1].load(field, fieldData, i) + end + if not fieldPopup then + if lineIndex == 0 and field.hidden ~= true and field.type and field.type ~= 11 and field.type ~= 12 then + initLineIndex() + end + fieldId = 1 + (fieldId % #fields) + end + fieldData = {} + end +end + +local function refreshNext() + local command, data = crossfireTelemetryPop() + if command == nil then + local time = getTime() + if fieldPopup then + if time > fieldTimeout then + local frame = { deviceId, 0xEA, fieldPopup.id } + crossfireTelemetryPush(0x2D, frame) + fieldTimeout = time + fieldPopup.timeout + end + elseif time > fieldTimeout and not edit then + crossfireTelemetryPush(0x2C, { deviceId, 0xEA, fieldId, fieldChunk }) + fieldTimeout = time + 200 -- 2s + end + elseif command == 0x29 then + parseDeviceInfoMessage(data) + elseif command == 0x2B then + parseParameterInfoMessage(data) + fieldTimeout = 0 + end +end + +-- Main +local function runDevicePage(event) + if event == EVT_EXIT_BREAK then -- exit script + if edit == true then + edit = false + local field = getField(lineIndex) + fieldTimeout = getTime() + 200 -- 2s + fieldId, fieldChunk = field.id, 0 + fieldData = {} + functions[field.type+1].save(field) + else + return "crossfire.lua" + end + elseif event == EVT_ROT_BREAK then -- toggle editing/selecting current field + local field = getField(lineIndex) + if field.name then + if field.type == 10 then + if edit == false then + edit = true + charIndex = 1 + else + charIndex = charIndex + 1 + end + elseif field.type < 11 then + edit = not edit + end + if edit == false then + fieldTimeout = getTime() + 200 -- 2s + fieldId, fieldChunk = field.id, 0 + fieldData = {} + functions[field.type+1].save(field) + end + end + elseif edit then + if event == EVT_ROT_LEFT then + incrField(1) + elseif event == EVT_ROT_RIGHT then + incrField(-1) + end + else + if event == EVT_ROT_RIGHT then + selectField(1) + elseif event == EVT_ROT_LEFT then + selectField(-1) + end + end + + lcd.clear() + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5,deviceName, MENU_TITLE_COLOR) + + for y = 1, 11 do + local field = getField(pageOffset+y) + if not field then + break + elseif field.name == nil then + lcd.drawText(1, y*22+10, "...") + else + local attr = lineIndex == (pageOffset+y) and ((edit == true and BLINK or 0) + INVERS) or 0 + lcd.drawText(1, y*22+10, field.name) + if functions[field.type+1] then + functions[field.type+1].display(field, y*22+10, attr) + end + end + end + + return 0 +end + +local function runPopupPage(event) + local result + if fieldPopup.status == 3 then + result = popupConfirmation(fieldPopup.info, event) + else + result = popupWarning(fieldPopup.info, event) + end + if result == "OK" then + crossfireTelemetryPush(0x2D, { deviceId, 0xEA, fieldPopup.id, 4 }) + elseif result == "CANCEL" then + crossfireTelemetryPush(0x2D, { deviceId, 0xEA, fieldPopup.id, 5 }) + end + return 0 +end + +-- Init +local function init() + lineIndex, edit = 0, false +end + +-- Main +local function run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + end + + local result + if fieldPopup ~= nil then + result = runPopupPage(event) + else + result = runDevicePage(event) + end + + refreshNext() + + return result +end + +return { init=init, run=run } diff --git a/radio/sdcard/nv14/FIRMWARE/readme.txt b/radio/sdcard/nv14/FIRMWARE/readme.txt new file mode 100644 index 000000000..4ba9522c9 --- /dev/null +++ b/radio/sdcard/nv14/FIRMWARE/readme.txt @@ -0,0 +1,2 @@ +You can put firmware for your modules or radio in this folder. +Files in this folder are NOT automaticaly applied. diff --git a/radio/sdcard/nv14/IMAGES/readme.txt b/radio/sdcard/nv14/IMAGES/readme.txt new file mode 100644 index 000000000..a8412eabd --- /dev/null +++ b/radio/sdcard/nv14/IMAGES/readme.txt @@ -0,0 +1,3 @@ +2.2 : BMP renamed to IMAGES for consistency between Taranis and Horus. +You can put your model and other graphic files in this directory. +Format include bmp and png (including png with transparancy). diff --git a/radio/sdcard/nv14/LOGS/readme.txt b/radio/sdcard/nv14/LOGS/readme.txt new file mode 100644 index 000000000..f9c005b26 --- /dev/null +++ b/radio/sdcard/nv14/LOGS/readme.txt @@ -0,0 +1 @@ +Logs files created by "SD Logs" special function will be stored in this directory. diff --git a/radio/sdcard/nv14/SCRIPTS/FUNCTIONS/readme.txt b/radio/sdcard/nv14/SCRIPTS/FUNCTIONS/readme.txt new file mode 100644 index 000000000..e14fc62df --- /dev/null +++ b/radio/sdcard/nv14/SCRIPTS/FUNCTIONS/readme.txt @@ -0,0 +1 @@ +This directory is for lua functions scripts. diff --git a/radio/sdcard/nv14/SCRIPTS/MIXES/readme.txt b/radio/sdcard/nv14/SCRIPTS/MIXES/readme.txt new file mode 100644 index 000000000..8a5e2e738 --- /dev/null +++ b/radio/sdcard/nv14/SCRIPTS/MIXES/readme.txt @@ -0,0 +1 @@ +This directory is for lua mixer scripts. diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/icon.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/icon.png new file mode 100755 index 000000000..6869f18e9 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/icon.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/background.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/background.png new file mode 100755 index 000000000..e21f7bc81 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/background.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/brake.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/brake.png new file mode 100755 index 000000000..701cc2bdc Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/brake.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/flap.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/flap.png new file mode 100755 index 000000000..074bdf8d3 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/flap.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/lail.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/lail.png new file mode 100755 index 000000000..074bdf8d3 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/lail.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/mark_bg.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/mark_bg.png new file mode 100755 index 000000000..fbe6b0ee2 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/mark_bg.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/pagedn.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/pagedn.png new file mode 100755 index 000000000..0733ccfa1 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/pagedn.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/pageup.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/pageup.png new file mode 100755 index 000000000..0dbaa5202 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/pageup.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/plane.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/plane.png new file mode 100755 index 000000000..af1785270 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/plane.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/prop.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/prop.png new file mode 100644 index 000000000..4139d3542 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/prop.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/rail.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/rail.png new file mode 100755 index 000000000..074bdf8d3 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/rail.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/summary.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/summary.png new file mode 100755 index 000000000..8b23b0b42 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/summary.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/tail.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/tail.png new file mode 100755 index 000000000..6c6143cfd Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/tail.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/tail_rud.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/tail_rud.png new file mode 100755 index 000000000..a777c0c80 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/tail_rud.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/vtail.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/vtail.png new file mode 100755 index 000000000..f68035ece Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/img/vtail.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/wizard.lua b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/wizard.lua new file mode 100644 index 000000000..05a8bf6e0 --- /dev/null +++ b/radio/sdcard/nv14/SCRIPTS/WIZARD/glider/wizard.lua @@ -0,0 +1,485 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### +local VALUE = 0 +local COMBO = 1 + +local edit = false +local page = 1 +local current = 1 +local pages = {} +local fields = {} + +-- load common Bitmaps +local ImgMarkBg = Bitmap.open("img/mark_bg.png") +local BackgroundImg = Bitmap.open("img/background.png") +local ImgPlane = Bitmap.open("img/plane.png") +local ImgPageUp = Bitmap.open("img/pageup.png") +local ImgPageDn = Bitmap.open("img/pagedn.png") + + +-- Change display attribute to current field +local function addField(step) + local field = fields[current] + local min, max + if field[3] == VALUE then + min = field[6] + max = field[7] + elseif field[3] == COMBO then + min = 0 + max = #(field[6]) - 1 + end + if (step < 0 and field[5] > min) or (step > 0 and field[5] < max) then + field[5] = field[5] + step + end +end + +-- Select the next or previous page +local function selectPage(step) + page = 1 + ((page + step - 1 + #pages) % #pages) + edit = false + current = 1 +end + +-- Select the next or previous editable field +local function selectField(step) + repeat + current = 1 + ((current + step - 1 + #fields) % #fields) + until fields[current][4]==1 +end + +-- Redraw the current page +local function redrawFieldsPage(event) + + for index = 1, 10, 1 do + local field = fields[index] + if field == nil then + break + end + + local attr = current == (index) and ((edit == true and BLINK or 0) + INVERS) or 0 + attr = attr + TEXT_COLOR + + if field[4] == 1 then + if field[3] == VALUE then + lcd.drawNumber(field[1], field[2], field[5], LEFT + attr) + elseif field[3] == COMBO then + if field[5] >= 0 and field[5] < #(field[6]) then + lcd.drawText(field[1],field[2], field[6][1+field[5]], attr) + end + end + end + end +end + +local function updateField(field) + local value = field[5] +end + +-- Main +local function runFieldsPage(event) + if event == EVT_EXIT_BREAK then -- exit script + return 2 + elseif event == EVT_ENTER_BREAK or event == EVT_ROT_BREAK then -- toggle editing/selecting current field + if fields[current][5] ~= nil then + edit = not edit + if edit == false then + updateField(fields[current]) + end + end + elseif edit then + if event == EVT_PLUS_FIRST or event == EVT_ROT_RIGHT or event == EVT_PLUS_REPT then + addField(1) + elseif event == EVT_MINUS_FIRST or event == EVT_ROT_LEFT or event == EVT_MINUS_REPT then + addField(-1) + end + else + if event == EVT_MINUS_FIRST or event == EVT_ROT_RIGHT then + selectField(1) + elseif event == EVT_PLUS_FIRST or event == EVT_ROT_LEFT then + selectField(-1) + end + end + redrawFieldsPage(event) + return 0 +end + +-- set visibility flags starting with SECOND field of fields +local function setFieldsVisible(...) + local arg={...} + local cnt = 2 + for i,v in ipairs(arg) do + fields[cnt][4] = v + cnt = cnt + 1 + end +end + +-- draws one letter mark +local function drawMark(x, y, name) + lcd.drawBitmap(ImgMarkBg, x, y) + lcd.drawText(x+8, y+3, name, TEXT_COLOR) +end + + +local MotorFields = { + {50, 50, COMBO, 1, 1, { "No", "Yes"} }, + {50, 127, COMBO, 1, 2, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, +} + +local ImgEngine + +local function runMotorConfig(event) + lcd.clear() + if ImgEngine == nil then + ImgEngine = Bitmap.open("img/prop.png") + end + lcd.drawBitmap(BackgroundImg, 0, 0) + lcd.drawBitmap(ImgPageDn, 455, 95) + lcd.drawBitmap(ImgEngine, 310, 50) + lcd.setColor(CUSTOM_COLOR, lcd.RGB(255, 255, 255)) + fields = MotorFields + lcd.drawText(40, 20, "Does your model have a motor ?", TEXT_COLOR) + lcd.drawFilledRectangle(40, 45, 200, 30, CUSTOM_COLOR) + fields[2][4]=0 + if fields[1][5] == 1 then + lcd.drawText(40, 100, "What channel is it on ?", TEXT_COLOR) + lcd.drawFilledRectangle(40, 122, 100, 30, CUSTOM_COLOR) + fields[2][4]=1 + end + local result = runFieldsPage(event) + return result +end + +-- fields format : {[1]x, [2]y, [3]COMBO, [4]visible, [5]default, [6]{values}} +-- fields format : {[1]x, [2]y, [3]VALUE, [4]visible, [5]default, [6]min, [7]max} +local AilFields = { + {50, 50, COMBO, 1, 2, { "None", "One, or two with Y cable", "Two"} }, + {50, 127, COMBO, 1, 0, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, -- Ail1 chan + {50, 167, COMBO, 1, 4, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, -- Ail2 chan +} + +local ImgAilR +local ImgAilL + +local function runAilConfig(event) + lcd.clear() + if ImgAilR == nil then + ImgAilR = Bitmap.open("img/rail.png") + ImgAilL = Bitmap.open("img/lail.png") + end + lcd.drawBitmap(BackgroundImg, 0, 0) + lcd.drawBitmap(ImgPageUp, 0, 95) + lcd.drawBitmap(ImgPageDn, 455, 95) + lcd.drawBitmap(ImgPlane, 252, 100) + fields = AilFields + if fields[1][5] == 1 then + lcd.drawBitmap(ImgAilR, 274, 123) + lcd.drawBitmap(ImgAilL, 395, 235) + drawMark(308, 115, "A") + drawMark(422, 220, "A") + lcd.drawFilledRectangle(40, 122, 100, 30, CUSTOM_COLOR) + drawMark(152, 124, "A") + setFieldsVisible(1, 0) + elseif fields[1][5] == 2 then + lcd.drawBitmap(ImgAilR, 274, 123) + lcd.drawBitmap(ImgAilL, 395, 235) + drawMark(308, 115, "A") + drawMark(422, 220, "B") + lcd.drawFilledRectangle(40, 122, 100, 30, CUSTOM_COLOR) + drawMark(152, 124, "A") + lcd.drawFilledRectangle(40, 162, 100, 30, CUSTOM_COLOR) + drawMark(152, 164, "B") + setFieldsVisible(1, 1) + else + setFieldsVisible(0, 0) + end + lcd.drawText(40, 20, "Number of ailerons on your model ?", TEXT_COLOR) + lcd.drawFilledRectangle(40, 45, 400, 30, CUSTOM_COLOR) + local result = runFieldsPage(event) + return result +end + +local FlapsFields = { + {50, 50, COMBO, 1, 0, { "No", "Yes, on one channel", "Yes, on two channels"} }, + {50, 127, COMBO, 1, 6, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, + {50, 167, COMBO, 1, 7, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, +} + +local ImgFlp + +local function runFlapsConfig(event) + lcd.clear() + if ImgFlp == nil then + ImgFlp = Bitmap.open("img/flap.png") + end + lcd.drawBitmap(BackgroundImg, 0, 0) + lcd.drawBitmap(ImgPageUp, 0, 95) + lcd.drawBitmap(ImgPageDn, 455, 95) + lcd.drawBitmap(ImgPlane, 252, 100) + fields = FlapsFields + if fields[1][5] == 1 then + lcd.drawBitmap(ImgFlp, 315, 160) + lcd.drawBitmap(ImgFlp, 358, 202) + drawMark(332, 132, "A") + drawMark(412, 215, "A") + lcd.drawFilledRectangle(40, 122, 100, 30, CUSTOM_COLOR) + drawMark(152, 124, "A") + setFieldsVisible(1, 0) + elseif fields[1][5] == 2 then + lcd.drawBitmap(ImgFlp, 315, 160) + lcd.drawBitmap(ImgFlp, 358, 202) + drawMark(332, 132, "A") + drawMark(412, 215, "B") + lcd.drawFilledRectangle(40, 122, 100, 30, CUSTOM_COLOR) + drawMark(152, 124, "A") + lcd.drawFilledRectangle(40, 162, 100, 30, CUSTOM_COLOR) + drawMark(152, 164, "B") + setFieldsVisible(1, 1) + else + setFieldsVisible(0, 0) + end + lcd.drawText(40, 20, "Does your model have flaps ?", TEXT_COLOR) + lcd.drawFilledRectangle(40, 45, 400, 30, CUSTOM_COLOR) + local result = runFieldsPage(event) + return result +end + +local TailFields = { + {50, 50, COMBO, 1, 1, { "1 channel for Elevator, no Rudder", "One chan for Elevator, one for Rudder", "Two chans for Elevator, one for Rudder", "V Tail"} }, + {50, 127, COMBO, 1, 1, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, --ele + {50, 167, COMBO, 1, 3, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, --rud + {50, 207, COMBO, 0, 5, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, --ele2 +} + +local ImgTail +local ImgVTail +local ImgTailRud + +local function runTailConfig(event) + lcd.clear() + if ImgTail == nil then + ImgTail = Bitmap.open("img/tail.png") + ImgVTail = Bitmap.open("img/vtail.png") + ImgTailRud = Bitmap.open("img/tailrud.png") + end + lcd.drawBitmap(BackgroundImg, 0, 0) + lcd.drawBitmap(ImgPageUp, 0, 95) + lcd.drawBitmap(ImgPageDn, 455, 95) + fields = TailFields + if fields[1][5] == 0 then + lcd.drawBitmap(ImgTail, 252, 100) + drawMark(360, 125, "A") + drawMark(390, 155, "A") + lcd.drawFilledRectangle(40, 122, 100, 30, CUSTOM_COLOR) + drawMark(152, 124, "A") + setFieldsVisible(1, 0, 0) + end + if fields[1][5] == 1 then + lcd.drawBitmap(ImgTail, 252, 100) + lcd.drawBitmap(ImgTailRud, 340, 100) + drawMark(415, 150, "A") + drawMark(380, 120, "A") + drawMark(390, 185, "B") + lcd.drawFilledRectangle(40, 122, 100, 30, CUSTOM_COLOR) + drawMark(152, 124, "A") + lcd.drawFilledRectangle(40, 162, 100, 30, CUSTOM_COLOR) + drawMark(152, 164, "B") + setFieldsVisible(1, 1, 0) + end + if fields[1][5] == 2 then + lcd.drawBitmap(ImgTail, 252, 100) + lcd.drawBitmap(ImgTailRud, 340, 100) + drawMark(415, 150, "A") + drawMark(380, 120, "C") + drawMark(390, 185, "B") + lcd.drawFilledRectangle(40, 122, 100, 30, CUSTOM_COLOR) + drawMark(152, 124, "A") + lcd.drawFilledRectangle(40, 162, 100, 30, CUSTOM_COLOR) + drawMark(152, 164, "B") + lcd.drawFilledRectangle(40, 202, 100, 30, CUSTOM_COLOR) + drawMark(152, 204, "C") + setFieldsVisible(1, 1, 1) + end + if fields[1][5] == 3 then + lcd.drawBitmap(ImgVTail, 252, 100) + drawMark(315, 110, "A") + drawMark(382, 120, "B") + lcd.drawFilledRectangle(40, 122, 100, 30, CUSTOM_COLOR) + drawMark(152, 124, "A") + lcd.drawFilledRectangle(40, 162, 100, 30, CUSTOM_COLOR) + drawMark(152, 164, "B") + setFieldsVisible(1, 1, 0) + end + lcd.drawText(40, 20, "Pick the tail config of your model", TEXT_COLOR) + lcd.drawFilledRectangle(40, 45, 400, 30, CUSTOM_COLOR) + local result = runFieldsPage(event) + return result +end + +local lineIndex +local function drawNextLine(text, text2) + lcd.drawText(40, lineIndex, text, TEXT_COLOR) + lcd.drawText(250, lineIndex, text2 + 1, TEXT_COLOR) + lineIndex = lineIndex + 20 +end + +local ConfigSummaryFields = { + {110, 250, COMBO, 1, 0, { "No, I need to change something", "Yes, all is well, create the plane !"} }, +} + +local ImgSummary + +local function runConfigSummary(event) + lcd.clear() + if ImgSummary == nil then + ImgSummary = Bitmap.open("img/summary.png") + end + fields = ConfigSummaryFields + lcd.drawBitmap(BackgroundImg, 0, 0) + lcd.drawBitmap(ImgPageUp, 0, 95) + lcd.drawBitmap(ImgSummary, 300, 60) + lineIndex = 40 + -- motors + if(MotorFields[1][5] == 1) then + drawNextLine("Motor chan :", MotorFields[2][5]) + elseif (MotorFields[1][5] == 2) then + drawNextLine("Motor 1 chan :", MotorFields[2][5]) + drawNextLine("Motor 2 chan :", MotorFields[3][5]) + end + -- ail + if(AilFields[1][5] == 1) then + drawNextLine("Aileron chan :",AilFields[2][5]) + elseif (AilFields[1][5] == 2) then + drawNextLine("Aileron 1 chan :",AilFields[2][5]) + drawNextLine("Aileron 2 chan :",AilFields[3][5]) + end + -- flaps + if(FlapsFields[1][5] == 1) then + drawNextLine("Flaps chan :",FlapsFields[2][5]) + elseif (FlapsFields[1][5] == 2) then + drawNextLine("Flaps 1 chan :",FlapsFields[2][5]) + drawNextLine("Flaps 2 chan :",FlapsFields[3][5]) + end + -- tail + if(TailFields[1][5] == 0) then + drawNextLine("Elevator chan :",TailFields[2][5]) + elseif (TailFields[1][5] == 1) then + drawNextLine("Elevator chan :",TailFields[2][5]) + drawNextLine("Rudder chan :",TailFields[3][5]) + elseif (TailFields[1][5] == 2) then + drawNextLine("Elevator 1 chan :",TailFields[2][5]) + drawNextLine("Rudder chan :",TailFields[3][5]) + drawNextLine("Elevator 2 chan :",TailFields[4][5]) + elseif (TailFields[1][5] == 3) then + drawNextLine("V-Tail elevator :", TailFields[2][5]) + drawNextLine("V-Tail rudder :", TailFields[3][5]) + end + local result = runFieldsPage(event) + if(fields[1][5] == 1 and edit == false) then + selectPage(1) + end + return result +end + +local function addMix(channel, input, name, weight, index) + local mix = { source=input, name=name } + if weight ~= nil then + mix.weight = weight + end + if index == nil then + index = 0 + end + model.insertMix(channel, index, mix) +end + +local function createModel(event) + lcd.clear() + lcd.drawBitmap(BackgroundImg, 0, 0) + lcd.drawBitmap(ImgSummary, 300, 60) + model.defaultInputs() + model.deleteMixes() + -- motor + if(MotorFields[1][5] == 1) then + addMix(MotorFields[2][5], MIXSRC_FIRST_INPUT+defaultChannel(2), "Motor") + elseif (MotorFields[1][5] == 2) then + addMix(MotorFields[2][5], MIXSRC_FIRST_INPUT+defaultChannel(2), "Motor1") + addMix(MotorFields[3][5], MIXSRC_FIRST_INPUT+defaultChannel(2), "Motor2") + end + -- Ailerons + if(AilFields[1][5] == 1) then + addMix(AilFields[2][5], MIXSRC_FIRST_INPUT+defaultChannel(3), "Ail") + elseif (AilFields[1][5] == 2) then + addMix(AilFields[2][5], MIXSRC_FIRST_INPUT+defaultChannel(3), "AilL") + addMix(AilFields[3][5], MIXSRC_FIRST_INPUT+defaultChannel(3), "AilR", -100) + end + -- Flaps + if(FlapsFields[1][5] == 1) then + addMix(FlapsFields[2][5], MIXSRC_SA, "Flaps") + elseif (FlapsFields[1][5] == 2) then + addMix(FlapsFields[2][5], MIXSRC_SA, "FlapsL") + addMix(FlapsFields[3][5], MIXSRC_SA, "FlapsR") + end + -- Tail + if(TailFields[1][5] == 0) then + addMix(TailFields[2][5], MIXSRC_FIRST_INPUT+defaultChannel(1), "Elev") + elseif (TailFields[1][5] == 1) then + addMix(TailFields[2][5], MIXSRC_FIRST_INPUT+defaultChannel(1), "Elev") + addMix(TailFields[3][5], MIXSRC_FIRST_INPUT+defaultChannel(0), "Rudder") + elseif (TailFields[1][5] == 2) then + addMix(TailFields[2][5], MIXSRC_FIRST_INPUT+defaultChannel(1), "ElevL") + addMix(TailFields[3][5], MIXSRC_FIRST_INPUT+defaultChannel(0), "Rudder") + addMix(TailFields[4][5], MIXSRC_FIRST_INPUT+defaultChannel(1), "ElevR") + elseif (TailFields[1][5] == 3) then + addMix(TailFields[2][5], MIXSRC_FIRST_INPUT+defaultChannel(1), "V-EleL", 50) + addMix(TailFields[2][5], MIXSRC_FIRST_INPUT+defaultChannel(0), "V-RudL", 50, 1) + addMix(TailFields[3][5], MIXSRC_FIRST_INPUT+defaultChannel(1), "V-EleR", 50) + addMix(TailFields[3][5], MIXSRC_FIRST_INPUT+defaultChannel(0), "V-RudR", -50, 1) + end + lcd.drawText(70, 90, "Model successfully created !", TEXT_COLOR) + lcd.drawText(100, 130, "Press RTN to exit", TEXT_COLOR) + return 2 +end + +-- Init +local function init() + current, edit = 1, false + pages = { + runMotorConfig, + runAilConfig, + runFlapsConfig, + runTailConfig, + runConfigSummary, + createModel, + } +end + +-- Main +local function run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif (event == EVT_PAGE_BREAK or event == EVT_PAGEDN_FIRST) and page < #pages-1 then + selectPage(1) + elseif (event == EVT_PAGE_LONG or event == EVT_PAGEUP_FIRST) and page > 1 then + killEvents(event); + selectPage(-1) + end + + local result = pages[page](event) + return result +end + +return { init=init, run=run } diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/icon.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/icon.png new file mode 100755 index 000000000..2deea1d9a Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/icon.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/background.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/background.png new file mode 100755 index 000000000..e21f7bc81 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/background.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/flap.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/flap.png new file mode 100755 index 000000000..82a844891 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/flap.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/lail.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/lail.png new file mode 100755 index 000000000..00cff0cf6 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/lail.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/mark_bg.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/mark_bg.png new file mode 100755 index 000000000..fbe6b0ee2 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/mark_bg.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/pagedn.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/pagedn.png new file mode 100755 index 000000000..0733ccfa1 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/pagedn.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/pageup.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/pageup.png new file mode 100755 index 000000000..0dbaa5202 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/pageup.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/plane.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/plane.png new file mode 100644 index 000000000..ab9db2606 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/plane.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/prop.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/prop.png new file mode 100644 index 000000000..9c353400f Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/prop.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/rail.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/rail.png new file mode 100755 index 000000000..e2f80ffe2 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/rail.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/summary.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/summary.png new file mode 100755 index 000000000..8b23b0b42 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/summary.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/tail.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/tail.png new file mode 100755 index 000000000..db3f29bcd Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/tail.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/tailrud.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/tailrud.png new file mode 100755 index 000000000..d42a7244e Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/tailrud.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/vtail.png b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/vtail.png new file mode 100755 index 000000000..fae86aae4 Binary files /dev/null and b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/img/vtail.png differ diff --git a/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/wizard.lua b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/wizard.lua new file mode 100755 index 000000000..b5701e56d --- /dev/null +++ b/radio/sdcard/nv14/SCRIPTS/WIZARD/plane/wizard.lua @@ -0,0 +1,485 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### +local VALUE = 0 +local COMBO = 1 + +local edit = false +local page = 1 +local current = 1 +local pages = {} +local fields = {} + +-- load common Bitmaps +local ImgMarkBg = Bitmap.open("img/mark_bg.png") +local BackgroundImg = Bitmap.open("img/background.png") +local ImgPlane = Bitmap.open("img/plane.png") +local ImgPageUp = Bitmap.open("img/pageup.png") +local ImgPageDn = Bitmap.open("img/pagedn.png") + + +-- Change display attribute to current field +local function addField(step) + local field = fields[current] + local min, max + if field[3] == VALUE then + min = field[6] + max = field[7] + elseif field[3] == COMBO then + min = 0 + max = #(field[6]) - 1 + end + if (step < 0 and field[5] > min) or (step > 0 and field[5] < max) then + field[5] = field[5] + step + end +end + +-- Select the next or previous page +local function selectPage(step) + page = 1 + ((page + step - 1 + #pages) % #pages) + edit = false + current = 1 +end + +-- Select the next or previous editable field +local function selectField(step) + repeat + current = 1 + ((current + step - 1 + #fields) % #fields) + until fields[current][4]==1 +end + +-- Redraw the current page +local function redrawFieldsPage(event) + + for index = 1, 10, 1 do + local field = fields[index] + if field == nil then + break + end + + local attr = current == (index) and ((edit == true and BLINK or 0) + INVERS) or 0 + attr = attr + TEXT_COLOR + + if field[4] == 1 then + if field[3] == VALUE then + lcd.drawNumber(field[1], field[2], field[5], LEFT + attr) + elseif field[3] == COMBO then + if field[5] >= 0 and field[5] < #(field[6]) then + lcd.drawText(field[1],field[2], field[6][1+field[5]], attr) + end + end + end + end +end + +local function updateField(field) + local value = field[5] +end + +-- Main +local function runFieldsPage(event) + if event == EVT_EXIT_BREAK then -- exit script + return 2 + elseif event == EVT_ENTER_BREAK or event == EVT_ROT_BREAK then -- toggle editing/selecting current field + if fields[current][5] ~= nil then + edit = not edit + if edit == false then + updateField(fields[current]) + end + end + elseif edit then + if event == EVT_PLUS_FIRST or event == EVT_ROT_RIGHT or event == EVT_PLUS_REPT then + addField(1) + elseif event == EVT_MINUS_FIRST or event == EVT_ROT_LEFT or event == EVT_MINUS_REPT then + addField(-1) + end + else + if event == EVT_MINUS_FIRST or event == EVT_ROT_RIGHT then + selectField(1) + elseif event == EVT_PLUS_FIRST or event == EVT_ROT_LEFT then + selectField(-1) + end + end + redrawFieldsPage(event) + return 0 +end + +-- set visibility flags starting with SECOND field of fields +local function setFieldsVisible(...) + local arg={...} + local cnt = 2 + for i,v in ipairs(arg) do + fields[cnt][4] = v + cnt = cnt + 1 + end +end + +-- draws one letter mark +local function drawMark(x, y, name) + lcd.drawBitmap(ImgMarkBg, x, y) + lcd.drawText(x+8, y+3, name, TEXT_COLOR) +end + + +local MotorFields = { + {50, 50, COMBO, 1, 1, { "No", "Yes"} }, + {50, 127, COMBO, 1, 2, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, +} + +local ImgEngine + +local function runMotorConfig(event) + lcd.clear() + if ImgEngine == nil then + ImgEngine = Bitmap.open("img/prop.png") + end + lcd.drawBitmap(BackgroundImg, 0, 0) + lcd.drawBitmap(ImgPageDn, 455, 95) + lcd.drawBitmap(ImgEngine, 310, 50) + lcd.setColor(CUSTOM_COLOR, lcd.RGB(255, 255, 255)) + fields = MotorFields + lcd.drawText(40, 20, "Does your model have a motor ?", TEXT_COLOR) + lcd.drawFilledRectangle(40, 45, 200, 30, CUSTOM_COLOR) + fields[2][4]=0 + if fields[1][5] == 1 then + lcd.drawText(40, 100, "What channel is it on ?", TEXT_COLOR) + lcd.drawFilledRectangle(40, 122, 100, 30, CUSTOM_COLOR) + fields[2][4]=1 + end + local result = runFieldsPage(event) + return result +end + +-- fields format : {[1]x, [2]y, [3]COMBO, [4]visible, [5]default, [6]{values}} +-- fields format : {[1]x, [2]y, [3]VALUE, [4]visible, [5]default, [6]min, [7]max} +local AilFields = { + {50, 50, COMBO, 1, 2, { "None", "One, or two with Y cable", "Two"} }, + {50, 127, COMBO, 1, 0, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, -- Ail1 chan + {50, 167, COMBO, 1, 4, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, -- Ail2 chan +} + +local ImgAilR +local ImgAilL + +local function runAilConfig(event) + lcd.clear() + if ImgAilR == nil then + ImgAilR = Bitmap.open("img/rail.png") + ImgAilL = Bitmap.open("img/lail.png") + end + lcd.drawBitmap(BackgroundImg, 0, 0) + lcd.drawBitmap(ImgPageUp, 0, 95) + lcd.drawBitmap(ImgPageDn, 455, 95) + lcd.drawBitmap(ImgPlane, 252, 100) + fields = AilFields + if fields[1][5] == 1 then + lcd.drawBitmap(ImgAilR, 324, 123) + lcd.drawBitmap(ImgAilL, 275, 210) + drawMark(362, 132, "A") + drawMark(302, 227, "A") + lcd.drawFilledRectangle(40, 122, 100, 30, CUSTOM_COLOR) + drawMark(152, 124, "A") + setFieldsVisible(1, 0) + elseif fields[1][5] == 2 then + lcd.drawBitmap(ImgAilR, 324, 123) + lcd.drawBitmap(ImgAilL, 275, 210) + drawMark(362, 132, "A") + drawMark(302, 227, "B") + lcd.drawFilledRectangle(40, 122, 100, 30, CUSTOM_COLOR) + drawMark(152, 124, "A") + lcd.drawFilledRectangle(40, 162, 100, 30, CUSTOM_COLOR) + drawMark(152, 164, "B") + setFieldsVisible(1, 1) + else + setFieldsVisible(0, 0) + end + lcd.drawText(40, 20, "Number of ailerons on your model ?", TEXT_COLOR) + lcd.drawFilledRectangle(40, 45, 400, 30, CUSTOM_COLOR) + local result = runFieldsPage(event) + return result +end + +local FlapsFields = { + {50, 50, COMBO, 1, 0, { "No", "Yes, on one channel", "Yes, on two channels"} }, + {50, 127, COMBO, 1, 6, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, + {50, 167, COMBO, 1, 7, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, +} + +local ImgFlp + +local function runFlapsConfig(event) + lcd.clear() + if ImgFlp == nil then + ImgFlp = Bitmap.open("img/flap.png") + end + lcd.drawBitmap(BackgroundImg, 0, 0) + lcd.drawBitmap(ImgPageUp, 0, 95) + lcd.drawBitmap(ImgPageDn, 455, 95) + lcd.drawBitmap(ImgPlane, 252, 100) + fields = FlapsFields + if fields[1][5] == 1 then + lcd.drawBitmap(ImgFlp, 315, 143) + lcd.drawBitmap(ImgFlp, 285, 191) + drawMark(362, 132, "A") + drawMark(302, 227, "A") + lcd.drawFilledRectangle(40, 122, 100, 30, CUSTOM_COLOR) + drawMark(152, 124, "A") + setFieldsVisible(1, 0) + elseif fields[1][5] == 2 then + lcd.drawBitmap(ImgFlp, 315, 143) + lcd.drawBitmap(ImgFlp, 285, 191) + drawMark(362, 132, "A") + drawMark(302, 227, "B") + lcd.drawFilledRectangle(40, 122, 100, 30, CUSTOM_COLOR) + drawMark(152, 124, "A") + lcd.drawFilledRectangle(40, 162, 100, 30, CUSTOM_COLOR) + drawMark(152, 164, "B") + setFieldsVisible(1, 1) + else + setFieldsVisible(0, 0) + end + lcd.drawText(40, 20, "Does your model have flaps ?", TEXT_COLOR) + lcd.drawFilledRectangle(40, 45, 400, 30, CUSTOM_COLOR) + local result = runFieldsPage(event) + return result +end + +local TailFields = { + {50, 50, COMBO, 1, 1, { "1 channel for Elevator, no Rudder", "One chan for Elevator, one for Rudder", "Two chans for Elevator, one for Rudder", "V Tail"} }, + {50, 127, COMBO, 1, 1, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, --ele + {50, 167, COMBO, 1, 3, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, --rud + {50, 207, COMBO, 0, 5, { "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8" } }, --ele2 +} + +local ImgTail +local ImgVTail +local ImgTailRud + +local function runTailConfig(event) + lcd.clear() + if ImgTail == nil then + ImgTail = Bitmap.open("img/tail.png") + ImgVTail = Bitmap.open("img/vtail.png") + ImgTailRud = Bitmap.open("img/tailrud.png") + end + lcd.drawBitmap(BackgroundImg, 0, 0) + lcd.drawBitmap(ImgPageUp, 0, 95) + lcd.drawBitmap(ImgPageDn, 455, 95) + fields = TailFields + if fields[1][5] == 0 then + lcd.drawBitmap(ImgTail, 252, 100) + drawMark(275, 120, "A") + drawMark(390, 155, "A") + lcd.drawFilledRectangle(40, 122, 100, 30, CUSTOM_COLOR) + drawMark(152, 124, "A") + setFieldsVisible(1, 0, 0) + end + if fields[1][5] == 1 then + lcd.drawBitmap(ImgTail, 252, 100) + lcd.drawBitmap(ImgTailRud, 340, 100) + drawMark(275, 120, "A") + drawMark(390, 155, "A") + drawMark(370, 112, "B") + lcd.drawFilledRectangle(40, 122, 100, 30, CUSTOM_COLOR) + drawMark(152, 124, "A") + lcd.drawFilledRectangle(40, 162, 100, 30, CUSTOM_COLOR) + drawMark(152, 164, "B") + setFieldsVisible(1, 1, 0) + end + if fields[1][5] == 2 then + lcd.drawBitmap(ImgTail, 252, 100) + lcd.drawBitmap(ImgTailRud, 340, 100) + drawMark(275, 120, "A") + drawMark(390, 155, "C") + drawMark(370, 112, "B") + lcd.drawFilledRectangle(40, 122, 100, 30, CUSTOM_COLOR) + drawMark(152, 124, "A") + lcd.drawFilledRectangle(40, 162, 100, 30, CUSTOM_COLOR) + drawMark(152, 164, "B") + lcd.drawFilledRectangle(40, 202, 100, 30, CUSTOM_COLOR) + drawMark(152, 204, "C") + setFieldsVisible(1, 1, 1) + end + if fields[1][5] == 3 then + lcd.drawBitmap(ImgVTail, 252, 100) + drawMark(315, 110, "A") + drawMark(382, 120, "B") + lcd.drawFilledRectangle(40, 122, 100, 30, CUSTOM_COLOR) + drawMark(152, 124, "A") + lcd.drawFilledRectangle(40, 162, 100, 30, CUSTOM_COLOR) + drawMark(152, 164, "B") + setFieldsVisible(1, 1, 0) + end + lcd.drawText(40, 20, "Pick the tail config of your model", TEXT_COLOR) + lcd.drawFilledRectangle(40, 45, 400, 30, CUSTOM_COLOR) + local result = runFieldsPage(event) + return result +end + +local lineIndex +local function drawNextLine(text, text2) + lcd.drawText(40, lineIndex, text, TEXT_COLOR) + lcd.drawText(250, lineIndex, text2 + 1, TEXT_COLOR) + lineIndex = lineIndex + 20 +end + +local ConfigSummaryFields = { + {110, 250, COMBO, 1, 0, { "No, I need to change something", "Yes, all is well, create the plane !"} }, +} + +local ImgSummary + +local function runConfigSummary(event) + lcd.clear() + if ImgSummary == nil then + ImgSummary = Bitmap.open("img/summary.png") + end + fields = ConfigSummaryFields + lcd.drawBitmap(BackgroundImg, 0, 0) + lcd.drawBitmap(ImgPageUp, 0, 95) + lcd.drawBitmap(ImgSummary, 300, 60) + lineIndex = 40 + -- motors + if(MotorFields[1][5] == 1) then + drawNextLine("Motor chan :", MotorFields[2][5]) + elseif (MotorFields[1][5] == 2) then + drawNextLine("Motor 1 chan :", MotorFields[2][5]) + drawNextLine("Motor 2 chan :", MotorFields[3][5]) + end + -- ail + if(AilFields[1][5] == 1) then + drawNextLine("Aileron chan :",AilFields[2][5]) + elseif (AilFields[1][5] == 2) then + drawNextLine("Aileron 1 chan :",AilFields[2][5]) + drawNextLine("Aileron 2 chan :",AilFields[3][5]) + end + -- flaps + if(FlapsFields[1][5] == 1) then + drawNextLine("Flaps chan :",FlapsFields[2][5]) + elseif (FlapsFields[1][5] == 2) then + drawNextLine("Flaps 1 chan :",FlapsFields[2][5]) + drawNextLine("Flaps 2 chan :",FlapsFields[3][5]) + end + -- tail + if(TailFields[1][5] == 0) then + drawNextLine("Elevator chan :",TailFields[2][5]) + elseif (TailFields[1][5] == 1) then + drawNextLine("Elevator chan :",TailFields[2][5]) + drawNextLine("Rudder chan :",TailFields[3][5]) + elseif (TailFields[1][5] == 2) then + drawNextLine("Elevator 1 chan :",TailFields[2][5]) + drawNextLine("Rudder chan :",TailFields[3][5]) + drawNextLine("Elevator 2 chan :",TailFields[4][5]) + elseif (TailFields[1][5] == 3) then + drawNextLine("V-Tail elevator :", TailFields[2][5]) + drawNextLine("V-Tail rudder :", TailFields[3][5]) + end + local result = runFieldsPage(event) + if(fields[1][5] == 1 and edit == false) then + selectPage(1) + end + return result +end + +local function addMix(channel, input, name, weight, index) + local mix = { source=input, name=name } + if weight ~= nil then + mix.weight = weight + end + if index == nil then + index = 0 + end + model.insertMix(channel, index, mix) +end + +local function createModel(event) + lcd.clear() + lcd.drawBitmap(BackgroundImg, 0, 0) + lcd.drawBitmap(ImgSummary, 300, 60) + model.defaultInputs() + model.deleteMixes() + -- motor + if(MotorFields[1][5] == 1) then + addMix(MotorFields[2][5], MIXSRC_FIRST_INPUT+defaultChannel(2), "Motor") + elseif (MotorFields[1][5] == 2) then + addMix(MotorFields[2][5], MIXSRC_FIRST_INPUT+defaultChannel(2), "Motor1") + addMix(MotorFields[3][5], MIXSRC_FIRST_INPUT+defaultChannel(2), "Motor2") + end + -- Ailerons + if(AilFields[1][5] == 1) then + addMix(AilFields[2][5], MIXSRC_FIRST_INPUT+defaultChannel(3), "Ail") + elseif (AilFields[1][5] == 2) then + addMix(AilFields[2][5], MIXSRC_FIRST_INPUT+defaultChannel(3), "AilL") + addMix(AilFields[3][5], MIXSRC_FIRST_INPUT+defaultChannel(3), "AilR", -100) + end + -- Flaps + if(FlapsFields[1][5] == 1) then + addMix(FlapsFields[2][5], MIXSRC_SA, "Flaps") + elseif (FlapsFields[1][5] == 2) then + addMix(FlapsFields[2][5], MIXSRC_SA, "FlapsL") + addMix(FlapsFields[3][5], MIXSRC_SA, "FlapsR") + end + -- Tail + if(TailFields[1][5] == 0) then + addMix(TailFields[2][5], MIXSRC_FIRST_INPUT+defaultChannel(1), "Elev") + elseif (TailFields[1][5] == 1) then + addMix(TailFields[2][5], MIXSRC_FIRST_INPUT+defaultChannel(1), "Elev") + addMix(TailFields[3][5], MIXSRC_FIRST_INPUT+defaultChannel(0), "Rudder") + elseif (TailFields[1][5] == 2) then + addMix(TailFields[2][5], MIXSRC_FIRST_INPUT+defaultChannel(1), "ElevL") + addMix(TailFields[3][5], MIXSRC_FIRST_INPUT+defaultChannel(0), "Rudder") + addMix(TailFields[4][5], MIXSRC_FIRST_INPUT+defaultChannel(1), "ElevR") + elseif (TailFields[1][5] == 3) then + addMix(TailFields[2][5], MIXSRC_FIRST_INPUT+defaultChannel(1), "V-EleL", 50) + addMix(TailFields[2][5], MIXSRC_FIRST_INPUT+defaultChannel(0), "V-RudL", 50, 1) + addMix(TailFields[3][5], MIXSRC_FIRST_INPUT+defaultChannel(1), "V-EleR", 50) + addMix(TailFields[3][5], MIXSRC_FIRST_INPUT+defaultChannel(0), "V-RudR", -50, 1) + end + lcd.drawText(70, 90, "Model successfully created !", TEXT_COLOR) + lcd.drawText(100, 130, "Press RTN to exit", TEXT_COLOR) + return 2 +end + +-- Init +local function init() + current, edit = 1, false + pages = { + runMotorConfig, + runAilConfig, + runFlapsConfig, + runTailConfig, + runConfigSummary, + createModel, + } +end + +-- Main +local function run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif (event == EVT_PAGE_BREAK or event == EVT_PAGEDN_FIRST) and page < #pages-1 then + selectPage(1) + elseif (event == EVT_PAGE_LONG or event == EVT_PAGEUP_FIRST) and page > 1 then + killEvents(event); + selectPage(-1) + end + + local result = pages[page](event) + return result +end + +return { init=init, run=run } diff --git a/radio/sdcard/nv14/SxR/SxR.lua b/radio/sdcard/nv14/SxR/SxR.lua new file mode 100755 index 000000000..0e76fb781 --- /dev/null +++ b/radio/sdcard/nv14/SxR/SxR.lua @@ -0,0 +1,318 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### +local VALUE = 0 +local COMBO = 1 + +local COLUMN_2 = 300 + +local edit = false +local page = 1 +local current = 1 +local refreshState = 0 +local refreshIndex = 0 +local calibrationState = 0 +local pageOffset = 0 +local calibrationStep = 0 +local pages = {} +local fields = {} +local modifications = {} +local wingBitmaps = {} +local mountBitmaps = {} + +local configFields = { + {"Wing type:", COMBO, 0x80, nil, { "Normal", "Delta", "VTail" } }, + {"Mounting type:", COMBO, 0x81, nil, { "Horz", "Horz rev.", "Vert", "Vert rev." } }, +} +local wingBitmapsFile = {"img/plane_b.png", "img/delta_b.png", "img/planev_b.png"} +local mountBitmapsFile = {"img/up.png", "img/down.png", "img/vert.png", "img/vert-r.png"} + +local settingsFields = { + {"SxR functions:", COMBO, 0x9C, nil, { "Disable", "Enable" } }, + {"Quick Mode:", COMBO, 0xAA, nil, { "Disable", "Enable" } }, + {"CH5 mode:", COMBO, 0xA8, nil, { "AIL2", "AUX1" } }, + {"CH6 mode:", COMBO, 0xA9, nil, { "ELE2", "AUX2" } }, + {"AIL direction:", COMBO, 0x82, nil, { "Normal", "Invers" }, { 255, 0 } }, + {"ELE direction:", COMBO, 0x83, nil, { "Normal", "Invers" }, { 255, 0 } }, + {"RUD direction:", COMBO, 0x84, nil, { "Normal", "Invers" }, { 255, 0 } }, + {"AIL2 direction:", COMBO, 0x9A, nil, { "Normal", "Invers" }, { 255, 0 } }, + {"ELE2 direction:", COMBO, 0x9B, nil, { "Normal", "Invers" }, { 255, 0 } }, + {"AIL stabilize gain:", VALUE, 0x85, nil, 0, 200, "%"}, + {"ELE stabilize gain:", VALUE, 0x86, nil, 0, 200, "%"}, + {"RUD stabilize gain:", VALUE, 0x87, nil, 0, 200, "%"}, + {"AIL auto level gain:", VALUE, 0x88, nil, 0, 200, "%"}, + {"ELE auto level gain:", VALUE, 0x89, nil, 0, 200, "%"}, + {"ELE upright gain:", VALUE, 0x8C, nil, 0, 200, "%"}, + {"RUD upright gain:", VALUE, 0x8D, nil, 0, 200, "%"}, + {"AIL crab gain:", VALUE, 0x8E, nil, 0, 200, "%"}, + {"RUD crab gain:", VALUE, 0x90, nil, 0, 200, "%"}, + {"AIL auto angle offset:", VALUE, 0x91, nil, -20, 20, "%", 0x6C}, + {"ELE auto angle offset:", VALUE, 0x92, nil, -20, 20, "%", 0x6C}, + {"ELE upright angle offset:", VALUE, 0x95, nil, -20, 20, "%", 0x6C}, + {"RUD upright angle offset:", VALUE, 0x96, nil, -20, 20, "%", 0x6C}, + {"AIL crab angle offset:", VALUE, 0x97, nil, -20, 20, "%", 0x6C}, + {"RUD crab angle offset:", VALUE, 0x99, nil, -20, 20, "%", 0x6C}, +} + +local calibrationFields = { + {"X:", VALUE, 0x9E, 0, -100, 100, "%"}, + {"Y:", VALUE, 0x9F, 0, -100, 100, "%"}, + {"Z:", VALUE, 0xA0, 0, -100, 100, "%"} +} + +local function drawScreenTitle(title,page, pages) + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, title, MENU_TITLE_COLOR) + lcd.drawText(LCD_W-40, 5, page.."/"..pages, MENU_TITLE_COLOR) +end + +-- Change display attribute to current field +local function addField(step) + local field = fields[current] + local min, max + if field[2] == VALUE then + min = field[5] + max = field[6] + elseif field[2] == COMBO then + min = 0 + max = #(field[5]) - 1 + end + if (step < 0 and field[4] > min) or (step > 0 and field[4] < max) then + field[4] = field[4] + step + end +end + +-- Select the next or previous page +local function selectPage(step) + page = 1 + ((page + step - 1 + #pages) % #pages) + refreshIndex = 0 + calibrationStep = 0 + pageOffset = 0 +end + +-- Select the next or previous editable field +local function selectField(step) + current = 1 + ((current + step - 1 + #fields) % #fields) + if current > 7 + pageOffset then + pageOffset = current - 7 + elseif current <= pageOffset then + pageOffset = current - 1 + end +end + +local function drawProgressBar() + local width = (300 * refreshIndex) / #fields + lcd.drawRectangle(100, 10, 300, 6) + lcd.drawFilledRectangle(102, 13, width, 2); +end + +-- Redraw the current page +local function redrawFieldsPage(event) + lcd.clear() + drawScreenTitle("SxR", page, #pages) + + if refreshIndex < #fields then + drawProgressBar() + end + + for index = 1, 10, 1 do + local field = fields[pageOffset+index] + if field == nil then + break + end + + local attr = current == (pageOffset+index) and ((edit == true and BLINK or 0) + INVERS) or 0 + attr = attr + TEXT_COLOR + + lcd.drawText(1, 30+20*index, field[1], TEXT_COLOR) + + if field[4] == nil then + lcd.drawText(COLUMN_2, 30+20*index, "---", attr) + else + if field[2] == VALUE then + lcd.drawNumber(COLUMN_2, 30+20*index, field[4], LEFT + attr) + elseif field[2] == COMBO then + if field[4] >= 0 and field[4] < #(field[5]) then + lcd.drawText(COLUMN_2, 30+20*index, field[5][1+field[4]], attr) + end + end + end + end +end + +local function telemetryRead(field) + return sportTelemetryPush(0x17, 0x30, 0x0C30, field) +end + +local function telemetryWrite(field, value) + return sportTelemetryPush(0x17, 0x31, 0x0C30, field + value*256) +end + +local telemetryPopTimeout = 0 +local function refreshNext() + if refreshState == 0 then + if calibrationState == 1 then + if telemetryWrite(0x9D, calibrationStep) == true then + refreshState = 1 + calibrationState = 2 + telemetryPopTimeout = getTime() + 120 -- normal delay is 500ms + end + elseif #modifications > 0 then + telemetryWrite(modifications[1][1], modifications[1][2]) + modifications[1] = nil + elseif refreshIndex < #fields then + local field = fields[refreshIndex + 1] + if telemetryRead(field[3]) == true then + refreshState = 1 + telemetryPopTimeout = getTime() + 80 -- normal delay is 500ms + end + end + elseif refreshState == 1 then + local physicalId, primId, dataId, value = sportTelemetryPop() + if physicalId == 0x1A and primId == 0x32 and dataId == 0x0C30 then + local fieldId = value % 256 + if calibrationState == 2 then + if fieldId == 0x9D then + refreshState = 0 + calibrationState = 0 + calibrationStep = (calibrationStep + 1) % 7 + end + else + local field = fields[refreshIndex + 1] + if fieldId == field[3] then + local value = math.floor(value / 256) + if field[3] == 0xAA then + value = bit32.band(value, 0x0001) + end + if field[3] >= 0x9E and field[3] <= 0xA0 then + local b1 = value % 256 + local b2 = math.floor(value / 256) + value = b1*256 + b2 + value = value - bit32.band(value, 0x8000) * 2 + end + if field[2] == COMBO and #field == 6 then + for index = 1, #(field[6]), 1 do + if value == field[6][index] then + value = index - 1 + break + end + end + elseif field[2] == VALUE and #field == 8 then + value = value - field[8] + field[5] + end + fields[refreshIndex + 1][4] = value + refreshIndex = refreshIndex + 1 + refreshState = 0 + end + end + elseif getTime() > telemetryPopTimeout then + fields[refreshIndex + 1][4] = nil + refreshIndex = refreshIndex + 1 + refreshState = 0 + calibrationState = 0 + end + end +end + +local function updateField(field) + local value = field[4] + if field[2] == COMBO and #field == 6 then + value = field[6][1+value] + elseif field[2] == VALUE and #field == 8 then + value = value + field[8] - field[5] + end + modifications[#modifications+1] = { field[3], value } +end + +-- Main +local function runFieldsPage(event) + if event == EVT_EXIT_BREAK then -- exit script + return 2 + elseif event == EVT_ENTER_BREAK or event == EVT_ROT_BREAK then -- toggle editing/selecting current field + if fields[current][4] ~= nil then + edit = not edit + if edit == false then + updateField(fields[current]) + end + end + elseif edit then + if event == EVT_PLUS_FIRST or event == EVT_ROT_RIGHT or event == EVT_PLUS_REPT then + addField(1) + elseif event == EVT_MINUS_FIRST or event == EVT_ROT_LEFT or event == EVT_MINUS_REPT then + addField(-1) + end + else + if event == EVT_MINUS_FIRST or event == EVT_ROT_RIGHT then + selectField(1) + elseif event == EVT_PLUS_FIRST or event == EVT_ROT_LEFT then + selectField(-1) + end + end + redrawFieldsPage(event) + return 0 +end + +local function runConfigPage(event) + fields = configFields + local result = runFieldsPage(event) + if fields[1][4] ~= nil then + if wingBitmaps[1 + fields[1][4]] == nil then + wingBitmaps[1 + fields[1][4]] = Bitmap.open(wingBitmapsFile[1 + fields[1][4]]) + end + lcd.drawBitmap(wingBitmaps[1 + fields[1][4]], 10, 90) + end + if fields[2][4] ~= nil then + if mountBitmaps[1 + fields[2][4]] == nil then + mountBitmaps[1 + fields[2][4]] = Bitmap.open(mountBitmapsFile[1 + fields[2][4]]) + end + lcd.drawBitmap(mountBitmaps[1 + fields[2][4]], 190, 110) + end + return result +end + +local function runSettingsPage(event) + fields = settingsFields + return runFieldsPage(event) +end + +-- Init +local function init() + current, edit, refreshState, refreshIndex = 1, false, 0, 0 + pages = { + runConfigPage, + runSettingsPage, + } +end + +-- Main +local function run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_PAGE_BREAK or event == EVT_PAGEDN_FIRST then + selectPage(1) + elseif event == EVT_PAGE_LONG or event == EVT_PAGEUP_FIRST then + killEvents(event); + selectPage(-1) + end + + local result = pages[page](event) + refreshNext() + + return result +end + +return { init=init, run=run } diff --git a/radio/sdcard/nv14/SxR/SxR_Calibrate.lua b/radio/sdcard/nv14/SxR/SxR_Calibrate.lua new file mode 100755 index 000000000..4568b50eb --- /dev/null +++ b/radio/sdcard/nv14/SxR/SxR_Calibrate.lua @@ -0,0 +1,357 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### +local VALUE = 0 +local COMBO = 1 + +local COLUMN_2 = 300 + +local edit = false +local page = 1 +local current = 1 +local refreshState = 0 +local refreshIndex = 0 +local calibrationState = 0 +local pageOffset = 0 +local calibrationStep = 0 +local pages = {} +local fields = {} +local modifications = {} +local wingBitmaps = {} +local mountBitmaps = {} +local calibBitmaps = {} + +local configFields = { + {"Wing type:", COMBO, 0x80, nil, { "Normal", "Delta", "VTail" } }, + {"Mounting type:", COMBO, 0x81, nil, { "Horz", "Horz rev.", "Vert", "Vert rev." } }, +} +local calibrationPositions = { "up", "down", "left", "right", "forward", "back" } +local wingBitmapsFile = {"img/plane_b.png", "img/delta_b.png", "img/planev_b.png"} +local mountBitmapsFile = {"img/up.png", "img/down.png", "img/vert.png", "img/vert-r.png"} +local calibBitmapsFile = {"img/up.png", "img/down.png", "img/left.png", "img/right.png", "img/forward.png", "img/back.png"} + +local settingsFields = { + {"SxR functions:", COMBO, 0x9C, nil, { "Disable", "Enable" } }, + {"CH5 mode:", COMBO, 0xA8, nil, { "AIL2", "AUX1" } }, + {"CH6 mode:", COMBO, 0xA9, nil, { "ELE2", "AUX2" } }, + {"AIL direction:", COMBO, 0x82, nil, { "Normal", "Invers" }, { 255, 0 } }, + {"ELE direction:", COMBO, 0x83, nil, { "Normal", "Invers" }, { 255, 0 } }, + {"RUD direction:", COMBO, 0x84, nil, { "Normal", "Invers" }, { 255, 0 } }, + {"AIL2 direction:", COMBO, 0x9A, nil, { "Normal", "Invers" }, { 255, 0 } }, + {"ELE2 direction:", COMBO, 0x9B, nil, { "Normal", "Invers" }, { 255, 0 } }, + {"AIL stabilize gain:", VALUE, 0x85, nil, 0, 200, "%"}, + {"ELE stabilize gain:", VALUE, 0x86, nil, 0, 200, "%"}, + {"RUD stabilize gain:", VALUE, 0x87, nil, 0, 200, "%"}, + {"AIL auto level gain:", VALUE, 0x88, nil, 0, 200, "%"}, + {"ELE auto level gain:", VALUE, 0x89, nil, 0, 200, "%"}, + {"ELE upright gain:", VALUE, 0x8C, nil, 0, 200, "%"}, + {"RUD upright gain:", VALUE, 0x8D, nil, 0, 200, "%"}, + {"AIL crab gain:", VALUE, 0x8E, nil, 0, 200, "%"}, + {"RUD crab gain:", VALUE, 0x90, nil, 0, 200, "%"}, + {"AIL auto angle offset:", VALUE, 0x91, nil, -20, 20, "%", 0x6C}, + {"ELE auto angle offset:", VALUE, 0x92, nil, -20, 20, "%", 0x6C}, + {"ELE upright angle offset:", VALUE, 0x95, nil, -20, 20, "%", 0x6C}, + {"RUD upright angle offset:", VALUE, 0x96, nil, -20, 20, "%", 0x6C}, + {"AIL crab angle offset:", VALUE, 0x97, nil, -20, 20, "%", 0x6C}, + {"RUD crab angle offset:", VALUE, 0x99, nil, -20, 20, "%", 0x6C}, +} + +local calibrationFields = { + {"X:", VALUE, 0x9E, 0, -100, 100, "%"}, + {"Y:", VALUE, 0x9F, 0, -100, 100, "%"}, + {"Z:", VALUE, 0xA0, 0, -100, 100, "%"} +} + +local function drawScreenTitle(title,page, pages) + lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR) + lcd.drawText(1, 5, title, MENU_TITLE_COLOR) + lcd.drawText(LCD_W-40, 5, page.."/"..pages, MENU_TITLE_COLOR) +end + +-- Change display attribute to current field +local function addField(step) + local field = fields[current] + local min, max + if field[2] == VALUE then + min = field[5] + max = field[6] + elseif field[2] == COMBO then + min = 0 + max = #(field[5]) - 1 + end + if (step < 0 and field[4] > min) or (step > 0 and field[4] < max) then + field[4] = field[4] + step + end +end + +-- Select the next or previous page +local function selectPage(step) + page = 1 + ((page + step - 1 + #pages) % #pages) + refreshIndex = 0 + calibrationStep = 0 + pageOffset = 0 +end + +-- Select the next or previous editable field +local function selectField(step) + current = 1 + ((current + step - 1 + #fields) % #fields) + if current > 7 + pageOffset then + pageOffset = current - 7 + elseif current <= pageOffset then + pageOffset = current - 1 + end +end + +local function drawProgressBar() + local width = (300 * refreshIndex) / #fields + lcd.drawRectangle(100, 10, 300, 6) + lcd.drawFilledRectangle(102, 13, width, 2); +end + +-- Redraw the current page +local function redrawFieldsPage(event) + lcd.clear() + drawScreenTitle("SxR", page, #pages) + + if refreshIndex < #fields then + drawProgressBar() + end + + for index = 1, 10, 1 do + local field = fields[pageOffset+index] + if field == nil then + break + end + + local attr = current == (pageOffset+index) and ((edit == true and BLINK or 0) + INVERS) or 0 + attr = attr + TEXT_COLOR + + lcd.drawText(1, 30+20*index, field[1], TEXT_COLOR) + + if field[4] == nil then + lcd.drawText(COLUMN_2, 30+20*index, "---", attr) + else + if field[2] == VALUE then + lcd.drawNumber(COLUMN_2, 30+20*index, field[4], LEFT + attr) + elseif field[2] == COMBO then + if field[4] >= 0 and field[4] < #(field[5]) then + lcd.drawText(COLUMN_2, 30+20*index, field[5][1+field[4]], attr) + end + end + end + end +end + +local function telemetryRead(field) + return sportTelemetryPush(0x17, 0x30, 0x0C30, field) +end + +local function telemetryWrite(field, value) + return sportTelemetryPush(0x17, 0x31, 0x0C30, field + value*256) +end + +local telemetryPopTimeout = 0 +local function refreshNext() + if refreshState == 0 then + if calibrationState == 1 then + if telemetryWrite(0x9D, calibrationStep) == true then + refreshState = 1 + calibrationState = 2 + telemetryPopTimeout = getTime() + 80 -- normal delay is 500ms + end + elseif #modifications > 0 then + telemetryWrite(modifications[1][1], modifications[1][2]) + modifications[1] = nil + elseif refreshIndex < #fields then + local field = fields[refreshIndex + 1] + if telemetryRead(field[3]) == true then + refreshState = 1 + telemetryPopTimeout = getTime() + 80 -- normal delay is 500ms + end + end + elseif refreshState == 1 then + local physicalId, primId, dataId, value = sportTelemetryPop() + if physicalId == 0x1A and primId == 0x32 and dataId == 0x0C30 then + local fieldId = value % 256 + if calibrationState == 2 then + if fieldId == 0x9D then + refreshState = 0 + calibrationState = 0 + calibrationStep = (calibrationStep + 1) % 7 + end + else + local field = fields[refreshIndex + 1] + if fieldId == field[3] then + local value = math.floor(value / 256) + value = bit32.band(value, 0xffff) + if field[3] >= 0x9E and field[3] <= 0xA0 then + local b1 = value % 256 + local b2 = math.floor(value / 256) + value = b1*256 + b2 + value = value - bit32.band(value, 0x8000) * 2 + end + if field[2] == COMBO and #field == 6 then + for index = 1, #(field[6]), 1 do + if value == field[6][index] then + value = index - 1 + break + end + end + elseif field[2] == VALUE and #field == 8 then + value = value - field[8] + field[5] + end + fields[refreshIndex + 1][4] = value + refreshIndex = refreshIndex + 1 + refreshState = 0 + end + end + elseif getTime() > telemetryPopTimeout then + refreshState = 0 + calibrationState = 0 + end + end +end + +local function updateField(field) + local value = field[4] + if field[2] == COMBO and #field == 6 then + value = field[6][1+value] + elseif field[2] == VALUE and #field == 8 then + value = value + field[8] - field[5] + end + modifications[#modifications+1] = { field[3], value } +end + +-- Main +local function runFieldsPage(event) + if event == EVT_EXIT_BREAK then -- exit script + return 2 + elseif event == EVT_ENTER_BREAK or event == EVT_ROT_BREAK then -- toggle editing/selecting current field + if fields[current][4] ~= nil then + edit = not edit + if edit == false then + updateField(fields[current]) + end + end + elseif edit then + if event == EVT_PLUS_FIRST or event == EVT_ROT_RIGHT or event == EVT_PLUS_REPT then + addField(1) + elseif event == EVT_MINUS_FIRST or event == EVT_ROT_LEFT or event == EVT_MINUS_REPT then + addField(-1) + end + else + if event == EVT_MINUS_FIRST or event == EVT_ROT_RIGHT then + selectField(1) + elseif event == EVT_PLUS_FIRST or event == EVT_ROT_LEFT then + selectField(-1) + end + end + redrawFieldsPage(event) + return 0 +end + +local function runConfigPage(event) + fields = configFields + local result = runFieldsPage(event) + if fields[1][4] ~= nil then + if wingBitmaps[1 + fields[1][4]] == nil then + wingBitmaps[1 + fields[1][4]] = Bitmap.open(wingBitmapsFile[1 + fields[1][4]]) + end + lcd.drawBitmap(wingBitmaps[1 + fields[1][4]], 10, 90) + end + if fields[2][4] ~= nil then + if mountBitmaps[1 + fields[2][4]] == nil then + mountBitmaps[1 + fields[2][4]] = Bitmap.open(mountBitmapsFile[1 + fields[2][4]]) + end + lcd.drawBitmap(mountBitmaps[1 + fields[2][4]], 190, 110) + end + return result +end + +local function runSettingsPage(event) + fields = settingsFields + return runFieldsPage(event) +end + +local function runCalibrationPage(event) + fields = calibrationFields + if refreshIndex == #fields then + refreshIndex = 0 + end + lcd.clear() + drawScreenTitle("SxR", page, #pages) + if(calibrationStep < 6) then + local position = calibrationPositions[1 + calibrationStep] + lcd.drawText(100, 50, "Place the SxR in the following position", TEXT_COLOR) + if calibBitmaps[calibrationStep + 1] == nil then + calibBitmaps[calibrationStep + 1] = Bitmap.open(calibBitmapsFile[calibrationStep + 1]) + end + lcd.drawBitmap(calibBitmaps[calibrationStep + 1], 200, 70) + for index = 1, 3, 1 do + local field = fields[index] + lcd.drawText(70, 80+20*index, field[1]..":", TEXT_COLOR) + lcd.drawNumber(90, 80+20*index, field[4]/10, LEFT+PREC2) + end + + local attr = calibrationState == 0 and INVERS or 0 + lcd.drawText(160, 220, "Press [Enter] when ready", attr) + else + lcd.drawText(160, 50, "Calibration completed", 0) + lcd.drawBitmap(Bitmap.open("img/done.bmp"),200, 100) + lcd.drawText(160, 220, "Press [RTN] when ready", attr) + end + if calibrationStep > 6 and (event == EVT_ENTER_BREAK or event == EVT_EXIT_BREAK) then + return 2 + elseif event == EVT_ENTER_BREAK then + calibrationState = 1 + elseif event == EVT_EXIT_BREAK then + if calibrationStep > 0 then + calibrationStep = 0 + end + end + return 0 + +end + +-- Init +local function init() + current, edit, refreshState, refreshIndex = 1, false, 0, 0 + pages = { + -- runConfigPage, + -- runSettingsPage, + runCalibrationPage + } +end + +-- Main +local function run(event) + if event == nil then + error("Cannot be run as a model script!") + return 2 + elseif event == EVT_PAGE_BREAK or event == EVT_PAGEDN_FIRST then + selectPage(1) + elseif event == EVT_PAGE_LONG or event == EVT_PAGEUP_FIRST then + killEvents(event); + selectPage(-1) + end + + local result = pages[page](event) + refreshNext() + + return result +end + +return { init=init, run=run } diff --git a/radio/sdcard/nv14/SxR/img/back.png b/radio/sdcard/nv14/SxR/img/back.png new file mode 100755 index 000000000..2cf3cd699 Binary files /dev/null and b/radio/sdcard/nv14/SxR/img/back.png differ diff --git a/radio/sdcard/nv14/SxR/img/delta_b.png b/radio/sdcard/nv14/SxR/img/delta_b.png new file mode 100755 index 000000000..80c0302f9 Binary files /dev/null and b/radio/sdcard/nv14/SxR/img/delta_b.png differ diff --git a/radio/sdcard/nv14/SxR/img/done.bmp b/radio/sdcard/nv14/SxR/img/done.bmp new file mode 100755 index 000000000..9ac501398 Binary files /dev/null and b/radio/sdcard/nv14/SxR/img/done.bmp differ diff --git a/radio/sdcard/nv14/SxR/img/down.png b/radio/sdcard/nv14/SxR/img/down.png new file mode 100755 index 000000000..8eac8039a Binary files /dev/null and b/radio/sdcard/nv14/SxR/img/down.png differ diff --git a/radio/sdcard/nv14/SxR/img/forward.png b/radio/sdcard/nv14/SxR/img/forward.png new file mode 100755 index 000000000..de09680b7 Binary files /dev/null and b/radio/sdcard/nv14/SxR/img/forward.png differ diff --git a/radio/sdcard/nv14/SxR/img/left.png b/radio/sdcard/nv14/SxR/img/left.png new file mode 100755 index 000000000..658c9916b Binary files /dev/null and b/radio/sdcard/nv14/SxR/img/left.png differ diff --git a/radio/sdcard/nv14/SxR/img/plane_b.png b/radio/sdcard/nv14/SxR/img/plane_b.png new file mode 100755 index 000000000..37abedc3a Binary files /dev/null and b/radio/sdcard/nv14/SxR/img/plane_b.png differ diff --git a/radio/sdcard/nv14/SxR/img/planev_b.png b/radio/sdcard/nv14/SxR/img/planev_b.png new file mode 100755 index 000000000..4e33f660b Binary files /dev/null and b/radio/sdcard/nv14/SxR/img/planev_b.png differ diff --git a/radio/sdcard/nv14/SxR/img/planev_s.png b/radio/sdcard/nv14/SxR/img/planev_s.png new file mode 100755 index 000000000..48283a290 Binary files /dev/null and b/radio/sdcard/nv14/SxR/img/planev_s.png differ diff --git a/radio/sdcard/nv14/SxR/img/right.png b/radio/sdcard/nv14/SxR/img/right.png new file mode 100755 index 000000000..01d555394 Binary files /dev/null and b/radio/sdcard/nv14/SxR/img/right.png differ diff --git a/radio/sdcard/nv14/SxR/img/up.png b/radio/sdcard/nv14/SxR/img/up.png new file mode 100755 index 000000000..f033aba55 Binary files /dev/null and b/radio/sdcard/nv14/SxR/img/up.png differ diff --git a/radio/sdcard/nv14/SxR/img/vert-r.png b/radio/sdcard/nv14/SxR/img/vert-r.png new file mode 100755 index 000000000..7bfc9450c Binary files /dev/null and b/radio/sdcard/nv14/SxR/img/vert-r.png differ diff --git a/radio/sdcard/nv14/SxR/img/vert.png b/radio/sdcard/nv14/SxR/img/vert.png new file mode 100755 index 000000000..de09680b7 Binary files /dev/null and b/radio/sdcard/nv14/SxR/img/vert.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/X10.bmp b/radio/sdcard/nv14/THEMES/Darkblue/X10.bmp new file mode 100755 index 000000000..24893bad0 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/X10.bmp differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/X10S.bmp b/radio/sdcard/nv14/THEMES/Darkblue/X10S.bmp new file mode 100755 index 000000000..e27932eef Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/X10S.bmp differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/asterisk.bmp b/radio/sdcard/nv14/THEMES/Darkblue/asterisk.bmp new file mode 100644 index 000000000..3dd3cfef4 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/asterisk.bmp differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/busy.bmp b/radio/sdcard/nv14/THEMES/Darkblue/busy.bmp new file mode 100644 index 000000000..8e6a8b7f7 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/busy.bmp differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/horus.bmp b/radio/sdcard/nv14/THEMES/Darkblue/horus.bmp new file mode 100644 index 000000000..98bc631cd Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/horus.bmp differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_currentmenu_bg.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_currentmenu_bg.png new file mode 100644 index 000000000..715792023 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_currentmenu_bg.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_currentmenu_dot.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_currentmenu_dot.png new file mode 100644 index 000000000..6f76965a9 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_currentmenu_dot.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_currentmenu_shadow.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_currentmenu_shadow.png new file mode 100644 index 000000000..97bf6c577 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_currentmenu_shadow.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_library.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_library.png new file mode 100644 index 000000000..237883fc5 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_library.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_menu_model.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_menu_model.png new file mode 100644 index 000000000..9832267b7 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_menu_model.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_menu_radio.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_menu_radio.png new file mode 100644 index 000000000..6d93fbccc Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_menu_radio.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_menu_stats.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_menu_stats.png new file mode 100644 index 000000000..ebd482588 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_menu_stats.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_menu_theme.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_menu_theme.png new file mode 100644 index 000000000..3a9fc15e7 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_menu_theme.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_model_curves.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_curves.png new file mode 100644 index 000000000..6e1e19e8e Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_curves.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_model_flight_modes.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_flight_modes.png new file mode 100644 index 000000000..e963f0abb Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_flight_modes.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_model_gvars.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_gvars.png new file mode 100644 index 000000000..0b5015dac Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_gvars.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_model_heli.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_heli.png new file mode 100644 index 000000000..ecd93d2ff Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_heli.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_model_inputs.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_inputs.png new file mode 100644 index 000000000..720099c47 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_inputs.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_model_logical_switches.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_logical_switches.png new file mode 100644 index 000000000..5cd1e3834 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_logical_switches.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_model_lua_scripts.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_lua_scripts.png new file mode 100644 index 000000000..a20c1ccbd Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_lua_scripts.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_model_mixer.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_mixer.png new file mode 100644 index 000000000..009b360f1 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_mixer.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_model_outputs.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_outputs.png new file mode 100644 index 000000000..a209ae8d3 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_outputs.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_model_setup.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_setup.png new file mode 100644 index 000000000..afe526c8f Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_setup.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_model_special_functions.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_special_functions.png new file mode 100644 index 000000000..a12a2a50d Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_special_functions.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_model_telemetry.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_telemetry.png new file mode 100644 index 000000000..d95d3b25f Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_model_telemetry.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor.png new file mode 100644 index 000000000..68ddd4516 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_channels1.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_channels1.png new file mode 100644 index 000000000..b886fc1bc Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_channels1.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_channels2.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_channels2.png new file mode 100644 index 000000000..4d0354e25 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_channels2.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_channels3.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_channels3.png new file mode 100644 index 000000000..d9ba27de7 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_channels3.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_channels4.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_channels4.png new file mode 100644 index 000000000..fb321151f Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_channels4.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_gvars.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_gvars.png new file mode 100644 index 000000000..55b488d1e Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_gvars.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_inver.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_inver.png new file mode 100644 index 000000000..64f251238 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_inver.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_lockch.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_lockch.png new file mode 100644 index 000000000..360923a09 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_lockch.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_logsw.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_logsw.png new file mode 100644 index 000000000..dc6355143 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_logsw.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_mixes1.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_mixes1.png new file mode 100644 index 000000000..7b0c71f60 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_mixes1.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_mixes2.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_mixes2.png new file mode 100644 index 000000000..f8c04780e Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_monitor_mixes2.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_mplex_add.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_mplex_add.png new file mode 100644 index 000000000..14f4ecc23 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_mplex_add.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_mplex_multi.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_mplex_multi.png new file mode 100644 index 000000000..bcf79695f Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_mplex_multi.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_mplex_replace.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_mplex_replace.png new file mode 100644 index 000000000..faccba0ea Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_mplex_replace.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_opentx.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_opentx.png new file mode 100644 index 000000000..df2b0b356 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_opentx.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_opentx_testmode.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_opentx_testmode.png new file mode 100644 index 000000000..b535cf8ee Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_opentx_testmode.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_calibration.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_calibration.png new file mode 100644 index 000000000..db696778b Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_calibration.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_global_functions.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_global_functions.png new file mode 100644 index 000000000..3b3efefb5 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_global_functions.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_hardware.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_hardware.png new file mode 100644 index 000000000..f51a1aee1 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_hardware.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_sd_browser.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_sd_browser.png new file mode 100644 index 000000000..75b6a9296 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_sd_browser.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_setup.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_setup.png new file mode 100644 index 000000000..0010a06ab Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_setup.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_tools.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_tools.png new file mode 100755 index 000000000..711d499de Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_tools.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_trainer.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_trainer.png new file mode 100644 index 000000000..7a0b56970 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_trainer.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_version.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_version.png new file mode 100644 index 000000000..8730f6c1a Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_radio_version.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_sbar_mixer.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_sbar_mixer.png new file mode 100644 index 000000000..154406419 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_sbar_mixer.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_sbar_output.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_sbar_output.png new file mode 100644 index 000000000..12d599823 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_sbar_output.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_sbar_to.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_sbar_to.png new file mode 100644 index 000000000..813610307 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_sbar_to.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_stats_analogs.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_stats_analogs.png new file mode 100644 index 000000000..96fc0c8a2 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_stats_analogs.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_stats_debug.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_stats_debug.png new file mode 100644 index 000000000..0dce13d46 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_stats_debug.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_stats_lua.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_stats_lua.png new file mode 100644 index 000000000..d1b54a4d6 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_stats_lua.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_stats_throttle_graph.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_stats_throttle_graph.png new file mode 100644 index 000000000..a3720a005 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_stats_throttle_graph.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_stats_timers.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_stats_timers.png new file mode 100644 index 000000000..78bf00064 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_stats_timers.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_stats_value.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_stats_value.png new file mode 100644 index 000000000..180f16e8c Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_stats_value.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_switches.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_switches.png new file mode 100644 index 000000000..4097ed751 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_switches.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_curve.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_curve.png new file mode 100644 index 000000000..050229393 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_curve.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_delay.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_delay.png new file mode 100644 index 000000000..9b771a94a Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_delay.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_delayslow.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_delayslow.png new file mode 100644 index 000000000..de2f7d1f7 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_delayslow.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_fm.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_fm.png new file mode 100644 index 000000000..b9a2c4a32 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_fm.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_label.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_label.png new file mode 100644 index 000000000..8be12e39b Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_label.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_slow.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_slow.png new file mode 100644 index 000000000..1afbea37d Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_slow.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_switch.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_switch.png new file mode 100644 index 000000000..7c7d646e4 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_textline_switch.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_add_view.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_add_view.png new file mode 100644 index 000000000..7e240372f Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_add_view.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_setup.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_setup.png new file mode 100644 index 000000000..2a39d1e40 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_setup.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view1.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view1.png new file mode 100644 index 000000000..9e67515ec Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view1.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view2.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view2.png new file mode 100644 index 000000000..45cec234d Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view2.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view3.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view3.png new file mode 100644 index 000000000..e5a776b06 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view3.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view4.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view4.png new file mode 100644 index 000000000..9064b9403 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view4.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view5.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view5.png new file mode 100644 index 000000000..724bcc141 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view5.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view6.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view6.png new file mode 100644 index 000000000..2aa43514b Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view6.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view7.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view7.png new file mode 100644 index 000000000..d6a0540f5 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view7.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view8.png b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view8.png new file mode 100644 index 000000000..82dfd7875 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/mask_theme_view8.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_boat1.png b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_boat1.png new file mode 100644 index 000000000..a9335c4bc Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_boat1.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_boat2.png b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_boat2.png new file mode 100644 index 000000000..e3c899d18 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_boat2.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_default.png b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_default.png new file mode 100644 index 000000000..6f3042c67 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_default.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_favorites.png b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_favorites.png new file mode 100644 index 000000000..e4a37dacb Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_favorites.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_plane1.png b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_plane1.png new file mode 100644 index 000000000..a49a50271 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_plane1.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_plane2.png b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_plane2.png new file mode 100644 index 000000000..8305b4150 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_plane2.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_plane3.png b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_plane3.png new file mode 100644 index 000000000..d998144b2 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/icon_plane3.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/modelsel/mask_iconback.png b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/mask_iconback.png new file mode 100644 index 000000000..966f6b7f2 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/mask_iconback.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/modelsel/mask_modelname.png b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/mask_modelname.png new file mode 100644 index 000000000..2edca73ea Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/mask_modelname.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/modelsel/mask_modelqty.png b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/mask_modelqty.png new file mode 100644 index 000000000..9a38e4f13 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/mask_modelqty.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/modelsel/mask_moveback.png b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/mask_moveback.png new file mode 100644 index 000000000..63f56df92 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/mask_moveback.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/modelsel/mask_moveico.png b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/mask_moveico.png new file mode 100644 index 000000000..3662b9bb0 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/mask_moveico.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/modelsel/mask_sdfree.png b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/mask_sdfree.png new file mode 100644 index 000000000..f0ae81982 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/modelsel/mask_sdfree.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/question.bmp b/radio/sdcard/nv14/THEMES/Darkblue/question.bmp new file mode 100644 index 000000000..101d7ed53 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/question.bmp differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/stick_background.png b/radio/sdcard/nv14/THEMES/Darkblue/stick_background.png new file mode 100644 index 000000000..9f6d53d92 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/stick_background.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/stick_pointer.png b/radio/sdcard/nv14/THEMES/Darkblue/stick_pointer.png new file mode 100644 index 000000000..0d75765f9 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/stick_pointer.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/thumb.bmp b/radio/sdcard/nv14/THEMES/Darkblue/thumb.bmp new file mode 100644 index 000000000..af5de373e Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/thumb.bmp differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/topleft.png b/radio/sdcard/nv14/THEMES/Darkblue/topleft.png new file mode 100644 index 000000000..603bfe1cf Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/topleft.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/topmenu_opentx.bmp b/radio/sdcard/nv14/THEMES/Darkblue/topmenu_opentx.bmp new file mode 100644 index 000000000..cbc7f8149 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/topmenu_opentx.bmp differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/trackp_background.png b/radio/sdcard/nv14/THEMES/Darkblue/trackp_background.png new file mode 100644 index 000000000..5beada59e Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/trackp_background.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/wizard/background.png b/radio/sdcard/nv14/THEMES/Darkblue/wizard/background.png new file mode 100644 index 000000000..6b31b2914 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/wizard/background.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/wizard/itembg.png b/radio/sdcard/nv14/THEMES/Darkblue/wizard/itembg.png new file mode 100644 index 000000000..b899446b2 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/wizard/itembg.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/wizard/pgdn.png b/radio/sdcard/nv14/THEMES/Darkblue/wizard/pgdn.png new file mode 100644 index 000000000..6a1377620 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/wizard/pgdn.png differ diff --git a/radio/sdcard/nv14/THEMES/Darkblue/wizard/pgup.png b/radio/sdcard/nv14/THEMES/Darkblue/wizard/pgup.png new file mode 100644 index 000000000..06fab5cac Binary files /dev/null and b/radio/sdcard/nv14/THEMES/Darkblue/wizard/pgup.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/NV14.bmp b/radio/sdcard/nv14/THEMES/FlySky/NV14.bmp new file mode 100644 index 000000000..4d64ac89e Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/NV14.bmp differ diff --git a/radio/sdcard/horus/THEMES/Default/X10.bmp b/radio/sdcard/nv14/THEMES/FlySky/X10.bmp similarity index 100% rename from radio/sdcard/horus/THEMES/Default/X10.bmp rename to radio/sdcard/nv14/THEMES/FlySky/X10.bmp diff --git a/radio/sdcard/horus/THEMES/Default/X10S.bmp b/radio/sdcard/nv14/THEMES/FlySky/X10S.bmp similarity index 100% rename from radio/sdcard/horus/THEMES/Default/X10S.bmp rename to radio/sdcard/nv14/THEMES/FlySky/X10S.bmp diff --git a/radio/sdcard/nv14/THEMES/FlySky/asterisk.bmp b/radio/sdcard/nv14/THEMES/FlySky/asterisk.bmp new file mode 100644 index 000000000..411c60c5d Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/asterisk.bmp differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/background.png b/radio/sdcard/nv14/THEMES/FlySky/background.png new file mode 100644 index 000000000..09b2dcc08 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/background.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/busy.bmp b/radio/sdcard/nv14/THEMES/FlySky/busy.bmp new file mode 100644 index 000000000..d68df82e4 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/busy.bmp differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/horus.bmp b/radio/sdcard/nv14/THEMES/FlySky/horus.bmp new file mode 100644 index 000000000..98bc631cd Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/horus.bmp differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_back.png b/radio/sdcard/nv14/THEMES/FlySky/mask_back.png new file mode 100755 index 000000000..ffd31b820 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_back.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_currentmenu_bg.png b/radio/sdcard/nv14/THEMES/FlySky/mask_currentmenu_bg.png new file mode 100644 index 000000000..715792023 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_currentmenu_bg.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_currentmenu_dot.png b/radio/sdcard/nv14/THEMES/FlySky/mask_currentmenu_dot.png new file mode 100644 index 000000000..6f76965a9 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_currentmenu_dot.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_currentmenu_shadow.png b/radio/sdcard/nv14/THEMES/FlySky/mask_currentmenu_shadow.png new file mode 100644 index 000000000..97bf6c577 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_currentmenu_shadow.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_menu_model.png b/radio/sdcard/nv14/THEMES/FlySky/mask_menu_model.png new file mode 100644 index 000000000..49c390c93 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_menu_model.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_menu_radio.png b/radio/sdcard/nv14/THEMES/FlySky/mask_menu_radio.png new file mode 100644 index 000000000..3b67ac8d4 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_menu_radio.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_menu_stats.png b/radio/sdcard/nv14/THEMES/FlySky/mask_menu_stats.png new file mode 100644 index 000000000..ebd482588 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_menu_stats.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_menu_theme.png b/radio/sdcard/nv14/THEMES/FlySky/mask_menu_theme.png new file mode 100644 index 000000000..3a9fc15e7 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_menu_theme.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_model_curves.png b/radio/sdcard/nv14/THEMES/FlySky/mask_model_curves.png new file mode 100644 index 000000000..6e1e19e8e Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_model_curves.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_model_flight_modes.png b/radio/sdcard/nv14/THEMES/FlySky/mask_model_flight_modes.png new file mode 100644 index 000000000..e963f0abb Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_model_flight_modes.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_model_gvars.png b/radio/sdcard/nv14/THEMES/FlySky/mask_model_gvars.png new file mode 100644 index 000000000..0b5015dac Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_model_gvars.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_model_heli.png b/radio/sdcard/nv14/THEMES/FlySky/mask_model_heli.png new file mode 100644 index 000000000..ecd93d2ff Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_model_heli.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_model_inputs.png b/radio/sdcard/nv14/THEMES/FlySky/mask_model_inputs.png new file mode 100644 index 000000000..720099c47 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_model_inputs.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_model_logical_switches.png b/radio/sdcard/nv14/THEMES/FlySky/mask_model_logical_switches.png new file mode 100644 index 000000000..5cd1e3834 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_model_logical_switches.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_model_lua_scripts.png b/radio/sdcard/nv14/THEMES/FlySky/mask_model_lua_scripts.png new file mode 100644 index 000000000..a20c1ccbd Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_model_lua_scripts.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_model_mixer.png b/radio/sdcard/nv14/THEMES/FlySky/mask_model_mixer.png new file mode 100644 index 000000000..009b360f1 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_model_mixer.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_model_outputs.png b/radio/sdcard/nv14/THEMES/FlySky/mask_model_outputs.png new file mode 100644 index 000000000..a209ae8d3 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_model_outputs.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_model_setup.png b/radio/sdcard/nv14/THEMES/FlySky/mask_model_setup.png new file mode 100644 index 000000000..afe526c8f Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_model_setup.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_model_special_functions.png b/radio/sdcard/nv14/THEMES/FlySky/mask_model_special_functions.png new file mode 100644 index 000000000..a12a2a50d Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_model_special_functions.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_model_telemetry.png b/radio/sdcard/nv14/THEMES/FlySky/mask_model_telemetry.png new file mode 100644 index 000000000..d95d3b25f Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_model_telemetry.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_modelselect.png b/radio/sdcard/nv14/THEMES/FlySky/mask_modelselect.png new file mode 100644 index 000000000..237883fc5 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_modelselect.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_monitor.png b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor.png new file mode 100644 index 000000000..68ddd4516 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_channels1.png b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_channels1.png new file mode 100644 index 000000000..b886fc1bc Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_channels1.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_channels2.png b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_channels2.png new file mode 100644 index 000000000..4d0354e25 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_channels2.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_channels3.png b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_channels3.png new file mode 100644 index 000000000..d9ba27de7 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_channels3.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_channels4.png b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_channels4.png new file mode 100644 index 000000000..fb321151f Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_channels4.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_gvars.png b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_gvars.png new file mode 100644 index 000000000..55b488d1e Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_gvars.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_inver.png b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_inver.png new file mode 100644 index 000000000..64f251238 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_inver.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_lockch.png b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_lockch.png new file mode 100644 index 000000000..360923a09 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_lockch.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_logsw.png b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_logsw.png new file mode 100644 index 000000000..dc6355143 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_logsw.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_mixes1.png b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_mixes1.png new file mode 100644 index 000000000..7b0c71f60 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_mixes1.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_mixes2.png b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_mixes2.png new file mode 100644 index 000000000..f8c04780e Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_monitor_mixes2.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_mplex_add.png b/radio/sdcard/nv14/THEMES/FlySky/mask_mplex_add.png new file mode 100644 index 000000000..14f4ecc23 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_mplex_add.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_mplex_multi.png b/radio/sdcard/nv14/THEMES/FlySky/mask_mplex_multi.png new file mode 100644 index 000000000..bcf79695f Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_mplex_multi.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_mplex_replace.png b/radio/sdcard/nv14/THEMES/FlySky/mask_mplex_replace.png new file mode 100644 index 000000000..faccba0ea Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_mplex_replace.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_next.png b/radio/sdcard/nv14/THEMES/FlySky/mask_next.png new file mode 100644 index 000000000..848019e8e Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_next.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_opentx.png b/radio/sdcard/nv14/THEMES/FlySky/mask_opentx.png new file mode 100644 index 000000000..df2b0b356 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_opentx.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_opentx_testmode.png b/radio/sdcard/nv14/THEMES/FlySky/mask_opentx_testmode.png new file mode 100644 index 000000000..b535cf8ee Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_opentx_testmode.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_radio_calibration.png b/radio/sdcard/nv14/THEMES/FlySky/mask_radio_calibration.png new file mode 100644 index 000000000..db696778b Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_radio_calibration.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_radio_global_functions.png b/radio/sdcard/nv14/THEMES/FlySky/mask_radio_global_functions.png new file mode 100644 index 000000000..3b3efefb5 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_radio_global_functions.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_radio_hardware.png b/radio/sdcard/nv14/THEMES/FlySky/mask_radio_hardware.png new file mode 100644 index 000000000..f51a1aee1 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_radio_hardware.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_radio_sd_browser.png b/radio/sdcard/nv14/THEMES/FlySky/mask_radio_sd_browser.png new file mode 100644 index 000000000..75b6a9296 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_radio_sd_browser.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_radio_setup.png b/radio/sdcard/nv14/THEMES/FlySky/mask_radio_setup.png new file mode 100644 index 000000000..0010a06ab Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_radio_setup.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_radio_tools.png b/radio/sdcard/nv14/THEMES/FlySky/mask_radio_tools.png new file mode 100755 index 000000000..711d499de Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_radio_tools.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_radio_trainer.png b/radio/sdcard/nv14/THEMES/FlySky/mask_radio_trainer.png new file mode 100644 index 000000000..7a0b56970 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_radio_trainer.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_radio_version.png b/radio/sdcard/nv14/THEMES/FlySky/mask_radio_version.png new file mode 100644 index 000000000..8730f6c1a Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_radio_version.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_sbar_mixer.png b/radio/sdcard/nv14/THEMES/FlySky/mask_sbar_mixer.png new file mode 100644 index 000000000..154406419 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_sbar_mixer.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_sbar_output.png b/radio/sdcard/nv14/THEMES/FlySky/mask_sbar_output.png new file mode 100644 index 000000000..12d599823 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_sbar_output.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_sbar_to.png b/radio/sdcard/nv14/THEMES/FlySky/mask_sbar_to.png new file mode 100644 index 000000000..813610307 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_sbar_to.png differ diff --git a/radio/src/bitmaps/480x272/mask_shutdown_circle.png b/radio/sdcard/nv14/THEMES/FlySky/mask_shutdown_circle.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_shutdown_circle.png rename to radio/sdcard/nv14/THEMES/FlySky/mask_shutdown_circle.png diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_stats_analogs.png b/radio/sdcard/nv14/THEMES/FlySky/mask_stats_analogs.png new file mode 100644 index 000000000..96fc0c8a2 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_stats_analogs.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_stats_debug.png b/radio/sdcard/nv14/THEMES/FlySky/mask_stats_debug.png new file mode 100644 index 000000000..0dce13d46 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_stats_debug.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_stats_lua.png b/radio/sdcard/nv14/THEMES/FlySky/mask_stats_lua.png new file mode 100644 index 000000000..d1b54a4d6 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_stats_lua.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_stats_throttle_graph.png b/radio/sdcard/nv14/THEMES/FlySky/mask_stats_throttle_graph.png new file mode 100644 index 000000000..a3720a005 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_stats_throttle_graph.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_stats_timers.png b/radio/sdcard/nv14/THEMES/FlySky/mask_stats_timers.png new file mode 100644 index 000000000..78bf00064 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_stats_timers.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_stats_value.png b/radio/sdcard/nv14/THEMES/FlySky/mask_stats_value.png new file mode 100644 index 000000000..180f16e8c Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_stats_value.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_switches.png b/radio/sdcard/nv14/THEMES/FlySky/mask_switches.png new file mode 100644 index 000000000..4097ed751 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_switches.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_textline_curve.png b/radio/sdcard/nv14/THEMES/FlySky/mask_textline_curve.png new file mode 100644 index 000000000..050229393 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_textline_curve.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_textline_delay.png b/radio/sdcard/nv14/THEMES/FlySky/mask_textline_delay.png new file mode 100644 index 000000000..9b771a94a Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_textline_delay.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_textline_delayslow.png b/radio/sdcard/nv14/THEMES/FlySky/mask_textline_delayslow.png new file mode 100644 index 000000000..de2f7d1f7 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_textline_delayslow.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_textline_fm.png b/radio/sdcard/nv14/THEMES/FlySky/mask_textline_fm.png new file mode 100644 index 000000000..b9a2c4a32 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_textline_fm.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_textline_label.png b/radio/sdcard/nv14/THEMES/FlySky/mask_textline_label.png new file mode 100644 index 000000000..8be12e39b Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_textline_label.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_textline_slow.png b/radio/sdcard/nv14/THEMES/FlySky/mask_textline_slow.png new file mode 100644 index 000000000..1afbea37d Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_textline_slow.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_textline_switch.png b/radio/sdcard/nv14/THEMES/FlySky/mask_textline_switch.png new file mode 100644 index 000000000..7c7d646e4 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_textline_switch.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_theme_add_view.png b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_add_view.png new file mode 100644 index 000000000..7e240372f Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_add_view.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_theme_setup.png b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_setup.png new file mode 100644 index 000000000..2a39d1e40 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_setup.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view1.png b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view1.png new file mode 100644 index 000000000..9e67515ec Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view1.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view2.png b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view2.png new file mode 100644 index 000000000..45cec234d Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view2.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view3.png b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view3.png new file mode 100644 index 000000000..e5a776b06 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view3.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view4.png b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view4.png new file mode 100644 index 000000000..9064b9403 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view4.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view5.png b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view5.png new file mode 100644 index 000000000..724bcc141 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view5.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view6.png b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view6.png new file mode 100644 index 000000000..2aa43514b Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view6.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view7.png b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view7.png new file mode 100644 index 000000000..d6a0540f5 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view7.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view8.png b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view8.png new file mode 100644 index 000000000..82dfd7875 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/mask_theme_view8.png differ diff --git a/radio/src/bitmaps/480x272/mask_timer.png b/radio/sdcard/nv14/THEMES/FlySky/mask_timer.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_timer.png rename to radio/sdcard/nv14/THEMES/FlySky/mask_timer.png diff --git a/radio/src/bitmaps/480x272/mask_timer_bg.png b/radio/sdcard/nv14/THEMES/FlySky/mask_timer_bg.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_timer_bg.png rename to radio/sdcard/nv14/THEMES/FlySky/mask_timer_bg.png diff --git a/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_boat1.png b/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_boat1.png new file mode 100644 index 000000000..a9335c4bc Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_boat1.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_boat2.png b/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_boat2.png new file mode 100644 index 000000000..e3c899d18 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_boat2.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_default.png b/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_default.png new file mode 100644 index 000000000..6f3042c67 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_default.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_favorites.png b/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_favorites.png new file mode 100644 index 000000000..e4a37dacb Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_favorites.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_plane1.png b/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_plane1.png new file mode 100644 index 000000000..a49a50271 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_plane1.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_plane2.png b/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_plane2.png new file mode 100644 index 000000000..8305b4150 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_plane2.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_plane3.png b/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_plane3.png new file mode 100644 index 000000000..d998144b2 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/modelsel/icon_plane3.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/modelsel/mask_iconback.png b/radio/sdcard/nv14/THEMES/FlySky/modelsel/mask_iconback.png new file mode 100644 index 000000000..6f19c3ee0 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/modelsel/mask_iconback.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/modelsel/mask_modelname.png b/radio/sdcard/nv14/THEMES/FlySky/modelsel/mask_modelname.png new file mode 100644 index 000000000..2edca73ea Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/modelsel/mask_modelname.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/modelsel/mask_modelqty.png b/radio/sdcard/nv14/THEMES/FlySky/modelsel/mask_modelqty.png new file mode 100644 index 000000000..9a38e4f13 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/modelsel/mask_modelqty.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/modelsel/mask_moveback.png b/radio/sdcard/nv14/THEMES/FlySky/modelsel/mask_moveback.png new file mode 100644 index 000000000..63f56df92 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/modelsel/mask_moveback.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/modelsel/mask_moveico.png b/radio/sdcard/nv14/THEMES/FlySky/modelsel/mask_moveico.png new file mode 100644 index 000000000..3662b9bb0 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/modelsel/mask_moveico.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/modelsel/mask_sdfree.png b/radio/sdcard/nv14/THEMES/FlySky/modelsel/mask_sdfree.png new file mode 100644 index 000000000..f0ae81982 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/modelsel/mask_sdfree.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/question.bmp b/radio/sdcard/nv14/THEMES/FlySky/question.bmp new file mode 100644 index 000000000..79792574b Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/question.bmp differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/shutdown.bmp b/radio/sdcard/nv14/THEMES/FlySky/shutdown.bmp new file mode 100644 index 000000000..be7034db7 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/shutdown.bmp differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/sleep.bmp b/radio/sdcard/nv14/THEMES/FlySky/sleep.bmp new file mode 100644 index 000000000..279a9b855 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/sleep.bmp differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/stick_background.png b/radio/sdcard/nv14/THEMES/FlySky/stick_background.png new file mode 100644 index 000000000..9f6d53d92 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/stick_background.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/stick_pointer.png b/radio/sdcard/nv14/THEMES/FlySky/stick_pointer.png new file mode 100644 index 000000000..0d75765f9 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/stick_pointer.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/thumb.bmp b/radio/sdcard/nv14/THEMES/FlySky/thumb.bmp new file mode 100644 index 000000000..37de4998e Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/thumb.bmp differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/topleft.png b/radio/sdcard/nv14/THEMES/FlySky/topleft.png new file mode 100644 index 000000000..603bfe1cf Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/topleft.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/trackp_background.png b/radio/sdcard/nv14/THEMES/FlySky/trackp_background.png new file mode 100644 index 000000000..5beada59e Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/trackp_background.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/wizard/background.png b/radio/sdcard/nv14/THEMES/FlySky/wizard/background.png new file mode 100644 index 000000000..6b31b2914 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/wizard/background.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/wizard/itembg.png b/radio/sdcard/nv14/THEMES/FlySky/wizard/itembg.png new file mode 100644 index 000000000..b899446b2 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/wizard/itembg.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/wizard/pgdn.png b/radio/sdcard/nv14/THEMES/FlySky/wizard/pgdn.png new file mode 100644 index 000000000..6a1377620 Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/wizard/pgdn.png differ diff --git a/radio/sdcard/nv14/THEMES/FlySky/wizard/pgup.png b/radio/sdcard/nv14/THEMES/FlySky/wizard/pgup.png new file mode 100644 index 000000000..06fab5cac Binary files /dev/null and b/radio/sdcard/nv14/THEMES/FlySky/wizard/pgup.png differ diff --git a/radio/sdcard/nv14/WIDGETS/BattCheck/main.lua b/radio/sdcard/nv14/WIDGETS/BattCheck/main.lua new file mode 100755 index 000000000..11edfe67d --- /dev/null +++ b/radio/sdcard/nv14/WIDGETS/BattCheck/main.lua @@ -0,0 +1,401 @@ +---- ######################################################################### +---- # # +---- # Telemetry Widget script for FrSky Horus # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### + +local shadowed = 0 + +local options = { + { "Sensor", SOURCE, 0 }, + { "Color", COLOR, WHITE }, + { "Shadow", BOOL, 0 }, + { "LowestCell", BOOL, 0 } +} + +-- This function is runned once at the creation of the widget +local function create(zone, options) + local myZone = { zone=zone, options=options, counter=0 } + histCellData = {} + + -- use default if user did not set, So widget is operational when selecting the widget + if myZone.options.Sensor == 0 then + myZone.options.Sensor = "Cels" + end + + return myZone +end + +-- This function allow updates when you change widgets settings +local function update(myZone, options) + myZone.options = options + + -- use default if user did not set, So widget is operational when selecting the widget + if myZone.options.Sensor == 0 then + myZone.options.Sensor = "Cels" + end + +end + +-- A quick and dirty check for empty table +local function isEmpty(self) + for _, _ in pairs(self) do + return false + end + return true +end + +--- This function returns a table with cels values +local function getCels(sensor) + liveCellData = getValue(sensor) + if type(liveCellData) == "table" then + -- initialize historical table if not done yet + if isEmpty(histCellData) then + for k, v in pairs(liveCellData) do + histCellData[k] = v + end + end + -- this is necessary for simu where cellcount can change + if #histCellData ~= #liveCellData then + for k, v in pairs(liveCellData) do + histCellData[k] = v + end + end + -- stores the lowest cell values in historical table + for k, v in pairs(liveCellData) do + if v < histCellData[k] then histCellData[k] = v end + end + else + -- erase low cell memory when you change lipo + histCellData = {} + end + return liveCellData +end + +--- This function returns the number of cels +local function getCellCount(cellData) + return #cellData +end + +--- This function returns the value of the highest of all cels +local function getCellMax(cellData) + cellMax = 0 + if type(cellData) == "table" then + for k, v in pairs(cellData) do + if v > cellMax then cellMax = v end + end + end + return cellMax +end + +--- This function returns the value of the lowest of all cels +local function getCellMin(cellData) + cellMin = 5 + if type(cellData) == "table" then + for k, v in pairs(cellData) do + if v < cellMin then cellMin = v end + end + end + return cellMin +end + +--- This function return the percentage remaining in a single Lipo cel +local function getCellPercent(cellValue) + if cellValue == nil then + return 0 + end + --## Data gathered from commercial lipo sensors + local myArrayPercentList = +{{3, 0}, {3.093, 1}, {3.196, 2}, {3.301, 3}, {3.401, 4}, {3.477, 5}, {3.544, 6}, {3.601, 7}, {3.637, 8}, {3.664, 9}, {3.679, 10}, {3.683, 11}, {3.689, 12}, {3.692, 13}, {3.705, 14}, {3.71, 15}, {3.713, 16}, {3.715, 17}, {3.72, 18}, {3.731, 19}, {3.735, 20}, {3.744, 21}, {3.753, 22}, {3.756, 23}, {3.758, 24}, {3.762, 25}, {3.767, 26}, {3.774, 27}, {3.78, 28}, {3.783, 29}, {3.786, 30}, {3.789, 31}, {3.794, 32}, {3.797, 33}, {3.8, 34}, {3.802, 35}, {3.805, 36}, {3.808, 37}, {3.811, 38}, {3.815, 39}, {3.818, 40}, {3.822, 41}, {3.825, 42}, {3.829, 43}, {3.833, 44}, {3.836, 45}, {3.84, 46}, {3.843, 47}, {3.847, 48}, {3.85, 49}, {3.854, 50}, {3.857, 51}, {3.86, 52}, {3.863, 53}, {3.866, 54}, {3.87, 55}, {3.874, 56}, {3.879, 57}, {3.888, 58}, {3.893, 59}, {3.897, 60}, {3.902, 61}, {3.906, 62}, {3.911, 63}, {3.918, 64}, {3.923, 65}, {3.928, 66}, {3.939, 67}, {3.943, 68}, {3.949, 69}, {3.955, 70}, {3.961, 71}, {3.968, 72}, {3.974, 73}, {3.981, 74}, {3.987, 75}, {3.994, 76}, {4.001, 77}, {4.007, 78}, {4.014, 79}, {4.021, 80}, {4.029, 81}, {4.036, 82}, {4.044, 83}, {4.052, 84}, {4.062, 85}, {4.074, 86}, {4.085, 87}, {4.095, 88}, {4.105, 89}, {4.111, 90}, {4.116, 91}, {4.12, 92}, {4.125, 93}, {4.129, 94}, {4.135, 95}, {4.145, 96}, {4.176, 97}, {4.179, 98}, {4.193, 99}, {4.2, 100}} + + for i, v in ipairs( myArrayPercentList ) do + if v[ 1 ] >= cellValue then + result = v[ 2 ] + break + end + end + return result +end + +--- This function parse each individual cell and return the sum of all cels +local function getCellSum(cellData) + cellSum = 0 + if type(cellData) == "table" then + for k, v in pairs(cellData) do cellSum = cellSum + v end + end + return cellSum +end + +--- This function returns the average of all cels +local function getCellAvg(cellData) + if type(cellData) == "table" then + return getCellSum(cellData)/getCellCount(cellData) + end +end + +-- This function returns green at 100%, red bellow 30% and graduate in between +-- color for battery +local function getPercentColor(cpercent) + if cpercent < 30 then + return lcd.RGB(0xff, 0, 0) + else + g = math.floor(0xdf * cpercent / 100) + r = 0xdf - g + return lcd.RGB(r, g, 0) + end +end + +-- This function returns green at gvalue, red at rvalue and graduate in between +-- color for cell +local function getRangeColor(value, gvalue, rvalue) + if gvalue > rvalue and not range==0 then + range = gvalue - rvalue + if value > gvalue then return lcd.RGB(0, 0xdf, 0) end + if value < rvalue then return lcd.RGB(0xdf, 0, 0) end + g = math.floor(0xdf * (value-rvalue) / range) + r = 0xdf - g + return lcd.RGB(r, g, 0) + else + range = rvalue - gvalue + if value > gvalue then return lcd.RGB(0, 0xdf, 0) end + if value < rvalue then return lcd.RGB(0xdf, 0, 0) end + r = math.floor(0xdf * (value-gvalue) / range) + g = 0xdf - r + return lcd.RGB(r, g, 0) + end +end + +local function getMainValue(mySensor, options) + if options.LowestCell == 1 then + return getCellMin(mySensor) + else + return getCellSum(mySensor) + end +end + +local function getSecondaryValue(mySensor, options) + if options.LowestCell == 1 then + return getCellSum(mySensor) + else + return getCellMin(mySensor) + end +end + + +-- This size is for top bar widgets +local function refreshZoneTiny(zone) + local mySensor = getCels(zone.options.Sensor) + local isHaveData = (type(mySensor) == "table") + if isHaveData then + local myString = string.format("%2.1fV", getMainValue(mySensor, zone.options)) + local percent = getCellPercent(getCellAvg(mySensor)) + lcd.drawText(zone.zone.x + zone.zone.w, zone.zone.y, percent.."%", RIGHT + SMLSIZE + CUSTOM_COLOR) + lcd.drawText(zone.zone.x + zone.zone.w, zone.zone.y + 15, myString, RIGHT + SMLSIZE + CUSTOM_COLOR) + -- draw batt + lcd.drawRectangle(zone.zone.x, zone.zone.y + 6, 16, 25, CUSTOM_COLOR, 2) + lcd.drawFilledRectangle(zone.zone.x + 4, zone.zone.y + 4, 6, 3, CUSTOM_COLOR) + local rect_h = math.floor(25 * percent / 100) + lcd.drawFilledRectangle(zone.zone.x, zone.zone.y + 6 + 25 - rect_h , 16, rect_h, CUSTOM_COLOR) + end +end + +--- Size is 160x32 1/8th +local function refreshZoneSmall(zone) + local myBatt = {["x"]=0, ["y"]=0, ["w"]=155, ["h"]=35, ["segments_w"]=25, ["color"]=WHITE, ["cath_w"]=6, ["cath_h"]=20} + local mySensor = getCels(zone.options.Sensor) + local isDataAvailable = (type(mySensor) == "table") + + local percent = getCellPercent(getCellAvg(mySensor)) + + -- draws bat + lcd.setColor(CUSTOM_COLOR, WHITE) + lcd.drawRectangle(zone.zone.x + myBatt.x, zone.zone.y + myBatt.y, myBatt.w, myBatt.h, CUSTOM_COLOR, 2) + lcd.drawFilledRectangle(zone.zone.x + myBatt.x + myBatt.w, zone.zone.y + myBatt.h/2 - myBatt.cath_h/2, myBatt.cath_w, myBatt.cath_h, CUSTOM_COLOR) + + -- fils batt + lcd.setColor(CUSTOM_COLOR, getPercentColor(percent)) + lcd.drawGauge(zone.zone.x+2, zone.zone.y+2, myBatt.w-4, zone.zone.h, percent, 100, CUSTOM_COLOR) + -- write text + if isDataAvailable then + lcd.setColor(CUSTOM_COLOR, zone.options.Color) + local topLine = string.format("%2.1fV %2.0f%%", getMainValue(mySensor, zone.options), percent) + lcd.drawText(zone.zone.x + 20, zone.zone.y +2, topLine , MIDSIZE + CUSTOM_COLOR + shadowed) + else + lcd.drawText(zone.zone.x + 10, zone.zone.y+10, "No Cells Data", LEFT + SMLSIZE + INVERS + CUSTOM_COLOR + shadowed) + end + + + return +end + +--- Size is 225x98 1/4th (no sliders/trim) +local function refreshZoneMedium(zone) + local myBatt = {["x"]=0, ["y"]=0, ["w"]=85, ["h"]=35, ["segments_w"]=15, ["color"]=WHITE, ["cath_w"]=6, ["cath_h"]=20} + local mySensor = getCels(zone.options.Sensor) + local isDataAvailable = (type(mySensor) == "table") + + lcd.setColor(CUSTOM_COLOR, zone.options.Color) + if isDataAvailable then + -- draw values + local percent = getCellPercent(getCellAvg(mySensor)) + lcd.drawText(zone.zone.x + zone.zone.w, zone.zone.y, string.format("%2.1fV", getMainValue(mySensor, zone.options)) , DBLSIZE + CUSTOM_COLOR + RIGHT+ shadowed) + + -- fils batt + lcd.setColor(CUSTOM_COLOR, getPercentColor(percent)) + lcd.drawGauge(zone.zone.x + myBatt.x, zone.zone.y + myBatt.y, myBatt.w, myBatt.h, percent, 100, CUSTOM_COLOR) + -- draw cells + local pos = {{x=2, y=38}, {x=60, y=38}, {x=118, y=38}, {x=2, y=57}, {x=60, y=57}, {x=118, y=57}} + for i=1, getCellCount(mySensor), 1 do + lcd.setColor(CUSTOM_COLOR, getRangeColor(mySensor[i], getCellMax(mySensor), getCellMax(mySensor) - 0.2)) + lcd.drawFilledRectangle(zone.zone.x + pos[i].x, zone.zone.y + pos[i].y, 58, 20, CUSTOM_COLOR) + lcd.setColor(CUSTOM_COLOR, WHITE) + lcd.drawText(zone.zone.x + pos[i].x+10, zone.zone.y + pos[i].y, string.format("%.2f", mySensor[i]), CUSTOM_COLOR + shadowed) + lcd.drawRectangle(zone.zone.x + pos[i].x, zone.zone.y + pos[i].y, 59, 20, CUSTOM_COLOR,1) + end + else + lcd.drawText(zone.zone.x, zone.zone.y+35, "No Cells Data", LEFT + SMLSIZE + INVERS + CUSTOM_COLOR) + end + -- draws bat + lcd.setColor(CUSTOM_COLOR, WHITE) + lcd.drawRectangle(zone.zone.x + myBatt.x , zone.zone.y + myBatt.y, myBatt.w, myBatt.h, CUSTOM_COLOR, 2) + lcd.drawFilledRectangle(zone.zone.x + myBatt.x + myBatt.w, zone.zone.y + myBatt.h/2 - myBatt.cath_h/2, myBatt.cath_w, myBatt.cath_h, CUSTOM_COLOR) + lcd.drawText(zone.zone.x + myBatt.x + 20 , zone.zone.y + myBatt.y + 5, string.format("%2.0f%%", getCellPercent(getCellAvg(mySensor))), LEFT + MIDSIZE + CUSTOM_COLOR + shadowed) + --for i=1, myBatt.w - myBatt.segments_w, myBatt.segments_w do + -- lcd.drawRectangle(zone.zone.x + myBatt.x + i, zone.zone.y + myBatt.y, myBatt.segments_w, myBatt.h, CUSTOM_COLOR, 1) + --end + return +end + +--- Size is 192x152 1/2 +local function refreshZoneLarge(zone) + local myBatt = {["x"]=0, ["y"]=18, ["w"]=76, ["h"]=121, ["segments_h"]=30, ["color"]=WHITE, ["cath_w"]=30, ["cath_h"]=10} + local mySensor = getCels(zone.options.Sensor) + local isDataAvailable = (type(mySensor) == "table") + + lcd.setColor(CUSTOM_COLOR, zone.options.Color) + if isDataAvailable then + local percent = getCellPercent(getCellAvg(mySensor)) + + lcd.drawText(zone.zone.x+zone.zone.w, zone.zone.y, percent.."%", RIGHT + DBLSIZE + CUSTOM_COLOR + shadowed) + lcd.drawText(zone.zone.x+zone.zone.w, zone.zone.y+30, string.format("%2.1fV", getMainValue(mySensor, zone.options)), RIGHT + DBLSIZE + CUSTOM_COLOR + shadowed) + lcd.drawText(zone.zone.x+zone.zone.w, zone.zone.y+70, string.format("%2.1fV %2.1fS", getSecondaryValue(mySensor, options), getCellCount(mySensor)), RIGHT + SMLSIZE + CUSTOM_COLOR + shadowed) + -- fils batt + lcd.setColor(CUSTOM_COLOR, getPercentColor(percent)) + lcd.drawFilledRectangle(zone.zone.x + myBatt.x, zone.zone.y + myBatt.y + myBatt.h + myBatt.cath_h - math.floor(percent/100 * myBatt.h), myBatt.w, math.floor(percent/100 * myBatt.h), CUSTOM_COLOR) + -- draw cells + local pos = {{x=80, y=90}, {x=138, y=90}, {x=80, y=109}, {x=138, y=109}, {x=80, y=128}, {x=138, y=128}} + for i=1, getCellCount(mySensor), 1 do + lcd.setColor(CUSTOM_COLOR, getRangeColor(mySensor[i], getCellMax(mySensor), getCellMax(mySensor) - 0.2)) + lcd.drawFilledRectangle(zone.zone.x + pos[i].x, zone.zone.y + pos[i].y, 58, 20, CUSTOM_COLOR) + lcd.setColor(CUSTOM_COLOR, WHITE) + lcd.drawText(zone.zone.x + pos[i].x+10, zone.zone.y + pos[i].y, string.format("%.2f", mySensor[i]), CUSTOM_COLOR + shadowed) + lcd.drawRectangle(zone.zone.x + pos[i].x, zone.zone.y + pos[i].y, 59, 20, CUSTOM_COLOR, 1) + end + else + lcd.drawText(zone.zone.x+5, zone.zone.y, "No Cells Data", LEFT + SMLSIZE + INVERS + CUSTOM_COLOR + shadowed) + end + -- draws bat + lcd.setColor(CUSTOM_COLOR, WHITE) + lcd.drawRectangle(zone.zone.x + myBatt.x, zone.zone.y + myBatt.y + myBatt.cath_h , myBatt.w, myBatt.h, CUSTOM_COLOR, 2) + lcd.drawFilledRectangle(zone.zone.x + myBatt.x + myBatt.w/2 - myBatt.cath_w/2, zone.zone.y + myBatt.y, myBatt.cath_w, myBatt.cath_h, CUSTOM_COLOR) + for i=1, myBatt.h - myBatt.segments_h, myBatt.segments_h do + lcd.drawRectangle(zone.zone.x + myBatt.x, zone.zone.y + myBatt.y + myBatt.cath_h + i, myBatt.w, myBatt.segments_h, CUSTOM_COLOR, 1) + end + return +end + +--- Size is 390x172 1/1 +--- Size is 460x252 1/1 (no sliders/trim/topbar) +local function refreshZoneXLarge(zone) + local myBatt = {["x"]=10, ["y"]=20, ["w"]=80, ["h"]=121, ["segments_h"]=30, ["color"]=WHITE, ["cath_w"]=30, ["cath_h"]=10} + local mySensor = getCels(zone.options.Sensor) + local isDataAvailable = (type(mySensor) == "table") + + lcd.setColor(CUSTOM_COLOR, zone.options.Color) + if isDataAvailable then + local percent = getCellPercent(getCellAvg(mySensor)) + -- fils batt + lcd.setColor(CUSTOM_COLOR, getPercentColor(percent)) + lcd.drawFilledRectangle(zone.zone.x + myBatt.x, zone.zone.y + myBatt.y + myBatt.h + myBatt.cath_h - math.floor(percent/100 * myBatt.h), myBatt.w, math.floor(percent/100 * myBatt.h), CUSTOM_COLOR) + -- draw right text section + lcd.setColor(CUSTOM_COLOR, zone.options.Color) + lcd.drawText(zone.zone.x+zone.zone.w, zone.zone.y + myBatt.y, percent.."%", RIGHT + DBLSIZE + CUSTOM_COLOR + shadowed) + + lcd.drawText(zone.zone.x+zone.zone.w, zone.zone.y + myBatt.y + 30, string.format("%2.1fV", getMainValue(mySensor, zone.options)), RIGHT + DBLSIZE + CUSTOM_COLOR + shadowed) + lcd.drawText(zone.zone.x+zone.zone.w, zone.zone.y + myBatt.y + 105, string.format("%2.1fV %2.1fS", getSecondaryValue(mySensor, zone.options), getCellCount(mySensor)) , RIGHT + SMLSIZE + CUSTOM_COLOR + shadowed) + + -- draw cells + local pos = {{x=111, y=38}, {x=164, y=38}, {x=217, y=38}, {x=111, y=57}, {x=164, y=57}, {x=217, y=57}} + for i=1, getCellCount(mySensor), 1 do + lcd.setColor(CUSTOM_COLOR, getRangeColor(mySensor[i], getCellMax(mySensor), getCellMax(mySensor) - 0.2)) + lcd.drawFilledRectangle(zone.zone.x + pos[i].x, zone.zone.y + pos[i].y, 53, 20, CUSTOM_COLOR) + lcd.setColor(CUSTOM_COLOR, WHITE) + lcd.drawText(zone.zone.x + pos[i].x+10, zone.zone.y + pos[i].y, string.format("%.2f", mySensor[i]), CUSTOM_COLOR + shadowed) + lcd.drawRectangle(zone.zone.x + pos[i].x, zone.zone.y + pos[i].y, 54, 20, CUSTOM_COLOR, 1) + end + -- draw cells for lowest cells + local pos = {{x=111, y=110}, {x=164, y=110}, {x=217, y=110}, {x=111, y=129}, {x=164, y=129}, {x=217, y=129}} + for i=1, getCellCount(mySensor), 1 do + lcd.setColor(CUSTOM_COLOR, getRangeColor(histCellData[i], mySensor[i], mySensor[i] - 0.3)) + lcd.drawFilledRectangle(zone.zone.x + pos[i].x, zone.zone.y + pos[i].y, 53, 20, CUSTOM_COLOR) + lcd.setColor(CUSTOM_COLOR, WHITE) + lcd.drawRectangle(zone.zone.x + pos[i].x, zone.zone.y + pos[i].y, 54, 20, CUSTOM_COLOR, 1) + lcd.drawText(zone.zone.x + pos[i].x+10, zone.zone.y + pos[i].y, string.format("%.2f", histCellData[i]), CUSTOM_COLOR + shadowed) + end + else + lcd.drawText(zone.zone.x+5, zone.zone.y, "No Cells Data", LEFT + SMLSIZE + INVERS + CUSTOM_COLOR) + end + -- draws bat + lcd.setColor(CUSTOM_COLOR, WHITE) + lcd.drawRectangle(zone.zone.x + myBatt.x, zone.zone.y + myBatt.y + myBatt.cath_h , myBatt.w, myBatt.h, CUSTOM_COLOR, 2) + lcd.drawFilledRectangle(zone.zone.x + myBatt.x + myBatt.w/2 - myBatt.cath_w/2, zone.zone.y + myBatt.y, myBatt.cath_w, myBatt.cath_h, CUSTOM_COLOR) + for i=1, myBatt.h - myBatt.segments_h, myBatt.segments_h do + lcd.drawRectangle(zone.zone.x + myBatt.x, zone.zone.y + myBatt.y + myBatt.cath_h + i, myBatt.w, myBatt.segments_h, CUSTOM_COLOR, 1) + end + -- draw middle rectangles + lcd.drawRectangle(zone.zone.x + 110, zone.zone.y + 38, 161, 40, CUSTOM_COLOR, 1) + lcd.drawText(zone.zone.x + 220, zone.zone.y + 21, "Live data", RIGHT + SMLSIZE + INVERS + CUSTOM_COLOR + shadowed) + lcd.drawRectangle(zone.zone.x + 110, zone.zone.y + 110, 161, 40, CUSTOM_COLOR, 1) + lcd.drawText(zone.zone.x + 230, zone.zone.y + 93, "Lowest data", RIGHT + SMLSIZE + INVERS + CUSTOM_COLOR + shadowed) + return +end + +-- This function allow recording of lowest cells when widget is not active +local function background(myZone) + getCels(myZone.options.Sensor) + return +end + +function refresh(myZone) + if myZone.options.Shadow == 1 then + shadowed = SHADOWED + else + shadowed = 0 + end + + --if myZone.options.Sensor == 1 then + -- lcd.drawText(myZone.zone.x+2, myZone.zone.y+2, "Not configured", LEFT + SMLSIZE + CUSTOM_COLOR) + -- lcd.drawText(myZone.zone.x+2, myZone.zone.y+20, "Requires setting a Voltage Sensor", LEFT + SMLSIZE + INVERS + CUSTOM_COLOR) + -- return + --end + if myZone.zone.w > 380 and myZone.zone.h > 165 then refreshZoneXLarge(myZone) + elseif myZone.zone.w > 180 and myZone.zone.h > 145 then refreshZoneLarge(myZone) + elseif myZone.zone.w > 170 and myZone.zone.h > 65 then refreshZoneMedium(myZone) + elseif myZone.zone.w > 150 and myZone.zone.h > 28 then refreshZoneSmall(myZone) + elseif myZone.zone.w > 65 and myZone.zone.h > 35 then refreshZoneTiny(myZone) + end +end + +return { name="BattCheck", options=options, create=create, update=update, background=background, refresh=refresh } diff --git a/radio/sdcard/nv14/WIDGETS/Counter/main.lua b/radio/sdcard/nv14/WIDGETS/Counter/main.lua new file mode 100644 index 000000000..49c89962e --- /dev/null +++ b/radio/sdcard/nv14/WIDGETS/Counter/main.lua @@ -0,0 +1,47 @@ +---- ######################################################################### +---- # # +---- # Copyright (C) OpenTX # +-----# # +---- # 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. # +---- # # +---- ######################################################################### +local options = { + { "Option1", SOURCE, 1 }, + { "Option2", VALUE, 1000 }, + { "Option3", COLOR, RED }, + { "Shadow", BOOL, 0 } +} + +local function create(zone, options) + local pie = { zone=zone, options=options, counter=0 } + print(options.Option2) + return pie +end + +local function update(pie, options) + pie.options = options +end + +local function background(pie) + pie.counter = pie.counter + 1 +end + +function refresh(pie) + pie.counter = pie.counter + 1 + if pie.options.Shadow == 1 then + lcd.drawNumber(pie.zone.x, pie.zone.y, pie.counter, LEFT + DBLSIZE + TEXT_COLOR); + else + lcd.drawNumber(pie.zone.x, pie.zone.y, pie.counter, LEFT + DBLSIZE + TEXT_COLOR + SHADOWED); + end +end + +return { name="Counter", options=options, create=create, update=update, refresh=refresh, background=background } diff --git a/radio/sdcard/nv14/WIDGETS/Timer2/main.lua b/radio/sdcard/nv14/WIDGETS/Timer2/main.lua new file mode 100644 index 000000000..6354eee4e --- /dev/null +++ b/radio/sdcard/nv14/WIDGETS/Timer2/main.lua @@ -0,0 +1,100 @@ +-- A Timer version that fill better the widget area +-- Offer Shmuely +-- Date: 2018 +-- ver: 0.2 + +local options = { + { "TextColor", COLOR, YELLOW }, + { "Timer", VALUE, 1, 1, 3} +} + +local function create(zone, options) + local wgt = { zone=zone, options=options} + return wgt +end + +local function update(wgt, options) + if (wgt==nil) then + print("update(nil)") + return + end + wgt.options = options + print("TimerNumB:" .. options.Timer) +end + +local function background(wgt) + return +end + +------------------------------------------------------------ + +-- This size is for top bar wgts +local function refreshZoneTiny(wgt) +end + +--- Size is 160x32 1/8th +local function refreshZoneSmall(wgt) + local t1 = model.getTimer(wgt.options.Timer-1) + local timerInfo = string.format("T%s: ", wgt.options.Timer) + lcd.drawText(wgt.zone.x, wgt.zone.y, timerInfo, SMLSIZE + CUSTOM_COLOR) + lcd.drawTimer(wgt.zone.x+35, wgt.zone.y, t1.value, DBLSIZE + CUSTOM_COLOR) + return +end + +--- Size is 225x98 1/4th (no sliders/trim) +local function refreshZoneMedium(wgt) + local t1 = model.getTimer(wgt.options.Timer-1) + --print(t1.value) + local timerInfo = string.format("Timer:%s", wgt.options.Timer) + lcd.drawText (wgt.zone.x, wgt.zone.y, timerInfo, SMLSIZE + CUSTOM_COLOR) + lcd.drawTimer(wgt.zone.x, wgt.zone.y+3, t1.value, XXLSIZE + CUSTOM_COLOR) +end + +--- Size is 192x152 1/2 +local function refreshZoneLarge(wgt) + local t1 = model.getTimer(wgt.options.Timer-1) + --print(t1.value) + local timerInfo = string.format("Timer:%s", wgt.options.Timer) + lcd.drawText(wgt.zone.x, wgt.zone.y, timerInfo, SMLSIZE + CUSTOM_COLOR) + lcd.drawTimer(wgt.zone.x, wgt.zone.y+15, t1.value, XXLSIZE + CUSTOM_COLOR) +end + +--- Size is 390x172 1/1 +--- Size is 460x252 1/1 (no sliders/trim/topbar) +local function refreshZoneXLarge(wgt) + local t1 = model.getTimer(wgt.options.Timer-1) + --print(t1.value) + local timerInfo = string.format("Timer:%s", wgt.options.Timer) + lcd.drawText(wgt.zone.x, wgt.zone.y, timerInfo, SMLSIZE + CUSTOM_COLOR) + lcd.drawTimer(wgt.zone.x, wgt.zone.y+15, t1.value, XXLSIZE + CUSTOM_COLOR) +end + + +function refresh(wgt) + + if (wgt==nil) then + print("refresh(nil)") + return + end + + if (wgt.options==nil) then + print("refresh(wgt.options=nil)") + return + end + + if (wgt.options.Timer==nil) then + print("refresh(wgt.options.Timer=nil)") + return + end + + lcd.setColor(CUSTOM_COLOR, wgt.options.TextColor) + + if wgt.zone.w > 380 and wgt.zone.h > 165 then refreshZoneXLarge(wgt) + elseif wgt.zone.w > 180 and wgt.zone.h > 145 then refreshZoneLarge(wgt) + elseif wgt.zone.w > 170 and wgt.zone.h > 65 then refreshZoneMedium(wgt) + elseif wgt.zone.w > 150 and wgt.zone.h > 28 then refreshZoneSmall(wgt) + elseif wgt.zone.w > 65 and wgt.zone.h > 35 then refreshZoneTiny(wgt) + end +end + +return { name="Timer2", options=options, create=create, update=update, background=background, refresh=refresh } diff --git a/radio/src/CMakeLists.txt b/radio/src/CMakeLists.txt index 1b2191022..beb9cb90a 100644 --- a/radio/src/CMakeLists.txt +++ b/radio/src/CMakeLists.txt @@ -1,7 +1,7 @@ include(CMakeForceCompiler) include(Bitmaps) -set(PCB_TYPES X9LITE X7 XLITE XLITES X9D X9D+ X9E X10 X12S SKY9X 9XRPRO AR9X) +set(PCB_TYPES X9LITE X7 XLITE XLITES X9D X9D+ X9E X10 X12S SKY9X 9XRPRO AR9X NV14) set(GUI_LANGUAGES CZ DE EN ES FR IT PT SK SE PL HU NL) set(TTS_LANGUAGES CZ DE EN ES FR IT PT SK SE PL HU NL RU) @@ -44,11 +44,13 @@ option(RAS "RAS (SWR) enabled" ON) option(TEMPLATES "Model templates menu" OFF) option(TRACE_SIMPGMSPACE "Turn on traces in simpgmspace.cpp" ON) option(TRACE_LUA_INTERNALS "Turn on traces for Lua internals" OFF) +option(DEBUG_WINDOWS "Turn on windows traces" OFF) option(FRSKY_STICKS "Reverse sticks for FrSky sticks" OFF) option(NANO "Use nano newlib and binalloc") option(NIGHTLY_BUILD_WARNING "Warn this is a nightly build" OFF) option(R9M_PROTO_FLEX "Add R9M options for non certified firmwwares" OFF) option(FRSKY_RELEASE "Used to build FrSky released firmware" OFF) +option(BOOTLOADER "Include Bootloader" ON) # since we reset all default CMAKE compiler flags for firmware builds, provide an alternate way for user to specify additional flags. set(FIRMWARE_C_FLAGS "" CACHE STRING "Additional flags for firmware target c compiler (note: all CMAKE_C_FLAGS[_*] are ignored for firmware/bootloader).") @@ -64,10 +66,6 @@ set(RADIO_BIN_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) configure_file(stamp.h.in stamp.h @ONLY) -add_subdirectory(translations) -add_subdirectory(bitmaps) -add_subdirectory(fonts) - set(EEPROM_VARIANT 0) set(GVARS_VARIANT 1) set(FRSKY_VARIANT 2) @@ -83,6 +81,8 @@ set(FATFS_SRC if(PCB STREQUAL X12S OR PCB STREQUAL X10) include(targets/horus/CMakeLists.txt) +elseif(PCB STREQUAL NV14) + include(targets/nv14/CMakeLists.txt) elseif(PCB STREQUAL X9E OR PCB STREQUAL X9D+ OR PCB STREQUAL X9D OR PCB STREQUAL X7 OR PCB STREQUAL X9LITE OR PCB STREQUAL XLITE OR PCB STREQUAL XLITES) include(targets/taranis/CMakeLists.txt) elseif(PCB STREQUAL SKY9X OR PCB STREQUAL 9XRPRO OR PCB STREQUAL AR9X) @@ -91,6 +91,10 @@ else() message(FATAL_ERROR "Unknown PCB '${PCB}'") endif() +add_subdirectory(translations) +add_subdirectory(bitmaps) +add_subdirectory(fonts) + if(NOT PCB STREQUAL 9XRPRO) option(DBLKEYS "Double Keys" ON) if(DBLKEYS) @@ -125,7 +129,7 @@ endforeach() set(SRC ${SRC} debug.cpp) if(${EEPROM} STREQUAL SDCARD) - set(SRC ${SRC} storage/storage_common.cpp storage/sdcard_raw.cpp storage/modelslist.cpp) + set(SRC ${SRC} storage/storage_common.cpp storage/sdcard_raw.cpp) elseif(${EEPROM} STREQUAL EEPROM_RLC) set(SRC ${SRC} storage/storage_common.cpp storage/eeprom_common.cpp storage/eeprom_rlc.cpp) add_definitions(-DEEPROM -DEEPROM_RLC) @@ -165,12 +169,8 @@ if(NOT LUA STREQUAL NO) set(FIRMWARE_DEPENDENCIES ${FIRMWARE_DEPENDENCIES} ${LUA_EXPORT}) if(LUA STREQUAL YES) add_definitions(-DLUA_MODEL_SCRIPTS) - set(GUI_SRC ${GUI_SRC} model_custom_scripts.cpp) endif() set(SRC ${SRC} lua/interface.cpp lua/api_general.cpp lua/api_lcd.cpp lua/api_model.cpp) - if(PCB STREQUAL X12S OR PCB STREQUAL X10) - set(SRC ${SRC} lua/widgets.cpp) - endif() set(LUA_SRC lapi.c lcode.c lctype.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c ltable.c lrotable.c ltm.c lundump.c lvm.c lzio.c linit.c lbaselib.c lmathlib.c lbitlib.c loadlib.c lauxlib.c ltablib.c lcorolib.c liolib.c lstrlib.c @@ -190,7 +190,6 @@ endif() if(FLIGHT_MODES) add_definitions(-DFLIGHT_MODES) - set(GUI_SRC ${GUI_SRC} model_flightmodes.cpp) endif() if(CURVES) @@ -293,10 +292,22 @@ if(TRACE_LUA_INTERNALS) add_definitions(-DTRACE_LUA_INTERNALS_ENABLED) endif() +if(DEBUG_WINDOWS) + add_definitions(-DDEBUG_WINDOWS) +endif() + if(FRSKY_STICKS) add_definitions(-DFRSKY_STICKS) endif() +if(FLYSKY_HALL_STICKS) + add_definitions(-DFLYSKY_HALL_STICKS) +endif() + +if(FLYSKY_HALL_STICKS_REVERSE) + add_definitions(-DFLYSKY_HALL_STICKS_REVERSE) +endif() + if(NOT FONT STREQUAL "") add_definitions(-DFONT=${DEFAULT_MODE}) endif() @@ -320,6 +331,10 @@ endif() add_definitions(-DPOPUP_LEVEL=${POPUP_LEVEL}) +if(BOOTLOADER) + add_definitions(-DBOOTLOADER) +endif(BOOTLOADER) + set(SRC ${SRC} opentx.cpp @@ -416,7 +431,7 @@ use_cxx11() # ensure gnu++11 in CXX_FLAGS with CMake < 3.1 find_program(BASH bash HINTS "c:/cygwin/bin/" "c:/msys/bin/" DOC "bash shell for firmware size report (eg. c:/cygwin/bin/bash.exe on Windows).") if(NOT MSVC) - set(OPT s) + set(OPT "s" CACHE STRING "Optimization mode (0/1/2/s") if(ARCH STREQUAL ARM) enable_language(ASM) @@ -454,7 +469,7 @@ if(NOT MSVC) set(SRC ${SRC} bin_allocator.cpp) endif() - if(PCB STREQUAL XLITE OR PCB STREQUAL XLITES OR PCB STREQUAL X9D OR PCB STREQUAL X9D+ OR PCB STREQUAL X9E OR PCB STREQUAL X7 OR PCB STREQUAL X9LITE OR PCB STREQUAL X10 OR PCB STREQUAL X12S) + if(PCB STREQUAL XLITE OR PCB STREQUAL XLITES OR PCB STREQUAL X9D OR PCB STREQUAL X9D+ OR PCB STREQUAL X9E OR PCB STREQUAL X9LITE OR PCB STREQUAL X7 OR PCB STREQUAL X3 OR PCB STREQUAL X10 OR PCB STREQUAL X12S) add_subdirectory(targets/common/arm/stm32/bootloader) include_directories(${CMAKE_CURRENT_BINARY_DIR}/targets/common/arm/stm32/bootloader) set(FIRMWARE_DEPENDENCIES ${FIRMWARE_DEPENDENCIES} bootloader) diff --git a/radio/src/audio.cpp b/radio/src/audio.cpp index 5116003b9..e23c06412 100644 --- a/radio/src/audio.cpp +++ b/radio/src/audio.cpp @@ -207,7 +207,7 @@ const char * const audioFilenames[] = { "midstck2", "midstck3", "midstck4", -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) "midpot1", "midpot2", #if defined(PCBX9E) @@ -317,7 +317,7 @@ void getSwitchAudioFile(char * filename, swsrc_t index) { char * str = getModelAudioPath(filename); -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) if (index <= SWSRC_LAST_SWITCH) { div_t swinfo = switchInfo(index); *str++ = 'S'; @@ -345,7 +345,7 @@ void getLogicalSwitchAudioFile(char * filename, int index, unsigned int event) { char * str = getModelAudioPath(filename); -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) *str++ = 'L'; if (index >= 9) { div_t qr = div(index+1, 10); @@ -524,7 +524,7 @@ void audioTask(void * pdata) setSampleRate(AUDIO_SAMPLE_RATE); -#if defined(PCBX12S) +#if defined(PCBX12S) || defined(PCBNV14) // The audio amp needs ~2s to start RTOS_WAIT_MS(1000); // 1s #endif @@ -1129,7 +1129,7 @@ void audioEvent(unsigned int index) case AU_POT3_MIDDLE: case AU_POT4_MIDDLE: #endif -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) case AU_SLIDER1_MIDDLE: case AU_SLIDER2_MIDDLE: #if defined(PCBX9E) diff --git a/radio/src/audio.h b/radio/src/audio.h index ac356863a..67ca22061 100644 --- a/radio/src/audio.h +++ b/radio/src/audio.h @@ -22,7 +22,9 @@ #define _AUDIO_ARM_H_ #include +#include #include "ff.h" +#include "opentx_types.h" /* Implements a bit field, number of bits is set by the template, @@ -101,7 +103,7 @@ enum AudioBufferState #define AUDIO_DATA_MIN 0 #define AUDIO_DATA_MAX 0xffff #define AUDIO_BITS_PER_SAMPLE 16 -#elif defined(PCBX12S) +#elif defined(PCBX12S) || defined(PCBNV14) typedef int16_t audio_data_t; #define AUDIO_DATA_SILENCE 0 #define AUDIO_DATA_MIN INT16_MIN diff --git a/radio/src/bitmaps/480x272/button/alpha_button_off.png b/radio/src/bitmaps/480x272/button/alpha_button_off.png deleted file mode 100644 index 056368e30..000000000 Binary files a/radio/src/bitmaps/480x272/button/alpha_button_off.png and /dev/null differ diff --git a/radio/src/bitmaps/480x272/button/alpha_button_on.png b/radio/src/bitmaps/480x272/button/alpha_button_on.png deleted file mode 100644 index cbb0f0a35..000000000 Binary files a/radio/src/bitmaps/480x272/button/alpha_button_on.png and /dev/null differ diff --git a/radio/src/bitmaps/CMakeLists.txt b/radio/src/bitmaps/CMakeLists.txt index e723cf313..eba043f46 100644 --- a/radio/src/bitmaps/CMakeLists.txt +++ b/radio/src/bitmaps/CMakeLists.txt @@ -1,3 +1 @@ -add_subdirectory(128x64) -add_subdirectory(212x64) -add_subdirectory(480x272) +add_subdirectory(${GUI_DIR}) diff --git a/radio/src/bitmaps/480x272/CMakeLists.txt b/radio/src/bitmaps/colorlcd/CMakeLists.txt similarity index 59% rename from radio/src/bitmaps/480x272/CMakeLists.txt rename to radio/src/bitmaps/colorlcd/CMakeLists.txt index 9337292af..3c1efead4 100644 --- a/radio/src/bitmaps/480x272/CMakeLists.txt +++ b/radio/src/bitmaps/colorlcd/CMakeLists.txt @@ -1,4 +1,7 @@ -if(PCB STREQUAL X12S) +if(PCB STREQUAL NV14) + set(BITMAP_TARGET_PREFIX nv14) + set(BITMAP_FMT_SUFFIX "") +elseif(PCB STREQUAL X12S) set(BITMAP_TARGET_PREFIX x12s) set(BITMAP_FMT_SUFFIX "") else() @@ -6,19 +9,19 @@ else() set(BITMAP_FMT_SUFFIX "-R") endif() -add_bitmaps_target(${BITMAP_TARGET_PREFIX}_bitmaps "${RADIO_SRC_DIRECTORY}/bitmaps/480x272/bmp_*.png" 480 "5/6/5${BITMAP_FMT_SUFFIX}") -add_bitmaps_target(${BITMAP_TARGET_PREFIX}_calibration_bitmaps "${RADIO_SRC_DIRECTORY}/bitmaps/480x272/calibration/bmp_*.png" 480 "5/6/5${BITMAP_FMT_SUFFIX}") -add_bitmaps_target(${BITMAP_TARGET_PREFIX}_button_bitmaps "${RADIO_SRC_DIRECTORY}/bitmaps/480x272/button/alpha_*.png" 480 "4/4/4/4${BITMAP_FMT_SUFFIX}") -add_bitmaps_target(${BITMAP_TARGET_PREFIX}_alpha_bitmaps "${RADIO_SRC_DIRECTORY}/bitmaps/480x272/alpha_*.png" 480 "4/4/4/4${BITMAP_FMT_SUFFIX}") -add_bitmaps_target(${BITMAP_TARGET_PREFIX}_alpha_calibration_bitmaps "${RADIO_SRC_DIRECTORY}/bitmaps/480x272/calibration/alpha_*.png" 480 "4/4/4/4${BITMAP_FMT_SUFFIX}") -add_bitmaps_target(${BITMAP_TARGET_PREFIX}_masks ${RADIO_SRC_DIRECTORY}/bitmaps/480x272/mask_*.png 480 8bits) -add_bitmaps_target(${BITMAP_TARGET_PREFIX}_slider_masks "${RADIO_SRC_DIRECTORY}/bitmaps/480x272/slider/*.png" 480 8bits) -add_bitmaps_target(${BITMAP_TARGET_PREFIX}_layouts_masks "${RADIO_SRC_DIRECTORY}/gui/480x272/layouts/*.png" 480 8bits) -add_bitmaps_target(${BITMAP_TARGET_PREFIX}_themes_bitmaps "${RADIO_SRC_DIRECTORY}/gui/480x272/themes/*.png" 480 "5/6/5${BITMAP_FMT_SUFFIX}") -add_bitmaps_target(${BITMAP_TARGET_PREFIX}_fonts ${RADIO_SRC_DIRECTORY}/fonts/480x272/*.png 480 8bits rle) -add_bitmaps_target(${BITMAP_TARGET_PREFIX}_volume_masks ${RADIO_SRC_DIRECTORY}/bitmaps/480x272/volume/*.png 480 8bits) -add_bitmaps_target(${BITMAP_TARGET_PREFIX}_bootloader_bitmaps ${RADIO_SRC_DIRECTORY}/bitmaps/480x272/bootloader/bmp_*.png 480 "5/6/5${BITMAP_FMT_SUFFIX}" rle) -add_bitmaps_target(${BITMAP_TARGET_PREFIX}_bootloader_icons ${RADIO_SRC_DIRECTORY}/bitmaps/480x272/bootloader/icon_*.png 480 8bits) +add_bitmaps_target(${BITMAP_TARGET_PREFIX}_bitmaps "${RADIO_SRC_DIRECTORY}/bitmaps/colorlcd/bmp_*.png" 480 "5/6/5${BITMAP_FMT_SUFFIX}") +add_bitmaps_target(${BITMAP_TARGET_PREFIX}_calibration_bitmaps "${RADIO_SRC_DIRECTORY}/bitmaps/colorlcd/calibration/bmp_*.png" 480 "5/6/5${BITMAP_FMT_SUFFIX}") +add_bitmaps_target(${BITMAP_TARGET_PREFIX}_button_bitmaps "${RADIO_SRC_DIRECTORY}/bitmaps/colorlcd/button/alpha_*.png" 480 "4/4/4/4${BITMAP_FMT_SUFFIX}") +add_bitmaps_target(${BITMAP_TARGET_PREFIX}_alpha_bitmaps "${RADIO_SRC_DIRECTORY}/bitmaps/colorlcd/alpha_*.png" 480 "4/4/4/4${BITMAP_FMT_SUFFIX}") +add_bitmaps_target(${BITMAP_TARGET_PREFIX}_alpha_calibration_bitmaps "${RADIO_SRC_DIRECTORY}/bitmaps/colorlcd/calibration/alpha_*.png" 480 "4/4/4/4${BITMAP_FMT_SUFFIX}") +add_bitmaps_target(${BITMAP_TARGET_PREFIX}_masks ${RADIO_SRC_DIRECTORY}/bitmaps/colorlcd/mask_*.png 480 8bits) +add_bitmaps_target(${BITMAP_TARGET_PREFIX}_slider_masks "${RADIO_SRC_DIRECTORY}/bitmaps/colorlcd/slider/*.png" 480 8bits) +add_bitmaps_target(${BITMAP_TARGET_PREFIX}_layouts_masks "${RADIO_SRC_DIRECTORY}/gui/colorlcd/layouts/*.png" 480 8bits) +add_bitmaps_target(${BITMAP_TARGET_PREFIX}_themes_bitmaps "${RADIO_SRC_DIRECTORY}/gui/colorlcd/themes/*.png" 480 "5/6/5${BITMAP_FMT_SUFFIX}") +add_bitmaps_target(${BITMAP_TARGET_PREFIX}_fonts ${RADIO_SRC_DIRECTORY}/fonts/truetype/*.png 480 8bits rle) +add_bitmaps_target(${BITMAP_TARGET_PREFIX}_volume_masks ${RADIO_SRC_DIRECTORY}/bitmaps/colorlcd/volume/*.png 480 8bits) +add_bitmaps_target(${BITMAP_TARGET_PREFIX}_bootloader_bitmaps ${RADIO_SRC_DIRECTORY}/bitmaps/colorlcd/bootloader/bmp_*.png 480 "5/6/5${BITMAP_FMT_SUFFIX}" rle) +add_bitmaps_target(${BITMAP_TARGET_PREFIX}_bootloader_icons ${RADIO_SRC_DIRECTORY}/bitmaps/colorlcd/bootloader/icon_*.png 480 8bits) add_dependencies(${BITMAP_TARGET_PREFIX}_bitmaps ${BITMAP_TARGET_PREFIX}_calibration_bitmaps ${BITMAP_TARGET_PREFIX}_button_bitmaps ${BITMAP_TARGET_PREFIX}_alpha_bitmaps ${BITMAP_TARGET_PREFIX}_alpha_calibration_bitmaps ${BITMAP_TARGET_PREFIX}_masks ${BITMAP_TARGET_PREFIX}_slider_masks ${BITMAP_TARGET_PREFIX}_layouts_masks diff --git a/radio/src/bitmaps/480x272/bmp_splash.png b/radio/src/bitmaps/colorlcd/bmp_splash.png similarity index 100% rename from radio/src/bitmaps/480x272/bmp_splash.png rename to radio/src/bitmaps/colorlcd/bmp_splash.png diff --git a/radio/src/bitmaps/480x272/bootloader/bmp_plug_usb.png b/radio/src/bitmaps/colorlcd/bootloader/bmp_plug_usb.png similarity index 100% rename from radio/src/bitmaps/480x272/bootloader/bmp_plug_usb.png rename to radio/src/bitmaps/colorlcd/bootloader/bmp_plug_usb.png diff --git a/radio/src/bitmaps/480x272/bootloader/bmp_usb_plugged.png b/radio/src/bitmaps/colorlcd/bootloader/bmp_usb_plugged.png similarity index 100% rename from radio/src/bitmaps/480x272/bootloader/bmp_usb_plugged.png rename to radio/src/bitmaps/colorlcd/bootloader/bmp_usb_plugged.png diff --git a/radio/src/bitmaps/480x272/bootloader/icon_error.png b/radio/src/bitmaps/colorlcd/bootloader/icon_error.png similarity index 100% rename from radio/src/bitmaps/480x272/bootloader/icon_error.png rename to radio/src/bitmaps/colorlcd/bootloader/icon_error.png diff --git a/radio/src/bitmaps/480x272/bootloader/icon_exit.png b/radio/src/bitmaps/colorlcd/bootloader/icon_exit.png similarity index 100% rename from radio/src/bitmaps/480x272/bootloader/icon_exit.png rename to radio/src/bitmaps/colorlcd/bootloader/icon_exit.png diff --git a/radio/src/bitmaps/480x272/bootloader/icon_file.png b/radio/src/bitmaps/colorlcd/bootloader/icon_file.png similarity index 100% rename from radio/src/bitmaps/480x272/bootloader/icon_file.png rename to radio/src/bitmaps/colorlcd/bootloader/icon_file.png diff --git a/radio/src/bitmaps/480x272/bootloader/icon_flash.png b/radio/src/bitmaps/colorlcd/bootloader/icon_flash.png similarity index 100% rename from radio/src/bitmaps/480x272/bootloader/icon_flash.png rename to radio/src/bitmaps/colorlcd/bootloader/icon_flash.png diff --git a/radio/src/bitmaps/480x272/bootloader/icon_ok.png b/radio/src/bitmaps/colorlcd/bootloader/icon_ok.png similarity index 100% rename from radio/src/bitmaps/480x272/bootloader/icon_ok.png rename to radio/src/bitmaps/colorlcd/bootloader/icon_ok.png diff --git a/radio/src/bitmaps/480x272/bootloader/icon_sd.png b/radio/src/bitmaps/colorlcd/bootloader/icon_sd.png similarity index 100% rename from radio/src/bitmaps/480x272/bootloader/icon_sd.png rename to radio/src/bitmaps/colorlcd/bootloader/icon_sd.png diff --git a/radio/src/bitmaps/colorlcd/button/alpha_button_off.png b/radio/src/bitmaps/colorlcd/button/alpha_button_off.png new file mode 100644 index 000000000..0d82fb58e Binary files /dev/null and b/radio/src/bitmaps/colorlcd/button/alpha_button_off.png differ diff --git a/radio/src/bitmaps/colorlcd/button/alpha_button_on.png b/radio/src/bitmaps/colorlcd/button/alpha_button_on.png new file mode 100644 index 000000000..9bd2cf5cf Binary files /dev/null and b/radio/src/bitmaps/colorlcd/button/alpha_button_on.png differ diff --git a/radio/src/bitmaps/480x272/mask_about_headico.png b/radio/src/bitmaps/colorlcd/mask_about_headico.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_about_headico.png rename to radio/src/bitmaps/colorlcd/mask_about_headico.png diff --git a/radio/src/bitmaps/480x272/mask_carroussel_left.png b/radio/src/bitmaps/colorlcd/mask_carroussel_left.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_carroussel_left.png rename to radio/src/bitmaps/colorlcd/mask_carroussel_left.png diff --git a/radio/src/bitmaps/480x272/mask_carroussel_right.png b/radio/src/bitmaps/colorlcd/mask_carroussel_right.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_carroussel_right.png rename to radio/src/bitmaps/colorlcd/mask_carroussel_right.png diff --git a/radio/src/bitmaps/480x272/mask_coord_shadow.png b/radio/src/bitmaps/colorlcd/mask_coord_shadow.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_coord_shadow.png rename to radio/src/bitmaps/colorlcd/mask_coord_shadow.png diff --git a/radio/src/bitmaps/480x272/mask_cvpoint.png b/radio/src/bitmaps/colorlcd/mask_cvpoint.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_cvpoint.png rename to radio/src/bitmaps/colorlcd/mask_cvpoint.png diff --git a/radio/src/bitmaps/480x272/mask_cvpoint_center.png b/radio/src/bitmaps/colorlcd/mask_cvpoint_center.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_cvpoint_center.png rename to radio/src/bitmaps/colorlcd/mask_cvpoint_center.png diff --git a/radio/src/bitmaps/480x272/mask_dot.png b/radio/src/bitmaps/colorlcd/mask_dot.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_dot.png rename to radio/src/bitmaps/colorlcd/mask_dot.png diff --git a/radio/src/bitmaps/colorlcd/mask_dropdown.png b/radio/src/bitmaps/colorlcd/mask_dropdown.png new file mode 100644 index 000000000..5e876fd9f Binary files /dev/null and b/radio/src/bitmaps/colorlcd/mask_dropdown.png differ diff --git a/radio/src/bitmaps/colorlcd/mask_folder.png b/radio/src/bitmaps/colorlcd/mask_folder.png new file mode 100644 index 000000000..0edea4cb2 Binary files /dev/null and b/radio/src/bitmaps/colorlcd/mask_folder.png differ diff --git a/radio/src/bitmaps/480x272/mask_htrim_frame.png b/radio/src/bitmaps/colorlcd/mask_htrim_frame.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_htrim_frame.png rename to radio/src/bitmaps/colorlcd/mask_htrim_frame.png diff --git a/radio/src/bitmaps/colorlcd/mask_key_backspace.png b/radio/src/bitmaps/colorlcd/mask_key_backspace.png new file mode 100644 index 000000000..a8361a9f2 Binary files /dev/null and b/radio/src/bitmaps/colorlcd/mask_key_backspace.png differ diff --git a/radio/src/bitmaps/colorlcd/mask_key_letters.png b/radio/src/bitmaps/colorlcd/mask_key_letters.png new file mode 100644 index 000000000..50840940a Binary files /dev/null and b/radio/src/bitmaps/colorlcd/mask_key_letters.png differ diff --git a/radio/src/bitmaps/colorlcd/mask_key_lowercase.png b/radio/src/bitmaps/colorlcd/mask_key_lowercase.png new file mode 100644 index 000000000..582a2cbb1 Binary files /dev/null and b/radio/src/bitmaps/colorlcd/mask_key_lowercase.png differ diff --git a/radio/src/bitmaps/colorlcd/mask_key_numbers.png b/radio/src/bitmaps/colorlcd/mask_key_numbers.png new file mode 100644 index 000000000..1093170a0 Binary files /dev/null and b/radio/src/bitmaps/colorlcd/mask_key_numbers.png differ diff --git a/radio/src/bitmaps/colorlcd/mask_key_spacebar.png b/radio/src/bitmaps/colorlcd/mask_key_spacebar.png new file mode 100644 index 000000000..e00f91374 Binary files /dev/null and b/radio/src/bitmaps/colorlcd/mask_key_spacebar.png differ diff --git a/radio/src/bitmaps/colorlcd/mask_key_uppercase.png b/radio/src/bitmaps/colorlcd/mask_key_uppercase.png new file mode 100644 index 000000000..0a3b8ac37 Binary files /dev/null and b/radio/src/bitmaps/colorlcd/mask_key_uppercase.png differ diff --git a/radio/src/bitmaps/480x272/mask_library_active_model.png b/radio/src/bitmaps/colorlcd/mask_library_active_model.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_library_active_model.png rename to radio/src/bitmaps/colorlcd/mask_library_active_model.png diff --git a/radio/src/bitmaps/480x272/mask_library_score_0.png b/radio/src/bitmaps/colorlcd/mask_library_score_0.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_library_score_0.png rename to radio/src/bitmaps/colorlcd/mask_library_score_0.png diff --git a/radio/src/bitmaps/480x272/mask_library_score_1.png b/radio/src/bitmaps/colorlcd/mask_library_score_1.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_library_score_1.png rename to radio/src/bitmaps/colorlcd/mask_library_score_1.png diff --git a/radio/src/bitmaps/480x272/mask_library_slot.png b/radio/src/bitmaps/colorlcd/mask_library_slot.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_library_slot.png rename to radio/src/bitmaps/colorlcd/mask_library_slot.png diff --git a/radio/src/bitmaps/480x272/mask_library_star_0.png b/radio/src/bitmaps/colorlcd/mask_library_star_0.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_library_star_0.png rename to radio/src/bitmaps/colorlcd/mask_library_star_0.png diff --git a/radio/src/bitmaps/480x272/mask_library_star_1.png b/radio/src/bitmaps/colorlcd/mask_library_star_1.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_library_star_1.png rename to radio/src/bitmaps/colorlcd/mask_library_star_1.png diff --git a/radio/src/bitmaps/480x272/mask_mainviews_item_in.png b/radio/src/bitmaps/colorlcd/mask_mainviews_item_in.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_mainviews_item_in.png rename to radio/src/bitmaps/colorlcd/mask_mainviews_item_in.png diff --git a/radio/src/bitmaps/480x272/mask_mainviews_item_out.png b/radio/src/bitmaps/colorlcd/mask_mainviews_item_out.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_mainviews_item_out.png rename to radio/src/bitmaps/colorlcd/mask_mainviews_item_out.png diff --git a/radio/src/bitmaps/480x272/mask_point.png b/radio/src/bitmaps/colorlcd/mask_point.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_point.png rename to radio/src/bitmaps/colorlcd/mask_point.png diff --git a/radio/src/bitmaps/480x272/mask_rscale.png b/radio/src/bitmaps/colorlcd/mask_rscale.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_rscale.png rename to radio/src/bitmaps/colorlcd/mask_rscale.png diff --git a/radio/src/bitmaps/colorlcd/mask_shutdown_circle.png b/radio/src/bitmaps/colorlcd/mask_shutdown_circle.png new file mode 100644 index 000000000..465ead89b Binary files /dev/null and b/radio/src/bitmaps/colorlcd/mask_shutdown_circle.png differ diff --git a/radio/src/bitmaps/480x272/mask_swipe_circle.png b/radio/src/bitmaps/colorlcd/mask_swipe_circle.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_swipe_circle.png rename to radio/src/bitmaps/colorlcd/mask_swipe_circle.png diff --git a/radio/src/bitmaps/480x272/mask_swipe_left.png b/radio/src/bitmaps/colorlcd/mask_swipe_left.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_swipe_left.png rename to radio/src/bitmaps/colorlcd/mask_swipe_left.png diff --git a/radio/src/bitmaps/480x272/mask_swipe_right.png b/radio/src/bitmaps/colorlcd/mask_swipe_right.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_swipe_right.png rename to radio/src/bitmaps/colorlcd/mask_swipe_right.png diff --git a/radio/src/bitmaps/colorlcd/mask_timer.png b/radio/src/bitmaps/colorlcd/mask_timer.png new file mode 100644 index 000000000..7038d41c8 Binary files /dev/null and b/radio/src/bitmaps/colorlcd/mask_timer.png differ diff --git a/radio/src/bitmaps/colorlcd/mask_timer_bg.png b/radio/src/bitmaps/colorlcd/mask_timer_bg.png new file mode 100644 index 000000000..e8d000e78 Binary files /dev/null and b/radio/src/bitmaps/colorlcd/mask_timer_bg.png differ diff --git a/radio/src/bitmaps/480x272/mask_topmenu_altitude_ico.png b/radio/src/bitmaps/colorlcd/mask_topmenu_altitude_ico.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_topmenu_altitude_ico.png rename to radio/src/bitmaps/colorlcd/mask_topmenu_altitude_ico.png diff --git a/radio/src/bitmaps/480x272/mask_topmenu_masterpart_ico.png b/radio/src/bitmaps/colorlcd/mask_topmenu_masterpart_ico.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_topmenu_masterpart_ico.png rename to radio/src/bitmaps/colorlcd/mask_topmenu_masterpart_ico.png diff --git a/radio/src/bitmaps/480x272/mask_topmenu_sdcard.png b/radio/src/bitmaps/colorlcd/mask_topmenu_sdcard.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_topmenu_sdcard.png rename to radio/src/bitmaps/colorlcd/mask_topmenu_sdcard.png diff --git a/radio/src/bitmaps/480x272/mask_topmenu_slavepart_ico.png b/radio/src/bitmaps/colorlcd/mask_topmenu_slavepart_ico.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_topmenu_slavepart_ico.png rename to radio/src/bitmaps/colorlcd/mask_topmenu_slavepart_ico.png diff --git a/radio/src/bitmaps/480x272/mask_topmenu_usb.png b/radio/src/bitmaps/colorlcd/mask_topmenu_usb.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_topmenu_usb.png rename to radio/src/bitmaps/colorlcd/mask_topmenu_usb.png diff --git a/radio/src/bitmaps/480x272/mask_topmenu_volume_ico.png b/radio/src/bitmaps/colorlcd/mask_topmenu_volume_ico.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_topmenu_volume_ico.png rename to radio/src/bitmaps/colorlcd/mask_topmenu_volume_ico.png diff --git a/radio/src/bitmaps/480x272/mask_trim_shadow.png b/radio/src/bitmaps/colorlcd/mask_trim_shadow.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_trim_shadow.png rename to radio/src/bitmaps/colorlcd/mask_trim_shadow.png diff --git a/radio/src/bitmaps/480x272/mask_txbat.png b/radio/src/bitmaps/colorlcd/mask_txbat.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_txbat.png rename to radio/src/bitmaps/colorlcd/mask_txbat.png diff --git a/radio/src/bitmaps/480x272/mask_vtrim_frame.png b/radio/src/bitmaps/colorlcd/mask_vtrim_frame.png similarity index 100% rename from radio/src/bitmaps/480x272/mask_vtrim_frame.png rename to radio/src/bitmaps/colorlcd/mask_vtrim_frame.png diff --git a/radio/src/bitmaps/480x272/slider/bar_left.png b/radio/src/bitmaps/colorlcd/slider/bar_left.png similarity index 100% rename from radio/src/bitmaps/480x272/slider/bar_left.png rename to radio/src/bitmaps/colorlcd/slider/bar_left.png diff --git a/radio/src/bitmaps/480x272/slider/bar_right.png b/radio/src/bitmaps/colorlcd/slider/bar_right.png similarity index 100% rename from radio/src/bitmaps/480x272/slider/bar_right.png rename to radio/src/bitmaps/colorlcd/slider/bar_right.png diff --git a/radio/src/bitmaps/480x272/slider/point_in.png b/radio/src/bitmaps/colorlcd/slider/point_in.png similarity index 100% rename from radio/src/bitmaps/480x272/slider/point_in.png rename to radio/src/bitmaps/colorlcd/slider/point_in.png diff --git a/radio/src/bitmaps/480x272/slider/point_mid.png b/radio/src/bitmaps/colorlcd/slider/point_mid.png similarity index 100% rename from radio/src/bitmaps/480x272/slider/point_mid.png rename to radio/src/bitmaps/colorlcd/slider/point_mid.png diff --git a/radio/src/bitmaps/480x272/slider/point_out.png b/radio/src/bitmaps/colorlcd/slider/point_out.png similarity index 100% rename from radio/src/bitmaps/480x272/slider/point_out.png rename to radio/src/bitmaps/colorlcd/slider/point_out.png diff --git a/radio/src/bitmaps/480x272/volume/mask_volume_0.png b/radio/src/bitmaps/colorlcd/volume/mask_volume_0.png similarity index 100% rename from radio/src/bitmaps/480x272/volume/mask_volume_0.png rename to radio/src/bitmaps/colorlcd/volume/mask_volume_0.png diff --git a/radio/src/bitmaps/480x272/volume/mask_volume_1.png b/radio/src/bitmaps/colorlcd/volume/mask_volume_1.png similarity index 100% rename from radio/src/bitmaps/480x272/volume/mask_volume_1.png rename to radio/src/bitmaps/colorlcd/volume/mask_volume_1.png diff --git a/radio/src/bitmaps/480x272/volume/mask_volume_2.png b/radio/src/bitmaps/colorlcd/volume/mask_volume_2.png similarity index 100% rename from radio/src/bitmaps/480x272/volume/mask_volume_2.png rename to radio/src/bitmaps/colorlcd/volume/mask_volume_2.png diff --git a/radio/src/bitmaps/480x272/volume/mask_volume_3.png b/radio/src/bitmaps/colorlcd/volume/mask_volume_3.png similarity index 100% rename from radio/src/bitmaps/480x272/volume/mask_volume_3.png rename to radio/src/bitmaps/colorlcd/volume/mask_volume_3.png diff --git a/radio/src/bitmaps/480x272/volume/mask_volume_4.png b/radio/src/bitmaps/colorlcd/volume/mask_volume_4.png similarity index 100% rename from radio/src/bitmaps/480x272/volume/mask_volume_4.png rename to radio/src/bitmaps/colorlcd/volume/mask_volume_4.png diff --git a/radio/src/bitmaps/480x272/volume/mask_volume_scale.png b/radio/src/bitmaps/colorlcd/volume/mask_volume_scale.png similarity index 100% rename from radio/src/bitmaps/480x272/volume/mask_volume_scale.png rename to radio/src/bitmaps/colorlcd/volume/mask_volume_scale.png diff --git a/radio/src/bluetooth.cpp b/radio/src/bluetooth.cpp index 877d7466b..9de8bc403 100644 --- a/radio/src/bluetooth.cpp +++ b/radio/src/bluetooth.cpp @@ -684,7 +684,7 @@ const char * Bluetooth::bootloaderWriteFlash(const uint8_t * data, uint32_t size return nullptr; } -const char * Bluetooth::doFlashFirmware(const char * filename) +const char * Bluetooth::doFlashFirmware(const char * filename, ProgressHandler progressHandler) { const char * result; FIL file; @@ -718,7 +718,7 @@ const char * Bluetooth::doFlashFirmware(const char * filename) return "Format error"; } - drawProgressScreen(getBasename(filename), STR_FLASH_ERASE, 0, 0); + progressHandler(getBasename(filename), STR_FLASH_ERASE, 0, 0); result = bootloaderEraseFlash(CC26XX_FIRMWARE_BASE, information->size); if (result) { @@ -727,7 +727,7 @@ const char * Bluetooth::doFlashFirmware(const char * filename) } uint32_t size = information->size; - drawProgressScreen(getBasename(filename), STR_FLASH_WRITE, 0, size); + progressHandler(getBasename(filename), STR_FLASH_WRITE, 0, size); result = bootloaderStartWriteFlash(CC26XX_FIRMWARE_BASE, size); if (result) @@ -735,7 +735,7 @@ const char * Bluetooth::doFlashFirmware(const char * filename) uint32_t done = 0; while (1) { - drawProgressScreen(getBasename(filename), STR_FLASH_WRITE, done, size); + progressHandler(getBasename(filename), STR_FLASH_WRITE, done, size); if (f_read(&file, buffer, min(sizeof(buffer), size - done), &count) != FR_OK) { f_close(&file); return "Error reading file"; @@ -751,9 +751,9 @@ const char * Bluetooth::doFlashFirmware(const char * filename) } } -const char * Bluetooth::flashFirmware(const char * filename) +const char * Bluetooth::flashFirmware(const char * filename, ProgressHandler progressHandler) { - drawProgressScreen(getBasename(filename), STR_MODULE_RESET, 0, 0); + progressHandler(getBasename(filename), STR_MODULE_RESET, 0, 0); state = BLUETOOTH_STATE_FLASH_FIRMWARE; @@ -767,7 +767,7 @@ const char * Bluetooth::flashFirmware(const char * filename) watchdogSuspend(1000); RTOS_WAIT_MS(1000); - const char * result = doFlashFirmware(filename); + const char * result = doFlashFirmware(filename, progressHandler); AUDIO_PLAY(AU_SPECIAL_SOUND_BEEP1 ); BACKLIGHT_ENABLE(); @@ -780,7 +780,7 @@ const char * Bluetooth::flashFirmware(const char * filename) POPUP_INFORMATION(STR_FIRMWARE_UPDATE_SUCCESS); } - drawProgressScreen(getBasename(filename), STR_MODULE_RESET, 0, 0); + progressHandler(getBasename(filename), STR_MODULE_RESET, 0, 0); /* wait 1s off */ watchdogSuspend(1000); diff --git a/radio/src/bluetooth.h b/radio/src/bluetooth.h index 42c5abaa7..26f8b308b 100644 --- a/radio/src/bluetooth.h +++ b/radio/src/bluetooth.h @@ -57,7 +57,7 @@ class Bluetooth void forwardTelemetry(const uint8_t * packet); void wakeup(); - const char * flashFirmware(const char * filename); + const char * flashFirmware(const char * filename, ProgressHandler progressHandler); volatile uint8_t state; char localAddr[LEN_BLUETOOTH_ADDR+1]; @@ -85,7 +85,7 @@ class Bluetooth const char * bootloaderEraseFlash(uint32_t start, uint32_t size); const char * bootloaderStartWriteFlash(uint32_t start, uint32_t size); const char * bootloaderWriteFlash(const uint8_t * data, uint32_t size); - const char * doFlashFirmware(const char * filename); + const char * doFlashFirmware(const char * filename, ProgressHandler progressHandler); uint8_t buffer[BLUETOOTH_LINE_LENGTH+1]; uint8_t bufferIndex = 0; diff --git a/radio/src/cli.cpp b/radio/src/cli.cpp index cb72b8076..8259638c0 100644 --- a/radio/src/cli.cpp +++ b/radio/src/cli.cpp @@ -413,7 +413,7 @@ void testDrawText() void testDrawTextVertical() { - lcdDrawText(30, LCD_H, "The quick brown fox ", TEXT_COLOR|VERTICAL|NO_FONTCACHE); + lcdDrawText(30, LCD_H, "The quick brown fox ", TEXT_COLOR|VERTICAL); } void testClear() @@ -616,7 +616,7 @@ int cliTestMemorySpeed() return 0; } -#include "storage/modelslist.h" +#include "modelslist.h" using std::list; int cliTestModelsList() diff --git a/radio/src/curves.cpp b/radio/src/curves.cpp index 3a1bf1f27..865a5ed5b 100644 --- a/radio/src/curves.cpp +++ b/radio/src/curves.cpp @@ -2,7 +2,7 @@ * Copyright (C) OpenTX * * Based on code named - * th9x - http://code.google.com/p/th9x + * th9x - http://code.google.com/p/th9x * er9x - http://code.google.com/p/er9x * gruvin9x - http://code.google.com/p/gruvin9x * @@ -70,14 +70,14 @@ bool moveCurve(uint8_t index, int8_t shift) AUDIO_WARNING2(); return false; } - + int8_t * nextCrv = curveAddress(index+1); memmove(nextCrv+shift, nextCrv, 5*(MAX_CURVES-index-1)+curveEnd[MAX_CURVES-1]-curveEnd[index]); if (shift < 0) memclear(&g_model.points[MAX_CURVE_POINTS-1] + shift, -shift); while (index0 && i 0 && index < count - 1) { + result.x = calc100toRESX(points[count + index - 1]); } + else { + result.x = -RESX + calc100toRESX(200 * index / (count - 1)); + } + result.y = calc100toRESX(points[index]); } return result; } diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index d6e74cd95..0d0e49891 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -35,7 +35,7 @@ #define LUA_EXPORT_EXTRA(...) #endif -#if defined(PCBHORUS) +#if defined(PCBHORUS) || defined(PCBNV14) #define MAX_MODELS 60 #define MAX_OUTPUT_CHANNELS 32 // number of real output channels CH1-CH32 #define MAX_FLIGHT_MODES 9 @@ -99,7 +99,7 @@ enum CurveType { #define MIN_POINTS_PER_CURVE 3 #define MAX_POINTS_PER_CURVE 17 -#if defined(PCBHORUS) +#if defined(PCBHORUS) || defined(PCBNV14) #define LEN_MODEL_NAME 15 #define LEN_TIMER_NAME 8 #define LEN_FLIGHT_MODE_NAME 10 @@ -136,7 +136,7 @@ enum CurveType { #define MAX_CURVE_POINTS 512 #endif -#if defined(PCBTARANIS) || defined(PCBSKY9X) || defined(PCBHORUS) +#if defined(PCBFRSKY) || defined(PCBSKY9X) || defined(PCBNV14) #define NUM_MODULES 2 #else #define NUM_MODULES 1 @@ -179,7 +179,7 @@ enum BeeperMode { e_mode_all }; -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) || defined(PCBNV14) enum ModuleIndex { INTERNAL_MODULE, EXTERNAL_MODULE, @@ -214,7 +214,7 @@ enum TrainerMode { #define TRAINER_MODE_MAX() HAS_WIRELESS_TRAINER_HARDWARE() ? TRAINER_MODE_MASTER_BATTERY_COMPARTMENT : TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE #endif -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) || defined(PCBNV14) #define IS_INTERNAL_MODULE_ENABLED() (g_model.moduleData[INTERNAL_MODULE].type != MODULE_TYPE_NONE) #elif defined(PCBSKY9X) #define IS_INTERNAL_MODULE_ENABLED() (false) @@ -236,7 +236,7 @@ enum UartModes { UART_MODE_MAX = UART_MODE_COUNT-1 }; -#if defined(PCBHORUS) +#if defined(PCBHORUS) || defined(PCBNV14) #define LEN_SWITCH_NAME 3 #define LEN_ANA_NAME 3 #define LEN_MODEL_FILENAME 16 @@ -592,6 +592,7 @@ enum MixSources { MIXSRC_Thr, LUA_EXPORT("thr", "Throttle") MIXSRC_Ail, LUA_EXPORT("ail", "Aileron") + MIXSRC_LAST_STICK = MIXSRC_Ail, MIXSRC_FIRST_POT, #if defined(PCBHORUS) MIXSRC_S1 = MIXSRC_FIRST_POT, LUA_EXPORT("s1", "Potentiometer S1") @@ -623,7 +624,7 @@ enum MixSources { MIXSRC_SLIDER3, LUA_EXPORT("lcs", "Left center slider (X9E only)") MIXSRC_SLIDER4, LUA_EXPORT("rcs", "Right center slider (X9E only)") MIXSRC_LAST_POT = MIXSRC_SLIDER4, -#elif defined(PCBX7) || defined(PCBXLITE) +#elif defined(PCBX7) || defined(PCBXLITE) || defined(PCBNV14) MIXSRC_POT1 = MIXSRC_FIRST_POT, LUA_EXPORT("s1", "Potentiometer 1") MIXSRC_POT2, LUA_EXPORT("s2", "Potentiometer 2") MIXSRC_LAST_POT = MIXSRC_POT2, @@ -661,6 +662,7 @@ enum MixSources { MIXSRC_CYC1 = MIXSRC_FIRST_HELI, LUA_EXPORT("cyc1", "Cyclic 1") MIXSRC_CYC2, LUA_EXPORT("cyc2", "Cyclic 2") MIXSRC_CYC3, LUA_EXPORT("cyc3", "Cyclic 3") + MIXSRC_LAST_HELI = MIXSRC_CYC3, MIXSRC_FIRST_TRIM, MIXSRC_TrimRud = MIXSRC_FIRST_TRIM, LUA_EXPORT("trim-rud", "Rudder trim") @@ -840,12 +842,14 @@ enum Functions { }; enum TimerModes { - TMRMODE_NONE, - TMRMODE_ABS, + TMRMODE_OFF, + TMRMODE_ON, + TMRMODE_START, TMRMODE_THR, TMRMODE_THR_REL, - TMRMODE_THR_TRG, - TMRMODE_COUNT + TMRMODE_THR_START, + TMRMODE_COUNT, + TMRMODE_MAX = TMRMODE_COUNT - 1 }; enum CountDownModes { diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index fcac2169d..918ba87d7 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -55,7 +55,7 @@ #define NOBACKUP(...) __VA_ARGS__ #endif -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) typedef uint16_t source_t; #else typedef uint8_t source_t; @@ -225,14 +225,14 @@ PACK(struct GVarData { */ PACK(struct TimerData { - int32_t mode:9; // timer trigger source -> off, abs, stk, stk%, sw/!sw, !m_sw/!m_sw - uint32_t start:23; - int32_t value:24; + uint32_t start:22; + int32_t swtch:10; + int32_t value:22; + uint32_t mode:3; // timer mode (OFF, ON, Start, THs, TH%, THt) uint32_t countdownBeep:2; uint32_t minuteBeep:1; uint32_t persistent:2; int32_t countdownStart:2; - uint32_t direction:1; NOBACKUP(char name[LEN_TIMER_NAME]); }); @@ -430,12 +430,13 @@ PACK(struct ModuleData { }; union { - struct { + uint8_t raw[PXX2_MAX_RECEIVERS_PER_MODULE * PXX2_LEN_RX_NAME + 1]; + NOBACKUP(struct { int8_t delay:6; uint8_t pulsePol:1; uint8_t outputType:1; // false = open drain, true = push pull int8_t frameLength; - } ppm; + } ppm); NOBACKUP(struct { uint8_t rfProtocolExtra:2; uint8_t spare1:3; @@ -463,6 +464,13 @@ PACK(struct ModuleData { uint8_t receivers; // 5 bits spare char receiverName[PXX2_MAX_RECEIVERS_PER_MODULE][PXX2_LEN_RX_NAME]; }) pxx2); +#if defined(PCBFLYSKY) + NOBACKUP(struct { + uint8_t rx_id[4]; + uint8_t mode; + uint8_t rx_freq[2]; + } flysky); +#endif }; // Helper functions to set both of the rfProto protocol at the same time @@ -526,9 +534,9 @@ typedef uint8_t swarnenable_t; swarnenable_t switchWarningEnable; // TODO remove it in 2.4 #endif -#if defined(PCBHORUS) -#include "gui/480x272/layout.h" -#include "gui/480x272/topbar.h" +#if defined(COLORLCD) +#include "gui/colorlcd/layout.h" +#include "gui/colorlcd/topbar.h" #define LAYOUT_NAME_LEN 10 PACK(struct CustomScreenData { char layoutName[LAYOUT_NAME_LEN]; @@ -650,10 +658,10 @@ PACK(struct TrainerData { NOBACKUP(TrainerMix mix[4]); }); -#if defined(PCBHORUS) +#if defined(COLORLCD) #define SPLASH_MODE uint8_t splashSpares:3 #else - #define SPLASH_MODE int8_t splashMode:3 + #define SPLASH_MODE uint8_t splashMode:1; uint8_t splashSpare:2 #endif #if defined(PCBXLITES) || defined(PCBHORUS) @@ -664,7 +672,7 @@ PACK(struct TrainerData { #define GYRO_FIELDS #endif -#if defined(PCBHORUS) +#if defined(PCBHORUS) || defined(PCBNV14) #define EXTRA_GENERAL_FIELDS \ NOBACKUP(uint8_t auxSerialMode); \ swconfig_t switchConfig; \ @@ -676,7 +684,7 @@ PACK(struct TrainerData { NOBACKUP(uint8_t spare4:1); \ NOBACKUP(uint8_t blOffBright:7); \ NOBACKUP(char bluetoothName[LEN_BLUETOOTH_NAME]); -#elif defined(PCBTARANIS) +#elif defined(PCBTARANIS) || defined(PCBNV14) #if defined(STORAGE_BLUETOOTH) #define BLUETOOTH_FIELDS \ uint8_t spare4; \ @@ -710,8 +718,8 @@ PACK(struct TrainerData { #define EXTRA_GENERAL_FIELDS #endif -#if defined(PCBHORUS) - #include "gui/480x272/theme.h" +#if defined(COLORLCD) + #include "theme.h" #define THEME_NAME_LEN 8 #define THEME_DATA \ NOBACKUP(char themeName[THEME_NAME_LEN]); \ @@ -873,6 +881,8 @@ static inline void check_struct() CHKSIZE(CurveData, 4); CHKSIZE(CustomScreenData, 610); CHKSIZE(Topbar::PersistentData, 216); +#elif defined(PCBNV14) + // TODO #elif defined(PCBSKY9X) CHKSIZE(MixData, 20); CHKSIZE(ExpoData, 17); diff --git a/radio/src/debug.cpp b/radio/src/debug.cpp index f7a0bd1f5..c4de32e9f 100644 --- a/radio/src/debug.cpp +++ b/radio/src/debug.cpp @@ -26,6 +26,10 @@ traceCallbackFunc traceCallback = 0; #endif +#if !defined(SIMU) +uint32_t debugCounter1ms = 0; +#endif + #if defined(SIMU) #define PRINTF_BUFFER_SIZE 1024 void debugPrintf(const char * format, ...) diff --git a/radio/src/debug.h b/radio/src/debug.h index 3a5303ef3..d85a19b71 100644 --- a/radio/src/debug.h +++ b/radio/src/debug.h @@ -66,6 +66,12 @@ uint8_t auxSerialTracesEnabled(); #define TRACE_WARNING_WP(...) debugPrintf(__VA_ARGS__) #define TRACE_ERROR(...) debugPrintf("-E- " __VA_ARGS__) +#if defined(DEBUG_WINDOWS) +#define TRACE_WINDOWS(f_, ...) debugPrintf((f_ "\r\n"), ##__VA_ARGS__) +#else +#define TRACE_WINDOWS(...) +#endif + #if defined(TRACE_LUA_INTERNALS_ENABLED) #define TRACE_LUA_INTERNALS(f_, ...) debugPrintf(("[LUA INT] " f_ "\r\n"), ##__VA_ARGS__) @@ -423,5 +429,9 @@ extern const char * const debugTimerNames[DEBUG_TIMERS_COUNT]; #endif //#if defined(DEBUG_TIMERS) +#if !defined(SIMU) +extern uint32_t debugCounter1ms; +#endif + #endif // _DEBUG_H_ diff --git a/radio/src/definitions.h b/radio/src/definitions.h index 4a7244faa..a569fd458 100644 --- a/radio/src/definitions.h +++ b/radio/src/definitions.h @@ -21,6 +21,22 @@ #ifndef _DEFINITIONS_H_ #define _DEFINITIONS_H_ +#if !defined(M_PI) +#define M_PI 3.14159265358979323846 // pi +#define M_PI_2 1.57079632679489661923 // pi/2 +#define M_PI_4 0.785398163397448309616 // pi/4 +#define M_1_PI 0.318309886183790671538 // 1/pi +#define M_2_PI 0.636619772367581343076 // 2/pi +#define M_2_SQRTPI 1.12837916709551257390 // 2/sqrt(pi) +#define M_SQRT2 1.41421356237309504880 // sqrt(2) +#define M_SQRT1_2 0.707106781186547524401 // 1/sqrt(2) +#define M_E 2.71828182845904523536 // e +#define M_LOG2E 1.44269504088896340736 // log2(e) +#define M_LOG10E 0.434294481903251827651 // log10(e) +#define M_LN2 0.693147180559945309417 // ln(2) +#define M_LN10 2.30258509299404568402 // ln(10) +#endif + #include #include #include diff --git a/radio/src/fonts.h b/radio/src/fonts.h index a839d48ee..86c38ec5b 100644 --- a/radio/src/fonts.h +++ b/radio/src/fonts.h @@ -21,6 +21,8 @@ #ifndef _FONTS_H_ #define _FONTS_H_ +#include "opentx.h" + #if defined(COLORLCD) #if !defined(BOOT) @@ -31,9 +33,7 @@ extern const uint16_t * const fontspecsTable[FONT_TABLE_SIZE]; extern const uint8_t * fontsTable[FONT_TABLE_SIZE]; -extern BitmapBuffer * fontCache[2]; -void loadFontCache(); void loadFonts(); #else diff --git a/radio/src/fonts/480x272/CMakeLists.txt b/radio/src/fonts/480x272/CMakeLists.txt deleted file mode 100644 index 09a68efdb..000000000 --- a/radio/src/fonts/480x272/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -set(FONT "Roboto/Roboto-Regular") -set(FONT_BOLD "Roboto/Roboto-Bold") -set(FONT_OFFSET 1) - -#set(FONT "Kanit/Kanit-Regular") -#set(FONT_BOLD "Kanit/Kanit-Bold") -#set(FONT_OFFSET -2) - -#set(FONT "Ubuntu/Ubuntu-Regular") -#set(FONT_BOLD "Ubuntu/Ubuntu-Bold") -#set(FONT_OFFSET 1) - -add_truetype_font_target(480x272 tinsize ${FONT} 9 ${FONT_OFFSET}) -add_truetype_font_target(480x272 smlsize ${FONT} 13 ${FONT_OFFSET}) -add_truetype_font_target(480x272 stdsize ${FONT} 16 ${FONT_OFFSET}) -add_truetype_font_target(480x272 stdsizebold ${FONT_BOLD} 16 ${FONT_OFFSET}) -add_truetype_font_target(480x272 midsize ${FONT} 24 ${FONT_OFFSET}) -add_truetype_font_target(480x272 dblsize ${FONT_BOLD} 32 ${FONT_OFFSET}) -add_truetype_font_target(480x272 xxlsize ${FONT_BOLD} 64 ${FONT_OFFSET}) - -add_custom_target(ttf_horus_fonts DEPENDS ${ttf_480x272_fonts_targets}) diff --git a/radio/src/fonts/480x272/font_dblsize.png b/radio/src/fonts/480x272/font_dblsize.png deleted file mode 100644 index 174c70f9b..000000000 Binary files a/radio/src/fonts/480x272/font_dblsize.png and /dev/null differ diff --git a/radio/src/fonts/480x272/font_dblsize.specs b/radio/src/fonts/480x272/font_dblsize.specs deleted file mode 100644 index 07d83c610..000000000 --- a/radio/src/fonts/480x272/font_dblsize.specs +++ /dev/null @@ -1 +0,0 @@ -0,4,14,25,45,64,89,111,117,129,141,157,175,184,197,207,220,239,258,277,296,315,334,353,372,391,410,420,429,446,465,483,500,513,536,557,579,601,620,639,662,686,696,715,737,755,784,808,831,853,876,897,918,939,961,983,1012,1033,1054,1074,1084,1100,1110,1125,1141,1153,1171,1190,1208,1227,1245,1258,1277,1296,1305,1314,1333,1342,1371,1390,1409,1428,1447,1460,1477,1489,1508,1525,1550,1568,1585,1602,1624,1633,1651,1651,1651,1651,1651,1651,1651,1651,1651,1651,1651,1651,1651,1651,1651,1651,1651,1651,1651,1651,1651,1651,1669,1687,1705,1717,1735,1758,1776,1799,1818,1840,1859,1880,1898,1915,1933,1947,1964,1981,1999,2011,2029,2048,2067,2086,2105,2129,2144,2163,2184,2206,2227,2247,2268,2291,2303,2322,2346,2369,2391,2413,2435,2456,2480,2499,2517,2535,2554,2577,2600,2623,2641,2660,2678,2696,2714,2725,2744,2763,2780,2797,2814,2837,2859,2878,2897,2921,2944,2965,2985,3005,3023,3041,3060,3083,3106,3129 \ No newline at end of file diff --git a/radio/src/fonts/480x272/font_midsize.png b/radio/src/fonts/480x272/font_midsize.png deleted file mode 100644 index 3391516af..000000000 Binary files a/radio/src/fonts/480x272/font_midsize.png and /dev/null differ diff --git a/radio/src/fonts/480x272/font_midsize.specs b/radio/src/fonts/480x272/font_midsize.specs deleted file mode 100644 index 9e98f5af3..000000000 --- a/radio/src/fonts/480x272/font_midsize.specs +++ /dev/null @@ -1 +0,0 @@ -0,4,11,20,36,50,69,85,90,99,108,119,134,140,148,155,166,180,194,208,222,236,250,264,278,292,306,313,319,332,346,360,372,382,399,415,432,449,464,478,495,513,521,535,552,566,588,606,624,640,658,674,689,704,721,737,759,775,791,806,814,825,832,843,855,863,877,891,905,920,934,944,958,972,979,986,1000,1007,1029,1043,1058,1072,1087,1096,1109,1118,1132,1145,1164,1177,1189,1202,1219,1226,1240,1240,1240,1240,1240,1240,1240,1240,1240,1240,1240,1240,1240,1240,1240,1240,1240,1240,1240,1240,1240,1240,1254,1268,1282,1290,1304,1321,1335,1353,1368,1385,1399,1414,1428,1441,1455,1465,1478,1490,1504,1512,1526,1540,1555,1569,1583,1599,1609,1624,1639,1656,1672,1687,1703,1720,1729,1744,1762,1780,1797,1814,1831,1846,1864,1878,1892,1906,1921,1938,1955,1973,1987,2001,2015,2029,2043,2051,2065,2080,2093,2106,2119,2136,2153,2168,2182,2200,2218,2233,2248,2263,2277,2291,2306,2323,2340,2358 \ No newline at end of file diff --git a/radio/src/fonts/480x272/font_smlsize.png b/radio/src/fonts/480x272/font_smlsize.png deleted file mode 100644 index 30aef1915..000000000 Binary files a/radio/src/fonts/480x272/font_smlsize.png and /dev/null differ diff --git a/radio/src/fonts/480x272/font_smlsize.specs b/radio/src/fonts/480x272/font_smlsize.specs deleted file mode 100644 index 8802a9945..000000000 --- a/radio/src/fonts/480x272/font_smlsize.specs +++ /dev/null @@ -1 +0,0 @@ -0,4,8,13,22,30,41,51,54,60,66,73,81,85,90,94,100,108,116,124,132,141,149,157,165,173,181,185,189,197,205,213,220,226,236,245,254,264,272,280,290,300,305,313,323,331,343,353,363,372,382,391,400,409,418,427,440,449,458,467,472,479,483,489,496,501,509,517,525,533,541,547,555,563,567,571,579,583,595,603,611,619,627,633,641,646,654,661,672,680,687,695,705,709,717,717,717,717,717,717,717,717,717,717,717,717,717,717,717,717,717,717,717,717,717,717,725,733,741,746,754,764,772,782,790,799,807,816,824,832,840,846,854,861,869,874,882,890,898,906,914,924,931,939,948,957,966,975,984,994,999,1007,1017,1027,1036,1045,1055,1064,1074,1082,1090,1098,1106,1116,1126,1136,1144,1152,1160,1168,1176,1181,1189,1197,1205,1213,1221,1231,1240,1248,1256,1266,1276,1285,1294,1303,1311,1319,1327,1337,1347,1357 \ No newline at end of file diff --git a/radio/src/fonts/480x272/font_stdsize.png b/radio/src/fonts/480x272/font_stdsize.png deleted file mode 100644 index 48646859e..000000000 Binary files a/radio/src/fonts/480x272/font_stdsize.png and /dev/null differ diff --git a/radio/src/fonts/480x272/font_stdsize.specs b/radio/src/fonts/480x272/font_stdsize.specs deleted file mode 100644 index 4b75e6d0d..000000000 --- a/radio/src/fonts/480x272/font_stdsize.specs +++ /dev/null @@ -1 +0,0 @@ -0,4,9,15,26,36,49,60,64,71,78,86,96,100,106,111,119,129,139,149,159,169,179,189,199,209,219,224,228,237,247,256,265,272,284,295,306,318,328,338,350,362,367,377,389,399,414,426,438,449,461,472,483,494,505,516,531,542,553,564,570,578,583,591,600,606,616,626,635,645,654,661,671,681,686,691,701,706,721,731,741,751,761,767,776,782,792,801,814,823,832,841,853,858,867,880,893,906,919,932,945,958,971,984,999,1014,1029,1044,1059,1074,1089,1104,1119,1134,1149,1164,1173,1182,1192,1198,1207,1219,1229,1241,1251,1262,1272,1283,1292,1301,1310,1317,1326,1335,1345,1351,1360,1370,1380,1390,1400,1412,1419,1429,1440,1451,1462,1473,1484,1496,1502,1512,1524,1536,1547,1558,1570,1581,1593,1603,1613,1623,1633,1645,1657,1669,1679,1689,1699,1708,1717,1723,1733,1743,1752,1761,1770,1782,1793,1803,1813,1825,1837,1848,1859,1870,1880,1890,1900,1912,1924,1936 \ No newline at end of file diff --git a/radio/src/fonts/480x272/font_stdsizebold.png b/radio/src/fonts/480x272/font_stdsizebold.png deleted file mode 100644 index 5dfd2a49b..000000000 Binary files a/radio/src/fonts/480x272/font_stdsizebold.png and /dev/null differ diff --git a/radio/src/fonts/480x272/font_stdsizebold.specs b/radio/src/fonts/480x272/font_stdsizebold.specs deleted file mode 100644 index ac4ebc21b..000000000 --- a/radio/src/fonts/480x272/font_stdsizebold.specs +++ /dev/null @@ -1 +0,0 @@ -0,4,9,15,26,36,49,61,65,72,79,88,98,103,110,116,123,133,143,153,163,173,183,193,203,213,223,229,234,243,253,262,271,278,290,301,312,323,333,343,355,367,373,383,395,405,420,432,444,455,467,478,489,500,512,524,539,550,561,572,578,587,592,600,609,615,625,635,644,654,664,671,681,691,696,701,711,716,731,741,751,761,771,778,787,793,803,812,825,835,844,853,864,869,878,891,904,917,930,943,956,969,982,995,1010,1025,1040,1055,1070,1085,1100,1115,1130,1145,1160,1175,1185,1195,1205,1211,1220,1232,1242,1254,1264,1276,1286,1297,1307,1316,1325,1332,1341,1350,1360,1366,1376,1386,1396,1406,1416,1428,1436,1446,1457,1468,1479,1490,1501,1513,1519,1529,1541,1553,1565,1577,1588,1599,1611,1621,1631,1641,1651,1663,1675,1687,1697,1707,1717,1726,1736,1742,1752,1762,1771,1780,1789,1801,1812,1822,1832,1844,1856,1867,1878,1889,1899,1909,1919,1931,1943,1955 \ No newline at end of file diff --git a/radio/src/fonts/480x272/font_tinsize.png b/radio/src/fonts/480x272/font_tinsize.png deleted file mode 100644 index 66a09bd54..000000000 Binary files a/radio/src/fonts/480x272/font_tinsize.png and /dev/null differ diff --git a/radio/src/fonts/480x272/font_tinsize.specs b/radio/src/fonts/480x272/font_tinsize.specs deleted file mode 100644 index 1f6806bfc..000000000 --- a/radio/src/fonts/480x272/font_tinsize.specs +++ /dev/null @@ -1 +0,0 @@ -0,4,7,11,18,24,32,39,42,46,50,55,61,64,68,71,76,82,88,94,100,106,112,118,124,130,136,139,142,148,154,160,165,169,176,183,190,197,203,209,216,223,226,232,239,245,254,261,268,275,282,289,295,302,309,316,325,332,339,346,350,355,358,363,369,373,379,385,391,397,403,408,414,420,423,426,432,435,444,450,456,462,468,472,478,482,488,493,501,507,513,519,526,529,535,535,535,535,535,535,535,535,535,535,535,535,535,535,535,535,535,535,535,535,535,535,541,547,553,557,563,570,576,583,589,596,602,608,614,620,626,631,637,643,649,653,659,665,671,677,683,690,695,701,707,714,721,728,735,742,746,752,759,766,773,780,787,794,801,807,813,819,825,832,839,846,852,858,864,870,876,880,886,892,898,904,910,917,924,930,936,943,950,956,963,970,976,982,988,995,1002,1009 \ No newline at end of file diff --git a/radio/src/fonts/480x272/font_xxlsize.png b/radio/src/fonts/480x272/font_xxlsize.png deleted file mode 100644 index c9a16dfe4..000000000 Binary files a/radio/src/fonts/480x272/font_xxlsize.png and /dev/null differ diff --git a/radio/src/fonts/480x272/font_xxlsize.specs b/radio/src/fonts/480x272/font_xxlsize.specs deleted file mode 100644 index ac4d4eb30..000000000 --- a/radio/src/fonts/480x272/font_xxlsize.specs +++ /dev/null @@ -1 +0,0 @@ -0,4,22,44,83,121,169,212,223,246,270,300,336,353,379,399,424,462,500,538,576,614,652,690,728,766,804,823,841,875,913,947,980,1006,1050,1092,1135,1178,1215,1251,1296,1342,1362,1399,1442,1478,1535,1581,1626,1668,1713,1755,1795,1836,1879,1922,1979,2021,2062,2102,2121,2151,2170,2199,2229,2251,2286,2323,2357,2394,2430,2454,2492,2529,2547,2565,2601,2619,2675,2712,2749,2786,2823,2847,2881,2904,2941,2974,3022,3056,3089,3123,3166,3183,3217,3217,3217,3217,3217,3217,3217,3217,3217,3217,3217,3217,3217,3217,3217,3217,3217,3217,3217,3217,3217,3217,3253,3289,3324,3346,3380,3424,3459,3504,3541,3584,3621,3662,3698,3732,3766,3793,3827,3860,3895,3917,3953,3990,4027,4064,4101,4146,4174,4211,4251,4294,4336,4376,4417,4461,4484,4521,4567,4612,4655,4698,4741,4782,4828,4865,4900,4935,4972,5016,5060,5105,5140,5177,5212,5246,5282,5303,5340,5377,5411,5445,5479,5523,5566,5603,5640,5686,5731,5771,5811,5851,5886,5921,5958,6002,6046,6091 \ No newline at end of file diff --git a/radio/src/fonts/CMakeLists.txt b/radio/src/fonts/CMakeLists.txt index 4f9b537cf..7564a4cc8 100644 --- a/radio/src/fonts/CMakeLists.txt +++ b/radio/src/fonts/CMakeLists.txt @@ -1 +1 @@ -add_subdirectory(480x272) +add_subdirectory(truetype) diff --git a/radio/src/fonts/extra_17px.png b/radio/src/fonts/extra_17px.png new file mode 100644 index 000000000..aa140bdb4 Binary files /dev/null and b/radio/src/fonts/extra_17px.png differ diff --git a/radio/src/fonts/truetype/CMakeLists.txt b/radio/src/fonts/truetype/CMakeLists.txt new file mode 100644 index 000000000..ee4809c66 --- /dev/null +++ b/radio/src/fonts/truetype/CMakeLists.txt @@ -0,0 +1,24 @@ +set(FONT "Roboto/Roboto-Regular") +set(FONT_BOLD "Roboto/Roboto-Bold") +set(FONT_OFFSET 1) + +#set(FONT "Kanit/Kanit-Regular") +#set(FONT_BOLD "Kanit/Kanit-Bold") +#set(FONT_OFFSET -2) + +#set(FONT "Ubuntu/Ubuntu-Regular") +#set(FONT_BOLD "Ubuntu/Ubuntu-Bold") +#set(FONT_OFFSET 1) + +add_truetype_font_target(9 ${FONT} 9 ${FONT_OFFSET}) +add_truetype_font_target(13 ${FONT} 13 ${FONT_OFFSET}) +add_truetype_font_target(15 ${FONT} 15 ${FONT_OFFSET}) +add_truetype_font_target(16 ${FONT} 16 ${FONT_OFFSET}) +add_truetype_font_target(bold16 ${FONT_BOLD} 16 ${FONT_OFFSET}) +add_truetype_font_target(17 ${FONT} 17 ${FONT_OFFSET}) +add_truetype_font_target(bold17 ${FONT_BOLD} 17 ${FONT_OFFSET}) +add_truetype_font_target(24 ${FONT} 24 ${FONT_OFFSET}) +add_truetype_font_target(32 ${FONT_BOLD} 32 ${FONT_OFFSET}) +add_truetype_font_target(64 ${FONT_BOLD} 64 ${FONT_OFFSET}) + +add_custom_target(truetype_fonts DEPENDS ${truetype_fonts_targets}) diff --git a/radio/src/fonts/truetype/font_13.png b/radio/src/fonts/truetype/font_13.png new file mode 100644 index 000000000..13a7d3015 Binary files /dev/null and b/radio/src/fonts/truetype/font_13.png differ diff --git a/radio/src/fonts/truetype/font_13.specs b/radio/src/fonts/truetype/font_13.specs new file mode 100644 index 000000000..13f28e0a0 --- /dev/null +++ b/radio/src/fonts/truetype/font_13.specs @@ -0,0 +1 @@ +17,0,4,9,15,24,33,44,54,58,64,70,77,86,90,95,100,107,116,125,134,143,152,161,170,179,188,197,202,206,214,223,231,239,245,255,265,275,285,294,303,313,324,329,338,348,356,369,380,390,400,410,419,428,437,447,457,470,480,489,498,503,510,515,522,529,535,544,553,561,570,578,584,593,602,607,613,621,626,639,648,657,666,675,681,689,695,704,712,723,731,739,747,757,762,770,770,770,770,770,770,770,770,770,770,770,770,770,770,770,770,770,770,770,770,770,770,778,786,795,801,809,819,828,838,847,857,866,875,883,891,899,905,913,921,930,935,943,952,961,970,979,989,995,1004,1013,1023,1032,1041,1050,1060,1065,1074,1085,1095,1105,1115,1125,1134,1145,1154,1163,1172,1181,1191,1201,1211,1220,1229,1238,1246,1254,1259,1268,1277,1285,1293,1301,1311,1321,1330,1338,1349,1359,1368,1377,1386,1395,1404,1413,1423,1433,1443 \ No newline at end of file diff --git a/radio/src/fonts/truetype/font_15.png b/radio/src/fonts/truetype/font_15.png new file mode 100644 index 000000000..6c904c941 Binary files /dev/null and b/radio/src/fonts/truetype/font_15.png differ diff --git a/radio/src/fonts/truetype/font_15.specs b/radio/src/fonts/truetype/font_15.specs new file mode 100644 index 000000000..662f43195 --- /dev/null +++ b/radio/src/fonts/truetype/font_15.specs @@ -0,0 +1 @@ +19,0,4,9,15,26,36,48,59,63,70,77,85,95,99,105,110,118,128,138,148,158,168,178,188,198,208,218,223,228,237,247,256,265,272,283,294,305,316,326,336,348,360,366,376,387,397,412,424,436,447,459,470,480,490,501,512,527,538,549,559,564,572,577,585,593,599,609,619,628,638,647,654,664,674,679,685,694,699,714,724,734,744,754,761,770,776,786,795,808,817,826,835,847,852,861,861,861,861,861,861,861,861,861,861,861,861,861,861,861,861,861,861,861,861,861,861,870,879,889,896,905,916,926,938,948,959,969,979,988,997,1006,1013,1022,1031,1041,1047,1056,1066,1076,1086,1096,1107,1114,1124,1134,1145,1156,1166,1177,1188,1194,1204,1216,1228,1239,1250,1261,1271,1283,1293,1303,1313,1323,1334,1345,1357,1367,1377,1387,1396,1405,1411,1421,1431,1440,1449,1458,1469,1480,1490,1500,1512,1524,1534,1544,1554,1564,1574,1584,1595,1606,1618 \ No newline at end of file diff --git a/radio/src/fonts/truetype/font_16.png b/radio/src/fonts/truetype/font_16.png new file mode 100644 index 000000000..a867e6bb9 Binary files /dev/null and b/radio/src/fonts/truetype/font_16.png differ diff --git a/radio/src/fonts/truetype/font_16.specs b/radio/src/fonts/truetype/font_16.specs new file mode 100644 index 000000000..15dd71f47 --- /dev/null +++ b/radio/src/fonts/truetype/font_16.specs @@ -0,0 +1 @@ +20,0,4,10,17,28,38,51,62,66,73,80,88,99,104,110,116,124,134,144,154,164,174,184,194,204,214,224,229,234,244,254,264,273,280,292,303,315,327,338,348,360,373,379,389,401,411,426,439,451,463,475,486,497,508,520,532,548,560,571,582,588,596,602,610,619,625,635,645,655,666,676,683,693,703,708,714,724,729,745,755,766,776,787,794,804,811,821,830,844,853,862,871,883,888,898,911,924,937,950,963,976,989,1002,1015,1030,1045,1060,1075,1090,1105,1120,1135,1150,1165,1180,1195,1205,1215,1225,1232,1242,1254,1264,1276,1287,1299,1309,1320,1330,1340,1350,1357,1366,1375,1385,1391,1401,1411,1422,1432,1442,1454,1461,1472,1483,1495,1506,1517,1528,1540,1546,1557,1570,1582,1594,1606,1618,1629,1642,1652,1662,1672,1683,1695,1707,1719,1729,1739,1749,1759,1769,1775,1785,1796,1806,1815,1824,1836,1848,1859,1869,1882,1894,1905,1916,1927,1937,1947,1958,1970,1982,1994 \ No newline at end of file diff --git a/radio/src/fonts/truetype/font_17.png b/radio/src/fonts/truetype/font_17.png new file mode 100644 index 000000000..f04d109d3 Binary files /dev/null and b/radio/src/fonts/truetype/font_17.png differ diff --git a/radio/src/fonts/truetype/font_17.specs b/radio/src/fonts/truetype/font_17.specs new file mode 100644 index 000000000..ce5cf396d --- /dev/null +++ b/radio/src/fonts/truetype/font_17.specs @@ -0,0 +1 @@ +21,0,4,10,17,29,40,54,66,70,77,84,93,104,109,115,121,129,140,151,162,173,184,195,206,217,228,239,245,250,260,271,281,291,299,312,324,337,350,361,372,385,399,405,416,428,439,455,469,482,494,507,519,531,543,556,568,585,597,609,621,627,635,641,650,659,666,677,688,698,709,719,726,737,748,754,761,771,777,793,804,815,826,837,844,854,861,872,882,896,906,916,926,939,945,955,968,981,994,1007,1020,1033,1046,1059,1072,1087,1102,1117,1132,1147,1162,1177,1192,1207,1222,1237,1252,1262,1272,1283,1290,1300,1313,1324,1337,1348,1361,1372,1384,1394,1404,1414,1421,1431,1441,1452,1458,1468,1479,1490,1501,1512,1524,1532,1543,1555,1568,1580,1592,1604,1617,1623,1634,1648,1661,1674,1687,1700,1712,1726,1737,1748,1759,1770,1783,1796,1809,1820,1831,1842,1852,1862,1868,1879,1890,1900,1910,1920,1933,1946,1957,1968,1982,1995,2007,2019,2031,2042,2053,2064,2077,2090,2103 \ No newline at end of file diff --git a/radio/src/fonts/truetype/font_24.png b/radio/src/fonts/truetype/font_24.png new file mode 100644 index 000000000..2682878d0 Binary files /dev/null and b/radio/src/fonts/truetype/font_24.png differ diff --git a/radio/src/fonts/truetype/font_24.specs b/radio/src/fonts/truetype/font_24.specs new file mode 100644 index 000000000..7d660a983 --- /dev/null +++ b/radio/src/fonts/truetype/font_24.specs @@ -0,0 +1 @@ +28,0,4,12,21,37,52,71,87,93,103,113,125,140,146,154,162,173,188,203,218,233,248,263,278,293,308,323,330,337,351,366,380,393,403,420,436,453,470,485,500,518,537,545,560,577,591,613,632,650,667,685,701,717,733,750,767,790,807,823,839,847,858,866,878,890,899,914,929,943,958,972,982,997,1012,1019,1027,1041,1048,1071,1086,1101,1116,1131,1141,1155,1164,1179,1192,1212,1225,1238,1251,1269,1276,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1304,1318,1333,1342,1356,1373,1388,1406,1421,1438,1453,1469,1483,1497,1511,1521,1534,1547,1562,1570,1584,1599,1614,1629,1644,1661,1671,1686,1702,1719,1735,1751,1767,1784,1792,1807,1826,1844,1861,1878,1895,1911,1930,1945,1960,1975,1990,2007,2024,2042,2057,2072,2087,2101,2115,2123,2138,2153,2167,2180,2193,2210,2227,2242,2256,2275,2293,2309,2325,2341,2356,2371,2386,2403,2420,2438 \ No newline at end of file diff --git a/radio/src/fonts/truetype/font_32.png b/radio/src/fonts/truetype/font_32.png new file mode 100644 index 000000000..4001c36dc Binary files /dev/null and b/radio/src/fonts/truetype/font_32.png differ diff --git a/radio/src/fonts/truetype/font_32.specs b/radio/src/fonts/truetype/font_32.specs new file mode 100644 index 000000000..7eb57e984 --- /dev/null +++ b/radio/src/fonts/truetype/font_32.specs @@ -0,0 +1 @@ +36,0,4,14,26,47,67,92,114,121,134,147,163,182,191,205,216,230,250,270,290,310,330,350,370,390,410,430,441,451,469,489,507,524,538,561,583,605,627,646,665,688,712,723,742,764,783,813,837,861,883,907,929,950,971,994,1016,1045,1067,1088,1109,1119,1135,1145,1160,1176,1188,1207,1227,1245,1265,1284,1297,1317,1336,1346,1358,1377,1387,1416,1435,1455,1475,1495,1508,1526,1538,1557,1575,1600,1618,1636,1654,1676,1686,1704,1704,1704,1704,1704,1704,1704,1704,1704,1704,1704,1704,1704,1704,1704,1704,1704,1704,1704,1704,1704,1704,1723,1742,1761,1775,1793,1816,1835,1859,1879,1902,1921,1943,1962,1980,1998,2012,2030,2048,2067,2079,2098,2117,2137,2156,2175,2198,2213,2232,2253,2275,2297,2318,2339,2362,2374,2393,2417,2441,2464,2487,2509,2530,2554,2573,2592,2611,2631,2654,2677,2701,2720,2739,2758,2776,2795,2806,2825,2845,2863,2881,2899,2922,2944,2963,2982,3006,3030,3051,3072,3093,3112,3131,3151,3174,3197,3221 \ No newline at end of file diff --git a/radio/src/fonts/truetype/font_64.png b/radio/src/fonts/truetype/font_64.png new file mode 100644 index 000000000..6e4e12a75 Binary files /dev/null and b/radio/src/fonts/truetype/font_64.png differ diff --git a/radio/src/fonts/truetype/font_64.specs b/radio/src/fonts/truetype/font_64.specs new file mode 100644 index 000000000..fb72ff6da --- /dev/null +++ b/radio/src/fonts/truetype/font_64.specs @@ -0,0 +1 @@ +68,0,4,23,45,85,123,172,215,227,251,275,305,341,358,384,404,430,468,506,544,582,620,658,696,734,772,810,830,848,882,920,955,988,1014,1059,1101,1144,1187,1224,1261,1306,1353,1373,1410,1453,1489,1547,1594,1640,1683,1729,1771,1812,1853,1897,1940,1997,2039,2080,2120,2139,2169,2188,2217,2247,2270,2306,2344,2379,2417,2453,2477,2515,2552,2570,2591,2627,2645,2702,2739,2777,2815,2853,2878,2912,2935,2972,3006,3055,3089,3123,3157,3200,3218,3253,3253,3253,3253,3253,3253,3253,3253,3253,3253,3253,3253,3253,3253,3253,3253,3253,3253,3253,3253,3253,3253,3289,3325,3361,3387,3422,3467,3503,3549,3587,3631,3668,3710,3746,3780,3815,3842,3876,3910,3946,3968,4004,4041,4079,4116,4153,4198,4226,4263,4304,4347,4389,4429,4470,4515,4537,4574,4621,4667,4711,4755,4798,4839,4886,4923,4959,4995,5033,5078,5123,5169,5205,5242,5278,5313,5349,5370,5407,5445,5479,5513,5547,5592,5635,5672,5709,5756,5802,5843,5883,5923,5959,5995,6033,6078,6123,6169 \ No newline at end of file diff --git a/radio/src/fonts/truetype/font_9.png b/radio/src/fonts/truetype/font_9.png new file mode 100644 index 000000000..add3b9eb6 Binary files /dev/null and b/radio/src/fonts/truetype/font_9.png differ diff --git a/radio/src/fonts/truetype/font_9.specs b/radio/src/fonts/truetype/font_9.specs new file mode 100644 index 000000000..e8dfa6851 --- /dev/null +++ b/radio/src/fonts/truetype/font_9.specs @@ -0,0 +1 @@ +13,0,4,8,12,19,26,34,41,44,49,54,59,66,69,73,77,82,89,96,103,110,117,124,131,138,145,152,156,159,165,171,177,183,188,195,202,209,216,223,229,237,245,249,255,262,268,277,285,293,300,308,315,322,329,336,343,352,359,366,373,377,382,386,391,397,401,407,414,420,427,433,438,445,451,455,460,466,470,479,485,492,499,506,511,517,521,527,533,541,547,553,559,567,571,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,577,583,589,595,600,606,613,619,627,634,641,647,654,660,666,672,677,683,689,695,699,705,711,718,724,730,737,742,749,756,763,770,777,784,791,795,802,810,818,825,832,839,846,854,860,866,872,879,886,893,901,907,913,919,925,931,935,941,948,954,960,966,973,980,987,993,1001,1009,1016,1023,1030,1036,1042,1049,1056,1063,1071 \ No newline at end of file diff --git a/radio/src/fonts/truetype/font_bold16.png b/radio/src/fonts/truetype/font_bold16.png new file mode 100644 index 000000000..a71570098 Binary files /dev/null and b/radio/src/fonts/truetype/font_bold16.png differ diff --git a/radio/src/fonts/truetype/font_bold16.specs b/radio/src/fonts/truetype/font_bold16.specs new file mode 100644 index 000000000..9e51207e0 --- /dev/null +++ b/radio/src/fonts/truetype/font_bold16.specs @@ -0,0 +1 @@ +20,0,4,10,17,28,39,52,64,68,75,82,91,101,106,114,120,128,139,150,161,172,183,194,205,216,227,238,244,250,260,271,281,290,298,310,322,334,346,356,366,378,391,397,407,419,429,445,458,471,483,496,508,519,530,542,554,569,581,592,603,609,618,624,632,641,648,658,669,679,690,700,707,718,728,734,741,751,757,772,782,793,804,815,822,832,839,849,859,872,882,892,902,914,920,930,943,956,969,982,995,1008,1021,1034,1047,1062,1077,1092,1107,1122,1137,1152,1167,1182,1197,1212,1227,1237,1247,1257,1265,1275,1287,1297,1310,1321,1333,1343,1355,1365,1375,1385,1393,1403,1413,1423,1430,1440,1450,1461,1471,1481,1493,1501,1511,1522,1534,1546,1557,1568,1580,1587,1597,1610,1623,1635,1647,1659,1670,1683,1693,1703,1713,1724,1736,1748,1761,1771,1781,1791,1801,1811,1817,1827,1838,1848,1858,1868,1880,1892,1902,1912,1925,1938,1949,1960,1971,1981,1991,2002,2014,2026,2039 \ No newline at end of file diff --git a/radio/src/fonts/truetype/font_bold17.png b/radio/src/fonts/truetype/font_bold17.png new file mode 100644 index 000000000..bcdc2bb0c Binary files /dev/null and b/radio/src/fonts/truetype/font_bold17.png differ diff --git a/radio/src/fonts/truetype/font_bold17.specs b/radio/src/fonts/truetype/font_bold17.specs new file mode 100644 index 000000000..1a8b4990d --- /dev/null +++ b/radio/src/fonts/truetype/font_bold17.specs @@ -0,0 +1 @@ +21,0,4,10,17,29,40,54,67,71,78,85,94,105,111,119,125,134,145,156,167,178,189,200,211,222,233,244,250,256,266,277,287,297,305,318,330,343,356,367,378,391,405,411,422,434,445,461,474,487,499,512,524,536,548,561,574,590,602,614,626,632,641,647,656,665,672,683,694,704,715,726,734,745,756,762,769,780,786,802,813,824,835,846,854,864,871,882,892,906,916,926,936,949,955,965,978,991,1004,1017,1030,1043,1056,1069,1082,1097,1112,1127,1142,1157,1172,1187,1202,1217,1232,1247,1262,1273,1284,1295,1303,1313,1326,1337,1350,1361,1374,1385,1397,1408,1418,1428,1436,1446,1456,1467,1474,1485,1496,1507,1518,1529,1542,1551,1562,1574,1587,1599,1611,1623,1636,1643,1654,1667,1680,1693,1706,1719,1731,1744,1755,1766,1777,1788,1801,1814,1827,1838,1849,1860,1870,1881,1888,1899,1910,1920,1930,1940,1953,1966,1977,1988,2001,2014,2026,2038,2050,2061,2072,2083,2096,2109,2122 \ No newline at end of file diff --git a/radio/src/functions.cpp b/radio/src/functions.cpp index bd840fc4a..3bb18805c 100644 --- a/radio/src/functions.cpp +++ b/radio/src/functions.cpp @@ -176,6 +176,9 @@ void evalFunctions(const CustomFunctionData * functions, CustomFunctionsContext case FUNC_INSTANT_TRIM: newActiveFunctions |= (1 << FUNCTION_INSTANT_TRIM); if (!isFunctionActive(FUNCTION_INSTANT_TRIM)) { +#if defined(COLORLCD) + #warning IS_INSTANT_TRIM_ALLOWED() is always false +#endif if (IS_INSTANT_TRIM_ALLOWED()) { instantTrim(); } diff --git a/radio/src/gui/128x64/lcd.cpp b/radio/src/gui/128x64/lcd.cpp index 00d64a9c8..d40843195 100644 --- a/radio/src/gui/128x64/lcd.cpp +++ b/radio/src/gui/128x64/lcd.cpp @@ -557,10 +557,10 @@ void lcdDrawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat, LcdFlags y = (y & 0x07); if (y) { ASSERT_IN_DISPLAY(p); - uint8_t msk = ~(BITMASK(y)-1); + uint8_t msk = ~(bfBitmask(y)); h -= 8-y; if (h < 0) - msk -= ~(BITMASK(8+h)-1); + msk -= ~(bfBitmask(8 + h)); lcdMaskPoint(p, msk & pat, att); p += LCD_W; } @@ -572,7 +572,7 @@ void lcdDrawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat, LcdFlags } if (h > 0) { ASSERT_IN_DISPLAY(p); - lcdMaskPoint(p, (BITMASK(h)-1) & pat, att); + lcdMaskPoint(p, (bfBitmask(h)) & pat, att); } } @@ -953,7 +953,7 @@ void lcdDrawPoint(coord_t x, coord_t y, LcdFlags att) { uint8_t * p = &displayBuf[ y / 8 * LCD_W + x ]; if (p < DISPLAY_END) { - lcdMaskPoint(p, BITMASK(y % 8), att); + lcdMaskPoint(p, bfBit(y % 8), att); } } @@ -975,7 +975,7 @@ void lcdDrawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlag if (x+w > LCD_W) { w = LCD_W - x; } uint8_t *p = &displayBuf[ y / 8 * LCD_W + x ]; - uint8_t msk = BITMASK(y%8); + uint8_t msk = bfBit(y % 8); while (w--) { if(pat&1) { lcdMaskPoint(p, msk, att); diff --git a/radio/src/gui/128x64/lcd.h b/radio/src/gui/128x64/lcd.h index 4f3da257a..38b648e00 100644 --- a/radio/src/gui/128x64/lcd.h +++ b/radio/src/gui/128x64/lcd.h @@ -24,6 +24,11 @@ #include typedef int coord_t; +struct point_t +{ + coord_t x; + coord_t y; +}; typedef uint32_t LcdFlags; typedef uint8_t display_t; diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 6e8709dea..87f09c552 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -46,16 +46,19 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_NAME, ITEM_MODEL_SETUP_TIMER1, ITEM_MODEL_SETUP_TIMER1_NAME, + ITEM_MODEL_SETUP_TIMER1_START, ITEM_MODEL_SETUP_TIMER1_PERSISTENT, ITEM_MODEL_SETUP_TIMER1_MINUTE_BEEP, ITEM_MODEL_SETUP_TIMER1_COUNTDOWN_BEEP, ITEM_MODEL_SETUP_TIMER2, ITEM_MODEL_SETUP_TIMER2_NAME, + ITEM_MODEL_SETUP_TIMER2_START, ITEM_MODEL_SETUP_TIMER2_PERSISTENT, ITEM_MODEL_SETUP_TIMER2_MINUTE_BEEP, ITEM_MODEL_SETUP_TIMER2_COUNTDOWN_BEEP, ITEM_MODEL_SETUP_TIMER3, ITEM_MODEL_SETUP_TIMER3_NAME, + ITEM_MODEL_SETUP_TIMER3_START, ITEM_MODEL_SETUP_TIMER3_PERSISTENT, ITEM_MODEL_SETUP_TIMER3_MINUTE_BEEP, ITEM_MODEL_SETUP_TIMER3_COUNTDOWN_BEEP, @@ -207,7 +210,7 @@ inline uint8_t EXTERNAL_MODULE_TYPE_ROW() } #define POT_WARN_ROWS ((g_model.potsWarnMode) ? (uint8_t)(NUM_POTS+NUM_SLIDERS) : (uint8_t)0) -#define TIMER_ROWS 2, 0, 0, 0, 0 +#define TIMER_ROWS 1, 0, 1, 0, 0, 0 #if defined(PCBSKY9X) #define EXTRA_MODULE_ROWS LABEL(ExtraModule), 1, 2, @@ -409,41 +412,42 @@ void menuModelSetup(event_t event) unsigned int timerIdx = (k>=ITEM_MODEL_SETUP_TIMER3 ? 2 : (k>=ITEM_MODEL_SETUP_TIMER2 ? 1 : 0)); TimerData * timer = &g_model.timers[timerIdx]; drawStringWithIndex(0*FW, y, STR_TIMER, timerIdx+1); - drawTimerMode(MODEL_SETUP_2ND_COLUMN, y, timer->mode, menuHorizontalPosition==0 ? attr : 0); - drawTimer(MODEL_SETUP_2ND_COLUMN+5*FW-2+5*FWNUM+1, y, timer->start, RIGHT | (menuHorizontalPosition==1 ? attr : 0), menuHorizontalPosition==2 ? attr : 0); + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VTMRMODES, timer->mode, menuHorizontalPosition==0 ? attr : 0); + drawSwitch(MODEL_SETUP_2ND_COLUMN+5*FW, y, timer->swtch, menuHorizontalPosition==1 ? attr : 0); + if (attr && s_editMode > 0) { + switch (menuHorizontalPosition) { + case 0: + CHECK_INCDEC_MODELVAR_ZERO(event, timer->mode, TMRMODE_MAX); + break; + case 1: + CHECK_INCDEC_MODELSWITCH(event, timer->swtch, SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES, isSwitchAvailableInMixes); + break; + } + } + break; + } + + case ITEM_MODEL_SETUP_TIMER1_START: + case ITEM_MODEL_SETUP_TIMER2_START: + case ITEM_MODEL_SETUP_TIMER3_START: + { + lcdDrawText(INDENT_WIDTH, y, STR_START); + TimerData *timer = &g_model.timers[k >= ITEM_MODEL_SETUP_TIMER3 ? 2 : (k >= ITEM_MODEL_SETUP_TIMER2 ? 1 : 0)]; + drawTimer(MODEL_SETUP_2ND_COLUMN, y, timer->start, menuHorizontalPosition == 0 ? attr : 0, menuHorizontalPosition == 1 ? attr : 0); if (attr && s_editMode > 0) { div_t qr = div(timer->start, 60); switch (menuHorizontalPosition) { case 0: - { - swsrc_t timerMode = timer->mode; - if (timerMode < 0) - timerMode -= TMRMODE_COUNT-1; - CHECK_INCDEC_MODELVAR_CHECK(event, timerMode, -TMRMODE_COUNT-SWSRC_LAST+1, TMRMODE_COUNT+SWSRC_LAST-1, isSwitchAvailableInTimers); - if (timerMode < 0) - timerMode += TMRMODE_COUNT-1; - timer->mode = timerMode; -#if defined(AUTOSWITCH) - if (s_editMode>0) { - int8_t val = timer->mode - (TMRMODE_COUNT-1); - int8_t switchVal = checkIncDecMovedSwitch(val); - if (val != switchVal) { - timer->mode = switchVal + (TMRMODE_COUNT-1); - storageDirty(EE_MODEL); - } - } -#endif - break; - } - case 1: CHECK_INCDEC_MODELVAR_ZERO(event, qr.quot, 539); // 8:59 - timer->start = qr.rem + qr.quot*60; + timer->start = qr.rem + qr.quot * 60; break; - case 2: - qr.rem -= checkIncDecModel(event, qr.rem+2, 1, 62)-2; - timer->start -= qr.rem ; - if ((int16_t)timer->start < 0) timer->start=0; - if ((int16_t)timer->start > 5999) timer->start=32399; // 8:59:59 + case 1: + qr.rem -= checkIncDecModel(event, qr.rem + 2, 1, 62) - 2; + timer->start -= qr.rem; + if ((int16_t) timer->start < 0) + timer->start = 0; + if ((int16_t) timer->start > 5999) + timer->start = 32399; // 8:59:59 break; } } diff --git a/radio/src/gui/128x64/radio_setup.cpp b/radio/src/gui/128x64/radio_setup.cpp index aabf4dc5e..9f5e3346e 100644 --- a/radio/src/gui/128x64/radio_setup.cpp +++ b/radio/src/gui/128x64/radio_setup.cpp @@ -522,7 +522,7 @@ void menuRadioSetup(event_t event) if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.pwrOffSpeed, -1, 2); break; #endif - + #if defined(PXX2) case ITEM_RADIO_SETUP_OWNER_ID: editSingleName(RADIO_SETUP_2ND_COLUMN, y, STR_OWNER_ID, g_eeGeneral.ownerRegistrationID, PXX2_LEN_REGISTRATION_ID, event, attr); diff --git a/radio/src/gui/128x64/view_main.cpp b/radio/src/gui/128x64/view_main.cpp index a54c00fa6..5251dfae4 100644 --- a/radio/src/gui/128x64/view_main.cpp +++ b/radio/src/gui/128x64/view_main.cpp @@ -179,12 +179,10 @@ void drawTimerWithMode(coord_t x, coord_t y, uint8_t index) drawTimer(x, y, timerState.val, att, att); uint8_t xLabel = (negative ? x-56 : x-49); uint8_t len = zlen(timer.name, LEN_TIMER_NAME); - if (len > 0) { + if (len > 0) lcdDrawSizedText(xLabel, y+FH, timer.name, len, RIGHT | ZCHAR); - } - else { - drawTimerMode(xLabel, y+FH, timer.mode, RIGHT); - } + else + lcdDrawTextAtIndex(xLabel, y+FH, STR_VTMRMODES, timer.mode, RIGHT); } } diff --git a/radio/src/gui/212x64/lcd.h b/radio/src/gui/212x64/lcd.h index 8d5fc785c..7b92acc7e 100644 --- a/radio/src/gui/212x64/lcd.h +++ b/radio/src/gui/212x64/lcd.h @@ -22,6 +22,11 @@ #define _LCD_H_ typedef int coord_t; +struct point_t +{ + coord_t x; + coord_t y; +}; typedef uint32_t LcdFlags; typedef uint8_t display_t; diff --git a/radio/src/gui/212x64/model_setup.cpp b/radio/src/gui/212x64/model_setup.cpp index 8b933dfbb..0ddee2461 100644 --- a/radio/src/gui/212x64/model_setup.cpp +++ b/radio/src/gui/212x64/model_setup.cpp @@ -155,11 +155,12 @@ void onModelSetupBitmapMenu(const char * result) } } +#warning "TODO Timer mode needs more work" void editTimerMode(int timerIdx, coord_t y, LcdFlags attr, event_t event) { TimerData & timer = g_model.timers[timerIdx]; drawStringWithIndex(0*FW, y, STR_TIMER, timerIdx+1); - drawTimerMode(MODEL_SETUP_2ND_COLUMN, y, timer.mode, menuHorizontalPosition==0 ? attr : 0); + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VTMRMODES, timer.mode, menuHorizontalPosition==0 ? attr : 0); drawTimer(MODEL_SETUP_3RD_COLUMN, y, timer.start, menuHorizontalPosition==1 ? attr|TIMEHOUR : TIMEHOUR, menuHorizontalPosition==2 ? attr|TIMEHOUR : TIMEHOUR); if (attr && menuHorizontalPosition < 0) { lcdDrawFilledRect(MODEL_SETUP_2ND_COLUMN-1, y-1, 13*FW-3, FH+1); @@ -170,8 +171,7 @@ void editTimerMode(int timerIdx, coord_t y, LcdFlags attr, event_t event) case 0: { swsrc_t timerMode = timer.mode; - if (timerMode < 0) timerMode -= TMRMODE_COUNT-1; - CHECK_INCDEC_MODELVAR_CHECK(event, timerMode, -TMRMODE_COUNT-SWSRC_LAST+1, TMRMODE_COUNT+SWSRC_LAST-1, isSwitchAvailableInTimers); + // TODO later CHECK_INCDEC_MODELVAR_CHECK(event, timerMode, -TMRMODE_COUNT-SWSRC_LAST+1, TMRMODE_COUNT+SWSRC_LAST-1, isSwitchAvailableInTimers); if (timerMode < 0) timerMode += TMRMODE_COUNT-1; timer.mode = timerMode; #if defined(AUTOSWITCH) diff --git a/radio/src/gui/212x64/view_main.cpp b/radio/src/gui/212x64/view_main.cpp index dd6900206..364d7fd3d 100644 --- a/radio/src/gui/212x64/view_main.cpp +++ b/radio/src/gui/212x64/view_main.cpp @@ -314,7 +314,7 @@ void displayTimers() lcdDrawSizedText(TIMERS_X, y-7, timerData.name, LEN_TIMER_NAME, ZCHAR|SMLSIZE); } else { - drawTimerMode(TIMERS_X, y-7, timerData.mode, SMLSIZE); + lcdDrawTextAtIndex(TIMERS_X, y-7, STR_VTMRMODES, timerData.mode, SMLSIZE); } drawTimer(TIMERS_X, y, timerState.val, TIMEHOUR|MIDSIZE|LEFT, TIMEHOUR|MIDSIZE|LEFT); if (timerData.persistent) { diff --git a/radio/src/gui/480x272/CMakeLists.txt b/radio/src/gui/480x272/CMakeLists.txt deleted file mode 100644 index e3e258285..000000000 --- a/radio/src/gui/480x272/CMakeLists.txt +++ /dev/null @@ -1,43 +0,0 @@ -set(GUI_SRC - ${GUI_SRC} - lcd.cpp - splash.cpp - fonts.cpp - popups.cpp - widgets.cpp - menu_model.cpp - model_select.cpp - model_setup.cpp - model_module_options.cpp - model_receiver_options.cpp - model_failsafe.cpp - model_logical_switches.cpp - model_special_functions.cpp - model_outputs.cpp - model_telemetry.cpp - model_telemetry_sensor.cpp - menu_radio.cpp - radio_setup.cpp - radio_trainer.cpp - radio_version.cpp - radio_hardware.cpp - radio_calibration.cpp - view_main.cpp - view_statistics.cpp - ) - -if(FLIGHT_MODES) - set(GUI_SRC - ${GUI_SRC} - model_flightmodes.cpp - ) -endif() - -if(LUA STREQUAL YES) - set(GUI_SRC - ${GUI_SRC} - model_custom_scripts.cpp - ) -endif() - -include(gui/common/colorlcd/CMakeLists.txt) diff --git a/radio/src/gui/480x272/lcd.cpp b/radio/src/gui/480x272/lcd.cpp deleted file mode 100644 index c260bf54b..000000000 --- a/radio/src/gui/480x272/lcd.cpp +++ /dev/null @@ -1,513 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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 -#include -#include "opentx.h" -#include "strhelpers.h" - -#if defined(SIMU) -display_t displayBuf[DISPLAY_BUFFER_SIZE]; -#endif - -uint16_t lcdColorTable[LCD_COLOR_COUNT]; - -coord_t lcdNextPos; - -uint8_t getMappedChar(uint8_t c) -{ - uint8_t result; - if (c == 0) - result = 0; -#if defined(TRANSLATIONS_FR) - else if (c >= 0x80 && c <= 0x84) { - result = 115 + c - 0x80; - } -#elif defined(TRANSLATIONS_DE) - else if (c >= 0x80 && c <= 0x86) { - result = 120 + c - 0x80; - } -#elif defined(TRANSLATIONS_CZ) - else if (c >= 0x80 && c <= 0x80+29) { - result = 127 + c - 0x80; - } -#elif defined(TRANSLATIONS_ES) - else if (c >= 0x80 && c <= 0x81) { - result = 157 + c - 0x80; - } -#elif defined(TRANSLATIONS_FI) - else if (c >= 0x80 && c <= 0x85) { - result = 159 + c - 0x80; - } -#elif defined(TRANSLATIONS_IT) - else if (c >= 0x80 && c <= 0x81) { - result = 165 + c - 0x80; - } -#elif defined(TRANSLATIONS_PL) - else if (c >= 0x80 && c <= 0x80+17) { - result = 167 + c - 0x80; - } -#elif defined(TRANSLATIONS_PT) - else if (c >= 0x80 && c <= 0x80+21) { - result = 185 + c - 0x80; - } -#elif defined(TRANSLATIONS_SE) - else if (c >= 0x80 && c <= 0x85) { - result = 207 + c - 0x80; - } -#endif - else if (c < 0xC0) - result = c - 0x20; - else - result = c - 0xC0 + 96; - // TRACE("getMappedChar '%c' (%d) = %d", c, c, result); - return result; -} - -int getFontPatternWidth(const uint16_t * spec, uint8_t index) -{ - return spec[index+1] - spec[index]; -} - -int getCharWidth(uint8_t c, const uint16_t * spec) -{ - return getFontPatternWidth(spec, getMappedChar(c)); -} - -void lcdPutFontPattern(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, int index, LcdFlags flags) -{ - coord_t offset = spec[index]; - coord_t width = spec[index+1] - offset; - if (width > 0) lcdDrawBitmapPattern(x, y, font, flags, offset, width); - lcdNextPos = x + width; -} - -void lcdDrawChar(coord_t x, coord_t y, char c, LcdFlags flags) -{ - uint32_t fontindex = FONTINDEX(flags); - const unsigned char * font = fontsTable[fontindex]; - const uint16_t * fontspecs = fontspecsTable[fontindex]; - lcdPutFontPattern(x, y, font, fontspecs, getMappedChar(c), flags); -} - -uint8_t getStringInfo(const char *s) -{ - uint8_t result = 0; - for (int i=0; s[i]; i++) { - result += s[i]; - } - return result; -} - -uint8_t getFontHeight(LcdFlags flags) -{ - static const uint8_t heightTable[16] = { 9, 13, 16, 24, 32, 64, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12 }; - return heightTable[FONTINDEX(flags)]; -} - -int getTextWidth(const char * s, int len, LcdFlags flags) -{ - const uint16_t * specs = fontspecsTable[FONTINDEX(flags)]; - - int result = 0; - for (int i=0; len==0 || i9 ? c+'A'-10 : c+'0'; - val >>= 4; - } - s[4] = '\0'; - lcdDrawText(x, y, s, flags); -} - -void lcdDrawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags, uint8_t len, const char * prefix, const char * suffix) -{ - char str[48+1]; // max=16 for the prefix, 16 chars for the number, 16 chars for the suffix - char *s = str+32; - *s = '\0'; - int idx = 0; - int mode = MODE(flags); - bool neg = false; - if (val < 0) { - val = -val; - neg = true; - } - do { - *--s = '0' + (val % 10); - ++idx; - val /= 10; - if (mode!=0 && idx==mode) { - mode = 0; - *--s = '.'; - if (val==0) - *--s = '0'; - } - } while (val!=0 || mode>0 || (mode==MODE(LEADING0) && idx>1; - int y = dxabs>>1; - int px = x1; - int py = y1; - - if (dxabs >= dyabs) { - /* the line is more horizontal than vertical */ - for (int i=0; i<=dxabs; i++) { - if ((1<<(px%8)) & pat) { - lcdDrawPoint(px, py, att); - } - y += dyabs; - if (y>=dxabs) { - y -= dxabs; - py += sdy; - } - px += sdx; - } - } - else { - /* the line is more vertical than horizontal */ - for (int i=0; i<=dyabs; i++) { - if ((1<<(py%8)) & pat) { - lcdDrawPoint(px, py, att); - } - x += dxabs; - if (x >= dyabs) { - x -= dyabs; - px += sdx; - } - py += sdy; - } - } -} - -#if !defined(BOOT) -void drawRtcTime(coord_t x, coord_t y, LcdFlags flags) -{ - drawTimer(x, y, getValue(MIXSRC_TX_TIME), flags); -} - -void drawTimer(coord_t x, coord_t y, int32_t tme, LcdFlags flags) -{ - char str[LEN_TIMER_STRING]; - getTimerString(str, tme, (flags & TIMEHOUR) != 0); - lcdDrawText(x, y, str, flags); -} - -void putsStickName(coord_t x, coord_t y, uint8_t idx, LcdFlags att) -{ - uint8_t length = STR_VSRCRAW[0]; - lcdDrawSizedText(x, y, STR_VSRCRAW+2+length*(idx+1), length-1, att); -} - -void drawSource(coord_t x, coord_t y, mixsrc_t idx, LcdFlags flags) -{ - char s[16]; - getSourceString(s, idx); - lcdDrawText(x, y, s, flags); -} - -void putsChnLetter(coord_t x, coord_t y, uint8_t idx, LcdFlags att) -{ - lcdDrawTextAtIndex(x, y, STR_RETA123, idx-1, att); -} - -void putsModelName(coord_t x, coord_t y, char * name, uint8_t id, LcdFlags att) -{ - uint8_t len = sizeof(g_model.header.name); - while (len>0 && !name[len-1]) --len; - if (len==0) { - drawStringWithIndex(x, y, STR_MODEL, id+1, att|LEADING0); - } - else { - lcdDrawSizedText(x, y, name, sizeof(g_model.header.name), ZCHAR|att); - } -} - -void drawSwitch(coord_t x, coord_t y, swsrc_t idx, LcdFlags flags) -{ - char s[8]; - getSwitchPositionName(s, idx); - lcdDrawText(x, y, s, flags); -} - -void drawCurveName(coord_t x, coord_t y, int8_t idx, LcdFlags flags) -{ - char s[8]; - getCurveString(s, idx); - lcdDrawText(x, y, s, flags); -} - -void drawTimerMode(coord_t x, coord_t y, swsrc_t mode, LcdFlags att) -{ - if (mode >= 0) { - if (mode < TMRMODE_COUNT) { - lcdDrawTextAtIndex(x, y, STR_VTMRMODES, mode, att); - return; - } - else { - mode -= (TMRMODE_COUNT-1); - } - } - drawSwitch(x, y, mode, att); -} - -void drawTrimMode(coord_t x, coord_t y, uint8_t phase, uint8_t idx, LcdFlags att) -{ - trim_t v = getRawTrimValue(phase, idx); - unsigned int mode = v.mode; - unsigned int p = mode >> 1; - - if (mode == TRIM_MODE_NONE) { - lcdDrawText(x, y, "--", att); - } - else { - char s[2]; - s[0] = (mode % 2 == 0) ? ':' : '+'; - s[1] = '0'+p; - lcdDrawSizedText(x, y, s, 2, att); - } -} - - -void drawDate(coord_t x, coord_t y, TelemetryItem & telemetryItem, LcdFlags att) -{ - // TODO - if (att & DBLSIZE) { - x -= 42; - att &= ~FONTSIZE_MASK; - lcdDrawNumber(x, y, telemetryItem.datetime.day, att|LEADING0|LEFT, 2); - lcdDrawChar(lcdNextPos-1, y, '-', att); - lcdDrawNumber(lcdNextPos-1, y, telemetryItem.datetime.month, att|LEFT, 2); - lcdDrawChar(lcdNextPos-1, y, '-', att); - lcdDrawNumber(lcdNextPos-1, y, telemetryItem.datetime.year-2000, att|LEFT); - y += FH; - lcdDrawNumber(x, y, telemetryItem.datetime.hour, att|LEADING0|LEFT, 2); - lcdDrawChar(lcdNextPos, y, ':', att); - lcdDrawNumber(lcdNextPos, y, telemetryItem.datetime.min, att|LEADING0|LEFT, 2); - lcdDrawChar(lcdNextPos, y, ':', att); - lcdDrawNumber(lcdNextPos, y, telemetryItem.datetime.sec, att|LEADING0|LEFT, 2); - } - else { - lcdDrawNumber(x, y, telemetryItem.datetime.day, att|LEADING0|LEFT, 2); - lcdDrawChar(lcdNextPos-1, y, '-', att); - lcdDrawNumber(lcdNextPos, y, telemetryItem.datetime.month, att|LEFT, 2); - lcdDrawChar(lcdNextPos-1, y, '-', att); - lcdDrawNumber(lcdNextPos, y, telemetryItem.datetime.year-2000, att|LEFT); - lcdDrawNumber(lcdNextPos+11, y, telemetryItem.datetime.hour, att|LEADING0|LEFT, 2); - lcdDrawChar(lcdNextPos, y, ':', att); - lcdDrawNumber(lcdNextPos, y, telemetryItem.datetime.min, att|LEADING0|LEFT, 2); - lcdDrawChar(lcdNextPos, y, ':', att); - lcdDrawNumber(lcdNextPos, y, telemetryItem.datetime.sec, att|LEADING0|LEFT, 2); - } -} - -void drawGPSCoord(coord_t x, coord_t y, int32_t value, const char * direction, LcdFlags flags, bool seconds=true) -{ - char s[32]; - uint32_t absvalue = abs(value); - char * tmp = strAppendUnsigned(s, absvalue / 1000000); - *tmp++ = '@'; - absvalue = absvalue % 1000000; - absvalue *= 60; - if (g_eeGeneral.gpsFormat == 0 || !seconds) { - tmp = strAppendUnsigned(tmp, absvalue / 1000000, 2); - *tmp++ = '\''; - if (seconds) { - absvalue %= 1000000; - absvalue *= 60; - absvalue /= 100000; - tmp = strAppendUnsigned(tmp, absvalue / 10); - *tmp++ = '.'; - tmp = strAppendUnsigned(tmp, absvalue % 10); - *tmp++ = '"'; - } - } - else { - tmp = strAppendUnsigned(tmp, absvalue / 1000000, 2); - *tmp++ = '.'; - absvalue /= 1000; - tmp = strAppendUnsigned(tmp, absvalue, 3); - } - *tmp++ = direction[value>=0 ? 0 : 1]; - *tmp = '\0'; - lcdDrawText(x, y, s, flags); -} - -void drawGPSPosition(coord_t x, coord_t y, int32_t longitude, int32_t latitude, LcdFlags flags) -{ - if (flags & EXPANDED) { - drawGPSCoord(x, y, latitude, "NS", flags, true); - drawGPSCoord(x, y + FH, longitude, "EW", flags, true); - } - else { - drawGPSCoord(x, y, latitude, "NS", flags, false); - drawGPSCoord(lcdNextPos+5, y, longitude, "EW", flags, false); - } -} - -void drawGPSSensorValue(coord_t x, coord_t y, TelemetryItem & telemetryItem, LcdFlags flags) -{ - drawGPSPosition(x, y, telemetryItem.gps.longitude, telemetryItem.gps.latitude, flags); -} -#endif - -void lcdSetContrast() -{ - lcdSetRefVolt(g_eeGeneral.contrast); -} - -void lcdDrawPoint(coord_t x, coord_t y, LcdFlags att) -{ - display_t * p = PIXEL_PTR(x, y); - display_t color = lcdColorTable[COLOR_IDX(att)]; - if (p < DISPLAY_END) { - *p = color; - } -} - -void lcdDrawBlackOverlay() -{ - lcdDrawFilledRect(0, 0, LCD_W, LCD_H, SOLID, OVERLAY_COLOR | OPACITY(8)); -} - -#if defined(SIMU) -BitmapBuffer _lcd(BMP_RGB565, LCD_W, LCD_H, displayBuf); -BitmapBuffer * lcd = &_lcd; - -void DMAFillRect(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) -{ -#if defined(PCBX10) && !defined(SIMU) - x = destw - (x + w); - y = desth - (y + h); -#endif - - for (int i=0; i> 12; - uint8_t red = ((((*q >> 8) & 0x0f) << 1) * alpha + (*p >> 11) * (0x0f-alpha)) / 0x0f; - uint8_t green = ((((*q >> 4) & 0x0f) << 2) * alpha + ((*p >> 5) & 0x3f) * (0x0f-alpha)) / 0x0f; - uint8_t blue = ((((*q >> 0) & 0x0f) << 1) * alpha + ((*p >> 0) & 0x1f) * (0x0f-alpha)) / 0x0f; - *p = (red << 11) + (green << 5) + (blue << 0); - p++; q++; - } - } -} - -void DMABitmapConvert(uint16_t * dest, const uint8_t * src, uint16_t w, uint16_t h, uint32_t format) -{ - if (format == DMA2D_ARGB4444) { - for (int row = 0; row < h; ++row) { - for (int col = 0; col < w; ++col) { - *dest = ARGB(src[0], src[1], src[2], src[3]); - ++dest; - src += 4; - } - } - } - else { - for (int row = 0; row < h; ++row) { - for(int col = 0; col < w; ++col) { - *dest = RGB(src[1], src[2], src[3]); - ++dest; - src += 4; - } - } - } -} -#endif diff --git a/radio/src/gui/480x272/menu_model.cpp b/radio/src/gui/480x272/menu_model.cpp deleted file mode 100644 index 93a7710e5..000000000 --- a/radio/src/gui/480x272/menu_model.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -const MenuHandlerFunc menuTabModel[] = { - menuModelSetup, - CASE_HELI(menuModelHeli) - CASE_FLIGHT_MODES(menuModelFlightModesAll) - menuModelExposAll, - menuModelMixAll, - menuModelLimits, - menuModelCurvesAll, -#if defined(GVARS) && defined(FLIGHT_MODES) - CASE_GVARS(menuModelGVars) -#endif - menuModelLogicalSwitches, - menuModelSpecialFunctions, -#if defined(LUA_MODEL_SCRIPTS) - menuModelCustomScripts, -#endif - menuModelTelemetry -}; - -void editCurveRef(coord_t x, coord_t y, CurveRef & curve, event_t event, LcdFlags attr); - -uint8_t editDelay(coord_t x, coord_t y, event_t event, uint8_t attr, uint8_t delay) -{ - lcdDrawNumber(x+MIXES_2ND_COLUMN, y, (10/DELAY_STEP)*delay, attr|PREC1|LEFT); - if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, delay, DELAY_MAX); - return delay; -} - -uint8_t s_copyMode = 0; -int8_t s_copySrcRow; -int8_t s_copyTgtOfs; -uint8_t s_copySrcIdx; -uint8_t s_copySrcCh; - -uint8_t editNameCursorPos = 0; - -void editName(coord_t x, coord_t y, char * name, uint8_t size, event_t event, uint8_t active, LcdFlags flags) -{ - if (active && s_editMode <= 0) { - flags |= INVERS; - } - - if (!active || s_editMode <= 0) { - if (flags & ZCHAR) { - uint8_t len = zlen(name, size); - if (len == 0) { - char tmp[] = "---"; - lcdDrawSizedText(x, y, tmp, size, flags-ZCHAR); - } - else { - lcdDrawSizedText(x, y, name, len, flags); - } - } - else { - lcdDrawText(x, y, name, flags); - } - } - - if (active) { - if (s_editMode > 0) { - int8_t c = name[editNameCursorPos]; - if (!(flags & ZCHAR)) { - c = char2zchar(c); - } - int8_t v = c; - - if (event==EVT_ROTARY_RIGHT || event==EVT_ROTARY_LEFT) { - v = checkIncDec(event, abs(v), 0, ZCHAR_MAX, 0); - if (c <= 0) v = -v; - } - - switch (event) { - case EVT_KEY_BREAK(KEY_LEFT): - if (editNameCursorPos>0) editNameCursorPos--; - break; - - case EVT_KEY_BREAK(KEY_RIGHT): - if (editNameCursorPos=-26 && v<=26) { - v = -v; // toggle case - if (event==EVT_KEY_LONG(KEY_LEFT)) - killEvents(KEY_LEFT); - } - break; - } - - if (c != v) { - if (!(flags & ZCHAR)) { - if (v != '\0' || name[editNameCursorPos+1] != '\0') - v = zchar2char(v); - } - name[editNameCursorPos] = v; - storageDirty(isModelMenuDisplayed() ? EE_MODEL : EE_GENERAL); - } - - lcdDrawSizedText(x, y, name, size, flags); - coord_t left = (editNameCursorPos == 0 ? 0 : getTextWidth(name, editNameCursorPos, flags)); - char s[] = { (flags & ZCHAR) ? zchar2char(name[editNameCursorPos]) : name[editNameCursorPos], '\0' }; - lcdDrawSolidFilledRect(x+left-1, y, getTextWidth(s, 1)+1, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR); - lcdDrawText(x+left, y, s, TEXT_INVERTED_COLOR); - } - else { - editNameCursorPos = 0; - } - } -} - -uint8_t s_currIdx; - -void copySelection(char * dst, const char * src, uint8_t size) -{ - if (memcmp(src, "---", 3) == 0) - memset(dst, 0, size); - else - memcpy(dst, src, size); -} diff --git a/radio/src/gui/480x272/menus.cpp b/radio/src/gui/480x272/menus.cpp deleted file mode 100644 index c1b507930..000000000 --- a/radio/src/gui/480x272/menus.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -MenuHandlerFunc menuHandlers[5]; -event_t menuEvent = 0; -uint8_t menuVerticalPositions[4]; -uint8_t menuLevel = 0; - -void popMenu() -{ - assert(menuLevel>0); - menuLevel = menuLevel-1; - menuEvent = EVT_ENTRY_UP; - AUDIO_KEY_PRESS(); - TRACE("popMenu(%d)", menuLevel); -} - -void abortPopMenu() -{ - menuLevel = menuLevel + 1; - menuEvent = 0; - TRACE("popMenu(%d) aborted", menuLevel); -} - - -void chainMenu(MenuHandlerFunc newMenu) -{ - menuHandlers[menuLevel] = newMenu; - menuEvent = EVT_ENTRY; - AUDIO_KEY_PRESS(); - TRACE("chainMenu(%d, %p)", menuLevel, newMenu); -} - -void pushMenu(MenuHandlerFunc newMenu) -{ - killEvents(KEY_ENTER); - - if (menuLevel == 0) { - if (newMenu == menuRadioSetup) - menuVerticalPositions[0] = 1; - if (newMenu == menuModelSetup) - menuVerticalPositions[0] = 0; - } - else { - menuVerticalPositions[menuLevel] = menuVerticalPosition; - } - - menuLevel++; - - assert(menuLevel < DIM(menuHandlers)); - - menuHandlers[menuLevel] = newMenu; - menuEvent = EVT_ENTRY; - AUDIO_KEY_PRESS(); - TRACE("pushMenu(%d, %p)", menuLevel, newMenu); -} - -void readModelNotes() -{ - LED_ERROR_BEGIN(); - - strcpy(s_text_file, MODELS_PATH "/"); - char *buf = strcat_currentmodelname(&s_text_file[sizeof(MODELS_PATH)]); - strcpy(buf, TEXT_EXT); - if (!isFileAvailable(s_text_file)) { - char *buf = strAppendFilename(&s_text_file[sizeof(MODELS_PATH)], g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME); - strcpy(buf, TEXT_EXT); - } - - waitKeysReleased(); - event_t event = EVT_ENTRY; - while (event != EVT_KEY_FIRST(KEY_EXIT)) { - lcdRefreshWait(); - lcdClear(); - menuTextView(event); - event = getEvent(); - lcdRefresh(); - wdt_reset(); - } - - LED_ERROR_END(); -} - -bool menuModelNotes(event_t event) -{ - if (event == EVT_ENTRY) { - strcpy(s_text_file, MODELS_PATH "/"); - char *buf = strcat_currentmodelname(&s_text_file[sizeof(MODELS_PATH)]); - strcpy(buf, TEXT_EXT); - if (!isFileAvailable(s_text_file)) { - char *buf = strAppendFilename(&s_text_file[sizeof(MODELS_PATH)], g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME); - strcpy(buf, TEXT_EXT); - } - } - return menuTextView(event); -} - -void pushModelNotes() -{ - pushMenu(menuModelNotes); -} diff --git a/radio/src/gui/480x272/menus.h b/radio/src/gui/480x272/menus.h deleted file mode 100644 index 6b3242c7a..000000000 --- a/radio/src/gui/480x272/menus.h +++ /dev/null @@ -1,615 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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. - */ - -#ifndef _MENUS_H_ -#define _MENUS_H_ - -#include "keys.h" -#include "audio.h" - -#define MENU_COLUMN2_X 280 -#define MIXES_2ND_COLUMN 140 -#define COLUMN_HEADER_X 150 - -extern uint8_t menuPageIndex; -extern uint8_t menuPageCount; -extern uint16_t linesCount; -extern uint8_t linesDisplayed; -extern int menuVerticalPosition; -extern int menuHorizontalPosition; -extern int menuVerticalOffset; -extern uint8_t menuCalibrationState; - -// Temporary no highlight -extern uint8_t noHighlightCounter; -#define NO_HIGHLIGHT() (noHighlightCounter > 0) -#define START_NO_HIGHLIGHT() do { noHighlightCounter = 25; } while(0) - -typedef bool (*MenuHandlerFunc)(event_t event); - -extern MenuHandlerFunc menuHandlers[5]; -extern uint8_t menuVerticalPositions[4]; -extern uint8_t menuLevel; -extern event_t menuEvent; - -void chainMenu(MenuHandlerFunc newMenu); -void pushMenu(MenuHandlerFunc newMenu); -void popMenu(); -void abortPopMenu(); - -inline bool isRadioMenuDisplayed() -{ - return menuVerticalPositions[0] == 1; -} - -inline bool isModelMenuDisplayed() -{ - return menuVerticalPositions[0] == 0; -} - -enum MenuIcons { - ICON_OPENTX, - ICON_RADIO, - ICON_RADIO_SETUP, - ICON_RADIO_SD_MANAGER, - ICON_RADIO_TOOLS, - ICON_RADIO_GLOBAL_FUNCTIONS, - ICON_RADIO_TRAINER, - ICON_RADIO_HARDWARE, - ICON_RADIO_CALIBRATION, - ICON_RADIO_VERSION, - ICON_MODEL, - ICON_MODEL_SETUP, - ICON_MODEL_HELI, - ICON_MODEL_FLIGHT_MODES, - ICON_MODEL_INPUTS, - ICON_MODEL_MIXER, - ICON_MODEL_OUTPUTS, - ICON_MODEL_CURVES, - ICON_MODEL_GVARS, - ICON_MODEL_LOGICAL_SWITCHES, - ICON_MODEL_SPECIAL_FUNCTIONS, - ICON_MODEL_LUA_SCRIPTS, - ICON_MODEL_TELEMETRY, - ICON_THEME, - ICON_THEME_SETUP, - ICON_THEME_VIEW1, - ICON_THEME_VIEW2, - ICON_THEME_VIEW3, - ICON_THEME_VIEW4, - ICON_THEME_VIEW5, - ICON_THEME_ADD_VIEW, - ICON_STATS, - ICON_STATS_THROTTLE_GRAPH, - ICON_STATS_TIMERS, - ICON_STATS_ANALOGS, - ICON_STATS_DEBUG, - ICON_MONITOR, - ICON_MONITOR_CHANNELS1, - ICON_MONITOR_CHANNELS2, - ICON_MONITOR_CHANNELS3, - ICON_MONITOR_CHANNELS4, - ICON_MONITOR_LOGICAL_SWITCHES, - ICON_RADIO_SPECTRUM_ANALYSER, - MENUS_ICONS_COUNT -}; - -enum EnumTabModel { - MENU_MODEL_SETUP, - CASE_HELI(MENU_MODEL_HELI) - CASE_FLIGHT_MODES(MENU_MODEL_FLIGHT_MODES) - MENU_MODEL_INPUTS, - MENU_MODEL_MIXES, - MENU_MODEL_OUTPUTS, - MENU_MODEL_CURVES, - CASE_GVARS(MENU_MODEL_GVARS) - MENU_MODEL_LOGICAL_SWITCHES, - MENU_MODEL_SPECIAL_FUNCTIONS, -#if defined(LUA_MODEL_SCRIPTS) - MENU_MODEL_CUSTOM_SCRIPTS, -#endif - MENU_MODEL_TELEMETRY, - MENU_MODEL_PAGES_COUNT -}; - -const uint8_t MODEL_ICONS[MENU_MODEL_PAGES_COUNT + 1] = { - ICON_MODEL, - ICON_MODEL_SETUP, - CASE_HELI(ICON_MODEL_HELI) - CASE_FLIGHT_MODES(ICON_MODEL_FLIGHT_MODES) - ICON_MODEL_INPUTS, - ICON_MODEL_MIXER, - ICON_MODEL_OUTPUTS, - ICON_MODEL_CURVES, - CASE_GVARS(ICON_MODEL_GVARS) - ICON_MODEL_LOGICAL_SWITCHES, - ICON_MODEL_SPECIAL_FUNCTIONS, -#if defined(LUA_MODEL_SCRIPTS) - ICON_MODEL_LUA_SCRIPTS, -#endif - ICON_MODEL_TELEMETRY -}; - -const uint8_t STATS_ICONS[] = { - ICON_STATS, - ICON_STATS_THROTTLE_GRAPH, - ICON_STATS_DEBUG, - ICON_STATS_ANALOGS, -#if defined(DEBUG_TRACE_BUFFER) - ICON_STATS_TIMERS -#endif -}; - -const uint8_t MONITOR_ICONS[] = { - ICON_MONITOR, - ICON_MONITOR_CHANNELS1, - ICON_MONITOR_CHANNELS2, - ICON_MONITOR_CHANNELS3, - ICON_MONITOR_CHANNELS4, - ICON_MONITOR_LOGICAL_SWITCHES -}; - -bool menuModelSetup(event_t event); -bool menuModelFailsafe(event_t event); -bool menuModelHeli(event_t event); -bool menuModelFlightModesAll(event_t event); -bool menuModelExposAll(event_t event); -bool menuModelMixAll(event_t event); -bool menuModelLimits(event_t event); -bool menuModelCurvesAll(event_t event); -bool menuModelCurveOne(event_t event); -bool menuModelGVars(event_t event); -bool menuModelLogicalSwitches(event_t event); -bool menuModelSpecialFunctions(event_t event); -bool menuModelCustomScripts(event_t event); -bool menuModelTelemetry(event_t event); -bool menuModelSensor(event_t event); -bool menuModelExpoOne(event_t event); -bool menuModelModuleOptions(event_t event); -bool menuModelReceiverOptions(event_t event); - -extern const MenuHandlerFunc menuTabModel[MENU_MODEL_PAGES_COUNT]; - -enum EnumTabRadio { - MENU_RADIO_SETUP, - MENU_RADIO_SD_MANAGER, -#if defined(LUA) || defined(PXX2) - MENU_RADIO_TOOLS, -#endif - MENU_RADIO_SPECIAL_FUNCTIONS, - MENU_RADIO_TRAINER, - MENU_RADIO_HARDWARE, - MENU_RADIO_VERSION, - MENU_RADIO_PAGES_COUNT -}; - -const uint8_t RADIO_ICONS[MENU_RADIO_PAGES_COUNT + 1] = { - ICON_RADIO, - ICON_RADIO_SETUP, - ICON_RADIO_SD_MANAGER, -#if defined(LUA) || defined(PXX2) - ICON_RADIO_TOOLS, -#endif - ICON_RADIO_GLOBAL_FUNCTIONS, - ICON_RADIO_TRAINER, - ICON_RADIO_HARDWARE, - ICON_RADIO_VERSION -}; - -bool menuRadioSetup(event_t event); -bool menuRadioSdManager(event_t event); -bool menuRadioTools(event_t event); -bool menuRadioSpecialFunctions(event_t event); -bool menuRadioTrainer(event_t event); -bool menuRadioVersion(event_t event); -bool menuRadioHardware(event_t event); -bool menuRadioCalibration(event_t event); -bool menuRadioSpectrumAnalyser(event_t event); -bool menuRadioPowerMeter(event_t event); - -extern const MenuHandlerFunc menuTabGeneral[MENU_RADIO_PAGES_COUNT]; - -enum MenuRadioIndexes -{ - e_StatsGraph, - e_StatsDebug, - e_StatsAnalogs, -#if defined(DEBUG_TRACE_BUFFER) - e_StatsTraces, -#endif -}; - -bool menuStatsGraph(event_t event); -bool menuStatsDebug(event_t event); -bool menuStatsAnalogs(event_t event); -bool menuStatsTraces(event_t event); - -static const MenuHandlerFunc menuTabStats[] = { - menuStatsGraph, - menuStatsDebug, - menuStatsAnalogs, -#if defined(DEBUG_TRACE_BUFFER) - menuStatsTraces, -#endif -}; - -enum EnumTabMonitors -{ - e_MonChannelsFirst, - e_MonChannels1 = e_MonChannelsFirst, - e_MonChannels2, - e_MonChannels3, - e_MonChannels4, - e_MonLogicalSwitches, - e_MonTabChannelsPagesCount -}; - -extern const MenuHandlerFunc menuTabMonitors[e_MonTabChannelsPagesCount]; -extern uint8_t lastMonitorPage; -extern void drawSingleMixerBar(coord_t, coord_t, coord_t, coord_t, uint8_t); -extern void drawSingleOutputBar(coord_t, coord_t, coord_t, coord_t, uint8_t); - -extern const MenuHandlerFunc menuTabScreensSetup[1+MAX_CUSTOM_SCREENS] ; - -bool menuFirstCalib(event_t event); -bool menuMainView(event_t event); -bool menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomFunctionsContext & functionsContext); -bool menuModelSelect(event_t event); -bool menuAboutView(event_t event); -bool menuMainViewChannelsMonitor(event_t event); -bool menuTextView(event_t event); -bool menuScreensTheme(event_t event); - -extern int8_t checkIncDec_Ret; // global helper vars - -#define EDIT_SELECT_FIELD 0 -#define EDIT_MODIFY_FIELD 1 -#define EDIT_MODIFY_STRING 2 -extern int8_t s_editMode; // global editmode - -// checkIncDec flags -#define EE_GENERAL 0x01 -#define EE_MODEL 0x02 -#define NO_INCDEC_MARKS 0x04 -#define INCDEC_SWITCH 0x08 -#define INCDEC_SOURCE 0x10 -#define INCDEC_REP10 0x40 -#define NO_DBLKEYS 0x80 - -// mawrow special values -#define ORPHAN_ROW ((uint8_t)-3) -#define NAVIGATION_LINE_BY_LINE 0x40 -#define CURSOR_ON_LINE() (menuHorizontalPosition<0) - -struct CheckIncDecStops { - const int count; - const int stops[]; - int min() const - { - return stops[0]; - } - int max() const - { - return stops[count-1]; - } - bool contains(int value) const - { - for (int i=0; i 0 ? (index < popupMenuItemsCount ? index : popupMenuItemsCount - 1) : 0); -} - -inline void POPUP_MENU_START(PopupMenuHandler handler) -{ - popupMenuHandler = handler; - AUDIO_KEY_PRESS(); -} - -inline void CLEAR_POPUP() -{ - warningText = nullptr; -} - -inline void POPUP_WAIT(const char * s) -{ - warningText = s; - warningInfoText = nullptr; - warningType = WARNING_TYPE_WAIT; - popupFunc = runPopupWarning; -} - -inline void POPUP_INFORMATION(const char * s) -{ - warningText = s; - warningInfoText = nullptr; - warningType = WARNING_TYPE_INFO; - popupFunc = runPopupWarning; -} - -inline void POPUP_WARNING(const char * s) -{ - warningText = s; - warningInfoText = nullptr; - warningType = WARNING_TYPE_ASTERISK; - popupFunc = runPopupWarning; -} - -inline void POPUP_CONFIRMATION(const char *s, void (* confirmHandler)(const char *) = nullptr) -{ - warningText = s; - warningType = WARNING_TYPE_CONFIRM; - warningInfoText = nullptr; - popupFunc = runPopupWarning; - popupMenuHandler = confirmHandler; -} - -#define TEXT_FILENAME_MAXLEN 40 -extern char s_text_file[TEXT_FILENAME_MAXLEN]; -void pushMenuTextView(const char * filename); -void pushModelNotes(); -void readModelNotes(); - -#define LABEL(...) (uint8_t)-1 - -#define CURSOR_MOVED_LEFT(event) (event==EVT_ROTARY_LEFT || EVT_KEY_MASK(event) == KEY_LEFT) -#define CURSOR_MOVED_RIGHT(event) (event==EVT_ROTARY_RIGHT || EVT_KEY_MASK(event) == KEY_RIGHT) -#define REPEAT_LAST_CURSOR_MOVE(last, refresh) { if (CURSOR_MOVED_RIGHT(event)) menuHorizontalPosition = (menuHorizontalPosition >= (last) ? 0 : menuHorizontalPosition + 1); else menuHorizontalPosition = (menuHorizontalPosition <= (0) ? last : menuHorizontalPosition - 1); if (refresh) putEvent(EVT_REFRESH); } - -#define MENU_FIRST_LINE_EDIT (MAXCOL((uint16_t)0) >= HIDDEN_ROW ? (MAXCOL((uint16_t)1) >= HIDDEN_ROW ? 2 : 1) : 0) -#define POS_HORZ_INIT(posVert) ((COLATTR(posVert) & NAVIGATION_LINE_BY_LINE) ? -1 : 0) -#define EDIT_MODE_INIT 0 // TODO enum - -typedef int (*FnFuncP) (int x); -void drawFunction(FnFuncP fn, int x, int y, int width); - -void onSourceLongEnterPress(const char *result); - -uint8_t switchToMix(uint8_t source); - -#endif // _MENUS_H_ diff --git a/radio/src/gui/480x272/model_curves.cpp b/radio/src/gui/480x272/model_curves.cpp deleted file mode 100644 index 7d15edcaf..000000000 --- a/radio/src/gui/480x272/model_curves.cpp +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -void drawCurve(int x, int y, int width) -{ - drawFunction(applyCurrentCurve, x, y, width); -} - -void runPopupCurvePreset(event_t event) -{ - warningResult = false; - - theme->drawMessageBox(warningText, warningInfoText, STR_POPUPS_ENTER_EXIT, warningType); - - switch (event) { - case EVT_KEY_BREAK(KEY_ENTER): - warningResult = true; - // no break - - case EVT_KEY_BREAK(KEY_EXIT): - warningText = NULL; - warningType = WARNING_TYPE_ASTERISK; - break; - - default: - s_editMode = EDIT_MODIFY_FIELD; - reusableBuffer.curveEdit.preset = checkIncDec(event, reusableBuffer.curveEdit.preset, -4, +4); - s_editMode = EDIT_SELECT_FIELD; - break; - } - - lcdDrawNumber(WARNING_LINE_X, WARNING_INFOLINE_Y-10, 45 * reusableBuffer.curveEdit.preset / 4, LEFT|INVERS, 0, NULL, "@"); - - if (warningResult) { - warningResult = 0; - CurveInfo & crv = g_model.curves[s_curveChan]; - int8_t * points = curveAddress(s_curveChan); - int k = 25 * reusableBuffer.curveEdit.preset; - int dx = 2000 / (5+crv.points-1); - for (uint8_t i=0; i<5+crv.points; i++) { - int x = -1000 + i * dx; - points[i] = div_and_round(div_and_round(k * x, 100), 10); - } - if (crv.type == CURVE_TYPE_CUSTOM) { - resetCustomCurveX(points, 5+crv.points); - } - } -} - -void onCurveOneMenu(const char * result) -{ - if (result == STR_CURVE_PRESET) { - reusableBuffer.curveEdit.preset = 4; // 45° - POPUP_INPUT(STR_PRESET, runPopupCurvePreset); - } - else if (result == STR_MIRROR) { - CurveInfo & crv = g_model.curves[s_curveChan]; - int8_t * points = curveAddress(s_curveChan); - for (int i=0; i<5+crv.points; i++) - points[i] = -points[i]; - } - else if (result == STR_CLEAR) { - CurveInfo & crv = g_model.curves[s_curveChan]; - int8_t * points = curveAddress(s_curveChan); - for (int i=0; i<5+crv.points; i++) - points[i] = 0; - if (crv.type == CURVE_TYPE_CUSTOM) { - resetCustomCurveX(points, 5+crv.points); - } - } -} - -#define MODEL_CURVE_ONE_2ND_COLUMN 130 - -enum MenuModelCurveOneItems { - ITEM_CURVE_NAME, - ITEM_CURVE_TYPE, - ITEM_CURVE_POINTS, - ITEM_CURVE_SMOOTH, - ITEM_CURVE_COORDS1, - ITEM_CURVE_COORDS2, -}; - -#define IS_COORDS1_LINE_NEEDED() (crv.type==CURVE_TYPE_CUSTOM && crv.points > -3) - -bool menuModelCurveOne(event_t event) -{ - static uint8_t pointsOfs = 0; - CurveData & crv = g_model.curves[s_curveChan]; - int8_t * points = curveAddress(s_curveChan); - - SUBMENU_WITH_OPTIONS(STR_MENUCURVE, ICON_MODEL_CURVES, IS_COORDS1_LINE_NEEDED() ? 6 : 5, OPTION_MENU_NO_FOOTER, { 0, 0, 0, 0, uint8_t(5+crv.points-1), uint8_t(5+crv.points-1) }); - drawStringWithIndex(50, 3+FH, STR_CV, s_curveChan+1, MENU_TITLE_COLOR); - lcdDrawSolidFilledRect(0, MENU_FOOTER_TOP, 250, MENU_FOOTER_HEIGHT, HEADER_BGCOLOR); - - if (IS_COORDS1_LINE_NEEDED() && menuVerticalPosition == ITEM_CURVE_COORDS1) { - if (menuHorizontalPosition == 0) { - if (CURSOR_MOVED_RIGHT(event)) - menuHorizontalPosition = 1; - else - menuVerticalPosition -= 1; - } - else if (menuHorizontalPosition == 4+crv.points) { - if (CURSOR_MOVED_RIGHT(event)) - (menuHorizontalPosition = 0, menuVerticalPosition += 1); - else - menuHorizontalPosition -= 1; - } - } - - // Curve name - lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP-FH, STR_NAME); - editName(MODEL_CURVE_ONE_2ND_COLUMN, MENU_CONTENT_TOP-FH, crv.name, sizeof(crv.name), event, menuVerticalPosition==ITEM_CURVE_NAME); - - // Curve type - LcdFlags attr = (menuVerticalPosition==ITEM_CURVE_TYPE ? (s_editMode>0 ? INVERS|BLINK : INVERS) : 0); - lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP, "Type"); - lcdDrawTextAtIndex(MODEL_CURVE_ONE_2ND_COLUMN, MENU_CONTENT_TOP, STR_CURVE_TYPES, crv.type, attr); - if (attr) { - uint8_t newType = checkIncDecModelZero(event, crv.type, CURVE_TYPE_LAST); - if (newType != crv.type) { - for (int i = 1; i < 4 + crv.points; i++) { - points[i] = calcRESXto100(applyCustomCurve(calc100toRESX(-100 + i * 200 / (4 + crv.points)), s_curveChan)); - } - if (moveCurve(s_curveChan, checkIncDec_Ret > 0 ? 3 + crv.points : -3 - crv.points)) { - if (newType == CURVE_TYPE_CUSTOM) { - resetCustomCurveX(points, 5 + crv.points); - } - crv.type = newType; - } - } - } - - // Curve points count - attr = (menuVerticalPosition==ITEM_CURVE_POINTS ? (s_editMode>0 ? INVERS|BLINK : INVERS) : 0); - lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + FH, STR_COUNT); - lcdDrawNumber(MODEL_CURVE_ONE_2ND_COLUMN, MENU_CONTENT_TOP + FH, 5+crv.points, LEFT|attr, 0, NULL, STR_PTS); - if (attr) { - int count = checkIncDecModel(event, crv.points, -3, 12); // 2pts - 17pts - if (checkIncDec_Ret) { - int newPoints[MAX_POINTS_PER_CURVE]; - newPoints[0] = points[0]; - newPoints[4+count] = points[4+crv.points]; - for (int i=1; i<4+count; i++) - newPoints[i] = calcRESXto100(applyCustomCurve(-RESX + (i * 2 * RESX) / (4 + count), s_curveChan)); - if (moveCurve(s_curveChan, checkIncDec_Ret*(crv.type==CURVE_TYPE_CUSTOM ? 2 :1))) { - for (int i = 0; i < 5 + count; i++) { - points[i] = newPoints[i]; - if (crv.type == CURVE_TYPE_CUSTOM && i != 0 && i != 4 + count) - points[5 + count + i - 1] = -100 + (i * 200) / (4 + count); - } - crv.points = count; - } - } - } - - // Curve smooth - lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 2*FH, STR_SMOOTH); - drawCheckBox(MODEL_CURVE_ONE_2ND_COLUMN, MENU_CONTENT_TOP + 2*FH, crv.smooth, menuVerticalPosition==ITEM_CURVE_SMOOTH ? INVERS : 0); - if (menuVerticalPosition==ITEM_CURVE_SMOOTH) crv.smooth = checkIncDecModel(event, crv.smooth, 0, 1); - - switch(event) { - case EVT_ENTRY: - pointsOfs = 0; - break; - case EVT_KEY_LONG(KEY_ENTER): - if (menuVerticalPosition > ITEM_CURVE_NAME) { - killEvents(event); - POPUP_MENU_ADD_ITEM(STR_CURVE_PRESET); - POPUP_MENU_ADD_ITEM(STR_MIRROR); - POPUP_MENU_ADD_ITEM(STR_CLEAR); - POPUP_MENU_START(onCurveOneMenu); - } - break; - // TODO? - // case EVT_KEY_LONG(KEY_MENU): - // pushMenu(menuChannelsView); - // killEvents(event); - } - - drawCurve(CURVE_CENTER_X, CURVE_CENTER_Y, CURVE_SIDE_WIDTH); - drawCurveHorizontalScale(); - if (menuVerticalPosition < ITEM_CURVE_COORDS1) drawCurveVerticalScale(CURVE_CENTER_X-CURVE_SIDE_WIDTH-15); - - coord_t posX = 47; - attr = (s_editMode > 0 ? INVERS|BLINK : INVERS); - for (int i=0; i<5+crv.points; i++) { - point_t point = getPoint(i); - uint8_t selectionMode = 0; - if (menuHorizontalPosition == i) { - if (menuVerticalPosition == ITEM_CURVE_COORDS1) - selectionMode = (IS_COORDS1_LINE_NEEDED() ? 1 : 2); - else if (menuVerticalPosition == ITEM_CURVE_COORDS2) - selectionMode = 2; - } - - int8_t x = -100 + 200*i/(5+crv.points-1); - if (crv.type==CURVE_TYPE_CUSTOM && i>0 && i<5+crv.points-1) { - x = points[5+crv.points+i-1]; - } - - if (i>=pointsOfs && i 0) { - lcdDrawSolidFilledRect(point.x, CURVE_CENTER_Y-CURVE_SIDE_WIDTH, 2, 2*CURVE_SIDE_WIDTH+2, CURVE_CURSOR_COLOR); - - char text[5]; - strAppendSigned(text, points[i]); - - if (point.x >= CURVE_CENTER_X) { - drawCurveVerticalScale(point.x-15); - if (points[i-1] > points[i]) { - drawCurveCoord(point.x+1-CURVE_COORD_WIDTH, point.y, text, selectionMode==2); - } - else { - drawCurveCoord(point.x+1-CURVE_COORD_WIDTH, point.y-CURVE_COORD_HEIGHT-1, text, selectionMode==2); - } - } - else { - drawCurveVerticalScale(point.x+7); - if (points[i+1] > points[i]) { - drawCurveCoord(point.x+1, point.y, text, selectionMode==2); - } - else { - drawCurveCoord(point.x+1, point.y-CURVE_COORD_HEIGHT-1, text, selectionMode==2); - } - } - - drawCurvePoint(point.x-3, point.y-4, CURVE_CURSOR_COLOR); - - strAppendSigned(text, x); - drawCurveCoord(limit(CURVE_CENTER_X-CURVE_SIDE_WIDTH-1, point.x-CURVE_COORD_WIDTH/2, CURVE_CENTER_X+CURVE_SIDE_WIDTH-CURVE_COORD_WIDTH+1), CURVE_CENTER_Y+CURVE_SIDE_WIDTH+2, text, selectionMode==1); - - if (s_editMode > 0) { - if (selectionMode == 1) - CHECK_INCDEC_MODELVAR(event, points[5+crv.points+i-1], i==1 ? -100 : points[5+crv.points+i-2], i==5+crv.points-2 ? 100 : points[5+crv.points+i]); // edit X - else if (selectionMode == 2) - CHECK_INCDEC_MODELVAR(event, points[i], -100, 100); - } - if (i < pointsOfs) - pointsOfs = i; - else if (i > pointsOfs+5-1) - pointsOfs = i-5+1; - } - else { - drawCurvePoint(point.x-3, point.y-4, TEXT_COLOR); - } - } - - lcdDrawHorizontalLine(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 6*FH - 13, SUBMENU_LINE_WIDTH, DOTTED, CURVE_AXIS_COLOR); - lcdDrawHorizontalLine(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 7*FH - 10, SUBMENU_LINE_WIDTH, DOTTED, CURVE_AXIS_COLOR); - drawHorizontalScrollbar(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 9*FH - 15, SUBMENU_LINE_WIDTH, pointsOfs, 5+crv.points, 5); - - return true; -} - -void editCurveRef(coord_t x, coord_t y, CurveRef & curve, event_t event, LcdFlags attr) -{ - lcdDrawTextAtIndex(x, y, "\004DiffExpoFuncCstm", curve.type, (menuHorizontalPosition==0 ? attr : 0)); - if (attr && menuHorizontalPosition==0) { - CHECK_INCDEC_MODELVAR_ZERO(event, curve.type, CURVE_REF_CUSTOM); - if (checkIncDec_Ret) curve.value = 0; - } - switch (curve.type) { - case CURVE_REF_DIFF: - case CURVE_REF_EXPO: - curve.value = GVAR_MENU_ITEM(lcdNextPos+10, y, curve.value, -100, 100, menuHorizontalPosition==1 ? LEFT|attr : LEFT, 0, event); - break; - case CURVE_REF_FUNC: - lcdDrawTextAtIndex(lcdNextPos+10, y, STR_VCURVEFUNC, curve.value, (menuHorizontalPosition==1 ? attr : 0)); - if (attr && menuHorizontalPosition==1) CHECK_INCDEC_MODELVAR_ZERO(event, curve.value, CURVE_BASE-1); - break; - case CURVE_REF_CUSTOM: - drawCurveName(lcdNextPos+10, y, curve.value, (menuHorizontalPosition==1 ? attr : 0)); - if (attr && menuHorizontalPosition==1) { - if (event==EVT_KEY_LONG(KEY_ENTER) && curve.value!=0) { - s_curveChan = (curve.value<0 ? -curve.value-1 : curve.value-1); - pushMenu(menuModelCurveOne); - } - else { - CHECK_INCDEC_MODELVAR(event, curve.value, -MAX_CURVES, MAX_CURVES); - } - } - break; - } -} - -#define CURVES_NAME_POS 60 -#define CURVES_POINTS_POS 120 - -bool menuModelCurvesAll(event_t event) -{ - SIMPLE_MENU(STR_MENUCURVES, MODEL_ICONS, menuTabModel, MENU_MODEL_CURVES, MAX_CURVES); - - s_curveChan = menuVerticalPosition; - - switch (event) { - case EVT_KEY_BREAK(KEY_ENTER): - if (!READ_ONLY()) { - pushMenu(menuModelCurveOne); - } - break; - } - - for (int i=0; i 8 ? 2 : 1; - - if (event == EVT_KEY_LONG(KEY_ENTER)) { - killEvents(event); - event = 0; - - if (menuVerticalPosition < sentModuleChannels(g_moduleIdx)) { - if (s_editMode) { - g_model.failsafeChannels[menuVerticalPosition] = channelOutputs[menuVerticalPosition+channelStart]; - s_editMode = 0; - } - else { - int16_t * failsafe = &g_model.failsafeChannels[menuVerticalPosition]; - if (*failsafe < FAILSAFE_CHANNEL_HOLD) - *failsafe = FAILSAFE_CHANNEL_HOLD; - else if (*failsafe == FAILSAFE_CHANNEL_HOLD) - *failsafe = FAILSAFE_CHANNEL_NOPULSE; - else - *failsafe = 0; - } - } - else { - // "Outputs => Failsafe" menu item - setCustomFailsafe(g_moduleIdx); - } - - storageDirty(EE_MODEL); - AUDIO_WARNING1(); - SEND_FAILSAFE_NOW(g_moduleIdx); - } - - SIMPLE_SUBMENU_WITH_OPTIONS("FAILSAFE", ICON_STATS_ANALOGS, sentModuleChannels(g_moduleIdx)+1, OPTION_MENU_NO_SCROLLBAR); - drawStringWithIndex(50, 3+FH, "Module", g_moduleIdx+1, MENU_TITLE_COLOR); - - for (uint8_t col=0; col < cols; col++) { - for (uint8_t line=0; line < 8; line++) { - coord_t x = col*(LCD_W/2); - const coord_t y = MENU_CONTENT_TOP - FH + line*(FH+2); - const int32_t channelValue = channelOutputs[ch+channelStart]; - int32_t failsafeValue = g_model.failsafeChannels[8*col+line]; - - // Channel name if present, number if not - if (g_model.limitData[ch+channelStart].name[0] != '\0') { - putsChn(x+MENUS_MARGIN_LEFT, y-3, ch+1, TINSIZE); - lcdDrawSizedText(x+MENUS_MARGIN_LEFT, y+5, g_model.limitData[ch+channelStart].name, sizeof(g_model.limitData[ch+channelStart].name), ZCHAR|SMLSIZE); - } - else { - putsChn(x+MENUS_MARGIN_LEFT, y, ch+1, 0); - } - - // Value - LcdFlags flags = RIGHT; - if (menuVerticalPosition == ch) { - flags |= INVERS; - if (s_editMode) { - if (failsafeValue == FAILSAFE_CHANNEL_HOLD || failsafeValue == FAILSAFE_CHANNEL_NOPULSE) { - s_editMode = 0; - } - else { - flags |= BLINK; - CHECK_INCDEC_MODELVAR(event, g_model.failsafeChannels[8*col+line], -lim, +lim); - } - } - } - - x += (LCD_W/2)-4-MENUS_MARGIN_LEFT-SLIDER_W; - - if (failsafeValue == FAILSAFE_CHANNEL_HOLD) { - lcdDrawText(x, y+2, STR_HOLD, flags|SMLSIZE); - failsafeValue = 0; - } - else if (failsafeValue == FAILSAFE_CHANNEL_NOPULSE) { - lcdDrawText(x, y+2, STR_NONE, flags|SMLSIZE); - failsafeValue = 0; - } - else { -#if defined(PPM_UNIT_US) - lcdDrawNumber(x, y, PPM_CH_CENTER(ch)+failsafeValue/2, flags); -#elif defined(PPM_UNIT_PERCENT_PREC1) - lcdDrawNumber(x, y, calcRESXto1000(failsafeValue), PREC1|flags); -#else - lcdDrawNumber(x, y, calcRESXto1000(failsafeValue)/10, flags); -#endif - } - - // Gauge - x += 4; - lcdDrawRect(x, y+3, SLIDER_W+1, 12); - const coord_t lenChannel = limit((uint8_t)1, uint8_t((abs(channelValue) * SLIDER_W/2 + lim/2) / lim), uint8_t(SLIDER_W/2)); - const coord_t lenFailsafe = limit((uint8_t)1, uint8_t((abs(failsafeValue) * SLIDER_W/2 + lim/2) / lim), uint8_t(SLIDER_W/2)); - x += SLIDER_W/2; - const coord_t xChannel = (channelValue>0) ? x : x+1-lenChannel; - const coord_t xFailsafe = (failsafeValue>0) ? x : x+1-lenFailsafe; - lcdDrawSolidFilledRect(xChannel, y+4, lenChannel, 5, TEXT_COLOR); - lcdDrawSolidFilledRect(xFailsafe, y+9, lenFailsafe, 5, ALARM_COLOR); - - if (++ch >= sentModuleChannels(g_moduleIdx)) { - - // draw that menu at the bottom center - flags = CENTERED | (menuVerticalPosition == ch ? INVERS : 0); - lcdDrawSizedText(LCD_W/2, MENU_CONTENT_TOP - FH + 8*(FH+2), STR_CHANNELS2FAILSAFE, 20, flags); - break; - } - } - } - - return true; -} diff --git a/radio/src/gui/480x272/model_flightmodes.cpp b/radio/src/gui/480x272/model_flightmodes.cpp deleted file mode 100644 index c4d40d121..000000000 --- a/radio/src/gui/480x272/model_flightmodes.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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 -#include "opentx.h" - -void displayFlightModes(coord_t x, coord_t y, FlightModesType value, uint8_t attr) -{ - for (int i=0; i=0) { - drawColumnHeader(STR_PHASES_HEADERS, NULL, menuHorizontalPosition); - } - - for (uint8_t i=0; i0) ? BLINK|INVERS : INVERS) : 0); - LcdFlags active = (attr && s_editMode>0) ; - switch (j) { - case ITEM_FLIGHT_MODES_NAME: - editName(FLIGHT_MODES_NAME_COLUMN, y, p->name, sizeof(p->name), event, attr); - break; - - case ITEM_FLIGHT_MODES_SWITCH: - if (active) CHECK_INCDEC_MODELSWITCH(event, p->swtch, SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES, isSwitchAvailableInMixes); - if (k == 0) - lcdDrawText(FLIGHT_MODES_SWITCH_COLUMN, y, "N/A"); - else - drawSwitch(FLIGHT_MODES_SWITCH_COLUMN, y, p->swtch, attr); - break; - - case ITEM_FLIGHT_MODES_TRIM_RUD: - case ITEM_FLIGHT_MODES_TRIM_ELE: - case ITEM_FLIGHT_MODES_TRIM_THR: - case ITEM_FLIGHT_MODES_TRIM_AIL: - case ITEM_FLIGHT_MODES_TRIM_T5: - case ITEM_FLIGHT_MODES_TRIM_T6: - { - uint8_t t = j-ITEM_FLIGHT_MODES_TRIM_RUD; - if (active) { - trim_t & v = p->trim[t]; - v.mode = checkIncDec(event, v.mode==TRIM_MODE_NONE ? -1 : v.mode, -1, k==0 ? 0 : 2*MAX_FLIGHT_MODES-1, EE_MODEL, isTrimModeAvailable); - } - drawTrimMode(FLIGHT_MODES_TRIMS_COLUMN+j*FLIGHT_MODES_TRIM_WIDTH, y, k, t, attr); - break; - } - - case ITEM_FLIGHT_MODES_FADE_IN: - if (active) p->fadeIn = checkIncDec(event, p->fadeIn, 0, DELAY_MAX, EE_MODEL|NO_INCDEC_MARKS); - lcdDrawNumber(FLIGHT_MODES_FADEIN_COLUMN, y, (10/DELAY_STEP)*p->fadeIn, attr|PREC1|RIGHT); - break; - - case ITEM_FLIGHT_MODES_FADE_OUT: - if (active) p->fadeOut = checkIncDec(event, p->fadeOut, 0, DELAY_MAX, EE_MODEL|NO_INCDEC_MARKS); - lcdDrawNumber(FLIGHT_MODES_FADEOUT_COLUMN, y, (10/DELAY_STEP)*p->fadeOut, attr|PREC1|RIGHT); - break; - - } - } - } - - return true; -} diff --git a/radio/src/gui/480x272/model_inputs.cpp b/radio/src/gui/480x272/model_inputs.cpp deleted file mode 100644 index 00ea22caa..000000000 --- a/radio/src/gui/480x272/model_inputs.cpp +++ /dev/null @@ -1,570 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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 -#include "opentx.h" - -#define EXPO_ONE_2ND_COLUMN 115 - -int expoFn(int x) -{ - ExpoData * ed = expoAddress(s_currIdx); - int16_t anas[MAX_INPUTS] = {0}; - applyExpos(anas, e_perout_mode_inactive_flight_mode, ed->srcRaw, x); - return anas[ed->chn]; -} - -int getExposLinesCount() -{ - int lastch = -1; - uint8_t count = MAX_INPUTS; - for (int i=0; ichn; - if (ch == lastch) { - count++; - } - else { - lastch = ch; - } - } - return count; -} - -uint8_t getExposCount() -{ - uint8_t count = 0; - uint8_t ch ; - - for (int i=MAX_EXPOS-1 ; i>=0; i--) { - ch = EXPO_VALID(expoAddress(i)); - if (ch != 0) { - count++; - } - } - return count; -} - -bool reachExposLimit() -{ - if (getExposCount() >= MAX_EXPOS) { - POPUP_WARNING(STR_NOFREEEXPO); - return true; - } - return false; -} - -void deleteExpo(uint8_t idx) -{ - pauseMixerCalculations(); - ExpoData * expo = expoAddress(idx); - int input = expo->chn; - memmove(expo, expo+1, (MAX_EXPOS-(idx+1))*sizeof(ExpoData)); - memclear(&g_model.expoData[MAX_EXPOS-1], sizeof(ExpoData)); - if (!isInputAvailable(input)) { - memclear(&g_model.inputNames[input], LEN_INPUT_NAME); - } - resumeMixerCalculations(); - storageDirty(EE_MODEL); -} - -// TODO avoid this global s_currCh on ARM boards ... -int8_t s_currCh; -void insertExpo(uint8_t idx) -{ - pauseMixerCalculations(); - ExpoData * expo = expoAddress(idx); - memmove(expo+1, expo, (MAX_EXPOS-(idx+1))*sizeof(ExpoData)); - memclear(expo, sizeof(ExpoData)); - expo->srcRaw = (s_currCh > 4 ? MIXSRC_Rud - 1 + s_currCh : MIXSRC_Rud - 1 + channelOrder(s_currCh)); - expo->curve.type = CURVE_REF_EXPO; - expo->mode = 3; // pos+neg - expo->chn = s_currCh - 1; - expo->weight = 100; - resumeMixerCalculations(); - storageDirty(EE_MODEL); -} - -void copyExpo(uint8_t idx) -{ - pauseMixerCalculations(); - ExpoData * expo = expoAddress(idx); - memmove(expo+1, expo, (MAX_EXPOS-(idx+1))*sizeof(ExpoData)); - resumeMixerCalculations(); - storageDirty(EE_MODEL); -} - -bool swapExpos(uint8_t & idx, uint8_t up) -{ - ExpoData * x, * y; - int8_t tgt_idx = (up ? idx-1 : idx+1); - - x = expoAddress(idx); - - if (tgt_idx < 0) { - if (x->chn == 0) - return false; - x->chn--; - return true; - } - - if (tgt_idx == MAX_EXPOS) { - if (x->chn == MAX_INPUTS-1) - return false; - x->chn++; - return true; - } - - y = expoAddress(tgt_idx); - if (x->chn != y->chn || !EXPO_VALID(y)) { - if (up) { - if (x->chn>0) x->chn--; - else return false; - } - else { - if (x->chnchn++; - else return false; - } - return true; - } - - pauseMixerCalculations(); - memswap(x, y, sizeof(ExpoData)); - resumeMixerCalculations(); - - idx = tgt_idx; - return true; -} - -enum ExposFields { - EXPO_FIELD_INPUT_NAME, - EXPO_FIELD_NAME, - EXPO_FIELD_SOURCE, - EXPO_FIELD_SCALE, - EXPO_FIELD_WEIGHT, - EXPO_FIELD_OFFSET, - EXPO_FIELD_CURVE, - CASE_FLIGHT_MODES(EXPO_FIELD_FLIGHT_MODES) - EXPO_FIELD_SWITCH, - EXPO_FIELD_SIDE, - EXPO_FIELD_TRIM, - EXPO_FIELD_MAX -}; - -#define CURVE_ROWS 1 - -bool menuModelExpoOne(event_t event) -{ - ExpoData * ed = expoAddress(s_currIdx); - - SUBMENU_WITH_OPTIONS(STR_MENUINPUTS, ICON_MODEL_INPUTS, EXPO_FIELD_MAX, OPTION_MENU_NO_FOOTER|OPTION_MENU_NO_SCROLLBAR, { 0, 0, 0, (ed->srcRaw >= MIXSRC_FIRST_TELEM ? (uint8_t)0 : (uint8_t)HIDDEN_ROW), 0, 0, CURVE_ROWS, CASE_FLIGHT_MODES((MAX_FLIGHT_MODES-1) | NAVIGATION_LINE_BY_LINE) 0 /*, ...*/}); - lcdDrawSizedText(50, 3+FH, g_model.inputNames[ed->chn], LEN_INPUT_NAME, ZCHAR|MENU_TITLE_COLOR); - - int sub = menuVerticalPosition; - - coord_t y = MENU_CONTENT_TOP-FH-2; - - drawFunction(expoFn, CURVE_CENTER_X, CURVE_CENTER_Y, CURVE_SIDE_WIDTH); - drawCurveHorizontalScale(); - drawCurveVerticalScale(CURVE_CENTER_X-CURVE_SIDE_WIDTH-15); - - { - char textx[5]; - char texty[5]; - int x = getValue(ed->srcRaw); - if (ed->srcRaw >= MIXSRC_FIRST_TELEM) { - strAppendUnsigned(textx, calcRESXto100(x)); - // TODO drawSensorCustomValue(LCD_W-8, 6*FH, ed->srcRaw - MIXSRC_FIRST_TELEM, x); - if (ed->scale > 0) x = (x * 1024) / convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale); - } - else { - strAppendSigned(textx, calcRESXto100(x)); - } - - x = limit(-1024, x, 1024); - int y = limit(-1024, expoFn(x), 1024); - strAppendSigned(texty, calcRESXto100(y)); - - x = divRoundClosest(x*CURVE_SIDE_WIDTH, RESX); - y = CURVE_CENTER_Y + getCurveYCoord(expoFn, x, CURVE_SIDE_WIDTH); - - lcdDrawSolidFilledRect(CURVE_CENTER_X+x, CURVE_CENTER_Y-CURVE_SIDE_WIDTH, 2, 2*CURVE_SIDE_WIDTH+2, CURVE_CURSOR_COLOR); - lcdDrawSolidFilledRect(CURVE_CENTER_X-CURVE_SIDE_WIDTH-2, y-1, 2*CURVE_SIDE_WIDTH+2, 2, CURVE_CURSOR_COLOR); - lcdDrawBitmapPattern(CURVE_CENTER_X+x-4, y-4, LBM_CURVE_POINT, CURVE_CURSOR_COLOR); - lcdDrawBitmapPattern(CURVE_CENTER_X+x-4, y-4, LBM_CURVE_POINT_CENTER, TEXT_BGCOLOR); - - int left = limit(CURVE_CENTER_X-CURVE_SIDE_WIDTH, CURVE_CENTER_X-CURVE_COORD_WIDTH/2+x, CURVE_CENTER_X+CURVE_SIDE_WIDTH-CURVE_COORD_WIDTH+2); - drawCurveCoord(left, CURVE_CENTER_Y+CURVE_SIDE_WIDTH+2, textx); - int top = limit(CURVE_CENTER_Y-CURVE_SIDE_WIDTH-1, -CURVE_COORD_HEIGHT/2+y, CURVE_CENTER_Y+CURVE_SIDE_WIDTH-CURVE_COORD_HEIGHT+1); - drawCurveCoord(CURVE_CENTER_X-CURVE_SIDE_WIDTH-37, top, texty); - } - - for (uint8_t k=0; k0 ? BLINK|INVERS : INVERS) : 0); - switch (i) { - case EXPO_FIELD_INPUT_NAME: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_INPUTNAME); - editName(EXPO_ONE_2ND_COLUMN, y, g_model.inputNames[ed->chn], sizeof(g_model.inputNames[ed->chn]), event, attr); - break; - - case EXPO_FIELD_NAME: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_EXPONAME); - editName(EXPO_ONE_2ND_COLUMN, y, ed->name, sizeof(ed->name), event, attr); - break; - - case EXPO_FIELD_SOURCE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SOURCE); - drawSource(EXPO_ONE_2ND_COLUMN, y, ed->srcRaw, menuHorizontalPosition==0 ? attr : 0); - if (attr && menuHorizontalPosition==0) - ed->srcRaw = checkIncDec(event, ed->srcRaw, INPUTSRC_FIRST, INPUTSRC_LAST, EE_MODEL|INCDEC_SOURCE|NO_INCDEC_MARKS, isInputSourceAvailable); - if (ed->srcRaw >= MIXSRC_FIRST_TELEM) { - drawSensorCustomValue(EXPO_ONE_2ND_COLUMN+75, y, (ed->srcRaw - MIXSRC_FIRST_TELEM)/3, convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale), LEFT|(menuHorizontalPosition==1?attr:0)); - if (attr && menuHorizontalPosition == 1) ed->scale = checkIncDec(event, ed->scale, 0, maxTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1), EE_MODEL); - } - else if (attr) { - menuHorizontalPosition = 0; - } - break; - - case EXPO_FIELD_SCALE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SCALE); - drawSensorCustomValue(EXPO_ONE_2ND_COLUMN, y, (ed->srcRaw - MIXSRC_FIRST_TELEM)/3, convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale), LEFT|attr); - if (attr) ed->scale = checkIncDec(event, ed->scale, 0, maxTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1), EE_MODEL); - break; - - case EXPO_FIELD_WEIGHT: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_WEIGHT); - ed->weight = GVAR_MENU_ITEM(EXPO_ONE_2ND_COLUMN, y, ed->weight, MIN_EXPO_WEIGHT, 100, LEFT|attr, 0, event); - break; - - case EXPO_FIELD_OFFSET: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_OFFSET); - ed->offset = GVAR_MENU_ITEM(EXPO_ONE_2ND_COLUMN, y, ed->offset, -100, 100, LEFT|attr, 0, event); - break; - - case EXPO_FIELD_CURVE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CURVE); - editCurveRef(EXPO_ONE_2ND_COLUMN, y, ed->curve, event, attr); - break; - -#if defined(FLIGHT_MODES) - case EXPO_FIELD_FLIGHT_MODES: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_FLMODE); - ed->flightModes = editFlightModes(EXPO_ONE_2ND_COLUMN, y, event, ed->flightModes, attr); - break; -#endif - - case EXPO_FIELD_SWITCH: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SWITCH); - ed->swtch = editSwitch(EXPO_ONE_2ND_COLUMN, y, ed->swtch, attr, event); - break; - - case EXPO_FIELD_SIDE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SIDE); - ed->mode = 4 - editChoice(EXPO_ONE_2ND_COLUMN, y, STR_VCURVEFUNC, 4-ed->mode, 1, 3, attr, event); - break; - - case EXPO_FIELD_TRIM: - uint8_t not_stick = (ed->srcRaw > MIXSRC_Ail); - int8_t carryTrim = -ed->carryTrim; - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TRIM); - lcdDrawTextAtIndex(EXPO_ONE_2ND_COLUMN, y, STR_VMIXTRIMS, (not_stick && carryTrim == 0) ? 0 : carryTrim+1, menuHorizontalPosition==0 ? attr : 0); - if (attr) ed->carryTrim = -checkIncDecModel(event, carryTrim, not_stick ? TRIM_ON : -TRIM_OFF, -TRIM_LAST); - break; - } - y += FH; - } - - return true; -} - -#define _STR_MAX(x) "/" #x -#define STR_MAX(x) _STR_MAX(x) - -#define EXPO_LINE_WEIGHT_POS 125 -#define EXPO_LINE_SRC_POS 135 -#define EXPO_LINE_CURVE_POS 185 -#define EXPO_LINE_SWITCH_POS 233 -#define EXPO_LINE_SIDE_POS 275 -#define EXPO_LINE_FM_POS 295 -#define EXPO_LINE_NAME_POS 405 -#define EXPO_LINE_SELECT_POS 50 -#define EXPO_LINE_SELECT_WIDTH (LCD_W-EXPO_LINE_SELECT_POS-15) - -void lineExpoSurround(coord_t y, LcdFlags flags=CURVE_AXIS_COLOR) -{ - lcdDrawRect(EXPO_LINE_SELECT_POS, y-INVERT_VERT_MARGIN+1, EXPO_LINE_SELECT_WIDTH, INVERT_LINE_HEIGHT, 1, s_copyMode == COPY_MODE ? SOLID : DOTTED, flags); -} - -void onExposMenu(const char * result) -{ - uint8_t chn = expoAddress(s_currIdx)->chn + 1; - - if (result == STR_EDIT) { - pushMenu(menuModelExpoOne); - } - else if (result == STR_INSERT_BEFORE || result == STR_INSERT_AFTER) { - if (!reachExposLimit()) { - s_currCh = chn; - if (result == STR_INSERT_AFTER) { s_currIdx++; menuVerticalPosition++; } - insertExpo(s_currIdx); - pushMenu(menuModelExpoOne); - } - } - else if (result == STR_COPY || result == STR_MOVE) { - s_copyMode = (result == STR_COPY ? COPY_MODE : MOVE_MODE); - s_copySrcIdx = s_currIdx; - s_copySrcCh = chn; - s_copySrcRow = menuVerticalPosition; - } - else if (result == STR_DELETE) { - deleteExpo(s_currIdx); - } -} - -void displayExpoInfos(coord_t y, ExpoData * ed) -{ - drawCurveRef(EXPO_LINE_CURVE_POS, y, ed->curve); - if (ed->swtch) { - drawSwitch(EXPO_LINE_SWITCH_POS, y, ed->swtch); - } - if (ed->mode != 3) { - lcdDrawText(EXPO_LINE_SIDE_POS, y, ed->mode == 2 ? "\176" : "\177"); - } -} - -void displayExpoLine(coord_t y, ExpoData * ed) -{ - drawSource(EXPO_LINE_SRC_POS, y, ed->srcRaw); - - displayExpoInfos(y, ed); - displayFlightModes(EXPO_LINE_FM_POS, y, ed->flightModes, 0); - - if (ed->name[0]) { - lcdDrawSizedText(EXPO_LINE_NAME_POS, y+2, ed->name, sizeof(ed->name), ZCHAR|SMLSIZE); - } -} - -bool menuModelExposAll(event_t event) -{ - int sub = menuVerticalPosition; - - if (s_editMode > 0) { - s_editMode = 0; - } - - uint8_t chn = expoAddress(s_currIdx)->chn + 1; - - int linesCount = getExposLinesCount(); - SIMPLE_MENU(STR_MENUINPUTS, MODEL_ICONS, menuTabModel, MENU_MODEL_INPUTS, linesCount); - - switch (event) { - case EVT_ENTRY: - case EVT_ENTRY_UP: - s_copyMode = 0; - s_copyTgtOfs = 0; - break; - case EVT_KEY_LONG(KEY_EXIT): - if (s_copyMode && s_copyTgtOfs == 0) { - deleteExpo(s_currIdx); - killEvents(event); - event = 0; - } - // no break - case EVT_KEY_BREAK(KEY_EXIT): - if (s_copyMode) { - if (s_copyTgtOfs) { - // cancel the current copy / move operation - if (s_copyMode == COPY_MODE) { - deleteExpo(s_currIdx); - } - else { - do { - swapExpos(s_currIdx, s_copyTgtOfs > 0); - s_copyTgtOfs += (s_copyTgtOfs < 0 ? +1 : -1); - } while (s_copyTgtOfs != 0); - storageDirty(EE_MODEL); - } - menuVerticalPosition = s_copySrcRow; - s_copyTgtOfs = 0; - } - s_copyMode = 0; - event = 0; - } - break; - case EVT_KEY_BREAK(KEY_ENTER): - if ((!s_currCh || (s_copyMode && !s_copyTgtOfs)) && !READ_ONLY()) { - s_copyMode = (s_copyMode == COPY_MODE ? MOVE_MODE : COPY_MODE); - s_copySrcIdx = s_currIdx; - s_copySrcCh = chn; - s_copySrcRow = sub; - break; - } - // no break - - case EVT_KEY_LONG(KEY_ENTER): - killEvents(event); - if (s_copyTgtOfs) { - s_copyMode = 0; - s_copyTgtOfs = 0; - } - else { - if (READ_ONLY()) { - if (!s_currCh) { - pushMenu(menuModelExpoOne); - } - } - else { - if (s_copyMode) s_currCh = 0; - if (s_currCh) { - if (reachExposLimit()) break; - insertExpo(s_currIdx); - pushMenu(menuModelExpoOne); - s_copyMode = 0; - } - else { - event = 0; - s_copyMode = 0; - POPUP_MENU_ADD_ITEM(STR_EDIT); - POPUP_MENU_ADD_ITEM(STR_INSERT_BEFORE); - POPUP_MENU_ADD_ITEM(STR_INSERT_AFTER); - POPUP_MENU_ADD_ITEM(STR_COPY); - POPUP_MENU_ADD_ITEM(STR_MOVE); - POPUP_MENU_ADD_ITEM(STR_DELETE); - POPUP_MENU_START(onExposMenu); - } - } - } - break; - - case EVT_ROTARY_LEFT: - case EVT_ROTARY_RIGHT: - if (s_copyMode) { - uint8_t next_ofs = ((event==EVT_ROTARY_LEFT) ? s_copyTgtOfs - 1 : s_copyTgtOfs + 1); - - if (s_copyTgtOfs==0 && s_copyMode==COPY_MODE) { - // insert a mix on the same channel (just above / just below) - if (reachExposLimit()) break; - copyExpo(s_currIdx); - if (event==EVT_ROTARY_RIGHT) s_currIdx++; - else if (sub-menuVerticalOffset >= 6) menuVerticalOffset++; - } - else if (next_ofs==0 && s_copyMode==COPY_MODE) { - // delete the mix - deleteExpo(s_currIdx); - if (event==EVT_ROTARY_LEFT) s_currIdx--; - } - else { - // only swap the mix with its neighbor - if (!swapExpos(s_currIdx, event==EVT_ROTARY_LEFT)) break; - storageDirty(EE_MODEL); - } - - s_copyTgtOfs = next_ofs; - } - break; - } - - char str[6]; - strAppendUnsigned(strAppend(strAppendUnsigned(str, getExposCount()), "/"), MAX_EXPOS, 2); - lcdDrawText(MENU_TITLE_NEXT_POS, MENU_TITLE_TOP+2, str, HEADER_COLOR); - - sub = menuVerticalPosition; - s_currCh = 0; - int cur = 0; - int i = 0; - - for (int ch=1; ch<=MAX_INPUTS; ch++) { - ExpoData * ed; - coord_t y = MENU_CONTENT_TOP + (cur-menuVerticalOffset)*FH; - if ((ichn+1 == ch && EXPO_VALID(ed))) { - if (cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES) { - drawSource(MENUS_MARGIN_LEFT, y, ch); - } - uint8_t mixCnt = 0; - do { - if (s_copyMode) { - if (s_copyMode == MOVE_MODE && cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES && s_copySrcCh == ch && s_copyTgtOfs != 0 && i == (s_copySrcIdx + (s_copyTgtOfs<0))) { - lineExpoSurround(y); - cur++; y+=FH; - } - if (s_currIdx == i) { - sub = menuVerticalPosition = cur; - s_currCh = ch; - } - } - else if (sub == cur) { - s_currIdx = i; - } - if (cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES) { - LcdFlags attr = ((s_copyMode || sub != cur) ? 0 : INVERS); - - GVAR_MENU_ITEM(EXPO_LINE_WEIGHT_POS, y, ed->weight, MIN_EXPO_WEIGHT, 100, RIGHT | attr | (isExpoActive(i) ? BOLD : 0), 0, 0); - displayExpoLine(y, ed); - - if (s_copyMode) { - if ((s_copyMode==COPY_MODE || s_copyTgtOfs == 0) && s_copySrcCh == ch && i == (s_copySrcIdx + (s_copyTgtOfs<0))) { - /* draw a border around the raw on selection mode (copy/move) */ - lineExpoSurround(y); - } - if (cur == sub) { - /* invert the raw when it's the current one */ - lineExpoSurround(y, ALARM_COLOR); - } - } - } - cur++; y+=FH; mixCnt++; i++; ed++; - } while (ichn+1 == ch && EXPO_VALID(ed)); - if (s_copyMode == MOVE_MODE && cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES && s_copySrcCh == ch && i == (s_copySrcIdx + (s_copyTgtOfs<0))) { - lineExpoSurround(y); - cur++; y+=FH; - } - } - else { - uint8_t attr = 0; - if (sub == cur) { - s_currIdx = i; - s_currCh = ch; - if (!s_copyMode) { - attr = INVERS; - } - } - if (cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES) { - drawSource(MENUS_MARGIN_LEFT, y, ch, attr); - if (s_copyMode == MOVE_MODE && s_copySrcCh == ch) { - lineExpoSurround(y); - } - } - cur++; y+=FH; - } - } - - if (sub >= linesCount-1) menuVerticalPosition = linesCount-1; - - return true; -} diff --git a/radio/src/gui/480x272/model_logical_switches.cpp b/radio/src/gui/480x272/model_logical_switches.cpp deleted file mode 100644 index bfc0b0ac9..000000000 --- a/radio/src/gui/480x272/model_logical_switches.cpp +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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 -#include "opentx.h" - -enum LogicalSwitchFields { - LS_FIELD_FUNCTION, - LS_FIELD_V1, - LS_FIELD_V2, - LS_FIELD_V3, - LS_FIELD_ANDSW, - LS_FIELD_DURATION, - LS_FIELD_DELAY, - LS_FIELD_COUNT, - LS_FIELD_LAST = LS_FIELD_COUNT-1 -}; - -#define CSW_1ST_COLUMN 50 -#define CSW_2ND_COLUMN 120 -#define CSW_3RD_COLUMN 200 -#define CSW_4TH_COLUMN 315 -#define CSW_5TH_COLUMN 375 -#define CSW_6TH_COLUMN 425 - -void putsEdgeDelayParam(coord_t x, coord_t y, LogicalSwitchData *cs, uint8_t lattr, uint8_t rattr) -{ - lcdDrawChar(x, y, '['); - lcdDrawNumber(lcdNextPos+2, y, lswTimerValue(cs->v2), LEFT|PREC1|lattr); - lcdDrawChar(lcdNextPos, y, ':'); - if (cs->v3 < 0) - lcdDrawText(lcdNextPos+3, y, "<<", rattr); - else if (cs->v3 == 0) - lcdDrawText(lcdNextPos+3, y, "--", rattr); - else - lcdDrawNumber(lcdNextPos+3, y, lswTimerValue(cs->v2+cs->v3), LEFT|PREC1|rattr); - lcdDrawChar(lcdNextPos, y, ']'); -} - -void onLogicalSwitchesMenu(const char * result) -{ - LogicalSwitchData * cs = lswAddress(menuVerticalPosition); - - if (result == STR_COPY) { - clipboard.type = CLIPBOARD_TYPE_CUSTOM_SWITCH; - clipboard.data.csw = *cs; - } - else if (result == STR_PASTE) { - *cs = clipboard.data.csw; - storageDirty(EE_MODEL); - } - else if (result == STR_CLEAR) { - memset(cs, 0, sizeof(LogicalSwitchData)); - storageDirty(EE_MODEL); - } -} - -bool menuModelLogicalSwitches(event_t event) -{ - INCDEC_DECLARE_VARS(EE_MODEL); - - MENU(STR_MENULOGICALSWITCHES, MODEL_ICONS, menuTabModel, MENU_MODEL_LOGICAL_SWITCHES, MAX_LOGICAL_SWITCHES, { NAVIGATION_LINE_BY_LINE|LS_FIELD_LAST/*repeated...*/} ); - - LogicalSwitchData * cs = lswAddress(menuVerticalPosition); - uint8_t cstate = lswFamily(cs->func); - if ((cstate == LS_FAMILY_EDGE && menuHorizontalPosition == LS_FIELD_DELAY) || - (cstate != LS_FAMILY_EDGE && menuHorizontalPosition == LS_FIELD_V3)) { - REPEAT_LAST_CURSOR_MOVE(LS_FIELD_LAST, false); - } - -#if defined(MENU_TOOLTIPS) - if (menuVerticalPosition>= 0 && menuHorizontalPosition>=0) { - drawColumnHeader(STR_LSW_HEADERS, STR_LSW_DESCRIPTIONS, menuHorizontalPosition); - } -#endif - - if (menuHorizontalPosition<0 && event==EVT_KEY_LONG(KEY_ENTER) && !READ_ONLY()) { - killEvents(event); - if (cs->func) - POPUP_MENU_ADD_ITEM(STR_COPY); - if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_SWITCH) - POPUP_MENU_ADD_ITEM(STR_PASTE); - if (cs->func || cs->v1 || cs->v2 || cs->delay || cs->duration || cs->andsw) - POPUP_MENU_ADD_ITEM(STR_CLEAR); - POPUP_MENU_START(onLogicalSwitchesMenu); - } - - for (uint8_t i=0; i0) ? BLINK|INVERS : INVERS) : 0); - LcdFlags attr1 = (menuHorizontalPosition==1 ? attr : 0); - LcdFlags attr2 = (menuHorizontalPosition==2 ? attr : 0); - cs = lswAddress(k); - - // CSW name - unsigned int sw = SWSRC_SW1+k; - drawSwitch(MENUS_MARGIN_LEFT, y, sw, (getSwitch(sw) ? BOLD : 0) | ((menuVerticalPosition==k && CURSOR_ON_LINE()) ? INVERS : 0)); - - // CSW func - lcdDrawTextAtIndex(CSW_1ST_COLUMN, y, STR_VCSWFUNC, cs->func, menuHorizontalPosition==0 ? attr : 0); - - // CSW params - cstate = lswFamily(cs->func); - int v1_val = cs->v1; - int16_t v1_min = 0, v1_max = MIXSRC_LAST_TELEM; - int16_t v2_min = 0, v2_max = MIXSRC_LAST_TELEM; - int16_t v3_min =-1, v3_max = 100; - - if (cstate == LS_FAMILY_BOOL || cstate == LS_FAMILY_STICKY) { - drawSwitch(CSW_2ND_COLUMN, y, cs->v1, attr1); - drawSwitch(CSW_3RD_COLUMN, y, cs->v2, attr2); - v1_min = SWSRC_FIRST_IN_LOGICAL_SWITCHES; v1_max = SWSRC_LAST_IN_LOGICAL_SWITCHES; - v2_min = SWSRC_FIRST_IN_LOGICAL_SWITCHES; v2_max = SWSRC_LAST_IN_LOGICAL_SWITCHES; - INCDEC_SET_FLAG(EE_MODEL | INCDEC_SWITCH); - INCDEC_ENABLE_CHECK(isSwitchAvailableInLogicalSwitches); - } - else if (cstate == LS_FAMILY_EDGE) { - drawSwitch(CSW_2ND_COLUMN, y, cs->v1, attr1); - putsEdgeDelayParam(CSW_3RD_COLUMN, y, cs, attr2, (menuHorizontalPosition==LS_FIELD_V3 ? attr : 0)); - v1_min = SWSRC_FIRST_IN_LOGICAL_SWITCHES; v1_max = SWSRC_LAST_IN_LOGICAL_SWITCHES; - v2_min=-129; v2_max = 122; - v3_max = 222 - cs->v2; - if (menuHorizontalPosition == 1) { - INCDEC_SET_FLAG(EE_MODEL | INCDEC_SWITCH); - INCDEC_ENABLE_CHECK(isSwitchAvailableInLogicalSwitches); - } - else { - INCDEC_SET_FLAG(EE_MODEL); - INCDEC_ENABLE_CHECK(NULL); - } - } - else if (cstate == LS_FAMILY_COMP) { - v1_val = cs->v1; - drawSource(CSW_2ND_COLUMN, y, v1_val, attr1); - drawSource(CSW_3RD_COLUMN, y, cs->v2, attr2); - INCDEC_SET_FLAG(EE_MODEL | INCDEC_SOURCE); - INCDEC_ENABLE_CHECK(isSourceAvailable); - } - else if (cstate == LS_FAMILY_TIMER) { - lcdDrawNumber(CSW_2ND_COLUMN, y, lswTimerValue(cs->v1), LEFT|PREC1|attr1); - lcdDrawNumber(CSW_3RD_COLUMN, y, lswTimerValue(cs->v2), LEFT|PREC1|attr2); - v1_min = v2_min = -128; - v1_max = v2_max = 122; - INCDEC_SET_FLAG(EE_MODEL); - INCDEC_ENABLE_CHECK(NULL); - } - else { - v1_val = cs->v1; - drawSource(CSW_2ND_COLUMN, y, v1_val, attr1); - if (menuHorizontalPosition == 1) { - INCDEC_SET_FLAG(EE_MODEL | INCDEC_SOURCE); - INCDEC_ENABLE_CHECK(isSourceAvailableInCustomSwitches); - } - else { - INCDEC_SET_FLAG(EE_MODEL); - INCDEC_ENABLE_CHECK(NULL); - } - LcdFlags lf = attr2 | LEFT; - getMixSrcRange(v1_val, v2_min, v2_max, &lf); - drawSourceCustomValue(CSW_3RD_COLUMN, y, v1_val, (v1_val <= MIXSRC_LAST_CH ? calc100toRESX(cs->v2) : cs->v2), lf); - } - - // CSW AND switch - drawSwitch(CSW_4TH_COLUMN, y, cs->andsw, menuHorizontalPosition==LS_FIELD_ANDSW ? attr : 0); - - // CSW duration - if (cs->duration > 0) - lcdDrawNumber(CSW_5TH_COLUMN, y, cs->duration, (menuHorizontalPosition==LS_FIELD_DURATION ? attr : 0)|PREC1|LEFT); - else - lcdDrawMMM(CSW_5TH_COLUMN, y, menuHorizontalPosition==LS_FIELD_DURATION ? attr : 0); - - // CSW delay - if (cstate == LS_FAMILY_EDGE) { - lcdDrawText(CSW_6TH_COLUMN, y, STR_NA); - } - else if (cs->delay > 0) { - lcdDrawNumber(CSW_6TH_COLUMN, y, cs->delay, (menuHorizontalPosition==LS_FIELD_DELAY ? attr : 0)|PREC1|LEFT); - } - else { - lcdDrawMMM(CSW_6TH_COLUMN, y, menuHorizontalPosition==LS_FIELD_DELAY ? attr : 0); - } - - if (s_editMode>0 && attr) { - switch (menuHorizontalPosition) { - case LS_FIELD_FUNCTION: - { - cs->func = checkIncDec(event, cs->func, 0, LS_FUNC_MAX, EE_MODEL, isLogicalSwitchFunctionAvailable); - uint8_t new_cstate = lswFamily(cs->func); - if (cstate != new_cstate) { - unsigned int save_func = cs->func; - memset(cs, 0, sizeof(LogicalSwitchData)); - cs->func = save_func; - if (new_cstate == LS_FAMILY_TIMER) { - cs->v1 = cs->v2 = -119; - } - else if (new_cstate == LS_FAMILY_EDGE) { - cs->v2 = -129; - } - } - break; - } - case LS_FIELD_V1: - cs->v1 = CHECK_INCDEC_PARAM(event, v1_val, v1_min, v1_max); - break; - case LS_FIELD_V2: - cs->v2 = CHECK_INCDEC_PARAM(event, cs->v2, v2_min, v2_max); - if (cstate==LS_FAMILY_OFS && cs->v1!=0 && event==EVT_KEY_LONG(KEY_ENTER)) { - killEvents(event); - getvalue_t x = getValue(v1_val); - if (v1_val <= MIXSRC_LAST_CH) - cs->v2 = calcRESXto100(x); - storageDirty(EE_MODEL); - } - break; - case LS_FIELD_V3: - cs->v3 = CHECK_INCDEC_PARAM(event, cs->v3, v3_min, v3_max); - break; - case LS_FIELD_ANDSW: - INCDEC_SET_FLAG(EE_MODEL | INCDEC_SWITCH); - INCDEC_ENABLE_CHECK(isSwitchAvailableInLogicalSwitches); - cs->andsw = CHECK_INCDEC_PARAM(event, cs->andsw, -MAX_LS_ANDSW, MAX_LS_ANDSW); - break; - case LS_FIELD_DURATION: - CHECK_INCDEC_MODELVAR_ZERO(event, cs->duration, MAX_LS_DURATION); - break; - case LS_FIELD_DELAY: - CHECK_INCDEC_MODELVAR_ZERO(event, cs->delay, MAX_LS_DELAY); - break; - } - } - } - - return true; -} diff --git a/radio/src/gui/480x272/model_mixes.cpp b/radio/src/gui/480x272/model_mixes.cpp deleted file mode 100644 index 1502b99ec..000000000 --- a/radio/src/gui/480x272/model_mixes.cpp +++ /dev/null @@ -1,624 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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 - -void displayMixStatus(uint8_t channel); - -int getMixesLinesCount() -{ - int lastch = -1; - uint8_t count = MAX_OUTPUT_CHANNELS; - for (int i=0; isrcRaw; - if (!valid) - break; - int ch = mixAddress(i)->destCh; - if (ch == lastch) { - count++; - } - else { - lastch = ch; - } - } - return count; -} - -uint8_t getMixesCount() -{ - uint8_t count = 0; - uint8_t ch ; - - for (int i=MAX_MIXERS-1; i>=0; i--) { - ch = mixAddress(i)->srcRaw; - if (ch != 0) { - count++; - } - } - return count; -} - -bool reachMixesLimit() -{ - if (getMixesCount() >= MAX_MIXERS) { - POPUP_WARNING(STR_NOFREEMIXER); - return true; - } - return false; -} - -void deleteMix(uint8_t idx) -{ - pauseMixerCalculations(); - MixData * mix = mixAddress(idx); - memmove(mix, mix+1, (MAX_MIXERS-(idx+1))*sizeof(MixData)); - memclear(&g_model.mixData[MAX_MIXERS-1], sizeof(MixData)); - resumeMixerCalculations(); - storageDirty(EE_MODEL); -} - -void insertMix(uint8_t idx) -{ - pauseMixerCalculations(); - MixData * mix = mixAddress(idx); - memmove(mix+1, mix, (MAX_MIXERS-(idx+1))*sizeof(MixData)); - memclear(mix, sizeof(MixData)); - mix->destCh = s_currCh-1; - mix->srcRaw = s_currCh; - if (!isSourceAvailable(mix->srcRaw)) { - mix->srcRaw = (s_currCh > 4 ? MIXSRC_Rud - 1 + s_currCh : MIXSRC_Rud - 1 + channelOrder(s_currCh)); - while (!isSourceAvailable(mix->srcRaw)) { - mix->srcRaw += 1; - } - } - mix->weight = 100; - resumeMixerCalculations(); - storageDirty(EE_MODEL); -} - -void copyMix(uint8_t idx) -{ - pauseMixerCalculations(); - MixData * mix = mixAddress(idx); - memmove(mix+1, mix, (MAX_MIXERS-(idx+1))*sizeof(MixData)); - resumeMixerCalculations(); - storageDirty(EE_MODEL); -} - -bool swapMixes(uint8_t & idx, uint8_t up) -{ - MixData * x, * y; - int8_t tgt_idx = (up ? idx-1 : idx+1); - - x = mixAddress(idx); - - if (tgt_idx < 0) { - if (x->destCh == 0) - return false; - x->destCh--; - return true; - } - - if (tgt_idx == MAX_MIXERS) { - if (x->destCh == MAX_OUTPUT_CHANNELS-1) - return false; - x->destCh++; - return true; - } - - y = mixAddress(tgt_idx); - uint8_t destCh = x->destCh; - if(!y->srcRaw || destCh != y->destCh) { - if (up) { - if (destCh>0) x->destCh--; - else return false; - } - else { - if (destChdestCh++; - else return false; - } - return true; - } - - pauseMixerCalculations(); - memswap(x, y, sizeof(MixData)); - resumeMixerCalculations(); - - idx = tgt_idx; - return true; -} - -enum MixFields { - MIX_FIELD_NAME, - MIX_FIELD_SOURCE, - MIX_FIELD_WEIGHT, - MIX_FIELD_OFFSET, - MIX_FIELD_TRIM, - MIX_FIELD_CURVE, - CASE_FLIGHT_MODES(MIX_FIELD_FLIGHT_MODE) - MIX_FIELD_SWITCH, - MIX_FIELD_WARNING, - MIX_FIELD_MLTPX, - MIX_FIELD_DELAY_UP, - MIX_FIELD_DELAY_DOWN, - MIX_FIELD_SLOW_UP, - MIX_FIELD_SLOW_DOWN, - MIX_FIELD_COUNT -}; - -void gvarWeightItem(coord_t x, coord_t y, MixData *md, LcdFlags attr, event_t event) -{ - u_int8int16_t weight; - MD_WEIGHT_TO_UNION(md, weight); - weight.word = GVAR_MENU_ITEM(x, y, weight.word, GV_RANGELARGE_WEIGHT_NEG, GV_RANGELARGE_WEIGHT, attr, 0, event); - MD_UNION_TO_WEIGHT(weight, md); -} - -bool menuModelMixOne(event_t event) -{ - MixData * md2 = mixAddress(s_currIdx) ; - - SUBMENU_WITH_OPTIONS(STR_MIXER, ICON_MODEL_MIXER, MIX_FIELD_COUNT, OPTION_MENU_NO_SCROLLBAR, { 0, 0, 0, 0, 0, 1, CASE_FLIGHT_MODES((MAX_FLIGHT_MODES-1) | NAVIGATION_LINE_BY_LINE) 0 /*, ...*/ }); - putsChn(50, 3+FH, md2->destCh+1, MENU_TITLE_COLOR); - displayMixStatus(md2->destCh); - - // The separation line between 2 columns - lcdDrawSolidVerticalLine(MENU_COLUMN2_X-10, DEFAULT_SCROLLBAR_Y-FH, DEFAULT_SCROLLBAR_H+5, TEXT_COLOR); - - int8_t sub = menuVerticalPosition; - int8_t editMode = s_editMode; - - for (int k=0; k<2*NUM_BODY_LINES; k++) { - coord_t y; - if (k > NUM_BODY_LINES) { - y = MENU_CONTENT_TOP - FH + (k-NUM_BODY_LINES)*FH; - } - else { - y = MENU_CONTENT_TOP - FH + k*FH; - } - int8_t i = k; - - LcdFlags attr = (sub==i ? (editMode>0 ? BLINK|INVERS : INVERS) : 0); - switch(i) { - case MIX_FIELD_NAME: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MIXNAME); - editName(MIXES_2ND_COLUMN, y, md2->name, sizeof(md2->name), event, attr); - break; - case MIX_FIELD_SOURCE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SOURCE); - drawSource(MIXES_2ND_COLUMN, y, md2->srcRaw, attr); - if (attr) CHECK_INCDEC_MODELSOURCE(event, md2->srcRaw, 1, MIXSRC_LAST); - break; - case MIX_FIELD_WEIGHT: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_WEIGHT); - gvarWeightItem(MIXES_2ND_COLUMN, y, md2, attr|LEFT, event); - break; - case MIX_FIELD_OFFSET: - { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_OFFSET); - u_int8int16_t offset; - MD_OFFSET_TO_UNION(md2, offset); - offset.word = GVAR_MENU_ITEM(MIXES_2ND_COLUMN, y, offset.word, GV_RANGELARGE_OFFSET_NEG, GV_RANGELARGE_OFFSET, attr|LEFT, 0, event); - MD_UNION_TO_OFFSET(offset, md2); -#if 0 - drawOffsetBar(x+MIXES_2ND_COLUMN+22, y, md2); -#endif - break; - } - case MIX_FIELD_TRIM: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TRIM); - drawCheckBox(MIXES_2ND_COLUMN, y, !md2->carryTrim, attr); - if (attr) md2->carryTrim = !checkIncDecModel(event, !md2->carryTrim, 0, 1); - break; - case MIX_FIELD_CURVE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CURVE); - editCurveRef(MIXES_2ND_COLUMN, y, md2->curve, event, attr); - break; -#if defined(FLIGHT_MODES) - case MIX_FIELD_FLIGHT_MODE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_FLMODE); - md2->flightModes = editFlightModes(MIXES_2ND_COLUMN, y, event, md2->flightModes, attr); - break; -#endif - case MIX_FIELD_SWITCH: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SWITCH); - md2->swtch = editSwitch(MIXES_2ND_COLUMN, y, md2->swtch, attr, event); - break; - case MIX_FIELD_WARNING: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MIXWARNING); - if (md2->mixWarn) { - lcdDrawNumber(MIXES_2ND_COLUMN, y, md2->mixWarn, attr|LEFT); - } - else { - lcdDrawText(MIXES_2ND_COLUMN, y, STR_OFF, attr); - } - if (attr) { - CHECK_INCDEC_MODELVAR_ZERO(event, md2->mixWarn, 3); - } - break; - case MIX_FIELD_MLTPX: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MULTPX); - md2->mltpx = editChoice(MIXES_2ND_COLUMN, y, STR_VMLTPX, md2->mltpx, 0, 2, attr, event); - break; - case MIX_FIELD_DELAY_UP: - lcdDrawText(MENU_COLUMN2_X+MENUS_MARGIN_LEFT, y, STR_DELAYUP); - md2->delayUp = editDelay(MENU_COLUMN2_X, y, event, attr, md2->delayUp); - break; - case MIX_FIELD_DELAY_DOWN: - lcdDrawText(MENU_COLUMN2_X+MENUS_MARGIN_LEFT, y, STR_DELAYDOWN); - md2->delayDown = editDelay(MENU_COLUMN2_X, y, event, attr, md2->delayDown); - break; - case MIX_FIELD_SLOW_UP: - lcdDrawText(MENU_COLUMN2_X+MENUS_MARGIN_LEFT, y, STR_SLOWUP); - md2->speedUp = editDelay(MENU_COLUMN2_X, y, event, attr, md2->speedUp); - break; - case MIX_FIELD_SLOW_DOWN: - lcdDrawText(MENU_COLUMN2_X+MENUS_MARGIN_LEFT, y, STR_SLOWDOWN); - md2->speedDown = editDelay(MENU_COLUMN2_X, y, event, attr, md2->speedDown); - break; - } - } - - return true; -} - -#define _STR_MAX(x) "/" #x -#define STR_MAX(x) _STR_MAX(x) - -#define MIX_LINE_WEIGHT_POS 105 -#define MIX_LINE_SRC_POS 120 -#define MIX_LINE_CURVE_ICON 175 -#define MIX_LINE_CURVE_POS 195 -#define MIX_LINE_SWITCH_ICON 260 -#define MIX_LINE_SWITCH_POS 280 -#define MIX_LINE_DELAY_SLOW_POS 340 -#define MIX_LINE_NAME_FM_ICON 370 -#define MIX_LINE_NAME_FM_POS 390 -#define MIX_LINE_SELECT_POS 50 -#define MIX_LINE_SELECT_WIDTH (LCD_W-MIX_LINE_SELECT_POS-15) -#define MIX_STATUS_BAR_W 130 -#define MIX_STATUS_BAR_H 13 -#define MIX_STATUS_CHAN_BAR MENUS_MARGIN_LEFT + 45 -#define MIX_STATUS_ICON_MIXER MIX_STATUS_CHAN_BAR + 140 -#define MIX_STATUS_ICON_TO MIX_STATUS_ICON_MIXER + 20 -#define MIX_STATUS_ICON_OUTPUT MIX_STATUS_ICON_TO + 35 -#define MIX_STATUS_OUT_NAME MIX_STATUS_ICON_OUTPUT + 25 -#define MIX_STATUS_OUT_BAR LCD_W - MENUS_MARGIN_LEFT - MIX_STATUS_BAR_W - -void lineMixSurround(coord_t y, LcdFlags flags=CURVE_AXIS_COLOR) -{ - lcdDrawRect(MIX_LINE_SELECT_POS, y-INVERT_VERT_MARGIN+1, MIX_LINE_SELECT_WIDTH, INVERT_LINE_HEIGHT, 1, s_copyMode == COPY_MODE ? SOLID : DOTTED, flags); -} - -void onMixesMenu(const char * result) -{ - uint8_t chn = mixAddress(s_currIdx)->destCh + 1; - - if (result == STR_EDIT) { - pushMenu(menuModelMixOne); - } - else if (result == STR_INSERT_BEFORE || result == STR_INSERT_AFTER) { - if (!reachMixesLimit()) { - s_currCh = chn; - if (result == STR_INSERT_AFTER) { s_currIdx++; menuVerticalPosition++; } - insertMix(s_currIdx); - pushMenu(menuModelMixOne); - } - } - else if (result == STR_COPY || result == STR_MOVE) { - s_copyMode = (result == STR_COPY ? COPY_MODE : MOVE_MODE); - s_copySrcIdx = s_currIdx; - s_copySrcCh = chn; - s_copySrcRow = menuVerticalPosition; - } - else if (result == STR_DELETE) { - deleteMix(s_currIdx); - } -} - -void displayMixInfos(coord_t y, MixData *md) -{ - if (md->curve.value != 0 ) lcd->drawBitmap(MIX_LINE_CURVE_ICON, y + 2, mixerSetupCurveBitmap); - drawCurveRef(MIX_LINE_CURVE_POS, y, md->curve); - - if (md->swtch) { - lcd->drawBitmap(MIX_LINE_SWITCH_ICON, y + 2, mixerSetupSwitchBitmap); - drawSwitch(MIX_LINE_SWITCH_POS, y, md->swtch); - } -} - -void displayMixSmallFlightModes(coord_t x, coord_t y, FlightModesType value) -{ - for (int i=0; idrawFilledRect(x, y+2, 8, 12 , SOLID, CURVE_AXIS_COLOR); - lcdDrawText(x, y, s, SMLSIZE); - x += 8; - } -} - -void displayMixLine(coord_t y, MixData *md) -{ - if (md->name[0] && md->flightModes) { - if (SLOW_BLINK_ON_PHASE) { - lcd->drawBitmap(MIX_LINE_NAME_FM_ICON, y + 2, mixerSetupFlightmodeBitmap); - displayMixSmallFlightModes(MIX_LINE_NAME_FM_POS, y + 2, md->flightModes); - } - else { - lcd->drawBitmap(MIX_LINE_NAME_FM_ICON, y + 2, mixerSetupLabelBitmap); - lcdDrawSizedText(MIX_LINE_NAME_FM_POS, y, md->name, sizeof(md->name), ZCHAR); - } - } - else if (md->name[0]) { - lcd->drawBitmap(MIX_LINE_NAME_FM_ICON, y + 2, mixerSetupLabelBitmap); - lcdDrawSizedText(MIX_LINE_NAME_FM_POS, y, md->name, sizeof(md->name), ZCHAR); - } - else if (md->flightModes) { - lcd->drawBitmap(MIX_LINE_NAME_FM_ICON, y + 2, mixerSetupFlightmodeBitmap); - displayMixSmallFlightModes(MIX_LINE_NAME_FM_POS, y + 2, md->flightModes); - } - displayMixInfos(y, md); -} - -void displayMixStatus(uint8_t channel) -{ - lcdDrawNumber(MENUS_MARGIN_LEFT, MENU_FOOTER_TOP, channel + 1, MENU_TITLE_COLOR, 0, "CH", NULL); - drawSingleMixerBar(MIX_STATUS_CHAN_BAR, MENU_FOOTER_TOP + 4, MIX_STATUS_BAR_W, MIX_STATUS_BAR_H, channel); - - lcd->drawBitmap(MIX_STATUS_ICON_MIXER, MENU_FOOTER_TOP, mixerSetupMixerBitmap); - lcd->drawBitmap(MIX_STATUS_ICON_TO, MENU_FOOTER_TOP, mixerSetupToBitmap); - lcd->drawBitmap(MIX_STATUS_ICON_OUTPUT, MENU_FOOTER_TOP, mixerSetupOutputBitmap); - - if (g_model.limitData[channel].name[0] == '\0') - lcdDrawNumber(MIX_STATUS_OUT_NAME, MENU_FOOTER_TOP, channel + 1, MENU_TITLE_COLOR, 0, "CH", NULL); - else - lcdDrawSizedText(MIX_STATUS_OUT_NAME, MENU_FOOTER_TOP, g_model.limitData[channel].name, sizeof(g_model.limitData[channel].name), MENU_TITLE_COLOR | LEFT | ZCHAR); - drawSingleOutputBar(MIX_STATUS_OUT_BAR, MENU_FOOTER_TOP + 4, MIX_STATUS_BAR_W, MIX_STATUS_BAR_H, channel); -} - -bool menuModelMixAll(event_t event) -{ - const BitmapBuffer * mpx_mode[] = { - mixerSetupAddBitmap, - mixerSetupMultiBitmap, - mixerSetupReplaceBitmap - }; - - - uint8_t sub = menuVerticalPosition; - - if (s_editMode > 0) { - s_editMode = 0; - } - - uint8_t chn = mixAddress(s_currIdx)->destCh + 1; - - int linesCount = getMixesLinesCount(); - SIMPLE_MENU(STR_MIXER, MODEL_ICONS, menuTabModel, MENU_MODEL_MIXES, linesCount); - - switch (event) { - case EVT_ENTRY: - case EVT_ENTRY_UP: - s_copyMode = 0; - s_copyTgtOfs = 0; - break; - case EVT_KEY_LONG(KEY_EXIT): - if (s_copyMode && s_copyTgtOfs == 0) { - deleteMix(s_currIdx); - killEvents(event); - event = 0; - } - // no break - case EVT_KEY_BREAK(KEY_EXIT): - if (s_copyMode) { - if (s_copyTgtOfs) { - // cancel the current copy / move operation - if (s_copyMode == COPY_MODE) { - deleteMix(s_currIdx); - } - else { - do { - swapMixes(s_currIdx, s_copyTgtOfs > 0); - s_copyTgtOfs += (s_copyTgtOfs < 0 ? +1 : -1); - } while (s_copyTgtOfs != 0); - storageDirty(EE_MODEL); - } - menuVerticalPosition = s_copySrcRow; - s_copyTgtOfs = 0; - } - s_copyMode = 0; - event = 0; - } - break; - case EVT_KEY_BREAK(KEY_ENTER): - if ((!s_currCh || (s_copyMode && !s_copyTgtOfs)) && !READ_ONLY()) { - s_copyMode = (s_copyMode == COPY_MODE ? MOVE_MODE : COPY_MODE); - s_copySrcIdx = s_currIdx; - s_copySrcCh = chn; - s_copySrcRow = sub; - break; - } - // no break - - case EVT_KEY_LONG(KEY_ENTER): - killEvents(event); - if (s_copyTgtOfs) { - s_copyMode = 0; - s_copyTgtOfs = 0; - return true; - } - else { - if (READ_ONLY()) { - if (!s_currCh) { - pushMenu(menuModelMixOne); - } - } - else { - if (s_copyMode) s_currCh = 0; - if (s_currCh) { - if (reachMixesLimit()) break; - insertMix(s_currIdx); - pushMenu(menuModelMixOne); - s_copyMode = 0; - return true; - } - else { - event = 0; - s_copyMode = 0; - POPUP_MENU_ADD_ITEM(STR_EDIT); - POPUP_MENU_ADD_ITEM(STR_INSERT_BEFORE); - POPUP_MENU_ADD_ITEM(STR_INSERT_AFTER); - POPUP_MENU_ADD_ITEM(STR_COPY); - POPUP_MENU_ADD_ITEM(STR_MOVE); - POPUP_MENU_ADD_ITEM(STR_DELETE); - POPUP_MENU_START(onMixesMenu); - } - } - } - break; - - case EVT_ROTARY_RIGHT: - case EVT_ROTARY_LEFT: - if (s_copyMode) { - uint8_t next_ofs = (event==EVT_ROTARY_LEFT ? s_copyTgtOfs - 1 : s_copyTgtOfs + 1); - - if (s_copyTgtOfs==0 && s_copyMode==COPY_MODE) { - // insert a mix on the same channel (just above / just below) - if (reachMixesLimit()) break; - copyMix(s_currIdx); - if (event==EVT_ROTARY_RIGHT) s_currIdx++; - else if (sub-menuVerticalOffset >= 6) menuVerticalOffset++; - } - else if (next_ofs==0 && s_copyMode==COPY_MODE) { - // delete the mix - deleteMix(s_currIdx); - if (event==EVT_ROTARY_LEFT) s_currIdx--; - } - else { - // only swap the mix with its neighbor - if (!swapMixes(s_currIdx, event==EVT_ROTARY_LEFT)) break; - storageDirty(EE_MODEL); - } - - s_copyTgtOfs = next_ofs; - } - break; - } - - char str[6]; - strAppendUnsigned(strAppend(strAppendUnsigned(str, getMixesCount()), "/"), MAX_MIXERS, 2); - lcdDrawText(MENU_TITLE_NEXT_POS, MENU_TITLE_TOP+1, str, HEADER_COLOR); - - sub = menuVerticalPosition; - s_currCh = 0; - int cur = 0; - int i = 0; - - for (int ch=1; ch<=MAX_OUTPUT_CHANNELS; ch++) { - MixData * md; - coord_t y = MENU_CONTENT_TOP + (cur-menuVerticalOffset)*FH; - if (isrcRaw && md->destCh+1 == ch) { - if (cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES) { - putsChn(MENUS_MARGIN_LEFT, y, ch, 0); // show CHx - } - uint8_t mixCnt = 0; - do { - if (s_copyMode) { - if (s_copyMode == MOVE_MODE && cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES && s_copySrcCh == ch && s_copyTgtOfs != 0 && i == (s_copySrcIdx + (s_copyTgtOfs<0))) { - lineMixSurround(y); - cur++; y+=FH; - } - if (s_currIdx == i) { - sub = menuVerticalPosition = cur; - s_currCh = ch; - } - } - else if (sub == cur) { - s_currIdx = i; - displayMixStatus(ch - 1); - } - if (cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES) { - LcdFlags attr = ((s_copyMode || sub != cur) ? 0 : INVERS); - - if (mixCnt > 0) { - lcd->drawBitmap(10, y, mpx_mode[md->mltpx]); - } - - drawSource(MIX_LINE_SRC_POS, y, md->srcRaw); - - if (mixCnt == 0 && md->mltpx == 1) - lcdDrawText(MIX_LINE_WEIGHT_POS, y, "MULT!", RIGHT | attr | (isMixActive(i) ? BOLD : 0)); - else - gvarWeightItem(MIX_LINE_WEIGHT_POS, y, md, RIGHT | attr | (isMixActive(i) ? BOLD : 0), event); - - displayMixLine(y, md); - - BitmapBuffer *delayslowbmp[] = {mixerSetupSlowBitmap, mixerSetupDelayBitmap, mixerSetupDelaySlowBitmap}; - uint8_t delayslow = 0; - if (md->speedDown || md->speedUp) - delayslow = 1; - if (md->delayUp || md->delayDown) - delayslow += 2; - if (delayslow) - lcd->drawBitmap(MIX_LINE_DELAY_SLOW_POS, y + 2, delayslowbmp[delayslow - 1]); - - if (s_copyMode) { - if ((s_copyMode==COPY_MODE || s_copyTgtOfs == 0) && s_copySrcCh == ch && i == (s_copySrcIdx + (s_copyTgtOfs<0))) { - /* draw a border around the raw on selection mode (copy/move) */ - lineMixSurround(y); - } - if (cur == sub) { - /* invert the raw when it's the current one */ - lineMixSurround(y, ALARM_COLOR); - } - } - } - cur++; y+=FH; mixCnt++; i++; md++; - } while (isrcRaw && md->destCh+1 == ch); - if (s_copyMode == MOVE_MODE && cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES && s_copySrcCh == ch && i == (s_copySrcIdx + (s_copyTgtOfs<0))) { - lineMixSurround(y); - cur++; y+=FH; - } - } - else { - uint8_t attr = 0; - if (sub == cur) { - s_currIdx = i; - s_currCh = ch; - if (!s_copyMode) { - attr = INVERS; - } - } - if (cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES) { - putsChn(MENUS_MARGIN_LEFT, y, ch, attr); // show CHx - if (s_copyMode == MOVE_MODE && s_copySrcCh == ch) { - lineMixSurround(y); - } - } - cur++; y+=FH; - } - } - - if (sub >= linesCount-1) menuVerticalPosition = linesCount-1; - - return true; -} diff --git a/radio/src/gui/480x272/model_module_options.cpp b/radio/src/gui/480x272/model_module_options.cpp deleted file mode 100644 index 4f088fd6a..000000000 --- a/radio/src/gui/480x272/model_module_options.cpp +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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 - -#define RECEIVER_OPTIONS_2ND_COLUMN 200 - -extern uint8_t g_moduleIdx; - -enum { - MODULE_SETTINGS_OK = 0, - MODULE_SETTINGS_DIRTY = 1, - MODULE_SETTINGS_REBIND = 2, - MODULE_SETTINGS_WRITING = 4, -}; - -void onTxOptionsUpdateConfirm(const char * result) -{ - if (result == STR_OK) { - reusableBuffer.hardwareAndSettings.moduleSettings.dirty = MODULE_SETTINGS_WRITING; - moduleState[g_moduleIdx].writeModuleSettings(&reusableBuffer.hardwareAndSettings.moduleSettings); - } - else { - popMenu(); - } -} - -bool isTelemetryAvailable() -{ - return reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].information.variant != PXX2_VARIANT_EU || - reusableBuffer.hardwareAndSettings.moduleSettings.txPower <= 14; -} - -enum { - ITEM_MODULE_SETTINGS_EXTERNAL_ANTENNA, - ITEM_MODULE_SETTINGS_POWER, - ITEM_MODULE_SETTINGS_TELEMETRY, - ITEM_MODULE_SETTINGS_COUNT -}; - -#define IF_MODULE_OPTIONS(option, count) uint8_t(isPXX2ModuleOptionAvailable(modelId, option) ? count : HIDDEN_ROW) - -bool isPowerAvailable(int value) -{ - uint8_t modelId = reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].information.modelID; - uint8_t variant = reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].information.variant; - - if (modelId == PXX2_MODULE_R9M_LITE) { - if (variant == PXX2_VARIANT_EU) - return (value == 14 /* 25 mW with telemetry */ || - value == 20 /* 100 mW without telemetry */); - else - return value == 20; /* 100 mW */ - } - else if (modelId == PXX2_MODULE_R9M || modelId == PXX2_MODULE_R9M_LITE_PRO) { - if (variant == PXX2_VARIANT_EU) - return (value == 14 /* 25 mW */ || - value == 23 /* 200 mW */ || - value == 27 /* 500 mW */); - else - return (value == 10 /* 10 mW */ || - value == 20 /* 100 mW */ || - value == 27 /* 500 mW */ || - value == 30 /* 1000 mW */); - } - else { - return (value <= 20); /* 100 mW max for XJTs */ - } -} - -bool menuModelModuleOptions(event_t event) -{ - if (event == EVT_ENTRY) { - memclear(&reusableBuffer.hardwareAndSettings, sizeof(reusableBuffer.hardwareAndSettings)); -#if defined(SIMU) - reusableBuffer.hardwareAndSettings.moduleSettings.state = PXX2_SETTINGS_OK; -#endif - } - - uint8_t modelId = reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].information.modelID; - // uint8_t variant = reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].information.variant; - uint8_t optionsAvailable = getPXX2ModuleOptions(modelId) & ((1 << MODULE_OPTION_EXTERNAL_ANTENNA) | (1 << MODULE_OPTION_POWER)); - - SUBMENU(STR_MODULE_OPTIONS, ICON_MODEL_SETUP, ITEM_MODULE_SETTINGS_COUNT, { - !optionsAvailable ? (uint8_t)0 : IF_MODULE_OPTIONS(MODULE_OPTION_EXTERNAL_ANTENNA, 0), - IF_MODULE_OPTIONS(MODULE_OPTION_POWER, 0), - IF_MODULE_OPTIONS(MODULE_OPTION_POWER, isTelemetryAvailable() ? HIDDEN_ROW : READONLY_ROW) - }); - - lcdDrawText(50, 3 + FH, getPXX2ModuleName(modelId), MENU_TITLE_COLOR); - - if (reusableBuffer.hardwareAndSettings.moduleSettings.state == PXX2_HARDWARE_INFO && moduleState[g_moduleIdx].mode == MODULE_MODE_NORMAL) { - if (!modelId) - moduleState[g_moduleIdx].readModuleInformation(&reusableBuffer.hardwareAndSettings.modules[g_moduleIdx], PXX2_HW_INFO_TX_ID, PXX2_HW_INFO_TX_ID); - else - moduleState[g_moduleIdx].readModuleSettings(&reusableBuffer.hardwareAndSettings.moduleSettings); - } - - if (menuEvent) { - killEvents(KEY_EXIT); - moduleState[g_moduleIdx].mode = MODULE_MODE_NORMAL; - if (reusableBuffer.hardwareAndSettings.moduleSettings.dirty) { - abortPopMenu(); - POPUP_CONFIRMATION(STR_UPDATE_TX_OPTIONS, onTxOptionsUpdateConfirm); - } - else { - return false; - } - } - - if (event == EVT_KEY_LONG(KEY_ENTER) && reusableBuffer.hardwareAndSettings.moduleSettings.dirty) { - killEvents(event); - reusableBuffer.hardwareAndSettings.moduleSettings.dirty = MODULE_SETTINGS_OK; - moduleState[g_moduleIdx].writeModuleSettings(&reusableBuffer.hardwareAndSettings.moduleSettings); - } - - if (reusableBuffer.hardwareAndSettings.moduleSettings.dirty == MODULE_SETTINGS_WRITING && reusableBuffer.hardwareAndSettings.moduleSettings.state == PXX2_SETTINGS_OK) { - popMenu(); - return false; - } - - if (modelId != 0 && mstate_tab[menuVerticalPosition] == HIDDEN_ROW) { - menuVerticalPosition = 0; - while (menuVerticalPosition < ITEM_MODULE_SETTINGS_COUNT && mstate_tab[menuVerticalPosition] == HIDDEN_ROW) { - ++menuVerticalPosition; - } - } - - int8_t sub = menuVerticalPosition; - - if (reusableBuffer.hardwareAndSettings.moduleSettings.state == PXX2_SETTINGS_OK) { - if (optionsAvailable) { - for (uint8_t k=0; k0 ? BLINK|INVERS : INVERS) : 0); - - switch (i) { - case ITEM_MODULE_SETTINGS_EXTERNAL_ANTENNA: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_EXT_ANTENNA); - reusableBuffer.hardwareAndSettings.moduleSettings.externalAntenna = editCheckBox(reusableBuffer.hardwareAndSettings.moduleSettings.externalAntenna, RECEIVER_OPTIONS_2ND_COLUMN, y, attr, event); - if (attr && checkIncDec_Ret) { - reusableBuffer.hardwareAndSettings.moduleSettings.dirty = MODULE_SETTINGS_DIRTY; - } - break; - - case ITEM_MODULE_SETTINGS_POWER: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_POWER); - lcdDrawNumber(RECEIVER_OPTIONS_2ND_COLUMN, y, reusableBuffer.hardwareAndSettings.moduleSettings.txPower, attr); - lcdDrawText(lcdNextPos, y, "dBm("); - drawPower(lcdNextPos, y, reusableBuffer.hardwareAndSettings.moduleSettings.txPower); - lcdDrawText(lcdNextPos, y, ")"); - if (attr) { - bool previousTelemetry = isTelemetryAvailable(); - reusableBuffer.hardwareAndSettings.moduleSettings.txPower = checkIncDec(event, reusableBuffer.hardwareAndSettings.moduleSettings.txPower, 0, 30, 0, &isPowerAvailable); - if (checkIncDec_Ret) { - reusableBuffer.hardwareAndSettings.moduleSettings.dirty = MODULE_SETTINGS_DIRTY; - if (previousTelemetry != isTelemetryAvailable()) { - reusableBuffer.hardwareAndSettings.moduleSettings.dirty |= MODULE_SETTINGS_REBIND; - } - } - if (s_editMode == 0 && (reusableBuffer.hardwareAndSettings.moduleSettings.dirty & MODULE_SETTINGS_REBIND)) { - reusableBuffer.hardwareAndSettings.moduleSettings.dirty &= ~MODULE_SETTINGS_REBIND; - POPUP_WARNING(STR_REBIND); - } - } - break; - - case ITEM_MODULE_SETTINGS_TELEMETRY: - // only displayed in EU mode when TX power > 25mW - lcdDrawText(RECEIVER_OPTIONS_2ND_COLUMN, y, "Telem OFF", attr | SMLSIZE); - break; - } - } - } - else { - lcdDrawCenteredText(LCD_H/2, STR_NO_TX_OPTIONS); - s_editMode = 0; - } - } - else { - lcdDrawCenteredText(LCD_H/2, STR_WAITING_FOR_TX); - s_editMode = 0; - } - - return true; -} diff --git a/radio/src/gui/480x272/model_outputs.cpp b/radio/src/gui/480x272/model_outputs.cpp deleted file mode 100644 index af5128fd7..000000000 --- a/radio/src/gui/480x272/model_outputs.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -enum LimitsItems { - ITEM_LIMITS_CH_NAME, - ITEM_LIMITS_OFFSET, - ITEM_LIMITS_MIN, - ITEM_LIMITS_MAX, - ITEM_LIMITS_DIRECTION, - ITEM_LIMITS_CURVE, -#if defined(PPM_CENTER_ADJUSTABLE) - ITEM_LIMITS_PPM_CENTER, -#endif -#if defined(PPM_LIMITS_SYMETRICAL) - ITEM_LIMITS_SYMETRICAL, -#endif - ITEM_LIMITS_COUNT, - ITEM_LIMITS_MAXROW = ITEM_LIMITS_COUNT-1 -}; - -#define LIMITS_NAME_POS 52 -#define LIMITS_OFFSET_POS 160 -#define LIMITS_MIN_POS 220 -#define LIMITS_DIRECTION_POS 235 -#define LIMITS_MAX_POS 300 -#define LIMITS_REVERT_POS 312 -#define LIMITS_CURVE_POS 340 -#define LIMITS_PPM_CENTER_POS 440 -#define LIMITS_SYMETRICAL_POS 450 - -#define LIMITS_MIN_MAX_OFFSET 1000 -#define CONVERT_US_MIN_MAX(x) (((x)*1280)/250) - -#if defined(PPM_UNIT_US) - #define SET_MIN_MAX(x, val) x = ((val)*250)/128 - #define MIN_MAX_DISPLAY(x) CONVERT_US_MIN_MAX(x) -#else - #define MIN_MAX_DISPLAY(x) (x) - #define SET_MIN_MAX(x, val) x = (val) -#endif - -void onLimitsMenu(const char *result) -{ - uint8_t ch = menuVerticalPosition; - - if (result == STR_RESET) { - LimitData *ld = limitAddress(ch); - ld->min = 0; - ld->max = 0; - ld->offset = 0; - ld->ppmCenter = 0; - ld->revert = false; - ld->curve = 0; - storageDirty(EE_MODEL); - } - else if (result == STR_COPY_STICKS_TO_OFS) { - copySticksToOffset(ch); - storageDirty(EE_MODEL); - } - else if (result == STR_COPY_TRIMS_TO_OFS) { - copyTrimsToOffset(ch); - storageDirty(EE_MODEL); - } -} - -bool menuModelLimits(event_t event) -{ - MENU(STR_MENULIMITS, MODEL_ICONS, menuTabModel, MENU_MODEL_OUTPUTS, MAX_OUTPUT_CHANNELS+1, - { NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, - NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, - NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, - NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, - 0 }); - - uint32_t sub = menuVerticalPosition; - - if (sub=0) { - drawColumnHeader(STR_LIMITS_HEADERS, NULL, menuHorizontalPosition); - } - - for (int i=0; irevert) ? -LIMIT_OFS(ld) : LIMIT_OFS(ld); - char swVal[2] = "-"; // '-', '<', '>' - if ((channelOutputs[k] - v) > 50) swVal[0] = (ld->revert ? 127 : 126); // Switch to raw inputs? - remove trim! - if ((channelOutputs[k] - v) < -50) swVal[0] = (ld->revert ? 126 : 127); - lcdDrawText(LIMITS_DIRECTION_POS, y, swVal); - - int limit = (g_model.extendedLimits ? LIMIT_EXT_MAX : 1000); - - putsChn(MENUS_MARGIN_LEFT, y, k+1, (sub==k && menuHorizontalPosition < 0) ? INVERS : 0); - if (sub==k && menuHorizontalPosition < 0 && event==EVT_KEY_LONG(KEY_ENTER) && !READ_ONLY()) { - killEvents(event); - POPUP_MENU_ADD_ITEM(STR_RESET); - POPUP_MENU_ADD_ITEM(STR_COPY_TRIMS_TO_OFS); - POPUP_MENU_ADD_ITEM(STR_COPY_STICKS_TO_OFS); - POPUP_MENU_START(onLimitsMenu); - } - - for (uint8_t j=0; j0) ? BLINK|INVERS : INVERS) : 0); - uint8_t active = (attr && s_editMode>0) ; - switch(j) - { - case ITEM_LIMITS_CH_NAME: - editName(LIMITS_NAME_POS, y, ld->name, sizeof(ld->name), event, attr); - break; - - case ITEM_LIMITS_OFFSET: - if (GV_IS_GV_VALUE(ld->offset, -1000, 1000) || (attr && event == EVT_KEY_LONG(KEY_ENTER))) { - ld->offset = GVAR_MENU_ITEM(LIMITS_OFFSET_POS, y, ld->offset, -1000, 1000, attr|PREC1|RIGHT, 0, event); - break; - } - -#if defined(PPM_UNIT_US) - lcdDrawNumber(LIMITS_OFFSET_POS, y, ((int32_t)ld->offset*128) / 25, attr|PREC1|RIGHT); -#else - lcdDrawNumber(LIMITS_OFFSET_POS, y, ld->offset, attr|PREC1|RIGHT); -#endif - if (active) { - ld->offset = checkIncDec(event, ld->offset, -1000, 1000, EE_MODEL, NULL, stops1000); - } - - // TODO with the contextual menu I think - // else if (attr && event==EVT_KEY_LONG(KEY_MENU)) { - // copySticksToOffset(k); - // s_editMode = 0; - // } - break; - - case ITEM_LIMITS_MIN: - if (GV_IS_GV_VALUE(ld->min, -GV_RANGELARGE, GV_RANGELARGE) || (attr && event == EVT_KEY_LONG(KEY_ENTER))) { - ld->min = GVAR_MENU_ITEM(LIMITS_MIN_POS, y, ld->min, -LIMIT_EXT_MAX, LIMIT_EXT_MAX, attr|PREC1|RIGHT, 0, event); - break; - } - lcdDrawNumber(LIMITS_MIN_POS, y, MIN_MAX_DISPLAY(ld->min-LIMITS_MIN_MAX_OFFSET), attr|PREC1|RIGHT); - if (active) ld->min = LIMITS_MIN_MAX_OFFSET + checkIncDec(event, ld->min-LIMITS_MIN_MAX_OFFSET, -limit, 0, EE_MODEL, NULL, stops1000); - break; - - case ITEM_LIMITS_MAX: - if (GV_IS_GV_VALUE(ld->max, -GV_RANGELARGE, GV_RANGELARGE) || (attr && event == EVT_KEY_LONG(KEY_ENTER))) { - ld->max = GVAR_MENU_ITEM(LIMITS_MAX_POS, y, ld->max, -LIMIT_EXT_MAX, LIMIT_EXT_MAX, attr|PREC1|RIGHT, 0, event); - break; - } - lcdDrawNumber(LIMITS_MAX_POS, y, MIN_MAX_DISPLAY(ld->max+LIMITS_MIN_MAX_OFFSET), attr|PREC1|RIGHT); - if (active) ld->max = -LIMITS_MIN_MAX_OFFSET + checkIncDec(event, ld->max+LIMITS_MIN_MAX_OFFSET, 0, +limit, EE_MODEL, NULL, stops1000); - break; - - case ITEM_LIMITS_DIRECTION: - { - lcdDrawText(LIMITS_REVERT_POS, y, ld->revert ? "\177" : "\176", attr); - if (active) { - CHECK_INCDEC_MODELVAR_ZERO(event, ld->revert, 1); - } - break; - } - - case ITEM_LIMITS_CURVE: - drawCurveName(LIMITS_CURVE_POS, y, ld->curve, attr); - if (attr && event==EVT_KEY_LONG(KEY_ENTER) && ld->curve>0) { - s_curveChan = (ld->curve<0 ? -ld->curve-1 : ld->curve-1); - pushMenu(menuModelCurveOne); - } - if (active) { - CHECK_INCDEC_MODELVAR(event, ld->curve, -MAX_CURVES, +MAX_CURVES); - } - break; - -#if defined(PPM_CENTER_ADJUSTABLE) - case ITEM_LIMITS_PPM_CENTER: - lcdDrawNumber(LIMITS_PPM_CENTER_POS, y, PPM_CENTER+ld->ppmCenter, attr|RIGHT); - if (active) { - CHECK_INCDEC_MODELVAR(event, ld->ppmCenter, -PPM_CENTER_MAX, +PPM_CENTER_MAX); - } - break; -#endif - -#if defined(PPM_LIMITS_SYMETRICAL) - case ITEM_LIMITS_SYMETRICAL: - lcdDrawText(LIMITS_SYMETRICAL_POS, y, ld->symetrical ? "=" : "\306", attr); - if (active) { - CHECK_INCDEC_MODELVAR_ZERO(event, ld->symetrical, 1); - } - break; -#endif - } - } - } - - return true; -} diff --git a/radio/src/gui/480x272/model_receiver_options.cpp b/radio/src/gui/480x272/model_receiver_options.cpp deleted file mode 100644 index b1ab35036..000000000 --- a/radio/src/gui/480x272/model_receiver_options.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -#define RECEIVER_OPTIONS_2ND_COLUMN 200 - -extern uint8_t g_moduleIdx; - -enum { - RECEIVER_SETTINGS_OK = 0, - RECEIVER_SETTINGS_DIRTY = 1, - RECEIVER_SETTINGS_WRITING = 2, -}; - -void onRxOptionsUpdateConfirm(const char * result) -{ - if (result == STR_OK) { - reusableBuffer.hardwareAndSettings.receiverSettings.state = PXX2_SETTINGS_WRITE; - reusableBuffer.hardwareAndSettings.receiverSettings.dirty = RECEIVER_SETTINGS_WRITING; - reusableBuffer.hardwareAndSettings.receiverSettings.timeout = 0; - moduleState[g_moduleIdx].mode = MODULE_MODE_RECEIVER_SETTINGS; - } - else { - popMenu(); - } -} - -enum { - ITEM_RECEIVER_SETTINGS_PWM_RATE, - ITEM_RECEIVER_SETTINGS_TELEMETRY, - ITEM_RECEIVER_SETTINGS_SPORT_FPORT, - ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1, - ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED2, - ITEM_RECEIVER_SETTINGS_PINMAP_FIRST -}; - - -#define IF_RECEIVER_CAPABILITY(capability, count) uint8_t((reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilities & (1 << capability)) ? count : HIDDEN_ROW) - -bool menuModelReceiverOptions(event_t event) -{ - const int lim = (g_model.extendedLimits ? (512 * LIMIT_EXT_PERCENT / 100) : 512) * 2; - uint8_t wbar = LCD_W / 2 - 20; - auto outputsCount = min(16, reusableBuffer.hardwareAndSettings.receiverSettings.outputsCount); - - if (event == EVT_ENTRY) { - // reusableBuffer.hardwareSettings should have been cleared before calling this menu -#if defined(SIMU) - reusableBuffer.hardwareAndSettings.receiverSettings.state = PXX2_SETTINGS_OK; - reusableBuffer.hardwareAndSettings.receiverSettings.outputsCount = 16; -#endif - } - - uint8_t receiverId = reusableBuffer.hardwareAndSettings.receiverSettings.receiverId; - uint8_t receiverModelId = reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.modelID; - uint8_t receiverVariant = reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.variant; - - SUBMENU(STR_RECEIVER_OPTIONS, ICON_MODEL_SETUP, ITEM_RECEIVER_SETTINGS_PINMAP_FIRST + outputsCount, { - 0, // PWM rate - isModuleR9MAccess(g_moduleIdx) && receiverVariant == PXX2_VARIANT_EU && reusableBuffer.hardwareAndSettings.moduleSettings.txPower > 14 /*25mW*/ ? READONLY_ROW : (uint8_t)0, // Telemetry - IF_RECEIVER_CAPABILITY(RECEIVER_CAPABILITY_FPORT, 0), - uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), - uint8_t(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].receivers[receiverId].information.capabilityNotSupported ? READONLY_ROW : HIDDEN_ROW), - 0 // channels ... - }); - - if (g_model.moduleData[g_moduleIdx].pxx2.receiverName[receiverId][0] != '\0') - lcdDrawSizedText(50, 3 + FH, g_model.moduleData[g_moduleIdx].pxx2.receiverName[receiverId], effectiveLen(g_model.moduleData[g_moduleIdx].pxx2.receiverName[receiverId], PXX2_LEN_RX_NAME), MENU_TITLE_COLOR); - else - lcdDrawText(50, 3 + FH, "---", MENU_TITLE_COLOR); - - if (menuEvent) { - killEvents(KEY_EXIT); - moduleState[g_moduleIdx].mode = MODULE_MODE_NORMAL; - if (reusableBuffer.hardwareAndSettings.receiverSettings.dirty) { - abortPopMenu(); - POPUP_CONFIRMATION(STR_UPDATE_RX_OPTIONS, onRxOptionsUpdateConfirm); - } - else { - return false; - } - } - - if (reusableBuffer.hardwareAndSettings.receiverSettings.state == PXX2_HARDWARE_INFO && moduleState[g_moduleIdx].mode == MODULE_MODE_NORMAL) { - if (!receiverModelId) - moduleState[g_moduleIdx].readModuleInformation(&reusableBuffer.hardwareAndSettings.modules[g_moduleIdx], receiverId, receiverId); - else if (isModuleR9MAccess(g_moduleIdx) && receiverVariant == PXX2_VARIANT_EU && !reusableBuffer.hardwareAndSettings.moduleSettings.txPower) - moduleState[g_moduleIdx].readModuleSettings(&reusableBuffer.hardwareAndSettings.moduleSettings); - else - moduleState[g_moduleIdx].readReceiverSettings(&reusableBuffer.hardwareAndSettings.receiverSettings); - } - - if (event == EVT_KEY_LONG(KEY_ENTER) && reusableBuffer.hardwareAndSettings.receiverSettings.dirty) { - killEvents(event); - reusableBuffer.hardwareAndSettings.receiverSettings.dirty = RECEIVER_SETTINGS_OK; - moduleState[g_moduleIdx].writeReceiverSettings(&reusableBuffer.hardwareAndSettings.receiverSettings); - } - - if (reusableBuffer.hardwareAndSettings.receiverSettings.dirty == RECEIVER_SETTINGS_WRITING && reusableBuffer.hardwareAndSettings.receiverSettings.state == PXX2_SETTINGS_OK) { - popMenu(); - return false; - } - - if (receiverModelId && mstate_tab[menuVerticalPosition] == HIDDEN_ROW) { - menuVerticalPosition = 0; - while (menuVerticalPosition < ITEM_RECEIVER_SETTINGS_PINMAP_FIRST && mstate_tab[menuVerticalPosition] == HIDDEN_ROW) { - ++menuVerticalPosition; - } - } - - int8_t sub = menuVerticalPosition; - - if (reusableBuffer.hardwareAndSettings.receiverSettings.state == PXX2_SETTINGS_OK) { - for (uint8_t k=0; k0 ? BLINK|INVERS : INVERS) : 0); - - switch (i) { - case ITEM_RECEIVER_SETTINGS_PWM_RATE: - lcdDrawText(MENUS_MARGIN_LEFT, y, isModuleR9MAccess(g_moduleIdx) ? "6.67ms PWM": "9ms PWM"); - reusableBuffer.hardwareAndSettings.receiverSettings.pwmRate = editCheckBox(reusableBuffer.hardwareAndSettings.receiverSettings.pwmRate, RECEIVER_OPTIONS_2ND_COLUMN, y, attr, event); - if (attr && checkIncDec_Ret) { - reusableBuffer.hardwareAndSettings.receiverSettings.dirty = RECEIVER_SETTINGS_DIRTY; - } - break; - - case ITEM_RECEIVER_SETTINGS_TELEMETRY: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TELEMETRY_DISABLED); - reusableBuffer.hardwareAndSettings.receiverSettings.telemetryDisabled = editCheckBox(reusableBuffer.hardwareAndSettings.receiverSettings.telemetryDisabled, RECEIVER_OPTIONS_2ND_COLUMN, y, attr, event); - if (attr && checkIncDec_Ret) { - reusableBuffer.hardwareAndSettings.receiverSettings.dirty = RECEIVER_SETTINGS_DIRTY; - } - break; - - case ITEM_RECEIVER_SETTINGS_SPORT_FPORT: - lcdDrawText(MENUS_MARGIN_LEFT, y, "F.Port"); - reusableBuffer.hardwareAndSettings.receiverSettings.fport = editCheckBox(reusableBuffer.hardwareAndSettings.receiverSettings.fport, RECEIVER_OPTIONS_2ND_COLUMN, y, attr, event); - if (attr && checkIncDec_Ret) { - reusableBuffer.hardwareAndSettings.receiverSettings.dirty = RECEIVER_SETTINGS_DIRTY; - } - break; - - case ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED1: - lcdDrawText(LCD_W/2, y+1, STR_MORE_OPTIONS_AVAILABLE, SMLSIZE|CENTERED); - break; - - case ITEM_RECEIVER_SETTINGS_CAPABILITY_NOT_SUPPORTED2: - lcdDrawText(LCD_W/2, y+1, STR_OPENTX_UPGRADE_REQUIRED, SMLSIZE|CENTERED); - break; - - default: - // Pin - { - uint8_t pin = i - ITEM_RECEIVER_SETTINGS_PINMAP_FIRST; - if (pin < reusableBuffer.hardwareAndSettings.receiverSettings.outputsCount) { - uint8_t & mapping = reusableBuffer.hardwareAndSettings.receiverSettings.outputsMapping[pin]; - uint8_t channel = g_model.moduleData[g_moduleIdx].channelsStart + mapping; - int32_t channelValue = channelOutputs[channel]; - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PIN); - lcdDrawNumber(lcdNextPos + 1, y, pin + 1); - putsChn(100, y, channel + 1, attr); - - // Channel - if (attr) { - mapping = checkIncDec(event, mapping, 0, sentModuleChannels(g_moduleIdx) - 1); - if (checkIncDec_Ret) { - reusableBuffer.hardwareAndSettings.receiverSettings.dirty = RECEIVER_SETTINGS_DIRTY; - } - } - - // Bargraph - lcdDrawRect(RECEIVER_OPTIONS_2ND_COLUMN, y + 4, wbar + 1, 10); - const uint8_t lenChannel = limit(1, (abs(channelValue) * wbar / 2 + lim / 2) / lim, wbar / 2); - const coord_t xChannel = (channelValue > 0) ? RECEIVER_OPTIONS_2ND_COLUMN + wbar / 2 : RECEIVER_OPTIONS_2ND_COLUMN + wbar / 2 + 1 - lenChannel; - lcdDrawSolidFilledRect(xChannel, y + 5, lenChannel, 8, TEXT_INVERTED_BGCOLOR); - } - break; - } - } - } - } - else { - lcdDrawCenteredText(LCD_H/2, STR_WAITING_FOR_RX); - } - - return true; -} diff --git a/radio/src/gui/480x272/model_setup.cpp b/radio/src/gui/480x272/model_setup.cpp deleted file mode 100644 index ac6f03bd0..000000000 --- a/radio/src/gui/480x272/model_setup.cpp +++ /dev/null @@ -1,1670 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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 "storage/modelslist.h" - -uint8_t g_moduleIdx; - -enum MenuModelSetupItems { - ITEM_MODEL_SETUP_NAME, - ITEM_MODEL_SETUP_BITMAP, - ITEM_MODEL_SETUP_TIMER1, - ITEM_MODEL_SETUP_TIMER1_NAME, - ITEM_MODEL_SETUP_TIMER1_PERSISTENT, - ITEM_MODEL_SETUP_TIMER1_MINUTE_BEEP, - ITEM_MODEL_SETUP_TIMER1_COUNTDOWN_BEEP, -#if TIMERS > 1 - ITEM_MODEL_SETUP_TIMER2, - ITEM_MODEL_SETUP_TIMER2_NAME, - ITEM_MODEL_SETUP_TIMER2_PERSISTENT, - ITEM_MODEL_SETUP_TIMER2_MINUTE_BEEP, - ITEM_MODEL_SETUP_TIMER2_COUNTDOWN_BEEP, -#endif -#if TIMERS > 2 - ITEM_MODEL_SETUP_TIMER3, - ITEM_MODEL_SETUP_TIMER3_NAME, - ITEM_MODEL_SETUP_TIMER3_PERSISTENT, - ITEM_MODEL_SETUP_TIMER3_MINUTE_BEEP, - ITEM_MODEL_SETUP_TIMER3_COUNTDOWN_BEEP, -#endif - ITEM_MODEL_SETUP_EXTENDED_LIMITS, - ITEM_MODEL_SETUP_EXTENDED_TRIMS, - ITEM_MODEL_SETUP_DISPLAY_TRIMS, - ITEM_MODEL_SETUP_TRIM_INC, - ITEM_MODEL_SETUP_THROTTLE_LABEL, - ITEM_MODEL_SETUP_THROTTLE_REVERSED, - ITEM_MODEL_SETUP_THROTTLE_TRACE, - ITEM_MODEL_SETUP_THROTTLE_TRIM, - ITEM_MODEL_SETUP_PREFLIGHT_LABEL, - ITEM_MODEL_SETUP_CHECKLIST_DISPLAY, - ITEM_MODEL_SETUP_THROTTLE_WARNING, - ITEM_MODEL_SETUP_SWITCHES_WARNING, - ITEM_MODEL_SETUP_POTS_SLIDERS_WARNING_STATE, - ITEM_MODEL_SETUP_POTS_WARNING, - ITEM_MODEL_SETUP_SLIDERS_WARNING, - ITEM_MODEL_SETUP_BEEP_CENTER, - ITEM_MODEL_SETUP_USE_GLOBAL_FUNCTIONS, - -#if defined(PXX2) - ITEM_MODEL_SETUP_REGISTRATION_ID, -#endif - - ITEM_MODEL_SETUP_INTERNAL_MODULE_LABEL, - ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE, - ITEM_MODEL_SETUP_INTERNAL_MODULE_CHANNELS, - ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_BIND, - ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM, -#if defined(EXTERNAL_ANTENNA) - ITEM_MODEL_SETUP_INTERNAL_MODULE_ANTENNA, -#endif - ITEM_MODEL_SETUP_INTERNAL_MODULE_FAILSAFE, - ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_REGISTER_RANGE, - ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_OPTIONS, - ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_1, - ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_2, - ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_3, - - ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL, - ITEM_MODEL_SETUP_EXTERNAL_MODULE_TYPE, -#if defined(MULTIMODULE) - ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS, - ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS, -#endif - ITEM_MODEL_SETUP_EXTERNAL_MODULE_CHANNELS, - ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_BIND, - ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_MODEL_NUM, - ITEM_MODEL_SETUP_EXTERNAL_MODULE_OPTIONS, -#if defined(MULTIMODULE) - ITEM_MODEL_SETUP_EXTERNAL_MODULE_AUTOBIND, -#endif - ITEM_MODEL_SETUP_EXTERNAL_MODULE_POWER, - ITEM_MODEL_SETUP_EXTERNAL_MODULE_FAILSAFE, - ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_REGISTER_RANGE, - ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_OPTIONS, - ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_1, - ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_2, - ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_3, - - ITEM_MODEL_SETUP_TRAINER_LABEL, - ITEM_MODEL_SETUP_TRAINER_MODE, -#if defined(BLUETOOTH) - ITEM_MODEL_SETUP_TRAINER_BLUETOOTH, -#endif - ITEM_MODEL_SETUP_TRAINER_CHANNELS, - ITEM_MODEL_SETUP_TRAINER_PPM_PARAMS, - ITEM_MODEL_SETUP_MAX -}; - -#define MODEL_SETUP_2ND_COLUMN 200 -#define MODEL_SETUP_3RD_COLUMN 270 -#define MODEL_SETUP_4TH_COLUMN 350 -#define MODEL_SETUP_BIND_OFS 40 -#define MODEL_SETUP_RANGE_OFS 80 -#define MODEL_SETUP_SET_FAILSAFE_OFS 100 -#define MODEL_SETUP_SLIDPOT_SPACING 45 - -#define CURRENT_MODULE_EDITED(k) (k >= ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL ? EXTERNAL_MODULE : INTERNAL_MODULE) -#define CURRENT_RECEIVER_EDITED(k) (k - (k >= ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL ? ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_1 : ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_1)) - -#include "common/stdlcd/model_setup_pxx1.cpp" - -void onPXX2R9MBindModeMenu(const char * result) -{ - if (result == STR_16CH_WITH_TELEMETRY) { - reusableBuffer.moduleSetup.bindInformation.lbtMode = 1; - } - else if (result == STR_16CH_WITHOUT_TELEMETRY) { - reusableBuffer.moduleSetup.bindInformation.lbtMode = 2; - } - else if (result == STR_FLEX_915) { - reusableBuffer.moduleSetup.bindInformation.flexMode = 0; - } - else if (result == STR_FLEX_868) { - reusableBuffer.moduleSetup.bindInformation.flexMode = 1; - } - else { - // the user pressed [Exit] - uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition); - uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(menuVerticalPosition); - moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; - reusableBuffer.moduleSetup.bindInformation.step = 0; - removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx); - return; - } - -#if defined(SIMU) - uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition); - uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(menuVerticalPosition); - memcpy(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], reusableBuffer.moduleSetup.bindInformation.candidateReceiversNames[reusableBuffer.moduleSetup.bindInformation.selectedReceiverIndex], PXX2_LEN_RX_NAME); - storageDirty(EE_MODEL); - moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; - reusableBuffer.moduleSetup.bindInformation.step = BIND_OK; - POPUP_INFORMATION(STR_BIND_OK); -#else - reusableBuffer.moduleSetup.bindInformation.step = BIND_START; -#endif -} - -// TODO code duplicated - - -void onResetReceiverConfirm(const char * result) -{ - if (result == STR_OK) { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition - HEADER_LINE); - uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(menuVerticalPosition - HEADER_LINE); - moduleState[moduleIdx].mode = MODULE_MODE_RESET; - removePXX2Receiver(moduleIdx, receiverIdx); - } -} - -void onPXX2ReceiverMenu(const char * result) -{ - uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition - HEADER_LINE); - uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(menuVerticalPosition - HEADER_LINE); - - if (result == STR_OPTIONS) { - memclear(&reusableBuffer.hardwareAndSettings, sizeof(reusableBuffer.hardwareAndSettings)); - reusableBuffer.hardwareAndSettings.receiverSettings.receiverId = receiverIdx; - g_moduleIdx = moduleIdx; - pushMenu(menuModelReceiverOptions); - } - else if (result == STR_BIND) { - memclear(&reusableBuffer.moduleSetup.bindInformation, sizeof(BindInformation)); - reusableBuffer.moduleSetup.bindInformation.rxUid = receiverIdx; - if (isModuleR9MAccess(moduleIdx)) { - reusableBuffer.moduleSetup.bindInformation.step = BIND_MODULE_TX_INFORMATION_REQUEST; -#if defined(SIMU) - reusableBuffer.moduleSetup.pxx2.moduleInformation.information.modelID = 1; - reusableBuffer.moduleSetup.pxx2.moduleInformation.information.variant = 2; -#else - moduleState[moduleIdx].readModuleInformation(&reusableBuffer.moduleSetup.pxx2.moduleInformation, PXX2_HW_INFO_TX_ID, PXX2_HW_INFO_TX_ID); -#endif - } - else { - moduleState[moduleIdx].startBind(&reusableBuffer.moduleSetup.bindInformation); - } - s_editMode = 1; - } - else if (result == STR_SHARE) { - reusableBuffer.moduleSetup.pxx2.shareReceiverIndex = receiverIdx; - moduleState[moduleIdx].mode = MODULE_MODE_SHARE; - s_editMode = 1; - } - else if (result == STR_DELETE || result == STR_RESET) { - memclear(&reusableBuffer.moduleSetup.pxx2, sizeof(reusableBuffer.moduleSetup.pxx2)); - reusableBuffer.moduleSetup.pxx2.resetReceiverIndex = receiverIdx; - reusableBuffer.moduleSetup.pxx2.resetReceiverFlags = (result == STR_RESET ? 0xFF : 0x01); - POPUP_CONFIRMATION(result == STR_RESET ? STR_RECEIVER_RESET : STR_RECEIVER_DELETE, onResetReceiverConfirm); - } - else { - removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx); - } -} - -// TODO code duplicated -void onPXX2BindMenu(const char * result) -{ - if (result != STR_EXIT) { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition - HEADER_LINE); - reusableBuffer.moduleSetup.bindInformation.selectedReceiverIndex = (result - reusableBuffer.moduleSetup.bindInformation.candidateReceiversNames[0]) / sizeof(reusableBuffer.moduleSetup.bindInformation.candidateReceiversNames[0]); - if (isModuleR9MAccess(moduleIdx) && reusableBuffer.moduleSetup.pxx2.moduleInformation.information.variant == PXX2_VARIANT_EU) { - reusableBuffer.moduleSetup.bindInformation.step = BIND_RX_NAME_SELECTED; - POPUP_MENU_ADD_ITEM(STR_16CH_WITH_TELEMETRY); - POPUP_MENU_ADD_ITEM(STR_16CH_WITHOUT_TELEMETRY); - POPUP_MENU_START(onPXX2R9MBindModeMenu); - } - else if (isModuleR9MAccess(moduleIdx) && reusableBuffer.moduleSetup.pxx2.moduleInformation.information.variant == PXX2_VARIANT_FLEX) { - reusableBuffer.moduleSetup.bindInformation.step = BIND_RX_NAME_SELECTED; - POPUP_MENU_ADD_ITEM(STR_FLEX_868); - POPUP_MENU_ADD_ITEM(STR_FLEX_915); - POPUP_MENU_START(onPXX2R9MBindModeMenu); - } - else { -#if defined(SIMU) - uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(menuVerticalPosition - HEADER_LINE); - memcpy(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], result, PXX2_LEN_RX_NAME); - storageDirty(EE_MODEL); - moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; - reusableBuffer.moduleSetup.bindInformation.step = BIND_OK; - POPUP_INFORMATION(STR_BIND_OK); -#else - reusableBuffer.moduleSetup.bindInformation.step = BIND_START; -#endif - } - } - else { - // the user pressed [Exit] - uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition - HEADER_LINE); - uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(menuVerticalPosition - HEADER_LINE); - moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; - removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx); - s_editMode = 0; - } -} - -enum PopupRegisterItems { - ITEM_REGISTER_PASSWORD, - ITEM_REGISTER_MODULE_INDEX, - ITEM_REGISTER_RECEIVER_NAME, - ITEM_REGISTER_BUTTONS -}; - -constexpr coord_t REGISTER_TOP = POPUP_Y + 10; -constexpr coord_t REGISTER_H = 6 * FH - 4; -constexpr coord_t REGISTER_COLUMN_1 = POPUP_X + 10; -constexpr coord_t REGISTER_COLUMN_2 = POPUP_X + 150; - -void runPopupRegister(event_t event) -{ - uint8_t backupVerticalPosition = menuVerticalPosition; - uint8_t backupHorizontalPosition = menuHorizontalPosition; - uint8_t backupVerticalOffset = menuVerticalOffset; - int8_t backupEditMode = s_editMode; - - menuVerticalPosition = reusableBuffer.moduleSetup.pxx2.registerPopupVerticalPosition; - menuHorizontalPosition = reusableBuffer.moduleSetup.pxx2.registerPopupHorizontalPosition; - s_editMode = reusableBuffer.moduleSetup.pxx2.registerPopupEditMode; - - switch (event) { - case EVT_KEY_BREAK(KEY_ENTER): - if (menuVerticalPosition != ITEM_REGISTER_BUTTONS) { - break; - } - else if (reusableBuffer.moduleSetup.pxx2.registerStep >= REGISTER_RX_NAME_RECEIVED && menuHorizontalPosition == 0) { - // [Enter] pressed - reusableBuffer.moduleSetup.pxx2.registerStep = REGISTER_RX_NAME_SELECTED; - backupEditMode = EDIT_MODIFY_FIELD; // so that the [Register] button blinks and the REGISTER process can continue - } - // no break - - case EVT_KEY_LONG(KEY_EXIT): - s_editMode = 0; - // no break; - - case EVT_KEY_BREAK(KEY_EXIT): - if (s_editMode <= 0) { - warningText = nullptr; - } - break; - } - - if (warningText) { - const uint8_t dialogRows[] = { 0, 0, uint8_t(reusableBuffer.moduleSetup.pxx2.registerStep < REGISTER_RX_NAME_RECEIVED ? READONLY_ROW : 0), uint8_t(reusableBuffer.moduleSetup.pxx2.registerStep < REGISTER_RX_NAME_RECEIVED ? 0 : 1)}; - check(event, 0, nullptr, 0, dialogRows, 3, 4); // TODO add a comment for 3 - HEADER_LINE once understood - - drawPopupBackgroundAndBorder(POPUP_X, REGISTER_TOP, POPUP_W, REGISTER_H); - // showMessageBox(warningText); - - // registration password - lcdDrawText(REGISTER_COLUMN_1, REGISTER_TOP + 8, STR_REG_ID); - editName(REGISTER_COLUMN_2, REGISTER_TOP + 8, g_model.modelRegistrationID, PXX2_LEN_REGISTRATION_ID, event, menuVerticalPosition == ITEM_REGISTER_PASSWORD); - - // loop index (will be removed in future) - lcdDrawText(REGISTER_COLUMN_1, REGISTER_TOP + 8 + FH, "UID"); - lcdDrawNumber(REGISTER_COLUMN_2, REGISTER_TOP + 8 + FH, reusableBuffer.moduleSetup.pxx2.registerLoopIndex, menuVerticalPosition == ITEM_REGISTER_MODULE_INDEX ? (s_editMode ? INVERS + BLINK : INVERS) : 0); - if (menuVerticalPosition == ITEM_REGISTER_MODULE_INDEX && s_editMode) { - CHECK_INCDEC_MODELVAR_ZERO(event, reusableBuffer.moduleSetup.pxx2.registerLoopIndex, 2); - } - - // RX name - if (reusableBuffer.moduleSetup.pxx2.registerStep < REGISTER_RX_NAME_RECEIVED) { - lcdDrawText(REGISTER_COLUMN_1, REGISTER_TOP + 8 + 2 * FH, STR_WAITING); - lcdDrawText(REGISTER_COLUMN_1, REGISTER_TOP + 8 + 4 * FH, TR_EXIT, menuVerticalPosition == ITEM_REGISTER_BUTTONS ? INVERS : 0); - } - else { - lcdDrawText(REGISTER_COLUMN_1, REGISTER_TOP + 8 + 2 * FH, STR_RX_NAME); - editName(REGISTER_COLUMN_2, REGISTER_TOP + 8 + 2 * FH, reusableBuffer.moduleSetup.pxx2.registerRxName, PXX2_LEN_RX_NAME, event, menuVerticalPosition == ITEM_REGISTER_RECEIVER_NAME); - lcdDrawText(REGISTER_COLUMN_1, REGISTER_TOP + 8 + 4 * FH, TR_ENTER, menuVerticalPosition == ITEM_REGISTER_BUTTONS && menuHorizontalPosition == 0 ? INVERS : 0); - lcdDrawText(REGISTER_COLUMN_2, REGISTER_TOP + 8 + 4 * FH, TR_EXIT, menuVerticalPosition == ITEM_REGISTER_BUTTONS && menuHorizontalPosition == 1 ? INVERS : 0); - } - - reusableBuffer.moduleSetup.pxx2.registerPopupVerticalPosition = menuVerticalPosition; - reusableBuffer.moduleSetup.pxx2.registerPopupHorizontalPosition = menuHorizontalPosition; - reusableBuffer.moduleSetup.pxx2.registerPopupEditMode = s_editMode; - } - - menuVerticalPosition = backupVerticalPosition; - menuHorizontalPosition = backupHorizontalPosition; - menuVerticalOffset = backupVerticalOffset; - s_editMode = backupEditMode; -} - -void startRegisterDialog(uint8_t module) -{ - memclear(&reusableBuffer.moduleSetup.pxx2, sizeof(reusableBuffer.moduleSetup.pxx2)); - moduleState[module].mode = MODULE_MODE_REGISTER; - s_editMode = 0; - POPUP_INPUT("", runPopupRegister); -} - -void checkModelIdUnique(uint8_t moduleIdx) -{ - if (isModuleXJTD8(moduleIdx)) - return; - - char * warn_buf = reusableBuffer.moduleSetup.msg; - - // cannot rely exactly on WARNING_LINE_LEN so using WARNING_LINE_LEN-2 - size_t warn_buf_len = sizeof(reusableBuffer.moduleSetup.msg) - WARNING_LINE_LEN - 2; - if (!modelslist.isModelIdUnique(moduleIdx,warn_buf,warn_buf_len)) { - if (warn_buf[0] != 0) { - POPUP_WARNING(STR_MODELIDUSED); - SET_WARNING_INFO(warn_buf, sizeof(reusableBuffer.moduleSetup.msg), 0); - } - } -} - -void onModelSetupBitmapMenu(const char * result) -{ - if (result == STR_UPDATE_LIST) { - if (!sdListFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(g_model.header.bitmap)-LEN_BITMAPS_EXT, NULL)) { - POPUP_WARNING(STR_NO_BITMAPS_ON_SD); - } - } - else if (result != STR_EXIT) { - // The user choosed a bmp file in the list - copySelection(g_model.header.bitmap, result, sizeof(g_model.header.bitmap)); - storageDirty(EE_MODEL); - if (modelslist.getCurrentModel()) - modelslist.getCurrentModel()->resetBuffer(); - } -} - -void editTimerMode(int timerIdx, coord_t y, LcdFlags attr, event_t event) -{ - TimerData & timer = g_model.timers[timerIdx]; - if (attr && menuHorizontalPosition < 0) { - lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN-INVERT_HORZ_MARGIN, y-INVERT_VERT_MARGIN+1, 115+2*INVERT_HORZ_MARGIN, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR); - } - drawStringWithIndex(MENUS_MARGIN_LEFT, y, STR_TIMER, timerIdx+1); - drawTimerMode(MODEL_SETUP_2ND_COLUMN, y, timer.mode, (menuHorizontalPosition<=0 ? attr : 0)); - drawTimer(MODEL_SETUP_2ND_COLUMN+50, y, timer.start, (menuHorizontalPosition!=0 ? attr|TIMEHOUR : TIMEHOUR)); - if (attr && s_editMode>0) { - switch (menuHorizontalPosition) { - case 0: - { - int32_t timerMode = timer.mode; - if (timerMode < 0) timerMode -= TMRMODE_COUNT-1; - CHECK_INCDEC_MODELVAR_CHECK(event, timerMode, -TMRMODE_COUNT-SWSRC_LAST+1, TMRMODE_COUNT+SWSRC_LAST-1, isSwitchAvailableInTimers); - if (timerMode < 0) timerMode += TMRMODE_COUNT-1; - timer.mode = timerMode; -#if defined(AUTOSWITCH) - if (s_editMode>0) { - int8_t val = timer.mode - (TMRMODE_COUNT-1); - int8_t switchVal = checkIncDecMovedSwitch(val); - if (val != switchVal) { - timer.mode = switchVal + (TMRMODE_COUNT-1); - storageDirty(EE_MODEL); - } - } -#endif - break; - } - case 1: - { - const int stopsMinutes[] = { 8, 60, 120, 180, 240, 300, 600, 900, 1200 }; - timer.start = checkIncDec(event, timer.start, 0, TIMER_MAX, EE_MODEL, NULL, (const CheckIncDecStops&)stopsMinutes); - break; - } - } - } -} - -void editTimerCountdown(int timerIdx, coord_t y, LcdFlags attr, event_t event) -{ - TimerData & timer = g_model.timers[timerIdx]; - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BEEPCOUNTDOWN); - lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VBEEPCOUNTDOWN, timer.countdownBeep, (menuHorizontalPosition == 0 ? attr : 0)); - if (timer.countdownBeep != COUNTDOWN_SILENT) { - lcdDrawNumber(MODEL_SETUP_3RD_COLUMN, y, TIMER_COUNTDOWN_START(timerIdx), (menuHorizontalPosition == 1 ? attr : 0) | LEFT, 0, NULL, "s"); - } - if (attr && s_editMode > 0) { - switch (menuHorizontalPosition) { - case 0: - CHECK_INCDEC_MODELVAR(event, timer.countdownBeep, COUNTDOWN_SILENT, COUNTDOWN_COUNT - 1); - break; - case 1: - timer.countdownStart = -checkIncDecModel(event, -timer.countdownStart, -1, +2); - break; - } - } -} - -int getSwitchWarningsCount() -{ - int count = 0; - for (int i=0; i= 0); - - int8_t old_editMode = s_editMode; - MENU(STR_MENUSETUP, MODEL_ICONS, menuTabModel, MENU_MODEL_SETUP, ITEM_MODEL_SETUP_MAX, - { 0, // Model name - 0, // Model bitmap - TIMERS_ROWS, - 0, // Extended limits - 1, // Extended trims - 0, // Show trims - 0, // Trims step - - LABEL(Throttle), - 0, // Throttle reverse - 0, // Throttle trace source - 0, // Throttle trim - - LABEL(PreflightCheck), - 0, // Display checklist - 0, // Throttle state - uint8_t(NAVIGATION_LINE_BY_LINE|(getSwitchWarningsCount()-1)), // Switches warnings - 0, // Pots & Sliders warning mode - g_model.potsWarnMode ? uint8_t(NAVIGATION_LINE_BY_LINE|(NUM_POTS-1)) : HIDDEN_ROW, // Pots positions - g_model.potsWarnMode ? uint8_t(NAVIGATION_LINE_BY_LINE|(NUM_SLIDERS-1)) : HIDDEN_ROW, // Sliders positions - - NAVIGATION_LINE_BY_LINE|(NUM_STICKS+NUM_POTS+NUM_SLIDERS-1), // Center beeps - 0, // Global functions - - REGISTRATION_ID_ROWS - - LABEL(InternalModule), - INTERNAL_MODULE_TYPE_ROWS, - INTERNAL_MODULE_CHANNELS_ROWS, - IF_NOT_ACCESS_MODULE_RF(INTERNAL_MODULE, IF_INTERNAL_MODULE_ON(IF_INTERNAL_MODULE_ON(isModuleRxNumAvailable(INTERNAL_MODULE) ? (uint8_t)2 : (uint8_t)1))), - IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), // RxNum - ANTENNA_ROW - IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE)), - IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 1), /* Range check and Register buttons */ \ - IF_PXX2_MODULE(INTERNAL_MODULE, 0), /* Module options */ \ - IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* Receiver 1 */ \ - IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* Receiver 2 */ \ - IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* Receiver 3 */ - - LABEL(ExternalModule), - EXTERNAL_MODULE_TYPE_ROW(), - MULTIMODULE_STATUS_ROWS - EXTERNAL_MODULE_CHANNELS_ROWS, - IF_NOT_ACCESS_MODULE_RF(EXTERNAL_MODULE, EXTERNAL_MODULE_BIND_ROWS), - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // RxNum for ACCESS - IF_NOT_PXX2_MODULE(EXTERNAL_MODULE, EXTERNAL_MODULE_OPTION_ROW), - MULTIMODULE_MODULE_ROWS - EXTERNAL_MODULE_POWER_ROW, - FAILSAFE_ROWS(EXTERNAL_MODULE), - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 1), // Range check and Register buttons - IF_PXX2_MODULE(EXTERNAL_MODULE, 0), // Module options - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 1 - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 2 - IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 3 - - TRAINER_ROWS - }); - - if (event == EVT_ENTRY) { - reusableBuffer.moduleSetup.r9mPower = g_model.moduleData[EXTERNAL_MODULE].pxx.power; - } - - if (menuEvent) { - moduleState[0].mode = 0; - moduleState[1].mode = 0; - } - - int sub = menuVerticalPosition; - - for (uint8_t i=0; i0) ? BLINK|INVERS : INVERS); - LcdFlags attr = (sub == k ? blink : 0); - - switch(k) { - case ITEM_MODEL_SETUP_NAME: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODELNAME); - editName(MODEL_SETUP_2ND_COLUMN, y, g_model.header.name, sizeof(g_model.header.name), event, attr); - break; - - case ITEM_MODEL_SETUP_BITMAP: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BITMAP); - if (ZEXIST(g_model.header.bitmap)) - lcdDrawSizedText(MODEL_SETUP_2ND_COLUMN, y, g_model.header.bitmap, sizeof(g_model.header.bitmap), attr); - else - lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VCSWFUNC, 0, attr); - if (attr && event==EVT_KEY_BREAK(KEY_ENTER) && READ_ONLY_UNLOCKED()) { - s_editMode = 0; - if (sdListFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(g_model.header.bitmap)-LEN_BITMAPS_EXT, g_model.header.bitmap, LIST_NONE_SD_FILE | LIST_SD_FILE_EXT)) { - POPUP_MENU_START(onModelSetupBitmapMenu); - } - else { - POPUP_WARNING(STR_NO_BITMAPS_ON_SD); - } - } - break; - - case ITEM_MODEL_SETUP_TIMER1: - editTimerMode(0, y, attr, event); - break; - - case ITEM_MODEL_SETUP_TIMER1_NAME: - lcdDrawText(MENUS_MARGIN_LEFT, y, INDENT TR_NAME); - editName(MODEL_SETUP_2ND_COLUMN, y, g_model.timers[0].name, LEN_TIMER_NAME, event, attr); - break; - - case ITEM_MODEL_SETUP_TIMER1_MINUTE_BEEP: - lcdDrawText(MENUS_MARGIN_LEFT, y, INDENT TR_MINUTEBEEP); - g_model.timers[0].minuteBeep = editCheckBox(g_model.timers[0].minuteBeep, MODEL_SETUP_2ND_COLUMN, y, attr, event); - break; - - case ITEM_MODEL_SETUP_TIMER1_COUNTDOWN_BEEP: - editTimerCountdown(0, y, attr, event); - break; - - case ITEM_MODEL_SETUP_TIMER1_PERSISTENT: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PERSISTENT); - g_model.timers[0].persistent = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_VPERSISTENT, g_model.timers[0].persistent, 0, 2, attr, event); - break; - -#if TIMERS > 1 - case ITEM_MODEL_SETUP_TIMER2: - editTimerMode(1, y, attr, event); - break; - - case ITEM_MODEL_SETUP_TIMER2_NAME: - lcdDrawText(MENUS_MARGIN_LEFT, y, INDENT TR_NAME); - editName(MODEL_SETUP_2ND_COLUMN, y, g_model.timers[1].name, LEN_TIMER_NAME, event, attr); - break; - - case ITEM_MODEL_SETUP_TIMER2_MINUTE_BEEP: - lcdDrawText(MENUS_MARGIN_LEFT, y, INDENT TR_MINUTEBEEP); - g_model.timers[1].minuteBeep = editCheckBox(g_model.timers[1].minuteBeep, MODEL_SETUP_2ND_COLUMN, y, attr, event); - break; - - case ITEM_MODEL_SETUP_TIMER2_COUNTDOWN_BEEP: - editTimerCountdown(1, y, attr, event); - break; - - case ITEM_MODEL_SETUP_TIMER2_PERSISTENT: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PERSISTENT); - g_model.timers[1].persistent = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_VPERSISTENT, g_model.timers[1].persistent, 0, 2, attr, event); - break; -#endif - -#if TIMERS > 2 - case ITEM_MODEL_SETUP_TIMER3: - editTimerMode(2, y, attr, event); - break; - - case ITEM_MODEL_SETUP_TIMER3_NAME: - lcdDrawText(MENUS_MARGIN_LEFT, y, INDENT TR_NAME); - editName(MODEL_SETUP_2ND_COLUMN, y, g_model.timers[2].name, LEN_TIMER_NAME, event, attr); - break; - - case ITEM_MODEL_SETUP_TIMER3_MINUTE_BEEP: - lcdDrawText(MENUS_MARGIN_LEFT, y, INDENT TR_MINUTEBEEP); - g_model.timers[2].minuteBeep = editCheckBox(g_model.timers[2].minuteBeep, MODEL_SETUP_2ND_COLUMN, y, attr, event); - break; - - case ITEM_MODEL_SETUP_TIMER3_COUNTDOWN_BEEP: - editTimerCountdown(2, y, attr, event); - break; - - case ITEM_MODEL_SETUP_TIMER3_PERSISTENT: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PERSISTENT); - g_model.timers[2].persistent = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_VPERSISTENT, g_model.timers[2].persistent, 0, 2, attr, event); - break; -#endif - - case ITEM_MODEL_SETUP_EXTENDED_LIMITS: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ELIMITS); - g_model.extendedLimits = editCheckBox(g_model.extendedLimits, MODEL_SETUP_2ND_COLUMN, y, attr, event); - break; - - case ITEM_MODEL_SETUP_EXTENDED_TRIMS: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ETRIMS); - g_model.extendedTrims = editCheckBox(g_model.extendedTrims, MODEL_SETUP_2ND_COLUMN, y, menuHorizontalPosition<=0 ? attr : 0, event==EVT_KEY_BREAK(KEY_ENTER) ? event : 0); - lcdDrawText(MODEL_SETUP_2ND_COLUMN+18, y, STR_RESET_BTN, menuHorizontalPosition>0 && !NO_HIGHLIGHT() ? attr : 0); - if (attr && menuHorizontalPosition>0) { - s_editMode = 0; - if (event==EVT_KEY_LONG(KEY_ENTER)) { - START_NO_HIGHLIGHT(); - for (uint8_t i=0; i MIXSRC_Thr) - idx += 1; - if (idx >= MIXSRC_FIRST_POT+NUM_POTS+NUM_SLIDERS) - idx += MIXSRC_CH1 - MIXSRC_FIRST_POT - NUM_POTS - NUM_SLIDERS; - drawSource(MODEL_SETUP_2ND_COLUMN, y, idx, attr); - break; - } - - case ITEM_MODEL_SETUP_THROTTLE_TRIM: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TTRIM); - g_model.thrTrim = editCheckBox(g_model.thrTrim, MODEL_SETUP_2ND_COLUMN, y, attr, event); - break; - - case ITEM_MODEL_SETUP_PREFLIGHT_LABEL: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PREFLIGHT); - break; - - case ITEM_MODEL_SETUP_CHECKLIST_DISPLAY: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CHECKLIST); - g_model.displayChecklist = editCheckBox(g_model.displayChecklist, MODEL_SETUP_2ND_COLUMN, y, attr, event); - break; - - case ITEM_MODEL_SETUP_THROTTLE_WARNING: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_THROTTLEWARNING); - g_model.disableThrottleWarning = !editCheckBox(!g_model.disableThrottleWarning, MODEL_SETUP_2ND_COLUMN, y, attr, event); - break; - - case ITEM_MODEL_SETUP_SWITCHES_WARNING: - { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SWITCHWARNING); - if (!READ_ONLY() && attr && menuHorizontalPosition<0 && event==EVT_KEY_LONG(KEY_ENTER)) { - killEvents(event); - START_NO_HIGHLIGHT(); - getMovedSwitch(); - for (int i=0; i> (3*i)) & 0x07); - if (enabled) { - g_model.switchWarningState &= ~(0x07 << (3*i)); - unsigned int newState = (switches_states >> (2*i) & 0x03) + 1; - g_model.switchWarningState |= (newState << (3*i)); - } - } - AUDIO_WARNING1(); - storageDirty(EE_MODEL); - } - - if (attr && menuHorizontalPosition < 0) { - lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN-INVERT_HORZ_MARGIN, y-INVERT_VERT_MARGIN+1, (NUM_SWITCHES-1)*25+INVERT_HORZ_MARGIN, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR); - } - - unsigned int newStates = 0; - for (int i=0, current=0; i> (3*i)) & 0x07); - LcdFlags color = (state > 0 ? TEXT_COLOR : TEXT_DISABLE_COLOR); - if (attr && menuHorizontalPosition < 0) { - color |= INVERS; - } - char s[3]; - s[0] = 'A' + i; - s[1] = "x\300-\301"[state]; - s[2] = '\0'; - lcdDrawText(MODEL_SETUP_2ND_COLUMN+i*25, y, s, color|(menuHorizontalPosition==current ? attr : 0)); - if (!READ_ONLY() && attr && menuHorizontalPosition==current) { - CHECK_INCDEC_MODELVAR_ZERO_CHECK(event, state, 3, IS_CONFIG_3POS(i) ? NULL : isSwitch2POSWarningStateAvailable); - } - newStates |= (state << (3*i)); - ++current; - } - } - g_model.switchWarningState = newStates; - break; - } - - case ITEM_MODEL_SETUP_POTS_SLIDERS_WARNING_STATE: - lcdDrawText(MENUS_MARGIN_LEFT, y,STR_POTWARNINGSTATE); - lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, "\004""OFF\0""Man\0""Auto", g_model.potsWarnMode, attr); - if (attr) { - CHECK_INCDEC_MODELVAR(event, g_model.potsWarnMode, POTS_WARN_OFF, POTS_WARN_AUTO); - storageDirty(EE_MODEL); - } - break; - - case ITEM_MODEL_SETUP_POTS_WARNING: - { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_POTWARNING); - if (attr) { - if (!READ_ONLY() && menuHorizontalPosition >= 0 && event==EVT_KEY_LONG(KEY_ENTER)) { - killEvents(event); - if (g_model.potsWarnMode == POTS_WARN_MANUAL) { - SAVE_POT_POSITION(menuHorizontalPosition); - AUDIO_WARNING1(); - storageDirty(EE_MODEL); - } - } - - if (!READ_ONLY() && menuHorizontalPosition >= 0 && s_editMode && event==EVT_KEY_BREAK(KEY_ENTER)) { - s_editMode = 0; - g_model.potsWarnEnabled ^= (1 << (menuHorizontalPosition)); - storageDirty(EE_MODEL); - } - } - - if (attr && menuHorizontalPosition < 0) { - lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN-INVERT_HORZ_MARGIN, y-INVERT_VERT_MARGIN+1, NUM_POTS*MODEL_SETUP_SLIDPOT_SPACING+INVERT_HORZ_MARGIN, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR); - } - - if (g_model.potsWarnMode) { - coord_t x = MODEL_SETUP_2ND_COLUMN; - for (int i=0; isubTypeString != nullptr) - lcdDrawTextAtIndex(MODEL_SETUP_4TH_COLUMN, y, pdef->subTypeString, g_model.moduleData[EXTERNAL_MODULE].subType, menuHorizontalPosition==2 ? attr : 0); - } - } -#endif - if (attr) { - if (s_editMode > 0) { - switch (menuHorizontalPosition) { - case 0: - { - uint8_t moduleType = checkIncDec(event, g_model.moduleData[EXTERNAL_MODULE].type, MODULE_TYPE_NONE, MODULE_TYPE_MAX, EE_MODEL, isExternalModuleAvailable); - if (checkIncDec_Ret) { - setModuleType(EXTERNAL_MODULE, moduleType); - } - break; - } - - case 1: - if (isModuleDSM2(EXTERNAL_MODULE)) - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, DSM2_PROTO_LP45, DSM2_PROTO_DSMX); -#if defined(MULTIMODULE) - else if (isModuleMultimodule(EXTERNAL_MODULE)) { - int multiRfProto = g_model.moduleData[EXTERNAL_MODULE].multi.customProto == 1 ? MODULE_SUBTYPE_MULTI_CUSTOM : g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false); - CHECK_INCDEC_MODELVAR(event, multiRfProto, MODULE_SUBTYPE_MULTI_FIRST, MODULE_SUBTYPE_MULTI_LAST); - if (checkIncDec_Ret) { - g_model.moduleData[EXTERNAL_MODULE].multi.customProto = (multiRfProto == MODULE_SUBTYPE_MULTI_CUSTOM); - if (!g_model.moduleData[EXTERNAL_MODULE].multi.customProto) - g_model.moduleData[EXTERNAL_MODULE].setMultiProtocol(multiRfProto); - g_model.moduleData[EXTERNAL_MODULE].subType = 0; - // Sensible default for DSM2 (same as for ppm): 7ch@22ms + Autodetect settings enabled - if (g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(true) == MODULE_SUBTYPE_MULTI_DSM2) { - g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode = 1; - } - else { - g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode = 0; - } - g_model.moduleData[EXTERNAL_MODULE].multi.optionValue = 0; - } - } -#endif - else if (isModuleR9MNonAccess(EXTERNAL_MODULE)) { - g_model.moduleData[EXTERNAL_MODULE].subType = checkIncDec(event, g_model.moduleData[EXTERNAL_MODULE].subType, MODULE_SUBTYPE_R9M_FCC, - MODULE_SUBTYPE_R9M_LAST, EE_MODEL, isR9MModeAvailable); - } - else { - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].subType, MODULE_SUBTYPE_PXX1_ACCST_D16, MODULE_SUBTYPE_PXX1_LAST); - } - if (checkIncDec_Ret) { - g_model.moduleData[EXTERNAL_MODULE].channelsStart = 0; - g_model.moduleData[EXTERNAL_MODULE].channelsCount = defaultModuleChannels_M8(EXTERNAL_MODULE); - } - break; - -#if defined(MULTIMODULE) - case 2: - if (g_model.moduleData[EXTERNAL_MODULE].multi.customProto) { - g_model.moduleData[EXTERNAL_MODULE].setMultiProtocol(checkIncDec(event, g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false), 0, 63, EE_MODEL)); - break; - } else { - const mm_protocol_definition *pdef = getMultiProtocolDefinition(g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false)); - if (pdef->maxSubtype > 0) - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].subType, 0, pdef->maxSubtype); - } - break; - case 3: - // Custom protocol, third column is subtype - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].subType, 0, 7); - break; -#endif - } - } -#if POPUP_LEVEL > 1 - else if (old_editMode > 0) { - if (isModuleR9MNonAccess(EXTERNAL_MODULE)) { - if (g_model.moduleData[EXTERNAL_MODULE].subType > MODULE_SUBTYPE_R9M_EU) { - POPUP_WARNING(STR_R9M_PROTO_FLEX_WARN_LINE1); - SET_WARNING_INFO(STR_R9M_PROTO_WARN_LINE2, sizeof(TR_R9M_PROTO_WARN_LINE2) - 1, 0); - } -#if POPUP_LEVEL >= 3 - else if (g_model.moduleData[EXTERNAL_MODULE].subType == MODULE_SUBTYPE_R9M_EU) { - POPUP_WARNING(STR_R9M_PROTO_EU_WARN_LINE1); - SET_WARNING_INFO(STR_R9M_PROTO_WARN_LINE2, sizeof(TR_R9M_PROTO_WARN_LINE2) - 1, 0); - } - else { - POPUP_WARNING(STR_R9M_PROTO_FCC_WARN_LINE1); - SET_WARNING_INFO(STR_R9M_PROTO_WARN_LINE2, sizeof(TR_R9M_PROTO_WARN_LINE2) - 1, 0); - } -#endif - } - } -#endif - } - break; - - case ITEM_MODEL_SETUP_INTERNAL_MODULE_CHANNELS: - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_CHANNELS: - { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); - ModuleData & moduleData = g_model.moduleData[moduleIdx]; - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CHANNELRANGE); - if ((int8_t)MODULE_CHANNELS_ROWS(moduleIdx) >= 0) { - drawStringWithIndex(MODEL_SETUP_2ND_COLUMN, y, STR_CH, moduleData.channelsStart+1, menuHorizontalPosition==0 ? attr : 0); - lcdDrawText(lcdNextPos+5, y, "-"); - drawStringWithIndex(lcdNextPos+5, y, STR_CH, moduleData.channelsStart+sentModuleChannels(moduleIdx), menuHorizontalPosition==1 ? attr : 0); - const char * delay = getModuleDelay(moduleIdx); - if (delay) - lcdDrawText(lcdNextPos + 15, y, delay); - if (attr && s_editMode>0) { - switch (menuHorizontalPosition) { - case 0: - CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.channelsStart, 32-8-moduleData.channelsCount); - break; - case 1: - CHECK_INCDEC_MODELVAR_CHECK(event, moduleData.channelsCount, -4, min(maxModuleChannels_M8(moduleIdx), 32-8-moduleData.channelsStart), moduleData.type == MODULE_TYPE_ISRM_PXX2 ? isPxx2IsrmChannelsCountAllowed : nullptr); - if (checkIncDec_Ret && moduleData.type == MODULE_TYPE_PPM) { - setDefaultPpmFrameLength(moduleIdx); - } - break; - } - } - } - break; - } - - case ITEM_MODEL_SETUP_TRAINER_LABEL: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TRAINER); - break; - - case ITEM_MODEL_SETUP_TRAINER_MODE: - lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_MODE); - g_model.trainerData.mode = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_VTRAINERMODES, g_model.trainerData.mode, 0, TRAINER_MODE_MAX(), attr, event); - if (attr && checkIncDec_Ret) { - bluetooth.state = BLUETOOTH_STATE_OFF; - bluetooth.distantAddr[0] = 0; - } - break; - - case ITEM_MODEL_SETUP_TRAINER_BLUETOOTH: - if (g_model.trainerData.mode == TRAINER_MODE_MASTER_BLUETOOTH) { - if (attr) { - s_editMode = 0; - } - if (bluetooth.distantAddr[0]) { - lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, bluetooth.distantAddr); - if (bluetooth.state != BLUETOOTH_STATE_CONNECTED) { - drawButton(MODEL_SETUP_2ND_COLUMN, y, "Bind", menuHorizontalPosition == 0 ? attr : 0); - drawButton(MODEL_SETUP_2ND_COLUMN+60, y, "Clear", menuHorizontalPosition == 1 ? attr : 0); - } - else { - drawButton(MODEL_SETUP_2ND_COLUMN, y, "Clear", attr); - } - if (attr && event == EVT_KEY_FIRST(KEY_ENTER)) { - if (bluetooth.state == BLUETOOTH_STATE_CONNECTED || menuHorizontalPosition == 1) { - bluetooth.state = BLUETOOTH_STATE_OFF; - bluetooth.distantAddr[0] = 0; - } - else { - bluetooth.state = BLUETOOTH_STATE_BIND_REQUESTED; - } - } - } - else { - lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, "---"); - if (bluetooth.state < BLUETOOTH_STATE_IDLE) - drawButton(MODEL_SETUP_2ND_COLUMN, y, STR_BLUETOOTH_INIT, attr); - else - drawButton(MODEL_SETUP_2ND_COLUMN, y, STR_BLUETOOTH_DISC, attr); - if (attr && event == EVT_KEY_FIRST(KEY_ENTER)) { - if (bluetooth.state < BLUETOOTH_STATE_IDLE) - bluetooth.state = BLUETOOTH_STATE_OFF; - else - bluetooth.state = BLUETOOTH_STATE_DISCOVER_REQUESTED; - } - } - } - break; - - case ITEM_MODEL_SETUP_TRAINER_CHANNELS: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CHANNELRANGE); - drawStringWithIndex(MODEL_SETUP_2ND_COLUMN, y, STR_CH, g_model.trainerData.channelsStart+1, menuHorizontalPosition==0 ? attr : 0); - lcdDrawText(lcdNextPos+5, y, "-"); - drawStringWithIndex(lcdNextPos+5, y, STR_CH, g_model.trainerData.channelsStart + 8 + g_model.trainerData.channelsCount, menuHorizontalPosition==1 ? attr : 0); - if (attr && s_editMode > 0) { - switch (menuHorizontalPosition) { - case 0: - CHECK_INCDEC_MODELVAR_ZERO(event, g_model.trainerData.channelsStart, 32-8-g_model.trainerData.channelsCount); - break; - case 1: - CHECK_INCDEC_MODELVAR(event, g_model.trainerData.channelsCount, -4, min(MAX_TRAINER_CHANNELS_M8, 32-8-g_model.trainerData.channelsStart)); - break; - } - } - break; - - case ITEM_MODEL_SETUP_TRAINER_PPM_PARAMS: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PPMFRAME); - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)g_model.trainerData.frameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1|LEFT, 0, NULL, STR_MS); - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN+80, y, (g_model.trainerData.delay*50)+300, (CURSOR_ON_LINE() || menuHorizontalPosition==1) ? attr|LEFT : LEFT, 0, NULL, "us"); - lcdDrawText(MODEL_SETUP_2ND_COLUMN+160, y, g_model.trainerData.pulsePol ? "+" : "-", (CURSOR_ON_LINE() || menuHorizontalPosition==2) ? attr : 0); - if (attr && s_editMode>0) { - switch (menuHorizontalPosition) { - case 0: - CHECK_INCDEC_MODELVAR(event, g_model.trainerData.frameLength, -20, 35); - break; - case 1: - CHECK_INCDEC_MODELVAR(event, g_model.trainerData.delay, -4, 10); - break; - case 2: - CHECK_INCDEC_MODELVAR_ZERO(event, g_model.trainerData.pulsePol, 1); - break; - } - } - break; - -#if defined(PXX2) - case ITEM_MODEL_SETUP_REGISTRATION_ID: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_REG_ID); - if (isDefaultModelRegistrationID()) - lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_PXX2_DEFAULT); - else - lcdDrawSizedText(MODEL_SETUP_2ND_COLUMN, y, g_model.modelRegistrationID, PXX2_LEN_REGISTRATION_ID, ZCHAR); - break; -#endif - - case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM: - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_MODEL_NUM: - { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); - lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_RECEIVER_NUM); - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId[moduleIdx], attr | LEADING0 | LEFT, 2); - if (attr) { - CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId[moduleIdx], getMaxRxNum(moduleIdx)); - if (event == EVT_KEY_LONG(KEY_ENTER)) { - killEvents(event); - uint8_t newVal = modelslist.findNextUnusedModelId(moduleIdx); - if (newVal != g_model.header.modelId[moduleIdx]) { - g_model.header.modelId[moduleIdx] = newVal; - storageDirty(EE_MODEL); - } - } - } - } - break; - - case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_REGISTER_RANGE: - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_REGISTER_RANGE: - { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); - lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_MODULE); - drawButton(MODEL_SETUP_2ND_COLUMN, y, STR_REGISTER, (menuHorizontalPosition == 0 ? attr : 0)); - drawButton(MODEL_SETUP_2ND_COLUMN + MODEL_SETUP_SET_FAILSAFE_OFS, y, STR_MODULE_RANGE, (menuHorizontalPosition == 1 ? attr : 0)); - if (attr) { - if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL && s_editMode > 0) { - if (menuHorizontalPosition == 0 && event == EVT_KEY_BREAK(KEY_ENTER)) { - startRegisterDialog(moduleIdx); - } - else if (menuHorizontalPosition == 1) { - moduleState[moduleIdx].mode = MODULE_MODE_RANGECHECK; - } - } - if (s_editMode == 0 && !warningText) { - moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; - } - if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL) { - // REGISTER finished - s_editMode = 0; - } - } - } - break; - - case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_OPTIONS: - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_OPTIONS: - lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_OPTIONS); - drawButton(MODEL_SETUP_2ND_COLUMN, y, STR_SET, attr); - if (event == EVT_KEY_BREAK(KEY_ENTER) && attr) { - g_moduleIdx = CURRENT_MODULE_EDITED(k); - pushMenu(menuModelModuleOptions); - } - break; - - case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_1: - case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_2: - case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_3: - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_1: - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_2: - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_3: - { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); - uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(k); - ModuleInformation & moduleInformation = reusableBuffer.moduleSetup.pxx2.moduleInformation; - - drawStringWithIndex(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_RECEIVER, receiverIdx + 1); - - if (!isPXX2ReceiverUsed(moduleIdx, receiverIdx)) { - drawButton(MODEL_SETUP_2ND_COLUMN, y, STR_MODULE_BIND, attr); - if (attr && event == EVT_KEY_BREAK(KEY_ENTER)) { - setPXX2ReceiverUsed(moduleIdx, receiverIdx); - memclear(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], PXX2_LEN_RX_NAME); - onPXX2ReceiverMenu(STR_BIND); - } - break; - } - - drawReceiverName(MODEL_SETUP_2ND_COLUMN, y, moduleIdx, receiverIdx, attr); - - if (s_editMode && isModuleR9MAccess(moduleIdx) && moduleState[moduleIdx].mode == MODULE_MODE_NORMAL && reusableBuffer.moduleSetup.bindInformation.step < 0) { - if (reusableBuffer.moduleSetup.bindInformation.step == BIND_MODULE_TX_INFORMATION_REQUEST && moduleInformation.information.modelID) { - // For R9M ACCESS the module information has been requested to know if we are in EU mode. We just receive it here and continue - if (moduleInformation.information.variant == PXX2_VARIANT_EU) { - // In EU mode we will need the power of the module to know if telemetry can be proposed - reusableBuffer.moduleSetup.bindInformation.step = BIND_MODULE_TX_SETTINGS_REQUEST; -#if defined(SIMU) - reusableBuffer.moduleSetup.pxx2.moduleSettings.txPower = 14; - break; -#else - moduleState[moduleIdx].readModuleSettings(&reusableBuffer.moduleSetup.pxx2.moduleSettings); -#endif - } - else { - reusableBuffer.moduleSetup.bindInformation.step = 0; - moduleState[moduleIdx].startBind(&reusableBuffer.moduleSetup.bindInformation); - } - } - else if (reusableBuffer.moduleSetup.bindInformation.step == BIND_MODULE_TX_SETTINGS_REQUEST && reusableBuffer.moduleSetup.pxx2.moduleSettings.txPower > 0) { - // We just receive the module settings (for TX power) - reusableBuffer.moduleSetup.bindInformation.step = 0; - moduleState[moduleIdx].startBind(&reusableBuffer.moduleSetup.bindInformation); - } - } - else if (attr && (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL || s_editMode == 0)) { - if (moduleState[moduleIdx].mode) { - moduleState[moduleIdx].mode = 0; - removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx); - killEvents(event); // we stopped BIND / SHARE, we don't want to re-open the menu - event = 0; - CLEAR_POPUP(); - } - s_editMode = 0; - } - - if (moduleState[moduleIdx].mode == MODULE_MODE_BIND) { - if (reusableBuffer.moduleSetup.bindInformation.step == BIND_INIT) { - if (reusableBuffer.moduleSetup.bindInformation.candidateReceiversCount > 0) { - popupMenuItemsCount = min(reusableBuffer.moduleSetup.bindInformation.candidateReceiversCount, PXX2_MAX_RECEIVERS_PER_MODULE); - for (auto rx = 0; rx < popupMenuItemsCount; rx++) { - popupMenuItems[rx] = reusableBuffer.moduleSetup.bindInformation.candidateReceiversNames[rx]; - } - // popupMenuTitle = STR_PXX2_SELECT_RX; - POPUP_MENU_START(onPXX2BindMenu); - } - } - } - - if (attr && event == EVT_KEY_BREAK(KEY_ENTER)) { - POPUP_MENU_ADD_ITEM(STR_BIND); - POPUP_MENU_ADD_ITEM(STR_OPTIONS); - POPUP_MENU_ADD_ITEM(STR_SHARE); - POPUP_MENU_ADD_ITEM(STR_DELETE); - POPUP_MENU_ADD_ITEM(STR_RESET); - POPUP_MENU_START(onPXX2ReceiverMenu); - } - } - break; - - case ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_BIND: - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_BIND: - { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); - ModuleData & moduleData = g_model.moduleData[moduleIdx]; - if (isModulePPM(moduleIdx)) { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PPMFRAME); - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)moduleData.ppm.frameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1|LEFT, 0, NULL, STR_MS); - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN+80, y, (moduleData.ppm.delay*50)+300, (CURSOR_ON_LINE() || menuHorizontalPosition==1) ? attr|LEFT : LEFT, 0, NULL, "us"); - lcdDrawText(MODEL_SETUP_2ND_COLUMN+160, y, moduleData.ppm.pulsePol ? "+" : "-", (CURSOR_ON_LINE() || menuHorizontalPosition==2) ? attr : 0); - if (attr && s_editMode>0) { - switch (menuHorizontalPosition) { - case 0: - CHECK_INCDEC_MODELVAR(event, moduleData.ppm.frameLength, -20, 35); - break; - case 1: - CHECK_INCDEC_MODELVAR(event, moduleData.ppm.delay, -4, 10); - break; - case 2: - CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.ppm.pulsePol, 1); - break; - } - } - } - else if (isModuleSBUS(moduleIdx)) { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_REFRESHRATE); - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)moduleData.ppm.frameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1|LEFT, 0, NULL, STR_MS); - lcdDrawText(MODEL_SETUP_3RD_COLUMN, y, moduleData.sbus.noninverted ? "not inverted" : "normal", (CURSOR_ON_LINE() || menuHorizontalPosition==1) ? attr : 0); - - if (attr && s_editMode>0) { - switch (menuHorizontalPosition) { - case 0: - CHECK_INCDEC_MODELVAR(event, moduleData.ppm.frameLength, -33, 35); - break; - case 1: - CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.sbus.noninverted, 1); - break; - } - } - } - else { - int l_posHorz = menuHorizontalPosition; - coord_t xOffsetBind = MODEL_SETUP_BIND_OFS; - if (!isModuleRxNumAvailable(moduleIdx)) { - xOffsetBind = 0; - lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_RECEIVER); - if (attr) l_posHorz += 1; - } - else { - lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_RECEIVER_NUM); - } - if (isModuleBindRangeAvailable(moduleIdx)) { - if (xOffsetBind) - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId[moduleIdx], (l_posHorz==0 ? attr : 0) | LEADING0 | LEFT, 2); - if (attr && l_posHorz==0) { - if (s_editMode>0) { - CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId[moduleIdx], getMaxRxNum(moduleIdx)); - if (event == EVT_KEY_LONG(KEY_ENTER)) { - killEvents(event); - uint8_t newVal = modelslist.findNextUnusedModelId(moduleIdx); - if (newVal != g_model.header.modelId[moduleIdx]) { - g_model.header.modelId[moduleIdx] = newVal; - storageDirty(EE_MODEL); - } - } - } - } - drawButton(MODEL_SETUP_2ND_COLUMN+xOffsetBind, y, STR_MODULE_BIND, (moduleState[moduleIdx].mode == MODULE_MODE_BIND ? BUTTON_ON : BUTTON_OFF) | (l_posHorz==1 ? attr : 0)); - drawButton(MODEL_SETUP_2ND_COLUMN+MODEL_SETUP_RANGE_OFS+xOffsetBind, y, STR_MODULE_RANGE, (moduleState[moduleIdx].mode == MODULE_MODE_RANGECHECK ? BUTTON_ON : BUTTON_OFF) | (l_posHorz==2 ? attr : 0)); - uint8_t newFlag = 0; -#if defined(MULTIMODULE) - if (multiBindStatus == MULTI_BIND_FINISHED) { - multiBindStatus = MULTI_NORMAL_OPERATION; - s_editMode = 0; - } -#endif - if (attr && l_posHorz>0) { - if (s_editMode>0) { - if (l_posHorz == 1) { - if (isModuleR9MNonAccess(moduleIdx) || isModuleD16(moduleIdx)) { - if (event == EVT_KEY_BREAK(KEY_ENTER)) { - startBindMenu(moduleIdx); - continue; - } - if (moduleState[moduleIdx].mode == MODULE_MODE_BIND) { - newFlag = MODULE_MODE_BIND; - } - else { - if (!popupMenuItemsCount) { - s_editMode = 0; // this is when popup is exited before a choice is made - } - } - } - else { - newFlag = MODULE_MODE_BIND; - } - } - else if (l_posHorz == 2) { - newFlag = MODULE_MODE_RANGECHECK; - } - } - } - moduleState[moduleIdx].mode = newFlag; -#if defined(MULTIMODULE) - if (newFlag == MODULE_MODE_BIND) - multiBindStatus = MULTI_BIND_INITIATED; -#endif - } - } - break; - } - - case ITEM_MODEL_SETUP_INTERNAL_MODULE_FAILSAFE: - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_FAILSAFE: - { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); - ModuleData & moduleData = g_model.moduleData[moduleIdx]; - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_FAILSAFE); - lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VFAILSAFE, moduleData.failsafeMode, menuHorizontalPosition==0 ? attr : 0); - if (moduleData.failsafeMode == FAILSAFE_CUSTOM) { - drawButton(MODEL_SETUP_2ND_COLUMN + MODEL_SETUP_SET_FAILSAFE_OFS, y, STR_SET, menuHorizontalPosition==1 ? attr : 0); - } - if (attr) { - if (moduleData.failsafeMode != FAILSAFE_CUSTOM) - menuHorizontalPosition = 0; - if (menuHorizontalPosition==0) { - if (s_editMode>0) { - CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.failsafeMode, isModuleR9M(moduleIdx) ? FAILSAFE_NOPULSES : FAILSAFE_LAST); - if (checkIncDec_Ret) SEND_FAILSAFE_NOW(moduleIdx); - } - } - else if (menuHorizontalPosition==1) { - s_editMode = 0; - if (moduleData.failsafeMode == FAILSAFE_CUSTOM) { - if (event == EVT_KEY_LONG(KEY_ENTER)) { - killEvents(event); - setCustomFailsafe(moduleIdx); - storageDirty(EE_MODEL); - AUDIO_WARNING1(); - SEND_FAILSAFE_NOW(moduleIdx); - } - else if (event == EVT_KEY_BREAK(KEY_ENTER)) { - g_moduleIdx = moduleIdx; - pushMenu(menuModelFailsafe); - } - } - } - else { - lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN, y, LCD_W - MODEL_SETUP_2ND_COLUMN - 2, 8, TEXT_COLOR); - } - } - break; - } - - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_OPTIONS: - { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); -#if defined(MULTIMODULE) - if (isModuleMultimodule(moduleIdx)) { - int optionValue = g_model.moduleData[moduleIdx].multi.optionValue; - - const uint8_t multi_proto = g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(true); - const mm_protocol_definition *pdef = getMultiProtocolDefinition(multi_proto); - if (pdef->optionsstr) - lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, pdef->optionsstr); - - if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) - optionValue = 50 + 5 * optionValue; - - lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr); - if (attr) { - if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) { - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, 0, 70); - } - else if (multi_proto == MODULE_SUBTYPE_MULTI_OLRS) { - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, -1, 7); - } - else { - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, -128, 127); - } - } - } -#endif - if (isModuleR9MNonAccess(moduleIdx)) { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODULE_TELEMETRY); - if (isSportLineUsedByInternalModule()) - lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_DISABLE_INTERNAL); - else - lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_MODULE_TELEM_ON); - } - else if (isModuleSBUS(moduleIdx)) { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_WARN_BATTVOLTAGE); - drawValueWithUnit(MODEL_SETUP_4TH_COLUMN, y, getBatteryVoltage(), UNIT_VOLTS, attr|PREC2|LEFT); - } - break; - } - - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_POWER: - { - uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); - if (isModuleR9MNonAccess(moduleIdx)) { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_RFPOWER); - if(isModuleR9M_FCC_VARIANT(moduleIdx)) { - lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_R9M_FCC_POWER_VALUES, g_model.moduleData[moduleIdx].pxx.power, LEFT | attr); - if (attr) - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].pxx.power, 0, R9M_FCC_POWER_MAX); - } - else { - lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_R9M_LBT_POWER_VALUES, g_model.moduleData[moduleIdx].pxx.power, LEFT | attr); - if (attr) - CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].pxx.power, 0, R9M_LBT_POWER_MAX); - if (attr && s_editMode == 0 && reusableBuffer.moduleSetup.r9mPower != g_model.moduleData[moduleIdx].pxx.power) { - if((reusableBuffer.moduleSetup.r9mPower + g_model.moduleData[moduleIdx].pxx.power) < 5) { //switching between mode 2 and 3 does not require rebind - POPUP_WARNING(STR_WARNING); - SET_WARNING_INFO(STR_REBIND, sizeof(TR_REBIND), 0); - } - reusableBuffer.moduleSetup.r9mPower = g_model.moduleData[moduleIdx].pxx.power; - } - } - } -#if defined(MULTIMODULE) - else if (isModuleMultimodule(moduleIdx)) { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MULTI_LOWPOWER); - g_model.moduleData[EXTERNAL_MODULE].multi.lowPowerMode = editCheckBox(g_model.moduleData[EXTERNAL_MODULE].multi.lowPowerMode, MODEL_SETUP_2ND_COLUMN, y, attr, event); - } -#endif - } - break; - -#if defined(MULTIMODULE) - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AUTOBIND: - if (g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(true) == MODULE_SUBTYPE_MULTI_DSM2) - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MULTI_DSM_AUTODTECT); - else - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MULTI_AUTOBIND); - g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode = editCheckBox(g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, attr, event); - break; - - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS: { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODULE_STATUS); - - char statusText[64]; - multiModuleStatus.getStatusString(statusText); - lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); - break; - - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS: { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODULE_SYNC); - - char statusText[64]; - multiSyncStatus.getRefreshString(statusText); - lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText); - break; - } - } -#endif - } - } - - if (isModuleInRangeCheckMode()) { - theme->drawMessageBox("RSSI :", NULL, NULL, WARNING_TYPE_INFO); - lcdDrawNumber(WARNING_LINE_X, WARNING_INFOLINE_Y, TELEMETRY_RSSI(), DBLSIZE|LEFT); - } - - // some field just finished being edited - if (old_editMode > 0 && s_editMode == 0) { - ModelCell* mod_cell = modelslist.getCurrentModel(); - if (mod_cell) { - switch(menuVerticalPosition) { - case ITEM_MODEL_SETUP_NAME: - mod_cell->setModelName(g_model.header.name); - break; - - case ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_BIND: - if (menuHorizontalPosition != 0) - break; - case ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE: - mod_cell->setRfData(&g_model); - checkModelIdUnique(INTERNAL_MODULE); - break; - - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_BIND: - if (menuHorizontalPosition != 0) - break; - case ITEM_MODEL_SETUP_EXTERNAL_MODULE_TYPE: - mod_cell->setRfData(&g_model); - if (g_model.moduleData[EXTERNAL_MODULE].type != MODULE_TYPE_NONE) - checkModelIdUnique(EXTERNAL_MODULE); - } - } - } - - return true; -} diff --git a/radio/src/gui/480x272/model_special_functions.cpp b/radio/src/gui/480x272/model_special_functions.cpp deleted file mode 100644 index 6ae8c4d11..000000000 --- a/radio/src/gui/480x272/model_special_functions.cpp +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -#define MODEL_SPECIAL_FUNC_1ST_COLUMN 60 -#define MODEL_SPECIAL_FUNC_2ND_COLUMN 120 -#define MODEL_SPECIAL_FUNC_2ND_COLUMN_EXT (lcdNextPos + 5) -#define MODEL_SPECIAL_FUNC_3RD_COLUMN 295 -#define MODEL_SPECIAL_FUNC_4TH_COLUMN 440 -#define MODEL_SPECIAL_FUNC_4TH_COLUMN_ONOFF 450 - -void onCustomFunctionsFileSelectionMenu(const char * result) -{ - CustomFunctionData * cf; - uint8_t eeFlags; - - if (menuHandlers[menuLevel] == menuModelSpecialFunctions) { - cf = &g_model.customFn[menuVerticalPosition]; - eeFlags = EE_MODEL; - } - else { - cf = &g_eeGeneral.customFn[menuVerticalPosition]; - eeFlags = EE_GENERAL; - } - - uint8_t func = CFN_FUNC(cf); - - if (result == STR_UPDATE_LIST) { - char directory[256]; - if (func == FUNC_PLAY_SCRIPT) { - strcpy(directory, SCRIPTS_FUNCS_PATH); - } - else { - strcpy(directory, SOUNDS_PATH); - strncpy(directory+SOUNDS_PATH_LNG_OFS, currentLanguagePack->id, 2); - } - if (!sdListFiles(directory, func==FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, sizeof(cf->play.name), NULL)) { - POPUP_WARNING(func==FUNC_PLAY_SCRIPT ? STR_NO_SCRIPTS_ON_SD : STR_NO_SOUNDS_ON_SD); - } - } - else if (result != STR_EXIT) { - // The user choosed a file in the list - memcpy(cf->play.name, result, sizeof(cf->play.name)); - storageDirty(eeFlags); - if (func == FUNC_PLAY_SCRIPT) { - LUA_LOAD_MODEL_SCRIPTS(); - } - } -} - -void onCustomFunctionsMenu(const char * result) -{ - CustomFunctionData * cfn; - uint8_t eeFlags; - - if (menuHandlers[menuLevel] == menuModelSpecialFunctions) { - cfn = &g_model.customFn[menuVerticalPosition]; - eeFlags = EE_MODEL; - } - else { - cfn = &g_eeGeneral.customFn[menuVerticalPosition]; - eeFlags = EE_GENERAL; - } - - if (result == STR_COPY) { - clipboard.type = CLIPBOARD_TYPE_CUSTOM_FUNCTION; - clipboard.data.cfn = *cfn; - } - else if (result == STR_PASTE) { - *cfn = clipboard.data.cfn; - storageDirty(eeFlags); - } - else if (result == STR_CLEAR) { - memset(cfn, 0, sizeof(CustomFunctionData)); - storageDirty(eeFlags); - } - else if (result == STR_INSERT) { - memmove(cfn+1, cfn, (MAX_SPECIAL_FUNCTIONS-menuVerticalPosition-1)*sizeof(CustomFunctionData)); - memset(cfn, 0, sizeof(CustomFunctionData)); - storageDirty(eeFlags); - } - else if (result == STR_DELETE) { - memmove(cfn, cfn+1, (MAX_SPECIAL_FUNCTIONS-menuVerticalPosition-1)*sizeof(CustomFunctionData)); - memset(&g_model.customFn[MAX_SPECIAL_FUNCTIONS-1], 0, sizeof(CustomFunctionData)); - storageDirty(eeFlags); - } -} - -void onAdjustGvarSourceLongEnterPress(const char * result) -{ - CustomFunctionData * cfn = &g_model.customFn[menuVerticalPosition]; - - if (result == STR_CONSTANT) { - CFN_GVAR_MODE(cfn) = FUNC_ADJUST_GVAR_CONSTANT; - CFN_PARAM(cfn) = 0; - storageDirty(EE_MODEL); - } - else if (result == STR_MIXSOURCE) { - CFN_GVAR_MODE(cfn) = FUNC_ADJUST_GVAR_SOURCE; - CFN_PARAM(cfn) = 0; - storageDirty(EE_MODEL); - } - else if (result == STR_GLOBALVAR) { - CFN_GVAR_MODE(cfn) = FUNC_ADJUST_GVAR_GVAR; - CFN_PARAM(cfn) = 0; - storageDirty(EE_MODEL); - } - else if (result == STR_INCDEC) { - CFN_GVAR_MODE(cfn) = FUNC_ADJUST_GVAR_INCDEC; - CFN_PARAM(cfn) = 0; - storageDirty(EE_MODEL); - } - else if (result != STR_EXIT) { - onSourceLongEnterPress(result); - } -} - -enum SpecialFunctionsItems { - ITEM_SPECIAL_FUNCTIONS_SWITCH, - ITEM_SPECIAL_FUNCTIONS_FUNCTION, - ITEM_SPECIAL_FUNCTIONS_PARAM, - ITEM_SPECIAL_FUNCTIONS_VALUE, - ITEM_SPECIAL_FUNCTIONS_ENABLE, - ITEM_SPECIAL_FUNCTIONS_COUNT, - ITEM_SPECIAL_FUNCTIONS_LAST = ITEM_SPECIAL_FUNCTIONS_COUNT-1 -}; - -bool menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomFunctionsContext & functionsContext) -{ - uint8_t eeFlags = (functions == g_model.customFn) ? EE_MODEL : EE_GENERAL; - - if (menuHorizontalPosition<0 && event==EVT_KEY_LONG(KEY_ENTER) && !READ_ONLY()) { - killEvents(event); - CustomFunctionData *cfn = &functions[menuVerticalPosition]; - if (!CFN_EMPTY(cfn)) - POPUP_MENU_ADD_ITEM(STR_COPY); - if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_FUNCTION && isAssignableFunctionAvailable(clipboard.data.cfn.func)) - POPUP_MENU_ADD_ITEM(STR_PASTE); - if (!CFN_EMPTY(cfn) && CFN_EMPTY(&functions[MAX_SPECIAL_FUNCTIONS-1])) - POPUP_MENU_ADD_ITEM(STR_INSERT); - if (!CFN_EMPTY(cfn)) - POPUP_MENU_ADD_ITEM(STR_CLEAR); - for (int i=menuVerticalPosition+1; i0) ? BLINK|INVERS : INVERS) : 0); - LcdFlags active = (attr && s_editMode>0); - switch (j) { - case ITEM_SPECIAL_FUNCTIONS_SWITCH: - drawSwitch(MODEL_SPECIAL_FUNC_1ST_COLUMN, y, CFN_SWITCH(cfn), attr | ((functionsContext.activeSwitches & ((MASK_CFN_TYPE)1 << k)) ? BOLD : 0)); - if (active || AUTOSWITCH_ENTER_LONG()) { - CHECK_INCDEC_SWITCH(event, CFN_SWITCH(cfn), SWSRC_FIRST, SWSRC_LAST, eeFlags, isSwitchAvailableInCustomFunctions); - } - if (func == FUNC_OVERRIDE_CHANNEL && functions != g_model.customFn) { - func = CFN_FUNC(cfn) = func+1; - } - break; - - case ITEM_SPECIAL_FUNCTIONS_FUNCTION: - if (CFN_SWITCH(cfn)) { - if (active) { - func = CFN_FUNC(cfn) = checkIncDec(event, CFN_FUNC(cfn), 0, FUNC_MAX-1, eeFlags, isAssignableFunctionAvailable); - if (checkIncDec_Ret) CFN_RESET(cfn); - } - lcdDrawTextAtIndex(MODEL_SPECIAL_FUNC_2ND_COLUMN, y, STR_VFSWFUNC, func, attr); - } - else { - j = ITEM_SPECIAL_FUNCTIONS_ENABLE; // skip other fields - while (menuVerticalPosition==k && menuHorizontalPosition > 0) { - REPEAT_LAST_CURSOR_MOVE(ITEM_SPECIAL_FUNCTIONS_LAST, true); - } - } - break; - - case ITEM_SPECIAL_FUNCTIONS_PARAM: - { - int8_t maxParam = MAX_OUTPUT_CHANNELS-1; -#if defined(OVERRIDE_CHANNEL_FUNCTION) - if (func == FUNC_OVERRIDE_CHANNEL) { - putsChn(MODEL_SPECIAL_FUNC_2ND_COLUMN_EXT, y, CFN_CH_INDEX(cfn)+1, attr); - } - else -#endif - if (func == FUNC_TRAINER) { - maxParam = 4; - drawSource(MODEL_SPECIAL_FUNC_2ND_COLUMN_EXT, y, CFN_CH_INDEX(cfn)==0 ? 0 : MIXSRC_Rud+CFN_CH_INDEX(cfn)-1, attr); - } -#if defined(GVARS) - else if (func == FUNC_ADJUST_GVAR) { - maxParam = MAX_GVARS-1; - drawStringWithIndex(MODEL_SPECIAL_FUNC_2ND_COLUMN_EXT, y, STR_GV, CFN_GVAR_INDEX(cfn)+1, attr); - if (active) CFN_GVAR_INDEX(cfn) = checkIncDec(event, CFN_GVAR_INDEX(cfn), 0, maxParam, eeFlags); - break; - } -#endif - else if (func == FUNC_SET_TIMER) { - maxParam = TIMERS-1; - drawStringWithIndex(MODEL_SPECIAL_FUNC_2ND_COLUMN_EXT, y, STR_TIMER, CFN_TIMER_INDEX(cfn)+1, attr); - if (active) CFN_TIMER_INDEX(cfn) = checkIncDec(event, CFN_TIMER_INDEX(cfn), 0, maxParam, eeFlags); - break; - } - else if (attr) { - REPEAT_LAST_CURSOR_MOVE(ITEM_SPECIAL_FUNCTIONS_LAST, true); - } - if (active) CHECK_INCDEC_MODELVAR_ZERO(event, CFN_CH_INDEX(cfn), maxParam); - break; - } - - case ITEM_SPECIAL_FUNCTIONS_VALUE: - { - INCDEC_DECLARE_VARS(eeFlags); - int16_t val_displayed = CFN_PARAM(cfn); - int16_t val_min = 0; - int16_t val_max = 255; - if (func == FUNC_RESET) { - val_max = FUNC_RESET_PARAM_FIRST_TELEM+lastUsedTelemetryIndex(); - int param = CFN_PARAM(cfn); - if (param < FUNC_RESET_PARAM_FIRST_TELEM) { - lcdDrawTextAtIndex(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, STR_VFSWRESET, param, attr); - } - else { - TelemetrySensor * sensor = & g_model.telemetrySensors[param-FUNC_RESET_PARAM_FIRST_TELEM]; - lcdDrawSizedText(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, sensor->label, TELEM_LABEL_LEN, attr|ZCHAR); - } - if (active) INCDEC_ENABLE_CHECK(functions == g_eeGeneral.customFn ? isSourceAvailableInGlobalResetSpecialFunction : isSourceAvailableInResetSpecialFunction); - } -#if defined(OVERRIDE_CHANNEL_FUNCTION) - else if (func == FUNC_OVERRIDE_CHANNEL) { - getMixSrcRange(MIXSRC_FIRST_CH, val_min, val_max); - lcdDrawNumber(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr|LEFT); - } -#endif -#if defined(DANGEROUS_MODULE_FUNCTIONS) - else if (func >= FUNC_RANGECHECK && func <= FUNC_BIND) { - val_max = NUM_MODULES-1; - lcdDrawTextAtIndex(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, "\004Int.Ext.", CFN_PARAM(cfn), attr); - } -#endif - else if (func == FUNC_SET_TIMER) { - getMixSrcRange(MIXSRC_FIRST_TIMER, val_min, val_max); - drawTimer(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr|LEFT|TIMEHOUR); - } - else if (func == FUNC_PLAY_SOUND) { - val_max = AU_SPECIAL_SOUND_LAST-AU_SPECIAL_SOUND_FIRST-1; - lcdDrawTextAtIndex(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, STR_FUNCSOUNDS, val_displayed, attr); - } -#if defined(HAPTIC) - else if (func == FUNC_HAPTIC) { - val_max = 3; - lcdDrawNumber(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr|LEFT); - } -#endif -#if defined(SDCARD) - else if (func == FUNC_PLAY_TRACK || func == FUNC_BACKGND_MUSIC || func == FUNC_PLAY_SCRIPT) { - coord_t x = MODEL_SPECIAL_FUNC_3RD_COLUMN; - if (ZEXIST(cfn->play.name)) - lcdDrawSizedText(x, y, cfn->play.name, sizeof(cfn->play.name), attr); - else - lcdDrawTextAtIndex(x, y, STR_VCSWFUNC, 0, attr); - if (attr) s_editMode = 0; - if (attr && event==EVT_KEY_FIRST(KEY_ENTER)) { - killEvents(KEY_ENTER); - char directory[256]; - if (func==FUNC_PLAY_SCRIPT) { - strcpy(directory, SCRIPTS_FUNCS_PATH); - } - else { - strcpy(directory, SOUNDS_PATH); - strncpy(directory+SOUNDS_PATH_LNG_OFS, currentLanguagePack->id, 2); - } - if (sdListFiles(directory, func==FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, sizeof(cfn->play.name), cfn->play.name)) { - POPUP_MENU_START(onCustomFunctionsFileSelectionMenu); - } - else { - POPUP_WARNING(func==FUNC_PLAY_SCRIPT ? STR_NO_SCRIPTS_ON_SD : STR_NO_SOUNDS_ON_SD); - } - } - break; - } - else if (func == FUNC_PLAY_VALUE) { - val_max = MIXSRC_LAST_TELEM; - drawSource(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr); - if (active) { - INCDEC_SET_FLAG(eeFlags | INCDEC_SOURCE); - INCDEC_ENABLE_CHECK(functions == g_eeGeneral.customFn ? isSourceAvailableInGlobalFunctions : isSourceAvailable); - } - } -#endif - else if (func == FUNC_VOLUME) { - val_max = MIXSRC_LAST_CH; - drawSource(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr); - if (active) { - INCDEC_SET_FLAG(eeFlags | INCDEC_SOURCE); - INCDEC_ENABLE_CHECK(isSourceAvailable); - } - } - else if (func == FUNC_LOGS) { - if (val_displayed) { - lcdDrawNumber(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr|PREC1|LEFT, 0, NULL, "s"); - } - else { - lcdDrawMMM(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, attr); - } - } -#if defined(GVARS) - else if (func == FUNC_ADJUST_GVAR) { - switch (CFN_GVAR_MODE(cfn)) { - case FUNC_ADJUST_GVAR_CONSTANT: - { - val_displayed = (int16_t)CFN_PARAM(cfn); - getMixSrcRange(CFN_GVAR_INDEX(cfn) + MIXSRC_FIRST_GVAR, val_min, val_max); - drawGVarValue(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, CFN_GVAR_INDEX(cfn), val_displayed, attr|LEFT); - break; - } - case FUNC_ADJUST_GVAR_SOURCE: - val_max = MIXSRC_LAST_CH; - drawSource(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr); - if (active) { - INCDEC_SET_FLAG(eeFlags | INCDEC_SOURCE); - INCDEC_ENABLE_CHECK(isSourceAvailable); - } - break; - case FUNC_ADJUST_GVAR_GVAR: - val_max = MAX_GVARS-1; - drawStringWithIndex(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, STR_GV, val_displayed+1, attr); - break; - default: // FUNC_ADJUST_GVAR_INC - getMixSrcRange(CFN_GVAR_INDEX(cfn) + MIXSRC_FIRST_GVAR, val_min, val_max); - getGVarIncDecRange(val_min, val_max); - lcdDrawText(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, (val_displayed < 0 ? "-= " : "+= "), attr); - drawGVarValue(lcdNextPos, y, CFN_GVAR_INDEX(cfn), abs(val_displayed), attr|LEFT); - break; - } - } -#endif - else if (attr) { - REPEAT_LAST_CURSOR_MOVE(ITEM_SPECIAL_FUNCTIONS_LAST, true); - } - - if (active || event==EVT_KEY_LONG(KEY_ENTER)) { - CFN_PARAM(cfn) = CHECK_INCDEC_PARAM(event, val_displayed, val_min, val_max); - if (func == FUNC_ADJUST_GVAR && attr && event==EVT_KEY_LONG(KEY_ENTER)) { - killEvents(event); - if (CFN_GVAR_MODE(cfn) != FUNC_ADJUST_GVAR_CONSTANT) - POPUP_MENU_ADD_ITEM(STR_CONSTANT); - if (CFN_GVAR_MODE(cfn) != FUNC_ADJUST_GVAR_SOURCE) - POPUP_MENU_ADD_ITEM(STR_MIXSOURCE); - if (CFN_GVAR_MODE(cfn) != FUNC_ADJUST_GVAR_GVAR) - POPUP_MENU_ADD_ITEM(STR_GLOBALVAR); - if (CFN_GVAR_MODE(cfn) != FUNC_ADJUST_GVAR_INCDEC) - POPUP_MENU_ADD_ITEM(STR_INCDEC); - POPUP_MENU_START(onAdjustGvarSourceLongEnterPress); - s_editMode = EDIT_MODIFY_FIELD; - } - } - break; - } - - case ITEM_SPECIAL_FUNCTIONS_ENABLE: - if (HAS_ENABLE_PARAM(func)) { - if (active) CFN_ACTIVE(cfn) = checkIncDec(event, CFN_ACTIVE(cfn), 0, 1, eeFlags); - drawCheckBox(MODEL_SPECIAL_FUNC_4TH_COLUMN_ONOFF, y, CFN_ACTIVE(cfn), attr); - } - else if (HAS_REPEAT_PARAM(func)) { - if (active) CFN_PLAY_REPEAT(cfn) = checkIncDec(event, CFN_PLAY_REPEAT(cfn)==CFN_PLAY_REPEAT_NOSTART?-1:CFN_PLAY_REPEAT(cfn), -1, 60/CFN_PLAY_REPEAT_MUL, eeFlags); - if (CFN_PLAY_REPEAT(cfn) == 0) { - lcdDrawText(MODEL_SPECIAL_FUNC_4TH_COLUMN+2, y, "1x", attr); - } - else if (CFN_PLAY_REPEAT(cfn) == CFN_PLAY_REPEAT_NOSTART) { - lcdDrawText(MODEL_SPECIAL_FUNC_4TH_COLUMN-1, y, "!1x", attr); - } - else { - lcdDrawNumber(MODEL_SPECIAL_FUNC_4TH_COLUMN+12, y, CFN_PLAY_REPEAT(cfn)*CFN_PLAY_REPEAT_MUL, attr|RIGHT, 0, NULL, "s"); - } - } - else if (attr) { - REPEAT_LAST_CURSOR_MOVE(ITEM_SPECIAL_FUNCTIONS_LAST, true); - } - break; - } - } - } - - return true; -} - -bool menuModelSpecialFunctions(event_t event) -{ - MENU(STR_MENUCUSTOMFUNC, MODEL_ICONS, menuTabModel, MENU_MODEL_SPECIAL_FUNCTIONS, MAX_SPECIAL_FUNCTIONS, { NAVIGATION_LINE_BY_LINE|4/*repeated*/ }); - return menuSpecialFunctions(event, g_model.customFn, modelFunctionsContext); -} diff --git a/radio/src/gui/480x272/model_telemetry.cpp b/radio/src/gui/480x272/model_telemetry.cpp deleted file mode 100644 index 5cd57b1dc..000000000 --- a/radio/src/gui/480x272/model_telemetry.cpp +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -enum MenuModelTelemetryFrskyItems { - ITEM_TELEMETRY_PROTOCOL_TYPE, - ITEM_TELEMETRY_RSSI_LABEL, - ITEM_TELEMETRY_RSSI_SOURCE, - ITEM_TELEMETRY_RSSI_ALARM1, - ITEM_TELEMETRY_RSSI_ALARM2, - ITEM_TELEMETRY_DISABLE_ALARMS, - ITEM_TELEMETRY_SENSORS_LABEL, - ITEM_TELEMETRY_SENSOR_FIRST, - ITEM_TELEMETRY_SENSOR_LAST = ITEM_TELEMETRY_SENSOR_FIRST + MAX_TELEMETRY_SENSORS - 1, - ITEM_TELEMETRY_DISCOVER_SENSORS, - ITEM_TELEMETRY_NEW_SENSOR, - ITEM_TELEMETRY_DELETE_ALL_SENSORS, - ITEM_TELEMETRY_IGNORE_SENSOR_INSTANCE, -#if defined(VARIO) - ITEM_TELEMETRY_VARIO_LABEL, - ITEM_TELEMETRY_VARIO_SOURCE, - ITEM_TELEMETRY_VARIO_RANGE, - ITEM_TELEMETRY_VARIO_CENTER, -#endif - ITEM_TELEMETRY_MAX -}; - -#define TELEM_COL2 200 -#define TELEM_COL3 275 -#define TELEM_COL4 350 -#define TELEM_COL5 425 - -#define RSSI_ROWS LABEL(RSSI), 0, 0, 0, 0, -#define SENSOR_ROWS(x) (isTelemetryFieldAvailable(x) ? (uint8_t)0 : HIDDEN_ROW) -#define SENSORS_ROWS LABEL(Sensors), SENSOR_ROWS(0), SENSOR_ROWS(1), SENSOR_ROWS(2), SENSOR_ROWS(3), SENSOR_ROWS(4), SENSOR_ROWS(5), SENSOR_ROWS(6), SENSOR_ROWS(7), SENSOR_ROWS(8), SENSOR_ROWS(9), SENSOR_ROWS(10), SENSOR_ROWS(11), SENSOR_ROWS(12), SENSOR_ROWS(13), SENSOR_ROWS(14), SENSOR_ROWS(15), SENSOR_ROWS(16), SENSOR_ROWS(17), SENSOR_ROWS(18), SENSOR_ROWS(19), SENSOR_ROWS(20), SENSOR_ROWS(21), SENSOR_ROWS(22), SENSOR_ROWS(23), SENSOR_ROWS(24), SENSOR_ROWS(25), SENSOR_ROWS(26), SENSOR_ROWS(27), SENSOR_ROWS(28), SENSOR_ROWS(29), SENSOR_ROWS(30), \ - SENSOR_ROWS(31), SENSOR_ROWS(32), SENSOR_ROWS(33), SENSOR_ROWS(34), SENSOR_ROWS(35), SENSOR_ROWS(36), SENSOR_ROWS(37), SENSOR_ROWS(38), SENSOR_ROWS(39), SENSOR_ROWS(40),SENSOR_ROWS(41), SENSOR_ROWS(42), SENSOR_ROWS(43), SENSOR_ROWS(44), SENSOR_ROWS(45), SENSOR_ROWS(46), SENSOR_ROWS(47), SENSOR_ROWS(48), SENSOR_ROWS(49), SENSOR_ROWS(50),SENSOR_ROWS(51), SENSOR_ROWS(52), SENSOR_ROWS(53), SENSOR_ROWS(54), SENSOR_ROWS(55), SENSOR_ROWS(56), SENSOR_ROWS(57), SENSOR_ROWS(58), SENSOR_ROWS(59), 0, 0, 0, 0, -#if defined(VARIO) - #define VARIO_ROWS LABEL(Vario), 0, 1, 2, -#else - #define VARIO_ROWS -#endif -#define TELEMETRY_TYPE_ROWS (!IS_INTERNAL_MODULE_ENABLED() && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_PPM) ? (uint8_t)0 : HIDDEN_ROW, - -void onSensorMenu(const char * result) -{ - uint8_t index = menuVerticalPosition - ITEM_TELEMETRY_SENSOR_FIRST; - - if (index < MAX_TELEMETRY_SENSORS) { - if (result == STR_EDIT) { - pushMenu(menuModelSensor); - } - else if (result == STR_DELETE) { - delTelemetryIndex(index); - index += 1; - if (index= 0) { - TelemetrySensor & sourceSensor = g_model.telemetrySensors[index]; - TelemetrySensor & newSensor = g_model.telemetrySensors[newIndex]; - newSensor = sourceSensor; - TelemetryItem & sourceItem = telemetryItems[index]; - TelemetryItem & newItem = telemetryItems[newIndex]; - newItem = sourceItem; - storageDirty(EE_MODEL); - } - else { - POPUP_WARNING(STR_TELEMETRYFULL); - } - } - } -} - -void onDeleteAllSensorsConfirm(const char * result) -{ - if (result == STR_OK) { - for (int i=0; i0) ? BLINK|INVERS : INVERS); - LcdFlags attr = (menuVerticalPosition == k ? blink : 0); - - if (k >= ITEM_TELEMETRY_SENSOR_FIRST && k <= ITEM_TELEMETRY_SENSOR_LAST) { - int index = k - ITEM_TELEMETRY_SENSOR_FIRST; - lcdDrawNumber(MENUS_MARGIN_LEFT+INDENT_WIDTH, y, index+1, LEFT|attr, 0, NULL, ":"); - lcdDrawSizedText(60, y, g_model.telemetrySensors[index].label, TELEM_LABEL_LEN, ZCHAR); - TelemetryItem & telemetryItem = telemetryItems[index]; - if (telemetryItem.isFresh()) { - lcdDrawText(130, y, "*"); - } - if (telemetryItem.isAvailable()) { - LcdFlags color = telemetryItem.isOld() ? ALARM_COLOR : TEXT_COLOR; - drawSensorCustomValue(TELEM_COL2, y, index, getValue(MIXSRC_FIRST_TELEM+3*index), LEFT|color); - } - else { - lcdDrawText(TELEM_COL2, y, "---"); // TODO shortcut - } - TelemetrySensor * sensor = & g_model.telemetrySensors[index]; -#if defined(MULTIMODULE) - if (IS_SPEKTRUM_PROTOCOL()) { - // Spektrum does not (yet?) really support multiple sensor of the same type. But a lot of - // different sensor display the same information (e.g. voltage, capacity). Show the id - // of the sensor in the overview to ease figuring out what sensors belong together - lcdDrawHexNumber(TELEM_COL5, y, sensor->id, LEFT); - } else -#endif - if (sensor->type == TELEM_TYPE_CUSTOM && !g_model.ignoreSensorIds) { - lcdDrawNumber(TELEM_COL5, y, sensor->instance, LEFT); - } - if (attr) { - s_editMode = 0; - s_currIdx = index; - if (event == EVT_KEY_LONG(KEY_ENTER)) { - killEvents(event); - POPUP_MENU_ADD_ITEM(STR_EDIT); - POPUP_MENU_ADD_ITEM(STR_COPY); - POPUP_MENU_ADD_ITEM(STR_DELETE); - POPUP_MENU_START(onSensorMenu); - } - else if (event == EVT_KEY_BREAK(KEY_ENTER)) { - pushMenu(menuModelSensor); - } - } - } - else - - switch (k) { - case ITEM_TELEMETRY_PROTOCOL_TYPE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TELEMETRY_TYPE); - lcdDrawTextAtIndex(TELEM_COL2, y, STR_TELEMETRY_PROTOCOLS, g_model.telemetryProtocol, attr); - g_model.telemetryProtocol = checkIncDec(event, g_model.telemetryProtocol, PROTOCOL_TELEMETRY_FIRST, PROTOCOL_TELEMETRY_LAST, EE_MODEL, isTelemetryProtocolAvailable); - break; - - case ITEM_TELEMETRY_SENSORS_LABEL: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TELEMETRY_SENSORS); - lcdDrawText(TELEM_COL2, y, STR_VALUE, 0); - if (!g_model.ignoreSensorIds && !IS_SPEKTRUM_PROTOCOL()) { - lcdDrawText(TELEM_COL5, y, STR_ID, 0); - } - break; - - case ITEM_TELEMETRY_DISCOVER_SENSORS: - lcdDrawText(MENUS_MARGIN_LEFT+INDENT_WIDTH, y, allowNewSensors ? STR_STOP_DISCOVER_SENSORS : STR_DISCOVER_SENSORS, attr); - if (attr && event==EVT_KEY_BREAK(KEY_ENTER)) { - s_editMode = 0; - allowNewSensors = !allowNewSensors; - } - break; - - case ITEM_TELEMETRY_NEW_SENSOR: - lcdDrawText(MENUS_MARGIN_LEFT+INDENT_WIDTH, y, NO_INDENT(STR_TELEMETRY_NEWSENSOR), attr); - if (attr && event==EVT_KEY_BREAK(KEY_ENTER)) { - s_editMode = 0; - int res = availableTelemetryIndex(); - if (res >= 0) { - s_currIdx = res; - pushMenu(menuModelSensor); - } - else { - POPUP_WARNING(STR_TELEMETRYFULL); - } - } - break; - - case ITEM_TELEMETRY_DELETE_ALL_SENSORS: - lcdDrawText(MENUS_MARGIN_LEFT+INDENT_WIDTH, y, STR_DELETE_ALL_SENSORS, attr); - if (attr) - s_editMode = 0; - if (attr && event == EVT_KEY_BREAK(KEY_ENTER)) { - killEvents(KEY_ENTER); - POPUP_CONFIRMATION(STR_CONFIRMDELETE, onDeleteAllSensorsConfirm); - } - break; - - case ITEM_TELEMETRY_IGNORE_SENSOR_INSTANCE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_IGNORE_INSTANCE); - g_model.ignoreSensorIds = editCheckBox(g_model.ignoreSensorIds, TELEM_COL2, y, attr, event); - break; - - case ITEM_TELEMETRY_RSSI_LABEL: -#if defined(MULTIMODULE) - if (!IS_INTERNAL_MODULE_ON() && - g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_MULTIMODULE && - g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false) == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) - lcdDrawText(MENUS_MARGIN_LEFT, y, "RSNR"); - else -#endif - lcdDrawText(MENUS_MARGIN_LEFT, y, "RSSI"); - break; - - case ITEM_TELEMETRY_RSSI_SOURCE: { - lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_SOURCE); - drawSource(TELEM_COL2, y, g_model.rssiSource ? MIXSRC_FIRST_TELEM + 3 * (g_model.rssiSource - 1) : 0, attr); - if (g_model.rssiSource) { - TelemetrySensor * sensor = &g_model.telemetrySensors[g_model.rssiSource - 1]; - lcdDrawText(lcdNextPos, y, " ", attr); - drawReceiverName(lcdNextPos, y, sensor->frskyInstance.rxIndex >> 2, sensor->frskyInstance.rxIndex & 0x03, attr); - } - if (attr) { - g_model.rssiSource = checkIncDec(event, g_model.rssiSource, 0, MAX_TELEMETRY_SENSORS, EE_MODEL | NO_INCDEC_MARKS, isRssiSensorAvailable); - } - break; - } - - case ITEM_TELEMETRY_RSSI_ALARM1: - case ITEM_TELEMETRY_RSSI_ALARM2: { - bool warning = (k==ITEM_TELEMETRY_RSSI_ALARM1); - lcdDrawText(MENUS_MARGIN_LEFT, y, (warning ? STR_LOWALARM : STR_CRITICALALARM)); - lcdDrawNumber(TELEM_COL2, y, warning? g_model.rssiAlarms.getWarningRssi() : g_model.rssiAlarms.getCriticalRssi(), LEFT|attr, 3); - - if (attr && s_editMode>0) { - if (warning) - CHECK_INCDEC_MODELVAR(event, g_model.rssiAlarms.warning, -30, 30); - else - CHECK_INCDEC_MODELVAR(event, g_model.rssiAlarms.critical, -30, 30); - } - break; - } - case ITEM_TELEMETRY_DISABLE_ALARMS: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_DISABLE_ALARM); - g_model.rssiAlarms.disabled = editCheckBox(g_model.rssiAlarms.disabled, TELEM_COL3, y, attr, event); - break; -#if defined(VARIO) - case ITEM_TELEMETRY_VARIO_LABEL: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_VARIO); - break; - - case ITEM_TELEMETRY_VARIO_SOURCE: - lcdDrawText(MENUS_MARGIN_LEFT, y, INDENT TR_SOURCE); - drawSource(TELEM_COL2, y, g_model.varioData.source ? MIXSRC_FIRST_TELEM+3*(g_model.varioData.source-1) : 0, attr); - if (attr) { - g_model.varioData.source = checkIncDec(event, g_model.varioData.source, 0, MAX_TELEMETRY_SENSORS, EE_MODEL|NO_INCDEC_MARKS, isSensorAvailable); - } - break; - - case ITEM_TELEMETRY_VARIO_RANGE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_RANGE); - lcdDrawNumber(TELEM_COL2, y, -10+g_model.varioData.min, (menuHorizontalPosition==0 ? attr : 0)|LEFT); - lcdDrawNumber(TELEM_COL3, y, 10+g_model.varioData.max, (menuHorizontalPosition==1 ? attr : 0)|LEFT); - if (attr && s_editMode>0) { - switch (menuHorizontalPosition) { - case 0: - CHECK_INCDEC_MODELVAR(event, g_model.varioData.min, -7, 7); - break; - case 1: - CHECK_INCDEC_MODELVAR(event, g_model.varioData.max, -7, 7); - break; - } - } - break; - - case ITEM_TELEMETRY_VARIO_CENTER: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CENTER); - lcdDrawNumber(TELEM_COL2, y, -5+g_model.varioData.centerMin, (menuHorizontalPosition==0 ? attr : 0)|PREC1|LEFT); - lcdDrawNumber(TELEM_COL3, y, 5+g_model.varioData.centerMax, (menuHorizontalPosition==1 ? attr : 0)|PREC1|LEFT); - lcdDrawTextAtIndex(TELEM_COL4, y, STR_VVARIOCENTER, g_model.varioData.centerSilent, (menuHorizontalPosition==2 ? attr : 0)); - if (attr && s_editMode>0) { - switch (menuHorizontalPosition) { - case 0: - CHECK_INCDEC_MODELVAR(event, g_model.varioData.centerMin, -16, 5+min(10, g_model.varioData.centerMax+5)); - break; - case 1: - CHECK_INCDEC_MODELVAR(event, g_model.varioData.centerMax, -5+max(-10, g_model.varioData.centerMin-5), +15); - break; - case 2: - CHECK_INCDEC_MODELVAR_ZERO(event, g_model.varioData.centerSilent, 1); - break; - } - } - break; -#endif - } - } - return true; -} diff --git a/radio/src/gui/480x272/model_telemetry_sensor.cpp b/radio/src/gui/480x272/model_telemetry_sensor.cpp deleted file mode 100644 index f4d285efb..000000000 --- a/radio/src/gui/480x272/model_telemetry_sensor.cpp +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -enum SensorFields { - SENSOR_FIELD_NAME, - SENSOR_FIELD_TYPE, - SENSOR_FIELD_ID, - SENSOR_FIELD_FORMULA=SENSOR_FIELD_ID, - SENSOR_FIELD_UNIT, - SENSOR_FIELD_PRECISION, - SENSOR_FIELD_PARAM1, - SENSOR_FIELD_PARAM2, - SENSOR_FIELD_PARAM3, - SENSOR_FIELD_PARAM4, - SENSOR_FIELD_AUTOOFFSET, - SENSOR_FIELD_ONLYPOSITIVE, - SENSOR_FIELD_FILTER, - SENSOR_FIELD_PERSISTENT, - SENSOR_FIELD_LOGS, - SENSOR_FIELD_MAX -}; - -#define SENSOR_2ND_COLUMN 140 -#define SENSOR_3RD_COLUMN 280 - -#define SENSOR_UNIT_ROWS ((sensor->type == TELEM_TYPE_CALCULATED && (sensor->formula == TELEM_FORMULA_DIST)) || sensor->isConfigurable() ? (uint8_t)0 : HIDDEN_ROW) -#define SENSOR_PREC_ROWS (sensor->isPrecConfigurable() && sensor->unit != UNIT_FAHRENHEIT ? (uint8_t)0 : HIDDEN_ROW) -#define SENSOR_PARAM1_ROWS (sensor->unit >= UNIT_FIRST_VIRTUAL ? HIDDEN_ROW : (uint8_t)0) -#define SENSOR_PARAM2_ROWS (sensor->unit == UNIT_GPS || sensor->unit == UNIT_DATETIME || sensor->unit == UNIT_CELLS || (sensor->type==TELEM_TYPE_CALCULATED && (sensor->formula==TELEM_FORMULA_CONSUMPTION || sensor->formula==TELEM_FORMULA_TOTALIZE)) ? HIDDEN_ROW : (uint8_t)0) -#define SENSOR_PARAM3_ROWS (sensor->type == TELEM_TYPE_CALCULATED && sensor->formula < TELEM_FORMULA_MULTIPLY) ? (uint8_t)0 : HIDDEN_ROW -#define SENSOR_PARAM4_ROWS (sensor->type == TELEM_TYPE_CALCULATED && sensor->formula < TELEM_FORMULA_MULTIPLY) ? (uint8_t)0 : HIDDEN_ROW -#define SENSOR_AUTOOFFSET_ROWS (sensor->unit != UNIT_RPMS && sensor->isConfigurable() ? (uint8_t)0 : HIDDEN_ROW) -#define SENSOR_ONLYPOS_ROWS (sensor->isConfigurable() ? (uint8_t)0 : HIDDEN_ROW) -#define SENSOR_FILTER_ROWS (sensor->isConfigurable() ? (uint8_t)0 : HIDDEN_ROW) -#define SENSOR_PERSISTENT_ROWS (sensor->type == TELEM_TYPE_CALCULATED ? (uint8_t)0 : HIDDEN_ROW) - -bool menuModelSensor(event_t event) -{ - TelemetrySensor * sensor = &g_model.telemetrySensors[s_currIdx]; - - SUBMENU("SENSOR", ICON_MODEL_TELEMETRY, SENSOR_FIELD_MAX, { 0, 0, sensor->type == TELEM_TYPE_CALCULATED ? (uint8_t)0 : (uint8_t)1, SENSOR_UNIT_ROWS, SENSOR_PREC_ROWS, SENSOR_PARAM1_ROWS, SENSOR_PARAM2_ROWS, SENSOR_PARAM3_ROWS, SENSOR_PARAM4_ROWS, SENSOR_AUTOOFFSET_ROWS, SENSOR_ONLYPOS_ROWS, SENSOR_FILTER_ROWS, SENSOR_PERSISTENT_ROWS, 0 }); - lcdDrawNumber(lcdNextPos, 3, s_currIdx+1, MENU_TITLE_COLOR|LEFT); - drawSensorCustomValue(50, 3 + FH, s_currIdx, getValue(MIXSRC_FIRST_TELEM+3*s_currIdx), MENU_TITLE_COLOR|LEFT); - - for (uint8_t i=0; i= (int)DIM(mstate_tab)) { - return true; - } - } - } - - LcdFlags attr = (menuVerticalPosition==k ? (s_editMode>0 ? BLINK|INVERS : INVERS) : 0); - - switch (k) { - case SENSOR_FIELD_NAME: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_NAME); - editName(SENSOR_2ND_COLUMN, y, sensor->label, TELEM_LABEL_LEN, event, attr); - break; - - case SENSOR_FIELD_TYPE: - lcdDrawText(MENUS_MARGIN_LEFT, y, NO_INDENT(STR_TYPE)); - sensor->type = editChoice(SENSOR_2ND_COLUMN, y, STR_VSENSORTYPES, sensor->type, 0, 1, attr, event); - if (attr && checkIncDec_Ret) { - sensor->instance = 0; - if (sensor->type == TELEM_TYPE_CALCULATED) { - sensor->param = 0; - sensor->filter = 0; - sensor->autoOffset = 0; - } - } - break; - - case SENSOR_FIELD_ID: - if (sensor->type == TELEM_TYPE_CUSTOM) { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ID); - lcdDrawHexNumber(SENSOR_2ND_COLUMN, y, sensor->id, LEFT|(menuHorizontalPosition==0 ? attr : 0)); - lcdDrawNumber(SENSOR_3RD_COLUMN, y, sensor->instance, LEFT|(menuHorizontalPosition==1 ? attr : 0)); - if (attr) { - switch (menuHorizontalPosition) { - case 0: - sensor->id = checkIncDec(event, sensor->id, 0x0000, 0xffff, INCDEC_REP10|NO_INCDEC_MARKS); - break; - - case 1: - CHECK_INCDEC_MODELVAR_ZERO(event, sensor->instance, 0xff); - break; - } - } - } - else { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_FORMULA); - sensor->formula = editChoice(SENSOR_2ND_COLUMN, y, STR_VFORMULAS, sensor->formula, 0, TELEM_FORMULA_LAST, attr, event); - if (attr && checkIncDec_Ret) { - sensor->param = 0; - if (sensor->formula == TELEM_FORMULA_CELL) { - sensor->unit = UNIT_VOLTS; - sensor->prec = 2; - } - else if (sensor->formula == TELEM_FORMULA_DIST) { - sensor->unit = UNIT_DIST; - sensor->prec = 0; - } - else if (sensor->formula == TELEM_FORMULA_CONSUMPTION) { - sensor->unit = UNIT_MAH; - sensor->prec = 0; - } - } - } - break; - - case SENSOR_FIELD_UNIT: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_UNIT); - lcdDrawTextAtIndex(SENSOR_2ND_COLUMN, y, STR_VTELEMUNIT, sensor->unit, attr); - if (attr) { - CHECK_INCDEC_MODELVAR_ZERO(event, sensor->unit, UNIT_MAX); - if (checkIncDec_Ret) { - if (sensor->unit == UNIT_FAHRENHEIT) { - sensor->prec = 0; - } - telemetryItems[s_currIdx].clear(); - } - } - break; - - case SENSOR_FIELD_PRECISION: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PRECISION); - sensor->prec = editChoice(SENSOR_2ND_COLUMN, y, STR_VPREC, sensor->prec, 0, 2, attr, event); - if (attr && checkIncDec_Ret) { - telemetryItems[s_currIdx].clear(); - } - break; - - case SENSOR_FIELD_PARAM1: - if (sensor->type == TELEM_TYPE_CALCULATED) { - if (sensor->formula == TELEM_FORMULA_CELL) { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CELLSENSOR); - drawSource(SENSOR_2ND_COLUMN, y, sensor->cell.source ? MIXSRC_FIRST_TELEM+3*(sensor->cell.source-1) : 0, attr); - if (attr) { - sensor->cell.source = checkIncDec(event, sensor->cell.source, 0, MAX_TELEMETRY_SENSORS, EE_MODEL|NO_INCDEC_MARKS, isCellsSensor); - } - break; - } - else if (sensor->formula == TELEM_FORMULA_DIST) { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_GPSSENSOR); - drawSource(SENSOR_2ND_COLUMN, y, sensor->dist.gps ? MIXSRC_FIRST_TELEM+3*(sensor->dist.gps-1) : 0, attr); - if (attr) { - sensor->dist.gps = checkIncDec(event, sensor->dist.gps, 0, MAX_TELEMETRY_SENSORS, EE_MODEL|NO_INCDEC_MARKS, isGPSSensor); - } - break; - } - else if (sensor->formula == TELEM_FORMULA_CONSUMPTION) { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CURRENTSENSOR); - drawSource(SENSOR_2ND_COLUMN, y, sensor->consumption.source ? MIXSRC_FIRST_TELEM+3*(sensor->consumption.source-1) : 0, attr); - if (attr) { - sensor->consumption.source = checkIncDec(event, sensor->consumption.source, 0, MAX_TELEMETRY_SENSORS, EE_MODEL|NO_INCDEC_MARKS, isSensorAvailable); - } - break; - } - else if (sensor->formula == TELEM_FORMULA_TOTALIZE) { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SOURCE); - drawSource(SENSOR_2ND_COLUMN, y, sensor->consumption.source ? MIXSRC_FIRST_TELEM+3*(sensor->consumption.source-1) : 0, attr); - if (attr) { - sensor->consumption.source = checkIncDec(event, sensor->consumption.source, 0, MAX_TELEMETRY_SENSORS, EE_MODEL|NO_INCDEC_MARKS, isSensorAvailable); - } - break; - } - } - else { - if (sensor->unit == UNIT_RPMS) { - lcdDrawText(MENUS_MARGIN_LEFT, y, NO_INDENT(STR_BLADES)); - if (attr) sensor->custom.ratio = checkIncDec(event, sensor->custom.ratio, 1, 30000, EE_MODEL|NO_INCDEC_MARKS|INCDEC_REP10); - lcdDrawNumber(SENSOR_2ND_COLUMN, y, sensor->custom.ratio, LEFT|attr); - break; - } - else { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_RATIO); - if (attr) sensor->custom.ratio = checkIncDec(event, sensor->custom.ratio, 0, 30000, EE_MODEL|NO_INCDEC_MARKS|INCDEC_REP10); - if (sensor->custom.ratio == 0) - lcdDrawText(SENSOR_2ND_COLUMN, y, "-", attr); - else - lcdDrawNumber(SENSOR_2ND_COLUMN, y, sensor->custom.ratio, LEFT|attr|PREC1); - break; - } - } - // no break - - case SENSOR_FIELD_PARAM2: - if (sensor->type == TELEM_TYPE_CALCULATED) { - if (sensor->formula == TELEM_FORMULA_CELL) { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CELLINDEX); - sensor->cell.index = editChoice(SENSOR_2ND_COLUMN, y, STR_VCELLINDEX, sensor->cell.index, 0, 8, attr, event); - break; - } - else if (sensor->formula == TELEM_FORMULA_DIST) { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ALTSENSOR); - drawSource(SENSOR_2ND_COLUMN, y, sensor->dist.alt ? MIXSRC_FIRST_TELEM+3*(sensor->dist.alt-1) : 0, attr); - if (attr) { - sensor->dist.alt = checkIncDec(event, sensor->dist.alt, 0, MAX_TELEMETRY_SENSORS, EE_MODEL|NO_INCDEC_MARKS, isAltSensor); - } - break; - } - } - else if (sensor->unit == UNIT_RPMS) { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MULTIPLIER); - if (attr) sensor->custom.offset = checkIncDec(event, sensor->custom.offset, 1, 30000, EE_MODEL|NO_INCDEC_MARKS|INCDEC_REP10); - lcdDrawNumber(SENSOR_2ND_COLUMN, y, sensor->custom.offset, LEFT|attr); - break; - } - else { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_OFFSET); - if (attr) sensor->custom.offset = checkIncDec(event, sensor->custom.offset, -30000, +30000, EE_MODEL|NO_INCDEC_MARKS|INCDEC_REP10); - if (sensor->prec > 0) attr |= (sensor->prec == 2 ? PREC2 : PREC1); - lcdDrawNumber(SENSOR_2ND_COLUMN, y, sensor->custom.offset, LEFT|attr); - break; - } - // no break - - case SENSOR_FIELD_PARAM3: - // no break - - case SENSOR_FIELD_PARAM4: - { - drawStringWithIndex(MENUS_MARGIN_LEFT, y, STR_SOURCE, k-SENSOR_FIELD_PARAM1+1); - int8_t * source = &sensor->calc.sources[k-SENSOR_FIELD_PARAM1]; - if (attr) { - *source = checkIncDec(event, *source, -MAX_TELEMETRY_SENSORS, MAX_TELEMETRY_SENSORS, EE_MODEL|NO_INCDEC_MARKS, isSensorAvailable); - } - if (*source < 0) { - lcdDrawText(SENSOR_2ND_COLUMN, y, "-", attr); - drawSource(SENSOR_2ND_COLUMN+5, y, MIXSRC_FIRST_TELEM+3*(-1-*source), attr); - } - else { - drawSource(SENSOR_2ND_COLUMN, y, *source ? MIXSRC_FIRST_TELEM+3*(*source-1) : 0, attr); - } - break; - } - - case SENSOR_FIELD_AUTOOFFSET: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_AUTOOFFSET); - sensor->autoOffset = editCheckBox(sensor->autoOffset, SENSOR_2ND_COLUMN, y, attr, event); - break; - - case SENSOR_FIELD_ONLYPOSITIVE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ONLYPOSITIVE); - sensor->onlyPositive = editCheckBox(sensor->onlyPositive, SENSOR_2ND_COLUMN, y, attr, event); - break; - - case SENSOR_FIELD_FILTER: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_FILTER); - sensor->filter = editCheckBox(sensor->filter, SENSOR_2ND_COLUMN, y, attr, event); - break; - - case SENSOR_FIELD_PERSISTENT: - lcdDrawText(MENUS_MARGIN_LEFT, y, NO_INDENT(STR_PERSISTENT)); - sensor->persistent = editCheckBox(sensor->persistent, SENSOR_2ND_COLUMN, y, attr, event); - if (checkIncDec_Ret && !sensor->persistent) { - sensor->persistentValue = 0; - } - break; - - case SENSOR_FIELD_LOGS: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_LOGS); - sensor->logs = editCheckBox(sensor->logs, SENSOR_2ND_COLUMN, y, attr, event); - if (attr && checkIncDec_Ret) { - logsClose(); - } - break; - } - } - return true; -} diff --git a/radio/src/gui/480x272/popups.cpp b/radio/src/gui/480x272/popups.cpp deleted file mode 100644 index 25a28779e..000000000 --- a/radio/src/gui/480x272/popups.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -const char *warningText = nullptr; -const char *warningInfoText; -uint8_t warningInfoLength; -uint8_t warningType; -uint8_t warningResult = 0; -uint8_t warningInfoFlags = ZCHAR; -PopupFunc popupFunc = nullptr; -const char *popupMenuItems[POPUP_MENU_MAX_LINES]; -uint8_t popupMenuSelectedItem = 0; -uint16_t popupMenuItemsCount = 0; -uint16_t popupMenuOffset = 0; -uint8_t popupMenuOffsetType = MENU_OFFSET_INTERNAL; -void (*popupMenuHandler)(const char * result); - -void drawAlertBox(const char * title, const char * text, const char * action) -{ - theme->drawMessageBox(title, text, action, WARNING_TYPE_ALERT); -} - -void showAlertBox(const char * title, const char * text, const char * action, uint8_t sound) -{ - drawAlertBox(title, text, action); - AUDIO_ERROR_MESSAGE(sound); - lcdRefresh(); - lcdSetContrast(); - waitKeysReleased(); - backlightOn(); - checkBacklight(); -} - -void showMessageBox(const char * title) -{ - // drawMessageBox(); - lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y, title, WARNING_LINE_LEN, DBLSIZE|WARNING_COLOR); - lcdRefresh(); -} - -void drawPopupBackgroundAndBorder(coord_t x, coord_t y, coord_t w, coord_t h) -{ - lcdDrawSolidFilledRect(x + 1, y + 1, w - 2, h - 2, TEXT_BGCOLOR); - lcdDrawSolidRect(x, y, w, h, 1, ALARM_COLOR); -} - -void runPopupWarning(event_t event) -{ - warningResult = false; - - const char * action; - switch (warningType) { - case WARNING_TYPE_INFO: - action = STR_OK; - break; - case WARNING_TYPE_ASTERISK: - case WARNING_TYPE_WAIT: - action = STR_EXIT; - break; - default: - action = STR_POPUPS_ENTER_EXIT; - break; - } - - theme->drawMessageBox(warningText, warningInfoText, action, warningType); - - switch (event) { - case EVT_KEY_BREAK(KEY_ENTER): - if (warningType == WARNING_TYPE_ASTERISK) - // key ignored, the user has to press [EXIT] - break; - - if (warningType == WARNING_TYPE_CONFIRM) { - warningType = WARNING_TYPE_ASTERISK; - warningText = nullptr; - if (popupMenuHandler) - popupMenuHandler(STR_OK); - else - warningResult = true; - break; - } - // no break - - case EVT_KEY_BREAK(KEY_EXIT): - if (warningType == WARNING_TYPE_CONFIRM) { - if (popupMenuHandler) - popupMenuHandler(STR_EXIT); - } - warningText = nullptr; - warningType = WARNING_TYPE_ASTERISK; - break; - } -} - -const char * runPopupMenu(event_t event) -{ - const char * result = nullptr; - - uint8_t display_count = min(popupMenuItemsCount, MENU_MAX_DISPLAY_LINES); - - switch (event) { - case EVT_ROTARY_LEFT: - if (popupMenuSelectedItem > 0) { - popupMenuSelectedItem--; - } - else if (popupMenuOffset > 0) { - popupMenuOffset--; - result = STR_UPDATE_LIST; - } - else { - popupMenuSelectedItem = min(display_count, MENU_MAX_DISPLAY_LINES) - 1; - if (popupMenuItemsCount > MENU_MAX_DISPLAY_LINES) { - popupMenuOffset = popupMenuItemsCount - MENU_MAX_DISPLAY_LINES; - result = STR_UPDATE_LIST; - } - } - break; - - case EVT_ROTARY_RIGHT: - if (popupMenuSelectedItem < display_count - 1 && popupMenuOffset + popupMenuSelectedItem + 1 < popupMenuItemsCount) { - popupMenuSelectedItem++; - } - else if (popupMenuItemsCount > popupMenuOffset + display_count) { - popupMenuOffset++; - result = STR_UPDATE_LIST; - } - else { - popupMenuSelectedItem = 0; - if (popupMenuOffset) { - popupMenuOffset = 0; - result = STR_UPDATE_LIST; - } - } - break; - - case EVT_KEY_BREAK(KEY_ENTER): - result = popupMenuItems[popupMenuSelectedItem + (popupMenuOffsetType == MENU_OFFSET_INTERNAL ? popupMenuOffset : 0)]; - popupMenuItemsCount = 0; - popupMenuSelectedItem = 0; - popupMenuOffset = 0; - break; - - case EVT_KEY_BREAK(KEY_EXIT): - result = STR_EXIT; - popupMenuItemsCount = 0; - popupMenuSelectedItem = 0; - popupMenuOffset = 0; - break; - } - - int y = (LCD_H - (display_count*(FH+1))) / 2; - - drawPopupBackgroundAndBorder(MENU_X, y, MENU_W, display_count * (FH+1) + 2); - - for (uint8_t i=0; i display_count) { - drawVerticalScrollbar(MENU_X+MENU_W-1, y+1, MENU_MAX_DISPLAY_LINES * (FH+1), popupMenuOffset, popupMenuItemsCount, MENU_MAX_DISPLAY_LINES); - } - - return result; -} diff --git a/radio/src/gui/480x272/radio_calibration.cpp b/radio/src/gui/480x272/radio_calibration.cpp deleted file mode 100644 index a81355b89..000000000 --- a/radio/src/gui/480x272/radio_calibration.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -#define XPOT_DELTA 10 -#define XPOT_DELAY 5 /* cycles */ -#define STICKS_WIDTH 90 -#define STICKS_Y 60 -#define STICK_LEFT_X 25 -#define STICK_RIGHT_X (LCD_W-STICK_LEFT_X-STICKS_WIDTH) -#define STICK_PANEL_WIDTH 68 - -void drawStick(coord_t x, coord_t y, const BitmapBuffer * background, int16_t xval, int16_t yval) -{ - lcd->drawBitmap(x, y, calibStickBackground); - lcd->drawBitmap(x + 2 + STICK_PANEL_WIDTH/2 + STICK_PANEL_WIDTH/2 * xval/RESX, y + 2 + STICK_PANEL_WIDTH/2 - STICK_PANEL_WIDTH/2 * yval/RESX, calibStick); -} - -void drawSticks() -{ - int16_t calibStickVert = calibratedAnalogs[CONVERT_MODE(1)]; - if (g_model.throttleReversed && CONVERT_MODE(1) == THR_STICK) - calibStickVert = -calibStickVert; - drawStick(STICK_LEFT_X, STICKS_Y, calibStickBackground, calibratedAnalogs[CONVERT_MODE(0)], calibStickVert); - - calibStickVert = calibratedAnalogs[CONVERT_MODE(2)]; - if (g_model.throttleReversed && CONVERT_MODE(2) == THR_STICK) - calibStickVert = -calibStickVert; - drawStick(STICK_RIGHT_X, STICKS_Y, calibStickBackground, calibratedAnalogs[CONVERT_MODE(3)], calibStickVert); -} - -void drawPots() -{ - // The pots which are displayed in the main view - extern void drawMainPots(); - drawMainPots(); - -#if defined(PCBX12S) - // The 2 main front sliders - drawVerticalSlider(125, 120, 120, calibratedAnalogs[CALIBRATED_SLIDER_FRONT_LEFT], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); - drawVerticalSlider(LCD_W-125-12, 120, 120, calibratedAnalogs[CALIBRATED_SLIDER_FRONT_RIGHT], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); -#endif -} - -#if defined(PCBX12S) -void drawMouse() -{ - drawStick(STICK_LEFT_X, STICKS_Y+100, calibTrackpBackground, calibratedAnalogs[CALIBRATED_MOUSE1], calibratedAnalogs[CALIBRATED_MOUSE2]); -} -#endif - -bool menuCommonCalib(event_t event) -{ - drawMenuTemplate(NULL, ICON_RADIO_CALIBRATION, NULL, OPTION_MENU_NO_FOOTER); - - for (uint8_t i=0; i= POT1 && i <= POT_LAST) { - if (IS_POT_WITHOUT_DETENT(i)) { - reusableBuffer.calib.midVals[i] = (reusableBuffer.calib.hiVals[i] + reusableBuffer.calib.loVals[i]) / 2; - } - uint8_t idx = i - POT1; - int count = reusableBuffer.calib.xpotsCalib[idx].stepsCount; - if (IS_POT_MULTIPOS(i) && count <= XPOTS_MULTIPOS_COUNT) { - // use raw analog value for multipos calibraton, anaIn() already has multipos decoded value - vt = getAnalogValue(i) >> 1; - if (reusableBuffer.calib.xpotsCalib[idx].lastCount == 0 || vt < reusableBuffer.calib.xpotsCalib[idx].lastPosition - XPOT_DELTA || vt > reusableBuffer.calib.xpotsCalib[idx].lastPosition + XPOT_DELTA) { - reusableBuffer.calib.xpotsCalib[idx].lastPosition = vt; - reusableBuffer.calib.xpotsCalib[idx].lastCount = 1; - } - else { - if (reusableBuffer.calib.xpotsCalib[idx].lastCount < 255) { - reusableBuffer.calib.xpotsCalib[idx].lastCount++; - } - } - if (reusableBuffer.calib.xpotsCalib[idx].lastCount == XPOT_DELAY) { - int16_t position = reusableBuffer.calib.xpotsCalib[idx].lastPosition; - bool found = false; - for (int j=0; j= step-XPOT_DELTA && position <= step+XPOT_DELTA) { - found = true; - break; - } - } - if (!found) { - if (count < XPOTS_MULTIPOS_COUNT) { - reusableBuffer.calib.xpotsCalib[idx].steps[count] = position; - } - reusableBuffer.calib.xpotsCalib[idx].stepsCount += 1; - } - } - } - } - } - - switch (event) { - case EVT_ENTRY: - case EVT_KEY_FIRST(KEY_EXIT): - menuCalibrationState = CALIB_START; - break; - - case EVT_KEY_FIRST(KEY_ENTER): - killEvents(event); - menuCalibrationState++; - break; - } - - switch (menuCalibrationState) { - case CALIB_START: - // START CALIBRATION - if (!READ_ONLY()) { - lcdDrawText(50, 3, STR_MENUCALIBRATION, MENU_TITLE_COLOR); - lcdDrawText(50, 3+FH, STR_MENUTOSTART, MENU_TITLE_COLOR); - } - break; - - case CALIB_SET_MIDPOINT: - // SET MIDPOINT - lcdDrawText(50, 3, STR_MENUCALIBRATION, MENU_TITLE_COLOR); - lcdDrawText(50, 3+FH, STR_SETMIDPOINT, MENU_TITLE_COLOR); - for (uint8_t i=0; i 50) { - g_eeGeneral.calib[i].mid = reusableBuffer.calib.midVals[i]; - int16_t v = reusableBuffer.calib.midVals[i] - reusableBuffer.calib.loVals[i]; - g_eeGeneral.calib[i].spanNeg = v - v/STICK_TOLERANCE; - v = reusableBuffer.calib.hiVals[i] - reusableBuffer.calib.midVals[i]; - g_eeGeneral.calib[i].spanPos = v - v/STICK_TOLERANCE; - } - } - for (int i=POT1; i<=POT_LAST; i++) { - int idx = i - POT1; - int count = reusableBuffer.calib.xpotsCalib[idx].stepsCount; - if (IS_POT_MULTIPOS(i)) { - if (count > 1 && count <= XPOTS_MULTIPOS_COUNT) { - for (int j=0; jcount = count - 1; - for (int j=0; jcount; j++) { - calib->steps[j] = (reusableBuffer.calib.xpotsCalib[idx].steps[j+1] + reusableBuffer.calib.xpotsCalib[idx].steps[j]) >> 5; - } - } - else { - // g_eeGeneral.potsConfig &= ~(0x03<<(2*idx)); - } - } - } - break; - - case CALIB_STORE: - g_eeGeneral.chkSum = evalChkSum(); - storageDirty(EE_GENERAL); - menuCalibrationState = CALIB_FINISHED; - break; - - default: - menuCalibrationState = CALIB_START; - break; - } - - - if (calibHorus) { - lcd->drawBitmap((LCD_W-calibHorus->getWidth())/2, LCD_H-20-calibHorus->getHeight(), calibHorus); - } - - drawSticks(); - drawPots(); - -#if defined(PCBX12S) - drawMouse(); -#endif - - return true; -} - -bool menuRadioCalibration(event_t event) -{ - if (event == EVT_ENTRY || event == EVT_ENTRY_UP) TRACE("Menu %s displayed ...", STR_MENUCALIBRATION); - if (menuCalibrationState == CALIB_FINISHED || (menuCalibrationState == CALIB_START && event == EVT_KEY_FIRST(KEY_EXIT))) { - menuCalibrationState = CALIB_START; - killEvents(event); - popMenu(); - return false; - } - if (!check_submenu_simple(event, 0)) { - return false; - } - - menuVerticalPosition = -1; - - return menuCommonCalib(READ_ONLY() ? 0 : event); -} - -bool menuFirstCalib(event_t event) -{ - if (event == EVT_KEY_FIRST(KEY_EXIT) || menuCalibrationState == CALIB_FINISHED) { - menuCalibrationState = CALIB_START; - chainMenu(menuMainView); - return false; - } - else { - return menuCommonCalib(event); - } -} diff --git a/radio/src/gui/480x272/radio_hardware.cpp b/radio/src/gui/480x272/radio_hardware.cpp deleted file mode 100644 index be5fb230e..000000000 --- a/radio/src/gui/480x272/radio_hardware.cpp +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -enum MenuRadioHardwareItems { - ITEM_RADIO_HARDWARE_CALIBRATION, - ITEM_RADIO_HARDWARE_LABEL_STICKS, - ITEM_RADIO_HARDWARE_STICK1, - ITEM_RADIO_HARDWARE_STICK2, - ITEM_RADIO_HARDWARE_STICK3, - ITEM_RADIO_HARDWARE_STICK4, - ITEM_RADIO_HARDWARE_LABEL_POTS, - ITEM_RADIO_HARDWARE_POT1, - ITEM_RADIO_HARDWARE_POT2, - ITEM_RADIO_HARDWARE_POT3, -#if defined(PCBX10) - ITEM_RADIO_HARDWARE_EXT1, - ITEM_RADIO_HARDWARE_EXT2, -#endif - ITEM_RADIO_HARDWARE_LS, - ITEM_RADIO_HARDWARE_RS, -#if defined(PCBX12S) - ITEM_RADIO_HARDWARE_LS2, - ITEM_RADIO_HARDWARE_RS2, -#endif - ITEM_RADIO_HARDWARE_LABEL_SWITCHES, - ITEM_RADIO_HARDWARE_SA, - ITEM_RADIO_HARDWARE_SB, - ITEM_RADIO_HARDWARE_SC, - ITEM_RADIO_HARDWARE_SD, - ITEM_RADIO_HARDWARE_SE, - ITEM_RADIO_HARDWARE_SF, - ITEM_RADIO_HARDWARE_SG, - ITEM_RADIO_HARDWARE_SH, - ITEM_RADIO_HARDWARE_SI, // Gimbal switch left - ITEM_RADIO_HARDWARE_SJ, // Gimbal switch right - ITEM_RADIO_HARDWARE_BATTERY_CALIB, - ITEM_RADIO_HARDWARE_RTC_BATTERY, - ITEM_RADIO_HARDWARE_SERIAL_BAUDRATE, - ITEM_RADIO_HARDWARE_BLUETOOTH_MODE, - ITEM_RADIO_HARDWARE_BLUETOOTH_PAIRING_CODE, - ITEM_RADIO_HARDWARE_BLUETOOTH_NAME, -#if defined(AUX_SERIAL) - ITEM_RADIO_HARDWARE_AUX_SERIAL_MODE, -#endif - ITEM_RADIO_HARDWARE_JITTER_FILTER, - ITEM_RADIO_HARDWARE_MAX -}; - -#define HW_SETTINGS_COLUMN 150 - - -#define POTS_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 -#define SWITCHES_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1 - -#define BLUETOOTH_ROWS 0, uint8_t(g_eeGeneral.bluetoothMode != BLUETOOTH_TELEMETRY ? HIDDEN_ROW : -1), uint8_t(g_eeGeneral.bluetoothMode == BLUETOOTH_OFF ? -1 : 0) - -// TODO should be moved to the HAL -#define SWITCH_TYPE_MAX(sw) ((MIXSRC_SF-MIXSRC_FIRST_SWITCH == sw || MIXSRC_SH-MIXSRC_FIRST_SWITCH == sw || MIXSRC_SI-MIXSRC_FIRST_SWITCH == sw || MIXSRC_SJ-MIXSRC_FIRST_SWITCH == sw) ? SWITCH_2POS : SWITCH_3POS) - -bool menuRadioHardware(event_t event) -{ - MENU(STR_HARDWARE, RADIO_ICONS, menuTabGeneral, MENU_RADIO_HARDWARE, ITEM_RADIO_HARDWARE_MAX, { - 0 /* calibration button */, - - LABEL(Sticks), - 0 /* stick 1 */, - 0 /* stick 2 */, - 0 /* stick 3 */, - 0 /* stick 4 */, - - LABEL(Pots), - POTS_ROWS, - - LABEL(Switches), - SWITCHES_ROWS, - - 0, /* battery */ - READONLY_ROW, /* RTC */ - - 0, /* max baudrate */ - - BLUETOOTH_ROWS, - - 0, /* aux serial mode */ - 0, /* ADC filter */ - }); - - uint8_t sub = menuVerticalPosition; - - for (int i=0; i0) ? BLINK|INVERS : INVERS) : 0); - switch (k) { - case ITEM_RADIO_HARDWARE_CALIBRATION: - lcdDrawText(MENUS_MARGIN_LEFT, y, "Calibration", attr); - if (attr && s_editMode>0) { - pushMenu(menuRadioCalibration); - } - break; - - case ITEM_RADIO_HARDWARE_LABEL_STICKS: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_STICKS); - break; - - case ITEM_RADIO_HARDWARE_STICK1: - case ITEM_RADIO_HARDWARE_STICK2: - case ITEM_RADIO_HARDWARE_STICK3: - case ITEM_RADIO_HARDWARE_STICK4: - editStickHardwareSettings(HW_SETTINGS_COLUMN, y, k - ITEM_RADIO_HARDWARE_STICK1, event, attr); - break; - - case ITEM_RADIO_HARDWARE_LS: - case ITEM_RADIO_HARDWARE_RS: -#if defined(PCBX12S) - case ITEM_RADIO_HARDWARE_LS2: - case ITEM_RADIO_HARDWARE_RS2: -#endif - { - int idx = k - ITEM_RADIO_HARDWARE_LS; - uint8_t mask = (0x01 << idx); - lcdDrawTextAtIndex(INDENT_WIDTH, y, STR_VSRCRAW, NUM_STICKS+NUM_POTS+idx+1, menuHorizontalPosition < 0 ? attr : 0); - if (ZEXIST(g_eeGeneral.anaNames[NUM_STICKS+NUM_POTS+idx]) || (attr && menuHorizontalPosition == 0)) - editName(HW_SETTINGS_COLUMN, y, g_eeGeneral.anaNames[NUM_STICKS+NUM_POTS+idx], LEN_ANA_NAME, event, attr && menuHorizontalPosition == 0); - else - lcdDrawMMM(HW_SETTINGS_COLUMN, y, 0); - uint8_t potType = (g_eeGeneral.slidersConfig & mask) >> idx; - potType = editChoice(HW_SETTINGS_COLUMN+50, y, STR_SLIDERTYPES, potType, SLIDER_NONE, SLIDER_WITH_DETENT, menuHorizontalPosition == 1 ? attr : 0, event); - g_eeGeneral.slidersConfig &= ~mask; - g_eeGeneral.slidersConfig |= (potType << idx); - break; - } - - case ITEM_RADIO_HARDWARE_LABEL_POTS: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_POTS); - break; - - case ITEM_RADIO_HARDWARE_POT1: - case ITEM_RADIO_HARDWARE_POT2: - case ITEM_RADIO_HARDWARE_POT3: -#if defined(PCBX10) - case ITEM_RADIO_HARDWARE_EXT1: - case ITEM_RADIO_HARDWARE_EXT2: -#endif - { - int index = k - ITEM_RADIO_HARDWARE_POT1; - lcdDrawTextAtIndex(INDENT_WIDTH, y, STR_VSRCRAW, NUM_STICKS+index+1, menuHorizontalPosition < 0 ? attr : 0); - if (ZEXIST(g_eeGeneral.anaNames[NUM_STICKS+index]) || (attr && menuHorizontalPosition == 0)) - editName(HW_SETTINGS_COLUMN, y, g_eeGeneral.anaNames[NUM_STICKS+index], LEN_ANA_NAME, event, attr && menuHorizontalPosition == 0); - else - lcdDrawMMM(HW_SETTINGS_COLUMN, y, 0); - uint32_t potType = bfGet(g_eeGeneral.potsConfig, 2*(index), 2); - potType = editChoice(HW_SETTINGS_COLUMN+50, y, STR_POTTYPES, potType, POT_NONE, POT_WITHOUT_DETENT, menuHorizontalPosition == 1 ? attr : 0, event); - g_eeGeneral.potsConfig = bfSet(g_eeGeneral.potsConfig, potType, 2*index, 2); - break; - } - - case ITEM_RADIO_HARDWARE_LABEL_SWITCHES: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SWITCHES); - break; - - case ITEM_RADIO_HARDWARE_SA: - case ITEM_RADIO_HARDWARE_SB: - case ITEM_RADIO_HARDWARE_SC: - case ITEM_RADIO_HARDWARE_SD: - case ITEM_RADIO_HARDWARE_SE: - case ITEM_RADIO_HARDWARE_SF: - case ITEM_RADIO_HARDWARE_SG: - case ITEM_RADIO_HARDWARE_SH: - case ITEM_RADIO_HARDWARE_SI: - case ITEM_RADIO_HARDWARE_SJ: - { - int index = k - ITEM_RADIO_HARDWARE_SA; - int config = SWITCH_CONFIG(index); - lcdDrawTextAtIndex(INDENT_WIDTH, y, STR_VSRCRAW, MIXSRC_FIRST_SWITCH-MIXSRC_Rud+index+1, menuHorizontalPosition < 0 ? attr : 0); - if (ZEXIST(g_eeGeneral.switchNames[index]) || (attr && menuHorizontalPosition == 0)) - editName(HW_SETTINGS_COLUMN, y, g_eeGeneral.switchNames[index], LEN_SWITCH_NAME, event, menuHorizontalPosition == 0 ? attr : 0); - else - lcdDrawMMM(HW_SETTINGS_COLUMN, y, 0); - config = editChoice(HW_SETTINGS_COLUMN+50, y, STR_SWTYPES, config, SWITCH_NONE, SWITCH_TYPE_MAX(index), menuHorizontalPosition == 1 ? attr : 0, event); - if (attr && checkIncDec_Ret) { - g_eeGeneral.switchConfig = bfSet(g_eeGeneral.switchConfig, config, 2*index, 2); - } - break; - } - - case ITEM_RADIO_HARDWARE_BATTERY_CALIB: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BATT_CALIB); - lcdDrawNumber(HW_SETTINGS_COLUMN+50, y, getBatteryVoltage(), attr|PREC2, 0, NULL, "V"); - if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.txVoltageCalibration, -127, 127); - break; - - case ITEM_RADIO_HARDWARE_RTC_BATTERY: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_RTC_BATT); - lcdDrawNumber(HW_SETTINGS_COLUMN+50, y, getRTCBatteryVoltage(), attr|PREC2, 0, NULL, "V"); - break; - - case ITEM_RADIO_HARDWARE_SERIAL_BAUDRATE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MAXBAUDRATE); - lcdDrawNumber(HW_SETTINGS_COLUMN+50, y, CROSSFIRE_BAUDRATES[g_eeGeneral.telemetryBaudrate], attr|LEFT); - if (attr) { - g_eeGeneral.telemetryBaudrate = DIM(CROSSFIRE_BAUDRATES) - 1 - checkIncDecModel(event, DIM(CROSSFIRE_BAUDRATES) - 1 - g_eeGeneral.telemetryBaudrate, 0, DIM(CROSSFIRE_BAUDRATES) - 1); - if (checkIncDec_Ret && IS_EXTERNAL_MODULE_ON()) { - pauseMixerCalculations(); - pausePulses(); - EXTERNAL_MODULE_OFF(); - RTOS_WAIT_MS(20); // 20ms so that the pulses interrupt will reinit the frame rate - telemetryProtocol = 255; // force telemetry port + module reinitialization - EXTERNAL_MODULE_ON(); - resumePulses(); - resumeMixerCalculations(); - } - } - break; - - case ITEM_RADIO_HARDWARE_BLUETOOTH_MODE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BLUETOOTH); - g_eeGeneral.bluetoothMode = editChoice(HW_SETTINGS_COLUMN+50, y, STR_BLUETOOTH_MODES, g_eeGeneral.bluetoothMode, BLUETOOTH_OFF, BLUETOOTH_TRAINER, attr, event); - break; - - case ITEM_RADIO_HARDWARE_BLUETOOTH_PAIRING_CODE: - lcdDrawText(INDENT_WIDTH, y, STR_BLUETOOTH_PIN_CODE); - lcdDrawText(HW_SETTINGS_COLUMN+50, y, "000000", 0); - break; - - case ITEM_RADIO_HARDWARE_BLUETOOTH_NAME: - lcdDrawText(INDENT_WIDTH, y, STR_NAME); - editName(HW_SETTINGS_COLUMN+50, y, g_eeGeneral.bluetoothName, LEN_BLUETOOTH_NAME, event, attr); - break; - -#if defined(AUX_SERIAL) - case ITEM_RADIO_HARDWARE_AUX_SERIAL_MODE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_AUX_SERIAL_MODE); - g_eeGeneral.auxSerialMode = editChoice(HW_SETTINGS_COLUMN+50, y, STR_AUX_SERIAL_MODES, g_eeGeneral.auxSerialMode, 0, UART_MODE_MAX, attr, event); - if (attr && checkIncDec_Ret) { - auxSerialInit(g_eeGeneral.auxSerialMode, modelTelemetryProtocol()); - } - break; -#endif - - case ITEM_RADIO_HARDWARE_JITTER_FILTER: - { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_JITTER_FILTER); - uint8_t b = 1-g_eeGeneral.jitterFilter; - g_eeGeneral.jitterFilter = 1 - editCheckBox(b, HW_SETTINGS_COLUMN+50, y, attr, event); - break; - } - } - } - - return true; -} diff --git a/radio/src/gui/480x272/radio_power_meter.cpp b/radio/src/gui/480x272/radio_power_meter.cpp deleted file mode 100644 index 46472d8bf..000000000 --- a/radio/src/gui/480x272/radio_power_meter.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -extern uint8_t g_moduleIdx; - -enum PowerMeterFields { - POWER_METER_FREQ_RANGE, - POWER_METER_ATTENUATOR, - POWER_METER_MAX, - POWER_METER_POWER, - POWER_METER_PEAK, - POWER_METER_FIELDS_MAX -}; - -constexpr coord_t POWER_METER_2ND_COLUMN = 100; - -bool menuRadioPowerMeter(event_t event) -{ - SUBMENU(STR_MENU_POWER_METER, ICON_RADIO_SPECTRUM_ANALYSER, POWER_METER_FIELDS_MAX-1, {0, 0, READONLY_ROW, READONLY_ROW, READONLY_ROW}); - - if (TELEMETRY_STREAMING()) { - lcdDrawCenteredText(LCD_H/2, STR_TURN_OFF_RECEIVER); - if (event == EVT_KEY_FIRST(KEY_EXIT)) { - killEvents(event); - popMenu(); - } - return false; - } - - if (menuEvent) { - lcdDrawCenteredText(LCD_H/2, STR_STOPPING); - lcdRefresh(); - moduleState[g_moduleIdx].readModuleInformation(&reusableBuffer.moduleSetup.pxx2.moduleInformation, PXX2_HW_INFO_TX_ID, PXX2_HW_INFO_TX_ID); - /* wait 1s to resume normal operation before leaving */ - watchdogSuspend(1000); - RTOS_WAIT_MS(1000); - return false; - } - - if (moduleState[g_moduleIdx].mode != MODULE_MODE_POWER_METER) { - memclear(&reusableBuffer.powerMeter, sizeof(reusableBuffer.powerMeter)); - reusableBuffer.powerMeter.freq = 2400000000; - reusableBuffer.powerMeter.attn = 4; - reusableBuffer.powerMeter.dirty = true; - moduleState[g_moduleIdx].mode = MODULE_MODE_POWER_METER; - } - - // The warning - if (reusableBuffer.powerMeter.attn != 0) { - lcdDrawCenteredText(MENU_BODY_TOP, STR_POWERMETER_ATTN_NEEDED, BOLD | INVERS); - } - - for (uint8_t i=0; i 0 ? INVERS | BLINK : INVERS) : 0); - coord_t y = MENU_BODY_TOP + 2 * FH + i * FH; - - switch (i) { - case POWER_METER_FREQ_RANGE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_POWERMETER_FREQ); - lcdDrawNumber(POWER_METER_2ND_COLUMN, y, reusableBuffer.powerMeter.freq / 1000000, LEFT | attr); - lcdDrawText(lcdNextPos, y, " MHz band"); - if (attr) { - reusableBuffer.powerMeter.freq = checkIncDec(event, reusableBuffer.powerMeter.freq == 900000000, 0, 1) ? 900000000 : 2400000000; - if (checkIncDec_Ret) { - reusableBuffer.powerMeter.power = 0; - reusableBuffer.powerMeter.peak = 0; - reusableBuffer.powerMeter.dirty = true; - } - } - break; - - case POWER_METER_ATTENUATOR: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_POWERMETER_ATTN); - lcdDrawNumber(POWER_METER_2ND_COLUMN, y, -10 * reusableBuffer.powerMeter.attn, LEFT | attr); - lcdDrawText(lcdNextPos, y, " dB"); - if (attr) { - reusableBuffer.powerMeter.attn = checkIncDec(event, reusableBuffer.powerMeter.attn, 0, 5, 0); - } - break; - - case POWER_METER_MAX: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MAX); - lcdDrawNumber(POWER_METER_2ND_COLUMN, y, -10 + 10 * reusableBuffer.powerMeter.attn); - lcdDrawText(lcdNextPos + 3, y, "dBm/ "); - drawPower(lcdNextPos + 3, y, -10 + 10 * reusableBuffer.powerMeter.attn, 0); - if (attr) { - reusableBuffer.powerMeter.attn = checkIncDec(event, reusableBuffer.powerMeter.attn, 0, 5, 0); - } - break; - - case POWER_METER_POWER: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_POWERMETER_POWER); - if (reusableBuffer.powerMeter.power) { - lcdDrawNumber(POWER_METER_2ND_COLUMN, y, reusableBuffer.powerMeter.power + reusableBuffer.powerMeter.attn * 1000, LEFT | PREC2); - lcdDrawText(lcdNextPos, y, "dBm"); - } - break; - - case POWER_METER_PEAK: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_POWERMETER_PEAK); - if (reusableBuffer.powerMeter.peak) { - lcdDrawNumber(POWER_METER_2ND_COLUMN, y, reusableBuffer.powerMeter.peak + reusableBuffer.powerMeter.attn * 1000, LEFT | PREC2); - lcdDrawText(lcdNextPos, y, "dBm"); - } - break; - } - } - - return true; -} diff --git a/radio/src/gui/480x272/radio_sdmanager.cpp b/radio/src/gui/480x272/radio_sdmanager.cpp deleted file mode 100644 index ceb3eae79..000000000 --- a/radio/src/gui/480x272/radio_sdmanager.cpp +++ /dev/null @@ -1,434 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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 -#include -#include "opentx.h" -#include "storage/modelslist.h" - -#define REFRESH_FILES() do { reusableBuffer.sdManager.offset = 65535; currentBitmapIndex = -1; } while (0) -#define NODE_TYPE(fname) fname[SD_SCREEN_FILE_LENGTH+1] -#define IS_DIRECTORY(fname) ((bool)(!NODE_TYPE(fname))) -#define IS_FILE(fname) ((bool)(NODE_TYPE(fname))) - -int currentBitmapIndex = 0; -BitmapBuffer * currentBitmap = NULL; - -bool menuRadioSdManagerInfo(event_t event) -{ - SIMPLE_SUBMENU(STR_SD_INFO_TITLE, ICON_RADIO_SD_MANAGER, 1); - - lcdDrawText(MENUS_MARGIN_LEFT, 2*FH, STR_SD_TYPE); - lcdDrawText(100, 2*FH, SD_IS_HC() ? STR_SDHC_CARD : STR_SD_CARD); - - lcdDrawText(MENUS_MARGIN_LEFT, 3*FH, STR_SD_SIZE); - lcdDrawNumber(100, 3*FH, sdGetSize(), LEFT, 0, NULL, "M"); - - lcdDrawText(MENUS_MARGIN_LEFT, 4*FH, STR_SD_SECTORS); -#if defined(SD_GET_FREE_BLOCKNR) - lcdDrawNumber(100, 4*FH, SD_GET_FREE_BLOCKNR()/1000, LEFT, 0, NULL, "/"); - lcdDrawNumber(150, 4*FH, sdGetNoSectors()/1000, LEFT); -#else - lcdDrawNumber(100, 4*FH, sdGetNoSectors()/1000, LEFT, 0, NULL, "k"); -#endif - - lcdDrawText(MENUS_MARGIN_LEFT, 5*FH, STR_SD_SPEED); - lcdDrawNumber(100, 5*FH, SD_GET_SPEED()/1000, LEFT, 0, NULL, "kb/s"); - - return true; -} - -inline bool isFilenameGreater(bool isfile, const char * fn, const char * line) -{ - return (isfile && IS_DIRECTORY(line)) || (isfile==IS_FILE(line) && strcasecmp(fn, line) > 0); -} - -inline bool isFilenameLower(bool isfile, const char * fn, const char * line) -{ - return (!isfile && IS_FILE(line)) || (isfile==IS_FILE(line) && strcasecmp(fn, line) < 0); -} - -void getSelectionFullPath(char * lfn) -{ - f_getcwd(lfn, _MAX_LFN); - strcat(lfn, "/"); - strcat(lfn, reusableBuffer.sdManager.lines[menuVerticalPosition - menuVerticalOffset]); -} - -void onSdManagerMenu(const char * result) -{ - TCHAR lfn[_MAX_LFN+1]; - - // TODO possible buffer overflows here! - - uint8_t index = menuVerticalPosition-menuVerticalOffset; - char *line = reusableBuffer.sdManager.lines[index]; - - if (result == STR_SD_INFO) { - pushMenu(menuRadioSdManagerInfo); - } - else if (result == STR_SD_FORMAT) { - POPUP_CONFIRMATION(STR_CONFIRM_FORMAT); - } - else if (result == STR_COPY_FILE) { - clipboard.type = CLIPBOARD_TYPE_SD_FILE; - f_getcwd(clipboard.data.sd.directory, CLIPBOARD_PATH_LEN); - strncpy(clipboard.data.sd.filename, line, CLIPBOARD_PATH_LEN-1); - } - else if (result == STR_PASTE) { - f_getcwd(lfn, _MAX_LFN); - // if destination is dir, copy into that dir - if (IS_DIRECTORY(line)) { - strcat(lfn, "/"); - strcat(lfn, line); - } - if (strcmp(clipboard.data.sd.directory, lfn)) { // prevent copying to the same directory - POPUP_WARNING(sdCopyFile(clipboard.data.sd.filename, clipboard.data.sd.directory, clipboard.data.sd.filename, lfn)); - REFRESH_FILES(); - } - } - else if (result == STR_RENAME_FILE) { - memcpy(reusableBuffer.sdManager.originalName, line, sizeof(reusableBuffer.sdManager.originalName)); - uint8_t fnlen = 0, extlen = 0; - getFileExtension(line, 0, LEN_FILE_EXTENSION_MAX, &fnlen, &extlen); - // write spaces to allow extending the length of a filename - memset(line + fnlen - extlen, ' ', SD_SCREEN_FILE_LENGTH - fnlen + extlen); - line[SD_SCREEN_FILE_LENGTH-extlen] = '\0'; - s_editMode = EDIT_MODIFY_STRING; - editNameCursorPos = 0; - } - else if (result == STR_DELETE_FILE) { - getSelectionFullPath(lfn); - f_unlink(lfn); - menuVerticalOffset = 0; - menuVerticalPosition = 0; - REFRESH_FILES(); - } - else if (result == STR_PLAY_FILE) { - getSelectionFullPath(lfn); - audioQueue.stopAll(); - audioQueue.playFile(lfn, 0, ID_PLAY_FROM_SD_MANAGER); - } - else if (result == STR_ASSIGN_BITMAP) { - memcpy(g_model.header.bitmap, line, sizeof(g_model.header.bitmap)); - if(modelslist.getCurrentModel()) - modelslist.getCurrentModel()->resetBuffer(); - storageDirty(EE_MODEL); - } - else if (result == STR_ASSIGN_SPLASH) { - f_getcwd(lfn, _MAX_LFN); - sdCopyFile(line, lfn, SPLASH_FILE, BITMAPS_PATH); - } - else if (result == STR_VIEW_TEXT) { - getSelectionFullPath(lfn); - pushMenuTextView(lfn); - } - else if (result == STR_FLASH_INTERNAL_MODULE) { - getSelectionFullPath(lfn); - FrskyDeviceFirmwareUpdate device(INTERNAL_MODULE); - device.flashFirmware(lfn); - } - else if (result == STR_FLASH_EXTERNAL_MODULE) { - getSelectionFullPath(lfn); - FrskyDeviceFirmwareUpdate device(EXTERNAL_MODULE); - device.flashFirmware(lfn); - } - else if (result == STR_FLASH_EXTERNAL_DEVICE) { - getSelectionFullPath(lfn); - FrskyDeviceFirmwareUpdate device(SPORT_MODULE); - device.flashFirmware(lfn); - } -#if defined(LUA) - else if (result == STR_EXECUTE_FILE) { - getSelectionFullPath(lfn); - luaExec(lfn); - } -#endif -} - -bool menuRadioSdManager(event_t _event) -{ - if (warningResult) { - warningResult = 0; - showMessageBox(STR_FORMATTING); - logsClose(); - audioQueue.stopSD(); - if(sdCardFormat()) { - f_chdir("/"); - REFRESH_FILES(); - } - } - - event_t event = (EVT_KEY_MASK(_event) == KEY_ENTER ? 0 : _event); - SIMPLE_MENU(SD_IS_HC() ? STR_SDHC_CARD : STR_SD_CARD, RADIO_ICONS, menuTabGeneral, MENU_RADIO_SD_MANAGER, reusableBuffer.sdManager.count); - - int index = menuVerticalPosition-menuVerticalOffset; - - switch(_event) { - case EVT_ENTRY: - f_chdir(ROOT_PATH); - REFRESH_FILES(); - break; - -#if 0 - // TODO: Implement it - case EVT_KEY_LONG(KEY_MENU): - if (!READ_ONLY() && s_editMode == 0) { - killEvents(_event); - POPUP_MENU_ADD_ITEM(STR_SD_INFO); - POPUP_MENU_ADD_ITEM(STR_SD_FORMAT); - POPUP_MENU_START(onSdManagerMenu); - } - break; -#endif - - case EVT_KEY_BREAK(KEY_EXIT): - REFRESH_FILES(); - break; - - case EVT_KEY_BREAK(KEY_ENTER): - if (s_editMode > 0) { - break; - } - else { - if (IS_DIRECTORY(reusableBuffer.sdManager.lines[index])) { - f_chdir(reusableBuffer.sdManager.lines[index]); - menuVerticalOffset = 0; - menuVerticalPosition = 1; - index = 1; - REFRESH_FILES(); - killEvents(_event); - return true; - } - } - // no break - - case EVT_KEY_LONG(KEY_ENTER): - if (s_editMode == 0) { - killEvents(_event); - char * line = reusableBuffer.sdManager.lines[index]; - if (!strcmp(line, "..")) { - break; // no menu for parent dir - } - const char * ext = getFileExtension(line); - if (ext) { - if (!strcasecmp(ext, SOUNDS_EXT)) { - POPUP_MENU_ADD_ITEM(STR_PLAY_FILE); - } - else if (isExtensionMatching(ext, BITMAPS_EXT)) { - TCHAR lfn[_MAX_LFN+1]; - f_getcwd(lfn, _MAX_LFN); - if (!READ_ONLY() && unsigned(strlen(ext)+ext-line) <= sizeof(g_model.header.bitmap) && !strcmp(lfn, BITMAPS_PATH)) { - POPUP_MENU_ADD_ITEM(STR_ASSIGN_BITMAP); - } - if (!strcmp(ext, PNG_EXT)) { - POPUP_MENU_ADD_ITEM(STR_ASSIGN_SPLASH); - } - } - else if (!READ_ONLY() && !strcasecmp(ext, SPORT_FIRMWARE_EXT)) { - if (HAS_SPORT_UPDATE_CONNECTOR()) - POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_DEVICE); - POPUP_MENU_ADD_ITEM(STR_FLASH_INTERNAL_MODULE); - POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_MODULE); - } - else if (!READ_ONLY() && !strcasecmp(ext, UPDATE_FIRMWARE_EXT)) { - FrSkyFirmwareInformation information; - if (readFirmwareInformation(line, information) == nullptr) { - if (information.productFamily == FIRMWARE_FAMILY_INTERNAL_MODULE) - POPUP_MENU_ADD_ITEM(STR_FLASH_INTERNAL_MODULE); - if (information.productFamily == FIRMWARE_FAMILY_EXTERNAL_MODULE) - POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_MODULE); - if (HAS_SPORT_UPDATE_CONNECTOR() && (information.productFamily == FIRMWARE_FAMILY_RECEIVER || information.productFamily == FIRMWARE_FAMILY_SENSOR)) - POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_DEVICE); -#if defined(PXX2) - if (information.productFamily == FIRMWARE_FAMILY_RECEIVER) - POPUP_MENU_ADD_ITEM(STR_FLASH_RECEIVER_OTA); -#endif -#if defined(BLUETOOTH) - if (information.productFamily == FIRMWARE_FAMILY_BLUETOOTH_CHIP) - POPUP_MENU_ADD_ITEM(STR_FLASH_BLUETOOTH_MODULE); -#endif -#if defined(HARDWARE_POWER_MANAGEMENT_UNIT) - if (information.productFamily == FIRMWARE_FAMILY_POWER_MANAGEMENT_UNIT) - POPUP_MENU_ADD_ITEM(STR_FLASH_POWER_MANAGEMENT_UNIT); -#endif - } - } - else if (isExtensionMatching(ext, SCRIPTS_EXT)) { - POPUP_MENU_ADD_ITEM(STR_EXECUTE_FILE); - } - if (isExtensionMatching(ext, TEXT_EXT) || isExtensionMatching(ext, SCRIPTS_EXT)) { - POPUP_MENU_ADD_ITEM(STR_VIEW_TEXT); - } - } - if (!READ_ONLY()) { - if (IS_FILE(line)) - POPUP_MENU_ADD_ITEM(STR_COPY_FILE); - if (clipboard.type == CLIPBOARD_TYPE_SD_FILE) - POPUP_MENU_ADD_ITEM(STR_PASTE); - POPUP_MENU_ADD_ITEM(STR_RENAME_FILE); - if (IS_FILE(line)) - POPUP_MENU_ADD_ITEM(STR_DELETE_FILE); - } - POPUP_MENU_START(onSdManagerMenu); - } - break; - } - - if (reusableBuffer.sdManager.offset != menuVerticalOffset) { - FILINFO fno; - DIR dir; - - if (menuVerticalOffset == 0) { - reusableBuffer.sdManager.offset = 0; - memset(reusableBuffer.sdManager.lines, 0, sizeof(reusableBuffer.sdManager.lines)); - } - else if (menuVerticalOffset == reusableBuffer.sdManager.count-NUM_BODY_LINES) { - reusableBuffer.sdManager.offset = menuVerticalOffset; - memset(reusableBuffer.sdManager.lines, 0, sizeof(reusableBuffer.sdManager.lines)); - } - else if (menuVerticalOffset > reusableBuffer.sdManager.offset) { - memmove(reusableBuffer.sdManager.lines[0], reusableBuffer.sdManager.lines[1], (NUM_BODY_LINES-1)*sizeof(reusableBuffer.sdManager.lines[0])); - memset(reusableBuffer.sdManager.lines[NUM_BODY_LINES-1], 0xff, SD_SCREEN_FILE_LENGTH); - NODE_TYPE(reusableBuffer.sdManager.lines[NUM_BODY_LINES-1]) = 1; - } - else { - memmove(reusableBuffer.sdManager.lines[1], reusableBuffer.sdManager.lines[0], (NUM_BODY_LINES-1)*sizeof(reusableBuffer.sdManager.lines[0])); - memset(reusableBuffer.sdManager.lines[0], 0, sizeof(reusableBuffer.sdManager.lines[0])); - } - - reusableBuffer.sdManager.count = 0; - - FRESULT res = f_opendir(&dir, "."); // Open the directory - if (res == FR_OK) { - bool firstTime = true; - for (;;) { - res = sdReadDir(&dir, &fno, firstTime); - if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ - if (strlen(fno.fname) > SD_SCREEN_FILE_LENGTH) continue; - if (fno.fattrib & AM_HID) continue; /* Ignore Windows hidden files */ - if (fno.fname[0] == '.' && fno.fname[1] != '.') continue; /* Ignore UNIX hidden files, but not .. */ - - reusableBuffer.sdManager.count++; - - bool isfile = !(fno.fattrib & AM_DIR); - - if (menuVerticalOffset == 0) { - for (uint8_t i=0; i=0; i--) { - char *line = reusableBuffer.sdManager.lines[i]; - if (line[0] == '\0' || isFilenameGreater(isfile, fno.fname, line)) { - if (i > 0) memmove(reusableBuffer.sdManager.lines[0], reusableBuffer.sdManager.lines[1], sizeof(reusableBuffer.sdManager.lines[0]) * i); - memset(line, 0, sizeof(reusableBuffer.sdManager.lines[0])); - strcpy(line, fno.fname); - NODE_TYPE(line) = isfile; - break; - } - } - } - else if (menuVerticalOffset > reusableBuffer.sdManager.offset) { - if (isFilenameGreater(isfile, fno.fname, reusableBuffer.sdManager.lines[NUM_BODY_LINES-2]) && isFilenameLower(isfile, fno.fname, reusableBuffer.sdManager.lines[NUM_BODY_LINES-1])) { - memset(reusableBuffer.sdManager.lines[NUM_BODY_LINES-1], 0, sizeof(reusableBuffer.sdManager.lines[0])); - strcpy(reusableBuffer.sdManager.lines[NUM_BODY_LINES-1], fno.fname); - NODE_TYPE(reusableBuffer.sdManager.lines[NUM_BODY_LINES-1]) = isfile; - } - } - else { - if (isFilenameLower(isfile, fno.fname, reusableBuffer.sdManager.lines[1]) && isFilenameGreater(isfile, fno.fname, reusableBuffer.sdManager.lines[0])) { - memset(reusableBuffer.sdManager.lines[0], 0, sizeof(reusableBuffer.sdManager.lines[0])); - strcpy(reusableBuffer.sdManager.lines[0], fno.fname); - NODE_TYPE(reusableBuffer.sdManager.lines[0]) = isfile; - } - } - } - f_closedir(&dir); - } - } - - reusableBuffer.sdManager.offset = menuVerticalOffset; - - for (uint8_t i=0; igetHeight(); - uint16_t width = currentBitmap->getWidth(); - if (height > MENU_BODY_HEIGHT-10) { - height = MENU_BODY_HEIGHT - 10; - } - if (width > LCD_W/2) { - width = LCD_W/2; - } - lcd->drawScaledBitmap(currentBitmap, LCD_W / 2 - 20 + LCD_W/4 - width/2, MENU_BODY_TOP + MENU_BODY_HEIGHT/2 - height/2, width, height); - } - } - - return true; -} diff --git a/radio/src/gui/480x272/radio_setup.cpp b/radio/src/gui/480x272/radio_setup.cpp deleted file mode 100644 index 58c061175..000000000 --- a/radio/src/gui/480x272/radio_setup.cpp +++ /dev/null @@ -1,525 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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. - */ - -#define LANGUAGE_PACKS_DEFINITION - -#include "opentx.h" - -#define RADIO_SETUP_2ND_COLUMN 235 - -int8_t editSlider(coord_t x, coord_t y, event_t event, int8_t value, int8_t min, int8_t max, LcdFlags attr) -{ - drawHorizontalSlider(x, y, 100, value, min, max, 0, OPTION_SLIDER_DBL_COLOR|attr); - return editChoice(x, y, NULL, value, min, max, attr, event); -} - -#define SLIDER_5POS(val) val = editSlider(RADIO_SETUP_2ND_COLUMN, y, event, val, -2, +2, attr) - -enum menuRadioSetupItems { - ITEM_SETUP_DATE, - ITEM_SETUP_TIME, - ITEM_SETUP_BATT_RANGE, - ITEM_SETUP_SOUND_LABEL, - ITEM_SETUP_BEEP_MODE, - ITEM_SETUP_GENERAL_VOLUME, - ITEM_SETUP_BEEP_VOLUME, - ITEM_SETUP_BEEP_LENGTH, - ITEM_SETUP_SPEAKER_PITCH, - ITEM_SETUP_WAV_VOLUME, - ITEM_SETUP_BACKGROUND_VOLUME, - CASE_VARIO(ITEM_SETUP_VARIO_LABEL) - CASE_VARIO(ITEM_SETUP_VARIO_VOLUME) - CASE_VARIO(ITEM_SETUP_VARIO_PITCH) - CASE_VARIO(ITEM_SETUP_VARIO_RANGE) - CASE_VARIO(ITEM_SETUP_VARIO_REPEAT) - CASE_HAPTIC(ITEM_SETUP_HAPTIC_LABEL) - CASE_HAPTIC(ITEM_SETUP_HAPTIC_MODE) - CASE_HAPTIC(ITEM_SETUP_HAPTIC_LENGTH) - CASE_HAPTIC(ITEM_SETUP_HAPTIC_STRENGTH) - // ITEM_SETUP_CONTRAST, - ITEM_SETUP_ALARMS_LABEL, - ITEM_SETUP_BATTERY_WARNING, - ITEM_SETUP_INACTIVITY_ALARM, - // ITEM_SETUP_MEMORY_WARNING, - ITEM_SETUP_ALARM_WARNING, - ITEM_SETUP_RSSI_POWEROFF_ALARM, - ITEM_SETUP_BACKLIGHT_LABEL, - ITEM_SETUP_BACKLIGHT_MODE, - ITEM_SETUP_BACKLIGHT_DELAY, - ITEM_SETUP_BRIGHTNESS, - ITEM_SETUP_DIM_LEVEL, - ITEM_SETUP_FLASH_BEEP, - CASE_PWR_BUTTON_PRESS(ITEM_SETUP_PWR_OFF_SPEED) -#if defined(PXX2) - ITEM_RADIO_SETUP_OWNER_ID, -#endif - CASE_GPS(ITEM_SETUP_LABEL_GPS) - CASE_GPS(ITEM_SETUP_TIMEZONE) - CASE_GPS(ITEM_SETUP_ADJUST_RTC) - CASE_GPS(ITEM_SETUP_GPSFORMAT) - CASE_PXX1(ITEM_SETUP_COUNTRYCODE) - ITEM_SETUP_LANGUAGE, - ITEM_SETUP_IMPERIAL, - IF_FAI_CHOICE(ITEM_SETUP_FAI) - ITEM_SETUP_SWITCHES_DELAY, - ITEM_SETUP_USB_MODE, - ITEM_SETUP_RX_CHANNEL_ORD, - ITEM_SETUP_STICK_MODE, - ITEM_SETUP_MAX -}; - -bool menuRadioSetup(event_t event) -{ -#if defined(RTCLOCK) - struct gtm t; - gettime(&t); - - if ((menuVerticalPosition==ITEM_SETUP_DATE || menuVerticalPosition==ITEM_SETUP_TIME) && - (s_editMode>0) && - (event==EVT_KEY_FIRST(KEY_ENTER) || event==EVT_KEY_BREAK(KEY_ENTER) || event==EVT_KEY_LONG(KEY_ENTER) || event==EVT_KEY_FIRST(KEY_EXIT))) { - // set the date and time into RTC chip - rtcSetTime(&t); - } -#endif - -#if defined(FAI_CHOICE) - #define FAI_CHOICE_ROW (g_eeGeneral.fai ? (uint8_t)-1 : (uint8_t)0), - - if (warningResult) { - warningResult = 0; - g_eeGeneral.fai = true; - storageDirty(EE_GENERAL); - } -#else - #define FAI_CHOICE_ROW -#endif - - MENU(STR_MENURADIOSETUP, RADIO_ICONS, menuTabGeneral, MENU_RADIO_SETUP, ITEM_SETUP_MAX, { - 2|NAVIGATION_LINE_BY_LINE, 2|NAVIGATION_LINE_BY_LINE, 1|NAVIGATION_LINE_BY_LINE, // Date Time Bat range - LABEL(SOUND), 0, 0, 0, 0, 0, 0, 0, - CASE_VARIO(LABEL(VARIO)) CASE_VARIO(0) CASE_VARIO(0) CASE_VARIO(0) CASE_VARIO(0) - CASE_HAPTIC(LABEL(HAPTIC)) CASE_HAPTIC(0) CASE_HAPTIC(0) CASE_HAPTIC(0) - LABEL(ALARMS), 0, 0, 0, 0, - LABEL(BACKLIGHT), 0, 0, 0, 0, 0, - CASE_PWR_BUTTON_PRESS(0) - CASE_PXX2(0) // owner registration ID - CASE_GPS(LABEL(GPS)) - CASE_GPS(0) // timezone - CASE_GPS(0) // adjust RTC - CASE_GPS(0) // GPS format - CASE_PXX1(0) // country code - 0, // voice language - 0, // imperial - FAI_CHOICE_ROW - 0, // switches delay - 0, // USB mode - 0, // RX channels order - 0, // sticks mode - 1 /*to force edit mode*/ - }); // Units - Fai choice - Play delay - USB mode - Chan order - Mode (1 to 4) - - if (event == EVT_ENTRY) { - reusableBuffer.generalSettings.stickMode = g_eeGeneral.stickMode; - } - - int sub = menuVerticalPosition; - - for (int i=0; i0) ? BLINK|INVERS : INVERS); - LcdFlags attr = (sub == k ? blink : 0); - - switch(k) { - case ITEM_SETUP_DATE: - { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_DATE); - LcdFlags flags = 0; - if (attr && menuHorizontalPosition < 0) { - flags |= INVERS; - } - for (uint8_t j=0; j<3; j++) { - uint8_t rowattr = (menuHorizontalPosition==j ? attr : 0); - switch (j) { - case 0: - lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, t.tm_year+TM_YEAR_BASE, flags|rowattr); - if (rowattr && s_editMode>0) t.tm_year = checkIncDec(event, t.tm_year, 112, 200, 0); - lcdDrawText(lcdNextPos+3, y, "-", flags); - break; - case 1: - lcdDrawNumber(lcdNextPos+3, y, t.tm_mon+1, flags|rowattr|LEADING0, 2); - if (rowattr && s_editMode>0) t.tm_mon = checkIncDec(event, t.tm_mon, 0, 11, 0); - lcdDrawText(lcdNextPos+3, y, "-", flags); - break; - case 2: - { - int16_t year = TM_YEAR_BASE + t.tm_year; - int8_t dlim = (((((year%4==0) && (year%100!=0)) || (year%400==0)) && (t.tm_mon==1)) ? 1 : 0); - static const uint8_t dmon[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - dlim += dmon[t.tm_mon]; - lcdDrawNumber(lcdNextPos+3, y, t.tm_mday, flags|rowattr|LEADING0, 2); - if (rowattr && s_editMode>0) t.tm_mday = checkIncDec(event, t.tm_mday, 1, dlim, 0); - break; - } - } - } - if (attr && checkIncDec_Ret) { - g_rtcTime = gmktime(&t); // update local timestamp and get wday calculated - } - break; - } - - case ITEM_SETUP_TIME: - { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TIME); - LcdFlags flags = 0; - if (attr && menuHorizontalPosition < 0) { - flags |= INVERS; - } - for (uint8_t j=0; j<3; j++) { - uint8_t rowattr = (menuHorizontalPosition==j ? attr : 0); - switch (j) { - case 0: - if (rowattr && s_editMode>0) t.tm_hour = checkIncDec(event, t.tm_hour, 0, 23, 0); - lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, t.tm_hour, flags|rowattr|LEADING0, 2); - lcdDrawText(lcdNextPos+3, y, ":", flags); - break; - case 1: - if (rowattr && s_editMode>0) t.tm_min = checkIncDec(event, t.tm_min, 0, 59, 0); - lcdDrawNumber(lcdNextPos+3, y, t.tm_min, flags|rowattr|LEADING0, 2); - lcdDrawText(lcdNextPos+3, y, ":", flags); - break; - case 2: - if (rowattr && s_editMode>0) t.tm_sec = checkIncDec(event, t.tm_sec, 0, 59, 0); - lcdDrawNumber(lcdNextPos+3, y, t.tm_sec, flags|rowattr|LEADING0, 2); - break; - } - } - if (attr && checkIncDec_Ret) - g_rtcTime = gmktime(&t); // update local timestamp and get wday calculated - break; - } - - case ITEM_SETUP_BATT_RANGE: - { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BATTERY_RANGE); - LcdFlags flags = 0; - if (attr && menuHorizontalPosition < 0) { - flags |= INVERS; - } - lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, 90+g_eeGeneral.vBatMin, flags|(menuHorizontalPosition==0 ? attr : 0)|PREC1|LEFT); - lcdDrawText(lcdNextPos+3, y, "-", flags); - lcdDrawNumber(lcdNextPos+3, y, 120+g_eeGeneral.vBatMax, flags|(menuHorizontalPosition>0 ? attr : 0)|PREC1|LEFT); - lcdDrawText(lcdNextPos+1, y, "V", flags); - if (attr && s_editMode>0) { - if (menuHorizontalPosition==0) - CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatMin, -50, g_eeGeneral.vBatMax+29); // min=4.0V - else - CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatMax, g_eeGeneral.vBatMin-29, +40); // max=16.0V - } - break; - } - - case ITEM_SETUP_SOUND_LABEL: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SOUND_LABEL); - break; - - case ITEM_SETUP_BEEP_MODE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SPEAKER); - g_eeGeneral.beepMode = editChoice(RADIO_SETUP_2ND_COLUMN, y, STR_VBEEPMODE, g_eeGeneral.beepMode, -2, 1, attr, event); - break; - - case ITEM_SETUP_GENERAL_VOLUME: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SPEAKER_VOLUME); - g_eeGeneral.speakerVolume = editSlider(RADIO_SETUP_2ND_COLUMN, y, event, g_eeGeneral.speakerVolume, -VOLUME_LEVEL_DEF, VOLUME_LEVEL_MAX-VOLUME_LEVEL_DEF, attr); - break; - - case ITEM_SETUP_BEEP_VOLUME: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BEEP_VOLUME); - g_eeGeneral.beepVolume = editSlider(RADIO_SETUP_2ND_COLUMN, y, event, g_eeGeneral.beepVolume, -2, +2, attr); - break; - - case ITEM_SETUP_WAV_VOLUME: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_WAV_VOLUME); - g_eeGeneral.wavVolume = editSlider(RADIO_SETUP_2ND_COLUMN, y, event, g_eeGeneral.wavVolume, -2, +2, attr); - break; - - case ITEM_SETUP_BACKGROUND_VOLUME: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BG_VOLUME); - g_eeGeneral.backgroundVolume = editSlider(RADIO_SETUP_2ND_COLUMN, y, event, g_eeGeneral.backgroundVolume, -2, +2, attr); - break; - - case ITEM_SETUP_BEEP_LENGTH: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BEEP_LENGTH); - SLIDER_5POS(g_eeGeneral.beepLength); - break; - - case ITEM_SETUP_SPEAKER_PITCH: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SPKRPITCH); - lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.speakerPitch*15, attr|LEFT, 0, "+", "Hz"); - if (attr) { - CHECK_INCDEC_GENVAR(event, g_eeGeneral.speakerPitch, 0, 20); - } - break; - -#if defined(VARIO) - case ITEM_SETUP_VARIO_LABEL: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_VARIO); - break; - case ITEM_SETUP_VARIO_VOLUME: - lcdDrawText(MENUS_MARGIN_LEFT, y, TR_SPEAKER_VOLUME); - g_eeGeneral.varioVolume = editSlider(RADIO_SETUP_2ND_COLUMN, y, event, g_eeGeneral.varioVolume, -2, +2, attr); - break; - case ITEM_SETUP_VARIO_PITCH: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PITCH_AT_ZERO); - lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, VARIO_FREQUENCY_ZERO+(g_eeGeneral.varioPitch*10), attr|LEFT, 0, NULL, "Hz"); - if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.varioPitch, -40, 40); - break; - case ITEM_SETUP_VARIO_RANGE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PITCH_AT_MAX); - lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, VARIO_FREQUENCY_ZERO+(g_eeGeneral.varioPitch*10)+VARIO_FREQUENCY_RANGE+(g_eeGeneral.varioRange*10), attr|LEFT, 0, NULL, "Hz"); - if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.varioRange, -80, 80); - break; - case ITEM_SETUP_VARIO_REPEAT: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_REPEAT_AT_ZERO); - lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, VARIO_REPEAT_ZERO+(g_eeGeneral.varioRepeat*10), attr|LEFT, 0, NULL, STR_MS); - if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.varioRepeat, -30, 50); - break; -#endif - -#if defined(HAPTIC) - case ITEM_SETUP_HAPTIC_LABEL: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_HAPTIC_LABEL); - break; - - case ITEM_SETUP_HAPTIC_MODE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODE); - g_eeGeneral.hapticMode = editChoice(RADIO_SETUP_2ND_COLUMN, y, STR_VBEEPMODE, g_eeGeneral.hapticMode, -2, 1, attr, event); - break; - - case ITEM_SETUP_HAPTIC_LENGTH: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_LENGTH); - SLIDER_5POS(g_eeGeneral.hapticLength); - break; - - case ITEM_SETUP_HAPTIC_STRENGTH: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_HAPTICSTRENGTH); - SLIDER_5POS(g_eeGeneral.hapticStrength); - break; -#endif - -#if 0 - case ITEM_SETUP_CONTRAST: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CONTRAST); - lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.contrast, attr|LEFT); - if (attr) { - CHECK_INCDEC_GENVAR(event, g_eeGeneral.contrast, LCD_CONTRAST_MIN, LCD_CONTRAST_MAX); - lcdSetContrast(); - } - break; -#endif - - case ITEM_SETUP_ALARMS_LABEL: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ALARMS_LABEL); - break; - - case ITEM_SETUP_BATTERY_WARNING: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BATTERYWARNING); - drawValueWithUnit(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.vBatWarn, UNIT_VOLTS, attr|PREC1|LEFT); - if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatWarn, 40, 120); //4-12V - break; - - case ITEM_SETUP_ALARM_WARNING: - { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ALARMWARNING); - uint8_t b = 1 - g_eeGeneral.disableAlarmWarning; - g_eeGeneral.disableAlarmWarning = 1 - editCheckBox(b, RADIO_SETUP_2ND_COLUMN, y, attr, event); - break; - } - - case ITEM_SETUP_RSSI_POWEROFF_ALARM: - { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_RSSISHUTDOWNALARM); - uint8_t b = 1 - g_eeGeneral.disableRssiPoweroffAlarm; - g_eeGeneral.disableRssiPoweroffAlarm = 1 - editCheckBox(b, RADIO_SETUP_2ND_COLUMN, y, attr, event); - break; - } - - case ITEM_SETUP_INACTIVITY_ALARM: - lcdDrawText(MENUS_MARGIN_LEFT, y,STR_INACTIVITYALARM); - lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.inactivityTimer, attr|LEFT, 0, NULL, "m"); - if (attr) g_eeGeneral.inactivityTimer = checkIncDec(event, g_eeGeneral.inactivityTimer, 0, 250, EE_GENERAL); //0..250minutes - break; - - case ITEM_SETUP_BACKLIGHT_LABEL: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BACKLIGHT_LABEL); - break; - - case ITEM_SETUP_BACKLIGHT_MODE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODE); - g_eeGeneral.backlightMode = editChoice(RADIO_SETUP_2ND_COLUMN, y, STR_VBLMODE, g_eeGeneral.backlightMode, e_backlight_mode_off, e_backlight_mode_on, attr, event); - break; - - case ITEM_SETUP_FLASH_BEEP: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ALARM); - g_eeGeneral.alarmsFlash = editCheckBox(g_eeGeneral.alarmsFlash, RADIO_SETUP_2ND_COLUMN, y, attr, event ) ; - break; - -#if defined(PXX2) - case ITEM_RADIO_SETUP_OWNER_ID: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_OWNER_ID); - editName(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.ownerRegistrationID, PXX2_LEN_REGISTRATION_ID, event, attr); - break; -#endif - - case ITEM_SETUP_BACKLIGHT_DELAY: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BLDELAY); - lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.lightAutoOff*5, attr|LEFT, 0, NULL, "s"); - if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.lightAutoOff, 0, 600/5); - break; - - case ITEM_SETUP_BRIGHTNESS: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BLONBRIGHTNESS); - g_eeGeneral.backlightBright = BACKLIGHT_LEVEL_MAX - editSlider(RADIO_SETUP_2ND_COLUMN, y, event, BACKLIGHT_LEVEL_MAX - g_eeGeneral.backlightBright, BACKLIGHT_LEVEL_MIN, BACKLIGHT_LEVEL_MAX, attr); - break; - - case ITEM_SETUP_DIM_LEVEL: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BLOFFBRIGHTNESS); - g_eeGeneral.blOffBright = editSlider(RADIO_SETUP_2ND_COLUMN, y, event, g_eeGeneral.blOffBright, BACKLIGHT_LEVEL_MIN, BACKLIGHT_LEVEL_MAX, attr); - break; - -#if defined(PWR_BUTTON_PRESS) - case ITEM_SETUP_PWR_OFF_SPEED: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PWR_OFF_SPEED); - g_eeGeneral.pwrOffSpeed = editSlider(RADIO_SETUP_2ND_COLUMN, y, event, g_eeGeneral.pwrOffSpeed, -1, 2, attr); - break; -#endif - - case ITEM_SETUP_LABEL_GPS: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_GPS); - break; - - case ITEM_SETUP_TIMEZONE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TIMEZONE); - lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.timezone, attr|LEFT); - if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.timezone, -12, 12); - break; - - case ITEM_SETUP_ADJUST_RTC: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ADJUST_RTC); - g_eeGeneral.adjustRTC = editCheckBox(g_eeGeneral.adjustRTC, RADIO_SETUP_2ND_COLUMN, y, attr, event ) ; - break; - - case ITEM_SETUP_GPSFORMAT: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_GPSCOORD); - g_eeGeneral.gpsFormat = editChoice(RADIO_SETUP_2ND_COLUMN, y, STR_GPSFORMAT, g_eeGeneral.gpsFormat, 0, 1, attr, event); - break; - -#if defined(PXX1) - case ITEM_SETUP_COUNTRYCODE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_COUNTRYCODE); - g_eeGeneral.countryCode = editChoice(RADIO_SETUP_2ND_COLUMN, y, STR_COUNTRYCODES, g_eeGeneral.countryCode, 0, 2, attr, event); - break; -#endif - - case ITEM_SETUP_LANGUAGE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_VOICELANG); - lcdDrawText(RADIO_SETUP_2ND_COLUMN, y, currentLanguagePack->name, attr); - if (attr) { - currentLanguagePackIdx = checkIncDec(event, currentLanguagePackIdx, 0, DIM(languagePacks)-2, EE_GENERAL); - if (checkIncDec_Ret) { - currentLanguagePack = languagePacks[currentLanguagePackIdx]; - strncpy(g_eeGeneral.ttsLanguage, currentLanguagePack->id, 2); - } - } - break; - - case ITEM_SETUP_IMPERIAL: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_UNITSSYSTEM); - g_eeGeneral.imperial = editChoice(RADIO_SETUP_2ND_COLUMN, y, STR_VUNITSSYSTEM, g_eeGeneral.imperial, 0, 1, attr, event); - break; - -#if defined(FAI_CHOICE) - case ITEM_SETUP_FAI: - lcdDrawText(MENUS_MARGIN_LEFT, y, "FAI Mode"); - if (g_eeGeneral.fai) { - lcdDrawText(RADIO_SETUP_2ND_COLUMN, y, "Locked in FAI Mode"); - } - else { - g_eeGeneral.fai = editCheckBox(g_eeGeneral.fai, RADIO_SETUP_2ND_COLUMN, y, attr, event); - if (attr && checkIncDec_Ret) { - g_eeGeneral.fai = false; - POPUP_CONFIRMATION("FAI mode?"); - } - } - break; -#endif - - - case ITEM_SETUP_SWITCHES_DELAY: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SWITCHES_DELAY); - lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, 10*SWITCHES_DELAY(), attr|LEFT, 0, NULL, STR_MS); - if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.switchesDelay, -15, 100-15); - break; - - case ITEM_SETUP_USB_MODE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_USBMODE); - g_eeGeneral.USBMode = editChoice(RADIO_SETUP_2ND_COLUMN, y, STR_USBMODES, g_eeGeneral.USBMode, USB_UNSELECTED_MODE, USB_MAX_MODE, attr, event); - break; - - case ITEM_SETUP_RX_CHANNEL_ORD: - { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_RXCHANNELORD); // RAET->AETR - char s[5]; - for (uint8_t i=0; i<4; i++) { - s[i] = STR_RETA123[channelOrder(i+1)]; - } - s[4] = '\0'; - lcdDrawText(RADIO_SETUP_2ND_COLUMN, y, s, attr); - if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.templateSetup, 0, 23); - break; - } - - case ITEM_SETUP_STICK_MODE: - { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODE); - char s[2] = " "; - s[0] = '1'+reusableBuffer.generalSettings.stickMode; - lcdDrawText(RADIO_SETUP_2ND_COLUMN, y, s, attr); - for (uint8_t i=0; i<4; i++) { - drawSource(RADIO_SETUP_2ND_COLUMN + 40 + 50*i, y, MIXSRC_Rud + *(modn12x3 + 4*reusableBuffer.generalSettings.stickMode + i)); - } - if (attr && s_editMode>0) { - CHECK_INCDEC_GENVAR(event, reusableBuffer.generalSettings.stickMode, 0, 3); - } - else if (reusableBuffer.generalSettings.stickMode != g_eeGeneral.stickMode) { - pausePulses(); - g_eeGeneral.stickMode = reusableBuffer.generalSettings.stickMode; - checkThrottleStick(); - resumePulses(); - waitKeysReleased(); - } - break; - } - } - } - - return true; -} diff --git a/radio/src/gui/480x272/radio_spectrum_analyser.cpp b/radio/src/gui/480x272/radio_spectrum_analyser.cpp deleted file mode 100644 index 260fbc767..000000000 --- a/radio/src/gui/480x272/radio_spectrum_analyser.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -extern uint8_t g_moduleIdx; - -enum SpectrumFields { - SPECTRUM_FREQUENCY, - SPECTRUM_SPAN, - SPECTRUM_FIELDS_MAX -}; - -bool menuRadioSpectrumAnalyser(event_t event) -{ - SUBMENU(STR_MENU_SPECTRUM_ANALYSER, ICON_RADIO_SPECTRUM_ANALYSER, 1, {1}); - - if (menuEvent) { - lcdDrawCenteredText(LCD_H/2, STR_STOPPING); - lcdRefresh(); - moduleState[g_moduleIdx].readModuleInformation(&reusableBuffer.moduleSetup.pxx2.moduleInformation, PXX2_HW_INFO_TX_ID, PXX2_HW_INFO_TX_ID); - /* wait 1s to resume normal operation before leaving */ - watchdogSuspend(1000); - RTOS_WAIT_MS(1000); - return false; - } - - if (moduleState[g_moduleIdx].mode != MODULE_MODE_SPECTRUM_ANALYSER) { - if (TELEMETRY_STREAMING()) { - lcdDrawCenteredText(LCD_H/2, STR_TURN_OFF_RECEIVER); - if (event == EVT_KEY_FIRST(KEY_EXIT)) { - killEvents(event); - popMenu(); - } - return false; - } - - memclear(&reusableBuffer.spectrumAnalyser, sizeof(reusableBuffer.spectrumAnalyser)); - - if (isModuleR9MAccess(g_moduleIdx)) { - reusableBuffer.spectrumAnalyser.spanDefault = 20; - reusableBuffer.spectrumAnalyser.spanMax = 40; - reusableBuffer.spectrumAnalyser.freqDefault = 890; - reusableBuffer.spectrumAnalyser.freqMin = 850; - reusableBuffer.spectrumAnalyser.freqMax = 930; - } - else { - reusableBuffer.spectrumAnalyser.spanDefault = 40; // 40MHz - reusableBuffer.spectrumAnalyser.spanMax = 80; - reusableBuffer.spectrumAnalyser.freqDefault = 2440; // 2440MHz - reusableBuffer.spectrumAnalyser.freqMin = 2400; - reusableBuffer.spectrumAnalyser.freqMax = 2485; - } - - reusableBuffer.spectrumAnalyser.span = reusableBuffer.spectrumAnalyser.spanDefault * 1000000; - reusableBuffer.spectrumAnalyser.freq = reusableBuffer.spectrumAnalyser.freqDefault * 1000000; - reusableBuffer.spectrumAnalyser.step = reusableBuffer.spectrumAnalyser.span / LCD_W; - reusableBuffer.spectrumAnalyser.dirty = true; - moduleState[g_moduleIdx].mode = MODULE_MODE_SPECTRUM_ANALYSER; - } - - for (uint8_t i=0; i0 ? INVERS|BLINK : INVERS) : 0); - - switch (i) { - case SPECTRUM_FREQUENCY: { - uint16_t frequency = reusableBuffer.spectrumAnalyser.freq / 1000000; - lcdDrawText(MENUS_MARGIN_LEFT, MENU_FOOTER_TOP, "F:", TEXT_INVERTED_COLOR); - lcdDrawNumber(lcdNextPos + 2, MENU_FOOTER_TOP, frequency, attr | TEXT_INVERTED_COLOR); - lcdDrawText(lcdNextPos + 2, MENU_FOOTER_TOP, "MHz", TEXT_INVERTED_COLOR); - if (attr) { - reusableBuffer.spectrumAnalyser.freq = uint32_t(checkIncDec(event, frequency, reusableBuffer.spectrumAnalyser.freqMin, reusableBuffer.spectrumAnalyser.freqMax, 0)) * 1000000; - if (checkIncDec_Ret) { - reusableBuffer.spectrumAnalyser.dirty = true; - } - } - break; - } - - case SPECTRUM_SPAN: - uint8_t span = reusableBuffer.spectrumAnalyser.span / 1000000; - lcdDrawText(MENUS_MARGIN_LEFT + 100, MENU_FOOTER_TOP, "S:", TEXT_INVERTED_COLOR); - lcdDrawNumber(lcdNextPos + 2, MENU_FOOTER_TOP, reusableBuffer.spectrumAnalyser.span/1000000, attr | TEXT_INVERTED_COLOR); - lcdDrawText(lcdNextPos + 2, MENU_FOOTER_TOP, "MHz", TEXT_INVERTED_COLOR); - if (attr) { - reusableBuffer.spectrumAnalyser.span = checkIncDec(event, span, 1, reusableBuffer.spectrumAnalyser.spanMax, 0) * 1000000; - if (checkIncDec_Ret) { - reusableBuffer.spectrumAnalyser.step = reusableBuffer.spectrumAnalyser.span / LCD_W; - reusableBuffer.spectrumAnalyser.dirty = true; - } - } - break; - } - } - - for (uint32_t frequency = ((reusableBuffer.spectrumAnalyser.freq - reusableBuffer.spectrumAnalyser.span / 2) / 10000000) * 10000000 + 10000000; ; frequency += 10000000) { - int offset = frequency - (reusableBuffer.spectrumAnalyser.freq - reusableBuffer.spectrumAnalyser.span / 2); - int x = offset / reusableBuffer.spectrumAnalyser.step; - if (x >= LCD_W - 1) - break; - lcdDrawVerticalLine(x, MENU_HEADER_HEIGHT, LCD_H - MENU_HEADER_HEIGHT - MENU_FOOTER_HEIGHT, STASHED, CURVE_AXIS_COLOR); - } - - for (uint8_t power = 20; ; power += 20) { - int y = MENU_FOOTER_TOP - 1 - limit(0, power << 1, LCD_H - MENU_HEADER_HEIGHT - MENU_FOOTER_HEIGHT); - if (y <= MENU_HEADER_HEIGHT) - break; - lcdDrawHorizontalLine(0, y, LCD_W, STASHED, CURVE_AXIS_COLOR); - } - - coord_t peak_y = LCD_H; - coord_t peak_x = 0; - - coord_t prev_yv = (coord_t)-1; - for (coord_t xv=0; xv(0, reusableBuffer.spectrumAnalyser.bars[xv] << 1, LCD_H - MENU_HEADER_HEIGHT - MENU_FOOTER_HEIGHT); - if (prev_yv != (coord_t)-1) { - if (yv < peak_y) { - peak_x = xv; - peak_y = yv; - } - if (prev_yv < yv) { - for (int y=prev_yv; y<=yv; y+=1) { - lcdDrawPoint(xv, y, TEXT_COLOR); - } - } - else { - for (int y=yv; y<=prev_yv; y+=1) { - lcdDrawPoint(xv, y, TEXT_COLOR); - } - } - } - prev_yv = yv; - } - - prev_yv = (coord_t)-1; - for (coord_t xv=0; xv(0, reusableBuffer.spectrumAnalyser.max[xv] << 1, LCD_H - MENU_HEADER_HEIGHT - MENU_FOOTER_HEIGHT); - if (prev_yv != (coord_t)-1) { - if (prev_yv < yv) { - for (int y=prev_yv; y<=yv; y+=1) { - lcdDrawPoint(xv, y, TEXT_INVERTED_BGCOLOR); - } - } - else { - for (int y=yv; y<=prev_yv; y+=1) { - lcdDrawPoint(xv, y, TEXT_INVERTED_BGCOLOR); - } - } - } - prev_yv = yv; - } - - coord_t y = max(MENU_HEADER_HEIGHT + 1, peak_y - FH); - lcdDrawNumber(limit(20, peak_x, LCD_W - 20), y, ((reusableBuffer.spectrumAnalyser.freq - reusableBuffer.spectrumAnalyser.span / 2) + peak_x * (reusableBuffer.spectrumAnalyser.span / LCD_W)) / 1000000, TINSIZE | CENTERED); - lcdDrawText(lcdNextPos, y, "M", TINSIZE); - - return true; -} diff --git a/radio/src/gui/480x272/radio_tools.cpp b/radio/src/gui/480x272/radio_tools.cpp deleted file mode 100644 index 72dbcf3c2..000000000 --- a/radio/src/gui/480x272/radio_tools.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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 -#include "opentx.h" - -extern uint8_t g_moduleIdx; - -bool addRadioTool(uint8_t index, const char * label) -{ - int8_t sub = menuVerticalPosition - HEADER_LINE; - LcdFlags attr = (sub == index ? INVERS : 0); - coord_t y = MENU_CONTENT_TOP + index * FH; - lcdDrawNumber(MENUS_MARGIN_LEFT, y, index + 1, LEADING0|LEFT, 2); - lcdDrawText(30, y, label, (sub == index ? INVERS : 0)); - if (attr && s_editMode > 0) { - s_editMode = 0; - killAllEvents(); - return true; - } - return false; -} - -void addRadioModuleTool(uint8_t index, const char * label, bool (* tool)(event_t), uint8_t module) -{ - if (addRadioTool(index, label)) { - g_moduleIdx = module; - pushMenu(tool); - } -} - -#if defined(LUA) -void addRadioScriptTool(uint8_t index, const char * path) -{ - char toolName[TOOL_NAME_MAXLEN + 1]; - const char * label; - char * ext = (char *)getFileExtension(path); - if (readToolName(toolName, path)) { - label = toolName; - } - else { - *ext = '\0'; - label = getBasename(path); - } - - if (addRadioTool(index, label)) { - f_chdir("/SCRIPTS/TOOLS/"); - *ext = '.'; - luaExec(path); - } -} -#endif - -bool menuRadioTools(event_t event) -{ - if (event == EVT_ENTRY || event == EVT_ENTRY_UP) { - memclear(&reusableBuffer.radioTools, sizeof(reusableBuffer.radioTools)); -#if defined(PXX2) - for (uint8_t module = 0; module < NUM_MODULES; module++) { - if (isModulePXX2(module) && (module == INTERNAL_MODULE ? IS_INTERNAL_MODULE_ON() : IS_EXTERNAL_MODULE_ON())) { - moduleState[module].readModuleInformation(&reusableBuffer.radioTools.modules[module], PXX2_HW_INFO_TX_ID, PXX2_HW_INFO_TX_ID); - } - } -#endif - } - - SIMPLE_MENU(STR_MENUTOOLS, RADIO_ICONS, menuTabGeneral, MENU_RADIO_TOOLS, HEADER_LINE + reusableBuffer.radioTools.linesCount); - - uint8_t index = 0; - -#if defined(PXX2) - if (isPXX2ModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE].information.modelID, MODULE_OPTION_SPECTRUM_ANALYSER)) - addRadioModuleTool(index++, STR_SPECTRUM_ANALYSER_INT, menuRadioSpectrumAnalyser, INTERNAL_MODULE); - - if (isPXX2ModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE].information.modelID, MODULE_OPTION_POWER_METER)) - addRadioModuleTool(index++, STR_POWER_METER_INT, menuRadioPowerMeter, INTERNAL_MODULE); - - if (isPXX2ModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE].information.modelID, MODULE_OPTION_SPECTRUM_ANALYSER)) - addRadioModuleTool(index++, STR_SPECTRUM_ANALYSER_EXT, menuRadioSpectrumAnalyser, EXTERNAL_MODULE); - - if (isPXX2ModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE].information.modelID, MODULE_OPTION_POWER_METER)) - addRadioModuleTool(index++, STR_POWER_METER_EXT, menuRadioPowerMeter, EXTERNAL_MODULE); -#endif - -#if defined(LUA) - FILINFO fno; - DIR dir; - -#if defined(CROSSFIRE) - if(isFileAvailable(SCRIPTS_TOOLS_PATH "/CROSSFIRE/crossfire.lua")) - addRadioScriptTool(index++, SCRIPTS_TOOLS_PATH "/CROSSFIRE/crossfire.lua"); -#endif - - FRESULT res = f_opendir(&dir, SCRIPTS_TOOLS_PATH); - if (res == FR_OK) { - for (;;) { - TCHAR path[_MAX_LFN+1] = SCRIPTS_TOOLS_PATH "/"; - res = f_readdir(&dir, &fno); /* Read a directory item */ - if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ - if (fno.fattrib & AM_DIR) continue; /* Skip subfolders */ - if (fno.fattrib & AM_HID) continue; /* Skip hidden files */ - if (fno.fattrib & AM_SYS) continue; /* Skip system files */ - - strcat(path, fno.fname); - if (isRadioScriptTool(fno.fname)) - addRadioScriptTool(index++, path); - } - } -#endif - - if (index == 0) { - lcdDrawCenteredText(LCD_H/2, STR_NO_TOOLS); - } - - reusableBuffer.radioTools.linesCount = index; - - return true; -} \ No newline at end of file diff --git a/radio/src/gui/480x272/radio_trainer.cpp b/radio/src/gui/480x272/radio_trainer.cpp deleted file mode 100644 index 48c6305ef..000000000 --- a/radio/src/gui/480x272/radio_trainer.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -#define TRAINER_COLUMN_WIDTH 60 -#define TRAINER_COLUMN_1 MENUS_MARGIN_LEFT+100 -#define TRAINER_COLUMN_2 TRAINER_COLUMN_1+TRAINER_COLUMN_WIDTH -#define TRAINER_COLUMN_3 TRAINER_COLUMN_2+TRAINER_COLUMN_WIDTH - -bool menuRadioTrainer(event_t event) -{ - uint8_t y; - bool slave = SLAVE_MODE(); - - MENU(STR_MENUTRAINER, RADIO_ICONS, menuTabGeneral, MENU_RADIO_TRAINER, (slave ? 0 : 6), { NAVIGATION_LINE_BY_LINE|2, NAVIGATION_LINE_BY_LINE|2, NAVIGATION_LINE_BY_LINE|2, NAVIGATION_LINE_BY_LINE|2, 0, 0}); - - if (slave) { - lcdDrawText(LCD_W/2, 5*FH, STR_SLAVE, CENTERED|TEXT_COLOR); - return true; - } - - LcdFlags attr; - LcdFlags blink = ((s_editMode>0) ? BLINK|INVERS : INVERS); - - /* lcdDrawText(TRAINER_COLUMN_1, MENU_HEADER_HEIGHT+1, "Mode", HEADER_COLOR); - lcdDrawText(TRAINER_COLUMN_2, MENU_HEADER_HEIGHT+1, "Weight", HEADER_COLOR); - lcdDrawText(TRAINER_COLUMN_3, MENU_HEADER_HEIGHT+1, "Source", HEADER_COLOR); - */ - - y = MENU_CONTENT_TOP; - - for (uint8_t i=0; imode, attr); - if (attr&BLINK) CHECK_INCDEC_GENVAR(event, td->mode, 0, 2); - break; - - case 1: - lcdDrawNumber(TRAINER_COLUMN_2, y, td->studWeight, LEFT|attr, 0, NULL, "%"); - if (attr&BLINK) CHECK_INCDEC_GENVAR(event, td->studWeight, -125, 125); - break; - - case 2: - lcdDrawTextAtIndex(TRAINER_COLUMN_3, y, STR_TRNCHN, td->srcChn, attr); - if (attr&BLINK) CHECK_INCDEC_GENVAR(event, td->srcChn, 0, 3); - break; - } - } - y += FH; - } - - attr = (menuVerticalPosition==4) ? blink : 0; - lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 5*FH, STR_MULTIPLIER); - lcdDrawNumber(TRAINER_COLUMN_1, MENU_CONTENT_TOP + 5*FH, g_eeGeneral.PPM_Multiplier+10, LEFT|attr|PREC1); - if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.PPM_Multiplier, -10, 40); - - attr = (menuVerticalPosition==5) ? INVERS : 0; - if (attr) s_editMode = 0; - lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 6*FH, STR_CAL, attr); - for (int i=0; i<4; i++) { -#if defined (PPM_UNIT_PERCENT_PREC1) - lcdDrawNumber(TRAINER_COLUMN_1+i*TRAINER_COLUMN_WIDTH, MENU_CONTENT_TOP + 6*FH, (ppmInput[i]-g_eeGeneral.trainer.calib[i])*2, LEFT|PREC1); -#else - lcdDrawNumber(TRAINER_COLUMN_1+i*TRAINER_COLUMN_WIDTH, MENU_CONTENT_TOP + 6*FH, (ppmInput[i]-g_eeGeneral.trainer.calib[i])/5, LEFT); -#endif - } - - if (attr) { - if (event==EVT_KEY_LONG(KEY_ENTER)){ - memcpy(g_eeGeneral.trainer.calib, ppmInput, sizeof(g_eeGeneral.trainer.calib)); - storageDirty(EE_GENERAL); - AUDIO_WARNING1(); - } - } - - return true; -} diff --git a/radio/src/gui/480x272/radio_version.cpp b/radio/src/gui/480x272/radio_version.cpp deleted file mode 100644 index 33ab73607..000000000 --- a/radio/src/gui/480x272/radio_version.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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 "options.h" - -#if defined(PXX2) -constexpr coord_t COLUMN2_X = 200; - -void drawPXX2Version(coord_t x, coord_t y, PXX2Version version) -{ - if (version.major == 0xFF && version.minor == 0x0F && version.revision == 0x0F) { - lcdDrawText(x, y, "---"); - } - else { - lcdDrawNumber(x, y, 1 + version.major, LEFT); - lcdDrawChar(lcdNextPos, y, '.'); - lcdDrawNumber(lcdNextPos, y, version.minor, LEFT); - lcdDrawChar(lcdNextPos, y, '.'); - lcdDrawNumber(lcdNextPos, y, version.revision, LEFT); - } -} - -void drawPXX2FullVersion(coord_t x, coord_t y, PXX2Version hwVersion, PXX2Version swVersion) -{ - drawPXX2Version(x, y, hwVersion); - lcdDrawText(lcdNextPos, y, "/"); - drawPXX2Version(lcdNextPos, y, swVersion); -} - -bool menuRadioModulesVersion(event_t event) -{ - if (menuEvent) { - moduleState[INTERNAL_MODULE].mode = MODULE_MODE_NORMAL; - moduleState[EXTERNAL_MODULE].mode = MODULE_MODE_NORMAL; - return false; - } - - drawMenuTemplate(STR_MENU_MODULES_RX_VERSION, 0, RADIO_ICONS, OPTION_MENU_TITLE_BAR); - - if (event == EVT_ENTRY) { - memclear(&reusableBuffer.hardwareAndSettings.modules, sizeof(reusableBuffer.hardwareAndSettings.modules)); - } - - if (event == EVT_ENTRY || get_tmr10ms() >= reusableBuffer.hardwareAndSettings.updateTime) { - if (isModulePXX2(INTERNAL_MODULE) && IS_INTERNAL_MODULE_ON()) { - moduleState[INTERNAL_MODULE].readModuleInformation(&reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE], PXX2_HW_INFO_TX_ID, PXX2_MAX_RECEIVERS_PER_MODULE - 1); - } - - if (isModulePXX2(EXTERNAL_MODULE) && IS_EXTERNAL_MODULE_ON()) { - moduleState[EXTERNAL_MODULE].readModuleInformation(&reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE], PXX2_HW_INFO_TX_ID, PXX2_MAX_RECEIVERS_PER_MODULE - 1); - } - - reusableBuffer.hardwareAndSettings.updateTime = get_tmr10ms() + 1000 /* 10s*/; - } - - coord_t y = MENU_BODY_TOP - menuVerticalOffset * FH; - - for (uint8_t module=0; module= MENU_BODY_TOP && y < MENU_FOOTER_TOP) { - if (module == INTERNAL_MODULE) - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_INTERNAL_MODULE); - else if (module == EXTERNAL_MODULE) - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_EXTERNAL_MODULE); - } - y += FH; - - // Module model - if (y >= MENU_BODY_TOP && y < MENU_FOOTER_TOP) { - lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_MODULE); - if (module == INTERNAL_MODULE) { - if (!IS_INTERNAL_MODULE_ON()) { - lcdDrawText(COLUMN2_X, y, STR_OFF); - y += FH; - continue; - } - if (!isModulePXX2(INTERNAL_MODULE)) { - lcdDrawText(COLUMN2_X, y, STR_NO_INFORMATION); - y += FH; - continue; - } - } - else if (module == EXTERNAL_MODULE) { - if (!IS_EXTERNAL_MODULE_ON()) { - lcdDrawText(COLUMN2_X, y, STR_OFF); - y += FH; - continue; - } - if (!isModulePXX2(EXTERNAL_MODULE)) { - lcdDrawText(COLUMN2_X, y, STR_NO_INFORMATION); - y += FH; - continue; - } - } - uint8_t modelId = reusableBuffer.hardwareAndSettings.modules[module].information.modelID; - lcdDrawText(COLUMN2_X, y, getPXX2ModuleName(modelId)); - } - y += FH; - - // Module version - if (y >= MENU_BODY_TOP && y < MENU_FOOTER_TOP) { - if (reusableBuffer.hardwareAndSettings.modules[module].information.modelID) { - drawPXX2FullVersion(COLUMN2_X, y, reusableBuffer.hardwareAndSettings.modules[module].information.hwVersion, reusableBuffer.hardwareAndSettings.modules[module].information.swVersion); - static const char * variants[] = {"FCC", "EU", "FLEX"}; - uint8_t variant = reusableBuffer.hardwareAndSettings.modules[module].information.variant - 1; - if (variant < DIM(variants)) { - lcdDrawText(lcdNextPos + 1, y, variants[variant]); - } - } - } - y += FH; - - for (uint8_t receiver=0; receiver= MENU_BODY_TOP && y < MENU_FOOTER_TOP) { - lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_RECEIVER); - lcdDrawNumber(lcdNextPos + 2, y, receiver + 1); - uint8_t modelId = reusableBuffer.hardwareAndSettings.modules[module].receivers[receiver].information.modelID; - lcdDrawText(COLUMN2_X, y, getPXX2ReceiverName(modelId)); - } - y += FH; - - // Receiver version - if (y >= MENU_BODY_TOP && y < MENU_FOOTER_TOP) { - drawPXX2FullVersion(COLUMN2_X, y, reusableBuffer.hardwareAndSettings.modules[module].receivers[receiver].information.hwVersion, reusableBuffer.hardwareAndSettings.modules[module].receivers[receiver].information.swVersion); - } - y += FH; - } - } - } - - uint8_t lines = (y - (FH + 1)) / FH + menuVerticalOffset; - if (lines > NUM_BODY_LINES) { - drawVerticalScrollbar(LCD_W-1, FH, LCD_H-FH, menuVerticalOffset, lines, NUM_BODY_LINES); - } - - switch(event) { - case EVT_ROTARY_LEFT: - if (lines > NUM_BODY_LINES) { - if (menuVerticalOffset-- == 0) - menuVerticalOffset = lines - 1; - } - break; - - case EVT_ROTARY_RIGHT: - if (lines > NUM_BODY_LINES) { - if (++menuVerticalOffset + NUM_BODY_LINES > lines) - menuVerticalOffset = 0; - } - break; - - case EVT_KEY_BREAK(KEY_EXIT): - if (menuVerticalOffset != 0) - menuVerticalOffset = 0; - else - popMenu(); - break; - } - - return true; -} -#endif - -bool menuRadioVersion(event_t event) -{ - char id[27]; - getCPUUniqueID(id); - - SIMPLE_MENU(STR_MENUVERSION, RADIO_ICONS, menuTabGeneral, MENU_RADIO_VERSION, 1); - - lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP, vers_stamp); - lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + FH, date_stamp); - lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 2*FH, time_stamp); - lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 3*FH, eeprom_stamp); - lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 4*FH, "UID:"); - lcdDrawText(MENUS_MARGIN_LEFT + 64, MENU_CONTENT_TOP + 4*FH, id); - - coord_t y = MENU_CONTENT_TOP + 5*FH; - lcdDrawText(MENUS_MARGIN_LEFT, y, "OPTS:"); - lcdNextPos = MENUS_MARGIN_LEFT + 64; - - for (uint8_t i=0; options[i]; i++) { - const char * option = options[i]; - coord_t width = getTextWidth(option); - - if ((lcdNextPos + 5 + width) > LCD_W) { - lcdDrawText(lcdNextPos, y, ","); - lcdNextPos = MENUS_MARGIN_LEFT; - y += FH; - } - if (i > 0 && lcdNextPos !=MENUS_MARGIN_LEFT) { - lcdDrawText(lcdNextPos, y, ", "); - } - lcdDrawText(lcdNextPos, y, option); - } - -#if defined(PXX2) - y += FH + FH / 2; - - lcdDrawText(MENUS_MARGIN_LEFT, y, BUTTON(TR_MODULES_RX_VERSION), menuVerticalPosition == 0 ? INVERS : 0); - y += FH; - if (menuVerticalPosition == 0 && event == EVT_KEY_BREAK(KEY_ENTER)) { - s_editMode = EDIT_SELECT_FIELD; - pushMenu(menuRadioModulesVersion); - } -#endif - - return true; -} diff --git a/radio/src/gui/480x272/themes/darkblue.cpp b/radio/src/gui/480x272/themes/darkblue.cpp deleted file mode 100644 index 017175c77..000000000 --- a/radio/src/gui/480x272/themes/darkblue.cpp +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -class DarkblueTheme: public Theme -{ - public: - DarkblueTheme(): - Theme("Darkblue") - { - } - - void loadColors() const - { - lcdColorTable[TEXT_COLOR_INDEX] = WHITE; - lcdColorTable[TEXT_BGCOLOR_INDEX] = RGB(10, 78, 121); - lcdColorTable[TEXT_INVERTED_COLOR_INDEX] = WHITE; - lcdColorTable[TEXT_INVERTED_BGCOLOR_INDEX] = RGB(32, 34, 42); - lcdColorTable[TEXT_STATUSBAR_COLOR_INDEX] = WHITE; - lcdColorTable[LINE_COLOR_INDEX] = GREY; - lcdColorTable[SCROLLBOX_COLOR_INDEX] = WHITE; - lcdColorTable[MENU_TITLE_BGCOLOR_INDEX] = DARKGREY; - lcdColorTable[MENU_TITLE_COLOR_INDEX] = WHITE; - lcdColorTable[MENU_TITLE_DISABLE_COLOR_INDEX] = BLACK; - lcdColorTable[HEADER_COLOR_INDEX] = RGB(32, 34, 42); - lcdColorTable[ALARM_COLOR_INDEX] = RGB(32, 34, 42); - lcdColorTable[WARNING_COLOR_INDEX] = YELLOW; - lcdColorTable[TEXT_DISABLE_COLOR_INDEX] = GREY; - lcdColorTable[CURVE_AXIS_COLOR_INDEX] = LIGHTGREY; - lcdColorTable[CURVE_COLOR_INDEX] = RGB(32, 34, 42); - lcdColorTable[CURVE_CURSOR_COLOR_INDEX] = RGB(32, 34, 42); - lcdColorTable[TITLE_BGCOLOR_INDEX] = RGB(32, 34, 42); - lcdColorTable[TRIM_BGCOLOR_INDEX] = RGB(32, 34, 42); - lcdColorTable[TRIM_SHADOW_COLOR_INDEX] = RGB(100, 100, 100); - lcdColorTable[MAINVIEW_PANES_COLOR_INDEX] = GREY; - lcdColorTable[MAINVIEW_GRAPHICS_COLOR_INDEX] = WHITE; - lcdColorTable[HEADER_BGCOLOR_INDEX] = BLACK; - lcdColorTable[HEADER_ICON_BGCOLOR_INDEX] = BLACK; - lcdColorTable[HEADER_CURRENT_BGCOLOR_INDEX] = RGB(10, 78, 121); - lcdColorTable[OVERLAY_COLOR_INDEX] = BLACK; - lcdColorTable[BARGRAPH1_COLOR_INDEX] = RGB(15, 150, 250); - lcdColorTable[BARGRAPH2_COLOR_INDEX] = RGB(167, 167, 167); - lcdColorTable[BARGRAPH_BGCOLOR_INDEX] = RGB(97, 97, 102); - } - - void loadMenuIcon(uint8_t index, const char * filename) const - { - BitmapBuffer * mask = BitmapBuffer::loadMask(getThemePath(filename)); - if (mask) { - menuIconNormal[index] = new BitmapBuffer(BMP_RGB565, mask->getWidth(), mask->getHeight()); - if (menuIconNormal[index]) { - menuIconNormal[index]->clear(HEADER_BGCOLOR); - menuIconNormal[index]->drawMask(0, 0, mask, HEADER_CURRENT_BGCOLOR); - } - menuIconSelected[index] = new BitmapBuffer(BMP_RGB565, mask->getWidth(), mask->getHeight()); - if (menuIconSelected[index]) { - menuIconSelected[index]->clear(HEADER_BGCOLOR); - menuIconSelected[index]->drawMask(0, 0, mask, MENU_TITLE_COLOR); - } - } - } - - void loadMenusIcons() const - { - loadMenuIcon(ICON_OPENTX, "mask_opentx.png"); - loadMenuIcon(ICON_RADIO, "mask_menu_radio.png"); - loadMenuIcon(ICON_RADIO_SETUP, "mask_radio_setup.png"); - loadMenuIcon(ICON_RADIO_SD_MANAGER, "mask_radio_sd_browser.png"); - loadMenuIcon(ICON_RADIO_TOOLS, "mask_radio_tools.png"); - loadMenuIcon(ICON_RADIO_SPECTRUM_ANALYSER, "/mask_radio_spectrum_analyser.png"); - loadMenuIcon(ICON_RADIO_GLOBAL_FUNCTIONS, "mask_radio_global_functions.png"); - loadMenuIcon(ICON_RADIO_TRAINER, "mask_radio_trainer.png"); - loadMenuIcon(ICON_RADIO_HARDWARE, "mask_radio_hardware.png"); - loadMenuIcon(ICON_RADIO_VERSION, "mask_radio_version.png"); - loadMenuIcon(ICON_MODEL, "mask_menu_model.png"); - loadMenuIcon(ICON_MODEL_SETUP, "mask_model_setup.png"); - loadMenuIcon(ICON_MODEL_HELI, "mask_model_heli.png"); - loadMenuIcon(ICON_MODEL_FLIGHT_MODES, "mask_model_flight_modes.png"); - loadMenuIcon(ICON_MODEL_INPUTS, "mask_model_inputs.png"); - loadMenuIcon(ICON_MODEL_MIXER, "mask_model_mixer.png"); - loadMenuIcon(ICON_MODEL_OUTPUTS, "mask_model_outputs.png"); - loadMenuIcon(ICON_MODEL_CURVES, "mask_model_curves.png"); - loadMenuIcon(ICON_MODEL_GVARS, "mask_model_gvars.png"); - loadMenuIcon(ICON_MODEL_LOGICAL_SWITCHES, "mask_model_logical_switches.png"); - loadMenuIcon(ICON_MODEL_SPECIAL_FUNCTIONS, "mask_model_special_functions.png"); - loadMenuIcon(ICON_MODEL_LUA_SCRIPTS, "mask_model_lua_scripts.png"); - loadMenuIcon(ICON_MODEL_TELEMETRY, "mask_model_telemetry.png"); - loadMenuIcon(ICON_STATS, "mask_menu_stats.png"); - loadMenuIcon(ICON_STATS_THROTTLE_GRAPH, "mask_stats_throttle_graph.png"); - loadMenuIcon(ICON_STATS_TIMERS, "mask_stats_timers.png"); - loadMenuIcon(ICON_STATS_ANALOGS, "mask_stats_analogs.png"); - loadMenuIcon(ICON_STATS_DEBUG, "mask_stats_debug.png"); - loadMenuIcon(ICON_THEME, "mask_menu_theme.png"); - loadMenuIcon(ICON_THEME_SETUP, "mask_theme_setup.png"); - loadMenuIcon(ICON_THEME_VIEW1, "mask_theme_view1.png"); - loadMenuIcon(ICON_THEME_VIEW2, "mask_theme_view2.png"); - loadMenuIcon(ICON_THEME_VIEW3, "mask_theme_view3.png"); - loadMenuIcon(ICON_THEME_VIEW4, "mask_theme_view4.png"); - loadMenuIcon(ICON_THEME_VIEW5, "mask_theme_view5.png"); - loadMenuIcon(ICON_THEME_ADD_VIEW, "mask_theme_add_view.png"); - loadMenuIcon(ICON_MONITOR, "mask_monitor.png"); - loadMenuIcon(ICON_MONITOR_CHANNELS1, "mask_monitor_channels1.png"); - loadMenuIcon(ICON_MONITOR_CHANNELS2, "mask_monitor_channels2.png"); - loadMenuIcon(ICON_MONITOR_CHANNELS3, "mask_monitor_channels3.png"); - loadMenuIcon(ICON_MONITOR_CHANNELS4, "mask_monitor_channels4.png"); - loadMenuIcon(ICON_MONITOR_LOGICAL_SWITCHES, "/mask_monitor_logsw.png"); - } - - void loadThemeBitmaps() const - { - // Calibration screen - delete calibStick; - calibStick = BitmapBuffer::load(getThemePath("stick_pointer.png")); - - delete calibStickBackground; - calibStickBackground = BitmapBuffer::load(getThemePath("stick_background.png")); - - delete calibTrackpBackground; - calibTrackpBackground = BitmapBuffer::load(getThemePath("trackp_background.png")); - - delete calibHorus; -#if defined(PCBX10) - if (STICKS_PWM_ENABLED()) - calibHorus = BitmapBuffer::load(getThemePath("X10S.bmp")); - else - calibHorus = BitmapBuffer::load(getThemePath("X10.bmp")); -#else - calibHorus = BitmapBuffer::load(getThemePath("horus.bmp")); -#endif - - // Channels monitor screen - delete chanMonLockedBitmap; - chanMonLockedBitmap = BitmapBuffer::loadMaskOnBackground("mask_monitor_lockch.png", TEXT_COLOR, TEXT_BGCOLOR); - - delete chanMonInvertedBitmap; - chanMonInvertedBitmap = BitmapBuffer::loadMaskOnBackground("mask_monitor_inver.png", TEXT_COLOR, TEXT_BGCOLOR); - - // Model Selection screen - delete modelselIconBitmap; - modelselIconBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_iconback.png", TITLE_BGCOLOR, TEXT_BGCOLOR); - if (modelselIconBitmap) { - BitmapBuffer * bitmap = BitmapBuffer::load(getThemePath("modelsel/icon_default.png")); - modelselIconBitmap->drawBitmap(20, 8, bitmap); - delete bitmap; - } - - delete modelselSdFreeBitmap; - modelselSdFreeBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_sdfree.png", TEXT_COLOR, TEXT_BGCOLOR); - - delete modelselModelQtyBitmap; - modelselModelQtyBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_modelqty.png", TEXT_COLOR, TEXT_BGCOLOR); - - delete modelselModelNameBitmap; - modelselModelNameBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_modelname.png", TEXT_COLOR, TEXT_BGCOLOR); - - delete modelselModelMoveBackground; - modelselModelMoveBackground = BitmapBuffer::loadMask(getThemePath("modelsel/mask_moveback.png")); - - delete modelselModelMoveIcon; - modelselModelMoveIcon = BitmapBuffer::loadMask(getThemePath("modelsel/mask_moveico.png")); - - delete modelselWizardBackground; - modelselWizardBackground = BitmapBuffer::load(getThemePath("wizard/background.png")); - - - // Mixer setup screen - delete mixerSetupMixerBitmap; - mixerSetupMixerBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_mixer.png", MENU_TITLE_COLOR, HEADER_BGCOLOR); - - delete mixerSetupToBitmap; - mixerSetupToBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_to.png", TEXT_BGCOLOR, HEADER_BGCOLOR); - - delete mixerSetupOutputBitmap; - mixerSetupOutputBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_output.png", MENU_TITLE_COLOR, HEADER_BGCOLOR); - - delete mixerSetupAddBitmap; - mixerSetupAddBitmap = BitmapBuffer::loadMaskOnBackground("mask_mplex_add.png", TEXT_COLOR, TEXT_BGCOLOR); - - delete mixerSetupMultiBitmap; - mixerSetupMultiBitmap = BitmapBuffer::loadMaskOnBackground("mask_mplex_multi.png", TEXT_COLOR, TEXT_BGCOLOR); - - delete mixerSetupReplaceBitmap; - mixerSetupReplaceBitmap = BitmapBuffer::loadMaskOnBackground("mask_mplex_replace.png", TEXT_COLOR, TEXT_BGCOLOR); - - delete mixerSetupLabelBitmap; - mixerSetupLabelBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_label.png", TEXT_COLOR, TEXT_BGCOLOR); - - delete mixerSetupCurveBitmap; - mixerSetupCurveBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_curve.png", TEXT_COLOR, TEXT_BGCOLOR); - - delete mixerSetupSwitchBitmap; - mixerSetupSwitchBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_switch.png", TEXT_COLOR, TEXT_BGCOLOR); - - delete mixerSetupSlowBitmap; - mixerSetupSlowBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_slow.png", TEXT_COLOR, TEXT_BGCOLOR); - - delete mixerSetupDelayBitmap; - mixerSetupDelayBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_delay.png", TEXT_COLOR, TEXT_BGCOLOR); - - delete mixerSetupDelaySlowBitmap; - mixerSetupDelaySlowBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_delayslow.png", TEXT_COLOR, TEXT_BGCOLOR); - - delete mixerSetupFlightmodeBitmap; - mixerSetupFlightmodeBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_fm.png", TEXT_COLOR, TEXT_BGCOLOR); - } - - virtual void load() const - { - loadColors(); - loadMenusIcons(); - loadThemeBitmaps(); - loadFontCache(); - } - - void drawTopbarBackground(uint8_t icon) const - { - lcdDrawSolidFilledRect(0, 0, LCD_W, MENU_HEADER_HEIGHT, HEADER_BGCOLOR); - lcdDrawSolidFilledRect(0, 0, 41, MENU_HEADER_HEIGHT, HEADER_ICON_BGCOLOR); - - if (icon == ICON_OPENTX) { - static BitmapBuffer * thumb = BitmapBuffer::load(getFilePath("topmenu_opentx.bmp")); - lcd->drawBitmap(5, 7, thumb); - } - else { - lcd->drawBitmap(5, 7, menuIconSelected[icon]); - } - - drawTopbarDatetime(); - } - - virtual void drawMenuIcon(uint8_t index, uint8_t position, bool selected) const - { - lcd->drawBitmap(50+position*MENU_ICONS_SPACING, 7, selected ? menuIconSelected[index] : menuIconNormal[index]); - } - - protected: - static BitmapBuffer * menuIconNormal[MENUS_ICONS_COUNT]; - static BitmapBuffer * menuIconSelected[MENUS_ICONS_COUNT]; -}; - -BitmapBuffer * DarkblueTheme::menuIconNormal[MENUS_ICONS_COUNT] = { NULL }; -BitmapBuffer * DarkblueTheme::menuIconSelected[MENUS_ICONS_COUNT] = { NULL }; - -const DarkblueTheme darkblueTheme; diff --git a/radio/src/gui/480x272/view_main.cpp b/radio/src/gui/480x272/view_main.cpp deleted file mode 100644 index d39f1b25d..000000000 --- a/radio/src/gui/480x272/view_main.cpp +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -#define TRIM_LH_X 10 -#define TRIM_LV_X 24 -#define TRIM_RV_X (LCD_W-35) -#define TRIM_RH_X (LCD_W-175) -#define TRIM_V_Y 55 -#define TRIM_H_Y (LCD_H-37) -#define TRIM_LEN 80 -#define POTS_LINE_Y (LCD_H-20) - -Layout * customScreens[MAX_CUSTOM_SCREENS] = { 0, 0, 0, 0, 0 }; -Topbar * topbar; - -void drawMainPots() -{ - // The 3 pots - drawHorizontalSlider(TRIM_LH_X, POTS_LINE_Y, 160, calibratedAnalogs[CALIBRATED_POT1], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); - if (IS_POT_MULTIPOS(POT2)) - drawHorizontalSlider(LCD_W/2-20, POTS_LINE_Y, XPOTS_MULTIPOS_COUNT*5, 1 + (potsPos[1] & 0x0f), 1, XPOTS_MULTIPOS_COUNT + 1, XPOTS_MULTIPOS_COUNT, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_NUMBER_BUTTON); - else - drawHorizontalSlider(LCD_W/2-40, POTS_LINE_Y, 80, calibratedAnalogs[CALIBRATED_POT2], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); - drawHorizontalSlider(TRIM_RH_X, POTS_LINE_Y, 160, calibratedAnalogs[CALIBRATED_POT3], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); - - // The 2 rear sliders - drawVerticalSlider(6, TRIM_V_Y, 160, calibratedAnalogs[CALIBRATED_SLIDER_REAR_LEFT], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); - drawVerticalSlider(LCD_W-18, TRIM_V_Y, 160, calibratedAnalogs[CALIBRATED_SLIDER_REAR_RIGHT], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); -} - -void drawTrims(uint8_t flightMode) -{ - for (uint8_t i=0; i<4; i++) { - static const coord_t x[4] = { TRIM_LH_X, TRIM_LV_X, TRIM_RV_X, TRIM_RH_X }; - static uint8_t vert[4] = {0, 1, 1, 0}; - unsigned int stickIndex = CONVERT_MODE(i); - coord_t xm = x[stickIndex]; - int32_t trim = getTrimValue(flightMode, i); - - - if(getRawTrimValue(flightMode, i).mode == TRIM_MODE_NONE) - continue; - - if (vert[i]) { - if (g_model.extendedTrims == 1) { - drawVerticalSlider(xm, TRIM_V_Y, 160, trim, TRIM_EXTENDED_MIN, TRIM_EXTENDED_MAX, 0, OPTION_SLIDER_EMPTY_BAR|OPTION_SLIDER_TRIM_BUTTON); - } - else { - drawVerticalSlider(xm, TRIM_V_Y, 160, trim, TRIM_MIN, TRIM_MAX, 0, OPTION_SLIDER_EMPTY_BAR|OPTION_SLIDER_TRIM_BUTTON); - } - if (g_model.displayTrims != DISPLAY_TRIMS_NEVER && trim != 0) { - if (g_model.displayTrims == DISPLAY_TRIMS_ALWAYS || (trimsDisplayTimer > 0 && (trimsDisplayMask & (1< 0 && (trimsDisplayMask & (1<0 ? -TRIM_LEN/2 : TRIM_LEN/2); - lcdDrawNumber(x, TRIM_H_Y+2, trim, TINSIZE | CENTERED); - } - } - } - } -} - -void onMainViewMenu(const char *result) -{ - if (result == STR_MODEL_SELECT) { - chainMenu(menuModelSelect); - } - else if (result == STR_RESET_TIMER1) { - timerReset(0); - } - else if (result == STR_RESET_TIMER2) { - timerReset(1); - } - else if (result == STR_RESET_TIMER3) { - timerReset(2); - } - else if (result == STR_VIEW_NOTES) { - pushModelNotes(); - } - else if (result == STR_RESET_SUBMENU) { - POPUP_MENU_ADD_ITEM(STR_RESET_FLIGHT); - POPUP_MENU_ADD_ITEM(STR_RESET_TIMER1); - POPUP_MENU_ADD_ITEM(STR_RESET_TIMER2); - POPUP_MENU_ADD_ITEM(STR_RESET_TIMER3); - POPUP_MENU_ADD_ITEM(STR_RESET_TELEMETRY); - } - else if (result == STR_RESET_TELEMETRY) { - telemetryReset(); - } - else if (result == STR_RESET_FLIGHT) { - flightReset(); - } - else if (result == STR_STATISTICS) { - pushMenu(menuTabStats[0]); - } - else if (result == STR_SETUP_SCREENS) { - pushMenu(menuTabScreensSetup[1]); - } - else if (result == STR_ABOUT_US) { - chainMenu(menuAboutView); - } - else if (result == STR_MONITOR_SCREENS) { - pushMenu(menuTabMonitors[lastMonitorPage]); - } -} - -int getMainViewsCount() -{ - for (int index=1; index 0) { - gvarDisplayTimer = 0; - } -#endif - break; - } - - if (g_model.view >= getMainViewsCount()) { - g_model.view = 0; - } - - for (uint8_t i=0; irefresh(); - else - customScreens[i]->background(); - } - } - - return true; -} - -#if 0 -bool menuMainViewChannelsMonitor(event_t event) -{ - switch (event) { - case EVT_KEY_BREAK(KEY_EXIT): - chainMenu(menuMainView); - event = 0; - return false; - } - - return menuChannelsView(event); -} -#endif diff --git a/radio/src/gui/CMakeLists.txt b/radio/src/gui/CMakeLists.txt index 48d14adad..b16fd4b58 100644 --- a/radio/src/gui/CMakeLists.txt +++ b/radio/src/gui/CMakeLists.txt @@ -1,12 +1,17 @@ include(gui/${GUI_DIR}/CMakeLists.txt) include_directories(gui gui/${GUI_DIR}) +if(NOT GUI_DIR STREQUAL colorlcd) + set(SRC + ${SRC} + gui/navigation/common.cpp + gui/navigation/navigation_${NAVIGATION_TYPE}.cpp + ) + string(TOUPPER ${NAVIGATION_TYPE} NAVIGATION_TYPE) + add_definitions(-DNAVIGATION_${NAVIGATION_TYPE}) +endif() + set(SRC ${SRC} - gui/navigation/common.cpp - gui/navigation/navigation_${NAVIGATION_TYPE}.cpp gui/screenshot.cpp ) - -string(TOUPPER ${NAVIGATION_TYPE} NAVIGATION_TYPE) -add_definitions(-DNAVIGATION_${NAVIGATION_TYPE}) diff --git a/radio/src/gui/colorlcd/CMakeLists.txt b/radio/src/gui/colorlcd/CMakeLists.txt new file mode 100644 index 000000000..8b7668b50 --- /dev/null +++ b/radio/src/gui/colorlcd/CMakeLists.txt @@ -0,0 +1,91 @@ +include_directories( + ${RADIO_SRC_DIRECTORY}/gui/colorlcd + ${RADIO_SRC_DIRECTORY}/gui/colorlcd/layouts + ${RADIO_SRC_DIRECTORY}/fonts/truetype +) + +file(GLOB THEMES_SRC RELATIVE ${RADIO_SRC_DIRECTORY}/gui/colorlcd ${RADIO_SRC_DIRECTORY}/gui/colorlcd/themes/*.cpp) +file(GLOB LAYOUTS_SRC RELATIVE ${RADIO_SRC_DIRECTORY}/gui/colorlcd ${RADIO_SRC_DIRECTORY}/gui/colorlcd/layouts/*.cpp) +file(GLOB WIDGETS_SRC RELATIVE ${RADIO_SRC_DIRECTORY}/gui/colorlcd ${RADIO_SRC_DIRECTORY}/gui/colorlcd/widgets/*.cpp) + +set(GUI_SRC + ${GUI_SRC} + lcd.cpp + splash.cpp + fonts.cpp + curves.cpp + bitmaps.cpp + + theme.cpp + # topbar.cpp + # layout.cpp + # widget.cpp + ${THEMES_SRC} + # ${LAYOUTS_SRC} + # ${WIDGETS_SRC} + rle.cpp + # popups.cpp + draw_functions.cpp + # menus.cpp + menu_model.cpp + # model_select.cpp + model_setup.cpp + model_flightmodes.cpp + model_inputs.cpp + model_mixes.cpp + model_outputs.cpp + model_curves.cpp + model_gvars.cpp + model_logical_switches.cpp + special_functions.cpp + model_telemetry.cpp + menu_radio.cpp + radio_setup.cpp + radio_sdmanager.cpp + radio_tools.cpp + radio_trainer.cpp + radio_version.cpp + radio_hardware.cpp + radio_calibration.cpp + view_main.cpp + # view_statistics.cpp + ) + +macro(add_gui_src src) + set(GUI_SRC + ${GUI_SRC} + ${src} + ) +endmacro(add_gui_src) + +if(HELI) + add_gui_src(model_heli.cpp) +endif() + +if(PXX2 OR LUA) + add_gui_src(radio_tools.cpp) +endif() + +if(PXX2) + add_gui_src(radio_spectrum_analyser.cpp) +endif() + +if(LUA STREQUAL YES) + # TODO set(SRC ${SRC} lua/widgets.cpp) + # TODO set(GUI_SRC ${GUI_SRC} model_custom_scripts.cpp) +endif() + +# gui/${GUI_DIR}/modelslist.cpp + +set(SRC + ${SRC} + stubs.cpp + ) + +# includes libopenui +include(gui/libopenui/CMakeLists.txt) +include_directories(gui/libopenui) +add_definitions(-DLIBOPENUI) +foreach(LIBOPENUI_FILE ${LIBOPENUI_SRC}) + set(SRC ${SRC} gui/libopenui/${LIBOPENUI_FILE}) +endforeach() diff --git a/radio/src/gui/480x272/bitmaps.cpp b/radio/src/gui/colorlcd/bitmaps.cpp similarity index 97% rename from radio/src/gui/480x272/bitmaps.cpp rename to radio/src/gui/colorlcd/bitmaps.cpp index 288e8c40d..f15311c55 100644 --- a/radio/src/gui/480x272/bitmaps.cpp +++ b/radio/src/gui/colorlcd/bitmaps.cpp @@ -183,7 +183,7 @@ const uint8_t LBM_SWIPE_RIGHT[] = { BitmapBuffer * calibStick = NULL; BitmapBuffer * calibStickBackground = NULL; BitmapBuffer * calibTrackpBackground = NULL; -BitmapBuffer * calibHorus = NULL; +BitmapBuffer * calibRadioPict = NULL; BitmapBuffer * modelselIconBitmap = NULL; BitmapBuffer * modelselSdFreeBitmap = NULL; BitmapBuffer * modelselModelQtyBitmap = NULL; @@ -201,8 +201,10 @@ BitmapBuffer * mixerSetupMultiBitmap = NULL; BitmapBuffer * mixerSetupReplaceBitmap = NULL; BitmapBuffer * mixerSetupLabelBitmap = NULL; BitmapBuffer * mixerSetupCurveBitmap = NULL; -BitmapBuffer * mixerSetupSwitchBitmap = NULL; +BitmapBuffer * mixerSetupSwitchIcon = NULL; BitmapBuffer * mixerSetupDelayBitmap = NULL; BitmapBuffer * mixerSetupSlowBitmap = NULL; BitmapBuffer * mixerSetupDelaySlowBitmap = NULL; +BitmapBuffer * mixerSetupFlightmodeIcon = NULL; BitmapBuffer * mixerSetupFlightmodeBitmap = NULL; +BitmapBuffer * mixerSetupSwitchBitmap = NULL; diff --git a/radio/src/gui/480x272/bitmaps.h b/radio/src/gui/colorlcd/bitmaps.h similarity index 96% rename from radio/src/gui/480x272/bitmaps.h rename to radio/src/gui/colorlcd/bitmaps.h index 60e38b948..8b5fce9f8 100644 --- a/radio/src/gui/480x272/bitmaps.h +++ b/radio/src/gui/colorlcd/bitmaps.h @@ -83,7 +83,7 @@ extern BitmapBuffer * modelselWizardBackground; extern BitmapBuffer * calibStick; extern BitmapBuffer * calibStickBackground; extern BitmapBuffer * calibTrackpBackground; -extern BitmapBuffer * calibHorus; +extern BitmapBuffer * calibRadioPict; // Channels monitor bitmaps extern BitmapBuffer * chanMonLockedBitmap; @@ -97,11 +97,13 @@ extern BitmapBuffer * mixerSetupAddBitmap; extern BitmapBuffer * mixerSetupMultiBitmap; extern BitmapBuffer * mixerSetupReplaceBitmap; extern BitmapBuffer * mixerSetupLabelBitmap; -extern BitmapBuffer * mixerSetupFlightmodeBitmap; +extern BitmapBuffer * mixerSetupFlightmodeIcon; extern BitmapBuffer * mixerSetupCurveBitmap; -extern BitmapBuffer * mixerSetupSwitchBitmap; +extern BitmapBuffer * mixerSetupSwitchIcon; extern BitmapBuffer * mixerSetupDelayBitmap; extern BitmapBuffer * mixerSetupSlowBitmap; extern BitmapBuffer * mixerSetupDelaySlowBitmap; +extern BitmapBuffer * mixerSetupFlightmodeBitmap; +extern BitmapBuffer * mixerSetupSwitchBitmap; #endif // _BITMAPS_H_ diff --git a/radio/src/gui/480x272/colors.h b/radio/src/gui/colorlcd/colors.h similarity index 96% rename from radio/src/gui/480x272/colors.h rename to radio/src/gui/colorlcd/colors.h index 5d5bf8131..ca2493117 100644 --- a/radio/src/gui/480x272/colors.h +++ b/radio/src/gui/colorlcd/colors.h @@ -32,6 +32,7 @@ #define BLACK RGB(0, 0, 0) #define YELLOW RGB(0xF0, 0xD0, 0x10) #define BLUE RGB(0x30, 0xA0, 0xE0) +#define DARKBLUE RGB(0, 0x35, 0x67) #define GREY RGB(96, 96, 96) #define DARKGREY RGB(64, 64, 64) #define LIGHTGREY RGB(180, 180, 180) @@ -79,7 +80,7 @@ enum LcdColorIndex extern uint16_t lcdColorTable[LCD_COLOR_COUNT]; -#define COLOR(index) ((index) << 16) +#define COLOR(index) LcdFlags((index) << 16) #define COLOR_IDX(att) uint8_t((att) >> 16) #define TEXT_COLOR COLOR(TEXT_COLOR_INDEX) @@ -105,7 +106,7 @@ extern uint16_t lcdColorTable[LCD_COLOR_COUNT]; #define TRIM_SHADOW_COLOR COLOR(TRIM_SHADOW_COLOR_INDEX) #define HEADER_BGCOLOR COLOR(HEADER_BGCOLOR_INDEX) #define HEADER_ICON_BGCOLOR COLOR(HEADER_ICON_BGCOLOR_INDEX) -#define HEADER_CURRENT_BGCOLOR COLOR(HEADER_CURRENT_BGCOLOR_INDEX) +constexpr LcdFlags HEADER_CURRENT_BGCOLOR = COLOR(HEADER_CURRENT_BGCOLOR_INDEX); #define MAINVIEW_PANES_COLOR COLOR(MAINVIEW_PANES_COLOR_INDEX) #define MAINVIEW_GRAPHICS_COLOR COLOR(MAINVIEW_GRAPHICS_COLOR_INDEX) #define OVERLAY_COLOR COLOR(OVERLAY_COLOR_INDEX) diff --git a/radio/src/gui/480x272/curves.cpp b/radio/src/gui/colorlcd/curves.cpp similarity index 100% rename from radio/src/gui/480x272/curves.cpp rename to radio/src/gui/colorlcd/curves.cpp diff --git a/radio/src/gui/480x272/widgets.cpp b/radio/src/gui/colorlcd/draw_functions.cpp similarity index 52% rename from radio/src/gui/480x272/widgets.cpp rename to radio/src/gui/colorlcd/draw_functions.cpp index 0e1dd5416..6aa055b37 100644 --- a/radio/src/gui/480x272/widgets.cpp +++ b/radio/src/gui/colorlcd/draw_functions.cpp @@ -33,37 +33,6 @@ void drawColumnHeader(const char * const * headers, const char * const * descrip } } -static const uint8_t __alpha_button_on[] __ALIGNED(4) = { -#include "alpha_button_on.lbm" -}; -Bitmap ALPHA_BUTTON_ON(BMP_ARGB4444, (const uint16_t*)__alpha_button_on); - -static const uint8_t __alpha_button_off[] __ALIGNED(4) = { -#include "alpha_button_off.lbm" -}; -Bitmap ALPHA_BUTTON_OFF(BMP_ARGB4444, (const uint16_t*)__alpha_button_off); - -void drawButton(coord_t x, coord_t y, const char * label, LcdFlags attr) -{ - int width = getTextWidth(label, 0, 0); - int padding = 0; - if (attr & (BUTTON_OFF|BUTTON_ON)) { - padding = 5; - } - if (attr & INVERS) { - lcdDrawSolidFilledRect(x, y, padding+width+16+padding, 19, TEXT_INVERTED_BGCOLOR); - lcdDrawText(x+padding+8, y, label, TEXT_INVERTED_COLOR); - } - else { - lcdDrawText(x+padding+8, y, label, TEXT_COLOR); - } - lcdDrawSolidRect(x-1, y-1, padding+width+18+padding, 21, 1, TEXT_COLOR); - if (attr & BUTTON_OFF) - lcd->drawBitmap(x-6, y+3, &ALPHA_BUTTON_OFF); - else if (attr & BUTTON_ON) - lcd->drawBitmap(x-6, y+3, &ALPHA_BUTTON_ON); -} - void drawCheckBox(coord_t x, coord_t y, uint8_t value, LcdFlags attr) { if (attr) { @@ -96,92 +65,6 @@ void drawVerticalScrollbar(coord_t x, coord_t y, coord_t h, uint16_t offset, uin } } -void drawHorizontalScrollbar(coord_t x, coord_t y, coord_t w, uint16_t offset, uint16_t count, uint8_t visible) -{ - if (visible < count) { - lcdDrawSolidHorizontalLine(x, y, w, LINE_COLOR); - coord_t xofs = (w*offset + count/2) / count; - coord_t xhgt = (w*visible + count/2) / count; - if (xhgt + xofs > w) - xhgt = w - xofs; - lcdDrawSolidFilledRect(x+xofs, y-1, xhgt, 3, SCROLLBOX_COLOR); - } -} - -void drawProgressScreen(const char * title, const char * message, int num, int den) -{ - lcdClear(); - lcdSetColor(WHITE); - if (message) { - lcdDrawText(MENUS_MARGIN_LEFT, LCD_H-42, message, CUSTOM_COLOR); - } - lcdDrawRect(MENUS_MARGIN_LEFT, LCD_H-22, LCD_W-2*MENUS_MARGIN_LEFT, 15, 1, SOLID, CUSTOM_COLOR); - lcdSetColor(RED); - if (num > 0 && den > 0) { - int width = ((LCD_W-2*MENUS_MARGIN_LEFT-4) * num) / den; - lcdDrawSolidFilledRect(MENUS_MARGIN_LEFT+2, LCD_H-20, width, 11, CUSTOM_COLOR); - } - lcdRefresh(); -} - -void drawShadow(coord_t x, coord_t y, coord_t w, coord_t h) -{ - lcdDrawSolidVerticalLine(x+w, y+1, h, TEXT_COLOR); - lcdDrawSolidHorizontalLine(x+1, y+h, w, TEXT_COLOR); - lcdDrawSolidVerticalLine(x+w+1, y+2, h, LINE_COLOR); - lcdDrawSolidHorizontalLine(x+2, y+h+1, w, LINE_COLOR); -} - -void drawMenuTemplate(const char * title, uint8_t icon, const uint8_t * icons, uint32_t options) -{ - coord_t bodyTop, bodyBottom; - - theme->drawTopbarBackground(icons ? icons[0] : icon); - - // Menu title bar - if (options & OPTION_MENU_TITLE_BAR) { - linesDisplayed = NUM_BODY_LINES; - bodyTop = MENU_BODY_TOP; - lcdDrawSolidFilledRect(0, MENU_HEADER_HEIGHT, LCD_W, MENU_TITLE_TOP - MENU_HEADER_HEIGHT, TEXT_BGCOLOR); - lcdDrawSolidFilledRect(0, MENU_TITLE_TOP, LCD_W, MENU_TITLE_HEIGHT, TITLE_BGCOLOR); - if (title) { - lcdDrawText(MENUS_MARGIN_LEFT, MENU_TITLE_TOP+1, title, MENU_TITLE_COLOR); - } - if (icons) { - for (int i=0; idrawMenuIcon(icons[i+1], i, false); - } - } - theme->drawMenuIcon(icons[menuPageIndex+1], menuPageIndex, true); - } - } - else { - linesDisplayed = NUM_BODY_LINES + 1; - bodyTop = MENU_HEADER_HEIGHT; - if (title) { - lcdDrawText(50, 3, title, MENU_TITLE_COLOR); - } - } - - // Footer - if (options & OPTION_MENU_NO_FOOTER) { - bodyBottom = LCD_H; - } - else { - bodyBottom = MENU_FOOTER_TOP; - lcdDrawSolidFilledRect(0, MENU_FOOTER_TOP, LCD_W, MENU_FOOTER_HEIGHT, HEADER_BGCOLOR); - } - - // Body - lcdDrawSolidFilledRect(0, bodyTop, LCD_W, bodyBottom-bodyTop, TEXT_BGCOLOR); - - // Scrollbar - if (!(options & OPTION_MENU_NO_SCROLLBAR) && linesCount>linesDisplayed) { - drawVerticalScrollbar(DEFAULT_SCROLLBAR_X, bodyTop+3, bodyBottom-bodyTop-6, menuVerticalOffset, linesCount, linesDisplayed); - } -} - void drawTrimSquare(coord_t x, coord_t y) { lcdDrawSolidFilledRect(x-2, y, 15, 15, TRIM_BGCOLOR); @@ -306,21 +189,13 @@ void drawHorizontalSlider(coord_t x, coord_t y, int len, int val, int min, int m } } -void drawSlider(coord_t x, coord_t y, int len, int val, int min, int max, uint8_t steps, uint32_t options) -{ - if (options & OPTION_SLIDER_VERTICAL) - drawVerticalSlider(x, y, len, val, min, max, steps, options); - else - drawHorizontalSlider(x, y, len, val, min, max, steps, options); -} - #if defined(GVARS) bool noZero(int val) { return val != 0; } -void drawGVarValue(coord_t x, coord_t y, uint8_t gvar, gvar_t value, LcdFlags flags) +void drawGVarValue(BitmapBuffer * dc, coord_t x, coord_t y, uint8_t gvar, gvar_t value, LcdFlags flags) { uint8_t prec = g_model.gvars[gvar].prec; if (prec > 0) { @@ -371,22 +246,24 @@ int16_t editGVarFieldValue(coord_t x, coord_t y, int16_t value, int16_t min, int return value; } #else -int16_t editGVarFieldValue(coord_t x, coord_t y, int16_t value, int16_t min, int16_t max, LcdFlags attr, event_t event) +void drawValueOrGVar(BitmapBuffer * dc, coord_t x, coord_t y, gvar_t value, LcdFlags flags) { - if (attr & INVERS) value = checkIncDec(event, value, min, max, EE_MODEL); - lcdDrawNumber(x, y, value, attr, 0, NULL, "%"); - return value; + drawNumber(dc, x, y, value, flags, 0, nullptr, "%"); } #endif void drawSleepBitmap() { + lcd->setOffset(0, 0); + lcd->clearClippingRect(); lcd->clear(); + const BitmapBuffer * bitmap = BitmapBuffer::load(getThemePath("sleep.bmp")); if (bitmap) { lcd->drawBitmap((LCD_W-bitmap->getWidth())/2, (LCD_H-bitmap->getHeight())/2, bitmap); delete bitmap; } + lcdRefresh(); } @@ -395,13 +272,13 @@ void drawShutdownAnimation(uint32_t duration, uint32_t totalDuration, const char { if (totalDuration == 0) return; - + static uint32_t lastDuration = 0xffffffff; static const BitmapBuffer * shutdown = BitmapBuffer::load(getThemePath("shutdown.bmp")); if (shutdown) { if (duration < lastDuration) { - theme->drawBackground(); + theme->drawBackground(lcd); lcd->drawBitmap((LCD_W-shutdown->getWidth())/2, (LCD_H-shutdown->getHeight())/2, shutdown); lcdStoreBackupBuffer(); } @@ -428,20 +305,300 @@ void drawShutdownAnimation(uint32_t duration, uint32_t totalDuration, const char lastDuration = duration; } -void drawReceiverName(coord_t x, coord_t y, uint8_t moduleIdx, uint8_t receiverIdx, LcdFlags flags) +void drawCurveRef(BitmapBuffer * dc, coord_t x, coord_t y, const CurveRef & curve, LcdFlags flags) { - if (isModulePXX2(moduleIdx)) { - if (g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx][0] != '\0') - lcdDrawSizedText(x, y, g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], effectiveLen(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], PXX2_LEN_RX_NAME), flags); - else - lcdDrawText(x, y, "---", flags); - } -#if defined(HARDWARE_INTERNAL_MODULE) - else if (moduleIdx == INTERNAL_MODULE) { - lcdDrawText(x, y, "Internal", flags); - } -#endif - else { - lcdDrawText(x, y, "External", flags); + if (curve.value != 0) { + switch (curve.type) { + case CURVE_REF_DIFF: + dc->drawText(x, y, "D", flags); + drawValueOrGVar(dc, lcdNextPos + 1, y + 2, curve.value, LEFT|SMLSIZE|flags); + break; + + case CURVE_REF_EXPO: + dc->drawText(x, y, "E", flags); + drawValueOrGVar(dc, lcdNextPos + 1, y + 2, curve.value, LEFT|SMLSIZE|flags); + break; + + case CURVE_REF_FUNC: + drawTextAtIndex(dc, x, y, STR_VCURVEFUNC, curve.value, flags); + break; + + case CURVE_REF_CUSTOM: + dc->drawText(x, y, getCurveString(curve.value), flags); + break; + } } } + +void putsStickName(coord_t x, coord_t y, uint8_t idx, LcdFlags att) +{ + uint8_t length = STR_VSRCRAW[0]; + lcdDrawSizedText(x, y, STR_VSRCRAW+2+length*(idx+1), length-1, att); +} + +void putsModelName(coord_t x, coord_t y, char * name, uint8_t id, LcdFlags att) +{ + uint8_t len = sizeof(g_model.header.name); + while (len>0 && !name[len-1]) --len; + if (len==0) { + drawStringWithIndex(x, y, STR_MODEL, id+1, att|LEADING0); + } + else { + lcdDrawSizedText(x, y, name, sizeof(g_model.header.name), ZCHAR|att); + } +} + +void drawCurveName(coord_t x, coord_t y, int8_t idx, LcdFlags flags) +{ + char s[8]; + getCurveString(s, idx); + lcdDrawText(x, y, s, flags); +} + +void drawSource(BitmapBuffer * dc, coord_t x, coord_t y, mixsrc_t idx, LcdFlags flags) +{ + char s[16]; + getSourceString(s, idx); + dc->drawText(x, y, s, flags); +} + +void drawSwitch(BitmapBuffer * dc, coord_t x, coord_t y, int32_t idx, LcdFlags flags) +{ + dc->drawText(x, y, getSwitchPositionName(idx), flags); +} + +void drawTrimMode(BitmapBuffer * dc, coord_t x, coord_t y, uint8_t phase, uint8_t idx, LcdFlags att) +{ + trim_t v = getRawTrimValue(phase, idx); + unsigned int mode = v.mode; + unsigned int p = mode >> 1; + + if (mode == TRIM_MODE_NONE) { + dc->drawText(x, y, "--", att); + } + else { + char s[2]; + s[0] = (mode % 2 == 0) ? ':' : '+'; + s[1] = '0'+p; + dc->drawSizedText(x, y, s, 2, att); + } +} + +void drawDate(BitmapBuffer * dc, coord_t x, coord_t y, TelemetryItem & telemetryItem, LcdFlags att) +{ + // TODO + if (att & DBLSIZE) { + x -= 42; + att &= ~FONTSIZE_MASK; + lcdDrawNumber(x, y, telemetryItem.datetime.day, att|LEADING0|LEFT, 2); + lcdDrawChar(lcdNextPos-1, y, '-', att); + lcdDrawNumber(lcdNextPos-1, y, telemetryItem.datetime.month, att|LEFT, 2); + lcdDrawChar(lcdNextPos-1, y, '-', att); + lcdDrawNumber(lcdNextPos-1, y, telemetryItem.datetime.year-2000, att|LEFT); + y += FH; + lcdDrawNumber(x, y, telemetryItem.datetime.hour, att|LEADING0|LEFT, 2); + lcdDrawChar(lcdNextPos, y, ':', att); + lcdDrawNumber(lcdNextPos, y, telemetryItem.datetime.min, att|LEADING0|LEFT, 2); + lcdDrawChar(lcdNextPos, y, ':', att); + lcdDrawNumber(lcdNextPos, y, telemetryItem.datetime.sec, att|LEADING0|LEFT, 2); + } + else { + lcdDrawNumber(x, y, telemetryItem.datetime.day, att|LEADING0|LEFT, 2); + lcdDrawChar(lcdNextPos-1, y, '-', att); + lcdDrawNumber(lcdNextPos, y, telemetryItem.datetime.month, att|LEFT, 2); + lcdDrawChar(lcdNextPos-1, y, '-', att); + lcdDrawNumber(lcdNextPos, y, telemetryItem.datetime.year-2000, att|LEFT); + lcdDrawNumber(lcdNextPos+11, y, telemetryItem.datetime.hour, att|LEADING0|LEFT, 2); + lcdDrawChar(lcdNextPos, y, ':', att); + lcdDrawNumber(lcdNextPos, y, telemetryItem.datetime.min, att|LEADING0|LEFT, 2); + lcdDrawChar(lcdNextPos, y, ':', att); + lcdDrawNumber(lcdNextPos, y, telemetryItem.datetime.sec, att|LEADING0|LEFT, 2); + } +} + +void drawGPSCoord(coord_t x, coord_t y, int32_t value, const char * direction, LcdFlags flags, bool seconds=true) +{ + char s[32]; + uint32_t absvalue = abs(value); + char * tmp = strAppendUnsigned(s, absvalue / 1000000); + *tmp++ = '@'; + absvalue = absvalue % 1000000; + absvalue *= 60; + if (g_eeGeneral.gpsFormat == 0 || !seconds) { + tmp = strAppendUnsigned(tmp, absvalue / 1000000, 2); + *tmp++ = '\''; + if (seconds) { + absvalue %= 1000000; + absvalue *= 60; + absvalue /= 100000; + tmp = strAppendUnsigned(tmp, absvalue / 10); + *tmp++ = '.'; + tmp = strAppendUnsigned(tmp, absvalue % 10); + *tmp++ = '"'; + } + } + else { + tmp = strAppendUnsigned(tmp, absvalue / 1000000, 2); + *tmp++ = '.'; + absvalue /= 1000; + tmp = strAppendUnsigned(tmp, absvalue, 3); + } + *tmp++ = direction[value>=0 ? 0 : 1]; + *tmp = '\0'; + lcdDrawText(x, y, s, flags); +} + +void drawGPSPosition(coord_t x, coord_t y, int32_t longitude, int32_t latitude, LcdFlags flags) +{ + if (flags & EXPANDED) { + drawGPSCoord(x, y, latitude, "NS", flags, true); + drawGPSCoord(x, y + FH, longitude, "EW", flags, true); + } + else { + drawGPSCoord(x, y, latitude, "NS", flags, false); + drawGPSCoord(lcdNextPos+5, y, longitude, "EW", flags, false); + } +} + +void drawGPSSensorValue(coord_t x, coord_t y, TelemetryItem & telemetryItem, LcdFlags flags) +{ + drawGPSPosition(x, y, telemetryItem.gps.longitude, telemetryItem.gps.latitude, flags); +} + + +void drawSensorCustomValue(BitmapBuffer * dc, coord_t x, coord_t y, uint8_t sensor, int32_t value, LcdFlags flags) +{ + if (sensor >= MAX_TELEMETRY_SENSORS) { + // Lua luaLcdDrawChannel() can call us with a bad value + return; + } + + TelemetryItem & telemetryItem = telemetryItems[sensor]; + TelemetrySensor & telemetrySensor = g_model.telemetrySensors[sensor]; + + if (telemetrySensor.unit == UNIT_DATETIME) { + drawDate(dc, x, y, telemetryItem, flags); + } + else if (telemetrySensor.unit == UNIT_GPS) { + drawGPSSensorValue(x, y, telemetryItem, flags); + } + else if (telemetrySensor.unit == UNIT_BITFIELD) { + if (IS_FRSKY_SPORT_PROTOCOL()) { + if (telemetrySensor.id >= RBOX_STATE_FIRST_ID && telemetrySensor.id <= RBOX_STATE_LAST_ID) { + if (telemetrySensor.subId == 0) { + if (value == 0) { + lcdDrawText(x, y, "OK", flags); + } + else { + for (uint8_t i=0; i<16; i++) { + if (value & (1 << i)) { + char s[] = "CH__ KO"; + strAppendUnsigned(&s[2], i+1, 2); + dc->drawText(x, flags & DBLSIZE ? y+1 : y, s, flags & ~DBLSIZE); + break; + } + } + } + } + else { + if (value == 0) { + dc->drawText(x, flags & DBLSIZE ? y+1 : y, "Rx OK", flags & ~DBLSIZE); + } + else { + static const char * const RXS_STATUS[] = { + "Rx1 Ovl", + "Rx2 Ovl", + "SBUS Ovl", + "Rx1 FS", + "Rx1 LF", + "Rx2 FS", + "Rx2 LF", + "Rx1 Lost", + "Rx2 Lost", + "Rx1 NS", + "Rx2 NS", + }; + for (uint8_t i=0; idrawText(x, flags & DBLSIZE ? y+1 : y, RXS_STATUS[i], flags & ~DBLSIZE); + break; + } + } + } + } + } + } + } + else if (telemetrySensor.unit == UNIT_TEXT) { + dc->drawSizedText(x, flags & DBLSIZE ? y+1 : y, telemetryItem.text, sizeof(telemetryItem.text), flags & ~DBLSIZE); + } + else { + if (telemetrySensor.prec > 0) { + flags |= (telemetrySensor.prec==1 ? PREC1 : PREC2); + } + drawValueWithUnit(dc, x, y, value, telemetrySensor.unit == UNIT_CELLS ? UNIT_VOLTS : telemetrySensor.unit, flags); + } +} + +void drawSourceCustomValue(BitmapBuffer * dc, coord_t x, coord_t y, source_t source, int32_t value, LcdFlags flags) +{ + if (source >= MIXSRC_FIRST_TELEM) { + source = (source-MIXSRC_FIRST_TELEM) / 3; + drawSensorCustomValue(dc, y, source, value, flags); + } + else if (source >= MIXSRC_FIRST_TIMER || source == MIXSRC_TX_TIME) { + if (value < 0) flags |= BLINK|INVERS; + // TODO drawTimer(dc, x, y, value, flags); + } + else if (source == MIXSRC_TX_VOLTAGE) { + lcdDrawNumber(x, y, value, flags|PREC1); + } +#if defined(INTERNAL_GPS) + else if (source == MIXSRC_TX_GPS) { + if (gpsData.fix) { + drawGPSPosition(x, y, gpsData.longitude, gpsData.latitude, flags); + } + else { + lcdDrawText(x, y, "sats: ", flags); + lcdDrawNumber(lcdNextPos, y, gpsData.numSat, flags); + } + } +#endif +#if defined(GVARS) + else if (source >= MIXSRC_FIRST_GVAR && source <= MIXSRC_LAST_GVAR) { + drawGVarValue(x, y, source - MIXSRC_FIRST_GVAR, value, flags); + } +#endif + else if (source < MIXSRC_FIRST_CH) { + lcdDrawNumber(x, y, calcRESXto100(value), flags); + } + else if (source <= MIXSRC_LAST_CH) { +#if defined(PPM_UNIT_PERCENT_PREC1) + lcdDrawNumber(x, y, calcRESXto1000(value), flags|PREC1); +#else + lcdDrawNumber(x, y, calcRESXto100(value), flags); +#endif + } + else { + lcdDrawNumber(x, y, value, flags); + } +} + +void drawValueWithUnit(BitmapBuffer * dc, coord_t x, coord_t y, int val, uint8_t unit, LcdFlags flags) +{ + // convertUnit(val, unit); + drawNumber(dc, x, y, val, flags & (~NO_UNIT)); + if (!(flags & NO_UNIT) && unit != UNIT_RAW) { + drawTextAtIndex(dc, lcdNextPos/*+1*/, y, STR_VTELEMUNIT, unit, 0); + } +} + +void drawHexNumber(BitmapBuffer * dc, coord_t x, coord_t y, uint32_t val, LcdFlags flags) +{ + for (int i=12; i>=0; i-=4) { + char c = (val >> i) & 0xf; + c = c>9 ? c+'A'-10 : c+'0'; + dc->drawSizedText(x, y, &c, 1, flags); + x = lcdNextPos; + } +} \ No newline at end of file diff --git a/radio/src/gui/480x272/draw_functions.h b/radio/src/gui/colorlcd/draw_functions.h similarity index 68% rename from radio/src/gui/480x272/draw_functions.h rename to radio/src/gui/colorlcd/draw_functions.h index 0d872d87e..b505fb22e 100644 --- a/radio/src/gui/480x272/draw_functions.h +++ b/radio/src/gui/colorlcd/draw_functions.h @@ -22,12 +22,14 @@ #define _DRAW_FUNCTIONS_H_ #include "opentx.h" -#include "common/colorlcd/draw_functions.h" +#include #define OPTION_MENU_NO_FOOTER 0x01 #define OPTION_MENU_TITLE_BAR 0x02 #define OPTION_MENU_NO_SCROLLBAR 0x04 +#define OPTION_SLIDER_INVERS INVERS +#define OPTION_SLIDER_BLINK BLINK #define OPTION_SLIDER_VERTICAL 0x04 #define OPTION_SLIDER_EMPTY_BAR 0x08 #define OPTION_SLIDER_DBL_COLOR 0x10 @@ -39,43 +41,34 @@ void drawStatusText(const char * text); void drawColumnHeader(const char * const * headers, const char * const * descriptions, uint8_t index); -void drawTopbarDatetime(); - -#define BUTTON_ON 0x10 -#define BUTTON_OFF 0x20 -void drawButton(coord_t x, coord_t y, const char * label, LcdFlags attr); void drawCheckBox(coord_t x, coord_t y, uint8_t value, LcdFlags attr); void drawVerticalScrollbar(coord_t x, coord_t y, coord_t h, uint16_t offset, uint16_t count, uint8_t visible); -void drawHorizontalScrollbar(coord_t x, coord_t y, coord_t w, uint16_t offset, uint16_t count, uint8_t visible); void drawProgressScreen(const char * title, const char * message, int num, int den); -void drawShadow(coord_t x, coord_t y, coord_t w, coord_t h); void drawTrimSquare(coord_t x, coord_t y); void drawHorizontalTrimPosition(coord_t x, coord_t y, int16_t dir); void drawVerticalTrimPosition(coord_t x, coord_t y, int16_t dir); void drawVerticalSlider(coord_t x, coord_t y, int len, int val, int min, int max, uint8_t steps, uint32_t options); void drawHorizontalSlider(coord_t x, coord_t y, int len, int val, int min, int max, uint8_t steps, uint32_t options); void drawSlider(coord_t x, coord_t y, int len, int val, int min, int max, uint8_t steps, uint32_t options); -#define drawStatusLine(...) - -void drawGVarValue(coord_t x, coord_t y, uint8_t gvar, gvar_t value, LcdFlags flags); - -#if defined(GVARS) -int16_t editGVarFieldValue(coord_t x, coord_t y, int16_t value, int16_t min, int16_t max, LcdFlags attr, uint8_t editflags, event_t event); -#else -int16_t editGVarFieldValue(coord_t x, coord_t y, int16_t value, int16_t min, int16_t max, LcdFlags attr, event_t event); -#endif +void drawValueOrGVar(BitmapBuffer * dc, coord_t x, coord_t y, gvar_t gvar, LcdFlags flags = 0); +void drawGVarValue(BitmapBuffer * dc, coord_t x, coord_t y, uint8_t gvar, gvar_t value, LcdFlags flags = 0); +void drawSourceCustomValue(BitmapBuffer * dc, coord_t x, coord_t y, source_t source, int32_t value, LcdFlags flags); +void drawSensorCustomValue(BitmapBuffer * dc, coord_t x, coord_t y, uint8_t sensor, int32_t value, LcdFlags flags=0); +void drawDate(BitmapBuffer * dc, coord_t x, coord_t y, TelemetryItem & telemetryItem, LcdFlags flags = 0); +void drawValueWithUnit(BitmapBuffer * dc, coord_t x, coord_t y, int val, uint8_t unit, LcdFlags flags = 0); +void drawHexNumber(BitmapBuffer * dc, coord_t x, coord_t y, uint32_t val, LcdFlags flags = 0); +inline void drawChn(BitmapBuffer * dc, coord_t x, coord_t y, uint8_t idx, LcdFlags flags) +{ + drawSource(dc, x, y, MIXSRC_CH1 + idx - 1, flags); +} // Screen templates -void drawMenuTemplate(const char * title, uint8_t icon, const uint8_t * icons=nullptr, uint32_t options=0); void drawSplash(); void drawSleepBitmap(); -void drawShutdownAnimation(uint32_t duration, uint32_t totalDuration, const char * message); +void drawShutdownAnimation(uint32_t index, const char * message); // Main view standard widgets -void drawTopBar(); void drawMainPots(); void drawTrims(uint8_t flightMode); -void drawReceiverName(coord_t x, coord_t y, uint8_t moduleIdx, uint8_t receiverIdx, LcdFlags flags); - #endif // _DRAW_FUNCTIONS_H_ diff --git a/radio/src/gui/480x272/fonts.cpp b/radio/src/gui/colorlcd/fonts.cpp similarity index 71% rename from radio/src/gui/480x272/fonts.cpp rename to radio/src/gui/colorlcd/fonts.cpp index 20245f081..ac5315d65 100644 --- a/radio/src/gui/480x272/fonts.cpp +++ b/radio/src/gui/colorlcd/fonts.cpp @@ -21,66 +21,88 @@ #include "opentx.h" #include "rle.h" -#if !defined(BOOT) const uint16_t font_tinsize_specs[] = { -#include "font_tinsize.specs" +#include "font_9.specs" }; const unsigned char font_tinsize[] = { -#include "font_tinsize.lbm" +#include "font_9.lbm" }; +#if defined(HARDWARE_TOUCH) const uint16_t font_smlsize_specs[] = { -#include "font_smlsize.specs" +#include "font_15.specs" }; const unsigned char font_smlsize[] = { -#include "font_smlsize.lbm" +#include "font_15.lbm" }; -#endif const uint16_t font_stdsize_specs[] = { -#include "font_stdsize.specs" +#include "font_17.specs" }; const unsigned char font_stdsize[] = { -#include "font_stdsize.lbm" -}; - -#if !defined(BOOT) -const uint16_t font_midsize_specs[] = { -#include "font_midsize.specs" -}; - -const unsigned char font_midsize[] = { -#include "font_midsize.lbm" -}; - -const uint16_t font_dblsize_specs[] = { -#include "font_dblsize.specs" -}; - -const unsigned char font_dblsize[] = { -#include "font_dblsize.lbm" -}; - -const uint16_t font_xxlsize_specs[] = { -#include "font_xxlsize.specs" -}; - -const unsigned char font_xxlsize[] = { -#include "font_xxlsize.lbm" +#include "font_17.lbm" }; const uint16_t font_stdsizebold_specs[] = { -#include "font_stdsizebold.specs" +#include "font_bold17.specs" }; const unsigned char font_stdsizebold[] = { -#include "font_stdsizebold.lbm" +#include "font_bold17.lbm" +}; +#else +const uint16_t font_smlsize_specs[] = { +#include "font_13.specs" +}; + +const unsigned char font_smlsize[] = { +#include "font_13.lbm" +}; + +const uint16_t font_stdsize_specs[] = { +#include "font_16.specs" +}; + +const unsigned char font_stdsize[] = { +#include "font_16.lbm" +}; + +const uint16_t font_stdsizebold_specs[] = { +#include "font_bold16.specs" +}; + +const unsigned char font_stdsizebold[] = { +#include "font_bold16.lbm" }; #endif +const uint16_t font_midsize_specs[] = { +#include "font_24.specs" +}; + +const unsigned char font_midsize[] = { +#include "font_24.lbm" +}; + +const uint16_t font_dblsize_specs[] = { +#include "font_32.specs" +}; + +const unsigned char font_dblsize[] = { +#include "font_32.lbm" +}; + +const uint16_t font_xxlsize_specs[] = { +#include "font_64.specs" +}; + +const unsigned char font_xxlsize[] = { +#include "font_64.lbm" +}; + #if !defined(BOOT) const uint16_t * const fontspecsTable[16] = { font_stdsize_specs, font_tinsize_specs, font_smlsize_specs, font_midsize_specs, font_dblsize_specs, font_xxlsize_specs, font_stdsize_specs, font_stdsize_specs, @@ -91,37 +113,12 @@ const uint8_t * fontsTable[16] = { font_stdsize, font_tinsize, font_smlsize, font_midsize, font_dblsize, font_xxlsize, font_stdsize, font_stdsize, font_stdsizebold, font_tinsize, font_smlsize, font_midsize, font_dblsize, font_xxlsize, font_stdsize, font_stdsize }; - #else const uint16_t * const fontspecsTable[1] = { font_stdsize_specs }; const uint8_t * fontsTable[1] = { font_stdsize }; #endif -BitmapBuffer * fontCache[2] = { NULL, NULL }; - -BitmapBuffer * createFontCache(const uint8_t * font, LcdFlags fg, LcdFlags bg) -{ - coord_t width = *((uint16_t *)font); - coord_t height = *(((uint16_t *)font)+1); - - BitmapBuffer * buffer = new BitmapBuffer(BMP_RGB565, width, height); - if (buffer) { - buffer->clear(bg); - buffer->drawBitmapPattern(0, 0, font, fg); - } - return buffer; -} - -void loadFontCache() -{ - delete fontCache[0]; - delete fontCache[1]; - fontCache[0] = createFontCache(fontsTable[0], TEXT_COLOR, TEXT_BGCOLOR); - fontCache[1] = createFontCache(fontsTable[0], TEXT_INVERTED_COLOR, TEXT_INVERTED_BGCOLOR); -} - - -static uint8_t* decompressFont(const uint8_t* font) +uint8_t * decompressFont(const uint8_t * font) { uint16_t width = *((uint16_t *)font); uint16_t height = *(((uint16_t *)font)+1); diff --git a/radio/src/gui/480x272/gui.h b/radio/src/gui/colorlcd/gui.h similarity index 52% rename from radio/src/gui/480x272/gui.h rename to radio/src/gui/colorlcd/gui.h index e17858f22..d2a025160 100644 --- a/radio/src/gui/480x272/gui.h +++ b/radio/src/gui/colorlcd/gui.h @@ -24,54 +24,11 @@ #include "gui_common.h" #include "lcd.h" #include "menus.h" +#include "popups.h" #include "draw_functions.h" #include "bitmaps.h" #include "theme.h" - -#define HEADER_LINE 0 - -#define MENU_TOOLTIPS -#define MENU_HEADER_HEIGHT 45 -#define MENU_TITLE_TOP 48 -#define MENU_TITLE_HEIGHT 21 -#define MENU_BODY_TOP (MENU_TITLE_TOP+MENU_TITLE_HEIGHT) -#define MENU_CONTENT_TOP (MENU_BODY_TOP+1) -#define MENU_FOOTER_HEIGHT 21 -#define MENU_FOOTER_TOP (LCD_H-MENU_FOOTER_HEIGHT) -#define MENU_BODY_HEIGHT (MENU_FOOTER_TOP-MENU_BODY_TOP) -#define MENUS_MARGIN_LEFT 6 - -#define DEFAULT_SCROLLBAR_X LCD_W-10 -#define DEFAULT_SCROLLBAR_Y MENU_CONTENT_TOP -#define DEFAULT_SCROLLBAR_H (MENU_FOOTER_TOP - DEFAULT_SCROLLBAR_Y - 6) - -#define FH 20 -#define NUM_BODY_LINES (MENU_BODY_HEIGHT / FH) - -#define INVERT_HORZ_MARGIN 2 -#define INVERT_VERT_MARGIN 1 -#define INVERT_LINE_HEIGHT 20 - -#define CURVE_SIDE_WIDTH 100 -#define CURVE_CENTER_X (LCD_W-CURVE_SIDE_WIDTH-7) -#define CURVE_CENTER_Y 151 -#define CURVE_COORD_WIDTH 36 -#define CURVE_COORD_HEIGHT 17 - -#define DATETIME_SEPARATOR_X (LCD_W-53) -#define DATETIME_LINE1 9 -#define DATETIME_LINE2 23 -#define DATETIME_MIDDLE (LCD_W+DATETIME_SEPARATOR_X+4)/2 - -#define MENU_TITLE_NEXT_POS (lcdNextPos + 10) - -#if defined(HELI) && defined(GVARS) && defined(LUA_MODEL_SCRIPTS) -#define MENU_ICONS_SPACING 31 -#else -#define MENU_ICONS_SPACING 33 -#endif - -#define SUBMENU_LINE_WIDTH 230 +#include "libopenui_config.h" #define LOAD_MODEL_BITMAP() @@ -89,7 +46,6 @@ void drawCurveVerticalScale(int x); void drawCurveHorizontalScale(); void drawCurveCoord(int x, int y, const char * text, bool active=false); void drawCurvePoint(int x, int y, LcdFlags color); -void drawPower(coord_t x, coord_t y, int8_t dBm, LcdFlags att = 0); extern Layout * customScreens[MAX_CUSTOM_SCREENS]; extern Topbar * topbar; diff --git a/radio/src/gui/480x272/layout.cpp b/radio/src/gui/colorlcd/layout.cpp similarity index 100% rename from radio/src/gui/480x272/layout.cpp rename to radio/src/gui/colorlcd/layout.cpp diff --git a/radio/src/gui/480x272/layout.h b/radio/src/gui/colorlcd/layout.h similarity index 100% rename from radio/src/gui/480x272/layout.h rename to radio/src/gui/colorlcd/layout.h diff --git a/radio/src/gui/480x272/layouts/layout1x1.cpp b/radio/src/gui/colorlcd/layouts/layout1x1.cpp similarity index 100% rename from radio/src/gui/480x272/layouts/layout1x1.cpp rename to radio/src/gui/colorlcd/layouts/layout1x1.cpp diff --git a/radio/src/gui/480x272/layouts/layout2+1.cpp b/radio/src/gui/colorlcd/layouts/layout2+1.cpp similarity index 100% rename from radio/src/gui/480x272/layouts/layout2+1.cpp rename to radio/src/gui/colorlcd/layouts/layout2+1.cpp diff --git a/radio/src/gui/480x272/layouts/layout2x1.cpp b/radio/src/gui/colorlcd/layouts/layout2x1.cpp similarity index 100% rename from radio/src/gui/480x272/layouts/layout2x1.cpp rename to radio/src/gui/colorlcd/layouts/layout2x1.cpp diff --git a/radio/src/gui/480x272/layouts/layout2x2.cpp b/radio/src/gui/colorlcd/layouts/layout2x2.cpp similarity index 100% rename from radio/src/gui/480x272/layouts/layout2x2.cpp rename to radio/src/gui/colorlcd/layouts/layout2x2.cpp diff --git a/radio/src/gui/480x272/layouts/layout2x4.cpp b/radio/src/gui/colorlcd/layouts/layout2x4.cpp similarity index 100% rename from radio/src/gui/480x272/layouts/layout2x4.cpp rename to radio/src/gui/colorlcd/layouts/layout2x4.cpp diff --git a/radio/src/gui/480x272/layouts/mask_layout1x1.png b/radio/src/gui/colorlcd/layouts/mask_layout1x1.png similarity index 100% rename from radio/src/gui/480x272/layouts/mask_layout1x1.png rename to radio/src/gui/colorlcd/layouts/mask_layout1x1.png diff --git a/radio/src/gui/480x272/layouts/mask_layout2+1.png b/radio/src/gui/colorlcd/layouts/mask_layout2+1.png similarity index 100% rename from radio/src/gui/480x272/layouts/mask_layout2+1.png rename to radio/src/gui/colorlcd/layouts/mask_layout2+1.png diff --git a/radio/src/gui/480x272/layouts/mask_layout2x1.png b/radio/src/gui/colorlcd/layouts/mask_layout2x1.png similarity index 100% rename from radio/src/gui/480x272/layouts/mask_layout2x1.png rename to radio/src/gui/colorlcd/layouts/mask_layout2x1.png diff --git a/radio/src/gui/480x272/layouts/mask_layout2x2.png b/radio/src/gui/colorlcd/layouts/mask_layout2x2.png similarity index 100% rename from radio/src/gui/480x272/layouts/mask_layout2x2.png rename to radio/src/gui/colorlcd/layouts/mask_layout2x2.png diff --git a/radio/src/gui/480x272/layouts/mask_layout2x4.png b/radio/src/gui/colorlcd/layouts/mask_layout2x4.png similarity index 100% rename from radio/src/gui/480x272/layouts/mask_layout2x4.png rename to radio/src/gui/colorlcd/layouts/mask_layout2x4.png diff --git a/radio/src/gui/colorlcd/lcd.cpp b/radio/src/gui/colorlcd/lcd.cpp new file mode 100644 index 000000000..4395ffa8b --- /dev/null +++ b/radio/src/gui/colorlcd/lcd.cpp @@ -0,0 +1,265 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 +#include +#include "opentx.h" +#include "strhelpers.h" + +#if defined(SIMU) +display_t displayBuf[DISPLAY_BUFFER_SIZE]; +#endif + +uint16_t lcdColorTable[LCD_COLOR_COUNT]; + +coord_t lcdNextPos; + +uint8_t getMappedChar(uint8_t c) +{ + uint8_t result; + if (c == 0) + result = 0; +#if defined(TRANSLATIONS_FR) + else if (c >= 0x80 && c <= 0x84) { + result = 115 + c - 0x80; + } +#elif defined(TRANSLATIONS_DE) + else if (c >= 0x80 && c <= 0x86) { + result = 120 + c - 0x80; + } +#elif defined(TRANSLATIONS_CZ) + else if (c >= 0x80 && c <= 0x80+29) { + result = 127 + c - 0x80; + } +#elif defined(TRANSLATIONS_ES) + else if (c >= 0x80 && c <= 0x81) { + result = 157 + c - 0x80; + } +#elif defined(TRANSLATIONS_FI) + else if (c >= 0x80 && c <= 0x85) { + result = 159 + c - 0x80; + } +#elif defined(TRANSLATIONS_IT) + else if (c >= 0x80 && c <= 0x81) { + result = 165 + c - 0x80; + } +#elif defined(TRANSLATIONS_PL) + else if (c >= 0x80 && c <= 0x80+17) { + result = 167 + c - 0x80; + } +#elif defined(TRANSLATIONS_PT) + else if (c >= 0x80 && c <= 0x80+21) { + result = 185 + c - 0x80; + } +#elif defined(TRANSLATIONS_SE) + else if (c >= 0x80 && c <= 0x85) { + result = 207 + c - 0x80; + } +#endif + else if (c < 0xC0) + result = c - 0x20; + else + result = c - 0xC0 + 96; + // TRACE("getMappedChar '%c' (%d) = %d", c, c, result); + return result; +} + +int getFontPatternWidth(const uint16_t * spec, uint8_t index) +{ + return spec[index + 2] - spec[index + 1]; +} + +int getCharWidth(uint8_t c, const uint16_t * spec) +{ + return getFontPatternWidth(spec, getMappedChar(c)); +} + +void lcdPutFontPattern(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, int index, LcdFlags flags) +{ + coord_t offset = spec[index + 1]; + coord_t width = spec[index + 2] - offset; + if (width > 0) + lcdDrawBitmapPattern(x, y, font, flags, offset, width); + lcdNextPos = x + width; +} + +void lcdDrawChar(coord_t x, coord_t y, char c, LcdFlags flags) +{ + uint32_t fontindex = FONTINDEX(flags); + const unsigned char * font = fontsTable[fontindex]; + const uint16_t * fontspecs = fontspecsTable[fontindex]; + lcdPutFontPattern(x, y, font, fontspecs, getMappedChar(c), flags); +} + +uint8_t getFontHeight(LcdFlags flags) +{ + uint32_t fontindex = FONTINDEX(flags); + return fontspecsTable[fontindex][0]; +} + +int getTextWidth(const char * s, int len, LcdFlags flags) +{ + const uint16_t * specs = fontspecsTable[FONTINDEX(flags)]; + + int result = 0; + for (int i=0; len==0 || i0 || (mode==MODE(LEADING0) && idx> 12; + uint8_t red = ((((*q >> 8) & 0x0f) << 1) * alpha + (*p >> 11) * (0x0f-alpha)) / 0x0f; + uint8_t green = ((((*q >> 4) & 0x0f) << 2) * alpha + ((*p >> 5) & 0x3f) * (0x0f-alpha)) / 0x0f; + uint8_t blue = ((((*q >> 0) & 0x0f) << 1) * alpha + ((*p >> 0) & 0x1f) * (0x0f-alpha)) / 0x0f; + *p = (red << 11) + (green << 5) + (blue << 0); + p++; q++; + } + } +} + +void DMABitmapConvert(uint16_t * dest, const uint8_t * src, uint16_t w, uint16_t h, uint32_t format) +{ + if (format == DMA2D_ARGB4444) { + for (int row = 0; row < h; ++row) { + for (int col = 0; col < w; ++col) { + *dest = ARGB(src[0], src[1], src[2], src[3]); + ++dest; + src += 4; + } + } + } + else { + for (int row = 0; row < h; ++row) { + for(int col = 0; col < w; ++col) { + *dest = RGB(src[1], src[2], src[3]); + ++dest; + src += 4; + } + } + } +} +#endif diff --git a/radio/src/gui/480x272/lcd.h b/radio/src/gui/colorlcd/lcd.h similarity index 76% rename from radio/src/gui/480x272/lcd.h rename to radio/src/gui/colorlcd/lcd.h index adce60fbb..5bb4e4399 100644 --- a/radio/src/gui/480x272/lcd.h +++ b/radio/src/gui/colorlcd/lcd.h @@ -56,47 +56,10 @@ /* telemetry flags */ #define NO_UNIT 0x40 -enum FontSizeIndex { - STDSIZE_INDEX, - TINSIZE_INDEX, - SMLSIZE_INDEX, - MIDSIZE_INDEX, - DBLSIZE_INDEX, - XXLSIZE_INDEX, - SPARE6_INDEX, - SPARE7_INDEX, - STDSIZE_BOLD_INDEX, - SPARE9_INDEX, - SPAREa_INDEX, - SPAREb_INDEX, - SPAREc_INDEX, - SPAREd_INDEX, - SPAREe_INDEX, - SPAREf_INDEX, -}; - -#define STDSIZE (STDSIZE_INDEX << 8) -#define TINSIZE (TINSIZE_INDEX << 8) -#define SMLSIZE (SMLSIZE_INDEX << 8) -#define MIDSIZE (MIDSIZE_INDEX << 8) -#define DBLSIZE (DBLSIZE_INDEX << 8) -#define XXLSIZE (XXLSIZE_INDEX << 8) -#define BOLD (STDSIZE_BOLD_INDEX << 8) -#define FONTSIZE_MASK 0x0f00 - -#if !defined(BOOT) -#define FONTSIZE(flags) ((flags) & FONTSIZE_MASK) -#define FONTINDEX(flags) (FONTSIZE(flags) >> 8) -#else -#define FONTSIZE(flags) STDSIZE -#define FONTINDEX(flags) STDSIZE_INDEX -#endif - #define TIMEBLINK 0x1000 #define TIMEHOUR 0x2000 #define EXPANDED 0x2000 #define VERTICAL 0x4000 -#define NO_FONTCACHE 0x8000 #include "colors.h" @@ -113,6 +76,8 @@ extern display_t displayBuf[DISPLAY_BUFFER_SIZE]; #define ASSERT_IN_DISPLAY(p) assert((p) >= displayBuf && (p) < DISPLAY_END) extern coord_t lcdNextPos; +void lcdNextLayer(); + void lcdDrawChar(coord_t x, coord_t y, char c, LcdFlags flags=0); @@ -138,25 +103,8 @@ inline void lcdDrawSizedText(coord_t x, coord_t y, const char * s, uint8_t len, lcd->drawSizedText(x, y, s, len, attr); } -void lcdDrawHexNumber(coord_t x, coord_t y, uint32_t val, LcdFlags mode=0); void lcdDrawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags=0, uint8_t len=0, const char * prefix=NULL, const char * suffix=NULL); -#if !defined(BOOT) -void drawRtcTime(coord_t x, coord_t y, LcdFlags att=0); -void drawTimer(coord_t x, coord_t y, int32_t tme, LcdFlags att=0); - -void putsModelName(coord_t x, coord_t y, char *name, uint8_t id, LcdFlags att); -void putsStickName(coord_t x, coord_t y, uint8_t idx, LcdFlags att=0); -void drawSwitch(coord_t x, coord_t y, swsrc_t swtch, LcdFlags flags=0); -void drawSource(coord_t x, coord_t y, mixsrc_t idx, LcdFlags att=0); -void drawCurveName(coord_t x, coord_t y, int8_t idx, LcdFlags att=0); -void drawTimerMode(coord_t x, coord_t y, swsrc_t mode, LcdFlags att=0); -void drawTrimMode(coord_t x, coord_t y, uint8_t phase, uint8_t idx, LcdFlags att); - -#define putsChn(x, y, idx, att) drawSource(x, y, MIXSRC_CH1+idx-1, att) -void putsChnLetter(coord_t x, coord_t y, uint8_t idx, LcdFlags attr); -#endif // !BOOT - #define SOLID 0xff #define DOTTED 0x55 #define STASHED 0x33 @@ -252,6 +200,7 @@ inline int getBitmapHeight(const uint8_t * bmp) } uint8_t getMappedChar(uint8_t c); +int getCharWidth(uint8_t c, const uint16_t * spec); uint8_t getFontHeight(LcdFlags flags); int getTextWidth(const char * s, int len=0, LcdFlags flags=0); diff --git a/radio/src/gui/colorlcd/menu_model.cpp b/radio/src/gui/colorlcd/menu_model.cpp new file mode 100644 index 000000000..df69617f9 --- /dev/null +++ b/radio/src/gui/colorlcd/menu_model.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "menu_model.h" +#include "model_setup.h" +#include "model_heli.h" +#include "model_flightmodes.h" +#include "model_mixes.h" +#include "model_inputs.h" +#include "model_outputs.h" +#include "model_curves.h" +#include "model_logical_switches.h" +#include "special_functions.h" +#include "model_telemetry.h" + +ModelMenu::ModelMenu(): + TabsGroup(ICON_MODEL) +{ + addTab(new ModelSetupPage()); +#if defined(HELI) + addTab(new ModelHeliPage()); +#endif + addTab(new ModelFlightModesPage()); + addTab(new ModelInputsPage()); + addTab(new ModelMixesPage()); + addTab(new ModelOutputsPage()); + addTab(new ModelCurvesPage()); + addTab(new ModelLogicalSwitchesPage()); + addTab(new SpecialFunctionsPage(g_model.customFn)); + addTab(new ModelTelemetryPage()); +} diff --git a/radio/src/gui/colorlcd/menu_model.h b/radio/src/gui/colorlcd/menu_model.h new file mode 100644 index 000000000..97e778e8e --- /dev/null +++ b/radio/src/gui/colorlcd/menu_model.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _MENU_MODEL_H_ +#define _MENU_MODEL_H_ + +#include "tabsgroup.h" + +class ModelMenu: public TabsGroup { + public: + ModelMenu(); +}; + +#endif // _MENU_MODEL_H_ diff --git a/radio/src/gui/colorlcd/menu_radio.cpp b/radio/src/gui/colorlcd/menu_radio.cpp new file mode 100644 index 000000000..7ccb677dd --- /dev/null +++ b/radio/src/gui/colorlcd/menu_radio.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "menu_radio.h" +#include "radio_setup.h" +#include "radio_sdmanager.h" +#include "radio_tools.h" +#include "special_functions.h" +#include "radio_calibration.h" +#include "radio_trainer.h" +#include "radio_version.h" +#include "radio_hardware.h" +#include "myeeprom.h" + +RadioMenu::RadioMenu(): + TabsGroup(ICON_RADIO) +{ + addTab(new RadioSetupPage()); + addTab(new RadioSdManagerPage()); + addTab(new RadioToolsPage()); + addTab(new SpecialFunctionsPage(g_eeGeneral.customFn)); + addTab(new RadioTrainerPage()); + addTab(new RadioHardwarePage()); + addTab(new RadioVersionPage()); +} diff --git a/radio/src/gui/colorlcd/menu_radio.h b/radio/src/gui/colorlcd/menu_radio.h new file mode 100644 index 000000000..2042dd9b7 --- /dev/null +++ b/radio/src/gui/colorlcd/menu_radio.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _MENU_RADIO_H_ +#define _MENU_RADIO_H_ + +#include "tabsgroup.h" + +class RadioMenu: public TabsGroup { + public: + RadioMenu(); +}; + +#endif // _MENU_RADIO_H_ diff --git a/radio/src/gui/colorlcd/menus.h b/radio/src/gui/colorlcd/menus.h new file mode 100644 index 000000000..f9d05b32a --- /dev/null +++ b/radio/src/gui/colorlcd/menus.h @@ -0,0 +1,359 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _MENUS_H_ +#define _MENUS_H_ + +#include "keys.h" + +#define MIXES_2ND_COLUMN 140 + +extern int menuVerticalPosition; +extern int menuHorizontalPosition; +extern int menuVerticalOffset; +extern uint8_t menuCalibrationState; + +// Temporary no highlight +extern uint8_t noHighlightCounter; +#define NO_HIGHLIGHT() (noHighlightCounter > 0) +#define START_NO_HIGHLIGHT() do { noHighlightCounter = 25; } while(0) + +typedef bool (*MenuHandlerFunc)(event_t event); + +extern MenuHandlerFunc menuHandlers[5]; +extern uint8_t menuVerticalPositions[4]; +extern uint8_t menuLevel; +extern event_t menuEvent; + +void chainMenu(MenuHandlerFunc newMenu); +void pushMenu(MenuHandlerFunc newMenu); +void popMenu(); +void abortPopMenu(); + +inline bool isModelMenuDisplayed() +{ + return menuVerticalPositions[0] == 0; +} + +enum MenuIcons { + ICON_OPENTX, +#if defined(HARDWARE_TOUCH) + ICON_NEXT, + ICON_BACK, +#endif + ICON_RADIO, + ICON_RADIO_SETUP, + ICON_RADIO_SD_MANAGER, + ICON_RADIO_TOOLS, + ICON_RADIO_SPECTRUM_ANALYSER, + ICON_RADIO_GLOBAL_FUNCTIONS, + ICON_RADIO_TRAINER, + ICON_RADIO_HARDWARE, + ICON_RADIO_CALIBRATION, + ICON_RADIO_VERSION, + ICON_MODEL, + ICON_MODEL_SETUP, + ICON_MODEL_HELI, + ICON_MODEL_FLIGHT_MODES, + ICON_MODEL_INPUTS, + ICON_MODEL_MIXER, + ICON_MODEL_OUTPUTS, + ICON_MODEL_CURVES, + ICON_MODEL_GVARS, + ICON_MODEL_LOGICAL_SWITCHES, + ICON_MODEL_SPECIAL_FUNCTIONS, + ICON_MODEL_LUA_SCRIPTS, + ICON_MODEL_TELEMETRY, + ICON_THEME, + ICON_THEME_SETUP, + ICON_THEME_VIEW1, + ICON_THEME_VIEW2, + ICON_THEME_VIEW3, + ICON_THEME_VIEW4, + ICON_THEME_VIEW5, + ICON_THEME_ADD_VIEW, + ICON_STATS, + ICON_STATS_THROTTLE_GRAPH, + ICON_STATS_TIMERS, + ICON_STATS_ANALOGS, + ICON_STATS_DEBUG, + ICON_MONITOR, + ICON_MONITOR_CHANNELS1, + ICON_MONITOR_CHANNELS2, + ICON_MONITOR_CHANNELS3, + ICON_MONITOR_CHANNELS4, + ICON_MONITOR_LOGICAL_SWITCHES, + MENUS_ICONS_COUNT +}; + +enum EnumTabModel { + MENU_MODEL_SETUP, + CASE_HELI(MENU_MODEL_HELI) + CASE_FLIGHT_MODES(MENU_MODEL_FLIGHT_MODES) + MENU_MODEL_INPUTS, + MENU_MODEL_MIXES, + MENU_MODEL_OUTPUTS, + MENU_MODEL_CURVES, + CASE_GVARS(MENU_MODEL_GVARS) + MENU_MODEL_LOGICAL_SWITCHES, + MENU_MODEL_SPECIAL_FUNCTIONS, +#if defined(LUA_MODEL_SCRIPTS) + MENU_MODEL_CUSTOM_SCRIPTS, +#endif + MENU_MODEL_TELEMETRY, + MENU_MODEL_PAGES_COUNT +}; + +const uint8_t MODEL_ICONS[MENU_MODEL_PAGES_COUNT + 1] = { + ICON_MODEL, + ICON_MODEL_SETUP, + CASE_HELI(ICON_MODEL_HELI) + CASE_FLIGHT_MODES(ICON_MODEL_FLIGHT_MODES) + ICON_MODEL_INPUTS, + ICON_MODEL_MIXER, + ICON_MODEL_OUTPUTS, + ICON_MODEL_CURVES, + CASE_GVARS(ICON_MODEL_GVARS) + ICON_MODEL_LOGICAL_SWITCHES, + ICON_MODEL_SPECIAL_FUNCTIONS, +#if defined(LUA_MODEL_SCRIPTS) + ICON_MODEL_LUA_SCRIPTS, +#endif + ICON_MODEL_TELEMETRY +}; + +const uint8_t STATS_ICONS[] = { + ICON_STATS, + ICON_STATS_THROTTLE_GRAPH, + ICON_STATS_DEBUG, + ICON_STATS_ANALOGS, +#if defined(DEBUG_TRACE_BUFFER) + ICON_STATS_TIMERS +#endif +}; + +const uint8_t MONITOR_ICONS[] = { + ICON_MONITOR, + ICON_MONITOR_CHANNELS1, + ICON_MONITOR_CHANNELS2, + ICON_MONITOR_CHANNELS3, + ICON_MONITOR_CHANNELS4, + ICON_MONITOR_LOGICAL_SWITCHES +}; + +bool menuModelSetup(event_t event); +bool menuModelFailsafe(event_t event); +bool menuModelHeli(event_t event); +bool menuModelFlightModesAll(event_t event); +bool menuModelExposAll(event_t event); +bool menuModelMixAll(event_t event); +bool menuModelLimits(event_t event); +bool menuModelCurvesAll(event_t event); +bool menuModelCurveOne(event_t event); +bool menuModelGVars(event_t event); +bool menuModelLogicalSwitches(event_t event); +bool menuModelSpecialFunctions(event_t event); +bool menuModelCustomScripts(event_t event); +bool menuModelTelemetryFrsky(event_t event); +bool menuModelSensor(event_t event); +bool menuModelExpoOne(event_t event); + +extern const MenuHandlerFunc menuTabModel[MENU_MODEL_PAGES_COUNT]; + +enum EnumTabRadio { + MENU_RADIO_SETUP, + MENU_RADIO_SD_MANAGER, + MENU_RADIO_TOOLS, + MENU_RADIO_SPECIAL_FUNCTIONS, + MENU_RADIO_TRAINER, + MENU_RADIO_HARDWARE, + MENU_RADIO_VERSION, + MENU_RADIO_PAGES_COUNT +}; + +const uint8_t RADIO_ICONS[MENU_RADIO_PAGES_COUNT + 1] = { + ICON_RADIO, + ICON_RADIO_SETUP, + ICON_RADIO_SD_MANAGER, + ICON_RADIO_TOOLS, + ICON_RADIO_GLOBAL_FUNCTIONS, + ICON_RADIO_TRAINER, + ICON_RADIO_HARDWARE, + ICON_RADIO_VERSION +}; + +bool menuRadioSetup(event_t event); +bool menuRadioSdManager(event_t event); +bool menuRadioTools(event_t event); +bool menuRadioSpecialFunctions(event_t event); +bool menuRadioTrainer(event_t event); +bool menuRadioVersion(event_t event); +bool menuRadioHardware(event_t event); +bool menuRadioCalibration(event_t event); + +extern const MenuHandlerFunc menuTabGeneral[MENU_RADIO_PAGES_COUNT]; + +enum MenuRadioIndexes +{ + e_StatsGraph, + e_StatsDebug, + e_StatsAnalogs, +#if defined(DEBUG_TRACE_BUFFER) + e_StatsTraces, +#endif +}; + +bool menuStatsGraph(event_t event); +bool menuStatsDebug(event_t event); +bool menuStatsAnalogs(event_t event); +bool menuStatsTraces(event_t event); + +enum EnumTabMonitors +{ + e_MonChannelsFirst, + e_MonChannels1 = e_MonChannelsFirst, + e_MonChannels2, + e_MonChannels3, + e_MonChannels4, + e_MonLogicalSwitches, + e_MonTabChannelsPagesCount +}; + +extern const MenuHandlerFunc menuTabMonitors[e_MonTabChannelsPagesCount]; +extern uint8_t lastMonitorPage; +extern void drawSingleMixerBar(coord_t, coord_t, coord_t, coord_t, uint8_t); +extern void drawSingleOutputBar(coord_t, coord_t, coord_t, coord_t, uint8_t); + +extern const MenuHandlerFunc menuTabScreensSetup[1+MAX_CUSTOM_SCREENS] ; + +bool menuFirstCalib(event_t event); +bool menuMainView(event_t event); +bool menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomFunctionsContext & functionsContext); +bool menuModelSelect(event_t event); +bool menuAboutView(event_t event); +bool menuMainViewChannelsMonitor(event_t event); +bool menuTextView(event_t event); +bool menuScreensTheme(event_t event); +bool menuRadioSpectrumAnalyser(event_t event); +bool menuRadioPowerMeter(event_t event); + +extern int8_t checkIncDec_Ret; // global helper vars + +#define EDIT_SELECT_FIELD 0 +#define EDIT_MODIFY_FIELD 1 +#define EDIT_MODIFY_STRING 2 +extern int8_t s_editMode; // global editmode + +// checkIncDec flags +#define EE_GENERAL 0x01 +#define EE_MODEL 0x02 +#define NO_INCDEC_MARKS 0x04 +#define INCDEC_SWITCH 0x08 +#define INCDEC_SOURCE 0x10 +#define INCDEC_REP10 0x40 +#define NO_DBLKEYS 0x80 + +// mawrow special values +//#define READONLY_ROW ((uint8_t)-1) +//#define TITLE_ROW READONLY_ROW +//#define ORPHAN_ROW ((uint8_t)-2) +//#define HIDDEN_ROW ((uint8_t)-3) +//#define NAVIGATION_LINE_BY_LINE 0x40 +//#define CURSOR_ON_LINE() (menuHorizontalPosition<0) + +extern uint8_t s_curveChan; + +extern const char * warningText; +extern const char * warningInfoText; +extern uint8_t warningInfoLength; +extern uint8_t warningResult; +extern uint8_t warningType; + +#define COPY_MODE 1 +#define MOVE_MODE 2 +extern uint8_t s_copyMode; +extern int8_t s_copySrcRow; +extern int8_t s_copyTgtOfs; +extern uint8_t s_currIdx; +extern int8_t s_currCh; +extern uint8_t s_copySrcIdx; +extern uint8_t s_copySrcCh; + +uint8_t getExposCount(); +void deleteExpo(uint8_t idx); +void insertExpo(uint8_t idx, uint8_t input); + +uint8_t getMixesCount(); +void deleteMix(uint8_t idx); +void insertMix(uint8_t idx); +void copyMix(uint8_t source, uint8_t dest, int8_t ch); + +#define MENU_X 80 +#define MENU_W LCD_W-(2*MENU_X) +#define POPUP_X 120 +#define POPUP_Y 70 +#define WARNING_LINE_LEN 32 +#define WARNING_LINE_X (POPUP_X+66) +#define WARNING_LINE_Y (POPUP_Y+9) + +void copySelection(char * dst, const char * src, uint8_t size); + +void showMessageBox(const char * title); +void runPopupWarning(event_t event); + +extern void (* popupFunc)(event_t event); +extern uint8_t warningInfoFlags; + +#define DISPLAY_WARNING(evt) (*popupFunc)(evt) +#define POPUP_WARNING(s) (warningType = WARNING_TYPE_ASTERISK, warningText = s, warningInfoText = 0, popupFunc = runPopupWarning) +#define POPUP_INPUT(s, func) (warningText = s, popupFunc = func) +#define SET_WARNING_INFO(info, len, flags) (warningInfoText = info, warningInfoLength = len, warningInfoFlags = flags) + +#define POPUP_MENU_START(func) do { popupMenuHandler = (func); AUDIO_KEY_PRESS(); } while(0) +#define POPUP_MENU_MAX_LINES 12 +#define MENU_MAX_DISPLAY_LINES 9 +#define MENU_LINE_LENGTH (LEN_MODEL_NAME+12) + +extern const char * popupMenuItems[POPUP_MENU_MAX_LINES]; +extern uint16_t popupMenuItemsCount; +extern uint16_t popupMenuOffset; +enum { + MENU_OFFSET_INTERNAL, + MENU_OFFSET_EXTERNAL +}; +extern uint8_t popupMenuOffsetType; +extern uint8_t popupMenuSelectedItem; +const char * runPopupMenu(event_t event); +extern void (*popupMenuHandler)(const char * result); + +#define TEXT_FILENAME_MAXLEN 40 +extern char s_text_file[TEXT_FILENAME_MAXLEN]; +void pushMenuTextView(const char * filename); +void pushModelNotes(); +void readModelNotes(); + +typedef int (*FnFuncP) (int x); +void drawFunction(FnFuncP fn, int x, int y, int width); + +void onSourceLongEnterPress(const char *result); + +uint8_t switchToMix(uint8_t source); + +#endif // _MENUS_H_ diff --git a/radio/src/gui/colorlcd/model_curves.cpp b/radio/src/gui/colorlcd/model_curves.cpp new file mode 100644 index 000000000..501f5648c --- /dev/null +++ b/radio/src/gui/colorlcd/model_curves.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "model_curves.h" +#include "opentx.h" +#include "libopenui.h" + +#define SET_DIRTY() storageDirty(EE_MODEL) + +bool isCurveFilled(uint8_t index) +{ + CurveInfo &curve = g_model.curves[index]; + int8_t * points = curveAddress(index); + for (int i = 0; i < 5 + curve.points; i++) { + if (points[i] != 0) { + return true; + } + } + return false; +} + +class CurveEditWindow : public Page { + public: + explicit CurveEditWindow(uint8_t index): + Page(ICON_MODEL_CURVES), + index(index) + { + buildBody(&body); + buildHeader(&header); + } + + protected: + uint8_t index; + CurveEdit * curveEdit = nullptr; + + void buildHeader(Window * window) + { + new StaticText(window, {70, 4, LCD_W - 100, 20}, STR_MENUCURVE, MENU_TITLE_COLOR); + char s[16]; + strAppendStringWithIndex(s, STR_CV, index + 1); + new StaticText(window, {70, 28, LCD_W - 100, 20}, s, MENU_TITLE_COLOR); + } + + void buildBody(Window * window) + { + FormGridLayout grid; + grid.setMarginLeft(20); + grid.setMarginRight(20); + grid.setLabelWidth(100); + grid.spacer(20); + + CurveInfo & curve = g_model.curves[index]; + int8_t * points = curveAddress(index); + + // Curve editor + curveEdit = new CurveEdit(window, { 20, grid.getWindowHeight(), LCD_W - 40, LCD_W - 40}, index); + grid.spacer(curveEdit->height() + 15); + + // Name + new StaticText(window, grid.getLabelSlot(), STR_NAME); + new TextEdit(window, grid.getFieldSlot(), curve.name, sizeof(curve.name)); + grid.nextLine(); + + // Type + new StaticText(window, grid.getLabelSlot(), STR_TYPE); + new Choice(window, grid.getFieldSlot(2, 0), STR_CURVE_TYPES, 0, 1, GET_DEFAULT(g_model.curves[index].type), + [=](int32_t newValue) { + CurveInfo &curve = g_model.curves[index]; + if (newValue != curve.type) { + for (int i = 1; i < 4 + curve.points; i++) { + points[i] = calcRESXto100(applyCustomCurve(calc100toRESX(-100 + i * 200 / (4 + curve.points)), index)); + } + if (moveCurve(index, newValue == CURVE_TYPE_CUSTOM ? 3 + curve.points : -3 - curve.points)) { + if (newValue == CURVE_TYPE_CUSTOM) { + resetCustomCurveX(points, 5 + curve.points); + } + curve.type = newValue; + } + SET_DIRTY(); + curveEdit->update(); + } + }); + + // Points count + auto edit = new NumberEdit(window, grid.getFieldSlot(2, 1), 2, 17, GET_DEFAULT(g_model.curves[index].points + 5), + [=](int32_t newValue) { + newValue -= 5; + CurveInfo &curve = g_model.curves[index]; + int newPoints[MAX_POINTS_PER_CURVE]; + newPoints[0] = points[0]; + newPoints[4 + newValue] = points[4 + curve.points]; + for (int i = 1; i < 4 + newValue; i++) + newPoints[i] = calcRESXto100(applyCustomCurve(-RESX + (i * 2 * RESX) / (4 + newValue), index)); + if (moveCurve(index, (newValue - curve.points) * (curve.type == CURVE_TYPE_CUSTOM ? 2 : 1))) { + for (int i = 0; i < 5 + newValue; i++) { + points[i] = newPoints[i]; + if (curve.type == CURVE_TYPE_CUSTOM && i != 0 && i != 4 + newValue) + points[5 + newValue + i - 1] = -100 + (i * 200) / (4 + newValue); + } + curve.points = newValue; + SET_DIRTY(); + curveEdit->update(); + } + }); + edit->setSuffix(STR_PTS); + grid.nextLine(); + + // Smooth + new StaticText(window, grid.getLabelSlot(), STR_SMOOTH); + new CheckBox(window, grid.getFieldSlot(), GET_DEFAULT(g_model.curves[index].smooth), + [=](int32_t newValue) { + g_model.curves[index].smooth = newValue; + SET_DIRTY(); + curveEdit->update(); + }); + grid.nextLine(); + + } +}; + +class CurveButton : public Button { + public: + CurveButton(Window * parent, const rect_t &rect, uint8_t index) : + Button(parent, rect), + index(index) + { + if (isCurveFilled(index)) { + setHeight(130); + new Curve(this, {5, 5, 120, 120}, + [=](int x) -> int { + return applyCustomCurve(x, index); + }); + } + } + + void paint(BitmapBuffer * dc) override + { + // bounding rect + drawSolidRect(dc, 0, 0, rect.w, rect.h, 2, hasFocus() ? SCROLLBOX_COLOR : CURVE_AXIS_COLOR); + + // curve characteristics + if (isCurveFilled(index)) { + CurveInfo &curve = g_model.curves[index]; + drawNumber(dc, 130, 5, 5 + curve.points, LEFT, 0, nullptr, STR_PTS); + drawTextAtIndex(dc, 130, 25, STR_CURVE_TYPES, curve.type); + if (curve.smooth) + dc->drawText(130, 45, "Smooth"); + } + } + + protected: + uint8_t index; +}; + +ModelCurvesPage::ModelCurvesPage() : + PageTab(STR_MENUCURVES, ICON_MODEL_CURVES) +{ +} + +void ModelCurvesPage::rebuild(FormWindow * window, int8_t focusIndex) +{ + coord_t scrollPosition = window->getScrollPositionY(); + window->clear(); + build(window, focusIndex); + window->setScrollPositionY(scrollPosition); +} + +void ModelCurvesPage::editCurve(FormWindow * window, uint8_t curve) +{ + Window * editWindow = new CurveEditWindow(curve); + editWindow->setCloseHandler([=]() { + rebuild(window, curve); + }); +} + +void ModelCurvesPage::build(FormWindow * window, int8_t focusIndex) +{ + FormGridLayout grid; + grid.spacer(PAGE_PADDING); + grid.setLabelWidth(66); + + for (uint8_t index = 0; index < MAX_CURVES; index++) { + CurveInfo &curve = g_model.curves[index]; + int8_t * points = curveAddress(index); + + // Curve label + new StaticText(window, grid.getLabelSlot(), getCurveString(1 + index), BUTTON_BACKGROUND | CENTERED); + + // Curve drawing + Button * button = new CurveButton(window, grid.getFieldSlot(), index); + button->setPressHandler([=]() -> uint8_t { + Menu * menu = new Menu(); + menu->addLine(STR_EDIT, [=]() { + editCurve(window, index); + }); + menu->addLine(STR_CURVE_PRESET, [=]() { + Menu * menu = new Menu(); + for (int angle = -45; angle <= 45; angle += 15) { + char label[16]; + strAppend(strAppendSigned(label, angle), "@"); + menu->addLine(label, [=]() { + int dx = 2000 / (5 + curve.points - 1); + for (uint8_t i = 0; i < 5 + curve.points; i++) { + int x = -1000 + i * dx; + points[i] = div_and_round(angle * x, 450); + } + if (curve.type == CURVE_TYPE_CUSTOM) { + resetCustomCurveX(points, 5 + curve.points); + } + storageDirty(EE_MODEL); + rebuild(window, index); + }); + } + }); + if (isCurveFilled(index)) { + menu->addLine(STR_MIRROR, [=]() { + for (int i = 0; i < 5 + curve.points; i++) + points[i] = -points[i]; + storageDirty(EE_MODEL); + button->invalidate(); + }); + menu->addLine(STR_CLEAR, [=]() { + for (int i = 0; i < 5 + curve.points; i++) + points[i] = 0; + if (curve.type == CURVE_TYPE_CUSTOM) + resetCustomCurveX(points, 5 + curve.points); + storageDirty(EE_MODEL); + rebuild(window, index); + }); + } + return 0; + }); + + if (focusIndex == index) { + button->setFocus(); + } + + grid.spacer(button->height() + 5); + } + + grid.nextLine(); + + window->setLastField(); + window->setInnerHeight(grid.getWindowHeight()); +} diff --git a/radio/src/gui/colorlcd/model_curves.h b/radio/src/gui/colorlcd/model_curves.h new file mode 100644 index 000000000..a747b50e6 --- /dev/null +++ b/radio/src/gui/colorlcd/model_curves.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + + +#ifndef _MODEL_CURVES_H_ +#define _MODEL_CURVES_H_ + +#include "tabsgroup.h" + +class ModelCurvesPage: public PageTab { + public: + ModelCurvesPage(); + + virtual void build(FormWindow * window) override + { + build(window, -1); + } + + protected: + void build(FormWindow * window, int8_t focusIndex); + void rebuild(FormWindow * window, int8_t focusIndex); + void editCurve(FormWindow * window, uint8_t curve); +}; + +#endif // _MODEL_CURVES_H_ diff --git a/radio/src/gui/480x272/model_custom_scripts.cpp b/radio/src/gui/colorlcd/model_custom_scripts.cpp similarity index 99% rename from radio/src/gui/480x272/model_custom_scripts.cpp rename to radio/src/gui/colorlcd/model_custom_scripts.cpp index 96ab0b6ca..b2c0a08b8 100644 --- a/radio/src/gui/480x272/model_custom_scripts.cpp +++ b/radio/src/gui/colorlcd/model_custom_scripts.cpp @@ -29,7 +29,7 @@ void onModelCustomScriptMenu(const char *result) POPUP_WARNING(STR_NO_SCRIPTS_ON_SD); } } - else if (result != STR_EXIT) { + else { // The user choosed a lua file in the list copySelection(sd.file, result, sizeof(sd.file)); memset(sd.inputs, 0, sizeof(sd.inputs)); diff --git a/radio/src/gui/colorlcd/model_flightmodes.cpp b/radio/src/gui/colorlcd/model_flightmodes.cpp new file mode 100644 index 000000000..9a592edbd --- /dev/null +++ b/radio/src/gui/colorlcd/model_flightmodes.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "model_flightmodes.h" +#include "opentx.h" + +#define SET_DIRTY() storageDirty(EE_MODEL) + +ModelFlightModesPage::ModelFlightModesPage(): + PageTab(STR_MENUFLIGHTMODES, ICON_MODEL_FLIGHT_MODES) +{ +} + +bool isTrimModeAvailable(int mode) +{ + return (mode < 0 || (mode%2) == 0 || (mode/2) != 0); //ToDo menuVerticalPosition +} + +class FlightModeGroup: public FormGroup +{ + public: + FlightModeGroup(Window * parent, uint8_t index, const rect_t & rect) : + FormGroup(parent, rect), + index(index) + { + } + + void checkEvents() + { + FormGroup::checkEvents(); + bool newActive = (getFlightMode() == index); + if (newActive != active) { + active = newActive; + invalidate(); + } + } + + void paint(BitmapBuffer * dc) override + { + if (index == getFlightMode()) { + dc->drawSolidFilledRect(0, 0, width(), height(), WARNING_COLOR); + } + FormGroup::paint(dc); + } + + protected: + uint8_t index; + bool active = false; +}; + +void ModelFlightModesPage::build(FormWindow * window) +{ + coord_t y = 2; + + for (int i = 0; i < MAX_FLIGHT_MODES; i++) { + auto group = new FlightModeGroup(window, i, { 2, y, LCD_W - 10, 0 }); + if (i == 0) { + window->setFirstField(group); + } + + FormGridLayout grid; + grid.setMarginRight(15); + grid.spacer(); + grid.setLabelWidth(140); + + char label[16]; + getFlightModeString(label, i+1); + new Subtitle(group, grid.getLabelSlot(), label); + grid.nextLine(); + + // Flight mode name + new StaticText(group, grid.getLabelSlot(true), STR_NAME); + auto nameEdit = new TextEdit(group, grid.getFieldSlot(), g_model.flightModeData[i].name, LEN_FLIGHT_MODE_NAME); + group->setFirstField(nameEdit); + grid.nextLine(); + + // Flight mode switch + if (i > 0) { + new StaticText(group, grid.getLabelSlot(true), STR_SWITCH); + new SwitchChoice(group, grid.getFieldSlot(), SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES, GET_SET_DEFAULT(g_model.flightModeData[i].swtch)); + grid.nextLine(); + } + + // Flight mode trims + new StaticText(group, grid.getLabelSlot(true), STR_TRIMS); + + const char * STR_VTRIMS_MODES = "\002" + "--" + "=0" + "+0" + "=1" + "+1" + "=2" + "+2" + "=3" + "+3" + "=4" + "+4" + "=5" + "+5" + "=6" + "+6" + "=7" + "+7" + "=8" + "+8"; + for (int t=0; tsetStep(10 / DELAY_STEP); + edit = new NumberEdit(group, grid.getFieldSlot(2, 1), 0, DELAY_MAX, + GET_DEFAULT(g_model.flightModeData[i].fadeOut * (10 / DELAY_STEP)), + SET_VALUE(g_model.flightModeData[i].fadeOut, newValue / (10 / DELAY_STEP)), + PREC1); + edit->setStep(10 / DELAY_STEP); + grid.nextLine(); + + group->setLastField(FormField::getCurrentField()); + + grid.spacer(); + coord_t height = grid.getWindowHeight(); + group->setHeight(height); + y += height + 2; + } + + char label[32]; + sprintf(label, "Check FM Trims"); + // TODO rather use a centered slot? + // TODO dynamic text with FM index for the button text + auto button = new TextButton(window, { 60, y + 5, LCD_W - 120, PAGE_LINE_HEIGHT }, label, + [&]() -> uint8_t { + if (trimsCheckTimer) + trimsCheckTimer = 0; + else + trimsCheckTimer = 200; // 2 seconds trims cancelled + return trimsCheckTimer; + }); + + window->setInnerHeight(y + 40); + window->setLastField(button); +} diff --git a/radio/src/gui/colorlcd/model_flightmodes.h b/radio/src/gui/colorlcd/model_flightmodes.h new file mode 100644 index 000000000..f1fdc6271 --- /dev/null +++ b/radio/src/gui/colorlcd/model_flightmodes.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "tabsgroup.h" + +class ModelFlightModesPage: public PageTab { + public: + ModelFlightModesPage(); + + void build(FormWindow * window); +}; diff --git a/radio/src/gui/480x272/model_gvars.cpp b/radio/src/gui/colorlcd/model_gvars.cpp similarity index 95% rename from radio/src/gui/480x272/model_gvars.cpp rename to radio/src/gui/colorlcd/model_gvars.cpp index 202647e08..1bbd978fd 100644 --- a/radio/src/gui/480x272/model_gvars.cpp +++ b/radio/src/gui/colorlcd/model_gvars.cpp @@ -20,31 +20,32 @@ #include "opentx.h" +#if 0 void editGVarValue(coord_t x, coord_t y, event_t event, uint8_t gvar, uint8_t flightMode, LcdFlags flags) { FlightModeData * fm = &g_model.flightModeData[flightMode]; - gvar_t * v = &fm->gvars[gvar]; + gvar_t & v = fm->gvars[gvar]; int16_t vmin, vmax; - if (*v > GVAR_MAX) { - uint8_t fm = *v - GVAR_MAX - 1; + if (v > GVAR_MAX) { + uint8_t fm = v - GVAR_MAX - 1; if (fm >= flightMode) fm++; drawFlightMode(x, y, fm + 1, flags&(~LEFT)); vmin = GVAR_MAX + 1; vmax = GVAR_MAX + MAX_FLIGHT_MODES - 1; } else { - drawGVarValue(x, y, gvar, *v, flags); + drawGVarValue(x, y, gvar, v, flags); vmin = GVAR_MIN + g_model.gvars[gvar].min; vmax = GVAR_MAX - g_model.gvars[gvar].max; } if (flags & INVERS) { if (event == EVT_KEY_LONG(KEY_ENTER) && flightMode > 0) { - *v = (*v > GVAR_MAX ? 0 : GVAR_MAX+1); + v = (v > GVAR_MAX ? 0 : GVAR_MAX+1); storageDirty(EE_MODEL); } else if (s_editMode > 0) { - *v = checkIncDec(event, *v, vmin, vmax, EE_MODEL); + v = checkIncDec(event, v, vmin, vmax, EE_MODEL); } } } @@ -101,7 +102,7 @@ bool menuModelGVarOne(event_t event) break; default: - drawStringWithIndex(MENUS_MARGIN_LEFT, y, STR_FM, k-GVAR_FIELD_FM0); + drawStringWithIndex(MENUS_MARGIN_LEFT, y, STR_FP, k-GVAR_FIELD_FM0); editGVarValue(MIXES_2ND_COLUMN, y, event, s_currIdx, k-GVAR_FIELD_FM0, LEFT|attr); break; } @@ -182,3 +183,4 @@ bool menuModelGVars(event_t event) return true; } +#endif \ No newline at end of file diff --git a/radio/src/gui/colorlcd/model_heli.cpp b/radio/src/gui/colorlcd/model_heli.cpp new file mode 100644 index 000000000..43d2984eb --- /dev/null +++ b/radio/src/gui/colorlcd/model_heli.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "model_heli.h" +#include "opentx.h" +#include "libopenui.h" + +#define SET_DIRTY() storageDirty(EE_MODEL) + +ModelHeliPage::ModelHeliPage(): + PageTab(STR_MENUHELISETUP, ICON_MODEL_HELI) +{ +} + +void ModelHeliPage::build(FormWindow * window) +{ + FormGridLayout grid; + grid.spacer(PAGE_PADDING); + + // Swash type + new StaticText(window, grid.getLabelSlot(), STR_SWASHTYPE); + auto first = new Choice(window, grid.getFieldSlot(), STR_VSWASHTYPE, 0, SWASH_TYPE_MAX, GET_SET_DEFAULT(g_model.swashR.type)); + grid.nextLine(); + + // Swash ring + new StaticText(window, grid.getLabelSlot(), STR_SWASHRING); + new NumberEdit(window, grid.getFieldSlot(), 0, 100, GET_SET_DEFAULT(g_model.swashR.value)); + grid.nextLine(); + + // Elevator source + new StaticText(window, grid.getLabelSlot(), STR_ELEVATOR); + new SourceChoice(window, grid.getFieldSlot(), 0, MIXSRC_LAST_CH, GET_SET_DEFAULT(g_model.swashR.elevatorSource)); + grid.nextLine(); + + // Elevator weight + new StaticText(window, grid.getLabelSlot(), STR_WEIGHT); + new NumberEdit(window, grid.getFieldSlot(), -100, 100, GET_SET_DEFAULT(g_model.swashR.elevatorWeight)); + grid.nextLine(); + + // Aileron source + new StaticText(window, grid.getLabelSlot(), STR_AILERON); + new SourceChoice(window, grid.getFieldSlot(), 0, MIXSRC_LAST_CH, GET_SET_DEFAULT(g_model.swashR.aileronWeight)); + grid.nextLine(); + + // Aileron weight + new StaticText(window, grid.getLabelSlot(), STR_WEIGHT); + new NumberEdit(window, grid.getFieldSlot(), -100, 100, GET_SET_DEFAULT(g_model.swashR.aileronWeight)); + grid.nextLine(); + + // Collective source + new StaticText(window, grid.getLabelSlot(), STR_COLLECTIVE); + new SourceChoice(window, grid.getFieldSlot(), 0, MIXSRC_LAST_CH, GET_SET_DEFAULT(g_model.swashR.collectiveSource)); + grid.nextLine(); + + // Collective weight + new StaticText(window, grid.getLabelSlot(), STR_WEIGHT); + auto last = new NumberEdit(window, grid.getFieldSlot(), -100, 100, GET_SET_DEFAULT(g_model.swashR.collectiveWeight)); + FormField::link(last, first); + + grid.nextLine(); + + window->setInnerHeight(grid.getWindowHeight()); +} diff --git a/radio/src/gui/colorlcd/model_heli.h b/radio/src/gui/colorlcd/model_heli.h new file mode 100644 index 000000000..4f46fdae6 --- /dev/null +++ b/radio/src/gui/colorlcd/model_heli.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "tabsgroup.h" + +class ModelHeliPage: public PageTab { + public: + ModelHeliPage(); + + void build(FormWindow * window); +}; diff --git a/radio/src/gui/colorlcd/model_inputs.cpp b/radio/src/gui/colorlcd/model_inputs.cpp new file mode 100644 index 000000000..d83b91014 --- /dev/null +++ b/radio/src/gui/colorlcd/model_inputs.cpp @@ -0,0 +1,518 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "model_inputs.h" +#include "opentx.h" + +#define SET_DIRTY() storageDirty(EE_MODEL) + +#define PASTE_BEFORE -2 +#define PASTE_AFTER -1 + +uint8_t getExposCount() +{ + uint8_t count = 0; + uint8_t ch ; + + for (int i=MAX_EXPOS-1 ; i>=0; i--) { + ch = EXPO_VALID(expoAddress(i)); + if (ch != 0) { + count++; + } + } + return count; +} + +bool reachExposLimit() +{ + if (getExposCount() >= MAX_EXPOS) { + POPUP_WARNING(STR_NOFREEEXPO); + return true; + } + return false; +} + +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)); + memcpy(expo, &sourceExpo, sizeof(ExpoData)); + expo->chn = (expo+1)->chn; + } + else { + memmove(expo+1, expo, (MAX_EXPOS-(source+1))*sizeof(ExpoData)); + memcpy(expo, &sourceExpo, sizeof(ExpoData)); + expo->chn = input; + } + resumeMixerCalculations(); + storageDirty(EE_MODEL); +} + +void deleteExpo(uint8_t idx) +{ + pauseMixerCalculations(); + ExpoData * expo = expoAddress(idx); + int input = expo->chn; + memmove(expo, expo+1, (MAX_EXPOS-(idx+1))*sizeof(ExpoData)); + memclear(&g_model.expoData[MAX_EXPOS-1], sizeof(ExpoData)); + if (!isInputAvailable(input)) { + memclear(&g_model.inputNames[input], LEN_INPUT_NAME); + } + resumeMixerCalculations(); + storageDirty(EE_MODEL); +} + +class InputEditWindow: public Page { + public: + InputEditWindow(int8_t input, uint8_t index): + Page(ICON_MODEL_INPUTS), + input(input), + index(index), + preview(this, {LCD_W - 158, 0, 158, 158}, + [=](int x) -> int { + ExpoData * line = expoAddress(index); + int16_t anas[MAX_INPUTS] = {0}; + applyExpos(anas, e_perout_mode_inactive_flight_mode, line->srcRaw, x); + return anas[line->chn]; + }, + [=]() -> int { + return getValue(expoAddress(index)->srcRaw); + }) + { + buildBody(&body); + buildHeader(&header); + } + + protected: + uint8_t input; + uint8_t index; + Curve preview; + Choice * trimChoice = nullptr; + Window * updateCurvesWindow = nullptr; + Choice * curveTypeChoice = nullptr; + + void buildHeader(Window * window) { + new StaticText(window, { 50, 0, 100, 20 }, STR_MENUINPUTS, MENU_TITLE_COLOR); + new StaticText(window, { 50, FH+2, 100, 20 }, getSourceString(MIXSRC_FIRST_INPUT + input), MENU_TITLE_COLOR); + } + + void updateCurves() { + FormGridLayout grid; + grid.setLabelWidth(120); + + updateCurvesWindow->clear(); + + ExpoData * line = expoAddress(index) ; + + new StaticText(updateCurvesWindow, grid.getLabelSlot(), STR_CURVE); + curveTypeChoice = new Choice(updateCurvesWindow, grid.getFieldSlot(2, 0), "\004DiffExpoFuncCstm", 0, CURVE_REF_CUSTOM, + GET_DEFAULT(line->curve.type), + [=](int32_t newValue) { + line->curve.type = newValue; + line->curve.value = 0; + SET_DIRTY(); + updateCurves(); + curveTypeChoice->setFocus(); + }); + + switch (line->curve.type) { + case CURVE_REF_DIFF: + case CURVE_REF_EXPO: { + // TODO GVAR + NumberEdit * edit = new NumberEdit(updateCurvesWindow, grid.getFieldSlot(2, 1), -100, 100, + GET_SET_DEFAULT(line->curve.value)); + edit->setSuffix("%"); + break; + } + case CURVE_REF_FUNC: + new Choice(updateCurvesWindow, grid.getFieldSlot(2, 1), STR_VCURVEFUNC, 0, CURVE_BASE-1, GET_SET_DEFAULT(line->curve.value)); + break; + case CURVE_REF_CUSTOM: + #warning "TODO Custom Curve Choice" + // new CustomCurveChoice(updateCurvesWindow, grid.getFieldSlot(2, 1), -MAX_CURVES, MAX_CURVES, GET_SET_DEFAULT(line->curve.value)); + break; + } + } + + void buildBody(FormWindow * window) + { + NumberEdit * edit; + + FormGridLayout grid; + grid.setLabelWidth(120); + grid.spacer(PAGE_PADDING); + + ExpoData * line = expoAddress(index) ; + + grid.setMarginRight(163); + + // Input Name + new StaticText(window, grid.getLabelSlot(), STR_INPUTNAME); + auto name = new TextEdit(window, grid.getFieldSlot(), g_model.inputNames[line->chn], sizeof(g_model.inputNames[line->chn])); + window->setFirstField(name); + grid.nextLine(); + + // Switch + new StaticText(window, grid.getLabelSlot(), STR_SWITCH); + new SwitchChoice(window, grid.getFieldSlot(), SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES, GET_SET_DEFAULT(line->swtch)); + grid.nextLine(); + + // Side + new StaticText(window, grid.getLabelSlot(), STR_SIDE); + new Choice(window, grid.getFieldSlot(), STR_VCURVEFUNC, 1, 3, + [=]() -> int16_t { + return 4 - line->mode; + }, + [=](int16_t newValue) { + line->mode = 4 - newValue; + SET_DIRTY(); + }); + grid.nextLine(); + + // Name + new StaticText(window, grid.getLabelSlot(), STR_EXPONAME); + new TextEdit(window, grid.getFieldSlot(), line->name, sizeof(line->name)); + grid.nextLine(); + + // Source + new StaticText(window, grid.getLabelSlot(), STR_SOURCE); + new SourceChoice(window, grid.getFieldSlot(), INPUTSRC_FIRST, INPUTSRC_LAST, + GET_DEFAULT(line->srcRaw), + [=] (int32_t newValue) { + line->srcRaw = newValue; + if (line->srcRaw > MIXSRC_Ail && line->carryTrim == TRIM_ON) { + line->carryTrim = TRIM_OFF; + trimChoice->invalidate(); + } + SET_DIRTY(); + } + ); + /* TODO telemetry current value + if (ed->srcRaw >= MIXSRC_FIRST_TELEM) { + drawSensorCustomValue(EXPO_ONE_2ND_COLUMN+75, y, (ed->srcRaw - MIXSRC_FIRST_TELEM)/3, convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale), LEFT|(menuHorizontalPosition==1?attr:0)); + } */ + grid.nextLine(); + + // Scale + // TODO only displayed when source is telemetry + unfinished + new StaticText(window, grid.getLabelSlot(), STR_SCALE); + new NumberEdit(window, grid.getFieldSlot(), -100, 100, GET_SET_DEFAULT(line->scale)); + grid.nextLine(); + + // Weight + new StaticText(window, grid.getLabelSlot(), STR_WEIGHT); + // TODO GVAR ? + edit = new NumberEdit(window, grid.getFieldSlot(), -100, 100, GET_SET_DEFAULT(line->weight)); + edit->setSuffix("%"); + grid.nextLine(); + + // Offset + new StaticText(window, grid.getLabelSlot(), STR_OFFSET); + edit = new NumberEdit(window, grid.getFieldSlot(), -100, 100, GET_SET_DEFAULT(line->offset)); + edit->setSuffix("%"); + grid.nextLine(); + + // Trim + new StaticText(window, grid.getLabelSlot(), STR_TRIM); + trimChoice = new Choice(window, grid.getFieldSlot(), STR_VMIXTRIMS, -TRIM_OFF, -TRIM_LAST, + GET_VALUE(-line->carryTrim), + SET_VALUE(line->carryTrim, -newValue)); + trimChoice->setAvailableHandler([=](int value) { + return value != TRIM_ON || line->srcRaw <= MIXSRC_Ail; + }); + grid.nextLine(); + + // grid.setMarginRight(10); + + // Curve + updateCurvesWindow = new Window(window, { 0, grid.getWindowHeight(), LCD_W - 162, 0 }); + updateCurves(); + grid.addWindow(updateCurvesWindow); + + // Flight modes + new StaticText(window, grid.getLabelSlot(), STR_FLMODE); + TextButton * flightmode = nullptr; + for (uint8_t i=0; i 0 && (i % 4) == 0) + grid.nextLine(); + flightmode = new TextButton(window, grid.getFieldSlot(4, i % 4), fm, + [=]() -> uint8_t { + BFBIT_FLIP(line->flightModes, bfBit(i)); + SET_DIRTY(); + return !(bfSingleBitGet(line->flightModes, i)); + }, + bfSingleBitGet(line->flightModes, i) ? 0 : BUTTON_CHECKED); + } + grid.nextLine(); + window->setLastField(flightmode); + window->setInnerHeight(grid.getWindowHeight()); + } +}; + +void CommonInputOrMixButton::checkEvents() +{ + if (active != isActive()) { + invalidate(); + active = !active; + } + + Button::checkEvents(); +} + +void CommonInputOrMixButton::drawFlightModes(BitmapBuffer *dc, FlightModesType value) +{ + dc->drawBitmap(146, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupFlightmodeBitmap); + coord_t x = 166; + for (int i = 0; i < MAX_FLIGHT_MODES; i++) { + char s[] = " "; + s[0] = '0' + i; + if (value & (1 << i)) { + dc->drawText(x, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, s, SMLSIZE | TEXT_DISABLE_COLOR); + } + else { + dc->drawSolidFilledRect(x, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, 8, 3, SCROLLBOX_COLOR); + dc->drawText(x, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, s, SMLSIZE); + } + x += 8; + } +} + +void CommonInputOrMixButton::paint(BitmapBuffer * dc) +{ + if (active) + dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, WARNING_COLOR); + paintBody(dc); + drawSolidRect(dc, 0, 0, rect.w, rect.h, 2, hasFocus() ? SCROLLBOX_COLOR : CURVE_AXIS_COLOR); +} + +class InputLineButton : public CommonInputOrMixButton { + public: + InputLineButton(Window * parent, const rect_t & rect, uint8_t index): + CommonInputOrMixButton(parent, rect, index) + { + const ExpoData & line = g_model.expoData[index]; + if (line.swtch || line.curve.value != 0 || line.flightModes) { + setHeight(getHeight() + 20); + } + } + + bool isActive() override + { + return isExpoActive(index); + } + + void paintBody(BitmapBuffer * dc) override + { + const ExpoData & line = g_model.expoData[index]; + + // first line ... + drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight); + drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw); + + if (line.name[0]) { + dc->drawBitmap(146, 2 + FIELD_PADDING_TOP, mixerSetupLabelBitmap); + dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name), ZCHAR); + } + + // second line ... + if (line.swtch) { + dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupSwitchIcon, TEXT_COLOR); + drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch); + } + + if (line.curve.value != 0 ) { + dc->drawBitmap(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupCurveBitmap); + drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve); + } + + if (line.flightModes) { + drawFlightModes(dc, line.flightModes); + } + } +}; + +ModelInputsPage::ModelInputsPage(): + PageTab(STR_MENUINPUTS, ICON_MODEL_INPUTS) +{ +} + +void ModelInputsPage::rebuild(FormWindow * window, int8_t focusIndex) +{ + coord_t scrollPosition = window->getScrollPositionY(); + window->clear(); + build(window, focusIndex); + window->setScrollPositionY(scrollPosition); +} + +void ModelInputsPage::editInput(FormWindow * window, uint8_t input, uint8_t index) +{ + Window * editWindow = new InputEditWindow(input, index); + editWindow->setCloseHandler([=]() { + rebuild(window, index); + }); +} + +void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) +{ + FormGridLayout grid; + grid.spacer(PAGE_PADDING); + grid.setLabelWidth(66); + + Window::clearFocus(); + + int inputIndex = 0; + ExpoData * line = g_model.expoData; + for (uint8_t input=0; inputchn == input && EXPO_VALID(line)) { + new StaticText(window, grid.getLabelSlot(), getSourceString(MIXSRC_FIRST_INPUT + input), BUTTON_BACKGROUND | CENTERED); + while (inputIndex < MAX_EXPOS && line->chn == input && EXPO_VALID(line)) { + Button * button = new InputLineButton(window, grid.getFieldSlot(), inputIndex); + if (focusIndex == inputIndex) + button->setFocus(); + button->setPressHandler([=]() -> uint8_t { + button->bringToTop(); + Menu * menu = new Menu(); + menu->addLine(STR_EDIT, [=]() { + editInput(window, input, inputIndex); + }); + if (!reachExposLimit()) { + menu->addLine(STR_INSERT_BEFORE, [=]() { + insertExpo(inputIndex, input); + editInput(window, input, inputIndex); + }); + menu->addLine(STR_INSERT_AFTER, [=]() { + insertExpo(inputIndex + 1, input); + editInput(window, input, inputIndex + 1); + }); + menu->addLine(STR_COPY, [=]() { + s_copyMode = COPY_MODE; + s_copySrcIdx = inputIndex; + }); + if (s_copyMode != 0) { + menu->addLine(STR_PASTE_BEFORE, [=]() { + copyExpo(s_copySrcIdx, inputIndex, PASTE_BEFORE); + if (s_copyMode == MOVE_MODE) { + deleteExpo((s_copySrcIdx > inputIndex) ? s_copySrcIdx+1 : s_copySrcIdx); + s_copyMode = 0; + } + rebuild(window, inputIndex); + }); + menu->addLine(STR_PASTE_AFTER, [=]() { + copyExpo(s_copySrcIdx, inputIndex, PASTE_AFTER); + if (s_copyMode == MOVE_MODE) { + deleteExpo((s_copySrcIdx > inputIndex) ? s_copySrcIdx+1 : s_copySrcIdx); + s_copyMode = 0; + } + rebuild(window, inputIndex+1); + }); + } + } + menu->addLine(STR_MOVE, [=]() { + s_copyMode = MOVE_MODE; + s_copySrcIdx = inputIndex; + }); + menu->addLine(STR_DELETE, [=]() { + deleteExpo(inputIndex); + rebuild(window, -1); + }); + return 0; + }); + + grid.spacer(button->height() - 2); + + ++inputIndex; + ++line; + } + + grid.spacer(7); + } + else { + auto button = new TextButton(window, grid.getLabelSlot(), getSourceString(MIXSRC_FIRST_INPUT + input)); + if (focusIndex == inputIndex) + button->setFocus(); + button->setPressHandler([=]() -> uint8_t { + button->bringToTop(); + Menu * menu = new Menu(); + menu->addLine(STR_EDIT, [=]() { + insertExpo(inputIndex, input); + editInput(window, input, inputIndex); + return 0; + }); + if (!reachExposLimit()) { + if (s_copyMode != 0) { + menu->addLine(STR_PASTE, [=]() { + copyExpo(s_copySrcIdx, inputIndex, input); + if(s_copyMode == MOVE_MODE) { + deleteExpo((s_copySrcIdx >= inputIndex) ? s_copySrcIdx+1 : s_copySrcIdx); + s_copyMode = 0; + } + rebuild(window, -1); + return 0; + }); + } + } + return 0; + }); + + grid.spacer(button->height() + 5); + } + } + + Window * focus = Window::getFocus(); + if (focus) { + focus->bringToTop(); + } + + grid.nextLine(); + + window->setLastField(); + window->setInnerHeight(grid.getWindowHeight()); +} + +// TODO port: avoid global s_currCh on ARM boards (as done here)... +int8_t s_currCh; +uint8_t s_copyMode; +int8_t s_copySrcRow; + +void insertExpo(uint8_t idx, uint8_t input) +{ + pauseMixerCalculations(); + ExpoData * expo = expoAddress(idx); + memmove(expo+1, expo, (MAX_EXPOS-(idx+1))*sizeof(ExpoData)); + memclear(expo, sizeof(ExpoData)); + expo->srcRaw = (input >= 4 ? MIXSRC_Rud + input : MIXSRC_Rud + channelOrder(input + 1) - 1); + expo->curve.type = CURVE_REF_EXPO; + expo->mode = 3; // pos+neg + expo->chn = input; + expo->weight = 100; + resumeMixerCalculations(); + storageDirty(EE_MODEL); +} \ No newline at end of file diff --git a/radio/src/gui/colorlcd/model_inputs.h b/radio/src/gui/colorlcd/model_inputs.h new file mode 100644 index 000000000..f1c0b9b3c --- /dev/null +++ b/radio/src/gui/colorlcd/model_inputs.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _MODEL_INPUTS_H +#define _MODEL_INPUTS_H + +#include "tabsgroup.h" + +class ModelInputsPage: public PageTab { + public: + ModelInputsPage(); + + void build(FormWindow * window) override + { + build(window, 0); + } + + protected: + void build(FormWindow * window, int8_t focusIndex); + void rebuild(FormWindow * window, int8_t focusIndex); + void editInput(FormWindow * window, uint8_t channel, uint8_t index); + uint8_t s_copySrcIdx; +}; + +class CommonInputOrMixButton : public Button { + public: + CommonInputOrMixButton(Window * parent, const rect_t & rect, uint8_t index): + Button(parent, rect), + index(index) + { + setFocusHandler([=]() { + bringToTop(); + }); + } + + virtual bool isActive() = 0; + + void checkEvents() override; + + void drawFlightModes(BitmapBuffer *dc, FlightModesType value); + + void paint(BitmapBuffer * dc) override; + + virtual void paintBody(BitmapBuffer * dc) = 0; + + protected: + uint8_t index; + bool active = false; +}; + + +#endif //_MODEL_INPUTS_H diff --git a/radio/src/gui/colorlcd/model_logical_switches.cpp b/radio/src/gui/colorlcd/model_logical_switches.cpp new file mode 100644 index 000000000..a1161b0d5 --- /dev/null +++ b/radio/src/gui/colorlcd/model_logical_switches.cpp @@ -0,0 +1,387 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "model_logical_switches.h" +#include "opentx.h" +#include "libopenui.h" + +#define SET_DIRTY() storageDirty(EE_MODEL) + +void putsEdgeDelayParam(coord_t x, coord_t y, LogicalSwitchData * ls) +{ + lcdDrawChar(x, y, '['); + lcdDrawNumber(lcdNextPos+2, y, lswTimerValue(ls->v2), LEFT|PREC1); + lcdDrawChar(lcdNextPos, y, ':'); + if (ls->v3 < 0) + lcdDrawText(lcdNextPos+3, y, "<<"); + else if (ls->v3 == 0) + lcdDrawText(lcdNextPos+3, y, "--"); + else + lcdDrawNumber(lcdNextPos+3, y, lswTimerValue(ls->v2+ls->v3), LEFT|PREC1); + lcdDrawChar(lcdNextPos, y, ']'); +} + +class LogicalSwitchEditWindow: public Page { + public: + LogicalSwitchEditWindow(uint8_t ls): + Page(ICON_MODEL_LOGICAL_SWITCHES), + ls(ls) + { + buildBody(&body); + buildHeader(&header); + } + + bool isActive() { + return getSwitch(SWSRC_FIRST_LOGICAL_SWITCH+ls); + } + + void checkEvents() override + { + if (active != isActive()) { + headerSwitchName->setFlags(isActive() ? BOLD|WARNING_COLOR : MENU_TITLE_COLOR); + active = !active; + } + } + + protected: + uint8_t ls; + bool active = false; + Window * logicalSwitchOneWindow = nullptr; + StaticText * headerSwitchName = nullptr; + NumberEdit * v2Edit = nullptr; + + void updateLogicalSwitchOneWindow() + { + FormGridLayout grid; + logicalSwitchOneWindow->clear(); + + LogicalSwitchData * cs = lswAddress(ls); + uint8_t cstate = lswFamily(cs->func); + + if (cstate == LS_FAMILY_BOOL || cstate == LS_FAMILY_STICKY) { + new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V1); + auto choice = new SwitchChoice(logicalSwitchOneWindow, grid.getFieldSlot(), SWSRC_FIRST_IN_LOGICAL_SWITCHES, SWSRC_LAST_IN_LOGICAL_SWITCHES, GET_SET_DEFAULT(cs->v1)); + choice->setAvailableHandler(isSwitchAvailableInLogicalSwitches); + grid.nextLine(); + + new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V2); + choice = new SwitchChoice(logicalSwitchOneWindow, grid.getFieldSlot(), SWSRC_FIRST_IN_LOGICAL_SWITCHES, SWSRC_LAST_IN_LOGICAL_SWITCHES, GET_SET_DEFAULT(cs->v2)); + choice->setAvailableHandler(isSwitchAvailableInLogicalSwitches); + grid.nextLine(); + } + else if (cstate == LS_FAMILY_EDGE) { + new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V1); + auto choice = new SwitchChoice(logicalSwitchOneWindow, grid.getFieldSlot(), SWSRC_FIRST_IN_LOGICAL_SWITCHES, SWSRC_LAST_IN_LOGICAL_SWITCHES, GET_SET_DEFAULT(cs->v1)); + choice->setAvailableHandler(isSwitchAvailableInLogicalSwitches); + grid.nextLine(); + + auto edit1 = new NumberEdit(logicalSwitchOneWindow, grid.getFieldSlot(2, 0), -129, 122, GET_DEFAULT(cs->v2)); + auto edit2 = new NumberEdit(logicalSwitchOneWindow, grid.getFieldSlot(2, 1), -1, 222 - cs->v2, GET_SET_DEFAULT(cs->v3)); + edit1->setSetValueHandler([=](int32_t newValue) { + cs->v2 = newValue; + cs->v3 = min(cs->v3, 222 - cs->v2); + SET_DIRTY(); + edit2->setMax(222 - cs->v2); + edit2->invalidate(); + }); + edit1->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + drawNumber(dc, 2, 2, lswTimerValue(value), flags | PREC1); + }); + edit2->setDisplayHandler([=](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + if (value < 0) + dc->drawText(2, 2, "<<", flags); + else if (value == 0) + dc->drawText(2, 2, "--", flags); + else + drawNumber(dc, 2, 2, lswTimerValue(cs->v2 + value), flags | PREC1); + }); + grid.nextLine(); + } + else if (cstate == LS_FAMILY_COMP) { + new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V1); + new SourceChoice(logicalSwitchOneWindow, grid.getFieldSlot(), 0, MIXSRC_LAST_TELEM, GET_SET_DEFAULT(cs->v1)); + grid.nextLine(); + + new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V2); + new SourceChoice(logicalSwitchOneWindow, grid.getFieldSlot(), 0, MIXSRC_LAST_TELEM, GET_SET_DEFAULT(cs->v2)); + grid.nextLine(); + } + else if (cstate == LS_FAMILY_TIMER) { + 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) { + drawNumber(dc, 2, 2, lswTimerValue(value), flags | PREC1); + }); + 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) { + drawNumber(dc, 2, 2, lswTimerValue(value), flags | PREC1); + }); + grid.nextLine(); + } + else { + new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V1); + new SourceChoice(logicalSwitchOneWindow, grid.getFieldSlot(), 0, MIXSRC_LAST_TELEM, GET_DEFAULT(cs->v1), + [=](int32_t newValue) { + cs->v1 = newValue; + SET_DIRTY(); + v2Edit->invalidate(); + }); + grid.nextLine(); + + new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V2); + int16_t v2_min = 0, v2_max = 0; + getMixSrcRange(cs->v1, v2_min, v2_max); + v2Edit = new NumberEdit(logicalSwitchOneWindow, grid.getFieldSlot(), 0, MAX_LS_DELAY, GET_SET_DEFAULT(cs->v2)); + v2Edit->setDisplayHandler([=](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + drawSourceCustomValue(dc, 2, 2, cs->v1, (cs->v1 <= MIXSRC_LAST_CH ? calc100toRESX(value) : value), flags); + }); + grid.nextLine(); + } + + // AND switch + new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_AND_SWITCH); + auto choice = new SwitchChoice(logicalSwitchOneWindow, grid.getFieldSlot(), -MAX_LS_ANDSW, MAX_LS_ANDSW, GET_SET_DEFAULT(cs->andsw)); + choice->setAvailableHandler(isSwitchAvailableInLogicalSwitches); + grid.nextLine(); + + // Duration + new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_DURATION); + auto edit = new NumberEdit(logicalSwitchOneWindow, grid.getFieldSlot(), 0, MAX_LS_DURATION, GET_SET_DEFAULT(cs->duration), PREC1); + edit->setZeroText("---"); + grid.nextLine(); + + // Delay + new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_DELAY); + if (cstate == LS_FAMILY_EDGE) { + new StaticText(logicalSwitchOneWindow, grid.getFieldSlot(), STR_NA); + } + else { + auto edit = new NumberEdit(logicalSwitchOneWindow, grid.getFieldSlot(), 0, MAX_LS_DELAY, GET_SET_DEFAULT(cs->delay), PREC1); + edit->setZeroText("---"); + } + grid.nextLine(); + } + + void buildHeader(Window * window) { + new StaticText(window, { 70, 4, LCD_W - 100, 20 }, STR_MENULOGICALSWITCHES, MENU_TITLE_COLOR); + headerSwitchName = new StaticText(window, { 70, 28, LCD_W - 100, 20 }, getSwitchPositionName(SWSRC_SW1+ls), MENU_TITLE_COLOR); + } + + void buildBody(Window * window) { + LogicalSwitchData * cs = lswAddress(ls); + + FormGridLayout grid; + grid.spacer(10); + + // LS Func + new StaticText(window, grid.getLabelSlot(), STR_FUNC); + Choice * funcChoice = new Choice(window, grid.getFieldSlot(), STR_VCSWFUNC, 0, LS_FUNC_MAX, + GET_DEFAULT(cs->func), + [=](int32_t newValue) { + cs->func = newValue; + if (lswFamily(cs->func) == LS_FAMILY_TIMER) { + cs->v1 = cs->v2 = 0; + } + else if (lswFamily(cs->func) == LS_FAMILY_EDGE) { + cs->v1 = 0; cs->v2 = -129; cs->v3 = 0; + } + else { + cs->v1 = cs->v2 = 0; + } + SET_DIRTY(); + updateLogicalSwitchOneWindow(); + }); + funcChoice->setAvailableHandler(isLogicalSwitchFunctionAvailable); + grid.nextLine(); + + logicalSwitchOneWindow = new Window(window, { 0, grid.getWindowHeight(), LCD_W, 0 }); + updateLogicalSwitchOneWindow(); + grid.addWindow(logicalSwitchOneWindow); + } +}; + +static constexpr coord_t line1 = 2; +static constexpr coord_t line2 = 22; +static constexpr coord_t line3 = 42; +static constexpr coord_t col1 = 20; +static constexpr coord_t col2 = (LCD_W - 100) / 3 + col1; +static constexpr coord_t col3 = ((LCD_W - 100) / 3) * 2 + col1; + +class LogicalSwitchButton : public Button { + public: + LogicalSwitchButton(Window * parent, const rect_t & rect, int lsIndex, std::function onPress): + Button(parent, rect, onPress), + lsIndex(lsIndex), + active(isActive()) + { + LogicalSwitchData * ls = lswAddress(lsIndex); + if (ls->andsw != SWSRC_NONE || ls->duration != 0 || ls->delay != 0) + setHeight(getHeight() + 20); + if (lswFamily(ls->func) == LS_FAMILY_EDGE) + setHeight(getHeight() + 20); + } + + bool isActive() + { + return getSwitch(SWSRC_FIRST_LOGICAL_SWITCH + lsIndex); + } + + void checkEvents() override + { + if (active != isActive()) { + invalidate(); + active = !active; + } + + Button::checkEvents(); + } + + virtual void paint(BitmapBuffer * dc) override + { + LogicalSwitchData * ls = lswAddress(lsIndex); + uint8_t lsFamily = lswFamily(ls->func); + + if (active) + dc->drawSolidFilledRect(2, 2, rect.w-4, rect.h-4, WARNING_COLOR); + + // The bounding rect + drawSolidRect(dc, 0, 0, rect.w, rect.h, 2, hasFocus() ? SCROLLBOX_COLOR : CURVE_AXIS_COLOR); + + // CSW func + lcdDrawTextAtIndex(col1, line1, STR_VCSWFUNC, ls->func); + + // CSW params + if (lsFamily == LS_FAMILY_BOOL || lsFamily == LS_FAMILY_STICKY) { + drawSwitch(dc, col2, line1, ls->v1); + drawSwitch(dc, col3, line1, ls->v2); + } + else if (lsFamily == LS_FAMILY_EDGE) { + drawSwitch(dc, col1, line2, ls->v1); + putsEdgeDelayParam(col2, line2, ls); + } + else if (lsFamily == LS_FAMILY_COMP) { + drawSource(dc, col2, line1, ls->v1, 0); + drawSource(dc, col3, line1, ls->v2, 0); + } + else if (lsFamily == LS_FAMILY_TIMER) { + lcdDrawNumber(col2, line1, lswTimerValue(ls->v1), LEFT|PREC1); + lcdDrawNumber(col3, line1, lswTimerValue(ls->v2), LEFT|PREC1); + } + else { + drawSource(dc, col2, line1, ls->v1, 0); + drawSourceCustomValue(dc, col3, line1, ls->v1, (ls->v1 <= MIXSRC_LAST_CH ? calc100toRESX(ls->v2) : ls->v2), 0); + } + + // AND switch + drawSwitch(dc, col1, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, ls->andsw, 0); + + // CSW duration + if (ls->duration > 0) { + drawNumber(dc, col2, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, ls->duration, PREC1 | LEFT); + } + + // CSW delay + if (lsFamily != LS_FAMILY_EDGE && ls->delay > 0) { + drawNumber(dc, col3, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, ls->delay, PREC1 | LEFT); + } + } + + protected: + uint8_t lsIndex; + bool active; +}; + +ModelLogicalSwitchesPage::ModelLogicalSwitchesPage(): + PageTab(STR_MENULOGICALSWITCHES, ICON_MODEL_LOGICAL_SWITCHES) +{ +} + +void ModelLogicalSwitchesPage::rebuild(FormWindow * window, int8_t focusIndex) +{ + coord_t scrollPosition = window->getScrollPositionY(); + window->clear(); + build(window, focusIndex); + window->setScrollPositionY(scrollPosition); +} + +void ModelLogicalSwitchesPage::editLogicalSwitch(FormWindow * window, uint8_t lsIndex) +{ + Window * lsWindow = new LogicalSwitchEditWindow(lsIndex); + lsWindow->setCloseHandler([=]() { + rebuild(window, lsIndex); + }); +} + +void ModelLogicalSwitchesPage::build(FormWindow * window, int8_t focusIndex) +{ + FormGridLayout grid; + grid.spacer(PAGE_PADDING); + grid.setLabelWidth(66); + + for (uint8_t i=0; ifunc == LS_FUNC_NONE) { + auto label = new TextButton(window, grid.getLabelSlot(), getSwitchPositionName(SWSRC_SW1+i)); + grid.spacer(label->height() + 5); + } + else { + new StaticText(window, grid.getLabelSlot(), getSwitchPositionName(SWSRC_SW1 + i), BUTTON_BACKGROUND | CENTERED); + Button * button = new LogicalSwitchButton(window, grid.getFieldSlot(), i, + [=]() -> uint8_t { + Menu *menu = new Menu(); + LogicalSwitchData *ls = lswAddress(i); + menu->addLine(STR_EDIT, [=]() { + editLogicalSwitch(window, i); + }); + if (ls->func) + menu->addLine(STR_COPY, [=]() { + clipboard.type = CLIPBOARD_TYPE_CUSTOM_SWITCH; + clipboard.data.csw = *ls; + }); + if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_SWITCH) + menu->addLine(STR_PASTE, [=]() { + *ls = clipboard.data.csw; + storageDirty(EE_MODEL); + rebuild(window, i); + }); + if (ls->func || ls->v1 || ls->v2 || ls->delay || ls->duration || ls->andsw) + menu->addLine(STR_CLEAR, [=]() { + memset(ls, 0, sizeof(LogicalSwitchData)); + storageDirty(EE_MODEL); + rebuild(window, i); + }); + return 0; + }); + if (focusIndex == i) { + button->setFocus(); + } + + grid.spacer(button->height() + 5); + } + } + + grid.nextLine(); + + window->setLastField(); + window->setInnerHeight(grid.getWindowHeight()); +} diff --git a/radio/src/gui/colorlcd/model_logical_switches.h b/radio/src/gui/colorlcd/model_logical_switches.h new file mode 100644 index 000000000..fc3a505bf --- /dev/null +++ b/radio/src/gui/colorlcd/model_logical_switches.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _MODEL_LOGICAL_SWITCHES_H +#define _MODEL_LOGICAL_SWITCHES_H + +#include "tabsgroup.h" + +class ModelLogicalSwitchesPage: public PageTab { +public: + ModelLogicalSwitchesPage(); + + virtual void build(FormWindow * window) override + { + build(window, -1); + } + +protected: + void build(FormWindow * window, int8_t focusIndex); + void rebuild(FormWindow * window, int8_t focusIndex); + void editLogicalSwitch(FormWindow * window, uint8_t lsIndex); +}; + +#endif //_MODEL_LOGICAL_SWITCHES_H diff --git a/radio/src/gui/colorlcd/model_mixes.cpp b/radio/src/gui/colorlcd/model_mixes.cpp new file mode 100644 index 000000000..149c9afb8 --- /dev/null +++ b/radio/src/gui/colorlcd/model_mixes.cpp @@ -0,0 +1,530 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "model_mixes.h" +#include "opentx.h" +#include "libopenui.h" +#include "bitfield.h" +#include "model_inputs.h" + +#define SET_DIRTY() storageDirty(EE_MODEL) + +#define PASTE_BEFORE -2 +#define PASTE_AFTER -1 + +uint8_t getMixesCount() +{ + uint8_t count = 0; + uint8_t ch; + + for (int i = MAX_MIXERS - 1; i >= 0; i--) { + ch = mixAddress(i)->srcRaw; + if (ch != 0) { + count++; + } + } + return count; +} + +bool reachMixesLimit() +{ + if (getMixesCount() >= MAX_MIXERS) { + POPUP_WARNING(STR_NOFREEMIXER); + return true; + } + return false; +} + + +class MixEditWindow : public Page { + public: + MixEditWindow(int8_t channel, uint8_t mixIndex) : + Page(ICON_MODEL_MIXER), + channel(channel), + mixIndex(mixIndex) + { + buildBody(&body); + buildHeader(&header); + } + + protected: + uint8_t channel; + uint8_t mixIndex; + Window * updateCurvesWindow = nullptr; + Choice * curveTypeChoice = nullptr; + + void buildHeader(Window * window) + { + new StaticText(window, {70, 4, 100, 20}, STR_MIXER, MENU_TITLE_COLOR); + new StaticText(window, {70, 28, 100, 20}, getSourceString(MIXSRC_CH1 + channel), MENU_TITLE_COLOR); + } + + void updateCurves() + { + FormGridLayout grid; + updateCurvesWindow->clear(); + + MixData * line = mixAddress(mixIndex); + + new StaticText(updateCurvesWindow, grid.getLabelSlot(), STR_CURVE); + curveTypeChoice = new Choice(updateCurvesWindow, grid.getFieldSlot(2, 0), "\004DiffExpoFuncCstm", 0, CURVE_REF_CUSTOM, + GET_DEFAULT(line->curve.type), + [=](int32_t newValue) { + line->curve.type = newValue; + line->curve.value = 0; + SET_DIRTY(); + updateCurves(); + curveTypeChoice->setFocus(); + }); + + switch (line->curve.type) { + case CURVE_REF_DIFF: + case CURVE_REF_EXPO: { + // TODO GVAR + NumberEdit * edit = new NumberEdit(updateCurvesWindow, grid.getFieldSlot(2, 1), -100, 100, + GET_SET_DEFAULT(line->curve.value)); + edit->setSuffix("%"); + break; + } + case CURVE_REF_FUNC: + new Choice(updateCurvesWindow, grid.getFieldSlot(2, 1), STR_VCURVEFUNC, 0, CURVE_BASE - 1, GET_SET_DEFAULT(line->curve.value)); + break; + case CURVE_REF_CUSTOM: + //new CustomCurveChoice(updateCurvesWindow, grid.getFieldSlot(2, 1), -MAX_CURVES, MAX_CURVES, GET_SET_DEFAULT(line->curve.value)); + break; + } + } + + void buildBody(Window * window) + { + FormGridLayout grid; + grid.spacer(8); + + MixData * mix = mixAddress(mixIndex); + + // Mix name + new StaticText(window, grid.getLabelSlot(), STR_MIXNAME); + new TextEdit(window, grid.getFieldSlot(), mix->name, sizeof(mix->name)); + grid.nextLine(); + + // Source + new StaticText(window, grid.getLabelSlot(), STR_SOURCE); + new SourceChoice(window, grid.getFieldSlot(), 0, MIXSRC_LAST, GET_SET_DEFAULT(mix->srcRaw)); + grid.nextLine(); + + // Weight + new StaticText(window, grid.getLabelSlot(), STR_WEIGHT); + // TODO GVAR ? + NumberEdit * edit = new NumberEdit(window, grid.getFieldSlot(), -100, 100, GET_SET_DEFAULT(mix->weight)); + edit->setSuffix("%"); + grid.nextLine(); + + // Offset + new StaticText(window, grid.getLabelSlot(), STR_OFFSET); + edit = new NumberEdit(window, grid.getFieldSlot(), GV_RANGELARGE_OFFSET_NEG, GV_RANGELARGE_OFFSET, GET_SET_DEFAULT(mix->offset)); + edit->setSuffix("%"); + grid.nextLine(); + + // Trim + new StaticText(window, grid.getLabelSlot(), STR_TRIM); + new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(mix->carryTrim)); + grid.nextLine(); + + // Curve + updateCurvesWindow = new Window(window, {0, grid.getWindowHeight(), LCD_W, 0}); + updateCurves(); + grid.addWindow(updateCurvesWindow); + + // Flight modes + new StaticText(window, grid.getLabelSlot(), STR_FLMODE); + for (uint8_t i = 0; i < MAX_FLIGHT_MODES; i++) { + char fm[2] = {char('0' + i), '\0'}; + if (i > 0 && (i % 4) == 0) + grid.nextLine(); + new TextButton(window, grid.getFieldSlot(4, i % 4), fm, + [=]() -> uint8_t { + BFBIT_FLIP(mix->flightModes, bfBit(i)); + SET_DIRTY(); + return !(bfSingleBitGet(mix->flightModes, i)); + }, + bfSingleBitGet(mix->flightModes, i) ? 0 : BUTTON_CHECKED); + } + grid.nextLine(); + + // Switch + new StaticText(window, grid.getLabelSlot(), STR_SWITCH); + new SwitchChoice(window, grid.getFieldSlot(), SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES, GET_SET_DEFAULT(mix->swtch)); + grid.nextLine(); + + // Warning + new StaticText(window, grid.getLabelSlot(), STR_MIXWARNING); + edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, 3, GET_SET_DEFAULT(mix->mixWarn)); + edit->setZeroText(STR_OFF); + grid.nextLine(); + + // Multiplex + new StaticText(window, grid.getLabelSlot(), STR_MULTPX); + new Choice(window, grid.getFieldSlot(), STR_VMLTPX, 0, 2, GET_SET_DEFAULT(mix->mltpx)); + grid.nextLine(); + + // Delay up + new StaticText(window, grid.getLabelSlot(), STR_DELAYUP); + edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, + GET_DEFAULT(mix->delayUp), + SET_VALUE(mix->delayUp, newValue), + PREC1); + edit->setStep(10 / DELAY_STEP); + edit->setSuffix("s"); + grid.nextLine(); + + // Delay down + new StaticText(window, grid.getLabelSlot(), STR_DELAYDOWN); + edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, + GET_DEFAULT(mix->delayDown), + SET_VALUE(mix->delayDown, newValue), + PREC1); + edit->setStep(10 / DELAY_STEP); + edit->setSuffix("s"); + grid.nextLine(); + + // Slow up + new StaticText(window, grid.getLabelSlot(), STR_SLOWUP); + edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, + GET_DEFAULT(mix->speedUp), + SET_VALUE(mix->speedUp, newValue), + PREC1); + edit->setStep(10 / DELAY_STEP); + edit->setSuffix("s"); + grid.nextLine(); + + // Slow down + new StaticText(window, grid.getLabelSlot(), STR_SLOWDOWN); + edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, + GET_DEFAULT(mix->speedDown), + SET_VALUE(mix->speedDown, newValue), + PREC1); + edit->setStep(10 / DELAY_STEP); + edit->setSuffix("s"); + grid.nextLine(); + + grid.nextLine(); + + window->setInnerHeight(grid.getWindowHeight()); + } +}; + +class MixLineButton : public CommonInputOrMixButton { + public: + MixLineButton(Window * parent, const rect_t &rect, uint8_t index) : + CommonInputOrMixButton(parent, rect, index) + { + const MixData & mix = g_model.mixData[index]; + if (mix.swtch || mix.curve.value != 0 || mix.flightModes) { + setHeight(getHeight() + 20); + } + } + + bool isActive() override + { + return isMixActive(index); + } + + void paintBody(BitmapBuffer * dc) override + { + const MixData & line = g_model.mixData[index]; + + // first line ... + drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight); + drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw); + + if (line.name[0]) { + dc->drawBitmap(146, FIELD_PADDING_TOP, mixerSetupLabelBitmap); + dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name), ZCHAR); + } + + // second line ... + if (line.swtch) { + dc->drawBitmap(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupSwitchBitmap); + drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch); + } + + if (line.curve.value) { + dc->drawBitmap(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupCurveBitmap); + drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve, 0); + } + + if (line.flightModes) { + drawFlightModes(dc, line.flightModes); + } + } +}; + +void insertMix(uint8_t idx, uint8_t channel) +{ + pauseMixerCalculations(); + MixData * mix = mixAddress(idx); + memmove(mix + 1, mix, (MAX_MIXERS - (idx + 1)) * sizeof(MixData)); + memclear(mix, sizeof(MixData)); + mix->destCh = channel; + mix->srcRaw = channel + 1; + if (!isSourceAvailable(mix->srcRaw)) { + mix->srcRaw = (channel > 3 ? MIXSRC_Rud - 1 + channel : MIXSRC_Rud - 1 + channelOrder(channel)); + while (!isSourceAvailable(mix->srcRaw)) { + mix->srcRaw += 1; + } + } + mix->weight = 100; + resumeMixerCalculations(); + storageDirty(EE_MODEL); +} + +ModelMixesPage::ModelMixesPage() : + PageTab(STR_MIXER, ICON_MODEL_MIXER) +{ +} + +void ModelMixesPage::rebuild(FormWindow * window, int8_t focusMixIndex) +{ + coord_t scrollPosition = window->getScrollPositionY(); + window->clear(); + build(window, focusMixIndex); + window->setScrollPositionY(scrollPosition); +} + +void ModelMixesPage::editMix(FormWindow * window, uint8_t channel, uint8_t mixIndex) +{ + Window * editWindow = new MixEditWindow(channel, mixIndex); + editWindow->setCloseHandler([=]() { + rebuild(window, mixIndex); + }); +} + +void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex) +{ + FormGridLayout grid; + grid.spacer(PAGE_PADDING); + grid.setLabelWidth(66); + + Window::clearFocus(); + + const BitmapBuffer * const mixerMultiplexBitmap[] = { + mixerSetupAddBitmap, + mixerSetupMultiBitmap, + mixerSetupReplaceBitmap + }; + + int mixIndex = 0; + MixData * mix = g_model.mixData; + for (uint8_t ch = 0; ch < MAX_OUTPUT_CHANNELS; ch++) { + if (mixIndex < MAX_MIXERS && mix->srcRaw > 0 && mix->destCh == ch) { + new StaticText(window, grid.getLabelSlot(), getSourceString(MIXSRC_CH1 + ch), BUTTON_BACKGROUND | CENTERED); + uint8_t count = 0; + while (mixIndex < MAX_MIXERS && mix->srcRaw > 0 && mix->destCh == ch) { + Button * button = new MixLineButton(window, grid.getFieldSlot(), mixIndex); + if (focusMixIndex == mixIndex) + button->setFocus(); + button->setPressHandler([=]() -> uint8_t { + button->bringToTop(); + Menu * menu = new Menu(); + menu->addLine(STR_EDIT, [=]() { + editMix(window, ch, mixIndex); + }); + if (!reachMixesLimit()) { + menu->addLine(STR_INSERT_BEFORE, [=]() { + insertMix(mixIndex, ch); + editMix(window, ch, mixIndex); + }); + menu->addLine(STR_INSERT_AFTER, [=]() { + insertMix(mixIndex + 1, ch); + editMix(window, ch, mixIndex + 1); + }); + menu->addLine(STR_COPY, [=]() { + s_copyMode = COPY_MODE; + s_copySrcIdx =mixIndex; + }); + if (s_copyMode != 0) { + menu->addLine(STR_PASTE_BEFORE, [=]() { + copyMix(s_copySrcIdx, mixIndex, PASTE_BEFORE); + if(s_copyMode == MOVE_MODE) { + deleteMix((s_copySrcIdx > mixIndex) ? s_copySrcIdx+1 : s_copySrcIdx); + s_copyMode = 0; + } + rebuild(window, mixIndex); + }); + menu->addLine(STR_PASTE_AFTER, [=]() { + copyMix(s_copySrcIdx, mixIndex, PASTE_AFTER); + if(s_copyMode == MOVE_MODE) { + deleteMix((s_copySrcIdx > mixIndex) ? s_copySrcIdx+1 : s_copySrcIdx); + s_copyMode = 0; + } + rebuild(window, mixIndex+1); + }); + } + } + menu->addLine(STR_MOVE, [=]() { + s_copyMode = MOVE_MODE; + s_copySrcIdx = mixIndex; + }); + menu->addLine(STR_DELETE, [=]() { + deleteMix(mixIndex); + rebuild(window, -1); + }); + return 0; + }); + + if (count++ > 0) { + new StaticBitmap(window, {35, button->top() + (button->height() - 18) / 2, 25, 17}, mixerMultiplexBitmap[mix->mltpx]); + } + + grid.spacer(button->height() - 2); + ++mixIndex; + ++mix; + } + + grid.spacer(7); + } + else { + auto button = new TextButton(window, grid.getLabelSlot(), getSourceString(MIXSRC_CH1 + ch)); + if (focusMixIndex == mixIndex) + button->setFocus(); + button->setPressHandler([=]() -> uint8_t { + button->bringToTop(); + Menu * menu = new Menu(); + menu->addLine(STR_EDIT, [=]() { + insertMix(mixIndex, ch); + editMix(window, ch, mixIndex); + return 0; + }); + if (!reachMixesLimit()) { + if (s_copyMode != 0) { + menu->addLine(STR_PASTE, [=]() { + copyMix(s_copySrcIdx, mixIndex, ch); + if(s_copyMode == MOVE_MODE) { + deleteMix((s_copySrcIdx >= mixIndex) ? s_copySrcIdx+1 : s_copySrcIdx); + s_copyMode = 0; + } + rebuild(window, -1); + return 0; + }); + } + } + // TODO STR_MOVE + return 0; + }); + + grid.spacer(button->height() + 5); + } + } + + Window * focus = Window::getFocus(); + if (focus) { + focus->bringToTop(); + } + + grid.nextLine(); + + window->setLastField(); + window->setInnerHeight(grid.getWindowHeight()); +} + +void deleteMix(uint8_t idx) +{ + pauseMixerCalculations(); + MixData * mix = mixAddress(idx); + memmove(mix, mix + 1, (MAX_MIXERS - (idx + 1)) * sizeof(MixData)); + memclear(&g_model.mixData[MAX_MIXERS - 1], sizeof(MixData)); + resumeMixerCalculations(); + storageDirty(EE_MODEL); +} + +void insertMix(uint8_t idx) +{ + insertMix(idx, s_currCh + 1); +} + +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)); + memcpy(mix, &sourceMix, sizeof(MixData)); + mix->destCh = (mix+1)->destCh; + } + else { + memmove(mix+1, mix, (MAX_MIXERS-(source+1))*sizeof(MixData)); + memcpy(mix, &sourceMix, sizeof(MixData)); + mix->destCh = ch; + } + + //memmove(mix + 1, mix, (MAX_MIXERS - (dest + 1)) * sizeof(MixData)); + resumeMixerCalculations(); + storageDirty(EE_MODEL); +} + +bool swapMixes(uint8_t &idx, uint8_t up) +{ + MixData * x, * y; + int8_t tgt_idx = (up ? idx - 1 : idx + 1); + + x = mixAddress(idx); + + if (tgt_idx < 0) { + if (x->destCh == 0) + return false; + x->destCh--; + return true; + } + + if (tgt_idx == MAX_MIXERS) { + if (x->destCh == MAX_OUTPUT_CHANNELS - 1) + return false; + x->destCh++; + return true; + } + + y = mixAddress(tgt_idx); + uint8_t destCh = x->destCh; + if (!y->srcRaw || destCh != y->destCh) { + if (up) { + if (destCh > 0) x->destCh--; + else return false; + } + else { + if (destCh < MAX_OUTPUT_CHANNELS - 1) x->destCh++; + else return false; + } + return true; + } + + pauseMixerCalculations(); + memswap(x, y, sizeof(MixData)); + resumeMixerCalculations(); + + idx = tgt_idx; + return true; +} \ No newline at end of file diff --git a/radio/src/gui/colorlcd/model_mixes.h b/radio/src/gui/colorlcd/model_mixes.h new file mode 100644 index 000000000..53bc827f1 --- /dev/null +++ b/radio/src/gui/colorlcd/model_mixes.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _MODEL_MIXES_H_ +#define _MODEL_MIXES_H_ + +#include "tabsgroup.h" + +class ModelMixesPage: public PageTab { + public: + ModelMixesPage(); + + void build(FormWindow * window) override + { + build(window, 0); + } + + protected: + void build(FormWindow * window, int8_t focusMixIndex); + void rebuild(FormWindow * window, int8_t focusMixIndex); + void editMix(FormWindow * window, uint8_t channel, uint8_t mixIndex); + uint8_t s_copySrcIdx; +}; + +#endif // _MODEL_MIXES_H_ \ No newline at end of file diff --git a/radio/src/gui/colorlcd/model_outputs.cpp b/radio/src/gui/colorlcd/model_outputs.cpp new file mode 100644 index 000000000..c9913fdde --- /dev/null +++ b/radio/src/gui/colorlcd/model_outputs.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "model_outputs.h" +#include "opentx.h" +#include "libopenui.h" + +#define SET_DIRTY() storageDirty(EE_MODEL) + +class OutputEditWindow : public Page { + public: + OutputEditWindow(uint8_t channel) : + Page(ICON_MODEL_OUTPUTS), + channel(channel) + { + buildBody(&body); + buildHeader(&header); + } + + protected: + uint8_t channel; + + void buildHeader(Window * window) + { + new StaticText(window, {70, 4, 100, 20}, STR_MENULIMITS, MENU_TITLE_COLOR); + new StaticText(window, {70, 28, 100, 20}, getSourceString(MIXSRC_CH1 + channel), MENU_TITLE_COLOR); + } + + void buildBody(Window * window) + { + FormGridLayout grid; + grid.spacer(8); + + int limit = (g_model.extendedLimits ? LIMIT_EXT_MAX : 1000); + + LimitData * output = limitAddress(channel); + + // Name + new StaticText(window, grid.getLabelSlot(), STR_NAME); + new TextEdit(window, grid.getFieldSlot(), output->name, sizeof(output->name)); + grid.nextLine(); + + // Offset + new StaticText(window, grid.getLabelSlot(), TR_LIMITS_HEADERS_SUBTRIM); + new NumberEdit(window, grid.getFieldSlot(), -1000, +1000, GET_SET_DEFAULT(output->offset), PREC1); + grid.nextLine(); + + // Min + new StaticText(window, grid.getLabelSlot(), TR_MIN); + new NumberEdit(window, grid.getFieldSlot(), -limit, 0, + GET_VALUE(output->min - 1000), + SET_VALUE(output->min, newValue + 1000), + PREC1); + grid.nextLine(); + + // Max + new StaticText(window, grid.getLabelSlot(), TR_MAX); + new NumberEdit(window, grid.getFieldSlot(), 0, +limit, + GET_VALUE(output->max + 1000), + SET_VALUE(output->max, newValue - 1000), + PREC1); + grid.nextLine(); + + // Direction + new StaticText(window, grid.getLabelSlot(), "Inverted"); // TODO translation + new CheckBox(window, grid.getFieldSlot(), GET_SET_DEFAULT(output->revert)); + grid.nextLine(); + + // Curve + new StaticText(window, grid.getLabelSlot(), TR_CURVE); + auto edit = new NumberEdit(window, grid.getFieldSlot(), -MAX_CURVES, +MAX_CURVES, GET_SET_DEFAULT(output->curve)); + edit->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + dc->drawText(2, 2, getCurveString(value)); + }); + grid.nextLine(); + + // PPM center + new StaticText(window, grid.getLabelSlot(), TR_LIMITS_HEADERS_PPMCENTER); + new NumberEdit(window, grid.getFieldSlot(), PPM_CENTER - PPM_CENTER_MAX, PPM_CENTER + PPM_CENTER_MAX, + GET_VALUE(output->ppmCenter + PPM_CENTER), + SET_VALUE(output->ppmCenter, newValue - PPM_CENTER)); + grid.nextLine(); + + // PPM center + new StaticText(window, grid.getLabelSlot(), TR_LIMITS_HEADERS_SUBTRIMMODE); + new Choice(window, grid.getFieldSlot(), "\001\306=", 0, 1, GET_SET_DEFAULT(output->symetrical)); + grid.nextLine(); + + window->setInnerHeight(grid.getWindowHeight()); + } +}; + +class OutputLineButton : public Button { + public: + OutputLineButton(Window * parent, const rect_t &rect, LimitData * output) : + Button(parent, rect), + output(output) + { + if (output->revert || output->curve || output->name[0]) { + setHeight(getHeight() + PAGE_LINE_HEIGHT); + } + } + + virtual void paint(BitmapBuffer * dc) override + { + // first line + drawNumber(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, output->min - 1000, PREC1); + drawNumber(dc, 68, FIELD_PADDING_TOP, output->max + 1000, PREC1); + drawNumber(dc, 132, FIELD_PADDING_TOP, output->offset, PREC1); + drawNumber(dc, 226, FIELD_PADDING_TOP, PPM_CENTER + output->ppmCenter, RIGHT); + dc->drawText(228, FIELD_PADDING_TOP - 3, output->symetrical ? "=" : "\306"); + + // second line + if (output->revert) { + drawTextAtIndex(dc, 4, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, STR_MMMINV, output->revert); + } + if (output->curve) { + dc->drawBitmap(68, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupCurveBitmap); + dc->drawText(88, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, getCurveString(output->curve)); + } + if (output->name[0]) { + dc->drawBitmap(146, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupLabelBitmap); + dc->drawSizedText(166, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, output->name, sizeof(output->name), ZCHAR); + } + + // bounding rect + drawSolidRect(dc, 0, 0, rect.w, rect.h, 2, hasFocus() ? SCROLLBOX_COLOR : CURVE_AXIS_COLOR); + } + + protected: + LimitData * output; +}; + +ModelOutputsPage::ModelOutputsPage() : + PageTab(STR_MENULIMITS, ICON_MODEL_OUTPUTS) +{ +} + +void ModelOutputsPage::rebuild(FormWindow * window, int8_t focusChannel) +{ + coord_t scrollPosition = window->getScrollPositionY(); + window->clear(); + build(window, focusChannel); + window->setScrollPositionY(scrollPosition); +} + +void ModelOutputsPage::build(FormWindow * window, int8_t focusChannel) +{ + FormGridLayout grid; + grid.spacer(PAGE_PADDING); + grid.setLabelWidth(66); + + for (uint8_t ch = 0; ch < MAX_OUTPUT_CHANNELS; ch++) { + LimitData * output = limitAddress(ch); + + // Channel label + new StaticText(window, grid.getLabelSlot(), getSourceString(MIXSRC_CH1 + ch), BUTTON_BACKGROUND | CENTERED); + + // Channel settings + Button * button = new OutputLineButton(window, grid.getFieldSlot(), output); + button->setPressHandler([=]() -> uint8_t { + Menu * menu = new Menu(); + menu->addLine(STR_EDIT, [=]() { + editOutput(window, ch); + }); + menu->addLine(STR_RESET, [=]() { + output->min = 0; + output->max = 0; + output->offset = 0; + output->ppmCenter = 0; + output->revert = false; + output->curve = 0; + output->symetrical = 0; + storageDirty(EE_MODEL); + rebuild(window, ch); + }); + menu->addLine(STR_COPY_STICKS_TO_OFS, [=]() { + copySticksToOffset(ch); + storageDirty(EE_MODEL); + button->invalidate(); + }); + menu->addLine(STR_COPY_TRIMS_TO_OFS, [=]() { + copyTrimsToOffset(ch); + storageDirty(EE_MODEL); + button->invalidate(); + }); + return 0; + }); + + if (focusChannel == ch) { + button->setFocus(); + } + + grid.spacer(button->height() + 5); + } + + grid.nextLine(); + + window->setLastField(); + window->setInnerHeight(grid.getWindowHeight()); +} + +void ModelOutputsPage::editOutput(FormWindow * window, uint8_t channel) +{ + Window * editWindow = new OutputEditWindow(channel); + editWindow->setCloseHandler([=]() { + rebuild(window, channel); + }); +} diff --git a/radio/src/gui/colorlcd/model_outputs.h b/radio/src/gui/colorlcd/model_outputs.h new file mode 100644 index 000000000..2a5c61734 --- /dev/null +++ b/radio/src/gui/colorlcd/model_outputs.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _MODEL_OUTPUTS_H_ +#define _MODEL_OUTPUTS_H_ + +#include "tabsgroup.h" + +class ModelOutputsPage: public PageTab { + public: + ModelOutputsPage(); + + virtual void build(FormWindow * window) override + { + build(window, -1); + } + + protected: + void build(FormWindow * window, int8_t focusChannel); + void rebuild(FormWindow * window, int8_t focusChannel); + void editOutput(FormWindow * window, uint8_t channel); +}; + +#endif // _MODEL_OUTPUTS_H_ diff --git a/radio/src/gui/480x272/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp similarity index 99% rename from radio/src/gui/480x272/model_select.cpp rename to radio/src/gui/colorlcd/model_select.cpp index b79963423..138308d10 100644 --- a/radio/src/gui/480x272/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -19,7 +19,7 @@ */ #include "opentx.h" -#include "storage/modelslist.h" +#include "modelslist.h" #define CATEGORIES_WIDTH 120 #define MODELS_LEFT 123 diff --git a/radio/src/gui/colorlcd/model_setup.cpp b/radio/src/gui/colorlcd/model_setup.cpp new file mode 100644 index 000000000..740905832 --- /dev/null +++ b/radio/src/gui/colorlcd/model_setup.cpp @@ -0,0 +1,1069 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "model_setup.h" +#include "opentx.h" +#include "libopenui.h" + +#define SET_DIRTY() storageDirty(EE_MODEL) + +class ChannelFailsafeBargraph: public Window { + public: + ChannelFailsafeBargraph(Window * parent, const rect_t & rect, uint8_t moduleIdx, uint8_t channel): + Window(parent, rect), + moduleIdx(moduleIdx), + channel(channel) + { + } + + void checkEvents() override + { + invalidate(); + } + + void paint(BitmapBuffer * dc) override + { + int32_t failsafeValue = g_model.failsafeChannels[channel]; + int32_t channelValue = channelOutputs[channel]; + + const int lim = (g_model.extendedLimits ? (512 * LIMIT_EXT_PERCENT / 100) : 512) * 2; + + coord_t x = 0; + lcdDrawRect(x, 0, width(), height()); + const coord_t lenChannel = limit((uint8_t) 1, uint8_t((abs(channelValue) * width() / 2 + lim / 2) / lim), + uint8_t(width() / 2)); + const coord_t lenFailsafe = limit((uint8_t) 1, uint8_t((abs(failsafeValue) * width() / 2 + lim / 2) / lim), + uint8_t(width() / 2)); + x += width() / 2; + const coord_t xChannel = (channelValue > 0) ? x : x + 1 - lenChannel; + const coord_t xFailsafe = (failsafeValue > 0) ? x : x + 1 - lenFailsafe; + lcdDrawSolidFilledRect(xChannel, + 2, lenChannel, (height() / 2) - 3, TEXT_COLOR); + lcdDrawSolidFilledRect(xFailsafe, (height() / 2) + 1, lenFailsafe, (height() / 2) - 3, ALARM_COLOR); + } + + protected: + uint8_t moduleIdx; + uint8_t channel; +}; + +class FailSafeBody : public Window { + public: + FailSafeBody(Window * parent, const rect_t &rect, uint8_t moduleIdx) : + Window(parent, rect), + moduleIdx(moduleIdx) + { + build(); + } + + void checkEvents() override + { + invalidate(); + } + + void build() + { + FormGridLayout grid; + grid.setLabelWidth(60); + grid.spacer(8); + + const int lim = (g_model.extendedLimits ? (512 * LIMIT_EXT_PERCENT / 100) : 512) * 2; + + for (int ch=0; ch < maxModuleChannels(moduleIdx); ch++) { + // Channel name + // TODO if (g_model.limitData[ch].name[0] != '\0') { <= add channel name + new StaticText(this, grid.getLabelSlot(), getSourceString(MIXSRC_CH1 + ch)); + + // Channel numeric value + new NumberEdit(this, grid.getFieldSlot(3, 0), -lim, lim, + GET_DEFAULT(calcRESXto1000(g_model.failsafeChannels[ch])), + SET_VALUE(g_model.failsafeChannels[ch], newValue), + PREC1); + + // Channel bargraph + new ChannelFailsafeBargraph(this, {150, grid.getWindowHeight(), 150, PAGE_LINE_HEIGHT}, moduleIdx, ch); + grid.nextLine(); + } + + auto out2fail = new TextButton(this, grid.getLineSlot(), STR_CHANNELS2FAILSAFE); + out2fail->setPressHandler([=]() { + setCustomFailsafe(moduleIdx); + storageDirty(EE_MODEL); + return 0; + }); + + grid.nextLine(); + setInnerHeight(grid.getWindowHeight()); + } + + protected: + uint8_t moduleIdx; +}; + +class FailSafeFooter : public Window { + public: + FailSafeFooter(Window * parent, const rect_t &rect) : + Window(parent, rect) + { + } + + void paint(BitmapBuffer * dc) override + { + } +}; + +class FailSafePage : public PageTab { + public: + FailSafePage(uint8_t moduleIdx) : + PageTab("FAILSAFE", ICON_STATS_ANALOGS), + moduleIdx(moduleIdx) + { + } + + void build(FormWindow * window) override + { + new FailSafeBody(window, {0, 0, LCD_W, window->height() - footerHeight}, moduleIdx ); + new FailSafeFooter(window, {0, window->height() - footerHeight, LCD_W, footerHeight}); + } + + protected: + static constexpr coord_t footerHeight = 30; + uint8_t moduleIdx; +}; + +FailSafeMenu::FailSafeMenu(uint8_t moduleIdx) : + TabsGroup(ICON_MODEL) +{ + addTab(new FailSafePage(moduleIdx)); +} + +class RegisterDialog: public Dialog { + public: + RegisterDialog(uint8_t moduleIdx): + Dialog(STR_REGISTER, {50, 73, LCD_W - 100, LCD_H - 146}), + moduleIdx(moduleIdx) + { + FormGridLayout grid(width()); + grid.setLabelWidth(150); + grid.spacer(PAGE_LINE_HEIGHT + 8); + + // Register ID + new StaticText(this, grid.getLabelSlot(), STR_REG_ID); + auto edit = new TextEdit(this, grid.getFieldSlot(), g_model.modelRegistrationID, sizeof(g_model.modelRegistrationID)); + grid.nextLine(); + + // UID + new StaticText(this, grid.getLabelSlot(), "UID"); + uid = new NumberEdit(this, grid.getFieldSlot(), 0, 2, GET_SET_DEFAULT(reusableBuffer.moduleSetup.pxx2.registerLoopIndex)); + grid.nextLine(); + + // RX name + new StaticText(this, grid.getLabelSlot(), STR_RX_NAME); + waiting = new StaticText(this, grid.getFieldSlot(), STR_WAITING); + grid.nextLine(); + grid.spacer(6); + + // Buttons + exitButton = new TextButton(this, grid.getLabelSlot(), "EXIT", + [=]() -> int8_t { + this->deleteLater(); + return 0; + }); + exitButton->setFocus(); + FormField::link(exitButton, edit); + + start(); + + setCloseHandler([=]() { + moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; + }); + } + + void start() + { + memclear(&reusableBuffer.moduleSetup.pxx2, sizeof(reusableBuffer.moduleSetup.pxx2)); + moduleState[moduleIdx].mode = MODULE_MODE_REGISTER; + } + + void checkEvents() override + { + if (!rxName && reusableBuffer.moduleSetup.pxx2.registerStep >= REGISTER_RX_NAME_RECEIVED) { + rect_t rect = waiting->getRect(); + waiting->deleteLater(); + FormField::clearCurrentField(); + rxName = new TextEdit(this, rect, reusableBuffer.moduleSetup.pxx2.registerRxName, PXX2_LEN_RX_NAME); + rect = exitButton->getRect(); + auto okButton = new TextButton(this, rect, "OK", + [=]() -> int8_t { + reusableBuffer.moduleSetup.pxx2.registerStep = REGISTER_RX_NAME_SELECTED; + return 0; + }); + exitButton->setLeft(left() + rect.w + 10); + FormField::link(uid, rxName); + FormField::link(rxName, okButton); + FormField::link(okButton, exitButton); + okButton->setFocus(); + } + else if (reusableBuffer.moduleSetup.pxx2.registerStep == REGISTER_OK) { + deleteLater(); + POPUP_INFORMATION(STR_REG_OK); + } + + Dialog::checkEvents(); + } + + protected: + uint8_t moduleIdx; + NumberEdit * uid; + StaticText * waiting; + TextEdit * rxName = nullptr; + TextButton * exitButton; +}; + +class BindWaitDialog: public Dialog { + public: + BindWaitDialog(uint8_t moduleIdx, uint8_t receiverIdx): + Dialog(STR_BIND, {50, 73, LCD_W - 100, LCD_H - 146}), + moduleIdx(moduleIdx), + receiverIdx(receiverIdx) + { + new StaticText(this, {0, height() / 2, width(), PAGE_LINE_HEIGHT}, STR_WAITING_FOR_RX, CENTERED); + } + + void checkEvents() override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override + { + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_EXIT)) { + moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; + deleteLater(); + } + } +#endif + + protected: + uint8_t moduleIdx; + uint8_t receiverIdx; +}; + +class BindRxChoiceMenu: public Menu { + public: + BindRxChoiceMenu(uint8_t moduleIdx, uint8_t receiverIdx): + Menu(), + moduleIdx(moduleIdx), + receiverIdx(receiverIdx) + { + uint8_t receiversCount = min(reusableBuffer.moduleSetup.bindInformation.candidateReceiversCount, PXX2_MAX_RECEIVERS_PER_MODULE); + for (uint8_t i = 0; i < receiversCount; i++) { + const char * receiverName = reusableBuffer.moduleSetup.bindInformation.candidateReceiversNames[i]; + addLine(receiverName, [=]() { + reusableBuffer.moduleSetup.bindInformation.selectedReceiverIndex = i; + if (isModuleR9MAccess(moduleIdx) && reusableBuffer.moduleSetup.pxx2.moduleInformation.information.variant == PXX2_VARIANT_EU) { + reusableBuffer.moduleSetup.bindInformation.step = BIND_RX_NAME_SELECTED; +// POPUP_MENU_ADD_ITEM(STR_16CH_WITH_TELEMETRY); +// POPUP_MENU_ADD_ITEM(STR_16CH_WITHOUT_TELEMETRY); +// POPUP_MENU_START(onPXX2R9MBindModeMenu); + } + else if (isModuleR9MAccess(moduleIdx) && reusableBuffer.moduleSetup.pxx2.moduleInformation.information.variant == PXX2_VARIANT_FLEX) { + reusableBuffer.moduleSetup.bindInformation.step = BIND_RX_NAME_SELECTED; +// POPUP_MENU_ADD_ITEM(STR_FLEX_868); +// POPUP_MENU_ADD_ITEM(STR_FLEX_915); +// POPUP_MENU_START(onPXX2R9MBindModeMenu); + } + else { +#if defined(SIMU) + memcpy(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], receiverName, PXX2_LEN_RX_NAME); + storageDirty(EE_MODEL); + reusableBuffer.moduleSetup.bindInformation.step = BIND_OK; + new MessageDialog(STR_BIND, STR_BIND_OK); +#else + reusableBuffer.moduleSetup.bindInformation.step = BIND_START; + new BindWaitDialog(moduleIdx, receiverIdx); +#endif + } + }); + } + + setCancelHandler([=]() { + moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; + }); + } + + protected: + uint8_t moduleIdx; + uint8_t receiverIdx; +}; + +void BindWaitDialog::checkEvents() +{ + if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL) { + removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx); + deleteLater(); + if (reusableBuffer.moduleSetup.bindInformation.step == BIND_OK) + POPUP_INFORMATION(STR_BIND_OK); + return; + } + + if (reusableBuffer.moduleSetup.bindInformation.step == BIND_INIT && reusableBuffer.moduleSetup.bindInformation.candidateReceiversCount > 0) { + deleteLater(); + new BindRxChoiceMenu(moduleIdx, receiverIdx); + return; + } + + Dialog::checkEvents(); +} + +class ReceiverButton: public TextButton { + public: + ReceiverButton(Window * parent, rect_t rect, uint8_t moduleIdx, uint8_t receiverIdx): + TextButton(parent, rect, STR_BIND, [=]() { + if (g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx][0] == '\0') { + startBind(); + } + else { + auto menu = new Menu(); + menu->addLine(STR_BIND, [=]() { + startBind(); + return 0; + }); + menu->addLine(STR_OPTIONS, [=]() { + memclear(&reusableBuffer.hardwareAndSettings, sizeof(reusableBuffer.hardwareAndSettings)); + reusableBuffer.hardwareAndSettings.receiverSettings.receiverId = receiverIdx; + // g_moduleIdx = moduleIdx; + // pushMenu(menuModelReceiverOptions); + return 0; + }); + menu->addLine(STR_SHARE, [=]() { + reusableBuffer.moduleSetup.pxx2.shareReceiverIndex = receiverIdx; + moduleState[moduleIdx].mode = MODULE_MODE_SHARE; + return 0; + }); + menu->addLine(STR_DELETE, [=]() { + memclear(&reusableBuffer.moduleSetup.pxx2, sizeof(reusableBuffer.moduleSetup.pxx2)); + reusableBuffer.moduleSetup.pxx2.resetReceiverIndex = receiverIdx; + reusableBuffer.moduleSetup.pxx2.resetReceiverFlags = 0x01; + new ConfirmDialog(STR_RECEIVER, STR_RECEIVER_DELETE, [=]() { + moduleState[moduleIdx].mode = MODULE_MODE_RESET; + removePXX2Receiver(moduleIdx, receiverIdx); + }); + return 0; + }); + menu->addLine(STR_RESET, [=]() { + memclear(&reusableBuffer.moduleSetup.pxx2, sizeof(reusableBuffer.moduleSetup.pxx2)); + reusableBuffer.moduleSetup.pxx2.resetReceiverIndex = receiverIdx; + reusableBuffer.moduleSetup.pxx2.resetReceiverFlags = 0xFF; + new ConfirmDialog(STR_RECEIVER, STR_RECEIVER_DELETE, [=]() { + moduleState[moduleIdx].mode = MODULE_MODE_RESET; + removePXX2Receiver(moduleIdx, receiverIdx); + }); + return 0; + }); + menu->setCloseHandler([=]() { + removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx); + }); + } + return 0; + }), + moduleIdx(moduleIdx), + receiverIdx(receiverIdx) + { + } + + void startBind() + { + memclear(&reusableBuffer.moduleSetup.bindInformation, sizeof(BindInformation)); + reusableBuffer.moduleSetup.bindInformation.rxUid = receiverIdx; + if (isModuleR9MAccess(moduleIdx)) { +#if defined(SIMU) + reusableBuffer.moduleSetup.pxx2.moduleInformation.information.modelID = 1; + reusableBuffer.moduleSetup.pxx2.moduleInformation.information.variant = 2; +#else + moduleState[moduleIdx].readModuleInformation(&reusableBuffer.moduleSetup.pxx2.moduleInformation, PXX2_HW_INFO_TX_ID, PXX2_HW_INFO_TX_ID); +#endif + } + else { + moduleState[moduleIdx].startBind(&reusableBuffer.moduleSetup.bindInformation); + } + + new BindWaitDialog(moduleIdx, receiverIdx); + } + + void checkEvents() override + { + if (g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx][0] != '\0') { + char receiverName[PXX2_LEN_RX_NAME + 1]; + memset(receiverName, 0, sizeof(receiverName)); + strncpy(receiverName, g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], + effectiveLen(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], PXX2_LEN_RX_NAME)); + setText(receiverName); + } + else { + setText(STR_BIND); + } + + TextButton::checkEvents(); + } + + protected: + uint8_t moduleIdx; + uint8_t receiverIdx; +}; + +class ModuleWindow : public Window { + public: + ModuleWindow(Window * parent, const rect_t &rect, uint8_t moduleIdx) : + Window(parent, rect, FORWARD_SCROLL), + moduleIdx(moduleIdx) + { + update(); + } + + ~ModuleWindow() + { + deleteChildren(); + } + + protected: + uint8_t moduleIdx; + Choice * moduleChoice = nullptr; + Choice * rfChoice = nullptr; + FormField * lastField = nullptr; // moduleChoice is always the firstField + TextButton * bindButton = nullptr; + TextButton * rangeButton = nullptr; + TextButton * registerButton = nullptr; + Choice * failSafeChoice = nullptr; + + void addChannelRange(FormGridLayout &grid, uint8_t moduleIdx) + { + new StaticText(this, grid.getLabelSlot(true), STR_CHANNELRANGE); + auto channelStart = new NumberEdit(this, grid.getFieldSlot(2, 0), 1, + MAX_OUTPUT_CHANNELS - sentModuleChannels(moduleIdx) + 1, + GET_DEFAULT(1 + g_model.moduleData[moduleIdx].channelsStart)); + auto channelEnd = new NumberEdit(this, grid.getFieldSlot(2, 1), + g_model.moduleData[moduleIdx].channelsStart + minModuleChannels(moduleIdx), + min(MAX_OUTPUT_CHANNELS, g_model.moduleData[moduleIdx].channelsStart + maxModuleChannels(moduleIdx)), + GET_DEFAULT(g_model.moduleData[moduleIdx].channelsStart + 8 + g_model.moduleData[moduleIdx].channelsCount)); + if (isModulePXX2(moduleIdx)) { + channelEnd->setAvailableHandler([=](int value) { + return isPxx2IsrmChannelsCountAllowed(value - 8); + }); + } + channelStart->setPrefix(STR_CH); + channelEnd->setPrefix(STR_CH); + channelStart->setSetValueHandler([=](int32_t newValue) { + g_model.moduleData[moduleIdx].channelsStart = newValue - 1; + SET_DIRTY(); + channelEnd->setMin(g_model.moduleData[moduleIdx].channelsStart + minModuleChannels(moduleIdx)); + channelEnd->setMax(min(MAX_OUTPUT_CHANNELS, g_model.moduleData[moduleIdx].channelsStart + maxModuleChannels(moduleIdx))); + channelEnd->invalidate(); + }); + channelEnd->setSetValueHandler([=](int32_t newValue) { + g_model.moduleData[moduleIdx].channelsCount = newValue - g_model.moduleData[moduleIdx].channelsStart - 8; + SET_DIRTY(); + channelStart->setMax(MAX_OUTPUT_CHANNELS - sentModuleChannels(moduleIdx) + 1); + }); + channelEnd->enable(minModuleChannels(moduleIdx) < maxModuleChannels(moduleIdx)); + } + + void update() + { + FormGridLayout grid; + grid.setLabelWidth(175); + + FormField * previousField = moduleChoice ? moduleChoice->getPreviousField() : moduleChoice->getCurrentField(); + FormField * nextField = lastField ? lastField->getNextField() : nullptr; + + clear(); + FormField::clearCurrentField(); + + // Module Type + new StaticText(this, grid.getLabelSlot(true), STR_MODE); + moduleChoice = new Choice(this, grid.getFieldSlot(2, 0), STR_INTERNAL_MODULE_PROTOCOLS, + MODULE_TYPE_NONE, MODULE_TYPE_COUNT - 1, + GET_DEFAULT(g_model.moduleData[moduleIdx].type), + [=](int32_t newValue) { + g_model.moduleData[moduleIdx].type = newValue; + SET_DIRTY(); + // TODO resetModuleSettings(moduleIdx); + update(); + moduleChoice->setFocus(); + }); + moduleChoice->setAvailableHandler([=](int8_t moduleType) { + return moduleIdx == INTERNAL_MODULE ? isInternalModuleAvailable(moduleType) : isExternalModuleAvailable(moduleType); + }); + FormField::link(previousField, moduleChoice); + + // Module parameters + if (isModuleXJT(moduleIdx)) { + auto xjtChoice = new Choice(this, grid.getFieldSlot(2, 1), STR_XJT_ACCST_RF_PROTOCOLS, MODULE_SUBTYPE_PXX1_OFF, MODULE_SUBTYPE_PXX1_ACCST_LR12, + GET_SET_DEFAULT(g_model.moduleData[moduleIdx].rfProtocol)); + xjtChoice->setAvailableHandler([](int index) { + return index != MODULE_SUBTYPE_PXX1_OFF; + }); + } + else if (isModuleDSM2(moduleIdx)) { + new Choice(this, grid.getFieldSlot(2, 1), STR_DSM_PROTOCOLS, DSM2_PROTO_LP45, DSM2_PROTO_DSMX, + GET_SET_DEFAULT(g_model.moduleData[moduleIdx].rfProtocol)); + } + else if (isModuleR9M(moduleIdx)) { + rfChoice = new Choice(this, grid.getFieldSlot(2, 1), STR_R9M_REGION, MODULE_SUBTYPE_R9M_FCC, MODULE_SUBTYPE_R9M_EU, + GET_DEFAULT(g_model.moduleData[moduleIdx].subType), + [=](int32_t newValue) { + g_model.moduleData[moduleIdx].subType = newValue; + SET_DIRTY(); + update(); + rfChoice->setFocus(); + }); + } + else if (isModulePXX2(moduleIdx)) { + rfChoice = new Choice(this, grid.getFieldSlot(2, 1), STR_ISRM_RF_PROTOCOLS, 0, MODULE_SUBTYPE_ISRM_PXX2_ACCST_LR12, + GET_DEFAULT(g_model.moduleData[moduleIdx].subType), + [=](int32_t newValue) { + g_model.moduleData[moduleIdx].subType = newValue; + SET_DIRTY(); + update(); + rfChoice->setFocus(); + }); + } +#if defined(MULTIMODULE) + else if (isModuleMultimodule(moduleIdx)) { + grid.nextLine(); + new StaticText(this, grid.getLabelSlot(true), STR_RF_PROTOCOL); + + // Multi type (CUSTOM, brand A, brand B,...) + int multiRfProto = g_model.moduleData[moduleIdx].multi.customProto == 1 ? MODULE_SUBTYPE_MULTI_CUSTOM : g_model.moduleData[moduleIdx].getMultiProtocol(false); + 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].multi.customProto = (newValue == MODULE_SUBTYPE_MULTI_CUSTOM); + if (!g_model.moduleData[moduleIdx].multi.customProto) + g_model.moduleData[moduleIdx].setMultiProtocol(newValue); + g_model.moduleData[moduleIdx].subType = 0; + // Sensible default for DSM2 (same as for ppm): 7ch@22ms + Autodetect settings enabled + if (g_model.moduleData[moduleIdx].getMultiProtocol(true) == MODULE_SUBTYPE_MULTI_DSM2) { + g_model.moduleData[moduleIdx].multi.autoBindMode = 1; + } + else { + g_model.moduleData[moduleIdx].multi.autoBindMode = 0; + } + g_model.moduleData[moduleIdx].multi.optionValue = 0; + SET_DIRTY(); + update(); + rfChoice->setFocus(); + }); + + if (g_model.moduleData[moduleIdx].multi.customProto) { + // Proto column 1 + new NumberEdit(this, grid.getFieldSlot(3, 1), 0, 63, + GET_DEFAULT(g_model.moduleData[moduleIdx].getMultiProtocol(false)), + [=](int32_t newValue) { + g_model.moduleData[moduleIdx].setMultiProtocol(newValue); + SET_DIRTY(); + }); + + // Proto column 2 + new NumberEdit(this, grid.getFieldSlot(3, 2), 0, 7, GET_SET_DEFAULT(g_model.moduleData[moduleIdx].subType)); + } + else { + // Subtype (D16, DSMX,...) + const mm_protocol_definition * pdef = getMultiProtocolDefinition(g_model.moduleData[moduleIdx].getMultiProtocol(false)); + if (pdef->maxSubtype > 0) + new Choice(this, grid.getFieldSlot(2, 1), pdef->subTypeString, 0, pdef->maxSubtype,GET_SET_DEFAULT(g_model.moduleData[moduleIdx].subType)); + } + grid.nextLine(); + + // Multimodule status + new StaticText(this, grid.getLabelSlot(true), STR_MODULE_STATUS); + char statusText[64]; + multiModuleStatus.getStatusString(statusText); + new StaticText(this, grid.getFieldSlot(), statusText); + + // Multimodule sync + if(multiSyncStatus.isValid()) { + grid.nextLine(); + new StaticText(this, grid.getLabelSlot(true), STR_MODULE_SYNC); + multiSyncStatus.getRefreshString(statusText); + new StaticText(this, grid.getFieldSlot(), statusText); + } + + // Multi optional feature row + const uint8_t multi_proto = g_model.moduleData[moduleIdx].getMultiProtocol(true); + const mm_protocol_definition *pdef = getMultiProtocolDefinition(multi_proto); + 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)); + } + } + + // Bind on power up + grid.nextLine(); + new StaticText(this, grid.getLabelSlot(true),g_model.moduleData[moduleIdx].getMultiProtocol(true) == 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)); + + // 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)); + } +#endif + grid.nextLine(); + + // Channel Range + if (g_model.moduleData[moduleIdx].type != MODULE_TYPE_NONE) { + addChannelRange(grid, moduleIdx); //TODO XJT2 should only set channel count of 8/16/24 + grid.nextLine(); + } + + // PPM modules + if (isModulePPM(moduleIdx)) { + // PPM frame + new StaticText(this, grid.getLabelSlot(true), STR_PPMFRAME); + + // PPM frame length + auto edit = new NumberEdit(this, grid.getFieldSlot(2, 0), 125, 35 * 5 + 225, + GET_DEFAULT(g_model.moduleData[moduleIdx].ppm.frameLength * 5 + 225), + SET_VALUE(g_model.moduleData[moduleIdx].ppm.frameLength, (newValue - 225) / 5), + PREC1); + edit->setStep(5); + edit->setSuffix(STR_MS); + + // PPM frame delay + edit = new NumberEdit(this, grid.getFieldSlot(2, 1), 100, 800, + GET_DEFAULT(g_model.moduleData[moduleIdx].ppm.delay * 50 + 300), + SET_VALUE(g_model.moduleData[moduleIdx].ppm.delay, (newValue - 300) / 50)); + edit->setStep(50); + edit->setSuffix("us"); + grid.nextLine(); + } + + // Module parameters + + // Bind and Range buttons + if (!isModuleRFAccess(moduleIdx) && isModuleBindRangeAvailable(moduleIdx)) { + uint8_t thirdColumn = 0; + new StaticText(this, grid.getLabelSlot(true), STR_RECEIVER); + + // Model index + if (isModuleModelIndexAvailable(moduleIdx)) { + thirdColumn++; + new NumberEdit(this, grid.getFieldSlot(3, 0), 0, getMaxRxNum(moduleIdx), GET_SET_DEFAULT(g_model.header.modelId[moduleIdx])); + } + + bindButton = new TextButton(this, grid.getFieldSlot(2+thirdColumn, 0+thirdColumn), STR_MODULE_BIND); + bindButton->setPressHandler([=]() -> uint8_t { + if (moduleState[moduleIdx].mode == MODULE_MODE_RANGECHECK) { + rangeButton->check(false); + } + if (moduleState[moduleIdx].mode == MODULE_MODE_BIND) { + bindButton->setText(STR_MODULE_BIND); + moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; + return 0; + } + else { + bindButton->setText(STR_MODULE_BIND); + moduleState[moduleIdx].mode = MODULE_MODE_BIND; + return 1; + } + }); + bindButton->setCheckHandler([=]() { + if (moduleState[moduleIdx].mode != MODULE_MODE_BIND) { + bindButton->setText(STR_MODULE_BIND); + bindButton->check(false); + } + }); + + rangeButton = new TextButton(this, grid.getFieldSlot(2+thirdColumn, 1+thirdColumn), STR_MODULE_RANGE); + rangeButton->setPressHandler([=]() -> uint8_t { + if (moduleState[moduleIdx].mode == MODULE_MODE_BIND) { + bindButton->setText(STR_MODULE_BIND); + bindButton->check(false); + moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; + } + if (moduleState[moduleIdx].mode == MODULE_MODE_RANGECHECK) { + moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; + return 0; + } + else { + moduleState[moduleIdx].mode = MODULE_MODE_RANGECHECK; + return 1; + } + }); + + grid.nextLine(); + } + + // Failsafe + if (isModuleFailsafeAvailable(moduleIdx)) { + new StaticText(this, grid.getLabelSlot(true), STR_FAILSAFE); + failSafeChoice = new Choice(this, grid.getFieldSlot(2, 0), STR_VFAILSAFE, 0, FAILSAFE_LAST, + GET_DEFAULT(g_model.moduleData[moduleIdx].failsafeMode), + [=](int32_t newValue) { + g_model.moduleData[moduleIdx].failsafeMode = newValue; + SET_DIRTY(); + update(); + failSafeChoice->setFocus(); + }); + if (g_model.moduleData[moduleIdx].failsafeMode == FAILSAFE_CUSTOM) { + new TextButton(this, grid.getFieldSlot(2, 1), STR_SET, + [=]() -> uint8_t { + new FailSafeMenu(moduleIdx); + return 1; + }); + } + grid.nextLine(); + } + + // Register and Range buttons + if (isModuleRFAccess(moduleIdx)) { + new StaticText(this, grid.getLabelSlot(true), STR_MODULE); + registerButton = new TextButton(this, grid.getFieldSlot(2, 0), STR_REGISTER); + registerButton->setPressHandler([=]() -> uint8_t { + new RegisterDialog(moduleIdx); + return 0; + }); + + rangeButton = new TextButton(this, grid.getFieldSlot(2, 1), STR_MODULE_RANGE); + rangeButton->setPressHandler([=]() -> uint8_t { + if (moduleState[moduleIdx].mode == MODULE_MODE_RANGECHECK) { + moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; + return 0; + } + else { + moduleState[moduleIdx].mode = MODULE_MODE_RANGECHECK; + return 1; + } + }); + + grid.nextLine(); + } + + // R9M Power + if (isModuleR9M_FCC(moduleIdx)) { + new StaticText(this, grid.getLabelSlot(true), STR_RFPOWER); + new Choice(this, grid.getFieldSlot(), STR_R9M_FCC_POWER_VALUES, 0, R9M_FCC_POWER_MAX, + GET_SET_DEFAULT(g_model.moduleData[moduleIdx].pxx.power)); + } + + if (isModuleR9M_LBT(moduleIdx)) { + new StaticText(this, grid.getLabelSlot(true), STR_RFPOWER); + new Choice(this, grid.getFieldSlot(), STR_R9M_LBT_POWER_VALUES, 0, R9M_LBT_POWER_MAX, + GET_DEFAULT(min(g_model.moduleData[moduleIdx].pxx.power, R9M_LBT_POWER_MAX)), + SET_DEFAULT(g_model.moduleData[moduleIdx].pxx.power)); + } + + // Receivers + if (isModulePXX2(moduleIdx)) { + for (uint8_t receiverIdx = 0; receiverIdx < PXX2_MAX_RECEIVERS_PER_MODULE; receiverIdx++) { + char label[] = TR_RECEIVER " X"; + label[sizeof(label) - 2] = '1' + receiverIdx; + new StaticText(this, grid.getLabelSlot(true), label); + new ReceiverButton(this, grid.getFieldSlot(2, 0), moduleIdx, receiverIdx); + grid.nextLine(); + } + } + + getParent()->moveWindowsTop(top(), adjustHeight()); + getParent()->invalidate(); // TODO should be automatically done + + lastField = FormField::getCurrentField(); + if (nextField) + FormField::link(lastField, nextField); + } +}; + +ModelSetupPage::ModelSetupPage() : + PageTab(STR_MENUSETUP, ICON_MODEL_SETUP) +{ +} + +uint8_t g_moduleIdx; + +void onBindMenu(const char * result) +{ + uint8_t moduleIdx = 0; // TODO (menuVerticalPosition >= ITEM_MODEL_EXTERNAL_MODULE_LABEL ? EXTERNAL_MODULE : INTERNAL_MODULE); + + if (result == STR_BINDING_1_8_TELEM_ON) { + g_model.moduleData[moduleIdx].pxx.receiver_telem_off = false; + g_model.moduleData[moduleIdx].pxx.receiver_channel_9_16 = false; + } + else if (result == STR_BINDING_1_8_TELEM_OFF) { + g_model.moduleData[moduleIdx].pxx.receiver_telem_off = true; + g_model.moduleData[moduleIdx].pxx.receiver_channel_9_16 = false; + } + else if (result == STR_BINDING_9_16_TELEM_ON) { + g_model.moduleData[moduleIdx].pxx.receiver_telem_off = false; + g_model.moduleData[moduleIdx].pxx.receiver_channel_9_16 = true; + } + else if (result == STR_BINDING_9_16_TELEM_OFF) { + g_model.moduleData[moduleIdx].pxx.receiver_telem_off = true; + g_model.moduleData[moduleIdx].pxx.receiver_channel_9_16 = true; + } + else { + return; + } + + moduleState[moduleIdx].mode = MODULE_MODE_BIND; +} + +void ModelSetupPage::build(FormWindow * window) +{ + FormGridLayout grid; + grid.spacer(PAGE_PADDING); + + // Model name + new StaticText(window, grid.getLabelSlot(), STR_MODELNAME); + auto first = new TextEdit(window, grid.getFieldSlot(), g_model.header.name, sizeof(g_model.header.name)); + window->setFirstField(first); + grid.nextLine(); + + // Bitmap + new StaticText(window, grid.getLabelSlot(), STR_BITMAP); + new FileChoice(window, grid.getFieldSlot(), BITMAPS_PATH, BITMAPS_EXT, sizeof(g_model.header.bitmap) - LEN_BITMAPS_EXT, + [=]() { + return std::string(g_model.header.bitmap, ZLEN(g_model.header.bitmap)); + }, + [=](std::string newValue) { + strncpy(g_model.header.bitmap, newValue.c_str(), sizeof(g_model.header.bitmap)); + SET_DIRTY(); + }); + grid.nextLine(); + + for (uint8_t i = 0; i < TIMERS; i++) { + TimerData * timer = &g_model.timers[i]; + + // Timer label + char timerLabel[8]; + strAppendStringWithIndex(timerLabel, STR_TIMER, i + 1); + new Subtitle(window, grid.getLineSlot(), timerLabel); + grid.nextLine(); + + auto group = new FormGroup(window, grid.getFieldSlot(), BORDER_FOCUS_ONLY | PAINT_CHILDREN_FIRST); + GridLayout timerGrid(group); + + // Timer name + new StaticText(window, grid.getLabelSlot(true), STR_NAME); + grid.nextLine(); + auto nameEdit = new TextEdit(group, timerGrid.getSlot(), timer->name, LEN_TIMER_NAME); + group->setFirstField(nameEdit); + timerGrid.nextLine(); + + // Timer mode + new StaticText(window, grid.getLabelSlot(true), STR_MODE); + grid.nextLine(); + new Choice(group, timerGrid.getSlot(2, 0), "\011""OFF\0 ""ON\0 ""Start\0 ""Thr\0 ""Thr%\0 ""Thr Start", 0, TMRMODE_MAX, GET_SET_DEFAULT(timer->mode)); + new SwitchChoice(group, timerGrid.getSlot(2, 1), SWSRC_FIRST, SWSRC_LAST, GET_SET_DEFAULT(timer->swtch)); + timerGrid.nextLine(); + + // Timer start value + new StaticText(window, grid.getLabelSlot(true), "Start"); + grid.nextLine(); + new TimeEdit(group, timerGrid.getSlot(), 0, TIMER_MAX, GET_SET_DEFAULT(timer->start)); + timerGrid.nextLine(); + + // Timer minute beep + new StaticText(window, grid.getLabelSlot(true), STR_MINUTEBEEP); + grid.nextLine(); + new CheckBox(group, timerGrid.getSlot(), GET_SET_DEFAULT(timer->minuteBeep)); + timerGrid.nextLine(); + + // Timer countdown + new StaticText(window, grid.getLabelSlot(true), STR_BEEPCOUNTDOWN); + grid.nextLine(); + new Choice(group, timerGrid.getSlot(2, 0), STR_VBEEPCOUNTDOWN, COUNTDOWN_SILENT, COUNTDOWN_COUNT - 1, GET_SET_DEFAULT(timer->countdownBeep)); + new Choice(group, timerGrid.getSlot(2, 1), STR_COUNTDOWNVALUES, 0, 3, GET_SET_WITH_OFFSET(timer->countdownStart, 2)); + timerGrid.nextLine(); + + // Timer persistent + new StaticText(window, grid.getLabelSlot(true), STR_PERSISTENT); + grid.nextLine(); + auto persistentChoice = new Choice(group, timerGrid.getSlot(), STR_VPERSISTENT, 0, 2, GET_SET_DEFAULT(timer->persistent)); + group->setLastField(persistentChoice); + timerGrid.nextLine(); + + coord_t height = timerGrid.getWindowHeight() - 1; + group->setHeight(height); + } + + // Extended limits + new StaticText(window, grid.getLabelSlot(), STR_ELIMITS); + new CheckBox(window, grid.getFieldSlot(), GET_SET_DEFAULT(g_model.extendedLimits)); + grid.nextLine(); + + // Extended trims + new StaticText(window, grid.getLabelSlot(), STR_ETRIMS); + new CheckBox(window, grid.getFieldSlot(2, 0), GET_SET_DEFAULT(g_model.extendedTrims)); + new TextButton(window, grid.getFieldSlot(2, 1), STR_RESET_BTN, + []() -> uint8_t { + START_NO_HIGHLIGHT(); + for (auto &flightModeData : g_model.flightModeData) { + memclear(&flightModeData, TRIMS_ARRAY_SIZE); + } + storageDirty(EE_MODEL); + AUDIO_WARNING1(); + return 0; + }); + grid.nextLine(); + + // Display trims + new StaticText(window, grid.getLabelSlot(), STR_DISPLAY_TRIMS); + new Choice(window, grid.getFieldSlot(), "\006No\0 ChangeYes", 0, 2, GET_SET_DEFAULT(g_model.displayTrims)); + grid.nextLine(); + + // Trim step + new StaticText(window, grid.getLabelSlot(), STR_TRIMINC); + new Choice(window, grid.getFieldSlot(), STR_VTRIMINC, -2, 2, GET_SET_DEFAULT(g_model.trimInc)); + grid.nextLine(); + + // Throttle parameters + { + new Subtitle(window, grid.getLineSlot(), STR_THROTTLE_LABEL); + grid.nextLine(); + + // Throttle reversed + new StaticText(window, grid.getLabelSlot(true), STR_THROTTLEREVERSE); + new CheckBox(window, grid.getFieldSlot(), GET_SET_DEFAULT(g_model.throttleReversed)); + grid.nextLine(); + + // 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)); + grid.nextLine(); + + // Throttle trim + new StaticText(window, grid.getLabelSlot(true), STR_TTRIM); + new CheckBox(window, grid.getFieldSlot(), GET_SET_DEFAULT(g_model.thrTrim)); + grid.nextLine(); + } + + // Preflight parameters + { + new Subtitle(window, grid.getLineSlot(), STR_PREFLIGHT); + grid.nextLine(); + + // Display checklist + new StaticText(window, grid.getLabelSlot(true), STR_CHECKLIST); + new CheckBox(window, grid.getFieldSlot(), GET_SET_DEFAULT(g_model.displayChecklist)); + grid.nextLine(); + + // Throttle warning + new StaticText(window, grid.getLabelSlot(true), STR_THROTTLEWARNING); + new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(g_model.disableThrottleWarning)); + grid.nextLine(); + + // Switches warning + new StaticText(window, grid.getLabelSlot(true), STR_SWITCHWARNING); + auto group = new FormGroup(window, grid.getFieldSlot(), BORDER_FOCUS_ONLY | PAINT_CHILDREN_FIRST); + GridLayout switchesGrid(group); + for (int i = 0; i < NUM_SWITCHES; i++) { + char s[SWITCH_WARNING_STR_SIZE]; + if (i > 0 && (i % 3) == 0) + switchesGrid.nextLine(); + auto button = new TextButton(group, switchesGrid.getSlot(3, i % 3), getSwitchWarningString(s, i), nullptr, + (bfGet(g_model.switchWarningState, 3 * i, 3) == 0 ? 0 : BUTTON_CHECKED)); + button->setPressHandler([button, i] { + swarnstate_t newstate = bfGet(g_model.switchWarningState, 3 * i, 3); + if (newstate == 1 && SWITCH_CONFIG(i) != SWITCH_3POS) + newstate = 3; + else + newstate = (newstate + 1) % 4; + g_model.switchWarningState = bfSet(g_model.switchWarningState, newstate, 3 * i, 3); + SET_DIRTY(); + button->setText(getSwitchWarningString(i)); + return newstate > 0; + }); + if (i == 0) + group->setFirstField(button); + else if (i == NUM_SWITCHES - 1) + group->setLastField(button); + } + grid.addWindow(group); + } + + // Center beeps + { + new StaticText(window, grid.getLabelSlot(false), STR_BEEPCTR); + auto group = new FormGroup(window, grid.getFieldSlot(), BORDER_FOCUS_ONLY | PAINT_CHILDREN_FIRST); + GridLayout centerGrid(group); + for (int i = 0; i < NUM_STICKS + NUM_POTS + NUM_SLIDERS; i++) { + char s[2]; + if (i > 0 && (i % 6) == 0) + centerGrid.nextLine(); + + auto button = new TextButton(group, centerGrid.getSlot(6, i % 6), getStringAtIndex(s, STR_RETA123, i), + [=]() -> uint8_t { + BFBIT_FLIP(g_model.beepANACenter, bfBit(i)); + SET_DIRTY(); + return bfSingleBitGet(g_model.beepANACenter, i); + }, + bfSingleBitGet(g_model.beepANACenter, i) ? BUTTON_CHECKED : 0); + if (i == 0) + group->setFirstField(button); + else if (i == NUM_STICKS + NUM_POTS + NUM_SLIDERS - 1) + group->setLastField(button); + } + grid.addWindow(group); + } + + // Global functions + new StaticText(window, grid.getLabelSlot(), STR_USE_GLOBAL_FUNCS); + new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(g_model.noGlobalFunctions)); + grid.nextLine(); + + // Internal module + { + new Subtitle(window, grid.getLineSlot(), TR_INTERNALRF); + grid.nextLine(); + grid.addWindow(new ModuleWindow(window, {0, grid.getWindowHeight(), LCD_W, 0}, INTERNAL_MODULE)); + } + + // External module + { + new Subtitle(window, grid.getLineSlot(), TR_EXTERNALRF); + grid.nextLine(); + grid.addWindow(new ModuleWindow(window, {0, grid.getWindowHeight(), LCD_W, 0}, EXTERNAL_MODULE)); + } + + FormField::link(FormField::getCurrentField(), first); + + grid.nextLine(); + + window->setInnerHeight(grid.getWindowHeight()); +} + +// Switch to external antenna confirmation +// bool newAntennaSel; +// if (warningResult) { +// warningResult = 0; +// g_model.moduleData[INTERNAL_MODULE].pxx.external_antenna = XJT_EXTERNAL_ANTENNA; +// } + diff --git a/radio/src/gui/colorlcd/model_setup.h b/radio/src/gui/colorlcd/model_setup.h new file mode 100644 index 000000000..0e3abbaf6 --- /dev/null +++ b/radio/src/gui/colorlcd/model_setup.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "tabsgroup.h" + +class FailSafeMenu: public TabsGroup { + public: + FailSafeMenu(uint8_t moduleIndex); +}; + +class TextButton; +class ModuleWindow; + +class ModelSetupPage: public PageTab { + public: + ModelSetupPage(); + + void build(FormWindow * window) override; +}; diff --git a/radio/src/gui/colorlcd/model_telemetry.cpp b/radio/src/gui/colorlcd/model_telemetry.cpp new file mode 100644 index 000000000..67f218b99 --- /dev/null +++ b/radio/src/gui/colorlcd/model_telemetry.cpp @@ -0,0 +1,542 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "model_telemetry.h" +#include "opentx.h" +#include "libopenui.h" + +#define SET_DIRTY() storageDirty(EE_MODEL) + +static constexpr coord_t SENSOR_COL1 = 30; +static constexpr coord_t SENSOR_COL2 = SENSOR_COL1 + 70; +static constexpr coord_t SENSOR_COL3 = LCD_W - 50; + +class SensorSourceChoice : public SourceChoice { + public: + SensorSourceChoice(Window * window, const rect_t &rect, uint8_t * source, IsValueAvailable isValueAvailable) : + SourceChoice(window, rect, MIXSRC_NONE, MIXSRC_LAST_TELEM, + GET_DEFAULT(*source ? MIXSRC_FIRST_TELEM + 3 * (*source - 1) : MIXSRC_NONE), + [=](uint8_t newValue) { + *source = newValue == MIXSRC_NONE ? 0 : (newValue - MIXSRC_FIRST_TELEM) / 3 + 1; + }) + { + setAvailableHandler([=](int16_t value) { + if (value == MIXSRC_NONE) + return true; + if (value < MIXSRC_FIRST_TELEM) + return false; + auto qr = div(value - MIXSRC_FIRST_TELEM, 3); + return qr.rem == 0 && isValueAvailable(qr.quot + 1); + }); + } +}; + +class SensorButton : public Button { + public: + SensorButton(Window * parent, const rect_t &rect, uint8_t index) : + Button(parent, rect), + index(index) + { + } + + static constexpr coord_t line1 = 1; + + void checkEvents() override + { + TelemetryItem &telemetryItem = telemetryItems[index]; + if (telemetryItem.isFresh()) { + invalidate(); + } + } + + void paint(BitmapBuffer * dc) override + { + TelemetryItem &telemetryItem = telemetryItems[index]; + + if (telemetryItem.isFresh()) { + dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, WARNING_COLOR); + } + + drawNumber(dc, 2, 1, index + 1, LEFT, 0, NULL, ":"); + + lcdDrawSizedText(SENSOR_COL1, line1, g_model.telemetrySensors[index].label, TELEM_LABEL_LEN, ZCHAR); + + if (telemetryItem.isAvailable()) { + LcdFlags color = telemetryItem.isOld() ? ALARM_COLOR : TEXT_COLOR; + drawSensorCustomValue(dc, SENSOR_COL2, line1, index, getValue(MIXSRC_FIRST_TELEM + 3 * index), LEFT | color); + } + else { + dc->drawText(SENSOR_COL2, line1, "---", CURVE_COLOR); + } + + TelemetrySensor * sensor = & g_model.telemetrySensors[index]; + if (IS_SPEKTRUM_PROTOCOL()) { + drawHexNumber(dc, SENSOR_COL3, line1, sensor->id, LEFT); + } + else if (sensor->type == TELEM_TYPE_CUSTOM && !g_model.ignoreSensorIds) { + drawNumber(dc, SENSOR_COL3, line1, sensor->instance, LEFT); + } + drawSolidRect(dc, 0, 0, rect.w, rect.h, 2, hasFocus() ? SCROLLBOX_COLOR : CURVE_AXIS_COLOR); + } + + protected: + uint8_t index; +}; + +class SensorEditWindow : public Page { + public: + SensorEditWindow(uint8_t index) : + Page(ICON_MODEL_TELEMETRY), + index(index) + { + buildBody(&body); + buildHeader(&header); + } + + ~SensorEditWindow() + { + body.deleteChildren(); + } + + protected: + uint8_t index; + Window * sensorOneWindow = nullptr; + + void buildHeader(Window * window) + { + new StaticText(window, {70, 4, 200, 20}, STR_SENSOR + std::to_string(index + 1), MENU_TITLE_COLOR); + // dynamic display of sensor value ? + //new StaticText(window, {70, 28, 100, 20}, "SF" + std::to_string(index), MENU_TITLE_COLOR); + } + + void updateSensorOneWindow() + { + // Sensor variable part + FormGridLayout grid; + sensorOneWindow->clear(); + TelemetrySensor * sensor = &g_model.telemetrySensors[index]; + + if (sensor->type == TELEM_TYPE_CALCULATED) { + // Formula + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_FORMULA); + new Choice(sensorOneWindow, grid.getFieldSlot(), STR_VFORMULAS, 0, TELEM_FORMULA_LAST, GET_DEFAULT(sensor->formula), + [=](uint8_t newValue) { + sensor->formula = newValue; + sensor->param = 0; + if (sensor->formula == TELEM_FORMULA_CELL) { + sensor->unit = UNIT_VOLTS; + sensor->prec = 2; + } + else if (sensor->formula == TELEM_FORMULA_DIST) { + sensor->unit = UNIT_DIST; + sensor->prec = 0; + } + else if (sensor->formula == TELEM_FORMULA_CONSUMPTION) { + sensor->unit = UNIT_MAH; + sensor->prec = 0; + } + SET_DIRTY(); + telemetryItems[index].clear(); + updateSensorOneWindow(); + }); + grid.nextLine(); + } + else { + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_ID); + auto hex = new NumberEdit(sensorOneWindow, grid.getFieldSlot(2, 0), 0, 0xffff, GET_SET_DEFAULT(sensor->id)); + hex->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + drawHexNumber(dc, 2, 2, value, 0); + }); + new NumberEdit(sensorOneWindow, grid.getFieldSlot(2, 1), 0, 0xff, GET_SET_DEFAULT(sensor->instance)); + grid.nextLine(); + } + + // Unit + if ((sensor->type == TELEM_TYPE_CALCULATED && (sensor->formula == TELEM_FORMULA_DIST)) || sensor->isConfigurable()) { + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_UNIT); + new Choice(sensorOneWindow, grid.getFieldSlot(), STR_VTELEMUNIT, 0, UNIT_MAX, GET_DEFAULT(sensor->unit), + [=](uint8_t newValue) { + sensor->unit = newValue; + if (sensor->unit == UNIT_FAHRENHEIT) { + sensor->prec = 0; + } + SET_DIRTY(); + telemetryItems[index].clear(); + updateSensorOneWindow(); + }); + grid.nextLine(); + } + + // Precision + if (sensor->isPrecConfigurable() && sensor->unit != UNIT_FAHRENHEIT) { + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_PRECISION); + new Choice(sensorOneWindow, grid.getFieldSlot(), STR_VPREC, 0, 2, GET_DEFAULT(sensor->prec), + [=](uint8_t newValue) { + sensor->prec = newValue; + SET_DIRTY(); + telemetryItems[index].clear(); + updateSensorOneWindow(); + }); + grid.nextLine(); + } + + // Params + if (sensor->unit < UNIT_FIRST_VIRTUAL) { + if (sensor->type == TELEM_TYPE_CALCULATED) { + if (sensor->formula == TELEM_FORMULA_CELL) { + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_CELLSENSOR); + new SensorSourceChoice(sensorOneWindow, grid.getFieldSlot(), &sensor->cell.source, isCellsSensor); + } + else if (sensor->formula == TELEM_FORMULA_DIST) { + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_GPSSENSOR); + new SensorSourceChoice(sensorOneWindow, grid.getFieldSlot(), &sensor->dist.gps, isGPSSensor); + } + else if (sensor->formula == TELEM_FORMULA_CONSUMPTION) { + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_CURRENTSENSOR); + new SensorSourceChoice(sensorOneWindow, grid.getFieldSlot(), &sensor->consumption.source, isSensorAvailable); + } + else if (sensor->formula == TELEM_FORMULA_TOTALIZE) { + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_SOURCE); + new SensorSourceChoice(sensorOneWindow, grid.getFieldSlot(), &sensor->consumption.source, isSensorAvailable); + } + else { + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_SOURCE + std::to_string(1)); + new SensorSourceChoice(sensorOneWindow, grid.getFieldSlot(), (uint8_t *) &sensor->calc.sources[0], isSensorAvailable); + } + } + else { + if (sensor->unit == UNIT_RPMS) { + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_BLADES); + new NumberEdit(sensorOneWindow, grid.getFieldSlot(), 1, 30000, GET_SET_DEFAULT(sensor->custom.ratio)); + } + else { + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_RATIO); + auto edit = new NumberEdit(sensorOneWindow, grid.getFieldSlot(), 0, 30000, GET_SET_DEFAULT(sensor->custom.ratio)); + edit->setZeroText("-"); + } + } + grid.nextLine(); + } + + if (!(sensor->unit == UNIT_GPS || sensor->unit == UNIT_DATETIME || sensor->unit == UNIT_CELLS || + (sensor->type == TELEM_TYPE_CALCULATED && (sensor->formula == TELEM_FORMULA_CONSUMPTION || sensor->formula == TELEM_FORMULA_TOTALIZE)))) { + if (sensor->type == TELEM_TYPE_CALCULATED) { + if (sensor->formula == TELEM_FORMULA_CELL) { + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_CELLINDEX); + new Choice(sensorOneWindow, grid.getFieldSlot(), STR_VCELLINDEX, 0, 8, GET_SET_DEFAULT(sensor->cell.index)); + } + else if (sensor->formula == TELEM_FORMULA_DIST) { + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_ALTSENSOR); + new SensorSourceChoice(sensorOneWindow, grid.getFieldSlot(), &sensor->dist.alt, isAltSensor); + } + else { + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_SOURCE + std::to_string(2)); + new SensorSourceChoice(sensorOneWindow, grid.getFieldSlot(), (uint8_t *) &sensor->calc.sources[1], isSensorAvailable); + } + } + else if (sensor->unit == UNIT_RPMS) { + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_MULTIPLIER); + new NumberEdit(sensorOneWindow, grid.getFieldSlot(), 1, 30000, GET_SET_DEFAULT(sensor->custom.offset)); + } + else { + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_OFFSET); + new NumberEdit(sensorOneWindow, grid.getFieldSlot(), -30000, 30000, GET_SET_DEFAULT(sensor->custom.offset), + (sensor->prec > 0) ? (sensor->prec == 2 ? PREC2 : PREC1) : 0); + } + grid.nextLine(); + } + + if ((sensor->type == TELEM_TYPE_CALCULATED && sensor->formula < TELEM_FORMULA_MULTIPLY)) { + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_SOURCE + std::to_string(3)); + new SensorSourceChoice(sensorOneWindow, grid.getFieldSlot(), (uint8_t *) &sensor->calc.sources[2], isSensorAvailable); + grid.nextLine(); + + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_SOURCE + std::to_string(4)); + new SensorSourceChoice(sensorOneWindow, grid.getFieldSlot(), (uint8_t *) &sensor->calc.sources[3], isSensorAvailable); + grid.nextLine(); + } + + // Auto Offset + if (sensor->unit != UNIT_RPMS && sensor->isConfigurable()) { + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_AUTOOFFSET); + new CheckBox(sensorOneWindow, grid.getFieldSlot(), GET_SET_DEFAULT(sensor->autoOffset)); + grid.nextLine(); + } + + if (sensor->isConfigurable()) { + // Only positive + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_ONLYPOSITIVE); + new CheckBox(sensorOneWindow, grid.getFieldSlot(), GET_SET_DEFAULT(sensor->onlyPositive)); + grid.nextLine(); + + // Filter + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_FILTER); + new CheckBox(sensorOneWindow, grid.getFieldSlot(), GET_SET_DEFAULT(sensor->filter)); + grid.nextLine(); + } + + if (sensor->type == TELEM_TYPE_CALCULATED) { + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_PERSISTENT); + new CheckBox(sensorOneWindow, grid.getFieldSlot(), GET_DEFAULT(sensor->persistent), [=](int32_t newValue) { + sensor->persistent = newValue; + if (!sensor->persistent) + sensor->persistentValue = 0; + SET_DIRTY(); + }); + grid.nextLine(); + } + + new StaticText(sensorOneWindow, grid.getLabelSlot(), STR_LOGS); + new CheckBox(sensorOneWindow, grid.getFieldSlot(), GET_DEFAULT(sensor->logs), [=](int32_t newValue) { + sensor->logs = newValue; + logsClose(); + SET_DIRTY(); + }); + + coord_t delta = sensorOneWindow->adjustHeight(); + Window * parent = sensorOneWindow->getParent(); + parent->moveWindowsTop(sensorOneWindow->top(), delta); + } + + void buildBody(Window * window) + { + // Sensor one + FormGridLayout grid; + grid.spacer(8); + + TelemetrySensor * sensor = &g_model.telemetrySensors[index]; + + // Name + new StaticText(window, grid.getLabelSlot(), STR_NAME); + new TextEdit(window, grid.getFieldSlot(), sensor->label, TELEM_LABEL_LEN); + grid.nextLine(); + + // Type + new StaticText(window, grid.getLabelSlot(), STR_TYPE); + new Choice(window, grid.getFieldSlot(), STR_VSENSORTYPES, 0, 1, GET_DEFAULT(sensor->type), + [=](uint8_t newValue) { + sensor->type = newValue; + sensor->instance = 0; + if (sensor->type == TELEM_TYPE_CALCULATED) { + sensor->param = 0; + sensor->filter = 0; + sensor->autoOffset = 0; + } + SET_DIRTY(); + updateSensorOneWindow(); + }); + grid.nextLine(); + + sensorOneWindow = new Window(window, {0, grid.getWindowHeight(), LCD_W, 0}); + updateSensorOneWindow(); + grid.addWindow(sensorOneWindow); + + window->setInnerHeight(grid.getWindowHeight()); + } +}; + +ModelTelemetryPage::ModelTelemetryPage() : + PageTab(STR_MENUTELEMETRY, ICON_MODEL_TELEMETRY) +{ +} + +void ModelTelemetryPage::checkEvents() +{ + if (lastKnownIndex != availableTelemetryIndex()) { + lastKnownIndex = availableTelemetryIndex(); + rebuild(window); + } + + PageTab::checkEvents(); +} + +void ModelTelemetryPage::rebuild(FormWindow * window, int8_t focusSensorIndex) +{ + coord_t scrollPosition = window->getScrollPositionY(); + window->clear(); + build(window, focusSensorIndex); + window->setScrollPositionY(scrollPosition); +} + + +void ModelTelemetryPage::editSensor(FormWindow * window, uint8_t index) +{ + Window * editWindow = new SensorEditWindow(index); + editWindow->setCloseHandler([=]() { + rebuild(window, index); + }); +} + +void ModelTelemetryPage::build(FormWindow * window, int8_t focusSensorIndex) +{ + FormGridLayout grid; + grid.spacer(8); + grid.setLabelWidth(250); + + this->window = window; + + // RSSI + if (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_MULTIMODULE && + g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false) == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) + new Subtitle(window, grid.getLineSlot(), "RSNR"); + else + new Subtitle(window, grid.getLineSlot(), "RSSI"); + grid.nextLine(); + + new StaticText(window, grid.getLabelSlot(true), STR_LOWALARM); + auto edit = new NumberEdit(window, grid.getFieldSlot(), -30, 30, GET_SET_DEFAULT(g_model.rssiAlarms.warning)); + edit->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + drawNumber(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, g_model.rssiAlarms.getWarningRssi(), flags); + }); + window->setFirstField(edit); + grid.nextLine(); + + new StaticText(window, grid.getLabelSlot(true), STR_CRITICALALARM); + edit = new NumberEdit(window, grid.getFieldSlot(), -30, 30, GET_SET_DEFAULT(g_model.rssiAlarms.critical)); + edit->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + drawNumber(dc, 2, 2, g_model.rssiAlarms.getCriticalRssi(), flags); + }); + grid.nextLine(); + + new StaticText(window, grid.getLabelSlot(true), STR_DISABLE_ALARM); + new CheckBox(window, grid.getFieldSlot(), GET_SET_DEFAULT(g_model.rssiAlarms.disabled)); + grid.nextLine(); + + // Sensors + grid.setLabelWidth(140); + new Subtitle(window, grid.getLineSlot(), STR_TELEMETRY_SENSORS); + new StaticText(window, {SENSOR_COL2, grid.getWindowHeight() + 3, SENSOR_COL3 - SENSOR_COL2, PAGE_LINE_HEIGHT}, STR_VALUE, SMLSIZE | TEXT_DISABLE_COLOR); + if (!g_model.ignoreSensorIds && !IS_SPEKTRUM_PROTOCOL()) + new StaticText(window, {SENSOR_COL3, grid.getWindowHeight() + 3, LCD_W - SENSOR_COL3, PAGE_LINE_HEIGHT}, STR_ID, SMLSIZE | TEXT_DISABLE_COLOR); + grid.nextLine(); + + for (uint8_t idx = 0; idx < MAX_TELEMETRY_SENSORS; idx++) { + if (g_model.telemetrySensors[idx].isAvailable()) { + Button * button = new SensorButton(window, grid.getLineSlot(), idx); + button->setPressHandler([=]() -> uint8_t { + button->bringToTop(); + Menu * menu = new Menu(); + menu->addLine(STR_EDIT, [=]() { + editSensor(window, idx); + }); + menu->addLine(STR_COPY, [=]() { + auto newIndex = availableTelemetryIndex(); + if (newIndex >= 0) { + TelemetrySensor &sourceSensor = g_model.telemetrySensors[idx]; + TelemetrySensor &newSensor = g_model.telemetrySensors[newIndex]; + newSensor = sourceSensor; + TelemetryItem &sourceItem = telemetryItems[idx]; + TelemetryItem &newItem = telemetryItems[newIndex]; + newItem = sourceItem; + SET_DIRTY(); + rebuild(window, newIndex); + } + else { + new FullScreenDialog(WARNING_TYPE_ALERT, "", STR_TELEMETRYFULL); + } + }); + menu->addLine(STR_DELETE, [=]() { + delTelemetryIndex(idx); // calls setDirty internally + rebuild(window); + }); + return 0; + }); + if (focusSensorIndex == idx) { + button->setFocus(); + } + grid.nextLine(); + } + } + + // Autodiscover button + auto discover = new TextButton(window, grid.getLineSlot(), STR_DISCOVER_SENSORS); + discover->setPressHandler([=]() { + allowNewSensors = !allowNewSensors; + if (allowNewSensors) { + discover->setText(STR_STOP_DISCOVER_SENSORS); + return 1; + } + else { + discover->setText(STR_DISCOVER_SENSORS); + return 0; + } + }); + grid.nextLine(); + + // New sensor button + new TextButton(window, grid.getLineSlot(), STR_TELEMETRY_NEWSENSOR, + [=]() -> uint8_t { + int res = availableTelemetryIndex(); + if (res >= 0) + editSensor(window, res); + else + new FullScreenDialog(WARNING_TYPE_ALERT, "", STR_TELEMETRYFULL); + return 0; + }); + grid.nextLine(); + + // Delete all sensors button + new TextButton(window, grid.getLineSlot(), STR_DELETE_ALL_SENSORS, + []() -> uint8_t { + new FullScreenDialog(WARNING_TYPE_CONFIRM, STR_CONFIRMDELETE, "", [=]() { + for (int i = 0; i < MAX_TELEMETRY_SENSORS; i++) { + delTelemetryIndex(i); + } + }); + return 0; + }); + grid.nextLine(); + + // Ignore instance button + new StaticText(window, grid.getLabelSlot(true), STR_IGNORE_INSTANCE); + new CheckBox(window, grid.getFieldSlot(), GET_SET_DEFAULT(g_model.ignoreSensorIds)); + grid.nextLine(); + + // Vario + grid.setLabelWidth(100); + new Subtitle(window, grid.getLineSlot(), STR_VARIO); + grid.nextLine(); + new StaticText(window, grid.getLabelSlot(true), STR_SOURCE); + auto choice = new SourceChoice(window, grid.getFieldSlot(), MIXSRC_NONE, MIXSRC_LAST_TELEM, + GET_DEFAULT(g_model.varioData.source ? MIXSRC_FIRST_TELEM + 3 * (g_model.varioData.source - 1) : MIXSRC_NONE), + SET_VALUE(g_model.varioData.source, newValue == MIXSRC_NONE ? 0 : (newValue - MIXSRC_FIRST_TELEM) / 3 + 1)); + choice->setAvailableHandler([=](int16_t value) { + if (value == MIXSRC_NONE) + return true; + if (value < MIXSRC_FIRST_TELEM) + return false; + auto qr = div(value - MIXSRC_FIRST_TELEM, 3); + return qr.rem == 0 && isSensorAvailable(qr.quot + 1); + }); + grid.nextLine(); + new StaticText(window, grid.getLabelSlot(true), STR_RANGE); + new NumberEdit(window, grid.getFieldSlot(2, 0), -7, 7, GET_SET_WITH_OFFSET(g_model.varioData.min, -10)); + new NumberEdit(window, grid.getFieldSlot(2, 1), -7, 7, GET_SET_WITH_OFFSET(g_model.varioData.max, 10)); + grid.nextLine(); + new StaticText(window, grid.getLabelSlot(true), STR_CENTER); + new NumberEdit(window, grid.getFieldSlot(3, 0), -7, 7, GET_SET_WITH_OFFSET(g_model.varioData.min, -5), PREC1); + new NumberEdit(window, grid.getFieldSlot(3, 1), -7, 7, GET_SET_WITH_OFFSET(g_model.varioData.max, 5), PREC1); + new Choice(window, grid.getFieldSlot(3, 2), STR_VVARIOCENTER, 0, 1, GET_SET_DEFAULT(g_model.varioData.centerSilent)); + grid.nextLine(); + + grid.nextLine(); + + window->setInnerHeight(grid.getWindowHeight()); +} diff --git a/radio/src/gui/colorlcd/model_telemetry.h b/radio/src/gui/colorlcd/model_telemetry.h new file mode 100644 index 000000000..89d2721d5 --- /dev/null +++ b/radio/src/gui/colorlcd/model_telemetry.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _MODEL_TELEMETRY_H +#define _MODEL_TELEMETRY_H + +#include "tabsgroup.h" + +class ModelTelemetryPage: public PageTab { + public: + ModelTelemetryPage(); + + virtual void build(FormWindow * window) override + { + build(window, -1); + } + + void checkEvents() override; + + protected: + FormWindow * window = nullptr; + void editSensor(FormWindow * window, uint8_t index); + int lastKnownIndex = 0; + void build(FormWindow * window, int8_t focusSensorIndex=-1); + void rebuild(FormWindow * window, int8_t focusSensorIndex=-1); +}; + +#endif //_MODEL_TELEMETRY_H \ No newline at end of file diff --git a/radio/src/storage/modelslist.cpp b/radio/src/gui/colorlcd/modelslist.cpp similarity index 99% rename from radio/src/storage/modelslist.cpp rename to radio/src/gui/colorlcd/modelslist.cpp index be4a38adb..4f73afebd 100644 --- a/radio/src/storage/modelslist.cpp +++ b/radio/src/gui/colorlcd/modelslist.cpp @@ -75,9 +75,9 @@ void ModelCell::loadBitmap() uint8_t version; PACK(struct { - ModelHeader header; - TimerData timers[MAX_TIMERS]; - }) partialmodel; + ModelHeader header; + TimerData timers[MAX_TIMERS]; + }) partialmodel; const char * error = NULL; if (strncmp(modelFilename, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME) == 0) { @@ -202,13 +202,13 @@ bool ModelCell::fetchRfData() setRfModuleData(i, &modData); } - valid_rfData = true; + valid_rfData = true; f_close(&file); return true; - - error: + + error: f_close(&file); - return false; + return false; } ModelsCategory::ModelsCategory(const char * name) @@ -434,7 +434,7 @@ void ModelsList::moveModel(ModelsCategory * category, ModelCell * model, int8_t } void ModelsList::moveModel(ModelCell * model, ModelsCategory * previous_category, ModelsCategory * new_category) -{ +{ previous_category->remove(model); new_category->push_back(model); save(); @@ -519,7 +519,7 @@ uint8_t ModelsList::findNextUnusedModelId(uint8_t moduleIdx) // and use 64 bits uint8_t usedModelIds[8]; memset(usedModelIds, 0, sizeof(usedModelIds)); - + const std::list& cats = modelslist.getCategories(); std::list::const_iterator cat_it = cats.begin(); for (;cat_it != cats.end(); cat_it++) { diff --git a/radio/src/storage/modelslist.h b/radio/src/gui/colorlcd/modelslist.h similarity index 100% rename from radio/src/storage/modelslist.h rename to radio/src/gui/colorlcd/modelslist.h diff --git a/radio/src/gui/colorlcd/popups.h b/radio/src/gui/colorlcd/popups.h new file mode 100644 index 000000000..f5fad5350 --- /dev/null +++ b/radio/src/gui/colorlcd/popups.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _POPUPS_H_ +#define _POPUPS_H_ + +#include + +typedef std::function ProgressHandler; + +#endif // _POPUPS_H_ diff --git a/radio/src/gui/colorlcd/radio_calibration.cpp b/radio/src/gui/colorlcd/radio_calibration.cpp new file mode 100644 index 000000000..842d90d80 --- /dev/null +++ b/radio/src/gui/colorlcd/radio_calibration.cpp @@ -0,0 +1,297 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "radio_calibration.h" +#include "opentx.h" + +#define XPOT_DELTA 10 +#define XPOT_DELAY 5 /* cycles */ + +uint8_t menuCalibrationState; + +class StickCalibrationWindow: public Window { + public: + StickCalibrationWindow(Window * parent, const rect_t & rect, uint8_t stickX, uint8_t stickY): + Window(parent, rect, REFRESH_ALWAYS), + stickX(stickX), + stickY(stickY) + { + } + + void paint(BitmapBuffer * dc) override + { + dc->drawBitmap(0, 0, calibStickBackground); + int16_t x = calibratedAnalogs[CONVERT_MODE(stickX)]; + int16_t y = calibratedAnalogs[CONVERT_MODE(stickY)]; + dc->drawBitmap(width() / 2 - 9 + (bitmapSize / 2 * x) / RESX, height() / 2 - 9 - (bitmapSize / 2 * y) / RESX, calibStick); + } + + protected: + static constexpr coord_t bitmapSize = 68; + uint8_t stickX, stickY; +}; + +class MultiPosCalibrationWindow: public Window { + public: + MultiPosCalibrationWindow(Window * parent, const rect_t & rect, uint8_t potIndex): + Window(parent, rect), + potIndex(potIndex) + { + } + + void checkEvents() override + { + // will always force a full monitor window refresh + invalidate(); + } + + virtual void paint(BitmapBuffer * dc) + { + drawHorizontalSlider(5, 8, rect.w - 18, 1 + (potsPos[potIndex] & 0x0f), 1, XPOTS_MULTIPOS_COUNT+1, XPOTS_MULTIPOS_COUNT, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_NUMBER_BUTTON); + } + + protected: + static constexpr coord_t bitmapSize = 68; + uint8_t potIndex; +}; + +class PotCalibrationWindow: public Window { + public: + PotCalibrationWindow(Window * parent, const rect_t & rect, uint8_t potIndex): + Window(parent, rect), + potIndex(potIndex) + { + } + + void checkEvents() override + { + // will always force a full monitor window refresh + invalidate(); + } + + virtual void paint(BitmapBuffer * dc) + { + if(rect.h > rect.w) + drawVerticalSlider(5, 8, rect.h - 18, calibratedAnalogs[potIndex], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); + else + drawHorizontalSlider(5, 8, rect.w - 18, calibratedAnalogs[potIndex], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); + } + + protected: + static constexpr coord_t bitmapSize = 68; + uint8_t potIndex; +}; + +RadioCalibrationPage::RadioCalibrationPage(bool initial): + Page(ICON_RADIO_CALIBRATION), + initial(initial) +{ + buildHeader(&header); + buildBody(&body); +} + +void RadioCalibrationPage::buildHeader(Window * window) +{ + new StaticText(window, { 55, 0, 300, 20 }, STR_MENUCALIBRATION, MENU_TITLE_COLOR); + text = new StaticText(window, { 55, FH+2, LCD_W-55, 20 }, STR_MENUTOSTART, MENU_TITLE_COLOR); +} + +void RadioCalibrationPage::buildBody(FormWindow * window) +{ + menuCalibrationState = CALIB_START; + + // Background radio image +#if LCD_W > LCD_H + new StaticBitmap(window, {LCD_W/2 -calibRadioPict->getWidth()/2, 5, calibRadioPict->getHeight(), calibRadioPict->getWidth()}, calibRadioPict); +#else + new StaticBitmap(window, {LCD_W/2 -calibRadioPict->getWidth()/2, LCD_H/2 -calibRadioPict->getHeight()/2, calibRadioPict->getHeight(), calibRadioPict->getWidth()}, calibRadioPict); +#endif + // The two sticks + new StickCalibrationWindow(window, {40, 20, 90, 90}, STICK1, STICK2); + new StickCalibrationWindow(window, {LCD_W-130, 20, 90, 90}, STICK4, STICK3); + +#if defined(PCBHORUS) + // The 2 main sliders + new PotCalibrationWindow(window, {0, 2, 20, 150}, SLIDER1); + new PotCalibrationWindow(window, {LCD_W-20, 2, 20, 150}, SLIDER2); + + // The 3 pots + new PotCalibrationWindow(window, {0, window->height()-25, 150, 20}, POT1); + new MultiPosCalibrationWindow(window, {LCD_W/2-25,window->height()-25, 50, 20}, POT2); + new PotCalibrationWindow(window, {LCD_W-150, window->height()-25, 150, 20}, POT3); +#elif defined(PCBNV14) + new PotCalibrationWindow(window, {0, window->height()-25, 150, 20}, POT1); + new PotCalibrationWindow(window, {LCD_W-150, window->height()-25, 150, 20}, POT2); + #endif +} + +void RadioCalibrationPage::checkEvents() +{ + Page::checkEvents(); + + for (uint8_t i=0; i= POT1 && i <= POT_LAST) { + if (IS_POT_WITHOUT_DETENT(i)) { + reusableBuffer.calib.midVals[i] = (reusableBuffer.calib.hiVals[i] + reusableBuffer.calib.loVals[i]) / 2; + } +#if NUM_XPOTS > 0 + uint8_t idx = i - POT1; + int count = reusableBuffer.calib.xpotsCalib[idx].stepsCount; + if (IS_POT_MULTIPOS(i) && count <= XPOTS_MULTIPOS_COUNT) { + // use raw analog value for multipos calibraton, anaIn() already has multipos decoded value + vt = getAnalogValue(i) >> 1; + if (reusableBuffer.calib.xpotsCalib[idx].lastCount == 0 || vt < reusableBuffer.calib.xpotsCalib[idx].lastPosition - XPOT_DELTA || vt > reusableBuffer.calib.xpotsCalib[idx].lastPosition + XPOT_DELTA) { + reusableBuffer.calib.xpotsCalib[idx].lastPosition = vt; + reusableBuffer.calib.xpotsCalib[idx].lastCount = 1; + } + else { + if (reusableBuffer.calib.xpotsCalib[idx].lastCount < 255) { + reusableBuffer.calib.xpotsCalib[idx].lastCount++; + } + } + if (reusableBuffer.calib.xpotsCalib[idx].lastCount == XPOT_DELAY) { + int16_t position = reusableBuffer.calib.xpotsCalib[idx].lastPosition; + bool found = false; + for (int j=0; j= step-XPOT_DELTA && position <= step+XPOT_DELTA) { + found = true; + break; + } + } + if (!found) { + if (count < XPOTS_MULTIPOS_COUNT) { + reusableBuffer.calib.xpotsCalib[idx].steps[count] = position; + } + reusableBuffer.calib.xpotsCalib[idx].stepsCount += 1; + } + } + } +#endif + } + } + + if (menuCalibrationState == CALIB_SET_MIDPOINT) { + for (uint8_t i = 0; i < NUM_STICKS + NUM_POTS + NUM_SLIDERS + NUM_MOUSE_ANALOGS; i++) { + reusableBuffer.calib.loVals[i] = 15000; + reusableBuffer.calib.hiVals[i] = -15000; + reusableBuffer.calib.midVals[i] = i < TX_VOLTAGE ? anaIn(i) : anaIn(i + 1); +#if NUM_XPOTS > 0 + if (i < NUM_XPOTS) { + reusableBuffer.calib.xpotsCalib[i].stepsCount = 0; + reusableBuffer.calib.xpotsCalib[i].lastCount = 0; + } +#endif + } + } + else if (menuCalibrationState == CALIB_MOVE_STICKS) { + for (uint8_t i = 0; i < NUM_STICKS + NUM_POTS + NUM_SLIDERS + NUM_MOUSE_ANALOGS; i++) { + if (abs(reusableBuffer.calib.loVals[i] - reusableBuffer.calib.hiVals[i]) > 50) { + g_eeGeneral.calib[i].mid = reusableBuffer.calib.midVals[i]; + int16_t v = reusableBuffer.calib.midVals[i] - reusableBuffer.calib.loVals[i]; + g_eeGeneral.calib[i].spanNeg = v - v / STICK_TOLERANCE; + v = reusableBuffer.calib.hiVals[i] - reusableBuffer.calib.midVals[i]; + g_eeGeneral.calib[i].spanPos = v - v / STICK_TOLERANCE; + } + } +#if NUM_XPOTS > 0 + for (int i=POT1; i<=POT_LAST; i++) { + int idx = i - POT1; + int count = reusableBuffer.calib.xpotsCalib[idx].stepsCount; + if (IS_POT_MULTIPOS(i)) { + if (count > 1 && count <= XPOTS_MULTIPOS_COUNT) { + for (int j=0; jcount = count - 1; + for (int j=0; jcount; j++) { + calib->steps[j] = (reusableBuffer.calib.xpotsCalib[idx].steps[j+1] + reusableBuffer.calib.xpotsCalib[idx].steps[j]) >> 5; + } + } + else { + // g_eeGeneral.potsConfig &= ~(0x03<<(2*idx)); + } + } + } +#endif + } +} + +#if defined(HARDWARE_KEYS) +void RadioCalibrationPage::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + nextStep(); + } + else if (event == EVT_KEY_BREAK(KEY_EXIT) && menuCalibrationState != CALIB_START) + { + menuCalibrationState = CALIB_START; + text->setText(STR_MENUTOSTART); + + } + else { + Page::onKeyEvent(event); + } +} +#endif + +void RadioCalibrationPage::nextStep() +{ + menuCalibrationState++; + + switch (menuCalibrationState) { + case CALIB_SET_MIDPOINT: + text->setText(STR_SETMIDPOINT); + break; + + case CALIB_MOVE_STICKS: + text->setText(STR_MOVESTICKSPOTS); + break; + + case CALIB_STORE: + text->setText(STR_CALIB_DONE); + g_eeGeneral.chkSum = evalChkSum(); + storageDirty(EE_GENERAL); + menuCalibrationState = CALIB_FINISHED; + if (initial) + text->getParent()->getParent()->deleteLater(); + break; + + default: + text->setText(STR_MENUTOSTART); + menuCalibrationState = CALIB_START; + break; + } +} + +void startCalibration() +{ + new RadioCalibrationPage(); +} diff --git a/radio/src/gui/colorlcd/radio_calibration.h b/radio/src/gui/colorlcd/radio_calibration.h new file mode 100644 index 000000000..0de41ed25 --- /dev/null +++ b/radio/src/gui/colorlcd/radio_calibration.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "page.h" + +class StaticText; + +class RadioCalibrationPage: public Page { + public: + RadioCalibrationPage(bool initial=false); + + void checkEvents() override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + + protected: + StaticText * text = nullptr; + TextButton * button = nullptr; + bool initial; + void nextStep(); + void buildHeader(Window * window); + void buildBody(FormWindow * window); +}; + +void startCalibration(); diff --git a/radio/src/gui/colorlcd/radio_hardware.cpp b/radio/src/gui/colorlcd/radio_hardware.cpp new file mode 100644 index 000000000..65bb69d29 --- /dev/null +++ b/radio/src/gui/colorlcd/radio_hardware.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "radio_hardware.h" +#include "radio_calibration.h" +#include "opentx.h" + +#define SET_DIRTY() storageDirty(EE_GENERAL) + +#if defined(PCBHORUS) +#define SWITCH_TYPE_MAX(sw) ((MIXSRC_SF-MIXSRC_FIRST_SWITCH == sw || MIXSRC_SH-MIXSRC_FIRST_SWITCH == sw) ? SWITCH_2POS : SWITCH_3POS) +#else +#define SWITCH_TYPE_MAX(sw) (SWITCH_3POS) +#endif + +class SwitchDynamicLabel : public StaticText { + public: + SwitchDynamicLabel(Window * parent, const rect_t & rect, uint8_t index): + StaticText(parent, rect), + index(index) + { + update(); + } + + std::string label() + { + return TEXT_AT_INDEX(STR_VSRCRAW, (index + MIXSRC_FIRST_SWITCH - MIXSRC_Rud + 1)) + std::string(&"\300-\301"[lastpos], 1); + } + + void update() + { + uint8_t newpos = position(); + if (newpos != lastpos) { + lastpos = newpos; + setText(label()); + } + } + + uint8_t position() + { + auto value = getValue(MIXSRC_FIRST_SWITCH + index); + if (value > 0) + return 2; + else if (value < 0) + return 0; + else + return 1; + } + + void checkEvents() override + { + update(); + } + + protected: + uint8_t index; + uint8_t lastpos = 0xff; +}; + +RadioHardwarePage::RadioHardwarePage(): + PageTab(STR_HARDWARE, ICON_RADIO_HARDWARE) +{ +} + +void RadioHardwarePage::build(FormWindow * window) +{ + FormGridLayout grid; +#if LCD_W > LCD_H + grid.setLabelWidth(130); +#else + grid.setLabelWidth(100); +#endif + grid.spacer(6); + + // Calibration + new StaticText(window, grid.getLabelSlot(), STR_INPUTS, BOLD); + auto calib = new TextButton(window, grid.getFieldSlot(), STR_CALIBRATION); + calib->setPressHandler([=]() -> uint8_t { + auto calibrationPage = new RadioCalibrationPage(); + calibrationPage->setCloseHandler([=]() { + calib->setFocus(); + }); + return 0; + }); + grid.nextLine(); + + // Sticks + new Subtitle(window, grid.getLineSlot(), STR_STICKS); + grid.nextLine(); + for(int i=0; i < NUM_STICKS; i++){ + new StaticText(window, grid.getLabelSlot(true), TEXT_AT_INDEX(STR_VSRCRAW, (i + 1))); + new TextEdit(window, grid.getFieldSlot(2,0), g_eeGeneral.anaNames[i], LEN_ANA_NAME); + grid.nextLine(); + } + + // Pots + new Subtitle(window, grid.getLineSlot(), STR_POTS); + grid.nextLine(); + for(int i=0; i < NUM_POTS; i++){ + new StaticText(window, grid.getLabelSlot(true), TEXT_AT_INDEX(STR_VSRCRAW, (i + NUM_STICKS + 1))); + new TextEdit(window, grid.getFieldSlot(2,0), g_eeGeneral.anaNames[i + NUM_STICKS], LEN_ANA_NAME); + new Choice(window, grid.getFieldSlot(2,1), STR_POTTYPES, POT_NONE, POT_WITHOUT_DETENT, GET_SET_BF(g_eeGeneral.potsConfig, 2 * i, 2)); + grid.nextLine(); + } + + // Switches + new Subtitle(window, grid.getLineSlot(), STR_SWITCHES); + grid.nextLine(); + for(int i=0; i < NUM_SWITCHES; i++) { + new SwitchDynamicLabel(window, grid.getLabelSlot(true), i); + new TextEdit(window, grid.getFieldSlot(2, 0), g_eeGeneral.switchNames[i], LEN_SWITCH_NAME); + new Choice(window, grid.getFieldSlot(2, 1), STR_SWTYPES, SWITCH_NONE, SWITCH_TYPE_MAX(i), + [=]() -> int { + return SWITCH_CONFIG(i); + }, + [=](int newValue) { + swconfig_t mask = (swconfig_t)0x03 << (2*i); + g_eeGeneral.switchConfig = (g_eeGeneral.switchConfig & ~mask) | ((swconfig_t(newValue) & 0x03) << (2*i)); + SET_DIRTY(); + }); + grid.nextLine(); + } +#if LCD_W > LCD_H + grid.setLabelWidth(180); +#else + grid.setLabelWidth(130); +#endif + + // ADC filter + new StaticText(window, grid.getLabelSlot(), STR_JITTER_FILTER); + new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(g_eeGeneral.jitterFilter)); + grid.nextLine(); + + // Bat calibration + new StaticText(window, grid.getLabelSlot(), STR_BATT_CALIB); + auto batCal = new NumberEdit(window, grid.getFieldSlot(), -127, 127, GET_SET_DEFAULT(g_eeGeneral.txVoltageCalibration)); + batCal->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + drawNumber(dc, 2, 0, getBatteryVoltage(), flags | PREC2, 0, nullptr, "V"); + }); + batCal->setWindowFlags(REFRESH_ALWAYS); + grid.nextLine(); + + // Factory reset + new TextButton(window, grid.getFieldSlot(), STR_FACTORYRESET, + [=]() -> int8_t { + new FullScreenDialog(WARNING_TYPE_CONFIRM, STR_CONFIRMRESET, STR_POPUPS_ENTER_EXIT, [=]() { + storageEraseAll(false); + NVIC_SystemReset(); + return 0; + }); + return 0; + }); + grid.nextLine(); + + window->setLastField(); + window->setInnerHeight(grid.getWindowHeight()); +} diff --git a/radio/src/gui/colorlcd/radio_hardware.h b/radio/src/gui/colorlcd/radio_hardware.h new file mode 100644 index 000000000..2df7c2190 --- /dev/null +++ b/radio/src/gui/colorlcd/radio_hardware.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _RADIO_HARDWARE_H_ +#define _RADIO_HARDWARE_H_ + +#include "tabsgroup.h" + +class RadioHardwarePage: public PageTab { + public: + RadioHardwarePage(); + + void build(FormWindow * window) override; + + protected: + +}; + +#endif //_RADIO_HARDWARE_H_ diff --git a/radio/src/gui/colorlcd/radio_sdmanager.cpp b/radio/src/gui/colorlcd/radio_sdmanager.cpp new file mode 100644 index 000000000..69acfec55 --- /dev/null +++ b/radio/src/gui/colorlcd/radio_sdmanager.cpp @@ -0,0 +1,321 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "radio_sdmanager.h" +#include "opentx.h" +#include "libopenui.h" +#include "io/frsky_firmware_update.h" + +RadioSdManagerPage::RadioSdManagerPage() : + PageTab(SD_IS_HC() ? STR_SDHC_CARD : STR_SD_CARD, ICON_RADIO_SD_MANAGER) +{ +} + +void RadioSdManagerPage::rebuild(FormWindow *window) +{ + coord_t scrollPosition = window->getScrollPositionY(); + window->clear(); + build(window); + window->setScrollPositionY(scrollPosition); +} + +// TODO elsewhere +extern bool compare_nocase(const std::string &first, const std::string &second); + + +char *getFullPath(const std::string &filename) +{ + static char full_path[_MAX_LFN + 1]; // TODO optimize that! + f_getcwd(full_path, _MAX_LFN); + strcat(full_path, "/"); + strcat(full_path, filename.c_str()); + return full_path; +} + +class FilePreview : public Window +{ + public: + FilePreview(Window *parent, const rect_t &rect) : + Window(parent, rect, NO_SCROLLBAR) + { + } + +#if defined(DEBUG_WINDOWS) + virtual std::string getName() + { + return "FilePreview"; + } +#endif + + void setFile(const char *filename) + { + delete bitmap; + const char *ext = getFileExtension(filename); + if (ext && isExtensionMatching(ext, BITMAPS_EXT)) { + bitmap = BitmapBuffer::load(filename); + } else { + bitmap = nullptr; + } + invalidate(); + } + + void paint(BitmapBuffer *dc) override + { + coord_t y = parent->getScrollPositionY() + 2; + coord_t h = MENU_BODY_HEIGHT - 4; + lcd->drawSolidFilledRect(0, y, width(), h, CURVE_AXIS_COLOR); + if (bitmap) { + coord_t bitmapHeight = min(h, bitmap->getHeight()); + coord_t bitmapWidth = min(width(), bitmap->getWidth()); + dc->drawScaledBitmap(bitmap, (width() - bitmapWidth) / 2, y + (h - bitmapHeight) / 2, bitmapWidth, + bitmapHeight); + } + } + + protected: + BitmapBuffer *bitmap = nullptr; +}; + +class FlashModuleDialog: public FullScreenDialog +{ + public: + FlashModuleDialog(ModuleIndex module): + FullScreenDialog(WARNING_TYPE_INFO, "Flash device"), + device(module), + progress(this, {100, 100, 100, 15}) + { + } + + void flash(const char * filename) + { + device.flashFirmware(filename, [=](const char * title, const char * message, int count, int total) -> void { + setMessage(message); + progress.setValue(total > 0 ? count * 100 / total : 0); + mainWindow.run(false); + }); + deleteLater(); + } + + protected: + FrskyDeviceFirmwareUpdate device; + Progress progress; +}; + +void RadioSdManagerPage::build(FormWindow *window) +{ + FormGridLayout grid; + grid.spacer(PAGE_PADDING); + + FILINFO fno; + DIR dir; + std::list files; + std::list directories; + + auto preview = new FilePreview(window, {LCD_W / 2 + 6, 0, LCD_W / 2 - 16, window->height()}); + + FRESULT res = f_opendir(&dir, "."); // Open the directory + if (res == FR_OK) { + // read all entries + bool firstTime = true; + for (;;) { + res = sdReadDir(&dir, &fno, firstTime); + + if (res != FR_OK || fno.fname[0] == 0) + break; // Break on error or end of dir + if (strlen(fno.fname) > SD_SCREEN_FILE_LENGTH) + continue; + if (fno.fname[0] == '.' && fno.fname[1] != '.') + continue; // Ignore hidden files under UNIX, but not .. + + if (fno.fattrib & AM_DIR) { + directories.push_back(fno.fname); + } else { + files.push_back(fno.fname); + } + } + + // sort directories and files + directories.sort(compare_nocase); + files.sort(compare_nocase); + + for (auto name: directories) { + auto button = new TextButton(window, grid.getLabelSlot(), name, [=]() -> uint8_t { + f_chdir(name.data()); + window->clear(); + build(window); + return 0; + }, 0); + grid.nextLine(); + } + + for (auto name: files) { + auto button = new TextButton(window, grid.getLabelSlot(), name, [=]() -> uint8_t { + auto menu = new Menu(); + const char *ext = getFileExtension(name.data()); + if (ext) { + if (!strcasecmp(ext, SOUNDS_EXT)) { + menu->addLine(STR_PLAY_FILE, [=]() { + audioQueue.stopAll(); + audioQueue.playFile(getFullPath(name), 0, ID_PLAY_FROM_SD_MANAGER); + }); + } + else if (isExtensionMatching(ext, BITMAPS_EXT)) { + // TODO + } + else if (!strcasecmp(ext, TEXT_EXT)) { + menu->addLine(STR_VIEW_TEXT, [=]() { + // TODO + }); + } + else if (!READ_ONLY() && !strcasecmp(ext, SPORT_FIRMWARE_EXT)) { + if (HAS_SPORT_UPDATE_CONNECTOR()) { + menu->addLine(STR_FLASH_EXTERNAL_DEVICE, [=]() { + auto dialog = new FlashModuleDialog(SPORT_MODULE); + dialog->flash(getFullPath(name)); + }); + } + menu->addLine(STR_FLASH_INTERNAL_MODULE, [=]() { + auto dialog = new FlashModuleDialog(INTERNAL_MODULE); + dialog->flash(getFullPath(name)); + }); + menu->addLine(STR_FLASH_EXTERNAL_MODULE, [=]() { + auto dialog = new FlashModuleDialog(EXTERNAL_MODULE); + dialog->flash(getFullPath(name)); + }); + } +#if defined(LUA) + else if (isExtensionMatching(ext, SCRIPTS_EXT)) { + menu->addLine(STR_EXECUTE_FILE, [=]() { + luaExec(getFullPath(name)); + }); + } +#endif + } + if (!READ_ONLY()) { + menu->addLine(STR_COPY_FILE, [=]() { + clipboard.type = CLIPBOARD_TYPE_SD_FILE; + f_getcwd(clipboard.data.sd.directory, CLIPBOARD_PATH_LEN); + strncpy(clipboard.data.sd.filename, name.c_str(), CLIPBOARD_PATH_LEN - 1); + }); + if (clipboard.type == CLIPBOARD_TYPE_SD_FILE) { + menu->addLine(STR_PASTE, [=]() { + // TODO + }); + } + menu->addLine(STR_RENAME_FILE, [=]() { + // TODO + }); + menu->addLine(STR_DELETE_FILE, [=]() { + f_unlink(getFullPath(name)); + rebuild(window); + }); + } + return 0; + }, 0); + button->setFocusHandler([=]() { + preview->setFile(getFullPath(name)); + }); + grid.nextLine(); + } + } + + window->setLastField(); + window->setInnerHeight(grid.getWindowHeight()); + preview->setHeight(max(window->height(), grid.getWindowHeight())); +} + +#if 0 +bool menuRadioSdManagerInfo(event_t event) +{ + SIMPLE_SUBMENU(STR_SD_INFO_TITLE, ICON_RADIO_SD_MANAGER, 1); + + lcdDrawText(MENUS_MARGIN_LEFT, 2*FH, STR_SD_TYPE); + lcdDrawText(100, 2*FH, SD_IS_HC() ? STR_SDHC_CARD : STR_SD_CARD); + + lcdDrawText(MENUS_MARGIN_LEFT, 3*FH, STR_SD_SIZE); + lcdDrawNumber(100, 3*FH, sdGetSize(), LEFT, 0, NULL, "M"); + + lcdDrawText(MENUS_MARGIN_LEFT, 4*FH, STR_SD_SECTORS); +#if defined(SD_GET_FREE_BLOCKNR) + lcdDrawNumber(100, 4*FH, SD_GET_FREE_BLOCKNR()/1000, LEFT, 0, NULL, "/"); + lcdDrawNumber(150, 4*FH, sdGetNoSectors()/1000, LEFT); +#else + lcdDrawNumber(100, 4*FH, sdGetNoSectors()/1000, LEFT, 0, NULL, "k"); +#endif + + lcdDrawText(MENUS_MARGIN_LEFT, 5*FH, STR_SD_SPEED); + lcdDrawNumber(100, 5*FH, SD_GET_SPEED()/1000, LEFT, 0, NULL, "kb/s"); + + return true; +} +#endif + +#if 0 +void onSdManagerMenu(const char * result) +{ + TCHAR lfn[_MAX_LFN+1]; + + // TODO possible buffer overflows here! + + uint8_t index = menuVerticalPosition-menuVerticalOffset; + char *line = reusableBuffer.sdmanager.lines[index]; + + if (result == STR_SD_INFO) { + pushMenu(menuRadioSdManagerInfo); + } + else if (result == STR_SD_FORMAT) { + POPUP_CONFIRMATION(STR_CONFIRM_FORMAT); + } + else if (result == STR_PASTE) { + f_getcwd(lfn, _MAX_LFN); + // if destination is dir, copy into that dir + if (IS_DIRECTORY(line)) { + strcat(lfn, PSTR("/")); + strcat(lfn, line); + } + if (strcmp(clipboard.data.sd.directory, lfn)) { // prevent copying to the same directory + POPUP_WARNING(sdCopyFile(clipboard.data.sd.filename, clipboard.data.sd.directory, clipboard.data.sd.filename, lfn)); + REFRESH_FILES(); + } + } + else if (result == STR_RENAME_FILE) { + memcpy(reusableBuffer.sdmanager.originalName, line, sizeof(reusableBuffer.sdmanager.originalName)); + uint8_t fnlen = 0, extlen = 0; + getFileExtension(line, 0, LEN_FILE_EXTENSION_MAX, &fnlen, &extlen); + // write spaces to allow extending the length of a filename + memset(line + fnlen - extlen, ' ', SD_SCREEN_FILE_LENGTH - fnlen + extlen); + line[SD_SCREEN_FILE_LENGTH-extlen] = '\0'; + s_editMode = EDIT_MODIFY_STRING; + editNameCursorPos = 0; + } + else if (result == STR_ASSIGN_BITMAP) { + memcpy(g_model.header.bitmap, line, sizeof(g_model.header.bitmap)); + storageDirty(EE_MODEL); + } + else if (result == STR_ASSIGN_SPLASH) { + f_getcwd(lfn, _MAX_LFN); + sdCopyFile(line, lfn, SPLASH_FILE, BITMAPS_PATH); + } + else if (result == STR_VIEW_TEXT) { + getSelectionFullPath(lfn); + pushMenuTextView(lfn); + } +} +#endif diff --git a/radio/src/gui/colorlcd/radio_sdmanager.h b/radio/src/gui/colorlcd/radio_sdmanager.h new file mode 100644 index 000000000..8f5e91178 --- /dev/null +++ b/radio/src/gui/colorlcd/radio_sdmanager.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "tabsgroup.h" + +class RadioSdManagerPage: public PageTab { + public: + RadioSdManagerPage(); + + void build(FormWindow * window) override; + + protected: + void rebuild(FormWindow * window); +}; diff --git a/radio/src/gui/colorlcd/radio_setup.cpp b/radio/src/gui/colorlcd/radio_setup.cpp new file mode 100644 index 000000000..bb6181e55 --- /dev/null +++ b/radio/src/gui/colorlcd/radio_setup.cpp @@ -0,0 +1,470 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#define LANGUAGE_PACKS_DEFINITION + +#include "radio_setup.h" +#include "opentx.h" + +#define SET_DIRTY() storageDirty(EE_GENERAL) + +class DateTimeWindow : public Window { + public: + DateTimeWindow(Window * parent, const rect_t &rect) : + Window(parent, rect, FORWARD_SCROLL) + { + build(); + } + + ~DateTimeWindow() + { + deleteChildren(); + } + + void checkEvents() override + { + Window::checkEvents(); + + if (get_tmr10ms() - lastRefresh > 100) { + invalidate(); + lastRefresh = get_tmr10ms(); + } + } + + FormField * getFirstField() + { + return firstField; + } + + protected: + tmr10ms_t lastRefresh = 0; + FormField * firstField = nullptr; + + void build() + { + FormGridLayout grid; + + // Date + new StaticText(this, grid.getLabelSlot(), STR_DATE); + firstField = new NumberEdit(this, grid.getFieldSlot(3, 0), 2018, 2100, + [=]() -> int32_t { + struct gtm t; + gettime(&t); + return TM_YEAR_BASE + t.tm_year; + }, + [=](int32_t newValue) { + struct gtm t; + gettime(&t); + t.tm_year = newValue - TM_YEAR_BASE; + SET_LOAD_DATETIME(&t); + }); + auto month = new NumberEdit(this, grid.getFieldSlot(3, 1), 1, 12, + [=]() -> int32_t { + struct gtm t; + gettime(&t); + return 1 + t.tm_mon; + }, + [=](int32_t newValue) { + struct gtm t; + gettime(&t); + t.tm_mon = newValue - 1; + SET_LOAD_DATETIME(&t); + }); + month->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + drawNumber(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, flags | LEADING0, 2); + }); + + /* TODO dynamic max instead of 31 ... + int16_t year = TM_YEAR_BASE + t.tm_year; + int8_t dlim = (((((year%4==0) && (year%100!=0)) || (year%400==0)) && (t.tm_mon==1)) ? 1 : 0); + static const pm_uint8_t dmon[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + dlim += *(&dmon[t.tm_mon]);*/ + int8_t dlim = 31; + auto day = new NumberEdit(this, grid.getFieldSlot(3, 2), 1, dlim, + [=]() -> int32_t { + struct gtm t; + gettime(&t); + return t.tm_mday; + }, + [=](int32_t newValue) { + struct gtm t; + gettime(&t); + t.tm_mday = newValue; + SET_LOAD_DATETIME(&t); + }); + day->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + drawNumber(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, flags | LEADING0, 2); + }); + grid.nextLine(); + + + //Time + new StaticText(this, grid.getLabelSlot(), STR_TIME); + auto hour = new NumberEdit(this, grid.getFieldSlot(3, 0), 0, 24, + [=]() -> int32_t { + struct gtm t; + gettime(&t); + return t.tm_hour; + }, + [=](int32_t newValue) { + struct gtm t; + gettime(&t); + t.tm_hour = newValue; + SET_LOAD_DATETIME(&t); + }); + hour->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + drawNumber(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, flags | LEADING0, 2); + }); + + auto minutes = new NumberEdit(this, grid.getFieldSlot(3, 1), 0, 59, + [=]() -> int32_t { + struct gtm t; + gettime(&t); + return t.tm_min; + }, + [=](int32_t newValue) { + struct gtm t; + gettime(&t); + t.tm_min = newValue; + SET_LOAD_DATETIME(&t); + }); + minutes->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + drawNumber(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, flags | LEADING0, 2); + }); + + auto seconds = new NumberEdit(this, grid.getFieldSlot(3, 2), 0, 59, + [=]() -> int32_t { + struct gtm t; + gettime(&t); + return t.tm_sec; + }, + [=](int32_t newValue) { + struct gtm t; + gettime(&t); + t.tm_sec = newValue; + SET_LOAD_DATETIME(&t); + }); + seconds->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + drawNumber(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, flags | LEADING0, 2); + }); + grid.nextLine(); + getParent()->moveWindowsTop(top(), adjustHeight()); + } +}; + +RadioSetupPage::RadioSetupPage(): + PageTab(STR_MENURADIOSETUP, ICON_RADIO_SETUP) +{ +} + +void RadioSetupPage::build(FormWindow * window) +{ + FormGridLayout grid; + grid.spacer(PAGE_PADDING); + + // Date and Time + auto timeWindow = new DateTimeWindow(window, {0, grid.getWindowHeight(), LCD_W, 0}); + grid.addWindow(timeWindow); + window->setFirstField(timeWindow->getFirstField()); + + // Batt meter range - Range 3.0v to 16v + new StaticText(window, grid.getLabelSlot(), STR_BATTERY_RANGE); + auto batMinEdit = new NumberEdit(window, grid.getFieldSlot(2, 0), -60 + 90, g_eeGeneral.vBatMax + 29 + 90, GET_SET_WITH_OFFSET(g_eeGeneral.vBatMin, 90), PREC1); + batMinEdit->setSuffix("V"); + auto batMaxEdit = new NumberEdit(window, grid.getFieldSlot(2, 1), g_eeGeneral.vBatMin - 29 + 120, 40 + 120, GET_SET_WITH_OFFSET(g_eeGeneral.vBatMax, 120), PREC1); + batMaxEdit->setSuffix("V"); + batMinEdit->setSetValueHandler([=](int32_t newValue) { + g_eeGeneral.vBatMin= newValue - 90; + SET_DIRTY(); + batMaxEdit->setMin(g_eeGeneral.vBatMin - 29 + 120); + batMaxEdit->invalidate(); + }); + batMaxEdit->setSetValueHandler([=](int32_t newValue) { + g_eeGeneral.vBatMax= newValue - 120; + SET_DIRTY(); + batMinEdit->setMax(g_eeGeneral.vBatMax + 29 + 90); + batMinEdit->invalidate(); + }); + grid.nextLine(); + + new Subtitle(window, grid.getLabelSlot(), STR_SOUND_LABEL); + grid.nextLine(); + + // Beeps mode + new StaticText(window, grid.getLabelSlot(true), STR_SPEAKER); + new Choice(window, grid.getFieldSlot(), STR_VBEEPMODE, -2, 1, GET_SET_DEFAULT(g_eeGeneral.beepMode)); + grid.nextLine(); + + // Main volume + new StaticText(window, grid.getLabelSlot(true), STR_SPEAKER_VOLUME); + new Slider(window, grid.getFieldSlot(), -VOLUME_LEVEL_DEF, VOLUME_LEVEL_MAX-VOLUME_LEVEL_DEF, GET_SET_DEFAULT(g_eeGeneral.speakerVolume)); + grid.nextLine(); + + // Beeps volume + new StaticText(window, grid.getLabelSlot(true), STR_BEEP_VOLUME); + new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.beepVolume)); + grid.nextLine(); + + // Wav volume + new StaticText(window, grid.getLabelSlot(true), STR_WAV_VOLUME); + new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.wavVolume)); + grid.nextLine(); + + // Background volume + new StaticText(window, grid.getLabelSlot(true), STR_BG_VOLUME); + new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.backgroundVolume)); + grid.nextLine(); + + // Beeps length + new StaticText(window, grid.getLabelSlot(true), STR_BEEP_LENGTH); + new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.beepLength)); + grid.nextLine(); + + // Beeps pitch + new StaticText(window, grid.getLabelSlot(true), STR_SPKRPITCH); + auto edit = new NumberEdit(window, grid.getFieldSlot(), 0, 300, + GET_DEFAULT(15 * g_eeGeneral.speakerPitch), + [=](int32_t newValue) { + g_eeGeneral.speakerPitch = newValue / 15; + SET_DIRTY(); + }); + edit->setStep(15); + edit->setPrefix("+"); + edit->setSuffix("Hz"); + grid.nextLine(); + +#if defined(VARIO) + new Subtitle(window, grid.getLabelSlot(), STR_VARIO); + grid.nextLine(); + + // Vario volume + new StaticText(window, grid.getLabelSlot(true), TR_SPEAKER_VOLUME); + new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.varioVolume)); + grid.nextLine(); + + new StaticText(window, grid.getLabelSlot(true), STR_PITCH_AT_ZERO); + edit = new NumberEdit(window, grid.getFieldSlot(), VARIO_FREQUENCY_ZERO-400, VARIO_FREQUENCY_ZERO+400, + GET_DEFAULT(VARIO_FREQUENCY_ZERO+(g_eeGeneral.varioPitch*10)), + SET_VALUE(g_eeGeneral.varioPitch, (newValue - VARIO_FREQUENCY_ZERO) / 10)); + edit->setStep(10); + edit->setSuffix("Hz"); + grid.nextLine(); + +#endif + +#if defined(HAPTIC) + { + new Subtitle(window, grid.getLabelSlot(), STR_HAPTIC_LABEL); + grid.nextLine(); + + // Haptic mode + new StaticText(window, grid.getLabelSlot(true), STR_MODE); + new Choice(window, grid.getFieldSlot(), STR_VBEEPMODE, -2, 1, GET_SET_DEFAULT(g_eeGeneral.hapticMode)); + grid.nextLine(); + + // Haptic duration + new StaticText(window, grid.getLabelSlot(true), STR_LENGTH); + new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.hapticLength)); + grid.nextLine(); + + // Haptic strength + new StaticText(window, grid.getLabelSlot(true), STR_HAPTICSTRENGTH); + new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.hapticStrength)); + grid.nextLine(); + } +#endif + + // Alarms + { + new Subtitle(window, grid.getLabelSlot(), STR_ALARMS_LABEL); + grid.nextLine(); + + // Battery warning + new StaticText(window, grid.getLabelSlot(true), STR_BATTERYWARNING); + edit = new NumberEdit(window, grid.getFieldSlot(), 30, 120, GET_SET_DEFAULT(g_eeGeneral.vBatWarn), PREC1); + edit->setSuffix("v"); + grid.nextLine(); + + // Alarms warning + new StaticText(window, grid.getLabelSlot(true), STR_ALARMWARNING); + new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(g_eeGeneral.disableAlarmWarning)); + grid.nextLine(); + + // RSSI shutdown alarm + new StaticText(window, grid.getLabelSlot(true), STR_RSSISHUTDOWNALARM); + new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(g_eeGeneral.disableRssiPoweroffAlarm)); + grid.nextLine(); + + // Inactivity alarm + new StaticText(window, grid.getLabelSlot(true), STR_INACTIVITYALARM); + edit = new NumberEdit(window, grid.getFieldSlot(), 0, 250, GET_SET_DEFAULT(g_eeGeneral.inactivityTimer)); + edit->setSuffix("minutes"); + grid.nextLine(); + } + + // Backlight + { + new Subtitle(window, grid.getLabelSlot(), STR_BACKLIGHT_LABEL); + grid.nextLine(); + + // Backlight mode + new StaticText(window, grid.getLabelSlot(true), STR_MODE); + new Choice(window, grid.getFieldSlot(2,0), STR_VBLMODE, e_backlight_mode_off, e_backlight_mode_on, GET_SET_DEFAULT(g_eeGeneral.backlightMode)); + //grid.nextLine(); + + // Delay + auto edit = new NumberEdit(window, grid.getFieldSlot(2, 1), 0, 600, + GET_DEFAULT(g_eeGeneral.lightAutoOff * 5), + SET_VALUE(g_eeGeneral.lightAutoOff, newValue / 5)); + edit->setStep(5); + edit->setSuffix("s"); + grid.nextLine(); + + // Backlight ON bright + new StaticText(window, grid.getLabelSlot(true), STR_BLONBRIGHTNESS); + new Slider(window, grid.getFieldSlot(), BACKLIGHT_LEVEL_MIN, BACKLIGHT_LEVEL_MAX, + [=]() -> int32_t { + return BACKLIGHT_LEVEL_MAX - g_eeGeneral.backlightBright; + }, + [=](int32_t newValue) { + g_eeGeneral.backlightBright = BACKLIGHT_LEVEL_MAX - newValue; + }); + grid.nextLine(); + + // Backlight OFF bright + new StaticText(window, grid.getLabelSlot(true), STR_BLOFFBRIGHTNESS); + new Slider(window, grid.getFieldSlot(), BACKLIGHT_LEVEL_MIN, BACKLIGHT_LEVEL_MAX, GET_SET_DEFAULT(g_eeGeneral.blOffBright)); + grid.nextLine(); + + // Flash beep + new StaticText(window, grid.getLabelSlot(true), STR_ALARM); + new CheckBox(window, grid.getFieldSlot(), GET_SET_DEFAULT(g_eeGeneral.alarmsFlash)); + grid.nextLine(); + } + + // GPS + { + new Subtitle(window, grid.getLabelSlot(), STR_GPS); + grid.nextLine(); + + // Timezone + new StaticText(window, grid.getLabelSlot(true), STR_TIMEZONE); + new NumberEdit(window, grid.getFieldSlot(2, 0), -12, 12, GET_SET_DEFAULT(g_eeGeneral.timezone)); + grid.nextLine(); + + // Adjust RTC (from telemetry) + new StaticText(window, grid.getLabelSlot(true), STR_ADJUST_RTC); + new CheckBox(window, grid.getFieldSlot(), GET_SET_DEFAULT(g_eeGeneral.adjustRTC)); + grid.nextLine(); + + // GPS format + new StaticText(window, grid.getLabelSlot(true), STR_GPSCOORD); + new Choice(window, grid.getFieldSlot(), STR_GPSFORMAT, 0, 1, GET_SET_DEFAULT(g_eeGeneral.gpsFormat)); + grid.nextLine(); + } + +#if defined(PXX2) + // Owner ID + new StaticText(window, grid.getLabelSlot(), STR_OWNER_ID); + new TextEdit(window, grid.getFieldSlot(), g_eeGeneral.ownerRegistrationID, PXX2_LEN_REGISTRATION_ID); + grid.nextLine(); +#endif + + // Country code + new StaticText(window, grid.getLabelSlot(), STR_COUNTRYCODE); + new Choice(window, grid.getFieldSlot(), STR_COUNTRYCODES, 0, 2, GET_SET_DEFAULT(g_eeGeneral.countryCode)); + grid.nextLine(); + + // Audio language + new StaticText(window, grid.getLabelSlot(), STR_VOICELANG); + auto choice = new Choice(window, grid.getFieldSlot(), nullptr, 0, DIM(languagePacks) - 2, GET_VALUE(currentLanguagePackIdx), + [](uint8_t newValue) { + currentLanguagePackIdx = newValue; + currentLanguagePack = languagePacks[currentLanguagePackIdx]; + strncpy(g_eeGeneral.ttsLanguage, currentLanguagePack->id, 2); + }); + choice->setTextHandler([](uint8_t value) { + return languagePacks[value]->name; + }); + grid.nextLine(); + + // Imperial units + new StaticText(window, grid.getLabelSlot(), STR_UNITSSYSTEM); + new Choice(window, grid.getFieldSlot(), STR_VUNITSSYSTEM, 0, 1, GET_SET_DEFAULT(g_eeGeneral.imperial)); + grid.nextLine(); + +#if defined(FAI_CHOICE) +/* case ITEM_SETUP_FAI: + lcdDrawText(MENUS_MARGIN_LEFT, y, "FAI Mode"); + if (g_eeGeneral.fai) { + lcdDrawText(RADIO_SETUP_2ND_COLUMN, y, "Locked in FAI Mode"); + } + else { + g_eeGeneral.fai = editCheckBox(g_eeGeneral.fai, RADIO_SETUP_2ND_COLUMN, y, attr, event); + if (attr && checkIncDec_Ret) { + g_eeGeneral.fai = false; + POPUP_CONFIRMATION("FAI mode?"); + } + } + break;*/ +#endif + + // Switches delay + new StaticText(window, grid.getLabelSlot(), STR_SWITCHES_DELAY); + edit = new NumberEdit(window, grid.getFieldSlot(2, 0), -15, 100 - 15, GET_SET_VALUE_WITH_OFFSET(g_eeGeneral.switchesDelay, 15)); + edit->setSuffix(std::string("0") + STR_MS); + grid.nextLine(); + + // USB mode + new StaticText(window, grid.getLabelSlot(), STR_USBMODE); + new Choice(window, grid.getFieldSlot(), STR_USBMODES, USB_UNSELECTED_MODE, USB_MAX_MODE, GET_SET_DEFAULT(g_eeGeneral.USBMode)); + grid.nextLine(); + + // RX channel order + new StaticText(window, grid.getLabelSlot(), STR_RXCHANNELORD); // RAET->AETR + choice = new Choice(window, grid.getFieldSlot(), nullptr, 0, 4*3*2 - 1, GET_SET_DEFAULT(g_eeGeneral.templateSetup)); + choice->setTextHandler([](uint8_t value) { + char s[5]; + for (uint8_t i=0; i<4; i++) { + s[i] = STR_RETA123[channelOrder(value, i + 1)]; + } + s[4] = '\0'; + return std::string(s); + }); + grid.nextLine(); + + // Stick mode + new StaticText(window, grid.getLabelSlot(), STR_MODE); + choice = new Choice(window, grid.getFieldSlot(), nullptr, 0, 3, GET_DEFAULT(g_eeGeneral.stickMode), + [=](uint8_t newValue) { + pausePulses(); + g_eeGeneral.stickMode = newValue; + SET_DIRTY(); + checkThrottleStick(); + resumePulses(); + }); + choice->setTextHandler([](uint8_t value) { + return std::to_string(1 + value) + ": left=" + std::string(&getSourceString(MIXSRC_Rud + modn12x3[4 * value])[1]) + "+" + std::string(&getSourceString(MIXSRC_Rud + modn12x3[4 * value + 1])[1]); + }); + grid.nextLine(); + + FormField::link(FormField::getCurrentField(), timeWindow->getFirstField()); + window->setInnerHeight(grid.getWindowHeight()); +} \ No newline at end of file diff --git a/radio/src/gui/colorlcd/radio_setup.h b/radio/src/gui/colorlcd/radio_setup.h new file mode 100644 index 000000000..6ff1e2de5 --- /dev/null +++ b/radio/src/gui/colorlcd/radio_setup.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "tabsgroup.h" + +class RadioSetupPage: public PageTab { + public: + RadioSetupPage(); + + void build(FormWindow * window) override; +}; diff --git a/radio/src/gui/colorlcd/radio_spectrum_analyser.cpp b/radio/src/gui/colorlcd/radio_spectrum_analyser.cpp new file mode 100644 index 000000000..b3c8e71a5 --- /dev/null +++ b/radio/src/gui/colorlcd/radio_spectrum_analyser.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "radio_spectrum_analyser.h" +#include "opentx.h" + +#define SPECTRUM_HEIGHT 200 + +class SpectrumWindow : public Window +{ + public: + SpectrumWindow(Window *parent, const rect_t &rect) : + Window(parent, rect, REFRESH_ALWAYS) + { + } + + void paint(BitmapBuffer * dc) override + { + if (TELEMETRY_STREAMING()) { + dc->drawText(width() / 2, height() / 2, STR_TURN_OFF_RECEIVER, CENTERED); + return; + } + + coord_t peak_y = 1; + coord_t peak_x = 0; + for (coord_t i = 0; i < LCD_W; i++) { + coord_t h = min(reusableBuffer.spectrumAnalyser.bars[i], SPECTRUM_HEIGHT); + if (h > peak_y) { + peak_x = i; + peak_y = h; + } + dc->drawVerticalLine(i, SPECTRUM_HEIGHT - h, h, SOLID, 0); + } + + coord_t y = max(FH, SPECTRUM_HEIGHT - peak_y - FH); + drawNumber(dc, min(100, peak_x), y, ((reusableBuffer.spectrumAnalyser.freq - reusableBuffer.spectrumAnalyser.span / 2) + peak_x * (reusableBuffer.spectrumAnalyser.span / 128)) / 1000000, TINSIZE); + dc->drawText(lcdNextPos, y, "M", TINSIZE); + } + + protected: + +}; + +RadioSpectrumAnalyser::RadioSpectrumAnalyser(uint8_t moduleIdx) : + Page(ICON_RADIO_TOOLS), + moduleIdx(moduleIdx) +{ + buildBody(&body); + + start(); +} + +void RadioSpectrumAnalyser::buildBody(FormWindow * window) +{ + new SpectrumWindow(window, {0, 0, LCD_W, SPECTRUM_HEIGHT}); +} + +void RadioSpectrumAnalyser::start() +{ + if (isModuleR9MAccess(moduleIdx)) { + reusableBuffer.spectrumAnalyser.spanDefault = 20; + reusableBuffer.spectrumAnalyser.spanMax = 40; + reusableBuffer.spectrumAnalyser.freqDefault = 890; + reusableBuffer.spectrumAnalyser.freqMin = 850; + reusableBuffer.spectrumAnalyser.freqMax = 930; + } + else { + reusableBuffer.spectrumAnalyser.spanDefault = 40; // 40MHz + reusableBuffer.spectrumAnalyser.spanMax = 80; + reusableBuffer.spectrumAnalyser.freqDefault = 2440; // 2440MHz + reusableBuffer.spectrumAnalyser.freqMin = 2400; + reusableBuffer.spectrumAnalyser.freqMax = 2485; + } + + if (moduleState[moduleIdx].mode != MODULE_MODE_SPECTRUM_ANALYSER) { + memclear(reusableBuffer.spectrumAnalyser.bars, sizeof(reusableBuffer.spectrumAnalyser.bars)); + reusableBuffer.spectrumAnalyser.span = reusableBuffer.spectrumAnalyser.spanDefault * 1000000; + reusableBuffer.spectrumAnalyser.freq = reusableBuffer.spectrumAnalyser.freqDefault * 1000000; + reusableBuffer.spectrumAnalyser.step = reusableBuffer.spectrumAnalyser.span / LCD_W; + reusableBuffer.spectrumAnalyser.dirty = true; + moduleState[moduleIdx].mode = MODULE_MODE_SPECTRUM_ANALYSER; + } +} + +void RadioSpectrumAnalyser::stop() +{ + new MessageDialog(STR_MODULE, STR_STOPPING); + moduleState[moduleIdx].readModuleInformation(&reusableBuffer.moduleSetup.pxx2.moduleInformation, PXX2_HW_INFO_TX_ID, PXX2_HW_INFO_TX_ID); + /* wait 1s to resume normal operation before leaving */ + // watchdogSuspend(1000); + // RTOS_WAIT_MS(1000); +} diff --git a/radio/src/gui/colorlcd/radio_spectrum_analyser.h b/radio/src/gui/colorlcd/radio_spectrum_analyser.h new file mode 100644 index 000000000..71a05f2a4 --- /dev/null +++ b/radio/src/gui/colorlcd/radio_spectrum_analyser.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _RADIO_SPECTRUM_ANALYSER_H +#define _RADIO_SPECTRUM_ANALYSER_H + +#include "page.h" + +class RadioSpectrumAnalyser: public Page +{ + public: + RadioSpectrumAnalyser(uint8_t moduleIdx); + + protected: + void buildBody(FormWindow * window); + void start(); + void stop(); + + uint8_t moduleIdx; +}; + +#endif // _RADIO_SPECTRUM_ANALYSER_H diff --git a/radio/src/gui/colorlcd/radio_tools.cpp b/radio/src/gui/colorlcd/radio_tools.cpp new file mode 100644 index 000000000..1fcd3bb19 --- /dev/null +++ b/radio/src/gui/colorlcd/radio_tools.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 +#include "radio_tools.h" +#include "radio_spectrum_analyser.h" +#include "opentx.h" +#include "libopenui.h" + +extern uint8_t g_moduleIdx; + +#define TOOL_NAME_MAXLEN 16 + +#if defined(LUA) || defined(DEBUG) +bool readToolName(const char * filename, char * name) +{ + FIL file; + char buffer[1024]; + UINT count; + + if (f_open(&file, filename, FA_READ) != FR_OK) { + return "Error opening file"; + } + + if (f_read(&file, &buffer, sizeof(buffer), &count) != FR_OK) { + f_close(&file); + return false; + } + + const char * tns = "TNS|"; + auto * start = std::search(buffer, buffer + sizeof(buffer), tns, tns + 4); + if (start >= buffer + sizeof(buffer)) + return false; + + start += 4; + + const char * tne = "|TNE"; + auto * end = std::search(buffer, buffer + sizeof(buffer), tne, tne + 4); + if (end >= buffer + sizeof(buffer) || end <= start) + return false; + + uint8_t len = end - start; + if (len > TOOL_NAME_MAXLEN) + return false; + + strncpy(name, start, len); + memclear(name + len, TOOL_NAME_MAXLEN + 1 - len); + + return true; +} + +bool isRadioScriptTool(const char * filename) +{ + const char * ext = getFileExtension(filename); + return ext && !strcasecmp(ext, SCRIPT_EXT); +} +#endif + +RadioToolsPage::RadioToolsPage() : + PageTab(STR_MENUTOOLS, ICON_RADIO_TOOLS) +{ +} + +void RadioToolsPage::build(FormWindow * window) +{ + this->window = window; + + memclear(&reusableBuffer.radioTools, sizeof(reusableBuffer.radioTools)); + waiting = 0; + +#if defined(PXX2) + for (uint8_t module = 0; module < NUM_MODULES; module++) { + if (isModulePXX2(module) && (module == INTERNAL_MODULE ? IS_INTERNAL_MODULE_ON() : IS_EXTERNAL_MODULE_ON())) { + waiting |= (1 << module); + moduleState[module].readModuleInformation(&reusableBuffer.radioTools.modules[module], PXX2_HW_INFO_TX_ID, PXX2_HW_INFO_TX_ID); + } + } +#endif + + rebuild(window); +} + +void RadioToolsPage::checkEvents() +{ + bool refresh = false; + + for (uint8_t module = 0; module < NUM_MODULES; module++) { + if ((waiting & (1 << module)) && reusableBuffer.radioTools.modules[module].information.modelID) { + waiting &= ~(1 << module); + refresh = true; + } + } + + if (refresh) { + rebuild(window); + } + + PageTab::checkEvents(); +} + +void RadioToolsPage::rebuild(FormWindow * window) +{ + FormGridLayout grid; + grid.spacer(8); + grid.setLabelWidth(100); + + window->clear(); + +// LUA scripts in TOOLS +#if defined(LUA) || defined(DEBUG) + FILINFO fno; + DIR dir; + +#if defined(CROSSFIRE) +// if (isFileAvailable(SCRIPTS_TOOLS_PATH "/CROSSFIRE/crossfire.lua")) +// addRadioScriptTool(index++, SCRIPTS_TOOLS_PATH "/CROSSFIRE/crossfire.lua"); +#endif + + FRESULT res = f_opendir(&dir, SCRIPTS_TOOLS_PATH); + if (res == FR_OK) { + for (;;) { + TCHAR path[_MAX_LFN+1] = SCRIPTS_TOOLS_PATH "/"; + res = f_readdir(&dir, &fno); /* Read a directory item */ + if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ + if (fno.fattrib & AM_DIR) continue; /* Skip subfolders */ + if (fno.fattrib & AM_HID) continue; /* Skip hidden files */ + if (fno.fattrib & AM_SYS) continue; /* Skip system files */ + + strcat(path, fno.fname); + if (isRadioScriptTool(fno.fname)) { + char toolName[TOOL_NAME_MAXLEN + 1]; + const char * label; + char * ext = (char *)getFileExtension(path); + if (readToolName(path, toolName)) { + label = toolName; + } + else { + *ext = '\0'; + label = getBasename(path); + } + new StaticText(window, grid.getLabelSlot(), "lua", BUTTON_BACKGROUND | CENTERED); + new TextButton(window, grid.getFieldSlot(1), label, [=]() -> uint8_t { + f_chdir("/SCRIPTS/TOOLS/"); + //luaExec(path); + return 0; + }, 0); + grid.nextLine(); + } + } + } +#endif + +#if defined(PXX2) + // PXX2 modules tools + if (isModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE].information.modelID, MODULE_OPTION_SPECTRUM_ANALYSER)) { + new StaticText(window, grid.getLabelSlot(), "access", BUTTON_BACKGROUND | CENTERED); + new TextButton(window, grid.getFieldSlot(1), STR_SPECTRUM_ANALYSER_INT, [=]() -> uint8_t { + new RadioSpectrumAnalyser(INTERNAL_MODULE); + return 0; + }, 0); + grid.nextLine(); + } + + if (isModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE].information.modelID, MODULE_OPTION_POWER_METER)) { + new StaticText(window, grid.getLabelSlot(), "access", BUTTON_BACKGROUND | CENTERED); + new TextButton(window, grid.getFieldSlot(1), STR_POWER_METER_INT, [=]() -> uint8_t { +// new RadioPowerMeter(INTERNAL_MODULE); + return 0; + }, 0); + grid.nextLine(); + } + + if (isModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE].information.modelID, MODULE_OPTION_SPECTRUM_ANALYSER)) { + new StaticText(window, grid.getLabelSlot(), "access", BUTTON_BACKGROUND | CENTERED); + new TextButton(window, grid.getFieldSlot(1), STR_SPECTRUM_ANALYSER_EXT, [=]() -> uint8_t { + new RadioSpectrumAnalyser(EXTERNAL_MODULE); + return 0; + }, 0); + grid.nextLine(); + } + + if (isModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE].information.modelID, MODULE_OPTION_POWER_METER)) { + new StaticText(window, grid.getLabelSlot(), "access", BUTTON_BACKGROUND | CENTERED); + new TextButton(window, grid.getFieldSlot(1), STR_POWER_METER_EXT, [=]() -> uint8_t { +// new RadioPowerMeter(EXTERNAL_MODULE); + return 0; + }, 0); + + grid.nextLine(); + } +#endif + + window->setInnerHeight(grid.getWindowHeight()); +} \ No newline at end of file diff --git a/radio/src/gui/colorlcd/radio_tools.h b/radio/src/gui/colorlcd/radio_tools.h new file mode 100644 index 000000000..8dcf15ed0 --- /dev/null +++ b/radio/src/gui/colorlcd/radio_tools.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "tabsgroup.h" + +class RadioToolsPage: public PageTab { + public: + RadioToolsPage(); + + void build(FormWindow * window) override; + + void checkEvents() override; + + protected: + void rebuild(FormWindow * window); + FormWindow * window = nullptr; + uint8_t waiting = 0; +}; diff --git a/radio/src/gui/colorlcd/radio_trainer.cpp b/radio/src/gui/colorlcd/radio_trainer.cpp new file mode 100644 index 000000000..1f740fc81 --- /dev/null +++ b/radio/src/gui/colorlcd/radio_trainer.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "radio_trainer.h" +#include "opentx.h" + +#define SET_DIRTY() storageDirty(EE_GENERAL) + +RadioTrainerPage::RadioTrainerPage(): + PageTab(STR_MENUTRAINER, ICON_RADIO_TRAINER) +{ +} + +void RadioTrainerPage::build(FormWindow * window) +{ +#if LCD_W > LCD_H +#define TRAINER_LABEL_WIDTH 180 +#else +#define TRAINER_LABEL_WIDTH 100 +#endif + FormGridLayout grid; + grid.setLabelWidth(TRAINER_LABEL_WIDTH); + grid.nextLine(); + + for (uint8_t i=0; imode)); + if(i == 0) { + window->setFirstField(mode); + } + auto perc = new NumberEdit(window, grid.getFieldSlot(3, 1), -125, 125, GET_SET_DEFAULT(td->studWeight)); + perc->setSuffix("%"); + new Choice(window, grid.getFieldSlot(3,2), STR_TRNCHN, 0, 3, GET_SET_DEFAULT(td->srcChn)); + grid.nextLine(); + } + grid.nextLine(); + + // Trainer multiplier + new StaticText(window, grid.getLabelSlot(), STR_MULTIPLIER); + auto multiplier = new NumberEdit(window, grid.getFieldSlot(3, 0), -10, 40, GET_SET_DEFAULT(g_eeGeneral.PPM_Multiplier)); + multiplier->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + drawNumber(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value+10, flags | PREC1, 0); + }); + grid.nextLine(); + grid.nextLine(); + + // Trainer calibration + new StaticText(window, grid.getLabelSlot(), STR_CAL); + for (int i=0; i<4; i++) { + #if defined (PPM_UNIT_PERCENT_PREC1) + auto calib = new NumberEdit(window, grid.getFieldSlot(4,i), 0 , 0, [=]() { return (ppmInput[i]-g_eeGeneral.trainer.calib[i])*2; }, nullptr, LEFT|PREC1); +#else + auto calib = new NumberEdit(window, grid.getFieldSlot(4,i), 0 , 0, [=]() { return (ppmInput[i]-g_eeGeneral.trainer.calib[i])/5; }, nullptr, LEFT); +#endif + calib->setWindowFlags(REFRESH_ALWAYS); + + FormField::link(multiplier, window->getFirstField()); + } +} diff --git a/radio/src/gui/colorlcd/radio_trainer.h b/radio/src/gui/colorlcd/radio_trainer.h new file mode 100644 index 000000000..82c5f2c83 --- /dev/null +++ b/radio/src/gui/colorlcd/radio_trainer.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "tabsgroup.h" + +class RadioTrainerPage: public PageTab { + public: + RadioTrainerPage(); + + void build(FormWindow * window) override; + + protected: + +}; diff --git a/radio/src/gui/colorlcd/radio_version.cpp b/radio/src/gui/colorlcd/radio_version.cpp new file mode 100644 index 000000000..f44c3b2f0 --- /dev/null +++ b/radio/src/gui/colorlcd/radio_version.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "radio_version.h" +#include "opentx.h" +#include "options.h" +#include "libopenui.h" + +class OptionsText : public StaticText { + public: + OptionsText(Window * parent, const rect_t &rect) : + StaticText(parent, rect) + { + coord_t optionWidth = 0; + for (uint8_t i=0; options[i]; i++) { + const char * option = options[i]; + optionWidth+= getTextWidth(option); + if(optionWidth + 5 > width()) { + setHeight(getHeight() + 20); + optionWidth = 0; + } + } + }; + + virtual void paint(BitmapBuffer * dc) override + { + coord_t y = 0; + lcdNextPos = 0; + for (uint8_t i=0; options[i]; i++) { + const char * option = options[i]; + coord_t optionWidth = getTextWidth(option); + + if((lcdNextPos + 5 + optionWidth) > width()) { + dc->drawText(lcdNextPos, y, ","); + lcdNextPos = 0; + y += FH; + } + if (i > 0 && lcdNextPos != 0) + dc->drawText(lcdNextPos, y, ", "); + dc->drawText(lcdNextPos, y, option); + } + } + + protected: +}; + +RadioVersionPage::RadioVersionPage(): + PageTab(STR_MENUVERSION, ICON_RADIO_VERSION) +{ +} + +void RadioVersionPage::build(FormWindow * window) +{ + FormGridLayout grid; + grid.setLabelWidth(120); + grid.spacer(PAGE_PADDING); + + getCPUUniqueID(reusableBuffer.version.id); + + new StaticText(window, grid.getLabelSlot(), "FW Version"); + new StaticText(window, grid.getFieldSlot(), vers_stamp); + grid.nextLine(); + + new StaticText(window, grid.getLabelSlot(), "FW Options"); + auto firmwareOptions = new OptionsText(window, grid.getFieldSlot()); + grid.spacer(firmwareOptions->height() + PAGE_LINE_SPACING); + + new StaticText(window, grid.getLabelSlot(), "FW Date"); + new StaticText(window, grid.getFieldSlot(), date_stamp); + grid.nextLine(); + + new StaticText(window, grid.getLabelSlot(), "FW Time"); + new StaticText(window, grid.getFieldSlot(), time_stamp); + grid.nextLine(); + + new StaticText(window, grid.getLabelSlot(), "Data version"); + new StaticText(window, grid.getFieldSlot(), eeprom_stamp); + grid.nextLine(); + + new StaticText(window, grid.getLabelSlot(), "CPU UID"); + new StaticText(window, grid.getFieldSlot(), reusableBuffer.version.id); + grid.nextLine(); +} diff --git a/radio/src/gui/colorlcd/radio_version.h b/radio/src/gui/colorlcd/radio_version.h new file mode 100644 index 000000000..c3adc2b0b --- /dev/null +++ b/radio/src/gui/colorlcd/radio_version.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "tabsgroup.h" + +class RadioVersionPage: public PageTab { + public: + RadioVersionPage(); + + void build(FormWindow * window) override; +}; diff --git a/radio/src/gui/480x272/rle.cpp b/radio/src/gui/colorlcd/rle.cpp similarity index 100% rename from radio/src/gui/480x272/rle.cpp rename to radio/src/gui/colorlcd/rle.cpp diff --git a/radio/src/gui/480x272/rle.h b/radio/src/gui/colorlcd/rle.h similarity index 100% rename from radio/src/gui/480x272/rle.h rename to radio/src/gui/colorlcd/rle.h diff --git a/radio/src/gui/480x272/screens_setup.cpp b/radio/src/gui/colorlcd/screens_setup.cpp similarity index 100% rename from radio/src/gui/480x272/screens_setup.cpp rename to radio/src/gui/colorlcd/screens_setup.cpp diff --git a/radio/src/gui/colorlcd/special_functions.cpp b/radio/src/gui/colorlcd/special_functions.cpp new file mode 100644 index 000000000..5c836f8aa --- /dev/null +++ b/radio/src/gui/colorlcd/special_functions.cpp @@ -0,0 +1,498 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "special_functions.h" +#include "opentx.h" + +#define SET_DIRTY() storageDirty(functions == g_model.customFn ? EE_MODEL : EE_GENERAL) + +class SpecialFunctionEditPage : public Page { + public: + SpecialFunctionEditPage(CustomFunctionData * functions, uint8_t index) : + Page(functions == g_model.customFn ? ICON_MODEL_SPECIAL_FUNCTIONS : ICON_RADIO_GLOBAL_FUNCTIONS), + functions(functions), + index(index) + { + buildHeader(&header); + buildBody(&body); + } + + protected: + CustomFunctionData * functions; + uint8_t index; + Window * specialFunctionOneWindow = nullptr; + StaticText * headerSF = nullptr; + bool active = false; + + bool isActive() + { + return ((functions == g_model.customFn ? modelFunctionsContext.activeSwitches : globalFunctionsContext.activeSwitches) & ((MASK_CFN_TYPE)1 << index) ? 1 : 0); + } + + void checkEvents() override + { + Page::checkEvents(); + if (active != isActive()) { + invalidate(); + headerSF->setFlags(isActive() ? BOLD|WARNING_COLOR : MENU_TITLE_COLOR); + active = !active; + } + } + + void buildHeader(Window * window) + { + new StaticText(window, { 60, 2, 200, 20 }, functions == g_model.customFn ? STR_MENUCUSTOMFUNC : STR_MENUSPECIALFUNCS, MENU_TITLE_COLOR); + headerSF = new StaticText(window, { 60, 22, 100, 20 }, (functions == g_model.customFn ? "SF" : "GF" ) + std::to_string(index), MENU_TITLE_COLOR); + } + + void updateSpecialFunctionOneWindow(FormField * previousField, FormField * nextField) + { + // SF.one variable part + FormGridLayout grid; + specialFunctionOneWindow->clear(); + + CustomFunctionData * cfn = &functions[index]; + uint8_t func = CFN_FUNC(cfn); + + FormField::setCurrentField(previousField); + + // Func param + switch (func) { + case FUNC_OVERRIDE_CHANNEL: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_CH); + new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 1, MAX_OUTPUT_CHANNELS, GET_SET_VALUE_WITH_OFFSET(CFN_CH_INDEX(cfn), 1)); + grid.nextLine(); + + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); + new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), -100, 100, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_TRAINER: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_TIMER); + new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, 4, GET_SET_DEFAULT(CFN_TIMER_INDEX(cfn))); + grid.nextLine(); + break; + + case FUNC_RESET: + if (CFN_PARAM(cfn) < FUNC_RESET_PARAM_FIRST_TELEM) { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_RESET); + auto choice = new Choice(specialFunctionOneWindow, grid.getFieldSlot(), nullptr, 0, + FUNC_RESET_PARAM_FIRST_TELEM + lastUsedTelemetryIndex(), + GET_SET_DEFAULT(CFN_PARAM(cfn))); + choice->setAvailableHandler(isSourceAvailableInResetSpecialFunction); + choice->setTextHandler([=](int32_t value) { + if (value < FUNC_RESET_PARAM_FIRST_TELEM) + return TEXT_AT_INDEX(STR_VFSWRESET, value); + else + return std::string(g_model.telemetrySensors[value - FUNC_RESET_PARAM_FIRST_TELEM].label, TELEM_LABEL_LEN); + }); + grid.nextLine(); + } + break; + + case FUNC_VOLUME: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_SPEAKER_VOLUME); + new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, MIXSRC_LAST_CH, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_PLAY_SOUND: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), STR_FUNCSOUNDS, 0, + AU_SPECIAL_SOUND_LAST - AU_SPECIAL_SOUND_FIRST - 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_PLAY_TRACK: + case FUNC_BACKGND_MUSIC: + case FUNC_PLAY_SCRIPT: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); + new FileChoice(specialFunctionOneWindow, grid.getFieldSlot(), + func == FUNC_PLAY_SCRIPT ? SCRIPTS_FUNCS_PATH : std::string(SOUNDS_PATH, SOUNDS_PATH_LNG_OFS) + + std::string(currentLanguagePack->id, 2), + func == FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, + sizeof(cfn->play.name), + [=]() { + return std::string(cfn->play.name, ZLEN(cfn->play.name)); + }, + [=](std::string newValue) { + strncpy(cfn->play.name, newValue.c_str(), sizeof(cfn->play.name)); + SET_DIRTY(); + }); + grid.nextLine(); + break; + + case FUNC_SET_TIMER: { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_TIMER); + auto timerchoice = new Choice(specialFunctionOneWindow, grid.getFieldSlot(), nullptr, 0, TIMERS - 1, GET_SET_DEFAULT(CFN_TIMER_INDEX(cfn))); + timerchoice->setTextHandler([](int32_t value) { + return std::string(STR_TIMER) + std::to_string(value + 1); + }); + grid.nextLine(); + + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); + new TimeEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, 9 * 60 * 60 - 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + } + + case FUNC_SET_FAILSAFE: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_MODULE); + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), "\004Int.Ext.", 0, NUM_MODULES - 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_PLAY_VALUE: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); + new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, MIXSRC_LAST_TELEM, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_HAPTIC: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); + new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, 3, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_LOGS: { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); + auto edit = new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, 255, GET_SET_DEFAULT(CFN_PARAM(cfn))); + edit->setDisplayHandler([=](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + lcdDrawNumber(2, 2, CFN_PARAM(cfn), PREC1, sizeof(CFN_PARAM(cfn)), nullptr, "s"); + }); + break; + } + } + + if (HAS_ENABLE_PARAM(func)) { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_ENABLE); + new CheckBox(specialFunctionOneWindow, grid.getFieldSlot(), GET_SET_DEFAULT(CFN_ACTIVE(cfn))); + grid.nextLine(); + } + else if (HAS_REPEAT_PARAM(func)) { // !1x 1x 1s 2s 3s ... + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_REPEAT); + auto repeat = new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(2, 1), -1, 60/CFN_PLAY_REPEAT_MUL, GET_SET_DEFAULT(CFN_PLAY_REPEAT(cfn))); + repeat->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + if (value == 0) + lcdDrawText(3, 0, "1x", flags); + else if (value == CFN_PLAY_REPEAT_NOSTART) + lcdDrawText(3, 0, "!1x", flags); + else + drawNumber(dc, 3, 0, value * CFN_PLAY_REPEAT_MUL, flags, 0, nullptr, "s"); + }); + } + + FormField::link(FormField::getCurrentField(), nextField); + } + + void buildBody(FormWindow * window) + { + // SF.one + FormGridLayout grid; + grid.spacer(8); + + CustomFunctionData * cfn = &functions[index]; + + // Switch + new StaticText(window, grid.getLabelSlot(), STR_SWITCH); + auto switchChoice = new SwitchChoice(window, grid.getFieldSlot(), SWSRC_FIRST, SWSRC_LAST, GET_SET_DEFAULT(CFN_SWITCH(cfn))); + switchChoice->setAvailableHandler([=](int value) { + return (functions == g_model.customFn ? isSwitchAvailable(value, ModelCustomFunctionsContext) + : isSwitchAvailable(value, + GeneralCustomFunctionsContext)); + }); + window->setFirstField(switchChoice); + grid.nextLine(); + + // Function + new StaticText(window, grid.getLabelSlot(), STR_FUNC); + auto functionChoice = new Choice(window, grid.getFieldSlot(), STR_VFSWFUNC, 0, FUNC_MAX - 1, GET_DEFAULT(CFN_FUNC(cfn))); + functionChoice->setSetValueHandler([=](int32_t newValue) { + CFN_FUNC(cfn) = newValue; + CFN_RESET(cfn); + SET_DIRTY(); + updateSpecialFunctionOneWindow(functionChoice, switchChoice); + }); + functionChoice->setAvailableHandler([=](int value) { + return isAssignableFunctionAvailable(value, functions); + }); + grid.nextLine(); + + specialFunctionOneWindow = new Window(window, {0, grid.getWindowHeight(), LCD_W, 0}); + updateSpecialFunctionOneWindow(functionChoice, switchChoice); + grid.addWindow(specialFunctionOneWindow); + } +}; + +static constexpr coord_t line1 = 0; +static constexpr coord_t line2 = 20; +static constexpr coord_t col1 = 20; +static constexpr coord_t col2 = (LCD_W - 100) / 3 + col1; +static constexpr coord_t col3 = ((LCD_W - 100) / 3) * 2 + col1 + 20; + +class SpecialFunctionButton : public Button { + public: + SpecialFunctionButton(FormWindow * parent, const rect_t &rect, CustomFunctionData * functions, uint8_t index) : + Button(parent, rect), + functions(functions), + index(index) + { + const CustomFunctionData * cfn = &functions[index]; + uint8_t func = CFN_FUNC(cfn); + if (!CFN_EMPTY(cfn) && + (HAS_ENABLE_PARAM(func) || HAS_REPEAT_PARAM(func) || (func == FUNC_PLAY_TRACK || func == FUNC_BACKGND_MUSIC || func == FUNC_PLAY_SCRIPT))) { + setHeight(getHeight() + 20); + } + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "SpecialFunctionButton"; + } +#endif + + bool isActive() + { + return ((functions == g_model.customFn ? modelFunctionsContext.activeSwitches : globalFunctionsContext.activeSwitches) & ((MASK_CFN_TYPE)1 << index) ? 1 : 0); + } + + void checkEvents() override + { + Button::checkEvents(); + if (active != isActive()) { + invalidate(); + active = !active; + } + } + + void paintSpecialFunctionLine(BitmapBuffer * dc) + { + // SF.all + const CustomFunctionData * cfn = &functions[index]; + if (functions[index].func == FUNC_OVERRIDE_CHANNEL && functions != g_model.customFn) { + functions[index].func = FUNC_OVERRIDE_CHANNEL + 1; + } + uint8_t func = CFN_FUNC(cfn); + + drawSwitch(dc, col1, line1, CFN_SWITCH(cfn), 0); + if (CFN_EMPTY(cfn)) + return; + + lcdDrawTextAtIndex(col2, line1, STR_VFSWFUNC, func, 0); + int16_t val_min = 0; + int16_t val_max = 255; + + switch (func) { + case FUNC_OVERRIDE_CHANNEL: + drawChn(dc, col1, line2, CFN_CH_INDEX(cfn) + 1, 0); + getMixSrcRange(MIXSRC_FIRST_CH, val_min, val_max); + lcdDrawNumber(col2, line2, CFN_PARAM(cfn)); + break; + + case FUNC_TRAINER: + drawSource(dc, col1, line2, CFN_CH_INDEX(cfn) == 0 ? 0 : MIXSRC_Rud + CFN_CH_INDEX(cfn) - 1); + break; + + case FUNC_RESET: + if (CFN_PARAM(cfn) < FUNC_RESET_PARAM_FIRST_TELEM) { + lcdDrawTextAtIndex(col1, line2, STR_VFSWRESET, CFN_PARAM(cfn)); + } + else { + TelemetrySensor * sensor = &g_model.telemetrySensors[CFN_PARAM(cfn) - FUNC_RESET_PARAM_FIRST_TELEM]; + lcdDrawSizedText(col1, line2, sensor->label, TELEM_LABEL_LEN, ZCHAR); + } + break; + + case FUNC_VOLUME: + drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); + break; + + case FUNC_PLAY_SOUND: + lcdDrawTextAtIndex(col1, line2, STR_FUNCSOUNDS, CFN_PARAM(cfn)); + break; + + case FUNC_PLAY_TRACK: + case FUNC_BACKGND_MUSIC: + case FUNC_PLAY_SCRIPT: + if (ZEXIST(cfn->play.name)) + lcdDrawSizedText(col1, line2, cfn->play.name, sizeof(cfn->play.name), 0); + else + lcdDrawTextAtIndex(col1, line2, STR_VCSWFUNC, 0, 0); + break; + + case FUNC_SET_TIMER: + drawStringWithIndex(col1, line2, STR_TIMER, CFN_TIMER_INDEX(cfn) + 1, 0); + break; + + case FUNC_SET_FAILSAFE: + lcdDrawTextAtIndex(col1, line2, "\004Int.Ext.", CFN_PARAM(cfn), 0); + break; + + case FUNC_PLAY_VALUE: + drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); + break; + + case FUNC_HAPTIC: + lcdDrawNumber(col1, line2, CFN_PARAM(cfn), 0); + break; + + case FUNC_LOGS: + lcdDrawNumber(col3, line1, CFN_PARAM(cfn), PREC1, sizeof(CFN_PARAM(cfn)), nullptr, "s"); + break; + } + if (HAS_ENABLE_PARAM(func)) { + drawCheckBox(col3, line2, CFN_ACTIVE(cfn), 0); + } + else if (HAS_REPEAT_PARAM(func)) { + if (CFN_PLAY_REPEAT(cfn) == 0) { + lcdDrawText(col3, line2, "1x", 0); + } + else if (CFN_PLAY_REPEAT(cfn) == CFN_PLAY_REPEAT_NOSTART) { + lcdDrawText(col3, line2, "!1x", 0); + } + else { + lcdDrawNumber(col3 + 12, line2, CFN_PLAY_REPEAT(cfn) * CFN_PLAY_REPEAT_MUL, 0 | RIGHT, 0, nullptr, "s"); + } + } + } + + virtual void paint(BitmapBuffer * dc) override + { + if (active) + dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, WARNING_COLOR); + paintSpecialFunctionLine(dc); + drawSolidRect(dc, 0, 0, rect.w, rect.h, 2, hasFocus() ? SCROLLBOX_COLOR : CURVE_AXIS_COLOR); + } + + protected: + CustomFunctionData * functions; + uint8_t index; + bool active = false; +}; + +SpecialFunctionsPage::SpecialFunctionsPage(CustomFunctionData * functions) : + PageTab(functions == g_model.customFn ? STR_MENUCUSTOMFUNC : STR_MENUSPECIALFUNCS, + functions == g_model.customFn ? ICON_MODEL_SPECIAL_FUNCTIONS : ICON_RADIO_GLOBAL_FUNCTIONS), + functions(functions) +{ +} + +void SpecialFunctionsPage::rebuild(FormWindow * window, int8_t focusSpecialFunctionIndex) +{ + coord_t scrollPosition = window->getScrollPositionY(); + window->clear(); + build(window, focusSpecialFunctionIndex); + window->setScrollPositionY(scrollPosition); +} + +void SpecialFunctionsPage::editSpecialFunction(FormWindow * window, uint8_t index) +{ + auto editPage = new SpecialFunctionEditPage(functions, index); + editPage->setCloseHandler([=]() { + rebuild(window, index); + }); +} + +void SpecialFunctionsPage::build(FormWindow * window, int8_t focusIndex) +{ + FormGridLayout grid; + grid.spacer(PAGE_PADDING); + grid.setLabelWidth(66); + + Window::clearFocus(); + + char s[5] = "SF"; + if (functions == g_eeGeneral.customFn) + s[0] = 'G'; + + FormField * first = nullptr; + + for (uint8_t i = 0; i < MAX_SPECIAL_FUNCTIONS; i++) { + CustomFunctionData * cfn = &functions[i]; + + strAppendUnsigned(&s[2], i); + new StaticText(window, grid.getLabelSlot(), s, BUTTON_BACKGROUND | CENTERED); + + Button * button = new SpecialFunctionButton(window, grid.getFieldSlot(), functions, i); + if (i == 0) + first = button; + + if (focusIndex == i) + button->setFocus(); + + button->setPressHandler([=]() -> uint8_t { + button->bringToTop(); + Menu * menu = new Menu(); + #warning "TODO When only EDIT available => directy open the edit window without the menu" + menu->addLine(STR_EDIT, [=]() { + editSpecialFunction(window, i); + }); + if (!CFN_EMPTY(cfn)) { + menu->addLine(STR_COPY, [=]() { + clipboard.type = CLIPBOARD_TYPE_CUSTOM_FUNCTION; + clipboard.data.cfn = *cfn; + }); + } + if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_FUNCTION) { + menu->addLine(STR_PASTE, [=]() { + *cfn = clipboard.data.cfn; + SET_DIRTY(); + rebuild(window, i); + }); + } + if (!CFN_EMPTY(cfn) && CFN_EMPTY(&functions[MAX_SPECIAL_FUNCTIONS - 1])) { + menu->addLine(STR_INSERT, [=]() { + memmove(cfn + 1, cfn, (MAX_SPECIAL_FUNCTIONS - i - 1) * sizeof(CustomFunctionData)); + memset(cfn, 0, sizeof(CustomFunctionData)); + SET_DIRTY(); + rebuild(window, i); + }); + } + if (!CFN_EMPTY(cfn)) { + menu->addLine(STR_CLEAR, [=]() { + memset(cfn, 0, sizeof(CustomFunctionData)); + SET_DIRTY(); + }); + } + for (int j = i; j < MAX_SPECIAL_FUNCTIONS; j++) { + if (!CFN_EMPTY(&functions[j])) { + menu->addLine(STR_DELETE, [=]() { + memmove(cfn, cfn + 1, (MAX_SPECIAL_FUNCTIONS - i - 1) * sizeof(CustomFunctionData)); + memset(&functions[MAX_SPECIAL_FUNCTIONS - 1], 0, sizeof(CustomFunctionData)); + SET_DIRTY(); + rebuild(window, i); + }); + break; + } + } + return 0; + }); + + grid.spacer(button->height() + 5); + } + + FormField::link(FormField::getCurrentField(), first); + + grid.nextLine(); + + window->setInnerHeight(grid.getWindowHeight()); +} diff --git a/radio/src/gui/colorlcd/special_functions.h b/radio/src/gui/colorlcd/special_functions.h new file mode 100644 index 000000000..0fa96341c --- /dev/null +++ b/radio/src/gui/colorlcd/special_functions.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _SPECIAL_FUNCTIONS_H +#define _SPECIAL_FUNCTIONS_H + +#include "tabsgroup.h" + +struct CustomFunctionData; + +class SpecialFunctionsPage: public PageTab { + public: + SpecialFunctionsPage(CustomFunctionData * functions); + + void build(FormWindow * window) override + { +#if defined(HARDWARE_TOUCH) + build(window, -1); +#else + build(window, 0); +#endif + } + + protected: + CustomFunctionData * functions; + void build(FormWindow * window, int8_t focusSpecialFunctionIndex); + void rebuild(FormWindow * window, int8_t focusSpecialFunctionIndex); + void editSpecialFunction(FormWindow * window, uint8_t index); +}; + +#endif //_SPECIAL_FUNCTIONS_H diff --git a/radio/src/gui/480x272/splash.cpp b/radio/src/gui/colorlcd/splash.cpp similarity index 100% rename from radio/src/gui/480x272/splash.cpp rename to radio/src/gui/colorlcd/splash.cpp diff --git a/radio/src/gui/480x272/theme.cpp b/radio/src/gui/colorlcd/theme.cpp similarity index 66% rename from radio/src/gui/480x272/theme.cpp rename to radio/src/gui/colorlcd/theme.cpp index 27756859a..8d7abff3e 100644 --- a/radio/src/gui/480x272/theme.cpp +++ b/radio/src/gui/colorlcd/theme.cpp @@ -70,7 +70,7 @@ const char * Theme::getFilePath(const char * filename) const return path; } -void Theme::drawThumb(uint16_t x, uint16_t y, uint32_t flags) +void Theme::drawThumb(coord_t x, coord_t y, uint32_t flags) { #define THUMB_WIDTH 51 #define THUMB_HEIGHT 31 @@ -83,46 +83,49 @@ void Theme::drawThumb(uint16_t x, uint16_t y, uint32_t flags) } } -void Theme::drawBackground() const +void Theme::drawBackground(BitmapBuffer * dc) const { - lcdDrawSolidFilledRect(0, 0, LCD_W, LCD_H, TEXT_BGCOLOR); + dc->drawSolidFilledRect(0, 0, LCD_W, LCD_H, TEXT_BGCOLOR); } void Theme::drawMessageBox(const char * title, const char * text, const char * action, uint32_t type) const { -// if (type == WARNING_TYPE_ALERT) { - drawBackground(); - lcdDrawFilledRect(0, POPUP_Y, LCD_W, POPUP_H, SOLID, TEXT_INVERTED_COLOR | OPACITY(8)); -// } - if (type == WARNING_TYPE_ALERT || type == WARNING_TYPE_ASTERISK) - lcd->drawBitmap(POPUP_X-80, POPUP_Y+12, asterisk); - else if (type == WARNING_TYPE_INFO) - lcd->drawBitmap(POPUP_X-80, POPUP_Y+12, busy); - else - lcd->drawBitmap(POPUP_X-80, POPUP_Y+12, question); - - if (type == WARNING_TYPE_ALERT) { -#if defined(TRANSLATIONS_FR) || defined(TRANSLATIONS_IT) || defined(TRANSLATIONS_CZ) - lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y, STR_WARNING, ALARM_COLOR|DBLSIZE); - lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y+28, title, ALARM_COLOR|DBLSIZE); -#else - lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y, title, ALARM_COLOR|DBLSIZE); - lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y+28, STR_WARNING, ALARM_COLOR|DBLSIZE); -#endif - } - else if (title) { - lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y, title, ALARM_COLOR|DBLSIZE); - } - - if (text) { - lcdDrawText(WARNING_LINE_X, WARNING_INFOLINE_Y, text); - } - - if (action) { - lcdDrawText(WARNING_LINE_X, WARNING_INFOLINE_Y+24, action); - } } +//{ +// //if (flags & MESSAGEBOX_TYPE_ALERT) { +// drawBackground(); +// lcdDrawFilledRect(0, POPUP_Y, LCD_W, POPUP_H, SOLID, TEXT_INVERTED_COLOR | OPACITY(8)); +// //} +// +// if (type == WARNING_TYPE_ALERT || type == WARNING_TYPE_ASTERISK) +// lcd->drawBitmap(POPUP_X-80, POPUP_Y+12, asterisk); +// else if (type == WARNING_TYPE_INFO) +// lcd->drawBitmap(POPUP_X-80, POPUP_Y+12, busy); +// else +// lcd->drawBitmap(POPUP_X-80, POPUP_Y+12, question); +// +// if (type == WARNING_TYPE_ALERT) { +//#if defined(TRANSLATIONS_FR) || defined(TRANSLATIONS_IT) || defined(TRANSLATIONS_CZ) +// lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y, STR_WARNING, ALARM_COLOR|DBLSIZE); +// lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y+28, title, ALARM_COLOR|DBLSIZE); +//#else +// lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y, title, ALARM_COLOR|DBLSIZE); +// lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y+28, STR_WARNING, ALARM_COLOR|DBLSIZE); +//#endif +// } +// else if (title) { +// lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y, title, ALARM_COLOR|DBLSIZE); +// } +// +// if (text) { +// lcdDrawText(WARNING_LINE_X, WARNING_INFOLINE_Y, text); +// } +// +// if (action) { +// lcdDrawText(WARNING_LINE_X, WARNING_INFOLINE_Y+24, action); +// } +//} Theme * getTheme(const char * name) { @@ -132,13 +135,13 @@ Theme * getTheme(const char * name) return (*it); } } - return NULL; + return nullptr; } -void loadTheme(Theme * new_theme) +void loadTheme(Theme * newTheme) { - TRACE("load theme %s", new_theme->getName()); - theme = new_theme; + TRACE("load theme %s", newTheme->getName()); + theme = newTheme; theme->load(); } @@ -147,9 +150,9 @@ void loadTheme() char name[THEME_NAME_LEN + 1]; memset(name, 0, sizeof(name)); strncpy(name, g_eeGeneral.themeName, THEME_NAME_LEN); - Theme * new_theme = getTheme(name); - if (new_theme) { - loadTheme(new_theme); + Theme * newTheme = getTheme(name); + if (newTheme) { + loadTheme(newTheme); } else { theme->load(); diff --git a/radio/src/gui/480x272/theme.h b/radio/src/gui/colorlcd/theme.h similarity index 68% rename from radio/src/gui/480x272/theme.h rename to radio/src/gui/colorlcd/theme.h index 8fb71c2f9..76da75558 100644 --- a/radio/src/gui/480x272/theme.h +++ b/radio/src/gui/colorlcd/theme.h @@ -22,8 +22,11 @@ #define _THEME_H_ #include +#include +#include "zone.h" class BitmapBuffer; +class PageTab; #define MAX_THEME_OPTIONS 5 @@ -37,10 +40,10 @@ class Theme ZoneOptionValue options[MAX_THEME_OPTIONS]; }; - Theme(const char * name, const ZoneOption * options=NULL): + Theme(const char * name, const ZoneOption * options = nullptr): name(name), options(options), - thumb(NULL) + thumb(nullptr) { registerTheme(this); } @@ -52,7 +55,7 @@ class Theme const char * getFilePath(const char * filename) const; - void drawThumb(uint16_t x, uint16_t y, uint32_t flags); + void drawThumb(coord_t x, coord_t y, uint32_t flags); inline const ZoneOption * getOptions() const { @@ -69,18 +72,34 @@ class Theme virtual void load() const; - virtual void drawBackground() const; + virtual void drawBackground(BitmapBuffer * dc) const; - virtual void drawTopbarBackground(uint8_t icon) const = 0; + virtual const BitmapBuffer * getIconButtonBitmap(uint8_t index, bool selected) const + { + TRACE("No bitmap for icon %d in this theme", index); + return nullptr; + } - virtual void drawMenuIcon(uint8_t index, uint8_t position, bool selected) const { } + virtual const BitmapBuffer * getIconMask(uint8_t index) const + { + TRACE("No mask for icon %d in this theme", index); + return nullptr; + } + + virtual void drawMenuBackground(BitmapBuffer * dc, uint8_t icon, const char *title) const = 0; + + virtual void drawMenuHeader(BitmapBuffer * dc, std::vector & tabs, uint8_t currentIndex) const = 0; virtual void drawMessageBox(const char * title, const char * text, const char * action, uint32_t flags) const; + virtual void drawProgressBar(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h, int value) const = 0; + protected: const char * name; const ZoneOption * options; BitmapBuffer * thumb; + + public: static const BitmapBuffer * asterisk; static const BitmapBuffer * question; static const BitmapBuffer * busy; diff --git a/radio/src/gui/colorlcd/themes/flysky.cpp b/radio/src/gui/colorlcd/themes/flysky.cpp new file mode 100644 index 000000000..18d6d41e3 --- /dev/null +++ b/radio/src/gui/colorlcd/themes/flysky.cpp @@ -0,0 +1,436 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "tabsgroup.h" + +const ZoneOption OPTIONS_THEME_DEFAULT[] = { + { STR_BACKGROUND_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(WHITE) }, + { STR_MAIN_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(RED) }, + { nullptr, ZoneOption::Bool } +}; + +class FlyskyTheme: public Theme +{ + public: + FlyskyTheme(): + Theme("FlySky", OPTIONS_THEME_DEFAULT) + { + loadColors(); + } + + void loadColors() const + { + TRACE("Load FlySky theme colors"); + + lcdColorTable[TEXT_COLOR_INDEX] = BLACK; + lcdColorTable[TEXT_BGCOLOR_INDEX] = WHITE; + lcdColorTable[TEXT_INVERTED_COLOR_INDEX] = WHITE; + lcdColorTable[TEXT_INVERTED_BGCOLOR_INDEX] = RED; + lcdColorTable[TEXT_STATUSBAR_COLOR_INDEX] = WHITE; + lcdColorTable[LINE_COLOR_INDEX] = GREY; + lcdColorTable[SCROLLBOX_COLOR_INDEX] = RED; + lcdColorTable[MENU_TITLE_BGCOLOR_INDEX] = DARKGREY; + lcdColorTable[MENU_TITLE_COLOR_INDEX] = WHITE; + lcdColorTable[MENU_TITLE_DISABLE_COLOR_INDEX] = RGB(GET_RED(RED)>>1, GET_GREEN(RED)>>1, GET_BLUE(RED)>>1); + lcdColorTable[HEADER_COLOR_INDEX] = DARKGREY; + lcdColorTable[ALARM_COLOR_INDEX] = RED; + lcdColorTable[WARNING_COLOR_INDEX] = YELLOW; + lcdColorTable[TEXT_DISABLE_COLOR_INDEX] = GREY; + lcdColorTable[CURVE_AXIS_COLOR_INDEX] = LIGHTGREY; + lcdColorTable[CURVE_COLOR_INDEX] = RED; + lcdColorTable[CURVE_CURSOR_COLOR_INDEX] = RED; + lcdColorTable[TRIM_BGCOLOR_INDEX] = RED; + lcdColorTable[TRIM_SHADOW_COLOR_INDEX] = BLACK; + lcdColorTable[MAINVIEW_PANES_COLOR_INDEX] = WHITE; + lcdColorTable[MAINVIEW_GRAPHICS_COLOR_INDEX] = RED; + + // The menu header (top bar) + lcdColorTable[HEADER_BGCOLOR_INDEX] = DARKBLUE; + lcdColorTable[HEADER_ICON_BGCOLOR_INDEX] = RGB(0, 71, 157); + lcdColorTable[HEADER_CURRENT_BGCOLOR_INDEX] = RGB(2, 114, 250); + + // The menu title + lcdColorTable[TITLE_BGCOLOR_INDEX] = RGB(0, 71, 157); + + lcdColorTable[OVERLAY_COLOR_INDEX] = BLACK; + lcdColorTable[BARGRAPH1_COLOR_INDEX] = RED; + lcdColorTable[BARGRAPH2_COLOR_INDEX] = RGB(167, 167, 167); + lcdColorTable[BARGRAPH_BGCOLOR_INDEX] = RGB(222, 222, 222); + } + + void loadMenuIcon(uint8_t index, const char * filename, uint32_t color=MENU_TITLE_COLOR) const + { + TRACE("loadMenuIcon %s", getThemePath(filename)); + + BitmapBuffer * mask = BitmapBuffer::loadMask(getThemePath(filename)); + if (mask) { + delete iconMask[index]; + iconMask[index] = mask; + + uint8_t width = mask->getWidth(); + uint8_t height = mask->getHeight(); + + if (0) { + delete menuIconNormal[index]; + menuIconNormal[index] = new BitmapBuffer(BMP_RGB565, width, height); + if (menuIconNormal[index]) { + menuIconNormal[index]->clear(HEADER_BGCOLOR); + menuIconNormal[index]->drawMask(0, 0, mask, color); + } + + delete menuIconSelected[index]; + menuIconSelected[index] = new BitmapBuffer(BMP_RGB565, width, height); + if (menuIconSelected[index]) { + menuIconSelected[index]->clear(HEADER_CURRENT_BGCOLOR); + menuIconSelected[index]->drawMask(0, 0, mask, color); + } + } + else { + delete menuIconNormal[index]; + menuIconNormal[index] = new BitmapBuffer(BMP_RGB565, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH); + if (menuIconNormal[index]) { + menuIconNormal[index]->clear(HEADER_BGCOLOR); + menuIconNormal[index]->drawMask((MENU_HEADER_BUTTON_WIDTH-width)/2, (MENU_HEADER_BUTTON_WIDTH-width)/2, mask, color); + } + + delete menuIconSelected[index]; + menuIconSelected[index] = new BitmapBuffer(BMP_RGB565, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH); + if (menuIconSelected[index]) { + if (index < ICON_RADIO) + menuIconSelected[index]->clear(HEADER_CURRENT_BGCOLOR /*HEADER_LOGO_BGCOLOR*/); + else + menuIconSelected[index]->clear(HEADER_CURRENT_BGCOLOR); + menuIconSelected[index]->drawMask((MENU_HEADER_BUTTON_WIDTH-width)/2, (MENU_HEADER_BUTTON_WIDTH-height)/2, mask, color); + } + } + } + } + + void loadIcons() const + { +#if defined(LOG_TELEMETRY) || defined(WATCHDOG_DISABLED) + loadMenuIcon(ICON_OPENTX, "mask_opentx_testmode.png", TEXT_COLOR); +#else + loadMenuIcon(ICON_OPENTX, "mask_opentx.png"); +#endif +#if defined(HARDWARE_TOUCH) + loadMenuIcon(ICON_NEXT, "mask_next.png"); + loadMenuIcon(ICON_BACK, "mask_back.png"); +#endif + loadMenuIcon(ICON_RADIO, "mask_menu_radio.png"); + loadMenuIcon(ICON_RADIO_SETUP, "mask_radio_setup.png"); + loadMenuIcon(ICON_RADIO_SD_MANAGER, "mask_radio_sd_browser.png"); + loadMenuIcon(ICON_RADIO_TOOLS, "mask_radio_tools.png"); + loadMenuIcon(ICON_RADIO_GLOBAL_FUNCTIONS, "mask_radio_global_functions.png"); + loadMenuIcon(ICON_RADIO_TRAINER, "mask_radio_trainer.png"); + loadMenuIcon(ICON_RADIO_HARDWARE, "mask_radio_hardware.png"); + loadMenuIcon(ICON_RADIO_CALIBRATION, "mask_radio_calibration.png"); + loadMenuIcon(ICON_RADIO_VERSION, "mask_radio_version.png"); + loadMenuIcon(ICON_MODEL, "mask_menu_model.png"); + loadMenuIcon(ICON_MODEL_SETUP, "mask_model_setup.png"); + loadMenuIcon(ICON_MODEL_HELI, "mask_model_heli.png"); + loadMenuIcon(ICON_MODEL_FLIGHT_MODES, "mask_model_flight_modes.png"); + loadMenuIcon(ICON_MODEL_INPUTS, "mask_model_inputs.png"); + loadMenuIcon(ICON_MODEL_MIXER, "mask_model_mixer.png"); + loadMenuIcon(ICON_MODEL_OUTPUTS, "mask_model_outputs.png"); + loadMenuIcon(ICON_MODEL_CURVES, "mask_model_curves.png"); + loadMenuIcon(ICON_MODEL_GVARS, "mask_model_gvars.png"); + loadMenuIcon(ICON_MODEL_LOGICAL_SWITCHES, "mask_model_logical_switches.png"); + loadMenuIcon(ICON_MODEL_SPECIAL_FUNCTIONS, "mask_model_special_functions.png"); + loadMenuIcon(ICON_MODEL_LUA_SCRIPTS, "mask_model_lua_scripts.png"); + loadMenuIcon(ICON_MODEL_TELEMETRY, "mask_model_telemetry.png"); + loadMenuIcon(ICON_STATS, "mask_menu_stats.png"); + loadMenuIcon(ICON_STATS_THROTTLE_GRAPH, "mask_stats_throttle_graph.png"); + loadMenuIcon(ICON_STATS_TIMERS, "mask_stats_timers.png"); + loadMenuIcon(ICON_STATS_ANALOGS, "mask_stats_analogs.png"); + loadMenuIcon(ICON_STATS_DEBUG, "mask_stats_debug.png"); + loadMenuIcon(ICON_THEME, "mask_menu_theme.png"); + loadMenuIcon(ICON_THEME_SETUP, "mask_theme_setup.png"); + loadMenuIcon(ICON_THEME_VIEW1, "mask_theme_view1.png"); + loadMenuIcon(ICON_THEME_VIEW2, "mask_theme_view2.png"); + loadMenuIcon(ICON_THEME_VIEW3, "mask_theme_view3.png"); + loadMenuIcon(ICON_THEME_VIEW4, "mask_theme_view4.png"); + loadMenuIcon(ICON_THEME_VIEW5, "mask_theme_view5.png"); + loadMenuIcon(ICON_THEME_ADD_VIEW, "mask_theme_add_view.png"); + loadMenuIcon(ICON_MONITOR, "mask_monitor.png"); + loadMenuIcon(ICON_MONITOR_CHANNELS1, "mask_monitor_channels1.png"); + loadMenuIcon(ICON_MONITOR_CHANNELS2, "mask_monitor_channels2.png"); + loadMenuIcon(ICON_MONITOR_CHANNELS3, "mask_monitor_channels3.png"); + loadMenuIcon(ICON_MONITOR_CHANNELS4, "mask_monitor_channels4.png"); + loadMenuIcon(ICON_MONITOR_LOGICAL_SWITCHES, "mask_monitor_logsw.png"); + + BitmapBuffer * background = BitmapBuffer::loadMask(getThemePath("mask_currentmenu_bg.png")); + BitmapBuffer * shadow = BitmapBuffer::loadMask(getThemePath("mask_currentmenu_shadow.png")); + BitmapBuffer * dot = BitmapBuffer::loadMask(getThemePath("mask_currentmenu_dot.png")); + + if (!currentMenuBackground) { + currentMenuBackground = new BitmapBuffer(BMP_RGB565, 36, 53); + } + if (currentMenuBackground) { + currentMenuBackground->drawSolidFilledRect(0, 0, currentMenuBackground->getWidth(), MENU_HEADER_HEIGHT, HEADER_BGCOLOR); + currentMenuBackground->drawSolidFilledRect(0, MENU_HEADER_HEIGHT, currentMenuBackground->getWidth(), MENU_TITLE_TOP - MENU_HEADER_HEIGHT, TEXT_BGCOLOR); + currentMenuBackground->drawSolidFilledRect(0, MENU_TITLE_TOP, currentMenuBackground->getWidth(), currentMenuBackground->getHeight() - MENU_TITLE_TOP, TITLE_BGCOLOR); + currentMenuBackground->drawMask(0, 0, background, HEADER_CURRENT_BGCOLOR); + currentMenuBackground->drawMask(0, 0, shadow, TRIM_SHADOW_COLOR); + currentMenuBackground->drawMask(10, 39, dot, MENU_TITLE_COLOR); + } + + delete topleftBitmap; + topleftBitmap = BitmapBuffer::loadMaskOnBackground("topleft.png", TITLE_BGCOLOR, HEADER_BGCOLOR); + + delete background; + delete shadow; + delete dot; + } + + void loadThemeBitmaps() const + { + // Calibration screen + delete calibStick; + calibStick = BitmapBuffer::load(getThemePath("stick_pointer.png")); + + delete calibStickBackground; + calibStickBackground = BitmapBuffer::load(getThemePath("stick_background.png")); + + delete calibTrackpBackground; + calibTrackpBackground = BitmapBuffer::load(getThemePath("trackp_background.png")); + + delete calibRadioPict; + calibRadioPict = BitmapBuffer::load(getThemePath("NV14.bmp")); + + // Model Selection screen + delete modelselIconBitmap; + modelselIconBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_iconback.png", TITLE_BGCOLOR, TEXT_BGCOLOR); + if (modelselIconBitmap) { + BitmapBuffer * bitmap = BitmapBuffer::load(getThemePath("modelsel/icon_default.png")); + modelselIconBitmap->drawBitmap(20, 8, bitmap); + delete bitmap; + } + + delete modelselSdFreeBitmap; + modelselSdFreeBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_sdfree.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete modelselModelQtyBitmap; + modelselModelQtyBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_modelqty.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete modelselModelNameBitmap; + modelselModelNameBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_modelname.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete modelselModelMoveBackground; + modelselModelMoveBackground = BitmapBuffer::loadMask(getThemePath("modelsel/mask_moveback.png")); + + delete modelselModelMoveIcon; + modelselModelMoveIcon = BitmapBuffer::loadMask(getThemePath("modelsel/mask_moveico.png")); + + delete modelselWizardBackground; + modelselWizardBackground = BitmapBuffer::load(getThemePath("wizard/background.png")); + + // Channels monitor screen + delete chanMonLockedBitmap; + chanMonLockedBitmap = BitmapBuffer::loadMaskOnBackground("mask_monitor_lockch.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete chanMonInvertedBitmap; + chanMonInvertedBitmap = BitmapBuffer::loadMaskOnBackground("mask_monitor_inver.png", TEXT_COLOR, TEXT_BGCOLOR); + + // Mixer setup screen + delete mixerSetupMixerBitmap; + mixerSetupMixerBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_mixer.png", MENU_TITLE_COLOR, HEADER_BGCOLOR); + + delete mixerSetupToBitmap; + mixerSetupToBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_to.png", MENU_TITLE_COLOR, HEADER_BGCOLOR); + + delete mixerSetupOutputBitmap; + mixerSetupOutputBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_output.png", MENU_TITLE_COLOR, HEADER_BGCOLOR); + + delete mixerSetupAddBitmap; + mixerSetupAddBitmap = BitmapBuffer::loadMaskOnBackground("mask_mplex_add.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete mixerSetupMultiBitmap; + mixerSetupMultiBitmap = BitmapBuffer::loadMaskOnBackground("mask_mplex_multi.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete mixerSetupReplaceBitmap; + mixerSetupReplaceBitmap = BitmapBuffer::loadMaskOnBackground("mask_mplex_replace.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete mixerSetupLabelBitmap; + mixerSetupLabelBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_label.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete mixerSetupCurveBitmap; + mixerSetupCurveBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_curve.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete mixerSetupSwitchIcon; + mixerSetupSwitchIcon = BitmapBuffer::loadMask(getThemePath("mask_textline_switch.png")); + + delete mixerSetupFlightmodeIcon; + mixerSetupFlightmodeIcon = BitmapBuffer::loadMask(getThemePath("mask_textline_fm.png")); + + delete mixerSetupSlowBitmap; + mixerSetupSlowBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_slow.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete mixerSetupDelayBitmap; + mixerSetupDelayBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_delay.png", TEXT_COLOR, TEXT_BGCOLOR); + + delete mixerSetupDelaySlowBitmap; + mixerSetupDelaySlowBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_delayslow.png", TEXT_COLOR, TEXT_BGCOLOR); + } + + virtual void load() const override + { + loadColors(); + Theme::load(); + if (!backgroundBitmap) { + backgroundBitmap = BitmapBuffer::load(getThemePath("background.png")); + } + update(); + } + + virtual void update() const override + { + TRACE("TODO THEME::UPDATE()"); + +#if 0 + uint32_t color = g_eeGeneral.themeData.options[1].unsignedValue; + uint32_t bg_color = UNEXPECTED_SHUTDOWN() ? WHITE : g_eeGeneral.themeData.options[0].unsignedValue; + + lcdColorTable[TEXT_BGCOLOR_INDEX] = bg_color; + lcdColorTable[TEXT_INVERTED_BGCOLOR_INDEX] = color; + lcdColorTable[SCROLLBOX_COLOR_INDEX] = color; + lcdColorTable[CURVE_COLOR_INDEX] = color; + lcdColorTable[CURVE_CURSOR_COLOR_INDEX] = color; + lcdColorTable[TITLE_BGCOLOR_INDEX] = color; + lcdColorTable[MENU_TITLE_DISABLE_COLOR_INDEX] = + RGB(GET_RED(color)>>1, GET_GREEN(color)>>1, GET_BLUE(color)>>1); + lcdColorTable[TRIM_BGCOLOR_INDEX] = color; + lcdColorTable[MAINVIEW_GRAPHICS_COLOR_INDEX] = color; + #define DARKER(x) ((x * 70) / 100) + lcdColorTable[HEADER_BGCOLOR_INDEX] = RGB(DARKER(GET_RED(color)), DARKER(GET_GREEN(color)), DARKER(GET_BLUE(color))); + lcdColorTable[HEADER_ICON_BGCOLOR_INDEX] = color; + lcdColorTable[HEADER_CURRENT_BGCOLOR_INDEX] = color; +#endif + loadIcons(); + loadThemeBitmaps(); + } + + void drawBackground(BitmapBuffer * dc) const override + { + if (backgroundBitmap) { + dc->drawBitmap(0, 0, backgroundBitmap); + } + else { + lcdSetColor(g_eeGeneral.themeData.options[0].unsignedValue); + dc->drawSolidFilledRect(0, 0, LCD_W, LCD_H, CUSTOM_COLOR); + } + } + + void drawMenuBackground(BitmapBuffer * dc, uint8_t icon, const char * title) const override + { + dc->drawSolidFilledRect(0, 0, LCD_W, MENU_HEADER_HEIGHT, HEADER_BGCOLOR); + + if (topleftBitmap) { + dc->drawBitmap(0, 0, topleftBitmap); + uint16_t width = topleftBitmap->getWidth(); + dc->drawSolidFilledRect(width, 0, LCD_W-width, MENU_HEADER_HEIGHT, HEADER_BGCOLOR); + } + else { + dc->drawSolidFilledRect(0, 0, LCD_W, MENU_HEADER_HEIGHT, HEADER_BGCOLOR); + } + + if (icon == ICON_OPENTX) + dc->drawBitmap(4, 10, menuIconSelected[ICON_OPENTX]); + else + dc->drawBitmap(5, 7, menuIconSelected[icon]); + + dc->drawSolidFilledRect(0, MENU_HEADER_HEIGHT, LCD_W, MENU_TITLE_TOP - MENU_HEADER_HEIGHT, TEXT_BGCOLOR); // the white separation line + dc->drawSolidFilledRect(0, MENU_TITLE_TOP, LCD_W, MENU_TITLE_HEIGHT, TITLE_BGCOLOR); // the title line background + if (title) { + lcdDrawText(MENUS_MARGIN_LEFT, MENU_TITLE_TOP, title, MENU_TITLE_COLOR); + } + } + + const BitmapBuffer * getIconMask(uint8_t index) const override + { + return iconMask[index]; + } + + const BitmapBuffer * getIconButtonBitmap(uint8_t index, bool selected) const override + { + return selected ? menuIconSelected[index] : menuIconNormal[index]; + } + + void drawMenuHeader(BitmapBuffer * dc, std::vector & tabs, uint8_t currentIndex) const override + { + uint8_t padding_left = 0; + + dc->drawSolidFilledRect(0, 0, padding_left, MENU_HEADER_BUTTON_WIDTH, HEADER_BGCOLOR); + for (unsigned i=0; idrawBitmap(padding_left + i*MENU_HEADER_BUTTON_WIDTH, 0, theme->getIconButtonBitmap(tabs[i]->getIcon(), currentIndex == i)); + } + coord_t x = padding_left + MENU_HEADER_BUTTON_WIDTH * tabs.size(); +// coord_t w = width() - x; +// if (w > 0) { +// dc->drawSolidFilledRect(x, 0, w, MENU_HEADER_BUTTON_WIDTH, HEADER_BGCOLOR); +// } + } + + void drawMenuDatetime(BitmapBuffer * dc) const + { + dc->drawSolidVerticalLine(DATETIME_SEPARATOR_X, 7, 31, TEXT_INVERTED_COLOR); + + struct gtm t; + gettime(&t); + char str[10]; + const char * const STR_MONTHS[] = TR_MONTHS; + sprintf(str, "%d %s", t.tm_mday, STR_MONTHS[t.tm_mon]); + lcdDrawText(DATETIME_MIDDLE, DATETIME_LINE1, str, SMLSIZE|TEXT_INVERTED_COLOR|CENTERED); + + getTimerString(str, getValue(MIXSRC_TX_TIME)); + lcdDrawText(DATETIME_MIDDLE, DATETIME_LINE2, str, SMLSIZE|TEXT_INVERTED_COLOR|CENTERED); + } + + void drawProgressBar(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h, int value) const override + { + drawSolidRect(dc, x, y, w, h, 1, TEXT_COLOR); + if (value > 0) { + int width = (w * value) / 100; + dc->drawSolidFilledRect(x + 2, y + 2, width - 4, h - 4, SCROLLBOX_COLOR); + } + } + + protected: + static const BitmapBuffer * backgroundBitmap; + static BitmapBuffer * topleftBitmap; + static BitmapBuffer * menuIconNormal[MENUS_ICONS_COUNT]; + static BitmapBuffer * menuIconSelected[MENUS_ICONS_COUNT]; + static BitmapBuffer * iconMask[MENUS_ICONS_COUNT]; + static BitmapBuffer * currentMenuBackground; +}; + +const BitmapBuffer * FlyskyTheme::backgroundBitmap = nullptr; +BitmapBuffer * FlyskyTheme::topleftBitmap = nullptr; +BitmapBuffer * FlyskyTheme::iconMask[MENUS_ICONS_COUNT] = { nullptr }; +BitmapBuffer * FlyskyTheme::menuIconNormal[MENUS_ICONS_COUNT] = { nullptr }; +BitmapBuffer * FlyskyTheme::menuIconSelected[MENUS_ICONS_COUNT] = { nullptr }; +BitmapBuffer * FlyskyTheme::currentMenuBackground = nullptr; + +FlyskyTheme flyskyTheme; + +#if defined(PCBFLYSKY) +Theme * theme = &flyskyTheme; +#endif \ No newline at end of file diff --git a/radio/src/gui/480x272/themes/default.cpp b/radio/src/gui/colorlcd/themes/frsky.cpp similarity index 74% rename from radio/src/gui/480x272/themes/default.cpp rename to radio/src/gui/colorlcd/themes/frsky.cpp index 0183a601c..4c7328f53 100644 --- a/radio/src/gui/480x272/themes/default.cpp +++ b/radio/src/gui/colorlcd/themes/frsky.cpp @@ -19,24 +19,26 @@ */ #include "opentx.h" +#include "tabsgroup.h" const ZoneOption OPTIONS_THEME_DEFAULT[] = { { STR_BACKGROUND_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(WHITE) }, { STR_MAIN_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(RED) }, - { NULL, ZoneOption::Bool } + { nullptr, ZoneOption::Bool } }; -class DefaultTheme: public Theme +class FrskyTheme: public Theme { public: - DefaultTheme(): - Theme("Default", OPTIONS_THEME_DEFAULT) + FrskyTheme(): + Theme("FrSky", OPTIONS_THEME_DEFAULT) { loadColors(); } void loadColors() const { + TRACE("Load FrSky theme colors"); lcdColorTable[TEXT_COLOR_INDEX] = BLACK; lcdColorTable[TEXT_BGCOLOR_INDEX] = WHITE; lcdColorTable[TEXT_INVERTED_COLOR_INDEX] = WHITE; @@ -46,8 +48,7 @@ class DefaultTheme: public Theme lcdColorTable[SCROLLBOX_COLOR_INDEX] = RED; lcdColorTable[MENU_TITLE_BGCOLOR_INDEX] = DARKGREY; lcdColorTable[MENU_TITLE_COLOR_INDEX] = WHITE; - lcdColorTable[MENU_TITLE_DISABLE_COLOR_INDEX] = - RGB(GET_RED(RED)>>1, GET_GREEN(RED)>>1, GET_BLUE(RED)>>1); + lcdColorTable[MENU_TITLE_DISABLE_COLOR_INDEX] = RGB(GET_RED(RED)>>1, GET_GREEN(RED)>>1, GET_BLUE(RED)>>1); lcdColorTable[HEADER_COLOR_INDEX] = DARKGREY; lcdColorTable[ALARM_COLOR_INDEX] = RED; lcdColorTable[WARNING_COLOR_INDEX] = YELLOW; @@ -71,21 +72,25 @@ class DefaultTheme: public Theme void loadMenuIcon(uint8_t index, const char * filename, uint32_t color=MENU_TITLE_COLOR) const { + TRACE("loadMenuIcon %s", getThemePath(filename)); BitmapBuffer * mask = BitmapBuffer::loadMask(getThemePath(filename)); if (mask) { + delete iconMask[index]; + iconMask[index] = mask; + delete menuIconNormal[index]; menuIconNormal[index] = new BitmapBuffer(BMP_RGB565, mask->getWidth(), mask->getHeight()); if (menuIconNormal[index]) { menuIconNormal[index]->clear(HEADER_BGCOLOR); menuIconNormal[index]->drawMask(0, 0, mask, color); } + delete menuIconSelected[index]; menuIconSelected[index] = new BitmapBuffer(BMP_RGB565, mask->getWidth(), mask->getHeight()); if (menuIconSelected[index]) { menuIconSelected[index]->clear(HEADER_CURRENT_BGCOLOR); menuIconSelected[index]->drawMask(0, 0, mask, color); } - delete mask; } } @@ -95,6 +100,10 @@ class DefaultTheme: public Theme loadMenuIcon(ICON_OPENTX, "mask_opentx_testmode.png", TEXT_COLOR); #else loadMenuIcon(ICON_OPENTX, "mask_opentx.png"); +#endif +#if defined(HARDWARE_TOUCH) + loadMenuIcon(ICON_NEXT, "mask_next.png"); + loadMenuIcon(ICON_BACK, "mask_back.png"); #endif loadMenuIcon(ICON_RADIO, "mask_menu_radio.png"); loadMenuIcon(ICON_RADIO_SETUP, "mask_radio_setup.png"); @@ -175,16 +184,16 @@ class DefaultTheme: public Theme delete calibTrackpBackground; calibTrackpBackground = BitmapBuffer::load(getThemePath("trackp_background.png")); - delete calibHorus; + delete calibRadioPict; #if defined(PCBX10) if(STICKS_PWM_ENABLED()) { - calibHorus = BitmapBuffer::load(getThemePath("X10S.bmp")); + calibRadioPict = BitmapBuffer::load(getThemePath("X10S.bmp")); } else { - calibHorus = BitmapBuffer::load(getThemePath("X10.bmp")); + calibRadioPict = BitmapBuffer::load(getThemePath("X10.bmp")); } #else - calibHorus = BitmapBuffer::load(getThemePath("horus.bmp")); + calibRadioPict = BitmapBuffer::load(getThemePath("horus.bmp")); #endif // Model Selection screen @@ -246,11 +255,11 @@ class DefaultTheme: public Theme delete mixerSetupCurveBitmap; mixerSetupCurveBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_curve.png", TEXT_COLOR, TEXT_BGCOLOR); - delete mixerSetupSwitchBitmap; - mixerSetupSwitchBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_switch.png", TEXT_COLOR, TEXT_BGCOLOR); + delete mixerSetupSwitchIcon; + mixerSetupSwitchIcon = BitmapBuffer::loadMask(getThemePath("mask_textline_switch.png")); - delete mixerSetupFlightmodeBitmap; - mixerSetupFlightmodeBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_fm.png", TEXT_COLOR, TEXT_BGCOLOR); + delete mixerSetupFlightmodeIcon; + mixerSetupFlightmodeIcon = BitmapBuffer::loadMask(getThemePath("mask_textline_fm.png")); delete mixerSetupSlowBitmap; mixerSetupSlowBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_slow.png", TEXT_COLOR, TEXT_BGCOLOR); @@ -262,7 +271,7 @@ class DefaultTheme: public Theme mixerSetupDelaySlowBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_delayslow.png", TEXT_COLOR, TEXT_BGCOLOR); } - virtual void load() const + virtual void load() const override { loadColors(); Theme::load(); @@ -272,8 +281,10 @@ class DefaultTheme: public Theme update(); } - virtual void update() const + virtual void update() const override { + TRACE("TODO THEME::UPDATE()"); +#if 0 uint32_t color = g_eeGeneral.themeData.options[1].unsignedValue; uint32_t bg_color = UNEXPECTED_SHUTDOWN() ? WHITE : g_eeGeneral.themeData.options[0].unsignedValue; @@ -291,49 +302,91 @@ class DefaultTheme: public Theme lcdColorTable[HEADER_BGCOLOR_INDEX] = RGB(DARKER(GET_RED(color)), DARKER(GET_GREEN(color)), DARKER(GET_BLUE(color))); lcdColorTable[HEADER_ICON_BGCOLOR_INDEX] = color; lcdColorTable[HEADER_CURRENT_BGCOLOR_INDEX] = color; +#endif loadIcons(); loadThemeBitmaps(); - loadFontCache(); } - virtual void drawBackground() const + void drawBackground(BitmapBuffer * dc) const override { if (backgroundBitmap) { - lcd->drawBitmap(0, 0, backgroundBitmap); + dc->drawBitmap(0, 0, backgroundBitmap); } else { lcdSetColor(g_eeGeneral.themeData.options[0].unsignedValue); - lcdDrawSolidFilledRect(0, 0, LCD_W, LCD_H, CUSTOM_COLOR); + dc->drawSolidFilledRect(0, 0, LCD_W, LCD_H, CUSTOM_COLOR); } } - virtual void drawTopbarBackground(uint8_t icon) const + void drawMenuBackground(BitmapBuffer * dc, uint8_t icon, const char * title) const override { + dc->drawSolidFilledRect(0, 0, LCD_W, MENU_HEADER_HEIGHT, HEADER_BGCOLOR); + if (topleftBitmap) { - lcd->drawBitmap(0, 0, topleftBitmap); + dc->drawBitmap(0, 0, topleftBitmap); uint16_t width = topleftBitmap->getWidth(); - lcd->drawSolidFilledRect(width, 0, LCD_W-width, MENU_HEADER_HEIGHT, HEADER_BGCOLOR); + dc->drawSolidFilledRect(width, 0, LCD_W-width, MENU_HEADER_HEIGHT, HEADER_BGCOLOR); } else { - lcd->drawSolidFilledRect(0, 0, LCD_W, MENU_HEADER_HEIGHT, HEADER_BGCOLOR); + dc->drawSolidFilledRect(0, 0, LCD_W, MENU_HEADER_HEIGHT, HEADER_BGCOLOR); } if (icon == ICON_OPENTX) - lcd->drawBitmap(4, 10, menuIconSelected[ICON_OPENTX]); + dc->drawBitmap(4, 10, menuIconSelected[ICON_OPENTX]); else - lcd->drawBitmap(5, 7, menuIconSelected[icon]); + dc->drawBitmap(5, 7, menuIconSelected[icon]); - drawTopbarDatetime(); + dc->drawSolidFilledRect(0, MENU_HEADER_HEIGHT, LCD_W, MENU_TITLE_TOP - MENU_HEADER_HEIGHT, TEXT_BGCOLOR); // the white separation line + dc->drawSolidFilledRect(0, MENU_TITLE_TOP, LCD_W, MENU_TITLE_HEIGHT, TITLE_BGCOLOR); // the title line background + if (title) { + lcdDrawText(MENUS_MARGIN_LEFT, MENU_TITLE_TOP + 1, title, MENU_TITLE_COLOR); + } + + drawMenuDatetime(dc); } - virtual void drawMenuIcon(uint8_t index, uint8_t position, bool selected) const + const BitmapBuffer * getIconMask(uint8_t index) const override { - if (selected) { - lcd->drawBitmap(58+position*MENU_ICONS_SPACING-10, 0, currentMenuBackground); - lcd->drawBitmap(50+position*MENU_ICONS_SPACING, 7, menuIconSelected[index], MENU_TITLE_COLOR); + return iconMask[index]; + } + + const BitmapBuffer * getIconButtonBitmap(uint8_t index, bool selected) const override + { + return selected ? menuIconSelected[index] : menuIconNormal[index]; + } + + void drawMenuHeader(BitmapBuffer * dc, std::vector & tabs, uint8_t currentIndex) const override + { + for (unsigned index = 0; index < tabs.size(); index++) { + if (index != currentIndex) { + dc->drawBitmap(index*MENU_HEADER_BUTTON_WIDTH + 2, 7, menuIconNormal[tabs[index]->getIcon()]); + } } - else { - lcd->drawBitmap(50+position*MENU_ICONS_SPACING, 7, menuIconNormal[index], MENU_TITLE_COLOR); + dc->drawBitmap(currentIndex*MENU_HEADER_BUTTON_WIDTH, 0, currentMenuBackground); + dc->drawBitmap(currentIndex*MENU_HEADER_BUTTON_WIDTH + 2, 7, menuIconSelected[tabs[currentIndex]->getIcon()]); + } + + void drawMenuDatetime(BitmapBuffer * dc) const + { + dc->drawSolidVerticalLine(DATETIME_SEPARATOR_X, 7, 31, TEXT_INVERTED_COLOR); + + struct gtm t; + gettime(&t); + char str[10]; + const char * const STR_MONTHS[] = TR_MONTHS; + sprintf(str, "%d %s", t.tm_mday, STR_MONTHS[t.tm_mon]); + lcdDrawText(DATETIME_MIDDLE, DATETIME_LINE1, str, SMLSIZE|TEXT_INVERTED_COLOR|CENTERED); + + getTimerString(str, getValue(MIXSRC_TX_TIME)); + lcdDrawText(DATETIME_MIDDLE, DATETIME_LINE2, str, SMLSIZE|TEXT_INVERTED_COLOR|CENTERED); + } + + void drawProgressBar(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h, int value) const override + { + drawSolidRect(dc, x, y, w, h, 1, TEXT_COLOR); + if (value > 0) { + int width = (w * value) / 100; + dc->drawSolidFilledRect(x + 2, y + 2, width - 4, h - 4, SCROLLBOX_COLOR); } } @@ -342,14 +395,19 @@ class DefaultTheme: public Theme static BitmapBuffer * topleftBitmap; static BitmapBuffer * menuIconNormal[MENUS_ICONS_COUNT]; static BitmapBuffer * menuIconSelected[MENUS_ICONS_COUNT]; + static BitmapBuffer * iconMask[MENUS_ICONS_COUNT]; static BitmapBuffer * currentMenuBackground; }; -const BitmapBuffer * DefaultTheme::backgroundBitmap = NULL; -BitmapBuffer * DefaultTheme::topleftBitmap = NULL; -BitmapBuffer * DefaultTheme::menuIconNormal[MENUS_ICONS_COUNT] = { NULL }; -BitmapBuffer * DefaultTheme::menuIconSelected[MENUS_ICONS_COUNT] = { NULL }; -BitmapBuffer * DefaultTheme::currentMenuBackground = NULL; +const BitmapBuffer * FrskyTheme::backgroundBitmap = nullptr; +BitmapBuffer * FrskyTheme::topleftBitmap = nullptr; +BitmapBuffer * FrskyTheme::iconMask[MENUS_ICONS_COUNT] = { nullptr }; +BitmapBuffer * FrskyTheme::menuIconNormal[MENUS_ICONS_COUNT] = { nullptr }; +BitmapBuffer * FrskyTheme::menuIconSelected[MENUS_ICONS_COUNT] = { nullptr }; +BitmapBuffer * FrskyTheme::currentMenuBackground = nullptr; -DefaultTheme defaultTheme; -Theme * theme = &defaultTheme; +FrskyTheme frskyTheme; + +#if defined(PCBFRSKY) +Theme * theme = &frskyTheme; +#endif diff --git a/radio/src/gui/480x272/topbar.cpp b/radio/src/gui/colorlcd/topbar.cpp similarity index 90% rename from radio/src/gui/480x272/topbar.cpp rename to radio/src/gui/colorlcd/topbar.cpp index a181dfc97..729fce856 100644 --- a/radio/src/gui/480x272/topbar.cpp +++ b/radio/src/gui/colorlcd/topbar.cpp @@ -38,20 +38,6 @@ Zone Topbar::getZone(unsigned int index) const const char * const STR_MONTHS[] = TR_MONTHS; -void drawTopbarDatetime() -{ - lcd->drawSolidVerticalLine(DATETIME_SEPARATOR_X, 7, 31, TEXT_INVERTED_COLOR); - - struct gtm t; - gettime(&t); - char str[10]; - sprintf(str, "%d %s", t.tm_mday, STR_MONTHS[t.tm_mon]); - lcdDrawText(DATETIME_MIDDLE, DATETIME_LINE1, str, SMLSIZE|TEXT_INVERTED_COLOR|CENTERED); - - getTimerString(str, getValue(MIXSRC_TX_TIME)); - lcdDrawText(DATETIME_MIDDLE, DATETIME_LINE2, str, SMLSIZE|TEXT_INVERTED_COLOR|CENTERED); -} - void drawTopBar() { theme->drawTopbarBackground(0); diff --git a/radio/src/gui/480x272/topbar.h b/radio/src/gui/colorlcd/topbar.h similarity index 100% rename from radio/src/gui/480x272/topbar.h rename to radio/src/gui/colorlcd/topbar.h diff --git a/radio/src/gui/480x272/view_about.cpp b/radio/src/gui/colorlcd/view_about.cpp similarity index 100% rename from radio/src/gui/480x272/view_about.cpp rename to radio/src/gui/colorlcd/view_about.cpp diff --git a/radio/src/gui/480x272/view_channels.cpp b/radio/src/gui/colorlcd/view_channels.cpp similarity index 100% rename from radio/src/gui/480x272/view_channels.cpp rename to radio/src/gui/colorlcd/view_channels.cpp diff --git a/radio/src/gui/480x272/view_logical_switches.cpp b/radio/src/gui/colorlcd/view_logical_switches.cpp similarity index 100% rename from radio/src/gui/480x272/view_logical_switches.cpp rename to radio/src/gui/colorlcd/view_logical_switches.cpp diff --git a/radio/src/gui/colorlcd/view_main.cpp b/radio/src/gui/colorlcd/view_main.cpp new file mode 100644 index 000000000..d649b14d0 --- /dev/null +++ b/radio/src/gui/colorlcd/view_main.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "view_main.h" +#include "menu_model.h" +#include "menu_radio.h" +// #include "menu_screens.h" +// #include "model_select.h" +// #include "view_channels.h" +// #include "view_statistics.h" +#include "opentx.h" + +#define TRIM_WIDTH 121 +#define TRIM_LH_X 10 +#define TRIM_LV_X 14 +#define TRIM_RV_X (LCD_W-25) +#define TRIM_RH_X (LCD_W-TRIM_LH_X-TRIM_WIDTH) +#define TRIM_V_Y 285 +#define TRIM_H_Y (LCD_H-37) +#define TRIM_LEN 80 +#define POTS_LINE_Y (LCD_H-20) + +Layout * customScreens[MAX_CUSTOM_SCREENS] = { 0, 0, 0, 0, 0 }; +Topbar * topbar; + +#if 0 +void drawMainPots() +{ + // The 3 pots +#if defined(PCBHORUS) + drawHorizontalSlider(TRIM_LH_X, POTS_LINE_Y, TRIM_WIDTH, calibratedAnalogs[CALIBRATED_POT1], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); + drawHorizontalSlider(LCD_W/2-20, POTS_LINE_Y, XPOTS_MULTIPOS_COUNT*5, 1 + (potsPos[1] & 0x0f), 1, XPOTS_MULTIPOS_COUNT + 1, XPOTS_MULTIPOS_COUNT, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_NUMBER_BUTTON); + drawHorizontalSlider(TRIM_RH_X, POTS_LINE_Y, TRIM_WIDTH, calibratedAnalogs[CALIBRATED_POT3], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); + + // The 2 rear sliders + drawVerticalSlider(6, TRIM_V_Y, 160, calibratedAnalogs[CALIBRATED_SLIDER_REAR_LEFT], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); + drawVerticalSlider(LCD_W-18, TRIM_V_Y, 160, calibratedAnalogs[CALIBRATED_SLIDER_REAR_RIGHT], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); +#elif defined(PCBNV14) + drawHorizontalSlider(TRIM_LH_X, POTS_LINE_Y, TRIM_WIDTH, calibratedAnalogs[CALIBRATED_POT1], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); + drawHorizontalSlider(TRIM_LH_X, POTS_LINE_Y, TRIM_WIDTH, calibratedAnalogs[CALIBRATED_POT2], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON); +#endif +} +#endif + +#if 0 +void drawTrims(uint8_t flightMode) +{ + for (uint8_t i=0; i<4; i++) { + static const coord_t x[4] = { TRIM_LH_X, TRIM_LV_X, TRIM_RV_X, TRIM_RH_X }; + static uint8_t vert[4] = {0, 1, 1, 0}; + unsigned int stickIndex = CONVERT_MODE(i); + coord_t xm = x[stickIndex]; + int32_t trim = getTrimValue(flightMode, i); + + if (vert[i]) { + if (g_model.extendedTrims == 1) { + drawVerticalSlider(xm, TRIM_V_Y, 120, trim, TRIM_EXTENDED_MIN, TRIM_EXTENDED_MAX, 0, OPTION_SLIDER_EMPTY_BAR|OPTION_SLIDER_TRIM_BUTTON); + } + else { + drawVerticalSlider(xm, TRIM_V_Y, 120, trim, TRIM_MIN, TRIM_MAX, 0, OPTION_SLIDER_EMPTY_BAR|OPTION_SLIDER_TRIM_BUTTON); + } + if (g_model.displayTrims != DISPLAY_TRIMS_NEVER && trim != 0) { + if (g_model.displayTrims == DISPLAY_TRIMS_ALWAYS || (trimsDisplayTimer > 0 && (trimsDisplayMask & (1< 0 && (trimsDisplayMask & (1<0 ? -TRIM_LEN/2 : TRIM_LEN/2); + lcdDrawNumber(x, TRIM_H_Y+2, trim, TINSIZE | CENTERED); + } + } + } + } +} +#endif + +void onMainViewMenu(const char *result) +{ + /*if (result == STR_VIEW_NOTES) { + pushModelNotes(); + } + else if (result == STR_STATISTICS) { + pushMenu(menuTabStats[0]); + } + else if (result == STR_ABOUT_US) { + chainMenu(menuAboutView); + } + else if (result == STR_MONITOR_SCREENS) { + pushMenu(menuTabMonitors[lastMonitorPage]); + }*/ +} + +int getMainViewsCount() +{ + for (int index=1; index uint8_t { + new ModelMenu(); + return 0; + }); + + new FabIconButton(this, LCD_W / 2, 100, ICON_RADIO, + [=]() -> uint8_t { + new RadioMenu(); + return 0; + }); + +// new FabIconButton(this, LCD_W - 50, 100, ICON_THEME, +// [=]() -> uint8_t { +// new ScreensMenu(); +// return 0; +// }); + } +#endif +} + +ViewMain::~ViewMain() +{ + deleteChildren(); +} + +#if defined(HARDWARE_KEYS) +void ViewMain::onKeyEvent(event_t event) +{ + switch (event) { + case EVT_KEY_LONG(KEY_MODEL): + killEvents(event); + new ModelMenu(); + break; + + case EVT_KEY_LONG(KEY_RADIO): + killEvents(event); + new RadioMenu(); + break; + + case EVT_KEY_LONG(KEY_ENTER): + Menu * menu = new Menu(); + menu->addLine(STR_MODEL_SELECT, [=]() { + // new ModelselectMenu(); + }); + if (1/*modelHasNotes()*/) { + menu->addLine(STR_VIEW_NOTES, [=]() { + // TODO + }); + } + menu->addLine(STR_MONITOR_SCREENS, [=]() { + // new ChannelsMonitorMenu(); + }); + menu->addLine(STR_RESET_SUBMENU, [=]() { + Menu * menu = new Menu(); + menu->addLine(STR_RESET_FLIGHT, [=]() { + flightReset(); + }); + menu->addLine(STR_RESET_TIMER1, [=]() { + timerReset(0); + }); + menu->addLine(STR_RESET_TIMER2, [=]() { + timerReset(1); + }); + menu->addLine(STR_RESET_TIMER3, [=]() { + timerReset(2); + }); + menu->addLine(STR_RESET_TELEMETRY, [=]() { + telemetryReset(); + }); + }); + menu->addLine(STR_STATISTICS, [=]() { + // new StatisticsMenu(); + }); + menu->addLine(STR_ABOUT_US, [=]() { + // TODO + }); + } +} +#endif + +void ViewMain::checkEvents() +{ + Window::checkEvents(); + + // TODO temporary for refreshing the trims + invalidate(); +} + +void ViewMain::paint(BitmapBuffer * dc) +{ + theme->drawBackground(dc); + +// drawMainPots(); + + if (g_model.view >= getMainViewsCount()) { + g_model.view = 0; + } + + for (uint8_t i=0; irefresh(); } + else + customScreens[i]->background(); + } + } +} + +#if 0 +bool menuMainViewChannelsMonitor(event_t event) +{ + switch (event) { + case EVT_KEY_BREAK(KEY_EXIT): + chainMenu(menuMainView); + event = 0; + return false; + } + + return menuChannelsView(event); +} +#endif diff --git a/radio/src/gui/colorlcd/view_main.h b/radio/src/gui/colorlcd/view_main.h new file mode 100644 index 000000000..134725529 --- /dev/null +++ b/radio/src/gui/colorlcd/view_main.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _VIEW_MAIN_H_ +#define _VIEW_MAIN_H_ + +#include "window.h" + +class ViewMain: public Window { + public: + ViewMain(bool icons=true); + + ~ViewMain() override; + +#if defined(DEBUG_WINDOWS) + virtual std::string getName() + { + return "ViewMain"; + } +#endif + + static ViewMain * instance; + + protected: +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + void paint(BitmapBuffer * dc) override; + void checkEvents() override; +}; + +#endif // _VIEW_MAIN_H_ + diff --git a/radio/src/gui/480x272/view_statistics.cpp b/radio/src/gui/colorlcd/view_statistics.cpp similarity index 100% rename from radio/src/gui/480x272/view_statistics.cpp rename to radio/src/gui/colorlcd/view_statistics.cpp diff --git a/radio/src/gui/480x272/view_text.cpp b/radio/src/gui/colorlcd/view_text.cpp similarity index 100% rename from radio/src/gui/480x272/view_text.cpp rename to radio/src/gui/colorlcd/view_text.cpp diff --git a/radio/src/gui/480x272/widget.cpp b/radio/src/gui/colorlcd/widget.cpp similarity index 100% rename from radio/src/gui/480x272/widget.cpp rename to radio/src/gui/colorlcd/widget.cpp diff --git a/radio/src/gui/480x272/widget.h b/radio/src/gui/colorlcd/widget.h similarity index 100% rename from radio/src/gui/480x272/widget.h rename to radio/src/gui/colorlcd/widget.h diff --git a/radio/src/gui/480x272/widgets/gauge.cpp b/radio/src/gui/colorlcd/widgets/gauge.cpp similarity index 100% rename from radio/src/gui/480x272/widgets/gauge.cpp rename to radio/src/gui/colorlcd/widgets/gauge.cpp diff --git a/radio/src/gui/480x272/widgets/modelbmp.cpp b/radio/src/gui/colorlcd/widgets/modelbmp.cpp similarity index 100% rename from radio/src/gui/480x272/widgets/modelbmp.cpp rename to radio/src/gui/colorlcd/widgets/modelbmp.cpp diff --git a/radio/src/gui/480x272/widgets/outputs.cpp b/radio/src/gui/colorlcd/widgets/outputs.cpp similarity index 100% rename from radio/src/gui/480x272/widgets/outputs.cpp rename to radio/src/gui/colorlcd/widgets/outputs.cpp diff --git a/radio/src/gui/480x272/widgets/text.cpp b/radio/src/gui/colorlcd/widgets/text.cpp similarity index 100% rename from radio/src/gui/480x272/widgets/text.cpp rename to radio/src/gui/colorlcd/widgets/text.cpp diff --git a/radio/src/gui/480x272/widgets/timer.cpp b/radio/src/gui/colorlcd/widgets/timer.cpp similarity index 100% rename from radio/src/gui/480x272/widgets/timer.cpp rename to radio/src/gui/colorlcd/widgets/timer.cpp diff --git a/radio/src/gui/480x272/widgets/value.cpp b/radio/src/gui/colorlcd/widgets/value.cpp similarity index 100% rename from radio/src/gui/480x272/widgets/value.cpp rename to radio/src/gui/colorlcd/widgets/value.cpp diff --git a/radio/src/gui/480x272/widgets_container.h b/radio/src/gui/colorlcd/widgets_container.h similarity index 100% rename from radio/src/gui/480x272/widgets_container.h rename to radio/src/gui/colorlcd/widgets_container.h diff --git a/radio/src/gui/480x272/zone.h b/radio/src/gui/colorlcd/zone.h similarity index 100% rename from radio/src/gui/480x272/zone.h rename to radio/src/gui/colorlcd/zone.h diff --git a/radio/src/gui/common/colorlcd/CMakeLists.txt b/radio/src/gui/common/colorlcd/CMakeLists.txt deleted file mode 100644 index 8ebd0ddbd..000000000 --- a/radio/src/gui/common/colorlcd/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -set(GUI_SRC - ${GUI_SRC} - ../common/colorlcd/draw_functions.cpp - ) - -if(HELI) - set(GUI_SRC ${GUI_SRC} ../common/colorlcd/model_heli.cpp) -endif() - diff --git a/radio/src/gui/common/colorlcd/draw_functions.cpp b/radio/src/gui/common/colorlcd/draw_functions.cpp deleted file mode 100644 index 30455b707..000000000 --- a/radio/src/gui/common/colorlcd/draw_functions.cpp +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -void drawStringWithIndex(coord_t x, coord_t y, const char * str, int idx, LcdFlags flags, const char * prefix, const char * suffix) -{ - char s[64]; - char * tmp = (prefix ? strAppend(s, prefix) : s); - tmp = strAppend(tmp, str); - tmp = strAppendUnsigned(tmp, abs(idx)); - if (suffix) - strAppend(tmp, suffix); - lcdDrawText(x, y, s, flags); -} - -void drawValueWithUnit(coord_t x, coord_t y, int val, uint8_t unit, LcdFlags att) -{ - // convertUnit(val, unit); - if (!(att & NO_UNIT) && unit != UNIT_RAW) { - char unitStr[8]; - strAppend(unitStr, STR_VTELEMUNIT+1+unit*STR_VTELEMUNIT[0], STR_VTELEMUNIT[0]); - lcdDrawNumber(x, y, val, att, 0, NULL, unitStr); - } - else { - lcdDrawNumber(x, y, val, att); - } -} - -int editChoice(coord_t x, coord_t y, const char * values, int value, int min, int max, LcdFlags attr, event_t event) -{ - if (attr & INVERS) value = checkIncDec(event, value, min, max, (isModelMenuDisplayed()) ? EE_MODEL : EE_GENERAL); - if (values) lcdDrawTextAtIndex(x, y, values, value-min, attr); - return value; -} - -uint8_t editCheckBox(uint8_t value, coord_t x, coord_t y, LcdFlags attr, event_t event ) -{ - value = editChoice(x, y, NULL, value, 0, 1, attr, event); - drawCheckBox(x, y, value, attr); - return value; -} - -swsrc_t editSwitch(coord_t x, coord_t y, swsrc_t value, LcdFlags attr, event_t event) -{ - if (attr & INVERS) CHECK_INCDEC_MODELSWITCH(event, value, SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES, isSwitchAvailableInMixes); - drawSwitch(x, y, value, attr); - return value; -} - -void drawFatalErrorScreen(const char * message) -{ - lcdClear(); - lcdDrawText(LCD_W/2, LCD_H/2-20, message, DBLSIZE|CENTERED|TEXT_BGCOLOR); - lcdRefresh(); -} - -void runFatalErrorScreen(const char * message) -{ - while (1) { - drawFatalErrorScreen(message); - backlightEnable(100); - uint8_t refresh = false; - while (1) { - uint32_t pwr_check = pwrCheck(); - if (pwr_check == e_power_off) { - boardOff(); - return; // only happens in SIMU, required for proper shutdown - } - else if (pwr_check == e_power_press) { - refresh = true; - } - else if (pwr_check == e_power_on && refresh) { - break; - } - } - } -} - -void drawPower(coord_t x, coord_t y, int8_t dBm, LcdFlags att) -{ - float power_W_PREC1 = pow(10.0, (dBm - 30.0) / 10.0) * 10; - if (dBm >= 30) { - lcdDrawNumber(x, y, power_W_PREC1, PREC1 | att); - lcdDrawText(lcdNextPos, y, "W", att); - } - else if (dBm < 10) { - uint16_t power_MW_PREC1 = round(power_W_PREC1 * 1000); - lcdDrawNumber(x, y, power_MW_PREC1, PREC1 | att); - lcdDrawText(lcdNextPos, y, "mW", att); - } - else { - uint16_t power_MW = round(power_W_PREC1 * 100); - if (power_MW >= 50) { - power_MW = (power_MW / 5) * 5; - lcdDrawNumber(x, y, power_MW, att); - lcdDrawText(lcdNextPos, y, "mW", att); - } - else { - lcdDrawNumber(x, y, power_MW, att); - lcdDrawText(lcdNextPos, y, "mW", att); - } - } -} - -void lcdDrawMMM(coord_t x, coord_t y, LcdFlags flags) -{ - lcdDrawTextAtIndex(x, y, STR_MMMINV, 0, flags); -} - -#if defined(FLIGHT_MODES) -void drawFlightMode(coord_t x, coord_t y, int8_t idx, LcdFlags att) -{ - if (idx==0) { - lcdDrawMMM(x, y, att); - return; - } - // TODO this code was not included in Taranis! and used with abs(...) on Horus - if (idx < 0) { - lcdDrawChar(x-2, y, '!', att); - idx = -idx; - } -#if defined(CONDENSED) - if (att & CONDENSED) { - lcdDrawNumber(x+FW*1, y, idx-1, (att & ~CONDENSED), 1); - return; - } -#endif - drawStringWithIndex(x, y, STR_FM, idx-1, att); -} -#endif - -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -void drawCurveRef(coord_t x, coord_t y, CurveRef & curve, LcdFlags att) -{ - if (curve.value != 0) { - switch (curve.type) { - case CURVE_REF_DIFF: - lcdDrawText(x, y, "D", att); - GVAR_MENU_ITEM(lcdNextPos, y, curve.value, -100, 100, LEFT|att, 0, 0); - break; - - case CURVE_REF_EXPO: - lcdDrawText(x, y, "E", att); - GVAR_MENU_ITEM(lcdNextPos, y, curve.value, -100, 100, LEFT|att, 0, 0); - break; - - case CURVE_REF_FUNC: - lcdDrawTextAtIndex(x, y, STR_VCURVEFUNC, curve.value, att); - break; - - case CURVE_REF_CUSTOM: - drawCurveName(x, y, curve.value, att); - break; - } - } -} - -void drawSensorCustomValue(coord_t x, coord_t y, uint8_t sensor, int32_t value, LcdFlags flags) -{ - if (sensor >= MAX_TELEMETRY_SENSORS) { - // Lua luaLcdDrawChannel() can call us with a bad value - return; - } - - TelemetryItem & telemetryItem = telemetryItems[sensor]; - TelemetrySensor & telemetrySensor = g_model.telemetrySensors[sensor]; - - if (telemetrySensor.unit == UNIT_DATETIME) { - drawDate(x, y, telemetryItem, flags); - } - else if (telemetrySensor.unit == UNIT_GPS) { - drawGPSSensorValue(x, y, telemetryItem, flags); - } - else if (telemetrySensor.unit == UNIT_BITFIELD) { - if (IS_FRSKY_SPORT_PROTOCOL()) { - if (telemetrySensor.id >= RBOX_STATE_FIRST_ID && telemetrySensor.id <= RBOX_STATE_LAST_ID) { - if (telemetrySensor.subId == 0) { - if (value == 0) { - lcdDrawText(x, y, "OK", flags); - } - else { - for (uint8_t i=0; i<16; i++) { - if (value & (1 << i)) { - char s[] = "CH__ KO"; - strAppendUnsigned(&s[2], i+1, 2); - lcdDrawText(x, flags & DBLSIZE ? y+1 : y, s, flags & ~DBLSIZE); - break; - } - } - } - } - else { - if (value == 0) { - lcdDrawText(x, flags & DBLSIZE ? y+1 : y, "Rx OK", flags & ~DBLSIZE); - } - else { - static const char * const RXS_STATUS[] = { - "Rx1 Ovl", - "Rx2 Ovl", - "SBUS Ovl", - "Rx1 FS", - "Rx1 LF", - "Rx2 FS", - "Rx2 LF", - "Rx1 Lost", - "Rx2 Lost", - "Rx1 NS", - "Rx2 NS", - }; - for (uint8_t i=0; i 0) { - flags |= (telemetrySensor.prec==1 ? PREC1 : PREC2); - } - drawValueWithUnit(x, y, value, telemetrySensor.unit == UNIT_CELLS ? UNIT_VOLTS : telemetrySensor.unit, flags); - } -} - -void drawSourceCustomValue(coord_t x, coord_t y, source_t source, int32_t value, LcdFlags flags) -{ - if (source >= MIXSRC_FIRST_TELEM) { - source = (source-MIXSRC_FIRST_TELEM) / 3; - drawSensorCustomValue(x, y, source, value, flags); - } - else if (source >= MIXSRC_FIRST_TIMER || source == MIXSRC_TX_TIME) { - if (value < 0) flags |= BLINK|INVERS; - drawTimer(x, y, value, flags); - } - else if (source == MIXSRC_TX_VOLTAGE) { - lcdDrawNumber(x, y, value, flags|PREC1); - } -#if defined(INTERNAL_GPS) - else if (source == MIXSRC_TX_GPS) { - if (gpsData.fix) { - drawGPSPosition(x, y, gpsData.longitude, gpsData.latitude, flags); - } - else { - lcdDrawText(x, y, "sats: ", flags); - lcdDrawNumber(lcdNextPos, y, gpsData.numSat, flags); - } - } -#endif -#if defined(GVARS) - else if (source >= MIXSRC_FIRST_GVAR && source <= MIXSRC_LAST_GVAR) { - drawGVarValue(x, y, source - MIXSRC_FIRST_GVAR, value, flags); - } -#endif - else if (source < MIXSRC_FIRST_CH) { - lcdDrawNumber(x, y, calcRESXto100(value), flags); - } - else if (source <= MIXSRC_LAST_CH) { -#if defined(PPM_UNIT_PERCENT_PREC1) - lcdDrawNumber(x, y, calcRESXto1000(value), flags|PREC1); -#else - lcdDrawNumber(x, y, calcRESXto100(value), flags); -#endif - } - else { - lcdDrawNumber(x, y, value, flags); - } -} - -void drawSourceValue(coord_t x, coord_t y, source_t source, LcdFlags flags) -{ - getvalue_t value = getValue(source); - drawSourceCustomValue(x, y, source, value, flags); -} diff --git a/radio/src/gui/common/colorlcd/model_heli.cpp b/radio/src/gui/common/colorlcd/model_heli.cpp deleted file mode 100644 index d09df4941..000000000 --- a/radio/src/gui/common/colorlcd/model_heli.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * 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" - -enum MenuModelHeliItems { - ITEM_HELI_SWASHTYPE, - ITEM_HELI_SWASHRING, - ITEM_HELI_ELE, - ITEM_HELI_ELE_WEIGHT, - ITEM_HELI_AIL, - ITEM_HELI_AIL_WEIGHT, - ITEM_HELI_COL, - ITEM_HELI_COL_WEIGHT, - ITEM_HELI_MAX -}; - -#define MODEL_HELI_2ND_COLUMN (230) - -bool menuModelHeli(event_t event) -{ - SIMPLE_MENU(STR_MENUHELISETUP, MODEL_ICONS, menuTabModel, MENU_MODEL_HELI, ITEM_HELI_MAX); - - uint8_t sub = menuVerticalPosition; - - for (uint8_t i=0; i0) ? BLINK|INVERS : INVERS); - LcdFlags attr = (sub == k ? blink : 0); - - switch (k) { - case ITEM_HELI_SWASHTYPE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SWASHTYPE); - g_model.swashR.type = editChoice(MODEL_HELI_2ND_COLUMN, y, STR_VSWASHTYPE, g_model.swashR.type, 0, SWASH_TYPE_MAX, attr, event); - break; - - case ITEM_HELI_SWASHRING: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SWASHRING); - lcdDrawNumber(MODEL_HELI_2ND_COLUMN, y, g_model.swashR.value, LEFT|attr); - if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, g_model.swashR.value, 100); - break; - - case ITEM_HELI_ELE: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ELEVATOR); - drawSource(MODEL_HELI_2ND_COLUMN, y, g_model.swashR.elevatorSource, attr); - if (attr) CHECK_INCDEC_MODELSOURCE(event, g_model.swashR.elevatorSource, 0, MIXSRC_LAST_CH); - break; - - case ITEM_HELI_ELE_WEIGHT: - lcdDrawText(MENUS_MARGIN_LEFT+INDENT_WIDTH, y, STR_WEIGHT); - lcdDrawNumber(MODEL_HELI_2ND_COLUMN, y, g_model.swashR.elevatorWeight, LEFT|attr); - if (attr) CHECK_INCDEC_MODELVAR(event, g_model.swashR.elevatorWeight, -100, 100); - break; - - case ITEM_HELI_AIL: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_AILERON); - drawSource(MODEL_HELI_2ND_COLUMN, y, g_model.swashR.aileronSource, attr); - if (attr) CHECK_INCDEC_MODELSOURCE(event, g_model.swashR.aileronSource, 0, MIXSRC_LAST_CH); - break; - - case ITEM_HELI_AIL_WEIGHT: - lcdDrawText(MENUS_MARGIN_LEFT+INDENT_WIDTH, y, STR_WEIGHT); - lcdDrawNumber(MODEL_HELI_2ND_COLUMN, y, g_model.swashR.aileronWeight, LEFT|attr); - if (attr) CHECK_INCDEC_MODELVAR(event, g_model.swashR.aileronWeight, -100, 100); - break; - - case ITEM_HELI_COL: - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_COLLECTIVE); - drawSource(MODEL_HELI_2ND_COLUMN, y, g_model.swashR.collectiveSource, attr); - if (attr) CHECK_INCDEC_MODELSOURCE(event, g_model.swashR.collectiveSource, 0, MIXSRC_LAST_CH); - break; - - case ITEM_HELI_COL_WEIGHT: - lcdDrawText(MENUS_MARGIN_LEFT+INDENT_WIDTH, y, STR_WEIGHT); - lcdDrawNumber(MODEL_HELI_2ND_COLUMN, y, g_model.swashR.collectiveWeight, LEFT|attr); - if (attr) CHECK_INCDEC_MODELVAR(event, g_model.swashR.collectiveWeight, -100, 100); - break; - - } - } - - return true; -} diff --git a/radio/src/gui/common/stdlcd/draw_functions.cpp b/radio/src/gui/common/stdlcd/draw_functions.cpp index 06eb7137a..cf96389db 100644 --- a/radio/src/gui/common/stdlcd/draw_functions.cpp +++ b/radio/src/gui/common/stdlcd/draw_functions.cpp @@ -217,6 +217,23 @@ void drawGVarName(coord_t x, coord_t y, int8_t idx, LcdFlags flags) lcdDrawText(x, y, s, flags); } +void editStickHardwareSettings(coord_t x, coord_t y, int idx, event_t event, LcdFlags flags) +{ + lcdDrawTextAtIndex(INDENT_WIDTH, y, STR_VSRCRAW, idx+1, 0); + if (ZEXIST(g_eeGeneral.anaNames[idx]) || (flags && s_editMode > 0)) + editName(x, y, g_eeGeneral.anaNames[idx], LEN_ANA_NAME, event, flags); + else + lcdDrawMMM(x, y, flags); +} + +bool isSwitchAvailableInCustomFunctions(int swtch) +{ + if (menuHandlers[menuLevel] == menuModelSpecialFunctions) + return isSwitchAvailable(swtch, ModelCustomFunctionsContext); + else + return isSwitchAvailable(swtch, GeneralCustomFunctionsContext); +} + void drawPower(coord_t x, coord_t y, int8_t dBm, LcdFlags att) { float power_W_PREC1 = pow(10.0, (dBm - 30.0) / 10.0) * 10; diff --git a/radio/src/gui/common/stdlcd/draw_functions.h b/radio/src/gui/common/stdlcd/draw_functions.h index 855b8600c..1c2cffb8e 100644 --- a/radio/src/gui/common/stdlcd/draw_functions.h +++ b/radio/src/gui/common/stdlcd/draw_functions.h @@ -37,6 +37,8 @@ inline void drawTimer(coord_t x, coord_t y, int32_t tme, LcdFlags att = 0) drawTimer(x, y, tme, att, att); } +void drawFlightMode(coord_t x, coord_t y, int8_t idx, LcdFlags att = 0); + void drawStartupAnimation(uint32_t duration, uint32_t totalDuration); void drawShutdownAnimation(uint32_t duration, uint32_t totalDuration, const char * message); void drawSleepBitmap(); diff --git a/radio/src/gui/common/stdlcd/popups.cpp b/radio/src/gui/common/stdlcd/popups.cpp index 6c86dff68..918312017 100644 --- a/radio/src/gui/common/stdlcd/popups.cpp +++ b/radio/src/gui/common/stdlcd/popups.cpp @@ -183,7 +183,7 @@ void runPopupWarning(event_t event) break; case WARNING_TYPE_ASTERISK: - lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y+2*FH+2, STR_EXIT); + lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y+2*FH+2, BUTTON(TR_EXIT)); break; default: diff --git a/radio/src/gui/common/stdlcd/popups.h b/radio/src/gui/common/stdlcd/popups.h index c68d3b2a1..b65cfb07e 100644 --- a/radio/src/gui/common/stdlcd/popups.h +++ b/radio/src/gui/common/stdlcd/popups.h @@ -23,6 +23,7 @@ #include #include "audio.h" +#include "lcd.h" extern const char * warningText; extern const char * warningInfoText; @@ -57,6 +58,7 @@ void showAlertBox(const char * title, const char * text, const char * action , u // Full screen with 2 lines and a progress bar void drawProgressScreen(const char * title, const char * message, int num, int den); +typedef void (* ProgressHandler)(const char *, const char *, int, int); enum { diff --git a/radio/src/gui/common/stdlcd/radio_sdmanager.cpp b/radio/src/gui/common/stdlcd/radio_sdmanager.cpp index 6b7fc5e3a..16fc257df 100644 --- a/radio/src/gui/common/stdlcd/radio_sdmanager.cpp +++ b/radio/src/gui/common/stdlcd/radio_sdmanager.cpp @@ -91,7 +91,7 @@ void onUpdateConfirmation(const char * result) if (result == STR_OK) { OtaUpdateInformation * destination = moduleState[EXTERNAL_MODULE].otaUpdateInformation; Pxx2OtaUpdate otaUpdate(EXTERNAL_MODULE, destination->candidateReceiversNames[destination->selectedReceiverIndex]); - otaUpdate.flashFirmware(destination->filename); + otaUpdate.flashFirmware(destination->filename, drawProgressScreen); } else { moduleState[EXTERNAL_MODULE].mode = MODULE_MODE_NORMAL; @@ -202,30 +202,30 @@ void onSdManagerMenu(const char * result) else if (result == STR_FLASH_INTERNAL_MODULE) { getSelectionFullPath(lfn); FrskyDeviceFirmwareUpdate device(INTERNAL_MODULE); - device.flashFirmware(lfn); + device.flashFirmware(lfn, drawProgressScreen); } else if (result == STR_FLASH_EXTERNAL_MODULE) { // needed on X-Lite (as the R9M needs 2S while the external device flashing port only provides 5V) getSelectionFullPath(lfn); FrskyDeviceFirmwareUpdate device(EXTERNAL_MODULE); - device.flashFirmware(lfn); + device.flashFirmware(lfn, drawProgressScreen); } else if (result == STR_FLASH_EXTERNAL_DEVICE) { getSelectionFullPath(lfn); FrskyDeviceFirmwareUpdate device(SPORT_MODULE); - device.flashFirmware(lfn); + device.flashFirmware(lfn, drawProgressScreen); } #if defined(BLUETOOTH) else if (result == STR_FLASH_BLUETOOTH_MODULE) { getSelectionFullPath(lfn); - bluetooth.flashFirmware(lfn); + bluetooth.flashFirmware(lfn, drawProgressScreen); } #endif #if defined(HARDWARE_POWER_MANAGEMENT_UNIT) else if (result == STR_FLASH_POWER_MANAGEMENT_UNIT) { getSelectionFullPath(lfn); FrskyChipFirmwareUpdate device; - device.flashFirmware(lfn); + device.flashFirmware(lfn, drawProgressScreen); } #endif #if defined(PXX2) diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index c3ec84d09..c0e1b70a0 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -20,7 +20,7 @@ #include "opentx.h" -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) || defined(PCBFLYSKY) uint8_t switchToMix(uint8_t source) { div_t qr = div(source-1, 3); @@ -283,15 +283,6 @@ bool isInputSourceAvailable(int source) return false; } -enum SwitchContext -{ - LogicalSwitchesContext, - ModelCustomFunctionsContext, - GeneralCustomFunctionsContext, - TimersContext, - MixesContext -}; - bool isLogicalSwitchAvailable(int index) { LogicalSwitchData * lsw = lswAddress(index); @@ -395,14 +386,6 @@ bool isSwitchAvailableInLogicalSwitches(int swtch) return isSwitchAvailable(swtch, LogicalSwitchesContext); } -bool isSwitchAvailableInCustomFunctions(int swtch) -{ - if (menuHandlers[menuLevel] == menuModelSpecialFunctions) - return isSwitchAvailable(swtch, ModelCustomFunctionsContext); - else - return isSwitchAvailable(swtch, GeneralCustomFunctionsContext); -} - bool isSwitchAvailableInMixes(int swtch) { return isSwitchAvailable(swtch, MixesContext); @@ -415,23 +398,23 @@ bool isSwitch2POSWarningStateAvailable(int state) } #endif // #if defined(COLORLCD) -bool isSwitchAvailableInTimers(int swtch) -{ - if (swtch >= 0) { - if (swtch < TMRMODE_COUNT) - return true; - else - swtch -= TMRMODE_COUNT-1; - } - else { - if (swtch > -TMRMODE_COUNT) - return false; - else - swtch += TMRMODE_COUNT-1; - } - - return isSwitchAvailable(swtch, TimersContext); -} +//bool isSwitchAvailableInTimers(int swtch) +//{ +// if (swtch >= 0) { +// if (swtch < TMRMODE_COUNT) +// return true; +// else +// swtch -= TMRMODE_COUNT-1; +// } +// else { +// if (swtch > -TMRMODE_COUNT) +// return false; +// else +// swtch += TMRMODE_COUNT-1; +// } +// +// return isSwitchAvailable(swtch, TimersContext); +//} bool isThrottleSourceAvailable(int source) { @@ -446,10 +429,10 @@ bool isLogicalSwitchFunctionAvailable(int function) return function != LS_FUNC_RANGE; } -bool isAssignableFunctionAvailable(int function) +bool isAssignableFunctionAvailable(int function, CustomFunctionData * functions) { #if defined(OVERRIDE_CHANNEL_FUNCTION) || defined(GVARS) - bool modelFunctions = (menuHandlers[menuLevel] == menuModelSpecialFunctions); + bool modelFunctions = (functions == g_model.customFn); #endif switch (function) { @@ -474,7 +457,7 @@ bool isAssignableFunctionAvailable(int function) case FUNC_BIND: #endif #if !defined(LUA) - case FUNC_PLAY_SCRIPT: + case FUNC_PLAY_SCRIPT: #endif case FUNC_RESERVE5: return false; @@ -484,6 +467,13 @@ bool isAssignableFunctionAvailable(int function) } } +#if !defined(COLORLCD) +bool isAssignableFunctionAvailable(int function) +{ + return isAssignableFunctionAvailable(function, menuHandlers[menuLevel] == menuModelSpecialFunctions ? g_model.customFn : g_eeGeneral.customFn); +} +#endif + bool isSourceAvailableInGlobalResetSpecialFunction(int index) { if (index >= FUNC_RESET_PARAM_FIRST_TELEM) @@ -861,12 +851,3 @@ const mm_protocol_definition *getMultiProtocolDefinition (uint8_t protocol) return pdef; } #endif - -void editStickHardwareSettings(coord_t x, coord_t y, int idx, event_t event, LcdFlags flags) -{ - lcdDrawTextAtIndex(INDENT_WIDTH, y, STR_VSRCRAW, idx+1, 0); - if (ZEXIST(g_eeGeneral.anaNames[idx]) || (flags && s_editMode > 0)) - editName(x, y, g_eeGeneral.anaNames[idx], LEN_ANA_NAME, event, flags); - else - lcdDrawMMM(x, y, flags); -} diff --git a/radio/src/gui/gui_common.h b/radio/src/gui/gui_common.h index 206508a96..22c01a49e 100644 --- a/radio/src/gui/gui_common.h +++ b/radio/src/gui/gui_common.h @@ -21,6 +21,7 @@ #ifndef _GUI_COMMON_H_ #define _GUI_COMMON_H_ +#include #include "lcd.h" #include "keys.h" #include "telemetry/telemetry_sensors.h" @@ -37,7 +38,20 @@ #define CASE_EVT_ROTARY_RIGHT #endif +#if defined(LIBOPENUI) +typedef std::function IsValueAvailable; +#else typedef bool (*IsValueAvailable)(int); +#endif + +enum SwitchContext +{ + LogicalSwitchesContext, + ModelCustomFunctionsContext, + GeneralCustomFunctionsContext, + TimersContext, + MixesContext +}; int circularIncDec(int current, int inc, int min, int max, IsValueAvailable isValueAvailable=nullptr); int getFirstAvailable(int min, int max, IsValueAvailable isValueAvailable); @@ -54,6 +68,7 @@ bool isSourceAvailableInGlobalFunctions(int source); bool isSourceAvailableInCustomSwitches(int source); bool isSourceAvailableInResetSpecialFunction(int index); bool isSourceAvailableInGlobalResetSpecialFunction(int index); +bool isSwitchAvailable(int swtch, SwitchContext context); bool isSwitchAvailableInLogicalSwitches(int swtch); bool isSwitchAvailableInCustomFunctions(int swtch); bool isSwitchAvailableInMixes(int swtch); @@ -65,6 +80,7 @@ bool isInternalModuleAvailable(int moduleType); bool isRfProtocolAvailable(int protocol); bool isTelemetryProtocolAvailable(int protocol); bool isTrainerModeAvailable(int mode); +bool isAssignableFunctionAvailable(int function, CustomFunctionData * functions); bool isSensorUnit(int sensor, uint8_t unit); bool isCellsSensor(int sensor); @@ -83,16 +99,14 @@ bool modelHasNotes(); bool isSwitch2POSWarningStateAvailable(int state); #endif -#if defined(GUI) +#if defined(LIBOPENUI) +#define IS_INSTANT_TRIM_ALLOWED() false +#elif defined(GUI) #define IS_INSTANT_TRIM_ALLOWED() (IS_MAIN_VIEW_DISPLAYED() || IS_TELEMETRY_VIEW_DISPLAYED() || IS_OTHER_VIEW_DISPLAYED()) #else #define IS_INSTANT_TRIM_ALLOWED() true #endif -#if defined(FLIGHT_MODES) -void drawFlightMode(coord_t x, coord_t y, int8_t idx, LcdFlags att=0); -#endif - swsrc_t checkIncDecMovedSwitch(swsrc_t val); void drawCurveRef(coord_t x, coord_t y, CurveRef & curve, LcdFlags flags=0); @@ -108,6 +122,7 @@ void drawCurve(coord_t offset=0); #if defined(COLORLCD) void drawStringWithIndex(coord_t x, coord_t y, const char * str, int idx, LcdFlags flags=0, const char * prefix=nullptr, const char * suffix=nullptr); +void drawCurveRef(BitmapBuffer * dc, coord_t x, coord_t y, const CurveRef & curve, LcdFlags flags=0); int editChoice(coord_t x, coord_t y, const char * values, int value, int min, int max, LcdFlags flags, event_t event); uint8_t editCheckBox(uint8_t value, coord_t x, coord_t y, LcdFlags flags, event_t event); swsrc_t editSwitch(coord_t x, coord_t y, swsrc_t value, LcdFlags flags, event_t event); diff --git a/radio/src/gui/libopenui/CMakeLists.txt b/radio/src/gui/libopenui/CMakeLists.txt new file mode 100644 index 000000000..ffd7461cd --- /dev/null +++ b/radio/src/gui/libopenui/CMakeLists.txt @@ -0,0 +1,34 @@ +set(LIBOPENUI_SRC + mainwindow.cpp + window.cpp + bitmapbuffer.cpp + button.cpp + draw_functions.cpp + checkbox.cpp + form.cpp + textedit.cpp + numberedit.cpp + timeedit.cpp + choice.cpp + tabsgroup.cpp + page.cpp + sourcechoice.cpp + switchchoice.cpp + filechoice.cpp + slider.cpp + progress.cpp + menu.cpp + dialog.cpp + curve.cpp + curveedit.cpp + # coloredit.cpp + ) + +if(HARDWARE_TOUCH) + set(LIBOPENUI_SRC + ${LIBOPENUI_SRC} + keyboard_text.cpp + keyboard_number.cpp + keyboard_curve.cpp + ) +endif() diff --git a/radio/src/gui/libopenui/LICENSE b/radio/src/gui/libopenui/LICENSE new file mode 100644 index 000000000..0a041280b --- /dev/null +++ b/radio/src/gui/libopenui/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/radio/src/gui/libopenui/basenumberedit.h b/radio/src/gui/libopenui/basenumberedit.h new file mode 100644 index 000000000..9d11a809f --- /dev/null +++ b/radio/src/gui/libopenui/basenumberedit.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _BASENUMBEREDIT_H_ +#define _BASENUMBEREDIT_H_ + +#include "form.h" + +class BaseNumberEdit : public FormField { + public: + BaseNumberEdit(Window * parent, const rect_t &rect, int32_t vmin, int32_t vmax, + std::function getValue, std::function setValue = nullptr, LcdFlags flags = 0): + FormField(parent, rect), + vmin(vmin), + vmax(vmax), + _getValue(std::move(getValue)), + _setValue(std::move(setValue)), + flags(flags) + { + } + + void setMin(int32_t value) + { + vmin = value; + } + + void setMax(int32_t value) + { + vmax = value; + } + + void setDefault(int32_t value) + { + vdefault = value; + } + + int32_t getMin() const + { + return vmin; + } + + int32_t getMax() const + { + return vmax; + } + + int32_t getDefault() const + { + return vdefault; + } + + void setStep(int32_t value) + { + step = value; + } + + int32_t getStep() const + { + return step; + } + + void setValue(int32_t value) + { + _setValue(limit(vmin, value, vmax)); + invalidate(); + } + + void setSetValueHandler(std::function handler) + { + _setValue = handler; + } + + int32_t getValue() + { + return _getValue(); + } + + protected: + int32_t vdefault = 0; + int32_t vmin; + int32_t vmax; + int32_t step = 1; + std::function _getValue; + std::function _setValue; + LcdFlags flags; +}; + +#endif // _BASENUMBEREDIT_H_ diff --git a/radio/src/gui/480x272/bitmapbuffer.cpp b/radio/src/gui/libopenui/bitmapbuffer.cpp similarity index 81% rename from radio/src/gui/480x272/bitmapbuffer.cpp rename to radio/src/gui/libopenui/bitmapbuffer.cpp index ae38a8500..ff1f8b647 100644 --- a/radio/src/gui/480x272/bitmapbuffer.cpp +++ b/radio/src/gui/libopenui/bitmapbuffer.cpp @@ -39,8 +39,14 @@ void BitmapBuffer::drawAlphaPixel(display_t * p, uint8_t opacity, uint16_t color void BitmapBuffer::drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags att) { - if (y >= height) return; - if (x+w > width) { w = width - x; } + APPLY_OFFSET(); + + if (y >= height || y >= ymax || y < ymin) + return; + + if (x+w > width) { + w = width - x; + } display_t * p = getPixelPtr(x, y); display_t color = lcdColorTable[COLOR_IDX(att)]; @@ -68,11 +74,27 @@ void BitmapBuffer::drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t p void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat, LcdFlags att) { - if (x >= width) return; - if (y >= height) return; - if (h<0) { y+=h; h=-h; } - if (y<0) { h+=y; y=0; if (h<=0) return; } - if (y+h > height) { h = height - y; } + APPLY_OFFSET(); + + if (x < xmin || x >= xmax) + return; + + if (h < 0) { + y += h; + h = -h; + } + + if (y < ymin) { + h += y-ymin; + y = ymin; + } + + if (y + h > ymax) { + h = ymax - y; + } + + if (h <= 0) + return; display_t color = lcdColorTable[COLOR_IDX(att)]; uint8_t opacity = 0x0F - (att >> 24); @@ -110,8 +132,60 @@ void BitmapBuffer::drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t } } +void BitmapBuffer::drawSolidFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags) +{ + APPLY_OFFSET(); + + if (x >= xmax || y >= ymax) + return; + + if (h < 0) { + y += h; + h = -h; + } + if (y < ymin) { + h += y-ymin; + y = ymin; + } + if (x < xmin) { + w += x - xmin; + x = xmin; + } + if (y + h > ymax) + h = ymax - y; + if (x + w > xmax) + w = xmax - x; + + if (!data || h<=0 || w<=0) + return; + + DMAFillRect(data, width, height, x, y, w, h, lcdColorTable[COLOR_IDX(flags)]); +} + void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t pat, LcdFlags att) { + APPLY_OFFSET(); + + if (x >= xmax || y >= ymax) + return; + + if (h < 0) { + y += h; + h = -h; + } + if (y < ymin) { + h += y-ymin; + y = ymin; + } + if (x < xmin) { + w += x - xmin; + x = xmin; + } + if (y + h > ymax) + h = ymax - y; + if (x + w > xmax) + w = xmax - x; + for (coord_t i=y; i= 360 || endAngle <= 0) @@ -176,7 +248,7 @@ bool evalSlopes(int * slopes, int startAngle, int endAngle) slopes[2] = -100000; } else { - float angle1 = float(startAngle) * (PI / 180.0f); + float angle1 = float(startAngle) * (M_PI / 180.0f); if (startAngle >= 180) { slopes[1] = -100000; slopes[2] = cosf(angle1) * 100 / sinf(angle1); @@ -192,7 +264,7 @@ bool evalSlopes(int * slopes, int startAngle, int endAngle) slopes[3] = 100000; } else { - float angle2 = float(endAngle) * (PI / 180.0f); + float angle2 = float(endAngle) * (M_PI / 180.0f); if (endAngle >= 180) { slopes[0] = -100000; slopes[3] = -cosf(angle2) * 100 / sinf(angle2); @@ -233,12 +305,14 @@ void BitmapBuffer::drawPie(int x0, int y0, int radius, int startAngle, int endAn } } -void BitmapBuffer::drawMask(coord_t x, coord_t y, BitmapBuffer * mask, LcdFlags flags, coord_t offset, coord_t width) +void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, LcdFlags flags, coord_t offset, coord_t width) { - if (mask == NULL) { + if (mask == nullptr) { return; } + APPLY_OFFSET(); + coord_t w = mask->getWidth(); coord_t height = mask->getHeight(); @@ -246,15 +320,21 @@ void BitmapBuffer::drawMask(coord_t x, coord_t y, BitmapBuffer * mask, LcdFlags width = w; } - if (x+width > this->width) { - width = this->width-x; + if (x + width > xmax) { + width = xmax - x; + } + + if (y >= ymax || x >= xmax || width <= 0 || x + width < xmin || y + height < ymin) { + return; } display_t color = lcdColorTable[COLOR_IDX(flags)]; for (coord_t row=0; row= ymax) + continue; display_t * p = getPixelPtr(x, y+row); - display_t * q = mask->getPixelPtr(offset, row); + const display_t * q = mask->getPixelPtr(offset, row); for (coord_t col=0; col this->width) { - width = this->width-x; + if (x + width > xmax) { + width = xmax - x; + } + + if (y >= ymax || x >= xmax || width <= 0 || x + width < xmin || y + height < ymin) { + return; } display_t color = lcdColorTable[COLOR_IDX(flags)]; for (coord_t row=0; row= ymax) + continue; const uint8_t * q = bmp + 4 + row*w + offset; for (coord_t col=0; col= xmin && xpixel < xmax) { + display_t * p = getPixelPtr(xpixel, ypixel); + if (p) + drawAlphaPixel(p, *q, color); + } q++; } } } -uint8_t BitmapBuffer::drawCharWithoutCache(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, int index, LcdFlags flags) +uint8_t BitmapBuffer::drawChar(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, int index, LcdFlags flags) { - coord_t offset = spec[index]; - coord_t width = spec[index+1] - offset; - if (width > 0) + coord_t offset = spec[index + 1]; + coord_t width = spec[index + 2] - offset; + if (width > 0) { drawBitmapPattern(x, y, font, flags, offset, width); - return width; -} - -uint8_t BitmapBuffer::drawCharWithCache(coord_t x, coord_t y, const BitmapBuffer * font, const uint16_t * spec, int index, LcdFlags flags) -{ - coord_t offset = spec[index]; - coord_t width = spec[index+1] - offset; - if (width > 0) - drawBitmap(x, y, font, offset, 0, width); + } return width; } void BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_t len, LcdFlags flags) { + MOVE_OFFSET(); + #define INCREMENT_POS(delta) \ do { if (flags & VERTICAL) y -= delta; else x += delta; } while(0) int width = getTextWidth(s, len, flags); int height = getFontHeight(flags); + + if (y + height <= ymin || y >= ymax) { + RESTORE_OFFSET(); + return; + } + uint32_t fontindex = FONTINDEX(flags); const unsigned char * font = fontsTable[fontindex]; const uint16_t * fontspecs = fontspecsTable[fontindex]; - BitmapBuffer * fontcache = NULL; if (flags & RIGHT) { INCREMENT_POS(-width); @@ -331,56 +426,6 @@ void BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_t l coord_t & pos = (flags & VERTICAL) ? y : x; - if ((flags & INVERS) && ((~flags & BLINK) || BLINK_ON_PHASE)) { - uint16_t fgColor = lcdColorTable[COLOR_IDX(flags)]; - if (fgColor == lcdColorTable[TEXT_COLOR_INDEX]) { - flags = TEXT_INVERTED_COLOR | (flags & 0x0ffff); - } - if (fontindex == STDSIZE_INDEX) { - if (fgColor == lcdColorTable[TEXT_COLOR_INDEX]) { - drawSolidFilledRect(x-INVERT_HORZ_MARGIN, y, INVERT_HORZ_MARGIN-1, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR); - drawSolidFilledRect(x+width-1, y, INVERT_HORZ_MARGIN, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR); - fontcache = fontCache[1]; - } - else { - drawSolidFilledRect(x-INVERT_HORZ_MARGIN, y, width+2*INVERT_HORZ_MARGIN-1, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR); - } - } - else if (fontindex == TINSIZE_INDEX) { - drawSolidFilledRect(x-INVERT_HORZ_MARGIN+2, y, width+2*INVERT_HORZ_MARGIN-5, height+2, TEXT_INVERTED_BGCOLOR); - } - else if (fontindex == SMLSIZE_INDEX) { - drawSolidFilledRect(x-INVERT_HORZ_MARGIN, y, width+2*INVERT_HORZ_MARGIN-2, height+2, TEXT_INVERTED_BGCOLOR); - } - else if (fontindex == MIDSIZE_INDEX) { - drawSolidFilledRect(x-INVERT_HORZ_MARGIN, y-1, width+2*INVERT_HORZ_MARGIN-2, height+3, TEXT_INVERTED_BGCOLOR); // MIDSIZE and DBLSIZE font are missing a pixel on top compared to others - } - else if (fontindex == DBLSIZE_INDEX) { - drawSolidFilledRect(x-INVERT_HORZ_MARGIN, y-1, width+2*INVERT_HORZ_MARGIN-2, height+3, TEXT_INVERTED_BGCOLOR); // MIDSIZE and DBLSIZE font are missing a pixel on top compared to others - } - else if (fontindex == XXLSIZE_INDEX) { - drawSolidFilledRect(x-INVERT_HORZ_MARGIN, y, width+2*INVERT_HORZ_MARGIN-2, height+2, TEXT_INVERTED_BGCOLOR); - } - else { - drawSolidFilledRect(x-INVERT_HORZ_MARGIN, y, width+2*INVERT_HORZ_MARGIN, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR); - } - } - else if (!(flags & NO_FONTCACHE)) { - if (fontindex == STDSIZE_INDEX) { - uint16_t fgColor = lcdColorTable[COLOR_IDX(flags)]; - uint16_t bgColor = *getPixelPtr(x, y); - if (fgColor == lcdColorTable[TEXT_COLOR_INDEX] && bgColor == lcdColorTable[TEXT_BGCOLOR_INDEX]) { - fontcache = fontCache[0]; - } - else if (fgColor == lcdColorTable[TEXT_INVERTED_COLOR_INDEX] && bgColor == lcdColorTable[TEXT_INVERTED_BGCOLOR_INDEX]) { - fontcache = fontCache[1]; - } - else { - // TRACE("No cache for \"%s\"", s); - } - } - } - bool setpos = false; const coord_t orig_pos = pos; while (len--) { @@ -398,10 +443,7 @@ void BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_t l } else if (c >= 0x20) { uint8_t width; - if (fontcache) - width = drawCharWithCache(x-1, y, fontcache, fontspecs, getMappedChar(c), flags); - else - width = drawCharWithoutCache(x-1, y, font, fontspecs, getMappedChar(c), flags); + width = drawChar(x-1, y, font, fontspecs, getMappedChar(c), flags); INCREMENT_POS(width); } else if (c == 0x1F) { // X-coord prefix @@ -422,7 +464,9 @@ void BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_t l } s++; } - lcdNextPos = pos; + lcdNextPos = pos - offsetX; + + RESTORE_OFFSET(); } void BitmapBuffer::drawBitmapPie(int x0, int y0, const uint16_t * img, int startAngle, int endAngle) @@ -491,8 +535,6 @@ void BitmapBuffer::drawBitmapPatternPie(coord_t x0, coord_t y0, const uint8_t * } } -#if !defined(BOOT) - BitmapBuffer * BitmapBuffer::load(const char * filename) { const char * ext = getFileExtension(filename); @@ -517,7 +559,7 @@ BitmapBuffer * BitmapBuffer::loadMask(const char * filename) BitmapBuffer * BitmapBuffer::loadMaskOnBackground(const char * filename, LcdFlags foreground, LcdFlags background) { - BitmapBuffer * result = NULL; + BitmapBuffer * result = nullptr; BitmapBuffer * mask = BitmapBuffer::loadMask(getThemePath(filename)); if (mask) { result = new BitmapBuffer(BMP_RGB565, mask->getWidth(), mask->getHeight()); @@ -834,4 +876,3 @@ BitmapBuffer * BitmapBuffer::load_stb(const char * filename) stbi_image_free(img); return bmp; } -#endif diff --git a/radio/src/gui/480x272/bitmapbuffer.h b/radio/src/gui/libopenui/bitmapbuffer.h similarity index 71% rename from radio/src/gui/480x272/bitmapbuffer.h rename to radio/src/gui/libopenui/bitmapbuffer.h index 760215198..adf460051 100644 --- a/radio/src/gui/480x272/bitmapbuffer.h +++ b/radio/src/gui/libopenui/bitmapbuffer.h @@ -21,10 +21,19 @@ #ifndef _BITMAP_BUFFER_H_ #define _BITMAP_BUFFER_H_ -#include +#include +#include +#include "libopenui_types.h" #include "colors.h" +#include "board.h" +#include "debug.h" +#include "opentx_types.h" #include "rle.h" +#define MOVE_OFFSET() coord_t offsetX = this->offsetX; x += offsetX; this->offsetX = 0; coord_t offsetY = this->offsetY; y += offsetY; this->offsetY = 0 +#define APPLY_OFFSET() x += offsetX; y += offsetY +#define RESTORE_OFFSET() this->offsetX = offsetX, this->offsetY = offsetY + #if defined(PCBX10) && !defined(SIMU) #define MOVE_PIXEL_RIGHT(p, count) p -= count #else @@ -36,9 +45,6 @@ #define USE_STB -// TODO should go to lcd.h again -typedef int coord_t; -typedef uint32_t LcdFlags; typedef uint16_t display_t; enum BitmapFormats @@ -55,16 +61,64 @@ class BitmapBufferBase format(format), width(width), height(height), + xmax(width), + ymax(height), data(data), data_end(data + (width * height)) { } BitmapBufferBase(uint8_t format, T * data): - BitmapBufferBase(format, data[0], data[1], data + 2) + format(format), + width(*((uint16_t*)data)), + height(*(((uint16_t*)data) + 1)), + xmax(width), + ymax(height), + data(data + 2), + data_end(data + 2 + (width * height)) { } + inline void clearClippingRect() + { + xmin = 0; + xmax = width; + ymin = 0; + ymax = height; + } + + inline void setClippingRect(coord_t xmin, coord_t xmax, coord_t ymin, coord_t ymax) + { + this->xmin = xmin; + this->xmax = xmax; + this->ymin = ymin; + this->ymax = ymax; + } + + inline void getClippingRect(coord_t & xmin, coord_t & xmax, coord_t & ymin, coord_t & ymax) + { + xmin = this->xmin; + xmax = this->xmax; + ymin = this->ymin; + ymax = this->ymax; + } + + inline void setOffset(coord_t offsetX, coord_t offsetY) + { + this->offsetX = offsetX; + this->offsetY = offsetY; + } + + coord_t getOffsetX() const + { + return offsetX; + } + + coord_t getOffsetY() const + { + return offsetY; + } + inline uint8_t getFormat() const { return format; @@ -101,8 +155,14 @@ class BitmapBufferBase protected: uint8_t format; - uint16_t width; - uint16_t height; + coord_t width; + coord_t height; + coord_t xmin = 0; + coord_t xmax; + coord_t ymin = 0; + coord_t ymax; + coord_t offsetX = 0; + coord_t offsetY = 0; T * data; T * data_end; }; @@ -112,23 +172,22 @@ typedef BitmapBufferBase Bitmap; class RLEBitmap: public BitmapBufferBase { -public: - RLEBitmap(uint8_t format, const uint8_t* rle_data) - : BitmapBufferBase(format, 0, 0, NULL) - { - width = *((uint16_t *)rle_data); - height = *(((uint16_t *)rle_data)+1); + public: + RLEBitmap(uint8_t format, const uint8_t* rle_data) + : BitmapBufferBase(format, 0, 0, nullptr) + { + width = *((uint16_t *)rle_data); + height = *(((uint16_t *)rle_data)+1); + uint32_t pixels = width * height; + data = (uint16_t*)malloc(pixels * sizeof(uint16_t)); + rle_decode_8bit((uint8_t*)data, pixels * sizeof(uint16_t), rle_data+4); + data_end = data + pixels; + } - uint32_t pixels = width * height; - data = (uint16_t*)malloc(pixels * sizeof(uint16_t)); - rle_decode_8bit((uint8_t*)data, pixels * sizeof(uint16_t), rle_data+4); - data_end = data + pixels; - } - - ~RLEBitmap() - { - free(data); - } + ~RLEBitmap() + { + free(data); + } }; class BitmapBuffer: public BitmapBufferBase @@ -142,7 +201,7 @@ class BitmapBuffer: public BitmapBufferBase public: BitmapBuffer(uint8_t format, uint16_t width, uint16_t height): - BitmapBufferBase(format, width, height, NULL), + BitmapBufferBase(format, width, height, nullptr), dataAllocated(true) #if defined(DEBUG) , leakReported(false) @@ -175,7 +234,7 @@ class BitmapBuffer: public BitmapBufferBase inline void clear(LcdFlags flags=0) { - drawSolidFilledRect(0, 0, width, height, flags); + drawSolidFilledRect(0, 0, width - offsetX, height - offsetY, flags); } inline void drawPixel(display_t * p, display_t value) @@ -239,13 +298,7 @@ class BitmapBuffer: public BitmapBufferBase void drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness, uint8_t pat, LcdFlags att); - inline void drawSolidFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags) - { - if (!data || h==0 || w==0) return; - if (h<0) { y+=h; h=-h; } - if (w<0) { x+=w; w=-w; } - DMAFillRect(data, width, height, (x>0)?x:0, (y>0)?y:0, w, h, lcdColorTable[COLOR_IDX(flags)]); - } + void drawSolidFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags); void drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t pat, LcdFlags att); @@ -259,33 +312,34 @@ class BitmapBuffer: public BitmapBufferBase void drawBitmapPatternPie(coord_t x0, coord_t y0, const uint8_t * img, LcdFlags flags, int startAngle, int endAngle); -#if !defined(BOOT) static BitmapBuffer * load(const char * filename); static BitmapBuffer * loadMask(const char * filename); static BitmapBuffer * loadMaskOnBackground(const char * filename, LcdFlags foreground, LcdFlags background); -#endif - void drawMask(coord_t x, coord_t y, BitmapBuffer * mask, LcdFlags flags, coord_t offset=0, coord_t width=0); + void drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, LcdFlags flags, coord_t offset=0, coord_t width=0); void drawBitmapPattern(coord_t x, coord_t y, const uint8_t * bmp, LcdFlags flags, coord_t offset=0, coord_t width=0); - uint8_t drawCharWithoutCache(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, int index, LcdFlags flags); + uint8_t drawChar(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, int index, LcdFlags flags); - uint8_t drawCharWithCache(coord_t x, coord_t y, const BitmapBuffer * font, const uint16_t * spec, int index, LcdFlags flags); + void drawSizedText(coord_t x, coord_t y, const char * s, uint8_t len, LcdFlags flags=0); - void drawText(coord_t x, coord_t y, const char * s, LcdFlags flags) + void drawText(coord_t x, coord_t y, const char * s, LcdFlags flags=0) { drawSizedText(x, y, s, 255, flags); } - void drawSizedText(coord_t x, coord_t y, const char * s, uint8_t len, LcdFlags flags); - template void drawBitmap(coord_t x, coord_t y, const T * bmp, coord_t srcx=0, coord_t srcy=0, coord_t w=0, coord_t h=0, float scale=0) { - if (!data || !bmp || x < 0 || x >= width || y < 0 || y >= height) + APPLY_OFFSET(); + + if (x >= xmax) return; + if (y >= ymax) return; + + if (!data || !bmp) return; coord_t srcw = bmp->getWidth(); @@ -295,18 +349,43 @@ class BitmapBuffer: public BitmapBufferBase w = srcw; if (h == 0) h = srch; - if (srcx+w > srcw) + if (srcx + w > srcw) w = srcw - srcx; - if (srcy+h > srch) + if (srcy + h > srch) h = srch - srcy; + if (x < xmin) { + w += x-xmin; + srcx -= x-xmin; + x = xmin; + } + + if (y < ymin) { + h += y-ymin; + srcy -= y-ymin; + y = ymin; + } + + if (x + w > xmax) { + w = xmax - x; + } + + if (y + h > ymax) { + h = ymax - y; + } + + if (w <= 0 || h <= 0) + return; + + // TRACE("x=%d xmin=%d xmax=%d w=%d", x, xmin, xmax, w); + if (scale == 0) { - if (x + w > width) { + /*if (x + w > width) { w = width - x; } if (y + h > height) { h = height - y; - } + }*/ if (bmp->getFormat() == BMP_ARGB4444) { DMACopyAlphaBitmap(data, width, height, x, y, bmp->getData(), srcw, srch, srcx, srcy, w, h); } @@ -355,10 +434,8 @@ class BitmapBuffer: public BitmapBufferBase } protected: -#if !defined(BOOT) static BitmapBuffer * load_bmp(const char * filename); static BitmapBuffer * load_stb(const char * filename); -#endif }; extern BitmapBuffer * lcd; diff --git a/radio/src/gui/libopenui/button.cpp b/radio/src/gui/libopenui/button.cpp new file mode 100644 index 000000000..927e4041f --- /dev/null +++ b/radio/src/gui/libopenui/button.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 +#include "button.h" +#include "draw_functions.h" +#include "lcd.h" +#include "theme.h" + +void Button::onPress() +{ + bool check = (pressHandler && pressHandler()); + if (check != bool(flags & BUTTON_CHECKED)) { + flags ^= BUTTON_CHECKED; + invalidate(); + } +} + +#if defined(HARDWARE_KEYS) +void Button::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + onPress(); + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool Button::onTouchEnd(coord_t x, coord_t y) +{ + if (enabled()) { + onPress(); + if (!(flags & BUTTON_NOFOCUS)) { + setFocus(); + } + } + return true; +} +#endif + +void Button::checkEvents() +{ + Window::checkEvents(); + if (checkHandler) + checkHandler(); +} + +void TextButton::paint(BitmapBuffer * dc) +{ + FormField::paint(dc); + + LcdFlags textColor = TEXT_COLOR; + + if (checked()) { + if (hasFocus()) { + drawSolidRect(dc, 0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); + dc->drawSolidFilledRect(3, 3, rect.w - 6, rect.h - 6, TEXT_INVERTED_BGCOLOR); + } + else { + drawSolidRect(dc, 0, 0, rect.w, rect.h, 1, CURVE_AXIS_COLOR); + dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, TEXT_INVERTED_BGCOLOR); + } + textColor = TEXT_INVERTED_COLOR; + } + else { + if (flags & BUTTON_BACKGROUND) { + if (hasFocus()) { + drawSolidRect(dc, 0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); + dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, CURVE_AXIS_COLOR); + } + else { + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, CURVE_AXIS_COLOR); + } + } + else { + if (hasFocus()) { + drawSolidRect(dc, 0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); + } + else { + drawSolidRect(dc, 0, 0, rect.w, rect.h, 1, CURVE_AXIS_COLOR); + } + } + } + + dc->drawText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2, text.c_str(), CENTERED | textColor); +} + +const uint8_t __alpha_button_on[] { +#include "alpha_button_on.lbm" +}; +Bitmap ALPHA_BUTTON_ON(BMP_ARGB4444, (const uint16_t*)__alpha_button_on); + +const uint8_t __alpha_button_off[] { +#include "alpha_button_off.lbm" +}; +Bitmap ALPHA_BUTTON_OFF(BMP_ARGB4444, (const uint16_t*)__alpha_button_off); + +void IconButton::paint(BitmapBuffer * dc) +{ + dc->drawBitmap(0, 0, theme->getIconButtonBitmap(icon, checked())); +} + +FabIconButton::FabIconButton(Window * parent, coord_t x, coord_t y, uint8_t icon, std::function pressHandler, uint8_t flags): + Button(parent, { x - 34, y - 34, 68, 68 }, pressHandler, flags), + icon(icon) +{ +} + +void FabIconButton::paint(BitmapBuffer * dc) +{ + // TODO ? dc->drawBitmap(0, 0, theme->getIconButtonBitmap(icon, checked())); + dc->drawBitmap(0, 0, checked() ? &ALPHA_BUTTON_ON : &ALPHA_BUTTON_OFF); + const BitmapBuffer * mask = theme->getIconMask(icon); + if (mask) { + dc->drawMask((68 - mask->getWidth()) / 2, (68 - mask->getHeight()) / 2, mask, TEXT_BGCOLOR); + } +} diff --git a/radio/src/gui/libopenui/button.h b/radio/src/gui/libopenui/button.h new file mode 100644 index 000000000..e193474e3 --- /dev/null +++ b/radio/src/gui/libopenui/button.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _BUTTON_H_ +#define _BUTTON_H_ + +#include "form.h" +#include + +enum ButtonFlags { + BUTTON_BACKGROUND = 1, + BUTTON_CHECKED = 2, + BUTTON_NOFOCUS = 4, + BUTTON_CHECKED_ON_FOCUS = 8, + BUTTON_DISABLED = 16, +}; + +class Button : public FormField { + public: + Button(Window * parent, const rect_t & rect, std::function pressHandler=nullptr, uint8_t flags=0): + FormField(parent, rect), + pressHandler(pressHandler), + flags(flags) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Button"; + } +#endif + + void enable(bool enabled=true) + { + if (!enabled != bool(flags & BUTTON_DISABLED)) { + flags ^= BUTTON_DISABLED; + invalidate(); + } + } + + void disable() + { + enable(false); + } + + void check(bool checked=true) + { + if (checked != bool(flags & BUTTON_CHECKED)) { + flags ^= BUTTON_CHECKED; + invalidate(); + } + } + + bool enabled() + { + return !(flags & BUTTON_DISABLED); + } + + bool checked() + { + if (flags & BUTTON_CHECKED_ON_FOCUS) + return hasFocus(); + else + return flags & BUTTON_CHECKED; + } + + void setPressHandler(std::function handler) + { + pressHandler = std::move(handler); + } + + void setCheckHandler(std::function handler) + { + checkHandler = std::move(handler); + } + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event); +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + void checkEvents() override; + + protected: + std::function pressHandler; + std::function checkHandler; + uint8_t flags; + + void onPress(); +}; + +class TextButton : public Button { + public: + TextButton(Window * parent, const rect_t & rect, std::string text, std::function pressHandler=nullptr, uint8_t flags=BUTTON_BACKGROUND): + Button(parent, rect, pressHandler, flags), + text(std::move(text)) + { + windowFlags = OPAQUE; + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TextButton \"" + text + "\""; + } +#endif + + void setText(std::string value) + { + if (value != text) { + text = std::move(value); + invalidate(); + } + } + + void paint(BitmapBuffer * dc) override; + + protected: + std::string text; +}; + +class IconButton: public Button { + public: + IconButton(Window * parent, const rect_t & rect, uint8_t icon, std::function pressHandler, uint8_t flags=0): + Button(parent, rect, pressHandler, flags), + icon(icon) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "IconButton(" + std::to_string(icon) + ")"; + } +#endif + + void paint(BitmapBuffer * dc) override; + + protected: + uint8_t icon; +}; + +class FabIconButton: public Button { + public: + FabIconButton(Window * parent, coord_t x, coord_t y, uint8_t icon, std::function pressHandler, uint8_t flags=0); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "FabIconButton(" + std::to_string(icon) + ")"; + } +#endif + + void paint(BitmapBuffer * dc) override; + + protected: + uint8_t icon; +}; + +#endif diff --git a/radio/src/gui/libopenui/checkbox.cpp b/radio/src/gui/libopenui/checkbox.cpp new file mode 100644 index 000000000..1d11e0839 --- /dev/null +++ b/radio/src/gui/libopenui/checkbox.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 +#include "checkbox.h" +#include "draw_functions.h" + +void CheckBox::paint(BitmapBuffer * dc) +{ + bool hasFocus = this->hasFocus(); + uint8_t value = getValue(); + if (hasFocus) { + dc->drawSolidFilledRect(0, 2, 16, 16, TEXT_INVERTED_BGCOLOR); + if (value) { + dc->drawSolidFilledRect(2, 4, 12, 12, TEXT_BGCOLOR); + dc->drawSolidFilledRect(3, 5, 10, 10, SCROLLBOX_COLOR); + } + else { + dc->drawSolidFilledRect(2, 4, 12, 12, TEXT_BGCOLOR); + } + } + else { + if (value) { + dc->drawSolidFilledRect(2, 4, 12, 12, SCROLLBOX_COLOR); + drawSolidRect(dc, 0, 2, 16, 16, 1, CURVE_AXIS_COLOR); + } + else { + drawSolidRect(dc, 0, 2, 16, 16, 1, CURVE_AXIS_COLOR); + } + } +} + +#if defined(HARDWARE_KEYS) +void CheckBox::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + setValue(!getValue()); + invalidate({0, 0, 20, 20}); + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool CheckBox::onTouchEnd(coord_t x, coord_t y) +{ + setValue(!getValue()); + setFocus(); + invalidate({0, 0, 20, 20}); + return true; +} +#endif diff --git a/radio/src/gui/libopenui/checkbox.h b/radio/src/gui/libopenui/checkbox.h new file mode 100644 index 000000000..5d33fa5c0 --- /dev/null +++ b/radio/src/gui/libopenui/checkbox.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _CHECKBOX_H_ +#define _CHECKBOX_H_ + +#include "form.h" + +class CheckBox : public FormField { + public: + CheckBox(Window * parent, const rect_t & rect, std::function getValue, std::function setValue, + LcdFlags flags = 0) : + FormField(parent, rect), + getValue(getValue), + setValue(setValue) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "CheckBox"; + } +#endif + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + const char * label; + std::function getValue; + std::function setValue; +}; + +#endif // _CHECKBOX_H_ diff --git a/radio/src/gui/libopenui/choice.cpp b/radio/src/gui/libopenui/choice.cpp new file mode 100644 index 000000000..04addf03b --- /dev/null +++ b/radio/src/gui/libopenui/choice.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "choice.h" +#include "menu.h" +#include "draw_functions.h" +#include "strhelpers.h" +#include "opentx.h" + +const uint8_t LBM_DROPDOWN[] = { +#include "mask_dropdown.lbm" +}; + +Choice::Choice(Window * parent, const rect_t &rect, const char * values, int16_t vmin, int16_t vmax, + std::function getValue, std::function setValue, LcdFlags flags) : + FormField(parent, rect), + values(values), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + setValue(std::move(setValue)), + flags(flags) +{ +} + +void Choice::paint(BitmapBuffer * dc) +{ + FormField::paint(dc); + + LcdFlags textColor; + if (editMode) + textColor = TEXT_INVERTED_COLOR; + else if (hasFocus()) + textColor = TEXT_INVERTED_BGCOLOR; + else + textColor = 0; + + if (textHandler) + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, textHandler(getValue()).c_str(), textColor); + else + drawTextAtIndex(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, values, getValue() - vmin, flags | textColor); + + dc->drawBitmapPattern(rect.w - 14, (rect.h - 5) / 2, LBM_DROPDOWN, textColor); +} + +#if defined(HARDWARE_KEYS) +void Choice::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + editMode = true; + invalidate(); + openMenu(); + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +void Choice::openMenu() +{ + auto menu = new Menu(); + auto value = getValue(); + int count = 0; + int current = -1; + + for (int i = vmin; i <= vmax; ++i) { + if (isValueAvailable && !isValueAvailable(i)) + continue; + if (textHandler) { + menu->addLine(textHandler(i), [=]() { + setValue(i); + }); + } + else { + menu->addLine(TEXT_AT_INDEX(values, i - vmin), [=]() { + setValue(i); + }); + } + if (value == i) { + current = count; + } + ++count; + } + + if (current >= 0) { + menu->select(current); + } + + menu->setCloseHandler([=]() { + editMode = false; + setFocus(); + }); +} + +#if defined(HARDWARE_TOUCH) +bool Choice::onTouchEnd(coord_t, coord_t) +{ + openMenu(); + setFocus(); + return true; +} +#endif diff --git a/radio/src/gui/libopenui/choice.h b/radio/src/gui/libopenui/choice.h new file mode 100644 index 000000000..afc99044e --- /dev/null +++ b/radio/src/gui/libopenui/choice.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _CHOICE_H_ +#define _CHOICE_H_ + +#include "form.h" +#include + +class Choice : public FormField { + public: + Choice(Window * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, LcdFlags flags = 0); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Choice"; + } +#endif + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + void setSetValueHandler(std::function handler) + { + setValue = std::move(handler); + } + + void setAvailableHandler(std::function handler) + { + isValueAvailable = std::move(handler); + } + + void setTextHandler(std::function handler) + { + textHandler = std::move(handler); + } + + protected: + const char * values; + int16_t vmin; + int16_t vmax; + std::function getValue; + std::function setValue; + std::function isValueAvailable; + std::function textHandler; + LcdFlags flags; + void openMenu(); +}; + +#endif // _CHOICE_H_ diff --git a/radio/src/gui/libopenui/coloredit.cpp b/radio/src/gui/libopenui/coloredit.cpp new file mode 100644 index 000000000..180335ada --- /dev/null +++ b/radio/src/gui/libopenui/coloredit.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "coloredit.h" +#include "numberedit.h" +#include "bitfield.h" +#include "lcd.h" + +constexpr uint8_t PART_BITS[3][2] = { {0, 5}, {5, 6}, {11, 5} }; + +class ColorBox: public Window { + public: + ColorBox(Window * parent, const rect_t & rect, LcdFlags color): + Window(parent, rect), + color(color) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "ColorBox"; + } +#endif + + void paint(BitmapBuffer * dc) override + { + lcdSetColor(color); + dc->drawSolidFilledRect(0, 0, width(), height(), TEXT_COLOR); + dc->drawSolidFilledRect(1, 1, width() - 2, height() - 2, CUSTOM_COLOR); + } + + void setColor(LcdFlags value) + { + color = value; + invalidate(); + } + + protected: + LcdFlags color; +}; + +ColorEdit::ColorEdit(Window * parent, const rect_t & rect, std::function getValue, std::function setValue): + Window(parent, rect) +{ + auto width = rect.w / 4 - 5; + + // The color box + auto box = new ColorBox(this, {0, 0, rect.w - 3 * width, rect.h}, getValue()); + + // The 3 parts of RGB + for (uint8_t part = 0; part < 3; part++) { + new NumberEdit(this, {rect.w - (3 - part) * width + 5, 0, width - 5, rect.h}, 0, (1 << PART_BITS[part][1]) - 1, + [=]() { + return BF_GET(getValue(), PART_BITS[part][0], PART_BITS[part][1]); + }, + [=](uint8_t newValue) { + uint16_t value = getValue(); + BF_SET(value, newValue, PART_BITS[part][0], PART_BITS[part][1]); + setValue(value); + box->setColor(value); + }); + } + +} + +ColorEdit::~ColorEdit() +{ + deleteChildren(); +} diff --git a/radio/src/gui/libopenui/coloredit.h b/radio/src/gui/libopenui/coloredit.h new file mode 100644 index 000000000..4fe2d4d38 --- /dev/null +++ b/radio/src/gui/libopenui/coloredit.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _COLOREDIT_H_ +#define _COLOREDIT_H_ + +#include "window.h" + +class ColorEdit : public Window { + public: + ColorEdit(Window * parent, const rect_t & rect, std::function getValue, std::function setValue = nullptr); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "ColorEdit"; + } +#endif + + ~ColorEdit() override; +}; + +#endif // _COLOREDIT_H_ diff --git a/radio/src/gui/libopenui/curve.cpp b/radio/src/gui/libopenui/curve.cpp new file mode 100644 index 000000000..ac0bcbb0b --- /dev/null +++ b/radio/src/gui/libopenui/curve.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "curve.h" +#include "lcd.h" +#include "bitmaps.h" +#include "strhelpers.h" + +void Curve::drawBackground(BitmapBuffer * dc) +{ + lcdSetColor(RGB(0xE0, 0xE0, 0xE0)); + dc->clear(CUSTOM_COLOR); + + // Axis + dc->drawSolidHorizontalLine(0, height()/2, width(), CURVE_AXIS_COLOR); + dc->drawSolidVerticalLine(width()/2, 0, height(), CURVE_AXIS_COLOR); + + // Extra lines + dc->drawVerticalLine(width()/4, 0, height(), STASHED, CURVE_AXIS_COLOR); + dc->drawVerticalLine(width()*3/4, 0, height(), STASHED, CURVE_AXIS_COLOR); + dc->drawHorizontalLine(0, height()/4, width(), STASHED, CURVE_AXIS_COLOR); + dc->drawHorizontalLine(0, height()*3/4, width(), STASHED, CURVE_AXIS_COLOR); + + // Outside border + // drawSolidRect(dc, 0, 0, width(), height(), 1, TEXT_COLOR); +} + +coord_t Curve::getPointX(int x) +{ + return limit(0, + width() / 2 + divRoundClosest(x * width() / 2, RESX), + width() - 1); +} + +coord_t Curve::getPointY(int y) +{ + return limit(0, + height() / 2 - divRoundClosest(y * height() / 2, RESX), + height() - 1); +} + +void Curve::drawCurve(BitmapBuffer * dc) +{ + auto prev = (coord_t) -1; + + for (int x = 0; x < width(); x++) { + coord_t y = getPointY(function(divRoundClosest((x - width() / 2) * RESX, width() / 2))); + if (prev >= 0) { + if (prev < y) { + for (int tmp = prev; tmp <= y; tmp++) { + dc->drawBitmapPattern(x - 2, tmp - 2, LBM_POINT, TEXT_COLOR); + } + } + else { + for (int tmp = y; tmp <= prev; tmp++) { + dc->drawBitmapPattern(x - 2, tmp - 2, LBM_POINT, TEXT_COLOR); + } + } + } + prev = y; + } +} + +void Curve::drawPosition(BitmapBuffer * dc) +{ + int valueX = position(); + int valueY = function(valueX); + + coord_t x = getPointX(valueX); + coord_t y = getPointY(valueY); + + // the 2 lines + dc->drawSolidHorizontalLine(0, y, width(), CURVE_CURSOR_COLOR); + dc->drawSolidVerticalLine(x, 0, height(), CURVE_CURSOR_COLOR); + + // the point (white inside) + dc->drawBitmapPattern(x-4, y-4, LBM_CURVE_POINT, CURVE_CURSOR_COLOR); + dc->drawBitmapPattern(x-4, y-4, LBM_CURVE_POINT_CENTER, TEXT_BGCOLOR); + + char coords[16]; + strAppendSigned(strAppend(strAppendSigned(coords, calcRESXto100(valueX)), ","), calcRESXto100(valueY)); + dc->drawSolidFilledRect(10, 11, 1 + getTextWidth(coords, 0, SMLSIZE), 17, CURVE_CURSOR_COLOR); + dc->drawText(11, 10, coords, SMLSIZE|TEXT_BGCOLOR); +} + +void Curve::drawPoint(BitmapBuffer * dc, const CurvePoint & point) +{ + coord_t x = getPointX(point.coords.x); + coord_t y = getPointY(point.coords.y); + + dc->drawBitmapPattern(x-4, y-4, LBM_CURVE_POINT, point.flags); + dc->drawBitmapPattern(x-4, y-4, LBM_CURVE_POINT_CENTER, TEXT_BGCOLOR); +} + +void Curve::paint(BitmapBuffer * dc) +{ + drawBackground(dc); + drawCurve(dc); + for (auto point: points) { + drawPoint(dc, point); + } + if (position) { + drawPosition(dc); + } +} + +void Curve::addPoint(const point_t & point, LcdFlags flags) +{ + points.push_back({point, flags}); + invalidate(); +} + +void Curve::clearPoints() +{ + points.clear(); + invalidate(); +} diff --git a/radio/src/gui/libopenui/curve.h b/radio/src/gui/libopenui/curve.h new file mode 100644 index 000000000..6805191d3 --- /dev/null +++ b/radio/src/gui/libopenui/curve.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + + +#ifndef _CURVE_H_ +#define _CURVE_H_ + +#include "window.h" + +struct CurvePoint { + point_t coords; + LcdFlags flags; +}; + +class Curve: public Window { + public: + Curve(Window * parent, const rect_t & rect, std::function function, std::function position=nullptr): + Window(parent, rect, OPAQUE), + function(std::move(function)), + position(std::move(position)) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Curve"; + } +#endif + + void checkEvents() override + { + // will always force a full window refresh + if (position) { + invalidate(); + } + } + + void addPoint(const point_t & point, LcdFlags flags); + + void clearPoints(); + + void paint(BitmapBuffer * dc) override; + + protected: + std::function function; + std::function position; + std::list points; + void drawBackground(BitmapBuffer * dc); + void drawCurve(BitmapBuffer * dc); + void drawPosition(BitmapBuffer * dc); + void drawPoint(BitmapBuffer * dc, const CurvePoint & point); + coord_t getPointX(int x); + coord_t getPointY(int y); +}; + +#endif // _CURVE_H_ diff --git a/radio/src/gui/libopenui/curveedit.cpp b/radio/src/gui/libopenui/curveedit.cpp new file mode 100644 index 000000000..f439e35e2 --- /dev/null +++ b/radio/src/gui/libopenui/curveedit.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "curveedit.h" +#include "keyboard_curve.h" +#include "opentx.h" // TODO for applyCustomCurve + +CurveEdit::CurveEdit(Window * parent, const rect_t &rect, uint8_t index) : + Curve(parent, rect, [=](int x) -> int { + return applyCustomCurve(x, index); + }), + index(index), + current(0) +{ + update(); +} + +void CurveEdit::update() +{ + clearPoints(); + CurveInfo & curve = g_model.curves[index]; + for (uint8_t i = 0; i < 5 + curve.points; i++) { + if (hasFocus() && current == i) { + position = [=] () -> int { + return getPoint(index, i).x; + }; + } + else { + addPoint(getPoint(index, i), TEXT_COLOR); + } + } + invalidate(); +} + +#if defined(HARDWARE_TOUCH) +bool CurveEdit::onTouchEnd(coord_t x, coord_t y) +{ + if (!hasFocus()) { + setFocus(); + update(); + } + + CurveKeyboard * keyboard = CurveKeyboard::instance(); + if (keyboard->getField() != this) { + keyboard->setField(this); + } + + CurveInfo & curve = g_model.curves[index]; + for (int i=0; i<5 + curve.points; i++) { + if (i != current) { + point_t point = getPoint(index, i); + if (abs(getPointX(point.x) - x) <= 10 && abs(getPointY(point.y) - y) <= 10) { + current = i; + update(); + break; + } + } + } + + return true; +} + +void CurveEdit::onFocusLost() +{ + CurveKeyboard::instance()->disable(true); +} +#endif + +void CurveEdit::next() +{ + if (++current == points.size()) { + current = 0; + } + update(); +} + +void CurveEdit::previous() +{ + if (current-- == 0) { + current = points.size() - 1; + } + update(); +} + +void CurveEdit::up() +{ + int8_t & point = curveAddress(index)[current]; + point = min(100, ++point); + storageDirty(EE_MODEL); + invalidate(); +} + +void CurveEdit::down() +{ + int8_t & point = curveAddress(index)[current]; + point = max(-100, --point); + storageDirty(EE_MODEL); + invalidate(); +} + +void CurveEdit::right() +{ + CurveInfo & curve = g_model.curves[index]; + if (curve.type == CURVE_TYPE_CUSTOM && current != 0 && current != curve.points - 1) { + int8_t * points = curveAddress(index); + int8_t * point = &points[5 + curve.points + current - 1]; + int8_t xmax = (current == (curve.points - 2) ? +100 : *(point + 1)); + *point = min(*point + 1, xmax-1); + storageDirty(EE_MODEL); + invalidate(); + } +} + +void CurveEdit::left() +{ + CurveInfo & curve = g_model.curves[index]; + if (curve.type == CURVE_TYPE_CUSTOM && current != 0 && current != curve.points - 1) { + int8_t * points = curveAddress(index); + int8_t * point = &points[5 + curve.points + current - 1]; + int8_t xmin = (current == 1 ? -100 : *(point - 1)); + *point = max(xmin+1, *point - 1); + storageDirty(EE_MODEL); + invalidate(); + } +} + +bool CurveEdit::isCustomCurve() +{ + return g_model.curves[index].type == CURVE_TYPE_CUSTOM; +} diff --git a/radio/src/gui/libopenui/curveedit.h b/radio/src/gui/libopenui/curveedit.h new file mode 100644 index 000000000..98fb76f74 --- /dev/null +++ b/radio/src/gui/libopenui/curveedit.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _CURVEEDIT_H_ +#define _CURVEEDIT_H_ + +#include "curve.h" + +class CurveEdit: public Curve { + friend class CurveKeyboard; + + public: + CurveEdit(Window * parent, const rect_t & rect, uint8_t index); + + void checkEvents() override + { + // no permanent refresh + } + + void update(); + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; + + void onFocusLost() override; +#endif + + protected: + uint8_t index; + uint8_t current; + void next(); + void previous(); + void up(); + void down(); + void right(); + void left(); + bool isCustomCurve(); +}; + +#endif // _CURVEEDIT_H_ diff --git a/radio/src/gui/libopenui/custom_curve_choice.cpp b/radio/src/gui/libopenui/custom_curve_choice.cpp new file mode 100644 index 000000000..5bb3f32f2 --- /dev/null +++ b/radio/src/gui/libopenui/custom_curve_choice.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "choice.h" +#include "menu.h" +#include "draw_functions.h" +#include "strhelpers.h" +#include "opentx.h" + +extern const uint8_t LBM_DROPDOWN[]; + +CustomCurveChoice::CustomCurveChoice(Window * parent, const rect_t &rect, int16_t vmin, int16_t vmax, + std::function getValue, std::function setValue, LcdFlags flags) : + FormField(parent, rect), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + setValue(std::move(setValue)), + flags(flags) +{ +} + +void CustomCurveChoice::paint(BitmapBuffer * dc) +{ + bool hasFocus = this->hasFocus(); + char s[8]; + int16_t value = getValue(); + LcdFlags textColor = 0; + LcdFlags lineColor = CURVE_AXIS_COLOR; + if (hasFocus) { + textColor = TEXT_INVERTED_BGCOLOR; + lineColor = TEXT_INVERTED_BGCOLOR; + } + dc->drawText(3, 2, getCurveString(s, value), flags | textColor); + drawSolidRect(dc, 0, 0, rect.w, rect.h, 1, lineColor); + dc->drawBitmapPattern(rect.w - 14, (rect.h - 5) / 2, LBM_DROPDOWN, lineColor); +} + +#if defined(HARDWARE_TOUCH) +bool CustomCurveChoice::onTouchEnd(coord_t x, coord_t y) +{ + auto menu = new Menu(); + auto value = getValue(); + int count = 0; + int current = -1; + char s[8]; + + for (int i = vmin; i <= vmax; ++i) { + menu->addLine(getCurveString(s, i), [=]() { + setValue(i); + }); + + if (value == i) { + current = count; + } + ++count; + } + + if (current >= 0) { + menu->select(current); + } + + setFocus(); + return true; +} +#endif diff --git a/radio/src/gui/libopenui/custom_curve_choice.h b/radio/src/gui/libopenui/custom_curve_choice.h new file mode 100644 index 000000000..1d6df4715 --- /dev/null +++ b/radio/src/gui/libopenui/custom_curve_choice.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _CHOICE_H_ +#define _CHOICE_H_ + +#include "form.h" +#include + +class CustomCurveChoice : public FormField { + public: + CustomCurveChoice(Window * parent, const rect_t & rect, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue, LcdFlags flags = 0); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "CustomCurveChoice"; + } +#endif + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + int16_t vmin; + int16_t vmax; + std::function getValue; + std::function setValue; + LcdFlags flags; +}; + +#endif // _CHOICE_H_ diff --git a/radio/src/gui/libopenui/dialog.cpp b/radio/src/gui/libopenui/dialog.cpp new file mode 100644 index 000000000..20322e8f1 --- /dev/null +++ b/radio/src/gui/libopenui/dialog.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "dialog.h" +#include "mainwindow.h" +#include "opentx.h" + +Dialog::Dialog(std::string title, const rect_t rect): + Window(&mainWindow, rect, OPAQUE), + title(std::move(title)), + previousFocus(focusWindow) +{ + bringToTop(); + FormField::clearCurrentField(); + setFocus(); +} + +void Dialog::paint(BitmapBuffer * dc) +{ + dc->drawRect(0, 0, width() - 1, height() - 1, 1, SOLID, TEXT_COLOR); + dc->drawSolidFilledRect(1, 1, width() - 3, PAGE_LINE_HEIGHT, TITLE_BGCOLOR); + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, title.c_str(), MENU_TITLE_COLOR); + + lcdSetColor(g_eeGeneral.themeData.options[0].unsignedValue); + dc->drawSolidFilledRect(1, PAGE_LINE_HEIGHT + 1, width() - 3, height() - PAGE_LINE_HEIGHT - 3, CUSTOM_COLOR); +} + +void Dialog::deleteLater() +{ + if (previousFocus) { + previousFocus->setFocus(); + } + + Window::deleteLater(); +} + +#define ALERT_FRAME_TOP 70 +#define ALERT_FRAME_PADDING 10 +#define ALERT_BITMAP_PADDING 15 +#define ALERT_TITLE_LEFT 135 +#define ALERT_TITLE_LINE_HEIGHT 30 +#define ALERT_MESSAGE_TOP 210 +#define ALERT_ACTION_TOP 230 +#define ALERT_BUTTON_TOP 300 + +FullScreenDialog::FullScreenDialog(uint8_t type, std::string title, std::string message, std::function confirmHandler): + Dialog(title, {0, 0, LCD_W, LCD_H}), + type(type), + message(std::move(message)), + confirmHandler(confirmHandler) +{ +#if defined(HARDWARE_TOUCH) + new FabIconButton(this, LCD_W - 50, ALERT_BUTTON_TOP, ICON_NEXT, + [=]() -> uint8_t { + if (confirmHandler) + confirmHandler(); + return 0; + }); +#endif +} + +FullScreenDialog::~FullScreenDialog() +{ +} + +void FullScreenDialog::paint(BitmapBuffer * dc) +{ + theme->drawBackground(dc); + + if (type == WARNING_TYPE_ALERT || type == WARNING_TYPE_ASTERISK) + dc->drawBitmap(ALERT_BITMAP_PADDING, ALERT_FRAME_TOP + ALERT_BITMAP_PADDING, theme->asterisk); + else if (type == WARNING_TYPE_INFO) + dc->drawBitmap(ALERT_BITMAP_PADDING, ALERT_FRAME_TOP + ALERT_BITMAP_PADDING, theme->busy); + else // confirmation + dc->drawBitmap(ALERT_BITMAP_PADDING, ALERT_FRAME_TOP + ALERT_BITMAP_PADDING, theme->question); + + if (type == WARNING_TYPE_ALERT) { +#if defined(TRANSLATIONS_FR) || defined(TRANSLATIONS_IT) || defined(TRANSLATIONS_CZ) + dc->drawText(ALERT_TITLE_LEFT, ALERT_FRAME_TOP + ALERT_FRAME_PADDING, STR_WARNING, ALARM_COLOR|DBLSIZE); + dc->drawText(ALERT_TITLE_LEFT, ALERT_FRAME_TOP + ALERT_FRAME_PADDING + ALERT_TITLE_LINE_HEIGHT, title, ALARM_COLOR|DBLSIZE); +#else + dc->drawText(ALERT_TITLE_LEFT, ALERT_FRAME_TOP + ALERT_FRAME_PADDING, title.c_str(), ALARM_COLOR|DBLSIZE); + dc->drawText(ALERT_TITLE_LEFT, ALERT_FRAME_TOP + ALERT_FRAME_PADDING + ALERT_TITLE_LINE_HEIGHT, STR_WARNING, ALARM_COLOR|DBLSIZE); +#endif + } + else if (!title.empty()) { + dc->drawText(ALERT_TITLE_LEFT, ALERT_FRAME_TOP + ALERT_FRAME_PADDING, title.c_str(), ALARM_COLOR|DBLSIZE); + } + + if (!message.empty()) { + dc->drawText(ALERT_FRAME_PADDING+5, ALERT_MESSAGE_TOP, message.c_str(), MIDSIZE); + } + +#if 0 + if (action) { + // dc->drawText(ALERT_FRAME_PADDING+5, ALERT_ACTION_TOP, action); + } +#endif +} + +#if defined(HARDWARE_KEYS) +void FullScreenDialog::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + deleteLater(); + if (confirmHandler) + confirmHandler(); + } + else if (event == EVT_KEY_BREAK(KEY_EXIT)) { + deleteLater(); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool FullScreenDialog::onTouchEnd(coord_t x, coord_t y) +{ + Window::onTouchEnd(x, y); + deleteLater(); + return true; +} +#endif + +void FullScreenDialog::checkEvents() +{ + Window::checkEvents(); + if (closeCondition && closeCondition()) { + deleteLater(); + } +} + +void FullScreenDialog::deleteLater() +{ + if (previousFocus) { + previousFocus->setFocus(); + } + if (running) { + running = false; + } + else { + Window::deleteLater(); + } +} + +void FullScreenDialog::runForever() +{ + running = true; + + while (running) { + auto check = pwrCheck(); + if (check == e_power_off) { + boardOff(); + } + else if (check == e_power_press) { + RTOS_WAIT_MS(20); + continue; + } + + checkBacklight(); + wdt_reset(); + + RTOS_WAIT_MS(20); + mainWindow.run(false); + } + + Window::deleteLater(); +} + +void raiseAlert(const char * title, const char * msg, const char * info, uint8_t sound) +{ + AUDIO_ERROR_MESSAGE(sound); + auto dialog = new FullScreenDialog(WARNING_TYPE_ALERT, title, msg); + dialog->runForever(); +} + +MessageDialog::MessageDialog(const char * title, const char * message): + Dialog(title, {50, 73, LCD_W - 100, LCD_H - 146}) +{ + new StaticText(this, {0, height() / 2, width(), PAGE_LINE_HEIGHT}, message, CENTERED); +} + +#if defined(HARDWARE_KEYS) +void MessageDialog::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_ENTER)) { + deleteLater(); + } +} +#endif + +ConfirmDialog::ConfirmDialog(const char *title, const char *message, std::function confirmHandler) : + Dialog(title, {50, 73, LCD_W - 100, LCD_H - 146}), + confirmHandler(std::move(confirmHandler)) +{ + new StaticText(this, {0, height() / 2, width(), PAGE_LINE_HEIGHT}, message, CENTERED); +} + +#if defined(HARDWARE_KEYS) +void ConfirmDialog::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + confirmHandler(); + deleteLater(); + } + else if (event == EVT_KEY_BREAK(KEY_EXIT)) { + deleteLater(); + } +} +#endif diff --git a/radio/src/gui/libopenui/dialog.h b/radio/src/gui/libopenui/dialog.h new file mode 100644 index 000000000..d8745aa54 --- /dev/null +++ b/radio/src/gui/libopenui/dialog.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _DIALOG_H_ +#define _DIALOG_H_ + +#include "button.h" + +enum DialogType { + WARNING_TYPE_ALERT, + WARNING_TYPE_ASTERISK, + WARNING_TYPE_CONFIRM, + WARNING_TYPE_INPUT, + WARNING_TYPE_INFO +}; + +class Dialog: public Window { + public: + Dialog(std::string title, const rect_t rect); + + void paint(BitmapBuffer * dc) override; + + void deleteLater(); + + protected: + std::string title; + Window * previousFocus; +}; + +class MessageDialog: public Dialog { + public: + MessageDialog(const char * title, const char * message); + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif +}; + +class ConfirmDialog: public Dialog { + public: + ConfirmDialog(const char * title, const char * message, std::function confirmHandler); + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + + protected: + std::function confirmHandler; +}; + +class FullScreenDialog : public Dialog { + public: + FullScreenDialog(uint8_t type, std::string title, std::string message="", std::function confirmHandler=nullptr); + + ~FullScreenDialog() override; + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "FullScreenDialog"; + } +#endif + + void setMessage(std::string text) + { + message = std::move(text); + invalidate(); + } + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + void deleteLater(); + + void checkEvents() override; + + void setCloseCondition(std::function handler) + { + closeCondition = std::move(handler); + } + + void runForever(); + + protected: + uint8_t type; + std::string message; + bool running = false; + std::function closeCondition; + std::function confirmHandler; +}; + +inline void POPUP_INFORMATION(const char * str) +{ + new MessageDialog("Message", str); +} + +#endif // _CONFIRMATION_H_ diff --git a/radio/src/gui/libopenui/draw_functions.cpp b/radio/src/gui/libopenui/draw_functions.cpp new file mode 100644 index 000000000..ee7c6d9ba --- /dev/null +++ b/radio/src/gui/libopenui/draw_functions.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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" + +void drawTextAtIndex(BitmapBuffer * dc, coord_t x, coord_t y, const char * s, uint8_t idx, LcdFlags flags) +{ + char length = *s++; + dc->drawSizedText(x, y, s+length*idx, length, flags & ~ZCHAR); +} + +void drawNumber(BitmapBuffer * dc, coord_t x, coord_t y, int32_t val, LcdFlags flags, uint8_t len, const char * prefix, const char * suffix) +{ + char str[48+1]; // max=16 for the prefix, 16 chars for the number, 16 chars for the suffix + char *s = str+32; + *s = '\0'; + int idx = 0; + int mode = MODE(flags); + bool neg = false; + if (val < 0) { + val = -val; + neg = true; + } + do { + *--s = '0' + (val % 10); + ++idx; + val /= 10; + if (mode!=0 && idx==mode) { + mode = 0; + *--s = '.'; + if (val==0) + *--s = '0'; + } + } while (val!=0 || mode>0 || (mode==MODE(LEADING0) && idxdrawText(x, y, s, flags); +} + +void drawSolidRect(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness, LcdFlags att) +{ + dc->drawSolidFilledRect(x, y, thickness, h, att); + dc->drawSolidFilledRect(x+w-thickness, y, thickness, h, att); + dc->drawSolidFilledRect(x, y, w, thickness, att); + dc->drawSolidFilledRect(x, y+h-thickness, w, thickness, att); +} diff --git a/radio/src/gui/libopenui/draw_functions.h b/radio/src/gui/libopenui/draw_functions.h new file mode 100644 index 000000000..ab63b727b --- /dev/null +++ b/radio/src/gui/libopenui/draw_functions.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _LIBOPENUI_DRAW_FUNCTIONS_H_ +#define _LIBOPENUI_DRAW_FUNCTIONS_H_ + +#include +#include "libopenui_types.h" + +void drawTextAtIndex(BitmapBuffer * dc, coord_t x, coord_t y, const char * s, uint8_t idx, LcdFlags flags=0); +void drawNumber(BitmapBuffer * dc, coord_t x, coord_t y, int32_t val, LcdFlags flags=0, uint8_t len=0, const char * prefix=nullptr, const char * suffix=nullptr); +void drawSolidRect(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness=1, LcdFlags flags=0); +void drawSource(BitmapBuffer * dc, coord_t x, coord_t y, mixsrc_t idx, LcdFlags flags=0); +void drawSwitch(BitmapBuffer * dc, coord_t x, coord_t y, int32_t idx, LcdFlags flags=0); +void drawTrimMode(BitmapBuffer * dc, coord_t x, coord_t y, uint8_t phase, uint8_t idx, LcdFlags flags = 0); + +#endif // _LIBOPENUI_DRAW_FUNCTIONS_H_ diff --git a/radio/src/gui/libopenui/filechoice.cpp b/radio/src/gui/libopenui/filechoice.cpp new file mode 100644 index 000000000..b36cdd9b6 --- /dev/null +++ b/radio/src/gui/libopenui/filechoice.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "filechoice.h" +#include "menu.h" +#include "draw_functions.h" +#include "sdcard.h" + +const uint8_t LBM_FOLDER[] = { +#include "mask_folder.lbm" +}; + +// comparison, not case sensitive. +bool compare_nocase(const std::string &first, const std::string &second) +{ + return strcasecmp(first.c_str(), second.c_str()) < 0; +} + +FileChoice::FileChoice(Window * parent, const rect_t & rect, std::string folder, const char * extension, int maxlen, std::function getValue, std::function setValue): + FormField(parent, rect), + folder(std::move(folder)), + extension(extension), + maxlen(maxlen), + getValue(std::move(getValue)), + setValue(std::move(setValue)) +{ +} + +void FileChoice::paint(BitmapBuffer * dc) +{ + FormField::paint(dc); + + const char * displayedValue = getValue().c_str(); + + LcdFlags textColor; + if (editMode) + textColor = TEXT_INVERTED_COLOR; + else if (hasFocus()) + textColor = TEXT_INVERTED_BGCOLOR; + else if (displayedValue[0] == '\0') + textColor = CURVE_AXIS_COLOR; + else + textColor = TEXT_COLOR; + + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, displayedValue[0] == '\0' ? "---" : displayedValue, textColor); + dc->drawBitmapPattern(rect.w - 20, (rect.h - 11) / 2, LBM_FOLDER, textColor); +} + +void FileChoice::openMenu() +{ + FILINFO fno; + DIR dir; + std::list files; + const char * fnExt; + uint8_t fnLen, extLen; + + FRESULT res = f_opendir(&dir, folder.c_str()); // Open the directory + if (res == FR_OK) { + bool firstTime = true; + for (;;) { + res = sdReadDir(&dir, &fno, firstTime); + if (res != FR_OK || fno.fname[0] == 0) + break; // break on error or end of dir + if (fno.fattrib & AM_DIR) + continue; // skip subfolders + if (fno.fattrib & AM_HID) + continue; // skip hidden files + if (fno.fattrib & AM_SYS) + continue; // skip system files + + fnExt = getFileExtension(fno.fname, 0, 0, &fnLen, &extLen); + fnLen -= extLen; + + if (!fnLen || fnLen > maxlen) + continue; // wrong size + if (extension && !isExtensionMatching(fnExt, extension)) + continue; // wrong extension + + files.emplace_back(fno.fname, fnLen); + } + + if (files.size() > 0) { + // sort files + files.sort(compare_nocase); + + files.push_front(""); + + auto menu = new Menu(); + int count = 0; + int current = -1; + std::string value = getValue(); + for (const auto &file: files) { + menu->addLine(file, [=]() { + setValue(file); + }); + // TRACE("%s %d %s %d", value.c_str(), value.size(), file.c_str(), file.size()); + if (value.compare(file) == 0) { + // TRACE("OK"); + current = count; + } + ++count; + } + + if (current >= 0) { + menu->select(current); + } + + menu->setCloseHandler([=]() { + editMode = false; + setFocus(); + }); + } + else { + TRACE("NO FILES !!!!!"); + // TODO popup "NO FILES" + } + } +} + +#if defined(HARDWARE_KEYS) +void FileChoice::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + editMode = true; + invalidate(); + openMenu(); + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool FileChoice::onTouchEnd(coord_t, coord_t) +{ + openMenu(); + setFocus(); + return true; +} +#endif diff --git a/radio/src/gui/libopenui/filechoice.h b/radio/src/gui/libopenui/filechoice.h new file mode 100644 index 000000000..284e3dcf6 --- /dev/null +++ b/radio/src/gui/libopenui/filechoice.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _FILECHOICE_H_ +#define _FILECHOICE_H_ + +#include "form.h" +#include + +class FileChoice: public FormField { + public: + FileChoice(Window * parent, const rect_t & rect, std::string folder, const char * extension, int maxlen, std::function getValue, std::function setValue); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "FileChoice"; + } +#endif + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + std::string folder; + const char * extension; + int maxlen; + std::function getValue; + std::function setValue; + + void openMenu(); +}; + +#endif // _FILECHOICE_H_ diff --git a/radio/src/gui/libopenui/form.cpp b/radio/src/gui/libopenui/form.cpp new file mode 100644 index 000000000..758c42d3c --- /dev/null +++ b/radio/src/gui/libopenui/form.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "form.h" +#include "opentx.h" +#include "draw_functions.h" + +FormField * FormField::current = nullptr; + +FormField::FormField(Window *parent, const rect_t &rect, uint8_t flags) : + Window(parent, rect, flags) +{ + if (current) { + setPreviousField(current); + current->setNextField(this); + } + + FormWindow * form = dynamic_cast(parent); + if (form && !form->getFirstField()) + form->setFirstField(this); + + current = this; +} + +#if defined(HARDWARE_KEYS) +void FormWindow::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_EXIT) && first && getFocus() != first) { + first->setFocus(); + } + else { + Window::onKeyEvent(event); + } +} + +void FormField::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", FormField::getWindowDebugString().c_str(), event); + + if (event == EVT_ROTARY_RIGHT/*EVT_KEY_BREAK(KEY_DOWN)*/) { + if (next) { + next->setFocus(); + } + } + else if (event == EVT_ROTARY_LEFT/*EVT_KEY_BREAK(KEY_UP)*/) { + if (previous) { + previous->setFocus(); + } + } + else if (event == EVT_KEY_BREAK(KEY_ENTER)) { + editMode = !editMode; + invalidate(); + } + else if (event == EVT_KEY_BREAK(KEY_EXIT) && editMode) { + editMode = false; + invalidate(); + } + else { + Window::onKeyEvent(event); + } +} + +void FormGroup::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + editMode = true; + first->setFocus(); + } + else if (event == EVT_KEY_BREAK(KEY_EXIT) && editMode) { + editMode = false; + setFocus(); + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +void FormField::paint(BitmapBuffer * dc) +{ + if (editMode) { + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, TEXT_INVERTED_BGCOLOR); + } + else if (hasFocus()) { + drawSolidRect(dc, 0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); + } + else if (!(windowFlags & BORDER_FOCUS_ONLY)) { + drawSolidRect(dc, 0, 0, rect.w, rect.h, 1, CURVE_AXIS_COLOR); + } +} + +void FormGroup::paint(BitmapBuffer * dc) +{ + if (!editMode && hasFocus()) { + drawSolidRect(dc, 0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); + } + else if (!(windowFlags & BORDER_FOCUS_ONLY)) { + drawSolidRect(dc, 0, 0, rect.w, rect.h, 1, CURVE_AXIS_COLOR); + } +} diff --git a/radio/src/gui/libopenui/form.h b/radio/src/gui/libopenui/form.h new file mode 100644 index 000000000..33ede76a7 --- /dev/null +++ b/radio/src/gui/libopenui/form.h @@ -0,0 +1,193 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _FORM_H_ +#define _FORM_H_ + +#include "window.h" + +class FormField: public Window { + friend class FormWindow; + + public: + FormField(Window * parent, const rect_t & rect, uint8_t flags=0); + + inline void setNextField(FormField *field) + { + next = field; + } + + inline void setPreviousField(FormField * field) + { + previous = field; + } + + inline static void setCurrentField(FormField * field) + { + current = field; + } + + inline static void clearCurrentField() + { + setCurrentField(nullptr); + }; + + inline static FormField * getCurrentField() + { + return current; + }; + + inline FormField * getPreviousField() + { + return previous; + }; + + inline FormField * getNextField() + { + return next; + }; + + inline static void link(FormField * previous, FormField * next) + { + previous->setNextField(next); + next->setPreviousField(previous); + } + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + + protected: + static FormField * current; + FormField * next = nullptr; + FormField * previous = nullptr; + bool editMode = false; +}; + +class FormGroup: public FormField { + public: + FormGroup(Window * parent, const rect_t & rect, uint8_t flags=0) : + FormField(parent, rect, flags) + { + FormField::current = nullptr; + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "FormGroup"; + } +#endif + + void clear() + { + Window::clear(); + first = nullptr; + FormField::current = nullptr; + } + + inline void setFirstField(FormField * field) + { + first = field; + } + + inline void setLastField(FormField * field = nullptr) + { + // by default the current field is the last one + if (!field) + field = getCurrentField(); + + FormField::link(field, first); + + // now the current field is the group, it will be linked to the next one + FormField::setCurrentField(this); + } + + FormField * getFirstField() + { + return first; + } + + protected: + FormField * first = nullptr; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + + void paint(BitmapBuffer * dc) override; +}; + +class FormWindow: public Window { + public: + FormWindow(Window * parent, const rect_t & rect) : + Window(parent, rect) + { + FormField::current = nullptr; + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "FormWindow"; + } +#endif + + void clear() + { + first = nullptr; + Window::clear(); + } + + FormField * getFirstField() + { + return first; + } + + inline void setFirstField(FormField * field) + { + first = field; + field->setFocus(); + } + + inline void setLastField(FormField * field = nullptr) + { + if (!field) + field = FormField::getCurrentField(); + FormField::link(field, first); + } + + protected: + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + + void paint(BitmapBuffer * dc) override + { + } + + protected: + FormField * first = nullptr; +}; + +#endif diff --git a/radio/src/gui/libopenui/getset_helpers.h b/radio/src/gui/libopenui/getset_helpers.h new file mode 100644 index 000000000..a13695433 --- /dev/null +++ b/radio/src/gui/libopenui/getset_helpers.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _GETSET_HELPERS_H_ +#define _GETSET_HELPERS_H_ + +#define GET_VALUE(value) [=] { return value; } +#define GET_DEFAULT(value) [=] { return value; } +#define GET_INVERTED(value) [=] { return !value; } +#define GET_VALUE_WITH_OFFSET(value, offset) [=] { return value + offset; } +#define GET_VALUE_WITH_BF(value, offset, bits) [=] { return bfGet(value, offset, bits); } + +#define SET_VALUE(value, _newValue) [=](int32_t newValue) { value = _newValue; SET_DIRTY(); } +#define SET_DEFAULT(value) [=](int32_t newValue) { value = newValue; SET_DIRTY(); } +#define SET_INVERTED(value) [=](uint8_t newValue) { value = !newValue; SET_DIRTY(); } +#define SET_VALUE_WITH_OFFSET(value, offset) [=](int32_t newValue) { value = newValue - offset; SET_DIRTY(); } +#define SET_VALUE_WITH_BF(value, offset, bits) [=](uint16_t newValue) { bfSet(value, newValue, offset, bits); SET_DIRTY(); } + +#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_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/libopenui/gridlayout.h b/radio/src/gui/libopenui/gridlayout.h new file mode 100644 index 000000000..cc8cb992b --- /dev/null +++ b/radio/src/gui/libopenui/gridlayout.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _GRIDLAYOUT_H_ +#define _GRIDLAYOUT_H_ + +#include "window.h" + +class GridLayout { + public: + GridLayout(coord_t width): + width(width) + { + } + + GridLayout(Window * window): + width(window->width()) + { + } + + rect_t getSlot(uint8_t count = 1, uint8_t index = 0) const + { + coord_t width = (this->width - (count - 1) * PAGE_LINE_SPACING) / count; + coord_t left = (width + PAGE_LINE_SPACING) * index; + return {left, currentY, width, PAGE_LINE_HEIGHT}; + } + + void spacer(coord_t height=PAGE_LINE_SPACING) + { + currentY += height; + } + + void nextLine(coord_t height=PAGE_LINE_HEIGHT) + { + spacer(height + PAGE_LINE_SPACING); + } + + void addWindow(Window * window) + { + window->adjustHeight(); + currentY += window->rect.h + PAGE_LINE_SPACING; + } + + coord_t getWindowHeight() const + { + return currentY; + } + + protected: + coord_t width; + coord_t currentY = 0; +}; + +class FormGridLayout: public GridLayout { + public: + FormGridLayout(coord_t width = LCD_W): + GridLayout(width) + { + } + + void setLabelWidth(coord_t value) + { + labelWidth = value; + } + + void setMarginLeft(coord_t value) + { + lineMarginLeft = value; + } + + void setMarginRight(coord_t value) + { + lineMarginRight = value; + } + + rect_t getLineSlot() + { + return { lineMarginLeft, currentY, this->width - lineMarginRight - lineMarginLeft, PAGE_LINE_HEIGHT }; + } + + rect_t getCenteredSlot(coord_t width = 0 ) const + { + if (width == 0) + width = this->width; + return { lineMarginLeft + (this->width - width)/2, currentY, min(width, this->width - lineMarginRight - lineMarginLeft), PAGE_LINE_HEIGHT }; + } + + rect_t getLabelSlot(bool indent = false) const + { + coord_t left = indent ? lineMarginLeft + PAGE_INDENT_WIDTH : lineMarginLeft; + return { left, currentY, labelWidth - left, PAGE_LINE_HEIGHT }; + } + + rect_t getFieldSlot(uint8_t count = 1, uint8_t index = 0) const + { + coord_t width = (this->width - labelWidth - lineMarginRight - (count - 1) * PAGE_LINE_SPACING) / count; + coord_t left = labelWidth + (width + PAGE_LINE_SPACING) * index; + return {left, currentY, width, PAGE_LINE_HEIGHT}; + } + + protected: + coord_t labelWidth = PAGE_LABEL_WIDTH; + coord_t lineMarginLeft = 6; + coord_t lineMarginRight = 10; +}; + +#endif diff --git a/radio/src/gui/libopenui/keyboard_base.h b/radio/src/gui/libopenui/keyboard_base.h new file mode 100644 index 000000000..006b8ab0c --- /dev/null +++ b/radio/src/gui/libopenui/keyboard_base.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _KEYBOARD_BASE_H_ +#define _KEYBOARD_BASE_H_ + +#include "mainwindow.h" +#include "libopenui_types.h" + +class Page; +class TabsGroup; + +template +class Keyboard: public Window { + public: + Keyboard(coord_t height): + Window(nullptr, {0, LCD_H - height, LCD_W, height}, OPAQUE) + { + } + + void setField(T * field) + { + this->field = field; + attach(&mainWindow); + Window * w = getPageBody(); + w->setHeight(LCD_H - height() - w->top()); + w->scrollTo(field); + invalidate(); + } + + void disable(bool resizePage) + { + detach(); + if (field) { + Window * w = getPageBody(); + w->setHeight(LCD_H - 0 - w->top()); + field = nullptr; + } + } + + T * getField() const + { + return field; + } + + protected: + T * field = nullptr; + Window * getPageBody() + { + Window * parent = field; + while (1) { + Window * tmp = parent->getParent(); + if ((tmp->getWindowFlags() & OPAQUE) && tmp->width() == LCD_W && tmp->height() == LCD_H) { + return parent; + } + parent = tmp; + } + } +}; + +#endif // _KEYBOARD_BASE_H_ \ No newline at end of file diff --git a/radio/src/gui/libopenui/keyboard_curve.cpp b/radio/src/gui/libopenui/keyboard_curve.cpp new file mode 100644 index 000000000..5b635a179 --- /dev/null +++ b/radio/src/gui/libopenui/keyboard_curve.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "keyboard_curve.h" +#include "curveedit.h" +#include "button.h" +#include "lcd.h" + +constexpr coord_t KEYBOARD_HEIGHT = 110; + +CurveKeyboard * CurveKeyboard::_instance = nullptr; + +CurveKeyboard::CurveKeyboard(): + Keyboard(KEYBOARD_HEIGHT) +{ + // up + new TextButton(this, {LCD_W/2 - 20, 5, 40, 40}, "\200", + [=]() -> uint8_t { + if (field) { + field->up(); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + // down + new TextButton(this, {LCD_W/2 - 20, 65, 40, 40}, "\201", + [=]() -> uint8_t { + field->down(); + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + // left + left = new TextButton(this, {LCD_W / 2 - 70, 35, 40, 40}, "\177", + [=]() -> uint8_t { + if (field) { + field->left(); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + // right + right = new TextButton(this, {LCD_W / 2 + 30, 35, 40, 40}, "\176", + [=]() -> uint8_t { + if (field) { + field->right(); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + // next + new TextButton(this, {LCD_W/2 + 80, 35, 60, 40}, "Next", + [=]() -> uint8_t { + if (field) { + field->next(); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + // previous + new TextButton(this, {LCD_W / 2 - 140, 35, 60, 40}, "Prev", + [=]() -> uint8_t { + if (field) { + field->previous(); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + +} + +CurveKeyboard::~CurveKeyboard() +{ + _instance = nullptr; +} + +void CurveKeyboard::setField(CurveEdit * field) +{ + Keyboard::setField(field); + bool custom = field->isCustomCurve(); + left->enable(custom); + right->enable(custom); +} + +void CurveKeyboard::paint(BitmapBuffer * dc) +{ + lcdSetColor(RGB(0xE0, 0xE0, 0xE0)); + dc->clear(CUSTOM_COLOR); +} diff --git a/radio/src/gui/libopenui/keyboard_curve.h b/radio/src/gui/libopenui/keyboard_curve.h new file mode 100644 index 000000000..d7b7d5afd --- /dev/null +++ b/radio/src/gui/libopenui/keyboard_curve.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _KEYBOARD_CURVE_H_ +#define _KEYBOARD_CURVE_H_ + +#include "keyboard_base.h" +#include "curveedit.h" + +class Button; + +class CurveKeyboard : public Keyboard { + friend class CurveEdit; + + public: + CurveKeyboard(); + + ~CurveKeyboard(); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "CurveKeyboard"; + } +#endif + + static CurveKeyboard * instance() + { + if (!_instance) + _instance = new CurveKeyboard(); + return _instance; + } + + void setField(CurveEdit * field); + + void paint(BitmapBuffer * dc) override; + + protected: + static CurveKeyboard * _instance; + Button * left = nullptr; + Button * right = nullptr; +}; + +#endif // _KEYBOARD_CURVE_H_ \ No newline at end of file diff --git a/radio/src/gui/libopenui/keyboard_number.cpp b/radio/src/gui/libopenui/keyboard_number.cpp new file mode 100644 index 000000000..a27b36157 --- /dev/null +++ b/radio/src/gui/libopenui/keyboard_number.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "keyboard_number.h" +#include "button.h" +#include "basenumberedit.h" +#include "lcd.h" + +constexpr coord_t KEYBOARD_HEIGHT = 90; + +NumberKeyboard * NumberKeyboard::_instance = nullptr; + +NumberKeyboard::NumberKeyboard(): + Keyboard(KEYBOARD_HEIGHT) +{ + new TextButton(this, { LCD_W/2 - 115, 10, 50, 30 }, "<<", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getValue() - 10 * field->getStep()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + new TextButton(this, { LCD_W/2 - 55, 10, 50, 30 }, "-", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getValue() - field->getStep()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + new TextButton(this, { LCD_W/2 + 5, 10, 50, 30 }, "+", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getValue() + field->getStep()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + new TextButton(this, { LCD_W/2 + 65, 10, 50, 30 }, ">>", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getValue() + 10 * field->getStep()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + new TextButton(this, { LCD_W/2 - 115, 50, 50, 30 }, "MIN", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getMin()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + new TextButton(this, { LCD_W/2 + 65, 50, 50, 30 }, "MAX", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getMax()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + new TextButton(this, { LCD_W/2 - 55, 50, 110, 30 }, "DEFAULT", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getDefault()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); +} + +NumberKeyboard::~NumberKeyboard() +{ + _instance = nullptr; +} + +void NumberKeyboard::paint(BitmapBuffer * dc) +{ + lcdSetColor(RGB(0xE0, 0xE0, 0xE0)); + dc->clear(CUSTOM_COLOR); +} diff --git a/radio/src/gui/libopenui/keyboard_number.h b/radio/src/gui/libopenui/keyboard_number.h new file mode 100644 index 000000000..7fb5bde29 --- /dev/null +++ b/radio/src/gui/libopenui/keyboard_number.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _KEYBOARD_NUMBER_H_ +#define _KEYBOARD_NUMBER_H_ + +#include "keyboard_base.h" +#include "basenumberedit.h" + +class NumberKeyboard : public Keyboard { + friend class BaseNumberEdit; + + public: + NumberKeyboard(); + + ~NumberKeyboard() override; + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "NumberKeyboard"; + } +#endif + + static NumberKeyboard * instance() + { + if (!_instance) + _instance = new NumberKeyboard(); + return _instance; + } + + void paint(BitmapBuffer * dc) override; + + protected: + static NumberKeyboard * _instance; +}; + +#endif // _KEYBOARD_NUMBER_H_ \ No newline at end of file diff --git a/radio/src/gui/libopenui/keyboard_text.cpp b/radio/src/gui/libopenui/keyboard_text.cpp new file mode 100644 index 000000000..9c406a370 --- /dev/null +++ b/radio/src/gui/libopenui/keyboard_text.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "keyboard_text.h" +#include "textedit.h" +#include "fonts.h" +#include "lcd.h" + +constexpr coord_t KEYBOARD_HEIGHT = 160; + +TextKeyboard * TextKeyboard::_instance = nullptr; + +const uint8_t LBM_KEY_UPPERCASE[] = { +#include "mask_key_uppercase.lbm" +}; + +const uint8_t LBM_KEY_LOWERCASE[] = { +#include "mask_key_lowercase.lbm" +}; + +const uint8_t LBM_KEY_BACKSPACE[] = { +#include "mask_key_backspace.lbm" +}; + +const uint8_t LBM_KEY_LETTERS[] = { +#include "mask_key_letters.lbm" +}; + +const uint8_t LBM_KEY_NUMBERS[] = { +#include "mask_key_numbers.lbm" +}; + +const uint8_t LBM_KEY_SPACEBAR[] = { +#include "mask_key_spacebar.lbm" +}; + +const uint8_t * const LBM_SPECIAL_KEYS[] = { + LBM_KEY_BACKSPACE, + LBM_KEY_UPPERCASE, + LBM_KEY_LOWERCASE, + LBM_KEY_LETTERS, + LBM_KEY_NUMBERS, +}; + +const char * const KEYBOARD_LOWERCASE[] = { + "qwertyuiop", + " asdfghjkl", + "\201zxcvbnm\200", + "\204\t\n" +}; + +const char * const KEYBOARD_UPPERCASE[] = { + "QWERTYUIOP", + " ASDFGHJKL", + "\202ZXCVBNM\200", + "\204\t\n" +}; + +const char * const KEYBOARD_NUMBERS[] = { + "1234567890", + "_-", + " \200", + "\203\t\n" +}; + +const char * const * const KEYBOARD_LAYOUTS[] = { + KEYBOARD_UPPERCASE, + KEYBOARD_LOWERCASE, + KEYBOARD_LOWERCASE, + KEYBOARD_NUMBERS, +}; + +TextKeyboard::TextKeyboard(): + Keyboard(KEYBOARD_HEIGHT), + layout(KEYBOARD_LOWERCASE) +{ +} + +TextKeyboard::~TextKeyboard() +{ + _instance = nullptr; +} + +void TextKeyboard::setCursorPos(coord_t x) +{ + if (!field) + return; + + uint8_t size = field->getMaxLength(); + char * data = field->getData(); + coord_t rest = x; + for (cursorIndex = 0; cursorIndex < size; cursorIndex++) { + if (data[cursorIndex] == '\0') + break; + char c = data[cursorIndex]; + c = zchar2char(c); + uint8_t w = getCharWidth(c, fontspecsTable[0]); + if (rest < w) + break; + rest -= w; + } + cursorPos = x - rest; + field->invalidate(); +} + +void TextKeyboard::paint(BitmapBuffer * dc) +{ + lcdSetColor(RGB(0xE0, 0xE0, 0xE0)); + dc->clear(CUSTOM_COLOR); + + for (uint8_t i=0; i<4; i++) { + coord_t y = 15 + i * 40; + coord_t x = 15; + const char * c = layout[i]; + while(*c) { + if (*c == ' ') { + x += 15; + } + else if (*c == '\t') { + // spacebar + dc->drawBitmapPattern(x, y, LBM_KEY_SPACEBAR, TEXT_COLOR); + x += 135; + } + else if (*c == '\n') { + // enter + dc->drawSolidFilledRect(x, y-2, 80, 25, TEXT_DISABLE_COLOR); + dc->drawText(x+40, y, "ENTER", CENTERED); + x += 80; + } + else if (int8_t(*c) < 0) { + dc->drawBitmapPattern(x, y, LBM_SPECIAL_KEYS[uint8_t(*c - 128)], TEXT_COLOR); + x += 45; + } + else { + dc->drawSizedText(x, y, c, 1); + x += 30; + } + c++; + } + } +} + +bool TextKeyboard::onTouchEnd(coord_t x, coord_t y) +{ + if (!field) + return false; + + uint8_t size = field->getMaxLength(); + char * data = field->getData(); + + char c = 0; + + uint8_t row = max(0, y - 5) / 40; + const char * key = layout[row]; + while(*key) { + if (*key == ' ') { + x -= 15; + } + else if (*key == '\t') { + if (x <= 135) { + c = ' '; + break; + } + x -= 135; + } + else if (*key == '\n') { + if (x <= 80) { + // enter + disable(true); + return true; + } + x -= 80; + } + else if (int8_t(*key) < 0) { + if (x <= 45) { + uint8_t specialKey = *key; + if (specialKey == 128) { + // backspace + if (cursorIndex > 0) { + char c = zchar2char(data[cursorIndex - 1]); + memmove(data + cursorIndex - 1, data + cursorIndex, size - cursorIndex); + data[size - 1] = '\0'; + cursorPos -= getCharWidth(c, fontspecsTable[0]); + --cursorIndex; + } + } + else { + layout = KEYBOARD_LAYOUTS[specialKey - 129]; + invalidate(); + } + break; + } + x -= 45; + } + else { + if (x <= 30) { + c = *key; + break; + } + x -= 30; + } + key++; + } + + if (c && zlen(data, size) < size) { + memmove(data + cursorIndex + 1, data + cursorIndex, size - cursorIndex - 1); + data[cursorIndex++] = char2zchar(c); + cursorPos += getCharWidth(c, fontspecsTable[0]); + } + + field->invalidate(); + return true; +} diff --git a/radio/src/gui/libopenui/keyboard_text.h b/radio/src/gui/libopenui/keyboard_text.h new file mode 100644 index 000000000..183ce0706 --- /dev/null +++ b/radio/src/gui/libopenui/keyboard_text.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _KEYBOARD_TEXT_H_ +#define _KEYBOARD_TEXT_H_ + +#include "keyboard_base.h" +#include "textedit.h" + +class TextKeyboard : public Keyboard { + friend class TextEdit; + + public: + TextKeyboard(); + + ~TextKeyboard() override; + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TextKeyboard"; + } +#endif + + static TextKeyboard * instance() { + if (!_instance) + _instance = new TextKeyboard(); + return _instance; + } + + coord_t getCursorPos() const + { + return cursorPos; + } + + void setCursorPos(coord_t x); + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + static TextKeyboard * _instance; + coord_t cursorPos = 0; + uint8_t cursorIndex = 0; + const char * const * layout; +}; + +#endif // _KEYBOARD_TEXT_H_ \ No newline at end of file diff --git a/radio/src/gui/libopenui/libopenui.h b/radio/src/gui/libopenui/libopenui.h new file mode 100644 index 000000000..7251976a9 --- /dev/null +++ b/radio/src/gui/libopenui/libopenui.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _LIBOPENUI_H_ +#define _LIBOPENUI_H_ + +#include "libopenui_config.h" +#include "window.h" +#include "mainwindow.h" +#include "static.h" +#include "button.h" +#include "checkbox.h" +#include "numberedit.h" +#include "timeedit.h" +#include "choice.h" +#include "sourcechoice.h" +#include "switchchoice.h" +#include "filechoice.h" +#include "textedit.h" +#include "slider.h" +#include "progress.h" +#include "keyboard_text.h" +#include "keyboard_number.h" +#include "keyboard_curve.h" +#include "tabsgroup.h" +#include "page.h" +#include "menu.h" +#include "dialog.h" +#include "gridlayout.h" +#include "getset_helpers.h" +#include "curveedit.h" +#include "coloredit.h" +#include "draw_functions.h" + +#endif // _LIBOPENUI_H_ diff --git a/radio/src/gui/libopenui/libopenui_defines.h b/radio/src/gui/libopenui/libopenui_defines.h new file mode 100644 index 000000000..24b19e3e8 --- /dev/null +++ b/radio/src/gui/libopenui/libopenui_defines.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _LIBOPEUI_DEFINES_H_ +#define _LIBOPEUI_DEFINES_H_ + +#define CENTER + +/* lcd common flags */ +#define BLINK 0x01 + +/* drawText flags */ +#define INVERS 0x02 +#define LEFT 0x00 /* align left */ +#define CENTERED 0x04 /* align center */ +#define RIGHT 0x08 /* align right */ +#define SHADOWED 0x80 /* black copy at +1 +1 */ + +/* drawNumber flags */ +#define LEADING0 0x10 +#define PREC1 0x20 +#define PREC2 0x30 +#define MODE(flags) ((((int8_t)(flags) & 0x30) - 0x10) >> 4) + +#define ZCHAR 0x10 + +/* rect, square flags */ +#define ROUND 0x04 + +/* telemetry flags */ +#define NO_UNIT 0x40 + +enum FontSizeIndex { + STDSIZE_INDEX, + TINSIZE_INDEX, + SMLSIZE_INDEX, + MIDSIZE_INDEX, + DBLSIZE_INDEX, + XXLSIZE_INDEX, + SPARE6_INDEX, + SPARE7_INDEX, + STDSIZE_BOLD_INDEX, + SPARE9_INDEX, + SPAREa_INDEX, + SPAREb_INDEX, + SPAREc_INDEX, + SPAREd_INDEX, + SPAREe_INDEX, + SPAREf_INDEX, +}; + +#define STDSIZE (STDSIZE_INDEX << 8) +#define TINSIZE (TINSIZE_INDEX << 8) +#define SMLSIZE (SMLSIZE_INDEX << 8) +#define MIDSIZE (MIDSIZE_INDEX << 8) +#define DBLSIZE (DBLSIZE_INDEX << 8) +#define XXLSIZE (XXLSIZE_INDEX << 8) +#define BOLD (STDSIZE_BOLD_INDEX << 8) +#define FONTSIZE_MASK 0x0f00 + +#if !defined(BOOT) +#define FONTSIZE(flags) ((flags) & FONTSIZE_MASK) +#define FONTINDEX(flags) (FONTSIZE(flags) >> 8) +#else +#define FONTSIZE(flags) STDSIZE +#define FONTINDEX(flags) STDSIZE_INDEX +#endif + +#define TIMEBLINK 0x1000 +#define TIMEHOUR 0x2000 +#define EXPANDED 0x2000 +#define VERTICAL 0x4000 + +#endif diff --git a/radio/src/gui/480x272/menu_radio.cpp b/radio/src/gui/libopenui/libopenui_helpers.h similarity index 56% rename from radio/src/gui/480x272/menu_radio.cpp rename to radio/src/gui/libopenui/libopenui_helpers.h index 42327fc6f..2c72d2330 100644 --- a/radio/src/gui/480x272/menu_radio.cpp +++ b/radio/src/gui/libopenui/libopenui_helpers.h @@ -18,22 +18,25 @@ * GNU General Public License for more details. */ -#include "opentx.h" +#ifndef _LIBOPEUI_HELPERS_H_ +#define _LIBOPEUI_HELPERS_H_ -const MenuHandlerFunc menuTabGeneral[MENU_RADIO_PAGES_COUNT] = { - menuRadioSetup, - menuRadioSdManager, -#if defined(LUA) || defined(PXX2) - menuRadioTools, -#endif - menuRadioSpecialFunctions, - menuRadioTrainer, - menuRadioHardware, - menuRadioVersion, -}; - -bool menuRadioSpecialFunctions(event_t event) +template +inline T min(T a, T b) { - MENU(STR_MENUSPECIALFUNCS, RADIO_ICONS, menuTabGeneral, MENU_RADIO_SPECIAL_FUNCTIONS, MAX_SPECIAL_FUNCTIONS, { NAVIGATION_LINE_BY_LINE|4/*repeated*/ }); - return menuSpecialFunctions(event, g_eeGeneral.customFn, globalFunctionsContext); + return a < b ? a : b; } + +template +inline T max(T a, T b) +{ + return a > b ? a : b; +} + +template +inline T limit(T vmin, T x, T vmax) +{ + return min(max(vmin, x), vmax); +} + +#endif diff --git a/radio/src/gui/libopenui/libopenui_types.h b/radio/src/gui/libopenui/libopenui_types.h new file mode 100644 index 000000000..cc839fe52 --- /dev/null +++ b/radio/src/gui/libopenui/libopenui_types.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _LIBOPEUI_TYPES_H_ +#define _LIBOPEUI_TYPES_H_ + +#include + +typedef int coord_t; + +struct point_t +{ + coord_t x; + coord_t y; +}; + +struct rect_t +{ + coord_t x, y, w, h; + + constexpr coord_t left() const + { + return x; + } + + constexpr coord_t right() const + { + return x + w; + } + + constexpr coord_t top() const + { + return y; + } + + constexpr coord_t bottom() const + { + return y + h; + } +}; + +typedef uint32_t LcdFlags; + +#endif // _LIBOPEUI_TYPES_H_ diff --git a/radio/src/gui/libopenui/mainwindow.cpp b/radio/src/gui/libopenui/mainwindow.cpp new file mode 100644 index 000000000..228c4e666 --- /dev/null +++ b/radio/src/gui/libopenui/mainwindow.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "lcd.h" +#include "mainwindow.h" +#include "colorlcd/draw_functions.h" + +#if defined(HARDWARE_TOUCH) +#include "touch_driver.h" +#endif + +MainWindow mainWindow; + +#if defined(HARDWARE_TOUCH) +STRUCT_TOUCH touchState; +#endif + +void MainWindow::emptyTrash() +{ + for (auto window: trash) { + delete window; + } + trash.clear(); +} + +void MainWindow::checkEvents(bool trash) +{ +#if defined(HARDWARE_TOUCH) + if (touchState.Event == TE_DOWN) { + onTouchStart(touchState.X + scrollPositionX, touchState.Y + scrollPositionY); + // touchState.Event = TE_NONE; + } + else if (touchState.Event == TE_UP) { + touchState.Event = TE_NONE; + onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); + } + else if (touchState.Event == TE_SLIDE) { + coord_t x = touchState.X - touchState.lastX; + coord_t y = touchState.Y - touchState.lastY; + onTouchSlide(touchState.X, touchState.Y, touchState.startX, touchState.startY, x, y); + touchState.lastX = touchState.X; + touchState.lastY = touchState.Y; + } +#endif + + Window::checkEvents(); + + if (trash) { + emptyTrash(); + } +} + +void MainWindow::invalidate(const rect_t & rect) +{ + if (invalidatedRect.w) { + coord_t left = min(invalidatedRect.left(), rect.left()); + coord_t right = max(invalidatedRect.right(), rect.right()); + coord_t top = min(invalidatedRect.top(), rect.top()); + coord_t bottom = max(invalidatedRect.bottom(), rect.bottom()); + invalidatedRect = {left, top, right - left, bottom - top}; + } + else { + invalidatedRect = rect; + } +} + +bool MainWindow::refresh() +{ + if (invalidatedRect.w) { + if (invalidatedRect.x > 0 || invalidatedRect.y > 0 || invalidatedRect.w < LCD_W || invalidatedRect.h < LCD_H) { + TRACE_WINDOWS("Refresh rect: left=%d top=%d width=%d height=%d", invalidatedRect.left(), invalidatedRect.top(), invalidatedRect.w, invalidatedRect.h); + BitmapBuffer * previous = lcd; + lcdNextLayer(); + lcdCopy(lcd->getData(), previous->getData()); + } + else { + TRACE_WINDOWS("Refresh full screen"); + lcdNextLayer(); + } + lcd->setOffset(0, 0); + lcd->setClippingRect(invalidatedRect.left(), invalidatedRect.right(), invalidatedRect.top(), invalidatedRect.bottom()); + fullPaint(lcd); + invalidatedRect.w = 0; + return true; + } + else { + return false; + } +} + +void MainWindow::run(bool trash) +{ + checkEvents(trash); + if (refresh()) { + lcdRefresh(); + } +} diff --git a/radio/src/gui/libopenui/mainwindow.h b/radio/src/gui/libopenui/mainwindow.h new file mode 100644 index 000000000..396f08b12 --- /dev/null +++ b/radio/src/gui/libopenui/mainwindow.h @@ -0,0 +1,66 @@ + + +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _MAINWINDOW_H_ +#define _MAINWINDOW_H_ + +#include +#include "window.h" +#include "bitmapbuffer.h" + +class MainWindow: public Window { + public: + MainWindow(): + Window(nullptr, {0, 0, LCD_W, LCD_H}), + invalidatedRect(rect) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "MainWindow"; + } +#endif + + void checkEvents(bool trash=true); + + void invalidate() + { + invalidate({0, 0, rect.w, rect.h}); + } + + void invalidate(const rect_t & rect) override; + + bool refresh(); + + void run(bool trash=true); + + protected: + void emptyTrash(); + rect_t invalidatedRect; + const char * shutdown = nullptr; +}; + +extern MainWindow mainWindow; + +#endif // _MAINWINDOW_H_ \ No newline at end of file diff --git a/radio/src/gui/libopenui/material-backup.cpp b/radio/src/gui/libopenui/material-backup.cpp new file mode 100644 index 000000000..f2a1355f3 --- /dev/null +++ b/radio/src/gui/libopenui/material-backup.cpp @@ -0,0 +1,200 @@ +class Window +{ + public: + Window(coord_t x, coord_t y, coord_t width, coord_t height): + x(x), + y(y), + width(width), + height(height), + offsetX(0), + offsetY(0), + innerWidth(width), + innerHeight(height), + focusX(-1), + focusY(-1) + { + } + + coord_t x, y, width, height, offsetX, offsetY, innerWidth, innerHeight, focusX, focusY; + + void scroll() { + if (touchState.Event == TE_SLIDE) { + if (innerHeight > height && touchState.startX >= this->x && touchState.startX <= this->x + this->width) { + this->offsetY = limit(-innerHeight + height, this->offsetY + touchState.Y - touchState.startY, 0); + touchState.startY = touchState.Y; + } + if (innerWidth > width && touchState.startY >= this->y && touchState.startY <= this->y + this->height) { + this->offsetX = limit(-innerWidth + width, this->offsetX + touchState.X - touchState.startX, 0); + touchState.startX = touchState.X; + } + } + } + + bool touchPressed(coord_t x, coord_t y, coord_t w, coord_t h) { + return ::touchPressed(this->x + this->offsetX + x, this->y + this->offsetY + y, w, h); + } + + void setHeight(coord_t height) { + this->height = height; + } + + void setInnerWidth(coord_t value) { + innerWidth = value; + } + + void setInnerHeight(coord_t value) { + innerHeight = value; + } + + void setFocus(coord_t x, coord_t y) { + focusX = x; + focusY = y; + } + + void clearFocus() { + setFocus(-1, -1); + } + + bool hasFocus(coord_t x, coord_t y) { + return (x == focusX && y == focusY); + } + + void drawVerticalScrollbar(); + + void drawSolidFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags) + { + setClippingRect(); + lcd->drawSolidFilledRect(this->x + this->offsetX + x, this->y + this->offsetY + y, w, h, flags); + clearLcdLimits(); + } + + template + void drawBitmap(coord_t x, coord_t y, const T * bmp, coord_t srcx=0, coord_t srcy=0, coord_t w=0, coord_t h=0, float scale=0) { + if (this->offsetX + x >= this->width || this->offsetY + y >= this->height) { + return; + } + setClippingRect(); + lcd->drawBitmap(this->x + this->offsetX + x, this->y + this->offsetY + y, bmp, srcx, srcy, w, h, scale); + clearLcdLimits(); + } + + inline void setClippingRect() { + lcd->setClippingRect(this->x, this->x + this->width, this->y, this->y + this->height); + } + + inline void clearLcdLimits() { + lcd->clearClippingRect(); + } + + void drawSizedText(coord_t x, coord_t y, const char * s, uint8_t len, LcdFlags flags) { + setClippingRect(); + lcd->drawSizedText(this->x + this->offsetX + x, this->y + this->offsetY + y, s, len, flags); + clearLcdLimits(); + } + +}; +template +bool Subtitle(W * window, rect_t & rect, const char * label) +{ + rect.h = 14; + drawText(window, rect.x, rect.y, label, BOLD); +} + +template +bool CheckBox(W * window, rect_t & rect, const char * label, uint8_t & value, LcdFlags flags=0) +{ + rect.h = 30; + bool hasFocus = (flags & HAS_FOCUS) || window->hasFocus(rect.x, rect.y); + drawText(window, rect.x, rect.y+4, label); + coord_t x = rect.x + rect.w - 20; + if (hasFocus) { + window->drawSolidFilledRect(x-1, rect.y+8, 14, 14, TEXT_INVERTED_BGCOLOR); + window->drawSolidFilledRect(x+1, rect.y+10, 10, 10, TEXT_BGCOLOR); + if (value) { + window->drawSolidFilledRect(x+2, rect.y+11, 8, 8, TEXT_INVERTED_BGCOLOR); + } + } + else { + if (value) { + window->drawSolidFilledRect(x+2, rect.y+11, 8, 8, SCROLLBOX_COLOR); + drawSolidRect(window, x, rect.y+9, 12, 12, 1, LINE_COLOR); + } + else { + drawSolidRect(window, x, rect.y+9, 12, 12, 1, LINE_COLOR); + } + } + if (window->touchPressed(rect.x, rect.y, rect.w, rect.h)) { + value ^= 1; + window->setFocus(rect.x, rect.y); + return true; + } + else { + return false; + } +} + + +template +void drawWidgetLabel(W * window, rect_t & rect, const char * label) { + drawText(window, rect.x, rect.y, label, TINSIZE); +} + +template +void drawWidgetLine(W * window, rect_t & rect, LcdFlags color) { + window->drawSolidFilledRect(rect.x, rect.y + rect.h, rect.w, 1, color); +} + +template +bool Choice(W * window, rect_t & rect, const char * label, const char * values, uint8_t & value, uint8_t vmin, uint8_t vmax, LcdFlags flags=0) +{ + rect.h = 30; + bool hasFocus = (flags & HAS_FOCUS) || window->hasFocus(rect.x, rect.y); + LcdFlags textColor = 0; + LcdFlags lineColor = CURVE_AXIS_COLOR; + if (hasFocus) { + textColor = TEXT_INVERTED_BGCOLOR; + lineColor = TEXT_INVERTED_BGCOLOR; + } + drawTextAtIndex(window, rect.x, rect.y + 9, values, value, textColor); + drawWidgetLine(window, rect, lineColor); + drawWidgetLabel(window, rect, label); + if (window->touchPressed(rect.x, rect.y, rect.w, rect.h)) { + // value ^= 1; + window->setFocus(rect.x, rect.y); + return true; + } + else { + return false; + } +} + + +template +bool TextEdit(W * window, rect_t & rect, const char * label, char * value, uint8_t length, LcdFlags flags=0) +{ + rect.h = 30; + bool hasFocus = (flags & HAS_FOCUS) || window->hasFocus(rect.x, rect.y); + LcdFlags textColor = 0; + LcdFlags lineColor = CURVE_AXIS_COLOR; + if (hasFocus) { + textColor = TEXT_INVERTED_BGCOLOR; + lineColor = TEXT_INVERTED_BGCOLOR; + } + if (!hasFocus && zlen(value, length) == 0) + menuBodyWindow.drawSizedText(rect.x, rect.y + 9, "---", length, textColor); + else + menuBodyWindow.drawSizedText(rect.x, rect.y + 9, value, length, ZCHAR | textColor); + drawWidgetLine(window, rect, lineColor); + drawWidgetLabel(window, rect, label); + if (window->touchPressed(rect.x, rect.y, rect.w, rect.h)) { + // value ^= 1; + window->setFocus(rect.x, rect.y); + return true; + } + else { + return false; + } +} +#define CHECKBOX(X, label, value) { rect.x = X; rect.w = LCD_W - (X) - 20; uint8_t _value = value; CheckBox(window, rect, label, _value); value = _value; rect.y += rect.h + 10; } +#define CHECKBOX_INVERTED(X, label, value) { rect.x = X; rect.w = LCD_W - (X) - 20; uint8_t _value = !value; CheckBox(window, rect, label, _value); value = !_value; rect.y += rect.h + 10; } +#endif \ No newline at end of file diff --git a/radio/src/gui/libopenui/menu.cpp b/radio/src/gui/libopenui/menu.cpp new file mode 100644 index 000000000..a06a24aca --- /dev/null +++ b/radio/src/gui/libopenui/menu.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "menu.h" + +MenuWindow::MenuWindow(Menu * parent): + Window(parent, {LCD_W / 2 - 100, LCD_H / 2, 200, 0}, OPAQUE) +{ + setScrollbarColor(WARNING_COLOR); + setFocus(); +} + +void MenuWindow::select(int index) +{ + selectedIndex = index; + if (innerHeight > height()) { + setScrollPositionY(lineHeight * index - 3 * lineHeight); + } + invalidate(); +} + +#if defined(HARDWARE_KEYS) +void MenuWindow::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_ROTARY_RIGHT) { + select((selectedIndex + 1) % lines.size()); + } + else if (event == EVT_ROTARY_LEFT) { + select(selectedIndex == 0 ? lines.size() - 1 : selectedIndex - 1); + } + else if (event == EVT_KEY_BREAK(KEY_ENTER)) { + Window::onKeyEvent(event); // the window above will be closed on event + lines[selectedIndex].onPress(); + } + else if (event == EVT_KEY_BREAK(KEY_EXIT)) { + if (onCancel) + onCancel(); + Window::onKeyEvent(event); + } + else { + Window::onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool MenuWindow::onTouchEnd(coord_t x, coord_t y) +{ + int index = y / lineHeight; + lines[index].onPress(); + return false; // = close the menu (inverted so that click outside the menu closes it) +} +#endif + +void MenuWindow::paint(BitmapBuffer * dc) +{ + int width = (innerHeight > height() ? 195 : 200); + dc->clear(HEADER_BGCOLOR); + for (unsigned i=0; idrawText(10, i * lineHeight + (lineHeight - 20) / 2, text[0] == '\0' ? "---" : text, selectedIndex == (int)i ? WARNING_COLOR : MENU_TITLE_COLOR); + if (i > 0) { + dc->drawSolidHorizontalLine(0, i * lineHeight - 1, width, CURVE_AXIS_COLOR); + } + } +} + +void Menu::updatePosition() +{ + if (!toolbar) { + // there is no navigation bar at the left, we may center the window on screen + int count = min(MenuWindow::maxLines, menuWindow.lines.size()); + coord_t h = count * MenuWindow::lineHeight - 1; + menuWindow.setTop((LCD_H - h) / 2 + 20); + menuWindow.setHeight(h); + } + menuWindow.setInnerHeight(menuWindow.lines.size() * MenuWindow::lineHeight - 1); +} + +void Menu::addLine(const std::string & text, std::function onPress) +{ + menuWindow.addLine(text, std::move(onPress)); + updatePosition(); +} + +void Menu::removeLines() +{ + menuWindow.removeLines(); + updatePosition(); +} + +#if defined(HARDWARE_KEYS) +void Menu::onKeyEvent(event_t event) +{ + if (toolbar && (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN))) { + toolbar->onKeyEvent(event); + } + else if (event == EVT_KEY_BREAK(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_ENTER)) { + deleteLater(); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool Menu::onTouchEnd(coord_t x, coord_t y) +{ + if (!Window::onTouchEnd(x, y)) { + deleteLater(); + } + return true; +} + +bool Menu::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) +{ + Window::onTouchSlide(x, y, startX, startY, slideX, slideY); + return true; +} +#endif diff --git a/radio/src/gui/libopenui/menu.h b/radio/src/gui/libopenui/menu.h new file mode 100644 index 000000000..202f4d2b5 --- /dev/null +++ b/radio/src/gui/libopenui/menu.h @@ -0,0 +1,184 @@ +#include + +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _MENU_H_ +#define _MENU_H_ + +#include +#include +#include "mainwindow.h" +#include +#include + +class Menu; + +class MenuWindow: public Window { + friend class Menu; + + class MenuLine { + friend class MenuWindow; + + public: + MenuLine(std::string text, std::function onPress): + text(std::move(text)), + onPress(std::move(onPress)) + { + } + + MenuLine(MenuLine &) = delete; + + MenuLine(MenuLine &&) = default; + + protected: + std::string text; + std::function onPress; + }; + + public: + MenuWindow(Menu * parent); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "MenuWindow"; + } +#endif + + void setCancelHandler(std::function handler) + { + onCancel = handler; + } + + void addLine(const std::string & text, std::function onPress) + { + lines.emplace_back(text, onPress); + invalidate(); + } + + void removeLines() + { + lines.clear(); + invalidate(); + } + + void select(int index); + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + std::vector lines; +#if defined(HARDWARE_TOUCH) + int selectedIndex = -1; +#else + int selectedIndex = 0; +#endif + std::function onCancel; + static constexpr uint8_t maxLines = 7; // TODO NV14 was 8 + static constexpr uint8_t lineHeight = 30; // TODO NV14 was 40 +}; + +class Menu : public Window { + public: + Menu() : + Window(&mainWindow, {0, 0, LCD_W, LCD_H}, TRANSPARENT), +#if !defined(HARDWARE_TOUCH) + previousFocus(focusWindow), +#endif + menuWindow(this) + { + } + + ~Menu() + { + delete toolbar; + } + + void setCancelHandler(std::function handler) + { + menuWindow.setCancelHandler(handler); + } + + void deleteLater() + { +#if !defined(HARDWARE_TOUCH) + if (previousFocus) { + previousFocus->setFocus(); + } +#endif + Window::deleteLater(); + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Menu"; + } +#endif + + void setToolbar(Window * window) + { + toolbar = window; + menuWindow.setLeft(toolbar->right()); + menuWindow.setTop(toolbar->top()); + menuWindow.setHeight(toolbar->height()); + } + + void addLine(const std::string & text, std::function onPress); + + void removeLines(); + + void select(int index) + { + menuWindow.select(index); + } + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchStart(coord_t x, coord_t y) override + { + return true; + } + + bool onTouchEnd(coord_t x, coord_t y) override; + + bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override; +#endif + + protected: + Window * previousFocus; + MenuWindow menuWindow; + Window * toolbar = nullptr; + void updatePosition(); +}; + +#endif diff --git a/radio/src/gui/libopenui/menutoolbar.h b/radio/src/gui/libopenui/menutoolbar.h new file mode 100644 index 000000000..27a42cd83 --- /dev/null +++ b/radio/src/gui/libopenui/menutoolbar.h @@ -0,0 +1,191 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _MENUTOOLBAR_H_ +#define _MENUTOOLBAR_H_ + +#include "button.h" +#include "menu.h" + +int getFirstAvailable(int min, int max, std::functionisValueAvailable); +uint8_t getFontHeight(LcdFlags flags); + +class MenuToolbarButton: public Button { + public: + MenuToolbarButton(Window * window, const rect_t & rect, char picto): +#if defined(HARDWARE_TOUCH) + Button(window, rect, nullptr, BUTTON_CHECKED_ON_FOCUS), +#else + Button(window, rect, nullptr), +#endif + picto(picto) + { + } + + void paint(BitmapBuffer * dc) override + { + if (checked()) { + dc->drawSolidFilledRect(MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, HEADER_BGCOLOR); + dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2 + 1, &picto, 1, CENTERED | MENU_TITLE_COLOR); + } + else { + dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2 + 1, &picto, 1, CENTERED | TEXT_COLOR); + } + } + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override + { + if (hasFocus()) { + check(false); + clearFocus(); + } + else { + setFocus(); + } + onPress(); + return true; + } +#endif + + void check(bool checked=true) + { + Button::check(checked); + onPress(); + Button::check(checked); + } + + protected: + char picto; +}; + +template +class MenuToolbar: public Window { + friend T; + + public: + MenuToolbar(T * choice, Menu * menu): + Window(menu, MENUS_TOOLBAR_RECT, NO_SCROLLBAR), + choice(choice), + menu(menu) + { + FormField::clearCurrentField(); + } + + ~MenuToolbar() override + { + deleteChildren(); + } + + void paint(BitmapBuffer * dc) override + { + dc->clear(CURVE_AXIS_COLOR); + } + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override + { + if (event == EVT_KEY_BREAK(KEY_PGDN)) { + if (current == children.end()) { + current = children.begin(); + } + else { + static_cast(*current)->check(false); + ++current; + } + if (current != children.end()) { + auto button = static_cast(*current); + button->check(true); + scrollTo(button); + } + else { + setScrollPositionY(0); + } + } + else if (event == EVT_KEY_LONG(KEY_PGDN)) { + killEvents(event); + if (current == children.end()) { + --current; + } + else { + static_cast(*current)->check(false); + if (current == children.begin()) + current = children.end(); + else + --current; + } + if (current != children.end()) { + auto button = static_cast(*current); + button->check(true); + scrollTo(button); + } + else { + setScrollPositionY(0); + } + } + } +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override + { + Window::onTouchEnd(x, y); + return true; // = don't close the menu (inverted so that click outside the menu closes it) + } +#endif + + protected: + std::list::iterator current = children.end(); + T * choice; + Menu * menu; + coord_t y = 0; // TODO NV14 was 5; + + void addButton(char picto, int16_t filtermin, int16_t filtermax) + { + int vmin = choice->vmin; + int vmax = choice->vmax; + + if (vmin > filtermin || vmax < filtermin) + return; + + if (choice->isValueAvailable && getFirstAvailable(filtermin, filtermax, choice->isValueAvailable) == 0) + return; + + auto button = new MenuToolbarButton(this, {0, y, MENUS_TOOLBAR_BUTTON_WIDTH, MENUS_TOOLBAR_BUTTON_WIDTH}, picto); + button->setPressHandler([=]() { + if (button->checked()) { + choice->fillMenu(menu, [=](int16_t index) { + return index >= filtermin && index <= filtermax; + }); + } + else { + choice->fillMenu(menu); + } + return 1; + }); + + y += 30; + + setInnerHeight(y); + } +}; + +#endif // _MENUTOOLBAR_H_ + diff --git a/radio/src/gui/libopenui/numberedit.cpp b/radio/src/gui/libopenui/numberedit.cpp new file mode 100644 index 000000000..5a019740f --- /dev/null +++ b/radio/src/gui/libopenui/numberedit.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 +#include +#include "numberedit.h" +#include "draw_functions.h" + +#if defined(HARDWARE_TOUCH) +#include "keyboard_number.h" +#endif + +NumberEdit::NumberEdit(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, std::function getValue, std::function setValue, LcdFlags flags): + BaseNumberEdit(parent, rect, vmin, vmax, std::move(getValue), std::move(setValue), flags) +{ +} + +void NumberEdit::paint(BitmapBuffer * dc) +{ + FormField::paint(dc); + + LcdFlags textColor; + if (editMode) + textColor = TEXT_INVERTED_COLOR; + else if (hasFocus()) + textColor = TEXT_INVERTED_BGCOLOR; + else if (enabled) + textColor = TEXT_COLOR; + else + textColor = CURVE_AXIS_COLOR; + + int32_t value = getValue(); + if (displayFunction) + displayFunction(dc, textColor, value); + else if (value == 0 && !zeroText.empty()) + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, zeroText.c_str(), textColor | flags); + else + drawNumber(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor | flags, 0, prefix.c_str(), suffix.c_str()); +} + +#if defined(HARDWARE_KEYS) +void NumberEdit::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (editMode) { + if (event == EVT_ROTARY_RIGHT) { + int value = getValue(); + do { + value += rotencSpeed * step; + } while (isValueAvailable && !isValueAvailable(value) && value <= vmax); + if (value <= vmax) + setValue(value); + else + AUDIO_KEY_ERROR(); + return; + } + else if (event == EVT_ROTARY_LEFT) { + int value = getValue(); + do { + value -= rotencSpeed * step; + } while (isValueAvailable && !isValueAvailable(value) && value >= vmin); + if (value >= vmin) + setValue(value); + else + AUDIO_KEY_ERROR(); + return; + } + } + + FormField::onKeyEvent(event); +} +#endif + +#if defined(HARDWARE_TOUCH) +bool NumberEdit::onTouchEnd(coord_t, coord_t) +{ + if (!enabled) { + return true; + } + + if (!hasFocus()) { + setFocus(); + } + + NumberKeyboard * keyboard = NumberKeyboard::instance(); + if (keyboard->getField() != this) { + keyboard->setField(this); + } + + return true; +} +#endif + +void NumberEdit::onFocusLost() +{ +#if defined(HARDWARE_TOUCH) + NumberKeyboard::instance()->disable(true); +#endif + + FormField::onFocusLost(); +} diff --git a/radio/src/gui/libopenui/numberedit.h b/radio/src/gui/libopenui/numberedit.h new file mode 100644 index 000000000..f4d37e138 --- /dev/null +++ b/radio/src/gui/libopenui/numberedit.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _NUMBEREDIT_H_ +#define _NUMBEREDIT_H_ + +#include "basenumberedit.h" +#include + +class NumberEdit : public BaseNumberEdit { + public: + NumberEdit(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, + std::function getValue, std::function setValue = nullptr, LcdFlags flags = 0); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "NumberEdit(" + std::to_string(getValue()) + ")"; + } +#endif + + void paint(BitmapBuffer * dc) override; + + void enable(bool enabled) + { + this->enabled = enabled; + } + + void setAvailableHandler(std::function handler) + { + isValueAvailable = std::move(handler); + } + + void setPrefix(std::string value) + { + prefix = std::move(value); + } + + void setSuffix(std::string value) + { + suffix = std::move(value); + } + + void setZeroText(std::string value) + { + zeroText = std::move(value); + } + + void setDisplayHandler(std::function function) + { + displayFunction = std::move(function); + } + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + void onFocusLost() override; + + protected: + std::function displayFunction; + std::string prefix; + std::string suffix; + std::string zeroText; + bool enabled = true; + std::function isValueAvailable; +}; + +#endif // _NUMBEREDIT_H_ diff --git a/radio/src/gui/libopenui/page.cpp b/radio/src/gui/libopenui/page.cpp new file mode 100644 index 000000000..14c20b2ff --- /dev/null +++ b/radio/src/gui/libopenui/page.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "page.h" +#include "mainwindow.h" +#include "keyboard_number.h" +#include "keyboard_text.h" +#include "keyboard_curve.h" +#include "opentx.h" // TODO for constants + +PageHeader::PageHeader(Page * parent, uint8_t icon): + Window(parent, { 0, 0, LCD_W, MENU_HEADER_HEIGHT }, OPAQUE), + icon(icon) +#if defined(HARDWARE_TOUCH) + , back(this, { 0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH }, ICON_BACK, + [=]() -> uint8_t { + parent->deleteLater(); + return 0; + }, BUTTON_NOFOCUS) +#endif +{ +} + +void PageHeader::paint(BitmapBuffer * dc) +{ + theme->drawMenuBackground(dc, icon, ""); + dc->drawSolidFilledRect(MENU_HEADER_HEIGHT, 0, LCD_W - MENU_HEADER_HEIGHT, MENU_HEADER_HEIGHT, HEADER_BGCOLOR); +} + +Page::Page(unsigned icon): + Window(&mainWindow, {0, 0, LCD_W, LCD_H}, OPAQUE), + header(this, icon), + body(this, { 0, MENU_HEADER_HEIGHT, LCD_W, LCD_H - MENU_HEADER_HEIGHT }), + previousFocus(focusWindow) +{ + setFocus(); +} + +Page::~Page() +{ +#if defined(HARDWARE_TOUCH) + TextKeyboard::instance()->disable(false); + NumberKeyboard::instance()->disable(false); + CurveKeyboard::instance()->disable(false); +#endif +} + +void Page::deleteLater() +{ + if (previousFocus) { + previousFocus->setFocus(); + } + + Window::deleteLater(); +} + +void Page::paint(BitmapBuffer * dc) +{ + dc->clear(TEXT_BGCOLOR); +} + +#if defined(HARDWARE_KEYS) +void Page::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_LONG(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) { + killEvents(event); + deleteLater(); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool Page::onTouchEnd(coord_t x, coord_t y) +{ + if (Window::onTouchEnd(x, y)) + return true; + + TextKeyboard::instance()->disable(true); + NumberKeyboard::instance()->disable(true); + CurveKeyboard::instance()->disable(true); + return true; +} +#endif diff --git a/radio/src/gui/libopenui/page.h b/radio/src/gui/libopenui/page.h new file mode 100644 index 000000000..60598c9bb --- /dev/null +++ b/radio/src/gui/libopenui/page.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _PAGE_H_ +#define _PAGE_H_ + +#include "window.h" +#include "button.h" + +class Page; + +class PageHeader: public Window { + public: + PageHeader(Page * parent, uint8_t icon); + + void paint(BitmapBuffer * dc) override; + + protected: + uint8_t icon; +#if defined(HARDWARE_TOUCH) + IconButton back; +#endif +}; + +class Page: public Window { + public: + Page(unsigned icon); + + ~Page() override; + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Page"; + } +#endif + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchStart(coord_t x, coord_t y) override + { + Window::onTouchStart(x, y); + return true; + } + + bool onTouchEnd(coord_t x, coord_t y) override; + + bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override + { + Window::onTouchSlide(x, y, startX, startY, slideX, slideY); + return true; + } +#endif + + void deleteLater(); + + void paint(BitmapBuffer * dc) override; + + protected: + PageHeader header; + FormWindow body; + Window * previousFocus; +}; + +#endif // _PAGE_H_ diff --git a/radio/src/gui/libopenui/progress.cpp b/radio/src/gui/libopenui/progress.cpp new file mode 100644 index 000000000..8f9f5540d --- /dev/null +++ b/radio/src/gui/libopenui/progress.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "progress.h" +#include "theme.h" + +void Progress::paint(BitmapBuffer * dc) +{ + theme->drawProgressBar(dc, 0, 0, width(), height(), value); +} diff --git a/radio/src/gui/libopenui/progress.h b/radio/src/gui/libopenui/progress.h new file mode 100644 index 000000000..1500e95ef --- /dev/null +++ b/radio/src/gui/libopenui/progress.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _PROGRESS_H_ +#define _PROGRESS_H_ + +#include "window.h" + +class Progress : public Window { + public: + Progress(Window * parent, const rect_t & rect): + Window(parent, rect) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Progress"; + } +#endif + + void setValue(int value) + { + this->value = value; + invalidate(); + } + + void paint(BitmapBuffer * dc) override; + + protected: + int value; +}; + +#endif diff --git a/radio/src/gui/libopenui/slider.cpp b/radio/src/gui/libopenui/slider.cpp new file mode 100644 index 000000000..a0b84099d --- /dev/null +++ b/radio/src/gui/libopenui/slider.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "slider.h" +#include "bitmaps.h" +#include "opentx.h" // TODO remove this + +void Slider::paint(BitmapBuffer * dc) +{ + int val = limit(vmin, getValue(), vmax); + int w = divRoundClosest((rect.w - 16) * (val - vmin), vmax - vmin); + + if (editMode) { + dc->drawBitmapPattern(0, 11, LBM_SLIDER_BAR_LEFT, TEXT_INVERTED_BGCOLOR); + dc->drawSolidFilledRect(4, 11, rect.w - 8, 4, TEXT_INVERTED_BGCOLOR); + dc->drawBitmapPattern(rect.w - 4, 11, LBM_SLIDER_BAR_RIGHT, TEXT_INVERTED_BGCOLOR); + } + else { + dc->drawBitmapPattern(0, 11, LBM_SLIDER_BAR_LEFT, LINE_COLOR); + dc->drawSolidFilledRect(4, 11, rect.w - 8, 4, LINE_COLOR); + dc->drawBitmapPattern(rect.w - 4, 11, LBM_SLIDER_BAR_RIGHT, LINE_COLOR); + } + + dc->drawBitmapPattern(w, 5, LBM_SLIDER_POINT_OUT, TEXT_COLOR); + dc->drawBitmapPattern(w, 5, LBM_SLIDER_POINT_MID, TEXT_BGCOLOR); + // if ((options & INVERS) && (!(options & BLINK) || !BLINK_ON_PHASE)) + if (hasFocus()) { + dc->drawBitmapPattern(w, 5, LBM_SLIDER_POINT_IN, TEXT_INVERTED_BGCOLOR); + } +} + +int Slider::value(coord_t x) const +{ + return vmin + ((vmax - vmin) * x + (rect.w / 2)) / rect.w; +} + +#if defined(HARDWARE_KEYS) +void Slider::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (editMode) { + if (event == EVT_ROTARY_RIGHT) { + setValue(getValue() + rotencSpeed); + return; + } + else if (event == EVT_ROTARY_LEFT) { + setValue(getValue() - rotencSpeed); + return; + } + } + + FormField::onKeyEvent(event); +} +#endif + +#if defined(HARDWARE_TOUCH) +bool Slider::onTouchStart(coord_t x, coord_t y) +{ + if (!hasFocus()) { + setFocus(); + } + sliding = (value(x) == getValue()); + return true; +} + +bool Slider::onTouchEnd(coord_t x, coord_t y) +{ + setValue(value(x)); + invalidate(); + return true; +} + +bool Slider::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) +{ + if (hasFocus() && sliding) { + int newValue = value(x); + if (getValue() != newValue) { + setValue(newValue); + invalidate(); + } + } + return true; +} +#endif diff --git a/radio/src/gui/libopenui/slider.h b/radio/src/gui/libopenui/slider.h new file mode 100644 index 000000000..84d8c5667 --- /dev/null +++ b/radio/src/gui/libopenui/slider.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _SLIDER_H_ +#define _SLIDER_H_ + +#include "form.h" + +class Slider : public FormField { + public: + Slider(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, std::function getValue, std::function setValue): + FormField(parent, rect), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + _setValue(std::move(setValue)) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Slider"; + } +#endif + + void setValue(int32_t value) + { + _setValue(limit(vmin, value, vmax)); + invalidate(); + } + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchStart(coord_t x, coord_t y) override; + + bool onTouchEnd(coord_t x, coord_t y) override; + + bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override; +#endif + + protected: + int value(coord_t x) const; + int32_t vmin; + int32_t vmax; + bool sliding = false; + std::function getValue; + std::function _setValue; +}; + +#endif diff --git a/radio/src/gui/libopenui/sourcechoice.cpp b/radio/src/gui/libopenui/sourcechoice.cpp new file mode 100644 index 000000000..6fa2d7177 --- /dev/null +++ b/radio/src/gui/libopenui/sourcechoice.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "libopenui_config.h" +#include "sourcechoice.h" +#include "menutoolbar.h" +#include "menu.h" +#include "dataconstants.h" +#include "lcd.h" +#include "strhelpers.h" +#include "draw_functions.h" + +class SourceChoiceMenuToolbar : public MenuToolbar { + public: + SourceChoiceMenuToolbar(SourceChoice * choice, Menu * menu): + MenuToolbar(choice, menu) + { + addButton(char('\314'), MIXSRC_FIRST_INPUT, MIXSRC_LAST_INPUT); +#if defined(LUA_MODEL_SCRIPTS) + addButton(char('\322'), MIXSRC_LAST_LUA, MIXSRC_FIRST_LUA); +#endif + addButton(char('\307'), MIXSRC_FIRST_STICK, MIXSRC_LAST_STICK); + addButton(char('\310'), MIXSRC_FIRST_POT, MIXSRC_LAST_POT); + addButton(char('\315'), MIXSRC_MAX, MIXSRC_MAX); +#if defined(HELI) + addButton(char('\316'), MIXSRC_FIRST_HELI, MIXSRC_LAST_HELI); +#endif + addButton(char('\313'), MIXSRC_FIRST_TRIM, MIXSRC_LAST_TRIM); + addButton(char('\312'), MIXSRC_FIRST_SWITCH, MIXSRC_LAST_SWITCH); + addButton(char('\317'), MIXSRC_FIRST_TRAINER, MIXSRC_LAST_TRAINER); + addButton(char('\320'), MIXSRC_FIRST_CH, MIXSRC_LAST_CH); +#if defined(GVARS) + addButton(char('\311'), MIXSRC_LAST_GVAR, MIXSRC_FIRST_GVAR); +#endif + addButton(char('\321'), MIXSRC_FIRST_TELEM, MIXSRC_LAST_TELEM); + } +}; + +void SourceChoice::paint(BitmapBuffer * dc) +{ + FormField::paint(dc); + + unsigned value = getValue(); + LcdFlags textColor; + if (editMode) + textColor = TEXT_INVERTED_COLOR; + else if (hasFocus()) + textColor = TEXT_INVERTED_BGCOLOR; + else if (value == 0) + textColor = CURVE_AXIS_COLOR; + else + textColor = 0; + drawSource(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor); +} + +void SourceChoice::fillMenu(Menu * menu, std::function filter) +{ + auto value = getValue(); + int count = 0; +#if defined(HARDWARE_TOUCH) + int current = -1; +#else + int current = 0; +#endif + + menu->removeLines(); + + for (int i = vmin; i <= vmax; ++i) { + if (filter && !filter(i)) + continue; + if (isValueAvailable && !isValueAvailable(i)) + continue; + menu->addLine(getSourceString(i), [=]() { + setValue(i); + }); + if (value == i) { + current = count; + } + ++count; + } + + if (current >= 0) { + menu->select(current); + } +} + +void SourceChoice::openMenu() +{ + auto menu = new Menu(); + fillMenu(menu); + menu->setToolbar(new SourceChoiceMenuToolbar(this, menu)); + menu->setCloseHandler([=]() { + editMode = false; + setFocus(); + }); +} + +#if defined(HARDWARE_KEYS) +void SourceChoice::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + editMode = true; + invalidate(); + openMenu(); + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool SourceChoice::onTouchEnd(coord_t, coord_t) +{ + openMenu(); + setFocus(); + return true; +} +#endif diff --git a/radio/src/gui/libopenui/sourcechoice.h b/radio/src/gui/libopenui/sourcechoice.h new file mode 100644 index 000000000..af98e5732 --- /dev/null +++ b/radio/src/gui/libopenui/sourcechoice.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _SOURCECHOICE_H_ +#define _SOURCECHOICE_H_ + +#include "form.h" + +class Menu; + +bool isSourceAvailable(int source); + +class SourceChoice : public FormField { + template friend class MenuToolbar; + + public: + SourceChoice(Window * parent, const rect_t & rect, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue): + FormField(parent, rect), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + setValue(std::move(setValue)) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "SourceChoice"; + } +#endif + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + void setAvailableHandler(std::function handler) + { + isValueAvailable = std::move(handler); + } + + protected: + int16_t vmin; + int16_t vmax; + std::function getValue; + std::function setValue; + std::function isValueAvailable = isSourceAvailable; + void fillMenu(Menu * menu, std::function condition=nullptr); + void openMenu(); +}; + +#endif // _SOURCECHOICE_H_ diff --git a/radio/src/gui/libopenui/static.h b/radio/src/gui/libopenui/static.h new file mode 100644 index 000000000..53893e944 --- /dev/null +++ b/radio/src/gui/libopenui/static.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _STATIC_H_ +#define _STATIC_H_ + +#include +#include "window.h" +#include "button.h" // TODO just for BUTTON_BACKGROUND + +class StaticText : public Window { + public: + StaticText(Window * parent, const rect_t & rect, std::string text = "", LcdFlags flags = 0) : + Window(parent, rect), + text(std::move(text)), + flags(flags) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "StaticText \"" + text + "\""; + } +#endif + + void setFlags(LcdFlags flags) + { + this->flags = flags; + invalidate(); + } + + void paint(BitmapBuffer * dc) + { + if (flags & BUTTON_BACKGROUND) + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, CURVE_AXIS_COLOR); + if (flags & CENTERED) + dc->drawText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2, text.c_str(), CENTERED | flags); + else + dc->drawText(0, 0, text.c_str(), flags); + } + + void setText(std::string value) + { + text = std::move(value); + invalidate(); + } + + protected: + std::string text; + LcdFlags flags; +}; + +class Subtitle: public StaticText { + public: + Subtitle(Window * parent, const rect_t & rect, const char * text): + StaticText(parent, rect, text, BOLD) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Subtitle \"" + text + "\""; + } +#endif +}; + +class StaticBitmap: public Window { + public: + StaticBitmap(Window * parent, const rect_t & rect, const char * filename): + Window(parent, rect), + bitmap(BitmapBuffer::load(filename)) + { + } + + StaticBitmap(Window * parent, const rect_t & rect, const BitmapBuffer * bitmap): + Window(parent, rect), + bitmap(bitmap) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "StaticBitmap"; + } +#endif + + void paint(BitmapBuffer * dc) override + { + dc->drawBitmap(0, 0, bitmap); + } + + protected: + const BitmapBuffer * bitmap; +}; + +#endif diff --git a/radio/src/gui/libopenui/switchchoice.cpp b/radio/src/gui/libopenui/switchchoice.cpp new file mode 100644 index 000000000..50977896c --- /dev/null +++ b/radio/src/gui/libopenui/switchchoice.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "libopenui_config.h" +#include "switchchoice.h" +#include "menutoolbar.h" +#include "menu.h" +#include "draw_functions.h" +#include "strhelpers.h" +#include "dataconstants.h" + +class SwitchChoiceMenuToolbar : public MenuToolbar { + public: + SwitchChoiceMenuToolbar(SwitchChoice * choice, Menu * menu): + MenuToolbar(choice, menu) + { + addButton(char('\312'), SWSRC_FIRST_SWITCH, SWSRC_LAST_SWITCH); + addButton(char('\313'), SWSRC_FIRST_TRIM, SWSRC_LAST_TRIM); + addButton(char('\312'), SWSRC_FIRST_LOGICAL_SWITCH, SWSRC_LAST_LOGICAL_SWITCH); + } +}; + +void SwitchChoice::paint(BitmapBuffer * dc) +{ + FormField::paint(dc); + + unsigned value = getValue(); + LcdFlags textColor; + if (editMode) + textColor = TEXT_INVERTED_COLOR; + else if (hasFocus()) + textColor = TEXT_INVERTED_BGCOLOR; + else if (value == 0) + textColor = CURVE_AXIS_COLOR; + else + textColor = 0; + drawSwitch(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor); +} + +void SwitchChoice::fillMenu(Menu * menu, std::function filter) +{ + auto value = getValue(); + int count = 0; +#if defined(HARDWARE_TOUCH) + int current = -1; +#else + int current = 0; +#endif + + menu->removeLines(); + + for (int i = vmin; i <= vmax; ++i) { + if (filter && !filter(i)) + continue; + if (isValueAvailable && !isValueAvailable(i)) + continue; + menu->addLine(getSwitchPositionName(i), [=]() { + setValue(i); + }); + if (value == i) { + current = count; + } + ++count; + } + + if (current >= 0) { + menu->select(current); + } +} + +void SwitchChoice::openMenu() +{ + auto menu = new Menu(); + fillMenu(menu); + menu->setToolbar(new SwitchChoiceMenuToolbar(this, menu)); + menu->setCloseHandler([=]() { + editMode = false; + setFocus(); + }); +} + +#if defined(HARDWARE_KEYS) +void SwitchChoice::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + editMode = true; + invalidate(); + openMenu(); + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool SwitchChoice::onTouchEnd(coord_t, coord_t) +{ + openMenu(); + setFocus(); + return true; +} +#endif diff --git a/radio/src/gui/libopenui/switchchoice.h b/radio/src/gui/libopenui/switchchoice.h new file mode 100644 index 000000000..b5755c6ae --- /dev/null +++ b/radio/src/gui/libopenui/switchchoice.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _SWITCHCHOICE_H_ +#define _SWITCHCHOICE_H_ + +#include "form.h" + +class Menu; +bool isSwitchAvailableInMixes(int swtch); + +class SwitchChoice : public FormField { + template friend class MenuToolbar; + + public: + SwitchChoice(Window * parent, const rect_t & rect, int vmin, int vmax, std::function getValue, std::function setValue): + FormField(parent, rect), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + setValue(std::move(setValue)) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "SwitchChoice"; + } +#endif + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override ; +#endif + + void setAvailableHandler(std::function handler) + { + isValueAvailable = std::move(handler); + } + + protected: + int16_t vmin; + int16_t vmax; + std::function getValue; + std::function setValue; + std::function isValueAvailable = isSwitchAvailableInMixes; + void fillMenu(Menu * menu, std::function condition=nullptr); + void openMenu(); +}; + +#endif // _SWITCHCHOICE_H_ diff --git a/radio/src/gui/libopenui/tabsgroup.cpp b/radio/src/gui/libopenui/tabsgroup.cpp new file mode 100644 index 000000000..248a096ca --- /dev/null +++ b/radio/src/gui/libopenui/tabsgroup.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "tabsgroup.h" +#include "mainwindow.h" + +#if defined(HARDWARE_TOUCH) +#include "keyboard_number.h" +#include "keyboard_text.h" +#include "keyboard_curve.h" +#endif + +#include "opentx.h" // TODO for constants... +#include "view_main.h" + +TabsGroupHeader::TabsGroupHeader(TabsGroup * parent, uint8_t icon): + Window(parent, { 0, 0, LCD_W, MENU_BODY_TOP }, OPAQUE), +#if defined(HARDWARE_TOUCH) + back(this, { 0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH }, ICON_BACK, + [=]() -> uint8_t { + parent->deleteLater(); + return 1; + }, BUTTON_NOFOCUS), +#endif + icon(icon), + carousel(this, parent) +{ +} + +void TabsGroupHeader::paint(BitmapBuffer * dc) +{ + theme->drawMenuBackground(dc, icon, title); +} + +TabsCarousel::TabsCarousel(Window * parent, TabsGroup * menu): + Window(parent, { MENU_HEADER_BUTTONS_LEFT, 0, LCD_W - MENU_HEADER_BUTTONS_LEFT, MENU_HEADER_HEIGHT + 10 }, TRANSPARENT), + menu(menu) +{ +} + +void TabsCarousel::updateInnerWidth() +{ + setInnerWidth(padding_left + MENU_HEADER_BUTTON_WIDTH * menu->tabs.size()); +} + +void TabsCarousel::paint(BitmapBuffer * dc) +{ + theme->drawMenuHeader(dc, menu->tabs, currentIndex); +} + +#if defined(HARDWARE_TOUCH) +bool TabsCarousel::onTouchEnd(coord_t x, coord_t y) +{ + unsigned index = (x - padding_left) / MENU_HEADER_BUTTON_WIDTH; + menu->setCurrentTab(index); + setCurrentIndex(index); + return true; +} +#endif + +TabsGroup::TabsGroup(uint8_t icon): + Window(&mainWindow, { 0, 0, LCD_W, LCD_H }, OPAQUE), + header(this, icon), + body(this, { 0, MENU_BODY_TOP, LCD_W, MENU_BODY_HEIGHT }) +{ +} + +TabsGroup::~TabsGroup() +{ + for (auto tab: tabs) { + delete tab; + } + + body.deleteChildren(); + +#if defined(HARDWARE_TOUCH) + TextKeyboard::instance()->disable(false); + NumberKeyboard::instance()->disable(false); + CurveKeyboard::instance()->disable(false); +#endif +} + +void TabsGroup::addTab(PageTab * page) +{ + tabs.push_back(page); + if (!currentTab) { + setCurrentTab(page); + } + header.carousel.updateInnerWidth(); +} + +void TabsGroup::removeTab(unsigned index) +{ + if (currentTab == tabs[index]) { + setCurrentTab(max(0, index - 1)); + } + + tabs.erase(tabs.begin() + index); +} + +void TabsGroup::setCurrentTab(PageTab * tab) +{ + if (tab != currentTab) { + setFocus(); + FormField::clearCurrentField(); + body.clear(); +#if defined(HARDWARE_TOUCH) + TextKeyboard::instance()->disable(false); + NumberKeyboard::instance()->disable(false); + CurveKeyboard::instance()->disable(false); +#endif + currentTab = tab; + tab->build(&body); + header.setTitle(tab->title.c_str()); + invalidate(); + } +} + +void TabsGroup::checkEvents() +{ + Window::checkEvents(); + if (currentTab) { + currentTab->checkEvents(); + } +} + +#if defined(HARDWARE_KEYS) +void TabsGroup::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_PGDN)) { + uint8_t current = header.carousel.getCurrentIndex() + 1; + setCurrentTab(current >= tabs.size() ? 0 : current); + } + else if (event == EVT_KEY_LONG(KEY_PGDN)) { + killEvents(event); + uint8_t current = header.carousel.getCurrentIndex(); + setCurrentTab(current == 0 ? tabs.size() - 1 : current - 1); + } + else if (event == EVT_KEY_LONG(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) { + killEvents(event); + ViewMain::instance->setFocus(); + deleteLater(); + } + else if (parent) { + parent->onKeyEvent(event); + } +} +#endif + +void TabsGroup::paint(BitmapBuffer * dc) +{ + dc->clear(TEXT_BGCOLOR); +} + +#if defined(HARDWARE_TOUCH) +bool TabsGroup::onTouchEnd(coord_t x, coord_t y) +{ + if (Window::onTouchEnd(x, y)) + return true; + + TextKeyboard::instance()->disable(true); + NumberKeyboard::instance()->disable(true); + CurveKeyboard::instance()->disable(true); + return true; +} +#endif diff --git a/radio/src/gui/libopenui/tabsgroup.h b/radio/src/gui/libopenui/tabsgroup.h new file mode 100644 index 000000000..b81a1ab06 --- /dev/null +++ b/radio/src/gui/libopenui/tabsgroup.h @@ -0,0 +1,186 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _TABSGROUP_H_ +#define _TABSGROUP_H_ + +#include +#include "button.h" +#include "form.h" + +class TabsGroup; + +class PageTab { + friend class TabsCarousel; + friend class TabsGroup; + + public: + PageTab(std::string title, unsigned icon): + title(std::move(title)), + icon(icon) + { + } + + virtual ~PageTab() + { + } + + virtual void build(FormWindow * window) = 0; + + virtual void checkEvents() + { + } + + void destroy() + { + if (onPageDestroyed) { + onPageDestroyed(); + } + delete this; + } + + void setOnPageDestroyedHandler(std::function handler) + { + onPageDestroyed = std::move(handler); + } + + unsigned getIcon() const + { + return icon; + } + + protected: + std::string title; + unsigned icon; + std::function onPageDestroyed; +}; + +class TabsCarousel: public Window { + public: + TabsCarousel(Window * parent, TabsGroup * menu); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TabsCarousel"; + } +#endif + + inline void setCurrentIndex(uint8_t index) + { + currentIndex = index; + } + + inline uint8_t getCurrentIndex() + { + return currentIndex; + } + + void updateInnerWidth(); + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + constexpr static uint8_t padding_left = 3; + TabsGroup * menu; + uint8_t currentIndex = 0; +}; + +class TabsGroupHeader: public Window { + friend class TabsGroup; + + public: + TabsGroupHeader(TabsGroup * menu, uint8_t icon); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TabsGroupHeader"; + } +#endif + + void paint(BitmapBuffer * dc) override; + + void setTitle(const char * value) + { + title = value; + } + + protected: +#if defined(HARDWARE_TOUCH) + IconButton back; +#endif + uint8_t icon; + TabsCarousel carousel; + const char * title = nullptr; +}; + +class TabsGroup: public Window { + friend class TabsCarousel; + + public: + TabsGroup(uint8_t icon); + + ~TabsGroup(); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TabsGroup"; + } +#endif + + void addTab(PageTab * page); + + void removeTab(unsigned index); + + void setCurrentTab(unsigned index) + { + if (index < tabs.size()) { + header.carousel.setCurrentIndex(index); + setCurrentTab(tabs[index]); + } + } + + void checkEvents() override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + TabsGroupHeader header; + FormWindow body; + std::vector tabs; + PageTab * currentTab = nullptr; + void setCurrentTab(PageTab * tab); +}; + +#endif diff --git a/radio/src/gui/libopenui/textedit.cpp b/radio/src/gui/libopenui/textedit.cpp new file mode 100644 index 000000000..b56d302a2 --- /dev/null +++ b/radio/src/gui/libopenui/textedit.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "textedit.h" +// #include "keyboard_text.h" +#include "draw_functions.h" +#include "opentx.h" + +void TextEdit::paint(BitmapBuffer * dc) +{ + LcdFlags flags = ZCHAR; + + FormField::paint(dc); + + if (editMode) { + dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, length, TEXT_INVERTED_COLOR | flags); + coord_t left = (cursorPos == 0 ? 0 : getTextWidth(value, cursorPos, flags)); + char s[] = { (flags & ZCHAR) ? zchar2char(value[cursorPos]) : value[cursorPos], '\0' }; + dc->drawSolidFilledRect(FIELD_PADDING_LEFT + left - 1, FIELD_PADDING_TOP + 1, getTextWidth(s, 1) + 1, height() - 2, TEXT_INVERTED_COLOR); + dc->drawText(FIELD_PADDING_LEFT + left, FIELD_PADDING_TOP, s, TEXT_COLOR); + } + else { + const char * displayedValue = value; + LcdFlags textColor; + if (zlen(value, length) == 0) { + displayedValue = "---"; + textColor = hasFocus() ? TEXT_INVERTED_BGCOLOR : CURVE_AXIS_COLOR; + } + else if (hasFocus()) { + textColor = TEXT_INVERTED_BGCOLOR | ZCHAR; + } + else { + textColor = TEXT_COLOR | ZCHAR; + } + dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, displayedValue, length, textColor); + } + +#if defined(HARDWARE_TOUCH) + auto keyboard = TextKeyboard::instance(); + if (hasFocus() && keyboard->getField() == this) { + coord_t cursorPos = keyboard->getCursorPos(); + dc->drawSolidFilledRect(cursorPos + 2, FIELD_PADDING_TOP, 2, 21, 0); // TEXT_INVERTED_BGCOLOR); + } +#endif +} + +#if defined(HARDWARE_KEYS) +void TextEdit::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + LcdFlags flags = ZCHAR; + + if (editMode) { + int8_t c = value[cursorPos]; + if (!(flags & ZCHAR)) { + c = char2zchar(c); + } + int8_t v = c; + + switch (event) { + case EVT_ROTARY_RIGHT: + case EVT_ROTARY_LEFT: + v = limit(0, abs(v) + (event == EVT_ROTARY_RIGHT ? +rotencSpeed : -rotencSpeed), ZCHAR_MAX); + if (c <= 0) v = -v; + break; + + case EVT_KEY_BREAK(KEY_LEFT): + if (cursorPos > 0) { + cursorPos--; + invalidate(); + } + break; + + case EVT_KEY_BREAK(KEY_RIGHT): + if (cursorPos < length - 1) { + cursorPos++; + invalidate(); + } + break; + + case EVT_KEY_BREAK(KEY_ENTER): + if (cursorPos < length - 1) { + cursorPos++; + invalidate(); + } + else { + FormField::onKeyEvent(event); + } + break; + + case EVT_KEY_BREAK(KEY_EXIT): + cursorPos = 0; + FormField::onKeyEvent(event); + break; + + case EVT_KEY_LONG(KEY_ENTER): + if (v == 0) { + killEvents(event); + FormField::onKeyEvent(EVT_KEY_BREAK(KEY_ENTER)); + break; + } + // no break + + case EVT_KEY_LONG(KEY_LEFT): + case EVT_KEY_LONG(KEY_RIGHT): + if (v >= -26 && v <= 26) { + v = -v; // toggle case + if (event == EVT_KEY_LONG(KEY_LEFT)) { + killEvents(KEY_LEFT); + } + } + break; + } + + if (c != v) { + if (!(flags & ZCHAR)) { + if (v != '\0' || value[cursorPos+1] != '\0') + v = zchar2char(v); + } + value[cursorPos] = v; + invalidate(); + // TODO storageDirty(...); + } + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool TextEdit::onTouchEnd(coord_t x, coord_t y) +{ + if (!hasFocus()) { + setFocus(); + } + + auto keyboard = TextKeyboard::instance(); + if (keyboard->getField() != this) { + keyboard->setField(this); + } + + keyboard->setCursorPos(x); + + return true; +} + +void TextEdit::onFocusLost() +{ + TextKeyboard::instance()->disable(true); + // TODO storageDirty(...); +} +#endif diff --git a/radio/src/gui/libopenui/textedit.h b/radio/src/gui/libopenui/textedit.h new file mode 100644 index 000000000..914d68061 --- /dev/null +++ b/radio/src/gui/libopenui/textedit.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _TEXTEDIT_H_ +#define _TEXTEDIT_H_ + +#include "form.h" + +uint8_t zlen(const char *str, uint8_t size); + +class TextEdit : public FormField { + friend class TextKeyboard; + + public: + TextEdit(Window * parent, const rect_t &rect, char * value, uint8_t length, LcdFlags flags = 0) : + FormField(parent, rect), + value(value), + length(length) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TextEdit"; + } +#endif + + uint8_t getMaxLength() + { + return length; + } + + char * getData() + { + return value; + } + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; + + void onFocusLost() override; +#endif + + protected: + char * value; + uint8_t length; + uint8_t cursorPos = 0; +}; + +#endif // _TEXTEDIT_H_ diff --git a/radio/src/gui/libopenui/timeedit.cpp b/radio/src/gui/libopenui/timeedit.cpp new file mode 100644 index 000000000..a23e95f67 --- /dev/null +++ b/radio/src/gui/libopenui/timeedit.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "libopenui_config.h" +#include "timeedit.h" +#include "keyboard_number.h" +#include "draw_functions.h" +#include "strhelpers.h" +#include +#include + +TimeEdit::TimeEdit(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, std::function getValue, std::function setValue, LcdFlags flags): + BaseNumberEdit(parent, rect, vmin, vmax, getValue, setValue, flags) +{ +} + +void TimeEdit::paint(BitmapBuffer * dc) +{ + FormField::paint(dc); + + LcdFlags textColor; + if (editMode) + textColor = TEXT_INVERTED_COLOR; + else if (hasFocus()) + textColor = TEXT_INVERTED_BGCOLOR; + else + textColor = TEXT_COLOR; + + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, getTimerString(getValue(), (flags & TIMEHOUR) != 0), textColor); +} + +#if defined(HARDWARE_KEYS) +// TODO could be moved to BaseNumberEdit +void TimeEdit::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (editMode) { + if (event == EVT_ROTARY_RIGHT) { + int value = getValue(); + value += rotencSpeed * step; + if (value <= vmax) + setValue(value); + else + AUDIO_KEY_ERROR(); + return; + } + else if (event == EVT_ROTARY_LEFT) { + int value = getValue(); + value -= rotencSpeed * step; + if (value >= vmin) + setValue(value); + else + AUDIO_KEY_ERROR(); + return; + } + } + + FormField::onKeyEvent(event); +} +#endif + +#if defined(HARDWARE_TOUCH) +bool TimeEdit::onTouchEnd(coord_t x, coord_t y) +{ + if (!hasFocus()) { + setFocus(); + } + + NumberKeyboard * keyboard = NumberKeyboard::instance(); + if (keyboard->getField() != this) { + keyboard->setField(this); + } + + return true; +} + +void TimeEdit::onFocusLost() +{ + NumberKeyboard::instance()->disable(true); +} +#endif diff --git a/radio/src/gui/libopenui/timeedit.h b/radio/src/gui/libopenui/timeedit.h new file mode 100644 index 000000000..0158c3eec --- /dev/null +++ b/radio/src/gui/libopenui/timeedit.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _TIMEEDIT_H_ +#define _TIMEEDIT_H_ + +#include "basenumberedit.h" + +class TimeEdit : public BaseNumberEdit { + public: + TimeEdit(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, std::function getValue, std::function setValue = nullptr, LcdFlags flags = 0); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TimeEdit"; + } +#endif + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; + + void onFocusLost() override; +#endif +}; + +#endif // _TIMEEDIT_H_ diff --git a/radio/src/gui/libopenui/window.cpp b/radio/src/gui/libopenui/window.cpp new file mode 100644 index 000000000..49025d5d7 --- /dev/null +++ b/radio/src/gui/libopenui/window.cpp @@ -0,0 +1,396 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "window.h" +#include "keys.h" + +Window * Window::focusWindow = nullptr; +std::list Window::trash; + +Window::Window(Window * parent, const rect_t & rect, uint8_t flags): + parent(parent), + rect(rect), + innerWidth(rect.w), + innerHeight(rect.h), + windowFlags(flags) +{ + if (parent) { + parent->addChild(this); + if (!(flags & TRANSPARENT)) { + invalidate(); + } + } +} + +Window::~Window() +{ + TRACE_WINDOWS("Destroy %p %s", this, getWindowDebugString().c_str()); + if (focusWindow == this) { + focusWindow = nullptr; + } +} + +void Window::attach(Window * window) +{ + if (parent) + detach(); + parent = window; + parent->addChild(this); +} + +void Window::detach() +{ + if (parent) { + parent->removeChild(this); + parent = nullptr; + } +} + +void Window::deleteLater(bool detach) +{ + TRACE_WINDOWS("Delete %p %s", this, getWindowDebugString().c_str()); + + if (focusWindow == this) { + focusWindow = nullptr; + } + + if (detach) + this->detach(); + else + parent = nullptr; + + if (closeHandler) { + closeHandler(); + } + + trash.push_back(this); +} + +void Window::clear() +{ + scrollPositionX = 0; + scrollPositionY = 0; + innerWidth = rect.w; + innerHeight = rect.h; + deleteChildren(); + invalidate(); +} + +void Window::deleteChildren() +{ + for (auto window: children) { + window->deleteLater(false); + } + children.clear(); +} + +void Window::clearFocus() +{ + if (focusWindow) { + focusWindow->onFocusLost(); + focusWindow = nullptr; + } +} + +void Window::setFocus() +{ + TRACE_WINDOWS("%s setFocus()", getWindowDebugString().c_str()); + + if (focusWindow != this) { + clearFocus(); + focusWindow = this; + if (focusHandler) { + focusHandler(); + } + } + + Window * parent = this->parent; + while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { + parent = parent->parent; + } + + if (parent) { + parent->scrollTo(this); + invalidate(); + } +} + +void Window::setScrollPositionX(coord_t value) +{ + coord_t newScrollPosition = max(0, min(innerWidth - width(), value)); + if (newScrollPosition != scrollPositionX) { + scrollPositionX = newScrollPosition; + invalidate(); + } +} + +void Window::setScrollPositionY(coord_t value) +{ + coord_t newScrollPosition = max(0, min(innerHeight - height(), value)); + if (newScrollPosition != scrollPositionY) { + scrollPositionY = newScrollPosition; + invalidate(); + } +} + +void Window::scrollTo(Window * child) +{ + coord_t offsetY = 0; + Window * parent = child->getParent(); + while (parent && parent != this) { + offsetY += parent->top(); + parent = parent->getParent(); + } + + coord_t top = offsetY + child->top(); + coord_t bottom = offsetY + child->bottom(); + + if (top < scrollPositionY) { + setScrollPositionY(top - 5); + } + else if (bottom > scrollPositionY + height() - 5) { + setScrollPositionY(bottom - height() + 5); + } +} + +void Window::fullPaint(BitmapBuffer * dc) +{ + TRACE_WINDOWS("%s", getWindowDebugString().c_str()); + + if (windowFlags & PAINT_CHILDREN_FIRST) { + coord_t xmin, xmax, ymin, ymax; + coord_t x = dc->getOffsetX(); + coord_t y = dc->getOffsetY(); + dc->getClippingRect(xmin, xmax, ymin, ymax); + paintChildren(dc); + dc->setOffset(x, y); + dc->setClippingRect(xmin, xmax, ymin, ymax); + } + + paint(dc); + + if (!(windowFlags & NO_SCROLLBAR)) { + drawVerticalScrollbar(dc); + } + + if (!(windowFlags & PAINT_CHILDREN_FIRST)) { + paintChildren(dc); + } +} + +bool Window::isChildFullSize(Window * child) +{ + return child->top() == 0 && child->height() == height() && child->left() == 0 && child->width() == width(); +} + +bool Window::isChildVisible(Window * window) +{ + for (auto rit = children.rbegin(); rit != children.rend(); rit++) { + auto child = *rit; + if (child == window) { + return true; + } + if ((child->getWindowFlags() & OPAQUE) & isChildFullSize(child)) { + return false; + } + } + return false; +} + +void Window::paintChildren(BitmapBuffer * dc) +{ + coord_t x = dc->getOffsetX(); + coord_t y = dc->getOffsetY(); + coord_t xmin, xmax, ymin, ymax; + dc->getClippingRect(xmin, xmax, ymin, ymax); + + auto it = children.end(); + + while(it != children.begin()) { + auto child = *(--it); + if ((child->windowFlags & OPAQUE) && isChildFullSize(child)) { + break; + } + } + + for (; it != children.end(); it++) { + auto child = *it; + + coord_t child_xmin = x + child->rect.x; + if (child_xmin >= xmax) + continue; + coord_t child_ymin = y + child->rect.y; + if (child_ymin >= ymax) + continue; + coord_t child_xmax = child_xmin + child->rect.w; + if (child_xmax <= xmin) + continue; + coord_t child_ymax = child_ymin + child->rect.h; + if (child_ymax <= ymin) + continue; + + dc->setOffset(x + child->rect.x - child->scrollPositionX, y + child->rect.y - child->scrollPositionY); + dc->setClippingRect(max(xmin, x + child->rect.left()), + min(xmax, x + child->rect.right()), + max(ymin, y + child->rect.top()), + min(ymax, y + child->rect.bottom())); + child->fullPaint(dc); + } +} + +void Window::checkEvents() +{ + auto copy = children; + for (auto child: copy) { + child->checkEvents(); + } + +#if defined(HARDWARE_KEYS) + if (this == focusWindow) { + event_t event = getEvent(false); + if (event) { + TRACE_WINDOWS("Event 0x%x received ...", event); + this->onKeyEvent(event); + } + } +#endif + + if (windowFlags & REFRESH_ALWAYS) { + invalidate(); + } +} + +#if defined(HARDWARE_KEYS) +void Window::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", Window::getWindowDebugString().c_str(), event); + if (parent) { + parent->onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool Window::onTouchStart(coord_t x, coord_t y) +{ + for (auto it = children.rbegin(); it != children.rend(); ++it) { + auto child = *it; + if (pointInRect(x, y, child->rect)) { + if (child->onTouchStart(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { + return true; + } + } + } + + return false; +} + +bool Window::onTouchEnd(coord_t x, coord_t y) +{ + for (auto it = children.rbegin(); it != children.rend(); ++it) { + auto child = *it; + if (pointInRect(x, y, child->rect)) { + if (child->onTouchEnd(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { + return true; + } + } + } + + return false; +} + +bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) +{ + for (auto it = children.rbegin(); it != children.rend(); ++it) { + auto child = *it; + if (pointInRect(startX, startY, child->rect)) { + if (child->onTouchSlide(x - child->rect.x, y - child->rect.y, startX - child->rect.x, startY - child->rect.y, slideX, slideY)) { + return true; + } + } + } + + if (slideY && innerHeight > rect.h) { + setScrollPositionY(scrollPositionY - slideY); + return true; + } + + if (slideX && innerWidth > rect.w) { + setScrollPositionX(scrollPositionX - slideX); + return true; + } + + return false; +} +#endif + +void Window::adjustInnerHeight() +{ + innerHeight = 0; + for (auto child: children) { + innerHeight = max(innerHeight, child->rect.y + child->rect.h); + } +} + +coord_t Window::adjustHeight() +{ + adjustInnerHeight(); + coord_t old = rect.h; + rect.h = innerHeight; + return rect.h - old; +} + +void Window::moveWindowsTop(coord_t y, coord_t delta) +{ + for (auto child: children) { + if (child->rect.y > y) { + child->rect.y += delta; + } + } + innerHeight += delta; +} + +void Window::invalidate(const rect_t & rect) +{ + if (isVisible()) { + parent->invalidate({this->rect.x + rect.x - parent->scrollPositionX, this->rect.y + rect.y - parent->scrollPositionY, rect.w, rect.h}); + } +} + +void Window::drawVerticalScrollbar(BitmapBuffer * dc) +{ + if (innerHeight > rect.h) { + coord_t x = rect.w - 3; + coord_t y = scrollPositionY + 3; + coord_t h = rect.h - 6; + // lcd->drawSolidFilledRect(x, y, 1, h, LINE_COLOR); + coord_t yofs = (h*scrollPositionY + innerHeight/2) / innerHeight; + coord_t yhgt = (h*rect.h + innerHeight/2) / innerHeight; + if (yhgt + yofs > h) + yhgt = h - yofs; + if (yhgt < 15) { + yhgt = 15; + if (y + yofs + yhgt > innerHeight) { + yofs = innerHeight - y - yhgt; + } + } + dc->drawSolidFilledRect(x-1, y + yofs, 3, yhgt, scrollbarColor); + } +} diff --git a/radio/src/gui/libopenui/window.h b/radio/src/gui/libopenui/window.h new file mode 100644 index 000000000..f80afab0e --- /dev/null +++ b/radio/src/gui/libopenui/window.h @@ -0,0 +1,330 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + + +#ifndef _WINDOW_H_ +#define _WINDOW_H_ + +#include +#include +#include +#include +#include +#include +#include "bitmapbuffer.h" +#include "libopenui_defines.h" +#include "libopenui_helpers.h" + +// OPAQUE/TRANSPARENT defined in WinGDI +#ifndef OPAQUE + #define OPAQUE 1 + #define TRANSPARENT 2 +#endif + +#define NO_SCROLLBAR 4 +#define FORWARD_SCROLL 8 +#define REFRESH_ALWAYS 16 +#define BORDER_FOCUS_ONLY 32 +#define PAINT_CHILDREN_FIRST 64 + +class Window { + friend class GridLayout; + + public: + Window(Window * parent, const rect_t & rect, uint8_t flags=0); + + virtual ~Window(); + +#if defined(DEBUG_WINDOWS) + virtual std::string getName() + { + return "Window"; + } + + std::string getRectString() + { + char result[32]; + sprintf(result, "[%d, %d, %d, %d]", left(), top(), width(), height()); + return result; + } + + std::string getIndentString() + { + std::string result; + auto tmp = parent; + while (tmp) { + result += " "; + tmp = tmp->getParent(); + } + return result; + } + + std::string getWindowDebugString() + { + return getIndentString() + getName() + " " + getRectString(); + } +#endif + + Window * getParent() const + { + return parent; + } + + uint8_t getWindowFlags() const + { + return windowFlags; + } + + void setWindowFlags(uint8_t flags) + { + windowFlags = flags; + } + + void setCloseHandler(std::function handler) + { + closeHandler = std::move(handler); + } + + void setFocusHandler(std::function handler) + { + focusHandler = std::move(handler); + } + + void deleteLater(bool detach=true); + + void clear(); + + void deleteChildren(); + + bool hasFocus() const + { + return focusWindow == this; + } + + static Window * getFocus() + { + return focusWindow; + } + + void scrollTo(Window * child); + + static void clearFocus(); + + void setFocus(); + + void setRect(rect_t rect) + { + this->rect = rect; + invalidate(); + } + + void setWidth(coord_t value) + { + rect.w = value; + invalidate(); + } + + void setHeight(coord_t value) + { + rect.h = value; + if (innerHeight <= value) { + setScrollPositionY(0); + } + invalidate(); + } + + coord_t getHeight() + { + return rect.h; + } + + void setLeft(coord_t x) + { + rect.x = x; + invalidate(); + } + + void setTop(coord_t y) + { + rect.y = y; + invalidate(); + } + + coord_t left() const + { + return rect.x; + } + + coord_t right() const + { + return rect.x + rect.w; + } + + coord_t top() const + { + return rect.y; + } + + coord_t bottom() const + { + return rect.y + rect.h; + } + + coord_t width() const + { + return rect.w; + } + + coord_t height() const + { + return rect.h; + } + + rect_t getRect() const + { + return rect; + } + + void setInnerWidth(coord_t w) + { + innerWidth = w; + if (width() >= w) { + scrollPositionX = 0; + } + } + + void setInnerHeight(coord_t h) + { + innerHeight = h; + if (height() >= h) { + scrollPositionY = 0; + } + } + + void setScrollbarColor(LcdFlags color) + { + scrollbarColor = color; + } + + coord_t getScrollPositionY() const + { + return scrollPositionY; + } + + void setScrollPositionX(coord_t value); + + void setScrollPositionY(coord_t value); + + bool isChildVisible(Window * window); + + bool isChildFullSize(Window * window); + + bool isVisible() + { + return parent && parent->isChildVisible(this); + } + + virtual void paint(BitmapBuffer * dc) + { + } + + void drawVerticalScrollbar(BitmapBuffer * dc); + + void paintChildren(BitmapBuffer * dc); + + void fullPaint(BitmapBuffer * dc); + + bool pointInRect(coord_t x, coord_t y, rect_t & rect) + { + return (x >= rect.x && x < rect.x + rect.w && y >= rect.y && y < rect.y + rect.h); + } + + virtual void onFocusLost() + { + TRACE_WINDOWS("%s onFocusLost()", getWindowDebugString().c_str()); + invalidate(); + }; + +#if defined(HARDWARE_KEYS) + virtual void onKeyEvent(event_t event); +#endif + +#if defined(HARDWARE_TOUCH) + virtual bool onTouchStart(coord_t x, coord_t y); + + virtual bool onTouchEnd(coord_t x, coord_t y); + + virtual bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY); +#endif + + void adjustInnerHeight(); + + coord_t adjustHeight(); + + void moveWindowsTop(coord_t y, coord_t delta); + + void invalidate() + { + invalidate({0, 0, rect.w, rect.h}); + } + + void bringToTop() + { + attach(parent); // does a detach + attach + } + + virtual void checkEvents(); + + protected: + Window * parent; + std::list children; + rect_t rect; + coord_t innerWidth; + coord_t innerHeight; + coord_t scrollPositionX = 0; + coord_t scrollPositionY = 0; + uint8_t windowFlags; + LcdFlags scrollbarColor = SCROLLBOX_COLOR; + + static Window * focusWindow; + static std::list trash; + + std::function closeHandler; + std::function focusHandler; + + void attach(Window * window); + + void detach(); + + void addChild(Window * window) + { + children.push_back(window); + } + + void removeChild(Window * window) + { + children.remove(window); + invalidate(); + } + + virtual void invalidate(const rect_t & rect); +}; + +#endif // _WINDOW_H_ diff --git a/radio/src/gui/navigation/common.cpp b/radio/src/gui/navigation/common.cpp index ceea990d6..00add48a6 100644 --- a/radio/src/gui/navigation/common.cpp +++ b/radio/src/gui/navigation/common.cpp @@ -26,7 +26,7 @@ swsrc_t checkIncDecMovedSwitch(swsrc_t val) if (s_editMode>0) { swsrc_t swtch = getMovedSwitch(); if (swtch) { -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) div_t info = switchInfo(swtch); if (IS_CONFIG_TOGGLE(info.quot)) { if (info.rem != 0) { diff --git a/radio/src/io/bootloader_flash.cpp b/radio/src/io/bootloader_flash.cpp index 7259b2f6a..15e7aec99 100644 --- a/radio/src/io/bootloader_flash.cpp +++ b/radio/src/io/bootloader_flash.cpp @@ -60,7 +60,9 @@ void bootloaderFlash(const char * filename) for (int j=0; j<1024; j+=FLASH_PAGESIZE) { flashWrite(CONVERT_UINT_PTR(FIRMWARE_ADDRESS+i+j), (uint32_t *)(buffer+j)); } +#if !defined(COLORLCD) drawProgressScreen("Bootloader", STR_WRITING, i, BOOTLOADER_SIZE); +#endif #if defined(SIMU) // add an artificial delay and check for simu quit if (SIMU_SLEEP_OR_EXIT_MS(30)) diff --git a/radio/src/io/frsky_firmware_update.cpp b/radio/src/io/frsky_firmware_update.cpp index 8baeacb72..34a96eb59 100644 --- a/radio/src/io/frsky_firmware_update.cpp +++ b/radio/src/io/frsky_firmware_update.cpp @@ -288,7 +288,7 @@ const char * FrskyDeviceFirmwareUpdate::sendReqVersion() return "Version request failed"; } -const char * FrskyDeviceFirmwareUpdate::doFlashFirmware(const char * filename) +const char * FrskyDeviceFirmwareUpdate::doFlashFirmware(const char * filename, ProgressHandler progressHandler) { FIL file; const char * result; @@ -316,7 +316,7 @@ const char * FrskyDeviceFirmwareUpdate::doFlashFirmware(const char * filename) INTERNAL_MODULE_ON(); intmoduleSerialStart(38400, true); GPIO_SetBits(INTMODULE_BOOTCMD_GPIO, INTMODULE_BOOTCMD_GPIO_PIN); - result = uploadFileToHorusXJT(filename, &file); + result = uploadFileToHorusXJT(filename, &file, progressHandler); GPIO_ResetBits(INTMODULE_BOOTCMD_GPIO, INTMODULE_BOOTCMD_GPIO_PIN); f_close(&file); return result; @@ -342,13 +342,13 @@ const char * FrskyDeviceFirmwareUpdate::doFlashFirmware(const char * filename) else SPORT_UPDATE_POWER_ON(); - result = uploadFileNormal(filename, &file); + result = uploadFileNormal(filename, &file, progressHandler); f_close(&file); return result; } #if defined(PCBHORUS) -const char * FrskyDeviceFirmwareUpdate::uploadFileToHorusXJT(const char * filename, FIL * file) +const char * FrskyDeviceFirmwareUpdate::uploadFileToHorusXJT(const char * filename, FIL * file, ProgressHandler progressHandler) { uint32_t buffer[1024 / sizeof(uint32_t)]; UINT count; @@ -370,7 +370,7 @@ const char * FrskyDeviceFirmwareUpdate::uploadFileToHorusXJT(const char * filena uint8_t index = 0; while (true) { - drawProgressScreen(getBasename(filename), STR_WRITING, file->fptr, file->obj.objsize); + progressHandler(getBasename(filename), STR_WRITING, file->fptr, file->obj.objsize); if (f_read(file, buffer, 1024, &count) != FR_OK) { return "Error reading file"; @@ -406,7 +406,7 @@ const char * FrskyDeviceFirmwareUpdate::uploadFileToHorusXJT(const char * filena } #endif -const char * FrskyDeviceFirmwareUpdate::uploadFileNormal(const char * filename, FIL * file) +const char * FrskyDeviceFirmwareUpdate::uploadFileNormal(const char * filename, FIL * file, ProgressHandler progressHandler) { uint32_t buffer[1024 / sizeof(uint32_t)]; UINT count; @@ -444,7 +444,7 @@ const char * FrskyDeviceFirmwareUpdate::uploadFileNormal(const char * filename, state = SPORT_DATA_TRANSFER, sendFrame(); if (i == 0) { - drawProgressScreen(getBasename(filename), STR_WRITING, file->fptr, file->obj.objsize); + progressHandler(getBasename(filename), STR_WRITING, file->fptr, file->obj.objsize); } } @@ -468,7 +468,7 @@ const char * FrskyDeviceFirmwareUpdate::endTransfer() return nullptr; } -const char * FrskyDeviceFirmwareUpdate::flashFirmware(const char * filename) +const char * FrskyDeviceFirmwareUpdate::flashFirmware(const char * filename, ProgressHandler progressHandler) { pausePulses(); @@ -482,13 +482,13 @@ const char * FrskyDeviceFirmwareUpdate::flashFirmware(const char * filename) SPORT_UPDATE_POWER_OFF(); - drawProgressScreen(getBasename(filename), STR_DEVICE_RESET, 0, 0); + progressHandler(getBasename(filename), STR_DEVICE_RESET, 0, 0); /* wait 2s off */ watchdogSuspend(2000); RTOS_WAIT_MS(2000); - const char * result = doFlashFirmware(filename); + const char * result = doFlashFirmware(filename, progressHandler); AUDIO_PLAY(AU_SPECIAL_SOUND_BEEP1 ); BACKLIGHT_ENABLE(); @@ -677,7 +677,7 @@ const char * FrskyChipFirmwareUpdate::sendUpgradeData(uint32_t index, uint8_t * return status == 0x00 ? nullptr : "Upgrade failed"; } -const char * FrskyChipFirmwareUpdate::doFlashFirmware(const char * filename) +const char * FrskyChipFirmwareUpdate::doFlashFirmware(const char * filename, ProgressHandler progressHandler) { const char * result; FIL file; @@ -699,7 +699,7 @@ const char * FrskyChipFirmwareUpdate::doFlashFirmware(const char * filename) } uint32_t packetsCount = (information->size + sizeof(buffer) - 1) / sizeof(buffer); - drawProgressScreen(getBasename(filename), STR_FLASH_WRITE, 0, packetsCount); + progressHandler(getBasename(filename), STR_FLASH_WRITE, 0, packetsCount); result = sendUpgradeCommand('A', packetsCount); if (result) @@ -707,7 +707,7 @@ const char * FrskyChipFirmwareUpdate::doFlashFirmware(const char * filename) uint32_t index = 0; while (1) { - drawProgressScreen(getBasename(filename), STR_FLASH_WRITE, index, packetsCount); + progressHandler(getBasename(filename), STR_FLASH_WRITE, index, packetsCount); if (f_read(&file, buffer, sizeof(buffer), &count) != FR_OK) { f_close(&file); return "Error reading file"; @@ -724,9 +724,9 @@ const char * FrskyChipFirmwareUpdate::doFlashFirmware(const char * filename) return sendUpgradeCommand('E', packetsCount); } -const char * FrskyChipFirmwareUpdate::flashFirmware(const char * filename, bool wait) +const char * FrskyChipFirmwareUpdate::flashFirmware(const char * filename, ProgressHandler progressHandler, bool wait) { - drawProgressScreen(getBasename(filename), STR_DEVICE_RESET, 0, 0); + progressHandler(getBasename(filename), STR_DEVICE_RESET, 0, 0); pausePulses(); @@ -748,7 +748,7 @@ const char * FrskyChipFirmwareUpdate::flashFirmware(const char * filename, bool telemetryInit(PROTOCOL_TELEMETRY_FRSKY_SPORT); - const char * result = doFlashFirmware(filename); + const char * result = doFlashFirmware(filename, progressHandler); AUDIO_PLAY(AU_SPECIAL_SOUND_BEEP1); BACKLIGHT_ENABLE(); diff --git a/radio/src/io/frsky_firmware_update.h b/radio/src/io/frsky_firmware_update.h index 9d3dcc820..872c948bc 100644 --- a/radio/src/io/frsky_firmware_update.h +++ b/radio/src/io/frsky_firmware_update.h @@ -25,6 +25,7 @@ #include "definitions.h" #include "frsky_pxx2.h" #include "ff.h" +#include "popups.h" enum FrskyFirmwareProductFamily { FIRMWARE_FAMILY_INTERNAL_MODULE, @@ -73,7 +74,7 @@ class FrskyDeviceFirmwareUpdate { module(module) { } - const char * flashFirmware(const char * filename); + const char * flashFirmware(const char * filename, ProgressHandler progressHandler); protected: uint8_t state = SPORT_IDLE; @@ -91,11 +92,11 @@ class FrskyDeviceFirmwareUpdate { bool waitState(State state, uint32_t timeout); void processFrame(const uint8_t * frame); - const char * doFlashFirmware(const char * filename); + const char * doFlashFirmware(const char * filename, ProgressHandler progressHandler); const char * sendPowerOn(); const char * sendReqVersion(); - const char * uploadFileNormal(const char * filename, FIL * file); - const char * uploadFileToHorusXJT(const char * filename, FIL * file); + const char * uploadFileNormal(const char * filename, FIL * file, ProgressHandler progressHandler); + const char * uploadFileToHorusXJT(const char * filename, FIL * file, ProgressHandler progressHandler); const char * endTransfer(); }; @@ -105,7 +106,7 @@ class FrskyChipFirmwareUpdate { { } - const char * flashFirmware(const char * filename, bool wait = true); + const char * flashFirmware(const char * filename, ProgressHandler progressHandler, bool wait = true); protected: uint8_t crc; @@ -116,7 +117,7 @@ class FrskyChipFirmwareUpdate { const char * sendUpgradeCommand(char command, uint32_t packetsCount); const char * sendUpgradeData(uint32_t index, uint8_t * data); - const char * doFlashFirmware(const char * filename); + const char * doFlashFirmware(const char * filename, ProgressHandler progressHandler); }; #endif // _FRSKY_FIRMWARE_UPDATE_H_ diff --git a/radio/src/keys.h b/radio/src/keys.h index e60e9dfe6..f2492570a 100644 --- a/radio/src/keys.h +++ b/radio/src/keys.h @@ -23,6 +23,7 @@ #include #include "board.h" +#include "opentx_types.h" #define EVT_KEY_MASK(e) ((e) & 0x1f) @@ -62,7 +63,7 @@ #define EVT_ROTARY_RIGHT 0xDE00 #define IS_NEXT_EVENT(event) (event==EVT_KEY_FIRST(KEY_DOWN) || event==EVT_KEY_REPT(KEY_DOWN)) #define IS_PREVIOUS_EVENT(event) (event==EVT_KEY_FIRST(KEY_UP) || event==EVT_KEY_REPT(KEY_UP)) -#elif (defined(PCBHORUS) || defined(PCBTARANIS)) && defined(ROTARY_ENCODER_NAVIGATION) +#elif defined(PCBFRSKY) && defined(ROTARY_ENCODER_NAVIGATION) #define EVT_ROTARY_BREAK EVT_KEY_BREAK(KEY_ENTER) #define EVT_ROTARY_LONG EVT_KEY_LONG(KEY_ENTER) #define EVT_ROTARY_LEFT 0xDF00 diff --git a/radio/src/logs.cpp b/radio/src/logs.cpp index 2ada6d7fd..b9776f69f 100644 --- a/radio/src/logs.cpp +++ b/radio/src/logs.cpp @@ -27,7 +27,7 @@ uint8_t logDelay; void writeHeader(); -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) || defined(PCBNV14) int getSwitchState(uint8_t swtch) { int value = getValue(MIXSRC_FIRST_SWITCH + swtch); return (value == 0) ? 0 : (value < 0) ? -1 : +1; @@ -155,7 +155,7 @@ void writeHeader() } } -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) || defined(PCBNV14) for (uint8_t i=1; i maxLuaDuration) { maxLuaDuration = t0; } -#else +#elif !defined(LIBOPENUI) lcdRefreshWait(); // WARNING: make sure no code above this line does any change to the LCD display buffer! #endif +#if defined(LIBOPENUI) + mainWindow.run(); +#else if (!refreshNeeded) { DEBUG_TIMER_START(debugTimerMenus); while (1) { @@ -360,6 +370,7 @@ void guiMain(event_t evt) lcdRefresh(); DEBUG_TIMER_STOP(debugTimerLcdRefresh); } +#endif } #elif defined(GUI) @@ -487,7 +498,9 @@ void perMain() doLoopCommonActions(); +#if !defined(LIBOPENUI) event_t evt = getEvent(false); +#endif #if defined(RAMBACKUP) if (unexpectedShutdown) { @@ -515,17 +528,25 @@ void perMain() #if defined(STM32) if (usbPlugged() && getSelectedUsbMode() == USB_MASS_STORAGE_MODE) { +#if defined(LIBOPENUI) + #warning "TODO USB plugged view" +#else // disable access to menus lcdClear(); menuMainView(0); lcdRefresh(); +#endif return; } #endif #if defined(GUI) DEBUG_TIMER_START(debugTimerGuiMain); +#if defined(LIBOPENUI) + guiMain(0); +#else guiMain(evt); +#endif DEBUG_TIMER_STOP(debugTimerGuiMain); #endif diff --git a/radio/src/mixer.cpp b/radio/src/mixer.cpp index 8905ca00d..59d1ee890 100644 --- a/radio/src/mixer.cpp +++ b/radio/src/mixer.cpp @@ -340,7 +340,8 @@ getvalue_t getValue(mixsrc_t i) return calc1000toRESX((int16_t)8 * getTrimValue(mixerCurrentFlightMode, i-MIXSRC_FIRST_TRIM)); } -#if defined(PCBTARANIS) || defined(PCBHORUS) + // TODO : find a better define +#if defined(PCBFRSKY) || defined(PCBFLYSKY) else if ((i >= MIXSRC_FIRST_SWITCH) && (i <= MIXSRC_LAST_SWITCH)) { mixsrc_t sw = i-MIXSRC_FIRST_SWITCH; if (SWITCH_EXISTS(sw)) { diff --git a/radio/src/module.h b/radio/src/module.h new file mode 100644 index 000000000..ff7077fde --- /dev/null +++ b/radio/src/module.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _MODULE_H_ +#define _MODULE_H_ + +#include + +void resetModuleSettings(uint8_t module); +void pxx2AddReceiver(uint8_t moduleIndex, uint8_t receiverIndex); +void pxx2DeleteReceiver(uint8_t moduleIndex, uint8_t receiverIndex); + +#endif diff --git a/radio/src/myeeprom.h b/radio/src/myeeprom.h index f1ec2e322..af040458b 100644 --- a/radio/src/myeeprom.h +++ b/radio/src/myeeprom.h @@ -78,7 +78,7 @@ #define MODEL_GVAR_MIN(idx) (CFN_GVAR_CST_MIN + g_model.gvars[idx].min) #define MODEL_GVAR_MAX(idx) (CFN_GVAR_CST_MAX - g_model.gvars[idx].max) -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) || defined(PCBNV14) #define SWITCH_CONFIG(x) (bfGet(g_eeGeneral.switchConfig, 2*(x), 2)) #define SWITCH_EXISTS(x) (SWITCH_CONFIG(x) != SWITCH_NONE) #define IS_CONFIG_3POS(x) (SWITCH_CONFIG(x) == SWITCH_3POS) @@ -93,7 +93,7 @@ #define ALTERNATE_VIEW 0x10 -#if defined(PCBHORUS) +#if defined(COLORLCD) #include "layout.h" #include "theme.h" #include "topbar.h" diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index dff7480a5..102ee16df 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -21,6 +21,12 @@ #include #include "opentx.h" +#if defined(LIBOPENUI) +// #include "shutdown_animation.h" +// #include "radio_calibration.h" +#include "view_main.h" +#endif + RadioData g_eeGeneral; ModelData g_model; @@ -119,7 +125,11 @@ void per10ms() #if defined(GUI) if (lightOffCounter) lightOffCounter--; if (flashCounter) flashCounter--; +#if defined(LIBOPENUI) + #warning "TODO remove noHighlightCounter on LIBOPENUI" +#else if (noHighlightCounter) noHighlightCounter--; +#endif #endif if (trimsCheckTimer) trimsCheckTimer--; @@ -530,17 +540,18 @@ void modelDefault(uint8_t id) g_model.header.name[6] = '\033' + id%10; #endif -#if defined(PCBHORUS) +#if defined(COLORLCD) + #warning "Initialization missing" extern const LayoutFactory * defaultLayout; - delete customScreens[0]; - customScreens[0] = defaultLayout->create(&g_model.screenData[0].layoutData); - strcpy(g_model.screenData[0].layoutName, "Layout2P1"); - extern const WidgetFactory * defaultWidget; - customScreens[0]->createWidget(0, defaultWidget); - // enable switch warnings - for (int i=0; icreate(&g_model.screenData[0].layoutData); +// strcpy(g_model.screenData[0].layoutName, "Layout2P1"); +// extern const WidgetFactory * defaultWidget; +// customScreens[0]->createWidget(0, defaultWidget); +// // enable switch warnings +// for (int i=0; i THRCHK_DEADBAND - 1024; } +#if defined(COLORLCD) +void checkThrottleStick() +{ + if (isThrottleWarningAlertNeeded()) { + AUDIO_ERROR_MESSAGE(AU_THROTTLE_ALERT); + auto dialog = new FullScreenDialog(WARNING_TYPE_ALERT, STR_THROTTLEWARN, STR_THROTTLENOTIDLE); + dialog->setCloseCondition([]() { + return !isThrottleWarningAlertNeeded(); + }); + dialog->runForever(); + } +} +#else void checkThrottleStick() { if (!isThrottleWarningAlertNeeded()) { @@ -1009,6 +1038,7 @@ void checkThrottleStick() LED_ERROR_END(); } +#endif void checkAlarm() // added by Gohst { @@ -1036,7 +1066,7 @@ void alert(const char * title, const char * msg , uint8_t sound) while (1) { RTOS_WAIT_MS(10); - if (keyDown()) // wait for key release + if (getEvent()) // wait for key release break; doLoopCommonActions(); @@ -1221,7 +1251,16 @@ void getADC() DEBUG_TIMER_STOP(debugTimerAdcRead); for (uint8_t x=0; x> (1 - ANALOG_SCALE); + uint16_t v; + +#if defined(FLYSKY_HALL_STICKS) + if (x < 4) + v = get_hall_adc_value(x) >> (1 - ANALOG_SCALE); + else + v = getAnalogValue(x) >> (1 - ANALOG_SCALE); +#else + v = getAnalogValue(x) >> (1 - ANALOG_SCALE); +#endif // Jitter filter: // * pass trough any big change directly @@ -1556,7 +1595,12 @@ void opentxStart(const uint8_t startOptions = OPENTX_START_DEFAULT_ARGS) #if defined(GUI) if (calibration_needed) { +#if defined(LIBOPENUI) + #warning "TODO add a startCalibration function" + // startCalibration(); +#else chainMenu(menuFirstCalib); +#endif } else if (!(startOptions & OPENTX_START_NO_CHECKS)) { checkAlarm(); @@ -1624,14 +1668,16 @@ void opentxResume() { TRACE("opentxResume"); +#if !defined(LIBOPENUI) menuHandlers[0] = menuMainView; +#endif sdMount(); storageReadAll(); #if defined(COLORLCD) + #warning "TODO call loadTheme (not sure, it has been removed in some earlier commit)" loadTheme(); - loadFontCache(); #endif // removed to avoid the double warnings (throttle, switch, etc.) @@ -1809,7 +1855,10 @@ void opentxInit() { TRACE("opentxInit"); -#if defined(GUI) +#if defined(LIBOPENUI) + new ViewMain(); +#elif defined(GUI) + // TODO add a function for this (duplicated) menuHandlers[0] = menuMainView; #if MENUS_LOCK != 2/*no menus*/ menuHandlers[1] = menuModelSelect; @@ -1886,7 +1935,9 @@ void opentxInit() // g_model.topbarData is still zero here (because it was not yet read from SDCARD), // but we only remember the pointer to in in constructor. // The storageReadAll() needs topbar object, so it must be created here - topbar = new Topbar(&g_model.topbarData); + + // TODO topbar removed from new UI for now + // topbar = new Topbar(&g_model.topbarData); // lua widget state must also be prepared before the call to storageReadAll() LUA_INIT_THEMES_AND_WIDGETS(); @@ -1944,7 +1995,6 @@ void opentxInit() #if defined(COLORLCD) loadTheme(); - loadFontCache(); #endif if (g_eeGeneral.backlightMode != e_backlight_mode_off) { @@ -2085,9 +2135,10 @@ uint32_t pwrCheck() #else while ((TELEMETRY_STREAMING() && !g_eeGeneral.disableRssiPoweroffAlarm)) { #endif + +#if !defined(COLORLCD) lcdRefreshWait(); lcdClear(); - POPUP_CONFIRMATION(STR_MODEL_SHUTDOWN, nullptr); SET_WARNING_INFO(STR_MODEL_STILL_POWERED, sizeof(TR_MODEL_STILL_POWERED), 0); event_t evt = getEvent(false); @@ -2103,6 +2154,7 @@ uint32_t pwrCheck() pwr_check_state = PWR_CHECK_PAUSED; return e_power_on; } +#endif } haptic.play(15, 3, PLAY_NOW); pwr_check_state = PWR_CHECK_OFF; @@ -2115,6 +2167,10 @@ uint32_t pwrCheck() } } else { +#if defined(COLORLCD) + if (pwr_press_time != 0) + mainWindow.invalidate(); +#endif pwr_check_state = PWR_CHECK_ON; pwr_press_time = 0; } diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 984cf05f8..5f01f8efc 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -29,7 +29,13 @@ #include "definitions.h" #include "opentx_types.h" #include "debounce.h" +#include "opentx_helpers.h" +#include "touch.h" +#include "bitfield.h" +#if defined(LIBOPENUI) +#include "libopenui.h" +#endif #if defined(SIMU) #include "targets/simu/simpgmspace.h" @@ -239,7 +245,7 @@ #include "debug.h" -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) #define SWSRC_THR SWSRC_SB2 #define SWSRC_GEA SWSRC_SG2 #define SWSRC_ID0 SWSRC_SA0 @@ -363,6 +369,7 @@ extern const uint8_t modn12x3[]; #endif extern uint8_t channelOrder(uint8_t x); +extern uint8_t channelOrder(uint8_t setup, uint8_t x); #define THRCHK_DEADBAND 16 @@ -440,7 +447,7 @@ bool cmpStrWithZchar(const char * charString, const char * zcharString, int size #include "keys.h" #include "pwr.h" -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) || defined(PCBNV14) div_t switchInfo(int switchPosition); extern uint8_t potsPos[NUM_XPOTS]; #endif @@ -455,6 +462,16 @@ void alert(const char * title, const char * msg, uint8_t sound); #if !defined(GUI) #define RAISE_ALERT(...) #define ALERT(...) +#elif defined(COLORLCD) + void raiseAlert(const char * title, const char * msg, const char * info, uint8_t sound); + inline void RAISE_ALERT(const char * title, const char * msg, const char * info, uint8_t sound) + { + raiseAlert(title, msg, info, sound); + } + inline void ALERT(const char * title, const char * msg, uint8_t sound) + { + raiseAlert(title, msg, nullptr, sound); + } #else inline void RAISE_ALERT(const char * title, const char * msg, const char * info, uint8_t sound) { @@ -510,7 +527,7 @@ void evalLogicalSwitches(bool isCurrentFlightmode=true); void logicalSwitchesCopyState(uint8_t src, uint8_t dst); #define LS_RECURSIVE_EVALUATION_RESET() -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) || defined(PCBFLYSKY) void getSwitchesPosition(bool startup); #else #define getSwitchesPosition(...) @@ -636,8 +653,6 @@ void backlightOn(); void checkBacklight(); void doLoopCommonActions(); -#define BITMASK(bit) (1<<(bit)) - uint16_t isqrt32(uint32_t n); #if defined(BOOT) @@ -678,6 +693,9 @@ inline int divRoundClosest(const int n, const int d) #define calc100to256_16Bits(x) calc100to256(x) #define calc100toRESX_16Bits(x) calc100toRESX(x) +#define calc100to256_16Bits(x) calc100to256(x) +#define calc100toRESX_16Bits(x) calc100toRESX(x) + inline int calc100to256(int x) { return divRoundClosest(x*256, 100); @@ -703,7 +721,6 @@ inline int calcRESXto100(int x) return divRoundClosest(x*100, RESX); } - #if defined(COLORLCD) extern const char vers_stamp[]; extern const char date_stamp[]; @@ -801,12 +818,9 @@ enum BaseCurves { CURVE_BASE }; int8_t * curveAddress(uint8_t idx); -struct point_t -{ - coord_t x; - coord_t y; -}; + point_t getPoint(uint8_t i); +point_t getPoint(uint8_t curveIndex, uint8_t index); typedef CurveData CurveInfo; void loadCurves(); #define LOAD_MODEL_CURVES() loadCurves() @@ -975,7 +989,7 @@ enum AUDIO_SOUNDS { AU_STICK2_MIDDLE, AU_STICK3_MIDDLE, AU_STICK4_MIDDLE, -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) AU_POT1_MIDDLE, AU_POT2_MIDDLE, #if defined(PCBX9E) @@ -1133,7 +1147,7 @@ union ReusableBuffer int16_t loVals[NUM_STICKS+NUM_POTS+NUM_SLIDERS+STORAGE_NUM_MOUSE_ANALOGS]; int16_t hiVals[NUM_STICKS+NUM_POTS+NUM_SLIDERS+STORAGE_NUM_MOUSE_ANALOGS]; uint8_t state; -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) struct { uint8_t stepsCount; int16_t steps[XPOTS_MULTIPOS_COUNT]; @@ -1155,6 +1169,13 @@ union ReusableBuffer } sdManager; #endif +#if defined(STM32) + struct + { + char id[27]; + } version; +#endif + struct { ModuleInformation modules[NUM_MODULES]; uint32_t updateTime; @@ -1165,6 +1186,7 @@ union ReusableBuffer struct { ModuleInformation modules[NUM_MODULES]; uint8_t linesCount; + char msg[64]; } radioTools; struct { @@ -1378,4 +1400,6 @@ inline bool isAsteriskDisplayed() return unexpectedShutdown; } +#include "module.h" + #endif // _OPENTX_H_ diff --git a/radio/src/opentx_helpers.h b/radio/src/opentx_helpers.h index 8ddeba85e..534dcaf3d 100644 --- a/radio/src/opentx_helpers.h +++ b/radio/src/opentx_helpers.h @@ -21,10 +21,20 @@ #ifndef _OPENTX_HELPERS_H_ #define _OPENTX_HELPERS_H_ -template inline t min(t a, t b) { return a inline t max(t a, t b) { return a>b?a:b; } -template inline t sgn(t a) { return a>0 ? 1 : (a < 0 ? -1 : 0); } -template inline t limit(t mi, t x, t ma) { return min(max(mi,x),ma); } -template inline void SWAP(t & a, t & b) { t tmp = b; b = a; a = tmp; } +#include "libopenui/libopenui_helpers.h" -#endif // _OPENTX_HELPERS_H_ +template +inline T sgn(T a) +{ + return a > 0 ? 1 : (a < 0 ? -1 : 0); +} + +template +inline void SWAP(T & a, T & b) +{ + T tmp = b; + b = a; + a = tmp; +} + +#endif diff --git a/radio/src/pulses/flysky.cpp b/radio/src/pulses/flysky.cpp new file mode 100644 index 000000000..14d1ce6bb --- /dev/null +++ b/radio/src/pulses/flysky.cpp @@ -0,0 +1,1146 @@ +/* + * Copyright (C) OpenTX + * + * Dedicate for FlySky NV14 board. + * + * Based on code named + * 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" + +#define END 0xC0 +#define ESC 0xDB +#define ESC_END 0xDC +#define ESC_ESC 0xDD + +#define FRAME_TYPE_REQUEST_ACK 0x01 +#define FRAME_TYPE_REQUEST_NACK 0x02 +#define FRAME_TYPE_ANSWER 0x10 + +enum FlySkyModuleCommandID { + COMMAND_ID_NONE, + COMMAND_ID_RF_INIT, + COMMAND_ID_BIND, + COMMAND_ID_SET_RECEIVER_ID, + COMMAND_ID_RF_GET_CONFIG, + COMMAND_ID_SEND_CHANNEL_DATA, + COMMAND_ID_RX_SENSOR_DATA, + COMMAND_ID_SET_RX_PWM_PPM, + COMMAND_ID_SET_RX_SERVO_FREQ, + COMMAND_ID_GET_FIRMWARE_REV, + COMMAND_ID_SET_RX_IBUS_SBUS, + COMMAND_ID_SET_RX_IBUS_SERVO_EXT, + COMMAND_ID0C_UPDATE_RF_FIRMWARE = 0x0C, + COMMAND_ID0D_SET_TX_POWER = 0x0D, + COMMAND_ID_SET_RF_PROTOCOL, + COMMAND_ID_TEST_RANGE, + COMMAND_ID_TEST_RF_RESERVED, + COMMAND_ID20_UPDATE_RX_FIRMWARE = 0x20, + + COMMAND_ID_LAST +}; +#define IS_VALID_COMMAND_ID(id) ((id) < COMMAND_ID_LAST) + +#ifndef custom_log +#define custom_log +#endif +enum DEBUG_RF_FRAME_PRINT_E { + FRAME_PRINT_OFF,// 0:OFF, 1:RF frame only, 2:TX frame only, 3:Both RF and TX frame + RF_FRAME_ONLY, + TX_FRAME_ONLY, + BOTH_FRAME_PRINT +}; +#define DEBUG_RF_FRAME_PRINT FRAME_PRINT_OFF +#define FLYSKY_MODULE_TIMEOUT 155 /* ms */ +#define NUM_OF_NV14_CHANNELS (14) +#define VALID_CH_DATA(v) ((v) > 900 && (v) < 2100) + +#define gRomData g_model.moduleData[INTERNAL_MODULE].flysky +#define SET_DIRTY() storageDirty(EE_MODEL) + +enum FlySkySensorType_E { + FLYSKY_SENSOR_RX_VOLTAGE, + FLYSKY_SENSOR_RX_SIGNAL, + FLYSKY_SENSOR_RX_RSSI, + FLYSKY_SENSOR_RX_NOISE, + FLYSKY_SENSOR_RX_SNR, + FLYSKY_SENSOR_TEMP, + FLYSKY_SENSOR_EXT_VOLTAGE, + FLYSKY_SENSOR_MOTO_RPM, + FLYSKY_SENSOR_PRESURRE, + FLYSKY_SENSOR_GPS +}; + +enum FlySkyModuleState_E { + FLYSKY_MODULE_STATE_SET_TX_POWER, + FLYSKY_MODULE_STATE_INIT, + FLYSKY_MODULE_STATE_BIND, + FLYSKY_MODULE_STATE_SET_RECEIVER_ID, + FLYSKY_MODULE_STATE_SET_RX_PWM_PPM, + FLYSKY_MODULE_STATE_SET_RX_IBUS_SBUS, + FLYSKY_MODULE_STATE_SET_RX_FREQUENCY, + FLYSKY_MODULE_STATE_UPDATE_RF_FIRMWARE, + FLYSKY_MODULE_STATE_UPDATE_RX_FIRMWARE, + FLYSKY_MODULE_STATE_UPDATE_HALL_FIRMWARE, + FLYSKY_MODULE_STATE_UPDATE_RF_PROTOCOL, + FLYSKY_MODULE_STATE_GET_RECEIVER_CONFIG, + FLYSKY_MODULE_STATE_GET_RX_FIRMWARE_INFO, + FLYSKY_MODULE_STATE_GET_RX_FW_VERSION, + FLYSKY_MODULE_STATE_GET_RF_FIRMWARE_INFO, + FLYSKY_MODULE_STATE_GET_RF_FW_VERSION, + FLYSKY_MODULE_STATE_IDLE, + FLYSKY_MODULE_STATE_DEFAULT, +}; + +enum FlySkyBindState_E { + BIND_LOW_POWER, + BIND_NORMAL_POWER, + BIND_EXIT, +}; + +enum FlySkyRxPulse_E { + FLYSKY_PWM, + FLYSKY_PPM +}; + +enum FlySkyRxPort_E { + FLYSKY_IBUS, + FLYSKY_SBUS +}; + +enum FlySkyFirmwareType_E { + FLYSKY_RX_FIRMWARE, + FLYSKY_RF_FIRMWARE +}; + +enum FlySkyChannelDataType_E { + FLYSKY_CHANNEL_DATA_NORMAL, + FLYSKY_CHANNEL_DATA_FAILSAFE +}; + +enum FlySkyPulseModeValue_E { + PWM_IBUS, PWM_SBUS, + PPM_IBUS, PPM_SBUS +}; +#define GET_FLYSKY_PWM_PPM (gRomData.mode < 2 ? FLYSKY_PWM: FLYSKY_PPM) +#define GET_FLYSKY_IBUS_SBUS (gRomData.mode & 1 ? FLYSKY_SBUS: FLYSKY_IBUS) + +typedef struct RX_FLYSKY_IBUS_S { + uint8_t id[2]; + uint8_t channel[2]; +} rx_ibus_t; + +typedef struct FLYSKY_GPS_INFO_S { + uint8_t position_fix; + uint8_t satell_cnt; + uint8_t latitude[4]; + uint8_t longtitude[4]; + uint8_t altitude[4]; + uint8_t g_speed[2]; + uint8_t direction[2]; +} gps_info_t; + +typedef struct FLYSKY_SENSOR_DATA_S { + uint8_t sensor_type; + uint8_t sensor_id; + uint8_t voltage[2]; + uint8_t signal; + uint8_t rssi[2]; + uint8_t noise[2]; + uint8_t snr[2]; + uint8_t temp[2]; + uint8_t ext_voltage[2]; + uint8_t moto_rpm[2]; + uint8_t pressure_value[2]; + gps_info_t gps_info; +} rx_sensor_t; + +typedef struct FLYSKY_FIRMWARE_INFO_S { + uint8_t fw_id[4]; + uint8_t fw_len[4]; + uint8_t hw_rev[4]; + uint8_t fw_rev[4]; + uint8_t fw_pkg_addr[4]; + uint8_t fw_pkg_len[4]; + uint8_t * pkg_data; +} fw_info_t; + +typedef struct RF_INFO_S { + uint8_t id[4]; + uint8_t bind_power; + uint8_t num_of_channel; + uint8_t channel_data_type; + uint8_t protocol; + uint8_t fw_state; // 0: normal, COMMAND_ID0C_UPDATE_RF_FIRMWARE or COMMAND_ID_UPDATE_FIRMWARE_END + fw_info_t fw_info; +} rf_info_t; + +typedef struct RX_INFO_S { + int16_t servo_value[NUM_OF_NV14_CHANNELS]; + rx_ibus_t ibus; + fw_info_t fw_info; +} rx_info_t; + +static rx_sensor_t rx_sensor_info; +static uint8_t tx_working_power = 90; + +static rf_info_t rf_info = { + .id = {8, 8, 8, 8}, + .bind_power = BIND_NORMAL_POWER, + .num_of_channel = NUM_OF_NV14_CHANNELS, // TODO + g_model.moduleData[port].channelsCount; + .channel_data_type= FLYSKY_CHANNEL_DATA_NORMAL, + .protocol = 0, + .fw_state = 0, + .fw_info = {0} +}; + +static rx_info_t rx_info = { + .servo_value = {1500, 1500, 1500, 1500}, + .ibus = {{0, 0}, + {0, 0}}, + .fw_info = {0} +}; + +/// APIs: ->SetXXXX ->OnXXXX ->GetXXXX +/// For Rx Binding: void onFlySkyBindReceiver(uint8_t port); + +uint16_t getFlySkyReceiverSensorVoltage(uint8_t port, uint16_t * voltage) +{ + uint8_t * flysky_sensor = (uint8_t *) voltage; + uint8_t * ptr = rx_sensor_info.voltage; + + if (flysky_sensor != NULL) { + flysky_sensor[0] = ptr[0]; + flysky_sensor[1] = ptr[1]; + } + + return ptr[1] * 256 + ptr[0]; +} + +uint8_t getFlySkyReceiverSensorSignal(uint8_t port, uint8_t * signal) +{ + uint8_t * flysky_sensor = (uint8_t *) signal; + + if (flysky_sensor != NULL) { + flysky_sensor[0] = rx_sensor_info.signal; + } + + return rx_sensor_info.signal; +} + +uint16_t getFlySkyReceiverSensorRSSI(uint8_t port, uint16_t * rssi) +{ + uint8_t * flysky_sensor = (uint8_t *) rssi; + uint8_t * ptr = rx_sensor_info.rssi; + + if (flysky_sensor != NULL) { + flysky_sensor[0] = ptr[0]; + flysky_sensor[1] = ptr[1]; + } + + return ptr[1] * 256 + ptr[0]; +} + +uint16_t getFlySkyReceiverSensorNoise(uint8_t port, uint16_t * noise) +{ + uint8_t * flysky_sensor = (uint8_t *) noise; + uint8_t * ptr = rx_sensor_info.noise; + + if (flysky_sensor != NULL) { + flysky_sensor[0] = ptr[0]; + flysky_sensor[1] = ptr[1]; + } + + return ptr[1] * 256 + ptr[0]; +} + +uint16_t getFlySkyReceiverSensorSNR(uint8_t port, uint16_t * snr) +{ + uint8_t * flysky_sensor = (uint8_t *) snr; + uint8_t * ptr = rx_sensor_info.snr; + + if (flysky_sensor != NULL) { + flysky_sensor[0] = ptr[0]; + flysky_sensor[1] = ptr[1]; + } + + return ptr[1] * 256 + ptr[0]; +} + +uint16_t getFlySkyReceiverSensorTemperature(uint8_t port, uint16_t * temp) +{ + uint8_t * flysky_sensor = (uint8_t *) temp; + uint8_t * ptr = rx_sensor_info.temp; + + if (flysky_sensor != NULL) { + flysky_sensor[0] = ptr[0]; + flysky_sensor[1] = ptr[1]; + } + + return ptr[1] * 256 + ptr[0]; +} + +uint16_t getFlySkyReceiverSensorExtVoltage(uint8_t port, uint16_t * voltage) +{ + uint8_t * flysky_sensor = (uint8_t *) voltage; + uint8_t * ptr = rx_sensor_info.ext_voltage; + + if (flysky_sensor != NULL) { + flysky_sensor[0] = ptr[0]; + flysky_sensor[1] = ptr[1]; + } + + return ptr[1] * 256 + ptr[0]; +} + + +void getFlySkyReceiverFirmwareRevision(uint8_t port, uint32_t * revision) +{ + uint8_t * fw_info = (uint8_t *) revision; + fw_info[0] = rx_info.fw_info.fw_rev[0]; + fw_info[1] = rx_info.fw_info.fw_rev[1]; + fw_info[2] = rx_info.fw_info.fw_rev[2]; + fw_info[3] = rx_info.fw_info.fw_rev[3]; +} + +void getFlySkyTransmitterFirmwareRevision(uint8_t port, uint32_t * revision) +{ + uint8_t * fw_info = (uint8_t *) revision; + fw_info[0] = rf_info.fw_info.fw_rev[0]; + fw_info[1] = rf_info.fw_info.fw_rev[1]; + fw_info[2] = rf_info.fw_info.fw_rev[2]; + fw_info[3] = rf_info.fw_info.fw_rev[3]; +} + +void getFlySkyFirmwareId(uint8_t port, bool is_receiver, uint32_t * firmware_id) +{ + uint8_t * fw_info = (uint8_t *) firmware_id; + if (is_receiver) { + fw_info[0] = rx_info.fw_info.fw_id[0]; + fw_info[1] = rx_info.fw_info.fw_id[1]; + fw_info[2] = rx_info.fw_info.fw_id[2]; + fw_info[3] = rx_info.fw_info.fw_id[3]; + } + else { + fw_info[0] = rf_info.fw_info.fw_id[0]; + fw_info[1] = rf_info.fw_info.fw_id[1]; + fw_info[2] = rf_info.fw_info.fw_id[2]; + fw_info[3] = rf_info.fw_info.fw_id[3]; + } +} + +void getFlySkyHardwareRevision(uint8_t port, bool is_receiver, uint32_t * revision) +{ + uint8_t * fw_info = (uint8_t *) revision; + if (is_receiver) { + fw_info[0] = rx_info.fw_info.hw_rev[0]; + fw_info[1] = rx_info.fw_info.hw_rev[1]; + fw_info[2] = rx_info.fw_info.hw_rev[2]; + fw_info[3] = rx_info.fw_info.hw_rev[3]; + } + else { + fw_info[0] = rf_info.fw_info.hw_rev[0]; + fw_info[1] = rf_info.fw_info.hw_rev[1]; + fw_info[2] = rf_info.fw_info.hw_rev[2]; + fw_info[3] = rf_info.fw_info.hw_rev[3]; + } +} + +void getFlySkyFirmwareRevision(uint8_t port, bool is_receiver, uint32_t * revision) +{ + if (is_receiver) getFlySkyReceiverFirmwareRevision(port, revision); + else getFlySkyTransmitterFirmwareRevision(port, revision); +} + + +void setFlySkyGetFirmwarePackageAddr(uint8_t port, bool is_receiver, uint32_t * package_address) +{ + uint8_t * fw_package = (uint8_t *) package_address; + if (is_receiver) { + fw_package[0] = rx_info.fw_info.fw_pkg_addr[0]; + fw_package[1] = rx_info.fw_info.fw_pkg_addr[1]; + fw_package[2] = rx_info.fw_info.fw_pkg_addr[2]; + fw_package[3] = rx_info.fw_info.fw_pkg_addr[3]; + } + else { + fw_package[0] = rf_info.fw_info.fw_pkg_addr[0]; + fw_package[1] = rf_info.fw_info.fw_pkg_addr[1]; + fw_package[2] = rf_info.fw_info.fw_pkg_addr[2]; + fw_package[3] = rf_info.fw_info.fw_pkg_addr[3]; + } +} + +void setFlySkyGetFirmwarePackageLen(uint8_t port, bool is_receiver, uint32_t * package_len) +{ + uint8_t * fw_package = (uint8_t *) package_len; + if (is_receiver) { + fw_package[0] = rx_info.fw_info.fw_pkg_len[0]; + fw_package[1] = rx_info.fw_info.fw_pkg_len[1]; + fw_package[2] = rx_info.fw_info.fw_pkg_len[2]; + fw_package[3] = rx_info.fw_info.fw_pkg_len[3]; + } + else { + fw_package[0] = rf_info.fw_info.fw_pkg_len[0]; + fw_package[1] = rf_info.fw_info.fw_pkg_len[1]; + fw_package[2] = rf_info.fw_info.fw_pkg_len[2]; + fw_package[3] = rf_info.fw_info.fw_pkg_len[3]; + } +} + +void setFlySkyGetFirmwarePackageBuffer(uint8_t port, bool is_receiver, uint8_t * buffer) +{ + if (is_receiver) { + rx_info.fw_info.pkg_data = buffer; + } + else { + rf_info.fw_info.pkg_data = buffer; + } +} + + +void setFlySkyTransmitterId(uint8_t port, uint32_t rf_id) +{ + rf_info.id[0] = rf_id & 0xff; + rf_info.id[1] = rf_id & 0xff00 >> 8; + rf_info.id[2] = rf_id & 0xff0000 >> 16; + rf_info.id[3] = rf_id & 0xff000000 >> 24; +} + +void setFlySkyTransmitterProtocol(uint8_t port, uint8_t protocol) +{ + rf_info.protocol = protocol; +} + +void setFlySkyReceiverBindPowerLow(uint8_t port) +{ + rf_info.bind_power = BIND_LOW_POWER; +} + +void setFlySkyReceiverBindPowerNormal(uint8_t port) +{ + rf_info.bind_power = BIND_NORMAL_POWER; +} + +void setFlySkyReceiverChannelCounts(uint8_t port, uint8_t channle_counts) +{ + rf_info.num_of_channel = channle_counts; +} + +void setFlySkyChannelDataFailSafe(uint8_t port) +{ + rf_info.channel_data_type = FLYSKY_CHANNEL_DATA_FAILSAFE; +} + +void setFlySkyChannelDataNormalMode(uint8_t port) +{ + rf_info.channel_data_type = FLYSKY_CHANNEL_DATA_NORMAL; +} + +void setFlySkyChannelOutputs(int channel, int16_t outValue) +{ + channelOutputs[channel] = outValue; +} + +static uint32_t set_loop_cnt = 0; + +void setFlySkyChannelData(int channel, int16_t servoValue) +{ + if (channel < NUM_OF_NV14_CHANNELS && VALID_CH_DATA(servoValue)) { + rx_info.servo_value[channel] = (1000 * (servoValue + 1024) / 2048) + 1000; + } + + if ((DEBUG_RF_FRAME_PRINT & TX_FRAME_ONLY) && (set_loop_cnt++ % 1000 == 0)) { + TRACE_NOCRLF("HALL(%0d): ", FLYSKY_HALL_BAUDRATE); + for (int idx = 0; idx < NUM_OF_NV14_CHANNELS; idx++) { + TRACE_NOCRLF("CH%0d:%0d ", idx + 1, rx_info.servo_value[idx]); + } + TRACE(" "); + } +} + +void sendPulsesFrameByState(uint8_t port, uint8_t frameState) +{ + modulePulsesData[port].flysky.state = frameState; + modulePulsesData[port].flysky.state_index = FLYSKY_MODULE_TIMEOUT; + //setupPulsesFlySky(port); // effect immediately +} + + +bool isRxBindingState(uint8_t port) +{ + return moduleFlag[port] == MODULE_BIND; +} + +bool isFlySkyUsbDownload(void) +{ + return rf_info.fw_state != 0; +} + +void onIntmoduleSetPower(bool isPowerOn) +{ + if (DEBUG_RF_FRAME_PRINT & RF_FRAME_ONLY) TRACE("RF Power %s", isPowerOn != 0 ? "ON" : "OFF"); + + if (isPowerOn) { + INTERNAL_MODULE_ON(); + } + else { + INTERNAL_MODULE_OFF(); + } +} + +void onIntmoduleBindReceiver(uint8_t port) +{ + resetPulsesFlySky(port); + moduleFlag[port] = MODULE_BIND; + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_INIT; +} + +void onFlySkyReceiverPulseMode(uint8_t port) +{ + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_SET_RX_PWM_PPM; +} + +void onFlySkyReceiverPulsePort(uint8_t port) +{ + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_SET_RX_IBUS_SBUS; +} + +void onIntmoduleReceiverSetFrequency(uint8_t port) +{ + sendPulsesFrameByState(port, FLYSKY_MODULE_STATE_SET_RX_FREQUENCY); +} + +void onIntmoduleReceiverSetPulse(uint8_t port, uint8_t mode_and_port) // mode_and_port = 0,1,2,3 +{ + if ((DEBUG_RF_FRAME_PRINT & TX_FRAME_ONLY)) TRACE("PulseMode+Port: %0d", mode_and_port); + sendPulsesFrameByState(port, FLYSKY_MODULE_STATE_SET_RX_PWM_PPM); +} + +void onFlySkyTransmitterPower(uint8_t port, uint8_t dBmValue) +{ + tx_working_power = dBmValue; + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_SET_TX_POWER; +} + + +void onFlySkyUpdateReceiverFirmwareStart(uint8_t port) +{ + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_UPDATE_RX_FIRMWARE; +} + +void onIntmoduleUsbDownloadStart(uint8_t port) +{ + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_UPDATE_RF_FIRMWARE; +} + +void onFlySkyUsbDownloadStart(uint8_t fw_state) +{ + rf_info.fw_state = fw_state; +} + +void onFlySkyUpdateTransmitterProtocol(uint8_t port) +{ + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_UPDATE_RF_PROTOCOL; +} + + +void onFlySkyGetReceiverFirmwareInfo(uint8_t port) +{ + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_GET_RX_FIRMWARE_INFO; +} + +void onFlySkyGetTransmitterFirmwareInfo(uint8_t port) +{ + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_GET_RF_FIRMWARE_INFO; +} + +void onFlySkyGetReceiverFirmwareVersion(uint8_t port) +{ + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_GET_RX_FW_VERSION; +} + +void onFlySkyGetTransmitterFirmwareVersion(uint8_t port) +{ + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_GET_RF_FW_VERSION; +} + +void initFlySkyArray(uint8_t port) +{ + modulePulsesData[port].flysky.ptr = modulePulsesData[port].flysky.pulses; + modulePulsesData[port].flysky.crc = 0; +} + +inline void putFlySkyByte(uint8_t port, uint8_t byte) +{ + if (END == byte) { + *modulePulsesData[port].flysky.ptr++ = ESC; + *modulePulsesData[port].flysky.ptr++ = ESC_END; + } + else if (ESC == byte) { + *modulePulsesData[port].flysky.ptr++ = ESC; + *modulePulsesData[port].flysky.ptr++ = ESC_ESC; + } + else { + *modulePulsesData[port].flysky.ptr++ = byte; + } +} + +void putFlySkyFrameByte(uint8_t port, uint8_t byte) +{ + modulePulsesData[port].flysky.crc += byte; + putFlySkyByte(port, byte); +} + +void putFlySkyFrameHead(uint8_t port) +{ + *modulePulsesData[port].flysky.ptr++ = END; +} + +void putFlySkyFrameIndex(uint8_t port) +{ + putFlySkyFrameByte(port, modulePulsesData[port].flysky.frame_index); +} + +void putFlySkyFrameCrc(uint8_t port) +{ + putFlySkyByte(port, modulePulsesData[port].flysky.crc ^ 0xff); +} + +void putFlySkyFrameTail(uint8_t port) +{ + *modulePulsesData[port].flysky.ptr++ = END; +} + + +void putFlySkyRfInit(uint8_t port) +{ + putFlySkyFrameByte(port, FRAME_TYPE_REQUEST_ACK); + putFlySkyFrameByte(port, COMMAND_ID_RF_INIT); +} + +void putFlySkyBindReceiver(uint8_t port) +{ + putFlySkyFrameByte(port, FRAME_TYPE_REQUEST_ACK); + putFlySkyFrameByte(port, COMMAND_ID_BIND); + putFlySkyFrameByte(port, rf_info.bind_power); + putFlySkyFrameByte(port, rf_info.id[0]); + putFlySkyFrameByte(port, rf_info.id[1]); + putFlySkyFrameByte(port, rf_info.id[2]); + putFlySkyFrameByte(port, rf_info.id[3]); +} + +void putFlySkySetReceiverID(uint8_t port) +{ + putFlySkyFrameByte(port, FRAME_TYPE_REQUEST_ACK); + putFlySkyFrameByte(port, COMMAND_ID_SET_RECEIVER_ID); + putFlySkyFrameByte(port, gRomData.rx_id[0]); // receiver ID (byte 0) + putFlySkyFrameByte(port, gRomData.rx_id[1]); // receiver ID (byte 1) + putFlySkyFrameByte(port, gRomData.rx_id[2]); // receiver ID (byte 2) + putFlySkyFrameByte(port, gRomData.rx_id[3]); // receiver ID (byte 3) +} + +void putFlySkyGetReceiverConfig(uint8_t port) +{ + putFlySkyFrameByte(port, FRAME_TYPE_ANSWER); + putFlySkyFrameByte(port, COMMAND_ID_RF_GET_CONFIG); + putFlySkyFrameByte(port, GET_FLYSKY_PWM_PPM); // 00:PWM, 01:PPM + putFlySkyFrameByte(port, GET_FLYSKY_IBUS_SBUS);// 00:I-BUS, 01:S-BUS + putFlySkyFrameByte(port, gRomData.rx_freq[0] < 50 ? 50 : gRomData.rx_freq[0]); // receiver servo freq bit[7:0] + putFlySkyFrameByte(port, gRomData.rx_freq[1]); // receiver servo freq bit[15:8] +} + +void putFlySkySetReceiverPulseMode(uint8_t port) +{ + putFlySkyFrameByte(port, FRAME_TYPE_REQUEST_ACK); + putFlySkyFrameByte(port, COMMAND_ID_SET_RX_PWM_PPM); + putFlySkyFrameByte(port, GET_FLYSKY_PWM_PPM); // 00:PWM, 01:PPM +} + +void putFlySkySetReceiverPort(uint8_t port) +{ + putFlySkyFrameByte(port, FRAME_TYPE_REQUEST_ACK); + putFlySkyFrameByte(port, COMMAND_ID_SET_RX_IBUS_SBUS); + putFlySkyFrameByte(port, GET_FLYSKY_IBUS_SBUS); // 0x00:I-BUS, 0x01:S-BUS +} + +void putFlySkySetReceiverServoFreq(uint8_t port) +{ + putFlySkyFrameByte(port, FRAME_TYPE_REQUEST_ACK); + putFlySkyFrameByte(port, COMMAND_ID_SET_RX_SERVO_FREQ); + putFlySkyFrameByte(port, gRomData.rx_freq[0]); // receiver servo freq bit[7:0] + putFlySkyFrameByte(port, gRomData.rx_freq[1]); // receiver servo freq bit[15:8] +} + +void putFlySkyGetFirmwareInfo(uint8_t port, uint8_t fw_word) +{ +#ifdef RF_PROTOCOL_V13 + putFlySkyFrameByte(port, FRAME_TYPE_REQUEST_ACK); + putFlySkyFrameByte(port, COMMAND_ID_GET_FIRMWARE_INFO); + putFlySkyFrameByte(port, fw_word); // 0x00:RX firmware, 0x01:RF firmware +#endif +} + +void putFlySkySetPowerdBm(uint8_t port, uint8_t dBm) +{ + putFlySkyFrameByte(port, FRAME_TYPE_REQUEST_ACK); + putFlySkyFrameByte(port, COMMAND_ID0D_SET_TX_POWER); + putFlySkyFrameByte(port, dBm); // 0x00:RX firmware, 0x01:RF firmware +} + +void putFlySkyGetFirmwareVersion(uint8_t port, uint8_t fw_word) +{ + putFlySkyFrameByte(port, FRAME_TYPE_REQUEST_ACK); + putFlySkyFrameByte(port, COMMAND_ID_GET_FIRMWARE_REV); + putFlySkyFrameByte(port, fw_word); // 0x00:RX firmware, 0x01:RF firmware +} + +void putFlySkySendChannelData(uint8_t port) +{ + putFlySkyFrameByte(port, FRAME_TYPE_REQUEST_NACK); + putFlySkyFrameByte(port, COMMAND_ID_SEND_CHANNEL_DATA); + + if (0 /* TODO send failsafe*/) { + putFlySkyFrameByte(port, 0x01); + uint8_t channels_start = g_model.moduleData[port].channelsStart; + uint8_t channels_count = min(NUM_OF_NV14_CHANNELS, channels_start + 8 + g_model.moduleData[port].channelsCount); + putFlySkyFrameByte(port, channels_count); + for (uint8_t channel = channels_start; channel < channels_count; channel++) { + int16_t failsafeValue = g_model.moduleData[port].failsafeChannels[channel]; + uint16_t pulseValue = limit(900, 900 + ((2100 - 900) * (failsafeValue + 1024) / 2048), 2100); + putFlySkyFrameByte(port, pulseValue & 0xff); + putFlySkyFrameByte(port, pulseValue >> 8); + } + } + else { + putFlySkyFrameByte(port, 0x00); + uint8_t channels_start = g_model.moduleData[port].channelsStart; + uint8_t channels_count = min(NUM_OF_NV14_CHANNELS, channels_start + 8 + g_model.moduleData[port].channelsCount); + putFlySkyFrameByte(port, channels_count); + for (uint8_t channel = channels_start; channel < channels_count; channel++) { + int channelValue = channelOutputs[channel] + 2*PPM_CH_CENTER(channel) - 2*PPM_CENTER; + uint16_t pulseValue = limit(900, 900 + ((2100 - 900) * (channelValue + 1024) / 2048), 2100); + putFlySkyFrameByte(port, pulseValue & 0xff); + putFlySkyFrameByte(port, pulseValue >> 8); + } + } +} + +void putFlySkyUpdateFirmwareStart(uint8_t port, uint8_t fw_word) +{ + putFlySkyFrameByte(port, FRAME_TYPE_REQUEST_ACK); + if (fw_word == FLYSKY_RX_FIRMWARE) { + fw_word = COMMAND_ID20_UPDATE_RX_FIRMWARE; + } + else { + fw_word = COMMAND_ID0C_UPDATE_RF_FIRMWARE; + } + putFlySkyFrameByte(port, fw_word); // 0x00:RX firmware, 0x01:RF firmware +} + +void putFlySkyUpdateRfProtocol(uint8_t port) +{ + putFlySkyFrameByte(port, FRAME_TYPE_REQUEST_ACK); + putFlySkyFrameByte(port, COMMAND_ID_SET_RF_PROTOCOL); + putFlySkyFrameByte(port, rf_info.protocol); // 0x00:AFHDS1 0x01:AFHDS2 0x02:AFHDS2A +} + + +void incrFlySkyFrame(uint8_t port) +{ + if (++modulePulsesData[port].flysky.frame_index == 0) + modulePulsesData[port].flysky.frame_index = 1; +} + +bool checkFlySkyFrameCrc(const uint8_t * ptr, uint8_t size) +{ + uint8_t crc = 0; + + for (uint8_t i = 0; i < size; i++) { + crc += ptr[i]; + } + + if (DEBUG_RF_FRAME_PRINT & RF_FRAME_ONLY) { + if (ptr[2] != 0x06 || (set_loop_cnt++ % 100 == 0)) { + TRACE_NOCRLF("RF(%0d): C0", INTERNAL_MODULE_BAUDRATE); + for (int idx = 0; idx <= size; idx++) { + TRACE_NOCRLF(" %02X", ptr[idx]); + } + TRACE(" C0;"); + + if ((crc ^ 0xff) != ptr[size]) { + TRACE("ErrorCRC %02X especting %02X", crc ^ 0xFF, ptr[size]); + } + } + } + + return (crc ^ 0xff) == ptr[size]; +} + +void parseFlySkyFeedbackFrame(uint8_t port) +{ + const uint8_t * ptr = modulePulsesData[port].flysky.telemetry; + if (*ptr++ != END) + return; + + uint8_t frame_number = *ptr++; + uint8_t frame_type = *ptr++; + uint8_t command_id = *ptr++; + uint8_t first_para = *ptr++; + uint8_t * p_data = NULL; + + if (!checkFlySkyFrameCrc(modulePulsesData[port].flysky.telemetry + 1, modulePulsesData[port].flysky.telemetry_index - 2)) { + return; + } + + switch (command_id) { + default: + if (moduleFlag[port] == MODULE_NORMAL_MODE + && modulePulsesData[port].flysky.state >= FLYSKY_MODULE_STATE_IDLE) { + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_DEFAULT; + if (DEBUG_RF_FRAME_PRINT & RF_FRAME_ONLY) TRACE("State back to channel data"); + } + break; + + case COMMAND_ID_RF_INIT: { + if (first_para == 0x01) { // action only RF ready + + if (moduleFlag[port] == MODULE_BIND) { + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_BIND; + incrFlySkyFrame(port); + } + + /*else if (moduleFlag[port] == MODULE_RANGECHECK) { + + }*/ + + if (modulePulsesData[port].flysky.state == FLYSKY_MODULE_STATE_INIT) { + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_SET_RECEIVER_ID; + incrFlySkyFrame(port); + } + } + else modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_IDLE; + break; + } + + case COMMAND_ID_BIND: { + if (frame_type != FRAME_TYPE_ANSWER) { + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_IDLE; + return; + } + g_model.header.modelId[port] = ptr[2]; + gRomData.rx_id[0] = first_para; + gRomData.rx_id[1] = *ptr++; + gRomData.rx_id[2] = *ptr++; + gRomData.rx_id[3] = *ptr++; + if (DEBUG_RF_FRAME_PRINT & RF_FRAME_ONLY) + TRACE("New Rx ID: %02X %02X %02X %02X", gRomData.rx_id[0], gRomData.rx_id[1], gRomData.rx_id[2], gRomData.rx_id[3]); + SET_DIRTY(); + resetPulsesFlySky(port); + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_INIT; + break; + + case COMMAND_ID_RF_GET_CONFIG: + modulePulsesData[port].flysky.frame_index = frame_number; + sendPulsesFrameByState(port, FLYSKY_MODULE_STATE_GET_RECEIVER_CONFIG); + break; + } + + case COMMAND_ID_RX_SENSOR_DATA: { + // rx_sensor_info.sensor_id = *ptr++; // TBC: in protocol doc, but no such byte in sample data + if (first_para == FLYSKY_SENSOR_RX_VOLTAGE) { + rx_sensor_info.voltage[0] = *ptr++; + rx_sensor_info.voltage[1] = *ptr++; + } + + else if (first_para == FLYSKY_SENSOR_GPS) { + p_data = &rx_sensor_info.gps_info.position_fix; + for (int idx = 17; idx > 0; idx--) { + *p_data++ = *ptr++; + } + } + + else if (first_para == FLYSKY_SENSOR_RX_SIGNAL) { + rx_sensor_info.signal = *ptr++; + } + + else if (first_para > FLYSKY_SENSOR_RX_SIGNAL && first_para < FLYSKY_SENSOR_GPS) { + p_data = rx_sensor_info.rssi + (first_para - FLYSKY_SENSOR_RX_RSSI) * 2; + p_data[0] = *ptr++; + p_data[1] = *ptr++; + } + + if (moduleFlag[port] == MODULE_RANGECHECK) { + moduleFlag[port] = MODULE_NORMAL_MODE; + onFlySkyTransmitterPower(port, 0); // set power 0 + break; + } + + if (moduleFlag[port] == MODULE_NORMAL_MODE && modulePulsesData[port].flysky.state >= FLYSKY_MODULE_STATE_IDLE) { + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_DEFAULT; + } + break; + } + + case COMMAND_ID_SET_RECEIVER_ID: { + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_DEFAULT; + return; + } + + case COMMAND_ID0D_SET_TX_POWER: { + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_INIT; + break; + } + + case COMMAND_ID_SET_RX_PWM_PPM: { + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_SET_RX_IBUS_SBUS; + break; + } + + case COMMAND_ID_SET_RX_IBUS_SBUS: { + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_SET_RX_FREQUENCY; + break; + } + + case COMMAND_ID_SET_RX_SERVO_FREQ: { + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_DEFAULT; + break; + } + + case COMMAND_ID0C_UPDATE_RF_FIRMWARE: { + rf_info.fw_state = FLYSKY_MODULE_STATE_UPDATE_RF_FIRMWARE; + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_IDLE; + break; + } + } +} + +extern uint8_t intmoduleGetByte(uint8_t * byte); + +static STRUCT_HALL rfProtocolRx = {0}; +static uint32_t rfRxCount = 0; + +bool isRfProtocolRxMsgOK(void) +{ + bool isMsgOK = (0 != rfRxCount); + rfRxCount = 0; + return isMsgOK && isFlySkyUsbDownload(); +} + +#if !defined(SIMU) +void checkFlySkyFeedback(uint8_t port) +{ + uint8_t byte; + + while (intmoduleGetByte(&byte)) { + //if ( modulePulsesData[port].flysky.state == FLYSKY_MODULE_STATE_IDLE + // && rf_info.fw_state == FLYSKY_MODULE_STATE_UPDATE_RF_FIRMWARE ) + { + Parse_Character(&rfProtocolRx, byte ); + if ( rfProtocolRx.msg_OK ) + { + rfRxCount++; + rfProtocolRx.msg_OK = 0; + uint8_t *pt = (uint8_t*)&rfProtocolRx; + pt[rfProtocolRx.length + 3] = rfProtocolRx.checkSum & 0xFF; + pt[rfProtocolRx.length + 4] = rfProtocolRx.checkSum >> 8; + + if((DEBUG_RF_FRAME_PRINT & RF_FRAME_ONLY)) { + TRACE("RF: %02X %02X %02X ...%04X; CRC:%04X", pt[0], pt[1], pt[2], + rfProtocolRx.checkSum, calc_crc16(pt, rfProtocolRx.length+3)); + } + + if ( 0x01 == rfProtocolRx.length && + ( 0x05 == rfProtocolRx.data[0] || 0x06 == rfProtocolRx.data[0]) ) + { + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_INIT; + rf_info.fw_state = 0; + } + + usbDownloadTransmit(pt, rfProtocolRx.length + 5); + } + //continue; + } + + if (byte == END && modulePulsesData[port].flysky.telemetry_index > 0) { + parseFlySkyFeedbackFrame(port); + modulePulsesData[port].flysky.telemetry_index = 0; + } + else { + if (byte == ESC) { + modulePulsesData[port].flysky.esc_state = 1; + } + else { + if (modulePulsesData[port].flysky.esc_state) { + modulePulsesData[port].flysky.esc_state = 0; + if (byte == ESC_END) + byte = END; + else if (byte == ESC_ESC) + byte = ESC; + } + modulePulsesData[port].flysky.telemetry[modulePulsesData[port].flysky.telemetry_index++] = byte; + if (modulePulsesData[port].flysky.telemetry_index >= sizeof(modulePulsesData[port].flysky.telemetry)) { + // TODO buffer is full, log an error? + modulePulsesData[port].flysky.telemetry_index = 0; + } + } + } + } +} +#endif + +void resetPulsesFlySky(uint8_t port) +{ + modulePulsesData[port].flysky.frame_index = 1; + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_SET_TX_POWER; + modulePulsesData[port].flysky.state_index = 0; + modulePulsesData[port].flysky.esc_state = 0; + moduleFlag[port] = MODULE_NORMAL_MODE; + uint16_t rx_freq = g_model.moduleData[port].flysky.rx_freq[0]; + rx_freq += (g_model.moduleData[port].flysky.rx_freq[1] * 256); + if (50 > rx_freq || 400 < rx_freq) { + g_model.moduleData[port].flysky.rx_freq[0] = 50; + } +} + +void setupPulsesFlySky(uint8_t port) +{ +#if !defined(SIMU) + checkFlySkyFeedback(port); +#endif + + initFlySkyArray(port); + putFlySkyFrameHead(port); + putFlySkyFrameIndex(port); + + if (modulePulsesData[port].flysky.state < FLYSKY_MODULE_STATE_DEFAULT) { + + if (++modulePulsesData[port].flysky.state_index >= FLYSKY_MODULE_TIMEOUT / PXX_PERIOD_DURATION) { + + modulePulsesData[port].flysky.state_index = 0; + + switch (modulePulsesData[port].flysky.state) { + + case FLYSKY_MODULE_STATE_INIT: + putFlySkyRfInit(port); + break; + + case FLYSKY_MODULE_STATE_BIND: + putFlySkyBindReceiver(port); + break; + + case FLYSKY_MODULE_STATE_SET_RECEIVER_ID: + putFlySkySetReceiverID(port); + break; + + case FLYSKY_MODULE_STATE_GET_RECEIVER_CONFIG: + putFlySkyGetReceiverConfig(port); + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_INIT; + break; + + case FLYSKY_MODULE_STATE_SET_TX_POWER: + putFlySkySetPowerdBm(port, tx_working_power); + break; + + case FLYSKY_MODULE_STATE_SET_RX_PWM_PPM: + putFlySkySetReceiverPulseMode(port); + break; + + case FLYSKY_MODULE_STATE_SET_RX_IBUS_SBUS: + putFlySkySetReceiverPort(port); + break; + + case FLYSKY_MODULE_STATE_SET_RX_FREQUENCY: + putFlySkySetReceiverServoFreq(port); + break; + + case FLYSKY_MODULE_STATE_UPDATE_RF_PROTOCOL: + putFlySkyUpdateRfProtocol(port); + break; + + case FLYSKY_MODULE_STATE_UPDATE_RX_FIRMWARE: + putFlySkyUpdateFirmwareStart(port, FLYSKY_RX_FIRMWARE); + break; + + case FLYSKY_MODULE_STATE_UPDATE_RF_FIRMWARE: + putFlySkyUpdateFirmwareStart(port, FLYSKY_RF_FIRMWARE); + break; + + case FLYSKY_MODULE_STATE_GET_RX_FIRMWARE_INFO: + putFlySkyGetFirmwareInfo(port, FLYSKY_RX_FIRMWARE); + break; + + case FLYSKY_MODULE_STATE_GET_RF_FIRMWARE_INFO: + putFlySkyGetFirmwareInfo(port, FLYSKY_RF_FIRMWARE); + break; + + case FLYSKY_MODULE_STATE_GET_RX_FW_VERSION: + putFlySkyGetFirmwareVersion(port, FLYSKY_RX_FIRMWARE); + break; + + case FLYSKY_MODULE_STATE_GET_RF_FW_VERSION: + putFlySkyGetFirmwareVersion(port, FLYSKY_RF_FIRMWARE); + break; + + case FLYSKY_MODULE_STATE_IDLE: + modulePulsesData[port].pxx_uart.ptr = modulePulsesData[port].pxx_uart.pulses; + return; + + default: + modulePulsesData[port].flysky.state = FLYSKY_MODULE_STATE_INIT; + initFlySkyArray(port); + if ((DEBUG_RF_FRAME_PRINT & TX_FRAME_ONLY)) { + TRACE("State back to INIT\r\n"); + } + return; + } + } + else { + initFlySkyArray(port); + return; + } + } + else { + putFlySkySendChannelData(port); + incrFlySkyFrame(port); + } + + putFlySkyFrameCrc(port); + putFlySkyFrameTail(port); + + if ((DEBUG_RF_FRAME_PRINT & TX_FRAME_ONLY)) { + /* print each command, except channel data by interval */ + uint8_t * data = modulePulsesData[port].pxx_uart.pulses; + if (data[3] != COMMAND_ID_SEND_CHANNEL_DATA || (set_loop_cnt++ % 100 == 0)) { + uint8_t size = modulePulsesData[port].pxx_uart.ptr - data; + TRACE_NOCRLF("TX(State%0d)%0dB:", modulePulsesData[port].flysky.state, size); + for (int idx = 0; idx < size; idx++) { + TRACE_NOCRLF(" %02X", data[idx]); + } + TRACE(";"); + } + } +} + +#if !defined(SIMU) +void usbDownloadTransmit(uint8_t *buffer, uint32_t size) +{ + for (int idx = 0; idx < size; idx++) + { + usbSerialPutc(buffer[idx]); + } +} +#endif diff --git a/radio/src/pulses/modules_constants.h b/radio/src/pulses/modules_constants.h index eec3c99ce..627724438 100644 --- a/radio/src/pulses/modules_constants.h +++ b/radio/src/pulses/modules_constants.h @@ -14,6 +14,7 @@ enum ModuleType { MODULE_TYPE_R9M_LITE_PRO_PXX2, MODULE_TYPE_SBUS, MODULE_TYPE_XJT_LITE_PXX2, + MODULE_TYPE_FLYSKY, MODULE_TYPE_COUNT, MODULE_TYPE_MAX = MODULE_TYPE_COUNT - 1 }; diff --git a/radio/src/pulses/modules_helpers.h b/radio/src/pulses/modules_helpers.h index 7f8dc1833..7c64dedd0 100644 --- a/radio/src/pulses/modules_helpers.h +++ b/radio/src/pulses/modules_helpers.h @@ -34,7 +34,7 @@ #if defined(MULTIMODULE) inline bool isModuleMultimodule(uint8_t idx) { - return idx == EXTERNAL_MODULE && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_MULTIMODULE; + return g_model.moduleData[idx].type == MODULE_TYPE_MULTIMODULE; } inline bool isModuleMultimoduleDSM2(uint8_t idx) @@ -110,6 +110,18 @@ inline bool isModuleCrossfire(uint8_t idx) } #endif +#if defined(PCBFLYSKY) +inline bool isModuleFlysky(uint8_t idx) +{ + return g_model.moduleData[idx].type == MODULE_TYPE_FLYSKY; +} +#else +inline bool isModuleFlysky(uint8_t idx) +{ + return idx == EXTERNAL_MODULE && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_FLYSKY; +} +#endif + #if defined(PCBSKY9X) inline bool isExtraModule(uint8_t idx) { @@ -256,8 +268,8 @@ inline bool isModuleSBUS(uint8_t moduleIdx) } // order is the same as in enum Protocols in myeeprom.h (none, ppm, pxx, pxx2, dsm, crossfire, multi, r9m, r9m2, sbus) -static const int8_t maxChannelsModules[] = { 0, 8, 8, 16, -2, 8, 4, 8, 16, 8}; // relative to 8! -static const int8_t maxChannelsXJT[] = { 0, 8, 0, 4 }; // relative to 8! +static const int8_t maxChannelsModules_M8[] = { 0, 8, 8, 16, -2, 8, 4, 8, 16, 8}; // relative to 8! +static const int8_t maxChannelsXJT_M8[] = { 0, 8, 0, 4 }; // relative to 8! constexpr int8_t MAX_TRAINER_CHANNELS_M8 = MAX_TRAINER_CHANNELS - 8; constexpr int8_t MAX_EXTRA_MODULE_CHANNELS_M8 = 8; // only 16ch PPM @@ -268,7 +280,7 @@ inline int8_t maxModuleChannels_M8(uint8_t moduleIdx) return MAX_EXTRA_MODULE_CHANNELS_M8; } else if (isModuleXJT(moduleIdx)) { - return maxChannelsXJT[1 + g_model.moduleData[moduleIdx].subType]; + return maxChannelsXJT_M8[1 + g_model.moduleData[moduleIdx].subType]; } else if (isModuleR9M(moduleIdx)) { if (isModuleR9M_LBT(moduleIdx)) { @@ -282,10 +294,23 @@ inline int8_t maxModuleChannels_M8(uint8_t moduleIdx) } } else { - return maxChannelsModules[g_model.moduleData[moduleIdx].type]; + return maxChannelsModules_M8[g_model.moduleData[moduleIdx].type]; } } +inline int8_t maxModuleChannels(uint8_t moduleIdx) +{ + return maxModuleChannels_M8(moduleIdx) + 8; +} + +inline int8_t minModuleChannels(uint8_t idx) +{ + if (isModuleCrossfire(idx)) + return 16; + else + return 1; +} + inline int8_t defaultModuleChannels_M8(uint8_t idx) { if (isModulePPM(idx)) @@ -300,6 +325,8 @@ inline int8_t defaultModuleChannels_M8(uint8_t idx) return 4; // 12 channels else if (isModulePXX2(idx)) return 8; // 16 channels + else if (isModuleFlysky(idx)) + return 6; // 14 channels else return maxModuleChannels_M8(idx); } @@ -336,6 +363,29 @@ inline bool isModuleRxNumAvailable(uint8_t moduleIdx) if (isModuleMultimodule(moduleIdx)) return true; + if(isModuleMultimodule(moduleIdx)) + return true; + + return false; +} + +inline bool isModuleModelIndexAvailable(uint8_t idx) +{ + if (isModuleXJT(idx)) + return g_model.moduleData[idx].subType != MODULE_SUBTYPE_PXX1_ACCST_D8; + + if (isModuleR9M(idx)) + return true; + + if (isModuleDSM2(idx)) + return true; + + if (isModuleISRM(idx)) + return true; + + if (isModuleMultimodule(idx)) + return true; + return false; } diff --git a/radio/src/pulses/pulses.h b/radio/src/pulses/pulses.h index 87208e7b9..d44830201 100644 --- a/radio/src/pulses/pulses.h +++ b/radio/src/pulses/pulses.h @@ -34,6 +34,12 @@ extern uint8_t dsm2BindTimer; #endif +#if defined(PCBFLYSKY) + #define IS_FLYSKY_PROTOCOL(protocol) (protocol==PROTO_FLYSKY) +#else + #define IS_FLYSKY_PROTOCOL(protocol) (0) +#endif + #if defined(DSM2) #define IS_DSM2_PROTOCOL(protocol) (protocol>=PROTOCOL_CHANNELS_DSM2_LP45 && protocol<=PROTOCOL_CHANNELS_DSM2_DSMX) #else @@ -350,6 +356,7 @@ enum ChannelsProtocols { PROTOCOL_CHANNELS_SBUS, PROTOCOL_CHANNELS_PXX2_LOWSPEED, PROTOCOL_CHANNELS_PXX2_HIGHSPEED, + PROTOCOL_CHANNELS_FLYSKY }; inline void stopPulses() diff --git a/radio/src/pulses/pxx2.cpp b/radio/src/pulses/pxx2.cpp index b313b356b..ef8666bb7 100644 --- a/radio/src/pulses/pxx2.cpp +++ b/radio/src/pulses/pxx2.cpp @@ -253,9 +253,11 @@ void Pxx2Pulses::setupAccessBindFrame(uint8_t module) if (destination->step == BIND_WAIT) { if (get_tmr10ms() > destination->timeout) { - moduleState[module].mode = MODULE_MODE_NORMAL; destination->step = BIND_OK; + moduleState[module].mode = MODULE_MODE_NORMAL; +#if !defined(COLORLCD) POPUP_INFORMATION(STR_BIND_OK); // TODO rather use the new callback +#endif } return; } @@ -451,7 +453,7 @@ const char * Pxx2OtaUpdate::nextStep(uint8_t step, const char * rxName, uint32_t } } -const char * Pxx2OtaUpdate::doFlashFirmware(const char * filename) +const char * Pxx2OtaUpdate::doFlashFirmware(const char * filename, ProgressHandler progressHandler) { FIL file; uint8_t buffer[32]; @@ -483,7 +485,7 @@ const char * Pxx2OtaUpdate::doFlashFirmware(const char * filename) uint32_t done = 0; while (1) { - drawProgressScreen(getBasename(filename), STR_OTA_UPDATE, done, size); + progressHandler(getBasename(filename), STR_OTA_UPDATE, done, size); if (f_read(&file, buffer, sizeof(buffer), &count) != FR_OK) { f_close(&file); return "Read file failed"; @@ -505,7 +507,7 @@ const char * Pxx2OtaUpdate::doFlashFirmware(const char * filename) return nextStep(OTA_UPDATE_EOF, nullptr, done, nullptr); } -void Pxx2OtaUpdate::flashFirmware(const char * filename) +void Pxx2OtaUpdate::flashFirmware(const char * filename, ProgressHandler progressHandler) { pausePulses(); @@ -513,7 +515,7 @@ void Pxx2OtaUpdate::flashFirmware(const char * filename) RTOS_WAIT_MS(100); moduleState[module].mode = MODULE_MODE_OTA_UPDATE; - const char * result = doFlashFirmware(filename); + const char * result = doFlashFirmware(filename, progressHandler); moduleState[module].mode = MODULE_MODE_NORMAL; AUDIO_PLAY(AU_SPECIAL_SOUND_BEEP1 ); diff --git a/radio/src/pulses/pxx2.h b/radio/src/pulses/pxx2.h index ed8c07418..9b08e1235 100644 --- a/radio/src/pulses/pxx2.h +++ b/radio/src/pulses/pxx2.h @@ -23,6 +23,7 @@ #include "fifo.h" #include "io/frsky_pxx2.h" +#include "popups.h" #include "./pxx.h" #define PXX2_TYPE_C_MODULE 0x01 @@ -430,13 +431,13 @@ class Pxx2OtaUpdate { { } - void flashFirmware(const char * filename); + void flashFirmware(const char * filename, ProgressHandler progressHandler); protected: uint8_t module; const char * rxName; - const char * doFlashFirmware(const char * filename); + const char * doFlashFirmware(const char * filename, ProgressHandler progressHandler); bool waitStep(uint8_t step, uint8_t timeout); const char * nextStep(uint8_t step, const char * rxName, uint32_t address, const uint8_t * buffer); }; diff --git a/radio/src/rtc.h b/radio/src/rtc.h index c65131b98..f6ec95056 100644 --- a/radio/src/rtc.h +++ b/radio/src/rtc.h @@ -30,6 +30,8 @@ #define TIME_T_MIN (-LONG_MAX) #define TIME_T_MAX (LONG_MAX) +#define SET_LOAD_DATETIME(x) { rtcSetTime(x); g_rtcTime = gmktime(x); } + typedef long int gtime_t; struct gtm diff --git a/radio/src/sdcard.h b/radio/src/sdcard.h index d56c9f7bd..40f716edc 100644 --- a/radio/src/sdcard.h +++ b/radio/src/sdcard.h @@ -74,7 +74,7 @@ const char RADIO_SETTINGS_PATH[] = RADIO_PATH "/radio.bin"; #define LEN_FILE_EXTENSION_MAX 5 // longest used, including the dot, excluding null term. -#if defined(PCBHORUS) +#if defined(COLORLCD) #define BITMAPS_EXT BMP_EXT JPG_EXT PNG_EXT #define LEN_BITMAPS_EXT 4 #else @@ -139,6 +139,8 @@ const char * getBasename(const char * path); #define OTX_FOURCC 0x3C78746F // otx for Taranis X9-Lite #elif defined(PCBX9D) || defined(PCBX9DP) #define OTX_FOURCC 0x3378746F // otx for Taranis X9D +#elif defined(PCBNV14) + #define OTX_FOURCC 0x3A78746F // otx for NV14 #elif defined(PCBSKY9X) #define OTX_FOURCC 0x3278746F // otx for sky9x #elif defined(RADIO_T12) diff --git a/radio/src/serial.cpp b/radio/src/serial.cpp index 953a865f5..351f58502 100644 --- a/radio/src/serial.cpp +++ b/radio/src/serial.cpp @@ -41,8 +41,9 @@ void serialPrintf(const char * format, ...) va_list arglist; char tmp[PRINTF_BUFFER_SIZE+1]; + snprintf(tmp, PRINTF_BUFFER_SIZE, "+%05lums: ", debugCounter1ms); va_start(arglist, format); - vsnprintf(tmp, PRINTF_BUFFER_SIZE, format, arglist); + vsnprintf(tmp+strlen(tmp), PRINTF_BUFFER_SIZE-strlen(tmp), format, arglist); tmp[PRINTF_BUFFER_SIZE] = '\0'; va_end(arglist); @@ -50,6 +51,7 @@ void serialPrintf(const char * format, ...) while (*t) { serialPutc(*t++); } + debugCounter1ms = 0; } void serialCrlf() diff --git a/radio/src/simu.cpp b/radio/src/simu.cpp index b92e16dab..f472b3b23 100644 --- a/radio/src/simu.cpp +++ b/radio/src/simu.cpp @@ -42,6 +42,8 @@ #define W2 LCD_W*LCD_ZOOM #define H2 LCD_H*LCD_ZOOM +extern STRUCT_TOUCH touchState; + class OpenTxSim: public FXMainWindow { FXDECLARE(OpenTxSim) @@ -52,6 +54,9 @@ class OpenTxSim: public FXMainWindow ~OpenTxSim(); void updateKeysAndSwitches(bool start=false); long onKeypress(FXObject*, FXSelector, void*); + long onMouseDown(FXObject*,FXSelector,void*); + long onMouseUp(FXObject*,FXSelector,void*); + long onMouseMove(FXObject*,FXSelector,void*); long onTimeout(FXObject*, FXSelector, void*); void createBitmap(int index, uint16_t *data, int x, int y, int w, int h); void makeSnapshot(const FXDrawable* drawable); @@ -73,6 +78,9 @@ FXDEFMAP(OpenTxSim) OpenTxSimMap[] = { // Message_Type _______ID____Message_Handler_______ FXMAPFUNC(SEL_TIMEOUT, 2, OpenTxSim::onTimeout), FXMAPFUNC(SEL_KEYPRESS, 0, OpenTxSim::onKeypress), + FXMAPFUNC(SEL_LEFTBUTTONPRESS, 0, OpenTxSim::onMouseDown), + FXMAPFUNC(SEL_LEFTBUTTONRELEASE, 0, OpenTxSim::onMouseUp), + FXMAPFUNC(SEL_MOTION, 0, OpenTxSim::onMouseMove), }; FXIMPLEMENT(OpenTxSim, FXMainWindow, OpenTxSimMap, ARRAYNUMBER(OpenTxSimMap)) @@ -139,6 +147,8 @@ OpenTxSim::OpenTxSim(FXApp* a): OpenTxSim::~OpenTxSim() { + TRACE("OpenTxSim::~OpenTxSim()"); + StopSimu(); StopAudioThread(); @@ -243,10 +253,65 @@ long OpenTxSim::onKeypress(FXObject *, FXSelector, void * v) return 0; } +long OpenTxSim::onMouseDown(FXObject*,FXSelector,void*v) +{ + FXEvent * evt = (FXEvent *)v; + UNUSED(evt); + + // TRACE("onMouseDown %d %d", evt->win_x, evt->win_y); + +#if defined(HARDWARE_TOUCH) + touchState.Event = TE_DOWN; + touchState.startX = touchState.lastX = touchState.X = evt->win_x; + touchState.startY = touchState.lastY = touchState.Y = evt->win_y; +#endif + + return 0; +} + +long OpenTxSim::onMouseUp(FXObject*,FXSelector,void*v) +{ + FXEvent * evt = (FXEvent *)v; + UNUSED(evt); + + // TRACE("onMouseUp %d %d", evt->win_x, evt->win_y); + +#if defined(HARDWARE_TOUCH) + if (touchState.Event == TE_DOWN) { + touchState.Event = TE_UP; + touchState.X = evt->win_x; + touchState.Y = evt->win_y; + } + else { + touchState.Event = TE_NONE; + } +#endif + + return 0; +} + +long OpenTxSim::onMouseMove(FXObject*,FXSelector,void*v) +{ + FXEvent * evt = (FXEvent *)v; + UNUSED(evt); + +#if defined(HARDWARE_TOUCH) + if (evt->state & LEFTBUTTONMASK && (touchState.Event == TE_SLIDE || abs(evt->win_x - touchState.X) > 5 || abs(evt->win_y - touchState.Y) > 5)) { + touchState.Event = TE_SLIDE; + touchState.X = evt->win_x; + touchState.Y = evt->win_y; + } +#endif + + return 0; +} + void OpenTxSim::updateKeysAndSwitches(bool start) { static int keys[] = { -#if defined(PCBHORUS) +#if defined(PCBNV14) + // no keys +#elif defined(PCBHORUS) KEY_Page_Up, KEY_PGUP, KEY_Page_Down, KEY_PGDN, KEY_Return, KEY_ENTER, diff --git a/radio/src/stamp.cpp b/radio/src/stamp.cpp index 85d5106a2..91d880472 100644 --- a/radio/src/stamp.cpp +++ b/radio/src/stamp.cpp @@ -26,17 +26,13 @@ #define EEPROM_STR DEFNUMSTR(EEPROM_VER); -#if defined(PCBHORUS) -#define TAB "\037\075" -#else #define TAB "\037\033" -#endif #if defined(COLORLCD) - const char vers_stamp[] = "VERS" TAB ": " "opentx-" FLAVOUR "-" VERSION " (" GIT_STR ")"; - const char date_stamp[] = "DATE" TAB ": " DATE; - const char time_stamp[] = "TIME" TAB ": " TIME; - const char eeprom_stamp[] = "EEPR" TAB ": " EEPROM_STR; + const char vers_stamp[] = "opentx-" FLAVOUR "-" VERSION " (" GIT_STR ")"; + const char date_stamp[] = DATE; + const char time_stamp[] = TIME; + const char eeprom_stamp[] = EEPROM_STR; #elif defined(BOARD_NAME) const char vers_stamp[] = "FW" TAB ": opentx-" BOARD_NAME "\036VERS" TAB ": " VERSION " (" GIT_STR ")" "\036DATE" TAB ": " DATE " " TIME "\036EEPR" TAB ": " EEPROM_STR; #else diff --git a/radio/src/storage/conversions/conversions_216_218.cpp b/radio/src/storage/conversions/conversions_216_218.cpp index dc9c03f2f..b54e61b25 100644 --- a/radio/src/storage/conversions/conversions_216_218.cpp +++ b/radio/src/storage/conversions/conversions_216_218.cpp @@ -919,8 +919,8 @@ void convertModelData_216_to_217(ModelData &model) for (uint8_t i=0; i<2; i++) { TimerData_v217 & timer = newModel.timers[i]; - if (oldModel.timers[i].mode >= TMRMODE_COUNT) - timer.mode = TMRMODE_COUNT + convertSwitch_216_to_217(oldModel.timers[i].mode - TMRMODE_COUNT + 1) - 1; + if (oldModel.timers[i].mode >= 5) + timer.mode = 5 + convertSwitch_216_to_217(oldModel.timers[i].mode - 5 + 1) - 1; else timer.mode = convertSwitch_216_to_217(oldModel.timers[i].mode); timer.start = oldModel.timers[i].start; @@ -1088,8 +1088,8 @@ void convertModelData_217_to_218(ModelData &model) newModel.header = oldModel.header; for (uint8_t i=0; i= TMRMODE_COUNT) - newModel.timers[i].mode = TMRMODE_COUNT + convertSwitch_217_to_218(oldModel.timers[i].mode - TMRMODE_COUNT + 1) - 1; + if (oldModel.timers[i].mode >= 5) + newModel.timers[i].mode = 5 + convertSwitch_217_to_218(oldModel.timers[i].mode - 5 + 1) - 1; else newModel.timers[i].mode = convertSwitch_217_to_218(oldModel.timers[i].mode); if (oldModel.timers[i].mode) diff --git a/radio/src/storage/conversions/datastructs_218.h b/radio/src/storage/conversions/datastructs_218.h index 62515cab9..d06324708 100644 --- a/radio/src/storage/conversions/datastructs_218.h +++ b/radio/src/storage/conversions/datastructs_218.h @@ -523,7 +523,7 @@ PACK(struct ModelData_v218 { #endif #if defined(COLORLCD) - #include "gui/480x272/theme.h" + #include "gui/colorlcd/theme.h" #define THEME_NAME_LEN 8 #define THEME_DATA \ char themeName[THEME_NAME_LEN]; \ diff --git a/radio/src/storage/datacopy.cpp b/radio/src/storage/datacopy.cpp index 2a08dc373..0a013b184 100644 --- a/radio/src/storage/datacopy.cpp +++ b/radio/src/storage/datacopy.cpp @@ -124,13 +124,13 @@ template void copyTimerData(A * dest, B * src) { dest->mode = src->mode; + dest->swtch = src->swtch; dest->start = src->start; dest->value = src->value; dest->countdownBeep = src->countdownBeep; dest->minuteBeep = src->minuteBeep; dest->persistent = src->persistent; dest->countdownStart = src->countdownStart; - dest->direction = src->direction; } template @@ -214,7 +214,6 @@ void copyModuleData(A * dest, B * src) dest->rfProtocol = src->rfProtocol; dest->channelsStart = src->channelsStart; dest->channelsCount = src->channelsCount; - copyModuleData_ppm(&dest->ppm, &src->ppm); } template @@ -321,7 +320,7 @@ void copyRadioData(A * dest, B * src) } dest->stickMode = src->stickMode; dest->telemetryBaudrate = src->telemetryBaudrate; - dest->splashSpares = src->splashSpares; + // dest->splashSpares = src->splashSpares; dest->switchesDelay = src->switchesDelay; for (int i=0; i<64; i++) { copyCustomFunctionData(&dest->customFn[i], &src->customFn[i]); diff --git a/radio/src/storage/sdcard_raw.cpp b/radio/src/storage/sdcard_raw.cpp index 1128eee5c..3e5fd71b1 100644 --- a/radio/src/storage/sdcard_raw.cpp +++ b/radio/src/storage/sdcard_raw.cpp @@ -19,7 +19,7 @@ */ #include "opentx.h" -#include "modelslist.h" +#include "gui/colorlcd/modelslist.h" #include "conversions/conversions.h" void getModelPath(char * path, const char * filename) @@ -147,9 +147,11 @@ const char * loadModel(const char * filename, bool alarms) storageCheck(true); alarms = false; } +#if defined(EEPROM_CONVERSIONS) else if (version < EEPROM_VER) { convertModelData(version); } +#endif postModelLoad(alarms); @@ -165,9 +167,11 @@ const char * loadRadioSettings(const char * path) return error; } +#if defined(EEPROM_CONVERSIONS) if (version < EEPROM_VER) { convertRadioData(version); } +#endif return nullptr; } @@ -225,10 +229,11 @@ void storageReadAll() // Wipe models list in case // it's being reloaded after USB connection - modelslist.clear(); + #warning "TODO modelslist.clear() + load()" + // modelslist.clear(); // and reload the list - modelslist.load(); + // modelslist.load(); } void storageCreateModelsList() @@ -274,7 +279,7 @@ void storageEraseAll(bool warn) { TRACE("storageEraseAll"); -#if defined(COLORLCD) +#if defined(LIBOPENUI) // the theme has not been loaded before theme->load(); #endif diff --git a/radio/src/storage/storage_common.cpp b/radio/src/storage/storage_common.cpp index 78cd7f5d0..d7a4bcb02 100644 --- a/radio/src/storage/storage_common.cpp +++ b/radio/src/storage/storage_common.cpp @@ -53,7 +53,7 @@ void preModelLoad() pauseMixerCalculations(); } -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) static void fixUpModel() { // Ensure that when rfProtocol is MODULE_SUBTYPE_PXX1_OFF the type of the module is MODULE_TYPE_NONE @@ -64,7 +64,7 @@ static void fixUpModel() void postModelLoad(bool alarms) { -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) fixUpModel(); #endif AUDIO_FLUSH(); diff --git a/radio/src/strhelpers.cpp b/radio/src/strhelpers.cpp index 3473cc5ab..f3aba7513 100644 --- a/radio/src/strhelpers.cpp +++ b/radio/src/strhelpers.cpp @@ -19,9 +19,10 @@ */ #include "opentx.h" +#include "strhelpers.h" #if !defined(BOOT) -const char s_charTab[] = "_-.,"; +const char s_charTab[] = "_-.,"; char hex2zchar(uint8_t hex) { @@ -241,6 +242,40 @@ char * getGVarString(char * dest, int idx) return dest; } +char * getFlightModeString(char * dest, int8_t idx) +{ + char * s = dest; + + if (idx==0) { + strcpy(s, "---"); + return dest; + } + + if (idx < 0) { + *s++ = '!'; + idx = -idx; + } + + s = strAppend(s, STR_FM); + strAppendUnsigned(s, idx - 1); + return dest; +} + +char * getSwitchWarningString(char * dest, swsrc_t idx) +{ + char * s = dest; + uint8_t state = g_model.switchWarningState >> (3*idx) & 0x07; + + *s++ = 'A' + idx; + if (state == 0) + *s = '\0'; + else { + *s++ = "x\300-\301"[state]; + *s = '\0'; + } + return dest; +} + #if !defined(PCBSKY9X) char * getSwitchName(char * dest, swsrc_t idx) { @@ -292,8 +327,8 @@ char * getSwitchPositionName(char * dest, swsrc_t idx) getStringAtIndex(s, STR_VSWITCHES, idx); } #else - #define IDX_TRIMS_IN_STR_VSWITCHES (1) - #define IDX_ON_IN_STR_VSWITCHES (IDX_TRIMS_IN_STR_VSWITCHES+SWSRC_LAST_TRIM-SWSRC_FIRST_TRIM+1) +#define IDX_TRIMS_IN_STR_VSWITCHES (1) +#define IDX_ON_IN_STR_VSWITCHES (IDX_TRIMS_IN_STR_VSWITCHES+SWSRC_LAST_TRIM-SWSRC_FIRST_TRIM+1) if (idx <= SWSRC_LAST_SWITCH) { div_t swinfo = switchInfo(idx); s = getSwitchName(s, idx); @@ -303,7 +338,7 @@ char * getSwitchPositionName(char * dest, swsrc_t idx) #endif // PCBSKY9X #if NUM_XPOTS > 0 - else if (idx <= SWSRC_LAST_MULTIPOS_SWITCH) { + else if (idx <= SWSRC_LAST_MULTIPOS_SWITCH) { div_t swinfo = div(int(idx - SWSRC_FIRST_MULTIPOS_SWITCH), XPOTS_MULTIPOS_COUNT); char temp[LEN_ANA_NAME+1]; getSourceString(temp, MIXSRC_FIRST_POT+swinfo.quot); @@ -313,7 +348,7 @@ char * getSwitchPositionName(char * dest, swsrc_t idx) #endif #if defined(PCBSKY9X) - else if (idx <= SWSRC_REa) { + else if (idx <= SWSRC_REa) { getStringAtIndex(s, STR_VSWITCHES, IDX_TRIMS_IN_STR_VSWITCHES+idx-SWSRC_FIRST_TRIM); } #else @@ -357,13 +392,13 @@ char * getSourceString(char * dest, mixsrc_t idx) } else if (idx <= MIXSRC_LAST_INPUT) { idx -= MIXSRC_FIRST_INPUT; - *dest++ = '\314'; + *dest = '\314'; if (ZEXIST(g_model.inputNames[idx])) { - zchar2str(dest, g_model.inputNames[idx], LEN_INPUT_NAME); - dest[LEN_INPUT_NAME] = '\0'; + zchar2str(dest+1, g_model.inputNames[idx], LEN_INPUT_NAME); + dest[LEN_INPUT_NAME+1] = '\0'; } else { - strAppendUnsigned(dest, idx+1, 2); + strAppendUnsigned(dest+1, idx+1, 2); } } #if defined(LUA_INPUTS) @@ -547,3 +582,30 @@ char * strAppendDate(char * str, bool time) } } #endif + +static char tmpHelpersString[32]; + +char * getSwitchWarningString(swsrc_t idx) +{ + return getSwitchWarningString(tmpHelpersString, idx); +} + +char * getSourceString(mixsrc_t idx) +{ + return getSourceString(tmpHelpersString, idx); +} + +char * getCurveString(int idx) +{ + return getCurveString(tmpHelpersString, idx); +} + +char * getTimerString(int32_t tme, uint8_t hours) +{ + return getTimerString(tmpHelpersString, tme, hours); +} + +char * getSwitchPositionName(swsrc_t idx) +{ + return getSwitchPositionName(tmpHelpersString, idx); +} diff --git a/radio/src/strhelpers.h b/radio/src/strhelpers.h index 114d7a009..2c00acfaf 100644 --- a/radio/src/strhelpers.h +++ b/radio/src/strhelpers.h @@ -21,6 +21,10 @@ #ifndef _STRHELPERS_H_ #define _STRHELPERS_H_ +#include + +#define TEXT_AT_INDEX(val, idx) std::string(val+1+val[0]*(idx), min(val[0], strlen(val+1+val[0]*(idx)))) + 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); char * strAppendSigned(char * dest, int32_t value, uint8_t digits=0, uint8_t radix=10); @@ -40,4 +44,14 @@ char * getSwitchName(char * dest, swsrc_t idx); char * getSourceString(char * dest, mixsrc_t idx); #endif +char * getFlightModeString(char * dest, int8_t idx); +#define SWITCH_WARNING_STR_SIZE 3 +char * getSwitchWarningString(char * dest, swsrc_t idx); + +char * getSourceString(mixsrc_t idx); +char * getSwitchPositionName(swsrc_t idx); +char * getSwitchWarningString(swsrc_t idx); +char * getCurveString(int idx); +char * getTimerString(int32_t tme, uint8_t hours); + #endif // _STRHELPERS_H_ diff --git a/radio/src/stubs.cpp b/radio/src/stubs.cpp new file mode 100644 index 000000000..6c33e2536 --- /dev/null +++ b/radio/src/stubs.cpp @@ -0,0 +1,63 @@ +#include "opentx.h" + +const char *warningText = NULL; +const char *warningInfoText; +uint8_t warningInfoLength; +uint8_t warningType; +uint8_t warningResult = 0; +uint8_t warningInfoFlags; +void (*popupFunc)(uint16_t event) = NULL; +const char *popupMenuItems[POPUP_MENU_MAX_LINES]; +uint16_t popupMenuItemsCount = 0; +uint16_t popupMenuOffset = 0; +uint8_t popupMenuOffsetType = 0; +void (*popupMenuHandler)(const char * result); +uint8_t noHighlightCounter; + +void checkLuaMemoryUsage() +{ +} + +void runPopupWarning(uint16_t event) +{ +} + +void drawFatalErrorScreen(const char * message) +{ +} + +void loadCustomScreens() +{ +} + +void showAlertBox(const char * title, const char * text, const char * action, uint8_t sound) +{ +} + +void showMessageBox(const char * title) +{ + +} + +bool menuFirstCalib(event_t event) +{ + return false; +} + +void drawAlertBox(const char * title, const char * text, const char * action) +{ + +} + +uint8_t menuLevel; +int menuVerticalOffset; + +void drawStringWithIndex(coord_t x, coord_t y, const char * str, int idx, LcdFlags flags, const char * prefix, const char * suffix) +{ + +} + +void runFatalErrorScreen(char const*) +{ + +} diff --git a/radio/src/switches.cpp b/radio/src/switches.cpp index 8e62a3212..2e691f848 100644 --- a/radio/src/switches.cpp +++ b/radio/src/switches.cpp @@ -22,7 +22,7 @@ #define CS_LAST_VALUE_INIT -32768 -#if defined(PCBHORUS) +#if defined(COLORLCD) #define SWITCH_WARNING_LIST_X WARNING_LINE_X #define SWITCH_WARNING_LIST_Y WARNING_LINE_Y+3*FH #elif LCD_W >= 212 @@ -56,7 +56,7 @@ LogicalSwitchesFlightModeContext lswFm[MAX_FLIGHT_MODES]; #define LS_LAST_VALUE(fm, idx) lswFm[fm].lsw[idx].lastValue -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) || defined(PCBFLYSKY) #if defined(PCBX9E) tmr10ms_t switchesMidposStart[16]; #else @@ -421,7 +421,7 @@ bool getSwitch(swsrc_t swtch, uint8_t flags) } #endif else if (cs_idx <= SWSRC_LAST_SWITCH) { -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) if (flags & GETSWITCH_MIDPOS_DELAY) result = SWITCH_POSITION(cs_idx-SWSRC_FIRST_SWITCH); else @@ -495,7 +495,7 @@ swsrc_t getMovedSwitch() static tmr10ms_t s_move_last_time = 0; swsrc_t result = 0; -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) for (int i=0; i> (i*2)) & 0x03)) { if (++numWarnings < 6) { // LcdFlags attr = ((states & mask) == (switches_states & mask)) ? TEXT_COLOR : ALARM_COLOR; - LcdFlags attr = ALARM_COLOR; - drawSwitch(x, y, SWSRC_FIRST_SWITCH+i*3+state-1, attr); + // LcdFlags attr = ALARM_COLOR; + // drawSwitch(x, y, SWSRC_FIRST_SWITCH+i*3+state-1, attr); x += 35; } } diff --git a/radio/src/targets/common/arm/CMakeLists.txt b/radio/src/targets/common/arm/CMakeLists.txt index d07988221..319a6cf2d 100644 --- a/radio/src/targets/common/arm/CMakeLists.txt +++ b/radio/src/targets/common/arm/CMakeLists.txt @@ -146,6 +146,10 @@ if(MODULE_SIZE_STD) add_definitions(-DHARDWARE_EXTERNAL_MODULE_SIZE_STD) endif() +if(MULTIMODULE STREQUAL INTERNAL) + add_definitions(-DINTERNAL_MODULE_MULTI) +endif() + if(MULTIMODULE) add_definitions(-DMULTIMODULE) set(SRC ${SRC} pulses/multi.cpp telemetry/spektrum.cpp telemetry/flysky_ibus.cpp telemetry/multi.cpp) @@ -163,6 +167,8 @@ if(CROSSFIRE) ) endif() +add_definitions(-g) + add_definitions(-DCPUARM) add_definitions(-DGPS) add_definitions(-DBOLD_FONT -DBATTGRAPH -DTHRTRACE) diff --git a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp index fc41ae08b..f6b011a1d 100644 --- a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp +++ b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp @@ -22,7 +22,14 @@ uint8_t auxSerialMode = 0; Fifo auxSerialTxFifo; + +#if defined(AUX_SERIAL_DMA_Stream_RX) AuxSerialRxFifo auxSerialRxFifo __DMA (AUX_SERIAL_DMA_Stream_RX); +#else +AuxSerialRxFifo auxSerialRxFifo; +#endif + +DMAFifo<32> auxSerialRxFifo __DMA (AUX_SERIAL_DMA_Stream_RX); void auxSerialSetup(unsigned int baudrate, bool dma) { @@ -47,6 +54,21 @@ void auxSerialSetup(unsigned int baudrate, bool dma) USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_Init(AUX_SERIAL_USART, &USART_InitStructure); +#if defined(AUX_SERIAL_TX_INVERT_GPIO_PIN) + GPIO_InitStructure.GPIO_Pin = AUX_SERIAL_TX_INVERT_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_Init(AUX_SERIAL_TX_INVERT_GPIO, &GPIO_InitStructure); + GPIO_ResetBits(AUX_SERIAL_TX_INVERT_GPIO, AUX_SERIAL_TX_INVERT_GPIO_PIN); +#endif + +#if defined(AUX_SERIAL_RX_INVERT_GPIO_PIN) + GPIO_InitStructure.GPIO_Pin = AUX_SERIAL_RX_INVERT_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_Init(AUX_SERIAL_RX_INVERT_GPIO, &GPIO_InitStructure); + GPIO_ResetBits(AUX_SERIAL_RX_INVERT_GPIO, AUX_SERIAL_RX_INVERT_GPIO_PIN); +#endif + +#if defined(SERIAL_DMA_Stream_RX) if (dma) { DMA_InitTypeDef DMA_InitStructure; auxSerialRxFifo.clear(); @@ -67,18 +89,20 @@ void auxSerialSetup(unsigned int baudrate, bool dma) DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; - DMA_Init(AUX_SERIAL_DMA_Stream_RX, &DMA_InitStructure); + DMA_Init(AUX_SERIAL_DMA_Channel_RX, &DMA_InitStructure); USART_DMACmd(AUX_SERIAL_USART, USART_DMAReq_Rx, ENABLE); USART_Cmd(AUX_SERIAL_USART, ENABLE); - DMA_Cmd(AUX_SERIAL_DMA_Stream_RX, ENABLE); - } - else { - USART_Cmd(AUX_SERIAL_USART, ENABLE); - USART_ITConfig(AUX_SERIAL_USART, USART_IT_RXNE, ENABLE); - USART_ITConfig(AUX_SERIAL_USART, USART_IT_TXE, DISABLE); - NVIC_SetPriority(AUX_SERIAL_USART_IRQn, 7); - NVIC_EnableIRQ(AUX_SERIAL_USART_IRQn); + DMA_Cmd(AUX_SERIAL_DMA_Channel_RX, ENABLE); + return; } +#endif + + // no DMA ... + USART_Cmd(AUX_SERIAL_USART, ENABLE); + USART_ITConfig(AUX_SERIAL_USART, USART_IT_RXNE, ENABLE); + USART_ITConfig(AUX_SERIAL_USART, USART_IT_TXE, DISABLE); + NVIC_SetPriority(AUX_SERIAL_USART_IRQn, 7); + NVIC_EnableIRQ(AUX_SERIAL_USART_IRQn); } void auxSerialInit(unsigned int mode, unsigned int protocol) @@ -136,7 +160,10 @@ void auxSerialSbusInit() void auxSerialStop() { +#if defined(AUX_SERIAL_DMA_Stream_RX) DMA_DeInit(AUX_SERIAL_DMA_Stream_RX); +#endif + USART_DeInit(AUX_SERIAL_USART); } @@ -149,7 +176,6 @@ uint8_t auxSerialTracesEnabled() #endif } -#if !defined(SIMU) extern "C" void AUX_SERIAL_USART_IRQHandler(void) { DEBUG_INTERRUPT(INT_SER2); @@ -183,4 +209,3 @@ extern "C" void AUX_SERIAL_USART_IRQHandler(void) } #endif } -#endif diff --git a/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt b/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt index 4141a766e..10e221d60 100644 --- a/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt +++ b/radio/src/targets/common/arm/stm32/bootloader/CMakeLists.txt @@ -97,14 +97,15 @@ if(NOT (PCB STREQUAL X10 OR PCB STREQUAL X12S)) ${BOOTLOADER_SRC} ../../../../../targets/${TARGET_DIR}/i2c_driver.cpp ) - remove_definitions(-DDEBUG) -else() +endif() + +if(PCB STREQUAL X10 OR PCB STREQUAL X12S OR PCB STREQUAL NV14) set(BOOTLOADER_SRC ${BOOTLOADER_SRC} + ../../../../../gui/libopenui/bitmapbuffer.cpp ../../../../../targets/${TARGET_DIR}/sdram_driver.c ../../../../../targets/common/arm/stm32/sdio_sd.c ../../../../../targets/${TARGET_DIR}/haptic_driver.cpp - ../../../../../gui/${GUI_DIR}/bitmapbuffer.cpp ../../../../../gui/${GUI_DIR}/rle.cpp ../../../../../syscalls.c ) @@ -119,6 +120,7 @@ else() endif() endif() +remove_definitions(-DDEBUG) remove_definitions(-DDISK_CACHE) remove_definitions(-DLUA) remove_definitions(-DCLI) diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index dc4359092..415e7e024 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -9,12 +9,11 @@ set(HSE_VALUE 12000000) set(SDCARD YES) set(EEPROM SDCARD) set(HAPTIC YES) -set(GUI_DIR 480x272) +set(GUI_DIR colorlcd) set(NAVIGATION_TYPE horus) set(TARGET_DIR horus) set(LINKER_SCRIPT targets/horus/stm32f4_flash.ld) set(RAMBACKUP YES) -set(LUA YES) set(PPM_LIMITS_SYMETRICAL YES) set(USB_SERIAL ON CACHE BOOL "Enable USB serial (CDC)") @@ -26,6 +25,8 @@ if(SPLASH STREQUAL OFF) message(FATAL_ERROR "Option SPLASH=OFF not supported on Horus") endif() +add_definitions(-DPCBFRSKY) + if (PCB STREQUAL X10) set(PCBREV "STD" CACHE STRING "PCB Revision") add_definitions(-DPCBX10) @@ -82,7 +83,7 @@ add_definitions(-DPCBREV_${PCBREV}) set(FIRMWARE_DEPENDENCIES ${FIRMWARE_DEPENDENCIES} ${BITMAPS_TARGET}) -add_definitions(-DPCBHORUS -DSTM32F429_439xx -DSDRAM -DCOLORLCD) +add_definitions(-DPCBHORUS -DSTM32F429_439xx -DSDRAM -DCOLORLCD -DHARDWARE_KEYS) add_definitions(-DEEPROM_VARIANT=0 -DAUDIO -DVOICE -DRTCLOCK) add_definitions(-DGPS_USART_BAUDRATE=${INTERNAL_GPS_BAUDRATE}) add_definitions(-DPWR_BUTTON_${PWR_BUTTON}) @@ -100,49 +101,6 @@ if(INTERNAL_MODULE_PXX2) add_definitions(-DINTERNAL_MODULE_PXX2) endif() -include_directories(${RADIO_SRC_DIRECTORY}/fonts/480x272 gui/${GUI_DIR} gui/${GUI_DIR}/layouts) - -file(GLOB THEMES_SRC RELATIVE ${RADIO_SRC_DIRECTORY}/gui/480x272 ${RADIO_SRC_DIRECTORY}/gui/480x272/themes/*.cpp) -file(GLOB LAYOUTS_SRC RELATIVE ${RADIO_SRC_DIRECTORY}/gui/480x272 ${RADIO_SRC_DIRECTORY}/gui/480x272/layouts/*.cpp) -file(GLOB WIDGETS_SRC RELATIVE ${RADIO_SRC_DIRECTORY}/gui/480x272 ${RADIO_SRC_DIRECTORY}/gui/480x272/widgets/*.cpp) -set(GUI_SRC - ${GUI_SRC} - bitmapbuffer.cpp - curves.cpp - bitmaps.cpp - radio_sdmanager.cpp - radio_hardware.cpp - model_inputs.cpp - model_mixes.cpp - model_curves.cpp - view_channels.cpp - view_logical_switches.cpp - view_about.cpp - view_text.cpp - screens_setup.cpp - theme.cpp - topbar.cpp - layout.cpp - widget.cpp - menus.cpp - ${THEMES_SRC} - ${LAYOUTS_SRC} - ${WIDGETS_SRC} - rle.cpp - ) - -if(PXX2 OR LUA) - set(GUI_SRC ${GUI_SRC} radio_tools.cpp) -endif() - -if(PXX2) - set(GUI_SRC - ${GUI_SRC} - radio_spectrum_analyser.cpp - radio_power_meter.cpp - ) -endif() - if(DISK_CACHE) set(SRC ${SRC} disk_cache.cpp) add_definitions(-DDISK_CACHE) @@ -154,6 +112,7 @@ if(INTERNAL_GPS) message("Horus: Internal GPS enabled") endif() +set(AUX_SERIAL_DRIVER ../common/arm/stm32/aux_serial_driver.cpp) set(GVAR_SCREEN model_gvars.cpp) set(TARGET_SRC @@ -161,7 +120,6 @@ set(TARGET_SRC led_driver.cpp extmodule_driver.cpp trainer_driver.cpp - ../common/arm/stm32/heartbeat_driver.cpp ../common/arm/stm32/timers_driver.cpp ../common/arm/stm32/intmodule_serial_driver.cpp ../common/arm/stm32/bluetooth_driver.cpp @@ -176,6 +134,7 @@ set(FIRMWARE_TARGET_SRC pwr_driver.cpp sdram_driver.c startup_stm32f42_43xxx.s + ../common/arm/stm32/heartbeat_driver.cpp ../common/arm/stm32/sdio_sd.c ../common/arm/loadboot.cpp ) diff --git a/radio/src/targets/horus/bootloader/boot_menu.cpp b/radio/src/targets/horus/bootloader/boot_menu.cpp index 6d6d1fce4..c8f76a01e 100644 --- a/radio/src/targets/horus/bootloader/boot_menu.cpp +++ b/radio/src/targets/horus/bootloader/boot_menu.cpp @@ -65,11 +65,13 @@ static void bootloaderDrawFooter() void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) { - // clear screen - lcdDrawSolidFilledRect(0, 0, LCD_W-1, LCD_H-1, TEXT_BGCOLOR); +#if defined(COLORLCD) + lcdNextLayer(); +#endif + + lcd->clear(TEXT_BGCOLOR); if (st == ST_START) { - bootloaderDrawTitle(88, "HORUS BOOTLOADER"); lcdDrawBitmapPattern(90, 72, LBM_FLASH, TEXT_COLOR); diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index a811610dc..383c489a3 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -591,12 +591,8 @@ #define INTMODULE_BOOTCMD_GPIO_PIN GPIO_Pin_9 // PI.09 #endif #if defined(PCBX10) || PCBREV >= 13 - #define INTMODULE_RCC_APB1Periph RCC_APB1Periph_TIM2 + #define INTMODULE_RCC_APB1Periph 0 #define INTMODULE_RCC_APB2Periph RCC_APB2Periph_USART1 - #define INTMODULE_TIMER TIM2 - #define INTMODULE_TIMER_IRQn TIM2_IRQn - #define INTMODULE_TIMER_IRQHandler TIM2_IRQHandler - #define INTMODULE_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) #else #define INTMODULE_RCC_APB1Periph 0 #define INTMODULE_RCC_APB2Periph (RCC_APB2Periph_TIM1 | RCC_APB2Periph_USART1) diff --git a/radio/src/targets/horus/lcd_driver.cpp b/radio/src/targets/horus/lcd_driver.cpp index 110db0007..1601e9f78 100644 --- a/radio/src/targets/horus/lcd_driver.cpp +++ b/radio/src/targets/horus/lcd_driver.cpp @@ -35,11 +35,21 @@ uint8_t LCD_FIRST_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(display_t)] __SDRAM; uint8_t LCD_SECOND_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(display_t)] __SDRAM; uint8_t LCD_BACKUP_FRAME_BUFFER[DISPLAY_BUFFER_SIZE * sizeof(display_t)] __SDRAM; +uint8_t currentLayer = LCD_FIRST_LAYER; -uint32_t CurrentLayer = LCD_FIRST_LAYER; +BitmapBuffer lcdBuffer1(BMP_RGB565, LCD_W, LCD_H, (uint16_t *)LCD_FIRST_FRAME_BUFFER); +BitmapBuffer lcdBuffer2(BMP_RGB565, LCD_W, LCD_H, (uint16_t *)LCD_SECOND_FRAME_BUFFER); +BitmapBuffer * lcd = &lcdBuffer1; -#define NRST_LOW() do { LCD_GPIO_NRST->BSRRH = LCD_GPIO_PIN_NRST; } while(0) -#define NRST_HIGH() do { LCD_GPIO_NRST->BSRRL = LCD_GPIO_PIN_NRST; } while(0) +inline void LCD_NRST_LOW() +{ + LCD_GPIO_NRST->BSRRH = LCD_GPIO_PIN_NRST; +} + +inline void LCD_NRST_HIGH() +{ + LCD_GPIO_NRST->BSRRL = LCD_GPIO_PIN_NRST; +} static void LCD_AF_GPIOConfig(void) { @@ -125,31 +135,21 @@ static void LCD_NRSTConfig(void) GPIO_Init(LCD_GPIO_NRST, &GPIO_InitStructure); } -// TODO delay function -static void delay3(uint32_t nCount) +static void lcdReset() { - uint32_t index = 0; - for(index = (1000 * 100 * nCount); index != 0; --index) - { - __asm("nop\n"); - } + LCD_NRST_HIGH(); + delay_ms(1); + + LCD_NRST_LOW(); // RESET; + delay_ms(20); + + LCD_NRST_HIGH(); + delay_ms(20); } -static void lcd_reset(void) +void LCD_Init_LTDC() { - NRST_HIGH(); - delay3(1); - - NRST_LOW(); // RESET(); - delay3(20); - - NRST_HIGH(); - delay3(30); -} - -void LCD_Init_LTDC(void) -{ - LTDC_InitTypeDef LTDC_InitStruct; + LTDC_InitTypeDef LTDC_InitStruct; /* Configure PLLSAI prescalers for LCD */ /* PLLSAI_VCO Input = HSE_VALUE/PLL_M = 1 Mhz */ @@ -159,7 +159,7 @@ void LCD_Init_LTDC(void) //second pam is for audio //third pam is for LCD RCC_PLLSAIConfig(192, 6, 3); - RCC_LTDCCLKDivConfig(RCC_PLLSAIDivR_Div4); //Modify by Fy + RCC_LTDCCLKDivConfig(RCC_PLLSAIDivR_Div4); /* Enable PLLSAI Clock */ RCC_PLLSAICmd(ENABLE); @@ -224,11 +224,6 @@ void LCD_Init_LTDC(void) #endif } -/** - * @brief Initializes the LCD Layers. - * @param None - * @retval None - */ void LCD_LayerInit() { LTDC_Layer_InitTypeDef LTDC_Layer_InitStruct; @@ -300,17 +295,11 @@ void LCD_LayerInit() LTDC_DitherCmd(ENABLE); } -/*********************************output****************************************/ -/** - * @brief Initializes the LCD. - * @param None - * @retval None - */ void LCD_Init(void) { /* Reset the LCD --------------------------------------------------------*/ LCD_NRSTConfig(); - lcd_reset(); + lcdReset(); /* Configure the LCD Control pins */ LCD_AF_GPIOConfig(); @@ -318,25 +307,15 @@ void LCD_Init(void) LCD_Init_LTDC(); } -BitmapBuffer lcdBuffer1(BMP_RGB565, LCD_W, LCD_H, (uint16_t *)LCD_FIRST_FRAME_BUFFER); -BitmapBuffer lcdBuffer2(BMP_RGB565, LCD_W, LCD_H, (uint16_t *)LCD_SECOND_FRAME_BUFFER); -BitmapBuffer * lcd = &lcdBuffer1; - -/** - * @brief Sets the LCD Layer. - * @param Layerx: specifies the Layer foreground or background. - * @retval None - */ -void LCD_SetLayer(uint32_t Layerx) +void LCD_SetLayer(uint32_t layer) { - if (Layerx == LCD_FIRST_LAYER) { + if (layer == LCD_FIRST_LAYER) { lcd = &lcdBuffer1; - CurrentLayer = LCD_FIRST_LAYER; } else { lcd = &lcdBuffer2; - CurrentLayer = LCD_SECOND_LAYER; } + currentLayer = layer; } /** @@ -347,7 +326,7 @@ void LCD_SetLayer(uint32_t Layerx) */ void LCD_SetTransparency(uint8_t transparency) { - if (CurrentLayer == LCD_FIRST_LAYER) { + if (currentLayer == LCD_FIRST_LAYER) { LTDC_LayerAlpha(LTDC_Layer1, transparency); } else { @@ -356,7 +335,7 @@ void LCD_SetTransparency(uint8_t transparency) LTDC_ReloadConfig(LTDC_IMReload); } -void lcdInit(void) +void lcdInit() { /* Initialize the LCD */ LCD_Init(); @@ -527,7 +506,7 @@ void DMABitmapConvert(uint16_t * dest, const uint8_t * src, uint16_t w, uint16_t while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } -void DMAcopy(void * src, void * dest, int len) +void lcdCopy(void * dest, void * src) { DMA2D_DeInit(); @@ -562,21 +541,34 @@ void DMAcopy(void * src, void * dest, int len) void lcdStoreBackupBuffer() { - DMAcopy(lcd->getData(), LCD_BACKUP_FRAME_BUFFER, DISPLAY_BUFFER_SIZE * sizeof(display_t)); + lcdCopy(LCD_BACKUP_FRAME_BUFFER, lcd->getData()); } int lcdRestoreBackupBuffer() { - DMAcopy(LCD_BACKUP_FRAME_BUFFER, lcd->getData(), DISPLAY_BUFFER_SIZE * sizeof(display_t)); + lcdCopy(lcd->getData(), LCD_BACKUP_FRAME_BUFFER); return 1; } void lcdRefresh() { - LCD_SetTransparency(255); - if (CurrentLayer == LCD_FIRST_LAYER) + if (currentLayer == LCD_FIRST_LAYER) { + LTDC_LayerAlpha(LTDC_Layer1, 255); + LTDC_LayerAlpha(LTDC_Layer2, 0); + } + else { + LTDC_LayerAlpha(LTDC_Layer1, 0); + LTDC_LayerAlpha(LTDC_Layer2, 255); + } + LTDC_ReloadConfig(LTDC_IMReload); +} + +void lcdNextLayer() +{ + if (currentLayer == LCD_FIRST_LAYER) { LCD_SetLayer(LCD_SECOND_LAYER); - else + } + else { LCD_SetLayer(LCD_FIRST_LAYER); - LCD_SetTransparency(0); + } } diff --git a/radio/src/targets/horus/libopenui_config.h b/radio/src/targets/horus/libopenui_config.h new file mode 100644 index 000000000..bf374466c --- /dev/null +++ b/radio/src/targets/horus/libopenui_config.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _LIBOPENUI_CONFIG_H_ +#define _LIBOPENUI_CONFIG_H_ + +#include "lcd.h" + +constexpr uint32_t MENU_HEADER_BUTTON_WIDTH = 33; +constexpr uint32_t MENU_HEADER_BUTTONS_LEFT = 47; + +#define MENU_TOOLTIPS +constexpr uint32_t MENU_HEADER_HEIGHT = 45; +constexpr uint32_t MENU_TITLE_TOP = 48; +constexpr uint32_t MENU_TITLE_HEIGHT = 21; +constexpr uint32_t MENU_BODY_TOP = MENU_TITLE_TOP + MENU_TITLE_HEIGHT; +constexpr uint32_t MENU_CONTENT_TOP = MENU_BODY_TOP + 1; +constexpr uint32_t MENU_FOOTER_HEIGHT = 0; +constexpr uint32_t MENU_FOOTER_TOP = LCD_H - MENU_FOOTER_HEIGHT; +constexpr uint32_t MENU_BODY_HEIGHT = MENU_FOOTER_TOP - MENU_BODY_TOP; +constexpr uint32_t MENUS_MARGIN_LEFT = 6; + +constexpr uint32_t DEFAULT_SCROLLBAR_X = LCD_W - 10; +constexpr uint32_t DEFAULT_SCROLLBAR_Y = MENU_CONTENT_TOP; +constexpr uint32_t DEFAULT_SCROLLBAR_H = MENU_FOOTER_TOP - DEFAULT_SCROLLBAR_Y - 6; + +constexpr uint32_t PAGE_PADDING = 6; +constexpr uint32_t PAGE_LINE_HEIGHT = 20; +constexpr uint32_t PAGE_LINE_SPACING = 2; +constexpr uint32_t PAGE_INDENT_WIDTH = 10; +constexpr uint32_t PAGE_LABEL_WIDTH = 240; +constexpr uint32_t FH = PAGE_LINE_HEIGHT; +constexpr uint32_t NUM_BODY_LINES = MENU_BODY_HEIGHT / PAGE_LINE_HEIGHT; + +constexpr uint32_t FIELD_PADDING_LEFT = 3; +constexpr uint32_t FIELD_PADDING_TOP = 0; + +constexpr uint32_t INVERT_HORZ_MARGIN = 2; +constexpr uint32_t INVERT_VERT_MARGIN = 1; +constexpr uint32_t INVERT_LINE_HEIGHT = 20; + +constexpr uint32_t CURVE_SIDE_WIDTH = 100; +constexpr uint32_t CURVE_CENTER_X = LCD_W - CURVE_SIDE_WIDTH - 7; +constexpr uint32_t CURVE_CENTER_Y = 151; +constexpr uint32_t CURVE_COORD_WIDTH = 36; +constexpr uint32_t CURVE_COORD_HEIGHT = 17; + +constexpr uint32_t DATETIME_SEPARATOR_X = LCD_W - 53; +constexpr uint32_t DATETIME_LINE1 = 9; +constexpr uint32_t DATETIME_LINE2 = 23; +constexpr uint32_t DATETIME_MIDDLE = (LCD_W + DATETIME_SEPARATOR_X + 4) / 2; + +constexpr uint32_t MENUS_TOOLBAR_BUTTON_WIDTH = 30; +constexpr uint32_t MENUS_TOOLBAR_BUTTON_PADDING = 3; +constexpr rect_t MENUS_TOOLBAR_RECT = { 100, 51, 30, 209 }; + +#endif // _LIBOPENUI_CONFIG_H_ diff --git a/radio/src/targets/nv14/CMakeLists.txt b/radio/src/targets/nv14/CMakeLists.txt new file mode 100644 index 000000000..d8bc52a09 --- /dev/null +++ b/radio/src/targets/nv14/CMakeLists.txt @@ -0,0 +1,134 @@ +option(DISK_CACHE "Enable SD card disk cache" YES) +option(UNEXPECTED_SHUTDOWN "Enable the Unexpected Shutdown screen" YES) +option(FLYSKY_HALL_STICKS "sticks for FlySky hall sticks" ON) +option(FLYSKY_HALL_STICKS_REVERSE "Reverse sticks for FlySky hall sticks" OFF) +set(PWR_BUTTON "PRESS" CACHE STRING "Pwr button type (PRESS/SWITCH)") + +set(CPU_TYPE STM32F4) +set(HSE_VALUE 12000000) +set(SDCARD YES) +set(EEPROM SDCARD) +set(HAPTIC YES) +set(GUI_DIR colorlcd) +# set(NAVIGATION_TYPE touch) +set(TARGET_DIR nv14) + +if(BOOTLOADER) + set(LINKER_SCRIPT targets/nv14/stm32f4_flash_bootloader.ld) +else() + set(LINKER_SCRIPT targets/nv14/stm32f4_flash.ld) +endif() + +set(RAMBACKUP YES) +set(PPM_LIMITS_SYMETRICAL YES) +# for size report script +set(CPU_TYPE_FULL STM32F429BIT6) +set(SIZE_TARGET_MEM_DEFINE "MEM_SIZE_SDRAM2=8192") +option(USB_SERIAL "Enable USB serial (CDC)" ON) + +set(RF_BAUD_RATE 921600 230400 115200 57600 38400 19200 9600 4800 2400 1200) +set(PCB_RF_BAUD 921600 CACHE STRING "INTERNAL_MODULE_BAUDRATE: ${RF_BAUD_RATE}") +set_property(CACHE PCB_RF_BAUD PROPERTY STRINGS ${RF_BAUD_RATE}) + +set(FLAVOUR nv14) +add_definitions(-DPCBNV14 -DPCBFLYSKY -DINTERNAL_MODULE_BAUDRATE=${PCB_RF_BAUD}) +#add_definitions(-DSOFTWARE_VOLUME) +set(TARGET_SRC + ${TARGET_SRC} + #../common/arm/stm32/audio_dac_driver.cpp + audio_spi_driver.cpp + adc_driver.cpp + ) + +set(FIRMWARE_TARGET_SRC + ${FIRMWARE_TARGET_SRC} + battery_driver.cpp + hallStick_driver.cpp + ) + +set(BITMAPS_TARGET nv14_bitmaps) +set(FONTS_TARGET x12_fonts) +set(LCD_DRIVER lcd_driver.cpp) +set(LUA_EXPORT lua_export_x10) +set(TOUCH_DRIVER touch_driver.cpp) +set(HARDWARE_TOUCH YES) + +set(FIRMWARE_DEPENDENCIES ${FIRMWARE_DEPENDENCIES} ${BITMAPS_TARGET}) + +add_definitions(-DSTM32F429_439xx -DSDRAM -DCOLORLCD -DLIBOPENUI -DHARDWARE_TOUCH) +add_definitions(-DEEPROM_VARIANT=0 -DAUDIO -DVOICE -DRTCLOCK) +add_definitions(-DGPS_USART_BAUDRATE=${INTERNAL_GPS_BAUDRATE}) +add_definitions(-DPWR_BUTTON_${PWR_BUTTON}) + +if(NOT UNEXPECTED_SHUTDOWN) + add_definitions(-DNO_UNEXPECTED_SHUTDOWN) +endif() + +include_directories(${RADIO_SRC_DIRECTORY}/fonts/colorlcd gui/${GUI_DIR} gui/${GUI_DIR}/layouts) + +file(GLOB THEMES_SRC RELATIVE ${RADIO_SRC_DIRECTORY}/gui/colorlcd ${RADIO_SRC_DIRECTORY}/gui/colorlcd/themes/*.cpp) +file(GLOB LAYOUTS_SRC RELATIVE ${RADIO_SRC_DIRECTORY}/gui/colorlcd ${RADIO_SRC_DIRECTORY}/gui/colorlcd/layouts/*.cpp) +file(GLOB WIDGETS_SRC RELATIVE ${RADIO_SRC_DIRECTORY}/gui/colorlcd ${RADIO_SRC_DIRECTORY}/gui/colorlcd/widgets/*.cpp) + +if(DISK_CACHE) + set(SRC ${SRC} disk_cache.cpp) + add_definitions(-DDISK_CACHE) +endif() + +set(AUX_SERIAL_DRIVER ../common/arm/stm32/aux_serial_driver.cpp) + +set(SRC + ${SRC} + io/frsky_firmware_update.cpp + ) + +set(GVAR_SCREEN model_gvars.cpp) + +set(TARGET_SRC + ${TARGET_SRC} + extmodule_driver.cpp + trainer_driver.cpp + ../common/arm/stm32/intmodule_serial_driver.cpp + ) + +set(FIRMWARE_TARGET_SRC + ${FIRMWARE_TARGET_SRC} + ${LCD_DRIVER} + ${TOUCH_DRIVER} + board.cpp + backlight_driver.cpp + pwr_driver.cpp + sdram_driver.c + startup_stm32f42_43xxx.s + ../common/arm/stm32/sdio_sd.c + ) + +if(BOOTLOADER) + set(FIRMWARE_TARGET_SRC + ${FIRMWARE_TARGET_SRC} + ../common/arm/loadboot.cpp + ) +endif() + +set(PULSES_SRC + ${PULSES_SRC} + #flysky.cpp + ) + +set(STM32LIB_SRC + STM32F4xx_StdPeriph_Driver/src/stm32f4xx_sdio.c + STM32F4xx_StdPeriph_Driver/src/stm32f4xx_fmc.c + STM32F4xx_StdPeriph_Driver/src/stm32f4xx_ltdc.c + STM32F4xx_StdPeriph_Driver/src/stm32f4xx_tim.c + STM32F4xx_StdPeriph_Driver/src/stm32f4xx_dma2d.c + ) + +if(PYTHONINTERP_FOUND) + add_custom_target(datacopy + WORKING_DIRECTORY ${RADIO_DIRECTORY}/src + COMMAND ${PYTHON_EXECUTABLE} ${RADIO_DIRECTORY}/util/generate_datacopy.py datastructs.h -DPCBFLYSKY -DPCBNV14 -DCPUARM -DCOLORLCD -DBACKUP > storage/datacopy.cpp + DEPENDS ${RADIO_DIRECTORY}/src/datastructs.h ${RADIO_DIRECTORY}/util/generate_datacopy.py + ) +endif() + +set(LUA_EXPORT lua_export_nv14) diff --git a/radio/src/targets/nv14/adc_driver.cpp b/radio/src/targets/nv14/adc_driver.cpp new file mode 100644 index 000000000..a3db41634 --- /dev/null +++ b/radio/src/targets/nv14/adc_driver.cpp @@ -0,0 +1,308 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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" + +#if defined(SIMU) + // not needed +#elif defined(PCBNV14) + const int8_t ana_direction[NUM_ANALOGS] = { 0 }; + const uint8_t anas_mapping[NUM_ANALOGS] = { 0 /*STICK1*/, 1 /*STICK2*/, 2 /*STICK3*/, 3 /*STICK4*/, + 4 /*POT1*/, 5 /*POT2*/, 6 /*SWA*/, 13 /*SWB*/, + 7 /*SWC*/, 14 /*SWD*/, 8 /*SWE*/, 9 /*SWF*/, + 11/*SWH*/, 10/*SWG*/, + 12 /*TX_VOLTAGE*/ }; + #define ANAS_MAPPING anas_mapping // TODO => hal.h +#elif defined(PCBX10) + const int8_t ana_direction[NUM_ANALOGS] = {1,-1,1,-1, -1,1,-1, 1,-1, 1, 1,1}; +#elif defined(PCBX9E) +#if defined(HORUS_STICKS) + const int8_t ana_direction[NUM_ANALOGS] = {1,-1,1,-1, -1,-1,-1,1, -1,1,-1,-1, -1}; +#else + const int8_t ana_direction[NUM_ANALOGS] = {1,1,-1,-1, -1,-1,-1,1, -1,1,-1,-1, -1}; +#endif + const uint8_t anas_mapping[NUM_ANALOGS] = { 0 /*STICK1*/, 1 /*STICK2*/, 2 /*STICK3*/, 3 /*STICK4*/, + 10 /*POT1*/, 4 /*POT2*/, 5 /*POT3*/, 6 /*POT4*/, + 11 /*SLIDER1*/, 12 /*SLIDER2*/, 7 /*SLIDER3*/, 8 /*SLIDER4*/, + 9 /*TX_VOLTAGE*/ }; + #define ANAS_MAPPING anas_mapping +#elif defined(PCBX9DP) + const int8_t ana_direction[NUM_ANALOGS] = {1,-1,1,-1, 1,1,-1, 1,1, 1}; +#elif defined(PCBX7) + const int8_t ana_direction[NUM_ANALOGS] = {-1,1,-1,1, 1,1, 1}; +#elif defined(PCBI8) + const int8_t ana_direction[NUM_ANALOGS] = {/*sticks*/1,1,1,1, /*pots*/1,1, /*switches*/1,1,1,1,1,1, /*batt*/1,1}; +#elif defined(PCBXLITE) + const int8_t ana_direction[NUM_ANALOGS] = {1,-1,-1,1, -1,1, 1}; +#elif defined(REV4a) + const int8_t ana_direction[NUM_ANALOGS] = {1,-1,1,-1, 1,-1,0, 1,1, 1}; +#else + const int8_t ana_direction[NUM_ANALOGS] = {1,-1,1,-1, 1,1,0, 1,1, 1}; +#endif + +#if NUM_PWMSTICKS > 0 + #define FIRST_ANALOG_ADC (STICKS_PWM_ENABLED() ? NUM_PWMSTICKS : 0) + #define NUM_MAIN_ANALOGS_ADC (STICKS_PWM_ENABLED() ? (NUM_MAIN_ANALOGS - NUM_PWMSTICKS) : NUM_MAIN_ANALOGS) +#elif defined(PCBX9E) + #define FIRST_ANALOG_ADC 0 + #define NUM_MAIN_ANALOGS_ADC 10 + #define NUM_MAIN_ANALOGS_ADC_EXT (NUM_MAIN_ANALOGS - 10) +#else + #define FIRST_ANALOG_ADC 0 + #define FIRST_SUB_ANALOG_ADC 0 + #define NUM_MAIN_ANALOGS_ADC (SUB_ANALOG_POS) + #define NUM_SUB_ANALOGS_ADC (NUM_ANALOGS - SUB_ANALOG_POS) +#endif + +uint16_t adcValues[NUM_ANALOGS] __DMA; + +void adcInit() +{ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + +#if defined(ADC_GPIOA_PINS) + GPIO_InitStructure.GPIO_Pin = ADC_GPIOA_PINS; + GPIO_Init(GPIOA, &GPIO_InitStructure); +#endif + +#if defined(ADC_GPIOB_PINS) + GPIO_InitStructure.GPIO_Pin = ADC_GPIOB_PINS; + GPIO_Init(GPIOB, &GPIO_InitStructure); +#endif + +#if defined(ADC_GPIOC_PINS) + GPIO_InitStructure.GPIO_Pin = ADC_GPIOC_PINS; + GPIO_Init(GPIOC, &GPIO_InitStructure); +#endif + +#if defined(ADC_GPIOF_PINS) + GPIO_InitStructure.GPIO_Pin = ADC_GPIOF_PINS; + GPIO_Init(GPIOF, &GPIO_InitStructure); +#endif + + ADC_MAIN->CR1 = ADC_CR1_SCAN; + ADC_MAIN->CR2 = ADC_CR2_ADON | ADC_CR2_DMA | ADC_CR2_DDS; + ADC_MAIN->SQR1 = (NUM_MAIN_ANALOGS_ADC-1) << 20; // bits 23:20 = number of conversions +#if defined(PCBNV14) + ADC_SUB->CR1 = ADC_CR1_SCAN; + ADC_SUB->CR2 = ADC_CR2_ADON | ADC_CR2_DMA | ADC_CR2_DDS; + ADC_SUB->SQR1 = (NUM_SUB_ANALOGS_ADC-1) << 20; // bits 23:20 = number of conversions +#endif +#if defined(PCBX10) + if (STICKS_PWM_ENABLED()) { + ADC_MAIN->SQR2 = (ADC_CHANNEL_EXT1<<0) + (ADC_CHANNEL_EXT2<<5); // conversions 7 and more + ADC_MAIN->SQR3 = (ADC_CHANNEL_POT1<<0) + (ADC_CHANNEL_POT2<<5) + (ADC_CHANNEL_POT3<<10) + (ADC_CHANNEL_SLIDER1<<15) + (ADC_CHANNEL_SLIDER2<<20) + (ADC_CHANNEL_BATT<<25); // conversions 1 to 6 + } + else { + ADC_MAIN->SQR2 = (ADC_CHANNEL_POT3<<0) + (ADC_CHANNEL_SLIDER1<<5) + (ADC_CHANNEL_SLIDER2<<10) + (ADC_CHANNEL_BATT<<15) + (ADC_CHANNEL_EXT1<<20) + (ADC_CHANNEL_EXT2<<25); // conversions 7 and more + ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH<<0) + (ADC_CHANNEL_STICK_LV<<5) + (ADC_CHANNEL_STICK_RV<<10) + (ADC_CHANNEL_STICK_RH<<15) + (ADC_CHANNEL_POT1<<20) + (ADC_CHANNEL_POT2<<25); // conversions 1 to 6 + } +#elif defined(PCBX9E) + ADC_MAIN->SQR2 = (ADC_CHANNEL_POT4<<0) + (ADC_CHANNEL_SLIDER3<<5) + (ADC_CHANNEL_SLIDER4<<10) + (ADC_CHANNEL_BATT<<15); // conversions 7 and more + ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH<<0) + (ADC_CHANNEL_STICK_LV<<5) + (ADC_CHANNEL_STICK_RV<<10) + (ADC_CHANNEL_STICK_RH<<15) + (ADC_CHANNEL_POT2<<20) + (ADC_CHANNEL_POT3<<25); // conversions 1 to 6 +#elif defined(PCBXLITE) + if (STICKS_PWM_ENABLED()) { + ADC_MAIN->SQR2 = 0; + ADC_MAIN->SQR3 = (ADC_CHANNEL_POT1<<0) + (ADC_CHANNEL_POT2<<5) + (ADC_CHANNEL_BATT<<10); + } + else { + ADC_MAIN->SQR2 = (ADC_CHANNEL_BATT<<0); + ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH<<0) + (ADC_CHANNEL_STICK_LV<<5) + (ADC_CHANNEL_STICK_RV<<10) + (ADC_CHANNEL_STICK_RH<<15) + (ADC_CHANNEL_POT1<<20) + (ADC_CHANNEL_POT2<<25); // conversions 1 to 6 + } +#elif defined(PCBX7) + // TODO why do we invert POT1 and POT2 here? + ADC_MAIN->SQR2 = (ADC_CHANNEL_BATT<<0); // conversions 7 and more + ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH<<0) + (ADC_CHANNEL_STICK_LV<<5) + (ADC_CHANNEL_STICK_RV<<10) + (ADC_CHANNEL_STICK_RH<<15) + (ADC_CHANNEL_POT1<<25) + (ADC_CHANNEL_POT2<<20); // conversions 1 to 6 +#elif defined(PCBI8) + ADC_MAIN->SQR2 = (ADC_CHANNEL_SWE<<0) + (ADC_CHANNEL_SWF<<5) + (ADC_CHANNEL_LIBATT<<10) + (ADC_CHANNEL_DRYBATT<<15); // conversions 7 and more + ADC_MAIN->SQR3 = (ADC_CHANNEL_POT1<<0) + (ADC_CHANNEL_POT2<<5) + (ADC_CHANNEL_SWA<<10) + (ADC_CHANNEL_SWB<<15) + (ADC_CHANNEL_SWC<<20) + (ADC_CHANNEL_SWD<<25); // conversions 1 to 6 +#elif defined(PCBNV14) + ADC_MAIN->SQR1 |= (ADC_CHANNEL_BATT <<0 ); + ADC_MAIN->SQR2 = (ADC_CHANNEL_SWA << 0) + (ADC_CHANNEL_SWC << 5) + (ADC_CHANNEL_SWE << 10) + (ADC_CHANNEL_SWF << 15) + (ADC_CHANNEL_SWG << 20) + (ADC_CHANNEL_SWH << 25); // conversions 7 and more + ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH<<0) + (ADC_CHANNEL_STICK_LV<<5) + (ADC_CHANNEL_STICK_RV<<10) + (ADC_CHANNEL_STICK_RH<<15) + (ADC_CHANNEL_POT1<<20) + (ADC_CHANNEL_POT2<<25); // conversions 1 to 6 + ADC_SUB->SQR3 = (ADC_CHANNEL_SWB<<0) + (ADC_CHANNEL_SWD<<5); // conversions 1 to 2 + +#else + ADC_MAIN->SQR2 = (ADC_CHANNEL_POT3<<0) + (ADC_CHANNEL_SLIDER1<<5) + (ADC_CHANNEL_SLIDER2<<10) + (ADC_CHANNEL_BATT<<15); // conversions 7 and more + ADC_MAIN->SQR3 = (ADC_CHANNEL_STICK_LH<<0) + (ADC_CHANNEL_STICK_LV<<5) + (ADC_CHANNEL_STICK_RV<<10) + (ADC_CHANNEL_STICK_RH<<15) + (ADC_CHANNEL_POT1<<20) + (ADC_CHANNEL_POT2<<25); // conversions 1 to 6 +#endif + + ADC_MAIN->SMPR1 = ADC_SAMPTIME + (ADC_SAMPTIME<<3) + (ADC_SAMPTIME<<6) + (ADC_SAMPTIME<<9) + (ADC_SAMPTIME<<12) + (ADC_SAMPTIME<<15) + (ADC_SAMPTIME<<18) + (ADC_SAMPTIME<<21) + (ADC_SAMPTIME<<24); + ADC_MAIN->SMPR2 = ADC_SAMPTIME + (ADC_SAMPTIME<<3) + (ADC_SAMPTIME<<6) + (ADC_SAMPTIME<<9) + (ADC_SAMPTIME<<12) + (ADC_SAMPTIME<<15) + (ADC_SAMPTIME<<18) + (ADC_SAMPTIME<<21) + (ADC_SAMPTIME<<24) + (ADC_SAMPTIME<<27); + + ADC->CCR = 0; + + ADC_MAIN_DMA_Stream->CR = DMA_SxCR_PL | ADC_MAIN_DMA_SxCR_CHSEL | DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC; + ADC_MAIN_DMA_Stream->PAR = CONVERT_PTR_UINT(&ADC_MAIN->DR); + ADC_MAIN_DMA_Stream->M0AR = CONVERT_PTR_UINT(&adcValues[FIRST_ANALOG_ADC]); + ADC_MAIN_DMA_Stream->NDTR = NUM_MAIN_ANALOGS_ADC; + ADC_MAIN_DMA_Stream->FCR = DMA_SxFCR_DMDIS | DMA_SxFCR_FTH_0; + +#if defined(PCBNV14) + ADC_SUB->SMPR1 = ADC_SAMPTIME + (ADC_SAMPTIME<<3) + (ADC_SAMPTIME<<6) + (ADC_SAMPTIME<<9) + (ADC_SAMPTIME<<12) + (ADC_SAMPTIME<<15) + (ADC_SAMPTIME<<18) + (ADC_SAMPTIME<<21) + (ADC_SAMPTIME<<24); + ADC_SUB->SMPR2 = ADC_SAMPTIME + (ADC_SAMPTIME<<3) + (ADC_SAMPTIME<<6) + (ADC_SAMPTIME<<9) + (ADC_SAMPTIME<<12) + (ADC_SAMPTIME<<15) + (ADC_SAMPTIME<<18) + (ADC_SAMPTIME<<21) + (ADC_SAMPTIME<<24) + (ADC_SAMPTIME<<27); + + ADC->CCR = 0; + + ADC_SUB_DMA_Stream->CR = DMA_SxCR_PL | ADC_SUB_DMA_SxCR_CHSEL | DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC; + ADC_SUB_DMA_Stream->PAR = CONVERT_PTR_UINT(&ADC_SUB->DR); + ADC_SUB_DMA_Stream->M0AR = CONVERT_PTR_UINT(&adcValues[SUB_ANALOG_POS]); + ADC_SUB_DMA_Stream->NDTR = NUM_SUB_ANALOGS_ADC; + ADC_SUB_DMA_Stream->FCR = DMA_SxFCR_DMDIS | DMA_SxFCR_FTH_0; +#endif +#if defined(PCBX9E) + ADC_EXT->CR1 = ADC_CR1_SCAN; + ADC_EXT->CR2 = ADC_CR2_ADON | ADC_CR2_DMA | ADC_CR2_DDS; + ADC_EXT->SQR1 = (NUM_MAIN_ANALOGS_ADC_EXT-1) << 20; + ADC_EXT->SQR2 = 0; + ADC_EXT->SQR3 = (ADC_CHANNEL_POT1<<0) + (ADC_CHANNEL_SLIDER1<<5) + (ADC_CHANNEL_SLIDER2<<10); // conversions 1 to 3 + ADC_EXT->SMPR1 = 0; + ADC_EXT->SMPR2 = (ADC_EXT_SAMPTIME<<(3*ADC_CHANNEL_POT1)) + (ADC_EXT_SAMPTIME<<(3*ADC_CHANNEL_SLIDER1)) + (ADC_EXT_SAMPTIME<<(3*ADC_CHANNEL_SLIDER2)); + + ADC_EXT_DMA_Stream->CR = DMA_SxCR_PL | DMA_SxCR_CHSEL_1 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC; + ADC_EXT_DMA_Stream->PAR = CONVERT_PTR_UINT(&ADC_EXT->DR); + ADC_EXT_DMA_Stream->M0AR = CONVERT_PTR_UINT(adcValues + NUM_MAIN_ANALOGS_ADC); + ADC_EXT_DMA_Stream->NDTR = NUM_MAIN_ANALOGS_ADC_EXT; + ADC_EXT_DMA_Stream->FCR = DMA_SxFCR_DMDIS | DMA_SxFCR_FTH_0; +#endif + +#if NUM_PWMSTICKS > 0 + if (STICKS_PWM_ENABLED()) { + sticksPwmInit(); + } +#endif +} + +void adcSingleRead() +{ + ADC_MAIN_DMA_Stream->CR &= ~DMA_SxCR_EN; // Disable DMA + ADC_MAIN->SR &= ~(uint32_t)(ADC_SR_EOC | ADC_SR_STRT | ADC_SR_OVR); + ADC_MAIN_SET_DMA_FLAGS(); + ADC_MAIN_DMA_Stream->CR |= DMA_SxCR_EN; // Enable DMA + ADC_MAIN->CR2 |= (uint32_t) ADC_CR2_SWSTART; +#if defined(PCBNV14) + ADC_SUB_DMA_Stream->CR &= ~DMA_SxCR_EN; // Disable DMA + ADC_SUB->SR &= ~(uint32_t)(ADC_SR_EOC | ADC_SR_STRT | ADC_SR_OVR); + ADC_SUB_SET_DMA_FLAGS(); + ADC_SUB_DMA_Stream->CR |= DMA_SxCR_EN; // Enable DMA + ADC_SUB->CR2 |= (uint32_t) ADC_CR2_SWSTART; +#endif +#if defined(PCBX9E) + ADC_EXT_DMA_Stream->CR &= ~DMA_SxCR_EN; // Disable DMA + ADC_EXT->SR &= ~(uint32_t) ( ADC_SR_EOC | ADC_SR_STRT | ADC_SR_OVR ); + ADC_EXT_SET_DMA_FLAGS(); + ADC_EXT_DMA_Stream->CR |= DMA_SxCR_EN; // Enable DMA + ADC_EXT->CR2 |= (uint32_t)ADC_CR2_SWSTART; +#endif + +#if defined(PCBX9E) + for (unsigned int i=0; i<10000; i++) { + if (ADC_TRANSFER_COMPLETE() && ADC_EXT_TRANSFER_COMPLETE()) { + break; + } + } + ADC_DMA_Stream->CR &= ~DMA_SxCR_EN; // Disable DMA + ADC_EXT_DMA_Stream->CR &= ~DMA_SxCR_EN; // Disable DMA +#else + for (unsigned int i = 0; i < 10000; i++) { +#if defined(PCBNV14) + if (ADC_MAIN_TRANSFER_COMPLETE() && ADC_SUB_TRANSFER_COMPLETE()) + { + break; + } +#else + if (ADC_MAIN_TRANSFER_COMPLETE()) + { + break; + } +#endif + } + + ADC_MAIN_DMA_Stream->CR &= ~DMA_SxCR_EN; // Disable DMA +#if defined(PCBNV14) + ADC_SUB_DMA_Stream->CR &= ~DMA_SxCR_EN; // Disable DMA +#endif +#endif +} + +void adcRead() +{ + int i, j; + + uint16_t temp[NUM_ANALOGS] = { 0 }; + + for (i = 0; i < 4; i++) + { + adcSingleRead(); + + for (j = FIRST_ANALOG_ADC; j < NUM_ANALOGS; j++) + { + uint16_t val = adcValues[j]; +#if defined(JITTER_MEASURE) + if (JITTER_MEASURE_ACTIVE()) { + rawJitter[j].measure(val); + } +#endif + temp[j] += val; + } + } + + for (uint8_t x = FIRST_ANALOG_ADC; x < NUM_ANALOGS; x++) + { + adcValues[x] = temp[x] >> 2; + } + +#if NUM_PWMSTICKS > 0 + if (STICKS_PWM_ENABLED()) { + sticksPwmRead(adcValues); + } +#endif +} + +uint16_t getRTCBatteryVoltage() +{ + #warning "TODO RTC voltage" + return 330; +} + +#if !defined(SIMU) +uint16_t getAnalogValue(uint8_t index) +{ + if (IS_POT(index) && !IS_POT_SLIDER_AVAILABLE(index)) { + // Use fixed analog value for non-existing and/or non-connected pots. + // Non-connected analog inputs will slightly follow the adjacent connected analog inputs, + // which produces ghost readings on these inputs. + return 0; + } + +#if defined(ANAS_MAPPING) + index = ANAS_MAPPING[index]; +#endif + + if (ana_direction[index] < 0) + return 4095 - adcValues[index]; + else + return adcValues[index]; +} +#endif // #if !defined(SIMU) diff --git a/radio/src/targets/nv14/audio_spi_driver.cpp b/radio/src/targets/nv14/audio_spi_driver.cpp new file mode 100644 index 000000000..c23d666c4 --- /dev/null +++ b/radio/src/targets/nv14/audio_spi_driver.cpp @@ -0,0 +1,524 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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" + +#if !defined(SIMU) + +#define VS_WRITE_COMMAND 0x02 +#define VS_READ_COMMAND 0x03 + +#define SPI_MODE 0x00 +#define SPI_STATUS 0x01 +#define SPI_BASS 0x02 +#define SPI_CLOCKF 0x03 +#define SPI_DECODE_TIME 0x04 +#define SPI_AUDATA 0x05 +#define SPI_WRAM 0x06 +#define SPI_WRAMADDR 0x07 +#define SPI_HDAT0 0x08 +#define SPI_HDAT1 0x09 +#define SPI_AIADDR 0x0a +#define SPI_VOL 0x0b +#define SPI_AICTRL0 0x0c +#define SPI_AICTRL1 0x0d +#define SPI_AICTRL2 0x0e +#define SPI_AICTRL3 0x0f + +#define SM_DIFF 0x01 +#define SM_LAYER12 0x02 +#define SM_RESET 0x04 +#define SM_CANCEL 0x08 +#define SM_EARSPEAKER_LO 0x10 +#define SM_TESTS 0x20 +#define SM_STREAM 0x40 +#define SM_EARSPEAKER_HI 0x80 +#define SM_DACT 0x100 +#define SM_SDIORD 0x200 +#define SM_SDISHARE 0x400 +#define SM_SDINEW 0x800 +#define SM_ADPCM 0x1000 +#define SM_LINE1 0x4000 +#define SM_CLK_RANGE 0x8000 + +#define SPI_SPEED_2 0 +#define SPI_SPEED_4 1 +#define SPI_SPEED_8 2 +#define SPI_SPEED_16 3 +#define SPI_SPEED_32 4 +#define SPI_SPEED_64 5 +#define SPI_SPEED_128 6 +#define SPI_SPEED_256 7 + +#define MP3_BUFFER_SIZE 32 + +#define CS_HIGH() do { AUDIO_CS_GPIO->BSRRL = AUDIO_CS_GPIO_PIN; } while (0) +#define CS_LOW() do { AUDIO_CS_GPIO->BSRRH = AUDIO_CS_GPIO_PIN; } while (0) +#define XDCS_HIGH() do { AUDIO_XDCS_GPIO->BSRRL = AUDIO_XDCS_GPIO_PIN; } while (0) +#define XDCS_LOW() do { AUDIO_XDCS_GPIO->BSRRH = AUDIO_XDCS_GPIO_PIN; } while (0) +#define RST_HIGH() do { AUDIO_RST_GPIO->BSRRL = AUDIO_RST_GPIO_PIN; } while (0) +#define RST_LOW() do { AUDIO_RST_GPIO->BSRRH = AUDIO_RST_GPIO_PIN; } while (0) + +#define READ_DREQ() (GPIO_ReadInputDataBit(AUDIO_DREQ_GPIO, AUDIO_DREQ_GPIO_PIN)) + +void audioSpiInit(void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + SPI_InitTypeDef SPI_InitStructure; + + GPIO_InitStructure.GPIO_Pin = AUDIO_SPI_MISO_GPIO_PIN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(AUDIO_SPI_MISO_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = AUDIO_SPI_SCK_GPIO_PIN; + GPIO_Init(AUDIO_SPI_SCK_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = AUDIO_SPI_MOSI_GPIO_PIN; + GPIO_Init(AUDIO_SPI_MOSI_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = AUDIO_CS_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_Init(AUDIO_CS_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = AUDIO_XDCS_GPIO_PIN; + GPIO_Init(AUDIO_XDCS_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = AUDIO_RST_GPIO_PIN; + GPIO_Init(AUDIO_RST_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = AUDIO_DREQ_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; + GPIO_Init(AUDIO_DREQ_GPIO, &GPIO_InitStructure); + + GPIO_PinAFConfig(AUDIO_SPI_SCK_GPIO, AUDIO_SPI_SCK_GPIO_PinSource, AUDIO_SPI_GPIO_AF); + GPIO_PinAFConfig(AUDIO_SPI_MISO_GPIO, AUDIO_SPI_MISO_GPIO_PinSource, AUDIO_SPI_GPIO_AF); + GPIO_PinAFConfig(AUDIO_SPI_MOSI_GPIO, AUDIO_SPI_MOSI_GPIO_PinSource, AUDIO_SPI_GPIO_AF); + + RCC_ClocksTypeDef RCC_Clocks; + RCC_GetClocksFreq(&RCC_Clocks); + + SPI_I2S_DeInit(AUDIO_SPI); + SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; + SPI_InitStructure.SPI_Mode = SPI_Mode_Master; + SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; + SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; + SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; + SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; + SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; + SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; + SPI_InitStructure.SPI_CRCPolynomial = 7; + SPI_Init(AUDIO_SPI, &SPI_InitStructure); + SPI_Cmd(AUDIO_SPI, ENABLE); + + SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_RXNE); + SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_TXE); +} + +void audioSpiSetSpeed(uint8_t speed) +{ + AUDIO_SPI->CR1 &= 0xFFC7; // Fsck=Fcpu/256 + switch (speed) { + case SPI_SPEED_2: + AUDIO_SPI->CR1 |= 0x00 << 3; // Fsck=Fpclk/2=36Mhz + break; + case SPI_SPEED_4: + AUDIO_SPI->CR1 |= 0x01 << 3; // Fsck=Fpclk/4=18Mhz + break; + case SPI_SPEED_8: + AUDIO_SPI->CR1 |= 0x02 << 3; // Fsck=Fpclk/8=9Mhz + break; + case SPI_SPEED_16: + AUDIO_SPI->CR1 |= 0x03 << 3; // Fsck=Fpclk/16=4.5Mhz + break; + case SPI_SPEED_32: + AUDIO_SPI->CR1 |= 0x04 << 3; // Fsck=Fpclk/32=2.25Mhz + break; + case SPI_SPEED_64: + AUDIO_SPI->CR1 |= 0x05 << 3; // Fsck=Fpclk/16=1.125Mhz + break; + case SPI_SPEED_128: + AUDIO_SPI->CR1 |= 0x06 << 3; // Fsck=Fpclk/16=562.5Khz + break; + case SPI_SPEED_256: + AUDIO_SPI->CR1 |= 0x07 << 3; // Fsck=Fpclk/16=281.25Khz + break; + } + AUDIO_SPI->CR1 |= 0x01 << 6; + + //SPI_Cmd(AUDIO_SPI, ENABLE); +} + +uint8_t audioSpiReadWriteByte(uint8_t value) +{ + uint8_t ret = 0; + uint16_t time_out = 0x0FFF; + while (SPI_I2S_GetFlagStatus(AUDIO_SPI, SPI_I2S_FLAG_TXE) == RESET) { + if (--time_out == 0) { + // reset SPI + SPI_Cmd(AUDIO_SPI, DISABLE); + SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_OVR); + SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_BSY); + SPI_I2S_ClearFlag(AUDIO_SPI, I2S_FLAG_UDR); + SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_TIFRFE); + SPI_Cmd(AUDIO_SPI, ENABLE); + break; + } + } + SPI_I2S_SendData(AUDIO_SPI, value); + + time_out = 0x0FFF; + while (SPI_I2S_GetFlagStatus(AUDIO_SPI, SPI_I2S_FLAG_RXNE) == RESET) { + if (--time_out == 0) { + // reset SPI + SPI_Cmd(AUDIO_SPI, DISABLE); + SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_OVR); + SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_BSY); + SPI_I2S_ClearFlag(AUDIO_SPI, I2S_FLAG_UDR); + SPI_I2S_ClearFlag(AUDIO_SPI, SPI_I2S_FLAG_TIFRFE); + SPI_Cmd(AUDIO_SPI, ENABLE); + break; + } + } + + ret = SPI_I2S_ReceiveData(AUDIO_SPI); + + return ret; +} + +uint8_t audioWaitDreq(int32_t delay_us) +{ + while (READ_DREQ() == 0) { + if (delay_us-- == 0) return 0; + delay_01us(10); + } + return 1; +} + +uint16_t audioSpiReadReg(uint8_t address) +{ + if (!audioWaitDreq(100)) + return 0; + + audioSpiSetSpeed(SPI_SPEED_64); + XDCS_HIGH(); + CS_LOW(); + audioSpiReadWriteByte(VS_READ_COMMAND); + audioSpiReadWriteByte(address); + uint16_t result = audioSpiReadWriteByte(0xff) << 8; + result += audioSpiReadWriteByte(0xff); + delay_01us(100); // 10us + CS_HIGH(); + audioSpiSetSpeed(SPI_SPEED_8); + return result; +} + +uint16_t audioSpiReadCmd(uint8_t address) +{ + if (!audioWaitDreq(100)) + return 0; + + audioSpiSetSpeed(SPI_SPEED_64); + XDCS_HIGH(); + CS_LOW(); + audioSpiReadWriteByte(VS_READ_COMMAND); + audioSpiReadWriteByte(address); + uint16_t result = audioSpiReadWriteByte(0) << 8; + result |= audioSpiReadWriteByte(0); + delay_01us(50); // 5us + CS_HIGH(); + audioSpiSetSpeed(SPI_SPEED_8); + return result; +} + +uint8_t audioSpiWriteCmd(uint8_t address, uint16_t data) +{ + if (!audioWaitDreq(100)) + return 0; + + audioSpiSetSpeed(SPI_SPEED_64); + XDCS_HIGH(); + CS_LOW(); + audioSpiReadWriteByte(VS_WRITE_COMMAND); + audioSpiReadWriteByte(address); + audioSpiReadWriteByte(data >> 8); + audioSpiReadWriteByte(data); + delay_01us(50); // 5us + CS_HIGH(); + audioSpiSetSpeed(SPI_SPEED_8); + return 1; +} + +void audioResetDecodeTime(void) +{ + audioSpiWriteCmd(SPI_DECODE_TIME, 0x0000); + audioSpiWriteCmd(SPI_DECODE_TIME, 0x0000); +} + +uint8_t audioHardReset(void) +{ + XDCS_HIGH(); + CS_HIGH(); + RST_LOW(); + delay_ms(100); // 100ms + RST_HIGH(); + + if (!audioWaitDreq(100)) + return 0; + + delay_ms(20); // 20ms + return 1; +} + +uint8_t audioSoftReset(void) +{ + audioSpiSetSpeed(SPI_SPEED_64); + if (!audioWaitDreq(2000)) + { + return 0; + } + + audioSpiReadWriteByte(0Xff); + + uint8_t retry = 0; + while (audioSpiReadReg(SPI_MODE) != 0x0800 && retry < 100) { + retry++; + audioSpiWriteCmd(SPI_MODE, 0x0804); + } + + // wait for set up successful + retry = 0; + while (audioSpiReadReg(SPI_CLOCKF) != 0x9800 && retry < 100) { + retry++; + audioSpiWriteCmd(SPI_CLOCKF, 0x9800); + } + + audioResetDecodeTime(); // reset the decoding time + audioSpiSetSpeed(SPI_SPEED_8); + XDCS_LOW(); + audioSpiReadWriteByte(0X0); + audioSpiReadWriteByte(0X0); + audioSpiReadWriteByte(0X0); + audioSpiReadWriteByte(0X0); + delay_01us(100); // 10us + XDCS_HIGH(); + + return 1; +} + +//正弦测试 +void Audio_Sine_Test(void) +{ + audioSpiWriteCmd(0x0b,0X2020); + audioSpiWriteCmd(SPI_MODE,0x0820); + while(!audioWaitDreq(100)); + //printf("mode sin:%x\n",VS_RD_Reg(SPI_MODE)); + + audioSpiSetSpeed(SPI_SPEED_64); + XDCS_LOW(); + audioSpiReadWriteByte(0x53); + audioSpiReadWriteByte(0xef); + audioSpiReadWriteByte(0x6e); + audioSpiReadWriteByte(0x24); + audioSpiReadWriteByte(0x00); + audioSpiReadWriteByte(0x00); + audioSpiReadWriteByte(0x00); + audioSpiReadWriteByte(0x00); + delay_ms(100); + XDCS_HIGH(); + + XDCS_LOW(); + audioSpiReadWriteByte(0x45); + audioSpiReadWriteByte(0x78); + audioSpiReadWriteByte(0x69); + audioSpiReadWriteByte(0x74); + audioSpiReadWriteByte(0x00); + audioSpiReadWriteByte(0x00); + audioSpiReadWriteByte(0x00); + audioSpiReadWriteByte(0x00); + delay_ms(100); + XDCS_HIGH(); + + + XDCS_LOW(); + audioSpiReadWriteByte(0x53); + audioSpiReadWriteByte(0xef); + audioSpiReadWriteByte(0x6e); + audioSpiReadWriteByte(0x44); + audioSpiReadWriteByte(0x00); + audioSpiReadWriteByte(0x00); + audioSpiReadWriteByte(0x00); + audioSpiReadWriteByte(0x00); + delay_ms(100); + XDCS_HIGH(); + + XDCS_LOW(); + audioSpiReadWriteByte(0x45); + audioSpiReadWriteByte(0x78); + audioSpiReadWriteByte(0x69); + audioSpiReadWriteByte(0x74); + audioSpiReadWriteByte(0x00); + audioSpiReadWriteByte(0x00); + audioSpiReadWriteByte(0x00); + audioSpiReadWriteByte(0x00); + delay_ms(100); + XDCS_HIGH(); +} + +uint32_t audioSpiWriteData(const uint8_t * buffer, uint32_t size) +{ + int i = 55; + + XDCS_LOW(); + + if (58 == i) + { + TRACE("\nbuffer = %d \r\n)", buffer); + } + + uint32_t index = 0; + while (index < size && READ_DREQ() != 0) { + for (int i=0; i 0) { + uint32_t written = audioSpiWriteData(p, size); + p += written; + size -= written; + } +} + +const uint8_t RiffHeader[] = { + 0x52, 0x49, 0x46, 0x46, 0xff, 0xff, 0xff, 0xff, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20, + 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, + 0x02, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +void audioSendRiffHeader() +{ + audioSpiWriteBuffer(RiffHeader, sizeof(RiffHeader)); +} + +#if defined(PCBNV14) +void audioShutdownInit() +{ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = AUDIO_SHUTDOWN_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_Init(AUDIO_SHUTDOWN_GPIO, &GPIO_InitStructure); + GPIO_SetBits(AUDIO_SHUTDOWN_GPIO, AUDIO_SHUTDOWN_GPIO_PIN); // we never RESET it, there is a 2s delay on STARTUP +} +#endif + +void audioInit() +{ +#if defined(PCBNV14) + audioShutdownInit(); + // TODO X10 code missing +#endif + + audioSpiInit(); +} + +uint8_t * currentBuffer = NULL; +uint32_t currentSize = 0; +int16_t newVolume = -1; + +void audioSetCurrentBuffer(const AudioBuffer * buffer) +{ + if (buffer) { + currentBuffer = (uint8_t *)buffer->data; + currentSize = buffer->size * 2; + } + else { + currentBuffer = NULL; + currentSize = 0; + } +} + +void audioConsumeCurrentBuffer() +{ + if (newVolume >= 0) { + uint8_t value = newVolume; + audioSpiWriteCmd(SPI_VOL, (value << 8) + value); + audioSendRiffHeader(); + newVolume = -1; + } + + if (!currentBuffer) { + audioSetCurrentBuffer(audioQueue.buffersFifo.getNextFilledBuffer()); + } + + if (currentBuffer) { + uint32_t written = audioSpiWriteData(currentBuffer, currentSize); + currentBuffer += written; + currentSize -= written; + if (currentSize == 0) { + audioQueue.buffersFifo.freeNextFilledBuffer(); + currentBuffer = NULL; + currentSize = 0; + } + } +} + +// adjust this value for a volume level just above the silence +// values is attenuation in dB, higher value - less volume +// max value is 126 +#define VOLUME_MIN_DB 40 + +void setScaledVolume(uint8_t volume) +{ + if (volume > VOLUME_LEVEL_MAX) { + volume = VOLUME_LEVEL_MAX; + } + // maximum volume is 0x00 and total silence is 0xFE + if (volume == 0) { + setVolume(0xFE); // silence + } + else { + uint32_t vol = (VOLUME_MIN_DB * 2) - ((uint32_t)volume * (VOLUME_MIN_DB * 2)) / VOLUME_LEVEL_MAX; + setVolume(vol); + } +} + +void setVolume(uint8_t volume) +{ + newVolume = volume; +} + +int32_t getVolume() +{ + return -1; // TODO +} + +#endif diff --git a/radio/src/targets/nv14/backlight_driver.cpp b/radio/src/targets/nv14/backlight_driver.cpp new file mode 100644 index 000000000..2aa6af0ef --- /dev/null +++ b/radio/src/targets/nv14/backlight_driver.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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" + +void backlightInit() +{ + // PIN init + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = BACKLIGHT_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(BACKLIGHT_GPIO, &GPIO_InitStructure); + GPIO_PinAFConfig(BACKLIGHT_GPIO, BACKLIGHT_GPIO_PinSource, BACKLIGHT_GPIO_AF); + + // TODO review this when the timer will be chosen + BACKLIGHT_TIMER->ARR = 100; + BACKLIGHT_TIMER->PSC = BACKLIGHT_TIMER_FREQ / 1000000 - 1; // 10kHz (same as FrOS) + BACKLIGHT_TIMER->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1PE; // PWM mode 1 + BACKLIGHT_TIMER->CCER = TIM_CCER_CC1E | TIM_CCER_CC1NE; + BACKLIGHT_TIMER->CCR1 = 100; // 100% on init + BACKLIGHT_TIMER->EGR = TIM_EGR_UG; + BACKLIGHT_TIMER->CR1 |= TIM_CR1_CEN; // Counter enable + BACKLIGHT_TIMER->BDTR |= TIM_BDTR_MOE; +} + +void backlightEnable(uint8_t dutyCycle) +{ + BACKLIGHT_TIMER->CCR1 = dutyCycle; +} diff --git a/radio/src/targets/nv14/battery_driver.cpp b/radio/src/targets/nv14/battery_driver.cpp new file mode 100644 index 000000000..42050aabb --- /dev/null +++ b/radio/src/targets/nv14/battery_driver.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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" + +#define __BATTERY_DRIVER_C__ + + +void init_battery_charge() +{ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = PWR_CHARGE_FINISHED_GPIO_PIN | PWR_CHARGING_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_Init(PWR_CHARGING_GPIO, &GPIO_InitStructure); + GPIO_SetBits(PWR_CHARGING_GPIO, PWR_CHARGE_FINISHED_GPIO_PIN | PWR_CHARGING_GPIO_PIN); +} + +uint16_t get_battery_charge_state() +{ + if (!READ_CHARGE_FINISHED_STATE()) + { + return CHARGE_FINISHED; + } + else if (!READ_CHARGING_STATE()) + { + return CHARGE_STARTED; + } + + return CHARGE_NONE; +} + + +uint16_t getBatteryVoltage() +{ +#if defined (SIMU) + return 350 + g_eeGeneral.txVoltageCalibration; +#else + int32_t t = getAnalogValue(TX_VOLTAGE) + g_eeGeneral.txVoltageCalibration; + + t *= ( 3300 * 20); + t /= ( 4095 * 10); + t = ( t * 100 + 500 ) / 1000; + + return t; +#endif +} + + + + + + + + + + + + + + + + + + + diff --git a/radio/src/targets/nv14/battery_driver.h b/radio/src/targets/nv14/battery_driver.h new file mode 100644 index 000000000..3e2c6c6f3 --- /dev/null +++ b/radio/src/targets/nv14/battery_driver.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +/*************************************************************************************************** + +***************************************************************************************************/ +#ifndef __BATTERY_DRIVER_H__ + #define __BATTERY_DRIVER_H__ +/*************************************************************************************************** + +***************************************************************************************************/ + +#include "board.h" + +enum ChargeState +{ + CHARGE_NONE, + CHARGE_STARTED, + CHARGE_FINISHED +}; + + +#define PWR_CHARGE_FINISHED_GPIO GPIOB +#define PWR_CHARGE_FINISHED_GPIO_REG PWR_CHARGE_FINISHED_GPIO->IDR +#define PWR_CHARGE_FINISHED_GPIO_PIN GPIO_Pin_13 // PB.13 + +#define PWR_CHARGING_GPIO GPIOB +#define PWR_CHARGING_GPIO_REG PWR_CHARGING_GPIO->IDR +#define PWR_CHARGING_GPIO_PIN GPIO_Pin_14 // PB.14 + +#define READ_CHARGE_FINISHED_STATE() GPIO_ReadInputDataBit( PWR_CHARGE_FINISHED_GPIO, PWR_CHARGE_FINISHED_GPIO_PIN ) +#define READ_CHARGING_STATE() GPIO_ReadInputDataBit( PWR_CHARGING_GPIO, PWR_CHARGING_GPIO_PIN ) + +extern void init_battery_charge(); +extern uint16_t get_battery_charge_state(); +extern uint16_t getBatteryVoltage(); // returns current battery voltage in 10mV steps + +#endif + + + + + + + + + + + + + + + + + diff --git a/radio/src/targets/nv14/board.cpp b/radio/src/targets/nv14/board.cpp new file mode 100644 index 000000000..0718bb489 --- /dev/null +++ b/radio/src/targets/nv14/board.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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" +#if defined(__cplusplus) && !defined(SIMU) +extern "C" { +#endif +#include "usb_dcd_int.h" +#include "usb_bsp.h" +#if defined(__cplusplus) && !defined(SIMU) +} +#endif + +HardwareOptions hardwareOptions; + +void watchdogInit(unsigned int duration) +{ + IWDG->KR = 0x5555; // Unlock registers + IWDG->PR = 3; // Divide by 32 => 1kHz clock + IWDG->KR = 0x5555; // Unlock registers + IWDG->RLR = duration; // 1.5 seconds nominal + IWDG->KR = 0xAAAA; // reload + IWDG->KR = 0xCCCC; // start +} + +// Start TIMER7 at 2000000Hz +void init2MhzTimer() +{ + TIMER_2MHz_TIMER->PSC = (PERI1_FREQUENCY * TIMER_MULT_APB1) / 2000000 - 1; // 0.5 uS, 2 MHz + TIMER_2MHz_TIMER->ARR = 65535; + TIMER_2MHz_TIMER->CR2 = 0; + TIMER_2MHz_TIMER->CR1 = TIM_CR1_CEN; +} + +// Starts TIMER at 1000Hz +void init1msTimer() +{ + INTERRUPT_xMS_TIMER->ARR = 999; // 1mS in uS + INTERRUPT_xMS_TIMER->PSC = (PERI1_FREQUENCY * TIMER_MULT_APB1) / 1000000 - 1; // 1uS + INTERRUPT_xMS_TIMER->CCER = 0; + INTERRUPT_xMS_TIMER->CCMR1 = 0; + INTERRUPT_xMS_TIMER->EGR = 0; + INTERRUPT_xMS_TIMER->CR1 = 5; + INTERRUPT_xMS_TIMER->DIER |= 1; + NVIC_EnableIRQ(INTERRUPT_xMS_IRQn); + NVIC_SetPriority(INTERRUPT_xMS_IRQn, 7); +} + +// TODO use the same than board_sky9x.cpp +void interrupt1ms() +{ + static uint8_t pre_scale; // Used to get 10 Hz counter + + ++pre_scale; + +#if defined(DEBUG) && !defined(SIMU) + debugCounter1ms++; +#endif + +#if defined(HAPTIC) + if (pre_scale == 5 || pre_scale == 10) { + DEBUG_TIMER_START(debugTimerHaptic); + HAPTIC_HEARTBEAT(); + DEBUG_TIMER_STOP(debugTimerHaptic); + } +#endif + + if (pre_scale == 10) { + pre_scale = 0; +#if !defined(SIMU) + TouchDriver(); + hall_stick_loop(); +#endif + DEBUG_TIMER_START(debugTimerPer10ms); + DEBUG_TIMER_SAMPLE(debugTimerPer10msPeriod); + per10ms(); + DEBUG_TIMER_STOP(debugTimerPer10ms); + } + + DEBUG_TIMER_START(debugTimerRotEnc); + DEBUG_TIMER_STOP(debugTimerRotEnc); +} + +extern "C" void INTERRUPT_xMS_IRQHandler() +{ + INTERRUPT_xMS_TIMER->SR &= ~TIM_SR_UIF; + interrupt1ms(); + DEBUG_INTERRUPT(INT_1MS); +} + +#if defined(SEMIHOSTING) +extern "C" void initialise_monitor_handles(); +#endif + +void boardInit() +{ +#if defined(SEMIHOSTING) + initialise_monitor_handles(); +#endif + +#if !defined(SIMU) + RCC_AHB1PeriphClockCmd(PWR_RCC_AHB1Periph | + LCD_RCC_AHB1Periph | + BACKLIGHT_RCC_AHB1Periph | + SD_RCC_AHB1Periph | + SDRAM_RCC_AHB1Periph | + AUDIO_RCC_AHB1Periph | + MONITOR_RCC_AHB1Periph | + KEYS_RCC_AHB1Periph | + ADC_RCC_AHB1Periph | + AUX_SERIAL_RCC_AHB1Periph | + TELEMETRY_RCC_AHB1Periph | + TRAINER_RCC_AHB1Periph | + AUDIO_RCC_AHB1Periph | + HAPTIC_RCC_AHB1Periph | + INTMODULE_RCC_AHB1Periph | + HALL_RCC_AHB1Periph | + EXTMODULE_RCC_AHB1Periph, + ENABLE); + + RCC_AHB3PeriphClockCmd(SDRAM_RCC_AHB3Periph, + ENABLE); + + RCC_APB1PeriphClockCmd(INTERRUPT_xMS_RCC_APB1Periph | + TIMER_2MHz_RCC_APB1Periph | + TELEMETRY_RCC_APB1Periph | + TRAINER_RCC_APB1Periph | + INTMODULE_RCC_APB1Periph | + HALL_RCC_APB1Periph | + EXTMODULE_RCC_APB1Periph | + BACKLIGHT_RCC_APB1Periph, + ENABLE); + + RCC_APB2PeriphClockCmd(LCD_RCC_APB2Periph | + ADC_RCC_APB2Periph | + HAPTIC_RCC_APB2Periph | + AUX_SERIAL_RCC_APB2Periph | + INTMODULE_RCC_APB2Periph | + AUDIO_RCC_APB2Periph | + EXTMODULE_RCC_APB2Periph, + ENABLE); + + pwrInit(); + +#if defined(DEBUG) + auxSerialInit(0, 0); // default serial mode (None if DEBUG nm ot defined) +#endif + + __enable_irq(); + + TRACE("\nNV14 board started :)"); + delay_ms(10); + TRACE("RCC->CSR = %08x", RCC->CSR); + audioInit(); + + // we need to initialize g_FATFS_Obj here, because it is in .ram section (because of DMA access) + // and this section is un-initialized + memset(&g_FATFS_Obj, 0, sizeof(g_FATFS_Obj)); + + init_battery_charge(); + monitorInit(); + keysInit(); + adcInit(); + backlightInit(); + lcdInit(); +#if defined(FLYSKY_HALL_STICKS) + hall_stick_init(FLYSKY_HALL_BAUDRATE); +#endif + + + + init2MhzTimer(); + init1msTimer(); + usbInit(); + hapticInit(); + + TouchInit(); + + +#if defined(DEBUG) + DBGMCU_APB1PeriphConfig(DBGMCU_IWDG_STOP|DBGMCU_TIM1_STOP|DBGMCU_TIM2_STOP|DBGMCU_TIM3_STOP|DBGMCU_TIM4_STOP|DBGMCU_TIM5_STOP|DBGMCU_TIM6_STOP|DBGMCU_TIM7_STOP|DBGMCU_TIM8_STOP|DBGMCU_TIM9_STOP|DBGMCU_TIM10_STOP|DBGMCU_TIM11_STOP|DBGMCU_TIM12_STOP|DBGMCU_TIM13_STOP|DBGMCU_TIM14_STOP, ENABLE); +#endif + +#endif +} + +void boardOff() +{ + BACKLIGHT_DISABLE(); + + while (pwrPressed()) { + wdt_reset(); + } + + SysTick->CTRL = 0; // turn off systick + pwrOff(); +} + +void init_intmodule_heartbeat() +{ + +} \ No newline at end of file diff --git a/radio/src/targets/nv14/board.h b/radio/src/targets/nv14/board.h new file mode 100644 index 000000000..e9a73d3b4 --- /dev/null +++ b/radio/src/targets/nv14/board.h @@ -0,0 +1,490 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _BOARD_H_ +#define _BOARD_H_ + +#include "../definitions.h" +#include "../opentx_constants.h" +#include "board_common.h" +#include "hal.h" + +#if !defined(LUA_EXPORT_GENERATION) +#include "STM32F4xx_DSP_StdPeriph_Lib_V1.4.0/Libraries/STM32F4xx_StdPeriph_Driver/inc/stm32f4xx_sdio.h" +#include "STM32F4xx_DSP_StdPeriph_Lib_V1.4.0/Libraries/STM32F4xx_StdPeriph_Driver/inc/stm32f4xx_dma2d.h" +#include "STM32F4xx_DSP_StdPeriph_Lib_V1.4.0/Libraries/STM32F4xx_StdPeriph_Driver/inc/stm32f4xx_ltdc.h" +#include "STM32F4xx_DSP_StdPeriph_Lib_V1.4.0/Libraries/STM32F4xx_StdPeriph_Driver/inc/stm32f4xx_fmc.h" +#endif + +#include "touch_driver.h" +#include "hallStick_driver.h" +#include "lcd_driver.h" +#include "battery_driver.h" + +#define FLASHSIZE 0x200000 +#define BOOTLOADER_SIZE 0x20000 +#define FIRMWARE_ADDRESS 0x08000000 + +#define MB *1024*1024 +#define LUA_MEM_EXTRA_MAX (2 MB) // max allowed memory usage for Lua bitmaps (in bytes) +#define LUA_MEM_MAX (6 MB) // max allowed memory usage for complete Lua (in bytes), 0 means unlimited + +// HSI is at 168Mhz (over-drive is not enabled!) +#define PERI1_FREQUENCY 42000000 +#define PERI2_FREQUENCY 84000000 +#define TIMER_MULT_APB1 2 +#define TIMER_MULT_APB2 2 + +extern uint16_t sessionTimer; + +#define SLAVE_MODE() (g_model.trainerMode == TRAINER_MODE_SLAVE) +#define TRAINER_CONNECTED() (GPIO_ReadInputDataBit(TRAINER_DETECT_GPIO, TRAINER_DETECT_GPIO_PIN) == Bit_SET) + +PACK(typedef struct { + uint8_t pxx2Enabled:1; +}) HardwareOptions; + +extern HardwareOptions hardwareOptions; + +// Board driver +void boardInit(); +void boardOff(); + +// Timers driver +void init2MhzTimer(); +void init5msTimer(); + +// Delays driver +#ifdef __cplusplus +extern "C" { +#endif +void delaysInit(); +void delay_01us(uint16_t nb); +void delay_us(uint16_t nb); +void delay_ms(uint32_t ms); +#ifdef __cplusplus +} +#endif + +// CPU Unique ID +#define LEN_CPU_UID (3*8+2) +void getCPUUniqueID(char * s); + +// SD driver +#define BLOCK_SIZE 512 /* Block Size in Bytes */ +#if !defined(SIMU) || defined(SIMU_DISKIO) +uint32_t sdIsHC(); +uint32_t sdGetSpeed(); +#define SD_IS_HC() (sdIsHC()) +#define SD_GET_SPEED() (sdGetSpeed()) +#define SD_GET_FREE_BLOCKNR() (sdGetFreeSectors()) +#define SD_CARD_PRESENT() (true) +void sdInit(); +void sdMount(); +void sdDone(); +#define sdPoll10ms() +uint32_t sdMounted(); +#else +#define SD_IS_HC() (0) +#define SD_GET_SPEED() (0) +#define sdInit() +#define sdMount() +#define sdDone() +#define SD_CARD_PRESENT() true +#endif + +#if defined(DISK_CACHE) +#include "diskio.h" +DRESULT __disk_read(BYTE drv, BYTE * buff, DWORD sector, UINT count); +DRESULT __disk_write(BYTE drv, const BYTE * buff, DWORD sector, UINT count); +#else +#define __disk_read disk_read +#define __disk_write disk_write +#endif + +// Flash Write driver +#define FLASH_PAGESIZE 256 +void unlockFlash(); +void lockFlash(); +void flashWrite(uint32_t * address, uint32_t * buffer); +uint32_t isFirmwareStart(const uint8_t * buffer); +uint32_t isBootloaderStart(const uint8_t * buffer); + +// SDRAM driver +void SDRAM_Init(); + +// Pulses driver +#define INTERNAL_MODULE_OFF() GPIO_SetBits(INTMODULE_PWR_GPIO, INTMODULE_PWR_GPIO_PIN) +#define INTERNAL_MODULE_ON() GPIO_ResetBits(INTMODULE_PWR_GPIO, INTMODULE_PWR_GPIO_PIN) +#define EXTERNAL_MODULE_ON() GPIO_SetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN) +#define EXTERNAL_MODULE_OFF() GPIO_ResetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN) +#define BLUETOOTH_MODULE_ON() GPIO_ResetBits(BLUETOOTH_ON_GPIO, BLUETOOTH_ON_GPIO_PIN) +#define BLUETOOTH_MODULE_OFF() GPIO_SetBits(BLUETOOTH_ON_GPIO, BLUETOOTH_ON_GPIO_PIN) +#define IS_INTERNAL_MODULE_ON() (GPIO_ReadInputDataBit(INTMODULE_PWR_GPIO, INTMODULE_PWR_GPIO_PIN) == Bit_SET) +#define IS_EXTERNAL_MODULE_ON() (GPIO_ReadInputDataBit(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN) == Bit_SET) +#define IS_UART_MODULE(port) (port == INTERNAL_MODULE) +#define IS_PXX2_INTERNAL_ENABLED() (false) + +void init_intmodule_heartbeat(); +void check_intmodule_heartbeat(); + +void intmoduleSerialStart(uint32_t baudrate, uint8_t rxEnable); +void intmoduleSendBuffer(const uint8_t * data, uint8_t size); +void intmoduleSendNextFrame(); + +void extmoduleSerialStart(uint32_t baudrate, uint32_t period_half_us, bool inverted); +void extmoduleSendNextFrame(); + +// Trainer driver +void init_trainer_ppm(); +void stop_trainer_ppm(); +void init_trainer_capture(); +void stop_trainer_capture(); + +// Keys driver +enum EnumKeys +{ + TRM_BASE, + TRM_LH_DWN = TRM_BASE, + TRM_LH_UP, + TRM_LV_DWN, + TRM_LV_UP, + TRM_RV_DWN, + TRM_RV_UP, + TRM_RH_DWN, + TRM_RH_UP, + TRM_LS_DWN, + TRM_LS_UP, + TRM_RS_DWN, + TRM_RS_UP, + TRM_LAST = TRM_RS_UP, + NUM_KEYS +}; + +#define IS_SHIFT_KEY(index) (false) +#define IS_SHIFT_PRESSED() (false)&é&é&é"" + +enum EnumSwitches +{ + SW_SA, + SW_SB, + SW_SC, + SW_SD, + SW_SE, + SW_SF, + SW_SG, + SW_SH, + NUM_SWITCHES +}; + +#define STORAGE_NUM_SWITCHES NUM_SWITCHES +#define DEFAULT_SWITCH_CONFIG (SWITCH_TOGGLE << 14) + (SWITCH_3POS << 12) + (SWITCH_3POS << 10) + (SWITCH_TOGGLE << 8) + (SWITCH_2POS << 6) + (SWITCH_TOGGLE << 4) + (SWITCH_3POS << 2) + (SWITCH_2POS << 0); + +enum EnumSwitchesPositions +{ + SW_SA0, + SW_SA1, + SW_SA2, + SW_SB0, + SW_SB1, + SW_SB2, + SW_SC0, + SW_SC1, + SW_SC2, + SW_SD0, + SW_SD1, + SW_SD2, + SW_SE0, + SW_SE1, + SW_SE2, + SW_SF0, + SW_SF1, + SW_SF2, + SW_SG0, + SW_SG1, + SW_SG2, + SW_SH0, + SW_SH1, + SW_SH2, + NUM_SWITCHES_POSITIONS +}; + +#define STORAGE_NUM_SWITCHES_POSITIONS (STORAGE_NUM_SWITCHES * 3) + +void monitorInit(); +void keysInit(); +uint8_t keyState(uint8_t index); +uint32_t switchState(uint8_t index); +uint32_t readKeys(); +uint32_t readTrims(); +#define NUM_TRIMS NUM_STICKS +#define TRIMS_PRESSED() (readTrims()) +#define KEYS_PRESSED() (readKeys()) +#define DBLKEYS_PRESSED_RGT_LFT(in) (false) +#define DBLKEYS_PRESSED_UP_DWN(in) (false) +#define DBLKEYS_PRESSED_RGT_UP(in) (false) +#define DBLKEYS_PRESSED_LFT_DWN(in) (false) + +// WDT driver +#define WDTO_500MS 500 +extern uint32_t powerupReason; + +#define SHUTDOWN_REQUEST 0xDEADBEEF +#define NO_SHUTDOWN_REQUEST ~SHUTDOWN_REQUEST +#define DIRTY_SHUTDOWN 0xCAFEDEAD +#define NORMAL_POWER_OFF ~DIRTY_SHUTDOWN + +#define wdt_disable() +void watchdogInit(unsigned int duration); +#if defined(SIMU) + #define WAS_RESET_BY_WATCHDOG() (false) + #define WAS_RESET_BY_SOFTWARE() (false) + #define WAS_RESET_BY_WATCHDOG_OR_SOFTWARE() (false) + #define wdt_enable(x) + #define wdt_reset() +#else + #if defined(WATCHDOG_DISABLED) + #define wdt_enable(x) + #define wdt_reset() + #else + #define wdt_enable(x) watchdogInit(x) + #define wdt_reset() IWDG->KR = 0xAAAA + #endif + #define WAS_RESET_BY_WATCHDOG() (RCC->CSR & (RCC_CSR_WDGRSTF | RCC_CSR_WWDGRSTF)) + #define WAS_RESET_BY_SOFTWARE() (RCC->CSR & RCC_CSR_SFTRSTF) + #define WAS_RESET_BY_WATCHDOG_OR_SOFTWARE() (RCC->CSR & (RCC_CSR_WDGRSTF | RCC_CSR_WWDGRSTF | RCC_CSR_SFTRSTF)) +#endif + +// ADC driver +#define NUM_POTS 2 +#define NUM_XPOTS 0 // NUM_POTS +#define NUM_SLIDERS 0 +#define NUM_PWMSTICKS 0 +#define NUM_MOUSE_ANALOGS 0 +#define STORAGE_NUM_POTS 5 +#define STORAGE_NUM_SLIDERS 0 +#define STORAGE_NUM_MOUSE_ANALOGS 0 + +enum Analogs { + STICK1, + STICK2, + STICK3, + STICK4, + POT_FIRST, + POT1 = POT_FIRST, + POT2, + POT_LAST = POT2, + SWITCH_FIRST, + SWA = SWITCH_FIRST, + SWB, + SWC, + SWD, + SWE, + SWF, + SWG, + SWH, + SUB_ANALOG_POS = SWH, + SWITCH_END = SWH, + TX_VOLTAGE, + NUM_ANALOGS +}; + +#define DEFAULT_POTS_CONFIG (POT_WITHOUT_DETENT << 0) + (POT_WITHOUT_DETENT << 2) // 2 pots without detent + +enum CalibratedAnalogs { + CALIBRATED_STICK1, + CALIBRATED_STICK2, + CALIBRATED_STICK3, + CALIBRATED_STICK4, + CALIBRATED_POT1, + CALIBRATED_POT2, + CALIBRATED_SWA, + CALIBRATED_SWB, + CALIBRATED_SWC, + CALIBRATED_SWD, + CALIBRATED_SWE, + CALIBRATED_SWF, + CALIBRATED_SWG, + CALIBRATED_SWH, + NUM_CALIBRATED_ANALOGS +}; + +#define IS_POT(x) ((x)>=POT_FIRST && (x)<=POT_LAST) +#define IS_SLIDER(x) (false) + +extern uint16_t adcValues[NUM_ANALOGS]; + +void adcInit(); +void adcRead(); +uint16_t getAnalogValue(uint8_t index); +uint16_t getBatteryVoltage(); // returns current battery voltage in 10mV steps + +#define BATTERY_WARN 37 // 3.7V +#define BATTERY_MIN 35 // 3.5V +#define BATTERY_MAX 43 // 4.3V + +#if defined(__cplusplus) && !defined(SIMU) +extern "C" { +#endif + +// Power driver +#define SOFT_PWR_CTRL +void pwrInit(); +uint32_t pwrCheck(); +#if defined(PCBFLYSKY) +uint32_t lowPowerCheck(); +#endif +void pwrOn(); +void pwrOff(); +void pwrResetHandler(); +bool pwrPressed(); +uint32_t pwrPressedDuration(); +#if defined(SIMU) || defined(NO_UNEXPECTED_SHUTDOWN) + #define UNEXPECTED_SHUTDOWN() (false) +#else + #define UNEXPECTED_SHUTDOWN() ((powerupReason == DIRTY_SHUTDOWN) || WAS_RESET_BY_WATCHDOG_OR_SOFTWARE()) +#endif + +// LCD driver +#define LCD_W 320 +#define LCD_H 480 +#define LCD_DEPTH 16 +#define LCD_CONTRAST_DEFAULT 20 +void lcdInit(); +void lcdNextLayer(); +void lcdRefresh(); +void lcdCopy(void * dest, void * src); +void DMAFillRect(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color); +void DMACopyBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); +void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); +void DMABitmapConvert(uint16_t * dest, const uint8_t * src, uint16_t w, uint16_t h, uint32_t format); +void lcdSetContrast(); +#define lcdOff() backlightEnable(0) /* just disable the backlight */ +#define lcdSetRefVolt(...) +#define lcdRefreshWait(...) +void lcdStoreBackupBuffer(); +int lcdRestoreBackupBuffer(); + +// Backlight driver +void backlightInit(); +#if defined(SIMU) +#define backlightEnable(...) +#else +void backlightEnable(uint8_t dutyCycle); +#endif + +#define BACKLIGHT_LEVEL_MAX 100 +#define BACKLIGHT_LEVEL_MIN 15 + +#define BACKLIGHT_ENABLE() backlightEnable(unexpectedShutdown ? BACKLIGHT_LEVEL_MAX : BACKLIGHT_LEVEL_MAX-g_eeGeneral.backlightBright) +#define BACKLIGHT_DISABLE() backlightEnable(unexpectedShutdown ? BACKLIGHT_LEVEL_MAX : ((g_eeGeneral.blOffBright == BACKLIGHT_LEVEL_MIN) && (g_eeGeneral.backlightMode != e_backlight_mode_off)) ? 0 : g_eeGeneral.blOffBright) +#define isBacklightEnabled() true + +#if !defined(SIMU) +void usbJoystickUpdate(); +#endif +#define USBD_MANUFACTURER_STRING "FlySky" +#define USB_NAME "FlySky NV14" +#define USB_MANUFACTURER 'F', 'l', 'y', 'S', 'k', 'y', ' ', ' ' /* 8 bytes */ +#define USB_PRODUCT 'N', 'V', '1', '4', ' ', ' ', ' ', ' ' /* 8 Bytes */ + +#if defined(__cplusplus) && !defined(SIMU) +} +#endif + +// Audio driver +void audioInit(); +void audioConsumeCurrentBuffer(); +void Audio_Sine_Test(); +void audioSpiWriteBuffer(const uint8_t * buffer, uint32_t size); +void audioSpiSetSpeed(uint8_t speed); +uint8_t audioHardReset(); +uint8_t audioSoftReset(); +void audioSendRiffHeader(); + +#define SPI_SPEED_2 0 +#define SPI_SPEED_4 1 +#define SPI_SPEED_8 2 +#define SPI_SPEED_16 3 +#define SPI_SPEED_32 4 +#define SPI_SPEED_64 5 +#define SPI_SPEED_128 6 +#define SPI_SPEED_256 7 + +#define audioDisableIrq() // interrupts must stay enabled on Horus +#define audioEnableIrq() // interrupts must stay enabled on Horus +#if defined(PCBNV14) +#define setSampleRate(freq) +#else +void setSampleRate(uint32_t frequency); +#endif +void setScaledVolume(uint8_t volume); +void setVolume(uint8_t volume); +int32_t getVolume(); +#define VOLUME_LEVEL_MAX 23 +#define VOLUME_LEVEL_DEF 12 + +// Telemetry driver +#define TELEMETRY_FIFO_SIZE 512 +void telemetryPortInit(uint32_t baudrate, uint8_t mode); +void telemetryPortSetDirectionOutput(); +void telemetryPortSetDirectionInput(); +void sportSendBuffer(uint8_t * buffer, uint32_t count); +uint8_t telemetryGetByte(uint8_t * byte); +void telemetryClearFifo(); +void sportSendByte(uint8_t byte); +extern uint32_t telemetryErrors; + +// Sport update driver +#define SPORT_UPDATE_POWER_ON() +#define SPORT_UPDATE_POWER_OFF() + +// Haptic driver +void hapticInit(); +void hapticDone(); +void hapticOff(); +void hapticOn(uint32_t pwmPercent); + +// Second serial port driver +#define AUX_SERIAL +#define DEBUG_BAUDRATE 115200 +extern uint8_t auxSerialMode; +void auxSerialInit(unsigned int mode, unsigned int protocol); +void auxSerialPutc(char c); +#define auxSerialTelemetryInit(protocol) auxSerialInit(UART_MODE_TELEMETRY, protocol) +void auxSerialSbusInit(); +void auxSerialStop(); +#define USART_FLAG_ERRORS (USART_FLAG_ORE | USART_FLAG_NE | USART_FLAG_FE | USART_FLAG_PE) + +extern uint8_t currentTrainerMode; +void checkTrainerSettings(); + +#if defined(__cplusplus) +#include "fifo.h" +#include "dmafifo.h" +extern DMAFifo<512> telemetryFifo; +typedef Fifo AuxSerialRxFifo; +extern AuxSerialRxFifo auxSerialRxFifo; +#endif + +uint8_t touchPressed(uint32_t x, uint32_t y, uint32_t w, uint32_t h); + + +#endif // _BOARD_H_ diff --git a/radio/src/targets/nv14/bootloader/boot_menu.cpp b/radio/src/targets/nv14/bootloader/boot_menu.cpp new file mode 100644 index 000000000..b50b9c729 --- /dev/null +++ b/radio/src/targets/nv14/bootloader/boot_menu.cpp @@ -0,0 +1,172 @@ +#include "opentx.h" +#include "../../common/arm/stm32/bootloader/boot.h" +#include "../../common/arm/stm32/bootloader/bin_files.h" + +#define SELECTED_COLOR (INVERS | TEXT_COLOR) + +#include "bmp_plug_usb.lbm" +#include "bmp_usb_plugged.lbm" + +const uint8_t LBM_FLASH[] = { +#include "icon_flash.lbm" +}; + +const uint8_t LBM_EXIT[] = { +#include "icon_exit.lbm" +}; + +const uint8_t LBM_SD[] = { +#include "icon_sd.lbm" +}; + +const uint8_t LBM_FILE[] = { +#include "icon_file.lbm" +}; + +const uint8_t LBM_ERROR[] = { +#include "icon_error.lbm" +}; + +const uint8_t LBM_OK[] = { +#include "icon_ok.lbm" +}; + +void bootloaderInitScreen() +{ + lcdColorTable[TEXT_COLOR_INDEX] = BLACK; + lcdColorTable[TEXT_BGCOLOR_INDEX] = WHITE; + lcdColorTable[LINE_COLOR_INDEX] = RED; + lcdColorTable[BARGRAPH1_COLOR_INDEX] = RED; + lcdColorTable[BARGRAPH2_COLOR_INDEX] = RGB(73, 219, 62); // green + + backlightEnable(BACKLIGHT_LEVEL_MAX); +} + +static void bootloaderDrawTitle(unsigned int x, const char* text) +{ + lcdDrawText(x, 28, text); + lcdDrawSolidFilledRect(28, 56, 422, 2, TEXT_COLOR); +} + +static void bootloaderDrawFooter() +{ + lcdDrawSolidFilledRect(28, 234, 422, 2, TEXT_COLOR); +} + +void bootloaderDrawScreen(BootloaderState st, int opt, const char* str) +{ + // clear screen + lcdDrawSolidFilledRect(0, 0, LCD_W-1, LCD_H-1, TEXT_BGCOLOR); + + if (st == ST_START) { + + bootloaderDrawTitle(88, "HORUS BOOTLOADER"); + + lcdDrawBitmapPattern(90, 72, LBM_FLASH, TEXT_COLOR); + lcdDrawText(124, 75, "Write Firmware"); + + lcdDrawBitmapPattern(90, 107, LBM_EXIT, TEXT_COLOR); + lcdDrawText(124, 110, "Exit"); + + lcdDrawSolidRect(119, (opt == 0) ? 72 : 107, 270, 26, 2, LINE_COLOR); + + lcd->drawBitmap(60, 166, &BMP_PLUG_USB); + lcdDrawText(195, 175, "Or plug in a USB cable"); + lcdDrawText(195, 200, "for mass storage"); + + bootloaderDrawFooter(); + lcdDrawText( 36, 242, "Current Firmware:"); + lcdDrawText(200, 242, getOtherVersion(nullptr)); + } + else if (st == ST_USB) { + + lcd->drawBitmap(136, 98, &BMP_USB_PLUGGED); + lcdDrawText(195, 128, "USB Connected"); + } + else if (st == ST_FILE_LIST || st == ST_DIR_CHECK || st == ST_FLASH_CHECK || + st == ST_FLASHING || st == ST_FLASH_DONE) { + + bootloaderDrawTitle(126, "SD>FIRMWARE"); + lcdDrawBitmapPattern(87, 16, LBM_SD, TEXT_COLOR); + + if (st == ST_FLASHING || st == ST_FLASH_DONE) { + + LcdFlags color = BARGRAPH1_COLOR; // red + + if (st == ST_FLASH_DONE) { + color = BARGRAPH2_COLOR/* green */; + opt = 100; // Completed > 100% + } + + lcdDrawRect(70, 120, 340, 31, 2); + lcdDrawSolidFilledRect(74, 124, (332 * opt) / 100, 23, color); + } + else if (st == ST_DIR_CHECK) { + + if (opt == FR_NO_PATH) { + lcdDrawText(90, 168, "Directory is missing"); + } + else { + lcdDrawText(90, 168, "Directory is empty"); + } + + lcdDrawBitmapPattern(356, 158, LBM_ERROR, BARGRAPH1_COLOR); + } + else if (st == ST_FLASH_CHECK) { + + bootloaderDrawFilename(str, 0, true); + + if (opt == FC_ERROR) { + lcdDrawText(94, 168, STR_INVALID_FIRMWARE); + lcdDrawBitmapPattern(356, 158, LBM_ERROR, BARGRAPH1_COLOR); + } + else if (opt == FC_OK) { + VersionTag tag; + extractFirmwareVersion(&tag); + + lcdDrawText(168, 158, "Version:", RIGHT); + lcdDrawText(174, 158, tag.version); + + lcdDrawText(168, 178, "Radio:", RIGHT); + lcdDrawText(174, 178, tag.flavour); + + lcdDrawBitmapPattern(356, 158, LBM_OK, BARGRAPH2_COLOR); + } + } + + bootloaderDrawFooter(); + + if ( st != ST_DIR_CHECK && (st != ST_FLASH_CHECK || opt == FC_OK)) { + + lcdDrawBitmapPattern(28, 242, LBM_FLASH, TEXT_COLOR); + + if (st == ST_FILE_LIST) { + lcdDrawText(56, 244, "[ENT] to select file"); + } + else if (st == ST_FLASH_CHECK && opt == FC_OK) { + lcdDrawText(56, 244, "Hold [ENT] long to flash"); + } + else if (st == ST_FLASHING) { + lcdDrawText(56, 244, "Writing Firmware ..."); + } + else if (st == ST_FLASH_DONE) { + lcdDrawText(56, 244, "Writing Completed"); + } + } + + if (st != ST_FLASHING) { + lcdDrawBitmapPattern(305, 242, LBM_EXIT, TEXT_COLOR); + lcdDrawText(335, 244, "[RTN] to exit"); + } + } +} + +void bootloaderDrawFilename(const char* str, uint8_t line, bool selected) +{ + lcdDrawBitmapPattern(94, 76 + (line * 25), LBM_FILE, TEXT_COLOR); + lcdDrawText(124, 75 + (line * 25), str); + + if (selected) { + lcdDrawSolidRect(119, 72 + (line * 25), 278, 26, 2, LINE_COLOR); + } +} diff --git a/radio/src/targets/nv14/diskio.cpp b/radio/src/targets/nv14/diskio.cpp new file mode 100644 index 000000000..1c5982a8c --- /dev/null +++ b/radio/src/targets/nv14/diskio.cpp @@ -0,0 +1,390 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2007 */ +/*-----------------------------------------------------------------------*/ +/* This is a stub disk I/O module that acts as front end of the existing */ +/* disk I/O modules and attach it to FatFs module with common interface. */ +/*-----------------------------------------------------------------------*/ + +#include "diskio.h" +#include +#include "opentx.h" +#include "targets/common/arm/stm32/sdio_sd.h" + +// TODO share this with Horus (and perhaps other STM32) + +/*-----------------------------------------------------------------------*/ +/* Lock / unlock functions */ +/*-----------------------------------------------------------------------*/ +#if !defined(BOOT) +static OS_MutexID ioMutex; +uint32_t ioMutexReq = 0, ioMutexRel = 0; +int ff_cre_syncobj (BYTE vol, _SYNC_t *mutex) +{ + *mutex = ioMutex; + return 1; +} + +int ff_req_grant (_SYNC_t mutex) +{ + ioMutexReq += 1; + RTOS_LOCK_MUTEX(mutex); + return 1; +} + +void ff_rel_grant (_SYNC_t mutex) +{ + ioMutexRel += 1; + RTOS_UNLOCK_MUTEX(mutex); +} + +int ff_del_syncobj (_SYNC_t mutex) +{ + return 1; +} +#endif + + +/*-----------------------------------------------------------------------*/ +/* Inidialize a Drive */ + +DSTATUS disk_initialize ( + BYTE drv /* Physical drive nmuber (0..) */ +) +{ + DSTATUS stat = 0; + + /* Supports only single drive */ + if (drv) + { + stat |= STA_NOINIT; + } + + /*-------------------------- SD Init ----------------------------- */ + SD_Error res = SD_Init(); + if (res != SD_OK) + { + TRACE("SD_Init() failed: %d", res); + stat |= STA_NOINIT; + } + + TRACE("SD card info:"); + TRACE("sectors: %u", (uint32_t)(SDCardInfo.CardCapacity / 512)); + TRACE("type: %u", (uint32_t)(SDCardInfo.CardType)); + TRACE("EraseGrSize: %u", (uint32_t)(SDCardInfo.SD_csd.EraseGrSize)); + TRACE("EraseGrMul: %u", (uint32_t)(SDCardInfo.SD_csd.EraseGrMul)); + TRACE("ManufacturerID: %u", (uint32_t)(SDCardInfo.SD_cid.ManufacturerID)); + + return(stat); +} + +DWORD scratch[BLOCK_SIZE / 4] __DMA; + +/*-----------------------------------------------------------------------*/ +/* Return Disk Status */ + +DSTATUS disk_status ( + BYTE drv /* Physical drive nmuber (0..) */ +) +{ + DSTATUS stat = 0; + + if (SD_Detect() != SD_PRESENT) + stat |= STA_NODISK; + + // STA_NOTINIT - Subsystem not initailized + // STA_PROTECTED - Write protected, MMC/SD switch if available + + return(stat); +} + +uint32_t sdReadRetries = 0; + +/*-----------------------------------------------------------------------*/ +/* Read Sector(s) */ + + +DRESULT disk_read_dma(BYTE drv, BYTE * buff, DWORD sector, UINT count) +{ + // this functions assumes that buff is properly aligned and in the right RAM segment for DMA + DRESULT res; + SD_Error Status; + SDTransferState State; + for (int retry=0; retry<3; retry++) { + res = RES_OK; + if (count == 1) { + Status = SD_ReadBlock(buff, sector, BLOCK_SIZE); // 4GB Compliant + } + else { + Status = SD_ReadMultiBlocks(buff, sector, BLOCK_SIZE, count); // 4GB Compliant + } + if (Status == SD_OK) { + Status = SD_WaitReadOperation(200*count); // Check if the Transfer is finished + while ((State = SD_GetStatus()) == SD_TRANSFER_BUSY); // BUSY, OK (DONE), ERROR (FAIL) + if (State == SD_TRANSFER_ERROR) { + TRACE("State=SD_TRANSFER_ERROR, c: %u", sector, (uint32_t)count); + res = RES_ERROR; + } + else if (Status != SD_OK) { + TRACE("Status(WaitRead)=%d, s:%u c: %u", Status, sector, (uint32_t)count); + res = RES_ERROR; + } + } + else { + TRACE("Status(ReadBlock)=%d, s:%u c: %u", Status, sector, (uint32_t)count); + res = RES_ERROR; + } + if (res == RES_OK) break; + sdReadRetries += 1; + } + return res; +} + +DRESULT __disk_read(BYTE drv, BYTE * buff, DWORD sector, UINT count) +{ + // If unaligned, do the single block reads with a scratch buffer. + // If aligned and single sector, do a single block read. + // If aligned and multiple sectors, try multi block read. + // If multi block read fails, try single block reads without + // an intermediate buffer (move trough the provided buffer) + + // TRACE("disk_read %d %p %10d %d", drv, buff, sector, count); + if (SD_Detect() != SD_PRESENT) { + TRACE("SD_Detect() != SD_PRESENT"); + return RES_NOTRDY; + } + + DRESULT res = RES_OK; + if (count == 0) return res; + + if ((DWORD)buff < 0x20000000 || ((DWORD)buff & 3)) { + // buffer is not aligned, use scratch buffer that is aligned + TRACE("disk_read bad alignment (%p)", buff); + while (count--) { + res = disk_read_dma(drv, (BYTE *)scratch, sector++, 1); + if (res != RES_OK) break; + memcpy(buff, scratch, BLOCK_SIZE); + buff += BLOCK_SIZE; + } + return res; + } + + res = disk_read_dma(drv, buff, sector, count); + if (res != RES_OK && count > 1) { + // multi-read failed, try reading same sectors, one by one + TRACE("disk_read() multi-block failed, trying single block reads..."); + while (count--) { + res = disk_read_dma(drv, buff, sector++, 1); + if (res != RES_OK) break; + buff += BLOCK_SIZE; + } + } + return res; +} + +/*-----------------------------------------------------------------------*/ +/* Write Sector(s) */ + +#if _READONLY == 0 +DRESULT __disk_write( + BYTE drv, /* Physical drive nmuber (0..) */ + const BYTE *buff, /* Data to be written */ + DWORD sector, /* Sector address (LBA) */ + UINT count /* Number of sectors to write (1..255) */ +) +{ + SD_Error Status; + DRESULT res = RES_OK; + + // TRACE("disk_write %d %p %10d %d", drv, buff, sector, count); + + if (SD_Detect() != SD_PRESENT) + return(RES_NOTRDY); + + if ((DWORD)buff < 0x20000000 || ((DWORD)buff & 3)) { + TRACE("disk_write bad alignment (%p)", buff); + while(count--) { + memcpy(scratch, buff, BLOCK_SIZE); + + res = __disk_write(drv, (BYTE *)scratch, sector++, 1); + + if (res != RES_OK) + break; + + buff += BLOCK_SIZE; + } + return(res); + } + + if (count == 1) { + Status = SD_WriteBlock((uint8_t *)buff, sector, BLOCK_SIZE); // 4GB Compliant + } + else { + Status = SD_WriteMultiBlocks((uint8_t *)buff, sector, BLOCK_SIZE, count); // 4GB Compliant + } + + if (Status == SD_OK) { + SDTransferState State; + + Status = SD_WaitWriteOperation(500*count); // Check if the Transfer is finished + + while((State = SD_GetStatus()) == SD_TRANSFER_BUSY); // BUSY, OK (DONE), ERROR (FAIL) + + if ((State == SD_TRANSFER_ERROR) || (Status != SD_OK)) { + TRACE("__disk_write() err, st:%d,%d, s:%u c: %u", Status, State, sector, (uint32_t)count); + res = RES_ERROR; + } + } + else { + res = RES_ERROR; + } + + // TRACE("result=%d", res); + return res; +} +#endif /* _READONLY */ + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ + +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive nmuber (0..) */ + BYTE ctrl, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + DRESULT res; + + if (drv) return RES_PARERR; + + res = RES_ERROR; + + switch (ctrl) { + case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ + // use 512 for sector size, SDCardInfo.CardBlockSize is not sector size and can be 1024 for 2G SD cards!!!! + *(DWORD*)buff = SDCardInfo.CardCapacity / BLOCK_SIZE; + res = RES_OK; + break; + + case GET_SECTOR_SIZE : /* Get R/W sector size (WORD) */ + *(WORD*)buff = BLOCK_SIZE; // force sector size. SDCardInfo.CardBlockSize is not sector size and can be 1024 for 2G SD cards!!!! + res = RES_OK; + break; + + case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */ + // TODO verify that this is the correct value + *(DWORD*)buff = (uint32_t)SDCardInfo.SD_csd.EraseGrSize * (uint32_t)SDCardInfo.SD_csd.EraseGrMul; + res = RES_OK; + break; + + case CTRL_SYNC: + while (SD_GetStatus() == SD_TRANSFER_BUSY); /* Complete pending write process (needed at _FS_READONLY == 0) */ + res = RES_OK; + break; + + default: + res = RES_OK; + break; + + } + + return res; +} + +// TODO everything here should not be in the driver layer ... + +FATFS g_FATFS_Obj __DMA; // initialized in boardInit() +#if defined(LOG_TELEMETRY) +FIL g_telemetryFile = {}; +#endif + +#if defined(BOOT) +void sdInit(void) +{ + if (f_mount(&g_FATFS_Obj, "", 1) == FR_OK) { + f_chdir("/"); + } +} +#else +void sdInit() +{ + TRACE("sdInit"); + + ioMutex = CoCreateMutex(); + if (ioMutex >= CFG_MAX_MUTEX) { + // sd error + return; + } + sdMount(); +} + +void sdMount() +{ + TRACE("sdMount"); + +#if defined(DISK_CACHE) + diskCache.clear(); +#endif + + if (f_mount(&g_FATFS_Obj, "", 1) == FR_OK) { + // call sdGetFreeSectors() now because f_getfree() takes a long time first time it's called + sdGetFreeSectors(); + +#if defined(LOG_TELEMETRY) + f_open(&g_telemetryFile, LOGS_PATH "/telemetry.log", FA_OPEN_ALWAYS | FA_WRITE); + if (f_size(&g_telemetryFile) > 0) { + f_lseek(&g_telemetryFile, f_size(&g_telemetryFile)); // append + } +#endif + } + else { + TRACE("f_mount() failed"); + } +} + +void sdDone() +{ + TRACE("sdDone"); + + if (sdMounted()) { + audioQueue.stopSD(); +#if defined(LOG_TELEMETRY) + f_close(&g_telemetryFile); +#endif + f_mount(NULL, "", 0); // unmount SD + } +} +#endif + +uint32_t sdMounted() +{ + return g_FATFS_Obj.fs_type != 0; +} + +uint32_t sdIsHC() +{ + return true; // TODO (CardType & CT_BLOCK); +} + +uint32_t sdGetSpeed() +{ + return 330000; +} diff --git a/radio/src/targets/nv14/extmodule_driver.cpp b/radio/src/targets/nv14/extmodule_driver.cpp new file mode 100644 index 000000000..4cde18972 --- /dev/null +++ b/radio/src/targets/nv14/extmodule_driver.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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" + +void extmoduleStop() +{ + EXTERNAL_MODULE_OFF(); + + NVIC_DisableIRQ(EXTMODULE_DMA_IRQn); + NVIC_DisableIRQ(EXTMODULE_TIMER_IRQn); + + EXTMODULE_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA + EXTMODULE_TIMER->DIER &= ~(TIM_DIER_CC2IE | TIM_DIER_UDE); + EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; +} + +void extmoduleNoneStart() +{ + EXTERNAL_MODULE_OFF(); + + GPIO_PinAFConfig(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PinSource, 0); + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = EXTMODULE_TX_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(EXTMODULE_TX_GPIO, &GPIO_InitStructure); + GPIO_SetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN); // Set high + + EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; + EXTMODULE_TIMER->PSC = EXTMODULE_TIMER_FREQ / 2000000 - 1; // 0.5uS (2Mhz) + EXTMODULE_TIMER->ARR = 36000; // 18mS + EXTMODULE_TIMER->CCR2 = 32000; // Update time + EXTMODULE_TIMER->EGR = 1; // Restart + EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; + EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt + EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; + + NVIC_EnableIRQ(EXTMODULE_TIMER_IRQn); + NVIC_SetPriority(EXTMODULE_TIMER_IRQn, 7); +} + +void extmodulePpmStart() +{ + EXTERNAL_MODULE_ON(); + + GPIO_PinAFConfig(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PinSource, EXTMODULE_TX_GPIO_AF); + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = EXTMODULE_TX_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(EXTMODULE_TX_GPIO, &GPIO_InitStructure); + + // PPM generation principle: + // + // Hardware timer in PWM mode is used for PPM generation + // Output is OFF if CNTCR1 &= ~TIM_CR1_CEN; // Stop timer + EXTMODULE_TIMER->PSC = EXTMODULE_TIMER_FREQ / 2000000 - 1; // 0.5uS (2Mhz) + EXTMODULE_TIMER->ARR = 45000; +#if defined(PCBX10) || PCBREV >= 13 + EXTMODULE_TIMER->CCMR2 = TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2; // PWM mode 1 + EXTMODULE_TIMER->BDTR = TIM_BDTR_MOE; + EXTMODULE_TIMER->EGR = 1; // Reloads register values now + EXTMODULE_TIMER->DIER = TIM_DIER_UDE; // Update DMA request + EXTMODULE_TIMER->CR1 = TIM_CR1_CEN; // Start timer +#elif defined(PCBNV14) + EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; // PWM mode 1 + EXTMODULE_TIMER->BDTR = TIM_BDTR_MOE; + EXTMODULE_TIMER->EGR = 1; // Reloads register values now + EXTMODULE_TIMER->DIER = TIM_DIER_UDE; // Update DMA request + EXTMODULE_TIMER->CR1 = TIM_CR1_CEN; // Start timer +#else + EXTMODULE_TIMER->CCR1 = GET_PPM_DELAY(EXTERNAL_MODULE)*2; + EXTMODULE_TIMER->CCER = TIM_CCER_CC1E | (GET_PPM_POLARITY(EXTERNAL_MODULE) ? TIM_CCER_CC1P : 0); + EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_0; // Force O/P high + EXTMODULE_TIMER->EGR = 1; // Reloads register values now + EXTMODULE_TIMER->DIER |= TIM_DIER_UDE; // Update DMA request + EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC2PE; // PWM mode 1 + EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; // Start timer +#endif + + extmoduleSendNextFrame(); + + NVIC_EnableIRQ(EXTMODULE_DMA_IRQn); + NVIC_SetPriority(EXTMODULE_DMA_IRQn, 7); + NVIC_EnableIRQ(EXTMODULE_TIMER_IRQn); + NVIC_SetPriority(EXTMODULE_TIMER_IRQn, 7); +} + +void extmoduleSerialStart(uint32_t baudrate, uint32_t period_half_us, bool inverted) +{ + +} + +void extmodulePxxStart() +{ + + EXTERNAL_MODULE_ON(); + + GPIO_PinAFConfig(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PinSource, EXTMODULE_TX_GPIO_AF); + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = EXTMODULE_TX_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(EXTMODULE_TX_GPIO, &GPIO_InitStructure); + + EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; + EXTMODULE_TIMER->PSC = EXTMODULE_TIMER_FREQ / 2000000 - 1; // 0.5uS (2Mhz) + EXTMODULE_TIMER->ARR = 18000; + +#if defined(PCBX10) || PCBREV >= 13 + EXTMODULE_TIMER->CCER = TIM_CCER_CC3E | TIM_CCER_CC3NE; + EXTMODULE_TIMER->BDTR = TIM_BDTR_MOE; // Enable outputs + EXTMODULE_TIMER->CCR3 = 18; + EXTMODULE_TIMER->CCMR2 = TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_0; // Force O/P high + EXTMODULE_TIMER->EGR = 1; // Restart + EXTMODULE_TIMER->DIER |= TIM_DIER_UDE; // Enable DMA on update + EXTMODULE_TIMER->CCMR2 = TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2; + EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; +#elif defined(PCBNV14) +{ + EXTMODULE_TIMER->CCER = TIM_CCER_CC1E | TIM_CCER_CC1NE; + EXTMODULE_TIMER->BDTR = TIM_BDTR_MOE; // Enable outputs + EXTMODULE_TIMER->CCR1 = 18; + EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_0; // Force O/P high + EXTMODULE_TIMER->EGR = 1; // Restart + EXTMODULE_TIMER->DIER |= TIM_DIER_UDE; // Enable DMA on update + EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; + EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; +} +#else + EXTMODULE_TIMER->CCER = TIM_CCER_CC1E | TIM_CCER_CC1P | TIM_CCER_CC1NE | TIM_CCER_CC1NP; // TIM_CCER_CC1E | TIM_CCER_CC1P; + EXTMODULE_TIMER->BDTR = TIM_BDTR_MOE; // Enable outputs + EXTMODULE_TIMER->CCR1 = 18; + EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_0; // Force O/P high + EXTMODULE_TIMER->EGR = 1; // Restart + EXTMODULE_TIMER->DIER |= TIM_DIER_UDE; // Enable DMA on update + EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; + EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; +#endif + + extmoduleSendNextFrame(); + + NVIC_EnableIRQ(EXTMODULE_DMA_IRQn); + NVIC_SetPriority(EXTMODULE_DMA_IRQn, 7); + NVIC_EnableIRQ(EXTMODULE_TIMER_IRQn); + NVIC_SetPriority(EXTMODULE_TIMER_IRQn, 7); +} + +#if defined(DSM2) +void extmoduleDsm2Start() +{ + EXTERNAL_MODULE_ON(); + + GPIO_PinAFConfig(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PinSource, EXTMODULE_TX_GPIO_AF); + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = EXTMODULE_TX_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(EXTMODULE_TX_GPIO, &GPIO_InitStructure); + + EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; + EXTMODULE_TIMER->PSC = EXTMODULE_TIMER_FREQ / 2000000 - 1; // 0.5uS (2Mhz) + EXTMODULE_TIMER->ARR = 44000; // 22mS + +#if defined(PCBX10) || PCBREV >= 13 + EXTMODULE_TIMER->CCER = TIM_CCER_CC3E | TIM_CCER_CC3P; + EXTMODULE_TIMER->BDTR = TIM_BDTR_MOE; // Enable outputs + EXTMODULE_TIMER->CCR3 = 0; + EXTMODULE_TIMER->CCMR2 = TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_0; // Force O/P high + EXTMODULE_TIMER->EGR = 1; // Restart + EXTMODULE_TIMER->DIER |= TIM_DIER_UDE; // Enable DMA on update + EXTMODULE_TIMER->CCMR2 = TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_0; + EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; +#else + EXTMODULE_TIMER->CCER = TIM_CCER_CC1E | TIM_CCER_CC1P; + EXTMODULE_TIMER->BDTR = TIM_BDTR_MOE; // Enable outputs + EXTMODULE_TIMER->CCR1 = 0; + EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_0; // Force O/P high + EXTMODULE_TIMER->EGR = 1; // Restart + EXTMODULE_TIMER->DIER |= TIM_DIER_UDE; // Enable DMA on update + EXTMODULE_TIMER->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0; + EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; +#endif + + extmoduleSendNextFrame(); + + NVIC_EnableIRQ(EXTMODULE_DMA_IRQn); + NVIC_SetPriority(EXTMODULE_DMA_IRQn, 7); + NVIC_EnableIRQ(EXTMODULE_TIMER_IRQn); + NVIC_SetPriority(EXTMODULE_TIMER_IRQn, 7); +} +#endif + +void extmoduleCrossfireStart() +{ + EXTERNAL_MODULE_ON(); + + GPIO_PinAFConfig(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PinSource, 0); + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = EXTMODULE_TX_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(EXTMODULE_TX_GPIO, &GPIO_InitStructure); + GPIO_SetBits(EXTMODULE_TX_GPIO, EXTMODULE_TX_GPIO_PIN); // Set high + + EXTMODULE_TIMER->CR1 &= ~TIM_CR1_CEN; + EXTMODULE_TIMER->PSC = EXTMODULE_TIMER_FREQ / 2000000 - 1; // 0.5uS (2Mhz) + EXTMODULE_TIMER->ARR = (2000 * CROSSFIRE_PERIOD); + EXTMODULE_TIMER->CCR2 = (2000 * CROSSFIRE_PERIOD) - 1000; + EXTMODULE_TIMER->EGR = 1; // Restart + EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; + EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt + EXTMODULE_TIMER->CR1 |= TIM_CR1_CEN; + + NVIC_EnableIRQ(EXTMODULE_TIMER_IRQn); + NVIC_SetPriority(EXTMODULE_TIMER_IRQn, 7); +} + +void extmoduleSendNextFrame() +{ + // TODO +} + + +extern "C" void EXTMODULE_DMA_IRQHandler() +{ + if (!DMA_GetITStatus(EXTMODULE_DMA_STREAM, EXTMODULE_DMA_FLAG_TC)) + return; + + DMA_ClearITPendingBit(EXTMODULE_DMA_STREAM, EXTMODULE_DMA_FLAG_TC); + + EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; // Clear flag + EXTMODULE_TIMER->DIER |= TIM_DIER_CC2IE; // Enable this interrupt +} + +extern "C" void EXTMODULE_TIMER_IRQHandler() +{ + EXTMODULE_TIMER->DIER &= ~TIM_DIER_CC2IE; // Stop this interrupt + EXTMODULE_TIMER->SR &= ~TIM_SR_CC2IF; + setupPulsesExternalModule(); + extmoduleSendNextFrame(); +} diff --git a/radio/src/targets/nv14/hal.h b/radio/src/targets/nv14/hal.h new file mode 100644 index 000000000..027da4cd2 --- /dev/null +++ b/radio/src/targets/nv14/hal.h @@ -0,0 +1,438 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _HAL_H_ +#define _HAL_H_ + +/* Timers Allocation: + * TIM1 = Haptic + * TIM4 = Trainer + * TIM6 = Audio + * TIM7 = 2MHz counter + * + * + * TIM14 = 5ms counter + */ + +/* DMA Allocation: + DMA/Stream/Channel + 1/5/7 DAC/Audio + 2/0/0 ADC1 + 2/3/4 SDIO +*/ + + +// Trims +#define TRIMS_GPIO_REG_RHL GPIOD->IDR +#define TRIMS_GPIO_PIN_RHL GPIO_Pin_7 // PD.07 +#define TRIMS_GPIO_REG_RHR GPIOG->IDR +#define TRIMS_GPIO_PIN_RHR GPIO_Pin_10 // PG.10 +#define TRIMS_GPIO_REG_RVD GPIOJ->IDR +#define TRIMS_GPIO_PIN_RVD GPIO_Pin_0 // PJ.00 +#define TRIMS_GPIO_REG_RVU GPIOB->IDR +#define TRIMS_GPIO_PIN_RVU GPIO_Pin_15 // PB.15 +#define TRIMS_GPIO_REG_RPRESS GPIOC->IDR +#define TRIMS_GPIO_PIN_RPRESS GPIO_Pin_13 // PC.13 + +#define TRIMS_GPIO_REG_LHL GPIOH->IDR +#define TRIMS_GPIO_PIN_LHL GPIO_Pin_2 // PH.02 +#define TRIMS_GPIO_REG_LHR GPIOG->IDR +#define TRIMS_GPIO_PIN_LHR GPIO_Pin_2 // PG.02 +#define TRIMS_GPIO_REG_LVU GPIOH->IDR +#define TRIMS_GPIO_PIN_LVU GPIO_Pin_7 // PH.07 +#define TRIMS_GPIO_REG_LVD GPIOJ->IDR +#define TRIMS_GPIO_PIN_LVD GPIO_Pin_12 // PJ.12 +#define TRIMS_GPIO_REG_LPRESS GPIOG->IDR +#define TRIMS_GPIO_PIN_LPRESS GPIO_Pin_11 // PG.11 + +// Monitor pin +#define MONITOR_RCC_AHB1Periph (RCC_AHB1Periph_GPIOJ) +#define VBUS_MONITOR_GPIO (GPIOJ) +#define VBUS_MONITOR_PIN (GPIO_Pin_14) + +// Switches +#define HARDWARE_SWITCH_A +#define STORAGE_SWITCH_A +#define HARDWARE_SWITCH_B +#define STORAGE_SWITCH_B +#define HARDWARE_SWITCH_C +#define STORAGE_SWITCH_C +#define HARDWARE_SWITCH_D +#define STORAGE_SWITCH_D +#define HARDWARE_SWITCH_E +#define STORAGE_SWITCH_E +#define HARDWARE_SWITCH_F +#define STORAGE_SWITCH_F +#define HARDWARE_SWITCH_G +#define STORAGE_SWITCH_G +#define HARDWARE_SWITCH_H +#define STORAGE_SWITCH_H + +// Index of all switches / trims +#define KEYS_RCC_AHB1Periph (RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOH | RCC_AHB1Periph_GPIOJ) +#define KEYS_GPIOB_PINS (GPIO_Pin_15) +#define KEYS_GPIOC_PINS (GPIO_Pin_13) +#define KEYS_GPIOD_PINS (GPIO_Pin_7) +#define KEYS_GPIOG_PINS (GPIO_Pin_2 | GPIO_Pin_10 | GPIO_Pin_11) +#define KEYS_GPIOH_PINS (GPIO_Pin_2 | GPIO_Pin_7) +#define KEYS_GPIOJ_PINS (GPIO_Pin_0 | GPIO_Pin_12) + +// ADC +#define ADC_RCC_AHB1Periph (RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_DMA2) +#define ADC_RCC_APB2Periph (RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC3) +#define ADC_GPIO_PIN_STICK_LH GPIO_Pin_2 // PA.02 +#define ADC_GPIO_PIN_STICK_LV GPIO_Pin_3 // PA.03 +#define ADC_GPIO_PIN_STICK_RH GPIO_Pin_4 // PA.04 +#define ADC_GPIO_PIN_STICK_RV GPIO_Pin_5 // PA.05 +#define ADC_GPIO_PIN_POT1 GPIO_Pin_6 // PA.06 VRA +#define ADC_GPIO_PIN_POT2 GPIO_Pin_4 // PC.04 VRB +#define ADC_GPIO_PIN_SWA GPIO_Pin_1 // PB.01 +#define ADC_GPIO_PIN_SWB GPIO_Pin_8 // PF.08 +#define ADC_GPIO_PIN_SWC GPIO_Pin_0 // PB.00 +#define ADC_GPIO_PIN_SWD GPIO_Pin_10 // PF.10 +#define ADC_GPIO_PIN_SWE GPIO_Pin_2 // PC.02 +#define ADC_GPIO_PIN_SWF GPIO_Pin_7 // PA.07 +#define ADC_GPIO_PIN_SWG GPIO_Pin_0 // PC.00 +#define ADC_GPIO_PIN_SWH GPIO_Pin_1 // PC.01 +#define ADC_GPIO_PIN_BATT GPIO_Pin_5 // PC.05 + +#define ADC_GPIOA_PINS (GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4| GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7) +#define ADC_GPIOB_PINS (GPIO_Pin_0 | GPIO_Pin_1) +#define ADC_GPIOC_PINS (GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_4 | GPIO_Pin_5) +#define ADC_GPIOF_PINS (GPIO_Pin_8 | GPIO_Pin_10) + +#define ADC_CHANNEL_STICK_LH ADC_Channel_2 // ADC123_IN2 -> ADC1_IN2 +#define ADC_CHANNEL_STICK_LV ADC_Channel_3 // ADC123_IN3 -> ADC1_IN3 +#define ADC_CHANNEL_STICK_RH ADC_Channel_4 // ADC12_IN4 -> ADC1_IN4 +#define ADC_CHANNEL_STICK_RV ADC_Channel_5 // ADC12_IN5 -> ADC1_IN5 +#define ADC_CHANNEL_POT1 ADC_Channel_6 // ADC12_IN6 -> ADC1_IN6 +#define ADC_CHANNEL_POT2 ADC_Channel_14 // ADC12_IN14 -> ADC1_IN14 +#define ADC_CHANNEL_SWA ADC_Channel_9 // ADC12_IN9 -> ADC1_IN9 +#define ADC_CHANNEL_SWB ADC_Channel_6 // ADC3_IN6 -> ADC3_IN6 +#define ADC_CHANNEL_SWC ADC_Channel_8 // ADC12_IN8 -> ADC1_IN8 +#define ADC_CHANNEL_SWD ADC_Channel_8 // ADC3_IN8 -> ADC3_IN8 +#define ADC_CHANNEL_SWE ADC_Channel_10 // ADC123_IN10-> ADC1_IN10 +#define ADC_CHANNEL_SWF ADC_Channel_11 // ADC123_IN11-> ADC1_IN11 +#define ADC_CHANNEL_SWG ADC_Channel_7 // ADC12_IN7 -> ADC1_IN7 +#define ADC_CHANNEL_SWH ADC_Channel_12 // ADC123_IN12-> ADC1_IN12 + +#define ADC_CHANNEL_BATT ADC_Channel_15 // ADC12_IN15 -> ADC1_IN15 +#define ADC_MAIN ADC1 +#define ADC_SUB ADC3 +#define ADC_SAMPTIME 2 +#define ADC_DMA DMA2 +#define ADC_MAIN_DMA_SxCR_CHSEL 0 // DMA_SxCR_CHSEL_1 +#define ADC_SUB_DMA_SxCR_CHSEL DMA_Channel_2 +#define ADC_MAIN_DMA_Stream DMA2_Stream4 +#define ADC_SUB_DMA_Stream DMA2_Stream0 +#define ADC_MAIN_SET_DMA_FLAGS() ADC_DMA->HIFCR = (DMA_HIFCR_CTCIF4 | DMA_HIFCR_CHTIF4 | DMA_HIFCR_CTEIF4 | DMA_HIFCR_CDMEIF4 | DMA_HIFCR_CFEIF4) +#define ADC_SUB_SET_DMA_FLAGS() ADC_DMA->LIFCR = (DMA_LIFCR_CTCIF0 | DMA_LIFCR_CHTIF0 | DMA_LIFCR_CTEIF0 | DMA_LIFCR_CDMEIF0 | DMA_LIFCR_CFEIF0) +#define ADC_MAIN_TRANSFER_COMPLETE() (ADC_DMA->HISR & DMA_HISR_TCIF4) +#define ADC_SUB_TRANSFER_COMPLETE() (ADC_DMA->LISR & DMA_LISR_TCIF0) + +// Power +#define PWR_RCC_AHB1Periph RCC_AHB1Periph_GPIOI +#define PWR_GPIO GPIOI +#define PWR_SWITCH_GPIO_REG PWR_GPIO->IDR +#define PWR_SWITCH_GPIO_PIN GPIO_Pin_11 // PI.11 +#define PWR_ON_GPIO_PIN GPIO_Pin_14 // PI.14 +#define PWR_ON_GPIO_MODER GPIO_MODER_MODER1 +#define PWR_ON_GPIO_MODER_OUT GPIO_MODER_MODER1_0 + +//BLUETOOTH +#define BLUETOOTH_ON_RCC_AHB1Periph RCC_AHB1Periph_GPIOI +#define BLUETOOTH_ON_GPIO GPIOI +#define BLUETOOTH_ON_GPIO_PIN GPIO_Pin_8 // PI.8 + +// S.Port update connector +#define SPORT_MAX_BAUDRATE 250000 // < 400000 +#define SPORT_UPDATE_RCC_AHB1Periph RCC_AHB1Periph_GPIOH +#define SPORT_UPDATE_PWR_GPIO GPIOH +#define SPORT_UPDATE_PWR_GPIO_PIN GPIO_Pin_13 // PH.13 +#define HAS_SPORT_UPDATE_CONNECTOR() (false) + +// Led +// #define STATUS_LEDS +#define LED_RCC_AHB1Periph RCC_AHB1Periph_GPIOI +#define LED_GPIO GPIOI +#define LED_GPIO_PIN GPIO_Pin_5 // PI.05 + +// Serial Port (DEBUG) +// We will temporarily used the PPM and the HEARTBEAT PINS +#define AUX_SERIAL_RCC_AHB1Periph (RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOE) +#define AUX_SERIAL_RCC_APB1Periph 0 +#define AUX_SERIAL_RCC_APB2Periph RCC_APB2Periph_USART6 +#define AUX_SERIAL_GPIO GPIOC +#define AUX_SERIAL_GPIO_PIN_TX GPIO_Pin_6 // PC.06 +#define AUX_SERIAL_GPIO_PIN_RX GPIO_Pin_7 // PC.07 +#define AUX_SERIAL_GPIO_PinSource_TX GPIO_PinSource6 +#define AUX_SERIAL_GPIO_PinSource_RX GPIO_PinSource7 +#define AUX_SERIAL_GPIO_AF GPIO_AF_USART6 +#define AUX_SERIAL_USART USART6 +#define AUX_SERIAL_USART_IRQHandler USART6_IRQHandler +#define AUX_SERIAL_USART_IRQn USART6_IRQn +#define AUX_SERIAL_TX_INVERT_GPIO GPIOE +#define AUX_SERIAL_TX_INVERT_GPIO_PIN GPIO_Pin_3 // PE.03 +#define AUX_SERIAL_RX_INVERT_GPIO GPIOI +#define AUX_SERIAL_RX_INVERT_GPIO_PIN GPIO_Pin_15 // PI.15 + +// Telemetry +#define TELEMETRY_RCC_AHB1Periph (RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOJ | RCC_AHB1Periph_DMA1) +#define TELEMETRY_RCC_APB1Periph RCC_APB1Periph_USART2 +#define TELEMETRY_REV_GPIO GPIOJ +#define TELEMETRY_RX_REV_GPIO_PIN GPIO_Pin_8 // PJ.08 +#define TELEMETRY_TX_REV_GPIO_PIN GPIO_Pin_7 // PJ.07 +#define TELEMETRY_DIR_GPIO GPIOJ +#define TELEMETRY_DIR_GPIO_PIN GPIO_Pin_13 // PJ.13 +#define TELEMETRY_GPIO GPIOD +#define TELEMETRY_TX_GPIO_PIN GPIO_Pin_5 // PD.05 +#define TELEMETRY_RX_GPIO_PIN GPIO_Pin_6 // PD.06 +#define TELEMETRY_GPIO_PinSource_TX GPIO_PinSource5 +#define TELEMETRY_GPIO_PinSource_RX GPIO_PinSource6 +#define TELEMETRY_GPIO_AF GPIO_AF_USART2 +#define TELEMETRY_USART USART2 +#define TELEMETRY_DMA_Stream_RX DMA1_Stream5 +#define TELEMETRY_DMA_Channel_RX DMA_Channel_4 +#define TELEMETRY_DMA_Stream_TX DMA1_Stream6 +#define TELEMETRY_DMA_Channel_TX DMA_Channel_4 +#define TELEMETRY_DMA_TX_Stream_IRQ DMA1_Stream6_IRQn +#define TELEMETRY_DMA_TX_IRQHandler DMA1_Stream6_IRQHandler +#define TELEMETRY_DMA_TX_FLAG_TC DMA_IT_TCIF6 +#define TELEMETRY_USART_IRQHandler USART2_IRQHandler +#define TELEMETRY_USART_IRQn USART2_IRQn + +#define TELEMETRY_DIR_OUTPUT() TELEMETRY_DIR_GPIO->BSRRH = TELEMETRY_DIR_GPIO_PIN +#define TELEMETRY_DIR_INPUT() TELEMETRY_DIR_GPIO->BSRRL = TELEMETRY_DIR_GPIO_PIN +#define TELEMETRY_TX_POL_NORM() TELEMETRY_REV_GPIO->BSRRH = TELEMETRY_TX_REV_GPIO_PIN +#define TELEMETRY_TX_POL_INV() TELEMETRY_REV_GPIO->BSRRL = TELEMETRY_TX_REV_GPIO_PIN +#define TELEMETRY_RX_POL_NORM() TELEMETRY_REV_GPIO->BSRRH = TELEMETRY_RX_REV_GPIO_PIN +#define TELEMETRY_RX_POL_INV() TELEMETRY_REV_GPIO->BSRRL = TELEMETRY_RX_REV_GPIO_PIN +// USB +#define USB_RCC_AHB1Periph_GPIO RCC_AHB1Periph_GPIOA +#define USB_GPIO GPIOA +#define USB_GPIO_PIN_VBUS GPIO_Pin_9 // PA.09 +#define USB_GPIO_PIN_DM GPIO_Pin_11 // PA.11 +#define USB_GPIO_PIN_DP GPIO_Pin_12 // PA.12 +#define USB_GPIO_PinSource_DM GPIO_PinSource11 +#define USB_GPIO_PinSource_DP GPIO_PinSource12 +#define USB_GPIO_AF GPIO_AF_OTG1_FS + +// LCD +#define LCD_RCC_AHB1Periph (RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOI | RCC_AHB1Periph_GPIOJ | RCC_AHB1Periph_GPIOK | RCC_AHB1Periph_DMA2D) +#define LCD_RCC_APB1Periph 0 +#define LCD_RCC_APB2Periph RCC_APB2Periph_LTDC +#define LCD_NRST_GPIO GPIOG +#define LCD_NRST_GPIO_PIN GPIO_Pin_9 // PG.09 +#define LCD_SPI_GPIO GPIOE +#define LCD_SPI_CS_GPIO_PIN GPIO_Pin_4 // PE.04 +#define LCD_SPI_SCK_GPIO_PIN GPIO_Pin_2 // PE.02 +#define LCD_SPI_MISO_GPIO_PIN GPIO_Pin_5 // PE.05 +#define LCD_SPI_MOSI_GPIO_PIN GPIO_Pin_6 // PE.06 +#define LTDC_IRQ_PRIO 4 +#define DMA_SCREEN_IRQ_PRIO 6 + +// Backlight +// TODO TIM3, TIM8, TIM14, review the channel in backlight_driver.cpp according to the chosen timer +#define BACKLIGHT_RCC_AHB1Periph RCC_AHB1Periph_GPIOA +#define BACKLIGHT_RCC_APB1Periph RCC_APB1Periph_TIM2 +#define BACKLIGHT_GPIO GPIOA +#define BACKLIGHT_GPIO_PIN GPIO_Pin_15 +#define BACKLIGHT_GPIO_PinSource GPIO_PinSource15 +#define BACKLIGHT_TIMER TIM2 +#define BACKLIGHT_GPIO_AF GPIO_AF_TIM2 +#define BACKLIGHT_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) + +// SD card +#define SD_RCC_AHB1Periph (RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_DMA2) +#define SD_RCC_APB1Periph 0 +#define SD_SDIO_DMA_STREAM DMA2_Stream3 +#define SD_SDIO_DMA_CHANNEL DMA_Channel_4 +#define SD_SDIO_DMA_FLAG_FEIF DMA_FLAG_FEIF3 +#define SD_SDIO_DMA_FLAG_DMEIF DMA_FLAG_DMEIF3 +#define SD_SDIO_DMA_FLAG_TEIF DMA_FLAG_TEIF3 +#define SD_SDIO_DMA_FLAG_HTIF DMA_FLAG_HTIF3 +#define SD_SDIO_DMA_FLAG_TCIF DMA_FLAG_TCIF3 +#define SD_SDIO_DMA_IRQn DMA2_Stream3_IRQn +#define SD_SDIO_DMA_IRQHANDLER DMA2_Stream3_IRQHandler +#define SD_SDIO_FIFO_ADDRESS ((uint32_t)0x40012C80) +#define SD_SDIO_CLK_DIV(fq) ((48000000 / (fq)) - 2) +#define SD_SDIO_INIT_CLK_DIV SD_SDIO_CLK_DIV(400000) +#define SD_SDIO_TRANSFER_CLK_DIV SD_SDIO_CLK_DIV(24000000) + +// SDRAM +#define SDRAM_RCC_AHB1Periph (RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOH) +#define SDRAM_RCC_AHB3Periph RCC_AHB3Periph_FMC + +// SPI FLASH +#define EEPROM_RCC_AHB1Periph RCC_AHB1Periph_GPIOG +#define EEPROM_RCC_APB1Periph RCC_APB1Periph_SPI6 +#define EEPROM_SPI_CS_GPIO GPIOG +#define EEPROM_SPI_CS_GPIO_PIN GPIO_Pin_6 // PG.06 +#define EEPROM_SPI_SCK_GPIO GPIOG +#define EEPROM_SPI_SCK_GPIO_PIN GPIO_Pin_13 // PG.13 +#define EEPROM_SPI_SCK_GPIO_PinSource GPIO_PinSource13 +#define EEPROM_SPI_MISO_GPIO GPIOG +#define EEPROM_SPI_MISO_GPIO_PIN GPIO_Pin_12 // PG.12 +#define EEPROM_SPI_MISO_GPIO_PinSource GPIO_PinSource12 +#define EEPROM_SPI_MOSI_GPIO GPIOG +#define EEPROM_SPI_MOSI_GPIO_PIN GPIO_Pin_14 // PG.14 +#define EEPROM_SPI_MOSI_GPIO_PinSource GPIO_PinSource14 + +// Audio +#define AUDIO_RCC_AHB1Periph (RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOH) +#define AUDIO_RCC_APB2Periph RCC_APB2Periph_SPI1 +#define AUDIO_SHUTDOWN_GPIO GPIOH +#define AUDIO_SHUTDOWN_GPIO_PIN GPIO_Pin_8 // PH.08 audio amp control pin +#define AUDIO_XDCS_GPIO GPIOH +#define AUDIO_XDCS_GPIO_PIN GPIO_Pin_14 // PH.14 +#define AUDIO_CS_GPIO GPIOH +#define AUDIO_CS_GPIO_PIN GPIO_Pin_13 // PH.13 +#define AUDIO_DREQ_GPIO GPIOH +#define AUDIO_DREQ_GPIO_PIN GPIO_Pin_15 // PH.15 +#define AUDIO_RST_GPIO GPIOD +#define AUDIO_RST_GPIO_PIN GPIO_Pin_4 // PD.4 +#define AUDIO_SPI SPI1 +#define AUDIO_SPI_GPIO_AF GPIO_AF_SPI1 +#define AUDIO_SPI_SCK_GPIO GPIOB +#define AUDIO_SPI_SCK_GPIO_PIN GPIO_Pin_3 // PB.03 +#define AUDIO_SPI_SCK_GPIO_PinSource GPIO_PinSource3 +#define AUDIO_SPI_MISO_GPIO GPIOB +#define AUDIO_SPI_MISO_GPIO_PIN GPIO_Pin_4 // PB.04 +#define AUDIO_SPI_MISO_GPIO_PinSource GPIO_PinSource4 +#define AUDIO_SPI_MOSI_GPIO GPIOB +#define AUDIO_SPI_MOSI_GPIO_PIN GPIO_Pin_5 // PB.05 +#define AUDIO_SPI_MOSI_GPIO_PinSource GPIO_PinSource5 + +// I2C Bus +#define I2C_RCC_AHB1Periph 0 +#define I2C_RCC_APB1Periph 0 + +// Haptic: TIM1_CH2 +#define HAPTIC_PWM +#define HAPTIC_RCC_AHB1Periph RCC_AHB1Periph_GPIOA +#define HAPTIC_RCC_APB2Periph RCC_APB2ENR_TIM1EN +#define HAPTIC_GPIO GPIOA +#define HAPTIC_GPIO_PIN GPIO_Pin_9 +#define HAPTIC_GPIO_TIMER TIM1 +#define HAPTIC_GPIO_AF GPIO_AF_TIM1 +#define HAPTIC_GPIO_PinSource GPIO_PinSource9 +#define HAPTIC_TIMER_OUTPUT_ENABLE TIM_CCER_CC2E +#define HAPTIC_TIMER_MODE TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 +#define HAPTIC_TIMER_COMPARE_VALUE HAPTIC_GPIO_TIMER->CCR2 + +// Internal Module +#define HARDWARE_INTERNAL_MODULE +#define INTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOH | RCC_AHB1Periph_DMA1) +#define INTMODULE_PWR_GPIO GPIOH +#define INTMODULE_PWR_GPIO_PIN GPIO_Pin_9 // PH.09 +#define INTMODULE_TX_GPIO GPIOF +#define INTMODULE_TX_GPIO_PIN GPIO_Pin_7 // PF.07 +#define INTMODULE_RX_GPIO GPIOF +#define INTMODULE_RX_GPIO_PIN GPIO_Pin_6 // PF.06 +#define INTMODULE_GPIO_PinSource_TX GPIO_PinSource7 +#define INTMODULE_GPIO_PinSource_RX GPIO_PinSource6 +#define INTMODULE_USART UART7 +#define INTMODULE_GPIO_AF GPIO_AF_UART7 +#define INTMODULE_USART_IRQn UART7_IRQn +#define INTMODULE_USART_IRQHandler UART7_IRQHandler +#define INTMODULE_RX_DMA_STREAM DMA1_Stream3 +#define INTMODULE_RX_DMA_Stream_IRQn DMA1_Stream3_IRQn +#define INTMODULE_RX_DMA_Stream_IRQHandler DMA1_Stream3_IRQHandler +#define INTMODULE_TX_DMA_STREAM DMA1_Stream1 +#define INTMODULE_TX_DMA_Stream_IRQn DMA1_Stream1_IRQn +#define INTMODULE_TX_DMA_Stream_IRQHandler DMA1_Stream1_IRQHandler +#define INTMODULE_TX_DMA_FLAG_TC DMA_IT_TCIF1 + +#define INTMODULE_DMA_STREAM DMA1_Stream1 +#define INTMODULE_DMA_CHANNEL DMA_Channel_5 +#define INTMODULE_GPIO INTMODULE_TX_GPIO +//#define INTMODULE_DMA_STREAM_IRQ INTMODULE_RX_DMA_Stream_IRQHandler +#define INTMODULE_DMA_STREAM_IRQ INTMODULE_TX_DMA_Stream_IRQn + +#define INTMODULE_RCC_APB1Periph (RCC_APB1Periph_UART7) +#define INTMODULE_RCC_APB2Periph (RCC_APB2Periph_TIM1) +#define INTMODULE_TIMER TIM1 +#define INTMODULE_TIMER_IRQn TIM1_CC_IRQn +#define INTMODULE_TIMER_IRQHandler TIM1_CC_IRQHandler +#define INTMODULE_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB2) + +// External Module +#define EXTMODULE +#define EXTMODULE_PULSES +#define EXTMODULE_PWR_GPIO GPIOD +#define EXTMODULE_PWR_GPIO_PIN GPIO_Pin_11 +#define EXTMODULE_RCC_AHB1Periph (RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_DMA2) +#define EXTMODULE_RCC_APB1Periph 0 +#define EXTMODULE_RCC_APB2Periph RCC_APB2Periph_TIM8 +#define EXTMODULE_TX_GPIO GPIOC +#define EXTMODULE_TX_GPIO_PIN GPIO_Pin_6 // PC.06 +#define EXTMODULE_TX_GPIO_PinSource GPIO_PinSource6 +#define EXTMODULE_TX_GPIO_AF GPIO_AF_TIM8 +#define EXTMODULE_TIMER TIM8 +#define EXTMODULE_TIMER_IRQn TIM8_CC_IRQn +#define EXTMODULE_TIMER_IRQHandler TIM8_CC_IRQHandler +#define EXTMODULE_TIMER_FREQ (PERI2_FREQUENCY * TIMER_MULT_APB1) +#define EXTMODULE_DMA_CHANNEL DMA_Channel_7 +#define EXTMODULE_DMA_STREAM DMA2_Stream1 +#define EXTMODULE_DMA_IRQn DMA2_Stream1_IRQn +#define EXTMODULE_DMA_IRQHandler DMA2_Stream1_IRQHandler +#define EXTMODULE_DMA_FLAG_TC DMA_IT_TCIF1 +//#define EXTMODULE_USART USART6 // TODO + +// Heartbeat (not used) +#define HEARTBEAT_RCC_AHB1Periph RCC_AHB1Periph_GPIOD +#define HEARTBEAT_GPIO GPIOD +#define HEARTBEAT_GPIO_PIN GPIO_Pin_12 // PD.12 + +// Trainer Port +#define TRAINERMODULE +#define TRAINER_RCC_AHB1Periph (RCC_AHB1Periph_GPIOD) +#define TRAINER_RCC_APB1Periph RCC_APB1Periph_TIM4 +#define TRAINER_GPIO GPIOD +#define TRAINER_IN_GPIO_PIN GPIO_Pin_12 // PD.12 +#define TRAINER_IN_GPIO_PinSource GPIO_PinSource12 +#define TRAINER_OUT_GPIO_PIN GPIO_Pin_13 // PD.13 +#define TRAINER_OUT_GPIO_PinSource GPIO_PinSource13 +// #define TRAINER_DETECT_GPIO GPIOB +// #define TRAINER_DETECT_GPIO_PIN GPIO_Pin_4 // PB.04 +#define TRAINER_TIMER TIM4 +#define TRAINER_TIMER_IRQn TIM4_IRQn +#define TRAINER_GPIO_AF GPIO_AF_TIM4 // TIM4_CH1 (in) + TIM4_CH2 (out) +#define TRAINER_OUT_DMA DMA1 +// #define TRAINER_OUT_DMA_CHANNEL DMA_Channel_2 +// #define TRAINER_OUT_DMA_STREAM DMA1_Stream3 +// #define TRAINER_OUT_DMA_IRQn DMA1_Stream3_IRQn +// #define TRAINER_OUT_DMA_IRQHandler DMA1_Stream3_IRQHandler +// #define TRAINER_OUT_DMA_FLAG_TC DMA_IT_TCIF3 +#define TRAINER_EXTMODULE_TIMER_IRQn TIM4_IRQn +#define TRAINER_EXTMODULE_TIMER_IRQHandler TIM4_IRQHandler +#define TRAINER_EXTMODULE_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) + +// Xms Interrupt +#define INTERRUPT_xMS_RCC_APB1Periph RCC_APB1Periph_TIM14 +#define INTERRUPT_xMS_TIMER TIM14 +#define INTERRUPT_xMS_IRQn TIM8_TRG_COM_TIM14_IRQn +#define INTERRUPT_xMS_IRQHandler TIM8_TRG_COM_TIM14_IRQHandler + +// 2MHz Timer +#define TIMER_2MHz_RCC_APB1Periph RCC_APB1Periph_TIM7 +#define TIMER_2MHz_TIMER TIM7 + +#endif // _HAL_H_ diff --git a/radio/src/targets/nv14/hallStick_driver.cpp b/radio/src/targets/nv14/hallStick_driver.cpp new file mode 100644 index 000000000..d05e3fd2d --- /dev/null +++ b/radio/src/targets/nv14/hallStick_driver.cpp @@ -0,0 +1,614 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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" + +DMAFifo hallDMAFifo __DMA (HALL_DMA_Stream_RX); +Fifo hallStickTxFifo; +static uint8_t hallStickSendState = HALLSTICK_SEND_STATE_IDLE; +unsigned char HallCmd[264] __DMA; + +STRUCT_HALL HallProtocol = { 0 }; +STRUCT_HALL HallProtocolTx = { 0 }; +STRUCT_HALL_CHANNEL Channel; +STRUCT_STICK_CALIBRATION StickCallbration[4] = { {0, 0, 0} }; +unsigned short HallChVal[4]; + +/* crc16 implementation according to CCITT standards */ +const unsigned short CRC16Table[256]= { + 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, + 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, + 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, + 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, + 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, + 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, + 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, + 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, + 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, + 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, + 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, + 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, + 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, + 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, + 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, + 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, + 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, + 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, + 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, + 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, + 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, + 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, + 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, + 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, + 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, + 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, + 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, + 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, + 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, + 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, + 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, + 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 +}; + +const uint8_t sticks_mapping[4] = { 0 /*STICK1*/, 1/*STICK2*/, 2/*STICK3*/, 3 /*STICK4*/}; + +unsigned short calc_crc16(void *pBuffer,unsigned char BufferSize) +{ + unsigned short crc16; + crc16 = 0xffff; + while (BufferSize) + { + crc16 = (crc16 << 8) ^ CRC16Table[((crc16>>8) ^ (*(unsigned char *)pBuffer)) & 0x00ff]; + pBuffer = (void *)((unsigned char *)pBuffer + 1); + BufferSize--; + } + return crc16; +} + +uint16_t get_hall_adc_value(uint8_t ch) +{ + if (ch >= FLYSKY_HALL_CHANNEL_COUNT) + { + return 0; + } + +#if defined(FLYSKY_HALL_STICKS_REVERSE) + ch = sticks_mapping[ch]; + + return MAX_ADC_CHANNEL_VALUE - HallChVal[ch]; +#else + if (ch < 2) + { + return MAX_ADC_CHANNEL_VALUE - HallChVal[ch]; + } + + return HallChVal[ch]; +#endif +} + + +void hall_stick_init(uint32_t baudrate) +{ + if (baudrate == 0) + { + USART_DeInit(HALL_SERIAL_USART); + return; + } + + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = HALL_SERIAL_RX_DMA_Stream_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + USART_InitTypeDef USART_InitStructure; + GPIO_InitTypeDef GPIO_InitStructure; + + GPIO_PinAFConfig(HALL_SERIAL_GPIO, HALL_SERIAL_RX_GPIO_PinSource, HALL_SERIAL_GPIO_AF); + GPIO_PinAFConfig(HALL_SERIAL_GPIO, HALL_SERIAL_TX_GPIO_PinSource, HALL_SERIAL_GPIO_AF); + + GPIO_InitStructure.GPIO_Pin = HALL_SERIAL_TX_GPIO_PIN | HALL_SERIAL_RX_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_Init(HALL_SERIAL_GPIO, &GPIO_InitStructure); + + USART_InitStructure.USART_BaudRate = baudrate; + USART_InitStructure.USART_WordLength = USART_WordLength_8b; + USART_InitStructure.USART_StopBits = USART_StopBits_1; + USART_InitStructure.USART_Parity = USART_Parity_No; + USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; + USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; + USART_Init(HALL_SERIAL_USART, &USART_InitStructure); + + DMA_Cmd(HALL_DMA_Stream_RX, DISABLE); + USART_DMACmd(HALL_SERIAL_USART, USART_DMAReq_Rx, DISABLE); + DMA_DeInit(HALL_DMA_Stream_RX); + + DMA_InitTypeDef DMA_InitStructure; + hallDMAFifo.clear(); + + USART_ITConfig(HALL_SERIAL_USART, USART_IT_RXNE, DISABLE); + USART_ITConfig(HALL_SERIAL_USART, USART_IT_TXE, DISABLE); + + DMA_InitStructure.DMA_Channel = HALL_DMA_Channel; + DMA_InitStructure.DMA_PeripheralBaseAddr = CONVERT_PTR_UINT(&HALL_SERIAL_USART->DR); + DMA_InitStructure.DMA_Memory0BaseAddr = CONVERT_PTR_UINT(hallDMAFifo.buffer()); + DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; + DMA_InitStructure.DMA_BufferSize = hallDMAFifo.size(); + DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; + DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; + DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; + DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; + DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; + DMA_InitStructure.DMA_Priority = DMA_Priority_Low; + DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; + DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; + DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; + DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; + DMA_Init(HALL_DMA_Stream_RX, &DMA_InitStructure); + USART_DMACmd(HALL_SERIAL_USART, USART_DMAReq_Rx, ENABLE); + USART_Cmd(HALL_SERIAL_USART, ENABLE); + DMA_Cmd(HALL_DMA_Stream_RX, ENABLE); + + reset_hall_stick(); +} + +void HallSendBuffer(uint8_t * buffer, uint32_t count) +{ + for(int idx = 0; buffer != HallCmd && idx < count; idx++) + { + HallCmd[idx] = buffer[idx]; + } + + DMA_InitTypeDef DMA_InitStructure; + DMA_DeInit(HALL_DMA_Stream_TX); + DMA_InitStructure.DMA_Channel = HALL_DMA_Channel; + DMA_InitStructure.DMA_PeripheralBaseAddr = CONVERT_PTR_UINT(&HALL_SERIAL_USART->DR); + DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; + DMA_InitStructure.DMA_Memory0BaseAddr = CONVERT_PTR_UINT(HallCmd); + DMA_InitStructure.DMA_BufferSize = count; + DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; + DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; + DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; + DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; + DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; + DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; + DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; + DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; + DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; + DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; + DMA_Init(HALL_DMA_Stream_TX, &DMA_InitStructure); + DMA_Cmd(HALL_DMA_Stream_TX, ENABLE); + USART_DMACmd(HALL_SERIAL_USART, USART_DMAReq_Tx, ENABLE); + DMA_ITConfig(HALL_DMA_Stream_TX, DMA_IT_TC, ENABLE); + + /* enable interrupt and set it's priority */ + NVIC_EnableIRQ(HALL_SERIAL_TX_DMA_Stream_IRQn); + NVIC_SetPriority(HALL_SERIAL_TX_DMA_Stream_IRQn, 7); +} + +extern "C" void HALL_TX_DMA_Stream_IRQHandler(void) +{ + DEBUG_INTERRUPT(INT_TELEM_DMA); + + if (DMA_GetITStatus(HALL_DMA_Stream_TX, HALL_DMA_TX_FLAG_TC)) + { + DMA_ClearITPendingBit(HALL_DMA_Stream_TX, HALL_DMA_TX_FLAG_TC); + HALL_SERIAL_USART->CR1 |= USART_CR1_TCIE; + } +} + +uint8_t HallGetByte(uint8_t * byte) +{ + return hallDMAFifo.pop(*byte); +} + + +void reset_hall_stick( void ) +{ + unsigned short crc16 = 0xffff; + + HallCmd[0] = 0x55; + HallCmd[1] = 0xD1; + HallCmd[2] = 0x01; + HallCmd[3] = 0x01; + + crc16 = calc_crc16(HallCmd, 4); + + HallCmd[4] = crc16 & 0xff; + HallCmd[5] = crc16 >>8 & 0xff; + + HallSendBuffer( HallCmd, 6); +} + +void get_hall_config( void ) +{ + unsigned short crc16 = 0xffff; + + HallCmd[0] = 0x55; + HallCmd[1] = 0xD1; + HallCmd[2] = 0x01; + HallCmd[3] = 0x00; + + crc16 = calc_crc16(HallCmd, 4); // 2B 2C + + HallCmd[4] = crc16 & 0xff; + HallCmd[5] = crc16 >>8 & 0xff ; + + HallSendBuffer( HallCmd, 6); +} + +void get_hall_firmware_info() +{ + unsigned short crc16 = 0xffff; + + HallCmd[0] = 0x55; + HallCmd[1] = 0xA2; + HallCmd[2] = 0x00; + + crc16 = calc_crc16(HallCmd, 3); // BE 02 + + HallCmd[3] = crc16 & 0xff; + HallCmd[4] = crc16 >>8 & 0xff ; + + HallSendBuffer( HallCmd, 5); +} + +void hallStickUpdatefwEnd( void ) +{ + unsigned short crc16 = 0xffff; + + HallCmd[0] = 0x55; + HallCmd[1] = 0xA2; + HallCmd[2] = 0x01; + HallCmd[3] = 0x07; + + crc16 = calc_crc16(HallCmd, 4); + + HallCmd[4] = crc16 & 0xff; + HallCmd[5] = crc16 >>8 & 0xff ; + + HallSendBuffer( HallCmd, 6);// 94 DD +} + +static int parse_ps_state = 0; +void Parse_Character(STRUCT_HALL *hallBuffer, unsigned char ch) +{ + if (parse_ps_state != 0) return; + parse_ps_state = 1; + + switch( hallBuffer->status ) + { + case GET_START: + { + if ( HALL_PROTOLO_HEAD == ch ) + { + hallBuffer->head = HALL_PROTOLO_HEAD; + hallBuffer->status = GET_ID; + hallBuffer->msg_OK = 0; + } + break; + } + case GET_ID: + { + hallBuffer->hallID.ID = ch; + hallBuffer->status = GET_LENGTH; + break; + } + case GET_LENGTH: + { + hallBuffer->length = ch; + hallBuffer->dataIndex = 0; + hallBuffer->status = GET_DATA; + if( 0 == hallBuffer->length ) + { + hallBuffer->status = GET_CHECKSUM; + hallBuffer->checkSum=0; + } + break; + } + case GET_DATA: + { + hallBuffer->data[hallBuffer->dataIndex++] = ch; + if( hallBuffer->dataIndex >= hallBuffer->length) + { + hallBuffer->checkSum = 0; + hallBuffer->dataIndex = 0; + hallBuffer->status = GET_STATE; + } + break; + } + case GET_STATE: + { + hallBuffer->checkSum = 0; + hallBuffer->dataIndex = 0; + hallBuffer->status = GET_CHECKSUM; + } + case GET_CHECKSUM: + { + hallBuffer->checkSum |= ch << ((hallBuffer->dataIndex++) * 8); + if( hallBuffer->dataIndex >= 2 ) + { + hallBuffer->dataIndex = 0; + hallBuffer->status = CHECKSUM; + } + else + { + break; + } + } + case CHECKSUM: + { + if(hallBuffer->checkSum == calc_crc16( (U8*)&hallBuffer->head, hallBuffer->length + 3 ) ) + { + hallBuffer->msg_OK = 1; + goto Label_restart; + } + else + { + goto Label_error; + } + } + } + + goto exit; + + Label_error: + Label_restart: + hallBuffer->status = GET_START; +exit: parse_ps_state = 0; + return ; +} + + +void convert_hall_to_adcVaule( void ) +{ + uint16_t value; + + for ( uint8_t i = 0; i < 4; i++ ) + { + if (Channel.channel[i] < StickCallbration[i].mid) + { + value = StickCallbration[i].mid - StickCallbration[i].min; + value = ( MIDDLE_ADC_CHANNLE_VALUE * (StickCallbration[i].mid - Channel.channel[i] ) ) / ( value ); + + if (value >= MIDDLE_ADC_CHANNLE_VALUE ) { + value = MIDDLE_ADC_CHANNLE_VALUE; + } + + HallChVal[i] = MIDDLE_ADC_CHANNLE_VALUE - value; + } + else + { + value = StickCallbration[i].max - StickCallbration[i].mid; + + value = ( MIDDLE_ADC_CHANNLE_VALUE * (Channel.channel[i] - StickCallbration[i].mid ) ) / (value ); + + if (value >= MIDDLE_ADC_CHANNLE_VALUE ) + { + value = MIDDLE_ADC_CHANNLE_VALUE; + } + + HallChVal[i] = MIDDLE_ADC_CHANNLE_VALUE + value + 1; + } + } +} + +uint8_t HallGetByteTx(uint8_t * byte) +{ + return hallStickTxFifo.pop(*byte); +} + +bool isHallStickUpdateFirmware( void ) +{ + return hallStickSendState == HALLSTICK_STATE_UPDATE_FW; +} + +void hallstick_send_by_state( void ) +{ + switch ( hallStickSendState ) + { + case HALLSTICK_STATE_SEND_RESET: + TRACE("HALLSTICK_STATE_SEND_RESET"); + reset_hall_stick(); + hallStickSendState = HALLSTICK_STATE_GET_FIRMWARE; + break; + + case HALLSTICK_STATE_GET_FIRMWARE: + TRACE("HALLSTICK_STATE_GET_FIRMWARE"); + get_hall_firmware_info(); + hallStickSendState = HALLSTICK_STATE_UPDATE_FW; + break; + + case HALLSTICK_STATE_UPDATE_FW: + return; + + default: break; + } +} + +void hallstick_wait_send_done(uint32_t timeOut) +{ + static unsigned int startTime = get_tmr10ms(); + + while ( hallStickSendState != HALLSTICK_SEND_STATE_IDLE ) + { + if ( (get_tmr10ms() - startTime) < timeOut ) + { + break; + } + } +} + +static uint32_t HallProtocolCount = 0; +bool isHallProtocolTxMsgOK( void ) +{ + bool isMsgOK = HallProtocolCount != 0; + HallProtocolCount = 0; + return isMsgOK; +} + +/* HallStick send main program */ +void hallStick_GetTxDataFromUSB( void ) +{ + unsigned char abyte; + uint8_t *pt = (uint8_t *)&HallProtocolTx; + + while( HallGetByteTx(&abyte) ) + { + Parse_Character(&HallProtocolTx, abyte ); + + if ( HallProtocolTx.msg_OK ) + { + HallProtocolTx.msg_OK = 0; + + pt[HallProtocolTx.length + 3] = HallProtocolTx.checkSum & 0xFF; + pt[HallProtocolTx.length + 4] = HallProtocolTx.checkSum >> 8; + + //TRACE("USB: %02X %02X %02X ...%04X; CRC:%04X", pt[0], pt[1], pt[2], + // HallProtocolTx.checkSum, calc_crc16(pt, HallProtocolTx.length+3)); + + switch ( HallProtocolTx.hallID.hall_Id.receiverID ) + { + case TRANSFER_DIR_TXMCU: + break; + + case TRANSFER_DIR_HALLSTICK: + onFlySkyUsbDownloadStart(TRANSFER_DIR_HALLSTICK); + + if ( 0xA2 == HallProtocolTx.hallID.ID ) + { + if ( 0 == HallProtocolTx.length ) // 55 A2 00 BE 02 + { + hallStickSendState = HALLSTICK_STATE_SEND_RESET; + break; + } + + else if ( 0x01 == HallProtocolTx.length && 0x07 == HallProtocol.data[0] ) + { + hallStickSendState = HALLSTICK_SEND_STATE_IDLE; + } + } + HallSendBuffer( pt, HallProtocolTx.length + 3 + 2 ); + break; + + case TRANSFER_DIR_RFMODULE: + onFlySkyUsbDownloadStart(TRANSFER_DIR_RFMODULE); + + if ( 0xAE == HallProtocolTx.hallID.ID && HallProtocolTx.length == 0 ) + { // 55 AE 00 D3 47 + onIntmoduleUsbDownloadStart(INTERNAL_MODULE); + break; + } + + //if ( isFlySkyUsbDownload() ) + { + intmoduleSendBufferDMA( pt, HallProtocolTx.length + 3 + 2 ); + } + break; + } + } + } + + if ( !usbPlugged() ) + { + onFlySkyUsbDownloadStart(0); + } +} + + +/* Run it in 1ms timer routine */ +void hall_stick_loop(void) +{ + unsigned char ch; + static unsigned int getCfgTime = get_tmr10ms(); + uint32_t printf_log_now = 0; + + hallStick_GetTxDataFromUSB(); + + hallstick_send_by_state(); + + while( HallGetByte(&ch) ) + { + HallProtocol.index++; + + Parse_Character(&HallProtocol, ch ); + + if ( HallProtocol.msg_OK ) + { + HallProtocol.msg_OK = 0; + HallProtocol.stickState = HallProtocol.data[HallProtocol.length - 1]; + + switch ( HallProtocol.hallID.hall_Id.receiverID ) + { + case TRANSFER_DIR_TXMCU: + if ( 0x0e == HallProtocol.hallID.hall_Id.packetID ) + { + memcpy(&StickCallbration, HallProtocol.data, sizeof(StickCallbration)); + } + else if ( 0x0c == HallProtocol.hallID.hall_Id.packetID ) + { + memcpy(&Channel, HallProtocol.data, sizeof(Channel)); + + convert_hall_to_adcVaule(); + } + break; + + case TRANSFER_DIR_HOSTPC: + if ( 0x01 == HallProtocol.length && + ( 0x05 == HallProtocol.data[0] || 0x06 == HallProtocol.data[0]) ) + { + hallStickSendState = HALLSTICK_SEND_STATE_IDLE; + } + case TRANSFER_DIR_HALLSTICK: + HallProtocolCount++; + uint8_t *pt = (uint8_t*)&HallProtocol; + //TRACE("HALL: %02X %02X %02X ...%04X", pt[0], pt[1], pt[2], HallProtocol.checkSum); + pt[HallProtocol.length + 3] = HallProtocol.checkSum & 0xFF; + pt[HallProtocol.length + 4] = HallProtocol.checkSum >> 8; + usbDownloadTransmit( pt, HallProtocol.length + 5 ); + break; + } + //printf_log_now = 1; + } + } + + if ((get_tmr10ms() - getCfgTime) > 200) + { + if ( (0 == StickCallbration[0].max) && (0 == StickCallbration[0].mid) && (0== StickCallbration[0].min) ) + { + get_hall_config(); + getCfgTime = get_tmr10ms(); + } + + if ( printf_log_now != 0) + { + TRACE_NOCRLF("Hall(%0d):", FLYSKY_HALL_BAUDRATE); + for (int idx = 0; idx < HallProtocol.length + 5; idx++) + { + TRACE_NOCRLF(" %02X", *((uint8_t*)&HallProtocol + idx)); + } + TRACE(";"); + } + } +} diff --git a/radio/src/targets/nv14/hallStick_driver.h b/radio/src/targets/nv14/hallStick_driver.h new file mode 100644 index 000000000..efc156511 --- /dev/null +++ b/radio/src/targets/nv14/hallStick_driver.h @@ -0,0 +1,221 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +/*************************************************************************************************** + +***************************************************************************************************/ +#ifndef __HALLSTICK_DRIVER_H__ + #define __HALLSTICK_DRIVER_H__ +/*************************************************************************************************** + +***************************************************************************************************/ + #ifdef EXTERN + #undef EXTERN + #endif + + #ifdef __HALLSTICK_DRIVER_C__ + #define EXTERN + #else + #define EXTERN extern + #endif +/*************************************************************************************************** + +***************************************************************************************************/ +#define HALLSTICK_BUFF_SIZE ( 512 ) +#define FLYSKY_HALL_BAUDRATE ( 921600 ) +#define FLYSKY_HALL_CHANNEL_COUNT ( 4 ) + +#define MAX_ADC_CHANNEL_VALUE ( 4095 ) +#define MIN_ADC_CHANNLE_VALUE ( 0 ) +#define MIDDLE_ADC_CHANNLE_VALUE ( 2047 ) + + + +extern unsigned short HallChVal[FLYSKY_HALL_CHANNEL_COUNT]; + +typedef struct +{ + signed short min; + signed short mid; + signed short max; +} STRUCT_STICK_CALIBRATION; + +typedef struct +{ + STRUCT_STICK_CALIBRATION sticksCalibration[4]; + unsigned char reststate; + unsigned short CRC16; +}STRUCT_STICK_CALIBRATION_PACK; + +typedef struct +{ + signed short channel[4]; +}STRUCT_HALL_CHANNEL; + +typedef struct +{ + STRUCT_HALL_CHANNEL channel; + unsigned char stickState; + unsigned short CRC16; +}STRUCT_CHANNEL_PACK; + +typedef union +{ + STRUCT_STICK_CALIBRATION_PACK channelPack; + STRUCT_CHANNEL_PACK sticksCalibrationPack; +}UNION_DATA; + +typedef struct +{ + unsigned char start; + unsigned char senderID:2; + unsigned char receiverID:2; + unsigned char packetID:4; + unsigned char length; + UNION_DATA payload; +} STRUCT_HALLDATA; + +typedef struct +{ + unsigned char senderID:2; + unsigned char receiverID:2; + unsigned char packetID:4; +} STRUCT_HALLID; + +typedef union +{ + STRUCT_HALLID hall_Id; + unsigned char ID; +}STRUCT_ID; + + +typedef union +{ + STRUCT_HALLDATA halldat; + unsigned char buffer[30]; +}UNION_HALLDATA; + + +typedef struct +{ + unsigned char head; + STRUCT_ID hallID; + unsigned char length; + unsigned char data[HALLSTICK_BUFF_SIZE]; + unsigned char reserved[15]; + unsigned short checkSum; + unsigned char stickState; + unsigned char startIndex; + unsigned char endIndex; + unsigned char index; + unsigned char dataIndex; + unsigned char deindex; + unsigned char completeFlg; + unsigned char status; + unsigned char recevied; + unsigned char msg_OK; +} STRUCT_HALL; + +enum +{ + GET_START = 0, + GET_ID, + GET_LENGTH, + GET_DATA, + GET_STATE, + GET_CHECKSUM, + CHECKSUM, +}; + +enum HALLSTICK_SEND_STATE_E { + HALLSTICK_SEND_STATE_IDLE, + HALLSTICK_STATE_SEND_RESET, + HALLSTICK_STATE_GET_CONFIG, + HALLSTICK_STATE_GET_FIRMWARE, + HALLSTICK_STATE_UPDATE_FW +}; + +enum TRANSFER_DIR_E { + TRANSFER_DIR_HALLSTICK, + TRANSFER_DIR_TXMCU, + TRANSFER_DIR_HOSTPC, + TRANSFER_DIR_RFMODULE, +}; + +#define HALL_PROTOLO_HEAD ( 0x55 ) + +#define HALL_SERIAL_USART UART4 +#define HALL_SERIAL_GPIO GPIOA +#define HALL_DMA_Channel DMA_Channel_4 +#define HALL_SERIAL_TX_GPIO_PIN GPIO_Pin_0 // PA.00 +#define HALL_SERIAL_RX_GPIO_PIN GPIO_Pin_1 // PA.01 +#define HALL_SERIAL_TX_GPIO_PinSource GPIO_PinSource0 +#define HALL_SERIAL_RX_GPIO_PinSource GPIO_PinSource1 +#define HALL_SERIAL_GPIO_AF GPIO_AF_UART4 + +#define HALL_SERIAL_RCC_APB2Periph RCC_APB2Periph_USART6 +#define HALL_RCC_AHB1Periph RCC_AHB1Periph_DMA1 +#define HALL_RCC_APB1Periph RCC_APB1Periph_UART4 + +#define HALL_SERIAL_USART_IRQHandler UART4_IRQHandler +#define HALL_SERIAL_USART_IRQn UART4_IRQn +#define HALL_SERIAL_RX_DMA_Stream_IRQn DMA1_Stream2_IRQn +#define HALL_SERIAL_TX_DMA_Stream_IRQn DMA1_Stream4_IRQn +#define HALL_DMA_Stream_RX DMA1_Stream2 +#define HALL_DMA_Stream_TX DMA1_Stream4 +#define HALL_DMA_TX_FLAG_TC DMA_IT_TCIF4 + +#define HALL_RX_DMA_Stream_IRQHandler DMA1_Stream2_IRQHandler +#define HALL_TX_DMA_Stream_IRQHandler DMA1_Stream4_IRQHandler + +//#include "fifo.h" +//extern Fifo hallStickTxFifo; + + +/*************************************************************************************************** + interface function +***************************************************************************************************/ +extern void reset_hall_stick( void ); +extern void get_hall_config( void ); +extern void hall_stick_init(uint32_t baudrate); +extern void hall_stick_loop( void ); +extern uint16_t get_hall_adc_value(uint8_t ch); +extern void hallSerialPutc(char c); +unsigned short calc_crc16(void *pBuffer,unsigned char BufferSize); +void Parse_Character(STRUCT_HALL *hallBuffer, unsigned char ch); +extern bool isFlySkyUsbDownload(void); +extern void onFlySkyUsbDownloadStart(uint8_t fw_state); +#endif + + + + + + + + + + + + + + + + diff --git a/radio/src/targets/nv14/haptic_driver.cpp b/radio/src/targets/nv14/haptic_driver.cpp new file mode 100644 index 000000000..b4dc1db64 --- /dev/null +++ b/radio/src/targets/nv14/haptic_driver.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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" + +void hapticOff(void) +{ + HAPTIC_TIMER_COMPARE_VALUE = 0; +} + +void hapticOn(uint32_t pwmPercent) +{ + if (pwmPercent > 100) { + pwmPercent = 100; + } + HAPTIC_TIMER_COMPARE_VALUE = pwmPercent; +} + +void hapticInit(void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = HAPTIC_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(HAPTIC_GPIO, &GPIO_InitStructure); + + GPIO_PinAFConfig(HAPTIC_GPIO, HAPTIC_GPIO_PinSource, HAPTIC_GPIO_AF); + + HAPTIC_GPIO_TIMER->ARR = 100; + HAPTIC_GPIO_TIMER->PSC = (PERI2_FREQUENCY * TIMER_MULT_APB2) / 10000 - 1; + HAPTIC_GPIO_TIMER->CCMR1 = HAPTIC_TIMER_MODE; // PWM + HAPTIC_GPIO_TIMER->CCER = HAPTIC_TIMER_OUTPUT_ENABLE; + + hapticOff(); + + HAPTIC_GPIO_TIMER->EGR = 0; + HAPTIC_GPIO_TIMER->CR1 = TIM_CR1_CEN; // counter enable +} + +void hapticDone(void) +{ + hapticOff(); + RCC_AHB1PeriphClockCmd(HAPTIC_RCC_AHB1Periph, DISABLE); +} diff --git a/radio/src/targets/nv14/keys_driver.cpp b/radio/src/targets/nv14/keys_driver.cpp new file mode 100644 index 000000000..68ec10901 --- /dev/null +++ b/radio/src/targets/nv14/keys_driver.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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" + +uint32_t readKeys() +{ + return 0; +} + +uint32_t readTrims() +{ + uint32_t result = 0; + + if (TRIMS_GPIO_REG_LHL & TRIMS_GPIO_PIN_LHL) + result |= 0x01; + if (TRIMS_GPIO_REG_LHR & TRIMS_GPIO_PIN_LHR) + result |= 0x02; + if (TRIMS_GPIO_REG_LVD & TRIMS_GPIO_PIN_LVD) + result |= 0x04; + if (TRIMS_GPIO_REG_LVU & TRIMS_GPIO_PIN_LVU) + result |= 0x08; + if (TRIMS_GPIO_REG_RVD & TRIMS_GPIO_PIN_RVD) + result |= 0x10; + if (TRIMS_GPIO_REG_RVU & TRIMS_GPIO_PIN_RVU) + result |= 0x20; + if (TRIMS_GPIO_REG_RHL & TRIMS_GPIO_PIN_RHL) + result |= 0x40; + if (TRIMS_GPIO_REG_RHR & TRIMS_GPIO_PIN_RHR) + result |= 0x80; + + // TRACE("readTrims(): result=0x%02x", result); + + return result; +} + +uint8_t trimDown(uint8_t idx) +{ + return readTrims() & (1 << idx); +} + +bool keyDown() +{ + return 0; +} + +/* TODO common to ARM */ +void readKeysAndTrims() +{ + int i; + + uint8_t index = 0; + uint32_t in = readKeys(); + for (i = 0; i < TRM_BASE; i++) { + keys[index++].input(in & (1 << i)); + } + + in = readTrims(); + for (i = 1; i <= 1 << (TRM_LAST-TRM_BASE); i <<= 1) { + keys[index++].input(in & i); + } +} + +#if !defined(BOOT) +uint32_t switchState(uint8_t index) +{ + uint16_t value = getAnalogValue(SWITCH_FIRST + index / 3); + uint8_t position; + + if (value < 1024) + position = 0; + else if (value > 3 * 1024) + position = 2; + else + position = 1; + + return position == (index % 3); +} +#endif + +void monitorInit() +{ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; + + GPIO_InitStructure.GPIO_Pin = VBUS_MONITOR_PIN; + GPIO_Init(GPIOJ, &GPIO_InitStructure); +} + +void keysInit() +{ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; + + GPIO_InitStructure.GPIO_Pin = KEYS_GPIOB_PINS; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = KEYS_GPIOC_PINS; + GPIO_Init(GPIOC, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = KEYS_GPIOD_PINS; + GPIO_Init(GPIOD, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = KEYS_GPIOG_PINS; + GPIO_Init(GPIOG, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = KEYS_GPIOH_PINS; + GPIO_Init(GPIOH, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = KEYS_GPIOJ_PINS; + GPIO_Init(GPIOJ, &GPIO_InitStructure); +} diff --git a/radio/src/targets/nv14/lcd_driver.cpp b/radio/src/targets/nv14/lcd_driver.cpp new file mode 100644 index 000000000..79bae4ab3 --- /dev/null +++ b/radio/src/targets/nv14/lcd_driver.cpp @@ -0,0 +1,1714 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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" + +uint8_t LCD_FIRST_FRAME_BUFFER[DISPLAY_BUFFER_SIZE] __SDRAM; +uint8_t LCD_SECOND_FRAME_BUFFER[DISPLAY_BUFFER_SIZE] __SDRAM; +uint8_t LCD_BACKUP_FRAME_BUFFER[DISPLAY_BUFFER_SIZE] __SDRAM; + +uint8_t currentLayer = LCD_FIRST_LAYER; + +lcdSpiInitFucPtr lcdSpiInitFuc; + +volatile U8 LCD_ReadBuffer[24] = { 0, 0 }; + + +static void LCD_Delay( void ) +{ + volatile unsigned int i; + + for( i = 0; i < 20; i++ ) + { + ; + } +} + +static void LCD_AF_GPIOConfig(void) +{ + /* + ----------------------------------------------------------------------------- + LCD_CLK <-> PG.07 | LCD_HSYNC <-> PI.12 | LCD_R3 <-> PJ.02 | LCD_G5 <-> PK.00 + | LCD VSYNC <-> PI.13 | LCD_R4 <-> PJ.03 | LCD_G6 <-> PK.01 + | | LCD_R5 <-> PJ.04 | LCD_G7 <-> PK.02 + | | LCD_R6 <-> PJ.05 | LCD_B4 <-> PK.03 + | | LCD_R7 <-> PJ.06 | LCD_B5 <-> PK.04 + | | LCD_G2 <-> PJ.09 | LCD_B6 <-> PK.05 + | | LCD_G3 <-> PJ.10 | LCD_B7 <-> PK.06 + | | LCD_G4 <-> PJ.11 | LCD_DE <-> PK.07 + | | LCD_B3 <-> PJ.15 | + */ + + // GPIOG configuration + GPIO_PinAFConfig(GPIOG, GPIO_PinSource7, GPIO_AF_LTDC); + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; + GPIO_Init(GPIOG, &GPIO_InitStructure); + + // GPIOI configuration + GPIO_PinAFConfig(GPIOI, GPIO_PinSource12, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOI, GPIO_PinSource13, GPIO_AF_LTDC); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13; + GPIO_Init(GPIOI, &GPIO_InitStructure); + + // GPIOJ configuration + GPIO_PinAFConfig(GPIOJ, GPIO_PinSource2, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOJ, GPIO_PinSource3, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOJ, GPIO_PinSource4, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOJ, GPIO_PinSource5, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOJ, GPIO_PinSource6, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOJ, GPIO_PinSource9, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOJ, GPIO_PinSource10, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOJ, GPIO_PinSource11, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOJ, GPIO_PinSource15, GPIO_AF_LTDC); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_15; + GPIO_Init(GPIOJ, &GPIO_InitStructure); + + // GPIOK configuration + GPIO_PinAFConfig(GPIOK, GPIO_PinSource0, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOK, GPIO_PinSource1, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOK, GPIO_PinSource2, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOK, GPIO_PinSource3, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOK, GPIO_PinSource4, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOK, GPIO_PinSource5, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOK, GPIO_PinSource6, GPIO_AF_LTDC); + GPIO_PinAFConfig(GPIOK, GPIO_PinSource7, GPIO_AF_LTDC); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; + GPIO_Init(GPIOK, &GPIO_InitStructure); +} + +static void lcdSpiConfig(void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + + + GPIO_InitStructure.GPIO_Pin = LCD_SPI_SCK_GPIO_PIN | LCD_SPI_MOSI_GPIO_PIN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_Init(LCD_SPI_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = LCD_SPI_CS_GPIO_PIN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_Init(LCD_SPI_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = LCD_NRST_GPIO_PIN; + GPIO_Init(LCD_NRST_GPIO, &GPIO_InitStructure); + + /* Set the chip select pin aways low */ + CLR_LCD_CS(); +} + +void lcdDelay() +{ + delay_01us(1); +} + +unsigned char LCD_ReadByteOnFallingEdge( void ) +{ + unsigned int i; + unsigned char ReceiveData = 0; + + SET_LCD_DATA(); + SET_LCD_DATA_INPUT(); + + for( i = 0; i < 8; i++ ) + { + LCD_DELAY(); + SET_LCD_CLK(); + LCD_DELAY(); + LCD_DELAY(); + ReceiveData <<= 1; + + CLR_LCD_CLK(); + LCD_DELAY(); + LCD_DELAY(); + if( READ_LCD_DATA_PIN() ) + { + ReceiveData |= 0x01; + } + } + + SET_LCD_DATA_OUTPUT(); + + return( ReceiveData ); +} + + +static void lcdWriteByte(uint8_t data_enable, uint8_t byte) +{ + LCD_SCK_LOW(); + lcdDelay(); + + if (data_enable) + { + LCD_MOSI_HIGH(); + } + else + { + LCD_MOSI_LOW(); + } + + LCD_SCK_HIGH(); + lcdDelay(); + + for ( int i = 0; i < 8; i++ ) + { + LCD_SCK_LOW(); + lcdDelay(); + + if (byte & 0x80) + { + LCD_MOSI_HIGH(); + } + else + { + LCD_MOSI_LOW(); + } + + LCD_SCK_HIGH(); + byte <<= 1; + + lcdDelay(); + } + + LCD_SCK_LOW(); +} + +unsigned char LCD_ReadByte( void ) +{ + unsigned int i; + unsigned char ReceiveData = 0; + + SET_LCD_DATA(); + SET_LCD_DATA_INPUT(); + for( i = 0; i < 8; i++ ) + { + CLR_LCD_CLK(); + LCD_DELAY(); + LCD_DELAY(); + ReceiveData <<= 1; + SET_LCD_CLK(); + LCD_DELAY(); + LCD_DELAY(); + if( READ_LCD_DATA_PIN() ) + { + ReceiveData |= 0x01; + } + } + CLR_LCD_CLK(); + SET_LCD_DATA_OUTPUT(); + return( ReceiveData ); +} + + + +unsigned char LCD_ReadRegister( unsigned char Register ) +{ + unsigned char ReadData = 0; + + lcdWriteByte( 0, Register ); + LCD_DELAY(); + LCD_DELAY(); + ReadData = LCD_ReadByte(); + return( ReadData ); +} + +void lcdWriteCommand(uint8_t command) +{ + lcdWriteByte(0, command); +} + +void lcdWriteData(uint8_t data) +{ + lcdWriteByte(1, data); +} + +void LCD_HX8357D_Init( void ) +{ +#if 0 +lcdWriteCommand(0x11); +delay_ms(200); + +lcdWriteCommand(0xB9); +lcdWriteData(0xFF); +lcdWriteData(0x83); +lcdWriteData(0x57); + +lcdWriteCommand(0xB1); +lcdWriteData(0x00); +lcdWriteData(0x14); +lcdWriteData(0x1C); +lcdWriteData(0x1C); +lcdWriteData(0xC7); +lcdWriteData(0x21); + +lcdWriteCommand(0xB3); +lcdWriteData(0x83); +lcdWriteData(0x00); +lcdWriteData(0x06); +lcdWriteData(0x06); + +lcdWriteCommand(0xB4); +lcdWriteData(0x11); +lcdWriteData(0x40); +lcdWriteData(0x00); +lcdWriteData(0x2A); +lcdWriteData(0x2A); +lcdWriteData(0x20); +lcdWriteData(0x4E); + +lcdWriteCommand(0xB5); +lcdWriteData(0x03); +lcdWriteData(0x03); + +lcdWriteCommand(0xB6); +lcdWriteData(0x38); + +lcdWriteCommand(0xC0); +lcdWriteData(0x24); +lcdWriteData(0x24); +lcdWriteData(0x00); +lcdWriteData(0x10); +lcdWriteData(0xc8); +lcdWriteData(0x08); + +lcdWriteCommand(0xC2); +lcdWriteData(0x00); +lcdWriteData(0x08); +lcdWriteData(0x04); + + +lcdWriteCommand(0xCC); +lcdWriteData(0x00); + +//GAMMA 2.5" +lcdWriteCommand(0xE0); +lcdWriteData(0x00); +lcdWriteData(0x06); +lcdWriteData(0x0D); +lcdWriteData(0x18); +lcdWriteData(0x23); +lcdWriteData(0x3B); +lcdWriteData(0x45); +lcdWriteData(0x4D); +lcdWriteData(0x4D); +lcdWriteData(0x46); +lcdWriteData(0x40); +lcdWriteData(0x37); +lcdWriteData(0x34); +lcdWriteData(0x2F); +lcdWriteData(0x2B); +lcdWriteData(0x21); +lcdWriteData(0x00); +lcdWriteData(0x06); +lcdWriteData(0x0D); +lcdWriteData(0x18); +lcdWriteData(0x23); +lcdWriteData(0x3B); +lcdWriteData(0x45); +lcdWriteData(0x4D); +lcdWriteData(0x4D); +lcdWriteData(0x46); +lcdWriteData(0x40); +lcdWriteData(0x37); +lcdWriteData(0x34); +lcdWriteData(0x2F); +lcdWriteData(0x2B); +lcdWriteData(0x21); +lcdWriteData(0x00); +lcdWriteData(0x01); + +lcdWriteCommand(0x3A); +lcdWriteData(0x66); + +lcdWriteCommand(0x36); +lcdWriteData(0x08); + + +lcdWriteCommand(0x29); +delay_ms(10); +#else + delay_ms(50); + lcdWriteCommand(0xB9); //EXTC + lcdWriteData(0xFF); //EXTC + lcdWriteData(0x83); //EXTC + lcdWriteData(0x57); //EXTC + delay_ms(5); + + + lcdWriteCommand(0x3A); + lcdWriteData(0x65); //262k + + + lcdWriteCommand(0xB3); //COLOR FORMAT + lcdWriteData(0x83); //SDO_EN,BYPASS,EPF[1:0],0,0,RM,DM //43 + + lcdWriteCommand(0xB6); // + lcdWriteData(0x5a); //VCOMDC + + lcdWriteCommand(0x35); // TE ON + lcdWriteData(0x01); + + + lcdWriteCommand(0xB0); + lcdWriteData(0x68); //70Hz + + lcdWriteCommand(0xCC); // Set Panel + lcdWriteData(0x00); // + + lcdWriteCommand(0xB1); // + lcdWriteData(0x00); // + lcdWriteData(0x11); //BT + lcdWriteData(0x1C); //VSPR + lcdWriteData(0x1C); //VSNR + lcdWriteData(0x83); //AP + lcdWriteData(0x48); //FS 0xAA + + lcdWriteCommand(0xB4); // + lcdWriteData(0x02); //NW + lcdWriteData(0x40); //RTN + lcdWriteData(0x00); //DIV + lcdWriteData(0x2A); //DUM + lcdWriteData(0x2A); //DUM + lcdWriteData(0x0D); //GDON + lcdWriteData(0x78); //GDOFF 0x4F + lcdWriteCommand(0xC0); //STBA + lcdWriteData(0x50); //OPON + lcdWriteData(0x50); //OPON + lcdWriteData(0x01); // + lcdWriteData(0x3C); // + lcdWriteData(0x1E); // + lcdWriteData(0x08); //GEN + + /* + lcdWriteCommand(0xE0); // + lcdWriteData(0x02); //1 + lcdWriteData(0x06); //2 + lcdWriteData(0x09); //3 + lcdWriteData(0x1C); //4 + lcdWriteData(0x27); //5 + lcdWriteData(0x3C); //6 + lcdWriteData(0x48); //7 + lcdWriteData(0x50); //8 + lcdWriteData(0x49); //9 + lcdWriteData(0x42); //10 + lcdWriteData(0x3E); //11 + lcdWriteData(0x35); //12 + lcdWriteData(0x31); //13 + lcdWriteData(0x2A); //14 + lcdWriteData(0x28); //15 + lcdWriteData(0x03); //16 + lcdWriteData(0x02); //17 v1 + lcdWriteData(0x06); //18 + lcdWriteData(0x09); //19 + lcdWriteData(0x1C); //20 + lcdWriteData(0x27); //21 + lcdWriteData(0x3C); //22 + lcdWriteData(0x48); //23 + lcdWriteData(0x50); //24 + lcdWriteData(0x49); //25 + lcdWriteData(0x42); //26 + lcdWriteData(0x3E); //27 + lcdWriteData(0x35); //28 + lcdWriteData(0x31); //29 + lcdWriteData(0x2A); //30 + lcdWriteData(0x28); //31 + lcdWriteData(0x03); //32 + lcdWriteData(0x44); //33 + lcdWriteData(0x01); //34 + */ + lcdWriteCommand(0xE0); +lcdWriteData(0x00); +lcdWriteData(0x06); +lcdWriteData(0x0D); +lcdWriteData(0x18); +lcdWriteData(0x23); +lcdWriteData(0x3B); +lcdWriteData(0x45); +lcdWriteData(0x4D); +lcdWriteData(0x4D); +lcdWriteData(0x46); +lcdWriteData(0x40); +lcdWriteData(0x37); +lcdWriteData(0x34); +lcdWriteData(0x2F); +lcdWriteData(0x2B); +lcdWriteData(0x21); +lcdWriteData(0x00); +lcdWriteData(0x06); +lcdWriteData(0x0D); +lcdWriteData(0x18); +lcdWriteData(0x23); +lcdWriteData(0x3B); +lcdWriteData(0x45); +lcdWriteData(0x4D); +lcdWriteData(0x4D); +lcdWriteData(0x46); +lcdWriteData(0x40); +lcdWriteData(0x37); +lcdWriteData(0x34); +lcdWriteData(0x2F); +lcdWriteData(0x2B); +lcdWriteData(0x21); +lcdWriteData(0x00); +lcdWriteData(0x01); + lcdWriteCommand(0x36); + lcdWriteData(0x18); + + lcdWriteCommand(0x11); // SLPOUT + delay_ms(200); + + lcdWriteCommand(0x29); // Display On + delay_ms(25); + lcdWriteCommand(0x2C); + + + +#endif + +} + +void LCD_HX8357D_On( void ) +{ + //lcdWriteCommand( 0x29 ); +} + +void LCD_HX8357D_Off( void ) +{ + //lcdWriteCommand( 0x28 ); +} + +unsigned int LCD_HX8357D_ReadID( void ) +{ + int ID = 0; + + + return( ID ); +} + + +void LCD_ILI9481_Init( void ) +{ + lcdWriteCommand(0x11); + delay_ms(120); + + lcdWriteCommand(0xE4); + lcdWriteData(0x0A); + + lcdWriteCommand(0xF0); + lcdWriteData(0x01); + + lcdWriteCommand(0xF3); + lcdWriteData(0x02); + lcdWriteData(0x1A); + + lcdWriteCommand(0xD0); + lcdWriteData(0x07); + lcdWriteData(0x42); + lcdWriteData(0x1B); + + lcdWriteCommand(0xD1); + lcdWriteData(0x00); + lcdWriteData(0x00);//04 + lcdWriteData(0x1A); + + lcdWriteCommand(0xD2); + lcdWriteData(0x01); + lcdWriteData(0x00);//11 + + lcdWriteCommand(0xC0); + lcdWriteData(0x10); + lcdWriteData(0x3B);// + lcdWriteData(0x00);// + lcdWriteData(0x02); + lcdWriteData(0x11); + + lcdWriteCommand(0xC5); + lcdWriteData(0x03); + + lcdWriteCommand(0xC8); + lcdWriteData(0x00); + lcdWriteData(0x01); + lcdWriteData(0x47); + lcdWriteData(0x60); + lcdWriteData(0x04); + lcdWriteData(0x16); + lcdWriteData(0x03); + lcdWriteData(0x67); + lcdWriteData(0x67); + lcdWriteData(0x06); + lcdWriteData(0x0F); + lcdWriteData(0x00); + + lcdWriteCommand(0x36); + lcdWriteData(0x08); + + lcdWriteCommand(0x3A); + lcdWriteData(0x66); //0x55=65k color, 0x66=262k color. + + lcdWriteCommand(0x2A); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x01); + lcdWriteData(0x3F); + + lcdWriteCommand(0x2B); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x01); + lcdWriteData(0xE0); + + lcdWriteCommand(0xB4); + lcdWriteData(0x11); + + lcdWriteCommand(0xc6); + lcdWriteData(0x82); + + delay_ms(120); + + lcdWriteCommand(0x21); + lcdWriteCommand(0x29); + lcdWriteCommand(0x2C); + +} + +void LCD_ILI9481_On( void ) +{ + //lcdWriteCommand( 0x29 ); +} +/*================================================================================================== +Name: +Function: +Input: +Output: +==================================================================================================*/ +void LCD_ILI9481_Off( void ) +{ + //lcdWriteCommand( 0x28 ); +} + +unsigned int LCD_ILI9481_ReadID( void ) +{ + int ID = 0; + int Data; + + /* Have a issue here */ + return 0; + + lcdWriteByte( 0, 0xBF ); + + + Data = LCD_ReadByteOnFallingEdge(); + Data = LCD_ReadByteOnFallingEdge(); + ID = LCD_ReadByteOnFallingEdge(); + ID <<= 8; + ID |= LCD_ReadByteOnFallingEdge(); + Data = LCD_ReadByteOnFallingEdge(); + Data = LCD_ReadByteOnFallingEdge(); + + LCD_DELAY(); + LCD_DELAY(); + LCD_DELAY(); + + lcdWriteCommand( 0xC6 ); + lcdWriteData( 0x82 ); + //lcdWriteData( 0x9b ); + return( ID ); +} + + +void LCD_ILI9486_Init( void ) +{ + lcdWriteCommand( 0XFB ); + lcdWriteData( 0x00 ); + + lcdWriteCommand( 0xf2 ); + lcdWriteData( 0x18 ); + lcdWriteData( 0xa3 ); + lcdWriteData( 0x12 ); + lcdWriteData( 0x02 ); + lcdWriteData( 0xb2 ); + lcdWriteData( 0x12 ); + lcdWriteData( 0xff ); + lcdWriteData( 0x13 ); + lcdWriteData( 0x00 ); + lcdWriteCommand( 0xf1 ); + lcdWriteData( 0x36 ); + lcdWriteData( 0x04 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x3c ); + lcdWriteData( 0x0f ); + lcdWriteData( 0x8f ); + lcdWriteCommand( 0xf8 ); + lcdWriteData( 0x21 ); + lcdWriteData( 0x04 ); + lcdWriteCommand( 0xf9 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x08 ); + lcdWriteCommand( 0x36 ); + lcdWriteData( 0x18 ); + lcdWriteCommand( 0x3a ); + lcdWriteData( 0x65 ); + lcdWriteCommand( 0xc0 ); + lcdWriteData( 0x0f ); + lcdWriteData( 0x0f ); + lcdWriteCommand( 0xc1 ); + lcdWriteData( 0x41 ); + + lcdWriteCommand( 0xc5 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x27 ); + lcdWriteData( 0x80 ); + lcdWriteCommand( 0xb6 ); + lcdWriteData( 0x22 ); + lcdWriteData( 0x02 ); + lcdWriteData( 0x3b ); + lcdWriteCommand( 0xb1 ); + lcdWriteData( 0xb0 ); + lcdWriteData( 0x11 ); + lcdWriteCommand( 0xb4 ); + lcdWriteData( 0x02 ); + lcdWriteCommand( 0xb7 ); + lcdWriteData( 0xC6 ); + + lcdWriteCommand( 0xe0 ); + lcdWriteData( 0x0f ); + lcdWriteData( 0x1C ); + lcdWriteData( 0x18 ); + lcdWriteData( 0x0B ); + lcdWriteData( 0x0D ); + lcdWriteData( 0x06 ); + lcdWriteData( 0x48 ); + lcdWriteData( 0x87 ); + lcdWriteData( 0x3A ); + lcdWriteData( 0x09 ); + lcdWriteData( 0x15 ); + lcdWriteData( 0x08 ); + lcdWriteData( 0x0D ); + lcdWriteData( 0x04 ); + lcdWriteData( 0x00 ); + + lcdWriteCommand( 0xe1 ); + lcdWriteData( 0x0f ); + lcdWriteData( 0x37 ); + lcdWriteData( 0x34 ); + lcdWriteData( 0x0A ); + lcdWriteData( 0x0B ); + lcdWriteData( 0x03 ); + lcdWriteData( 0x4B ); + lcdWriteData( 0x31 ); + lcdWriteData( 0x39 ); + lcdWriteData( 0x03 ); + lcdWriteData( 0x0F ); + lcdWriteData( 0x03 ); + lcdWriteData( 0x22 ); + lcdWriteData( 0x1D ); + lcdWriteData( 0x00 ); + + lcdWriteCommand( 0x21 ); + lcdWriteCommand( 0x11 ); + delay_ms( 120 ); + lcdWriteCommand( 0x28 ); + + LCD_ILI9486_On(); +} +/*================================================================================================== +Name: +Function: +Input: +Output: +==================================================================================================*/ +void LCD_ILI9486_On( void ) +{ + lcdWriteCommand( 0x29 ); +} +/*================================================================================================== +Name: +Function: +Input: +Output: +==================================================================================================*/ +void LCD_ILI9486_Off( void ) +{ + lcdWriteCommand( 0x28 ); +} + + +unsigned int LCD_ILI9486_ReadID( void ) +{ + int ID = 0; + + lcdWriteCommand( 0XF7 ); + lcdWriteData( 0xA9 ); + lcdWriteData( 0x51 ); + lcdWriteData( 0x2C ); + lcdWriteData( 0x82 ); + lcdWriteCommand( 0XB0 ); + lcdWriteData( 0X80 ); + + lcdWriteCommand( 0XFB ); + lcdWriteData( 0x10|0x00 ); + ID = LCD_ReadRegister( 0xd3 ); + + lcdWriteCommand( 0XFB ); + lcdWriteData( 0x10|0x01 ); + ID = LCD_ReadRegister( 0xd3 ); + + lcdWriteCommand( 0XFB ); + lcdWriteData( 0x10|0x02 ); + ID = LCD_ReadRegister( 0xd3 ); + ID <<= 8; + lcdWriteCommand( 0XFB ); + lcdWriteData( 0x10|0x03 ); + ID |= LCD_ReadRegister( 0xd3 ); + + lcdWriteCommand( 0XFB ); + lcdWriteData( 0x00 ); + + return( ID ); +} + +void LCD_ILI9488_Init( void ) +{ + lcdWriteCommand( 0XFB ); + lcdWriteData( 0x00 ); + + lcdWriteCommand( 0XF7 ); + lcdWriteData( 0xA9 ); + lcdWriteData( 0x51 ); + lcdWriteData( 0x2C ); + lcdWriteData( 0x82 ); + + lcdWriteCommand( 0xC0 ); + lcdWriteData( 0x11 ); + lcdWriteData( 0x09 ); + + lcdWriteCommand( 0xC1 ); + lcdWriteData( 0x41 ); + + lcdWriteCommand( 0XC5 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x0A ); + lcdWriteData( 0x80 ); + + lcdWriteCommand(0xB1); + lcdWriteData(0xB0); + lcdWriteData(0x11); + + lcdWriteCommand(0xB4); + lcdWriteData(0x02); + + lcdWriteCommand(0xB6); + lcdWriteData(0x30); + lcdWriteData(0x02); + + lcdWriteCommand(0xB7); + lcdWriteData(0xc6); + + lcdWriteCommand(0xBE); + lcdWriteData(0x00); + lcdWriteData(0x04); + + lcdWriteCommand(0xE9); + lcdWriteData(0x00); + + lcdWriteCommand(0x36); + lcdWriteData(0x08); + + lcdWriteCommand(0x3A); + lcdWriteData(0x65); + + lcdWriteCommand(0xE0); + lcdWriteData(0x00); + lcdWriteData(0x07); + lcdWriteData(0x10); + lcdWriteData(0x09); + lcdWriteData(0x17); + lcdWriteData(0x0B); + lcdWriteData(0x41); + lcdWriteData(0x89); + lcdWriteData(0x4B); + lcdWriteData(0x0A); + lcdWriteData(0x0C); + lcdWriteData(0x0E); + lcdWriteData(0x18); + lcdWriteData(0x1B); + lcdWriteData(0x0F); + + lcdWriteCommand(0XE1); + lcdWriteData(0x00); + lcdWriteData(0x17); + lcdWriteData(0x1A); + lcdWriteData(0x04); + lcdWriteData(0x0E); + lcdWriteData(0x06); + lcdWriteData(0x2F); + lcdWriteData(0x45); + lcdWriteData(0x43); + lcdWriteData(0x02); + lcdWriteData(0x0A); + lcdWriteData(0x09); + lcdWriteData(0x32); + lcdWriteData(0x36); + lcdWriteData(0x0F); + + lcdWriteCommand(0x11); + delay_ms(120); + lcdWriteCommand(0x28); + + LCD_ILI9488_On(); +} +/*================================================================================================== +Name: +Function: +Input: +Output: +==================================================================================================*/ +void LCD_ILI9488_On( void ) +{ + lcdWriteCommand( 0x29 ); +} +/*================================================================================================== +Name: +Function: +Input: +Output: +==================================================================================================*/ +void LCD_ILI9488_Off( void ) +{ + lcdWriteCommand( 0x28 ); +} + +void LCD_ILI9488_ReadDevice( void ) +{ + int Index = 0; + int Parameter = 0x80; + + #if 1 + + #if 1 + lcdWriteCommand(0XF7); + lcdWriteData(0xA9); + lcdWriteData(0x51); + lcdWriteData(0x2C); + lcdWriteData(0x82); + + lcdWriteCommand(0XB0); + lcdWriteData(0X80); + + #endif + lcdWriteCommand(0XFB); + lcdWriteData( Parameter|0x00 ); + LCD_ReadBuffer[Index++]=LCD_ReadRegister( 0xd3 ); + + //lcdWriteCommand(0X2E); + lcdWriteCommand(0XFB); + lcdWriteData( Parameter|0x01 ); //Parameter2=0X88 + LCD_ReadBuffer[Index++]=LCD_ReadRegister( 0xd3 ); + + + lcdWriteCommand(0XFB); + lcdWriteData( Parameter|0x02 ); //Parameter2=0X88 + LCD_ReadBuffer[Index++]=LCD_ReadRegister( 0xd3 ); + + + lcdWriteCommand(0XFB); + lcdWriteData( Parameter|0x03 ); //Parameter2=0X88 + LCD_ReadBuffer[Index++]=LCD_ReadRegister( 0xd3 ); + #endif + + #if 0 + lcdWriteCommand( 0XFB ); + lcdWriteData( Parameter|0x00 ); //Parameter3=0X94 + LCD_ReadBuffer[Index++] = LCD_ReadRegister( 0xd3 ); + lcdWriteData( Parameter|0x01 ); //Parameter3=0X94 + LCD_ReadBuffer[Index++] = LCD_ReadRegister( 0xd3 ); + lcdWriteCommand( 0XFB ); + lcdWriteData( Parameter|0x02 ); //Parameter3=0X94 + LCD_ReadBuffer[Index++] = LCD_ReadRegister( 0xd3 ); + + lcdWriteCommand( 0XFB ); + lcdWriteData( Parameter|0x03 ); //Parameter4=0X88 + LCD_ReadBuffer[Index++] = LCD_ReadRegister( 0xd3 ); + #else + //lcdWriteCommand( 0xd0 ); + //lcdWriteData( Parameter|0x03 ); //Parameter4=0X88 + //LCD_ReadBuffer[Index++] = LCD_ReadRegister( 0xd0 ); + #endif +} + + +unsigned int LCD_ILI9488_ReadID( void ) +{ + int ID = 0; + + lcdWriteCommand( 0XF7 ); + lcdWriteData( 0xA9 ); + lcdWriteData( 0x51 ); + lcdWriteData( 0x2C ); + lcdWriteData( 0x82 ); + lcdWriteCommand( 0XB0 ); + lcdWriteData( 0X80 ); + + lcdWriteCommand( 0XFB ); + lcdWriteData( 0x80|0x00 ); + ID = LCD_ReadRegister( 0xd3 ); + + lcdWriteCommand( 0XFB ); + lcdWriteData( 0x80|0x01 ); + ID = LCD_ReadRegister( 0xd3 ); + + lcdWriteCommand( 0XFB ); + lcdWriteData( 0x80|0x02 ); + ID = LCD_ReadRegister( 0xd3 ); + ID <<= 8; + + lcdWriteCommand( 0XFB ); + lcdWriteData( 0x80|0x03 ); + ID |= LCD_ReadRegister( 0xd3 ); + + lcdWriteCommand( 0XFB ); + lcdWriteData( 0x00 ); + return( ID ); +} + +void LCD_ST7796S_Init( void ) +{ + lcdWriteCommand( 0XFB ); + lcdWriteData( 0x00 ); + + lcdWriteCommand( 0x11 ); + delay_ms( 120 ); + lcdWriteCommand( 0x13 ); + + lcdWriteCommand( 0xf0 ); + lcdWriteData( 0xc3 ); + lcdWriteCommand( 0xf0 ); + lcdWriteData( 0x96 ); + + lcdWriteCommand( 0x36 ); + + #if defined( LCD_DIRECTION ) && ( LCD_DIRECTION == LCD_VERTICAL ) + + lcdWriteData( 0x08 ); + + #else + #if defined( LCD_DEBUG ) && ( LCD_DEBUG == ON ) + lcdWriteData( 0xcc ); + #else + lcdWriteData( 0xb8 ); + #endif + + #endif + + lcdWriteCommand( 0x3A ); + lcdWriteData( 0x65 ); + + lcdWriteCommand( 0xB4 ); + lcdWriteData( 0x01 ); + + lcdWriteCommand( 0xb5 ); + lcdWriteData( VERTICAL_FRONT_PORCH ); + lcdWriteData( VERTICAL_BACK_PORCH + VERTICAL_SYNC_HEIGHT ); + lcdWriteData( 0x00 ); + lcdWriteData( HORIZONTAL_BACK_PORCH + 4 ); + + lcdWriteCommand( 0xb6 ); + lcdWriteData( 0xe0 ); + lcdWriteData( 0x42 ); + lcdWriteData( 0x3b ); + + lcdWriteCommand( 0xB7 ); + lcdWriteData( 0x66 ); + + lcdWriteCommand( 0xe8 ); + lcdWriteData( 0x40 ); + lcdWriteData( 0x8a ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x00 ); + lcdWriteData( 0x29 ); + lcdWriteData( 0x19 ); + lcdWriteData( 0xa5 ); + lcdWriteData( 0x33 ); + + lcdWriteCommand( 0xc1 ); + lcdWriteData( 0x06 ); + + lcdWriteCommand( 0xc2 ); + lcdWriteData( 0xa7 ); + + lcdWriteCommand( 0xc5 ); + lcdWriteData( 0x18 ); + + lcdWriteCommand( 0xe0 ); //Positive Voltage Gamma Control + lcdWriteData( 0xf0 ); + lcdWriteData( 0x09 ); + lcdWriteData( 0x0b ); + lcdWriteData( 0x06 ); + lcdWriteData( 0x04 ); + lcdWriteData( 0x15 ); + lcdWriteData( 0x2f ); + lcdWriteData( 0x54 ); + lcdWriteData( 0x42 ); + lcdWriteData( 0x3c ); + lcdWriteData( 0x17 ); + lcdWriteData( 0x14 ); + lcdWriteData( 0x18 ); + lcdWriteData( 0x1b ); + + lcdWriteCommand( 0xe1 ); //Negative Voltage Gamma Control + lcdWriteData( 0xf0 ); + lcdWriteData( 0x09 ); + lcdWriteData( 0x0b ); + lcdWriteData( 0x06 ); + lcdWriteData( 0x04 ); + lcdWriteData( 0x03 ); + lcdWriteData( 0x2d ); + lcdWriteData( 0x43 ); + lcdWriteData( 0x42 ); + lcdWriteData( 0x3b ); + lcdWriteData( 0x16 ); + lcdWriteData( 0x14 ); + lcdWriteData( 0x17 ); + lcdWriteData( 0x1b ); + + lcdWriteCommand( 0xf0 ); + lcdWriteData( 0x3c ); + lcdWriteCommand( 0xf0 ); + lcdWriteData( 0x69 ); + + delay_ms( 5 ); + lcdWriteCommand( 0x28 ); + //lcdWriteCommand( 0x29 ); + lcdWriteCommand( 0x2C ); + + LCD_ST7796S_On(); +} + +void LCD_ST7796S_On( void ) +{ + lcdWriteCommand( 0x29 ); +} +/*================================================================================================== +Name: +Function: +Input: +Output: +==================================================================================================*/ +void LCD_ST7796S_Off( void ) +{ + lcdWriteCommand( 0x28 ); +} + +unsigned int LCD_ST7796S_ReadID( void ) +{ + int ID = 0; + + lcdWriteCommand( 0XF7 ); + lcdWriteData( 0xA9 ); + lcdWriteData( 0x51 ); + lcdWriteData( 0x2C ); + lcdWriteData( 0x82 ); + lcdWriteCommand( 0XB0 ); + lcdWriteData( 0X80 ); + + lcdWriteCommand( 0XFB ); + lcdWriteData( 0x10|0x00 ); + ID = LCD_ReadRegister( 0xd3 ); + + lcdWriteCommand( 0XFB ); + lcdWriteData( 0x10|0x01 ); + ID = LCD_ReadRegister( 0xd3 ); + + lcdWriteCommand( 0XFB ); + lcdWriteData( 0x10|0x02 ); + ID = LCD_ReadRegister( 0xd3 ); + ID <<= 8; + lcdWriteCommand( 0XFB ); + lcdWriteData( 0x10|0x03 ); + ID |= LCD_ReadRegister( 0xd3 ); + + lcdWriteCommand( 0XFB ); + lcdWriteData( 0x00 ); + + return( ID ); +} + + + +static void lcdSpiInit() +{ + lcdWriteCommand(0x11); + delay_ms( 120 ); + lcdWriteCommand(0x13); + + lcdWriteCommand(0xf0); + lcdWriteData(0xc3); + lcdWriteCommand(0xf0); + lcdWriteData(0x96); + + lcdWriteCommand(0x36); + +// #if defined( LCD_DIRECTION ) && ( LCD_DIRECTION == LCD_VERTICAL ) + lcdWriteData(0x08); +// #else + //lcdWriteData(0xf8); + //lcdWriteData(0xcc); +//#endif + + lcdWriteCommand(0x3A); + lcdWriteData(0x65); + + lcdWriteCommand(0xB4); + lcdWriteData(0x01); + + lcdWriteCommand(0xb5); + lcdWriteData(VFP); + lcdWriteData(VBP + VSH); + lcdWriteData(0x00); + lcdWriteData(HBP + 4); + + lcdWriteCommand(0xb6); + lcdWriteData(0xe0); + lcdWriteData(0x42); + lcdWriteData(0x3b); + + lcdWriteCommand(0xB7); + lcdWriteData(0x66); + + lcdWriteCommand(0xe8); + lcdWriteData(0x40); + lcdWriteData(0x8a); + lcdWriteData(0x00); + lcdWriteData(0x00); + lcdWriteData(0x29); + lcdWriteData(0x19); + lcdWriteData(0xa5); + lcdWriteData(0x33); + + lcdWriteCommand(0xc1); + lcdWriteData(0x06); + + lcdWriteCommand(0xc2); + lcdWriteData(0xa7); + + lcdWriteCommand(0xc5); + lcdWriteData(0x18); + + lcdWriteCommand(0xe0); //Positive Voltage Gamma Control + lcdWriteData(0xf0); + lcdWriteData(0x09); + lcdWriteData(0x0b); + lcdWriteData(0x06); + lcdWriteData(0x04); + lcdWriteData(0x15); + lcdWriteData(0x2f); + lcdWriteData(0x54); + lcdWriteData(0x42); + lcdWriteData(0x3c); + lcdWriteData(0x17); + lcdWriteData(0x14); + lcdWriteData(0x18); + lcdWriteData(0x1b); + + lcdWriteCommand(0xe1); //Negative Voltage Gamma Control + lcdWriteData(0xf0); + lcdWriteData(0x09); + lcdWriteData(0x0b); + lcdWriteData(0x06); + lcdWriteData(0x04); + lcdWriteData(0x03); + lcdWriteData(0x2d); + lcdWriteData(0x43); + lcdWriteData(0x42); + lcdWriteData(0x3b); + lcdWriteData(0x16); + lcdWriteData(0x14); + lcdWriteData(0x17); + lcdWriteData(0x1b); + + lcdWriteCommand(0xf0); + lcdWriteData(0x3c); + lcdWriteCommand(0xf0); + lcdWriteData(0x69); + + delay_ms(5); + lcdWriteCommand(0x28); + lcdWriteCommand(0x2C); + + delay_ms(50); + lcdWriteCommand(0x29); // LCD ON +} + +static void lcdReset() +{ + LCD_NRST_HIGH(); + delay_ms(1); + + LCD_NRST_LOW(); // RESET(); + delay_ms(100); + + LCD_NRST_HIGH(); + delay_ms(100); +} + +void LCD_Init_LTDC() +{ + LTDC_InitTypeDef LTDC_InitStruct; + + /* Configure PLLSAI prescalers for LCD */ + /* PLLSAI_VCO Input = HSE_VALUE/PLL_M = 1 Mhz */ + /* PLLSAI_VCO Output = PLLSAI_VCO Input * PLLSAI_N = 192 Mhz */ + /* PLLLCDCLK = PLLSAI_VCO Output/PLL_LTDC = 192/3 = 64 Mhz */ + /* LTDC clock frequency = PLLLCDCLK / RCC_PLLSAIDivR = 64/4 = 16 Mhz */ + RCC_PLLSAIConfig(192 * 2 / 3, 6, 3); + RCC_LTDCCLKDivConfig(RCC_PLLSAIDivR_Div4); + + /* Enable PLLSAI Clock */ + RCC_PLLSAICmd(ENABLE); + + /* Wait for PLLSAI activation */ + while (RCC_GetFlagStatus(RCC_FLAG_PLLSAIRDY) == RESET); + + /* LTDC Configuration *********************************************************/ + /* Polarity configuration */ + /* Initialize the horizontal synchronization polarity as active low */ + LTDC_InitStruct.LTDC_HSPolarity = LTDC_HSPolarity_AL; + /* Initialize the vertical synchronization polarity as active low */ + LTDC_InitStruct.LTDC_VSPolarity = LTDC_VSPolarity_AL; + /* Initialize the data enable polarity as active low */ + LTDC_InitStruct.LTDC_DEPolarity = LTDC_DEPolarity_AL; + /* Initialize the pixel clock polarity as input pixel clock */ + LTDC_InitStruct.LTDC_PCPolarity = LTDC_PCPolarity_IPC; + + /* Configure R,G,B component values for LCD background color */ + LTDC_InitStruct.LTDC_BackgroundRedValue = 0; + LTDC_InitStruct.LTDC_BackgroundGreenValue = 0; + LTDC_InitStruct.LTDC_BackgroundBlueValue = 0; + + /* Configure horizontal synchronization width */ + LTDC_InitStruct.LTDC_HorizontalSync = HSW; + /* Configure vertical synchronization height */ + LTDC_InitStruct.LTDC_VerticalSync = VSH; + /* Configure accumulated horizontal back porch */ + LTDC_InitStruct.LTDC_AccumulatedHBP = HBP; + /* Configure accumulated vertical back porch */ + LTDC_InitStruct.LTDC_AccumulatedVBP = VBP; + /* Configure accumulated active width */ + LTDC_InitStruct.LTDC_AccumulatedActiveW = LCD_W + HBP; + /* Configure accumulated active height */ + LTDC_InitStruct.LTDC_AccumulatedActiveH = LCD_H + VBP; + /* Configure total width */ + LTDC_InitStruct.LTDC_TotalWidth = LCD_W + HBP + HFP; + /* Configure total height */ + LTDC_InitStruct.LTDC_TotalHeigh = LCD_H + VBP + VFP; + + LTDC_Init(<DC_InitStruct); + +#if 0 + LTDC_ITConfig(LTDC_IER_LIE, ENABLE); + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = LTDC_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = LTDC_IRQ_PRIO; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; /* Not used as 4 bits are used for the pr e-emption priority. */; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init( &NVIC_InitStructure ); + + DMA2D_ITConfig(DMA2D_CR_TCIE, ENABLE); + NVIC_InitStructure.NVIC_IRQChannel = DMA2D_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = DMA_SCREEN_IRQ_PRIO; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; /* Not used as 4 bits are used for the pr e-emption priority. */; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init( &NVIC_InitStructure ); + + DMA2D->IFCR = (unsigned long)DMA2D_IFSR_CTCIF; +#endif +} + +void LCD_LayerInit() +{ + LTDC_Layer_InitTypeDef LTDC_Layer_InitStruct; + + /* Windowing configuration */ + /* In this case all the active display area is used to display a picture then : + Horizontal start = horizontal synchronization + Horizontal back porch = 30 + Horizontal stop = Horizontal start + window width -1 = 30 + 240 -1 + Vertical start = vertical synchronization + vertical back porch = 4 + Vertical stop = Vertical start + window height -1 = 4 + 320 -1 */ + LTDC_Layer_InitStruct.LTDC_HorizontalStart = HBP + 1; + LTDC_Layer_InitStruct.LTDC_HorizontalStop = (LCD_W + HBP); + LTDC_Layer_InitStruct.LTDC_VerticalStart = VBP + 1; + LTDC_Layer_InitStruct.LTDC_VerticalStop = (LCD_H + VBP); + + /* Pixel Format configuration*/ + LTDC_Layer_InitStruct.LTDC_PixelFormat = LTDC_Pixelformat_RGB565; + /* Alpha constant (255 totally opaque) */ + LTDC_Layer_InitStruct.LTDC_ConstantAlpha = 255; + /* Default Color configuration (configure A,R,G,B component values) */ + LTDC_Layer_InitStruct.LTDC_DefaultColorBlue = 0; + LTDC_Layer_InitStruct.LTDC_DefaultColorGreen = 0; + LTDC_Layer_InitStruct.LTDC_DefaultColorRed = 0; + LTDC_Layer_InitStruct.LTDC_DefaultColorAlpha = 0; + + /* Configure blending factors */ + LTDC_Layer_InitStruct.LTDC_BlendingFactor_1 = LTDC_BlendingFactor1_CA; + LTDC_Layer_InitStruct.LTDC_BlendingFactor_2 = LTDC_BlendingFactor2_CA; + + /* the length of one line of pixels in bytes + 3 then : + Line Lenth = Active high width x number of bytes per pixel + 3 + Active high width = LCD_W + number of bytes per pixel = 2 (pixel_format : RGB565) + */ + LTDC_Layer_InitStruct.LTDC_CFBLineLength = ((LCD_W * 2) + 3); + /* the pitch is the increment from the start of one line of pixels to the + start of the next line in bytes, then : + Pitch = Active high width x number of bytes per pixel */ + LTDC_Layer_InitStruct.LTDC_CFBPitch = (LCD_W * 2); + + /* Configure the number of lines */ + LTDC_Layer_InitStruct.LTDC_CFBLineNumber = LCD_H; + + /* Start Address configuration : the LCD Frame buffer is defined on SDRAM w/ Offset */ + LTDC_Layer_InitStruct.LTDC_CFBStartAdress = (uint32_t)LCD_FIRST_FRAME_BUFFER; + + /* Initialize LTDC layer 1 */ + LTDC_LayerInit(LTDC_Layer1, <DC_Layer_InitStruct); + + /* Configure Layer 2 */ + LTDC_Layer_InitStruct.LTDC_BlendingFactor_1 = LTDC_BlendingFactor1_PAxCA; + LTDC_Layer_InitStruct.LTDC_BlendingFactor_2 = LTDC_BlendingFactor2_PAxCA; + + /* Start Address configuration : the LCD Frame buffer is defined on SDRAM w/ Offset */ + LTDC_Layer_InitStruct.LTDC_CFBStartAdress = (uint32_t)LCD_SECOND_FRAME_BUFFER; + + /* Initialize LTDC layer 2 */ + LTDC_LayerInit(LTDC_Layer2, <DC_Layer_InitStruct); + + /* LTDC configuration reload */ + LTDC_ReloadConfig(LTDC_IMReload); + + LTDC_LayerCmd(LTDC_Layer1, ENABLE); + LTDC_LayerCmd(LTDC_Layer2, ENABLE); + + LTDC_ReloadConfig(LTDC_IMReload); + + /* dithering activation */ + LTDC_DitherCmd(ENABLE); +} + +BitmapBuffer lcdBuffer1(BMP_RGB565, LCD_W, LCD_H, (uint16_t *)LCD_FIRST_FRAME_BUFFER); +BitmapBuffer lcdBuffer2(BMP_RGB565, LCD_W, LCD_H, (uint16_t *)LCD_SECOND_FRAME_BUFFER); +BitmapBuffer * lcd = &lcdBuffer1; + +void LCD_SetLayer(uint32_t layer) +{ + if (layer == LCD_FIRST_LAYER) { + lcd = &lcdBuffer1; + } + else { + lcd = &lcdBuffer2; + } + currentLayer = layer; +} + +void LCD_SetTransparency(uint8_t transparency) +{ + if (currentLayer == LCD_FIRST_LAYER) { + LTDC_LayerAlpha(LTDC_Layer1, transparency); + } + else { + LTDC_LayerAlpha(LTDC_Layer2, transparency); + } + LTDC_ReloadConfig(LTDC_IMReload); +} + +void lcdInit() +{ + /* Configure the LCD SPI+RESET pins */ + lcdSpiConfig(); + + /* Reset the LCD --------------------------------------------------------*/ + lcdReset(); + + /* Configure the LCD Control pins */ + LCD_AF_GPIOConfig(); + + /* Send LCD initializaiton commands */ + if (LCD_ST7796S_ReadID() == LCD_ST7796S_ID) + { + lcdSpiInitFuc = LCD_ST7796S_Init; + } + else if (LCD_ILI9481_ReadID() == LCD_ILI9481_ID) + { + lcdSpiInitFuc = LCD_ILI9481_Init; + } + else if (LCD_ILI9486_ReadID() == LCD_ILI9486_ID) + { + lcdSpiInitFuc = LCD_ILI9486_Init; + } + else if (LCD_ILI9488_ReadID() == LCD_ILI9488_ID) + { + lcdSpiInitFuc = LCD_ILI9488_Init; + } + else if (LCD_HX8357D_ReadID() == LCD_HX8357D_ID) + { + lcdSpiInitFuc = LCD_HX8357D_Init; + } + + if (NULL != lcdSpiInitFuc) + { + lcdSpiInitFuc(); + } + else + { + /* Default is ST7796S */ + lcdSpiInitFuc = LCD_ST7796S_Init; + lcdSpiInitFuc(); + } + + //lcdSpiInit(); + + LCD_Init_LTDC(); + + LCD_LayerInit(); + + /* Enable LCD display */ + LTDC_Cmd(ENABLE); + + /* Set Background layer */ + LCD_SetLayer(LCD_FIRST_LAYER); + // lcdClear(); + LCD_SetTransparency(0); + + /* Set Foreground layer */ + LCD_SetLayer(LCD_SECOND_LAYER); + lcd->clear(); + LCD_SetTransparency(255); +} + +void DMAFillRect(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) +{ + DMA2D_DeInit(); + + DMA2D_InitTypeDef DMA2D_InitStruct; + DMA2D_InitStruct.DMA2D_Mode = DMA2D_R2M; + DMA2D_InitStruct.DMA2D_CMode = DMA2D_RGB565; + DMA2D_InitStruct.DMA2D_OutputGreen = (0x07E0 & color) >> 5; + DMA2D_InitStruct.DMA2D_OutputBlue = 0x001F & color; + DMA2D_InitStruct.DMA2D_OutputRed = (0xF800 & color) >> 11; + DMA2D_InitStruct.DMA2D_OutputAlpha = 0x0F; + DMA2D_InitStruct.DMA2D_OutputMemoryAdd = CONVERT_PTR_UINT(dest) + 2*(destw*y + x); + DMA2D_InitStruct.DMA2D_OutputOffset = (destw - w); + DMA2D_InitStruct.DMA2D_NumberOfLine = h; + DMA2D_InitStruct.DMA2D_PixelPerLine = w; + DMA2D_Init(&DMA2D_InitStruct); + + /* Start Transfer */ + DMA2D_StartTransfer(); + + /* Wait for CTC Flag activation */ + while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); +} + +void DMACopyBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h) +{ + DMA2D_DeInit(); + + DMA2D_InitTypeDef DMA2D_InitStruct; + DMA2D_InitStruct.DMA2D_Mode = DMA2D_M2M; + DMA2D_InitStruct.DMA2D_CMode = DMA2D_RGB565; + DMA2D_InitStruct.DMA2D_OutputMemoryAdd = CONVERT_PTR_UINT(dest + y*destw + x); + DMA2D_InitStruct.DMA2D_OutputGreen = 0; + DMA2D_InitStruct.DMA2D_OutputBlue = 0; + DMA2D_InitStruct.DMA2D_OutputRed = 0; + DMA2D_InitStruct.DMA2D_OutputAlpha = 0; + DMA2D_InitStruct.DMA2D_OutputOffset = destw - w; + DMA2D_InitStruct.DMA2D_NumberOfLine = h; + DMA2D_InitStruct.DMA2D_PixelPerLine = w; + DMA2D_Init(&DMA2D_InitStruct); + + DMA2D_FG_InitTypeDef DMA2D_FG_InitStruct; + DMA2D_FG_StructInit(&DMA2D_FG_InitStruct); + DMA2D_FG_InitStruct.DMA2D_FGMA = CONVERT_PTR_UINT(src + srcy*srcw + srcx); + DMA2D_FG_InitStruct.DMA2D_FGO = srcw - w; + DMA2D_FG_InitStruct.DMA2D_FGCM = CM_RGB565; + DMA2D_FG_InitStruct.DMA2D_FGPFC_ALPHA_MODE = NO_MODIF_ALPHA_VALUE; + DMA2D_FG_InitStruct.DMA2D_FGPFC_ALPHA_VALUE = 0; + DMA2D_FGConfig(&DMA2D_FG_InitStruct); + + /* Start Transfer */ + DMA2D_StartTransfer(); + + /* Wait for CTC Flag activation */ + while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); +} + +void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h) +{ + DMA2D_DeInit(); + + DMA2D_InitTypeDef DMA2D_InitStruct; + DMA2D_InitStruct.DMA2D_Mode = DMA2D_M2M_BLEND; + DMA2D_InitStruct.DMA2D_CMode = DMA2D_RGB565; + DMA2D_InitStruct.DMA2D_OutputMemoryAdd = CONVERT_PTR_UINT(dest + y*destw + x); + DMA2D_InitStruct.DMA2D_OutputGreen = 0; + DMA2D_InitStruct.DMA2D_OutputBlue = 0; + DMA2D_InitStruct.DMA2D_OutputRed = 0; + DMA2D_InitStruct.DMA2D_OutputAlpha = 0; + DMA2D_InitStruct.DMA2D_OutputOffset = destw - w; + DMA2D_InitStruct.DMA2D_NumberOfLine = h; + DMA2D_InitStruct.DMA2D_PixelPerLine = w; + DMA2D_Init(&DMA2D_InitStruct); + + DMA2D_FG_InitTypeDef DMA2D_FG_InitStruct; + DMA2D_FG_StructInit(&DMA2D_FG_InitStruct); + DMA2D_FG_InitStruct.DMA2D_FGMA = CONVERT_PTR_UINT(src + srcy*srcw + srcx); + DMA2D_FG_InitStruct.DMA2D_FGO = srcw - w; + DMA2D_FG_InitStruct.DMA2D_FGCM = CM_ARGB4444; + DMA2D_FG_InitStruct.DMA2D_FGPFC_ALPHA_MODE = NO_MODIF_ALPHA_VALUE; + DMA2D_FG_InitStruct.DMA2D_FGPFC_ALPHA_VALUE = 0; + DMA2D_FGConfig(&DMA2D_FG_InitStruct); + + DMA2D_BG_InitTypeDef DMA2D_BG_InitStruct; + DMA2D_BG_StructInit(&DMA2D_BG_InitStruct); + DMA2D_BG_InitStruct.DMA2D_BGMA = CONVERT_PTR_UINT(dest + y*destw + x); + DMA2D_BG_InitStruct.DMA2D_BGO = destw - w; + DMA2D_BG_InitStruct.DMA2D_BGCM = CM_RGB565; + DMA2D_BG_InitStruct.DMA2D_BGPFC_ALPHA_MODE = NO_MODIF_ALPHA_VALUE; + DMA2D_BG_InitStruct.DMA2D_BGPFC_ALPHA_VALUE = 0; + DMA2D_BGConfig(&DMA2D_BG_InitStruct); + + /* Start Transfer */ + DMA2D_StartTransfer(); + + /* Wait for CTC Flag activation */ + while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); +} + +void DMABitmapConvert(uint16_t * dest, const uint8_t * src, uint16_t w, uint16_t h, uint32_t format) +{ + DMA2D_DeInit(); + + DMA2D_InitTypeDef DMA2D_InitStruct; + DMA2D_InitStruct.DMA2D_Mode = DMA2D_M2M_PFC; + DMA2D_InitStruct.DMA2D_CMode = format; + DMA2D_InitStruct.DMA2D_OutputMemoryAdd = CONVERT_PTR_UINT(dest); + DMA2D_InitStruct.DMA2D_OutputGreen = 0; + DMA2D_InitStruct.DMA2D_OutputBlue = 0; + DMA2D_InitStruct.DMA2D_OutputRed = 0; + DMA2D_InitStruct.DMA2D_OutputAlpha = 0; + DMA2D_InitStruct.DMA2D_OutputOffset = 0; + DMA2D_InitStruct.DMA2D_NumberOfLine = h; + DMA2D_InitStruct.DMA2D_PixelPerLine = w; + DMA2D_Init(&DMA2D_InitStruct); + + DMA2D_FG_InitTypeDef DMA2D_FG_InitStruct; + DMA2D_FG_StructInit(&DMA2D_FG_InitStruct); + DMA2D_FG_InitStruct.DMA2D_FGMA = CONVERT_PTR_UINT(src); + DMA2D_FG_InitStruct.DMA2D_FGO = 0; + DMA2D_FG_InitStruct.DMA2D_FGCM = CM_ARGB8888; + DMA2D_FG_InitStruct.DMA2D_FGPFC_ALPHA_MODE = REPLACE_ALPHA_VALUE; + DMA2D_FG_InitStruct.DMA2D_FGPFC_ALPHA_VALUE = 0; + DMA2D_FGConfig(&DMA2D_FG_InitStruct); + + /* Start Transfer */ + DMA2D_StartTransfer(); + + /* Wait for CTC Flag activation */ + while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); +} + +void lcdCopy(void * dest, void * src) +{ + DMA2D_DeInit(); + + DMA2D_InitTypeDef DMA2D_InitStruct; + DMA2D_InitStruct.DMA2D_Mode = DMA2D_M2M; + DMA2D_InitStruct.DMA2D_CMode = DMA2D_RGB565; + DMA2D_InitStruct.DMA2D_OutputMemoryAdd = CONVERT_PTR_UINT(dest); + DMA2D_InitStruct.DMA2D_OutputGreen = 0; + DMA2D_InitStruct.DMA2D_OutputBlue = 0; + DMA2D_InitStruct.DMA2D_OutputRed = 0; + DMA2D_InitStruct.DMA2D_OutputAlpha = 0; + DMA2D_InitStruct.DMA2D_OutputOffset = 0; + DMA2D_InitStruct.DMA2D_NumberOfLine = LCD_H; + DMA2D_InitStruct.DMA2D_PixelPerLine = LCD_W; + DMA2D_Init(&DMA2D_InitStruct); + + DMA2D_FG_InitTypeDef DMA2D_FG_InitStruct; + DMA2D_FG_StructInit(&DMA2D_FG_InitStruct); + DMA2D_FG_InitStruct.DMA2D_FGMA = CONVERT_PTR_UINT(src); + DMA2D_FG_InitStruct.DMA2D_FGO = 0; + DMA2D_FG_InitStruct.DMA2D_FGCM = CM_RGB565; + DMA2D_FG_InitStruct.DMA2D_FGPFC_ALPHA_MODE = NO_MODIF_ALPHA_VALUE; + DMA2D_FG_InitStruct.DMA2D_FGPFC_ALPHA_VALUE = 0; + DMA2D_FGConfig(&DMA2D_FG_InitStruct); + + /* Start Transfer */ + DMA2D_StartTransfer(); + + /* Wait for CTC Flag activation */ + while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); +} + +void lcdStoreBackupBuffer() +{ + lcdCopy(LCD_BACKUP_FRAME_BUFFER, lcd->getData()); +} + +int lcdRestoreBackupBuffer() +{ + lcdCopy(lcd->getData(), LCD_BACKUP_FRAME_BUFFER); + return 1; +} + +void lcdRefresh() +{ + if (currentLayer == LCD_FIRST_LAYER) { + LTDC_LayerAlpha(LTDC_Layer1, 255); + LTDC_LayerAlpha(LTDC_Layer2, 0); + } + else { + LTDC_LayerAlpha(LTDC_Layer1, 0); + LTDC_LayerAlpha(LTDC_Layer2, 255); + } + LTDC_ReloadConfig(LTDC_IMReload); +} + +void lcdNextLayer() +{ + if (currentLayer == LCD_FIRST_LAYER) { + LCD_SetLayer(LCD_SECOND_LAYER); + } + else { + LCD_SetLayer(LCD_FIRST_LAYER); + } +} + + diff --git a/radio/src/targets/nv14/lcd_driver.h b/radio/src/targets/nv14/lcd_driver.h new file mode 100644 index 000000000..f8ac5dd1c --- /dev/null +++ b/radio/src/targets/nv14/lcd_driver.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef __LCD_DRIVER_H__ +#define __LCD_DRIVER_H__ + +#include "board.h" + +#define HBP ( 24 ) // TODO use names from FlySky +#define VBP ( 10 ) + +#define HSW ( 4 ) +#define VSH ( 2 ) + +#define HFP ( 140 - HBP ) +#define VFP ( 22 - VBP ) + +#define LCD_FIRST_LAYER ( 0 ) +#define LCD_SECOND_LAYER ( 1 ) + + +#define PORT_LCD_CS ( GPIOE ) +#define LCD_CS_PIN ( GPIO_Pin_4 ) +#define PIN_LCD_CS ( 4 ) + +#define PORT_LCD_CLK ( GPIOE ) +#define LCD_CLK_PIN ( GPIO_Pin_2 ) +#define PIN_LCD_CLK ( 2 ) + +#define PORT_LCD_MOSI ( GPIOE ) +#define LCD_MOSI_PIN ( GPIO_Pin_6 ) +#define PIN_LCD_MOSI ( 6 ) + +#define PORT_LCD_MISO ( GPIOE ) +#define LCD_MISO_PIN ( GPIO_Pin_5 ) +#define PIN_LCD_MISO ( 5 ) + +#define PORT_LCD_DE ( GPIOK ) +#define LCD_DE_PIN ( GPIO_Pin_7 ) +#define PIN_LCD_DE ( 7 ) + +#define PORT_LCD_RESET ( GPIOG ) +#define LCD_RESET_PIN ( GPIO_Pin_9 ) +#define PIN_LCD_RESET ( 9 ) + +#define PORT_LCD_HSYNC ( GPIOI ) +#define LCD_HSYNC_PIN ( GPIO_Pin_12 ) +#define PIN_LCD_HSYNC ( 12 ) + +#define PORT_LCD_VSYNC ( GPIOI ) +#define LCD_VSYNC_PIN ( GPIO_Pin_13 ) +#define PIN_LCD_VSYNC ( 13 ) + +#define PORT_LCD_DOTCLK ( GPIOG ) +#define LCD_DOTCLK_PIN ( GPIO_Pin_7 ) +#define PIN_LCD_DOTCLK ( 7 ) + +#define SUPPORTED_LCD_CNT ( 5 ) + +#define LCD_ST7796S_ID ( 0x7796 ) +#define LCD_ILI9481_ID ( 0x9481 ) +#define LCD_ILI9486_ID ( 0x9486 ) +#define LCD_ILI9488_ID ( 0x9488 ) +#define LCD_HX8357D_ID ( 0x99 ) + +#define LCD_DELAY() LCD_Delay() + +typedef void (*lcdSpiInitFucPtr)(void); +typedef unsigned int LcdReadIDFucPtr( void ); + +extern void GPIO_SetDirection( GPIO_TypeDef *GPIOx, unsigned char Pin, unsigned char IsInput ); + +extern void LCD_HX8357D_On( void ); +extern void LCD_ILI9481_On( void ); +extern void LCD_ILI9486_On( void ); +extern void LCD_ILI9488_On( void ); +extern void LCD_ST7796S_On( void ); + + +#define SET_IO_INPUT( PORT, PIN ) GPIO_SetDirection( PORT, PIN, 1 ) +#define SET_IO_OUTPUT( PORT, PIN ) GPIO_SetDirection( PORT, PIN, 0 ) + +#define LCD_NRST_HIGH() GPIO_WriteBit(LCD_NRST_GPIO, LCD_NRST_GPIO_PIN, Bit_SET) +#define LCD_NRST_LOW() GPIO_WriteBit(LCD_NRST_GPIO, LCD_NRST_GPIO_PIN, Bit_RESET) + +#define LCD_CS_HIGH() GPIO_WriteBit(LCD_SPI_GPIO, LCD_SPI_CS_GPIO_PIN, Bit_SET) +#define LCD_CS_LOW() GPIO_WriteBit(LCD_SPI_GPIO, LCD_SPI_CS_GPIO_PIN, Bit_RESET) + +#define LCD_SCK_HIGH() GPIO_WriteBit(LCD_SPI_GPIO, LCD_SPI_SCK_GPIO_PIN, Bit_SET) +#define LCD_SCK_LOW() GPIO_WriteBit(LCD_SPI_GPIO, LCD_SPI_SCK_GPIO_PIN, Bit_RESET) + +#define LCD_MOSI_HIGH() GPIO_WriteBit(LCD_SPI_GPIO, LCD_SPI_MOSI_GPIO_PIN, Bit_SET) +#define LCD_MOSI_LOW() GPIO_WriteBit(LCD_SPI_GPIO, LCD_SPI_MOSI_GPIO_PIN, Bit_RESET) + +#define SET_LCD_CS() GPIO_WriteBit(PORT_LCD_CS, LCD_CS_PIN, Bit_SET) +#define CLR_LCD_CS() GPIO_WriteBit(PORT_LCD_CS, LCD_CS_PIN, Bit_RESET) +#define SET_LCD_CS_OUTPUT() SET_IO_OUTPUT( PORT_LCD_CS, PIN_LCD_CS ) + +#define SET_LCD_CLK() GPIO_WriteBit( PORT_LCD_CLK, LCD_CLK_PIN, Bit_SET ) +#define CLR_LCD_CLK() GPIO_WriteBit( PORT_LCD_CLK, LCD_CLK_PIN, Bit_RESET ) +#define SET_LCD_CLK_OUTPUT() SET_IO_OUTPUT( PORT_LCD_CLK, PIN_LCD_CLK ) + +#define SET_LCD_DATA() GPIO_WriteBit( PORT_LCD_MOSI, LCD_MOSI_PIN, Bit_SET ) +#define CLR_LCD_DATA() GPIO_WriteBit( PORT_LCD_MOSI, LCD_MOSI_PIN, Bit_RESET ) +#define SET_LCD_DATA_INPUT() SET_IO_INPUT( PORT_LCD_MOSI, PIN_LCD_MOSI ) +#define SET_LCD_DATA_OUTPUT() SET_IO_OUTPUT( PORT_LCD_MOSI, PIN_LCD_MOSI ) + +#define READ_LCD_DATA_PIN() GPIO_ReadInputDataBit(PORT_LCD_MOSI, LCD_MOSI_PIN) + + + +#if 1 +#define HORIZONTAL_SYNC_WIDTH ( 4 ) +#define HORIZONTAL_BACK_PORCH ( 24 ) +#define HORIZONTAL_FRONT_PORCH ( 140 - HORIZONTAL_BACK_PORCH ) +#define VERTICAL_SYNC_HEIGHT ( 2 ) +#define VERTICAL_BACK_PORCH ( 10 ) +#define VERTICAL_FRONT_PORCH ( 22 - VERTICAL_BACK_PORCH ) +#else +#define HORIZONTAL_SYNC_WIDTH ( 4 ) +#define HORIZONTAL_BACK_PORCH ( 20 ) +#define HORIZONTAL_FRONT_PORCH ( 60 - HORIZONTAL_BACK_PORCH ) +#define VERTICAL_SYNC_HEIGHT ( 2 ) +#define VERTICAL_BACK_PORCH ( 6 ) +#define VERTICAL_FRONT_PORCH ( 14 - VERTICAL_BACK_PORCH ) +#endif + + + +#endif + + + + diff --git a/radio/src/targets/nv14/libopenui_config.h b/radio/src/targets/nv14/libopenui_config.h new file mode 100644 index 000000000..9db98fd2a --- /dev/null +++ b/radio/src/targets/nv14/libopenui_config.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef _LIBOPENUI_CONFIG_H_ +#define _LIBOPENUI_CONFIG_H_ + +#include "lcd.h" + +constexpr uint32_t MENU_HEADER_BUTTON_WIDTH = 60; +constexpr uint32_t MENU_HEADER_BUTTONS_LEFT = MENU_HEADER_BUTTON_WIDTH; + +#define MENU_TOOLTIPS +constexpr uint32_t MENU_HEADER_HEIGHT = 60; +constexpr uint32_t MENU_TITLE_TOP = 61; +constexpr uint32_t MENU_TITLE_HEIGHT = 22; +constexpr uint32_t MENU_BODY_TOP = MENU_TITLE_TOP + MENU_TITLE_HEIGHT; +constexpr uint32_t MENU_CONTENT_TOP = MENU_BODY_TOP + 1; +constexpr uint32_t MENU_FOOTER_HEIGHT = 0; +constexpr uint32_t MENU_FOOTER_TOP = LCD_H - MENU_FOOTER_HEIGHT; +constexpr uint32_t MENU_BODY_HEIGHT = MENU_FOOTER_TOP - MENU_BODY_TOP; +constexpr uint32_t MENUS_MARGIN_LEFT = 6; + +constexpr uint32_t DEFAULT_SCROLLBAR_X = LCD_W - 10; +constexpr uint32_t DEFAULT_SCROLLBAR_Y = MENU_CONTENT_TOP; +constexpr uint32_t DEFAULT_SCROLLBAR_H = MENU_FOOTER_TOP - DEFAULT_SCROLLBAR_Y - 6; + +constexpr uint32_t PAGE_PADDING = 8; +constexpr uint32_t PAGE_LINE_HEIGHT = 26; +constexpr uint32_t PAGE_LINE_SPACING = 6; +constexpr uint32_t PAGE_INDENT_WIDTH = 10; +constexpr uint32_t PAGE_LABEL_WIDTH = 140; +constexpr uint32_t FH = PAGE_LINE_HEIGHT; +constexpr uint32_t NUM_BODY_LINES = MENU_BODY_HEIGHT / PAGE_LINE_HEIGHT; + +constexpr uint32_t FIELD_PADDING_LEFT = 3; +constexpr uint32_t FIELD_PADDING_TOP = 2; + +constexpr uint32_t CURVE_SIDE_WIDTH = 100; +constexpr uint32_t CURVE_CENTER_X = LCD_W - CURVE_SIDE_WIDTH - 7; +constexpr uint32_t CURVE_CENTER_Y = 151; +constexpr uint32_t CURVE_COORD_WIDTH = 36; +constexpr uint32_t CURVE_COORD_HEIGHT = 17; + +constexpr uint32_t DATETIME_SEPARATOR_X = LCD_W - 53; +constexpr uint32_t DATETIME_LINE1 = 9; +constexpr uint32_t DATETIME_LINE2 = 23; +constexpr uint32_t DATETIME_MIDDLE = (LCD_W + DATETIME_SEPARATOR_X + 4) / 2; + +constexpr uint32_t MENUS_TOOLBAR_BUTTON_WIDTH = 50; +constexpr uint32_t MENUS_TOOLBAR_BUTTON_PADDING = 11; +constexpr rect_t MENUS_TOOLBAR_RECT = { 35, 95, 50, 370 }; + +constexpr uint32_t SUBMENU_LINE_WIDTH = 230; + +#endif // _LIBOPENUI_CONFIG_H_ diff --git a/radio/src/targets/nv14/pulses_driver.cpp b/radio/src/targets/nv14/pulses_driver.cpp new file mode 100644 index 000000000..6b0e126f2 --- /dev/null +++ b/radio/src/targets/nv14/pulses_driver.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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" + +void intmoduleStop(void); +void extmoduleStop(void); + +void intmoduleNoneStart(void); +void intmodulePxxStart(void); + +void extmoduleNoneStart(void); +void extmodulePpmStart(void); +void extmodulePxxStart(void); +#if defined(DSM2) +void extmoduleDsm2Start(void); +#endif +void extmoduleCrossfireStart(void); + +void init_no_pulses(uint32_t port) +{ + if (port == INTERNAL_MODULE) + intmoduleNoneStart(); + else + extmoduleNoneStart(); +} + +void disable_no_pulses(uint32_t port) +{ + if (port == INTERNAL_MODULE) + intmoduleStop(); + else + extmoduleStop(); +} + +void init_ppm(uint32_t port) +{ + if (port == EXTERNAL_MODULE) { + extmodulePpmStart(); + } +} + +void disable_ppm(uint32_t port) +{ + if (port == EXTERNAL_MODULE) { + extmoduleStop(); + } +} + +void init_pxx(uint32_t port) +{ + if (port == INTERNAL_MODULE) + intmodulePxxStart(); + else + extmodulePxxStart(); +} + +void disable_pxx(uint32_t port) +{ + if (port == INTERNAL_MODULE) + intmoduleStop(); + else + extmoduleStop(); +} + +#if defined(DSM2) +void init_dsm2(uint32_t port) +{ + if (port == EXTERNAL_MODULE) { + extmoduleDsm2Start(); + } +} + +void disable_dsm2(uint32_t port) +{ + if (port == EXTERNAL_MODULE) { + extmoduleStop(); + } +} +#endif + +void init_sbusOut(uint32_t port) +{ + init_dsm2(port); +} + +void disable_sbusOut(uint32_t port) +{ + disable_dsm2(port); +} + +void init_crossfire(uint32_t port) +{ + if (port == EXTERNAL_MODULE) { + extmoduleCrossfireStart(); + } +} + +void disable_crossfire(uint32_t port) +{ + if (port == EXTERNAL_MODULE) { + extmoduleStop(); + } +} + +void init_serial(uint32_t module_index, uint32_t baudrate, uint32_t period_half_us) +{ +} + +void disable_serial(uint32_t port) +{ +} \ No newline at end of file diff --git a/radio/src/targets/nv14/pwr_driver.cpp b/radio/src/targets/nv14/pwr_driver.cpp new file mode 100644 index 000000000..c89acaa3d --- /dev/null +++ b/radio/src/targets/nv14/pwr_driver.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "pwr.h" +#include "board.h" + +uint32_t shutdownRequest; // Stores intentional shutdown to avoid reboot loop +uint32_t shutdownReason; // Used for detecting unexpected reboots regardless of reason +uint32_t powerupReason __NOINIT; // Stores power up reason beyond initialization for emergency mode activation + +void pwrInit() +{ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = PWR_ON_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_Init(PWR_GPIO, &GPIO_InitStructure); + + // TODO move this elsewhere! + // Init Module PWR + //GPIO_ResetBits(INTMODULE_PWR_GPIO, INTMODULE_PWR_GPIO_PIN); + //GPIO_InitStructure.GPIO_Pin = INTMODULE_PWR_GPIO_PIN; + //GPIO_Init(INTMODULE_PWR_GPIO, &GPIO_InitStructure); + + //TODO move this elsewhere! + GPIO_ResetBits(EXTMODULE_PWR_GPIO, EXTMODULE_PWR_GPIO_PIN); + GPIO_InitStructure.GPIO_Pin = EXTMODULE_PWR_GPIO_PIN; + GPIO_Init(EXTMODULE_PWR_GPIO, &GPIO_InitStructure); + + //Bluetooth + GPIO_SetBits(BLUETOOTH_ON_GPIO, BLUETOOTH_ON_GPIO_PIN); + GPIO_InitStructure.GPIO_Pin = BLUETOOTH_ON_GPIO_PIN; + GPIO_Init(BLUETOOTH_ON_GPIO, &GPIO_InitStructure); + + // Init PWR SWITCH PIN + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; + GPIO_InitStructure.GPIO_Pin = PWR_SWITCH_GPIO_PIN; + GPIO_Init(PWR_GPIO, &GPIO_InitStructure); + + // TODO move this elsewhere! (plus SD DETECT on other boards) + // Init TRAINER DETECT PIN + // GPIO_InitStructure.GPIO_Pin = TRAINER_DETECT_GPIO_PIN; + // GPIO_Init(TRAINER_DETECT_GPIO, &GPIO_InitStructure); + + pwrOn(); +} + +void pwrOn() +{ + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = PWR_ON_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_Init(PWR_GPIO, &GPIO_InitStructure); + + GPIO_SetBits(PWR_GPIO, PWR_ON_GPIO_PIN); + + shutdownRequest = NO_SHUTDOWN_REQUEST; + shutdownReason = DIRTY_SHUTDOWN; +} + +void pwrOff() +{ + // Shutdown the Haptic + hapticDone(); + + shutdownRequest = SHUTDOWN_REQUEST; + shutdownReason = NORMAL_POWER_OFF; + GPIO_ResetBits(PWR_GPIO, PWR_ON_GPIO_PIN); +} + +uint32_t pwrPressed() +{ + return GPIO_ReadInputDataBit(PWR_GPIO, PWR_SWITCH_GPIO_PIN) == Bit_RESET; +} + +void pwrResetHandler() +{ + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOJEN; + + // these two NOPs are needed (see STM32F errata sheet) before the peripheral + // register can be written after the peripheral clock was enabled + __ASM volatile ("nop"); + __ASM volatile ("nop"); + + // We get here whether we are powering up normally, we had an unexpected reboot or we have just powered down normally. + // We want: + // - In the 2nd case, to power ON as soon as possible if an unexpected reboot happened + // (we get there running on remaining capacitor charge, soft power having been cut by the RESET). + // - In the 3rd case, NOT power on as that would prevent from turning the system off. + // - The 1st case does not need to be handled here, but will be as a result of the handling for the 3rd case, see below. + // + // shutdownRequest is used to handle the 3rd case. If we really powered down on purpose this will still be set to SHUTDOWN_REQUEST + // as we left it in pwrOff(). If however we had an unexpected reboot, it would be set to NO_SHUTDOWN_REQUEST as we set it in pwrOn(). + // Any other value (e.g. resulting from data corruption) would also keep power on for safety, so this variable can NOT be used + // to detect an unexpected reboot (on a normal power on the contents of the variable are random). + // + // shutdownReason is used to differentiate between an unexpected reboot and a normal power on. We set it to DIRTY_SHUTDOWN in pwrOn() + // in anticipation of a potential reboot. Should there be one the value should be preserved and signal below that we rebooted unexpectedly. + // If it is NOT set to DIRTY_SHUTDOWN we likely had a normal boot and its contents are random. Due to the need to initialize it to detect a + // potential failure ASAP we cannot use it to determine in the firmware why we got there, it has to be buffered. + // + // powerupReason is there to cater for that, and is what is used in the firmware to decide whether we have to enter emergency mode. + // This variable needs to be in a RAM section that is not initialized or zeroed, since once we exit this pwrResetHandler() function the + // C runtime would otherwise overwrite it during program init. + // Only for X12, X10 power circuit causes inability to shut down on some samples. + + if (WAS_RESET_BY_WATCHDOG_OR_SOFTWARE()) { + pwrOn(); + } +} diff --git a/radio/src/targets/nv14/sdram_driver.c b/radio/src/targets/nv14/sdram_driver.c new file mode 100644 index 000000000..e9630bfca --- /dev/null +++ b/radio/src/targets/nv14/sdram_driver.c @@ -0,0 +1,254 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "board.h" + +#define SDRAM_MODEREG_BURST_LENGTH_1 ((uint16_t)0x0000) +#define SDRAM_MODEREG_BURST_LENGTH_2 ((uint16_t)0x0001) +#define SDRAM_MODEREG_BURST_LENGTH_4 ((uint16_t)0x0002) +#define SDRAM_MODEREG_BURST_LENGTH_8 ((uint16_t)0x0004) +#define SDRAM_MODEREG_BURST_FULL_PAGE ((uint16_t)0x0007) +#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL ((uint16_t)0x0000) +#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED ((uint16_t)0x0008) +#define SDRAM_MODEREG_CAS_LATENCY_1 ((uint16_t)0x0010) +#define SDRAM_MODEREG_CAS_LATENCY_2 ((uint16_t)0x0020) +#define SDRAM_MODEREG_CAS_LATENCY_3 ((uint16_t)0x0030) +#define SDRAM_MODEREG_OPERATING_MODE_STANDARD ((uint16_t)0x0000) +#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000) +#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE ((uint16_t)0x0200) + +void SDRAM_GPIOConfig(void) +{ + /* + ------------------------------------------------------------------------------------------------------------------------------------------------ + PC3 <-> FMC_SDCKE0 | PD0 <-> FMC_D2 | PE0 <-> FMC_NBL0 | PF0 <-> FMC_A0 | PG0 <-> FMC_A10 | PH3 <-> FMC_SDNE0 | PI0 <-> FMC_D24 + | PD1 <-> FMC_D3 | PE1 <-> FMC_NBL1 | PF1 <-> FMC_A1 | PG1 <-> FMC_A11 | PH5 <-> FMC_SDNWE | PI1 <-> FMC_D25 + | PD8 <-> FMC_D13 | PE7 <-> FMC_D4 | PF2 <-> FMC_A2 | PG4 <-> FMC_BA0 | PH8 <-> FMC_D16 | PI2 <-> FMC_D26 + | PD9 <-> FMC_D14 | PE8 <-> FMC_D5 | PF3 <-> FMC_A3 | PG5 <-> FMC_BA1 | PH9 <-> FMC_D17 | PI3 <-> FMC_D27 + | PD10 <-> FMC_D15 | PE9 <-> FMC_D6 | PF4 <-> FMC_A4 | PG8 <-> FMC_SDCLK | PH10 <-> FMC_D18 | PI4 <-> FMC_NBL2 + | PD14 <-> FMC_D0 | PE10 <-> FMC_D7 | PF5 <-> FMC_A5 | PG15 <-> FMC_NCAS | PH11 <-> FMC_D19 | PI5 <-> FMC_NBL3 + | PD15 <-> FMC_D1 | PE11 <-> FMC_D8 | PF11 <-> FMC_NRAS | | PH12 <-> FMC_D20 | PI6 <-> FMC_D28 + | | PE12 <-> FMC_D9 | PF12 <-> FMC_A6 | | PH13 <-> FMC_D21 | PI7 <-> FMC_D29 + | | PE13 <-> FMC_D10 | PF13 <-> FMC_A7 | | PH14 <-> FMC_D22 | PI9 <-> FMC_D30 + | | PE14 <-> FMC_D11 | PF14 <-> FMC_A8 | | PH15 <-> FMC_D23 | PI10 <-> FMC_D31 + | | PE15 <-> FMC_D12 | PF15 <-> FMC_A9 | | | + */ + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + + /* GPIOC configuration */ + GPIO_PinAFConfig(GPIOC, GPIO_PinSource3 , GPIO_AF_FMC); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 ; + GPIO_Init(GPIOC, &GPIO_InitStructure); + + /* GPIOD configuration */ + GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOD, GPIO_PinSource10, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_FMC); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_14 | GPIO_Pin_15; + GPIO_Init(GPIOD, &GPIO_InitStructure); + + /* GPIOE configuration */ + GPIO_PinAFConfig(GPIOE, GPIO_PinSource0, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource1, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource7, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource8, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource9, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource10, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource11, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource12, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource13, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource14, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOE, GPIO_PinSource15, GPIO_AF_FMC); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; + GPIO_Init(GPIOE, &GPIO_InitStructure); + + /* GPIOF configuration */ + GPIO_PinAFConfig(GPIOF, GPIO_PinSource0, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource1, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource2, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource3, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource4, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource5, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource11, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource12, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource13, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource14, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOF, GPIO_PinSource15, GPIO_AF_FMC); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; + GPIO_Init(GPIOF, &GPIO_InitStructure); + + /* GPIOG configuration */ + GPIO_PinAFConfig(GPIOG, GPIO_PinSource0 , GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOG, GPIO_PinSource1 , GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOG, GPIO_PinSource4 , GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOG, GPIO_PinSource5 , GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOG, GPIO_PinSource8 , GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOG, GPIO_PinSource15 , GPIO_AF_FMC); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_8 | GPIO_Pin_15; + GPIO_Init(GPIOG, &GPIO_InitStructure); + + /* GPIOH configuration */ + GPIO_PinAFConfig(GPIOH, GPIO_PinSource3, GPIO_AF_FMC); + GPIO_PinAFConfig(GPIOH, GPIO_PinSource5, GPIO_AF_FMC); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_5; + GPIO_Init(GPIOH, &GPIO_InitStructure); +} + +void SDRAM_InitSequence(void) +{ + FMC_SDRAMCommandTypeDef FMC_SDRAMCommandStructure; + uint32_t tmpr = 0; + + /* Step 3 --------------------------------------------------------------------*/ + /* Configure a clock configuration enable command */ + FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_CLK_Enabled; + FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank1; + FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 1; + FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0; + /* Wait until the SDRAM controller is ready */ + while(FMC_GetFlagStatus(FMC_Bank1_SDRAM, FMC_FLAG_Busy) != RESET) { + } + /* Send the command */ + FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure); + + /* Step 4 --------------------------------------------------------------------*/ + /* Insert 100 ms delay */ + delay_ms(100); + + /* Step 5 --------------------------------------------------------------------*/ + /* Configure a PALL (precharge all) command */ + FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_PALL; + FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank1; + FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 1; + FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0; + /* Wait until the SDRAM controller is ready */ + while(FMC_GetFlagStatus(FMC_Bank1_SDRAM, FMC_FLAG_Busy) != RESET) { + } + /* Send the command */ + FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure); + + /* Step 6 --------------------------------------------------------------------*/ + /* Configure a Auto-Refresh command */ + FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_AutoRefresh; + FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank1; + FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 4; + FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0; + /* Wait until the SDRAM controller is ready */ + while(FMC_GetFlagStatus(FMC_Bank1_SDRAM, FMC_FLAG_Busy) != RESET) { + } + /* Send the first command */ + FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure); + + /* Wait until the SDRAM controller is ready */ + while(FMC_GetFlagStatus(FMC_Bank1_SDRAM, FMC_FLAG_Busy) != RESET) { + } + /* Send the second command */ + FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure); + + /* Step 7 --------------------------------------------------------------------*/ + /* Program the external memory mode register */ + tmpr = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_2 | + SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL | + SDRAM_MODEREG_CAS_LATENCY_3 | + SDRAM_MODEREG_OPERATING_MODE_STANDARD | + SDRAM_MODEREG_WRITEBURST_MODE_SINGLE; + + /* Configure a load Mode register command*/ + FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_LoadMode; + FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank1; + FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 1; + FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = tmpr; + /* Wait until the SDRAM controller is ready */ + while(FMC_GetFlagStatus(FMC_Bank1_SDRAM, FMC_FLAG_Busy) != RESET) { + } + /* Send the command */ + FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure); + + /* Step 8 --------------------------------------------------------------------*/ + /* Set the refresh rate counter */ + /* (15.62 us x Freq) - 20 */ + /* Set the device refresh counter */ + FMC_SetRefreshCount(683);//904 + /* Wait until the SDRAM controller is ready */ + while(FMC_GetFlagStatus(FMC_Bank1_SDRAM, FMC_FLAG_Busy) != RESET) { + } +} + + +void SDRAM_Init(void) +{ + // Clocks must be enabled here, because the SDRAM_Init() is called before main + RCC_AHB1PeriphClockCmd(SDRAM_RCC_AHB1Periph, ENABLE); + RCC_AHB3PeriphClockCmd(SDRAM_RCC_AHB3Periph, ENABLE); + + /* GPIO configuration for FMC SDRAM bank */ + SDRAM_GPIOConfig(); + + /* FMC Configuration ---------------------------------------------------------*/ + FMC_SDRAMInitTypeDef FMC_SDRAMInitStructure; + FMC_SDRAMTimingInitTypeDef FMC_SDRAMTimingInitStructure; + + /* FMC SDRAM Bank configuration */ + /* Timing configuration for 90 Mhz of SD clock frequency (168Mhz/2) */ + /* TMRD: 2 Clock cycles */ + FMC_SDRAMTimingInitStructure.FMC_LoadToActiveDelay = 2; + /* TXSR: min=70ns (7x11.11ns) */ + FMC_SDRAMTimingInitStructure.FMC_ExitSelfRefreshDelay = 7; + /* TRAS: min=42ns (4x11.11ns) max=120k (ns) */ + FMC_SDRAMTimingInitStructure.FMC_SelfRefreshTime = 4; + /* TRC: min=70 (7x11.11ns) */ + FMC_SDRAMTimingInitStructure.FMC_RowCycleDelay = 7; + /* TWR: min=1+ 7ns (1+1x11.11ns) */ + FMC_SDRAMTimingInitStructure.FMC_WriteRecoveryTime = 2; + /* TRP: 20ns => 2x11.11ns */ + FMC_SDRAMTimingInitStructure.FMC_RPDelay = 2; + /* TRCD: 20ns => 2x11.11ns */ + FMC_SDRAMTimingInitStructure.FMC_RCDDelay = 2; + + /* FMC SDRAM control configuration */ + FMC_SDRAMInitStructure.FMC_Bank = FMC_Bank1_SDRAM; + /* Row addressing: [7:0] */ + FMC_SDRAMInitStructure.FMC_ColumnBitsNumber = FMC_ColumnBits_Number_8b; + /* Column addressing: [11:0] */ + FMC_SDRAMInitStructure.FMC_RowBitsNumber = FMC_RowBits_Number_12b; + FMC_SDRAMInitStructure.FMC_SDMemoryDataWidth = FMC_SDMemory_Width_16b; + FMC_SDRAMInitStructure.FMC_InternalBankNumber = FMC_InternalBank_Number_4; + FMC_SDRAMInitStructure.FMC_CASLatency = FMC_CAS_Latency_3; + FMC_SDRAMInitStructure.FMC_WriteProtection = FMC_Write_Protection_Disable; + FMC_SDRAMInitStructure.FMC_SDClockPeriod = FMC_SDClock_Period_2; + FMC_SDRAMInitStructure.FMC_ReadBurst = FMC_Read_Burst_Enable; + FMC_SDRAMInitStructure.FMC_ReadPipeDelay = FMC_ReadPipe_Delay_1; + FMC_SDRAMInitStructure.FMC_SDRAMTimingStruct = &FMC_SDRAMTimingInitStructure; + + /* FMC SDRAM bank initialization */ + FMC_SDRAMInit(&FMC_SDRAMInitStructure); + + /* FMC SDRAM device initialization sequence */ + SDRAM_InitSequence(); +} diff --git a/radio/src/targets/nv14/startup_stm32f42_43xxx.s b/radio/src/targets/nv14/startup_stm32f42_43xxx.s new file mode 100644 index 000000000..68671dc34 --- /dev/null +++ b/radio/src/targets/nv14/startup_stm32f42_43xxx.s @@ -0,0 +1,565 @@ +/** + ****************************************************************************** + * @file startup_stm32f40_41xxx.s + * @author MCD Application Team + * @version V1.3.0 + * @date 08-November-2013 + * @brief STM32F40xxx/41xxx Devices vector table for RIDE7 toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Configure the clock system and the external SRAM mounted on + * STM324xG-EVAL board to be used as data memory (optional, + * to be enabled by user) + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M4 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT 2013 STMicroelectronics

+ * + * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.st.com/software_license_agreement_liberty_v2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss +/* stack used for SystemInit_ExtMemCtl; always internal RAM used */ + +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + + bl pwrResetHandler /*jump to WDT reset handler where soft power control pin is turned on as soon as possible */ + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss + +/*Paint Main Stack */ + ldr r2, = _main_stack_start +PaintMainStack: + movs r3, #0x55555555 + str r3, [r2], #4 +LoopPaintMainStack: + ldr r3, = _estack + cmp r2, r3 + bcc PaintMainStack + +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call C++ constructors for static objects */ + bl __libc_init_array +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * @param None + * @retval None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +*******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + + /* External Interrupts */ + .word WWDG_IRQHandler /* Window WatchDog */ + .word PVD_IRQHandler /* PVD through EXTI Line detection */ + .word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */ + .word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */ + .word FLASH_IRQHandler /* FLASH */ + .word RCC_IRQHandler /* RCC */ + .word EXTI0_IRQHandler /* EXTI Line0 */ + .word EXTI1_IRQHandler /* EXTI Line1 */ + .word EXTI2_IRQHandler /* EXTI Line2 */ + .word EXTI3_IRQHandler /* EXTI Line3 */ + .word EXTI4_IRQHandler /* EXTI Line4 */ + .word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */ + .word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */ + .word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */ + .word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */ + .word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */ + .word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */ + .word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */ + .word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */ + .word CAN1_TX_IRQHandler /* CAN1 TX */ + .word CAN1_RX0_IRQHandler /* CAN1 RX0 */ + .word CAN1_RX1_IRQHandler /* CAN1 RX1 */ + .word CAN1_SCE_IRQHandler /* CAN1 SCE */ + .word EXTI9_5_IRQHandler /* External Line[9:5]s */ + .word TIM1_BRK_TIM9_IRQHandler /* TIM1 Break and TIM9 */ + .word TIM1_UP_TIM10_IRQHandler /* TIM1 Update and TIM10 */ + .word TIM1_TRG_COM_TIM11_IRQHandler /* TIM1 Trigger and Commutation and TIM11 */ + .word TIM1_CC_IRQHandler /* TIM1 Capture Compare */ + .word TIM2_IRQHandler /* TIM2 */ + .word TIM3_IRQHandler /* TIM3 */ + .word TIM4_IRQHandler /* TIM4 */ + .word I2C1_EV_IRQHandler /* I2C1 Event */ + .word I2C1_ER_IRQHandler /* I2C1 Error */ + .word I2C2_EV_IRQHandler /* I2C2 Event */ + .word I2C2_ER_IRQHandler /* I2C2 Error */ + .word SPI1_IRQHandler /* SPI1 */ + .word SPI2_IRQHandler /* SPI2 */ + .word USART1_IRQHandler /* USART1 */ + .word USART2_IRQHandler /* USART2 */ + .word USART3_IRQHandler /* USART3 */ + .word EXTI15_10_IRQHandler /* External Line[15:10]s */ + .word RTC_Alarm_IRQHandler /* RTC Alarm (A and B) through EXTI Line */ + .word OTG_FS_WKUP_IRQHandler /* USB OTG FS Wakeup through EXTI line */ + .word TIM8_BRK_TIM12_IRQHandler /* TIM8 Break and TIM12 */ + .word TIM8_UP_TIM13_IRQHandler /* TIM8 Update and TIM13 */ + .word TIM8_TRG_COM_TIM14_IRQHandler /* TIM8 Trigger and Commutation and TIM14 */ + .word TIM8_CC_IRQHandler /* TIM8 Capture Compare */ + .word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */ + .word FSMC_IRQHandler /* FSMC */ + .word SDIO_IRQHandler /* SDIO */ + .word TIM5_IRQHandler /* TIM5 */ + .word SPI3_IRQHandler /* SPI3 */ + .word UART4_IRQHandler /* UART4 */ + .word UART5_IRQHandler /* UART5 */ + .word TIM6_DAC_IRQHandler /* TIM6 and DAC1&2 underrun errors */ + .word TIM7_IRQHandler /* TIM7 */ + .word DMA2_Stream0_IRQHandler /* DMA2 Stream 0 */ + .word DMA2_Stream1_IRQHandler /* DMA2 Stream 1 */ + .word DMA2_Stream2_IRQHandler /* DMA2 Stream 2 */ + .word DMA2_Stream3_IRQHandler /* DMA2 Stream 3 */ + .word DMA2_Stream4_IRQHandler /* DMA2 Stream 4 */ + .word ETH_IRQHandler /* Ethernet */ + .word ETH_WKUP_IRQHandler /* Ethernet Wakeup through EXTI line */ + .word CAN2_TX_IRQHandler /* CAN2 TX */ + .word CAN2_RX0_IRQHandler /* CAN2 RX0 */ + .word CAN2_RX1_IRQHandler /* CAN2 RX1 */ + .word CAN2_SCE_IRQHandler /* CAN2 SCE */ + .word OTG_FS_IRQHandler /* USB OTG FS */ + .word DMA2_Stream5_IRQHandler /* DMA2 Stream 5 */ + .word DMA2_Stream6_IRQHandler /* DMA2 Stream 6 */ + .word DMA2_Stream7_IRQHandler /* DMA2 Stream 7 */ + .word USART6_IRQHandler /* USART6 */ + .word I2C3_EV_IRQHandler /* I2C3 event */ + .word I2C3_ER_IRQHandler /* I2C3 error */ + .word OTG_HS_EP1_OUT_IRQHandler /* USB OTG HS End Point 1 Out */ + .word OTG_HS_EP1_IN_IRQHandler /* USB OTG HS End Point 1 In */ + .word OTG_HS_WKUP_IRQHandler /* USB OTG HS Wakeup through EXTI */ + .word OTG_HS_IRQHandler /* USB OTG HS */ + .word DCMI_IRQHandler /* DCMI */ + .word CRYP_IRQHandler /* CRYP crypto */ + .word HASH_RNG_IRQHandler /* Hash and Rng */ + .word FPU_IRQHandler /* FPU */ + .word UART7_IRQHandler /* UART7 */ + .word UART8_IRQHandler /* UART8 */ + .word SPI4_IRQHandler /* SPI4 */ + .word SPI5_IRQHandler /* SPI5 */ + .word SPI6_IRQHandler /* SPI6 */ + .word SAI1_IRQHandler /* SAI1 */ + .word LTDC_IRQHandler /* LTDC */ + .word LTDC_ER_IRQHandler /* LTDC error */ + .word DMA2D_IRQHandler /* DMA2D */ + +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMP_STAMP_IRQHandler + .thumb_set TAMP_STAMP_IRQHandler,Default_Handler + + .weak RTC_WKUP_IRQHandler + .thumb_set RTC_WKUP_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Stream0_IRQHandler + .thumb_set DMA1_Stream0_IRQHandler,Default_Handler + + .weak DMA1_Stream1_IRQHandler + .thumb_set DMA1_Stream1_IRQHandler,Default_Handler + + .weak DMA1_Stream2_IRQHandler + .thumb_set DMA1_Stream2_IRQHandler,Default_Handler + + .weak DMA1_Stream3_IRQHandler + .thumb_set DMA1_Stream3_IRQHandler,Default_Handler + + .weak DMA1_Stream4_IRQHandler + .thumb_set DMA1_Stream4_IRQHandler,Default_Handler + + .weak DMA1_Stream5_IRQHandler + .thumb_set DMA1_Stream5_IRQHandler,Default_Handler + + .weak DMA1_Stream6_IRQHandler + .thumb_set DMA1_Stream6_IRQHandler,Default_Handler + + .weak ADC_IRQHandler + .thumb_set ADC_IRQHandler,Default_Handler + + .weak CAN1_TX_IRQHandler + .thumb_set CAN1_TX_IRQHandler,Default_Handler + + .weak CAN1_RX0_IRQHandler + .thumb_set CAN1_RX0_IRQHandler,Default_Handler + + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + + .weak CAN1_SCE_IRQHandler + .thumb_set CAN1_SCE_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_TIM9_IRQHandler + .thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler + + .weak TIM1_UP_TIM10_IRQHandler + .thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_TIM11_IRQHandler + .thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTC_Alarm_IRQHandler + .thumb_set RTC_Alarm_IRQHandler,Default_Handler + + .weak OTG_FS_WKUP_IRQHandler + .thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler + + .weak TIM8_BRK_TIM12_IRQHandler + .thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler + + .weak TIM8_UP_TIM13_IRQHandler + .thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler + + .weak TIM8_TRG_COM_TIM14_IRQHandler + .thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler + + .weak TIM8_CC_IRQHandler + .thumb_set TIM8_CC_IRQHandler,Default_Handler + + .weak DMA1_Stream7_IRQHandler + .thumb_set DMA1_Stream7_IRQHandler,Default_Handler + + .weak FSMC_IRQHandler + .thumb_set FSMC_IRQHandler,Default_Handler + + .weak SDIO_IRQHandler + .thumb_set SDIO_IRQHandler,Default_Handler + + .weak TIM5_IRQHandler + .thumb_set TIM5_IRQHandler,Default_Handler + + .weak SPI3_IRQHandler + .thumb_set SPI3_IRQHandler,Default_Handler + + .weak UART4_IRQHandler + .thumb_set UART4_IRQHandler,Default_Handler + + .weak UART5_IRQHandler + .thumb_set UART5_IRQHandler,Default_Handler + + .weak TIM6_DAC_IRQHandler + .thumb_set TIM6_DAC_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + + .weak DMA2_Stream0_IRQHandler + .thumb_set DMA2_Stream0_IRQHandler,Default_Handler + + .weak DMA2_Stream1_IRQHandler + .thumb_set DMA2_Stream1_IRQHandler,Default_Handler + + .weak DMA2_Stream2_IRQHandler + .thumb_set DMA2_Stream2_IRQHandler,Default_Handler + + .weak DMA2_Stream3_IRQHandler + .thumb_set DMA2_Stream3_IRQHandler,Default_Handler + + .weak DMA2_Stream4_IRQHandler + .thumb_set DMA2_Stream4_IRQHandler,Default_Handler + + .weak ETH_IRQHandler + .thumb_set ETH_IRQHandler,Default_Handler + + .weak ETH_WKUP_IRQHandler + .thumb_set ETH_WKUP_IRQHandler,Default_Handler + + .weak CAN2_TX_IRQHandler + .thumb_set CAN2_TX_IRQHandler,Default_Handler + + .weak CAN2_RX0_IRQHandler + .thumb_set CAN2_RX0_IRQHandler,Default_Handler + + .weak CAN2_RX1_IRQHandler + .thumb_set CAN2_RX1_IRQHandler,Default_Handler + + .weak CAN2_SCE_IRQHandler + .thumb_set CAN2_SCE_IRQHandler,Default_Handler + + .weak OTG_FS_IRQHandler + .thumb_set OTG_FS_IRQHandler,Default_Handler + + .weak DMA2_Stream5_IRQHandler + .thumb_set DMA2_Stream5_IRQHandler,Default_Handler + + .weak DMA2_Stream6_IRQHandler + .thumb_set DMA2_Stream6_IRQHandler,Default_Handler + + .weak DMA2_Stream7_IRQHandler + .thumb_set DMA2_Stream7_IRQHandler,Default_Handler + + .weak USART6_IRQHandler + .thumb_set USART6_IRQHandler,Default_Handler + + .weak I2C3_EV_IRQHandler + .thumb_set I2C3_EV_IRQHandler,Default_Handler + + .weak I2C3_ER_IRQHandler + .thumb_set I2C3_ER_IRQHandler,Default_Handler + + .weak OTG_HS_EP1_OUT_IRQHandler + .thumb_set OTG_HS_EP1_OUT_IRQHandler,Default_Handler + + .weak OTG_HS_EP1_IN_IRQHandler + .thumb_set OTG_HS_EP1_IN_IRQHandler,Default_Handler + + .weak OTG_HS_WKUP_IRQHandler + .thumb_set OTG_HS_WKUP_IRQHandler,Default_Handler + + .weak OTG_HS_IRQHandler + .thumb_set OTG_HS_IRQHandler,Default_Handler + + .weak DCMI_IRQHandler + .thumb_set DCMI_IRQHandler,Default_Handler + + .weak CRYP_IRQHandler + .thumb_set CRYP_IRQHandler,Default_Handler + + .weak HASH_RNG_IRQHandler + .thumb_set HASH_RNG_IRQHandler,Default_Handler + + .weak FPU_IRQHandler + .thumb_set FPU_IRQHandler,Default_Handler + + .weak UART7_IRQHandler + .thumb_set UART7_IRQHandler,Default_Handler + + .weak UART8_IRQHandler + .thumb_set UART8_IRQHandler,Default_Handler + + .weak SPI4_IRQHandler + .thumb_set SPI4_IRQHandler,Default_Handler + + .weak SPI5_IRQHandler + .thumb_set SPI5_IRQHandler,Default_Handler + + .weak SPI6_IRQHandler + .thumb_set SPI6_IRQHandler,Default_Handler + + .weak SAI1_IRQHandler + .thumb_set SAI1_IRQHandler,Default_Handler + + .weak LTDC_IRQHandler + .thumb_set LTDC_IRQHandler,Default_Handler + + .weak LTDC_ER_IRQHandler + .thumb_set LTDC_ER_IRQHandler,Default_Handler + + .weak DMA2D_IRQHandler + .thumb_set DMA2D_IRQHandler,Default_Handler + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/radio/src/targets/nv14/stm32_ramboot.ld b/radio/src/targets/nv14/stm32_ramboot.ld new file mode 100644 index 000000000..8b85c4005 --- /dev/null +++ b/radio/src/targets/nv14/stm32_ramboot.ld @@ -0,0 +1,186 @@ +/* +***************************************************************************** +** +** File : stm32f4_flash.ld +** +** Abstract : Linker script for STM32F439 Device with +** 2MByte FLASH, 192KByte SRAM, 64KByte CCM +** +** Set heap size, stack size and stack location according +** to application requirements. +** +** Set memory bank area and size if external memory is used. +** +** Target : STMicroelectronics STM32 +** +***************************************************************************** +*/ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = 0x20030000; /* end of 192K RAM */ +_heap_end = 0xC0800000; /* end of 8192K SDRAM */ + +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 0; /* required amount of heap */ +_Main_Stack_Size = 1024; /* required amount of stack for interrupt stack (Main stack) */ + +/* Main stack end */ +_main_stack_start = _estack - _Main_Stack_Size; + +/* Specify the memory areas */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K + CCM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K + MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K + SDRAM(xrw) : ORIGIN = 0xC0000000, LENGTH = 8192K +} + +/* Define output sections */ +SECTIONS +{ + /* The startup code goes first into FLASH */ + + /* The program code and other data goes into FLASH */ + .text : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + KEEP(*(.version)) + KEEP(*(.bootversiondata)) + . = ALIGN(4); /* Align the start of the text part */ + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >FLASH + + .ARM.extab : + { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array*)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH + + /* used by the startup to initialize data */ + _sidata = .; + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : AT ( _sidata ) + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >CCM + + /* Uninitialized data section */ + . = ALIGN(4); + .bss : + { + /* This is used by the startup in order to initialize the .bss secion */ + _sbss = .; /* define a global symbol at bss start */ + /* __bss_start__ = _sbss; */ + *(.bss) + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + } >CCM + + /* Non-zeroed data section */ + . = ALIGN(4); + .noinit (NOLOAD) : + { + *(.noinit) + } >CCM + + /* collect all uninitialized .ccm sections */ + .ram (NOLOAD) : + { + . = ALIGN(4); + _sram = .; + *(.ram) + } >RAM + + /* User_heap_stack section, used to check that there is enough RAM left */ + ._user_heap_stack : + { + . = ALIGN(4); + . = . + _Min_Heap_Size; + . = . + _Main_Stack_Size; + . = ALIGN(4); + } >RAM + + /* MEMORY_bank1 section, code must be located here explicitly */ + /* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */ + .memory_b1_text : + { + *(.mb1text) /* .mb1text sections (code) */ + *(.mb1text*) /* .mb1text* sections (code) */ + *(.mb1rodata) /* read-only data (constants) */ + *(.mb1rodata*) + } >MEMORY_B1 + + .sdram (NOLOAD) : + { + *(.sdram) + *(.sdram*) + *(.sdram_rodata) + *(.sdram_rodata*) + . = ALIGN(4); + _eram = .; + } >SDRAM + + PROVIDE ( end = _eram ); + PROVIDE ( _end = _eram ); + + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/radio/src/targets/nv14/stm32f4_flash.ld b/radio/src/targets/nv14/stm32f4_flash.ld new file mode 100644 index 000000000..1f631f4eb --- /dev/null +++ b/radio/src/targets/nv14/stm32f4_flash.ld @@ -0,0 +1,192 @@ +/* +***************************************************************************** +** +** File : stm32f4_flash.ld +** +** Abstract : Linker script for STM32F439 Device with +** 2MByte FLASH, 192KByte SRAM, 64KByte CCM +** +** Set heap size, stack size and stack location according +** to application requirements. +** +** Set memory bank area and size if external memory is used. +** +** Target : STMicroelectronics STM32 +** +***************************************************************************** +*/ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = 0x10010000; /* end of 64K CCM */ +_heap_end = 0xC0800000; /* end of 8192K SDRAM */ + +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 0; /* required amount of heap */ +_Main_Stack_Size = 1024; /* required amount of stack for interrupt stack (Main stack) */ + +/* Main stack end */ +_main_stack_start = _estack - _Main_Stack_Size; + +/* Specify the memory areas */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K + CCM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K + MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K + SDRAM(xrw) : ORIGIN = 0xC0000000, LENGTH = 8192K +} + +/* Define output sections */ +SECTIONS +{ + /* The startup code goes first into FLASH */ + + /* The program code and other data goes into FLASH */ + .text : + { + FILL(0xFFFF) + + CREATE_OBJECT_SYMBOLS + + + + + _stext = .; /* Provide the name for the start of this section */ + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + KEEP(*(.fwversiondata)) + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >FLASH + + .ARM.extab : + { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array*)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH + + /* used by the startup to initialize data */ + _sidata = .; + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : AT ( _sidata ) + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >CCM + + /* Uninitialized data section */ + . = ALIGN(4); + .bss : + { + /* This is used by the startup in order to initialize the .bss secion */ + _sbss = .; /* define a global symbol at bss start */ + /* __bss_start__ = _sbss; */ + *(.bss) + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + } >CCM + + /* Non-zeroed data section */ + . = ALIGN(4); + .noinit (NOLOAD) : + { + *(.noinit) + } >CCM + + /* collect all uninitialized .ccm sections */ + .ram (NOLOAD) : + { + . = ALIGN(4); + _sram = .; + *(.ram) + } >RAM + + /* User_heap_stack section, used to check that there is enough RAM left */ + ._user_heap_stack : + { + . = ALIGN(4); + . = . + _Min_Heap_Size; + . = . + _Main_Stack_Size; + . = ALIGN(4); + } >RAM + + /* MEMORY_bank1 section, code must be located here explicitly */ + /* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */ + .memory_b1_text : + { + *(.mb1text) /* .mb1text sections (code) */ + *(.mb1text*) /* .mb1text* sections (code) */ + *(.mb1rodata) /* read-only data (constants) */ + *(.mb1rodata*) + } >MEMORY_B1 + + .sdram (NOLOAD) : + { + *(.sdram) + *(.sdram*) + *(.sdram_rodata) + *(.sdram_rodata*) + . = ALIGN(4); + _eram = .; + } >SDRAM + + PROVIDE ( end = _eram ); + PROVIDE ( _end = _eram ); + + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/radio/src/targets/nv14/stm32f4_flash_bootloader.ld b/radio/src/targets/nv14/stm32f4_flash_bootloader.ld new file mode 100644 index 000000000..93eb39de4 --- /dev/null +++ b/radio/src/targets/nv14/stm32f4_flash_bootloader.ld @@ -0,0 +1,192 @@ +/* +***************************************************************************** +** +** File : stm32f4_flash.ld +** +** Abstract : Linker script for STM32F439 Device with +** 1MByte FLASH, 192KByte SRAM, 64KByte CCM +** +** Set heap size, stack size and stack location according +** to application requirements. +** +** Set memory bank area and size if external memory is used. +** +** Target : STMicroelectronics STM32 +** +***************************************************************************** +*/ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = 0x10010000; /* end of 64K CCM */ +_heap_end = 0xC0800000; /* end of 8192K SDRAM */ + +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 0; /* required amount of heap */ +_Main_Stack_Size = 1024; /* required amount of stack for interrupt stack (Main stack) */ + +/* Main stack end */ +_main_stack_start = _estack - _Main_Stack_Size; + +/* Specify the memory areas */ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K + CCM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K + MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K + SDRAM(xrw) : ORIGIN = 0xC0000000, LENGTH = 8192K +} + +/* Define output sections */ +SECTIONS +{ + /* The startup code goes first into FLASH */ + + /* The program code and other data goes into FLASH */ + .text : + { + FILL(0xFFFF) + + CREATE_OBJECT_SYMBOLS + + KEEP(*(.bootloader)) /* Bootloader code */ + + . = 0x20000; /* Set the start of the main program */ + _stext = .; /* Provide the name for the start of this section */ + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + KEEP(*(.fwversiondata)) + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >FLASH + + .ARM.extab : + { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array*)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH + + /* used by the startup to initialize data */ + _sidata = .; + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : AT ( _sidata ) + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >CCM + + /* Uninitialized data section */ + . = ALIGN(4); + .bss : + { + /* This is used by the startup in order to initialize the .bss secion */ + _sbss = .; /* define a global symbol at bss start */ + /* __bss_start__ = _sbss; */ + *(.bss) + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + } >CCM + + /* Non-zeroed data section */ + . = ALIGN(4); + .noinit (NOLOAD) : + { + *(.noinit) + } >CCM + + /* collect all uninitialized .ccm sections */ + .ram (NOLOAD) : + { + . = ALIGN(4); + _sram = .; + *(.ram) + } >RAM + + /* User_heap_stack section, used to check that there is enough RAM left */ + ._user_heap_stack : + { + . = ALIGN(4); + . = . + _Min_Heap_Size; + . = . + _Main_Stack_Size; + . = ALIGN(4); + } >RAM + + /* MEMORY_bank1 section, code must be located here explicitly */ + /* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */ + .memory_b1_text : + { + *(.mb1text) /* .mb1text sections (code) */ + *(.mb1text*) /* .mb1text* sections (code) */ + *(.mb1rodata) /* read-only data (constants) */ + *(.mb1rodata*) + } >MEMORY_B1 + + .sdram (NOLOAD) : + { + *(.sdram) + *(.sdram*) + *(.sdram_rodata) + *(.sdram_rodata*) + . = ALIGN(4); + _eram = .; + } >SDRAM + + PROVIDE ( end = _eram ); + PROVIDE ( _end = _eram ); + + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/radio/src/targets/nv14/telemetry_driver.cpp b/radio/src/targets/nv14/telemetry_driver.cpp new file mode 100644 index 000000000..1629d8d69 --- /dev/null +++ b/radio/src/targets/nv14/telemetry_driver.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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" + +Fifo telemetryNoDMAFifo; +uint32_t telemetryErrors = 0; + +#if defined(PCBX12S) +DMAFifo telemetryDMAFifo __DMA (TELEMETRY_DMA_Stream_RX); +uint8_t telemetryFifoMode; +#endif + +void telemetryPortInit(uint32_t baudrate, uint8_t mode) +{ + if (baudrate == 0) { + USART_DeInit(TELEMETRY_USART); + return; + } + + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = TELEMETRY_DMA_TX_Stream_IRQ; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; /* Not used as 4 bits are used for the pre-emption priority. */; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + USART_InitTypeDef USART_InitStructure; + GPIO_InitTypeDef GPIO_InitStructure; + + GPIO_PinAFConfig(TELEMETRY_GPIO, TELEMETRY_GPIO_PinSource_RX, TELEMETRY_GPIO_AF); + GPIO_PinAFConfig(TELEMETRY_GPIO, TELEMETRY_GPIO_PinSource_TX, TELEMETRY_GPIO_AF); + + GPIO_InitStructure.GPIO_Pin = TELEMETRY_TX_GPIO_PIN | TELEMETRY_RX_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_Init(TELEMETRY_GPIO, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = TELEMETRY_DIR_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(TELEMETRY_DIR_GPIO, &GPIO_InitStructure); + TELEMETRY_DIR_INPUT(); + + GPIO_InitStructure.GPIO_Pin = TELEMETRY_TX_REV_GPIO_PIN | TELEMETRY_RX_REV_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(TELEMETRY_REV_GPIO, &GPIO_InitStructure); + TELEMETRY_TX_POL_INV(); + TELEMETRY_RX_POL_INV(); + + USART_InitStructure.USART_BaudRate = baudrate; + if (mode & TELEMETRY_SERIAL_8E2) { + USART_InitStructure.USART_WordLength = USART_WordLength_9b; + USART_InitStructure.USART_StopBits = USART_StopBits_2; + USART_InitStructure.USART_Parity = USART_Parity_Even; + } + else { + USART_InitStructure.USART_WordLength = USART_WordLength_8b; + USART_InitStructure.USART_StopBits = USART_StopBits_1; + USART_InitStructure.USART_Parity = USART_Parity_No; + } + USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; + USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; + USART_Init(TELEMETRY_USART, &USART_InitStructure); + + USART_Cmd(TELEMETRY_USART, ENABLE); + USART_ITConfig(TELEMETRY_USART, USART_IT_RXNE, ENABLE); + NVIC_SetPriority(TELEMETRY_USART_IRQn, 6); + NVIC_EnableIRQ(TELEMETRY_USART_IRQn); +} + +void telemetryPortSetDirectionOutput() +{ + TELEMETRY_DIR_OUTPUT(); + TELEMETRY_USART->CR1 &= ~USART_CR1_RE; // turn off receiver +} + +void telemetryPortSetDirectionInput() +{ + TELEMETRY_DIR_INPUT(); + TELEMETRY_USART->CR1 |= USART_CR1_RE; // turn on receiver +} + +void sportSendByte(uint8_t byte) +{ + while (!(TELEMETRY_USART->SR & USART_SR_TXE)); + USART_SendData(TELEMETRY_USART, byte); +} + +void sportSendBuffer(uint8_t * buffer, uint32_t count) +{ + telemetryPortSetDirectionOutput(); + + DMA_InitTypeDef DMA_InitStructure; + DMA_DeInit(TELEMETRY_DMA_Stream_TX); + DMA_InitStructure.DMA_Channel = TELEMETRY_DMA_Channel_TX; + DMA_InitStructure.DMA_PeripheralBaseAddr = CONVERT_PTR_UINT(&TELEMETRY_USART->DR); + DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; + DMA_InitStructure.DMA_Memory0BaseAddr = CONVERT_PTR_UINT(buffer); + DMA_InitStructure.DMA_BufferSize = count; + DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; + DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; + DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; + DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; + DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; + DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; + DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; + DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; + DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; + DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; + DMA_Init(TELEMETRY_DMA_Stream_TX, &DMA_InitStructure); + DMA_Cmd(TELEMETRY_DMA_Stream_TX, ENABLE); + USART_DMACmd(TELEMETRY_USART, USART_DMAReq_Tx, ENABLE); + DMA_ITConfig(TELEMETRY_DMA_Stream_TX, DMA_IT_TC, ENABLE); + + // enable interrupt and set it's priority + NVIC_EnableIRQ(TELEMETRY_DMA_TX_Stream_IRQ) ; + NVIC_SetPriority(TELEMETRY_DMA_TX_Stream_IRQ, 7); +} + +extern "C" void TELEMETRY_DMA_TX_IRQHandler(void) +{ + DEBUG_INTERRUPT(INT_TELEM_DMA); + if (DMA_GetITStatus(TELEMETRY_DMA_Stream_TX, TELEMETRY_DMA_TX_FLAG_TC)) { + DMA_ClearITPendingBit(TELEMETRY_DMA_Stream_TX, TELEMETRY_DMA_TX_FLAG_TC); + TELEMETRY_USART->CR1 |= USART_CR1_TCIE; + if (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT) { + outputTelemetryBuffer.reset(); + } + } +} + +#define USART_FLAG_ERRORS (USART_FLAG_ORE | USART_FLAG_NE | USART_FLAG_FE | USART_FLAG_PE) +extern "C" void TELEMETRY_USART_IRQHandler(void) +{ + DEBUG_INTERRUPT(INT_TELEM_USART); + uint32_t status = TELEMETRY_USART->SR; + + if ((status & USART_SR_TC) && (TELEMETRY_USART->CR1 & USART_CR1_TCIE)) { + TELEMETRY_USART->CR1 &= ~USART_CR1_TCIE; + telemetryPortSetDirectionInput(); + while (status & (USART_FLAG_RXNE)) { + status = TELEMETRY_USART->DR; + status = TELEMETRY_USART->SR; + } + } + + while (status & (USART_FLAG_RXNE | USART_FLAG_ERRORS)) { + uint8_t data = TELEMETRY_USART->DR; + if (status & USART_FLAG_ERRORS) { + telemetryErrors++; + } + else { + telemetryNoDMAFifo.push(data); +#if defined(LUA) + if (telemetryProtocol == PROTOCOL_TELEMETRY_FRSKY_SPORT) { + static uint8_t prevdata; + if (prevdata == 0x7E && outputTelemetryBuffer.size > 0 && outputTelemetryBuffer.destination == TELEMETRY_ENDPOINT_SPORT && data == outputTelemetryBuffer.sport.physicalId) { + sportSendBuffer(outputTelemetryBuffer.data + 1, outputTelemetryBuffer.size - 1); + } + prevdata = data; + } +#endif + } + status = TELEMETRY_USART->SR; + } +} + +// TODO we should have telemetry in an higher layer, functions above should move to a sport_driver.cpp +uint8_t telemetryGetByte(uint8_t * byte) +{ +#if defined(PCBX12S) + if (telemetryFifoMode & TELEMETRY_SERIAL_WITHOUT_DMA) + return telemetryNoDMAFifo.pop(*byte); + else + return telemetryDMAFifo.pop(*byte); +#else + return telemetryNoDMAFifo.pop(*byte); +#endif +} + +void telemetryClearFifo() +{ +#if defined(PCBX12S) + telemetryDMAFifo.clear(); +#endif + + telemetryNoDMAFifo.clear(); +} + diff --git a/radio/src/targets/nv14/touch_driver.cpp b/radio/src/targets/nv14/touch_driver.cpp new file mode 100644 index 000000000..7c3a05165 --- /dev/null +++ b/radio/src/targets/nv14/touch_driver.cpp @@ -0,0 +1,432 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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" + +enum ENUM_TOUCH_STATE +{ + TOUCH_NONE, + TOUCH_SLIDE, + TOUCH_CLICK +}; + +#define TOUCH_DELAY() TouchDelay() + +void GPIO_SetDirection( GPIO_TypeDef *GPIOx, unsigned char Pin, unsigned char IsInput ) +{ + unsigned int Mask; + unsigned int Position; + unsigned int Register; + + + Position = Pin << 1; + Mask = ~( 0x03UL << Position ); + + //EnterCritical(); + Register = GPIOx->OSPEEDR & Mask; + Register |= IO_SPEED_HIGH << Position; + GPIOx->OSPEEDR = Register; + //ExitCritical(); + + //EnterCritical(); + Register = GPIOx->MODER & Mask; + if( !IsInput ) + { + Register |= IO_MODE_OUTPUT << Position; + } + + GPIOx->MODER = Register; + //ExitCritical(); +} + +/*================================================================================================== +Name: +Function: +Input: +Output: +==================================================================================================*/ +void TouchInit( void ) +{ + SET_TOUCH_RESET_OUT(); + CLR_TOUCH_RESET(); + SET_TOUCH_SCL_OUT(); + SET_TOUCH_SDA_OUT(); + SET_TOUCH_SCL(); + SET_TOUCH_SDA(); + SET_TOUCH_INT_IN(); +} +/*================================================================================================== +Name: +Function: +Input: +Output: +==================================================================================================*/ +static void TouchConditionDelay( void ) +{ + volatile unsigned int i; + + for( i = 0; i < 40; i++ ) + { + ; + } +} +/*================================================================================================== +Name: +Function: +Input: +Output: +==================================================================================================*/ +static void TouchDelay( void ) +{ + volatile unsigned int i; + + for( i = 0; i < 3; i++ ) + { + ; + } +} +/*================================================================================================== +Name: +Function: +Input: +Output: +==================================================================================================*/ +static void TouchStart( void ) +{ + SET_TOUCH_SCL_OUT(); + SET_TOUCH_SDA_OUT(); + SET_TOUCH_SCL(); + SET_TOUCH_SDA(); + TouchConditionDelay(); + CLR_TOUCH_SDA(); + TouchConditionDelay(); + CLR_TOUCH_SCL(); + TOUCH_DELAY(); +} + +/*================================================================================================== +Name: +Function: +Input: +Output: +==================================================================================================*/ +static void TouchStop( void ) +{ + CLR_TOUCH_SCL(); + TOUCH_DELAY(); + CLR_TOUCH_SDA(); + SET_TOUCH_SCL(); + TouchConditionDelay(); + SET_TOUCH_SDA(); + TouchConditionDelay(); +} +/*================================================================================================== +Name: +Function: +Input: +Output: +==================================================================================================*/ +static void SendAckToTouch( unsigned char Ack ) +{ + if( Ack ) + { + SET_TOUCH_SDA(); + } + else + { + CLR_TOUCH_SDA(); + } + + SET_TOUCH_SCL(); + TOUCH_DELAY(); + CLR_TOUCH_SCL(); + TOUCH_DELAY(); +} +/*================================================================================================== +Name: +Function: +Input: +Output: +==================================================================================================*/ +static unsigned char GetTouchAck( void ) +{ + unsigned char i; + + SET_TOUCH_SDA(); + SET_TOUCH_SDA_IN(); + SET_TOUCH_SCL(); + TOUCH_DELAY(); + + if( READ_TOUCH_SDA() ) + { + i = 1; + } + else + { + i = 0; + } + + CLR_TOUCH_SCL(); + SET_TOUCH_SDA_OUT(); + return( i ); +} +/*================================================================================================== +Name: +Function: +Input: +Output: +==================================================================================================*/ +unsigned char ReceiveByteFromTouch( void ) +{ + unsigned char i; + unsigned char ucReceive = 0; + + SET_TOUCH_SDA_IN(); + + for( i = 0; i < 8; i++ ) + { + CLR_TOUCH_SCL(); + TOUCH_DELAY(); + ucReceive <<= 1; + SET_TOUCH_SCL(); + TOUCH_DELAY(); + + if( READ_TOUCH_SDA() ) + { + ucReceive |= 0x01; + } + } + + CLR_TOUCH_SCL(); + SET_TOUCH_SDA_OUT(); + TOUCH_DELAY(); + return( ucReceive ); +} +/*================================================================================================== +Name: +Function: +Input: +Output: +==================================================================================================*/ +static void SendByteToTouch( unsigned char ucSendData ) +{ + unsigned char i; + + for( i = 0; i < 8; i++ ) + { + CLR_TOUCH_SCL(); + TOUCH_DELAY(); + if( ucSendData & 0x80 ) + { + SET_TOUCH_SDA(); + } + else + { + CLR_TOUCH_SDA(); + } + + SET_TOUCH_SCL(); + ucSendData <<= 1; + TOUCH_DELAY(); + } + + CLR_TOUCH_SCL(); + TOUCH_DELAY(); +} +/*================================================================================================== +Name: +Function: +Input: +Output: +==================================================================================================*/ +unsigned char ReadTouchData( unsigned char *pData, unsigned char Length ) +{ + unsigned char ucTryCount; +#if 1 + ucTryCount = 0; + while( ucTryCount < 3 ) + { + do + { + TouchStart(); + SendByteToTouch( ( TOUCH_PANEL_ADDRESS << 1 )|0x00 ); + if( GetTouchAck() ) + { + break; + } + + SendByteToTouch( 0x00 ); + if( GetTouchAck() ) + { + break; + } + + TouchStart(); + SendByteToTouch( ( TOUCH_PANEL_ADDRESS << 1 )|0x01 ); + if( GetTouchAck() ) + { + break; + } + + while( Length ) + { + *pData = ReceiveByteFromTouch(); + Length--; + pData++; + + if( 0 != Length ) + { + SendAckToTouch( 0 ); + } + else + { + SendAckToTouch( 1 ); + } + } + + TouchStop(); + return( !0 ); + + }while( 0 ); + + ucTryCount++; + TouchStop(); + } +#endif + return( 0 ); +} + + +signed int x; +signed int y; +unsigned char TouchEvent; +unsigned char TouchBuffer[7]; +unsigned short Tx; +unsigned short Ty; +extern STRUCT_TOUCH touchState; //definition in mainwindow.cpp +static unsigned char TouchCycle = 0; +static unsigned char TouchState = TOUCH_NONE; +static unsigned short Txold; +static unsigned short Tyold; +static unsigned short TouchStartX; +static unsigned short TouchStartY; + +void TouchDriver( void ) +{ + SET_TOUCH_RESET(); + + TouchCycle++; + + if( TouchCycle >= 1 ) + { + TouchCycle = 0; + if( ReadTouchData( TouchBuffer, sizeof( TouchBuffer ) ) ) + { + TouchEvent = TouchBuffer[3] >> 6; + Tx = TouchBuffer[3] & 0x0f; + Tx <<= 8; + Tx |= TouchBuffer[4]; + Ty = TouchBuffer[5] & 0x0f; + Ty <<= 8; + Ty |= TouchBuffer[6]; + + #if defined( LCD_DIRECTION ) && ( LCD_DIRECTION == LCD_VERTICAL ) + Tx=LCD_WIDTH-Tx; + Ty=LCD_HEIGHT-Ty; + #else + x = ( LCD_WIDTH - 1 ) - Ty; + Ty = Tx; + Tx = x; + #endif + if( 2 == TouchEvent ) + { + touchState.X = Tx; + touchState.Y = Ty; + + if( TOUCH_NONE == TouchState ) + { + touchState.Event = TE_NONE; + TouchState = TOUCH_CLICK; + + TouchStartX = Tx; + TouchStartY = Ty; + + if (g_eeGeneral.backlightMode & e_backlight_mode_keys) + backlightOn(); // TODO is that the best place ? + } + else + { + + if ( TOUCH_CLICK == TouchState ) + { + x = Tx - Txold; + y = Ty - Tyold; + + if( ( x >= SLIDE_RANGE ) || ( x <= -SLIDE_RANGE ) || ( y >= SLIDE_RANGE ) || ( y <= -SLIDE_RANGE ) ) + { + TouchState = TOUCH_SLIDE; + touchState.Event = TE_SLIDE; + } + else + { + touchState.Event = TE_DOWN; + touchState.startX = touchState.lastX = touchState.X; + touchState.startY = touchState.lastY = touchState.Y; + } + } + else if ( TOUCH_SLIDE == TouchState ) + { + touchState.Event = TE_SLIDE; + } + else + { + TouchState = TOUCH_NONE; + } + } + Txold = Tx; + Tyold = Ty; + } + else + { + if ( TOUCH_CLICK == TouchState ) + { + touchState.X = Txold; + touchState.Y = Tyold; + touchState.Event = TE_UP; + touchState.Time = get_tmr10ms(); + } + else if (get_tmr10ms() > touchState.Time + 50) + { + touchState.X = LCD_WIDTH; + touchState.Y = LCD_HEIGHT; + touchState.Event = TE_NONE; + } + + TouchState = TOUCH_NONE; + } + } + else + { + touchState.X = LCD_WIDTH; + touchState.Y = LCD_HEIGHT; + touchState.Event = TE_NONE; + TouchState = TOUCH_NONE; + } + } +} diff --git a/radio/src/targets/nv14/touch_driver.h b/radio/src/targets/nv14/touch_driver.h new file mode 100644 index 000000000..c926c88ef --- /dev/null +++ b/radio/src/targets/nv14/touch_driver.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ +#ifndef __TOUCH_DRIVER_H__ +#define __TOUCH_DRIVER_H__ + +#include "board.h" +#include "touch.h" + +#define PORT_TOUCH_SCL ( GPIOB ) +#define PIN_TOUCH_SCL ( GPIO_Pin_8 ) +#define PORT_TOUCH_SDA ( GPIOB ) +#define PIN_TOUCH_SDA ( GPIO_Pin_7 ) +#define PORT_TOUCH_RESET ( GPIOB ) +#define PIN_TOUCH_RESET ( GPIO_Pin_12 ) + +#define PORT_TOUCH_INT ( GPIOB ) +#define PIN_TOUCH_INT ( GPIO_Pin_9 ) + +#define SET_TOUCH_INT_IN() GPIO_SetDirection( PORT_TOUCH_INT, 9, 1 ) + +#define SET_TOUCH_RESET_OUT() GPIO_SetDirection( PORT_TOUCH_RESET, 12, 0 ) +#define SET_TOUCH_RESET() GPIO_WriteBit( PORT_TOUCH_RESET,PIN_TOUCH_RESET, Bit_SET ) +#define CLR_TOUCH_RESET() GPIO_WriteBit( PORT_TOUCH_RESET,PIN_TOUCH_RESET, Bit_RESET ) + +#define SET_TOUCH_SCL_OUT() GPIO_SetDirection( PORT_TOUCH_SCL, 8, 0 ) +#define SET_TOUCH_SCL() GPIO_WriteBit( PORT_TOUCH_SCL,PIN_TOUCH_SCL, Bit_SET ) +#define CLR_TOUCH_SCL() GPIO_WriteBit( PORT_TOUCH_SCL,PIN_TOUCH_SCL, Bit_RESET ) + +#define SET_TOUCH_SDA_IN() GPIO_SetDirection( PORT_TOUCH_SDA, 7, 1 ) +#define SET_TOUCH_SDA_OUT() GPIO_SetDirection( PORT_TOUCH_SDA, 7, 0 ) + +#define SET_TOUCH_SDA() GPIO_WriteBit(PORT_TOUCH_SDA, PIN_TOUCH_SDA, Bit_SET) +#define CLR_TOUCH_SDA() GPIO_WriteBit(PORT_TOUCH_SDA, PIN_TOUCH_SDA, Bit_RESET) + +#define READ_TOUCH_SDA() GPIO_ReadInputDataBit( PORT_TOUCH_SDA, PIN_TOUCH_SDA ) + +#define TOUCH_PANEL_ADDRESS ( 0x38 ) + +#define LCD_VERTICAL ( 0x00 ) +#define LCD_HORIZONTAL ( 0x01 ) +#define LCD_DIRECTION ( LCD_VERTICAL ) + +#define LCD_WIDTH ( 320 ) +#define LCD_HEIGHT ( 480 ) + +#define TOUCH_INVALID ( 250 ) + +enum ENUM_IO_SPEED +{ + IO_SPEED_LOW, + IO_SPEED_MID, + IO_SPEED_QUICK, + IO_SPEED_HIGH +}; + +enum ENUM_IO_MODE +{ + IO_MODE_INPUT, + IO_MODE_OUTPUT, + IO_MODE_ALTERNATE, + IO_MODE_ANALOG +}; + +#define GET_ARRAY_SIZE( ARRAY ) ( ( sizeof( ARRAY ) ) / ( sizeof( ARRAY[0] ) ) ) + +extern void TouchInit( void ); +extern void TouchDriver( void ); + +#endif /*__TOUCH_DRIVER_H__*/ diff --git a/radio/src/targets/nv14/trainer_driver.cpp b/radio/src/targets/nv14/trainer_driver.cpp new file mode 100644 index 000000000..7cb9b6757 --- /dev/null +++ b/radio/src/targets/nv14/trainer_driver.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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" + +void trainerSendNextFrame(); + +void init_trainer_ppm() +{ +#if 0 + GPIO_PinAFConfig(TRAINER_GPIO, TRAINER_OUT_GPIO_PinSource, TRAINER_GPIO_AF); + + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = TRAINER_OUT_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(TRAINER_GPIO, &GPIO_InitStructure); + + TRAINER_TIMER->CR1 &= ~TIM_CR1_CEN; + TRAINER_TIMER->PSC = TRAINER_TIMER_FREQ / 2000000 - 1; // 0.5uS + TRAINER_TIMER->ARR = 45000; + TRAINER_TIMER->CCR2 = GET_PPM_DELAY(TRAINER_MODULE)*2; + TRAINER_TIMER->CCER = TIM_CCER_CC2E | (g_model.moduleData[TRAINER_MODULE].ppm.pulsePol ? 0 : TIM_CCER_CC2P); + TRAINER_TIMER->CCMR1 = TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_0; // Force O/P high + TRAINER_TIMER->BDTR = TIM_BDTR_MOE; + TRAINER_TIMER->EGR = 1; + TRAINER_TIMER->DIER |= TIM_DIER_UDE; + TRAINER_TIMER->CCMR1 = TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2PE; // PWM mode 1 + TRAINER_TIMER->CR1 |= TIM_CR1_CEN; + + setupPulsesPPMTrainer(); + trainerSendNextFrame(); + + NVIC_EnableIRQ(TRAINER_OUT_DMA_IRQn); + NVIC_SetPriority(TRAINER_OUT_DMA_IRQn, 7); + NVIC_EnableIRQ(TRAINER_TIMER_IRQn); + NVIC_SetPriority(TRAINER_TIMER_IRQn, 7); +#endif +} + +void stop_trainer_ppm() +{ +#if 0 + NVIC_DisableIRQ(TRAINER_OUT_DMA_IRQn); + NVIC_DisableIRQ(TRAINER_TIMER_IRQn); + + TRAINER_OUT_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA + TRAINER_TIMER->DIER = 0; // Stop Interrupt + TRAINER_TIMER->CR1 &= ~TIM_CR1_CEN; // Stop counter +#endif +} + +void init_trainer_capture() +{ +#if 0 + GPIO_InitTypeDef GPIO_InitStructure; + + GPIO_InitStructure.GPIO_Pin = TRAINER_IN_GPIO_PIN; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(TRAINER_GPIO, &GPIO_InitStructure); + GPIO_PinAFConfig(TRAINER_GPIO, TRAINER_IN_GPIO_PinSource, TRAINER_GPIO_AF); + + TRAINER_TIMER->ARR = 0xFFFF; + TRAINER_TIMER->PSC = TRAINER_TIMER_FREQ / 2000000 - 1; // 0.5uS + TRAINER_TIMER->CR2 = 0; + TRAINER_TIMER->CCMR1 = TIM_CCMR1_IC1F_0 | TIM_CCMR1_IC1F_1 | TIM_CCMR1_CC1S_0; + TRAINER_TIMER->CCER = TIM_CCER_CC1E; + TRAINER_TIMER->SR &= ~TIM_SR_CC1IF & ~TIM_SR_CC2IF & ~TIM_SR_UIF; // Clear flags + TRAINER_TIMER->DIER |= TIM_DIER_CC1IE; + TRAINER_TIMER->CR1 = TIM_CR1_CEN; + + NVIC_EnableIRQ(TRAINER_TIMER_IRQn); + NVIC_SetPriority(TRAINER_TIMER_IRQn, 7); +#endif +} + +void stop_trainer_capture() +{ +#if 0 + NVIC_DisableIRQ(TRAINER_TIMER_IRQn); // Stop Interrupt + + TRAINER_TIMER->CR1 &= ~TIM_CR1_CEN; // Stop counter + TRAINER_TIMER->DIER = 0; // Stop Interrupt +#endif +} + +void trainerSendNextFrame() +{ +#if 0 + TRAINER_TIMER->CCR2 = GET_PPM_DELAY(TRAINER_MODULE)*2; + TRAINER_TIMER->CCER = TIM_CCER_CC2E | (g_model.moduleData[TRAINER_MODULE].ppm.pulsePol ? 0 : TIM_CCER_CC2P); + TRAINER_TIMER->CCR1 = *(trainerPulsesData.ppm.ptr - 1) - 4000; // 2mS in advance + + TRAINER_OUT_DMA_STREAM->CR &= ~DMA_SxCR_EN; // Disable DMA + TRAINER_OUT_DMA_STREAM->CR |= TRAINER_OUT_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; + TRAINER_OUT_DMA_STREAM->PAR = CONVERT_PTR_UINT(&TRAINER_TIMER->ARR); + TRAINER_OUT_DMA_STREAM->M0AR = CONVERT_PTR_UINT(trainerPulsesData.ppm.pulses); + TRAINER_OUT_DMA_STREAM->NDTR = trainerPulsesData.ppm.ptr - trainerPulsesData.ppm.pulses; + TRAINER_OUT_DMA_STREAM->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // Enable DMA +#endif +} + +#if 0 +extern "C" void TRAINER_OUT_DMA_IRQHandler() +{ + if (!DMA_GetITStatus(TRAINER_OUT_DMA_STREAM, TRAINER_OUT_DMA_FLAG_TC)) + return; + + DMA_ClearITPendingBit(TRAINER_OUT_DMA_STREAM, TRAINER_OUT_DMA_FLAG_TC); + + TRAINER_TIMER->SR &= ~TIM_SR_CC1IF; // Clear flag + TRAINER_TIMER->DIER |= TIM_DIER_CC1IE; // Enable this interrupt +} + +extern "C" void TRAINER_TIMER_IRQHandler() +{ + DEBUG_INTERRUPT(INT_TRAINER); + + uint16_t capture = 0; + bool doCapture = false; + + // What mode? in or out? + if ((TRAINER_TIMER->DIER & TIM_DIER_CC1IE) && (TRAINER_TIMER->SR & TIM_SR_CC1IF)) { + // capture mode on trainer jack + capture = TRAINER_TIMER->CCR1; + if (TRAINER_CONNECTED() && currentTrainerMode == TRAINER_MODE_MASTER_TRAINER_JACK) { + doCapture = true; + } + } + + if (doCapture) { + captureTrainerPulses(capture); + } + + // PPM out compare interrupt + if ((TRAINER_TIMER->DIER & TIM_DIER_CC1IE) && (TRAINER_TIMER->SR & TIM_SR_CC1IF)) { + // compare interrupt + TRAINER_TIMER->DIER &= ~TIM_DIER_CC1IE; // stop this interrupt + TRAINER_TIMER->SR &= ~TIM_SR_CC1IF; // Clear flag + setupPulsesPPMTrainer(); + trainerSendNextFrame(); + } +} +#endif diff --git a/radio/src/targets/simu/opentxsimulator.cpp b/radio/src/targets/simu/opentxsimulator.cpp index 1864e0d7b..8c7b08528 100644 --- a/radio/src/targets/simu/opentxsimulator.cpp +++ b/radio/src/targets/simu/opentxsimulator.cpp @@ -284,13 +284,13 @@ void OpenTxSimulator::rotaryEncoderEvent(int steps) key = KEY_DOWN; else if (steps < 0) key = KEY_UP; -#else +#elif defined(HARDWARE_KEYS) if (steps > 0) key = KEY_MINUS; else if (steps < 0) key = KEY_PLUS; -#endif else +#endif // Should not happen but Clang complains that key is unset otherwise return; diff --git a/radio/src/targets/simu/simpgmspace.cpp b/radio/src/targets/simu/simpgmspace.cpp index fb3d11a66..86b593361 100644 --- a/radio/src/targets/simu/simpgmspace.cpp +++ b/radio/src/targets/simu/simpgmspace.cpp @@ -69,6 +69,16 @@ void lcdInit() { } +void lcdCopy(void * dest, void * src) +{ + +} + +void lcdNextLayer() +{ + +} + void toplcdOff() { } @@ -703,7 +713,7 @@ void boardOff() { } -#if defined(PCBHORUS) || defined(PCBTARANIS) +#if defined(PCBFRSKY) || defined(PCBFLYSKY) HardwareOptions hardwareOptions; #endif @@ -763,7 +773,11 @@ void rtcSetTime(const struct gtm * t) } #if defined(AUX_SERIAL) +#if defined(AUX_SERIAL_DMA_Stream_RX) AuxSerialRxFifo auxSerialRxFifo(nullptr); +#else +AuxSerialRxFifo auxSerialRxFifo; +#endif uint8_t auxSerialMode; void auxSerialInit(unsigned int mode, unsigned int protocol) { @@ -781,3 +795,19 @@ void auxSerialStop() { } #endif + +#if defined(INTMODULE_HEARTBEAT_GPIO) +volatile HeartbeatCapture heartbeatCapture; + +void init_intmodule_heartbeat() +{ +} + +void stop_intmodule_heartbeat() +{ +} + +void check_intmodule_heartbeat() +{ +} +#endif diff --git a/radio/src/targets/simu/simpgmspace.h b/radio/src/targets/simu/simpgmspace.h index 07b0f95f5..fa1318511 100644 --- a/radio/src/targets/simu/simpgmspace.h +++ b/radio/src/targets/simu/simpgmspace.h @@ -288,7 +288,7 @@ inline void DMA_Init(DMA_Stream_TypeDef* DMAy_Streamx, DMA_InitTypeDef* DMA_Init inline void DMA_ITConfig(DMA_Stream_TypeDef* DMAy_Streamx, uint32_t DMA_IT, FunctionalState NewState) { } inline void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct) { } inline void DMA_Cmd(DMA_Stream_TypeDef* DMAy_Streamx, FunctionalState NewState) { } -void DMACopy(void * src, void * dest, unsigned size); +void lcdCopy(void * dest, void * src); inline FlagStatus DMA_GetFlagStatus(DMA_Stream_TypeDef* DMAy_Streamx, uint32_t DMA_FLAG) { return RESET; } inline ITStatus DMA_GetITStatus(DMA_Stream_TypeDef* DMAy_Streamx, uint32_t DMA_IT) { return RESET; } inline void DMA_ClearITPendingBit(DMA_Stream_TypeDef* DMAy_Streamx, uint32_t DMA_IT) { } diff --git a/radio/src/targets/taranis/CMakeLists.txt b/radio/src/targets/taranis/CMakeLists.txt index b4bd44e53..ce5e207a8 100644 --- a/radio/src/targets/taranis/CMakeLists.txt +++ b/radio/src/targets/taranis/CMakeLists.txt @@ -6,6 +6,8 @@ option(PXX1 "PXX1 protocol support" ON) option(PXX2 "PXX2 protocol support" OFF) option(AUTOUPDATE "Auto update internal chips from SD" OFF) +add_definitions(-DPCBFRSKY) + if(PCB STREQUAL X9E) set(PWR_BUTTON "PRESS" CACHE STRING "Pwr button type (PRESS/SWITCH)") set(STICKS "STANDARD" CACHE STRING "Sticks type (STANDARD/HORUS)") @@ -278,7 +280,6 @@ set(TARGET_SRC ../common/arm/stm32/timers_driver.cpp ../common/arm/stm32/audio_dac_driver.cpp ../common/arm/stm32/adc_driver.cpp - ../common/arm/stm32/heartbeat_driver.cpp ) if(PCB STREQUAL XLITE OR PCB STREQUAL XLITES) @@ -294,6 +295,7 @@ set(FIRMWARE_TARGET_SRC board.cpp i2c_driver.cpp pwr_driver.cpp + ../common/arm/stm32/heartbeat_driver.cpp ../common/arm/loadboot.cpp ) diff --git a/radio/src/telemetry/crossfire.h b/radio/src/telemetry/crossfire.h index 5f40b6f93..c3d49e125 100644 --- a/radio/src/telemetry/crossfire.h +++ b/radio/src/telemetry/crossfire.h @@ -86,12 +86,12 @@ const uint32_t CROSSFIRE_BAUDRATES[] = { 400000, 115200, }; -const uint8_t CROSSFIRE_PERIODS[] = { +const uint8_t CROSSFIRE_FRAME_PERIODS[] = { 4, 16, }; -#define CROSSFIRE_BAUDRATE CROSSFIRE_BAUDRATES[g_eeGeneral.telemetryBaudrate] -#define CROSSFIRE_PERIOD CROSSFIRE_PERIODS[g_eeGeneral.telemetryBaudrate] +#define CROSSFIRE_BAUDRATE CROSSFIRE_BAUDRATES[g_eeGeneral.telemetryBaudrate] +#define CROSSFIRE_PERIOD CROSSFIRE_FRAME_PERIODS[g_eeGeneral.telemetryBaudrate] #else #define CROSSFIRE_BAUDRATE 400000 #define CROSSFIRE_PERIOD 4 // 4ms diff --git a/radio/src/telemetry/frsky_pxx2.cpp b/radio/src/telemetry/frsky_pxx2.cpp index e9db1fd88..6cb3d1cf7 100644 --- a/radio/src/telemetry/frsky_pxx2.cpp +++ b/radio/src/telemetry/frsky_pxx2.cpp @@ -140,7 +140,9 @@ void processRegisterFrame(uint8_t module, uint8_t * frame) cmpStrWithZchar((char *)&frame[12], g_model.modelRegistrationID, PXX2_LEN_REGISTRATION_ID)) { reusableBuffer.moduleSetup.pxx2.registerStep = REGISTER_OK; moduleState[module].mode = MODULE_MODE_NORMAL; +#if !defined(COLORLCD) POPUP_INFORMATION(STR_REG_OK); +#endif } } break; diff --git a/radio/src/telemetry/multi.cpp b/radio/src/telemetry/multi.cpp index 87944a862..3717a6a24 100644 --- a/radio/src/telemetry/multi.cpp +++ b/radio/src/telemetry/multi.cpp @@ -293,7 +293,7 @@ void MultiModuleSyncStatus::getRefreshString(char *statusText) void MultiModuleStatus::getStatusString(char *statusText) { if (!isValid()) { -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) if (IS_INTERNAL_MODULE_ENABLED()) strcpy(statusText, STR_DISABLE_INTERNAL); else diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index ac3a38a5b..a1411ae65 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -170,7 +170,7 @@ void telemetryWakeup() audioEvent(AU_SENSOR_LOST); } -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) if (isBadAntennaDetected()) { AUDIO_RAS_RED(); POPUP_WARNING(STR_WARNING); diff --git a/radio/src/tests/functions.cpp b/radio/src/tests/functions.cpp index 087af20e1..4995cf543 100644 --- a/radio/src/tests/functions.cpp +++ b/radio/src/tests/functions.cpp @@ -31,7 +31,7 @@ TEST_F(SpecialFunctionsTest, SwitchFiledSize) EXPECT_EQ(g_model.customFn[0].swtch, -SWSRC_LAST) << "CustomFunctionData.swtch member is too small to hold all possible values"; } -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) TEST_F(SpecialFunctionsTest, FlightReset) { g_model.customFn[0].swtch = SWSRC_SA0; @@ -114,5 +114,5 @@ TEST_F(SpecialFunctionsTest, GvarsInc) } #endif // #if defined(GVARS) -#endif // #if defined(PCBTARANIS) || defined(PCBHORUS) +#endif // #if defined(PCBFRSKY) diff --git a/radio/src/tests/gtests.h b/radio/src/tests/gtests.h index ab5c35abd..9f4f77ace 100644 --- a/radio/src/tests/gtests.h +++ b/radio/src/tests/gtests.h @@ -40,7 +40,7 @@ extern const char * nchar2string(const char * string, int size); #define EXPECT_ZSTREQ(c_string, z_string) EXPECT_STREQ(c_string, zchar2string(z_string, sizeof(z_string))) #define EXPECT_STRNEQ(c_string, n_string) EXPECT_STREQ(c_string, nchar2string(n_string, sizeof(n_string))) -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) #define RADIO_RESET() \ g_eeGeneral.switchConfig = 0x00007bff #else diff --git a/radio/src/tests/lcd_480x272.cpp b/radio/src/tests/lcd_480x272.cpp index 769fccc5f..04380abfc 100644 --- a/radio/src/tests/lcd_480x272.cpp +++ b/radio/src/tests/lcd_480x272.cpp @@ -32,7 +32,7 @@ #if defined(COLORLCD) -void doPaint_480x272(QPainter & p) +void doPaint_colorlcd(QPainter & p) { QRgb rgb = qRgb(0, 0, 0); p.setBackground(QBrush(rgb)); @@ -60,12 +60,12 @@ void doPaint_480x272(QPainter & p) } } -bool checkScreenshot_480x272(const QString & test) +bool checkScreenshot_colorlcd(const QString & test) { lcdRefresh(); QImage buffer(LCD_W, LCD_H, QImage::Format_RGB32); QPainter p(&buffer); - doPaint_480x272(p); + doPaint_colorlcd(p); QString filename(QString("%1_%2x%3.png").arg(test).arg(LCD_W).arg(LCD_H)); QImage reference(TESTS_PATH "/" + filename); @@ -80,22 +80,22 @@ bool checkScreenshot_480x272(const QString & test) } -TEST(Lcd_480x272, vline) +TEST(Lcd_colorlcd, vline) { loadFonts(); lcd->clear(TEXT_BGCOLOR); for (int x=0; x<100; x+=2) { lcdDrawSolidVerticalLine(x, x/2, 12, TEXT_COLOR); } - EXPECT_TRUE(checkScreenshot_480x272("vline")); + EXPECT_TRUE(checkScreenshot_colorlcd("vline")); } -TEST(Lcd_480x272, primitives) +TEST(Lcd_colorlcd, primitives) { loadFonts(); lcd->clear(TEXT_BGCOLOR); - lcdDrawText(8, 8, "The quick brown fox jumps over the lazy dog", CURVE_AXIS_COLOR|NO_FONTCACHE); - lcdDrawText(5, 5, "The quick brown fox jumps over the lazy dog", TEXT_COLOR|NO_FONTCACHE); + lcdDrawText(8, 8, "The quick brown fox jumps over the lazy dog", CURVE_AXIS_COLOR); + lcdDrawText(5, 5, "The quick brown fox jumps over the lazy dog", TEXT_COLOR); lcdDrawFilledRect(10, 30, 30, 30, SOLID, TITLE_BGCOLOR); lcdDrawFilledRect(50, 30, 30, 30, DOTTED, TEXT_COLOR); @@ -115,15 +115,15 @@ TEST(Lcd_480x272, primitives) lcdDrawHorizontalLine(30, 85, 70, SOLID, TEXT_COLOR); - EXPECT_TRUE(checkScreenshot_480x272("primitives")); + EXPECT_TRUE(checkScreenshot_colorlcd("primitives")); } -TEST(Lcd_480x272, transparency) +TEST(Lcd_colorlcd, transparency) { loadFonts(); lcd->clear(TEXT_BGCOLOR); - lcdDrawText(8, 8, "The quick brown fox jumps over the lazy dog", TEXT_COLOR|OPACITY(4)|NO_FONTCACHE); - lcdDrawText(5, 5, "The quick brown fox jumps over the lazy dog", TEXT_COLOR|OPACITY(12)|NO_FONTCACHE); + lcdDrawText(8, 8, "The quick brown fox jumps over the lazy dog", TEXT_COLOR|OPACITY(4)); + lcdDrawText(5, 5, "The quick brown fox jumps over the lazy dog", TEXT_COLOR|OPACITY(12)); lcdDrawFilledRect(10, 30, 30, 30, SOLID, TITLE_BGCOLOR|OPACITY(8)); lcdDrawFilledRect(50, 30, 30, 30, DOTTED, TEXT_COLOR|OPACITY(10)); @@ -152,26 +152,25 @@ TEST(Lcd_480x272, transparency) } - EXPECT_TRUE(checkScreenshot_480x272("transparency")); + EXPECT_TRUE(checkScreenshot_colorlcd("transparency")); } -TEST(Lcd_480x272, fonts) +TEST(Lcd_colorlcd, fonts) { loadFonts(); - loadFontCache(); lcd->clear(TEXT_BGCOLOR); lcdDrawText(8, 8, "The quick brown fox jumps over the lazy dog", TEXT_COLOR|OPACITY(4)); lcdDrawText(5, 5, "The quick brown fox jumps over the lazy dog", TEXT_COLOR|OPACITY(12)); - lcdDrawText(10, 200, "The quick", TEXT_COLOR|VERTICAL|NO_FONTCACHE); - lcdDrawText(30, 200, "The quick brown fox", TEXT_COLOR|VERTICAL|NO_FONTCACHE); - // lcdDrawText(50, 200, "The quick brown fox jumps over the lazy dog", TEXT_COLOR|VERTICAL|NO_FONTCACHE); + lcdDrawText(10, 200, "The quick", TEXT_COLOR|VERTICAL); + lcdDrawText(30, 200, "The quick brown fox", TEXT_COLOR|VERTICAL); + // lcdDrawText(50, 200, "The quick brown fox jumps over the lazy dog", TEXT_COLOR|VERTICAL); lcdDrawText(8, 208, "The quick brown fox jumps over the lazy dog", TITLE_BGCOLOR|OPACITY(4)); lcdDrawText(5, 205, "The quick brown fox jumps over the lazy dog", TITLE_BGCOLOR|OPACITY(12)); - EXPECT_TRUE(checkScreenshot_480x272("fonts")); + EXPECT_TRUE(checkScreenshot_colorlcd("fonts")); } diff --git a/radio/src/tests/mixer.cpp b/radio/src/tests/mixer.cpp index 4c84a8944..2fa661ab1 100644 --- a/radio/src/tests/mixer.cpp +++ b/radio/src/tests/mixer.cpp @@ -500,7 +500,7 @@ TEST_F(MixerTest, DelayOnSwitch) g_model.mixData[0].mltpx = MLTPX_ADD; g_model.mixData[0].srcRaw = MIXSRC_MAX; g_model.mixData[0].weight = 100; -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) g_model.mixData[0].swtch = SWSRC_SA2; int switch_index = 0; #else diff --git a/radio/src/tests/switches.cpp b/radio/src/tests/switches.cpp index 4eae3ba5c..152166eca 100644 --- a/radio/src/tests/switches.cpp +++ b/radio/src/tests/switches.cpp @@ -94,7 +94,7 @@ TEST(getSwitch, inputWithTrim) } #endif -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) TEST(evalLogicalSwitches, playFile) { SYSTEM_RESET(); diff --git a/radio/src/thirdparty/STM32F4xx_DSP_StdPeriph_Lib_V1.4.0/Libraries/CMSIS/Include/arm_common_tables.h b/radio/src/thirdparty/STM32F4xx_DSP_StdPeriph_Lib_V1.4.0/Libraries/CMSIS/Include/arm_common_tables.h index 7a59b5923..036a40e0e 100644 --- a/radio/src/thirdparty/STM32F4xx_DSP_StdPeriph_Lib_V1.4.0/Libraries/CMSIS/Include/arm_common_tables.h +++ b/radio/src/thirdparty/STM32F4xx_DSP_StdPeriph_Lib_V1.4.0/Libraries/CMSIS/Include/arm_common_tables.h @@ -1,13 +1,13 @@ /* ---------------------------------------------------------------------- -* Copyright (C) 2010-2013 ARM Limited. All rights reserved. +* Copyright (C) 2010-2014 ARM Limited. All rights reserved. * -* $Date: 17. January 2013 -* $Revision: V1.4.1 +* $Date: 19. March 2015 +* $Revision: V.1.4.5 * -* Project: CMSIS DSP Library -* Title: arm_common_tables.h +* Project: CMSIS DSP Library +* Title: arm_common_tables.h * -* Description: This file has extern declaration for common tables like Bitreverse, reciprocal etc which are used across different functions +* Description: This file has extern declaration for common tables like Bitreverse, reciprocal etc which are used across different functions * * Target Processor: Cortex-M4/Cortex-M3 * @@ -43,11 +43,15 @@ #include "arm_math.h" +#ifndef FAST_MATH_TABLE_SIZE +#define FAST_MATH_TABLE_SIZE 512 +#endif + extern const uint16_t armBitRevTable[1024]; extern const q15_t armRecipTableQ15[64]; extern const q31_t armRecipTableQ31[64]; -extern const q31_t realCoefAQ31[1024]; -extern const q31_t realCoefBQ31[1024]; +//extern const q31_t realCoefAQ31[1024]; +//extern const q31_t realCoefBQ31[1024]; extern const float32_t twiddleCoef_16[32]; extern const float32_t twiddleCoef_32[64]; extern const float32_t twiddleCoef_64[128]; @@ -58,8 +62,24 @@ extern const float32_t twiddleCoef_1024[2048]; extern const float32_t twiddleCoef_2048[4096]; extern const float32_t twiddleCoef_4096[8192]; #define twiddleCoef twiddleCoef_4096 -extern const q31_t twiddleCoefQ31[6144]; -extern const q15_t twiddleCoefQ15[6144]; +extern const q31_t twiddleCoef_16_q31[24]; +extern const q31_t twiddleCoef_32_q31[48]; +extern const q31_t twiddleCoef_64_q31[96]; +extern const q31_t twiddleCoef_128_q31[192]; +extern const q31_t twiddleCoef_256_q31[384]; +extern const q31_t twiddleCoef_512_q31[768]; +extern const q31_t twiddleCoef_1024_q31[1536]; +extern const q31_t twiddleCoef_2048_q31[3072]; +extern const q31_t twiddleCoef_4096_q31[6144]; +extern const q15_t twiddleCoef_16_q15[24]; +extern const q15_t twiddleCoef_32_q15[48]; +extern const q15_t twiddleCoef_64_q15[96]; +extern const q15_t twiddleCoef_128_q15[192]; +extern const q15_t twiddleCoef_256_q15[384]; +extern const q15_t twiddleCoef_512_q15[768]; +extern const q15_t twiddleCoef_1024_q15[1536]; +extern const q15_t twiddleCoef_2048_q15[3072]; +extern const q15_t twiddleCoef_4096_q15[6144]; extern const float32_t twiddleCoef_rfft_32[32]; extern const float32_t twiddleCoef_rfft_64[64]; extern const float32_t twiddleCoef_rfft_128[128]; @@ -70,6 +90,7 @@ extern const float32_t twiddleCoef_rfft_2048[2048]; extern const float32_t twiddleCoef_rfft_4096[4096]; +/* floating-point bit reversal tables */ #define ARMBITREVINDEXTABLE__16_TABLE_LENGTH ((uint16_t)20 ) #define ARMBITREVINDEXTABLE__32_TABLE_LENGTH ((uint16_t)48 ) #define ARMBITREVINDEXTABLE__64_TABLE_LENGTH ((uint16_t)56 ) @@ -90,4 +111,30 @@ extern const uint16_t armBitRevIndexTable1024[ARMBITREVINDEXTABLE1024_TABLE_LENG extern const uint16_t armBitRevIndexTable2048[ARMBITREVINDEXTABLE2048_TABLE_LENGTH]; extern const uint16_t armBitRevIndexTable4096[ARMBITREVINDEXTABLE4096_TABLE_LENGTH]; +/* fixed-point bit reversal tables */ +#define ARMBITREVINDEXTABLE_FIXED___16_TABLE_LENGTH ((uint16_t)12 ) +#define ARMBITREVINDEXTABLE_FIXED___32_TABLE_LENGTH ((uint16_t)24 ) +#define ARMBITREVINDEXTABLE_FIXED___64_TABLE_LENGTH ((uint16_t)56 ) +#define ARMBITREVINDEXTABLE_FIXED__128_TABLE_LENGTH ((uint16_t)112 ) +#define ARMBITREVINDEXTABLE_FIXED__256_TABLE_LENGTH ((uint16_t)240 ) +#define ARMBITREVINDEXTABLE_FIXED__512_TABLE_LENGTH ((uint16_t)480 ) +#define ARMBITREVINDEXTABLE_FIXED_1024_TABLE_LENGTH ((uint16_t)992 ) +#define ARMBITREVINDEXTABLE_FIXED_2048_TABLE_LENGTH ((uint16_t)1984) +#define ARMBITREVINDEXTABLE_FIXED_4096_TABLE_LENGTH ((uint16_t)4032) + +extern const uint16_t armBitRevIndexTable_fixed_16[ARMBITREVINDEXTABLE_FIXED___16_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_32[ARMBITREVINDEXTABLE_FIXED___32_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_64[ARMBITREVINDEXTABLE_FIXED___64_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_128[ARMBITREVINDEXTABLE_FIXED__128_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_256[ARMBITREVINDEXTABLE_FIXED__256_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_512[ARMBITREVINDEXTABLE_FIXED__512_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_1024[ARMBITREVINDEXTABLE_FIXED_1024_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_2048[ARMBITREVINDEXTABLE_FIXED_2048_TABLE_LENGTH]; +extern const uint16_t armBitRevIndexTable_fixed_4096[ARMBITREVINDEXTABLE_FIXED_4096_TABLE_LENGTH]; + +/* Tables for Fast Math Sine and Cosine */ +extern const float32_t sinTable_f32[FAST_MATH_TABLE_SIZE + 1]; +extern const q31_t sinTable_q31[FAST_MATH_TABLE_SIZE + 1]; +extern const q15_t sinTable_q15[FAST_MATH_TABLE_SIZE + 1]; + #endif /* ARM_COMMON_TABLES_H */ diff --git a/radio/src/timers.cpp b/radio/src/timers.cpp index 3dc88fb1e..0b209d2fb 100644 --- a/radio/src/timers.cpp +++ b/radio/src/timers.cpp @@ -69,15 +69,18 @@ void saveTimers() #define THR_TRG_TRESHOLD 13 // approximately 10% full throttle +#warning "Timer mode is switched into (mode + swtch), needs to be taken into account in UI" + void evalTimers(int16_t throttle, uint8_t tick10ms) { for (uint8_t i=0; istate == TMR_OFF) && (timerMode != TMRMODE_THR_TRG)) { + if (timerState->state == TMR_OFF) { timerState->state = TMR_RUNNING; timerState->cnt = 0; timerState->sum = 0; @@ -96,36 +99,19 @@ void evalTimers(int16_t throttle, uint8_t tick10ms) tmrval_t newTimerVal = timerState->val; if (timerStart) newTimerVal = timerStart - newTimerVal; - if (timerMode == TMRMODE_ABS) { + if (timerMode == TMRMODE_ON) { newTimerVal++; } else if (timerMode == TMRMODE_THR) { if (throttle) newTimerVal++; } - else if (timerMode == TMRMODE_THR_REL) { - if ((timerState->sum/timerState->cnt) >= 128) { // throttle was normalized to 0 to 128 value (throttle/64*2 (because - range is added as well) + else { + if (timerState->cnt && (timerState->sum / timerState->cnt) >= 128) { // throttle was normalized to 0 to 128 value (throttle/64*2 (because - range is added as well) newTimerVal++; // add second used of throttle - timerState->sum -= 128*timerState->cnt; + timerState->sum -= 128 * timerState->cnt; } timerState->cnt = 0; } - else if (timerMode == TMRMODE_THR_TRG) { - // we can't rely on (throttle || newTimerVal > 0) as a detection if timer should be running - // because having persistent timer brakes this rule - if ((throttle > THR_TRG_TRESHOLD) && timerState->state == TMR_OFF) { - timerState->state = TMR_RUNNING; // start timer running - timerState->cnt = 0; - timerState->sum = 0; - // TRACE("Timer[%d] THr triggered", i); - } - if (timerState->state != TMR_OFF) newTimerVal++; - } - else { - if (timerMode > 0) timerMode -= (TMRMODE_COUNT-1); - if (getSwitch(timerMode)) { - newTimerVal++; - } - } switch (timerState->state) { case TMR_RUNNING: @@ -151,7 +137,7 @@ void evalTimers(int16_t throttle, uint8_t tick10ms) if (g_model.timers[i].countdownBeep && g_model.timers[i].start) { AUDIO_TIMER_COUNTDOWN(i, newTimerVal); } - if (g_model.timers[i].minuteBeep && (newTimerVal % 60)==0) { + if (g_model.timers[i].minuteBeep && (newTimerVal % 60) == 0) { AUDIO_TIMER_MINUTE(newTimerVal); // TRACE("Timer[%d] %d minute announcement", i, newTimerVal/60); } diff --git a/radio/src/touch.h b/radio/src/touch.h new file mode 100644 index 000000000..0b10f0fe4 --- /dev/null +++ b/radio/src/touch.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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. + */ + +#ifndef TOUCH_H +#define TOUCH_H + +enum ENUM_TOUCH_EVENT +{ + TE_NONE, + TE_DOWN, + TE_UP, + TE_SLIDE, + TE_END +}; + +typedef struct +{ + unsigned char Event; + short X; + short Y; + short startX; + short startY; + short lastX; + short lastY; + uint32_t Time; +} STRUCT_TOUCH; + +#define SLIDE_RANGE ( 6 ) + +#endif // TOUCH_H \ No newline at end of file diff --git a/radio/src/trainer.cpp b/radio/src/trainer.cpp index 7761645ba..86b4dee66 100644 --- a/radio/src/trainer.cpp +++ b/radio/src/trainer.cpp @@ -133,7 +133,7 @@ void checkTrainerSettings() stop_intmodule_heartbeat(); else init_intmodule_heartbeat(); -#else +#elif defined(INTMODULE_HEARTBEAT_GPIO) init_intmodule_heartbeat(); #endif } diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index fa6c6bade..21a68a5ef 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -51,6 +51,7 @@ ISTR(VPERSISTENT); ISTR(VLCD); ISTR(VUNITSSYSTEM); ISTR(VBEEPCOUNTDOWN); +ISTR(COUNTDOWNVALUES); ISTR(VVARIOCENTER); ISTR(COUNTRYCODES); ISTR(USBMODES); @@ -110,6 +111,7 @@ const char STR_LOADINGMODEL[] = TR_LOADINGMODEL; const char STR_NAME[] = TR_NAME; const char STR_BITMAP[] = TR_BITMAP; const char STR_TIMER[] = TR_TIMER; +const char STR_START[] = TR_START; const char STR_ELIMITS[] = TR_ELIMITS; const char STR_ETRIMS[] = TR_ETRIMS; const char STR_TRIMINC[] = TR_TRIMINC; @@ -182,6 +184,7 @@ const char STR_SLAVE[] = TR_SLAVE; const char STR_MODESRC[] = TR_MODESRC; const char STR_MULTIPLIER[] = TR_MULTIPLIER; const char STR_CAL[] = TR_CAL; +const char STR_CALIB_DONE[] = TR_CALIB_DONE; const char STR_VTRIM[] = TR_VTRIM; const char STR_MENUTOSTART[] = TR_MENUTOSTART; const char STR_SETMIDPOINT[] = TR_SETMIDPOINT; @@ -296,6 +299,8 @@ const char STR_INSERT_AFTER[] = TR_INSERT_AFTER; const char STR_COPY[] = TR_COPY; const char STR_MOVE[] = TR_MOVE; const char STR_PASTE[] = TR_PASTE; +const char STR_PASTE_BEFORE[] = TR_PASTE_BEFORE; +const char STR_PASTE_AFTER[] = TR_PASTE_AFTER; const char STR_INSERT[] = TR_INSERT; const char STR_DELETE[] = TR_DELETE; const char STR_RESET_FLIGHT[] = TR_RESET_FLIGHT; @@ -474,6 +479,8 @@ const char STR_DISABLE_ALARM[] = TR_DISABLE_ALARM; const char STR_TELEMETRY_TYPE[] = TR_TELEMETRY_TYPE; const char STR_TELEMETRY_SENSORS[] = TR_TELEMETRY_SENSORS; const char STR_VALUE[] = TR_VALUE; +const char STR_REPEAT[] = TR_REPEAT; +const char STR_ENABLE[] = TR_ENABLE; const char STR_TOPLCDTIMER[] = TR_TOPLCDTIMER; const char STR_UNIT[] = TR_UNIT; const char STR_TELEMETRY_NEWSENSOR[] = TR_TELEMETRY_NEWSENSOR; diff --git a/radio/src/translations.h b/radio/src/translations.h index a51fedded..1c0e988a8 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -66,7 +66,10 @@ #define HINT(s) s #endif -#if LCD_W >= 480 +#if LCD_W < LCD_H // Portrait mode + #define TR3(x, y, z) x + #define TR2(x, y) x +#elif LCD_W >= 480 #define TR3(x, y, z) z #define TR2(x, y) y #elif LCD_W >= 212 @@ -140,6 +143,7 @@ extern const char STR_VPERSISTENT[]; extern const char STR_VLCD[]; extern const char STR_VUNITSSYSTEM[]; extern const char STR_VBEEPCOUNTDOWN[]; +extern const char STR_COUNTDOWNVALUES[]; extern const char STR_VVARIOCENTER[]; extern const char STR_COUNTRYCODES[]; @@ -201,6 +205,7 @@ extern const char STR_LOADINGMODEL[]; extern const char STR_NAME[]; extern const char STR_BITMAP[]; extern const char STR_TIMER[]; +extern const char STR_START[]; extern const char STR_ELIMITS[]; extern const char STR_ETRIMS[]; extern const char STR_TRIMINC[]; @@ -323,6 +328,8 @@ extern const char STR_MODESRC[]; extern const char STR_MULTIPLIER[]; #define LEN_MULTIPLIER PSIZE(TR_MULTIPLIER) extern const char STR_CAL[]; +extern const char STR_CALIB_DONE[]; +extern const char STR_CALIBRATION[]; extern const char STR_VTRIM[]; extern const char STR_MENUTOSTART[]; extern const char STR_SETMIDPOINT[]; @@ -471,6 +478,8 @@ extern const char STR_INSERT_AFTER[]; extern const char STR_COPY[]; extern const char STR_MOVE[]; extern const char STR_PASTE[]; +extern const char STR_PASTE_AFTER[]; +extern const char STR_PASTE_BEFORE[]; extern const char STR_INSERT[]; extern const char STR_DELETE[]; extern const char STR_RESET_FLIGHT[]; @@ -728,6 +737,7 @@ extern const char STR_BINDING_1_8_TELEM_OFF[]; extern const char STR_BINDING_9_16_TELEM_ON[]; extern const char STR_BINDING_9_16_TELEM_OFF[]; extern const char STR_CHANNELRANGE[]; +extern const char STR_RXFREQUENCY[]; extern const char STR_ANTENNASELECTION[]; extern const char STR_ANTENNACONFIRM1[]; extern const char STR_ANTENNACONFIRM2[]; @@ -749,6 +759,8 @@ extern const char STR_DISABLE_ALARM[]; extern const char STR_TELEMETRY_TYPE[]; extern const char STR_TELEMETRY_SENSORS[]; extern const char STR_VALUE[]; +extern const char STR_REPEAT[]; +extern const char STR_ENABLE[]; extern const char STR_TOPLCDTIMER[]; extern const char STR_UNIT[] ; extern const char STR_TELEMETRY_NEWSENSOR[]; diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index 5a8192525..ea297cf17 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -423,10 +423,10 @@ #endif #if defined(PCBHORUS) - #define TR_EXIT "[RTN]" + #define TR_EXIT "RTN" #define TR_OK TR_ENTER #else - #define TR_EXIT "[EXIT]" + #define TR_EXIT "EXIT" #define TR_OK TR("\010" "\010" "\010" "[OK]", "\010" "\010" "\010" "\010" "\010" "[OK]") #endif @@ -451,6 +451,7 @@ #define TR_EXPONAME "Popis" #define TR_BITMAP "Obrázek" #define TR_TIMER "Stopky" +#define TR_START "Start" #define TR_ELIMITS TR("Limit +25%", "Kanál +/- 125%") #define TR_ETRIMS TR("širší Trim", "Široký trim") #define TR_TRIMINC TR("Krok Trimu", "Krok trimu") diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index c483aaf0a..7955ce623 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -107,7 +107,10 @@ #define TR_VDISPLAYTRIMS "Nein\0 ""Kurz\0 ""Ja\0" //Trimmwerte Keine, kurze Anzeigen, Ja #define LEN_VBEEPCOUNTDOWN "\006" -#define TR_VBEEPCOUNTDOWN "Kein\0 Pieps\0StimmeHaptik" +#define TR_VBEEPCOUNTDOWN "Kein\0 ""Pieps\0""Stimme""Haptik" + +#define LEN_COUNTDOWNVALUES "\003" +#define TR_COUNTDOWNVALUES "5s\0""10s""20s""30s" #define LEN_VVARIOCENTER "\006" #define TR_VVARIOCENTER "Ton\0 ""Ruhe " @@ -428,17 +431,17 @@ #define BREAKSPACE " " #endif -#if defined(PCBTARANIS) || defined(PCBHORUS) - #define TR_ENTER "[ENTER]" +#if defined(PCBFRSKY) + #define TR_ENTER "[ENTER]" #else #define TR_ENTER "[MENU]" #endif #if defined(PCBHORUS) - #define TR_EXIT "[RTN]" + #define TR_EXIT "RTN" #define TR_OK TR_ENTER #else - #define TR_EXIT "[EXIT]" + #define TR_EXIT "EXIT" #define TR_OK TR("\010" "\010" "\010" "[OK]", "\010" "\010" "\010" "\010" "\010" "[OK]") #endif @@ -454,7 +457,7 @@ #define TR_COPYINGMODEL "Kopiere Modell" #define TR_MOVINGMODEL "Schiebe Modell" #define TR_LOADINGMODEL "Lade Modell..." -#define TR_NAME "Name" +#define STR_NAME "Name" #define TR_MODELNAME "Modellname" #define TR_PHASENAME "Phase-Name" #define TR_MIXNAME "Mix-Name" @@ -462,6 +465,7 @@ #define TR_EXPONAME "Info" #define TR_BITMAP "Modellfoto" #define TR_TIMER TR("Timer", "Timer ") // für Timer1 Timer2 Timer3 +#define TR_START "Start" #define TR_ELIMITS TR("Erw. Limit", "Erw. Wege auf 150%") #define TR_ETRIMS TR("Erw. Trims", "Erw. Trim auf 100%") #define TR_TRIMINC TR("Trim Schr.", "Trimmschritte") @@ -556,6 +560,7 @@ #define TR_PERSISTENT TR(INDENT "Permanent", INDENT "Permanent") #define TR_BACKLIGHT_LABEL "LCD-Beleuchtung" #define TR_BLDELAY INDENT "Dauer" +#define TR_BL_MODE_DUR INDENT "Mode-Duration" #define TR_BLONBRIGHTNESS INDENT "An-Helligkeit" #define TR_BLOFFBRIGHTNESS INDENT "Aus-Helligkeit" #define TR_BLCOLOR INDENT "Farbe" @@ -579,8 +584,10 @@ #define TR_MODESRC " Modus\003% Quelle" #define TR_MULTIPLIER "Multiplik." #define TR_CAL "Kal." +#define TR_CALIBRATION "Calibration" #define TR_VTRIM "Trim - +" #define TR_BG "BG:" +#define TR_CALIB_DONE "Calibration completed" #if defined(PCBHORUS) #define TR_MENUTOSTART "Drücke [Enter] zum Start" #define TR_SETMIDPOINT "Knüppel/Potis/Sliders auf Mitte [Enter]" @@ -900,6 +907,8 @@ #define TR_COPY "Zeile kopieren" #define TR_MOVE "Zeile verschieben" #define TR_PASTE "Zeile einfügen" +#define TR_PASTE_AFTER "Paste After" +#define TR_PASTE_BEFORE "Paste Before" #define TR_DELETE "Zeile löschen" #define TR_INSERT "Neue Zeile" #define TR_RESET_FLIGHT "Reset Flugdaten" @@ -950,10 +959,13 @@ #define TR_TELEMETRY_TYPE TR("Typ", "Telemetrietyp") #define TR_TELEMETRY_SENSORS "Sensoren" #define TR_VALUE "Wert" +#define TR_REPEAT "Repeat" +#define TR_ENABLE "Enable" #define TR_TOPLCDTIMER "Top LCD Timer" #define TR_UNIT "Einheit" #define TR_TELEMETRY_NEWSENSOR INDENT "Sensor hinzufügen ..." #define TR_CHANNELRANGE TR(INDENT "Kanäle", INDENT "Ausgangs Kanäle") //wg 9XR-Pro +#define TR_RXFREQUENCY TR("Rx Freqency", "Rx Servo Frequency") #define TR_ANTENNASELECTION TR(INDENT "Antenna", INDENT "Antenne auswählen") #define TR_ANTENNACONFIRM1 "Ant. umschalten" #define TR_ANTENNACONFIRM2 TR("Check antenna", "Ist eine externe Antenne installiert?") @@ -1080,7 +1092,7 @@ #define TR_ABOUT_MARTIN_1 "Martin Hotar" #define TR_ABOUT_MARTIN_2 "Grafikdesigner" -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) #define TR_ABOUT_HARDWARE_1 "FrSky" #define TR_ABOUT_HARDWARE_2 "Hardware designer/producer" #define TR_ABOUT_HARDWARE_3 "Firmware contributor" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index 0442da53c..ba711b102 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -107,7 +107,10 @@ #define TR_VDISPLAYTRIMS "No\0 ""Change""Yes\0" #define LEN_VBEEPCOUNTDOWN "\006" -#define TR_VBEEPCOUNTDOWN "SilentBeeps\0Voice\0Haptic" +#define TR_VBEEPCOUNTDOWN "Silent""Beeps\0""Voice\0""Haptic" + +#define LEN_COUNTDOWNVALUES "\003" +#define TR_COUNTDOWNVALUES "5s\0""10s""20s""30s" #define LEN_VVARIOCENTER "\006" #define TR_VVARIOCENTER "Tone\0 ""Silent" @@ -368,8 +371,8 @@ #define TR_RESERVE_VSRCRAW "[--]" #define TR_EXTRA_VSRCRAW "Batt""Time""GPS\0" TR_RESERVE_VSRCRAW TR_RESERVE_VSRCRAW TR_RESERVE_VSRCRAW TR_RESERVE_VSRCRAW "Tmr1""Tmr2""Tmr3" -#define LEN_VTMRMODES "\003" -#define TR_VTMRMODES "OFF""ON\0""THs""TH%""THt" +#define LEN_VTMRMODES "\004" +#define TR_VTMRMODES "OFF\0""ON\0 ""Strt""THs\0""TH%\0""THt" #define LEN_VTRAINERMODES "\022" #define TR_VTRAINER_MASTER_JACK "Master/Jack\0 " @@ -408,7 +411,7 @@ // ZERO TERMINATED STRINGS #if defined(COLORLCD) - #define INDENT " " + #define INDENT #define LEN_INDENT 3 #define INDENT_WIDTH 12 #define BREAKSPACE "\036" @@ -419,17 +422,17 @@ #define BREAKSPACE " " #endif -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) #define TR_ENTER "[ENTER]" #else #define TR_ENTER "[MENU]" #endif -#if defined(PCBHORUS) - #define TR_EXIT "[RTN]" +#if defined(COLORLCD) + #define TR_EXIT "RTN" #define TR_OK TR_ENTER #else - #define TR_EXIT "[EXIT]" + #define TR_EXIT "EXIT" #define TR_OK TR("\010" "\010" "\010" "[OK]", "\010" "\010" "\010" "\010" "\010" "[OK]") #endif @@ -453,6 +456,7 @@ #define TR_EXPONAME TR("Name", "Line name") #define TR_BITMAP "Model image" #define TR_TIMER TR("Timer", "Timer ") +#define TR_START "Start" #define TR_ELIMITS TR("E.Limits", "Extended limits") #define TR_ETRIMS TR("E.Trims", "Extended trims") #define TR_TRIMINC "Trim Step" @@ -547,6 +551,7 @@ #define TR_PERSISTENT TR(INDENT "Persist.", INDENT "Persistent") #define TR_BACKLIGHT_LABEL "Backlight" #define TR_BLDELAY INDENT "Duration" +#define TR_BL_MODE_DUR INDENT "Mode-Duration" #define TR_BLONBRIGHTNESS INDENT "ON brightness" #define TR_BLOFFBRIGHTNESS INDENT "OFF brightness" #define TR_BLCOLOR INDENT "Color" @@ -570,8 +575,10 @@ #define TR_MODESRC "Mode\006% Source" #define TR_MULTIPLIER "Multiplier" #define TR_CAL "Cal" +#define TR_CALIBRATION "Calibration" #define TR_VTRIM "Trim- +" #define TR_BG "BG:" +#define TR_CALIB_DONE "Calibration completed" #if defined(PCBHORUS) #define TR_MENUTOSTART "Press [Enter] to start" #define TR_SETMIDPOINT "Center sticks/pots/sliders and press [Enter]" @@ -580,7 +587,6 @@ #define TR_MENUTOSTART TR_ENTER " TO START" #define TR_SETMIDPOINT "CENTER STICKS/SLIDERS" #define TR_MOVESTICKSPOTS "MOVE STICKS/POTS" - #define TR_MENUWHENDONE TR_ENTER " WHEN DONE" #else #define TR_MENUTOSTART CENTER "\010" TR_ENTER " TO START" #define TR_SETMIDPOINT TR(CENTER "\004SET STICKS MIDPOINT", CENTER "\004CENTER STICKS/SLIDERS") @@ -891,6 +897,8 @@ #define TR_COPY "Copy" #define TR_MOVE "Move" #define TR_PASTE "Paste" +#define TR_PASTE_AFTER "Paste After" +#define TR_PASTE_BEFORE "Paste Before" #define TR_DELETE "Delete" #define TR_INSERT "Insert" #define TR_RESET_FLIGHT "Reset flight" @@ -945,10 +953,13 @@ #define TR_TELEMETRY_TYPE TR("Type", "Telemetry type") #define TR_TELEMETRY_SENSORS "Sensors" #define TR_VALUE "Value" +#define TR_REPEAT "Repeat" +#define TR_ENABLE "Enable" #define TR_TOPLCDTIMER "Top LCD Timer" #define TR_UNIT "Unit" #define TR_TELEMETRY_NEWSENSOR INDENT "Add a new sensor..." #define TR_CHANNELRANGE TR(INDENT "Ch. Range", INDENT "Channel Range") +#define TR_RXFREQUENCY TR("Rx Freqency", "Rx Servo Frequency") #define TR_ANTENNASELECTION TR(INDENT "Antenna", TR(INDENT "Antenna", INDENT "Antenna selection")) #define TR_ANTENNACONFIRM1 "Really switch?" #define TR_ANTENNACONFIRM2 TR("Check antenna", "Make sure antenna is installed!") @@ -1074,7 +1085,7 @@ #define TR_ABOUT_MARTIN_1 "Martin Hotar" #define TR_ABOUT_MARTIN_2 "Graphics designer" -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) #define TR_ABOUT_HARDWARE_1 "FrSky" #define TR_ABOUT_HARDWARE_2 TR("Hardware design/producer", "Hardware designer/producer") #define TR_ABOUT_HARDWARE_3 "Firmware contributor" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index 22864e0b7..aeb035d6c 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -107,7 +107,10 @@ #define TR_VDISPLAYTRIMS "No\0 ""Change""Yes\0" #define LEN_VBEEPCOUNTDOWN "\006" -#define TR_VBEEPCOUNTDOWN "SilentBeeps\0Voz\0 Haptic" +#define TR_VBEEPCOUNTDOWN "Silent""Beeps\0""Voz\0 ""Haptic" + +#define LEN_COUNTDOWNVALUES "\003" +#define TR_COUNTDOWNVALUES "5s\0""10s""20s""30s" #define LEN_VVARIOCENTER "\006" #define TR_VVARIOCENTER "Tone\0 ""Silent" @@ -423,7 +426,7 @@ // ZERO TERMINATED STRINGS #if defined(COLORLCD) - #define INDENT " " + #define INDENT #define LEN_INDENT 3 #define INDENT_WIDTH 12 #define BREAKSPACE "\036" @@ -441,10 +444,10 @@ #endif #if defined(PCBHORUS) - #define TR_EXIT "[RTN]" + #define TR_EXIT "RTN" #define TR_OK TR_ENTER #else - #define TR_EXIT "[EXIT]" + #define TR_EXIT "EXIT" #define TR_OK TR("\010" "\010" "\010" "[OK]", "\010" "\010" "\010" "\010" "\010" "[OK]") #endif @@ -461,7 +464,7 @@ #define TR_COPYINGMODEL "Copiando modelo.." #define TR_MOVINGMODEL "Mover modelo..." #define TR_LOADINGMODEL "Cargar modelo..." -#define TR_NAME "Nom." +#define STR_NAME "Nom." #define TR_MODELNAME "Nom.Modelo" #define TR_PHASENAME "Nom.Fase " #define TR_MIXNAME "Nom.Mezcla" @@ -473,6 +476,7 @@ #endif #define TR_BITMAP "Imagen Modelo" #define TR_TIMER TR("Reloj","Reloj ") +#define TR_START "Start" #define TR_ELIMITS TR("E.Limite","Ampliar Limites") #define TR_ETRIMS TR("E.Trims","Ampliar Trims") #define TR_TRIMINC "Paso Trim" @@ -567,6 +571,7 @@ #define TR_PERSISTENT TR(INDENT"Persist.", INDENT"Persistente") #define TR_BACKLIGHT_LABEL "Luz Fondo" #define TR_BLDELAY INDENT"Duracion" +#define TR_BL_MODE_DUR INDENT "Mode-Duration" #define TR_BLONBRIGHTNESS INDENT"MAS Brillo" #define TR_BLOFFBRIGHTNESS INDENT"MENOS Brillo" #define TR_BLCOLOR INDENT "Color" @@ -590,8 +595,10 @@ #define TR_MODESRC "Modo\006% Fuente" #define TR_MULTIPLIER "Multiplicar" #define TR_CAL "Cal" +#define TR_CALIBRATION "Calibration" #define TR_VTRIM "Trim- +" #define TR_BG "BG:" +#define TR_CALIB_DONE "Calibration completed" #if defined(PCBHORUS) #define TR_MENUTOSTART "Press [Enter] to start" #define TR_SETMIDPOINT "Center sticks/pots/sliders and press [Enter]" @@ -915,6 +922,8 @@ #define TR_COPY "Copiar" #define TR_MOVE "Mover" #define TR_PASTE "Pegar" +#define TR_PASTE_AFTER "Paste After" +#define TR_PASTE_BEFORE "Paste Before" #define TR_DELETE "Borrar" #define TR_INSERT "Insertar" #define TR_RESET_FLIGHT "Reset Vuelo" @@ -965,10 +974,13 @@ #define TR_TELEMETRY_TYPE TR("Type", "Telemetry Type") #define TR_TELEMETRY_SENSORS "Sensors" #define TR_VALUE "Value" +#define TR_REPEAT "Repeat" +#define TR_ENABLE "Enable" #define TR_TOPLCDTIMER "Top LCD Timer" #define TR_UNIT "Unit" #define TR_TELEMETRY_NEWSENSOR INDENT "Add a new sensor..." #define TR_CHANNELRANGE INDENT "Gama de canales" +#define TR_RXFREQUENCY TR("Rx Freqency", "Rx Servo Frequency") #define TR_ANTENNASELECTION TR(INDENT "Antenna", TR(INDENT "Antenna", INDENT "Antenna selection")) #define TR_ANTENNACONFIRM1 "Really switch?" #define TR_ANTENNACONFIRM2 TR("Check antenna", "Make sure antenna is installed!") diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index b4d2948f0..80d8e4d4d 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -107,7 +107,10 @@ #define TR_VDISPLAYTRIMS "No\0 ""Change""Yes\0" #define LEN_VBEEPCOUNTDOWN "\006" -#define TR_VBEEPCOUNTDOWN "SilentBeeps\0Voice\0Haptic" +#define TR_VBEEPCOUNTDOWN "Silent""Beeps\0""Voice\0""Haptic" + +#define LEN_COUNTDOWNVALUES "\003" +#define TR_COUNTDOWNVALUES "5s\0""10s""20s""30s" #define LEN_VVARIOCENTER "\006" #define TR_VVARIOCENTER "Tone\0 ""Silent" @@ -433,10 +436,10 @@ #endif #if defined(PCBHORUS) - #define TR_EXIT "[RTN]" + #define TR_EXIT "RTN" #define TR_OK TR_ENTER #else - #define TR_EXIT "[EXIT]" + #define TR_EXIT "EXIT" #define TR_OK TR("\010" "\010" "\010" "[OK]", "\010" "\010" "\010" "\010" "\010" "[OK]") #endif @@ -453,7 +456,7 @@ #define TR_COPYINGMODEL "Copying model..." #define TR_MOVINGMODEL "Moving model..." #define TR_LOADINGMODEL "Loading model..." -#define TR_NAME "Name" +#define STR_NAME "Name" #define TR_MODELNAME "Model Name" #define TR_PHASENAME "Mode Name" #define TR_MIXNAME "Mix Name" @@ -465,6 +468,7 @@ #endif #define TR_BITMAP "Model Image" #define TR_TIMER TR("Timer", "Timer ") +#define TR_START "Start" #define TR_ELIMITS TR("E.Limits", "Extended Limits") #define TR_ETRIMS TR("E.Trims", "Extended Trims") #define TR_TRIMINC "Trim Step" @@ -559,6 +563,7 @@ #define TR_PERSISTENT TR(INDENT"Persist.", INDENT"Persistent") #define TR_BACKLIGHT_LABEL "Backlight" #define TR_BLDELAY INDENT"Duration" +#define TR_BL_MODE_DUR INDENT "Mode-Duration" #define TR_BLONBRIGHTNESS INDENT"ON Brightness" #define TR_BLOFFBRIGHTNESS INDENT"OFF Brightness" #define TR_BLCOLOR INDENT "Color" @@ -582,8 +587,10 @@ #define TR_MODESRC "Mode\006% Source" #define TR_MULTIPLIER "Multiplier" #define TR_CAL "Cal" +#define TR_CALIBRATION "Calibration" #define TR_VTRIM "Trim- +" #define TR_BG "BG:" +#define TR_CALIB_DONE "Calibration completed" #if defined(PCBHORUS) #define TR_MENUTOSTART "Press [Enter] to start" #define TR_SETMIDPOINT "Center sticks/pots/sliders and press [Enter]" @@ -908,6 +915,8 @@ #define TR_COPY "Copy" #define TR_MOVE "Move" #define TR_PASTE "Paste" +#define TR_PASTE_AFTER "Paste After" +#define TR_PASTE_BEFORE "Paste Before" #define TR_DELETE "Delete" #define TR_INSERT "Insert" #define TR_RESET_FLIGHT "Reset Flight" @@ -958,10 +967,13 @@ #define TR_TELEMETRY_TYPE TR("Type", "Telemetry Type") #define TR_TELEMETRY_SENSORS "Sensors" #define TR_VALUE "Value" +#define TR_REPEAT "Repeat" +#define TR_ENABLE "Enable" #define TR_TOPLCDTIMER "Top LCD Timer" #define TR_UNIT "Unit" #define TR_TELEMETRY_NEWSENSOR INDENT "Add a new sensor..." #define TR_CHANNELRANGE INDENT "Channel Range" +#define TR_RXFREQUENCY TR("Rx Freqency", "Rx Servo Frequency") #define TR_ANTENNASELECTION TR(INDENT "Antenna", TR(INDENT "Antenna", INDENT "Antenna selection")) #define TR_ANTENNACONFIRM1 "Really switch?" #define TR_ANTENNACONFIRM2 TR("Check antenna", "Make sure antenna is installed!") diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index 68c4c739f..c9301b8f4 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -52,6 +52,9 @@ #define LEN_VBEEPMODE "\005" #define TR_VBEEPMODE "Aucun""Alarm""NoKey""Tout\0" +#define LEN_COUNTDOWNVALUES "\003" +#define TR_COUNTDOWNVALUES "5s\0""10s""20s""30s" + #define LEN_VRENAVIG "\003" #define TR_VRENAVIG "NonREaREb" @@ -425,7 +428,7 @@ // ZERO TERMINATED STRINGS #if defined(COLORLCD) - #define INDENT " " + #define INDENT #define LEN_INDENT 3 #define INDENT_WIDTH 12 #define BREAKSPACE "\036" @@ -436,17 +439,17 @@ #define BREAKSPACE " " #endif -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) #define TR_ENTER "[ENTER]" #else #define TR_ENTER "[MENU]" #endif #if defined(PCBHORUS) - #define TR_EXIT "[RTN]" + #define TR_EXIT "RTN" #define TR_OK TR_ENTER #else - #define TR_EXIT "[EXIT]" + #define TR_EXIT "EXIT" #define TR_OK TR("\010" "\010" "\010" "[OK]", "\010" "\010" "\010" "\010" "\010" "[OK]") #endif @@ -471,6 +474,7 @@ #define TR_EXPONAME TR("Nom", "Nom ligne") #define TR_BITMAP "Image du modèle" #define TR_TIMER "Chrono " +#define TR_START "Start" #define TR_ELIMITS TR("Limites ét.", "Limites étendues") #define TR_ETRIMS TR("Trims ét.", "Trims étendus") #define TR_TRIMINC TR("Pas Trim", "Pas des trims") @@ -565,6 +569,7 @@ #define TR_PERSISTENT TR(INDENT "Persist.", INDENT "Persistant") #define TR_BACKLIGHT_LABEL "Rétroéclairage" #define TR_BLDELAY INDENT "Durée" +#define TR_BL_MODE_DUR INDENT "Mode-Durée" #define TR_BLONBRIGHTNESS INDENT "Luminosité ON" #define TR_BLOFFBRIGHTNESS INDENT "Luminosité OFF" #define TR_BLCOLOR INDENT "Couleur" @@ -588,8 +593,10 @@ #define TR_MODESRC "Mode\006% Source" #define TR_MULTIPLIER "Multiplieur" #define TR_CAL "Cal" +#define TR_CALIBRATION "Calibration" #define TR_VTRIM "Trim- +" #define TR_BG "BG:" +#define TR_CALIB_DONE "Calibration completed" #if defined(PCBHORUS) #define TR_MENUTOSTART "Presser [Enter] pour commencer" #define TR_SETMIDPOINT "Centrer manches/pots/sliders puis [Enter]" @@ -910,6 +917,8 @@ #define TR_COPY "Copier" #define TR_MOVE "Déplacer" #define TR_PASTE "Coller" +#define TR_PASTE_AFTER "Paste After" +#define TR_PASTE_BEFORE "Paste Before" #define TR_DELETE "Supprimer" #define TR_INSERT "Insérer" #define TR_RESET_FLIGHT TR("Réinit. vol", "Réinitialiser vol") @@ -960,10 +969,13 @@ #define TR_TELEMETRY_TYPE TR("Type tél.", "Type télémétrie") #define TR_TELEMETRY_SENSORS "Capteurs" #define TR_VALUE "Valeur" +#define TR_REPEAT "Repeat" +#define TR_ENABLE "Enable" #define TR_TOPLCDTIMER "Timer LCD haut" #define TR_UNIT "Unité" #define TR_TELEMETRY_NEWSENSOR TR(INDENT"Nouveau capteur...", INDENT "Ajout d'un nouveau capteur...") #define TR_CHANNELRANGE TR(INDENT "Canaux", INDENT "Plage de canaux") +#define TR_RXFREQUENCY TR("Rx Freqency", "Rx Servo Frequency") #define TR_ANTENNASELECTION TR(INDENT "Antenne", INDENT "Choix antenne") #define TR_ANTENNACONFIRM1 "Vraiment changer?" #define TR_ANTENNACONFIRM2 TR("Vérif antenne", "Installer l'antenne d'abord!") diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index e529780f5..da508b4df 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -92,8 +92,11 @@ #define LEN_USBMODES TR("\006", "\010") #define TR_USBMODES TR("Ask\0 ""Joyst\0""SDCard""Serial", "Ask\0 ""Joystick""Storage\0""Serial\0 ") -#define LEN_JACKMODES "\007" -#define TR_JACKMODES "Ask\0 ""Audio\0 ""Trainer" +#define LEN_MODULE_PROTOCOLS "\004" +#define TR_MODULE_PROTOCOLS "OFF\0""PPM\0""XJT\0""XJT2""DSM2""CRSF""MULT""R9M\0""R9M2""SBUS" + +#define LEN_JACKMODES "\007" +#define TR_JACKMODES "Ask\0 ""Audio\0 ""Trainer" #define LEN_TELEMETRY_PROTOCOLS "\017" #define TR_TELEMETRY_PROTOCOLS "FrSky S.PORT\0 ""FrSky D\0 ""FrSky D (cable)""TBS Crossfire\0 ""Spektrum\0 ""AFHDS2A IBUS\0 ""Multi Telemetry" @@ -107,7 +110,10 @@ #define TR_VDISPLAYTRIMS "No\0 ""Cambio""Si\0 " #define LEN_VBEEPCOUNTDOWN "\006" -#define TR_VBEEPCOUNTDOWN "NienteSuoni\0Voce\0 Vibra\0" +#define TR_VBEEPCOUNTDOWN "Niente""Suoni\0""Voce\0 ""Vibra\0" + +#define LEN_COUNTDOWNVALUES "\003" +#define TR_COUNTDOWNVALUES "5s\0""10s""20s""30s" #define LEN_VVARIOCENTER "\006" #define TR_VVARIOCENTER "Tono\0 ""Silent" @@ -444,10 +450,10 @@ #endif #if defined(PCBHORUS) - #define TR_EXIT "[RTN]" + #define TR_EXIT "RTN" #define TR_OK TR_ENTER #else - #define TR_EXIT "[EXIT]" + #define TR_EXIT "EXIT" #define TR_OK TR("\010" "\010" "\010" "[OK]", "\010" "\010" "\010" "\010" "\010" "[OK]") #endif @@ -464,7 +470,7 @@ #define TR_COPYINGMODEL "Copia in corso.." #define TR_MOVINGMODEL "Spostamento..." #define TR_LOADINGMODEL "Caricamento..." -#define TR_NAME "Nome" +#define STR_NAME "Nome" #define TR_MODELNAME "Nome modello" #define TR_PHASENAME "Nome fase" #define TR_MIXNAME "Nome mix" @@ -472,6 +478,7 @@ #define TR_EXPONAME "Nome expo" #define TR_BITMAP "Immagine" #define TR_TIMER TR("Timer","Timer ") +#define TR_START "Start" #define TR_ELIMITS TR("Limiti.E","Limiti Estesi") #define TR_ETRIMS TR("Trim Ext","Trim Estesi") #define TR_TRIMINC "Passo Trim" @@ -566,6 +573,7 @@ #define TR_PERSISTENT TR(INDENT"Persist.", INDENT"Persistente") #define TR_BACKLIGHT_LABEL TR("Retroillum.", "Retroilluminazione") #define TR_BLDELAY INDENT"Durata" +#define TR_BL_MODE_DUR INDENT "Mode-Duration" #define TR_BLONBRIGHTNESS TR(INDENT"Lumin. ON",INDENT"Luminosità ON") #define TR_BLOFFBRIGHTNESS TR(INDENT"Lumin. OFF",INDENT"Luminosità OFF") #define TR_BLCOLOR INDENT "Colore" @@ -589,8 +597,10 @@ #define TR_MODESRC "Modo\006% Origine" #define TR_MULTIPLIER "Moltiplica" #define TR_CAL "Cal" +#define TR_CALIBRATION "Calibration" #define TR_VTRIM "Trim- +" #define TR_BG "BG:" +#define TR_CALIB_DONE "Calibration completed" #if defined(PCBHORUS) #define TR_MENUTOSTART "Press [Enter] to start" #define TR_SETMIDPOINT "Center sticks/pots/sliders and press [Enter]" @@ -909,6 +919,8 @@ #define TR_COPY "Copia" #define TR_MOVE "Sposta" #define TR_PASTE "Incolla" +#define TR_PASTE_AFTER "Paste After" +#define TR_PASTE_BEFORE "Paste Before" #define TR_DELETE "Elimina" #define TR_INSERT TR("Inser.","Inserisci") #define TR_RESET_FLIGHT "Azzera volo" @@ -959,10 +971,13 @@ #define TR_TELEMETRY_TYPE "Tipo Telemetria" #define TR_TELEMETRY_SENSORS "Sensori" #define TR_VALUE "Valore" +#define TR_REPEAT "Repeat" +#define TR_ENABLE "Enable" #define TR_TOPLCDTIMER "Timer LCD Sup." #define TR_UNIT "Unita" #define TR_TELEMETRY_NEWSENSOR INDENT "Aggiungi nuovo sensore..." #define TR_CHANNELRANGE INDENT "Numero Canali" +#define TR_RXFREQUENCY TR("Rx Freqency", "Rx Servo Frequency") #define TR_ANTENNASELECTION TR(INDENT "Antenna", INDENT "Antenna selection") #define TR_ANTENNACONFIRM1 "Really switch?" #define TR_ANTENNACONFIRM2 TR("Check antenna", "Make sure antenna is installed!") diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index 4546754e7..b33e74442 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -108,7 +108,10 @@ #define TR_VDISPLAYTRIMS "Nee\0 ""Kort\0 ""Ja\0 " #define LEN_VBEEPCOUNTDOWN "\006" -#define TR_VBEEPCOUNTDOWN "StilteBeeps\0SpraakTril\0 " +#define TR_VBEEPCOUNTDOWN "Stilte""Beeps\0""Spraak""Tril\0 " + +#define LEN_COUNTDOWNVALUES "\003" +#define TR_COUNTDOWNVALUES "5s\0""10s""20s""30s" #define LEN_VVARIOCENTER "\006" #define TR_VVARIOCENTER "Tonen\0""Stilte" @@ -408,7 +411,7 @@ TR_GYR_VSRCRAW // ZERO TERMINATED STRINGS #if defined(COLORLCD) - #define INDENT " " + #define INDENT #define LEN_INDENT 3 #define INDENT_WIDTH 12 #define BREAKSPACE "\036" @@ -419,17 +422,17 @@ TR_GYR_VSRCRAW #define BREAKSPACE " " #endif -#if defined(PCBTARANIS) || defined(PCBHORUS) - #define TR_ENTER "[ENTER]" +#if defined(PCBFRSKY) + #define TR_ENTER "[ENTER]" #else #define TR_ENTER "[MENU]" #endif #if defined(PCBHORUS) - #define TR_EXIT "[RTN]" + #define TR_EXIT "RTN" #define TR_OK TR_ENTER #else - #define TR_EXIT "[EXIT]" + #define TR_EXIT "EXIT" #define TR_OK TR("\010" "\010" "\010" "[OK]", "\010" "\010" "\010" "\010" "\010" "[OK]") #endif @@ -445,7 +448,7 @@ TR_GYR_VSRCRAW #define TR_COPYINGMODEL "Kopieer Model" #define TR_MOVINGMODEL "Verplaats Model" #define TR_LOADINGMODEL "Laad Model..." -#define TR_NAME "Naam" +#define STR_NAME "Naam" #define TR_MODELNAME "Modelnaam" #define TR_PHASENAME "Modus" #define TR_MIXNAME "Mix Naam" @@ -453,6 +456,7 @@ TR_GYR_VSRCRAW #define TR_EXPONAME "Lijn Naam" #define TR_BITMAP "Modelafbeelding" #define TR_TIMER TR("Timer", "Timer ") +#define TR_START "Start" #define TR_ELIMITS TR("E.Limits", "Extended Limits") #define TR_ETRIMS TR("E.Trims", "Extended Trims") #define TR_TRIMINC "Trim Step" @@ -549,6 +553,7 @@ TR_GYR_VSRCRAW #define TR_PERSISTENT TR(INDENT "Vasth.", INDENT "Vasthouden") #define TR_BACKLIGHT_LABEL "LCD-Verlichting" #define TR_BLDELAY INDENT "Duur" +#define TR_BL_MODE_DUR INDENT "Mode-Duration" #define TR_BLONBRIGHTNESS INDENT "Aan-Helderheid" #define TR_BLOFFBRIGHTNESS INDENT "Uit-Helderheid" #define TR_BLCOLOR INDENT "Kleur" @@ -572,8 +577,10 @@ TR_GYR_VSRCRAW #define TR_MODESRC "Mode\006% Source" #define TR_MULTIPLIER "Multiplier" #define TR_CAL "Cal" +#define TR_CALIBRATION "Calibration" #define TR_VTRIM "Trim - +" #define TR_BG "BG:" +#define TR_CALIB_DONE "Calibration completed" #if defined(PCBHORUS) #define TR_MENUTOSTART "Geef [Enter] om te starten" #define TR_SETMIDPOINT "Centreer sticks/pots/schuiven en geef [Enter]" @@ -897,6 +904,8 @@ TR_GYR_VSRCRAW #define TR_COPY "Kopieren" #define TR_MOVE "Verplaatsen" #define TR_PASTE "Plakken" +#define TR_PASTE_AFTER "Paste After" +#define TR_PASTE_BEFORE "Paste Before" #define TR_DELETE "Verwijderen" #define TR_INSERT "Invoegen" #define TR_RESET_FLIGHT "Reset Vliegdata" @@ -947,10 +956,13 @@ TR_GYR_VSRCRAW #define TR_TELEMETRY_TYPE TR("Type", "Telemetrietype") #define TR_TELEMETRY_SENSORS "Sensoren" #define TR_VALUE "Waarde" +#define TR_REPEAT "Repeat" +#define TR_ENABLE "Enable" #define TR_TOPLCDTIMER "Top LCD Timer" #define TR_UNIT "Eenheid" #define TR_TELEMETRY_NEWSENSOR INDENT "Sensor toevoegen ..." #define TR_CHANNELRANGE TR(INDENT "Kanalen", INDENT "Uitgangs Kanalen") //wg 9XR-Pro +#define TR_RXFREQUENCY TR("Rx Freqency", "Rx Servo Frequency") #define TR_ANTENNASELECTION TR(INDENT "Antenna", INDENT "Antenna selection") #define TR_ANTENNACONFIRM1 "Antennes wisselen?" #define TR_ANTENNACONFIRM2 TR("Check antenna", "Is er zeker een antenne geplaatst!") @@ -1083,7 +1095,7 @@ TR_GYR_VSRCRAW #define TR_ABOUT_MARTIN_1 "Martin Hotar" #define TR_ABOUT_MARTIN_2 "Graphics designer" -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) #define TR_ABOUT_HARDWARE_1 "FrSky" #define TR_ABOUT_HARDWARE_2 "Hardware designer/producer" #define TR_ABOUT_HARDWARE_3 "Firmware contributor" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index f89f0fdca..d72ccbe91 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -108,7 +108,10 @@ #define TR_VDISPLAYTRIMS "Nie\0 ""Zmień\0""Tak\0" #define LEN_VBEEPCOUNTDOWN "\006" -#define TR_VBEEPCOUNTDOWN "Cichy Pik \0DźwiękWibrac" +#define TR_VBEEPCOUNTDOWN "Cichy ""Pik \0""Dźwięk""Wibrac" + +#define LEN_COUNTDOWNVALUES "\003" +#define TR_COUNTDOWNVALUES "5s\0""10s""20s""30s" #define LEN_VVARIOCENTER "\006" #define TR_VVARIOCENTER "Ton\0 ""Cicho " @@ -444,10 +447,10 @@ #endif #if defined(PCBHORUS) - #define TR_EXIT "[RTN]" + #define TR_EXIT "RTN" #define TR_OK TR_ENTER #else - #define TR_EXIT "[EXIT]" + #define TR_EXIT "EXIT" #define TR_OK TR("\010" "\010" "\010" "[OK]", "\010" "\010" "\010" "\010" "\010" "[OK]") #endif @@ -464,7 +467,7 @@ #define TR_COPYINGMODEL "Kopiuj model.." #define TR_MOVINGMODEL "Przenieś model." #define TR_LOADINGMODEL "Załaduj model.." -#define TR_NAME "Nazwa" +#define STR_NAME "Nazwa" #define TR_MODELNAME "Nazwa modelu" #define TR_PHASENAME "Nazwa Fazy" #define TR_MIXNAME "Nazwa Mix" @@ -472,6 +475,7 @@ #define TR_EXPONAME "LiniaNazw" #define TR_BITMAP "Obrazek " #define TR_TIMER TR("Timer", "Timer ") +#define TR_START "Start" #define TR_ELIMITS TR("Limi+25%", "Kanał +/- 125% ") #define TR_ETRIMS TR("E.Trym ", "Poszer Trymery") #define TR_TRIMINC "Krok Trym" @@ -566,6 +570,7 @@ #define TR_PERSISTENT TR(INDENT "Dokł.", INDENT "Dokładny") #define TR_BACKLIGHT_LABEL "Podświetl" #define TR_BLDELAY INDENT"Czas trwania" +#define TR_BL_MODE_DUR INDENT "Mode-Duration" #define TR_BLONBRIGHTNESS INDENT"Jasnośc wł." #define TR_BLOFFBRIGHTNESS INDENT"Jasność wył." #define TR_BLCOLOR INDENT "Color" @@ -589,8 +594,10 @@ #define TR_MODESRC " Tryb\006% Źródło" #define TR_MULTIPLIER "Mnożnik" #define TR_CAL "Kal." +#define TR_CALIBRATION "Calibration" #define TR_VTRIM "Trym- +" #define TR_BG "BG:" +#define TR_CALIB_DONE "Calibration completed" #if defined(PCBHORUS) #define TR_MENUTOSTART "Press [Enter] to start" #define TR_SETMIDPOINT "Center sticks/pots/sliders and press [Enter]" @@ -909,6 +916,8 @@ #define TR_COPY "Kopiuj" #define TR_MOVE "Przenieś" #define TR_PASTE "Wklej" +#define TR_PASTE_AFTER "Paste After" +#define TR_PASTE_BEFORE "Paste Before" #define TR_DELETE "Kasuj" #define TR_INSERT "Wstaw" #define TR_RESET_FLIGHT "Zeruj lot" @@ -959,10 +968,13 @@ #define TR_TELEMETRY_TYPE TR("Typ ", "Typ Telemetrii") #define TR_TELEMETRY_SENSORS "Czujniki" #define TR_VALUE "Wartość" +#define TR_REPEAT "Repeat" +#define TR_ENABLE "Enable" #define TR_TOPLCDTIMER "Top LCD Timer" #define TR_UNIT "Jedn" #define TR_TELEMETRY_NEWSENSOR INDENT "Dodaj nowy czujnik..." #define TR_CHANNELRANGE TR(INDENT "ZakrKn",INDENT "Zakres kanału") +#define TR_RXFREQUENCY TR("Rx Freqency", "Rx Servo Frequency") #define TR_ANTENNASELECTION TR(INDENT "Antenna", INDENT "Antenna selection") #define TR_ANTENNACONFIRM1 "Really switch?" #define TR_ANTENNACONFIRM2 TR("Check antenna", "Make sure antenna is installed!") diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index dba497fb6..368446c4b 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -110,7 +110,10 @@ #define TR_VDISPLAYTRIMS "No\0 ""Change""Yes\0" #define LEN_VBEEPCOUNTDOWN "\006" -#define TR_VBEEPCOUNTDOWN "SilentBeeps\0Voice\0Haptic" +#define TR_VBEEPCOUNTDOWN "Silent""Beeps\0""Voice\0""Haptic" + +#define LEN_COUNTDOWNVALUES "\003" +#define TR_COUNTDOWNVALUES "5s\0""10s""20s""30s" #define LEN_VVARIOCENTER "\006" #define TR_VVARIOCENTER "Tone\0 ""Silent" @@ -408,7 +411,7 @@ // ZERO TERMINATED STRINGS #if defined(COLORLCD) - #define INDENT " " + #define INDENT #define LEN_INDENT 3 #define INDENT_WIDTH 12 #define BREAKSPACE "\036" @@ -426,10 +429,10 @@ #endif #if defined(PCBHORUS) - #define TR_EXIT "[RTN]" + #define TR_EXIT "RTN" #define TR_OK TR_ENTER #else - #define TR_EXIT "[EXIT]" + #define TR_EXIT "EXIT" #define TR_OK TR("\010" "\010" "\010" "[OK]", "\010" "\010" "\010" "\010" "\010" "[OK]") #endif @@ -446,7 +449,7 @@ #define TR_COPYINGMODEL "Copiando modelo" #define TR_MOVINGMODEL "Movendo modelo" #define TR_LOADINGMODEL "Carregar modelo" -#define TR_NAME "Nome" +#define STR_NAME "Nome" #define TR_MODELNAME "Model Name" #define TR_PHASENAME "Phase Name" #define TR_MIXNAME "Mix Name" @@ -458,6 +461,7 @@ #endif #define TR_BITMAP "Model Image" #define TR_TIMER "Cronom" +#define TR_START "Start" #define TR_ELIMITS "E.Limits" #define TR_ETRIMS "E.Trims" #define TR_TRIMINC "Trim Step" @@ -552,6 +556,7 @@ #define TR_PERSISTENT INDENT "Persist." #define TR_BACKLIGHT_LABEL "Backlight" #define TR_BLDELAY INDENT "Tempo Backlight" +#define TR_BL_MODE_DUR INDENT "Mode-Duration" #define TR_BLONBRIGHTNESS INDENT "ON Brightness" #define TR_BLOFFBRIGHTNESS INDENT "OFF Brightness" #define TR_BLCOLOR INDENT "Color" @@ -575,8 +580,10 @@ #define TR_MODESRC "Modo\006% Fonte" #define TR_MULTIPLIER "Multiplicar" #define TR_CAL "Cal" +#define TR_CALIBRATION "Calibration" #define TR_VTRIM "Trim- +" #define TR_BG "BG:" +#define TR_CALIB_DONE "Calibration completed" #if defined(PCBHORUS) #define TR_MENUTOSTART "Press [Enter] to start" #define TR_SETMIDPOINT "Center sticks/pots/sliders and press [Enter]" @@ -899,6 +906,8 @@ #define TR_COPY "Copy" #define TR_MOVE "Move" #define TR_PASTE "Paste" +#define TR_PASTE_AFTER "Paste After" +#define TR_PASTE_BEFORE "Paste Before" #define TR_DELETE "Delete" #define TR_INSERT "Insert" #define TR_RESET_FLIGHT "Reset Flight" @@ -949,10 +958,13 @@ #define TR_TELEMETRY_TYPE TR("Type", "Telemetry Type") #define TR_TELEMETRY_SENSORS "Sensors" #define TR_VALUE "Value" +#define TR_REPEAT "Repeat" +#define TR_ENABLE "Enable" #define TR_TOPLCDTIMER "Top LCD Timer" #define TR_UNIT "Unit" #define TR_TELEMETRY_NEWSENSOR INDENT "Add a new sensor..." #define TR_CHANNELRANGE INDENT "Channel Range" +#define TR_RXFREQUENCY TR("Rx Freqency", "Rx Servo Frequency") #define TR_ANTENNASELECTION TR(INDENT "Antenna", INDENT "Antenna selection") #define TR_ANTENNACONFIRM1 "Really switch?" #define TR_ANTENNACONFIRM2 TR("Check antenna", "Make sure antenna is installed!") diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 13667aacb..d45313510 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -52,6 +52,9 @@ #define LEN_VBEEPMODE "\005" #define TR_VBEEPMODE "Tyst\0""Alarm""EjKnp""Alla\0" +#define LEN_COUNTDOWNVALUES "\003" +#define TR_COUNTDOWNVALUES "5s\0""10s""20s""30s" + #define LEN_VRENAVIG "\003" #define TR_VRENAVIG "IngREaREb" @@ -421,7 +424,7 @@ // ZERO TERMINATED STRINGS #if defined(COLORLCD) - #define INDENT " " + #define INDENT #define LEN_INDENT 3 #define INDENT_WIDTH 12 #define BREAKSPACE "\036" @@ -432,17 +435,17 @@ #define BREAKSPACE " " #endif -#if defined(PCBTARANIS) || defined(PCBHORUS) - #define TR_ENTER "[ENTER]" +#if defined(PCBFRSKY) + #define TR_ENTER "[ENTER]" #else #define TR_ENTER "[MENU]" #endif #if defined(PCBHORUS) - #define TR_EXIT "[RTN]" + #define TR_EXIT "RTN" #define TR_OK TR_ENTER #else - #define TR_EXIT "[EXIT]" + #define TR_EXIT "EXIT" #define TR_OK TR("\010" "\010" "\010" "[OK]", "\010" "\010" "\010" "\010" "\010" "[OK]") #endif @@ -459,7 +462,7 @@ #define TR_COPYINGMODEL "Kopierar Modell " #define TR_MOVINGMODEL "Flyttar Modell " #define TR_LOADINGMODEL "Laddar Modell " -#define TR_NAME "Namn" +#define STR_NAME "Namn" #define TR_MODELNAME "Modellnamn" #define TR_PHASENAME "Lägesnamn " #define TR_MIXNAME "Mixnamn " @@ -471,6 +474,7 @@ #endif #define TR_BITMAP "Modellikon" #define TR_TIMER TR("Timer","Timer ") +#define TR_START "Start" #define TR_ELIMITS TR("Gränser++","Utökade Gränser") #define TR_ETRIMS TR("Trimmar++","Utökade Trimmar") #define TR_TRIMINC TR("Trimning","Trimökning") @@ -565,6 +569,7 @@ #define TR_PERSISTENT TR("Jämt på ", INDENT"Alltid På") #define TR_BACKLIGHT_LABEL "Belysning" #define TR_BLDELAY INDENT "Av efter" +#define TR_BL_MODE_DUR INDENT "Mode-Duration" #define TR_BLONBRIGHTNESS INDENT "På Ljusstyrka" #define TR_BLOFFBRIGHTNESS INDENT "Av Ljusstyrka" #define TR_SPLASHSCREEN "Startbild" @@ -588,8 +593,10 @@ #define TR_MODESRC " Läge\004% Källa" #define TR_MULTIPLIER "Styrka" #define TR_CAL "Kalib." +#define TR_CALIBRATION "Calibration" #define TR_VTRIM "Trim- +" #define TR_BG "BG:" +#define TR_CALIB_DONE "Calibration completed" #if defined(PCBHORUS) #define TR_MENUTOSTART "Press [Enter] to start" #define TR_SETMIDPOINT "Center sticks/pots/sliders and press [Enter]" @@ -909,6 +916,8 @@ #define TR_COPY "Kopiera" #define TR_MOVE "Flytta" #define TR_PASTE "Infoga" +#define TR_PASTE_AFTER "Paste After" +#define TR_PASTE_BEFORE "Paste Before" #define TR_DELETE "Radera" #define TR_INSERT "Addera" #define TR_RESET_FLIGHT "Nollställ Flygning" @@ -959,10 +968,13 @@ #define TR_TELEMETRY_TYPE "Telemetrityp" #define TR_TELEMETRY_SENSORS "Sensorer" #define TR_VALUE "Värde" +#define TR_REPEAT "Repeat" +#define TR_ENABLE "Enable" #define TR_TOPLCDTIMER "Top LCD Timer" #define TR_UNIT "Enhet" #define TR_TELEMETRY_NEWSENSOR INDENT "Lägg till sensor..." #define TR_CHANNELRANGE INDENT "Kanalområde" +#define TR_RXFREQUENCY TR("Rx Freqency", "Rx Servo Frequency") #define TR_ANTENNASELECTION TR(INDENT "Antenna", INDENT "Antenna selection") #define TR_ANTENNACONFIRM1 "Really switch?" #define TR_ANTENNACONFIRM2 TR("Check antenna", "Make sure antenna is installed!") @@ -1089,7 +1101,7 @@ #define TR_ABOUT_MARTIN_1 "Martin Hotar" #define TR_ABOUT_MARTIN_2 "Grafikdesigner" -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) #define TR_ABOUT_HARDWARE_1 "FrSky" #define TR_ABOUT_HARDWARE_2 "Hårdvarudesign/producent" #define TR_ABOUT_HARDWARE_3 "" diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index eec7706d5..12e0d861a 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -62,9 +62,12 @@ #elif defined(PCBSKY9X) #define TR_POTS_VSRCRAW "P1\0 ""P2\0 ""P3\0 " #define TR_SW_VSRCRAW "3POS" "THR\0""RUD\0""ELE\0""AIL\0""GEA\0""TRN\0" +#elif defined(PCBNV14) + #define TR_POTS_VSRCRAW "\310S1\0""\310S2\0""\310S3\0""\311LS\0""\311RS\0" + #define TR_SW_VSRCRAW "\312SA\0""\312SB\0""\312SC\0""\312SD\0""\312SE\0""\312SF\0""\312SG\0""\312SH\0" #endif -#if defined(PCBTARANIS) || defined(PCBHORUS) +#if defined(PCBFRSKY) || defined(PCBFLYSKY) // only special switches here #define TR_VSWITCHES "---" TR_TRIMS_SWITCHES TR_ON_ONE_SWITCHES #elif defined(PCBSKY9X) @@ -130,4 +133,8 @@ #define TR_DSM_PROTOCOLS "LP45""DSM2""DSMX" #define LEN_MULTI_PROTOCOLS "\007" -#define TR_MULTI_PROTOCOLS "FlySky\0""Hubsan\0""FrSky\0 ""Hisky\0 ""V2x2\0 ""DSM\0 ""Devo\0 ""YD717\0 ""KN\0 ""SymaX\0 ""SLT\0 ""CX10\0 ""CG023\0 ""Bayang\0""ESky\0 ""MT99XX\0""MJXq\0 ""Shenqi\0""FY326\0 ""SFHSS\0 ""J6 PRO\0""FQ777\0 ""Assan\0 ""Hontai\0""OpenLrs""FS 2A\0 ""Q2x2\0 ""Walk.\0 ""Q303\0 ""GW008\0 ""DM002\0 ""Cabell\0""Esy150\0""H8 3D\0 ""Corona\0""CFlie\0 ""Hitec\0 ""WFly\0 ""Bugs\0 ""BugMin\0""Traxas\0""NC1701\0""E01X\0 ""V911S\0 ""GD00X\0 ""V761\0 ""KF606\0 ""Redpin\0""Potens\0""ZSX\0 ""Flyzone" +#if defined(COLORLCD) +#define TR_MULTI_PROTOCOLS "Custom""FlySky\0""Hubsan\0""FrSky\0 ""Hisky\0 ""V2x2\0 ""DSM\0 ""Devo\0 ""YD717\0 ""KN\0 ""SymaX\0 ""SLT\0 ""CX10\0 ""CG023\0 ""Bayang\0""ESky\0 ""MT99XX\0""MJXq\0 ""Shenqi\0""FY326\0 ""SFHSS\0 ""J6 PRO\0""FQ777\0 ""Assan\0 ""Hontai\0""OpenLrs""FS 2A\0 ""Q2x2\0 ""Walk.\0 ""Q303\0 ""GW008\0 ""DM002\0 ""Cabell\0""Esy150\0""H8 3D\0 ""Corona\0""CFlie\0 ""Hitec\0 ""WFly\0 ""Bugs\0 ""BugMin\0""Traxas\0""NC1701\0""E01X\0 ""V911S\0 ""GD00X\0 ""V761\0 ""KF606\0 ""Redpin\0""Potens\0""ZSX\0 ""Flyzone" +#else +#define TR_MULTI_PROTOCOLS "FlySky""Hubsan""FrSky\0""Hisky\0""V2x2\0 ""DSM\0 ""Devo\0 ""YD717\0""KN\0 ""SymaX\0""SLT\0 ""CX10\0 ""CG023\0""Bayang""ESky\0 ""MT99XX""MJXq\0 ""Shenqi""FY326\0""SFHSS\0""J6 PRO""FQ777\0""Assan\0""Hontai""OpnLrs""FS 2A\0""Q2x2\0 ""Walk.\0""Q303\0 ""GW008\0""DM002\0""Cabell""Esy150""H8 3D\0""Corona""CFlie\0""Hitec\0""WFly\0 ""Bugs\0 ""BugMin""Traxas""NC1701""E01X\0 ""V911S\0""GD00X\0""V761\0 ""KF606\0""Redpin""Potens" +#endif diff --git a/radio/util/font2png.py b/radio/util/font2png.py index caadfac11..d9f1fdb3c 100755 --- a/radio/util/font2png.py +++ b/radio/util/font2png.py @@ -24,9 +24,7 @@ chars = chars_en + chars_extra + chars_fr + chars_de + chars_cz + chars_es + cha def createFontBitmap(filename, fontname, fontsize, fontoffset, foreground, background, coordsfile=True): - - - coords = [] + coords = [fontsize + 4] font_filename = 'fonts/' + fontname + '.ttf' print("Font filename: %s" % font_filename) @@ -37,7 +35,7 @@ def createFontBitmap(filename, fontname, fontsize, fontoffset, foreground, backg try: extraImage = Image.open(extraFilename) - extraImage = extraImage.convert('RGB'); + extraImage = extraImage.convert('RGB') except IOError: print("Missing extra symbol filename %s" % extraFilename) @@ -67,6 +65,8 @@ def createFontBitmap(filename, fontname, fontsize, fontoffset, foreground, backg width = getFontWidth() image = Image.new('RGB', (width, fontsize + 4), background) + + print(fontsize + 4) draw = ImageDraw.Draw(image) width = 0 @@ -75,7 +75,7 @@ def createFontBitmap(filename, fontname, fontsize, fontoffset, foreground, backg if c in chars_extra: if extraImage and c == chars_extra[0]: - image.paste(extraImage.copy(),(width,1)) + image.paste(extraImage.copy(), (width, fontsize % 2)) elif c == " ": pass else: @@ -92,6 +92,5 @@ def createFontBitmap(filename, fontname, fontsize, fontoffset, foreground, backg f.write(",".join(str(tmp) for tmp in coords)) -# Main if __name__ == "__main__": - createFontBitmap(sys.argv[4], sys.argv[1], int(sys.argv[2]), int(sys.argv[3]), (0,0,0), (255,255,255)) + createFontBitmap(sys.argv[4], sys.argv[1], int(sys.argv[2]), int(sys.argv[3]), (0, 0, 0), (255, 255, 255)) diff --git a/radio/util/img2lbm.py b/radio/util/img2lbm.py index 0837738c7..d526a01d3 100755 --- a/radio/util/img2lbm.py +++ b/radio/util/img2lbm.py @@ -6,9 +6,10 @@ import sys import os.path from PIL import Image + class RLE_encoder: RLE_BYTE = 0 - RLE_SEQ = 1 + RLE_SEQ = 1 def __init__(self, out): self.state = self.RLE_BYTE @@ -22,7 +23,6 @@ class RLE_encoder: return in_byte == self.prev_byte def encode_byte(self, in_byte): - if self.state == self.RLE_BYTE: self.out(in_byte) if self.eq_prev_byte(in_byte): @@ -48,7 +48,8 @@ class RLE_encoder: if self.state == self.RLE_SEQ: self.out(self.count) -class dummy_encoder: + +class DummyEncoder: def __init__(self, out): self.out = out @@ -58,207 +59,217 @@ class dummy_encoder: def encode_end(self): pass -def writeSize(f, width, height): - if lcdwidth > 255: + +def write_size(f, width, height): + if lcd_width > 255: f.write("%d,%d,%d,%d,\n" % (width % 256, width // 256, height % 256, height // 256)) else: f.write("%d,%d,\n" % (width, height)) + def F_writeValue(f): def writeValue(value): f.write("0x%02x," % value) return writeValue + def F_getPixelCoord(): - def getCoord(x,y): - return (x,y) + def getCoord(x, y): + return x, y return getCoord -def F_getPixelCoord_R(w,h): - def getCoord(x,y): - return (w-x-1, h-y-1) + +def F_getPixelCoord_R(w, h): + def getCoord(x, y): + return w-x-1, h-y-1 return getCoord -## MAIN ## -image = Image.open(sys.argv[1]) -width, height = image.size +def main(): + global lcd_width + + image = Image.open(sys.argv[1]) + width, height = image.size -image_fmt = image.format -output_filename = sys.argv[2] + image_fmt = image.format + output_filename = sys.argv[2] -with open(output_filename, "w") as f: - lcdwidth = int(sys.argv[3]) + with open(output_filename, "w") as f: + lcd_width = int(sys.argv[3]) - if len(sys.argv) > 4: - s = sys.argv[4].split('-') - what = s[0] - pixel_coord = F_getPixelCoord() - if (len(s) > 1) and (s[1] == 'R'): - pixel_coord = F_getPixelCoord_R(width,height) - else: - for s in ("03x05", "04x06", "05x07", "08x10", "10x14", "22x38"): - if s in sys.argv[2]: - what = s - break + if len(sys.argv) > 4: + s = sys.argv[4].split('-') + what = s[0] + pixel_coord = F_getPixelCoord() + if (len(s) > 1) and (s[1] == 'R'): + pixel_coord = F_getPixelCoord_R(width,height) + else: + for s in ("03x05", "04x06", "05x07", "08x10", "10x14", "22x38"): + if s in sys.argv[2]: + what = s + break - extension = os.path.splitext(output_filename)[1] - out = F_writeValue(f) + extension = os.path.splitext(output_filename)[1] + out = F_writeValue(f) - if extension == ".rle" or ((len(sys.argv) > 5) and (sys.argv[5] == "rle")): - encoder = RLE_encoder(out) - else: - encoder = dummy_encoder(out) + if extension == ".rle" or ((len(sys.argv) > 5) and (sys.argv[5] == "rle")): + encoder = RLE_encoder(out) + else: + encoder = DummyEncoder(out) - if what == "1bit": - rows = 1 - if len(sys.argv) > 5: - rows = int(sys.argv[5]) - image = image.convert(mode='1') - writeSize(f, width, height // rows) - for y in range(0, height, 8): - for x in range(width): - value = 0 - for z in range(8): - if y + z < height: - if image_fmt == 'XBM': - if image.getpixel((x, y + z)) > 0: - value += 1 << z - else: - if image.getpixel((x, y + z)) == 0: - value += 1 << z - encoder.encode_byte(value) - f.write("\n") - encoder.encode_end() - elif what == "4bits": - colors = [] - writeSize(f, width, height) - image = image.convert(mode='L') - for y in range(0, height, 2): - for x in range(width): - value = 0xFF - gray1 = image.getpixel((x, y)) - if y + 1 < height: - gray2 = image.getpixel((x, y + 1)) - else: - gray2 = 255 - for i in range(4): - if (gray1 & (1 << (4 + i))): - value -= 1 << i - if (gray2 & (1 << (4 + i))): - value -= 1 << (4 + i) - encoder.encode_byte(value) - f.write("\n") - encoder.encode_end() - elif what == "8bits": - colors = [] - writeSize(f, width, height) - image = image.convert(mode='L') - for y in range(height): - for x in range(width): - value = image.getpixel((x, y)) - value = 0x0f - (value >> 4) - encoder.encode_byte(value) - f.write("\n") - encoder.encode_end() - elif what == "4/4/4/4": - constant = sys.argv[2].upper()[:-4] - values = [] - writeSize(f, width, height) - for y in range(height): - for x in range(width): - pixel = image.getpixel(pixel_coord(x, y)) - val = ((pixel[3] // 16) << 12) + ((pixel[0] // 16) << 8) + ((pixel[1] // 16) << 4) + ((pixel[2] // 16) << 0) - encoder.encode_byte(val & 255) - encoder.encode_byte(val >> 8) - encoder.encode_end() - #f.write("\n};\n") - #if extension != ".rle": - # f.write("const Bitmap %s(BMP_ARGB4444, %d, %d, (uint16_t*)__%s);\n" % (constant, width, height, constant)) - elif what == "5/6/5": - constant = sys.argv[2].upper()[:-4] - values = [] - writeSize(f, width, height) - for y in range(height): - for x in range(width): - pixel = image.getpixel(pixel_coord(x, y)) - val = ((pixel[0] >> 3) << 11) + ((pixel[1] >> 2) << 5) + ((pixel[2] >> 3) << 0) - encoder.encode_byte(val & 255) - encoder.encode_byte(val >> 8) - encoder.encode_end() - #f.write("\n};\n") - #if extension != ".rle": - # f.write("const Bitmap %s(BMP_RGB565, %d, %d, (uint16_t*)__%s);\n" % (constant, width, height, constant)) - elif what == "03x05": - image = image.convert(mode='L') - for y in range(0, height, 5): - for x in range(width): - value = 0 - for z in range(5): - if image.getpixel((x, y + z)) == 0: - value += 1 << z - f.write("0x%02x," % value) - f.write("\n") - elif what == "04x06": - image = image.convert(mode='L') - for y in range(0, height, 7): - for x in range(width): - value = 0 - for z in range(7): - if image.getpixel((x, y + z)) == 0: - value += 1 << z - if value == 0x7f: - value = 0xff - f.write("0x%02x," % value) - f.write("\n") - elif what == "05x07": - image = image.convert(mode='L') - for y in range(0, height, 8): - for x in range(width): - value = 0 - for z in range(8): - if image.getpixel((x, y + z)) == 0: - value += 1 << z - f.write("0x%02x," % value) - f.write("\n") - elif what == "08x10": - image = image.convert(mode='L') - for y in range(0, height, 12): - for x in range(width): - skip = True - for l in range(0, 12, 8): + if what == "1bit": + rows = 1 + if len(sys.argv) > 5: + rows = int(sys.argv[5]) + image = image.convert(mode='1') + write_size(f, width, height // rows) + for y in range(0, height, 8): + for x in range(width): value = 0 for z in range(8): - if l + z < 12: - if image.getpixel((x, y + l + z)) == 0: - value += 1 << z + if y + z < height: + if image_fmt == 'XBM': + if image.getpixel((x, y + z)) > 0: + value += 1 << z else: - skip = False - if skip and l == 8: + if image.getpixel((x, y + z)) == 0: + value += 1 << z + encoder.encode_byte(value) + f.write("\n") + encoder.encode_end() + elif what == "4bits": + colors = [] + write_size(f, width, height) + image = image.convert(mode='L') + for y in range(0, height, 2): + for x in range(width): + value = 0xFF + gray1 = image.getpixel((x, y)) + if y + 1 < height: + gray2 = image.getpixel((x, y + 1)) + else: + gray2 = 255 + for i in range(4): + if gray1 & (1 << (4 + i)): + value -= 1 << i + if gray2 & (1 << (4 + i)): + value -= 1 << (4 + i) + encoder.encode_byte(value) + f.write("\n") + encoder.encode_end() + elif what == "8bits": + colors = [] + write_size(f, width, height) + image = image.convert(mode='L') + for y in range(height): + for x in range(width): + value = image.getpixel((x, y)) + value = 0x0f - (value >> 4) + encoder.encode_byte(value) + f.write("\n") + encoder.encode_end() + elif what == "4/4/4/4": + constant = sys.argv[2].upper()[:-4] + values = [] + write_size(f, width, height) + for y in range(height): + for x in range(width): + pixel = image.getpixel(pixel_coord(x, y)) + val = ((pixel[3] // 16) << 12) + ((pixel[0] // 16) << 8) + ((pixel[1] // 16) << 4) + ((pixel[2] // 16) << 0) + encoder.encode_byte(val & 255) + encoder.encode_byte(val >> 8) + encoder.encode_end() + # f.write("\n};\n") + # if extension != ".rle": + # f.write("const Bitmap %s(BMP_ARGB4444, %d, %d, (uint16_t*)__%s);\n" % (constant, width, height, constant)) + elif what == "5/6/5": + constant = sys.argv[2].upper()[:-4] + values = [] + write_size(f, width, height) + for y in range(height): + for x in range(width): + pixel = image.getpixel(pixel_coord(x, y)) + val = ((pixel[0] >> 3) << 11) + ((pixel[1] >> 2) << 5) + ((pixel[2] >> 3) << 0) + encoder.encode_byte(val & 255) + encoder.encode_byte(val >> 8) + encoder.encode_end() + #f.write("\n};\n") + #if extension != ".rle": + # f.write("const Bitmap %s(BMP_RGB565, %d, %d, (uint16_t*)__%s);\n" % (constant, width, height, constant)) + elif what == "03x05": + image = image.convert(mode='L') + for y in range(0, height, 5): + for x in range(width): + value = 0 + for z in range(5): + if image.getpixel((x, y + z)) == 0: + value += 1 << z + f.write("0x%02x," % value) + f.write("\n") + elif what == "04x06": + image = image.convert(mode='L') + for y in range(0, height, 7): + for x in range(width): + value = 0 + for z in range(7): + if image.getpixel((x, y + z)) == 0: + value += 1 << z + if value == 0x7f: value = 0xff f.write("0x%02x," % value) - f.write("\n") - elif what == "10x14": - image = image.convert(mode='L') - for y in range(0, height, 16): - for x in range(width): - for l in range(0, 16, 8): + f.write("\n") + elif what == "05x07": + image = image.convert(mode='L') + for y in range(0, height, 8): + for x in range(width): value = 0 for z in range(8): - if image.getpixel((x, y + l + z)) == 0: + if image.getpixel((x, y + z)) == 0: value += 1 << z f.write("0x%02x," % value) - f.write("\n") - elif what == "22x38": - image = image.convert(mode='L') - for y in range(0, height, 40): - for x in range(width): - for l in range(0, 40, 8): - value = 0 - for z in range(8): - if image.getpixel((x, y + l + z)) == 0: - value += 1 << z - f.write("0x%02x," % value) - f.write("\n") - else: - print("wrong argument", sys.argv[4]) + f.write("\n") + elif what == "08x10": + image = image.convert(mode='L') + for y in range(0, height, 12): + for x in range(width): + skip = True + for l in range(0, 12, 8): + value = 0 + for z in range(8): + if l + z < 12: + if image.getpixel((x, y + l + z)) == 0: + value += 1 << z + else: + skip = False + if skip and l == 8: + value = 0xff + f.write("0x%02x," % value) + f.write("\n") + elif what == "10x14": + image = image.convert(mode='L') + for y in range(0, height, 16): + for x in range(width): + for l in range(0, 16, 8): + value = 0 + for z in range(8): + if image.getpixel((x, y + l + z)) == 0: + value += 1 << z + f.write("0x%02x," % value) + f.write("\n") + elif what == "22x38": + image = image.convert(mode='L') + for y in range(0, height, 40): + for x in range(width): + for l in range(0, 40, 8): + value = 0 + for z in range(8): + if image.getpixel((x, y + l + z)) == 0: + value += 1 << z + f.write("0x%02x," % value) + f.write("\n") + else: + print("wrong argument", sys.argv[4]) + + +if __name__ == "__main__": + main() \ No newline at end of file