Signal Generator

Dependencies:   IniManager RA8875 Watchdog mbed-rtos mbed

Fork of speaker_demo_Analog by jim hamblen

Files at this revision

API Documentation at this revision

Comitter:
WiredHome
Date:
Fri Jan 13 12:33:37 2017 +0000
Parent:
0:1c8118ee4106
Child:
2:8f71b71fce1b
Commit message:
Added settings screen, started revising the scope layout

Changed in this revision

IniManager.lib Show annotated file Show diff for this revision Revisions of this file
RA8875.lib Show annotated file Show diff for this revision Revisions of this file
SignalGenDisplay.cpp Show annotated file Show diff for this revision Revisions of this file
SignalGenDisplay.h Show annotated file Show diff for this revision Revisions of this file
SignalGenerator.lib Show annotated file Show diff for this revision Revisions of this file
Speaker.h Show annotated file Show diff for this revision Revisions of this file
Watchdog.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-rtos.lib Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IniManager.lib	Fri Jan 13 12:33:37 2017 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/WiredHome/code/IniManager/#392d1ec637eb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/RA8875.lib	Fri Jan 13 12:33:37 2017 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/WiredHome/code/RA8875/#33ca352755a2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SignalGenDisplay.cpp	Fri Jan 13 12:33:37 2017 +0000
@@ -0,0 +1,1011 @@
+
+#include "SignalGenDisplay.h"
+#include "rtos.h"
+#include "IniManager.h"
+
+extern INI ini;
+
+// ##### Main Page #############################################################
+//
+// +---------------------------------------------------------------------------+
+// | +------------------------------------------+   Progam Name and version    |
+// | |                                          |   Manufacturer name          |
+// | |                                          |                              |
+// | |                                          |   [ Text Entry Box         ] |
+// | |      Scope Area                          |   +------------------------+ |
+// | |                                          |   |                        | |
+// | |                                          |   |                        | |
+// | |                                          |   |                        | |
+// | |                                          |   |                        | |
+// | |                                          |   |                        | |
+// | |                                          |   |    Keypad Area         | |
+// | +------------------------------------------+   |                        | |
+// |                                                |                        | |
+// | [duty cycle]  [frequency]     [amplitude]      |                        | |
+// |                                                |                        | |
+// | [    ...   ]  [period   ]     [offset   ]      |                        | |
+// |                                                |                        | |
+// | [     ] [      ] [        ] [        ] [    ]  |                        | |
+// | [Sine ] [Square] [Triangle] [Sawtooth] [User]  +------------------------+ |
+// +---------------------------------------------------------------------------+
+
+
+#define UI_BackColor            RGB(8,8,8)
+
+const rect_t UI_DATA_ENTRY      = {300,45, 475,65};
+
+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
+#define SC_TOP_MARGIN 20
+#define SC_RIGHT_MARGIN 30
+#define SC_BOT_MARGIN 30
+#define WaveOutlineColor        RGB(16,16,32)
+
+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
+};
+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)
+
+#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
+
+#define BTN_KEYP_X  300
+#define BTN_KEYP_Y  70
+
+const rect_t NavToSettings = { 4,200, 60,220 };
+
+
+
+const rect_t UI_Buttons[] = {
+    { 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 char ModeKeys[] = { 'S','Q','T','W','U' };
+
+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+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 },
+    {BTN_KEYP_X+0*(BTN_W+BTN_S),BTN_KEYP_Y+2*(BTN_H+BTN_S), BTN_KEYP_X+0*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+2*(BTN_H+BTN_S)+BTN_H },
+    {BTN_KEYP_X+1*(BTN_W+BTN_S),BTN_KEYP_Y+2*(BTN_H+BTN_S), BTN_KEYP_X+1*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+2*(BTN_H+BTN_S)+BTN_H },
+    {BTN_KEYP_X+2*(BTN_W+BTN_S),BTN_KEYP_Y+2*(BTN_H+BTN_S), BTN_KEYP_X+2*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+2*(BTN_H+BTN_S)+BTN_H },
+    {BTN_KEYP_X+0*(BTN_W+BTN_S),BTN_KEYP_Y+3*(BTN_H+BTN_S), BTN_KEYP_X+0*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+3*(BTN_H+BTN_S)+BTN_H },
+    {BTN_KEYP_X+1*(BTN_W+BTN_S),BTN_KEYP_Y+3*(BTN_H+BTN_S), BTN_KEYP_X+1*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+3*(BTN_H+BTN_S)+BTN_H },
+    {BTN_KEYP_X+2*(BTN_W+BTN_S),BTN_KEYP_Y+3*(BTN_H+BTN_S), BTN_KEYP_X+2*(BTN_W+BTN_S)+BTN_W,BTN_KEYP_Y+3*(BTN_H+BTN_S)+BTN_H },
+    {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 },
+};
+const int KeypadCount = sizeof(UI_Keypad)/sizeof(UI_Keypad[0]);
+const char UI_KeyLabels[] = {
+    '7', '8', '9',
+    '4', '5', '6',
+    '1', '2', '3',
+    '0', '.', '-',
+    '\x1F', '\x1E', '\xB6',
+};
+const char KeyPadKeys[] = { '7', '8', '9', '4', '5', '6', '1', '2', '3',
+     '0', '.', '-', '<', '>', '\n' };
+
+
+// ##### Settings  #############################################################
+//
+// +---------------------------------------------------------------------------+
+// |                                                Progam Name and version    |
+// |                                                Manufacturer name          |
+// |                                                                           |
+// |                                                                           |
+// |                                                                           |
+// |                                                                           |
+// |                                                                +--------+ |
+// |                                                                |        | |
+// |                                                                |        | |
+// |                                                                |        | |
+// |                                                                |        | |
+// |                                                                |        | |
+// |                                                                |        | |
+// |                                                                |--------| |
+// |                                                                |        | |
+// | [    ...   ]                                                   |        | |
+// |                                                                |        | |
+// |                                                                |        | |
+// |                                                                +--------+ |
+// +---------------------------------------------------------------------------+
+
+const point_t suncenter = { 450,65 };
+const rect_t sunray[] = {
+    { 450-2,65-25, 450+2,65+25 },
+    { 450-25,65-2, 450+25,65+2 }
+};
+const rect_t sungraph = { 450-20,100+0, 450+20,265+0 };
+const rect_t inrgraph = { 450-18,100+2, 450+18,265-2 };
+
+
+template <typename T> int sgn(T val) {
+    return (T(0) < val) - (val < T(0));
+}
+
+char SignalGenDisplay::GetTouchEvent(void) {
+    TouchCode_t touch;
+    
+    touch = lcd->TouchPanelReadable();                           // any touch to report?
+    if (touch) {
+        uint8_t id = lcd->TouchID(0);                        // 'id' tracks the individual touches
+        TouchCode_t ev = lcd->TouchCode(0);                  // 'ev'ent indicates no_touch, touch, held, release, ...
+        point_t point = lcd->TouchCoordinates(0);               // and of course the (x,y) coordinates
+        if (ev == touch) {
+            timer.start();
+            timer.reset();
+        }
+        if ((ev == release) || (ev == held && timer.read_ms() > 250)) {
+            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)) {
+                            return ModeKeys[i];
+                        }
+                    }
+                    // Parameters
+                    for (int i=0; i<ParameterCount; i++) {
+                        if (lcd->Intersect(Parameters[i], point)) {
+                            return ParameterKeys[i];
+                        }
+                    }
+                    
+                    // Keypad
+                    for (int i=0; i<KeypadCount; i++) {
+                        if (lcd->Intersect(UI_Keypad[i], point)) {
+                            return KeyPadKeys[i];
+                        }
+                    }
+                    
+                    if (lcd->Intersect(NavToSettings, point)) {
+printf("Nav\r\n");
+                        vis = VS_Settings;
+                        Init();
+                        while (lcd->TouchPanelReadable())
+                            ;
+                        Thread::wait(100);
+                    }
+                    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
+                        char buf[20];
+                        
+                        snprintf(buf, sizeof(buf), "%d", lcd->GetBacklight_u8());
+                        ini.WriteString("Settings", "Backlight", buf);
+                        
+                        // Switch to main screen
+                        vis = VS_MainScreen;
+                        Init();
+                        while (lcd->TouchPanelReadable())
+                            ;
+                        Thread::wait(100);
+                        ShowMenu();
+                    }
+                    break;
+            }
+        }
+    }
+    return 0;
+}
+
+
+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() {
+    switch (vis) {
+        case VS_MainScreen:
+            lcd->background(UI_BackColor);
+            lcd->cls(1);
+            lcd->SelectDrawingLayer(0);
+            // Clear Screen
+            lcd->SetLayerMode(RA8875::ShowLayer0);
+            
+            // Product Info
+            lcd->foreground(UI_ProductNameColor);
+            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();
+            break;
+            
+        case VS_Settings:
+            lcd->background(UI_BackColor);
+            lcd->cls(2);
+            lcd->SelectDrawingLayer(1);
+            lcd->SetLayerMode(RA8875::ShowLayer1);
+            lcd->foreground(UI_ProductNameColor);
+            ShowProductInfo();
+            ShowBrightnessSetting();
+            DrawNavGadget();            
+            break;
+    }
+}
+
+void SignalGenDisplay::DrawNavGadget(void) {
+    lcd->fillrect(NavToSettings, Black);
+    lcd->SetTextCursor(NavToSettings.p1.x+1, NavToSettings.p1.y+1);
+    lcd->foreground(White);
+    lcd->background(Black);
+    lcd->puts("  ...");
+}
+
+
+void SignalGenDisplay::ShowProductInfo(void) {
+    rect_t r = UI_PROD_RECT;
+    lcd->window(r);
+    lcd->SetTextCursor(r.p1.x, r.p1.y);
+    lcd->printf("%s v%s", ProgName, Ver);
+    lcd->SetTextCursor(r.p1.x, r.p1.y+16);
+    lcd->printf("by %s", Manuf);
+    lcd->window();
+}
+
+void SignalGenDisplay::ShowBrightnessSetting(void) {
+    // Sunbeam
+    lcd->fillrect(sunray[0], White);
+    lcd->fillrect(sunray[1], White);
+    lcd->fillcircle(suncenter, 18, UI_BackColor);
+    lcd->fillcircle(suncenter, 15, White);
+    lcd->rect(sungraph, Blue);
+    float bl = lcd->GetBacklight();
+    lcd->fillrect(inrgraph, UI_BackColor);
+    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;
+    
+    if (!c) {
+        c = GetTouchEvent();
+    }
+    if (c) {
+        printf("%02X: EntryMd: %d, textLen: %d [%s] VIS: %d\r\n", c, EntryMd, textLen, textBuffer, vis);
+    } 
+    ///     - 'd'       duty cycle entry
+    ///     - 'f'       frequency entry
+    ///     - 'p'       period entry
+    ///     - 'v'       voltage entry
+    ///     - 'o'       offset voltage entry
+    ///     - '0'-'9','.'   numeric entry
+    ///     - <enter>   complete numeric entry
+    ///     - <esc>     abandon numeric entry
+    ///     - <nul>     do nothing, just poll
+    switch (c) {
+        case '?':
+            ShowMenu();
+            break;
+        case 'S':
+            SelectWaveformMode(SG_SINE);
+            signal->SetSignalFrequency(SignalGenerator::SinusSignal, frequency);
+            //ret = SG_SINE;
+            break;
+        case 'Q':
+            SelectWaveformMode(SG_SQUARE);
+            signal->SetSignalFrequency(SignalGenerator::SquareSignal, frequency);
+            //ret = SG_SQUARE;
+            break;
+        case 'T':
+            SelectWaveformMode(SG_TRIANGLE);
+            signal->SetSignalFrequency(SignalGenerator::TriangleSignal, frequency);
+            //ret = SG_TRIANGLE;
+            break;
+        case 'W':
+            SelectWaveformMode(SG_SAWTOOTH);
+            signal->SetSignalFrequency(SignalGenerator::SawtoothSignal, frequency);
+            //ret = SG_SAWTOOTH;
+            break;
+        case 'U':
+            SelectWaveformMode(SG_USER);
+            //ret = SG_USER;
+            break;
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+        case '.':
+        case '-':
+            if (EntryMd) {
+                if (textLen<8) {
+                    textBuffer[textLen++] = c;
+                    textBuffer[textLen] = '\0';
+                    updateTextWindow();
+                }
+            }
+            break;
+        case '\x08':
+            if (EntryMd) {
+                if (textLen) {
+                    textLen--;
+                    textBuffer[textLen] = '\0';
+                    updateTextWindow();
+                }
+            }
+            break;
+        case '\x1B':
+            textBuffer[0] = '\0';
+            textLen = 0;
+            EntryMd = SG_NONE;
+            resetDataEntry();
+            break;
+        case '\r':
+        case '\n':
+            if (EntryMd) {
+                if (strlen(textBuffer)) {
+                    switch (EntryMd) {
+                        case SG_DUTY:
+                            SetDutyCycle(atof(textBuffer));
+                            break;
+                        case SG_FREQ:
+                            SetFrequency(atof(textBuffer));
+                            break;
+                        case SG_PERI:
+                            SetPeriod(atof(textBuffer));
+                            break;
+                        case SG_VOLT:
+                            SetVoltagePeakToPeak(atof(textBuffer));
+                            break;
+                        case SG_OFFS:
+                            SetVoltageOffset(atof(textBuffer));
+                            break;
+                        default:
+                            break;
+                    }
+                }
+                EntryMd = SG_NONE;
+                resetDataEntry();
+            }
+            break;
+        case '>':
+            switch (EntryMd) {
+                case SG_DUTY:
+                    SetDutyCycle(dutycycle + 1.0);
+                    break;
+                case SG_FREQ:
+                    SetFrequency(frequency + 1.0);
+                    break;
+                case SG_PERI:
+                    SetPeriod(1/frequency + 0.001);
+                    break;
+                case SG_VOLT:
+                    SetVoltagePeakToPeak(voltage + 0.1);
+                    break;
+                case SG_OFFS:
+                    SetVoltageOffset(offset + 0.01);
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case '<':
+            switch (EntryMd) {
+                case SG_DUTY:
+                    SetDutyCycle(dutycycle - 1.0);
+                    break;
+                case SG_FREQ:
+                    SetFrequency(frequency - 1.0);
+                    break;
+                case SG_PERI:
+                    SetPeriod(1/frequency - 0.001);
+                    break;
+                case SG_VOLT:
+                    SetVoltagePeakToPeak(voltage - 0.1);
+                    break;
+                case SG_OFFS:
+                    SetVoltageOffset(offset - 0.01);
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case 'd':
+            if (EntryMd != SG_DUTY) {
+                resetDataEntry();
+                EntryMd = SG_DUTY;
+                DrawKeypadEnabled(true);
+                updateDutyCycle();
+            } else {
+                EntryMd = SG_NONE;
+                resetDataEntry();
+            }
+            break;
+        case 'f':
+            if (EntryMd != SG_FREQ) {
+                resetDataEntry();
+                EntryMd = SG_FREQ;
+                DrawKeypadEnabled(true);
+                updateFrequency();
+            } else {
+                EntryMd = SG_NONE;
+                resetDataEntry();
+            }
+            break;
+        case 'p':
+            if (EntryMd != SG_PERI) {
+                resetDataEntry();
+                EntryMd = SG_PERI;
+                DrawKeypadEnabled(true);
+                updatePeriod();
+            } else {
+                EntryMd = SG_NONE;
+                resetDataEntry();
+            }
+            break;
+        case 'v':
+            if (EntryMd != SG_VOLT) {
+                resetDataEntry();
+                EntryMd = SG_VOLT;
+                DrawKeypadEnabled(true);
+                updateVoltage();
+            } else {
+                EntryMd = SG_NONE;
+                resetDataEntry();
+            }
+            break;
+        case 'o':
+            if (EntryMd != SG_OFFS) {
+                resetDataEntry();
+                EntryMd = SG_OFFS;
+                DrawKeypadEnabled(true);
+                updateOffset();
+            } else {
+                EntryMd = SG_NONE;
+                resetDataEntry();
+            }
+            break;
+        default:
+            break;
+    }
+    return ret;
+}
+
+bool SignalGenDisplay::SelectWaveformMode(SG_Mode _mode) {
+    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();
+        return true;
+    } else {
+        return false;
+    }
+}
+
+bool SignalGenDisplay::SetDutyCycle(float _dutyCycle) {
+    if (_dutyCycle >= 5 && _dutyCycle <= 95) {
+        dutycycle = _dutyCycle;
+        updateDutyCycle();
+        UpdateScope();
+        return true;
+    } else {
+        return false;
+    }
+}
+
+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;
+    }
+}
+
+bool SignalGenDisplay::SetPeriod(float _period) {
+    if (_period >= 1.0E-6 && _period <= 1.0) {
+        frequency = 1/_period;
+        updatePeriod();
+        updateFrequency();
+        UpdateScope();
+        return true;
+    } else {
+        return false;
+    }
+}
+
+bool SignalGenDisplay::SetVoltagePeakToPeak(float _voltage) {
+    if (_voltage >= 0.0 && _voltage <= 3.3) {
+        voltage = _voltage;
+        updateVoltage();
+        UpdateScope();
+        return true;
+    } else {
+        return false;
+    }
+}
+
+bool SignalGenDisplay::SetVoltageOffset(float _voltage) {
+    if (_voltage >= -1.65 && _voltage <= 1.65) {
+        if (abs(_voltage) < 0.008)     // if binary precision slips it, fix it
+            _voltage = 0.0;
+        offset = _voltage;
+        updateOffset();
+        UpdateScope();
+        return true;
+    } else {
+        return false;
+    }
+}
+
+// ########################   Private Methods past here #######################
+
+void SignalGenDisplay::UpdateScope(void) {
+    printf("-> UpdateScope()\r\n");
+    ClearScope();
+    rect_t r;
+
+    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);
+    
+    // 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,
+        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,
+        UI_VP2PColor);
+
+    // Draw the offset voltage markers
+    loc_t y = (r.p1.y + r.p2.y)/2;
+    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++) {
+        if ((i & 1) == 0) {
+            lcd->line(r.p1.x + i * w,y, r.p1.x + (i+1) * w, y, UI_VOffsetColor);
+        }
+    }
+    switch (sgn(offset)) {
+        default:
+        case 0:
+            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->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);
+            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.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,
+        r.p1.x+3,r.p2.y+3*SC_BOT_MARGIN/4-3-2,
+        r.p1.x+3,r.p2.y+3*SC_BOT_MARGIN/4-3+2,
+        UI_FreqColor);
+    lcd->filltriangle(
+        r.p1.x+1*w/2-0,r.p2.y+3*SC_BOT_MARGIN/4-3,
+        r.p1.x+1*w/2-3,r.p2.y+3*SC_BOT_MARGIN/4-3-2,
+        r.p1.x+1*w/2-3,r.p2.y+3*SC_BOT_MARGIN/4-3+2,
+        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);
+    point_t p;
+    p.x = r.p1.x;
+    p.y = r.p2.y+2*SC_BOT_MARGIN/4-3;
+    lcd->line(p.x,p.y, p.x+dc,p.y, UI_DutyColor);
+    lcd->filltriangle(
+        p.x,p.y,
+        p.x+3,p.y-2,
+        p.x+3,p.y+2,
+        UI_DutyColor);
+    p.x = r.p1.x + dc;
+    lcd->filltriangle(
+        p.x,p.y,
+        p.x-3,p.y-2,
+        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) {
+    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 a = (r.p2.y - r.p1.y)/2;
+    float v;
+    
+    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);
+                    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 + x, y, color);
+                }
+                for (x=0; x<=(w/2-dc); x++) {
+                    v = offset - voltage/2 * sin(x * 1 * PI / (w/2-dc));
+                    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);
+                }
+            }
+            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
+            }
+            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
+            }
+            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);
+            }
+            break;
+        case SG_USER:
+            lcd->line(r.p1.x, y0-1, r.p1.x+w, y0-1, color);
+            lcd->line(r.p1.x, y0-0, r.p1.x+w, y0-0, color);
+            lcd->line(r.p1.x, y0+1, r.p1.x+w, y0+1, color);
+            lcd->rect(r.p1.x+5*w/8, y0-a/4, r.p1.x+7*w/8, y0+a/4, color);
+            break;
+    }
+}
+
+void SignalGenDisplay::ClearScope(void) {
+    // Scope area
+    rect_t r = UI_SCOPE_RECT;
+    lcd->fillrect(r, UI_ScopeBackColor);
+    lcd->rect(r, UI_ScopeFrameColor);
+}
+
+void SignalGenDisplay::updateDutyCycle(void) {
+    rect_t r = Parameters[0];   // UI_DUTY_CYCLE_RECT;
+    color_t fcolor, bcolor;
+    
+    if (EntryMd != SG_DUTY) {
+        fcolor = UI_DutyColor;
+        bcolor = UI_ScopeBackColor;
+    } else {
+        fcolor = UI_ScopeBackColor;
+        bcolor = UI_DutyColor;
+    }
+    lcd->fillrect(r, bcolor);
+    lcd->foreground(fcolor);
+    lcd->background(bcolor);
+    lcd->SetTextCursor(r.p1.x+1, r.p1.y+1);
+    lcd->printf("%3.0f %%", dutycycle);
+}
+
+void SignalGenDisplay::updateFrequency(void) {
+    rect_t r = Parameters[1];   // UI_FREQ_RECT;
+    color_t fcolor, bcolor;
+    
+    if (EntryMd != SG_FREQ) {
+        fcolor = UI_FreqColor;
+        bcolor = UI_ScopeBackColor;
+    } else {
+        fcolor = UI_ScopeBackColor;
+        bcolor = UI_FreqColor;
+    }
+    lcd->fillrect(r, bcolor);
+    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
+        lcd->printf("%8.3f Hz ", frequency);    
+}
+
+void SignalGenDisplay::updatePeriod(void) {
+    float period = 1/frequency;
+    rect_t r = Parameters[2];   // UI_PERIOD_RECT;
+    color_t fcolor, bcolor;
+
+    if (EntryMd != SG_PERI) {
+        fcolor = UI_FreqColor;
+        bcolor = UI_ScopeBackColor;
+    } else {
+        fcolor = UI_ScopeBackColor;
+        bcolor = UI_FreqColor;
+    }
+    lcd->fillrect(r, bcolor);
+    lcd->foreground(fcolor);
+    lcd->background(bcolor);
+    lcd->SetTextCursor(r.p1.x+1, r.p1.y+1);
+    if (period < 0.001)
+        lcd->printf("%8.3f uS", period * 1000000);
+    else
+        lcd->printf("%8.3f mS", period * 1000);
+}
+
+void SignalGenDisplay::updateVoltage(void) {
+    rect_t r = Parameters[3];   // UI_VP2P_RECT;
+    color_t fcolor, bcolor;
+
+    if (EntryMd != SG_VOLT) {
+        fcolor = UI_VP2PColor;
+        bcolor = UI_ScopeBackColor;
+    } else {
+        fcolor = UI_ScopeBackColor;
+        bcolor = UI_VP2PColor;
+    }
+    lcd->fillrect(r, bcolor);
+    lcd->foreground(fcolor);
+    lcd->background(bcolor);
+    lcd->SetTextCursor(r.p1.x+1, r.p1.y+1);
+    lcd->printf("%5.1f v", voltage);
+}
+
+void SignalGenDisplay::updateOffset(void) {
+    rect_t r = Parameters[4];   // UI_VOFFSET_RECT;
+    color_t fcolor, bcolor;
+
+    if (EntryMd != SG_OFFS) {
+        fcolor = UI_VOffsetColor;
+        bcolor = UI_ScopeBackColor;
+    } else {
+        fcolor = UI_ScopeBackColor;
+        bcolor = UI_VOffsetColor;
+    }
+    lcd->fillrect(r, bcolor);
+    lcd->foreground(fcolor);
+    lcd->background(bcolor);
+    lcd->SetTextCursor(r.p1.x+1, r.p1.y+1);
+    lcd->printf("%+4.2f v", offset);
+}
+
+void SignalGenDisplay::DrawKeypadEnabled(bool enable) {
+    for (int i=0; i<KeypadCount; i++) {
+        DrawButton(UI_Keypad[i], false, SG_KEYPAD, enable, i);
+    }
+}
+
+void SignalGenDisplay::DrawButton(rect_t r, bool pressed, SG_Mode mode, bool enable, int label) {
+    rect_t wave;
+    color_t buttonface = UI_BUTTON_FACE_DISABLED;
+    color_t buttonshadow = UI_BUTTON_SHADOW_DISABLED;
+    
+    //lcd->fillrect(r, UI_ScopeBackColor);
+    if (pressed) {
+        if (enable) {
+            buttonface = UI_BUTTON_FACE_DN;
+            buttonshadow = UI_BUTTON_SHADOW;
+        }
+        lcd->fillrect(r, buttonface);
+        lcd->line(r.p1.x+0,r.p1.y+0, r.p2.x+0,r.p1.y+0, buttonshadow);      // top border
+        lcd->line(r.p1.x+1,r.p1.y+1, r.p2.x+0,r.p1.y+1, buttonshadow);      // top border
+        lcd->line(r.p1.x+2,r.p1.y+2, r.p2.x+0,r.p1.y+2, buttonshadow);      // top border
+        lcd->line(r.p1.x+0,r.p1.y+0, r.p1.x+0,r.p2.y+0, buttonshadow);      // left border
+        lcd->line(r.p1.x+1,r.p1.y+1, r.p1.x+1,r.p2.y+0, buttonshadow);      // left border
+        lcd->line(r.p1.x+2,r.p1.y+2, r.p1.x+2,r.p2.y+0, buttonshadow);      // left border
+        wave.p1.x = r.p1.x+5 + 2; wave.p1.y = r.p1.y + 5 + 2;
+        wave.p2.x = r.p2.x-5 + 2; wave.p2.y = r.p2.y - 5 + 2;
+    } else {
+        if (enable) {
+            buttonface = UI_BUTTON_FACE_UP;
+            buttonshadow = UI_BUTTON_SHADOW;
+        }
+        lcd->fillrect(r, buttonface);
+        lcd->line(r.p1.x+0,r.p2.y-0, r.p2.x-0,r.p2.y-0, buttonshadow);      // bottom border
+        lcd->line(r.p1.x+0,r.p2.y-1, r.p2.x-1,r.p2.y-1, buttonshadow);      // bottom border
+        lcd->line(r.p1.x+0,r.p2.y-2, r.p2.x-2,r.p2.y-2, buttonshadow);      // bottom border
+        lcd->line(r.p2.x-0,r.p1.y+0, r.p2.x-0,r.p2.y-0, buttonshadow);      // right border
+        lcd->line(r.p2.x-1,r.p1.y+0, r.p2.x-1,r.p2.y-1, buttonshadow);      // right border
+        lcd->line(r.p2.x-2,r.p1.y+0, r.p2.x-2,r.p2.y-2, buttonshadow);      // right border
+        wave.p1.x = r.p1.x+5 + 0; wave.p1.y = r.p1.y + 5 + 0;
+        wave.p2.x = r.p2.x-5 + 0; wave.p2.y = r.p2.y - 5 + 0;
+    }
+    switch (mode) {
+        case SG_SINE:
+        case SG_SQUARE:
+        case SG_TRIANGLE:
+        case SG_SAWTOOTH:
+        case SG_USER:
+            DrawWaveform(wave, mode, Black, 50.0);
+            break;
+        case SG_KEYPAD:
+            lcd->foreground(Black);
+            lcd->background(buttonface);
+            lcd->SetTextCursor((r.p1.x+r.p2.x)/2 - 4,r.p1.y + BTN_H/2 - 8);     // 8x16 char
+            lcd->putc(UI_KeyLabels[label]);
+            break;
+    }
+}
+
+void SignalGenDisplay::updateTextWindow(void) {
+    lcd->window(UI_DATA_ENTRY);
+    lcd->fillrect(UI_DATA_ENTRY, White);
+    lcd->foreground(Black);
+    lcd->background(White);
+    lcd->SetTextCursor(UI_DATA_ENTRY.p1.x+1,UI_DATA_ENTRY.p1.y+1);
+    lcd->printf("%21s", textBuffer);
+    lcd->window();
+}
+
+float SignalGenDisplay::rangelimit(float value, float min, float max) {
+    if (value < min)
+        return min;
+    else if (value > max)
+        return max;
+    else
+        return value;
+}
+
+
+void SignalGenDisplay::ShowMenu(void) {
+    if (Manuf) {
+        printf("\r\n%s v%s by %s build %s\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("                            <cr>:  Save number\r\n");
+    printf("                           <esc>:  Exit number entry\r\n");
+    //printf("  4:  Reverse sawtoothSignal\r\n");
+}
+
+
+void SignalGenDisplay::resetDataEntry(void) {
+    SG_Changes last = EntryMd;
+    printf("-> resetDataEntry()\r\n");
+    EntryMd = SG_NONE;
+    switch (last) {
+        case SG_NONE:
+            updateDutyCycle();
+            updateFrequency();
+            updatePeriod();
+            updateVoltage();
+            updateOffset();
+            lcd->fillrect(UI_DATA_ENTRY, UI_BackColor);
+            textBuffer[0] = '\0';
+            textLen = 0;
+            break;
+        case SG_DUTY:
+            updateDutyCycle();
+            break;
+        case SG_FREQ:
+            updateFrequency();
+            break;
+        case SG_PERI:
+            updatePeriod();
+            break;
+        case SG_VOLT:
+            updateVoltage();
+            break;
+        case SG_OFFS:
+            updateOffset();
+            break;
+        default:
+            break;
+    }
+    DrawKeypadEnabled(false);
+    printf("   end resetDataEntry()\r\n");
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SignalGenDisplay.h	Fri Jan 13 12:33:37 2017 +0000
@@ -0,0 +1,218 @@
+
+#ifndef SIGNALGENDISPLAY_H
+#define SIGNALGENDISPLAY_H
+
+#include "mbed.h"
+#include "RA8875.h"
+#include "SignalGenerator.h"
+
+#define SG_MIN_V 0.0    // Constraint, to match to the hardware
+#define SG_MAX_V 3.3    // 
+#define SG_AOUT_FS 3.3  // Analog output full scale
+
+class SignalGenDisplay {
+public:
+    /// Constructor for the Signal Generator User Interface.
+    ///
+    /// @param[in] lcd is a pointer to the Graphics Display
+    /// @param[in] signal is a handle to the signal generator
+    /// @param[in] ProgrName is a pointer to a constant string
+    /// @param[in] Manuf is a pointer to a constant string
+    /// @param[in] Ver is a pointer to a constant string
+    /// @param[in] Build is a pointer to a constant string
+    ///
+    SignalGenDisplay(RA8875 * lcd, SignalGenerator * signal,
+        const char * ProgName, const char * Manuf, const char * Ver,
+        const char * Build);
+    
+    /// Destructor
+    ////
+    ~SignalGenDisplay();
+    
+    /// Initialization to present the initial display
+    ///
+    /// As part of the display initialization, it also shows
+    /// program information.
+    ///
+    void Init(void);
+    
+    /// Set the frequency information
+    ///
+    /// This automatically sets the period as 1/frequency
+    ///
+    /// @param[in] frequency desired
+    /// @returns true if the value was accepted
+    ///
+    bool SetFrequency(float frequency);
+    
+    /// Get the current frequency setting
+    ///
+    /// @returns current frequency
+    ///
+    float GetFrequency(void) { return frequency; }
+    
+    /// Set the period instead of the frequency
+    ///
+    /// This automatically sets the frequency as 1/period
+    ///
+    /// @param[in] period desired
+    /// @returns true if the value was accepted
+    ///
+    bool SetPeriod(float period);
+    
+    /// Get the current period
+    ///
+    /// @returns current period
+    ///
+    float GetPeriod(void) { return 1/frequency; }
+    
+    /// Set the Duty Cycle
+    ///
+    /// This adjusts the duty cycle of the waveform
+    ///
+    /// @param[in] dutyCycle is a value ranging from 0 to 100.
+    /// @returns true if the value was accepted
+    ///
+    bool SetDutyCycle(float dutyCycle);
+    
+    /// Get the current duty cycle
+    ///
+    /// @returns the duty cycle
+    ///
+    float GetDutyCycle(void) { return dutycycle; }
+    
+    /// Set the peak-to-peak voltage of the of the waveform
+    ///
+    /// In the range of 0 to 3.3v
+    ///
+    /// @param[in] voltage is the peak to peak voltage
+    /// @returns true if the value was accepted
+    ///
+    bool SetVoltagePeakToPeak(float voltage);
+
+    /// Get the Peak to Peak voltage
+    ///
+    /// @returns peak to peak voltage
+    ///
+    float GetVoltagePeakToPeak(void) { return voltage; }
+    
+    /// Set the offset in the range of +/- 1.65v
+    ///
+    /// A zero volt offset is biased to VCC/2 (3.3/2)
+    ///
+    /// @param[in] voltage is the offset voltage.
+    /// @returns true if the value was accepted
+    ///
+    bool SetVoltageOffset(float voltage);
+
+    /// Get the offset voltage
+    ///
+    /// @returns offset voltage
+    ///
+    float GetVoltageOffset(void) { return offset; }
+    
+    /// Signal Generator Modes
+    ///
+    /// This defines the modes. However, SG_KEYPAD is not an mode,
+    /// it is a proprietary mechanism used for displaying the keypad, and
+    /// is not intended to be used by the application.
+    ///
+    typedef enum {
+        SG_SINE,        ///< Sine wave
+        SG_SQUARE,      ///< Square wave
+        SG_TRIANGLE,    ///< Triangle wave
+        SG_SAWTOOTH,    ///< Sawtooth
+        SG_USER,        ///< User defined waveform
+        SG_KEYPAD,      ///< This is an internal value, not for applications
+    } SG_Mode;
+    
+    /// Select a Waveform Mode
+    ///
+    /// The selection will update the display to reflect the current state
+    ///
+    /// @param[in] mode sets the signal generator mode.
+    /// @returns true if the value was accepted
+    ///
+    bool SelectWaveformMode(SG_Mode mode);
+    
+    /// Operating mode changes
+    ///
+    /// Changes in the operating mode are reported by a bitmask value, where
+    /// zero or more bits are set.
+    ///
+    typedef enum {
+        SG_NONE = 0,    ///< No change in operating mode
+        SG_MODE = 1,    ///< Signal mode changed; Sine, Square, Triangle, Sawtooth, User
+        SG_FREQ = 2,    ///< Change in the frequency
+        SG_PERI = 4,    ///< Change in the period (effectively same as frequency)
+        SG_DUTY = 8,    ///< Change in the duty cycle
+        SG_VOLT = 16,   ///< Change in the peak to peak amplitude
+        SG_OFFS = 32,   ///< Change in the offset voltage
+    } SG_Changes;
+
+    /// Poll the Signal Generator UI for changes in operation.
+    ///
+    /// Call this periodically, in order to determine if there is a user-activated
+    /// change in the operating mode of the signal generator.
+    ///
+    /// @param[in] c is the optional character, emulating the onscreen keypad
+    ///     - 'd'       duty cycle entry
+    ///     - 'f'       frequency entry
+    ///     - 'p'       period entry
+    ///     - 'v'       voltage entry
+    ///     - 'o'       offset voltage entry
+    ///     - '0'-'9','.'   numeric entry
+    ///     - <enter>   complete numeric entry
+    ///     - <esc>     abandon numeric entry
+    ///     - <nul>     do nothing, just poll
+    /// @returns a bitmask of which non-zero indicates changes in mode.
+    ///
+    SG_Changes Poll(char c = 0);
+
+    /// Show the menu of commands on the console interface
+    ///
+    void ShowMenu(void);
+
+private:
+    RA8875 * lcd;
+    SignalGenerator * signal;
+    const char * ProgName; 
+    const char * Manuf; 
+    const char * Ver;
+    const char * Build;
+    typedef enum {
+        VS_MainScreen,
+        VS_Settings,
+    } VisualScreen;
+    VisualScreen vis;
+    SG_Mode mode;       ///< signal mode
+    float frequency;    ///< selected frequency
+    float dutycycle;    ///< selected duty cycle
+    float voltage;      ///< selected voltage
+    float offset;       ///< selected offset
+    SG_Changes EntryMd; ///< indicates if in data entry mode
+    char textBuffer[10]; ///< a place to enter text
+    int textLen;        ///< num chars in textBuffer
+    Timer timer;
+    
+    void DrawNavGadget(void);
+    void ShowProductInfo(void);
+    void ShowBrightnessSetting(void);
+    char GetTouchEvent(void);
+    void ClearScope(void);
+    void UpdateScope(void);
+    void updateDutyCycle(void);
+    void updateFrequency(void);
+    void updatePeriod(void);
+    void updateVoltage(void);
+    void updateOffset(void);
+    void updateTextWindow(void);
+    void resetDataEntry(void);
+    void DrawKeypadEnabled(bool enable = false);
+    void DrawButton(rect_t r, bool pressed, SG_Mode mode, bool enable = false, int label=0);
+    void DrawWaveform(rect_t r, SG_Mode mode, color_t color, float dutycycleOverride = 0.0);
+    float rangelimit(float value, float minV, float maxV);
+};
+
+
+#endif // SIGNALGENDISPLAY_H
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SignalGenerator.lib	Fri Jan 13 12:33:37 2017 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/Yann/code/SignalGenerator/#b634f7945085
--- a/Speaker.h	Mon Jan 21 03:37:11 2013 +0000
+++ b/Speaker.h	Fri Jan 13 12:33:37 2017 +0000
@@ -1,3 +1,6 @@
+
+#if 0
+
 class Speaker
 {
 public:
@@ -50,3 +53,4 @@
     }
 };
 
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Watchdog.lib	Fri Jan 13 12:33:37 2017 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/WiredHome/code/Watchdog/#e0f547e22dd5
--- a/main.cpp	Mon Jan 21 03:37:11 2013 +0000
+++ b/main.cpp	Fri Jan 13 12:33:37 2017 +0000
@@ -1,22 +1,168 @@
+
 #include "mbed.h"
-// Audio output demo for speaker
-// Speaker Class demo - plays a note on the analog output pin
-// 32 data points on one sine wave cycle are precomputed,
-// scaled, stored in an array and
-// continuously output to the Digital to Analog convertor
+#include "SignalGenDisplay.h"
+#include "RA8875.h"
+#include "Watchdog.h"           // ver 2
+#include "IniManager.h"         // v19
+
+const char * PROG_MANF = "Smartware Computing";
+const char * PROG_NAME = "Signal Generator";
+const char * PROG_VERS = "0.01";
+const char * BUILD_DATE = __DATE__ " " __TIME__;
+
+RA8875 lcd(p5,p6,p7,p12, NC, "tft");             // SPI:{MOSI,MISO,SCK,/ChipSelect,/reset}, name
+INI ini;
+SignalGenerator g_signal(p18);
+
+SignalGenDisplay ui(&lcd, &g_signal, PROG_NAME, PROG_MANF, PROG_VERS, BUILD_DATE);
+
+RawSerial pc(USBTX, USBRX);
+LocalFileSystem local("local");
+Watchdog wd;
+
+/* CPU Available indicator
+ */
+DigitalOut g_availableLed(LED1); //<! Led used to indicate the program is alive
+void AvailableLedIndicator(); //<! Ticker callback
+Ticker g_available;
+ 
+/* SignalGenerator usage
+ */
+void SynchronousUserInput(); //<! Ticker callback
+Ticker g_synchronousUserInput;
+
+
 
-// add Speaker class and PlayNote
-// PlayNote args are frequency in hz (<=5000), duration in secs , and volume(0.0..1.0)
-#include  "Speaker.h"
-
-int main()
+// Calibrate the resistive touch screen, and store the data on the
+// local file system.
+//
+void CalibrateTS(void)
 {
-// setup instance of new Speaker class, mySpeaker
-// the pin must be the AnalogOut pin - p18
-    Speaker mySpeaker(p18);
-    // loops forever playing two notes on speaker using analog samples
-    while(1) {
-        mySpeaker.PlayNote(969.0, 0.5, 1.0);
-        mySpeaker.PlayNote(800.0, 0.5, 1.0);
+    FILE * fh;
+    tpMatrix_t matrix;
+    RetCode_t r;
+    Timer testperiod;
+ 
+    r = lcd.TouchPanelCalibrate("Calibrate the touch panel", &matrix);
+    if (r == noerror) {
+        fh = fopen("/local/tpcal.cfg", "wb");
+        if (fh) {
+            fwrite(&matrix, sizeof(tpMatrix_t), 1, fh);
+            fclose(fh);
+            printf("  tp cal written.\r\n");
+            lcd.cls();
+        } else {
+            printf("  couldn't open tpcal file.\r\n");
+        }
+    } else {
+        printf("error return: %d\r\n", r);
+    }
+    lcd.cls();
+}
+
+// Try to load a previous resistive touch screen calibration from storage. If it
+// doesn't exist, activate the touch screen calibration process.
+//
+void InitTS(void)
+{
+    FILE * fh;
+    tpMatrix_t matrix;
+
+    fh = fopen("/local/tpcal.cfg", "rb");
+    if (fh) {
+        fread(&matrix, sizeof(tpMatrix_t), 1, fh);
+        fclose(fh);
+        lcd.TouchPanelSetMatrix(&matrix);
+        printf("  tp cal loaded.\r\n");
+    } else {
+        CalibrateTS();
     }
 }
+
+
+
+/* Program Entry Point
+ */
+int main() {
+    pc.baud(460800);
+    pc.printf("\r\n%s %s\r\n", PROG_NAME, BUILD_DATE);
+
+    if (wd.WatchdogCausedReset()) {
+        pc.printf("**** Watchdog Event caused reset ****\r\n");
+    }
+    // Set very long timeout because the <PrintScreen> function is incredibly slow with local filesystem...
+//    wd.Configure(120.0);   // This is forever for real-time embedded, but for a casual network appliance...
+    ini.SetFile("/local/SigGen.ini", 2);
+
+    // Bring the LCD online
+    lcd.frequency(2000000);
+    lcd.init(480,272,16, true, true, true);
+    lcd.TouchPanelInit();
+    int bl = ini.ReadLongInt("Settings", "Backlight", 80);
+    lcd.Backlight_u8(bl);
+    InitTS();
+    
+    // Bring the signal generator online
+    ui.Init();
+    
+    //volatile SignalGenerator::SignalGeneratorType _choice; //<! User selects the desired signal type
+    //volatile int frequency; //<! Signal frequency
+ 
+    wait(1); // Needed after startup
+    
+    // Launch available indicator
+    g_availableLed = 1;
+    g_available.attach(&AvailableLedIndicator, 2.0); // Never detached
+
+    ui.ShowMenu();
+    // Start infinite loop
+    while(true)
+    {
+//        wd.Service();
+        if (pc.readable()) {
+            int c = pc.getc();
+            ui.Poll(c);
+        } else {
+            ui.Poll();
+        }
+#if 0
+        // Acquire settings
+        //_choice = DisplaySignalGeneratorTestMenuAndGetChoice();
+        printf("\r\nEnter signal frequency (< 1MHz/# of samples): ");
+        scanf("%d", &frequency);
+        
+        // Prepare the signal
+        g_signal.SetSignalFrequency(_choice, frequency);
+
+        // Launch execution
+        if (g_signal.BeginRunAsync() == -1) {
+            // Synchronous mode
+            g_synchronousUserInput.attach(&SynchronousUserInput, 0.005); // 5ms
+            g_signal.Run(); // Never stopped
+            printf("\r\nSignal Generator terminated\r\n");
+            g_synchronousUserInput.detach();
+        } else {
+            // Asynchronous mode
+            printf("\r\n\r\nPress 'q' to terminate\r\n");
+            while (getchar() != 'q');
+            g_signal.EndRunAsync();
+        }
+#endif
+    } // End of 'while' statement
+} // End of main program
+ 
+/** Callbak used by CPU availabe ticker to indicate the program is alive
+ */
+void AvailableLedIndicator() {
+    g_availableLed != g_availableLed;
+} // End of function AvailableLedIndicator
+ 
+/** Callbak used when SignalLibrary is used in synchronous mode (Run() method)
+ */
+void SynchronousUserInput() {
+    if (pc.readable()) {
+        g_signal.Stop();
+        getchar();
+    }
+} // End of function SynchronousUserInput
+ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-rtos.lib	Fri Jan 13 12:33:37 2017 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed-rtos/#bdd541595fc5
--- a/mbed.bld	Mon Jan 21 03:37:11 2013 +0000
+++ b/mbed.bld	Fri Jan 13 12:33:37 2017 +0000
@@ -1,1 +1,1 @@
-http://mbed.org/users/mbed_official/code/mbed/builds/0480438fc29c
\ No newline at end of file
+http://mbed.org/users/mbed_official/code/mbed/builds/9bcdf88f62b0
\ No newline at end of file