signal monitoring support

pull/3/head
Clyne 4 years ago
parent e8f312f881
commit 5f6181bb3c

@ -15,7 +15,7 @@ endif
# C++ specific options here (added to USE_OPT). # C++ specific options here (added to USE_OPT).
ifeq ($(USE_CPPOPT),) ifeq ($(USE_CPPOPT),)
USE_CPPOPT = -std=c++17 -fno-rtti USE_CPPOPT = -std=c++2a -fno-rtti
endif endif
# Enable this if you want the linker to remove unused code and data. # Enable this if you want the linker to remove unused code and data.
@ -25,7 +25,7 @@ endif
# Linker extra options here. # Linker extra options here.
ifeq ($(USE_LDOPT),) ifeq ($(USE_LDOPT),)
USE_LDOPT = # USE_LDOPT = -L.,-lzig
endif endif
# Enable this if you want link time optimizations (LTO). # Enable this if you want link time optimizations (LTO).

@ -1,18 +1,21 @@
CXX = g++-10 CXX = g++-10
CXXFLAGS = --std=c++20 -ggdb -Og \ CXXFLAGS = --std=c++20 -ggdb -O0 \
-Wall -Wextra -pedantic \ -Wall -Wextra -pedantic \
-Wno-deprecated-copy \ -Wno-deprecated-copy \
-Iserial/include \ -Iserial/include \
$(shell wx-config --cxxflags) $(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))) OFILES = $(patsubst %.cc, %.o, $(patsubst %.cpp, %.o, $(CXXFILES)))
LIBS = $(shell wx-config --libs) -lwx_gtk3u_stc-3.1 LIBS = $(shell wx-config --libs) -lwx_gtk3u_stc-3.1
OUTELF = stmdspgui OUTELF = stmdspgui
all: $(OUTELF) all: $(OUTELF)
$(OUTELF): $(OFILES) $(OUTELF): $(OFILES)
@echo " CXX " $(OUTELF) @echo " CXX " $(OUTELF)
@$(CXX) $(CXXFLAGS) $(OFILES) $(LIBS) -o $(OUTELF) @$(CXX) $(CXXFLAGS) $(OFILES) $(LIBS) -o $(OUTELF)

