simpleSA/ui.cpp
M0WID 16289698dc Bandscope Grid working
Waterfall next, then speed up frequency changes with offsets and reduce delaytime.
Sort out 1 pixel error at start/end
2020-08-16 22:24:08 +01:00

3197 lines
85 KiB
C++

/*
* "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.h> // 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 <TFT_eSPI.h> // 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;
}
}
/*
* "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;
}