Signal Generator

Dependencies:   IniManager RA8875 Watchdog mbed-rtos mbed

Fork of speaker_demo_Analog by jim hamblen

Revision:
2:8f71b71fce1b
Parent:
1:dd07e1deec6c
Child:
3:d22f3e52d06a
--- a/SignalGenDisplay.cpp	Fri Jan 13 12:33:37 2017 +0000
+++ b/SignalGenDisplay.cpp	Sun Jan 15 03:11:22 2017 +0000
@@ -1,4 +1,7 @@
-
+// 
+// Signal Generator Control System
+//
+//
 #include "SignalGenDisplay.h"
 #include "rtos.h"
 #include "IniManager.h"
@@ -8,16 +11,16 @@
 // ##### Main Page #############################################################
 //
 // +---------------------------------------------------------------------------+
-// | +------------------------------------------+   Progam Name and version    |
+// | +--- Scope Area ---------------------------+   Progam Name and version    |
 // | |                                          |   Manufacturer name          |
-// | |                                          |                              |
-// | |                                          |   [ Text Entry Box         ] |
-// | |      Scope Area                          |   +------------------------+ |
+// | | +---- Wave Outline - -                   |                              |
+// | | |                                        |   [Text Entry Box]  [ Back ] |
+// | | |                                        |                              |
+// | |                                          |   +------------------------+ |
 // | |                                          |   |                        | |
-// | |                                          |   |                        | |
-// | |                                          |   |                        | |
-// | |                                          |   |                        | |
-// | |                                          |   |                        | |
+// | |                                     |    |   |                        | |
+// | |                                     |    |   |                        | |
+// | |                                  ---+    |   |                        | |
 // | |                                          |   |    Keypad Area         | |
 // | +------------------------------------------+   |                        | |
 // |                                                |                        | |
@@ -29,82 +32,81 @@
 // | [Sine ] [Square] [Triangle] [Sawtooth] [User]  +------------------------+ |
 // +---------------------------------------------------------------------------+
 
-
-#define UI_BackColor            RGB(8,8,8)
+// Object Colors
+#define UI_BackColor                RGB(8,8,8)
+#define UI_ScopeBackColor           RGB(0,0,0)
+#define UI_ScopeFrameColor          RGB(255,255,255)
+#define WaveOutlineColor            RGB(16,16,32)
+#define UI_DutyColor                Magenta
+#define UI_FreqColor                BrightRed
+#define UI_VP2PColor                Yellow
+#define UI_VOffsetColor             Green
+#define UI_BUTTON_FACE_UP           White
+#define UI_BUTTON_FACE_DN           RGB(255,92,92)
+#define UI_BUTTON_SHADOW            RGB(128,0,0)
+#define UI_BUTTON_FACE_DISABLED     RGB(24,24,24)
+#define UI_BUTTON_SHADOW_DISABLED   RGB(32,0,0)
+#define UI_ProductNameColor         UI_BUTTON_FACE_DN
 
-const rect_t UI_DATA_ENTRY      = {300,45, 475,65};
-
+// Rectangular Zones
+const rect_t UI_DATA_ENTRY      = {300,53, 410,73};
 const rect_t UI_SCOPE_RECT      = {4,5, 290,160};
-#define UI_ScopeBackColor       RGB(0,0,0)
-#define UI_ScopeFrameColor      RGB(255,255,255)
+#define SC_LEFT_MARGIN              10       // Scope left margin
+#define SC_TOP_MARGIN               20
+#define SC_RIGHT_MARGIN             30
+#define SC_BOT_MARGIN               30
+const rect_t UI_WAVE_RECT       = {4+SC_LEFT_MARGIN,5+SC_TOP_MARGIN, 290-SC_RIGHT_MARGIN,160-SC_BOT_MARGIN};
 
-#define SC_LEFT_MARGIN 10
-#define SC_TOP_MARGIN 20
-#define SC_RIGHT_MARGIN 30
-#define SC_BOT_MARGIN 30
-#define WaveOutlineColor        RGB(16,16,32)
+#define BTN_W       54          // Button width
+#define BTN_H       32          // Button height
+#define BTN_S       5           // Button white-space
+
+#define BTN_MODE_X  2           // Mode Buttons left edge
+#define BTN_MODE_Y  233         // Mode Buttons top edge
+
+#define BTN_KEYP_X  300         // Keypad left edge
+#define BTN_KEYP_Y  53          // Keypad top edge
 
 const rect_t Parameters[] = {
-    {4,170, 60,190},    // 'd'uty cycle
-    {90,170, 186,190},  // 'f'requency
-    {90,200, 186,220},  // 'p'eriod
-    {230,170, 290,190}, // 'v'oltage
-    {230,200, 290,220}  // 'o'ffset
+    {4,170, 60,190},            // 'd'uty cycle
+    {90,170, 186,190},          // 'f'requency
+    {90,200, 186,220},          // 'p'eriod
+    {230,170, 290,190},         // 'v'oltage
+    {230,200, 290,220}          // 'o'ffset
 };
 const int ParameterCount = sizeof(Parameters)/sizeof(Parameters[0]);
-const char ParameterKeys[] = { 'd', 'f', 'p', 'v', 'o' };
-
-#define UI_DutyColor            Magenta
-#define UI_FreqColor            BrightRed
-#define UI_VP2PColor            DarkBrown
-#define UI_VOffsetColor         Green
-
-#define UI_BUTTON_FACE_UP       White
-#define UI_BUTTON_FACE_DN       RGB(255,92,92)
-#define UI_BUTTON_SHADOW        RGB(128,0,0)
-#define UI_BUTTON_FACE_DISABLED RGB(24,24,24)
-#define UI_BUTTON_SHADOW_DISABLED RGB(32,0,0)
-
-const rect_t UI_PROD_RECT       = {298,3, 479,40};
-#define UI_ProductNameColor     UI_BUTTON_FACE_DN // RGB(192,192,192)
+const char ParameterKeys[] =    { 'd', 'f', 'p', 'v', 'o' };
 
-#define PI 3.1415
-
-#define BTN_W 54
-#define BTN_H 35
-#define BTN_S  5    // space
-
-#define BTN_MODE_X  2
-#define BTN_MODE_Y  230
+const rect_t UI_PROD_RECT =     {298,3, 479,40};
+const rect_t NavToSettings =    {  4,200, 60,220 };
 
