From 5f6181bb3c6ab4274a8068aaf14b278fa65e443e Mon Sep 17 00:00:00 2001 From: Clyne Sullivan Date: Sun, 21 Feb 2021 08:59:15 -0500 Subject: [PATCH] signal monitoring support --- Makefile | 4 +- gui/Makefile | 17 ++++---- gui/stmdsp.cpp | 27 ++++++++++++ gui/stmdsp.hpp | 3 +- gui/wxmain.cpp | 91 +++++++++++++++++++++++++++++++++++++---- gui/wxmain.hpp | 6 ++- source/adc.hpp | 1 + source/main.cpp | 42 +++++++++++++++++-- source/samplebuffer.cpp | 14 +++++-- source/samplebuffer.hpp | 4 +- 10 files changed, 183 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index 53340e4..7c36535 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ endif # C++ specific options here (added to USE_OPT). ifeq ($(USE_CPPOPT),) - USE_CPPOPT = -std=c++17 -fno-rtti + USE_CPPOPT = -std=c++2a -fno-rtti endif # Enable this if you want the linker to remove unused code and data. @@ -25,7 +25,7 @@ endif # Linker extra options here. ifeq ($(USE_LDOPT),) - USE_LDOPT = +# USE_LDOPT = -L.,-lzig endif # Enable this if you want link time optimizations (LTO). diff --git a/gui/Makefile b/gui/Makefile index 679e1e4..20dd473 100644 --- a/gui/Makefile +++ b/gui/Makefile @@ -1,18 +1,21 @@ CXX = g++-10 -CXXFLAGS = --std=c++20 -ggdb -Og \ - -Wall -Wextra -pedantic \ - -Wno-deprecated-copy \ - -Iserial/include \ - $(shell wx-config --cxxflags) +CXXFLAGS = --std=c++20 -ggdb -O0 \ + -Wall -Wextra -pedantic \ + -Wno-deprecated-copy \ + -Iserial/include \ + $(shell wx-config --cxxflags) -CXXFILES = $(shell find serial/src -name "*.cc") $(wildcard *.cpp) +CXXFILES = serial/src/serial.cc \ + serial/src/impl/unix.cc \ + serial/src/impl/list_ports/list_ports_linux.cc \ + $(wildcard *.cpp) OFILES = $(patsubst %.cc, %.o, $(patsubst %.cpp, %.o, $(CXXFILES))) LIBS = $(shell wx-config --libs) -lwx_gtk3u_stc-3.1 OUTELF = stmdspgui all: $(OUTELF) - + $(OUTELF): $(OFILES) @echo " CXX " $(OUTELF) @$(CXX) $(CXXFLAGS) $(OFILES) $(LIBS) -o $(OUTELF) diff --git a/gui/stmdsp.cpp b/gui/stmdsp.cpp index 1b4bba6..5ea18af 100644 --- a/gui/stmdsp.cpp +++ b/gui/stmdsp.cpp @@ -120,6 +120,33 @@ namespace stmdsp return {}; } + std::vector device::continuous_read_input() { + if (connected()) { + m_serial.write("t"); + unsigned char sizebytes[2]; + m_serial.read(sizebytes, 2); + unsigned int size = sizebytes[0] | (sizebytes[1] << 8); + if (size > 0) { + std::vector data (size); + unsigned int total = size * sizeof(adcsample_t); + unsigned int offset = 0; + + while (total > 512) { + m_serial.read(reinterpret_cast(&data[0]) + offset, 512); + m_serial.write("n"); + offset += 512; + total -= 512; + } + m_serial.read(reinterpret_cast(&data[0]) + offset, total); + m_serial.write("n"); + return data; + + } + } + + return {}; + } + void device::continuous_stop() { if (connected()) m_serial.write("S"); diff --git a/gui/stmdsp.hpp b/gui/stmdsp.hpp index f805ab3..a22a55d 100644 --- a/gui/stmdsp.hpp +++ b/gui/stmdsp.hpp @@ -19,7 +19,7 @@ namespace stmdsp { - constexpr unsigned int SAMPLES_MAX = 4000; + constexpr unsigned int SAMPLES_MAX = 4096; class scanner { @@ -62,6 +62,7 @@ namespace stmdsp void continuous_start_measure(); uint32_t continuous_start_get_measurement(); std::vector continuous_read(); + std::vector continuous_read_input(); void continuous_stop(); void siggen_upload(dacsample_t *buffer, unsigned int size); diff --git a/gui/wxmain.cpp b/gui/wxmain.cpp index c7acddf..e5f0d4d 100644 --- a/gui/wxmain.cpp +++ b/gui/wxmain.cpp @@ -12,6 +12,7 @@ #include "wxmain.hpp" #include +#include #include #include #include @@ -25,6 +26,7 @@ #include #include +#include #include static const std::array srateValues { @@ -133,6 +135,7 @@ enum Id { MRunConnect, MRunStart, MRunMeasure, + MRunDrawSamples, MRunLogResults, MRunUpload, MRunUnload, @@ -177,6 +180,12 @@ MainFrame::MainFrame() : wxFrame(nullptr, wxID_ANY, "stmdspgui", wxPoint(50, 50) wxDefaultPosition, wxDefaultSize, srateValues.size(), srateValues.data(), wxCB_READONLY); + m_device_samples = reinterpret_cast(::mmap( + nullptr, stmdsp::SAMPLES_MAX * sizeof(stmdsp::adcsample_t), + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0)); + m_device_samples_input = reinterpret_cast(::mmap( + nullptr, stmdsp::SAMPLES_MAX * sizeof(stmdsp::adcsample_t), + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0)); m_menu_bar->Append(menuFile, "&File"); m_menu_bar->Append(menuRun, "&Run"); @@ -217,6 +226,7 @@ MainFrame::MainFrame() : wxFrame(nullptr, wxID_ANY, "stmdspgui", wxPoint(50, 50) // General Bind(wxEVT_TIMER, &MainFrame::onMeasureTimer, this, Id::MeasureTimer); Bind(wxEVT_CLOSE_WINDOW, &MainFrame::onCloseEvent, this, wxID_ANY); + Bind(wxEVT_PAINT, &MainFrame::onPaint, this, wxID_ANY); // Toolbar actions Bind(wxEVT_BUTTON, &MainFrame::onRunCompile, this, Id::MCodeCompile, wxID_ANY, comp); @@ -237,6 +247,7 @@ MainFrame::MainFrame() : wxFrame(nullptr, wxID_ANY, "stmdspgui", wxPoint(50, 50) menuRun->AppendSeparator(); Bind(wxEVT_MENU, &MainFrame::onRunStart, this, Id::MRunStart, wxID_ANY, menuRun->Append(MRunStart, "&Start")); m_run_measure = menuRun->AppendCheckItem(MRunMeasure, "&Measure code time"); + m_run_draw_samples = menuRun->AppendCheckItem(MRunDrawSamples, "&Draw samples"); Bind(wxEVT_MENU, &MainFrame::onRunLogResults, this, Id::MRunLogResults, wxID_ANY, menuRun->AppendCheckItem(MRunLogResults, "&Log results...")); menuRun->AppendSeparator(); Bind(wxEVT_MENU, &MainFrame::onRunUpload, this, Id::MRunUpload, wxID_ANY, menuRun->Append(MRunUpload, "&Upload code")); @@ -276,12 +287,21 @@ void MainFrame::onCloseEvent(wxCloseEvent& event) // Only called while connected and running. void MainFrame::onMeasureTimer(wxTimerEvent&) { - if (m_conv_result_log) { - // We're meant to log - if (auto samples = m_device->continuous_read(); samples.size() > 0) { - for (auto& s : samples) { - auto str = std::to_string(s); - m_conv_result_log->Write(str.c_str(), str.size()); + if (m_conv_result_log || m_run_draw_samples->IsChecked()) { + auto samples = m_device->continuous_read(); + if (samples.size() > 0) { + std::copy(samples.cbegin(), samples.cend(), m_device_samples); + + if (m_conv_result_log) { + for (auto& s : samples) { + auto str = std::to_string(s); + m_conv_result_log->Write(str.c_str(), str.size()); + } + } + if (m_run_draw_samples->IsChecked()) { + samples = m_device->continuous_read_input(); + std::copy(samples.cbegin(), samples.cend(), m_device_samples_input); + this->Refresh(); } } } @@ -304,6 +324,54 @@ void MainFrame::onMeasureTimer(wxTimerEvent&) } } +void MainFrame::onPaint(wxPaintEvent&) +{ + if (!m_is_running || !m_run_draw_samples->IsChecked()) { + if (!m_compile_output->IsShown()) + m_compile_output->Show(); + return; + } else if (m_compile_output->IsShown()) { + m_compile_output->Hide(); + } + + auto py = m_compile_output->GetScreenPosition().y - this->GetScreenPosition().y - 28; + wxRect rect { + 0, py, + this->GetSize().GetWidth(), + this->GetSize().GetHeight() - py - 60 + }; + + auto *dc = new wxPaintDC(this); + dc->SetBrush(*wxBLACK_BRUSH); + dc->SetPen(*wxBLACK_PEN); + dc->DrawRectangle(rect); + dc->SetBrush(*wxRED_BRUSH); + dc->SetPen(*wxRED_PEN); + auto stoy = [&](stmdsp::adcsample_t s) { + return static_cast(py) + rect.GetHeight() - + (static_cast(rect.GetHeight()) * s / 4095.f); + }; + auto scount = m_device->get_buffer_size(); + float dx = static_cast(rect.GetWidth()) / scount; + float x = 0; + float lasty = stoy(2048); + for (decltype(scount) i = 0; i < scount; i++) { + auto y = stoy(m_device_samples[i]); + dc->DrawLine(x, lasty, x + dx, y); + x += dx, lasty = y; + } + dc->SetBrush(*wxBLUE_BRUSH); + dc->SetPen(*wxBLUE_PEN); + x = 0; + lasty = stoy(2048); + for (decltype(scount) i = 0; i < scount; i++) { + auto y = stoy(m_device_samples_input[i]); + dc->DrawLine(x, lasty, x + dx, y); + x += dx, lasty = y; + } + delete dc; +} + void MainFrame::prepareEditor() { m_text_editor->SetLexer(wxSTC_LEX_CPP); @@ -536,11 +604,14 @@ void MainFrame::onRunStart(wxCommandEvent& ce) srateNums[m_rate_select->GetSelection()]); } else if (m_conv_result_log) { m_measure_timer->Start(15); + } else if (m_run_draw_samples->IsChecked()) { + m_measure_timer->Start(300); } m_device->continuous_start(); } + m_rate_select->Enable(false); menuItem->SetItemLabel("&Stop"); m_status_bar->SetStatusText("Running."); m_is_running = true; @@ -548,9 +619,13 @@ void MainFrame::onRunStart(wxCommandEvent& ce) m_device->continuous_stop(); m_measure_timer->Stop(); + m_rate_select->Enable(true); menuItem->SetItemLabel("&Start"); m_status_bar->SetStatusText("Ready."); m_is_running = false; + + if (m_run_draw_samples->IsChecked()) + m_compile_output->Refresh(); } } @@ -581,7 +656,7 @@ void MainFrame::onRunLogResults(wxCommandEvent& ce) void MainFrame::onRunEditBSize(wxCommandEvent&) { - wxTextEntryDialog dialog (this, "Enter new buffer size (100-4000)", "Set Buffer Size"); + wxTextEntryDialog dialog (this, "Enter new buffer size (100-4096)", "Set Buffer Size"); if (dialog.ShowModal() == wxID_OK) { if (wxString value = dialog.GetValue(); !value.IsEmpty()) { if (unsigned long n; value.ToULong(&n)) { @@ -667,9 +742,11 @@ void MainFrame::onRunGenStart(wxCommandEvent& ce) if (menuItem->IsChecked()) { m_device->siggen_start(); menuItem->SetItemLabel("Stop &generator"); + m_status_bar->SetStatusText("Generator running."); } else { m_device->siggen_stop(); menuItem->SetItemLabel("Start &generator"); + m_status_bar->SetStatusText("Ready."); } } diff --git a/gui/wxmain.hpp b/gui/wxmain.hpp index e96f51d..9b3db1e 100644 --- a/gui/wxmain.hpp +++ b/gui/wxmain.hpp @@ -58,7 +58,8 @@ public: void onRunCompile(wxCommandEvent&); void onCodeDisassemble(wxCommandEvent&); - void onMeasureTimer(wxTimerEvent& te); + void onPaint(wxPaintEvent&); + void onMeasureTimer(wxTimerEvent&); private: // Set to true if connected and running @@ -69,6 +70,7 @@ private: wxTextCtrl *m_compile_output = nullptr; wxControl *m_signal_area = nullptr; wxMenuItem *m_run_measure = nullptr; + wxMenuItem *m_run_draw_samples = nullptr; wxTimer *m_measure_timer = nullptr; wxStatusBar *m_status_bar = nullptr; wxMenuBar *m_menu_bar = nullptr; @@ -87,6 +89,8 @@ private: // Device interface // Not null if connected stmdsp::device *m_device = nullptr; + stmdsp::adcsample_t *m_device_samples = nullptr; + stmdsp::adcsample_t *m_device_samples_input = nullptr; // WAV data for signal generator // Not null when a WAV is loaded wav::clip *m_wav_clip = nullptr; diff --git a/source/adc.hpp b/source/adc.hpp index 488501c..d9a435a 100644 --- a/source/adc.hpp +++ b/source/adc.hpp @@ -42,6 +42,7 @@ private: static size_t m_current_buffer_size; static Operation m_operation; +public: static void conversionCallback(ADCDriver *); }; diff --git a/source/main.cpp b/source/main.cpp index 0dfc0e7..6e58510 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -52,7 +52,7 @@ static MAILBOX_DECL(conversionMB, conversionMBBuffer, 2); // Thread for LED status and wakeup hold __attribute__((section(".stacks"))) static THD_WORKING_AREA(monitorThreadWA, 4096); -static THD_FUNCTION(monitorThread, arg); +/*extern "C"*/static THD_FUNCTION(monitorThread, arg); // Thread for managing the conversion task __attribute__((section(".stacks"))) @@ -161,6 +161,7 @@ THD_FUNCTION(communicationThread, arg) // 'r' - Read or write sample rate. // 'S' - Stop conversion. // 's' - Get latest block of conversion results. + // 't' - Get latest block of conversion input. // 'W' - Start signal generator (siggen). // 'w' - Stop siggen. @@ -308,6 +309,28 @@ THD_FUNCTION(communicationThread, arg) USBSerial::write(reinterpret_cast("\0\0"), 2); } break; + case 't': + if (auto samps = samplesIn.modified(); samps != nullptr) { + unsigned char buf[2] = { + static_cast(samplesIn.size() / 2 & 0xFF), + static_cast(((samplesIn.size() / 2) >> 8) & 0xFF) + }; + USBSerial::write(buf, 2); + unsigned int total = samplesIn.bytesize() / 2; + unsigned int offset = 0; + unsigned char unused; + while (total > 512) { + USBSerial::write(reinterpret_cast(samps) + offset, 512); + while (USBSerial::read(&unused, 1) == 0); + offset += 512; + total -= 512; + } + USBSerial::write(reinterpret_cast(samps) + offset, total); + while (USBSerial::read(&unused, 1) == 0); + } else { + USBSerial::write(reinterpret_cast("\0\0"), 2); + } + break; case 'W': DAC::start(1, samplesSigGen.data(), samplesSigGen.size()); @@ -455,7 +478,13 @@ void signal_operate(adcsample_t *buffer, size_t) chSysUnlockFromISR(); conversion_abort(); } else { - chMBPostI(&conversionMB, buffer == samplesIn.data() ? MSG_CONVFIRST : MSG_CONVSECOND); + if (buffer == samplesIn.data()) { + samplesIn.setModified(); + chMBPostI(&conversionMB, MSG_CONVFIRST); + } else { + samplesIn.setMidmodified(); + chMBPostI(&conversionMB, MSG_CONVSECOND); + } chSysUnlockFromISR(); } } @@ -463,8 +492,13 @@ void signal_operate(adcsample_t *buffer, size_t) void signal_operate_measure(adcsample_t *buffer, [[maybe_unused]] size_t count) { chSysLockFromISR(); - chMBPostI(&conversionMB, buffer == samplesIn.data() ? MSG_CONVFIRST_MEASURE - : MSG_CONVSECOND_MEASURE); + if (buffer == samplesIn.data()) { + samplesIn.setModified(); + chMBPostI(&conversionMB, MSG_CONVFIRST_MEASURE); + } else { + samplesIn.setMidmodified(); + chMBPostI(&conversionMB, MSG_CONVSECOND_MEASURE); + } chSysUnlockFromISR(); ADC::setOperation(signal_operate); diff --git a/source/samplebuffer.cpp b/source/samplebuffer.cpp index 55ebc81..24cc424 100644 --- a/source/samplebuffer.cpp +++ b/source/samplebuffer.cpp @@ -9,15 +9,23 @@ void SampleBuffer::clear() { __attribute__((section(".convcode"))) void SampleBuffer::modify(Sample *data, unsigned int srcsize) { auto size = srcsize < m_size ? srcsize : m_size; - for (Sample *d = data, *s = m_buffer; d != data + size;) - *d++ = *s++; m_modified = m_buffer; + for (Sample *d = m_buffer, *s = data; s != data + size;) + *d++ = *s++; } __attribute__((section(".convcode"))) void SampleBuffer::midmodify(Sample *data, unsigned int srcsize) { auto size = srcsize < m_size / 2 ? srcsize : m_size / 2; - for (Sample *d = data, *s = middata(); d != data + size;) + m_modified = middata(); + for (Sample *d = middata(), *s = data; s != data + size;) *d++ = *s++; +} + +void SampleBuffer::setModified() { + m_modified = m_buffer; +} + +void SampleBuffer::setMidmodified() { m_modified = middata(); } diff --git a/source/samplebuffer.hpp b/source/samplebuffer.hpp index 334c048..bed52bf 100644 --- a/source/samplebuffer.hpp +++ b/source/samplebuffer.hpp @@ -6,7 +6,7 @@ using Sample = uint16_t; -// gives 8000 (8192) +// Gives 8000 (8192) samples total (algorithm works with max 4096). constexpr unsigned int MAX_SAMPLE_BUFFER_BYTESIZE = 16384; constexpr unsigned int MAX_SAMPLE_BUFFER_SIZE = MAX_SAMPLE_BUFFER_BYTESIZE / sizeof(Sample); @@ -19,6 +19,8 @@ public: void modify(Sample *data, unsigned int srcsize); void midmodify(Sample *data, unsigned int srcsize); + void setModified(); + void setMidmodified(); Sample *modified(); Sample *data();