Signal Generator
Dependencies: IniManager RA8875 Watchdog mbed-rtos mbed
Fork of speaker_demo_Analog by
SignalGenDisplay.cpp
- Committer:
- WiredHome
- Date:
- 2017-01-15
- Revision:
- 2:8f71b71fce1b
- Parent:
- 1:dd07e1deec6c
- Child:
- 3:d22f3e52d06a
File content as of revision 2:8f71b71fce1b:
// // Signal Generator Control System // // #include "SignalGenDisplay.h" #include "rtos.h" #include "IniManager.h" extern INI ini; // ##### Main Page ############################################################# // // +---------------------------------------------------------------------------+ // | +--- Scope Area ---------------------------+ Progam Name and version | // | | | Manufacturer name | // | | +---- Wave Outline - - | | // | | | | [Text Entry Box] [ Back ] | // | | | | | // | | | +------------------------+ | // | | | | | | // | | | | | | | // | | | | | | | // | | ---+ | | | | // | | | | Keypad Area | | // | +------------------------------------------+ | | | // | | | | // | [duty cycle] [frequency] [amplitude] | | | // | | | | // | [ ... ] [period ] [offset ] | | | // | | | | // | [ ] [ ] [ ] [ ] [ ] | | | // | [Sine ] [Square] [Triangle] [Sawtooth] [User] +------------------------+ | // +---------------------------------------------------------------------------+ // 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 // Rectangular Zones const rect_t UI_DATA_ENTRY = {300,53, 410,73}; const rect_t UI_SCOPE_RECT = {4,5, 290,160}; #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 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 }; const int ParameterCount = sizeof(Parameters)/sizeof(Parameters[0]); const char ParameterKeys[] = { 'd', 'f', 'p', 'v', 'o' }; const rect_t UI_PROD_RECT = {298,3, 479,40}; const rect_t NavToSettings = { 4,200, 60,220 }; // 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 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+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 }, {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 }, {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', '.', '-', '\x19', '\x18', '\xB6', }; const char KeyPadKeys[] = { '\x08', '7', '8', '9', '4', '5', '6', '1', '2', '3', '0', '.', '-', '<', '>', '\n' }; // ##### Settings ############################################################# // // +---------------------------------------------------------------------------+ // | Progam Name and version | // | Manufacturer name | // | | // | \ | / | // | = O = | // | / | \ | // | +--------+ | // | | | | // | | | | // | | | | // | | | | // | | | | // | | | | // | |--------| | // | | | | // | [ ... ] | | | // | | | | // | | | | // | +--------+ | // +---------------------------------------------------------------------------+ 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 }; #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)); } 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: // Mode Keys touch for (int i=0; i<ModeCount; i++) { if (lcd->Intersect(ModeButtons[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)) { vis = VS_Settings; Refresh(); while (lcd->TouchPanelReadable()) ; Thread::wait(100); } break; case VS_Settings: Thread::wait(20); 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 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; Refresh(); while (lcd->TouchPanelReadable()) ; Thread::wait(100); ShowMenu(); } break; } } } return 0; } 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); lcd->cls(1); lcd->SelectDrawingLayer(0); // Clear Screen lcd->SetLayerMode(RA8875::ShowLayer0); // Product Info lcd->foreground(UI_ProductNameColor); ShowProductInfo(); ClearScope(); resetDataEntry(); DrawNavGadget(); DrawModeButtons(); 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(); 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); 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); } 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(); } 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 '#': lcd->DumpRegisters(); break; case '?': ShowMenu(); break; case 'S': if (mode != SG_SINE) SaveSettings(OM_MODE); SetWaveformMode(SG_SINE); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); //ret = SG_SINE; break; case 'Q': if (mode != SG_SQUARE) SaveSettings(OM_MODE); SetWaveformMode(SG_SQUARE); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); //ret = SG_SQUARE; break; case 'T': if (mode != SG_TRIANGLE) SaveSettings(OM_MODE); SetWaveformMode(SG_TRIANGLE); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); //ret = SG_TRIANGLE; break; case 'W': if (mode != SG_SAWTOOTH) SaveSettings(OM_MODE); SetWaveformMode(SG_SAWTOOTH); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); //ret = SG_SAWTOOTH; break; case 'U': if (mode != SG_USER) SaveSettings(OM_MODE); SetWaveformMode(SG_USER); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); //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(); } if (textLen == 0) clearTextWindow(); } break; case '\x1B': textBuffer[0] = '\0'; textLen = 0; resetDataEntry(); break; case '\r': case '\n': if (EntryMd) { if (strlen(textBuffer)) { switch (EntryMd) { case OM_DUTY: SetDutyCycle(atof(textBuffer)); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); SaveSettings(OM_DUTY); break; case OM_FREQ: SetFrequency(atof(textBuffer)); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); SaveSettings(OM_FREQ); break; case OM_PERI: SetPeriod(atof(textBuffer)); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); SaveSettings(OM_FREQ); break; case OM_VOLT: SetVoltagePeakToPeak(atof(textBuffer)); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); SaveSettings(OM_VOLT); break; case OM_OFFS: SetVoltageOffset(atof(textBuffer)); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); SaveSettings(OM_OFFS); break; default: break; } } resetDataEntry(OM_NONE, true); } break; case '>': switch (EntryMd) { case OM_DUTY: SetDutyCycle(dutycycle + 1.0); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); SaveSettings(OM_DUTY); break; case OM_FREQ: SetFrequency(frequency + 1.0); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); SaveSettings(OM_FREQ); break; case OM_PERI: SetPeriod(1/frequency + 0.000001); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); SaveSettings(OM_FREQ); break; case OM_VOLT: SetVoltagePeakToPeak(voltage + 0.1); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); SaveSettings(OM_VOLT); break; case OM_OFFS: SetVoltageOffset(offset + 0.1); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); SaveSettings(OM_OFFS); break; default: break; } break; case '<': switch (EntryMd) { case OM_DUTY: SetDutyCycle(dutycycle - 1.0); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); SaveSettings(OM_DUTY); break; case OM_FREQ: SetFrequency(frequency - 1.0); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); SaveSettings(OM_FREQ); break; case OM_PERI: SetPeriod(1/frequency - 0.000001); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); SaveSettings(OM_FREQ); break; case OM_VOLT: SetVoltagePeakToPeak(voltage - 0.1); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); SaveSettings(OM_VOLT); break; 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 != OM_DUTY) { SaveSettings(EntryMd); resetDataEntry(OM_DUTY, true); updateDutyCycle(); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); } else { resetDataEntry(OM_NONE, true); } break; case 'f': if (EntryMd != OM_FREQ) { SaveSettings(EntryMd); resetDataEntry(OM_FREQ, true); updateFrequency(); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); } else { resetDataEntry(OM_NONE, true); } break; case 'p': if (EntryMd != OM_PERI) { SaveSettings(EntryMd); resetDataEntry(OM_PERI, true); updatePeriod(); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); } else { resetDataEntry(OM_NONE, true); } break; case 'v': if (EntryMd != OM_VOLT) { SaveSettings(EntryMd); resetDataEntry(OM_VOLT, true); updateVoltage(); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); } else { resetDataEntry(OM_NONE, true); } break; case 'o': if (EntryMd != OM_OFFS) { SaveSettings(EntryMd); resetDataEntry(OM_OFFS, true); updateOffset(); signal->PrepareWaveform(mode, frequency, dutycycle, voltage, offset); } else { resetDataEntry(OM_NONE, true); } break; default: break; } return ret; } bool SignalGenDisplay::SetWaveformMode(SG_Mode _mode, bool force) { if (/* _mode >= SG_SINE && */ _mode <= SG_USER) { mode = _mode; DrawModeButtons(); 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) { if (_frequency >= 1.0 && _frequency <= 1.0E6) { frequency = _frequency; updateFrequency(); updatePeriod(); UpdateScope(); return true; } else { 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) { ClearScope(); rect_t r = UI_WAVE_RECT; 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(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( // 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 y = r.p2.y - df; dim_t w = (r.p2.x + SC_RIGHT_MARGIN/3 - r.p1.x) / 35; 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); } } switch (sgn(offset)) { default: case 0: break; case -1: case 1: 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); 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-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, 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-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; 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); } // ++ +----+ + + // . . | | / \ /| // . . | | | / \ / / | // . | | \ / / | // ++ +----+ + + + // 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 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<=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 / vRange; lcd->pixel(r.p1.x + cycle * w/2 + x, y, color); } 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 / vRange; lcd->pixel(r.p1.x + cycle * w/2 + privDutyCycleX + x, y, color); } } break; case SG_SQUARE: for (int cycle=0; cycle<2; cycle++) { 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++) { 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++) { 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: 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 != OM_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 != OM_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); 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 != OM_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 != OM_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 != OM_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, true); 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(); } 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; else if (value > max) return max; else return value; } void SignalGenDisplay::SaveSettings(OM_Changes reportMode) { char buf[20]; 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); } } } void SignalGenDisplay::resetDataEntry(OM_Changes nextMode, bool save) { OM_Changes last = EntryMd; 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 OM_NONE: updateDutyCycle(); updateFrequency(); updatePeriod(); updateVoltage(); updateOffset(); break; case OM_DUTY: updateDutyCycle(); if (save) { SaveSettings(OM_DUTY); } break; case OM_FREQ: updateFrequency(); if (save) { SaveSettings(OM_FREQ); } break; case OM_PERI: updatePeriod(); if (save) { SaveSettings(OM_FREQ); } break; case OM_VOLT: updateVoltage(); if (save) { SaveSettings(OM_VOLT); } break; case OM_OFFS: updateOffset(); if (save) { SaveSettings(OM_OFFS); } break; default: break; } DrawKeypadEnabled(EntryMd != OM_NONE); if (EntryMd == OM_NONE) { clearTextWindow(); } printf("<- end resetDataEntry()\r\n"); }