-#define BTN_KEYP_X  300
-#define BTN_KEYP_Y  70
-
-const rect_t NavToSettings = { 4,200, 60,220 };
-
-
-
-const rect_t UI_Buttons[] = {
+// Mode Buttons
+const rect_t ModeButtons[] = {
     { BTN_MODE_X+0*(BTN_W+BTN_S),BTN_MODE_Y,  BTN_MODE_X+0*(BTN_W+BTN_S)+BTN_W,BTN_MODE_Y+BTN_H },
     { BTN_MODE_X+1*(BTN_W+BTN_S),BTN_MODE_Y,  BTN_MODE_X+1*(BTN_W+BTN_S)+BTN_W,BTN_MODE_Y+BTN_H },
     { BTN_MODE_X+2*(BTN_W+BTN_S),BTN_MODE_Y,  BTN_MODE_X+2*(BTN_W+BTN_S)+BTN_W,BTN_MODE_Y+BTN_H },
     { BTN_MODE_X+3*(BTN_W+BTN_S),BTN_MODE_Y,  BTN_MODE_X+3*(BTN_W+BTN_S)+BTN_W,BTN_MODE_Y+BTN_H },
     { BTN_MODE_X+4*(BTN_W+BTN_S),BTN_MODE_Y,  BTN_MODE_X+4*(BTN_W+BTN_S)+BTN_W,BTN_MODE_Y+BTN_H },
 };
-const int ButtonCount = sizeof(UI_Buttons)/sizeof(UI_Buttons[0]);
-SignalGenDisplay::SG_Mode UI_ModeList[] = {
-    SignalGenDisplay::SG_SINE,
-    SignalGenDisplay::SG_SQUARE,
-    SignalGenDisplay::SG_TRIANGLE,
-    SignalGenDisplay::SG_SAWTOOTH,
-    SignalGenDisplay::SG_USER,
+const int ModeCount = sizeof(ModeButtons)/sizeof(ModeButtons[0]);
+SG_Mode UI_ModeList[] = {
+    SG_SINE,
+    SG_SQUARE,
+    SG_TRIANGLE,
+    SG_SAWTOOTH,
+    SG_USER,
 };
 const char ModeKeys[] = { 'S','Q','T','W','U' };
+const char *ModeNames[] = {
+    "Sine",
+    "Square",
+    "Triangle",
+    "Sawtooth",
+    "User",
+};
 
 const rect_t UI_Keypad[] = {
-    {BTN_KEYP_X+0*(BTN_W+BTN_S),BTN_KEYP_Y+0*(BTN_H+BTN_S), BTN_KEYP_X+0*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+0*(BTN_H+BTN_S)+BTN_H },
-    {BTN_KEYP_X+1*(BTN_W+BTN_S),BTN_KEYP_Y+0*(BTN_H+BTN_S), BTN_KEYP_X+1*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+0*(BTN_H+BTN_S)+BTN_H },
-    {BTN_KEYP_X+2*(BTN_W+BTN_S),BTN_KEYP_Y+0*(BTN_H+BTN_S), BTN_KEYP_X+2*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+0*(BTN_H+BTN_S)+BTN_H },
+    {BTN_KEYP_X+2*(BTN_W+BTN_S),BTN_KEYP_Y+0*(BTN_H+BTN_S), BTN_KEYP_X+2*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+0*(BTN_H+BTN_S)+BTN_H },    // backspace
     {BTN_KEYP_X+0*(BTN_W+BTN_S),BTN_KEYP_Y+1*(BTN_H+BTN_S), BTN_KEYP_X+0*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+1*(BTN_H+BTN_S)+BTN_H },
     {BTN_KEYP_X+1*(BTN_W+BTN_S),BTN_KEYP_Y+1*(BTN_H+BTN_S), BTN_KEYP_X+1*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+1*(BTN_H+BTN_S)+BTN_H },
     {BTN_KEYP_X+2*(BTN_W+BTN_S),BTN_KEYP_Y+1*(BTN_H+BTN_S), BTN_KEYP_X+2*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+1*(BTN_H+BTN_S)+BTN_H },
@@ -117,17 +119,26 @@
     {BTN_KEYP_X+0*(BTN_W+BTN_S),BTN_KEYP_Y+4*(BTN_H+BTN_S), BTN_KEYP_X+0*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+4*(BTN_H+BTN_S)+BTN_H },
     {BTN_KEYP_X+1*(BTN_W+BTN_S),BTN_KEYP_Y+4*(BTN_H+BTN_S), BTN_KEYP_X+1*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+4*(BTN_H+BTN_S)+BTN_H },
     {BTN_KEYP_X+2*(BTN_W+BTN_S),BTN_KEYP_Y+4*(BTN_H+BTN_S), BTN_KEYP_X+2*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+4*(BTN_H+BTN_S)+BTN_H },
+    {BTN_KEYP_X+0*(BTN_W+BTN_S),BTN_KEYP_Y+5*(BTN_H+BTN_S), BTN_KEYP_X+0*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+5*(BTN_H+BTN_S)+BTN_H },
+    {BTN_KEYP_X+1*(BTN_W+BTN_S),BTN_KEYP_Y+5*(BTN_H+BTN_S), BTN_KEYP_X+1*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+5*(BTN_H+BTN_S)+BTN_H },
+    {BTN_KEYP_X+2*(BTN_W+BTN_S),BTN_KEYP_Y+5*(BTN_H+BTN_S), BTN_KEYP_X+2*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+5*(BTN_H+BTN_S)+BTN_H },
 };
 const int KeypadCount = sizeof(UI_Keypad)/sizeof(UI_Keypad[0]);
 const char UI_KeyLabels[] = {
+              '\x1B',
     '7', '8', '9',
     '4', '5', '6',
     '1', '2', '3',
     '0', '.', '-',
-    '\x1F', '\x1E', '\xB6',
+    '\x19', '\x18', '\xB6',
 };
-const char KeyPadKeys[] = { '7', '8', '9', '4', '5', '6', '1', '2', '3',
-     '0', '.', '-', '<', '>', '\n' };
+const char KeyPadKeys[] = { 
+              '\x08', 
+    '7', '8', '9', 
+    '4', '5', '6', 
+    '1', '2', '3',
+    '0', '.', '-', 
+    '<', '>', '\n' };
 
 
 // ##### Settings  #############################################################
@@ -136,9 +147,9 @@
 // |                                                Progam Name and version    |
 // |                                                Manufacturer name          |
 // |                                                                           |
-// |                                                                           |
-// |                                                                           |
-// |                                                                           |
+// |                                                                  \ | /    |
+// |                                                                  = O =    |
+// |                                                                  / | \    |
 // |                                                                +--------+ |
 // |                                                                |        | |
 // |                                                                |        | |
@@ -162,6 +173,19 @@
 const rect_t sungraph = { 450-20,100+0, 450+20,265+0 };
 const rect_t inrgraph = { 450-18,100+2, 450+18,265-2 };
 
+#define PI 3.1415       // Handy value
+
+
+SignalGenDisplay::SignalGenDisplay(RA8875 * _lcd, SignalGenDAC * _signal,
+    const char * _ProgName, const char * _Manuf, const char * _Ver, const char * _Build) :
+    lcd(_lcd), signal(_signal), ProgName(_ProgName), Manuf(_Manuf), Ver(_Ver), Build(_Build) {
+    needsInit = true;
+}
+
+
+SignalGenDisplay::~SignalGenDisplay() {
+}
+
 
 template <typename T> int sgn(T val) {
     return (T(0) < val) - (val < T(0));
@@ -183,10 +207,9 @@
             timer.reset();
             switch (vis) {
                 case VS_MainScreen:
-printf("touch [vis: %d] (%d,%d)\r\n", vis, point.x, point.y);
                     // Mode Keys touch
-                    for (int i=0; i<ButtonCount; i++) {
-                        if (lcd->Intersect(UI_Buttons[i], point)) {
+                    for (int i=0; i<ModeCount; i++) {
+                        if (lcd->Intersect(ModeButtons[i], point)) {
                             return ModeKeys[i];
                         }
                     }
@@ -205,9 +228,8 @@
                     }
                     
                     if (lcd->Intersect(NavToSettings, point)) {
-printf("Nav\r\n");
                         vis = VS_Settings;
-                        Init();
+                        Refresh();
                         while (lcd->TouchPanelReadable())
                             ;
                         Thread::wait(100);
@@ -215,22 +237,20 @@
                     break;
                 case VS_Settings:
                     Thread::wait(20);
-printf("touch [VIS: %d\r\n", vis);
                     if (lcd->Intersect(sungraph, point)) {
                         float bl = (float)(sungraph.p2.y - point.y)/(sungraph.p2.y - sungraph.p1.y);
                         lcd->Backlight(rangelimit(bl, 0.1, 1.0));
                         ShowBrightnessSetting();
                     }
                     if (lcd->Intersect(NavToSettings, point)) {
-                        // Save settings
+                        // Save Backlight settings on screen change
                         char buf[20];
-                        
                         snprintf(buf, sizeof(buf), "%d", lcd->GetBacklight_u8());
                         ini.WriteString("Settings", "Backlight", buf);
                         
                         // Switch to main screen
                         vis = VS_MainScreen;
-                        Init();
+                        Refresh();
                         while (lcd->TouchPanelReadable())
                             ;
                         Thread::wait(100);
@@ -244,18 +264,36 @@
 }
 
 
-SignalGenDisplay::SignalGenDisplay(RA8875 * _lcd, SignalGenerator * _signal,
-    const char * _ProgName, const char * _Manuf, const char * _Ver, const char * _Build) :
-    lcd(_lcd), signal(_signal), ProgName(_ProgName), Manuf(_Manuf), Ver(_Ver), Build(_Build) {
-    vis = VS_MainScreen;
-}
-
-
-SignalGenDisplay::~SignalGenDisplay() {
-}
-
-
-void SignalGenDisplay::Init() {
+void SignalGenDisplay::Refresh() {
+    if (needsInit) {
+        char buf[100];
+        
+        needsInit = false;
+        vis = VS_MainScreen;    // always start on main screen
+        
+        // Default the backlight
+        ini.ReadString("Settings", "Backlight", buf, sizeof(buf), "60");
+        lcd->Backlight_u8(atoi(buf));
+    
+        ini.ReadString("Signal", "Waveform", buf, sizeof(buf), "Sine");
+        for (int i=0; i<ModeCount; i++) {
+            if (strcmp(ModeNames[i], buf) == 0) {
+                mode = (SG_Mode)i;
+                break;
+            }
+        }
+        ini.ReadString("Signal", "Duty Cycle", buf, sizeof(buf), "50");
+        dutycycle = atof(buf);
+    
+        ini.ReadString("Signal", "Frequency", buf, sizeof(buf), "1000");
+        frequency = atof(buf);
+    
+        ini.ReadString("Signal", "Voltage", buf, sizeof(buf), "3.0");
+        voltage = atof(buf);
+    
+        ini.ReadString("Signal", "Offset", buf, sizeof(buf), "1.5");
+        offset = atof(buf);
+    }
     switch (vis) {
         case VS_MainScreen:
             lcd->background(UI_BackColor);
@@ -269,15 +307,9 @@
             ShowProductInfo();
             
             ClearScope();
-            // Some defaults for testing 
-            SetDutyCycle(30);
-            SetFrequency(1230.0);
-            SetVoltagePeakToPeak(3.0);
-            SetVoltageOffset(1.50);
             resetDataEntry();
-            SelectWaveformMode(SignalGenDisplay::SG_SINE);
-            DrawKeypadEnabled(false);
             DrawNavGadget();
+            DrawModeButtons();
             break;
             
         case VS_Settings:
@@ -288,11 +320,20 @@
             lcd->foreground(UI_ProductNameColor);
             ShowProductInfo();
             ShowBrightnessSetting();
-            DrawNavGadget();            
+            DrawNavGadget();
+            DrawModeButtons();
             break;
     }
 }
 
+
+void SignalGenDisplay::DrawModeButtons(void) {
+    for (int i=0; i<ModeCount; i++) {
+        DrawButton(ModeButtons[i], (UI_ModeList[i] == mode) ? true : false, UI_ModeList[i], true);
+    }
+    UpdateScope();
+}
+
 void SignalGenDisplay::DrawNavGadget(void) {
     lcd->fillrect(NavToSettings, Black);
     lcd->SetTextCursor(NavToSettings.p1.x+1, NavToSettings.p1.y+1);
@@ -324,9 +365,29 @@
     lcd->fillrect(inrgraph.p1.x,inrgraph.p2.y, inrgraph.p2.x, inrgraph.p2.y - bl * (inrgraph.p2.y - inrgraph.p1.y), White);
 }
 
-SignalGenDisplay::SG_Changes SignalGenDisplay::Poll(char c) {
-    SG_Changes ret = SG_NONE;
+void SignalGenDisplay::ShowMenu(void) {
+    if (Manuf) {
+        printf("\r\n%s v%s by %s build %s\r\n\r\n", ProgName, Ver, Manuf, Build);
+    }
+    printf(" Select:                  Signal:\r\n");
+    printf("      S:  Sine Wave            d:  Duty Cycle\r\n");
+    printf("      Q:  Square Wave          f:  Frequency\r\n");
+    printf("      T:  Triangle Wave        p:  Period\r\n");
+    printf("      W:  Sawtooth Wave        v:  Voltage\r\n");
+    printf("      U:  User Wave            o:  Offset\r\n");
+    printf("                                   \r\n");
+    printf("                        0-9 . - :  Numeric entry\r\n");
+    printf("                            < > :  Modify selected signal\r\n");
+    printf("                            <bs>:  Backspace entry\r\n");
+    printf("      ?: This help          <cr>:  Save number\r\n");
+    printf("      #: Dump RA8875       <esc>:  Exit number entry\r\n");
+    //printf("  4:  Reverse sawtoothSignal\r\n");
+}
+
+SignalGenDisplay::OM_Changes SignalGenDisplay::Poll(char c) {
+    OM_Changes ret = OM_NONE;
     
+    SaveSettings();
     if (!c) {
         c = GetTouchEvent();
     }
@@ -343,31 +404,45 @@
     ///     - <esc>     abandon numeric entry
     ///     - <nul>     do nothing, just poll
     switch (c) {
+        case '#':
+            lcd->DumpRegisters();
+            break;
         case '?':
             ShowMenu();
             break;
         case 'S':
-            SelectWaveformMode(SG_SINE);
-            signal->SetSignalFrequency(SignalGenerator::SinusSignal, frequency);
+            if (mode != SG_SINE)
+                SaveSettings(OM_MODE);
+            SetWaveformMode(SG_SINE);
+            signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
             //ret = SG_SINE;
             break;
         case 'Q':
-            SelectWaveformMode(SG_SQUARE);
-            signal->SetSignalFrequency(SignalGenerator::SquareSignal, frequency);
+            if (mode != SG_SQUARE)
+                SaveSettings(OM_MODE);
+            SetWaveformMode(SG_SQUARE);
+            signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
             //ret = SG_SQUARE;
             break;
         case 'T':
-            SelectWaveformMode(SG_TRIANGLE);
-            signal->SetSignalFrequency(SignalGenerator::TriangleSignal, frequency);
+            if (mode != SG_TRIANGLE)
+                SaveSettings(OM_MODE);
+            SetWaveformMode(SG_TRIANGLE);
+            signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
             //ret = SG_TRIANGLE;
             break;
         case 'W':
-            SelectWaveformMode(SG_SAWTOOTH);
-            signal->SetSignalFrequency(SignalGenerator::SawtoothSignal, frequency);
+            if (mode != SG_SAWTOOTH)
+                SaveSettings(OM_MODE);
+            SetWaveformMode(SG_SAWTOOTH);
+            signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
             //ret = SG_SAWTOOTH;
             break;
         case 'U':
-            SelectWaveformMode(SG_USER);
+            if (mode != SG_USER)
+                SaveSettings(OM_MODE);
+            SetWaveformMode(SG_USER);
+            signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
             //ret = SG_USER;
             break;
         case '0':
@@ -397,12 +472,13 @@
                     textBuffer[textLen] = '\0';
                     updateTextWindow();
                 }
+                if (textLen == 0)
+                    clearTextWindow();
             }
             break;
         case '\x1B':
             textBuffer[0] = '\0';
             textLen = 0;
-            EntryMd = SG_NONE;
             resetDataEntry();
             break;
         case '\r':
@@ -410,45 +486,64 @@
             if (EntryMd) {
                 if (strlen(textBuffer)) {
                     switch (EntryMd) {
-                        case SG_DUTY:
+                        case OM_DUTY:
                             SetDutyCycle(atof(textBuffer));
+                            signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
+                            SaveSettings(OM_DUTY);
                             break;
-                        case SG_FREQ:
+                        case OM_FREQ:
                             SetFrequency(atof(textBuffer));
+                            signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
+                            SaveSettings(OM_FREQ);
                             break;
-                        case SG_PERI:
+                        case OM_PERI:
                             SetPeriod(atof(textBuffer));
+                            signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
+                            SaveSettings(OM_FREQ);
                             break;
-                        case SG_VOLT:
+                        case OM_VOLT:
                             SetVoltagePeakToPeak(atof(textBuffer));
+                            signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
+                            SaveSettings(OM_VOLT);
                             break;
-                        case SG_OFFS:
+                        case OM_OFFS:
                             SetVoltageOffset(atof(textBuffer));
+                            signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
+                            SaveSettings(OM_OFFS);
                             break;
                         default:
                             break;
                     }
                 }
-                EntryMd = SG_NONE;
-                resetDataEntry();
+                resetDataEntry(OM_NONE, true);
             }
             break;
         case '>':
             switch (EntryMd) {
-                case SG_DUTY:
+                case OM_DUTY:
                     SetDutyCycle(dutycycle + 1.0);
+                    signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
+                    SaveSettings(OM_DUTY);
                     break;
-                case SG_FREQ:
+                case OM_FREQ:
                     SetFrequency(frequency + 1.0);
+                    signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
+                    SaveSettings(OM_FREQ);
                     break;
-                case SG_PERI:
-                    SetPeriod(1/frequency + 0.001);
+                case OM_PERI:
+                    SetPeriod(1/frequency + 0.000001);
+                    signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
+                    SaveSettings(OM_FREQ);
                     break;
-                case SG_VOLT:
+                case OM_VOLT:
                     SetVoltagePeakToPeak(voltage + 0.1);
+                    signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
+                    SaveSettings(OM_VOLT);
                     break;
-                case SG_OFFS:
-                    SetVoltageOffset(offset + 0.01);
+                case OM_OFFS:
+                    SetVoltageOffset(offset + 0.1);
+                    signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
+                    SaveSettings(OM_OFFS);
                     break;
                 default:
                     break;
@@ -456,78 +551,83 @@
             break;
         case '<':
             switch (EntryMd) {
-                case SG_DUTY:
+                case OM_DUTY:
                     SetDutyCycle(dutycycle - 1.0);
+                    signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
+                    SaveSettings(OM_DUTY);
                     break;
-                case SG_FREQ:
+                case OM_FREQ:
                     SetFrequency(frequency - 1.0);
+                    signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
+                    SaveSettings(OM_FREQ);
                     break;
-                case SG_PERI:
-                    SetPeriod(1/frequency - 0.001);
+                case OM_PERI:
+                    SetPeriod(1/frequency - 0.000001);
+                    signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
+                    SaveSettings(OM_FREQ);
                     break;
-                case SG_VOLT:
+                case OM_VOLT:
                     SetVoltagePeakToPeak(voltage - 0.1);
+                    signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
+                    SaveSettings(OM_VOLT);
                     break;
-                case SG_OFFS:
-                    SetVoltageOffset(offset - 0.01);
+                case OM_OFFS:
+                    SetVoltageOffset(offset - 0.1);
+                    signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
+                    SaveSettings(OM_OFFS);
                     break;
                 default:
                     break;
             }
             break;
         case 'd':
-            if (EntryMd != SG_DUTY) {
-                resetDataEntry();
-                EntryMd = SG_DUTY;
-                DrawKeypadEnabled(true);
+            if (EntryMd != OM_DUTY) {
+                SaveSettings(EntryMd);
+                resetDataEntry(OM_DUTY, true);
                 updateDutyCycle();
+                signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
             } else {
-                EntryMd = SG_NONE;
-                resetDataEntry();
+                resetDataEntry(OM_NONE, true);
             }
             break;
         case 'f':
-            if (EntryMd != SG_FREQ) {
-                resetDataEntry();
-                EntryMd = SG_FREQ;
-                DrawKeypadEnabled(true);
+            if (EntryMd != OM_FREQ) {
+                SaveSettings(EntryMd);
+                resetDataEntry(OM_FREQ, true);
                 updateFrequency();
+                signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
             } else {
-                EntryMd = SG_NONE;
-                resetDataEntry();
+                resetDataEntry(OM_NONE, true);
             }
             break;
         case 'p':
-            if (EntryMd != SG_PERI) {
-                resetDataEntry();
-                EntryMd = SG_PERI;
-                DrawKeypadEnabled(true);
+            if (EntryMd != OM_PERI) {
+                SaveSettings(EntryMd);
+                resetDataEntry(OM_PERI, true);
                 updatePeriod();
+                signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
             } else {
-                EntryMd = SG_NONE;
-                resetDataEntry();
+                resetDataEntry(OM_NONE, true);
             }
             break;
         case 'v':
-            if (EntryMd != SG_VOLT) {
-                resetDataEntry();
-                EntryMd = SG_VOLT;
-                DrawKeypadEnabled(true);
+            if (EntryMd != OM_VOLT) {
+                SaveSettings(EntryMd);
+                resetDataEntry(OM_VOLT, true);
                 updateVoltage();
+                signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
             } else {
-                EntryMd = SG_NONE;
-                resetDataEntry();
+                resetDataEntry(OM_NONE, true);
             }
             break;
         case 'o':
-            if (EntryMd != SG_OFFS) {
-                resetDataEntry();
-                EntryMd = SG_OFFS;
-                DrawKeypadEnabled(true);
+            if (EntryMd != OM_OFFS) {
+                SaveSettings(EntryMd);
+                resetDataEntry(OM_OFFS, true);
                 updateOffset();
+                signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset);
             } else {
-                EntryMd = SG_NONE;
-                resetDataEntry();
+                resetDataEntry(OM_NONE, true);
             }
             break;
         default:
@@ -536,13 +636,10 @@
     return ret;
 }
 
-bool SignalGenDisplay::SelectWaveformMode(SG_Mode _mode) {
+bool SignalGenDisplay::SetWaveformMode(SG_Mode _mode, bool force) {
     if (/* _mode >= SG_SINE && */ _mode <= SG_USER) {
-        mode= _mode;
-        for (int i=0; i<ButtonCount; i++) {
-            DrawButton(UI_Buttons[i], (UI_ModeList[i] == mode) ? true : false, UI_ModeList[i], true);
-        }
-        UpdateScope();
+        mode = _mode;
+        DrawModeButtons();
         return true;
     } else {
         return false;
@@ -561,16 +658,13 @@
 }
 
 bool SignalGenDisplay::SetFrequency(float _frequency) {
-    printf("-> SetFrequency(%f)\r\n", _frequency);
     if (_frequency >= 1.0 && _frequency <= 1.0E6) {
         frequency = _frequency;
         updateFrequency();
         updatePeriod();
         UpdateScope();
-        printf("   end SetFrequency\r\n");
         return true;
     } else {
-        printf("   end SetFrequency - out of range\r\n");
         return false;
     }
 }
@@ -614,36 +708,40 @@
 // ########################   Private Methods past here #######################
 
 void SignalGenDisplay::UpdateScope(void) {
-    printf("-> UpdateScope()\r\n");
     ClearScope();
-    rect_t r;
+    rect_t r = UI_WAVE_RECT;
 
-    r.p1.x = UI_SCOPE_RECT.p1.x + SC_LEFT_MARGIN;
-    r.p1.y = UI_SCOPE_RECT.p1.y + SC_TOP_MARGIN;
-    r.p2.x = UI_SCOPE_RECT.p2.x - SC_RIGHT_MARGIN;
-    r.p2.y = UI_SCOPE_RECT.p2.y - SC_BOT_MARGIN;
-    lcd->rect(r, WaveOutlineColor);
+    float vPeakPos, vPeakNeg;
+    dim_t waveHeight = (UI_WAVE_RECT.p2.y - UI_WAVE_RECT.p1.y);
+    vPeakPos = rangelimit(offset + voltage/2, SG_MIN_V, SG_MAX_V);
+    vPeakNeg = rangelimit(offset - voltage/2, SG_MIN_V, SG_MAX_V);
+    loc_t markerPos_y = UI_WAVE_RECT.p2.y - vPeakPos/(SG_MAX_V-SG_MIN_V) * waveHeight;
+    loc_t markerNeg_y = UI_WAVE_RECT.p2.y - vPeakNeg/(SG_MAX_V-SG_MIN_V) * waveHeight;
+    loc_t df = rangelimit(offset, SG_MIN_V, SG_MAX_V) / SG_MAX_V * (r.p2.y - r.p1.y);
+    loc_t y;
+
+    // Draw the Waveform rectangle
+    lcd->rect(UI_WAVE_RECT, WaveOutlineColor);
     
     // Draw the Peak to Peak markers
-    lcd->line(r.p1.x,r.p1.y, r.p2.x+3*SC_RIGHT_MARGIN/4,r.p1.y, UI_VP2PColor);
-    lcd->line(r.p1.x,r.p2.y, r.p2.x+3*SC_RIGHT_MARGIN/4,r.p2.y, UI_VP2PColor);
-    lcd->line(r.p2.x+3*SC_RIGHT_MARGIN/4-3,r.p1.y, r.p2.x+3*SC_RIGHT_MARGIN/4-3,r.p2.y, UI_VP2PColor);
-    lcd->filltriangle(
-        r.p2.x+3*SC_RIGHT_MARGIN/4-3,r.p1.y,
-        r.p2.x+3*SC_RIGHT_MARGIN/4-3+2,r.p1.y+3,
-        r.p2.x+3*SC_RIGHT_MARGIN/4-3-2,r.p1.y+3,
+    lcd->line(UI_WAVE_RECT.p1.x-3,markerPos_y, UI_WAVE_RECT.p2.x+3*SC_RIGHT_MARGIN/4,markerPos_y,   UI_VP2PColor);
+    lcd->line(UI_WAVE_RECT.p1.x-3,markerNeg_y, UI_WAVE_RECT.p2.x+3*SC_RIGHT_MARGIN/4,markerNeg_y,   UI_VP2PColor);
+    lcd->line(r.p2.x+3*SC_RIGHT_MARGIN/4-3,markerPos_y, r.p2.x+3*SC_RIGHT_MARGIN/4-3,markerNeg_y, UI_VP2PColor);  // vert
+    lcd->filltriangle(                              // top arrowhead
+        r.p2.x+3*SC_RIGHT_MARGIN/4-3,  markerPos_y,
+        r.p2.x+3*SC_RIGHT_MARGIN/4-3+2,markerPos_y+3,
+        r.p2.x+3*SC_RIGHT_MARGIN/4-3-2,markerPos_y+3,
         UI_VP2PColor);
-    lcd->filltriangle(
-        r.p2.x+3*SC_RIGHT_MARGIN/4-3,r.p2.y,
-        r.p2.x+3*SC_RIGHT_MARGIN/4-3+2,r.p2.y-3,
-        r.p2.x+3*SC_RIGHT_MARGIN/4-3-2,r.p2.y-3,
+    lcd->filltriangle(                              // bottom arrowhead
+        r.p2.x+3*SC_RIGHT_MARGIN/4-3,  markerNeg_y,
+        r.p2.x+3*SC_RIGHT_MARGIN/4-3+2,markerNeg_y-3,
+        r.p2.x+3*SC_RIGHT_MARGIN/4-3-2,markerNeg_y-3,
         UI_VP2PColor);
 
     // Draw the offset voltage markers
-    loc_t y = (r.p1.y + r.p2.y)/2;
+    y = r.p2.y - df;
     dim_t w = (r.p2.x + SC_RIGHT_MARGIN/3 - r.p1.x) / 35;
-    dim_t h = (r.p2.y - r.p1.y);
-    for (int i=0; i<=35+1; i++) {
+    for (int i=0; i<=35+1; i++) {       // dashed line
         if ((i & 1) == 0) {
             lcd->line(r.p1.x + i * w,y, r.p1.x + (i+1) * w, y, UI_VOffsetColor);
         }
@@ -654,24 +752,21 @@
             break;
         case -1:
         case 1:
-            lcd->line(r.p2.x+SC_RIGHT_MARGIN/3-3,y+sgn(offset)*(h/2+7), r.p2.x+SC_RIGHT_MARGIN/3-3,y, UI_VOffsetColor);
+            lcd->line(r.p2.x+SC_RIGHT_MARGIN/3-3,r.p2.y, r.p2.x+SC_RIGHT_MARGIN/3-3,y, UI_VOffsetColor); // vert
             lcd->filltriangle(
                 r.p2.x+SC_RIGHT_MARGIN/3-3,y, 
                 r.p2.x+SC_RIGHT_MARGIN/3-3+2,y+sgn(offset)*3, 
                 r.p2.x+SC_RIGHT_MARGIN/3-3-2,y+sgn(offset)*3, 
                 UI_VOffsetColor);
-            if (abs(offset) > voltage/2)
-                lcd->line(r.p1.x,y+sgn(offset)*(h/2+7), r.p2.x+SC_RIGHT_MARGIN/3,y+sgn(offset)*(h/2+7), UI_VOffsetColor);
-            else
-                lcd->line(r.p1.x,y+(offset/voltage)*h, r.p2.x+SC_RIGHT_MARGIN/3,y+(offset/voltage)*h, UI_VOffsetColor);
+            lcd->line(r.p2.x,r.p2.y, r.p2.x+SC_RIGHT_MARGIN/3,r.p2.y, UI_VOffsetColor);   // horz
             break;
     }
     
     // Draw the Frequency marker
     w = r.p2.x - r.p1.x;
     dim_t dc = dutycycle/100.0 * 1*w/2;
-    lcd->line(r.p1.x,r.p1.y, r.p1.x,r.p2.y+3*SC_BOT_MARGIN/4, UI_FreqColor);
-    lcd->line(r.p1.x+1*w/2,r.p1.y, r.p1.x+1*w/2,r.p2.y+3*SC_BOT_MARGIN/4, UI_FreqColor);
+    lcd->line(r.p1.x,r.p1.y-3, r.p1.x,r.p2.y+3*SC_BOT_MARGIN/4, UI_FreqColor);
+    lcd->line(r.p1.x+1*w/2,r.p1.y-3, r.p1.x+1*w/2,r.p2.y+3*SC_BOT_MARGIN/4, UI_FreqColor);
     lcd->line(r.p1.x,r.p2.y+3*SC_BOT_MARGIN/4-3, r.p1.x+1*w/2,r.p2.y+3*SC_BOT_MARGIN/4-3, UI_FreqColor);
     lcd->filltriangle(
         r.p1.x+0,r.p2.y+3*SC_BOT_MARGIN/4-3,
@@ -685,8 +780,8 @@
         UI_FreqColor);
     
     // Draw the Duty Cycle markers
-    lcd->line(r.p1.x,r.p1.y, r.p1.x,r.p2.y+2*SC_BOT_MARGIN/4, UI_DutyColor);
-    lcd->line(r.p1.x + dc,r.p1.y, r.p1.x + dc,r.p2.y+2*SC_BOT_MARGIN/4, UI_DutyColor);
+    lcd->line(r.p1.x,r.p1.y-3, r.p1.x,r.p2.y+2*SC_BOT_MARGIN/4, UI_DutyColor);
+    lcd->line(r.p1.x + dc,r.p1.y-3, r.p1.x + dc,r.p2.y+2*SC_BOT_MARGIN/4, UI_DutyColor);
     point_t p;
     p.x = r.p1.x;
     p.y = r.p2.y+2*SC_BOT_MARGIN/4-3;
@@ -703,62 +798,82 @@
         p.x-3,p.y+2,
         UI_DutyColor);
     DrawWaveform(r, mode, White);
-    printf("   end UpdateScope()\r\n");
 }
 
+
 //       ++           +----+            +             +
 //      .  .          |    |           / \           /|
 //     .    .         |    |    |     /   \   /     / |
 //           .             |    |          \ /     /  |
 //            ++           +----+           +     +   +
 //
-void SignalGenDisplay::DrawWaveform(rect_t r, SG_Mode mode, color_t color, float dutycycleOverride) {
+void SignalGenDisplay::DrawWaveform(rect_t r, SG_Mode mode, color_t color, bool drawPure) {
     loc_t x,y;
     loc_t y0 = (r.p1.y + r.p2.y)/2;
     dim_t w = r.p2.x - r.p1.x;
-    dim_t dc = ((dutycycleOverride >= 5.0) ? dutycycleOverride : dutycycle)/100.0 * 1*w/2;
+    dim_t h = r.p2.y - r.p1.y;
+    dim_t privDutyCycleX;
     dim_t a = (r.p2.y - r.p1.y)/2;
+    float privVoltage = voltage;
+    float privOffset = offset;
+    float privDutyCycle = dutycycle;
+    float vRange = SG_MAX_V - SG_MIN_V;
     float v;
-    
+
+    if (drawPure) {
+        privVoltage = vRange;
+        privOffset = vRange/2;
+        privDutyCycle = 50;
+    }
+    privDutyCycleX = privDutyCycle/100.0 * 1*w/2;
     switch (mode) {
         case SG_SINE:
             for (int cycle=0; cycle<2; cycle++) {
-                for (x=0; x<=dc; x++) {
-                    v = offset + voltage/2 * sin(x * 1 * PI / dc);
+                for (x=0; x<=privDutyCycleX; x++) {
+                    v = privOffset + privVoltage/2 * sin(x * 1 * PI / privDutyCycleX);
                     v = rangelimit(v, SG_MIN_V, SG_MAX_V);
-                    y = r.p2.y - 2 * a * v / SG_AOUT_FS;
+                    y = r.p2.y - 2 * a * v / vRange;
                     lcd->pixel(r.p1.x + cycle * w/2 + x, y, color);
                 }
-                for (x=0; x<=(w/2-dc); x++) {
-                    v = offset - voltage/2 * sin(x * 1 * PI / (w/2-dc));
+                for (x=0; x<=(w/2-privDutyCycleX); x++) {
+                    v = privOffset - privVoltage/2 * sin(x * 1 * PI / (w/2-privDutyCycleX));
                     v = rangelimit(v, SG_MIN_V, SG_MAX_V);
-                    y = r.p2.y - 2 * a * v / SG_AOUT_FS;
-                    lcd->pixel(r.p1.x + cycle * w/2 + dc + x, y, color);
+                    y = r.p2.y - 2 * a * v / vRange;
+                    lcd->pixel(r.p1.x + cycle * w/2 + privDutyCycleX + x, y, color);
                 }
             }
             break;
         case SG_SQUARE:
             for (int cycle=0; cycle<2; cycle++) {
-                lcd->line(r.p1.x+cycle*w/2+0*w/8, y0, r.p1.x+cycle*w/2+0*w/8, y0-a, color);   // rise
-                lcd->line(r.p1.x+cycle*w/2+0*w/8, y0-a, r.p1.x+cycle*w/2+dc, y0-a, color);      // horz
-                lcd->line(r.p1.x+cycle*w/2+dc, y0-a, r.p1.x+cycle*w/2+dc, y0+a, color);         // fall
-                lcd->line(r.p1.x+cycle*w/2+dc, y0+a, r.p1.x+cycle*w/2+4*w/8, y0+a, color);      // horz
-                lcd->line(r.p1.x+cycle*w/2+4*w/8, y0+a, r.p1.x+cycle*w/2+4*w/8, y0, color);   // rise
+                loc_t mid = r.p2.y - rangelimit(privOffset, SG_MIN_V, SG_MAX_V) / vRange * h;
+                loc_t upp = r.p2.y - rangelimit(privOffset + privVoltage/2, SG_MIN_V, SG_MAX_V) / vRange * h;
+                loc_t low = r.p2.y - rangelimit(privOffset - privVoltage/2, SG_MIN_V, SG_MAX_V) / vRange * h;
+                lcd->line(r.p1.x+cycle*w/2+0*w/8, mid, r.p1.x+cycle*w/2+0*w/8, upp, color);   // rise
+                lcd->line(r.p1.x+cycle*w/2+0*w/8, upp, r.p1.x+cycle*w/2+privDutyCycleX, upp, color);      // horz
+                lcd->line(r.p1.x+cycle*w/2+privDutyCycleX, upp, r.p1.x+cycle*w/2+privDutyCycleX, low, color);         // fall
+                lcd->line(r.p1.x+cycle*w/2+privDutyCycleX, low, r.p1.x+cycle*w/2+4*w/8, low, color);      // horz
+                lcd->line(r.p1.x+cycle*w/2+4*w/8, low, r.p1.x+cycle*w/2+4*w/8, mid, color);   // rise
             }
             break;
         case SG_TRIANGLE:
             for (int cycle=0; cycle<2; cycle++) {
-                lcd->line(r.p1.x+cycle*w/2+0*w/8, y0+0, r.p1.x+cycle*w/2+dc/2,  y0-a, color);   // rise 2
-                lcd->line(r.p1.x+cycle*w/2+dc/2,  y0-a, r.p1.x+cycle*w/2+dc/1,  y0,  color);    // fall 1
-                lcd->line(r.p1.x+cycle*w/2+dc/1,  y0,   r.p1.x+cycle*w/2+(w/2+dc)/2, y0+a, color);   // fall 2
-                lcd->line(r.p1.x+cycle*w/2+(w/2+dc)/2, y0+a, r.p1.x+cycle*w/2+4*w/8, y0, color);   // rise 1
+                loc_t mid = r.p2.y - rangelimit(privOffset, SG_MIN_V, SG_MAX_V) / vRange * h;
+                loc_t upp = r.p2.y - rangelimit(privOffset + privVoltage/2, SG_MIN_V, SG_MAX_V) / vRange * h;
+                loc_t low = r.p2.y - rangelimit(privOffset - privVoltage/2, SG_MIN_V, SG_MAX_V) / vRange * h;
+                lcd->line(r.p1.x+cycle*w/2+0*w/8, mid, r.p1.x+cycle*w/2+privDutyCycleX/2,  upp, color);                 // rise 2
+                lcd->line(r.p1.x+cycle*w/2+privDutyCycleX/2,  upp, r.p1.x+cycle*w/2+privDutyCycleX/1, mid,  color);     // fall 1
+                lcd->line(r.p1.x+cycle*w/2+privDutyCycleX/1,  mid,   r.p1.x+cycle*w/2+(w/2+privDutyCycleX)/2, low, color);  // fall 2
+                lcd->line(r.p1.x+cycle*w/2+(w/2+privDutyCycleX)/2, low, r.p1.x+cycle*w/2+4*w/8, mid, color);            // rise 1
             }
             break;
         case SG_SAWTOOTH:
             for (int cycle=0; cycle<2; cycle++) {
-                lcd->line(r.p1.x+cycle*w/2+0*w/8+0, y0+a, r.p1.x+cycle*w/2+dc,      y0, color);
-                lcd->line(r.p1.x+cycle*w/2+dc,      y0,   r.p1.x+cycle*w/2+4*w/8-1, y0-a, color);
-                lcd->line(r.p1.x+cycle*w/2+4*w/8-1, y0-a, r.p1.x+cycle*w/2+4*w/8,   y0+a, color);
+                loc_t mid = r.p2.y - rangelimit(privOffset, SG_MIN_V, SG_MAX_V) / vRange * h;
+                loc_t upp = r.p2.y - rangelimit(privOffset + privVoltage/2, SG_MIN_V, SG_MAX_V) / vRange * h;
+                loc_t low = r.p2.y - rangelimit(privOffset - privVoltage/2, SG_MIN_V, SG_MAX_V) / vRange * h;
+                lcd->line(r.p1.x+cycle*w/2+0*w/8+0, low, r.p1.x+cycle*w/2+privDutyCycleX,     mid, color);
+                lcd->line(r.p1.x+cycle*w/2+privDutyCycleX,    mid,   r.p1.x+cycle*w/2+4*w/8-1, upp, color);
+                lcd->line(r.p1.x+cycle*w/2+4*w/8-1, upp, r.p1.x+cycle*w/2+4*w/8,   low, color);
             }
             break;
         case SG_USER:
@@ -781,7 +896,7 @@
     rect_t r = Parameters[0];   // UI_DUTY_CYCLE_RECT;
     color_t fcolor, bcolor;
     
-    if (EntryMd != SG_DUTY) {
+    if (EntryMd != OM_DUTY) {
         fcolor = UI_DutyColor;
         bcolor = UI_ScopeBackColor;
     } else {
@@ -799,7 +914,7 @@
     rect_t r = Parameters[1];   // UI_FREQ_RECT;
     color_t fcolor, bcolor;
     
-    if (EntryMd != SG_FREQ) {
+    if (EntryMd != OM_FREQ) {
         fcolor = UI_FreqColor;
         bcolor = UI_ScopeBackColor;
     } else {
@@ -810,7 +925,6 @@
     lcd->foreground(fcolor);
     lcd->background(bcolor);
     lcd->SetTextCursor(r.p1.x+1, r.p1.y+1);
-    printf("EntryMode: %d, bg: %08X, fg: %08X\r\n", EntryMd, bcolor, fcolor);
     if (frequency >= 1000.0)
         lcd->printf("%8.3f kHz", frequency/1000);
     else
@@ -822,7 +936,7 @@
     rect_t r = Parameters[2];   // UI_PERIOD_RECT;
     color_t fcolor, bcolor;
 
-    if (EntryMd != SG_PERI) {
+    if (EntryMd != OM_PERI) {
         fcolor = UI_FreqColor;
         bcolor = UI_ScopeBackColor;
     } else {
@@ -843,7 +957,7 @@
     rect_t r = Parameters[3];   // UI_VP2P_RECT;
     color_t fcolor, bcolor;
 
-    if (EntryMd != SG_VOLT) {
+    if (EntryMd != OM_VOLT) {
         fcolor = UI_VP2PColor;
         bcolor = UI_ScopeBackColor;
     } else {
@@ -861,7 +975,7 @@
     rect_t r = Parameters[4];   // UI_VOFFSET_RECT;
     color_t fcolor, bcolor;
 
-    if (EntryMd != SG_OFFS) {
+    if (EntryMd != OM_OFFS) {
         fcolor = UI_VOffsetColor;
         bcolor = UI_ScopeBackColor;
     } else {
@@ -922,7 +1036,7 @@
         case SG_TRIANGLE:
         case SG_SAWTOOTH:
         case SG_USER:
-            DrawWaveform(wave, mode, Black, 50.0);
+            DrawWaveform(wave, mode, Black, true);
             break;
         case SG_KEYPAD:
             lcd->foreground(Black);
@@ -943,6 +1057,12 @@
     lcd->window();
 }
 
+void SignalGenDisplay::clearTextWindow(void) {
+    lcd->fillrect(UI_DATA_ENTRY, UI_BackColor);
+    textBuffer[0] = '\0';
+    textLen = 0;
+}
+
 float SignalGenDisplay::rangelimit(float value, float min, float max) {
     if (value < min)
         return min;
@@ -952,60 +1072,106 @@
         return value;
 }
 
+void SignalGenDisplay::SaveSettings(OM_Changes reportMode) {
+    char buf[20];
 
-void SignalGenDisplay::ShowMenu(void) {
-    if (Manuf) {
-        printf("\r\n%s v%s by %s build %s\r\n", ProgName, Ver, Manuf, Build);
+    if (reportMode != OM_NONE) {
+        Changes |= reportMode;
+        printf("SaveSettings - reset timer [%02X]\r\n", Changes);
+        timerSave.reset();
+        timerSave.start();
+    } else if (timerSave.read() > SAVE_AFTER_IDLE_S) {
+        timerSave.stop();
+        timerSave.reset();
+        printf("SaveSettings - timeout [%02X]\r\n", Changes);
+        if (Changes & OM_MODE) {
+            Changes &= ~ OM_MODE;
+            ini.WriteString("Signal", "Waveform", ModeNames[mode]);
+        }
+        if (Changes & OM_FREQ) {
+            Changes &= ~ OM_FREQ;
+            snprintf(buf, sizeof(buf),"%5.3f", frequency);
+            printf("  Signal:Frequency=%s\r\n", buf);
+            ini.WriteString("Signal", "Frequency", buf);
+        }
+        if (Changes & OM_PERI) {
+            Changes &= ~ OM_PERI;
+            snprintf(buf, sizeof(buf),"%5.3f", frequency);
+            printf("  Signal:Frequency=%s\r\n", buf);
+            ini.WriteString("Signal", "Frequency", buf);
+        }
+        if (Changes & OM_DUTY) {
+            Changes &= ~ OM_DUTY;
+            snprintf(buf, sizeof(buf),"%1.0f", dutycycle);
+            printf("  Signal:Duty Cycle=%s\r\n", buf);
+            ini.WriteString("Signal", "Duty Cycle", buf);
+        }
+        if (Changes & OM_VOLT) {
+            Changes &= ~ OM_VOLT;
+            snprintf(buf, sizeof(buf),"%3.2f", voltage);
+            printf("  Signal:Voltage=%s\r\n", buf);
+            ini.WriteString("Signal", "Voltage", buf);
+        }
+        if (Changes & OM_OFFS) {
+            Changes &= ~ OM_OFFS;
+            snprintf(buf, sizeof(buf),"%3.2f", offset);
+            printf("  Signal:Offset=%s\r\n", buf);
+            ini.WriteString("Signal", "Offset", buf);
+        }
     }
-    printf(" Select:                  Signal:\r\n");
-    printf("      S:  Sine Wave            d:  Duty Cycle\r\n");
-    printf("      Q:  Square Wave          f:  Frequency\r\n");
-    printf("      T:  Triangle Wave        p:  Period\r\n");
-    printf("      W:  Sawtooth Wave        v:  Voltage\r\n");
-    printf("      U:  User Wave            o:  Offset\r\n");
-    printf("                                   \r\n");
-    printf("                        0-9 . - :  Numeric entry\r\n");
-    printf("                            < > :  Modify selected signal\r\n");
-    printf("                            <bs>:  Backspace entry\r\n");
-    printf("                            <cr>:  Save number\r\n");
-    printf("                           <esc>:  Exit number entry\r\n");
-    //printf("  4:  Reverse sawtoothSignal\r\n");
 }
 
+void SignalGenDisplay::resetDataEntry(OM_Changes nextMode, bool save) {
+    OM_Changes last = EntryMd;
 
-void SignalGenDisplay::resetDataEntry(void) {
-    SG_Changes last = EntryMd;
-    printf("-> resetDataEntry()\r\n");
-    EntryMd = SG_NONE;
+    printf("-> resetDataEntry(next: %d) curr:%d, save:%d\r\n", nextMode, last, save);
+    EntryMd = nextMode;
+    if (last != OM_NONE)
+        signal->PrepareWaveform(SG_SAWTOOTH, frequency, dutycycle, voltage, offset);
     switch (last) {
-        case SG_NONE:
+        case OM_NONE:
             updateDutyCycle();
             updateFrequency();
             updatePeriod();
             updateVoltage();
             updateOffset();
-            lcd->fillrect(UI_DATA_ENTRY, UI_BackColor);
-            textBuffer[0] = '\0';
-            textLen = 0;
             break;
-        case SG_DUTY:
+        case OM_DUTY:
             updateDutyCycle();
+            if (save) {
+                SaveSettings(OM_DUTY);
+            }
             break;
-        case SG_FREQ:
+        case OM_FREQ:
             updateFrequency();
+            if (save) {
+                SaveSettings(OM_FREQ);
+            }
             break;
-        case SG_PERI:
+        case OM_PERI:
             updatePeriod();
+            if (save) {
+                SaveSettings(OM_FREQ);
+            }
             break;
-        case SG_VOLT:
+        case OM_VOLT:
             updateVoltage();
+            if (save) {
+                SaveSettings(OM_VOLT);
+            }
             break;
-        case SG_OFFS:
+        case OM_OFFS:
             updateOffset();
+            if (save) {
+                SaveSettings(OM_OFFS);
+            }
             break;
         default:
             break;
     }
-    DrawKeypadEnabled(false);
-    printf("   end resetDataEntry()\r\n");
+    DrawKeypadEnabled(EntryMd != OM_NONE);
+    if (EntryMd == OM_NONE) {
+        clearTextWindow();
+    }
+    printf("<- end resetDataEntry()\r\n");
 }
\ No newline at end of file