aboutsummaryrefslogtreecommitdiffstats
path: root/source/stmdsp
diff options
context:
space:
mode:
authorClyne Sullivan <clyne@clyne-lp.lan>2021-08-08 22:02:52 -0400
committerClyne Sullivan <clyne@clyne-lp.lan>2021-08-08 22:02:52 -0400
commit707b24dd07236243269cf092728f85172e94e8a4 (patch)
treec136716a5fc9ed9cbf570e24f8f6ab715adc73a2 /source/stmdsp
initial commit
Diffstat (limited to 'source/stmdsp')
-rw-r--r--source/stmdsp/stmdsp.cpp214
-rw-r--r--source/stmdsp/stmdsp.hpp99
-rw-r--r--source/stmdsp/stmdsp_code.hpp199
3 files changed, 512 insertions, 0 deletions
diff --git a/source/stmdsp/stmdsp.cpp b/source/stmdsp/stmdsp.cpp
new file mode 100644
index 0000000..b3fc8c3
--- /dev/null
+++ b/source/stmdsp/stmdsp.cpp
@@ -0,0 +1,214 @@
+/**
+ * @file stmdsp.cpp
+ * @brief Interface for communication with stmdsp device over serial.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "stmdsp.hpp"
+
+#include <serial/serial.h>
+
+namespace stmdsp
+{
+ std::list<std::string>& scanner::scan()
+ {
+ auto devices = serial::list_ports();
+ for (auto& device : devices) {
+ if (device.hardware_id.find(STMDSP_USB_ID) != std::string::npos)
+ m_available_devices.emplace_front(device.port);
+ }
+
+ return m_available_devices;
+ }
+
+ device::device(const std::string& file) :
+ m_serial(file, 1000000/*230400*/, serial::Timeout::simpleTimeout(50))
+ {
+ if (m_serial.isOpen()) {
+ m_serial.flush();
+ m_serial.write("i");
+ 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();
+ }
+ }
+ }
+
+ void device::continuous_set_buffer_size(unsigned int size) {
+ if (connected()) {
+ m_buffer_size = size;
+
+ uint8_t request[3] = {
+ 'B',
+ static_cast<uint8_t>(size),
+ static_cast<uint8_t>(size >> 8)
+ };
+ m_serial.write(request, 3);
+ }
+ }
+
+ void device::set_sample_rate(unsigned int id) {
+ if (connected()) {
+ uint8_t request[2] = {
+ 'r',
+ static_cast<uint8_t>(id)
+ };
+ m_serial.write(request, 2);
+ }
+ }
+
+ unsigned int device::get_sample_rate() {
+ unsigned char result = 0xFF;
+
+ if (connected()) {
+ uint8_t request[2] = {
+ 'r', 0xFF
+ };
+ m_serial.write(request, 2);
+ m_serial.read(&result, 1);
+ }
+
+ return result;
+ }
+
+ void device::continuous_start() {
+ if (connected()) {
+ m_serial.write("R");
+ m_is_running = true;
+ }
+ }
+
+ void device::continuous_start_measure() {
+ if (connected()) {
+ m_serial.write("M");
+ m_is_running = true;
+ }
+ }
+
+ uint32_t device::continuous_start_get_measurement() {
+ uint32_t count = 0;
+ if (connected()) {
+ m_serial.write("m");
+ m_serial.read(reinterpret_cast<uint8_t *>(&count), sizeof(uint32_t));
+ }
+
+ return count / 2;
+ }
+
+ std::vector<adcsample_t> device::continuous_read() {
+ if (connected()) {
+ m_serial.write("s");
+ 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 {};
+ }
+
+ 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");
+ m_is_running = false;
+ }
+ }
+
+ void device::siggen_upload(dacsample_t *buffer, unsigned int size) {
+ if (connected()) {
+ uint8_t request[3] = {
+ 'D',
+ static_cast<uint8_t>(size),
+ static_cast<uint8_t>(size >> 8)
+ };
+ m_serial.write(request, 3);
+
+ m_serial.write((uint8_t *)buffer, size * sizeof(dacsample_t));
+ }
+ }
+
+ void device::siggen_start() {
+ if (connected()) {
+ m_is_siggening = true;
+ m_serial.write("W");
+ }
+ }
+
+ void device::siggen_stop() {
+ if (connected()) {
+ m_is_siggening = false;
+ m_serial.write("w");
+ }
+ }
+
+ void device::upload_filter(unsigned char *buffer, size_t size) {
+ if (connected()) {
+ uint8_t request[3] = {
+ 'E',
+ static_cast<uint8_t>(size),
+ static_cast<uint8_t>(size >> 8)
+ };
+ m_serial.write(request, 3);
+
+ m_serial.write(buffer, size);
+ }
+ }
+
+ void device::unload_filter() {
+ if (connected())
+ m_serial.write("e");
+ }
+}
diff --git a/source/stmdsp/stmdsp.hpp b/source/stmdsp/stmdsp.hpp
new file mode 100644
index 0000000..8da98f2
--- /dev/null
+++ b/source/stmdsp/stmdsp.hpp
@@ -0,0 +1,99 @@
+/**
+ * @file stmdsp.hpp
+ * @brief Interface for communication with stmdsp device over serial.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_HPP_
+#define STMDSP_HPP_
+
+#include <cstdint>
+#include <list>
+#include <serial/serial.h>
+#include <string>
+
+namespace stmdsp
+{
+ constexpr unsigned int SAMPLES_MAX = 4096;
+
+ class scanner
+ {
+ private:
+ constexpr static const char *STMDSP_USB_ID =
+#ifndef STMDSP_WIN32
+ "USB VID:PID=0483:5740";
+#else
+ "USB\\VID_0483&PID_5740";
+#endif
+
+ public:
+ std::list<std::string>& scan();
+ auto& devices() {
+ return m_available_devices;
+ }
+
+ private:
+ std::list<std::string> m_available_devices;
+ };
+
+ using adcsample_t = uint16_t;
+ using dacsample_t = uint16_t;
+
+ enum class platform {
+ Unknown,
+ H7,
+ L4,
+ G4
+ };
+
+ class device
+ {
+ public:
+ device(const std::string& file);
+
+ ~device() {
+ m_serial.close();
+ }
+
+ bool connected() {
+ return m_serial.isOpen();
+ }
+
+ 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);
+ unsigned int get_sample_rate();
+ void continuous_start();
+ 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; }
+ bool is_running() const { return m_is_running; }
+
+ // buffer is ELF binary
+ void upload_filter(unsigned char *buffer, size_t size);
+ void unload_filter();
+
+ private:
+ serial::Serial m_serial;
+ platform m_platform = platform::Unknown;
+ unsigned int m_buffer_size = SAMPLES_MAX;
+ bool m_is_siggening = false;
+ bool m_is_running = false;
+ };
+}
+
+#endif // STMDSP_HPP_
+
diff --git a/source/stmdsp/stmdsp_code.hpp b/source/stmdsp/stmdsp_code.hpp
new file mode 100644
index 0000000..6850459
--- /dev/null
+++ b/source/stmdsp/stmdsp_code.hpp
@@ -0,0 +1,199 @@
+/**
+ * @file stmdsp_code.hpp
+ * @brief Source code and build scripts for stmdsp device algorithms.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_CODE_HPP
+#define STMDSP_CODE_HPP
+
+#ifdef STMDSP_WIN32
+#define NEWLINE "\r\n"
+#define COPY "copy"
+#else
+#define NEWLINE "\n"
+#define COPY "cp"
+#endif
+
+namespace stmdsp {
+
+// $0 = temp file name
+// TODO try -ffunction-sections -fdata-sections -Wl,--gc-sections
+static std::string makefile_text_h7 =
+#ifdef STMDSP_WIN32
+ "echo off" NEWLINE
+#endif
+ "arm-none-eabi-g++ -x c++ -Os -std=c++20 -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" NEWLINE
+ COPY " $0.o $0.orig.o" NEWLINE
+ "arm-none-eabi-strip -s -S --strip-unneeded $0.o" NEWLINE
+ "arm-none-eabi-objcopy --remove-section .ARM.attributes "
+ "--remove-section .comment "
+ "--remove-section .noinit "
+ "$0.o" NEWLINE
+ "arm-none-eabi-size $0.o" NEWLINE;
+static std::string makefile_text_l4 =
+#ifdef STMDSP_WIN32
+ "echo off" NEWLINE
+#endif
+ "arm-none-eabi-g++ -x c++ -Os -std=c++20 -fno-exceptions -fno-rtti "
+ "-mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mtune=cortex-m4 "
+ "-nostartfiles -I$1/cmsis "
+ "-Wl,-Ttext-segment=0x10000000 -Wl,-zmax-page-size=512 -Wl,-eprocess_data_entry "
+ "$0 -o $0.o" NEWLINE
+ COPY " $0.o $0.orig.o" NEWLINE
+ "arm-none-eabi-strip -s -S --strip-unneeded $0.o" NEWLINE
+ "arm-none-eabi-objcopy --remove-section .ARM.attributes "
+ "--remove-section .comment "
+ "--remove-section .noinit "
+ "$0.o" NEWLINE
+ "arm-none-eabi-size $0.o" NEWLINE;
+
+// $0 = buffer size
+static std::string file_header_h7 = R"cpp(
+#include <cstdint>
+#include <span>
+
+using Sample = uint16_t;
+using Samples = std::span<Sample, $0>;
+
+Sample *process_data(Samples samples);
+extern "C" void process_data_entry()
+{
+ Sample *samples;
+ asm("mov %0, r0" : "=r" (samples));
+ process_data(Samples(samples, $0));
+}
+
+static 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() {
+Sample s;
+asm("svc 3; mov %0, r0" : "=&r"(s));
+return s;
+}
+
+// End stmdspgui header code
+
+)cpp";
+static std::string file_header_l4 = R"cpp(
+#include <cstdint>
+#include <span>
+
+using Sample = uint16_t;
+using Samples = std::span<Sample, $0>;
+
+Sample *process_data(Samples samples);
+extern "C" void process_data_entry()
+{
+ Sample *samples;
+ asm("mov %0, r0" : "=r" (samples));
+ process_data(Samples(samples, $0));
+}
+
+static 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 readpot1() {
+Sample s;
+asm("push {r4-r11}; eor r0, r0; svc 3; mov %0, r0; pop {r4-r11}" : "=r"(s));
+return s;
+}
+auto readpot2() {
+Sample s;
+asm("push {r4-r11}; mov r0, #1; svc 3; mov %0, r0; pop {r4-r11}" : "=r"(s));
+return s;
+}
+
+//void puts(const char *s) {
+// 's' will already be in r0.
+//asm("push {r4-r6}; svc 4; pop {r4-r6}");
+//}
+
+// End stmdspgui header code
+
+)cpp";
+
+
+static std::string file_content =
+R"cpp(Sample *process_data(Samples samples)
+{
+ return samples.data();
+}
+)cpp";
+
+} // namespace stmdsp
+
+#endif // STMDSP_CODE_HPP
+