/* * "ui.cpp" * * This file implements the touch screen interface for the TinySA. * * Modifications through Version 2.5: * * Lots of general cleanup; removed a lot of the menu processing that was left * over from the NanoVNA code but not used here including the "marker" and * "calibration" stuff. For now some NanoVNA holdovers are left intact as they * may be used in the future such as multiple "traces". * * * Modifications in Version 2.6 by WA2FZW: * * Replaced the use of macros to define the menu items and replaced those with * a class/object implementation; much neater! * * * Modifications in Version 2.7 by WA2FZW: * * Eliminated the "kpf" array which contained the characters used to paint the numbers * in the keypad keys and in the numerical input box. The "kpf" array is replaced with * the "KP_Font" array, which contains bitmaps for the characters that make for a much * better looking display! I also eliminated all the characters leftover from the * NanoVNA code that aren't used here. */ #include // SPI Bus handling library #include "simpleSA.h" #include "ui.h" // User interface definitions and prototypes #include "cmd.h" // Command processing functions #include "preferences.h" // Save/recall functions #include // Display/Touch Screen header file #include "menu.h" // "Menuitem" class definition #include "marker.h" // Marker class definition #include "si4432.h" // Si4432 class definition extern Marker marker[MARKER_COUNT]; // Array of markers extern void TouchCalibrate (); // Function is in "tinySA.ino" extern char* FormatIPAddress (IPAddress ipAddress ); // Function in "TinySA_wifi.cpp" extern IPAddress ipAddress; // Global in "tinySA.ino" extern TFT_eSPI tft; // TFT Screen object //extern void RedrawHisto (); extern void menuExit(); extern void ClearDisplay ( void ); extern void DisplayError ( uint8_t severity, const char *l1, const char *l2, const char *l3, const char *l4 ); extern void SetRX ( int ); extern void setMode (uint16_t newMode); extern int changedSetting; // Display needs updated extern bool AGC_On; // Flag indicates if Preamp AGC is enabled extern uint8_t AGC_Reg; // Fixed value for preampGain if not auto extern Si4432 rcvr; // Si4432 Receiver object extern Si4432 xmit; // Si4432 Transmitter object #ifdef TG_IF_INSTALLED extern Si4432 tg_if; // Si4432 Tracking Generator IF #endif #ifdef TG_LO_INSTALLED extern Si4432 tg_lo; // Si4432 Tracking Generator LO #endif extern int VFO; // Selects transmitter or receiver Si4432 /* * This might seem a bit weird, but I've had to do something like this in other projects! * Sooner or later I'll figure out ir normal "true" and "false" can be used instead. As * these are used in some of the logic that has to do with multiple traces which are not * implemented (yet), it's impossible to tell if they can be replaced by the normal * indicators. */ #define FALSE 0 #define TRUE -1 /* * The "uistat" structure seems to contain information about the current state * of the user interface, but I haven't figured out what the elements all mean * yet. */ typedef struct { int8_t digit; // Original comment said "0 to 5", but it gets initialized // to '6' below! int8_t digit_mode; int8_t current_trace; // 0 to 3 ??? double value; // For editing at numeric input area double previous_value; } uistat_t; /* * Set the initial user interface status: */ uistat_t uistat = { 6, // digit - See note above 0, // digit_mode 0, // current_trace 0.0, // value 0.0 // previous_value }; /* * Create the "config" structure; not sure why in here and not in the main program file. */ config_t config; /* * Define the trace type structure. This is a NanoVNA holdover and needs to be eliminated, * but for now, there are a lot of places where it is used. I'll get to it eventually! */ typedef struct { uint8_t enabled; uint8_t type; uint8_t channel; uint8_t polar; float scale; float refpos; } trace_t; /* * Then create an array of trace types (Note, "TRACE_COUNT" is currently defined as '1' * in the "tinySA.h" file. */ trace_t trace[TRACE_COUNT]; /* * These definitions are related to manipulation of the menu on the touch screen: */ #define NO_EVENT 0 #define EVT_BUTTON_SINGLE_CLICK 0x01 #define EVT_BUTTON_DOUBLE_CLICK 0x02 #define EVT_BUTTON_DOWN_LONG 0x04 #define EVT_UP 0x10 #define EVT_DOWN 0x20 #define EVT_REPEAT 0x40 /* * Timing for touch screen button events: */ #define BUTTON_DOWN_LONG_TICKS 5000 // Original comment said 1 Second? #define BUTTON_DOUBLE_TICKS 5000 // 500 mS #define BUTTON_REPEAT_TICKS 1000 // 100 mS #define BUTTON_DEBOUNCE_TICKS 200 // 20 mS /* * These look like timer values, but they aren't used anywhere; the code compiles * fine with them commented out: */ static uint16_t last_button = 0; static uint32_t last_button_down_ticks; static uint32_t last_button_repeat_ticks; static int8_t inhibit_until_release = FALSE; /* * Requested operations: */ enum { OP_NONE = 0, OP_TOUCH, OP_FREQCHANGE }; uint8_t operation_requested = OP_NONE; // No operations so far /* * These define the things that are entered using the keypad. The numbers are used as an * index into the "keypad_mode_label" array which contains the strings that appear at * the left of the numerical entry box and the "keypads_mode_tbl" in the "menu_invoke" * function. */ enum { KM_START, KM_STOP, KM_CENTER, KM_SPAN, KM_FOCUS, KM_REFPOS, KM_ATTENUATION, KM_ACTUALPOWER, KM_IFFREQ, KM_PREAMP, KM_TUNE, KM_SGFREQ, KM_SGLEVEL, KM_SGLEVCAL, KM_IFSTART, KM_IFSTOP, KM_IFSIG, KM_TGOFFSET, KM_TGFREQ, KM_TGLO_DRIVE, KM_TGIF_DRIVE, KM_BANDSCOPESTART, KM_WFMIN, KM_WFGAIN, KM_BANDSCOPELEVEL, KM_RXSPAN, KM_RXSIG, KM_RBW, KM_EXTERN }; /* * These are the strings that appear in the numerical entry box under the keypad. * * The order of the labels corresponds to the "KM_" list above. * * WA2FZW - need to change the formatting as on some menus, the numerical entry * box covers the "<- BACK" button. */ static const char * const keypad_mode_label[] = { "START", "STOP", "CENTER", "SPAN", "FOCUS", "REFPOS", "ATTEN", "POWER", "IF FREQ", "PREAMP", "XTAL CAL", "SG FREQ", "dBm", "Max dBm", "IF START", "IF STOP", "IF Sig Freq", "TG OFFSET", "TG FREQ", "TG LO Drive", "TG IF Drive", "START", "WF MIN", "WF GAIN", "REF LEVEL", "RX SPAN", "RX Sig Freq", "RBW", "EXTERNAL" }; uint8_t ui_mode = UI_NORMAL; // User interface in "NORMAL" mode for now static uint8_t keypad_mode; // Current keypad mode static int8_t selection = 0; // Current menu selection, I think static int8_t last_touch_status = FALSE; static int16_t last_touch_x; static int16_t last_touch_y; #define EVT_TOUCH_NONE 0 // Touch screen status condition #define EVT_TOUCH_DOWN 1 // definitions #define EVT_TOUCH_PRESSED 2 #define EVT_TOUCH_RELEASED 3 #define NUMINPUT_LEN 10 // Length of the mumeric input box #define KP_CONTINUE 0 // Keypad entry status definitions #define KP_DONE 1 #define KP_CANCEL 2 static char kp_buf[NUMINPUT_LEN+1]; // Buffer for numerical entry static int8_t kp_index = 0; // Index to the above buffer static uint8_t selectedMarker = 0; // Currently selected marker (default is 1st one) static uint16_t bg = BLACK; // Background is normally black /* * The function prototypes are needed here as many of the functions are referenced in * setting up the menu structure which is done outside the context of any of the * actual program code. */ static void ui_mode_normal ( void ); static void ui_mode_menu ( void ); static void ui_mode_numeric ( int _keypad_mode ); static void ui_mode_keypad ( int _keypad_mode ); static void draw_menu ( void ); static void leave_ui_mode ( void ); static void erase_menu_buttons ( void ); static void ui_process_keypad ( void ); static void menu_push_submenu ( Menuitem *submenu ); static void menu_move_back ( void ); static void menu_mode_cb ( int item ); // M0WID added 3.0d static void menu_save_cb ( int item ); static void menu_refer_cb ( int item ); static void menu_refer_cb2 ( int item ); static void menu_trace_cb ( int item ); // WA2FZW - Repurposed in Version 2.6 static void menu_format2_cb ( int item ); static void menu_format_cb ( int item ); static void menu_scale_cb ( int item ); static void menu_sweep_cb ( int item ); static void menu_IF_sweep_cb ( int item ); // M0WID added 3.0c static void menu_RX_sweep_cb ( int item ); static void menu_bpfCal_cb ( int item ); static void menu_recall_cb ( int item ); static void menu_version_cb (int item ); static void menu_generate_cb(int item); // WA2FZW - Added in M0WID's Version 05 // forward declarations for bandscope menu items static void menu_BandscopeStart_cb ( int item ); static void menu_BandscopeRbw_cb ( int item ); static void menu_BandscopeSpan_cb ( int item ); static void menu_BandscopeLevel_cb ( int item ); static void menu_WaterfallMin_cb ( int item ); static void menu_WaterfallGain_cb ( int item ); static void menu_tracking_cb(int item); // M0WID - added in 3.0e static void menu_tg_offset_cb(int item); // M0WID - added in 3.0e static void menu_tg_frequency_cb(int item); // M0WID - added in 3.0e static void menu_tgIF_drive_cb(int item); // M0WID - added in 3.0e static void menu_tgLO_drive_cb(int item); // M0WID - added in 3.0e static void menu_rbw_cb ( int item ); static void menu_dBper_cb ( int item ); static void menu_dBgain_cb (int item ); // M0WID static void menu_autosettings_cb ( int item ); static void menu_average_cb (int item ); static void menu_spur_cb ( int item ); static void menu_actualpower_cb ( int item ); static void menu_storage_cb ( int item ); static void menu_IF_freq_cb ( int item ); // WA2FZW - Added in Version 2.5 static void menu_sig_freq_cb ( int item ); // M0WID - Added in Version 3.0a static void menu_sig_levCal_cb ( int item ); // M0WID - Added in Version 3.0a static void menu_sig_level_cb ( int item ); // M0WID - Added in Version 3.0b static void menu_atten_cb ( int item ); // WA2FZW - Added in Version 2.6 static void menu_touch_cb ( int item ); // WA2FZW - Added in Version 2.6 static void menu_extern_cb ( int item ); // M0WID - added static void KeyNumber ( int8_t key, int x, int y ); // WA2FZW - Added in Version 2.7 static void menu_tune_cb ( int item ); // WA2FZW - Added in Version 2.7 static void menu_save_config_cb ( int item ); // WA2FZW - Added in Version 2.7 static void menu_markers_cb ( int item ); // WA2FZW - Added in Version 2.7 static void menu_marker_select_cb ( int item ); // WA2FZW - Added in Version 2.9 static void menu_marker_color_cb ( int item ); // WA2FZW - Added in Version 2.9 static void StartMarkerMenu ( void ); // M0WID - push menu stack to start at marker level not root static void StartSweepMenu ( void ); // M0WID - push menu stack to start at sweep level not root static void StartDisplayMenu ( void ); // M0WID - push menu stack to start at display level not root static void StartRBWMenu ( void ); // M0WID - push menu stack to start at rbw level not root /* * The "KN_xxx" definitions (for key number) replace the "KP_xxx" definitions * previously used to paint the numbers on the keys and in the numerical entry * box. I eliminated all the leftovers from the NanoVNA code that aren't used * here. */ #define KN_PERIOD 10 // '.' #define KN_MINUS 11 // '-' #define KN_X 12 // 'X' (aka "Enter") #define KN_K 13 // 'K' (for "Kilo") #define KN_M 14 // 'M' (for "Mega") #define KN_BS 15 // Backspace arrow /* * The "KP_Font" array contains the bitmaps for the characters used to paint the keypas * keys and the numerical entry box. The array is indexed by the "KN_xxx" definitions * above, */ uint16_t KP_Font[] = { 0x0FF0, 0x3FFC, 0x7FFE, 0x7C3E, 0xF83F, 0xF07F, 0xF07F, // '0' => index = 0 0xF0FF, 0xF0FF, 0xF1EF, 0xF1EF, 0xF3CF, 0xF3CF, 0xF78F, 0xF78F, 0xFF0F, 0xFF0F, 0xFE1F, 0x7E3E, 0x7FFE, 0x3FFC, 0x0FF0, 0x00F0, 0x01F0, 0x03F0, 0x07F0, 0x0FF0, 0x0FF0, 0x0EF0, // '1' => index = 1 0x0CF0, 0x00F0, 0x00F0, 0x00F0, 0x00F0, 0x00F0, 0x00F0, 0x00F0, 0x00F0, 0x00F0, 0x00F0, 0x00F0, 0x03FC, 0x03FC, 0x03FC, 0x0FF0, 0x3FFC, 0x7FFE, 0x7C3E, 0xF81F, 0xF00F, 0xF00F, // '2' => index = 2 0x001F, 0x003F, 0x007E, 0x00FC, 0x01F8, 0x03F0, 0x07E0, 0x0FC0, 0x1F80, 0x3F00, 0x7E00, 0xFC00, 0xFFFF, 0xFFFF, 0xFFFF, 0x0FF0, 0x3FFC, 0x7FFE, 0x7C3E, 0xF81F, 0xF00F, 0xF00F, // '3' => index = 3 0x001E, 0x003E, 0x01FC, 0x01F8, 0x01FC, 0x007E, 0x001F, 0x000F, 0xF00F, 0xF00F, 0xF81F, 0x7C3E, 0x7FFE, 0x3FFC, 0x0FF0, 0x01F8, 0x01F8, 0x03F8, 0x03F8, 0x07F8, 0x07F8, 0x0FF8, // '4' => index = 4 0x0F78, 0x1F78, 0x1E78, 0x3E78, 0x3C78, 0x7C78, 0x7878, 0xF878, 0xFFFF, 0xFFFF, 0xFFFF, 0x0078, 0x0078, 0x0078, 0x0078, 0xFFFF, 0xFFFF, 0xFFFF, 0xF000, 0xF000, 0xF000, 0xF000, // '5' => index = 5 0xF7F0, 0xFFFC, 0xFFFE, 0xFC3E, 0xF81F, 0x000F, 0x000F, 0x000F, 0x000F, 0xF00F, 0xF81F, 0x7C3E, 0x7FFE, 0x3FFC, 0x0FF0, 0x0FF0, 0x3FFC, 0x7FFE, 0x7C3E, 0xF81F, 0xF00F, 0xF000, // '6' => index = 6 0xF000, 0xF7F0, 0xFFFC, 0xFFFE, 0xFC3E, 0xF81F, 0xF00F, 0xF00F, 0xF00F, 0xF00F, 0xF81F, 0x7C3E, 0x7FFE, 0x3FFC, 0x0FF0, 0xFFFF, 0xFFFF, 0xFFFF, 0x001F, 0x003E, 0x007C, 0x00F8, // '7' => index = 7 0x01F0, 0x01E0, 0x03E0, 0x03C0, 0x07C0, 0x0780, 0x0780, 0x0F80, 0x0F00, 0x0F00, 0x0F00, 0x0F00, 0x0F00, 0x0F00, 0x0F00, 0x07E0, 0x1FF8, 0x3FFC, 0x7C3E, 0x781E, 0x781E, 0x781E, // '8' => index = 8 0x3C3C, 0x1FF8, 0x0FF0, 0x3FFC, 0x7C3E, 0x781E, 0xF00F, 0xF00F, 0xF00F, 0xF00F, 0xF81F, 0x7C3E, 0x7FFE, 0x3FFC, 0x0FF0, 0x0FF0, 0x3FFC, 0x7FFE, 0x7C3E, 0xF81F, 0xF00F, 0xF00F, // '9' => index = 9 0xF00F, 0xF00F, 0xF81F, 0x7C3F, 0x7FFF, 0x3FFF, 0x0FFF, 0x000F, 0x000F, 0xF00F, 0xF81F, 0x7C3E, 0x7FFE, 0x3FFC, 0x0FF0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // '.' => index = KN_PERIOD 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0380, 0x07C0, 0x07C0, 0x0380, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // '-' => index = KN_MINUS 0x0000, 0x0000, 0x0000, 0x3FFC, 0x3FFC, 0x3FFC, 0x3FFC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xF00F, 0x781E, 0x781E, 0x3C3C, 0x3C3C, // 'X' => index = KN_X 0x1E78, 0x1E78, 0x0FF0, 0x07E0, 0x0FF0, 0x0FF0, 0x1E78, 0x1E78, 0x3C3C, 0x3C3C, 0x781E, 0x781E, 0xF00F, 0x0000, 0x0000, 0xF01F, 0xF03E, 0xF07C, 0xF0F8, 0xF1F0, 0xF3E0, 0xF7C2, // 'K' => index = KN_K 0xFF80, 0xFF00, 0xFE00, 0xFC00, 0xFC00, 0xFE00, 0xFF00, 0xFFC0, 0xF7E0, 0xF3F0, 0xF1F0, 0xF0F8, 0xF07E, 0xF03F, 0xF01F, 0xF00F, 0xF00F, 0xF81F, 0xF81F, 0xFC3F, 0xFC3F, 0xFE7F, // 'M' => index = KN_M 0xFE7F, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xF7EF, 0xF7EF, 0xF3CF, 0xF3CF, 0xF18F, 0xF18F, 0xF00F, 0xF00F, 0xF00F, 0xF00F, 0x0000, 0x0000, 0x0040, 0x00C0, 0x01C0, 0x03C0, 0x07C0, // Backspace arrow => index = KN_BS 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0x7FFF, 0x3FFF, 0x1FFF, 0x0FFF, 0x07C0, 0x03C0, 0x01C0, 0x00C0, 0x0040, 0x0000 }; /* * Define the actual menu item text and associated callback functions for all the menus. * * Modified in Version 2.6 by WA2FZW: * * Replaced all the "MENUITEM_xxxx" macros with the "Menuitem" class/object implementation. * This not only affects the menu definitions, but how things are handled throughout the * module. * * Note, that while it would be nice to have the main menu at the top of the list followed * by any sub-menus, the compiler doesn't like that. The sub-menus have to be defined before * they are referenced in a higher level menu. This was also the case with the original * macro implementation. */ static Menuitem menu_mode[] = // Select mode menu { Menuitem ( MT_FUNC, "\2SWEEP\0LOW", menu_mode_cb ), Menuitem ( MT_FUNC, "\2SIG\0GEN", menu_mode_cb ), Menuitem ( MT_FUNC, "\2BAND\0SCOPE",menu_mode_cb ), Menuitem ( MT_FUNC, "\2IF\0SWEEP", menu_mode_cb ), Menuitem ( MT_FUNC, "\2RX\0SWEEP", menu_mode_cb ), Menuitem ( MT_FUNC, "\2OTA\0UPDATE",menu_mode_cb ), Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_average[] = // Enables averaging and sets value { Menuitem ( MT_FUNC, "OFF", menu_average_cb ), // No averaging Menuitem ( MT_FUNC, "MIN", menu_average_cb ), // Not sure about Menuitem ( MT_FUNC, "MAX", menu_average_cb ), // These two Menuitem ( MT_FUNC, " 2 ", menu_average_cb ), // Divide by 2 Menuitem ( MT_FUNC, " 4 ", menu_average_cb ), // By 4 Menuitem ( MT_FUNC, " 8 ", menu_average_cb ), // By 8 Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_storage[] = // Sweep storage options { Menuitem ( MT_FUNC, "STORE", menu_storage_cb ), // Save current sweep Menuitem ( MT_FUNC, "CLEAR", menu_storage_cb ), // Erase saved sweep Menuitem ( MT_FUNC, "SUBTRACT", menu_storage_cb ), // Not sure what this does! Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_generate[] = // Frequency generator mode { // Menuitem ( MT_FUNC, "OFF", menu_generate_cb ), Menuitem ( MT_FUNC, "ON", menu_generate_cb ), Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; #ifdef TG_IF_INSTALLED static Menuitem menu_tracking[] = // Tracking generator mode { Menuitem ( MT_FUNC, "OFF", menu_tracking_cb ), Menuitem ( MT_FUNC, "ON", menu_tracking_cb ), #ifdef TG_LO_INSTALLED Menuitem ( MT_FUNC, "GENERATE", menu_tracking_cb ), Menuitem ( MT_FUNC, "FREQUENCY",menu_tg_frequency_cb ), Menuitem ( MT_FUNC, "OFFSET", menu_tg_offset_cb ), Menuitem ( MT_FUNC, "\2LO\0DRIVE", menu_tgLO_drive_cb ), #endif Menuitem ( MT_FUNC, "\2IF\0DRIVE", menu_tgIF_drive_cb ), Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; #endif static Menuitem menu_spur[] = // Turn spurious suppression on or off { Menuitem ( MT_FUNC, "OFF", menu_spur_cb ), Menuitem ( MT_FUNC, "ON", menu_spur_cb ), Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_rbw[] = // Resolution bandwidth settings { Menuitem ( MT_FUNC, " AUTO", menu_rbw_cb ), // In auto mode, there are many Menuitem ( MT_FUNC, " 3kHz", menu_rbw_cb ), // more available settings that Menuitem ( MT_FUNC, " 10kHz", menu_rbw_cb ), // are roughly the sweep range Menuitem ( MT_FUNC, " 30kHz", menu_rbw_cb ), // divided by 300. Menuitem ( MT_FUNC, "100kHz", menu_rbw_cb ), Menuitem ( MT_FUNC, "300kHz", menu_rbw_cb ), Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_dBper[] = // Scale setting menu { Menuitem ( MT_FUNC, " 1dB/", menu_dBper_cb ), Menuitem ( MT_FUNC, " 2dB/", menu_dBper_cb ), Menuitem ( MT_FUNC, " 5dB/", menu_dBper_cb ), Menuitem ( MT_FUNC, " 10dB/", menu_dBper_cb ), Menuitem ( MT_FUNC, " 20dB/", menu_dBper_cb ), Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; /* * Menu to set the gain overide register (REG_AGCOR 0x69) in the SI4432 transceiver. * See app note AN440 for the full explanation. * * Modified in Version 2.6 by WA2FZW: * * The original code had set values in a sub-menu. Now we use the keypad to set * any value one likes!. But here are rules! See the documentation for the * explanation. */ static Menuitem menu_dBgain[] = { Menuitem ( MT_FUNC, "AGC ON", menu_dBgain_cb ), //0x60 Menuitem ( MT_FUNC, "\2SET\0VALUE", menu_dBgain_cb ), //Set actual value Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_save[] = // Setting save options { Menuitem ( MT_FUNC, "SAVE 0", menu_save_cb ), // This works like the NanoVNA Menuitem ( MT_FUNC, "SAVE 1", menu_save_cb ), // where you an save a number of Menuitem ( MT_FUNC, "SAVE 2", menu_save_cb ), // different configurations Menuitem ( MT_FUNC, "SAVE 3", menu_save_cb ), Menuitem ( MT_FUNC, "SAVE 4", menu_save_cb ), Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_recall[] = // For recalling saved configurations { Menuitem ( MT_FUNC, "RECALL 0", menu_recall_cb ), Menuitem ( MT_FUNC, "RECALL 1", menu_recall_cb ), Menuitem ( MT_FUNC, "RECALL 2", menu_recall_cb ), Menuitem ( MT_FUNC, "RECALL 3", menu_recall_cb ), Menuitem ( MT_FUNC, "RECALL 4", menu_recall_cb ), Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_save_recall[] = // Save or recall options { Menuitem ( MT_MENU, "SAVE", menu_save ), // Show save options Menuitem ( MT_MENU, "RECALL", menu_recall ), // Show recall options Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; int menu_refer_value[] = { -1, 0, 1, 2, 3, 4, 5, 6 }; // Actual values for the register static Menuitem menu_refer2[] = // Part 2 of the reference values { Menuitem ( MT_FUNC, "3MHz", menu_refer_cb2 ), Menuitem ( MT_FUNC, "2MHz", menu_refer_cb2 ), Menuitem ( MT_FUNC, "1MHz", menu_refer_cb2 ), Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_refer[] = // GPIO2 reference frequency values { Menuitem ( MT_FUNC, "OFF", menu_refer_cb ), Menuitem ( MT_FUNC, "30MHz", menu_refer_cb ), Menuitem ( MT_FUNC, "15MHz", menu_refer_cb ), Menuitem ( MT_FUNC, "10MHz", menu_refer_cb ), Menuitem ( MT_FUNC, "4MHz", menu_refer_cb ), Menuitem ( MT_MENU, " MORE->", menu_refer2 ), Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_traces[] = // Toggle display traces on and off { Menuitem ( MT_FUNC, "\2dB\0On/Off", menu_trace_cb ), Menuitem ( MT_FUNC, "\2GAIN\0On/Off", menu_trace_cb ), Menuitem ( MT_MENU, "AVERAGE", menu_average ), // The sub menu allows values to be set Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_select_marker[] = { Menuitem ( MT_FUNC, "MKR #1", menu_marker_select_cb ), Menuitem ( MT_FUNC, "MKR #2", menu_marker_select_cb ), Menuitem ( MT_FUNC, "MKR #3", menu_marker_select_cb ), Menuitem ( MT_FUNC, "MKR #4", menu_marker_select_cb ), Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_marker_color[] = { Menuitem ( MT_FUNC, "WHITE", menu_marker_color_cb ), Menuitem ( MT_FUNC, "RED", menu_marker_color_cb ), Menuitem ( MT_FUNC, "BLUE", menu_marker_color_cb ), Menuitem ( MT_FUNC, "GREEN", menu_marker_color_cb ), Menuitem ( MT_FUNC, "YELLOW", menu_marker_color_cb ), Menuitem ( MT_FUNC, "ORANGE", menu_marker_color_cb ), Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_markers[] = { Menuitem ( MT_MENU, "\2SELECT\0MARKER", menu_select_marker ), Menuitem ( MT_MENU, "\2SELECT\0COLOR", menu_marker_color ), Menuitem ( MT_FUNC, "\2ENABLE/\0DISABLE", menu_markers_cb ), Menuitem ( MT_FUNC, "\2ENABLE\0ALL", menu_markers_cb ), Menuitem ( MT_FUNC, "\2DISABLE\0ALL", menu_markers_cb ), Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_display[] = // Display menu { Menuitem ( MT_MENU, "TRACES", menu_traces ), // Turn display traces on and off Menuitem ( MT_MENU, "\2PREAMP\0GAIN", menu_dBgain ), // Set preamp gain Menuitem ( MT_FUNC, "\2REF\0LEVEL", menu_scale_cb ), // Set top line of the grid Menuitem ( MT_MENU, "dB/DIV", menu_dBper ), // Menu to set vertical grid scale Menuitem ( MT_MENU, "\2SPUR\0REDUCTION", menu_spur ), // Enable or disable spur reduction Menuitem ( MT_FUNC, "\2DEFAULT\0SETTINGS",menu_autosettings_cb ), Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_sweep2[] = { Menuitem ( MT_MENU, "RBW", menu_rbw ), // Set the resolution bandwidth Menuitem ( MT_FUNC, "ATTEN", menu_atten_cb ), // Set the attenuation Menuitem ( MT_FUNC, "EXTERN", menu_extern_cb ), // Set the external gain Menuitem ( MT_MENU, "MARKERS", menu_markers ), // Marker sub menu Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_sweep[] = // This is the main "SWEEP" menu { Menuitem ( MT_FUNC, "\2SWEEP\0START", menu_sweep_cb ), Menuitem ( MT_FUNC, "\2SWEEP\0STOP", menu_sweep_cb ), Menuitem ( MT_FUNC, "\2SWEEP\0CENTER", menu_sweep_cb ), Menuitem ( MT_FUNC, "\2SWEEP\0SPAN", menu_sweep_cb ), Menuitem ( MT_FUNC, "\2FOCUS\0FREQ", menu_sweep_cb ), Menuitem ( MT_MENU, "MORE->", menu_sweep2 ), // Additional options above Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_output[] = // The "OUTPUT" menu { Menuitem ( MT_MENU, "REFERENCE", menu_refer ), // Select GPIO2 reference frequency Menuitem ( MT_FUNC, "GENERATOR", menu_generate_cb ), // Turn generator mode on or off #ifdef TG_IF_INSTALLED Menuitem ( MT_MENU, "TRACKING", menu_tracking ), // Tracking generator control #endif Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_touchscreen[] = { Menuitem ( MT_FUNC, "\2TOUCH\0CAL", menu_touch_cb ), // Calibrate touch screen Menuitem ( MT_FUNC, "\2TOUCH\0TEST", menu_touch_cb ), // Test touch screen calibration Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; /* * WA2FZW - Still to do - Add items to do the frequency calibrations. */ static Menuitem menu_calibrate[] = { Menuitem ( MT_FUNC, "TX FREQ", menu_tune_cb ), Menuitem ( MT_FUNC, "RX FREQ", menu_tune_cb ), Menuitem ( MT_FUNC, "\2ACTUAL\0POWER", menu_actualpower_cb ), // Calibrate power setting Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_config[] = // The "CONFIG" menu { Menuitem ( MT_MENU, "CALIBRATE", menu_calibrate ), // Calibration menu Menuitem ( MT_MENU, "\2TOUCH\0SCREEN", menu_touchscreen ), // Calibrate & test the touch screen Menuitem ( MT_FUNC, "\2IF\0FREQ", menu_IF_freq_cb ), // Set the IF frequency Menuitem ( MT_FUNC, "\2SAVE\0CONFIG", menu_save_config_cb ), // Save "config" structure Menuitem ( MT_FUNC, "VERSION", menu_version_cb ), // Display "About" information Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_sig_gen[] = // The "CONFIG" menu { Menuitem ( MT_MENU, "MODE", menu_mode ), Menuitem ( MT_FUNC, "\2SET\0FREQ", menu_sig_freq_cb ), // Set the output frequency Menuitem ( MT_FUNC, "\2SET\0dBm", menu_sig_level_cb ), // Set the output level Menuitem ( MT_FUNC, "\2CAL\0dBm", menu_sig_levCal_cb ), // Calibrate the sig gen output level Menuitem ( MT_END ) // End marker }; static Menuitem menu_IFsweep_top[] = // This is the main "IF_SWEEP" menu { Menuitem ( MT_MENU, "MODE", menu_mode ), Menuitem ( MT_FUNC, "\2SWEEP\0START", menu_IF_sweep_cb ), Menuitem ( MT_FUNC, "\2SWEEP\0STOP", menu_IF_sweep_cb ), Menuitem ( MT_FUNC, "\2SWEEP\0SIG", menu_IF_sweep_cb ), Menuitem ( MT_MENU, "REFERENCE", menu_refer ), // Select GPIO2 reference frequency Menuitem ( MT_END ) // End marker }; static Menuitem menu_RXsweep_top[] = // This is the main "IF_SWEEP" menu { Menuitem ( MT_MENU, "MODE", menu_mode ), Menuitem ( MT_FUNC, "\2SWEEP\0SPAN", menu_RX_sweep_cb ), Menuitem ( MT_FUNC, "\2SWEEP\0SIG", menu_RX_sweep_cb ), Menuitem ( MT_FUNC, "RBW", menu_RX_sweep_cb ), Menuitem ( MT_MENU, "REFERENCE", menu_refer ), // Select GPIO2 reference frequency Menuitem ( MT_FUNC, "\2BPF\0CAL", menu_bpfCal_cb ), Menuitem ( MT_END ) // End marker }; static Menuitem menu_BandscopeSpan[] = // Badscope Span settings { Menuitem ( MT_FUNC, "200kHz", menu_BandscopeSpan_cb ), Menuitem ( MT_FUNC, "400kHz", menu_BandscopeSpan_cb ), Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_BandscopeRBW[] = // Resolution bandwidth settings { Menuitem ( MT_FUNC, "2.6kHz", menu_BandscopeRbw_cb ), // In auto mode, there are many Menuitem ( MT_FUNC, "2.8kHz", menu_BandscopeRbw_cb ), // more available settings that Menuitem ( MT_FUNC, "3.1kHz", menu_BandscopeRbw_cb ), // are roughly the sweep range Menuitem ( MT_FUNC, "3.7kHz", menu_BandscopeRbw_cb ), // divided by 300. Menuitem ( MT_FUNC, "4.2kHz", menu_BandscopeRbw_cb ), Menuitem ( MT_FUNC, "5.4kHz", menu_BandscopeRbw_cb ), Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; static Menuitem menu_Bandscope_top[] = // This is the main "IF_SWEEP" menu { Menuitem ( MT_MENU, "MODE", menu_mode ), Menuitem ( MT_FUNC, "\2SWEEP\0START", menu_BandscopeStart_cb ), Menuitem ( MT_MENU, "\2SWEEP\0SPAN", menu_BandscopeSpan ), Menuitem ( MT_MENU, "RBW", menu_BandscopeRBW ), Menuitem ( MT_FUNC, "\2REF\0LEVEL", menu_BandscopeLevel_cb ), // Set top line of the grid Menuitem ( MT_FUNC, "\2W'FALL\0MIN", menu_WaterfallMin_cb ), Menuitem ( MT_FUNC, "\2W'FALL\0GAIN", menu_WaterfallGain_cb ), Menuitem ( MT_END ) // End marker }; /* * And last but not least the main menu! */ static Menuitem menu_top[] = { Menuitem ( MT_MENU, "MODE", menu_mode ), Menuitem ( MT_MENU, "SWEEP", menu_sweep ), Menuitem ( MT_MENU, "DISPLAY", menu_display ), Menuitem ( MT_MENU, "STORAGE", menu_storage ), Menuitem ( MT_MENU, "OUTPUT", menu_output ), Menuitem ( MT_MENU, "\2SAVE/\0RECALL", menu_save_recall ), Menuitem ( MT_MENU, "CONFIG", menu_config ), Menuitem ( MT_END ) // End marker }; static uint8_t menu_current_level = 0; // Current level (MAIN) static Menuitem *menu_stack[MENU_STACK_DEPTH] = // Initialize the stack { menu_top, // Main menu is on top NULL, // These get set as we NULL, // go along NULL }; /* * Some definitions and more variables associated with the menu handling: */ #define OFFSETX 15 // "X" offset (from what?) #define OFFSETY 0 // "Y" offset /* * Menu handling functions: */ static int touch_check ( void ); static void touch_wait_release ( void ) // Wait for touch screen release { int tstatus; do { tstatus = touch_check (); // Check the touch screen status } while ( tstatus != EVT_TOUCH_RELEASED ); // Loop until released } static int touch_status ( void ) // Get the touch screen status { return ( tft.getTouchRawZ() > TS_MINIMUM_Z ); // "RawZ" is a measure of touch pressure } #define RAWERR 20 // Deadband error allowed in successive position samples /* * "validTouch" - Added by M0WID in Version 05: * * Adds a lot of checking to make sure that the user actually intended to touch * something as opposed to a shaky-hand touch! * * WA2FZW - Why use the un-calibrated touch values here and the convert them * to calibrated values in "getTouch"? */ uint8_t validTouch ( uint16_t *x, uint16_t *y, uint16_t threshold ) { uint16_t x_tmp, y_tmp; // "X" and "Y coordinates static uint16_t x_tmp2, y_tmp2; // Second "X" and "Y coordinates /* * Wait until pressure stops increasing to debounce pressure, but only? */ uint16_t z1 = 1; uint16_t z2 = 0; while ( z1 > z2 ) { z2 = z1; // Old pressure = last pressure z1 = tft.getTouchRawZ (); // Read new pressure if ( z1 <= threshold ) // return immediately return false; } // Serial.print ( "Z = " ); Serial.println ( z1 ); // Debugging if ( z1 <= threshold ) // If pressure less than the specified threshold return false; // It's not a valid touch // x_tmp2 = x_tmp; // Save values from last scan // y_tmp2 = y_tmp; tft.getTouchRaw ( &x_tmp, &y_tmp ); // Get touch coordinates // Serial.print ( "Sample 1 x,y = " ); // Debugging // Serial.print ( x_tmp ); // Serial.print ( "," ); // Serial.print ( y_tmp ); // Serial.print ( "Z = " ); // Serial.println ( z1 ); delayMicroseconds ( 1000 ); // Small delay to the next sample if ( tft.getTouchRawZ() <= threshold ) // Threshold not met? return false; // Then not a valid touch delayMicroseconds ( 1000 ); // Small delay to the next sample tft.getTouchRaw ( &x_tmp2, &y_tmp2 ); // Get 2nd set of coordinates // Serial.print ( "Sample 2 x,y = " ); // More debugging // Serial.print ( x_tmp2 ); // Serial.print ( ", " ); // Serial.println ( y_tmp2 ); // Serial.print ( "Sample difference = " ); // Serial.print ( abs ( x_tmp - x_tmp2 )); // Serial.print ( "," ); // Serial.println ( abs (y_tmp - y_tmp2 )); if ( abs ( x_tmp - x_tmp2 ) > RAWERR ) // Error limit exceeded? return false; // Then invalid touch if ( abs (y_tmp - y_tmp2 ) > RAWERR ) // Same check for "Y" values return false; *x = x_tmp; // Set good *y = y_tmp; // Coordinates return true; // And indicate valid touch } /* * "getTouch" - Added by M0WID in Version 05: * * Replaces the TFT_eSPI library function. Returns false if not a valid touch. * If it is a valid touch, the calibrated x and y values returned in the * arguments. */ #define RELEASE_COUNT 10 #define PRESS_COUNT 3 uint8_t getTouch ( uint16_t *x, uint16_t *y ) { uint16_t x_tmp, y_tmp; // Temporary touch coordinates static unsigned long pressTime; static int lastState; static uint16_t threshold = TS_MINIMUM_Z; uint8_t n; if ( lastState ) { threshold = 50; // Change threshold limit if valid to provide hysteresis n = RELEASE_COUNT; // Need this no of not pressed read results to return not pressed } else { threshold = TS_MINIMUM_Z; // Higher limit to indicate pressed n = PRESS_COUNT; // Lower number of counts needed } int valid = 0; // Non zero if valid touch seen while ( n-- ) { if ( validTouch ( &x_tmp, &y_tmp, threshold )) valid++; // WA2FZW - Removed extra semicolon else valid--; // M0WID - add check that it is really released } // Serial.printf ( "last %i valid %i\n", lastState, valid ); if (( lastState && (valid <= -RELEASE_COUNT+1)) || (!lastState && (valid < PRESS_COUNT-1))) // Not a valid touch { // pressTime = 0; // Time = '0' lastState = false; return false; // Indicate invalid touch } /* * If we get this far touch is valid */ // pressTime = millis() + 50; // Debounce? tft.convertRawXY ( &x_tmp, &y_tmp ); // Calibrate the readings if ( x_tmp >= tft.width () || y_tmp >= tft.height ()) // Off the chart? { lastState = false; return false; // Then bad touch } *x = x_tmp; // Set return values *y = y_tmp; lastState = true; return true; // Indicate touch is valid } static int touch_check ( void ) // Check for TS touched { uint16_t x = 0; // Used to store the "X" and uint16_t y = 0; // "Y" coordinates int stat = getTouch ( &x, &y ); // Read the touch screen // Serial.printf ( "TouchCheck stat=%i, x=%i, y=%i \n", stat, x, y ); // Debugging if ( stat ) // Valid touch if non-zero { last_touch_x = x; // Save coordinates for ??? last_touch_y = y; } if ( stat != last_touch_status ) // Did the status change? { last_touch_status = stat; // Yes make new status old if ( stat ) // If non-zero screen was touched return EVT_TOUCH_PRESSED; // Indicate that fact else return EVT_TOUCH_RELEASED; } else // Status didn't change { if ( stat ) // If non-zero return EVT_TOUCH_DOWN; // Must still be touched else // If "stat" is zero return EVT_TOUCH_NONE; // Nothing happening! } } /* * "DrawString" - Paint a character in a specified size and color: * * "t" The character * "x & y" Coordinates * "tc" Text color * "size" Font size * * Modified in Version 2.2 by WA2FZW: * * Changed the name from "DrawStringSize" to "DrawString" and eliminated the * "DrawString_5x7" function and replaced all calls to that with calls to this one. * * Eliminated the "bc" (background color) argument, so everything displays with * a transparent background. */ void DrawString ( const char *t, int x, int y, int tc, int size ) { //Serial.println ( t ); // Debugging tft.setCursor ( x, y ); // Set location tft.setTextColor ( tc ); // Set text color tft.setTextSize ( size ); // and font size tft.print ( t ); // Paint the character } /* * "touch_cal_exec" - Runs the calibration procedure built into the "TFT_eSPI" * library. */ void touch_cal_exec ( void ) { TouchCalibrate (); // Function is in "tinySA.cpp" } /* * "touch_position" returns the calibrated x/y value corrected for screen rotation setting * when using the "ESP_eSPI" library. */ void touch_position ( int *x, int *y ) { *x = last_touch_x; *y = last_touch_y; // Serial.print ( "TP = " ); // Serial.print ( *x ); // Serial.print ( ", " ); // Serial.println ( *y ); } /* * "touch_draw_test" lets you scribble on the display */ void touch_draw_test ( void ) { int status; // Touch screen status int x0, y0; // Point '0' int x1, y1; // Point '1' tft.fillRect ( 0, 0, 320, 240, 0 ); // Fade to black DrawString ( "TOUCH TEST: DRAG PANEL", OFFSETX, 233, WHITE, 1 ); do { status = touch_check(); } while ( status != EVT_TOUCH_PRESSED ); touch_position ( &x0, &y0 ); do { status = touch_check (); touch_position ( &x1, &y1 ); tft.drawLine ( x0, y0, x1, y1, WHITE ); x0 = x1; y0 = y1; } while ( status != EVT_TOUCH_RELEASED ); } void ShowVersion ( void ) { char ipBuff[60]; // To format IP address ShowSplash (); // Like "Help - About" tft.setTextDatum ( TC_DATUM ); // Top center text position datum sprintf ( ipBuff, "IP address %s", FormatIPAddress ( ipAddress )); tft.drawString ( ipBuff, 160, 140 ); tft.setTextDatum ( TL_DATUM ); // Back to default top left while ( true ) { if ( touch_check () == EVT_TOUCH_PRESSED ) // Wait for screen to be touched break; // And bail out } } /* * "ShowSplash" displays a screen like a "Help - About" screen */ void ShowSplash ( void ) { ClearDisplay (); // Fade to black! tft.setTextDatum ( TC_DATUM ); // Top center text position datum tft.setTextColor ( MAGENTA ); tft.setFreeFont ( &FreeSerifBoldItalic18pt7b ); // Select Free Serif 9 point font tft.drawString ( "simpleSA for ESP32", 160, 20 ); tft.setTextColor ( WHITE ); tft.setFreeFont ( &FreeSansBold9pt7b ); // Select Free Serif 9 point font tft.drawString ( "By WA2FZW, M0WID,", 160, 60 ); tft.drawString ( "VK3PE and G3ZQC", 160, 80 ); tft.drawString ( PROGRAM_VERSION, 160, 100 ); tft.drawString ( "Original tinySA by Erik (PD0EK)", 160, 120 ); tft.setTextDatum ( TL_DATUM ); // Back to default top left tft.setCursor ( 0, 120 ); // Position cursor for any more messages tft.setFreeFont ( NULL ); // Select default font } void request_to_redraw_grid() // Clear the display and update { ClearDisplay (); changedSetting = true; menuExit(); // Handle the return from the menu // RedrawHisto (); } void draw_frequencies() {} // Do nothing /* * "menu_mode_cb" - handles the process of setting the operating mode */ void menu_mode_cb ( int item ) { switch ( item ) { case 0: // Set Sweep low range setMode(SA_LOW_RANGE); ui_mode_normal (); // No menu displayed break; case 1: // Set Signal Generator setMode(SIG_GEN_LOW_RANGE); ui_mode_normal (); // No menu displayed break; case 2: // Set IF Sweep mode setMode(BANDSCOPE); ui_mode_normal (); // No menu displayed break; case 3: // Set IF Sweep mode setMode(IF_SWEEP); ui_mode_normal (); // No menu displayed break; case 4: // Set RX Sweep mode setMode(RX_SWEEP); ui_mode_normal (); // No menu displayed break; case 5: setMode(OTA_UPDATE); // Set OTA mode ui_mode_normal(); break; } changedSetting = true; } /* * "menu_save_config_cb" - Saves the "config" structure (for use after calibration) */ void menu_save_config_cb ( int item ) { WriteConfig (); // Simple enough! menu_move_back(); ui_mode_normal(); } /* * "menu_tune_cb" - handles the process of calibrating frequencies of the Si4432 * modules. See the documentation for an explanation of how to perform the calibration. */ void menu_tune_cb ( int item ) { switch ( item ) { case 0: // Calibrate TX VFO = TX_4432; // Select transmitter module break; case 1: // Calibrate RX VFO = RX_4432; // Select receiver module break; } int km = KM_TUNE; ui_mode_keypad ( km ); ui_process_keypad (); } void menu_marker_color_cb ( int item ) { char colors[6] = { 'W', 'R', 'B', 'G', 'Y', 'O' }; UpdateMarker ( selectedMarker, colors[item] ); draw_menu (); menu_move_back(); } void menu_marker_select_cb ( int item ) { selectedMarker = item; draw_menu (); menu_move_back(); } /* * "menu_markers_cb" - Turn markers on or off */ void menu_markers_cb ( int item ) { switch ( item ) { case 0: // Now handled by "menu_marker_select_cb" break; // So do othing case 2: // Toggle the currently selected marker marker[selectedMarker].Toggle(); draw_menu (); break; case 3: // Turn them all on for ( int i = 0; i < MARKER_COUNT; i++ ) marker[i].Enable(); ui_mode_normal(); break; case 4: // Turn them all off for ( int i = 0; i < MARKER_COUNT; i++ ) marker[i].Disable(); ui_mode_normal(); break; } for ( int i = 0; i < MARKER_COUNT; i++ ) setting.MkrStatus[i] = marker[i].Status (); WriteSettings (); } /* * "menu_IF_freq_cb" - Handles setting the IF Frequency */ void menu_IF_freq_cb ( int item ) { int km = KM_IFFREQ; ui_mode_keypad ( km ); ui_process_keypad (); } /* * "menu_sig_freq_cb" - Handles setting the signal generator Frequency */ void menu_sig_freq_cb ( int item ) { int km = KM_SGFREQ; tft.fillScreen ( bg ); ui_mode_keypad ( km ); ui_process_keypad (); } /* * "menu_sig_level_cb" - Handles setting the signal generator output level */ void menu_sig_level_cb ( int item ) { int km = KM_SGLEVEL; tft.fillScreen ( bg ); ui_mode_keypad ( km ); ui_process_keypad (); } /* * "menu_sig_levCal_cb" - Handles setting the signal generator max level (no attenuation) */ void menu_sig_levCal_cb ( int item ) { int km = KM_SGLEVCAL; tft.fillScreen ( bg ); ui_mode_keypad ( km ); ui_process_keypad (); } /* * "menu_tg_offset_cb" - Handles setting the tracking generator offset from SA IF */ void menu_tg_offset_cb ( int item ) { int km = KM_TGOFFSET; tft.fillScreen ( bg ); ui_mode_keypad ( km ); ui_process_keypad (); } /* * "menu_tg_frequency_cb" - Handles setting the tracking generator frequency when in sig gen mode */ void menu_tg_frequency_cb ( int item ) { int km = KM_TGFREQ; tft.fillScreen ( bg ); ui_mode_keypad ( km ); ui_process_keypad (); } /* * "menu_tgIF_drive_cb" - Handles setting the tracking generator IF drive */ void menu_tgIF_drive_cb ( int item ) { int km = KM_TGIF_DRIVE; tft.fillScreen ( bg ); ui_mode_keypad ( km ); ui_process_keypad (); } /* * "menu_tgLo_drive_cb" - Handles setting the tracking generator LO drive */ void menu_tgLO_drive_cb ( int item ) { int km = KM_TGLO_DRIVE; tft.fillScreen ( bg ); ui_mode_keypad ( km ); ui_process_keypad (); } /* * "menu_generate_cb" - M0WID Version 05; replaces the function "menu_output_cb" * from previous versions. */ static void menu_generate_cb ( int item ) { SetGenerate ( 1 ); // Item 0 is off, 1 is on // selection = item; // draw_menu (); // menu_move_back(); ui_mode_normal (); // No menu displayed } /* * menu_tracking_cb handles settings for the tracking generator */ static void menu_tracking_cb (int item ) { switch ( item ) { case 0: // Turn Off SetTracking (0); ui_mode_normal (); // Back to sweep break; case 1: // Turn On SetTracking (1); ui_mode_normal (); // Back to sweep break; case 2: // Turn Sig gen mode SetTracking (2); ui_mode_normal (); // Back to sweep break; } } /* * "menu_autosettings_cb" sets defaults then clears the menu * from the display. * * Modified in Version 2.5 by WA2FZW: * * Added definitions in "My_SA.h" to allow the user to customize the "AUTO" settings. */ static void menu_autosettings_cb ( int item ) { SetSweepStart ( AUTO_SWEEP_START ); // Scan limits SetSweepStop ( AUTO_SWEEP_STOP ); SetPowerGrid ( AUTO_PWR_GRID ); SetPreampGain ( AUTO_LNA ); SetRefLevel ( AUTO_REF_LEVEL ); SetRefOutput ( AUTO_REF_OUTPUT ); SetAttenuation ( AUTO_ATTEN ); SetRBW ( AUTO_RBW ); // Auto mode SetRX ( 0 ); setting.ShowGain = 1; // Gain trace on setting.ShowSweep = 1; // Main sweep trace on menu_move_back (); ui_mode_normal (); // No menu displayed } /* * "menu_touch_cb" is the dispatcher for part of the "TOUCH SCREEN" menu */ static void menu_touch_cb ( int item ) { switch ( item ) { case 0: // Touch calibrate touch_cal_exec (); request_to_redraw_grid (); draw_menu (); break; case 1: // Touch Draw test touch_draw_test (); request_to_redraw_grid (); draw_menu (); break; } } /* * "menu_version_cb" handles displaying the "About" screen */ static void menu_version_cb ( int item ) { ShowVersion (); request_to_redraw_grid (); draw_menu (); } /* * "menu_save_cb" saves the current "setting" structure in a named location in the * flash memory. The "preferences Save" function adds the "item" number to the string * "Save" to create the saved name. */ static void menu_save_cb ( int item ) { if ( item < 0 || item > 4 ) // Legal "item" number? return; // Nope Save ( item ); menu_move_back(); ui_mode_normal(); } /* * "menu_recall_cb" - Works like "menu_save_cb" in reverse */ static void menu_recall_cb ( int item ) { if ( item < 0 || item > 4 ) // Test for illegal "item" return; Recall ( item ); menu_move_back(); ui_mode_normal(); } /* * "menu_refer_cb" - Set reference level from 1st page of the "REFERENCE" menu */ static void menu_refer_cb ( int item ) { // Serial.println ( item ); // Debugging SetRefOutput ( menu_refer_value[item] ); menu_move_back(); ui_mode_normal(); } /* * "menu_refer_cb2" - Set reference output frequency from 2nd page * of the "REFERENCE" menu */ static void menu_refer_cb2 ( int item ) { // Serial.println ( item ); // Debugging SetRefOutput ( menu_refer_value[item+5] ); // Items 0 - 4 are on page 1 menu_move_back (); ui_mode_normal (); } /* * "menu_spur_cb" - Handles the "Spur Reduction" menu item. */ static void menu_spur_cb ( int item ) { SetSpur ( item ); menu_move_back (); ui_mode_normal (); } /* * "menu_storage_cb" - Handles the "STORAGE" menu item. */ static void menu_storage_cb ( int item ) { switch ( item ) { case 0: // "STORE" SetStorage (); break; case 1: SetClearStorage (); // "CLEAR" break; case 2: // "SUBTRACT" - What does this mean? SetSubtractStorage (); break; } menu_move_back (); ui_mode_normal (); } /* * "menu_average_cb" - Handles the "AVERAGE" menu item. */ static void menu_average_cb ( int item ) { SetAverage ( item ); menu_move_back (); ui_mode_normal (); } /* * "menu_rbw_cb" - Handles the "RBW" menu item. * * WA2FZW - Moved the "rbwsel" choices into the function; they were previously * defined in global space. */ static void menu_rbw_cb ( int item ) { const int rbwsel[] = { 0, 31, 106, 322, 1121, 3355 }; // Resolution bandwidth choices (in KHz * 10) SetRBW ( rbwsel[item] ); menu_move_back (); ui_mode_normal (); } /* * "menu_dBper_cb" - Handles the "SCALE/DIV" menu item */ static void menu_dBper_cb ( int item ) { int menu_dBper_value[] = { 1, 2, 5, 10, 20 }; // Scale setting values (dB/division) SetPowerGrid ( menu_dBper_value[item] ); menu_move_back (); ui_mode_normal (); } /* * "menu_dBgain_cb" - Handles the "PREAMP GAIN" menu item. */ static void menu_dBgain_cb ( int item ) { if ( item == 0 ) // Turn the AGC on { SetPreampGain ( 0x60 ); // ACG on value menu_move_back (); ui_mode_normal (); } else // Get value from the keypad { int km = KM_PREAMP; ui_mode_keypad ( km ); ui_process_keypad (); } } /* * "choose_active_trace" - Holdover from NanoVNA but left in as it might be used * in the future in the TinySA. */ static void choose_active_trace ( void ) { int i; if ( trace[uistat.current_trace].enabled ) // If true return; // Do nothing for ( i = 0; i < TRACE_COUNT ; i++ ) // "TRACE_COUNT" is set to '1' somewhere if ( trace[i].enabled ) // Find first "enabled: trace { uistat.current_trace = i; // Save the index return; } } /* * Repurposed in Version 2.6 by WA2FZW: * * Now toggles the main sweep trace or the gain trace on or off. * * * Like so many other of these functions "item" is the position of the item in the * menu; this has to be fixed! * * Here, item '0' is the main sweep trace and item '1' is the gain trace. */ static void menu_trace_cb ( int item ) { switch ( item ) { case 0: // Toggle the main sweep setting.ShowSweep = !setting.ShowSweep; break; case 1: // Toggle the gain trace setting.ShowGain = !setting.ShowGain; break; } WriteSettings (); menu_move_back (); ui_mode_normal (); } static void menu_atten_cb ( int item ) { int km = KM_ATTENUATION; ui_mode_keypad ( km ); ui_process_keypad (); } /* * "menu_scale_cb" - Handles the "REF LEVEL" menu item. */ static void menu_scale_cb ( int item ) { int km = KM_REFPOS; ui_mode_keypad ( km ); ui_process_keypad (); } /* * "menu_actualpower_cb" - Handles the "ACTUAL POWER" menu item. */ static void menu_actualpower_cb ( int item ) { int km = KM_ACTUALPOWER; ui_mode_keypad ( km ); ui_process_keypad (); } /* * "menu_sweep_cb" - Handles the menu items under the "SCAN" menu. * * Modified in Version 2.5 by WA2FZW: * * Eliminated "case 5" which was the handler for "Pause Sweep". That menu * choice really didn't do anything, so it was eliminated altogether. I left * the switch intact should we wish to add something to the "Scan" menu list. * * * Modified in Version 2.9 by WA2FZW: * * Totally eliminated the "switch" as all the menu items that set frequencies * are handled the same. */ static void menu_sweep_cb ( int item ) { ui_mode_keypad ( item ); ui_process_keypad (); } static void menu_IF_sweep_cb ( int item ) { ui_mode_keypad ( item + KM_IFSTART - 1 ); // item = 1 -> KM_IFSTART, 2 -> KM_IFSTOP, 3-> KM_IFSIG ui_process_keypad (); } static void menu_RX_sweep_cb ( int item ) { ui_mode_keypad ( item + KM_RXSPAN - 1 ); // item = 1 -> KM_RXSPAN, 2 -> KM_RXSIG ui_process_keypad (); } static void menu_bpfCal_cb ( int item ) { StartBpfCal (); ui_mode_normal(); } static void menu_extern_cb ( int item ) { int km = KM_EXTERN; ui_mode_keypad ( km ); ui_process_keypad (); } /* * ********************************************* * BANDSCOPE MENU ITEMS * ********************************************* */ /* * Handles the Bandscope RBW menu item. * */ static void menu_BandscopeRbw_cb ( int item ) { const int rbwsel[] = { 0, 26, 28, 31, 37 , 42, 54 }; // Resolution bandwidth choices (in KHz * 10) SetBandscopeRBW ( rbwsel[item] ); menu_move_back (); ui_mode_normal (); } /* * Set the span for the bandscope */ static void menu_BandscopeSpan_cb ( int item ) { switch ( item ) { case 0: // 200kHz setting.BandscopeSpan = 200000; break; case 1: // 400kHz setting.BandscopeSpan = 400000; break; } WriteSettings (); menu_move_back (); ui_mode_normal (); } /* * Set the level of the top of the bandscope scale */ static void menu_BandscopeLevel_cb ( int item ) { ui_mode_keypad ( KM_BANDSCOPELEVEL ); ui_process_keypad (); } /* * Set the min for the bandscope waterfall colours */ static void menu_WaterfallMin_cb ( int item ) { ui_mode_keypad ( KM_WFMIN ); ui_process_keypad (); } /* * Set the gain for the bandscope waterfall colours */ static void menu_WaterfallGain_cb ( int item ) { ui_mode_keypad ( KM_WFGAIN ); ui_process_keypad (); } /* * Set the start frequency of the bandscope sweep */ static void menu_BandscopeStart_cb ( int item ) { ui_mode_keypad ( KM_BANDSCOPESTART ); ui_process_keypad (); } /* * "ensure_selection" - Validates that a menu selection is valid */ static void ensure_selection ( void ) { Menuitem *menu = menu_stack[menu_current_level]; int i; for ( i = 0; menu[i].Type() != MT_END; i++ ) {} // Drops out when the "END" is found if ( selection >= i ) selection = i - 1; } /* * "menu_move_back" - Go back to the previous menu level */ static void menu_move_back ( void ) { if ( menu_current_level == 0 ) return; // Serial.print ( "Poplevel=" ); // Serial.println ( menu_current_level ); menu_current_level--; ensure_selection (); // Make sure valid erase_menu_buttons (); // Erase old buttons draw_menu (); // Paint new ones } /* * "menu_push_submenu" - Paint a new sub-menu */ static void menu_push_submenu ( Menuitem *submenu ) { // Serial.print( "Pushlevel=" ); // Serial.println ( menu_current_level ); if ( menu_current_level < MENU_STACK_DEPTH - 1 ) menu_current_level++; menu_stack[menu_current_level] = submenu; ensure_selection (); erase_menu_buttons (); draw_menu (); } /* * "menu_invoke" - */ static void menu_invoke ( int item ) { Menuitem *menu = menu_stack[menu_current_level]; menu = &menu[item]; // Serial.print ( "Invoke=" ); // Serial.print ( item ); // Serial.print ( ", type=" ); // Serial.print ( menu->type ); // Serial.print ( ", label= " ); // Serial.println ( menu->label ); switch ( menu->Type() ) { case MT_END: ui_mode_normal (); // Clear menu display break; case MT_BACK: // Go up one level menu_move_back (); break; case MT_FUNC: // Call function to process selection { menu->Call ( item ); break; } case MT_MENU: // Display a sub-menu menu_push_submenu ( menu->GetSubmenu () ); break; } } /* * All the following stuff has to do with the keypad. The first two definitions * are the 'x' and 'y' coordinates of the entire keypad. The "KP_X" and "KP_Y" * macros set the 'x' and 'y' pixel coordinates for the key based on its row * and column in the keypad; pretty clever! */ #define KP_X(x) ( 48 * ( x ) + 2 + ( 320 - 64 - 192 )) #define KP_Y(y) ( 48 * ( y ) + 2 ) typedef struct { uint16_t x, y; // X and Y coordinates of a key int8_t c; // Index to the "kpf" array of characters? } keypads_t; static const keypads_t *keypads; // Pointer to array of keypads static uint8_t keypads_last_index; // Index into the array /* * There are different keypads for different things. * * The numbers in the "KP_X(n)" and "KP_Y(n) macro calls are the X and Y positions * of the keys the columns (X) are numbered from left to right as 0 to 3 and the * rows (Y) are numbered from top to bottom as 0 to 3. * * WA2FZW - Some space can be saved by creating one generic keypad with the digits * and decimal point and then individual supplemental ones for the special keys on * eash of the following. * * The first one is for entering frequencies. */ static const keypads_t keypads_freq[] = // Keypad array for frequencies { { KP_X(1), KP_Y(3), KN_PERIOD }, // Decimal point { KP_X(0), KP_Y(3), 0 }, // '0' digit { KP_X(0), KP_Y(2), 1 }, // '1' digit { KP_X(1), KP_Y(2), 2 }, { KP_X(2), KP_Y(2), 3 }, { KP_X(0), KP_Y(1), 4 }, { KP_X(1), KP_Y(1), 5 }, { KP_X(2), KP_Y(1), 6 }, { KP_X(0), KP_Y(0), 7 }, { KP_X(1), KP_Y(0), 8 }, { KP_X(2), KP_Y(0), 9 }, { KP_X(3), KP_Y(1), KN_M }, // Megahertz { KP_X(3), KP_Y(2), KN_K }, // Kilohertz { KP_X(3), KP_Y(3), KN_X }, // Enter { KP_X(2), KP_Y(3), KN_BS }, // Backspace { 0, 0, -1 } // Array end marker }; static const keypads_t keypads_signed_freq[] = // Keypad array for signed frequencies { { KP_X(1), KP_Y(3), KN_PERIOD }, // Decimal point { KP_X(0), KP_Y(3), 0 }, // '0' digit { KP_X(0), KP_Y(2), 1 }, // '1' digit { KP_X(1), KP_Y(2), 2 }, { KP_X(2), KP_Y(2), 3 }, { KP_X(0), KP_Y(1), 4 }, { KP_X(1), KP_Y(1), 5 }, { KP_X(2), KP_Y(1), 6 }, { KP_X(0), KP_Y(0), 7 }, { KP_X(1), KP_Y(0), 8 }, { KP_X(2), KP_Y(0), 9 }, { KP_X(3), KP_Y(0), KN_MINUS }, { KP_X(3), KP_Y(1), KN_M }, // Megahertz { KP_X(3), KP_Y(2), KN_K }, // Kilohertz { KP_X(3), KP_Y(3), KN_X }, // Enter { KP_X(2), KP_Y(3), KN_BS }, // Backspace { 0, 0, -1 } // Array end marker }; static const keypads_t keypads_integer[] = // Keypad array for integers (no decimal point) { { KP_X(1), KP_Y(3), 0 }, { KP_X(1), KP_Y(2), 1 }, { KP_X(2), KP_Y(2), 2 }, { KP_X(3), KP_Y(2), 3 }, { KP_X(1), KP_Y(1), 4 }, { KP_X(2), KP_Y(1), 5 }, { KP_X(3), KP_Y(1), 6 }, { KP_X(1), KP_Y(0), 7 }, { KP_X(2), KP_Y(0), 8 }, { KP_X(3), KP_Y(0), 9 }, { KP_X(3), KP_Y(3), KN_X }, { KP_X(2), KP_Y(3), KN_BS }, { 0, 0, -1 } }; static const keypads_t keypads_level[] = // Used for "ACTUAL POWER" input { { KP_X(1), KP_Y(3), KN_PERIOD }, { KP_X(0), KP_Y(3), 0 }, { KP_X(0), KP_Y(2), 1 }, { KP_X(1), KP_Y(2), 2 }, { KP_X(2), KP_Y(2), 3 }, { KP_X(0), KP_Y(1), 4 }, { KP_X(1), KP_Y(1), 5 }, { KP_X(2), KP_Y(1), 6 }, { KP_X(0), KP_Y(0), 7 }, { KP_X(1), KP_Y(0), 8 }, { KP_X(2), KP_Y(0), 9 }, { KP_X(3), KP_Y(2), KN_MINUS }, { KP_X(3), KP_Y(3), KN_X }, { KP_X(2), KP_Y(3), KN_BS }, { 0, 0, -1 } }; /* * This array is indexed by the enum at the top of the file that defines the values for * "KM_START", KM_STOP, etc. */ static const keypads_t * const keypads_mode_tbl[] = { keypads_freq, // KM_START..........Sweep start frequency keypads_freq, // KM_STOP...........Sweep stop frequency keypads_freq, // KM_CENTER.........Sweep center frequency keypads_freq, // KM_SPAN...........Sweep frequency span keypads_freq, // KM_FOCUS..........Focus Frequency keypads_level, // KM_REFPOS.........Grid reference level (top line) keypads_integer, // KM_ATTENUATION....Attenuation keypads_level, // KM_ACTUALPOWER....Power level calibration keypads_freq, // KM_IFFREQ.........IF frequency keypads_integer, // KM_PREAMP.........Preamp gain keypads_integer, // KM_TUNE...........Transceiver crystal load keypads_freq, // KM_SGFREQ.........Sig Gen frequency keypads_level, // KM_SGLEVEL........Sig Gen Power level keypads_level, // KM_SGLEVCAL.......Power level calibration keypads_freq, // KM_IFSTART........IF Sweep start frequency keypads_freq, // KM_IFSTOP.........IF Sweep stop frequency keypads_freq, // KM_IFSIG..........IF Sweep signal frequency keypads_signed_freq, // KM_TGOFFSET.......Offset Frequency of TG IF compared to SA IF keypads_freq, // KM_TGFREQ.........Frequency of TG in sig gen mode keypads_level, // KM_TGLO_DRIVE.....Tracking generator LO drive keypads_level, // KM_TGIF_DRIVE.....Tracking generator IF drive keypads_freq, // KM_BANDSCOPESTART.IF Sweep start frequency keypads_integer, // KM_WFMIN..........Waterfall min level (RSSI) keypads_level, // KM_WFGAIN.........Waterfall Gain keypads_level, // KM_BANDSCOPELEVEL.Grid reference level keypads_freq, // KM_RXSPAN.........RX Sweep span frequency keypads_freq, // KM_RXSIG..........RX Sweep signal frequency keypads_freq, // KM_RBW............RX Sweep RBW keypads_level // KM_EXTERN.........External gain }; /* * "draw_keypad" - Seems pretty self-explanatory! * * Modified in Version 2.7 by WA2FZW: * * Instead of simply painting reqular characters from the (no longer existing) * "kpf" array, the characters used in the keypad keys and in the number box are * now painted using bitmaps in the "KP_Font" array. * * The "KeyNumber" function replaces the "DrawFont" function to paint the bitmaps. */ static void draw_keypad ( void ) { int i = 0; while ( keypads[i].x ) // Non zero "X" location { uint16_t bg = config.menu_normal_color; // Color when key is not selected if ( i == selection ) bg = config.menu_active_color; // Color when a key is selected tft.fillRect ( keypads[i].x, keypads[i].y, 44, 44, bg ); // Set background color KeyNumber ( keypads[i].c, keypads[i].x+12, keypads[i].y+10 ); // Paint the character i++; // Next } } /* /* * "KeyNumber" uses the "KP_Font" array to draw a number in a keypad key. */ static void KeyNumber ( int8_t key, int x, int y ) { uint16_t bits; // Holds the bitmap for one line uint16_t* font = &KP_Font[key * 22]; // Calculate pointer to desired bitmap for ( int line = 0; line < 22; line++ ) // Do line by line; top to bottom { bits = font[line]; // Get the bit pattern for a line for ( int column = 0; column < 16; column++ ) // horizontally left to right { if ( bits & 0x8000 ) // Is the next pixel turned on? tft.drawPixel ( column+x, line+y, BLACK ); // Yes, always black bits <<= 1; // Shift the bitmap byte one place left } } // Next line } /* * "draw_numeric_area_frame" - Draws the numerical box uncer the keypad */ static void draw_numeric_area_frame ( void ) { tft.fillRect ( 0, 208, 256, 32, WHITE ); // White background DrawString ( keypad_mode_label[keypad_mode], 5, 220, BLACK, 1 ); tft.setCursor(5,230); tft.printf("%.0f",uistat.previous_value); } /* * "draw_numeric_input" - Puts numbers in the numerical entry box * * Modified in Version 2.7 by WA2FZW: * * Instead of using characters from the (no longer exists) "kpf" array to write * in the numerical input box, the characters are now defined by bitmaps in the * "KP_Font" array. The "KeyNumber" replaces the (no longer exists) "DrawFont" * function. * * Also re-did how it decides to add space after every third digit. A leading * minus sign no longer counts and if a decimal point is entered, we no longer * add the extra space between digits. */ static void draw_numeric_input ( const char *buf ) { int i = 0; // Buffer index int x = 64; // 'X' coordinate for first character int nextX = 20; // Normal character spacing bool sawDot = false; // True if decimal entered uint8_t digits = 0; // Digit counter for ( i = 0; i < 10 && buf[i]; i++ ) { nextX = 18; // Normal character spacing uint16_t fg = BLACK; // Foreground is black uint16_t bg = WHITE; // Background is white int c = buf[i]; // Get a character if ( c == '.' ) // Decimal point? { c = KN_PERIOD; // Index to '.' in the "KP_Font" array sawDot = true; // Decimal seen } else if ( c == '-' ) // Minus sign" c = KN_MINUS; // Index to '_' in the "KP_Font" array else if ( isDigit ( c )) // Digit? c = c - '0'; // Quick & dirty "atoi" else // None of the above c = -1; if ( c >= 0 ) // 'c' was found { KeyNumber ( c, x, 208+4 ); // Paint the character // addIx++; if ( c <= 9 ) // Is it a number? { digits++; // Increment the digit counter if ( !sawDot ) // No decimal yet if (( digits % 3 ) == 0 ) // Every third digit nextX += 6; // Add extra space } } else tft.fillRect ( x, 208+4, 20, 24, bg ); // If not found, erase it x += nextX; // Place for next digit } // End of for loop if ( i < 10 ) tft.fillRect ( x, 208+4, 20 * ( 10-i ), 24, WHITE ); // Fill something with white } /* * "draw_menu_buttons" - Paint the menu buttons */ static void draw_menu_buttons ( Menuitem *menu ) { int i = 0; //Serial.println( "----------------------------" ); // Debugging for ( i = 0; i < 7; i++ ) // Maximum number of menu buttons is 7 { if ( menu[i].Type() == MT_END ) // End of the menu break; // So no need to look any further int y = 32 * i; // "Y" coordinate in pixels? uint16_t bg = config.menu_normal_color; // Background is unselected color uint16_t fg = BLACK; // Text is in black if ( ui_mode == UI_MENU && i == selection ) // focus only in MENU mode but not in KEYPAD mode bg = config.menu_active_color; // Set background to selected color tft.fillRect ( 320-60, y, 60, 30, bg ); // These hard coded numbers have to go! if ( menu[i].isMultiline ()) // Multi-line label? { DrawString ( menu[i].Text1(), 320-54, y+6, fg, 1 ); // First line DrawString ( menu[i].Text2(), 320-54, y+17, fg, 1 ); // Second line } else // Single line label DrawString ( menu[i].Text1(), 320-54, y+12, fg, 1 ); } // End of for loop } /* * "menu_select_touch" - Wait for a menu button to be pushed */ static void menu_select_touch ( int i ) { selection = i; draw_menu (); // Draw the menu buttons // Serial.println ( "Before wait release" ); // Debugging touch_wait_release (); // Wait for a release indication // Serial.println ( "After wait release" ); selection = -1; // Don't understand this ??? menu_invoke ( i ); } /* * "menu_apply_touch" - Figure out which button was touched */ static void menu_apply_touch ( void ) { int touch_x, touch_y; // Touch coordinates Menuitem *menu = menu_stack[menu_current_level]; int i; touch_position ( &touch_x, &touch_y ); // Get coordinates for ( i = 0; i < 7; i++ ) // Up to 7 buttons { int y = 32 * i; // "Y" coordinate of button in pixels if ( y-2 < touch_y && touch_y < y+30+2 && 320-60 < touch_x ) { menu_select_touch ( i ); // This is the button that was selected return; // We're done! } } // End of for loop touch_wait_release (); // Wait for button release ui_mode_normal (); // No more menu } /* * "draw_menu" - Paint the entire menu */ static void draw_menu ( void ) { // Serial.println ( "draw menu" ); // Debugging draw_menu_buttons ( menu_stack[menu_current_level] ); // Paint the buttons } /* * "erase_menu_buttons" - Self explanatory */ static void erase_menu_buttons ( void ) { // Serial.println ( "erase buttons" ); // Debugging tft.fillRect ( 320-60, 0, 60, 32*7, bg ); } /* * "erase_numeric_input" - Erase the numerical input box below the keypad */ static void erase_numeric_input ( void ) { tft.fillRect ( 0, 240-32, 320, 32, bg ); // Poof! } /* * "request_to_draw_cells_behind_menu" - Do nothing function */ void request_to_draw_cells_behind_menu () {} /* * "leave_ui_mode" - Cleanup after using the touch screen. * * Modified in Version 2.1 by WA2FZW: * * Eliminated "changedSetting" test and "WriteSettings". Because savings the * settings was also being handled in some of the functions that set the members * of the "setting" structure, the structure was sometimes being saved twice. It * was also being saved when the parameters didn't actually change. This was * all fixed in "Cmd.cpp". */ static void leave_ui_mode ( void ) { if ( ui_mode == UI_MENU ) // In menu mode? { erase_menu_buttons (); // Erase the menu area request_to_redraw_grid(); } else if ( ui_mode == UI_NUMERIC ) // Using the keypad? erase_numeric_input (); // Erase the number box // Serial.println ( "leave UI" ); // Debugging } /* * "fetch_numeric_target" - Gets one of the scan frequency parameters. The * "GetSweepStart()" etc functions are in the cmd.cpp file. */ static void fetch_numeric_target ( void ) { switch ( keypad_mode ) { case KM_START: uistat.value = GetSweepStart (); break; case KM_STOP: uistat.value = GetSweepStop (); break; case KM_CENTER: uistat.value = GetSweepCenter (); break; case KM_SPAN: uistat.value = GetSweepSpan (); break; case KM_FOCUS: uistat.value = GetSweepCenter (); break; case KM_REFPOS: // uistat.value = get_trace_refpos ( uistat.current_trace ) * 1000; break; case KM_ATTENUATION: uistat.value = setting.Attenuate; break; case KM_ACTUALPOWER: // uistat.value = velocity_factor; break; case KM_IFFREQ: uistat.value = setting.IF_Freq; break; case KM_SGFREQ: uistat.value = sigGenSetting.Frequency; break; case KM_SGLEVEL: uistat.value = sigGenSetting.Power; break; case KM_SGLEVCAL: uistat.value = sigGenSetting.Calibration; break; case KM_PREAMP: uistat.value = GetPreampGain ( &AGC_On, &AGC_Reg ); break; case KM_IFSTART: uistat.value = GetIFsweepStart(); break; case KM_IFSTOP: uistat.value = GetIFsweepStop(); break; case KM_IFSIG: uistat.value = GetIFsweepSigFreq(); break; case KM_TGOFFSET: uistat.value = trackGenSetting.Offset; break; case KM_TGFREQ: uistat.value = trackGenSetting.Frequency; break; case KM_TGIF_DRIVE: uistat.value = trackGenSetting.IF_Drive; break; case KM_TGLO_DRIVE: uistat.value = trackGenSetting.LO_Drive; break; case KM_BANDSCOPESTART: uistat.value = setting.BandscopeStart; break; case KM_WFMIN: uistat.value = setting.WaterfallMin; break; case KM_WFGAIN: uistat.value = setting.WaterfallGain; break; case KM_BANDSCOPELEVEL: uistat.value = setting.BandscopeMaxGrid; break; case KM_RXSPAN: uistat.value = GetRXsweepSpan(); break; case KM_RXSIG: uistat.value = GetRXsweepSigFreq(); break; case KM_RBW: uistat.value = (double)setting.Bandwidth10 / 10.0; break; case KM_EXTERN: uistat.value = setting.ExternalGain; break; } uint32_t x = uistat.value; int n = 0; for (; x >= 10 && n < 9; n++ ) x /= 10; uistat.digit = n; uistat.previous_value = uistat.value; // Serial.printf("uistat previous value %f\n", uistat.previous_value ); } /* * "draw_numeric_area" - Paints adds the appropriate text to the numeric entry box * and requests it to be painted. */ static void draw_numeric_area ( void ) { char buf[10]; snprintf ( buf, sizeof buf, "%9d", uistat.value ); draw_numeric_input ( buf ); } /* * "ui_mode_menu" - Sets menu mode and paints the menu */ static void ui_mode_menu ( void ) { if ( ui_mode == UI_MENU ) // If already in menu mode return; // Nothing to do here ui_mode = UI_MENU; // Set menu mode Serial.println("ui_mode_menu"); // area_width = AREA_WIDTH_NORMAL - ( 64 - 8 ); // Narrower plotting area // area_height = HEIGHT; ensure_selection (); // Validate current selection draw_menu (); // And paint the menu } /* * "ui_mode_numeric" - Puts the UI into numeric mode */ static void ui_mode_numeric ( int _keypad_mode ) { if ( ui_mode == UI_NUMERIC ) // Already in numeric mode? return; // Nothing to do leave_ui_mode (); // Get out of previous mode? keypad_mode = _keypad_mode; // Set numeric mode ui_mode = UI_NUMERIC; // area_width = AREA_WIDTH_NORMAL; // area_height = 240 - 32; // HEIGHT - 32; draw_numeric_area_frame (); fetch_numeric_target (); draw_numeric_area (); } /* * "ui_mode_keypad" - Puts us in keypad mode */ static void ui_mode_keypad ( int _keypad_mode ) { if ( ui_mode == UI_KEYPAD ) // Already here? return; // Nuttin to do keypad_mode = _keypad_mode; // Pick the proper keypad format keypads = keypads_mode_tbl[_keypad_mode]; int i; for ( i = 0; keypads[i+1].c >= 0; i++ ) {} keypads_last_index = i; // Fall out of loop when found ui_mode = UI_KEYPAD; // Set keypad mode // area_width = AREA_WIDTH_NORMAL - ( 64 - 8 ); // area_height = HEIGHT - 32; draw_menu (); // Paint the menu draw_keypad (); // Then the keypad fetch_numeric_target (); //!!!!! draw_numeric_area_frame (); // Then the numeric entry area draw_numeric_input (""); // Blanked out? } /* * "ui_mode_normal" - Puts the UI into normal mode */ static void ui_mode_normal ( void ) { if ( ui_mode == UI_NORMAL ) // Already there? return; // Done //**HERE2 // area_width = AREA_WIDTH_NORMAL; // area_height = HEIGHT; menu_current_level = 0; leave_ui_mode(); ui_mode = UI_NORMAL; } /* * "my_atof" - A homebrew ascii to double function; not sure why needed? */ double my_atof ( const char *p ) { int neg = FALSE; if ( *p == '-' ) // If first character is a minus sign neg = TRUE; // Number is negative if ( *p == '-' || *p == '+' ) // If first character is a plus or minus sign p++; // Skip over it double x = atoi ( p ); // ??? while ( isdigit ( (int) *p )) // If next character is a digit p++; // Bump the pointer if ( *p == '.' ) // If it's a decimal point { double d = 1.0f; // "d" = '1' in floating point p++; // Next character while ( isdigit ( (int) *p )) // As long as we see digits { d /= 10; // Divide "d" by 10 x += d * ( *p - '0' ); // Then add in the next digit p++; // Next character } } if ( *p == 'e' || *p == 'E' ) // Not a digit then is it an exponent? { p++; // Next character int exp = atoi ( p ); // Convert exponent to integer while ( exp > 0 ) // If exponent positive { x *= 10; // Multiply the number by 10 exp--; // And decrement the exponent } while ( exp < 0 ) // If the exponent is negative { x /= 10; // Divide the number by 10 exp++; // And increment the exponent } } if ( neg ) // If the whole answer is negative x = -x; // Make the number negative return x; // And send it back } /* * "keypad_click" - */ static int keypad_click ( int key ) { int c = keypads[key].c; // Index to the "KP_Font" array char fBuff[20]; // Frequency string if (( c >= KN_X && c <= KN_M )) // 'X', 'K' or 'M' key pressed? { int32_t scale = 1; // Default "scale"? if ( c >= KN_X && c <= KN_M ) // Redundent test? Why? { int n = c - KN_X; // 'X' -> 0, 'K' _> 1, 'M' -> 2 while ( n-- > 0 ) // Multiplier will be 1K or 1M scale *= 1000; } /* * When we get here, the numeric input has completed. We convert the number to * a "double" and multiply it by the "scale". */ if (kp_index ==0) // no entry - treat same as <- return KP_CANCEL; double value = my_atof ( kp_buf ) * ( double ) scale; switch ( keypad_mode ) { case KM_START: // Start frequency entered? SetSweepStart (( int32_t ) value ); break; case KM_STOP: // Stop frequency entered? SetSweepStop (( int32_t ) value ); break; case KM_CENTER: // Center frequency entered? SetSweepCenter (( int32_t ) value, WIDE ); break; case KM_SPAN: // Frequency span entered? SetSweepSpan (( int32_t ) value ); break; case KM_FOCUS: // Focus frequency entered? SetSweepCenter ((int32_t ) value, NARROW ); break; case KM_REFPOS: // Reference level SetRefLevel (value ); break; case KM_ATTENUATION: // Attenuator setting if ( value > PE4302_MAX ) // Too high? { DisplayError ( ERR_WARN, "Illegal attenuator setting!", "Ignored!", NULL, NULL ); break; } SetAttenuation ( value ); break; case KM_ACTUALPOWER: // Power level RequestSetPowerLevel ( value ); break; case KM_IFFREQ: if ( !SetIFFrequency (( uint32_t ) value )) // Bad frequency? { strcpy ( fBuff, FormatFrequency (( uint32_t ) value )); DisplayError ( ERR_WARN, fBuff, "Invalid IF Frequency!", "IF Frequency set to:", FormatFrequency ( setting.IF_Freq )); } break; case KM_SGFREQ: if ( (value > MAX_SIGLO_FREQ) || (value < MIN_SIGLO_FREQ) ) { strcpy ( fBuff, FormatFrequency (( uint32_t ) value )); DisplayError ( ERR_WARN, fBuff, "Invalid Frequency!", NULL, NULL ); } else sigGenSetting.Frequency = value; break; case KM_SGLEVEL: if ( (value > sigGenSetting.Calibration) || (value < sigGenSetting.Calibration - ATTENUATOR_RANGE) ) { DisplayError ( ERR_WARN, "Invalid Level!", NULL, NULL, NULL ); } else sigGenSetting.Power = value; break; case KM_SGLEVCAL: if ( (value > 20) || (value < -30) ) { DisplayError ( ERR_WARN, "Invalid Level!", NULL, NULL, NULL ); } else sigGenSetting.Calibration = value; break; case KM_IFSTART: // IF Start frequency entered? SetIFsweepStart (( int32_t ) value ); break; case KM_IFSTOP: // IF Stop frequency entered? SetIFsweepStop (( int32_t ) value ); break; case KM_IFSIG: // IF Signal frequency entered? SetIFsweepSigFreq (( int32_t ) value ); break; case KM_PREAMP: if (( value < 5 ) || ( value > 49 )) // Range check { DisplayError ( ERR_WARN, "Illegal preamp gain setting!", "Ignored!", NULL, NULL ); break; } SetPreampGain ( value ); break; case KM_TUNE: if ( value > 255 ) // Illegal value? { DisplayError ( ERR_WARN, "Illegal calibration factor!", "Ignored!", NULL, NULL ); break; // Done! } if ( VFO == TX_4432 ) // Transmitter module selected? { xmit.Tune ( ( uint8_t ) value ); // Tune it config.RX_capacitance = ( uint8_t ) value; // And save it } else if ( VFO == RX_4432 ) // Receiver module selected? { rcvr.Tune ( ( uint8_t ) value ); // Tune it config.RX_capacitance = ( uint8_t ) value; // And save it } #ifdef TG_IF_INSTALLED else if ( VFO == TGIF_4432 ) // Tracking generator IF module selected? { tg_if.Tune ( ( uint8_t ) value ); // Tune it config.tgIF_capacitance = ( uint8_t ) value; // And save it } #endif #ifdef TG_LO_INSTALLED else if ( VFO == TGLO_4432 ) // Tracking generator LO module selected? { tg_lo.Tune ( ( uint8_t ) value ); // Tune it config.tgLO_capacitance = ( uint8_t ) value; // And save it } #endif break; case KM_TGOFFSET: if ( (value > MAX_TG_OFFSET) || (value < MIN_TG_OFFSET) ) { strcpy ( fBuff, FormatSignedFrequency (( int32_t ) value )); DisplayError ( ERR_WARN, fBuff, "Invalid Frequency!", NULL, NULL ); } else SetTGOffset ( (int32_t)value ); break; case KM_TGFREQ: if ( (value > MAX_SIGLO_FREQ) || (value < MIN_SIGLO_FREQ) ) { strcpy ( fBuff, FormatSignedFrequency (( int32_t ) value )); DisplayError ( ERR_WARN, fBuff, "Invalid Frequency!", NULL, NULL ); } else SetTGFreq ( (int32_t)value ); break; #ifdef TG_IF_INSTALLED case KM_TGIF_DRIVE: if ( (value > MAX_DRIVE) || (value < MIN_DRIVE) ) { DisplayError ( ERR_WARN, "Invalid drive level!", "(0-7)", NULL, NULL ); } else SetTGIfDrive ( (uint8_t)value ); break; #endif #ifdef TG_LO_INSTALLED case KM_TGLO_DRIVE: if ( (value > MAX_DRIVE) || (value < MIN_DRIVE) ) { DisplayError ( ERR_WARN, "Invalid drive level!", "(0-7)", NULL, NULL ); } else SetTGLoDrive ( (uint8_t)value ); break; #endif case KM_BANDSCOPESTART: // Bandscope Start frequency entered? SetBandscopeStart (( int32_t ) value ); break; case KM_WFMIN: // Bandscope span entered? if ( (value >= 0) && (value <= 200 ) ) setting.WaterfallMin = ( int16_t )value ; else { DisplayError ( ERR_WARN, "Invalid minimum level!", "(0 - 200)", NULL, NULL ); } break; case KM_WFGAIN: // Bandscope span entered? if ( (value >= 0.1) && (value <= 4.0 ) ) setting.WaterfallGain = value ; else { DisplayError ( ERR_WARN, "Invalid gain!", "(0.1 - 4)", NULL, NULL ); } break; case KM_BANDSCOPELEVEL: // Bandscope level entered? SetBandscopeLevel (( int32_t ) value ); break; case KM_RXSPAN: // RX Span frequency entered? Serial.printf( "Value = %f\n", value ); SetRXsweepSpan (( int32_t ) value ); break; case KM_RXSIG: // RX Signal frequency entered? SetRXsweepSigFreq (( int32_t ) value ); break; case KM_RBW: // RBW SetRBW (( int32_t ) (value * 10) ); break; case KM_EXTERN: SetExtGain ( value ); break; } // End of "switch" return KP_DONE; // Indicate finished with the keypad } else if ( c <= 9 && kp_index < NUMINPUT_LEN ) kp_buf[kp_index++] = '0' + c; else if ( c == KN_PERIOD && kp_index < NUMINPUT_LEN ) { /* * Check period in former input */ int j; for ( j = 0; j < kp_index && kp_buf[j] != '.'; j++ ) {} if ( kp_index == j ) kp_buf[kp_index++] = '.'; // Append period if there was no period } else if ( c == KN_MINUS ) { if ( kp_index == 0 ) kp_buf[kp_index++] = '-'; } else if ( c == KN_BS ) { if ( kp_index == 0 ) return KP_CANCEL; --kp_index; } kp_buf[kp_index] = '\0'; // NULL terminator draw_numeric_input ( kp_buf ); return KP_CONTINUE; // Still more to process } /* * "keypad_apply_touch" - Figures out which keypad pad was touched */ static int keypad_apply_touch ( void ) { int touch_x, touch_y; // Touch coordinates int i = 0; touch_position ( &touch_x, &touch_y ); // Get touch coordinates while ( keypads[i].x ) { if (keypads[i].x-2 < touch_x && touch_x < keypads[i].x+44+2 && keypads[i].y-2 < touch_y && touch_y < keypads[i].y+44+2) { selection = i; // Set focus draw_keypad (); // Repaint with selected pad highlighted touch_wait_release (); // Wait for pad released selection = -1; // Erase focus draw_keypad (); // And repaint again return i; // Return selected pad index } i++; // Try the next one } // End of "while" loop if ( touch_y > 48 * 4 ) return -2; // Exit keypad mode return -1; } /* * "numeric_apply_touch" - */ static void numeric_apply_touch ( void ) { int touch_x, touch_y; // Touch coordinates touch_position ( &touch_x, &touch_y ); // Get coordinates if ( touch_x < 64 ) // Out of touch area? { // Hard-coded numbers have to go! ui_mode_normal (); return; } if ( touch_x > 64 + 9 * 20 + 8 + 8 ) // Your guess is as good as mine! { ui_mode_keypad ( keypad_mode ); ui_process_keypad (); return; } if ( touch_y > 240 - 40 ) { int n = 9 - ( touch_x - 64 ) / 20; uistat.digit = n; uistat.digit_mode = TRUE; } else { int step, n; if ( touch_y < 100 ) step = 1; else step = -1; for (n = uistat.digit; n > 0; n-- ) step *= 10; uistat.value += step; } draw_numeric_area (); touch_wait_release (); uistat.digit_mode = FALSE; draw_numeric_area (); return; } /* * "ui_process_keypad" - */ static void ui_process_keypad ( void ) { int status; kp_index = 0; while ( TRUE ) // "TRUE = '-1" in this program { status = touch_check (); // Look for screen touched if ( status == EVT_TOUCH_PRESSED ) // If it was touched { int key = keypad_apply_touch (); // Process it if ( key >= 0 && keypad_click ( key )) break; // Exit loop on done or cancel else if ( key == -2 ) { // xxx; // ??????? // return; } } } //**HERE3 request_to_redraw_grid (); ui_mode_normal (); // draw_menu (); // menu_move_back(); } /* * "UiProcessTouch" - */ void UiProcessTouch ( void ) { int tstatus = touch_check(); // Look for screen touch if ( tstatus == EVT_TOUCH_PRESSED || tstatus == EVT_TOUCH_DOWN ) { // Serial.printf( "process Touch status = %i, UImode = %i \n", tstatus, ui_mode ); switch ( ui_mode ) { case UI_NORMAL: // Serial.println( "waiting for release" ); int touch_x, touch_y; // Touch coordinates touch_position ( &touch_x, &touch_y ); // Get coordinates Serial.printf("x:%i y:%i\n", touch_x, touch_y); touch_wait_release (); /* * Some touchscreen areas have fast access to the submenu levels, depends on the mode */ if ( setting.Mode == SA_LOW_RANGE ) { // test to see if the touch is in the marker area if ( (touch_y < 10) && (touch_x < 160) ) { marker[0].Toggle(); // marker 1 setting.MkrStatus[0] = marker[0].Status(); return; } else if ( (touch_y < 10) && (touch_x > 160) ) { marker[2].Toggle(); // marker 3 setting.MkrStatus[2] = marker[2].Status(); return; } else if ( (touch_y < 20) && (touch_x < 160) ) { marker[1].Toggle(); // marker 2 setting.MkrStatus[1] = marker[1].Status(); return; } else if ( (touch_y < 20) && (touch_x > 160) ) { marker[3].Toggle(); // marker 4 setting.MkrStatus[3] = marker[3].Status(); return; } else if ( (touch_y < 40) && (touch_x > 30) ) StartMarkerMenu(); else if ( touch_y > 210 ) StartSweepMenu(); else if ( (touch_x < 30) && (touch_y < 60) ) StartRBWMenu(); else if ( (touch_x < 30) && (touch_y > CHAR_HEIGHT * 20 ) && ( touch_y < CHAR_HEIGHT * 22 ) ) { SetSpur (!setting.Spur); return; } else if ( (touch_x < 30) ) StartDisplayMenu(); } selection = -1; // Switch menu mode bg = BLACK; // black background ui_mode_menu (); break; case UI_MENU: menu_apply_touch (); break; case UI_NUMERIC: numeric_apply_touch (); break; } } } /* * Function to enter menu at certain level and selection * HERE * static uint8_t menu_current_level = 0; // Current level (MAIN) * * static Menuitem *menu_stack[MENU_STACK_DEPTH] = // Initialize the stack * { menu_top, // Main menu is on top * NULL, // These get set as we * NULL, // go along * NULL * }; * * // helpers for setting menus from outside * enum { MENU_TOP, MENU_SWEEP, MENU_DISPLAY, MENU_STORAGE, MENU_OUTPUT, MENU_SAVE, MENU_CONFIG }; // Root Menu * enum { SWEEP_START, SWEEP_STOP, SWEEP_CENTRE, SWEEP_SPAN, FOCUS }; // Sweep menu - level 1 * enum { RBW, ATTEN, MARKERS }; // Sweep more menu - level 2 * enum { TRACES, PREAMP, REF_LEVEL, DB_DIV, SPUR, DEFAULT_SETTINGS }; // Display menu - level 1 * */ void StartSigGenMenu ( void ) { tft.unloadFont(); selection = -1; // Switch menu mode menu_current_level = 0; menu_stack[0] = menu_sig_gen; bg = SIG_BACKGROUND_COLOR; tft.fillRect ( 320-60, 0, 60, 32*ELEMENTS(menu_sig_gen), bg ); ui_mode_menu (); } void StartSigGenFreq ( void ) { tft.unloadFont(); selection = -1; // Switch menu mode // menu_current_level = 0; // menu_stack[0] = menu_sig_gen; bg = SIG_BACKGROUND_COLOR; tft.fillScreen ( bg ); menu_sig_freq_cb ( 0 ); } static void StartMarkerMenu ( void ) { selection = -1; // Switch menu mode menu_current_level = 3; menu_stack[1] = menu_sweep; menu_stack[2] = menu_sweep2; menu_stack[3] = menu_markers; } static void StartSweepMenu ( void ) { selection = -1; // Switch menu mode menu_current_level = 1; menu_stack[1] = menu_sweep; menu_stack[2] = NULL; menu_stack[3] = NULL; } static void StartDisplayMenu ( void ) { selection = -1; // Switch menu mode menu_current_level = 1; menu_stack[1] = menu_display; menu_stack[2] = NULL; menu_stack[3] = NULL; } static void StartRBWMenu ( void ) { selection = -1; // Switch menu mode menu_current_level = 3; menu_stack[1] = menu_sweep; menu_stack[2] = menu_sweep2; menu_stack[3] = menu_rbw; } /* * Resets the menu stack to root level for SA mode */ void ResetSAMenuStack (void) { tft.unloadFont(); selection = -1; // Switch menu mode menu_current_level = 0; menu_stack[0] = menu_top; menu_stack[1] = NULL; menu_stack[2] = NULL; menu_stack[3] = NULL; if (ui_mode != UI_NORMAL) ui_mode_normal (); } /* * Resets the menu stack to root level for IF_SWEEP mode */ void ResetIFsweepMenuStack (void) { tft.unloadFont(); selection = -1; // Switch menu mode menu_current_level = 0; menu_stack[0] = menu_IFsweep_top; menu_stack[1] = NULL; menu_stack[2] = NULL; menu_stack[3] = NULL; if (ui_mode != UI_NORMAL) ui_mode_normal (); } /* * Resets the menu stack to root level for RX_SWEEP mode */ void ResetRXsweepMenuStack (void) { tft.unloadFont(); selection = -1; // Switch menu mode menu_current_level = 0; menu_stack[0] = menu_RXsweep_top; menu_stack[1] = NULL; menu_stack[2] = NULL; menu_stack[3] = NULL; if (ui_mode != UI_NORMAL) ui_mode_normal (); } /* * Resets the menu stack to root level for Bandscope mode */ void ResetBandscopeMenuStack (void) { tft.unloadFont(); selection = -1; // Switch menu mode menu_current_level = 0; menu_stack[0] = menu_Bandscope_top; menu_stack[1] = NULL; menu_stack[2] = NULL; menu_stack[3] = NULL; if (ui_mode != UI_NORMAL) ui_mode_normal (); } /* * "ui_process" - */ void ui_process ( void ) { switch ( operation_requested ) // Only one case??? { case OP_TOUCH: UiProcessTouch (); break; } operation_requested = OP_NONE; }