diff options
author | clyne <clyne@bitgloo.com> | 2021-03-21 16:34:21 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-21 16:34:21 -0400 |
commit | 9b926b81ef1e8a4c7266494ae2a1369380e01b35 (patch) | |
tree | 746095fa69eccccdc1c2830fdd0c06bac01848f5 /gui | |
parent | e080a26651f90c88176140d63a74c93c2f4041a2 (diff) | |
parent | a4f1482a8b23d5f761f60d6f3821c84190d89e2f (diff) |
Merge pull request #3 from tcsullivan/stm32h7
Stm32h7
Diffstat (limited to 'gui')
-rw-r--r-- | gui/Makefile | 17 | ||||
-rw-r--r-- | gui/stmdsp.cpp | 45 | ||||
-rw-r--r-- | gui/stmdsp.hpp | 16 | ||||
-rw-r--r-- | gui/wxmain.cpp | 624 | ||||
-rw-r--r-- | gui/wxmain.hpp | 21 |
5 files changed, 498 insertions, 225 deletions
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 897e643..2293c71 100644 --- a/gui/stmdsp.cpp +++ b/gui/stmdsp.cpp @@ -31,8 +31,16 @@ namespace stmdsp { if (m_serial.isOpen()) { m_serial.write("i"); - if (m_serial.read(6) != "stmdsp") + if (auto id = m_serial.read(7); id.starts_with("stmdsp")) { + if (id.back() == 'h') + m_platform = platform::H7; + else if (id.back() == 'l') + m_platform = platform::L4; + else + m_serial.close(); + } else { m_serial.close(); + } } } @@ -120,6 +128,33 @@ namespace stmdsp 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() { if (connected()) m_serial.write("S"); @@ -139,13 +174,17 @@ namespace stmdsp } void device::siggen_start() { - if (connected()) + if (connected()) { + m_is_siggening = true; m_serial.write("W"); + } } void device::siggen_stop() { - if (connected()) + if (connected()) { + m_is_siggening = false; m_serial.write("w"); + } } void device::upload_filter(unsigned char *buffer, size_t size) { diff --git a/gui/stmdsp.hpp b/gui/stmdsp.hpp index 4551483..d56a1ab 100644 --- a/gui/stmdsp.hpp +++ b/gui/stmdsp.hpp @@ -19,7 +19,7 @@ namespace stmdsp { - constexpr unsigned int SAMPLES_MAX = 3000; + constexpr unsigned int SAMPLES_MAX = 4096; class scanner { @@ -39,6 +39,13 @@ namespace stmdsp using adcsample_t = uint16_t; using dacsample_t = uint16_t; + enum class platform { + Unknown, + H7, + L4, + G4 + }; + class device { public: @@ -52,8 +59,7 @@ namespace stmdsp return m_serial.isOpen(); } - //std::vector<adcsample_t> sample(unsigned long int count = 1); - + auto get_platform() const { return m_platform; } void continuous_set_buffer_size(unsigned int size); unsigned int get_buffer_size() const { return m_buffer_size; } void set_sample_rate(unsigned int id); @@ -62,11 +68,13 @@ namespace stmdsp void continuous_start_measure(); uint32_t continuous_start_get_measurement(); std::vector<adcsample_t> continuous_read(); + std::vector<adcsample_t> continuous_read_input(); void continuous_stop(); void siggen_upload(dacsample_t *buffer, unsigned int size); void siggen_start(); void siggen_stop(); + bool is_siggening() const { return m_is_siggening; } // buffer is ELF binary void upload_filter(unsigned char *buffer, size_t size); @@ -74,7 +82,9 @@ namespace stmdsp private: serial::Serial m_serial; + platform m_platform = platform::Unknown; unsigned int m_buffer_size = SAMPLES_MAX; + bool m_is_siggening = false; }; } diff --git a/gui/wxmain.cpp b/gui/wxmain.cpp index cbc1f8a..1a10b84 100644 --- a/gui/wxmain.cpp +++ b/gui/wxmain.cpp @@ -12,6 +12,7 @@ #include "wxmain.hpp" #include <wx/combobox.h> +#include <wx/dcclient.h> #include <wx/dir.h> #include <wx/filename.h> #include <wx/filedlg.h> @@ -25,6 +26,7 @@ #include <wx/textdlg.h> #include <array> +#include <sys/mman.h> #include <vector> static const std::array<wxString, 6> srateValues { @@ -44,7 +46,22 @@ static const std::array<unsigned int, 6> srateNums { 96000 }; -static const char *makefile_text = R"make( +static const char *makefile_text_h7 = R"make( +all: + @arm-none-eabi-g++ -x c++ -Os -fno-exceptions -fno-rtti \ + -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-d16 -mtune=cortex-m7 \ + -nostartfiles \ + -Wl,-Ttext-segment=0x00000000 -Wl,-zmax-page-size=512 -Wl,-eprocess_data_entry \ + $0 -o $0.o + @cp $0.o $0.orig.o + @arm-none-eabi-strip -s -S --strip-unneeded $0.o + @arm-none-eabi-objcopy --remove-section .ARM.attributes \ + --remove-section .comment \ + --remove-section .noinit \ + $0.o + arm-none-eabi-size $0.o +)make"; +static const char *makefile_text_l4 = R"make( all: @arm-none-eabi-g++ -x c++ -Os -fno-exceptions -fno-rtti \ -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mtune=cortex-m4 \ @@ -60,23 +77,116 @@ all: arm-none-eabi-size $0.o )make"; -static const char *file_header = R"cpp( +static const char *file_header_h7 = R"cpp( #include <cstdint> using adcsample_t = uint16_t; constexpr unsigned int SIZE = $0; - adcsample_t *process_data(adcsample_t *samples, unsigned int size); +extern "C" void process_data_entry() +{ + ((void (*)())process_data)(); +} + +constexpr double PI = 3.14159265358979323846L; +__attribute__((naked)) +auto sin(double x) { +asm("vmov.f64 r1, r2, d0;" + "eor r0, r0;" + "svc 1;" + "vmov.f64 d0, r1, r2;" + "bx lr"); +return 0; +} +__attribute__((naked)) +auto cos(double x) { +asm("vmov.f64 r1, r2, d0;" + "mov r0, #1;" + "svc 1;" + "vmov.f64 d0, r1, r2;" + "bx lr"); +return 0; +} +__attribute__((naked)) +auto tan(double x) { +asm("vmov.f64 r1, r2, d0;" + "mov r0, #2;" + "svc 1;" + "vmov.f64 d0, r1, r2;" + "bx lr"); +return 0; +} +__attribute__((naked)) +auto sqrt(double x) { +asm("vsqrt.f64 d0, d0; bx lr"); +return 0; +} +auto readalt() { +adcsample_t s; +asm("svc 3; mov %0, r0" : "=&r"(s)); +return s; +} + +// End stmdspgui header code + +)cpp"; +static const char *file_header_l4 = R"cpp( +#include <cstdint> + +using adcsample_t = uint16_t; +constexpr unsigned int SIZE = $0; +adcsample_t *process_data(adcsample_t *samples, unsigned int size); extern "C" void process_data_entry() { ((void (*)())process_data)(); } +constexpr float PI = 3.14159265358979L; +__attribute__((naked)) +auto sin(float x) { +asm("vmov.f32 r1, s0;" + "eor r0, r0;" + "svc 1;" + "vmov.f32 s0, r1;" + "bx lr"); +return 0; +} +__attribute__((naked)) +auto cos(float x) { +asm("vmov.f32 r1, s0;" + "mov r0, #1;" + "svc 1;" + "vmov.f32 s0, r1;" + "bx lr"); +return 0; +} +__attribute__((naked)) +auto tan(float x) { +asm("vmov.f32 r1, s0;" + "mov r0, #2;" + "svc 1;" + "vmov.f32 s0, r1;" + "bx lr"); +return 0; +} +__attribute__((naked)) +auto sqrt(float) { +asm("vsqrt.f32 s0, s0; bx lr"); +return 0; +} + +auto readalt() { +adcsample_t s; +asm("push {r4-r6}; svc 3; mov %0, r0; pop {r4-r6}" : "=&r"(s)); +return s; +} + // End stmdspgui header code )cpp"; + static const char *file_content = R"cpp(adcsample_t *process_data(adcsample_t *samples, unsigned int size) { @@ -97,11 +207,11 @@ enum Id { MRunConnect, MRunStart, MRunMeasure, + MRunDrawSamples, MRunLogResults, MRunUpload, MRunUnload, MRunEditBSize, - MRunEditSRate, MRunGenUpload, MRunGenStart, MCodeCompile, @@ -110,136 +220,166 @@ enum Id { MainFrame::MainFrame() : wxFrame(nullptr, wxID_ANY, "stmdspgui", wxPoint(50, 50), wxSize(640, 800)) { - auto splitter = new wxSplitterWindow(this, wxID_ANY); + // Main frame structure: + // Begin with a main splitter for the code and terminal panes + auto mainSplitter = new wxSplitterWindow(this, wxID_ANY); + auto panelCode = new wxPanel(mainSplitter, wxID_ANY); + auto panelOutput = new wxPanel(mainSplitter, wxID_ANY); + // Additional panel for the toolbar auto panelToolbar = new wxPanel(this, wxID_ANY); - auto panelCode = new wxPanel(splitter, wxID_ANY); - auto panelOutput = new wxPanel(splitter, wxID_ANY); + // Sizers for the controls auto sizerToolbar = new wxBoxSizer(wxHORIZONTAL); - auto sizerCode = new wxBoxSizer(wxVERTICAL); - auto sizerOutput = new wxBoxSizer(wxVERTICAL); - auto sizerSplitter = new wxBoxSizer(wxVERTICAL); + auto sizerCode = new wxBoxSizer(wxVERTICAL); + auto sizerOutput = new wxBoxSizer(wxVERTICAL); + auto sizerMain = new wxBoxSizer(wxVERTICAL); + // Menu objects auto menuFile = new wxMenu; - auto menuRun = new wxMenu; + auto menuRun = new wxMenu; auto menuCode = new wxMenu; // Member initialization - m_status_bar = new wxStatusBar(this); - m_text_editor = new wxStyledTextCtrl(panelCode, wxID_ANY, wxDefaultPosition, wxSize(620, 440)); - m_compile_output = new wxTextCtrl(panelOutput, wxID_ANY, wxEmptyString, wxDefaultPosition, - wxSize(620, 250), wxTE_READONLY | wxTE_MULTILINE | wxHSCROLL); - m_measure_timer = new wxTimer(this, Id::MeasureTimer); - m_menu_bar = new wxMenuBar; + m_status_bar = new wxStatusBar(this); + m_text_editor = new wxStyledTextCtrl(panelCode, wxID_ANY, + wxDefaultPosition, wxSize(620, 440)); + m_compile_output = new wxTextCtrl(panelOutput, wxID_ANY, + wxEmptyString, + wxDefaultPosition, wxSize(620, 250), + wxTE_READONLY | wxTE_MULTILINE | wxHSCROLL); + m_measure_timer = new wxTimer(this, Id::MeasureTimer); + m_menu_bar = new wxMenuBar; + m_rate_select = new wxComboBox(panelToolbar, wxID_ANY, + wxEmptyString, + wxDefaultPosition, wxDefaultSize, + srateValues.size(), srateValues.data(), + 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_status_bar->SetStatusText("Ready."); - SetStatusBar(m_status_bar); - - splitter->SetSashGravity(0.5); - splitter->SetMinimumPaneSize(20); + m_menu_bar->Append(menuFile, "&File"); + m_menu_bar->Append(menuRun, "&Run"); + m_menu_bar->Append(menuCode, "&Code"); + SetMenuBar(m_menu_bar); + // Toolbar initialization auto comp = new wxButton(panelToolbar, Id::MCodeCompile, "Compile"); - m_rate_select = new wxComboBox(panelToolbar, wxID_ANY, - wxEmptyString, wxDefaultPosition, wxDefaultSize, - srateValues.size(), srateValues.data(), wxCB_READONLY); - m_rate_select->Disable(); - sizerToolbar->Add(comp, 0, wxLEFT, 4); sizerToolbar->Add(m_rate_select, 0, wxLEFT, 12); panelToolbar->SetSizer(sizerToolbar); - Bind(wxEVT_BUTTON, &MainFrame::onRunCompile, this, Id::MCodeCompile, wxID_ANY, comp); - Bind(wxEVT_COMBOBOX, &MainFrame::onToolbarSampleRate, this, wxID_ANY, wxID_ANY, m_rate_select); + // Code panel init. prepareEditor(); sizerCode->Add(panelToolbar, 0, wxTOP | wxBOTTOM, 4); sizerCode->Add(m_text_editor, 1, wxEXPAND, 0); panelCode->SetSizer(sizerCode); + // Output panel init. m_compile_output->SetBackgroundColour(wxColour(0, 0, 0)); m_compile_output->SetDefaultStyle(wxTextAttr(*wxWHITE, *wxBLACK, wxFont("Hack"))); sizerOutput->Add(m_compile_output, 1, wxEXPAND | wxALL, 0); panelOutput->SetSizer(sizerOutput); - splitter->SplitHorizontally(panelCode, panelOutput, 440); + // Main splitter init. + mainSplitter->SetSashGravity(0.5); + mainSplitter->SetMinimumPaneSize(20); + mainSplitter->SplitHorizontally(panelCode, panelOutput, 440); + sizerMain->Add(mainSplitter, 1, wxEXPAND, 5); + sizerMain->SetSizeHints(this); + SetSizer(sizerMain); + + m_status_bar->SetStatusText("Ready."); + SetStatusBar(m_status_bar); - sizerSplitter->Add(splitter, 1, wxEXPAND, 5); - SetSizer(sizerSplitter); - sizerSplitter->SetSizeHints(this); + // Binds: - Bind(wxEVT_TIMER, &MainFrame::onMeasureTimer, this, Id::MeasureTimer); + // 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); - Bind(wxEVT_MENU, &MainFrame::onFileNew, this, Id::MFileNew, wxID_ANY, - menuFile->Append(MFileNew, "&New")); - Bind(wxEVT_MENU, &MainFrame::onFileOpen, this, Id::MFileOpen, wxID_ANY, - menuFile->Append(MFileOpen, "&Open")); + // Toolbar actions + Bind(wxEVT_BUTTON, &MainFrame::onRunCompile, this, Id::MCodeCompile, wxID_ANY, comp); + Bind(wxEVT_COMBOBOX, &MainFrame::onToolbarSampleRate, this, wxID_ANY, wxID_ANY, m_rate_select); - menuFile->Append(MFileOpenTemplate, "Open &Template", loadTemplates()); - Bind(wxEVT_MENU, &MainFrame::onFileSave, this, Id::MFileSave, wxID_ANY, - menuFile->Append(MFileSave, "&Save")); - Bind(wxEVT_MENU, &MainFrame::onFileSaveAs, this, Id::MFileSaveAs, wxID_ANY, - menuFile->Append(MFileSaveAs, "Save &As")); + // File menu actions + Bind(wxEVT_MENU, &MainFrame::onFileNew, this, Id::MFileNew, wxID_ANY, menuFile->Append(MFileNew, "&New")); + Bind(wxEVT_MENU, &MainFrame::onFileOpen, this, Id::MFileOpen, wxID_ANY, menuFile->Append(MFileOpen, "&Open")); + menuFile->Append(MFileOpenTemplate, "Open &Template", loadTemplates()); + Bind(wxEVT_MENU, &MainFrame::onFileSave, this, Id::MFileSave, wxID_ANY, menuFile->Append(MFileSave, "&Save")); + Bind(wxEVT_MENU, &MainFrame::onFileSaveAs, this, Id::MFileSaveAs, wxID_ANY, menuFile->Append(MFileSaveAs, "Save &As")); menuFile->AppendSeparator(); - Bind(wxEVT_MENU, &MainFrame::onFileQuit, this, Id::MFileQuit, wxID_ANY, - menuFile->Append(MFileQuit, "&Quit")); + Bind(wxEVT_MENU, &MainFrame::onFileQuit, this, Id::MFileQuit, wxID_ANY, menuFile->Append(MFileQuit, "&Quit")); - Bind(wxEVT_MENU, &MainFrame::onRunConnect, this, Id::MRunConnect, wxID_ANY, - menuRun->Append(MRunConnect, "&Connect")); + // Run menu actions + Bind(wxEVT_MENU, &MainFrame::onRunConnect, this, Id::MRunConnect, wxID_ANY, menuRun->Append(MRunConnect, "&Connect")); 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"); - Bind(wxEVT_MENU, &MainFrame::onRunLogResults, this, Id::MRunLogResults, wxID_ANY, - menuRun->AppendCheckItem(MRunLogResults, "&Log results...")); + 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")); - Bind(wxEVT_MENU, &MainFrame::onRunUnload, this, Id::MRunUnload, wxID_ANY, - menuRun->Append(MRunUnload, "U&nload code")); - Bind(wxEVT_MENU, &MainFrame::onRunEditBSize, this, Id::MRunEditBSize, wxID_ANY, - menuRun->Append(MRunEditBSize, "Set &buffer size...")); - + Bind(wxEVT_MENU, &MainFrame::onRunUpload, this, Id::MRunUpload, wxID_ANY, menuRun->Append(MRunUpload, "&Upload code")); + Bind(wxEVT_MENU, &MainFrame::onRunUnload, this, Id::MRunUnload, wxID_ANY, menuRun->Append(MRunUnload, "U&nload code")); + Bind(wxEVT_MENU, &MainFrame::onRunEditBSize, this, Id::MRunEditBSize, wxID_ANY, menuRun->Append(MRunEditBSize, "Set &buffer size...")); menuRun->AppendSeparator(); - Bind(wxEVT_MENU, &MainFrame::onRunGenUpload, this, Id::MRunGenUpload, wxID_ANY, - menuRun->Append(MRunGenUpload, "&Load signal generator...")); - Bind(wxEVT_MENU, &MainFrame::onRunGenStart, this, Id::MRunGenStart, wxID_ANY, - menuRun->AppendCheckItem(MRunGenStart, "Start &generator")); - - Bind(wxEVT_MENU, &MainFrame::onRunCompile, this, Id::MCodeCompile, wxID_ANY, - menuCode->Append(MCodeCompile, "&Compile code")); - Bind(wxEVT_MENU, &MainFrame::onCodeDisassemble, this, Id::MCodeDisassemble, wxID_ANY, - menuCode->Append(MCodeDisassemble, "Show &Disassembly")); + Bind(wxEVT_MENU, &MainFrame::onRunGenUpload, this, Id::MRunGenUpload, wxID_ANY, menuRun->Append(MRunGenUpload, "&Load signal generator...")); + Bind(wxEVT_MENU, &MainFrame::onRunGenStart, this, Id::MRunGenStart, wxID_ANY, menuRun->AppendCheckItem(MRunGenStart, "Start &generator")); - Bind(wxEVT_CLOSE_WINDOW, &MainFrame::onCloseEvent, this, wxID_ANY); + // Code menu actions + Bind(wxEVT_MENU, &MainFrame::onRunCompile, this, Id::MCodeCompile, wxID_ANY, menuCode->Append(MCodeCompile, "&Compile code")); + Bind(wxEVT_MENU, &MainFrame::onCodeDisassemble, this, Id::MCodeDisassemble, wxID_ANY, menuCode->Append(MCodeDisassemble, "Show &Disassembly")); + menuCode->AppendSeparator(); - m_menu_bar->Append(menuFile, "&File"); - m_menu_bar->Append(menuRun, "&Run"); - m_menu_bar->Append(menuCode, "&Code"); - SetMenuBar(m_menu_bar); + updateMenuOptions(); } +// Closes the window +// Needs to clean things up void MainFrame::onCloseEvent(wxCloseEvent& event) { SetMenuBar(nullptr); - m_menu_bar->Remove(2); - m_menu_bar->Remove(1); - m_menu_bar->Remove(0); - delete m_menu_bar; + //delete m_menu_bar->Remove(2); + //delete m_menu_bar->Remove(1); + //delete m_menu_bar->Remove(0); + //delete m_menu_bar; delete m_measure_timer; + delete m_device; + + Unbind(wxEVT_COMBOBOX, &MainFrame::onToolbarSampleRate, this, wxID_ANY, wxID_ANY); + Unbind(wxEVT_BUTTON, &MainFrame::onRunCompile, this, Id::MCodeCompile, wxID_ANY); event.Skip(); } -void MainFrame::onMeasureTimer([[maybe_unused]] wxTimerEvent&) +// Measure timer tick handler +// Only called while connected and running. +void MainFrame::onMeasureTimer(wxTimerEvent&) { - if (m_conv_result_log != nullptr) { - 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(); } } } - if (m_wav_clip != nullptr) { + if (m_wav_clip) { + // Stream out next WAV chunk auto size = m_device->get_buffer_size(); auto chunk = new stmdsp::adcsample_t[size]; auto src = reinterpret_cast<uint16_t *>(m_wav_clip->next(size)); @@ -249,12 +389,61 @@ void MainFrame::onMeasureTimer([[maybe_unused]] wxTimerEvent&) delete[] chunk; } - if (m_status_bar && m_run_measure && m_run_measure->IsChecked()) { + if (m_run_measure->IsChecked()) { + // Show execution time m_status_bar->SetStatusText(wxString::Format(wxT("Execution time: %u cycles"), m_device->continuous_start_get_measurement())); } } +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() { m_text_editor->SetLexer(wxSTC_LEX_CPP); @@ -295,18 +484,25 @@ void MainFrame::prepareEditor() wxString MainFrame::compileEditorCode() { + if (m_device == nullptr) { + m_status_bar->SetStatusText("Need device connected to compile."); + return ""; + } + if (m_temp_file_name.IsEmpty()) m_temp_file_name = wxFileName::CreateTempFileName("stmdspgui"); wxFile file (m_temp_file_name, wxFile::write); - wxString file_text (file_header); - file_text.Replace("$0", std::to_string(m_device != nullptr ? m_device->get_buffer_size() - : stmdsp::SAMPLES_MAX)); + wxString file_text (m_device->get_platform() == stmdsp::platform::L4 ? file_header_l4 + : file_header_h7); + file_text.Replace("$0", std::to_string(m_device ? m_device->get_buffer_size() + : stmdsp::SAMPLES_MAX)); file.Write(wxString(file_text) + m_text_editor->GetText()); file.Close(); wxFile makefile (m_temp_file_name + "make", wxFile::write); - wxString make_text (makefile_text); + wxString make_text (m_device->get_platform() == stmdsp::platform::L4 ? makefile_text_l4 + : makefile_text_h7); make_text.Replace("$0", m_temp_file_name); makefile.Write(make_text); makefile.Close(); @@ -331,7 +527,7 @@ wxString MainFrame::compileEditorCode() } } -void MainFrame::onFileNew([[maybe_unused]] wxCommandEvent&) +void MainFrame::onFileNew(wxCommandEvent&) { m_open_file_path = ""; m_text_editor->SetText(file_content); @@ -339,7 +535,7 @@ void MainFrame::onFileNew([[maybe_unused]] wxCommandEvent&) m_status_bar->SetStatusText("Ready."); } -void MainFrame::onFileOpen([[maybe_unused]] wxCommandEvent&) +void MainFrame::onFileOpen(wxCommandEvent&) { wxFileDialog openDialog(this, "Open filter file", "", "", "C++ source file (*.cpp)|*.cpp", @@ -356,9 +552,15 @@ void MainFrame::onFileOpen([[maybe_unused]] wxCommandEvent&) m_text_editor->DiscardEdits(); m_compile_output->ChangeValue(""); m_status_bar->SetStatusText("Ready."); + } else { + m_status_bar->SetStatusText("Failed to read file contents."); } delete[] buffer; + } else { + m_status_bar->SetStatusText("Failed to open file."); } + } else { + m_status_bar->SetStatusText("Ready."); } } @@ -375,8 +577,12 @@ void MainFrame::onFileOpenTemplate(wxCommandEvent& event) m_text_editor->SetText(buffer); //m_text_editor->DiscardEdits(); m_status_bar->SetStatusText("Ready."); + } else { + m_status_bar->SetStatusText("Failed to read file contents."); } delete[] buffer; + } else { + m_status_bar->SetStatusText("Ready."); } } @@ -401,7 +607,7 @@ void MainFrame::onFileSave(wxCommandEvent& ce) } } -void MainFrame::onFileSaveAs([[maybe_unused]] wxCommandEvent& ce) +void MainFrame::onFileSaveAs(wxCommandEvent&) { if (m_text_editor->IsModified()) { wxFileDialog saveDialog(this, "Save filter file", "", "", @@ -424,7 +630,7 @@ void MainFrame::onFileSaveAs([[maybe_unused]] wxCommandEvent& ce) } } -void MainFrame::onFileQuit([[maybe_unused]] wxCommandEvent&) +void MainFrame::onFileQuit(wxCommandEvent&) { Close(true); } @@ -433,15 +639,15 @@ void MainFrame::onRunConnect(wxCommandEvent& ce) { auto menuItem = dynamic_cast<wxMenuItem *>(ce.GetEventUserData()); - if (m_device == nullptr) { + if (!m_device) { stmdsp::scanner scanner; if (auto devices = scanner.scan(); devices.size() > 0) { m_device = new stmdsp::device(devices.front()); if (m_device->connected()) { auto rate = m_device->get_sample_rate(); m_rate_select->SetSelection(rate); - m_rate_select->Enable(); + updateMenuOptions(); menuItem->SetItemLabel("&Disconnect"); m_status_bar->SetStatusText("Connected."); } else { @@ -457,7 +663,7 @@ void MainFrame::onRunConnect(wxCommandEvent& ce) } else { delete m_device; m_device = nullptr; - m_rate_select->Disable(); + updateMenuOptions(); menuItem->SetItemLabel("&Connect"); m_status_bar->SetStatusText("Disconnected."); } @@ -468,33 +674,37 @@ void MainFrame::onRunStart(wxCommandEvent& ce) auto menuItem = dynamic_cast<wxMenuItem *>(ce.GetEventUserData()); if (!m_is_running) { - if (m_device != nullptr && m_device->connected()) { - if (m_run_measure && m_run_measure->IsChecked()) { - m_device->continuous_start_measure(); - m_measure_timer->StartOnce(1000); - } else if (m_wav_clip != nullptr) { - m_device->continuous_start(); + if (m_run_measure->IsChecked()) { + m_device->continuous_start_measure(); + m_measure_timer->StartOnce(1000); + } else { + if (m_device->is_siggening() && m_wav_clip) { m_measure_timer->Start(m_device->get_buffer_size() * 500 / srateNums[m_rate_select->GetSelection()]); - } else { - m_device->continuous_start(); + } else if (m_conv_result_log) { m_measure_timer->Start(15); + } else if (m_run_draw_samples->IsChecked()) { + m_measure_timer->Start(300); } - menuItem->SetItemLabel("&Stop"); - m_status_bar->SetStatusText("Running."); - m_is_running = true; - } else { - wxMessageBox("No device connected!", "Run", wxICON_WARNING); - m_status_bar->SetStatusText("Please connect."); + m_device->continuous_start(); } + + m_rate_select->Enable(false); + menuItem->SetItemLabel("&Stop"); + m_status_bar->SetStatusText("Running."); + m_is_running = true; } else { 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(); } } @@ -506,7 +716,7 @@ void MainFrame::onRunLogResults(wxCommandEvent& ce) wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (dialog.ShowModal() != wxID_CANCEL) { - if (m_conv_result_log != nullptr) { + if (m_conv_result_log) { m_conv_result_log->Close(); delete m_conv_result_log; m_conv_result_log = nullptr; @@ -516,174 +726,154 @@ void MainFrame::onRunLogResults(wxCommandEvent& ce) } m_status_bar->SetStatusText("Ready."); - } else if (m_conv_result_log != nullptr) { + } else if (m_conv_result_log) { m_conv_result_log->Close(); delete m_conv_result_log; m_conv_result_log = nullptr; } } -void MainFrame::onRunEditBSize([[maybe_unused]] wxCommandEvent&) +void MainFrame::onRunEditBSize(wxCommandEvent&) { - if (m_device != nullptr && m_device->connected()) { - wxTextEntryDialog dialog (this, "Enter new buffer size (100-3000)", "Set Buffer Size"); - if (dialog.ShowModal() == wxID_OK) { - if (wxString value = dialog.GetValue(); !value.IsEmpty()) { - if (unsigned long n; value.ToULong(&n)) { - if (n >= 100 && n <= stmdsp::SAMPLES_MAX) { - m_device->continuous_set_buffer_size(n); - } else { - m_status_bar->SetStatusText("Error: Invalid 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)) { + if (n >= 100 && n <= stmdsp::SAMPLES_MAX) { + m_device->continuous_set_buffer_size(n); } else { m_status_bar->SetStatusText("Error: Invalid buffer size."); } } else { - m_status_bar->SetStatusText("Ready."); + m_status_bar->SetStatusText("Error: Invalid buffer size."); } } else { m_status_bar->SetStatusText("Ready."); } } else { - wxMessageBox("No device connected!", "Run", wxICON_WARNING); - m_status_bar->SetStatusText("Please connect."); + m_status_bar->SetStatusText("Ready."); } } void MainFrame::onToolbarSampleRate(wxCommandEvent& ce) { - if (m_device != nullptr && m_device->connected()) { - auto combo = dynamic_cast<wxComboBox *>(ce.GetEventUserData()); - m_device->set_sample_rate(combo->GetCurrentSelection()); - m_status_bar->SetStatusText("Ready."); - } else { - wxMessageBox("No device connected!", "Run", wxICON_WARNING); - m_status_bar->SetStatusText("Please connect."); - } + auto combo = dynamic_cast<wxComboBox *>(ce.GetEventUserData()); + m_device->set_sample_rate(combo->GetCurrentSelection()); + m_status_bar->SetStatusText("Ready."); } -void MainFrame::onRunGenUpload([[maybe_unused]] wxCommandEvent&) +void MainFrame::onRunGenUpload(wxCommandEvent&) { - if (m_device != nullptr && m_device->connected()) { - wxTextEntryDialog dialog (this, "Enter generator values below. Values must be whole numbers " - "between zero and 4095.", "Enter Generator Values"); - if (dialog.ShowModal() == wxID_OK) { - if (wxString values = dialog.GetValue(); !values.IsEmpty()) { - if (values[0] == '/') { - m_wav_clip = new wav::clip(values.Mid(1)); - if (m_wav_clip->valid()) { - m_status_bar->SetStatusText("Generator ready."); - } else { - delete m_wav_clip; - m_wav_clip = nullptr; - m_status_bar->SetStatusText("Error: Bad WAV file."); - } + wxTextEntryDialog dialog (this, "Enter up to 8000 generator values below. " + "Values must be whole numbers between zero and 4095.", + "Enter Generator Values"); + if (dialog.ShowModal() == wxID_OK) { + if (wxString values = dialog.GetValue(); !values.IsEmpty()) { + if (values[0] == '/') { + m_wav_clip = new wav::clip(values.Mid(1)); + if (m_wav_clip->valid()) { + m_status_bar->SetStatusText("Generator ready."); } else { - std::vector<stmdsp::dacsample_t> samples; - while (!values.IsEmpty()) { - if (auto number_end = values.find_first_not_of("0123456789"); - number_end != wxString::npos && number_end > 0) + delete m_wav_clip; + m_wav_clip = nullptr; + m_status_bar->SetStatusText("Error: Bad WAV file."); + } + } else { + std::vector<stmdsp::dacsample_t> samples; + while (!values.IsEmpty() && samples.size() <= stmdsp::SAMPLES_MAX * 2) { + if (auto number_end = values.find_first_not_of("0123456789"); + number_end != wxString::npos && number_end > 0) + { + auto number = values.Left(number_end); + if (unsigned long n; number.ToULong(&n)) + samples.push_back(n & 4095); + + if (auto next = values.find_first_of("0123456789", number_end + 1); + next != wxString::npos) { - auto number = values.Left(number_end); - if (unsigned long n; number.ToULong(&n)) - samples.push_back(n & 4095); - - if (auto next = values.find_first_of("0123456789", number_end + 1); - next != wxString::npos) - { - values = values.Mid(next); - } else { - break; - } + values = values.Mid(next); } else { break; } - } - - if (samples.size() <= stmdsp::SAMPLES_MAX) { - m_device->siggen_upload(&samples[0], samples.size()); - m_status_bar->SetStatusText("Generator ready."); } else { - m_status_bar->SetStatusText("Error: Too many samples."); + break; } } - } else { - m_status_bar->SetStatusText("Error: No samples given."); + + if (samples.size() <= stmdsp::SAMPLES_MAX) { + m_device->siggen_upload(&samples[0], samples.size()); + m_status_bar->SetStatusText("Generator ready."); + } else { + m_status_bar->SetStatusText("Error: Too many samples (max is 8000)."); + } } } else { - m_status_bar->SetStatusText("Ready."); + m_status_bar->SetStatusText("Error: No samples given."); } } else { - wxMessageBox("No device connected!", "Run", wxICON_WARNING); - m_status_bar->SetStatusText("Please connect."); + m_status_bar->SetStatusText("Ready."); } } void MainFrame::onRunGenStart(wxCommandEvent& ce) { auto menuItem = dynamic_cast<wxMenuItem *>(ce.GetEventUserData()); - if (m_device != nullptr && m_device->connected()) { - if (menuItem->IsChecked()) { - m_device->siggen_start(); - menuItem->SetItemLabel("Stop &generator"); - } else { - m_device->siggen_stop(); - menuItem->SetItemLabel("Start &generator"); - } + if (menuItem->IsChecked()) { + m_device->siggen_start(); + menuItem->SetItemLabel("Stop &generator"); + m_status_bar->SetStatusText("Generator running."); } else { - wxMessageBox("No device connected!", "Run", wxICON_WARNING); - m_status_bar->SetStatusText("Please connect."); + m_device->siggen_stop(); + menuItem->SetItemLabel("Start &generator"); + m_status_bar->SetStatusText("Ready."); } } -void MainFrame::onRunUpload([[maybe_unused]] wxCommandEvent&) +void MainFrame::onRunUpload(wxCommandEvent&) { if (auto file = compileEditorCode(); !file.IsEmpty()) { if (wxFileInputStream file_stream (file); file_stream.IsOk()) { auto size = file_stream.GetSize(); auto buffer = new unsigned char[size]; - if (m_device != nullptr && m_device->connected()) { - file_stream.ReadAll(buffer, size); - m_device->upload_filter(buffer, size); - m_status_bar->SetStatusText("Code uploaded."); - } else { - wxMessageBox("No device connected!", "Run", wxICON_WARNING); - m_status_bar->SetStatusText("Please connect."); - } + file_stream.ReadAll(buffer, size); + m_device->upload_filter(buffer, size); + m_status_bar->SetStatusText("Code uploaded."); } else { m_status_bar->SetStatusText("Couldn't load compiled code."); } } } -void MainFrame::onRunUnload([[maybe_unused]] wxCommandEvent&) +void MainFrame::onRunUnload(wxCommandEvent&) { - if (m_device != nullptr && m_device->connected()) { - m_device->unload_filter(); - m_status_bar->SetStatusText("Unloaded code."); - } else { - m_status_bar->SetStatusText("No device connected."); - } + m_device->unload_filter(); + m_status_bar->SetStatusText("Unloaded code."); } -void MainFrame::onRunCompile([[maybe_unused]] wxCommandEvent&) +void MainFrame::onRunCompile(wxCommandEvent&) { compileEditorCode(); } -void MainFrame::onCodeDisassemble([[maybe_unused]] wxCommandEvent&) +void MainFrame::onCodeDisassemble(wxCommandEvent&) { - auto output = m_temp_file_name + ".asm.log"; - wxString command = wxString("arm-none-eabi-objdump -d --no-show-raw-insn ") + m_temp_file_name + ".orig.o" - " > " + output + " 2>&1"; - - if (system(command.ToAscii()) == 0) { - m_compile_output->LoadFile(output); - m_status_bar->SetStatusText(wxString::Format(wxT("Done. Line count: %u."), - m_compile_output->GetNumberOfLines())); + if (!m_temp_file_name.IsEmpty()) { + auto output = m_temp_file_name + ".asm.log"; + wxString command = wxString("arm-none-eabi-objdump -d --no-show-raw-insn ") + + m_temp_file_name + ".orig.o" // + + " > " + output + " 2>&1"; + + if (system(command.ToAscii()) == 0) { + m_compile_output->LoadFile(output); + m_status_bar->SetStatusText(wxString::Format(wxT("Done. Line count: %u."), + m_compile_output->GetNumberOfLines())); + } else { + m_compile_output->ChangeValue(""); + m_status_bar->SetStatusText("Failed to load disassembly."); + } } else { m_compile_output->ChangeValue(""); - m_status_bar->SetStatusText("Failed to load disassembly (code compiled?)."); + m_status_bar->SetStatusText("Need to compile code before analyzing."); } } @@ -705,3 +895,15 @@ wxMenu *MainFrame::loadTemplates() return menu; } +void MainFrame::updateMenuOptions() +{ + bool connected = m_device != nullptr; + m_menu_bar->Enable(MRunStart, connected); + m_menu_bar->Enable(MRunUpload, connected); + m_menu_bar->Enable(MRunUnload, connected); + m_menu_bar->Enable(MRunEditBSize, connected); + m_menu_bar->Enable(MRunGenUpload, connected); + m_menu_bar->Enable(MRunGenStart, connected); + m_rate_select->Enable(connected); +} + diff --git a/gui/wxmain.hpp b/gui/wxmain.hpp index 8068784..9b3db1e 100644 --- a/gui/wxmain.hpp +++ b/gui/wxmain.hpp @@ -58,30 +58,49 @@ 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 bool m_is_running = false; + wxComboBox *m_device_combo = nullptr; wxStyledTextCtrl *m_text_editor = nullptr; 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; wxComboBox *m_rate_select = nullptr; + + // File handle for logging output samples + // Not null when logging is enabled wxFileOutputStream *m_conv_result_log = nullptr; + // File path of currently opened file + // Empty if new file wxString m_open_file_path; + // File path for temporary files (e.g. compiled ELF) + // Set by compile action wxString m_temp_file_name; + // 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; bool tryDevice(); void prepareEditor(); wxString compileEditorCode(); wxMenu *loadTemplates(); + // Updates control availabilities based on device connection + void updateMenuOptions(); }; #endif // WXMAIN_HPP_ |