@ -120,6 +120,33 @@ namespace stmdsp
return {}; return {};
} }
std::vector<adcsample_t> 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<adcsample_t> data (size);
unsigned int total = size * sizeof(adcsample_t);
unsigned int offset = 0;
while (total > 512) {
m_serial.read(reinterpret_cast<uint8_t *>(&data[0]) + offset, 512);
m_serial.write("n");
offset += 512;
total -= 512;
}
m_serial.read(reinterpret_cast<uint8_t *>(&data[0]) + offset, total);
m_serial.write("n");
return data;
}
}
return {};
}
void device::continuous_stop() { void device::continuous_stop() {
if (connected()) if (connected())
m_serial.write("S"); m_serial.write("S");

@ -19,7 +19,7 @@
namespace stmdsp namespace stmdsp
{ {
constexpr unsigned int SAMPLES_MAX = 4000; constexpr unsigned int SAMPLES_MAX = 4096;
class scanner class scanner
{ {
@ -62,6 +62,7 @@ namespace stmdsp
void continuous_start_measure(); void continuous_start_measure();
uint32_t continuous_start_get_measurement(); uint32_t continuous_start_get_measurement();
std::vector<adcsample_t> continuous_read(); std::vector<adcsample_t> continuous_read();
std::vector<adcsample_t> continuous_read_input();
void continuous_stop(); void continuous_stop();
void siggen_upload(dacsample_t *buffer, unsigned int size); void siggen_upload(dacsample_t *buffer, unsigned int size);

@ -12,6 +12,7 @@
#include "wxmain.hpp" #include "wxmain.hpp"
#include <wx/combobox.h> #include <wx/combobox.h>
#include <wx/dcclient.h>
#include <wx/dir.h> #include <wx/dir.h>
#include <wx/filename.h> #include <wx/filename.h>
#include <wx/filedlg.h> #include <wx/filedlg.h>
@ -25,6 +26,7 @@
#include <wx/textdlg.h> #include <wx/textdlg.h>
#include <array> #include <array>
#include <sys/mman.h>
#include <vector> #include <vector>
static const std::array<wxString, 6> srateValues { static const std::array<wxString, 6> srateValues {
@ -133,6 +135,7 @@ enum Id {
MRunConnect, MRunConnect,
MRunStart, MRunStart,
MRunMeasure, MRunMeasure,
MRunDrawSamples,
MRunLogResults, MRunLogResults,
MRunUpload, MRunUpload,
MRunUnload, MRunUnload,
@ -177,6 +180,12 @@ MainFrame::MainFrame() : wxFrame(nullptr, wxID_ANY, "stmdspgui", wxPoint(50, 50)
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
srateValues.size(), srateValues.data(), srateValues.size(), srateValues.data(),
wxCB_READONLY); wxCB_READONLY);
m_device_samples = reinterpret_cast<stmdsp::adcsample_t *>(::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<stmdsp::adcsample_t *>(::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(menuFile, "&File");
m_menu_bar->Append(menuRun, "&Run"); m_menu_bar->Append(menuRun, "&Run");
@ -217,6 +226,7 @@ MainFrame::MainFrame() : wxFrame(nullptr, wxID_ANY, "stmdspgui", wxPoint(50, 50)
// General // General
Bind(wxEVT_TIMER, &MainFrame::onMeasureTimer, this, Id::MeasureTimer); Bind(wxEVT_TIMER, &MainFrame::onMeasureTimer, this, Id::MeasureTimer);
Bind(wxEVT_CLOSE_WINDOW, &MainFrame::onCloseEvent, this, wxID_ANY); Bind(wxEVT_CLOSE_WINDOW, &MainFrame::onCloseEvent, this, wxID_ANY);
Bind(wxEVT_PAINT, &MainFrame::onPaint, this, wxID_ANY);
// Toolbar actions // Toolbar actions
Bind(wxEVT_BUTTON, &MainFrame::onRunCompile, this, Id::MCodeCompile, wxID_ANY, comp); 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(); menuRun->AppendSeparator();
Bind(wxEVT_MENU, &MainFrame::onRunStart, this, Id::MRunStart, wxID_ANY, menuRun->Append(MRunStart, "&Start")); 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_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...")); Bind(wxEVT_MENU, &MainFrame::onRunLogResults, this, Id::MRunLogResults, wxID_ANY, menuRun->AppendCheckItem(MRunLogResults, "&Log results..."));
menuRun->AppendSeparator(); menuRun->AppendSeparator();
Bind(wxEVT_MENU, &MainFrame::onRunUpload, this, Id::MRunUpload, wxID_ANY, menuRun->Append(MRunUpload, "&Upload code")); 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. // Only called while connected and running.
void MainFrame::onMeasureTimer(wxTimerEvent&) void MainFrame::onMeasureTimer(wxTimerEvent&)
{ {
if (m_conv_result_log) { if (m_conv_result_log || m_run_draw_samples->IsChecked()) {
// We're meant to log auto samples = m_device->continuous_read();
if (auto samples = m_device->continuous_read(); samples.size() > 0) { if (samples.size() > 0) {
for (auto& s : samples) { std::copy(samples.cbegin(), samples.cend(), m_device_samples);
auto str = std::to_string(s);
m_conv_result_log->Write(str.c_str(), str.size()); 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<float>(py) + rect.GetHeight() -
(static_cast<float>(rect.GetHeight()) * s / 4095.f);
};
auto scount = m_device->get_buffer_size();
float dx = static_cast<float>(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() void MainFrame::prepareEditor()
{ {
m_text_editor->SetLexer(wxSTC_LEX_CPP); m_text_editor->SetLexer(wxSTC_LEX_CPP);
@ -536,11 +604,14 @@ void MainFrame::onRunStart(wxCommandEvent& ce)
srateNums[m_rate_select->GetSelection()]); srateNums[m_rate_select->GetSelection()]);
} else if (m_conv_result_log) { } else if (m_conv_result_log) {
m_measure_timer->Start(15); m_measure_timer->Start(15);
} else if (m_run_draw_samples->IsChecked()) {
m_measure_timer->Start(300);
} }
m_device->continuous_start(); m_device->continuous_start();
} }
m_rate_select->Enable(false);
menuItem->SetItemLabel("&Stop"); menuItem->SetItemLabel("&Stop");
m_status_bar->SetStatusText("Running."); m_status_bar->SetStatusText("Running.");
m_is_running = true; m_is_running = true;
@ -548,9 +619,13 @@ void MainFrame::onRunStart(wxCommandEvent& ce)
m_device->continuous_stop(); m_device->continuous_stop();
m_measure_timer->Stop(); m_measure_timer->Stop();
m_rate_select->Enable(true);
menuItem->SetItemLabel("&Start"); menuItem->SetItemLabel("&Start");
m_status_bar->SetStatusText("Ready."); m_status_bar->SetStatusText("Ready.");
m_is_running = false; 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&) 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 (dialog.ShowModal() == wxID_OK) {
if (wxString value = dialog.GetValue(); !value.IsEmpty()) { if (wxString value = dialog.GetValue(); !value.IsEmpty()) {
if (unsigned long n; value.ToULong(&n)) { if (unsigned long n; value.ToULong(&n)) {
@ -667,9 +742,11 @@ void MainFrame::onRunGenStart(wxCommandEvent& ce)
if (menuItem->IsChecked()) { if (menuItem->IsChecked()) {
m_device->siggen_start(); m_device->siggen_start();
menuItem->SetItemLabel("Stop &generator"); menuItem->SetItemLabel("Stop &generator");
m_status_bar->SetStatusText("Generator running.");
} else { } else {
m_device->siggen_stop(); m_device->siggen_stop();
menuItem->SetItemLabel("Start &generator"); menuItem->SetItemLabel("Start &generator");
m_status_bar->SetStatusText("Ready.");
} }
} }

@ -58,7 +58,8 @@ public:
void onRunCompile(wxCommandEvent&); void onRunCompile(wxCommandEvent&);
void onCodeDisassemble(wxCommandEvent&); void onCodeDisassemble(wxCommandEvent&);
void onMeasureTimer(wxTimerEvent& te); void onPaint(wxPaintEvent&);
void onMeasureTimer(wxTimerEvent&);
private: private:
// Set to true if connected and running // Set to true if connected and running
@ -69,6 +70,7 @@ private:
wxTextCtrl *m_compile_output = nullptr; wxTextCtrl *m_compile_output = nullptr;
wxControl *m_signal_area = nullptr; wxControl *m_signal_area = nullptr;
wxMenuItem *m_run_measure = nullptr; wxMenuItem *m_run_measure = nullptr;
wxMenuItem *m_run_draw_samples = nullptr;
wxTimer *m_measure_timer = nullptr; wxTimer *m_measure_timer = nullptr;
wxStatusBar *m_status_bar = nullptr; wxStatusBar *m_status_bar = nullptr;
wxMenuBar *m_menu_bar = nullptr; wxMenuBar *m_menu_bar = nullptr;
@ -87,6 +89,8 @@ private:
// Device interface // Device interface
// Not null if connected // Not null if connected
stmdsp::device *m_device = nullptr; 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 // WAV data for signal generator
// Not null when a WAV is loaded // Not null when a WAV is loaded
wav::clip *m_wav_clip = nullptr; wav::clip *m_wav_clip = nullptr;

@ -42,6 +42,7 @@ private:
static size_t m_current_buffer_size; static size_t m_current_buffer_size;
static Operation m_operation; static Operation m_operation;
public:
static void conversionCallback(ADCDriver *); static void conversionCallback(ADCDriver *);
}; };

@ -52,7 +52,7 @@ static MAILBOX_DECL(conversionMB, conversionMBBuffer, 2);
// Thread for LED status and wakeup hold // Thread for LED status and wakeup hold
__attribute__((section(".stacks"))) __attribute__((section(".stacks")))
static THD_WORKING_AREA(monitorThreadWA, 4096); static THD_WORKING_AREA(monitorThreadWA, 4096);
static THD_FUNCTION(monitorThread, arg); /*extern "C"*/static THD_FUNCTION(monitorThread, arg);
// Thread for managing the conversion task // Thread for managing the conversion task
__attribute__((section(".stacks"))) __attribute__((section(".stacks")))
@ -161,6 +161,7 @@ THD_FUNCTION(communicationThread, arg)
// 'r' - Read or write sample rate. // 'r' - Read or write sample rate.
// 'S' - Stop conversion. // 'S' - Stop conversion.
// 's' - Get latest block of conversion results. // 's' - Get latest block of conversion results.
// 't' - Get latest block of conversion input.
// 'W' - Start signal generator (siggen). // 'W' - Start signal generator (siggen).
// 'w' - Stop siggen. // 'w' - Stop siggen.
@ -308,6 +309,28 @@ THD_FUNCTION(communicationThread, arg)
USBSerial::write(reinterpret_cast<const uint8_t *>("\0\0"), 2); USBSerial::write(reinterpret_cast<const uint8_t *>("\0\0"), 2);
} }
break; break;
case 't':
if (auto samps = samplesIn.modified(); samps != nullptr) {
unsigned char buf[2] = {
static_cast<unsigned char>(samplesIn.size() / 2 & 0xFF),
static_cast<unsigned char>(((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<uint8_t *>(samps) + offset, 512);
while (USBSerial::read(&unused, 1) == 0);
offset += 512;
total -= 512;
}
USBSerial::write(reinterpret_cast<uint8_t *>(samps) + offset, total);
while (USBSerial::read(&unused, 1) == 0);
} else {
USBSerial::write(reinterpret_cast<const uint8_t *>("\0\0"), 2);
}
break;
case 'W': case 'W':
DAC::start(1, samplesSigGen.data(), samplesSigGen.size()); DAC::start(1, samplesSigGen.data(), samplesSigGen.size());
@ -455,7 +478,13 @@ void signal_operate(adcsample_t *buffer, size_t)
chSysUnlockFromISR(); chSysUnlockFromISR();
conversion_abort(); conversion_abort();
} else { } 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(); 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) void signal_operate_measure(adcsample_t *buffer, [[maybe_unused]] size_t count)
{ {
chSysLockFromISR(); chSysLockFromISR();
chMBPostI(&conversionMB, buffer == samplesIn.data() ? MSG_CONVFIRST_MEASURE if (buffer == samplesIn.data()) {
: MSG_CONVSECOND_MEASURE); samplesIn.setModified();
chMBPostI(&conversionMB, MSG_CONVFIRST_MEASURE);
} else {
samplesIn.setMidmodified();
chMBPostI(&conversionMB, MSG_CONVSECOND_MEASURE);
}
chSysUnlockFromISR(); chSysUnlockFromISR();
ADC::setOperation(signal_operate); ADC::setOperation(signal_operate);

@ -9,15 +9,23 @@ void SampleBuffer::clear() {
__attribute__((section(".convcode"))) __attribute__((section(".convcode")))
void SampleBuffer::modify(Sample *data, unsigned int srcsize) { void SampleBuffer::modify(Sample *data, unsigned int srcsize) {
auto size = srcsize < m_size ? srcsize : m_size; auto size = srcsize < m_size ? srcsize : m_size;
for (Sample *d = data, *s = m_buffer; d != data + size;)
*d++ = *s++;
m_modified = m_buffer; m_modified = m_buffer;
for (Sample *d = m_buffer, *s = data; s != data + size;)
*d++ = *s++;
} }
__attribute__((section(".convcode"))) __attribute__((section(".convcode")))
void SampleBuffer::midmodify(Sample *data, unsigned int srcsize) { void SampleBuffer::midmodify(Sample *data, unsigned int srcsize) {
auto size = srcsize < m_size / 2 ? srcsize : m_size / 2; 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++; *d++ = *s++;
}
void SampleBuffer::setModified() {
m_modified = m_buffer;
}
void SampleBuffer::setMidmodified() {
m_modified = middata(); m_modified = middata();
} }

@ -6,7 +6,7 @@
using Sample = uint16_t; 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_BYTESIZE = 16384;
constexpr unsigned int MAX_SAMPLE_BUFFER_SIZE = MAX_SAMPLE_BUFFER_BYTESIZE / sizeof(Sample); 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 modify(Sample *data, unsigned int srcsize);
void midmodify(Sample *data, unsigned int srcsize); void midmodify(Sample *data, unsigned int srcsize);
void setModified();
void setMidmodified();
Sample *modified(); Sample *modified();
Sample *data(); Sample *data();

Loading…
Cancel
Save