/* * "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 SI_TG_IF_CS extern Si4432 tg_if; // Si4432 Tracking Generator IF #endif #ifdef SI_TG_LO_CS 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 ??? int32_t value; // For editing at numeric input area int32_t 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, // value 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_TGLO_DRIVE, KM_TGIF_DRIVE, KM_BANDSCOPESTART, KM_BANDSCOPESPAN }; /* * 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 LO Drive", "TG IF Drive", "START", "SPAN" }; 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_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 static void menu_Bandscope_cb ( int item ); // M0WID added 3.0f 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_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 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, "\2IF\0SWEEP", menu_mode_cb ), Menuitem ( MT_FUNC, "\2BAND\0SCOPE",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 }; static Menuitem menu_tracking[] = // Tracking generator mode { Menuitem ( MT_FUNC, "OFF", menu_tracking_cb ), Menuitem ( MT_FUNC, "ON", menu_tracking_cb ), Menuitem ( MT_FUNC, "OFFSET", menu_tg_offset_cb ), Menuitem ( MT_FUNC, "\2IF\0DRIVE", menu_tgIF_drive_cb ), #ifdef SI_TG_LO_CS Menuitem ( MT_FUNC, "\2LO\0DRIVE", menu_tgLO_drive_cb ), #endif Menuitem ( MT_BACK, "<-BACK" ), // Next level up Menuitem ( MT_END ) // End marker }; 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_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 SI_TG_IF_CS 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_Bandscope_top[] = // This is the main "IF_SWEEP" menu { Menuitem ( MT_MENU, "MODE", menu_mode ), Menuitem ( MT_FUNC, "\2SWEEP\0START", menu_Bandscope_cb ), Menuitem ( MT_FUNC, "\2SWEEP\0SPAN", menu_Bandscope_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 ( "TinySA 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 ( "Version 3.0", 160, 100 ); tft.drawString ( "Original 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 enter_dfu(void) {} // Do nothing function 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(IF_SWEEP); ui_mode_normal (); // No menu displayed break; case 3: // Set IF Sweep mode setMode(BANDSCOPE); ui_mode_normal (); // No menu displayed 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_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; } } /* * "menu_autosettings_cb" seems to set all the 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 ); // SetPowerLevel ( 100 ); // Removed - Screws up calibration 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: // All these need symbols! touch_cal_exec (); request_to_redraw_grid (); draw_menu (); break; case 1: 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_Bandscope_cb ( int item ) { ui_mode_keypad ( item + KM_BANDSCOPESTART - 1 ); // item = 1 -> KM_BANDSCOPESTART, 2 -> KM_BANDSCOPESPAN 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_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_freq // KM_BANDSCOPESPAN..IF Sweep stop frequency }; /* * "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], 10, 220, BLACK, 1 ); tft.setCursor(0,230); tft.printf("%i",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 = 58; // '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_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_BANDSCOPESPAN: uistat.value = setting.BandscopeSpan; 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 SI_TG_IF_CS 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 SI_TG_LO_CS 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 trackGenSetting.Offset = value; break; case KM_TGIF_DRIVE: if ( (value > MAX_DRIVE) || (value < MIN_DRIVE) ) { DisplayError ( ERR_WARN, "Invalid drive level!", "(0-7)", NULL, NULL ); } else trackGenSetting.IF_Drive = value; break; case KM_TGLO_DRIVE: if ( (value > MAX_DRIVE) || (value < MIN_DRIVE) ) { DisplayError ( ERR_WARN, "Invalid drive level!", "(0-7)", NULL, NULL ); } else trackGenSetting.LO_Drive = value; break; case KM_BANDSCOPESTART: // Bandscope Start frequency entered? SetBandscopeStart (( int32_t ) value ); break; case KM_BANDSCOPESPAN: // Bandscope span entered? SetBandscopeSpan (( int32_t ) 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 (); // test to see if the touch is in the marker area if ( (touch_y < 10) && (touch_x < 160) ) { marker[0].Toggle(); // marker 1 return; } else if ( (touch_y < 10) && (touch_x > 160) ) { marker[2].Toggle(); // marker 3 return; } else if ( (touch_y < 20) && (touch_x < 160) ) { marker[1].Toggle(); // marker 2 return; } else if ( (touch_y < 20) && (touch_x > 160) ) { marker[3].Toggle(); // marker 4 return; } else if ( (touch_y < 40) && (touch_x > 30) ) StartMarkerMenu(); else if ( (touch_y > 210) && ( setting.Mode == SA_LOW_RANGE ) ) StartSweepMenu(); else if ( (touch_x < 30) && (touch_y < 60) && ( setting.Mode == SA_LOW_RANGE ) ) StartRBWMenu(); else if ( (touch_x < 30) && (touch_y > CHAR_HEIGHT * 20 ) && ( touch_y < CHAR_HEIGHT * 22 ) && ( setting.Mode == SA_LOW_RANGE ) ) { SetSpur (!setting.Spur); return; } else if ( (touch_x < 30) && ( setting.Mode == SA_LOW_RANGE ) ) 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 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; }