Signal Generator
Dependencies: IniManager RA8875 Watchdog mbed-rtos mbed
Fork of speaker_demo_Analog by
Diff: SignalGenDisplay.cpp
- Revision:
- 1:dd07e1deec6c
- Child:
- 2:8f71b71fce1b
diff -r 1c8118ee4106 -r dd07e1deec6c SignalGenDisplay.cpp --- /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