include $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/port_v7m.mk
# Auto-build files in ./source recursively.
#include $(CHIBIOS)/tools/mk/autobuild.mk
-ALLCSRC += $(wildcard source/*.c)
-ALLCPPSRC += $(wildcard source/*.cpp)
+ALLCSRC += $(wildcard source/*.c) $(wildcard source/periph/*.c)
+ALLCPPSRC += $(wildcard source/*.cpp) $(wildcard source/periph/*.cpp)
ALLASMSRC += $(wildcard source/*.s)
# Other files (optional).
#include $(CHIBIOS)/test/lib/test.mk
ASMXSRC = $(ALLXASMSRC)
# Inclusion directories.
-INCDIR = $(CONFDIR) $(ALLINC) $(TESTINC)
+INCDIR = $(CONFDIR) $(ALLINC) $(TESTINC) \
+ source source/periph
# Define C warning options here.
CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes -pedantic
source [find interface/stlink.cfg]
-source [find target/stm32h7x.cfg]
+source [find target/stm32l4x.cfg]
+++ /dev/null
-/**
- * @file adc.cpp
- * @brief Manages signal reading through the ADC.
- *
- * 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 "adc.hpp"
-
-#if defined(TARGET_PLATFORM_L4)
-ADCDriver *ADC::m_driver = &ADCD1;
-ADCDriver *ADC::m_driver2 = &ADCD3;
-#else
-ADCDriver *ADC::m_driver = &ADCD3;
-//ADCDriver *ADC::m_driver2 = &ADCD1; // TODO
-#endif
-
-const ADCConfig ADC::m_config = {
- .difsel = 0,
-#if defined(TARGET_PLATFORM_H7)
- .calibration = 0,
-#endif
-};
-
-const ADCConfig ADC::m_config2 = {
- .difsel = 0,
-#if defined(TARGET_PLATFORM_H7)
- .calibration = 0,
-#endif
-};
-
-ADCConversionGroup ADC::m_group_config = {
- .circular = true,
- .num_channels = 1,
- .end_cb = ADC::conversionCallback,
- .error_cb = nullptr,
- .cfgr = ADC_CFGR_EXTEN_RISING | ADC_CFGR_EXTSEL_SRC(13), /* TIM6_TRGO */
- .cfgr2 = ADC_CFGR2_ROVSE | ADC_CFGR2_OVSR_1 | ADC_CFGR2_OVSS_0, // Oversampling 2x
-#if defined(TARGET_PLATFORM_H7)
- .ccr = 0,
- .pcsel = 0,
- .ltr1 = 0, .htr1 = 4095,
- .ltr2 = 0, .htr2 = 4095,
- .ltr3 = 0, .htr3 = 4095,
-#else
- .tr1 = ADC_TR(0, 4095),
- .tr2 = ADC_TR(0, 4095),
- .tr3 = ADC_TR(0, 4095),
- .awd2cr = 0,
- .awd3cr = 0,
-#endif
- .smpr = {
- ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_12P5), 0
- },
- .sqr = {
- ADC_SQR1_SQ1_N(ADC_CHANNEL_IN5),
- 0, 0, 0
- },
-};
-
-static bool readAltDone = false;
-static void readAltCallback(ADCDriver *)
-{
- readAltDone = true;
-}
-ADCConversionGroup ADC::m_group_config2 = {
- .circular = false,
- .num_channels = 2,
- .end_cb = readAltCallback,
- .error_cb = nullptr,
- .cfgr = ADC_CFGR_EXTEN_RISING | ADC_CFGR_EXTSEL_SRC(13), /* TIM6_TRGO */
- .cfgr2 = ADC_CFGR2_ROVSE | ADC_CFGR2_OVSR_1 | ADC_CFGR2_OVSS_0, // Oversampling 2x
-#if defined(TARGET_PLATFORM_H7)
- .ccr = 0,
- .pcsel = 0,
- .ltr1 = 0, .htr1 = 4095,
- .ltr2 = 0, .htr2 = 4095,
- .ltr3 = 0, .htr3 = 4095,
-#else
- .tr1 = ADC_TR(0, 4095),
- .tr2 = ADC_TR(0, 4095),
- .tr3 = ADC_TR(0, 4095),
- .awd2cr = 0,
- .awd3cr = 0,
-#endif
- .smpr = {
- ADC_SMPR1_SMP_AN1(ADC_SMPR_SMP_12P5) | ADC_SMPR1_SMP_AN2(ADC_SMPR_SMP_12P5), 0
- },
- .sqr = {
- ADC_SQR1_SQ1_N(ADC_CHANNEL_IN1) | ADC_SQR1_SQ2_N(ADC_CHANNEL_IN2),
- 0, 0, 0
- },
-};
-
-adcsample_t *ADC::m_current_buffer = nullptr;
-size_t ADC::m_current_buffer_size = 0;
-ADC::Operation ADC::m_operation = nullptr;
-
-void ADC::begin()
-{
-#if defined(TARGET_PLATFORM_H7)
- palSetPadMode(GPIOF, 3, PAL_MODE_INPUT_ANALOG);
-#else
- palSetPadMode(GPIOA, 0, PAL_MODE_INPUT_ANALOG); // Algorithm in
- palSetPadMode(GPIOC, 0, PAL_MODE_INPUT_ANALOG); // Potentiometer 1
- palSetPadMode(GPIOC, 1, PAL_MODE_INPUT_ANALOG); // Potentiometer 2
-#endif
-
- adcStart(m_driver, &m_config);
- adcStart(m_driver2, &m_config2);
-}
-
-void ADC::start(adcsample_t *buffer, size_t count, Operation operation)
-{
- m_current_buffer = buffer;
- m_current_buffer_size = count;
- m_operation = operation;
-
- adcStartConversion(m_driver, &m_group_config, buffer, count);
- SClock::start();
-}
-
-void ADC::stop()
-{
- SClock::stop();
- adcStopConversion(m_driver);
-
- m_current_buffer = nullptr;
- m_current_buffer_size = 0;
- m_operation = nullptr;
-}
-
-adcsample_t ADC::readAlt(unsigned int id)
-{
- if (id > 1)
- return 0;
- static adcsample_t result[16] = {};
- readAltDone = false;
- adcStartConversion(m_driver2, &m_group_config2, result, 8);
- while (!readAltDone)
- __WFI();
- adcStopConversion(m_driver2);
- return result[id];
-}
-
-void ADC::setRate(SClock::Rate rate)
-{
-#if defined(TARGET_PLATFORM_H7)
- std::array<std::array<uint32_t, 2>, 6> m_rate_presets = {{
- // Rate PLL N PLL P
- {/* 8k */ 80, 20},
- {/* 16k */ 80, 10},
- {/* 20k */ 80, 8},
- {/* 32k */ 80, 5},
- {/* 48k */ 96, 4},
- {/* 96k */ 288, 10}
- }};
-
- auto& preset = m_rate_presets[static_cast<unsigned int>(rate)];
- auto pllbits = (preset[0] << RCC_PLL2DIVR_N2_Pos) |
- (preset[1] << RCC_PLL2DIVR_P2_Pos);
-
- adcStop(m_driver);
-
- // Adjust PLL2
- RCC->CR &= ~(RCC_CR_PLL2ON);
- while ((RCC->CR & RCC_CR_PLL2RDY) == RCC_CR_PLL2RDY);
- auto pll2divr = RCC->PLL2DIVR &
- ~(RCC_PLL2DIVR_N2_Msk | RCC_PLL2DIVR_P2_Msk);
- pll2divr |= pllbits;
- RCC->PLL2DIVR = pll2divr;
- RCC->CR |= RCC_CR_PLL2ON;
- while ((RCC->CR & RCC_CR_PLL2RDY) != RCC_CR_PLL2RDY);
-
- m_group_config.smpr[0] = rate != SClock::Rate::R96K ? ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_12P5)
- : ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_2P5);
-
- adcStart(m_driver, &m_config);
-#elif defined(TARGET_PLATFORM_L4)
- std::array<std::array<uint32_t, 3>, 6> m_rate_presets = {{
- // Rate PLLSAI2N R SMPR
- {/* 8k */ 8, 1, ADC_SMPR_SMP_12P5},
- {/* 16k */ 16, 1, ADC_SMPR_SMP_12P5},
- {/* 20k */ 20, 1, ADC_SMPR_SMP_12P5},
- {/* 32k */ 32, 1, ADC_SMPR_SMP_12P5},
- {/* 48k */ 24, 0, ADC_SMPR_SMP_12P5},
- {/* 96k */ 73, 1, ADC_SMPR_SMP_6P5} // Technically 96.05263kS/s
- }};
-
- auto& preset = m_rate_presets[static_cast<int>(rate)];
- auto pllnr = (preset[0] << RCC_PLLSAI2CFGR_PLLSAI2N_Pos) |
- (preset[1] << RCC_PLLSAI2CFGR_PLLSAI2R_Pos);
- auto smpr = preset[2];
-
- // Adjust PLLSAI2
- RCC->CR &= ~(RCC_CR_PLLSAI2ON);
- while ((RCC->CR & RCC_CR_PLLSAI2RDY) == RCC_CR_PLLSAI2RDY);
- RCC->PLLSAI2CFGR = (RCC->PLLSAI2CFGR & ~(RCC_PLLSAI2CFGR_PLLSAI2N_Msk | RCC_PLLSAI2CFGR_PLLSAI2R_Msk)) | pllnr;
- RCC->CR |= RCC_CR_PLLSAI2ON;
- while ((RCC->CR & RCC_CR_PLLSAI2RDY) != RCC_CR_PLLSAI2RDY);
-
- m_group_config.smpr[0] = ADC_SMPR1_SMP_AN5(smpr);
-
- // Set 2x oversampling
- m_group_config.cfgr2 = ADC_CFGR2_ROVSE | ADC_CFGR2_OVSR_0 | ADC_CFGR2_OVSS_1;
- m_group_config2.cfgr2 = ADC_CFGR2_ROVSE | ADC_CFGR2_OVSR_0 | ADC_CFGR2_OVSS_1;
-#endif
-}
-
-void ADC::setOperation(ADC::Operation operation)
-{
- m_operation = operation;
-}
-
-void ADC::conversionCallback(ADCDriver *driver)
-{
- if (m_operation != nullptr) {
- auto half_size = m_current_buffer_size / 2;
- if (adcIsBufferComplete(driver))
- m_operation(m_current_buffer + half_size, half_size);
- else
- m_operation(m_current_buffer, half_size);
- }
-}
-
+++ /dev/null
-/**
- * @file adc.hpp
- * @brief Manages signal reading through the ADC.
- *
- * 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_ADC_HPP_
-#define STMDSP_ADC_HPP_
-
-#include "hal.h"
-#include "sclock.hpp"
-
-#include <array>
-
-class ADC
-{
-public:
- using Operation = void (*)(adcsample_t *buffer, size_t count);
-
- static void begin();
-
- static void start(adcsample_t *buffer, size_t count, Operation operation);
- static void stop();
-
- static adcsample_t readAlt(unsigned int id);
-
- static void setRate(SClock::Rate rate);
- static void setOperation(Operation operation);
-
-private:
- static ADCDriver *m_driver;
- static ADCDriver *m_driver2;
-
- static const ADCConfig m_config;
- static const ADCConfig m_config2;
- static ADCConversionGroup m_group_config;
- static ADCConversionGroup m_group_config2;
-
- static adcsample_t *m_current_buffer;
- static size_t m_current_buffer_size;
- static Operation m_operation;
-
-public:
- static void conversionCallback(ADCDriver *);
-};
-
-#endif // STMDSP_ADC_HPP_
-
* @note You can add your board-specific code here.\r
*/\r
void boardInit(void) {\r
+ // Enable the FPU (floating-point unit)\r
+ SCB->CPACR |= 0xF << 20;\r
\r
+ // Setup the MPU (memory protection unit):\r
+ // Region 2: Data for algorithm thread\r
+ // Region 3: Code for algorithm thread\r
+ // Region 4: User algorithm code\r
+ mpuConfigureRegion(MPU_REGION_2,\r
+ 0x20000000,\r
+ MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |\r
+ MPU_RASR_SIZE_64K |\r
+ MPU_RASR_ENABLE);\r
+ mpuConfigureRegion(MPU_REGION_3,\r
+ 0x0807F800,\r
+ MPU_RASR_ATTR_AP_RO_RO | MPU_RASR_ATTR_NON_CACHEABLE |\r
+ MPU_RASR_SIZE_2K |\r
+ MPU_RASR_ENABLE);\r
+ mpuConfigureRegion(MPU_REGION_4,\r
+ 0x00000000,\r
+ MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |\r
+ MPU_RASR_SIZE_64K |\r
+ MPU_RASR_ENABLE);\r
}\r
* @note You can add your board-specific code here.\r
*/\r
void boardInit(void) {\r
+ palSetLineMode(LINE_LED_GREEN, PAL_MODE_OUTPUT_PUSHPULL);\r
+ palSetLineMode(LINE_LED_YELLOW, PAL_MODE_OUTPUT_PUSHPULL);\r
+ palSetLineMode(LINE_LED_RED, PAL_MODE_OUTPUT_PUSHPULL);\r
\r
+ SCB->CPACR |= 0xF << 20; // Enable FPU\r
+\r
+ // Region 2: Data for algorithm thread and ADC/DAC buffers\r
+ // Region 3: Code for algorithm thread\r
+ // Region 4: User algorithm code\r
+ mpuConfigureRegion(MPU_REGION_2,\r
+ 0x20008000,\r
+ MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |\r
+ MPU_RASR_SIZE_128K |\r
+ MPU_RASR_ENABLE);\r
+ mpuConfigureRegion(MPU_REGION_3,\r
+ 0x0807F800,\r
+ MPU_RASR_ATTR_AP_RO_RO | MPU_RASR_ATTR_NON_CACHEABLE |\r
+ MPU_RASR_SIZE_2K |\r
+ MPU_RASR_ENABLE);\r
+ mpuConfigureRegion(MPU_REGION_4,\r
+ 0x10000000,\r
+ MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |\r
+ MPU_RASR_SIZE_32K |\r
+ MPU_RASR_ENABLE);\r
}\r
#endif\r
#endif /* _FROM_ASM_ */\r
\r
+#define LINE_LED_GREEN PAL_LINE(GPIOC_BASE, 10U)\r
+#define LINE_LED_YELLOW PAL_LINE(GPIOC_BASE, 11U)\r
+#define LINE_LED_RED PAL_LINE(GPIOC_BASE, 12U)\r
+\r
#endif /* BOARD_H */\r
--- /dev/null
+#include "communication.hpp"
+
+#include "ch.h"
+#include "hal.h"
+
+#include "periph/adc.hpp"
+#include "periph/dac.hpp"
+#include "periph/usbserial.hpp"
+#include "elfload.hpp"
+#include "error.hpp"
+#include "conversion.hpp"
+#include "runstatus.hpp"
+#include "samples.hpp"
+
+#include <algorithm>
+#include <tuple>
+
+__attribute__((section(".stacks")))
+std::array<char, 4096> CommunicationManager::m_thread_stack = {};
+
+void CommunicationManager::begin()
+{
+ chThdCreateStatic(m_thread_stack.data(),
+ m_thread_stack.size(),
+ NORMALPRIO,
+ threadComm,
+ nullptr);
+}
+
+static void writeADCBuffer(unsigned char *);
+static void setBufferSize(unsigned char *);
+static void updateGenerator(unsigned char *);
+static void loadAlgorithm(unsigned char *);
+static void readStatus(unsigned char *);
+static void startConversionMeasure(unsigned char *);
+static void startConversion(unsigned char *);
+static void stopConversion(unsigned char *);
+static void startGenerator(unsigned char *);
+static void readADCBuffer(unsigned char *);
+static void readDACBuffer(unsigned char *);
+static void unloadAlgorithm(unsigned char *);
+static void readIdentifier(unsigned char *);
+static void readExecTime(unsigned char *);
+static void sampleRate(unsigned char *);
+static void readConversionResults(unsigned char *);
+static void readConversionInput(unsigned char *);
+static void readMessage(unsigned char *);
+static void stopGenerator(unsigned char *);
+
+static const std::array<std::pair<char, void (*)(unsigned char *)>, 19> commandTable {{
+ {'A', writeADCBuffer},
+ {'B', setBufferSize},
+ {'D', updateGenerator},
+ {'E', loadAlgorithm},
+ {'I', readStatus},
+ {'M', startConversionMeasure},
+ {'R', startConversion},
+ {'S', stopConversion},
+ {'W', startGenerator},
+ {'a', readADCBuffer},
+ {'d', readDACBuffer},
+ {'e', unloadAlgorithm},
+ {'i', readIdentifier},
+ {'m', readExecTime},
+ {'r', sampleRate},
+ {'s', readConversionResults},
+ {'t', readConversionInput},
+ {'u', readMessage},
+ {'w', stopGenerator}
+}};
+
+void CommunicationManager::threadComm(void *)
+{
+ while (1) {
+ if (USBSerial::isActive()) {
+ // Attempt to receive a command packet
+ if (unsigned char cmd[3]; USBSerial::read(&cmd[0], 1) > 0) {
+ // Packet received, first byte represents the desired command/action
+ auto func = std::find_if(commandTable.cbegin(), commandTable.cend(),
+ [&cmd](const auto& f) { return f.first == cmd[0]; });
+ if (func != commandTable.cend())
+ func->second(cmd);
+ }
+ }
+
+ chThdSleepMicroseconds(100);
+ }
+}
+
+void writeADCBuffer(unsigned char *)
+{
+ USBSerial::read(Samples::In.bytedata(), Samples::In.bytesize());
+}
+
+void setBufferSize(unsigned char *cmd)
+{
+ if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle) &&
+ EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize))
+ {
+ // count is multiplied by two since this command receives size of buffer
+ // for each algorithm application.
+ unsigned int count = (cmd[1] | (cmd[2] << 8)) * 2;
+ if (EM.assert(count <= MAX_SAMPLE_BUFFER_SIZE, Error::BadParam)) {
+ Samples::In.setSize(count);
+ Samples::Out.setSize(count);
+ }
+ }
+}
+
+void updateGenerator(unsigned char *cmd)
+{
+ if (EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize)) {
+ unsigned int count = cmd[1] | (cmd[2] << 8);
+ if (EM.assert(count <= MAX_SAMPLE_BUFFER_SIZE, Error::BadParam)) {
+ if (run_status == RunStatus::Idle) {
+ Samples::Generator.setSize(count);
+ USBSerial::read(
+ reinterpret_cast<uint8_t *>(Samples::Generator.data()),
+ Samples::Generator.bytesize());
+ } else if (run_status == RunStatus::Running) {
+ int more;
+ do {
+ chThdSleepMicroseconds(10);
+ more = DAC::sigGenWantsMore();
+ } while (more == -1);
+
+ USBSerial::read(reinterpret_cast<uint8_t *>(
+ more == 0 ? Samples::Generator.data() : Samples::Generator.middata()),
+ Samples::Generator.bytesize() / 2);
+ }
+ }
+ }
+}
+
+void loadAlgorithm(unsigned char *cmd)
+{
+ if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle) &&
+ EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize))
+ {
+ // Only load the binary if it can fit in the memory reserved for it.
+ unsigned int size = cmd[1] | (cmd[2] << 8);
+ if (EM.assert(size < MAX_ELF_FILE_SIZE, Error::BadUserCodeSize)) {
+ USBSerial::read(ELFManager::fileBuffer(), size);
+ auto entry = ELFManager::loadFromInternalBuffer();
+ EM.assert(entry != nullptr, Error::BadUserCodeLoad);
+ }
+ }
+}
+
+void readStatus(unsigned char *)
+{
+ unsigned char buf[2] = {
+ static_cast<unsigned char>(run_status),
+ static_cast<unsigned char>(EM.pop())
+ };
+
+ USBSerial::write(buf, sizeof(buf));
+}
+
+void startConversionMeasure(unsigned char *)
+{
+ if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle)) {
+ run_status = RunStatus::Running;
+ ConversionManager::startMeasured();
+ }
+}
+
+void startConversion(unsigned char *)
+{
+ if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle)) {
+ run_status = RunStatus::Running;
+ ConversionManager::start();
+ }
+}
+
+void stopConversion(unsigned char *)
+{
+ if (run_status == RunStatus::Running) {
+ ConversionManager::stop();
+ run_status = RunStatus::Idle;
+ }
+}
+
+void startGenerator(unsigned char *)
+{
+ DAC::start(1, Samples::Generator.data(), Samples::Generator.size());
+}
+
+void readADCBuffer(unsigned char *)
+{
+ USBSerial::write(Samples::In.bytedata(), Samples::In.bytesize());
+}
+
+void readDACBuffer(unsigned char *)
+{
+
+ USBSerial::write(Samples::Out.bytedata(), Samples::Out.bytesize());
+}
+
+void unloadAlgorithm(unsigned char *)
+{
+ ELFManager::unload();
+}
+
+void readIdentifier(unsigned char *)
+{
+#if defined(TARGET_PLATFORM_H7)
+ USBSerial::write(reinterpret_cast<const uint8_t *>("stmdsph"), 7);
+#else
+ USBSerial::write(reinterpret_cast<const uint8_t *>("stmdspl"), 7);
+#endif
+}
+
+void readExecTime(unsigned char *)
+{
+ USBSerial::write(reinterpret_cast<uint8_t *>(&conversion_time_measurement.last),
+ sizeof(rtcnt_t));
+}
+
+void sampleRate(unsigned char *cmd)
+{
+ if (EM.assert(USBSerial::read(&cmd[1], 1) == 1, Error::BadParamSize)) {
+ if (cmd[1] == 0xFF) {
+ unsigned char r = SClock::getRate();
+ USBSerial::write(&r, 1);
+ } else {
+ auto r = static_cast<SClock::Rate>(cmd[1]);
+ SClock::setRate(r);
+ ADC::setRate(r);
+ }
+ }
+}
+
+void readConversionResults(unsigned char *)
+{
+ if (auto samps = Samples::Out.modified(); samps != nullptr) {
+ unsigned char buf[2] = {
+ static_cast<unsigned char>(Samples::Out.size() / 2 & 0xFF),
+ static_cast<unsigned char>(((Samples::Out.size() / 2) >> 8) & 0xFF)
+ };
+ USBSerial::write(buf, 2);
+ unsigned int total = Samples::Out.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);
+ }
+}
+
+void readConversionInput(unsigned char *)
+{
+ if (auto samps = Samples::In.modified(); samps != nullptr) {
+ unsigned char buf[2] = {
+ static_cast<unsigned char>(Samples::In.size() / 2 & 0xFF),
+ static_cast<unsigned char>(((Samples::In.size() / 2) >> 8) & 0xFF)
+ };
+ USBSerial::write(buf, 2);
+ unsigned int total = Samples::In.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);
+ }
+}
+
+void readMessage(unsigned char *)
+{
+ //USBSerial::write(reinterpret_cast<uint8_t *>(userMessageBuffer), userMessageSize);
+}
+
+void stopGenerator(unsigned char *)
+{
+ DAC::stop(1);
+}
+
--- /dev/null
+/**
+ * @file communication.hpp
+ * @brief Manages communication with the host computer.
+ *
+ * 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_COMMUNICATION_HPP
+#define STMDSP_COMMUNICATION_HPP
+
+#include <array>
+
+class CommunicationManager
+{
+public:
+ static void begin();
+
+private:
+ static void threadComm(void *);
+
+ static std::array<char, 4096> m_thread_stack;
+};
+
+#endif // STMDSP_COMMUNICATION_HPP
+
--- /dev/null
+#include "conversion.hpp"
+
+#include "periph/adc.hpp"
+#include "periph/dac.hpp"
+#include "elfload.hpp"
+#include "error.hpp"
+#include "runstatus.hpp"
+#include "samples.hpp"
+
+constexpr msg_t MSG_CONVFIRST = 1;
+constexpr msg_t MSG_CONVSECOND = 2;
+constexpr msg_t MSG_CONVFIRST_MEASURE = 3;
+constexpr msg_t MSG_CONVSECOND_MEASURE = 4;
+
+constexpr auto MSG_FOR_FIRST = [](msg_t m) { return m & 1; };
+constexpr auto MSG_FOR_MEASURE = [](msg_t m) { return m > 2; };
+
+time_measurement_t conversion_time_measurement;
+
+__attribute__((section(".convdata")))
+thread_t *ConversionManager::m_thread_monitor = nullptr;
+thread_t *ConversionManager::m_thread_runner = nullptr;
+
+__attribute__((section(".stacks")))
+std::array<char, 1024> ConversionManager::m_thread_monitor_stack = {};
+__attribute__((section(".stacks")))
+std::array<char, 128> ConversionManager::m_thread_runner_entry_stack = {};
+__attribute__((section(".convdata")))
+std::array<char, CONVERSION_THREAD_STACK_SIZE> ConversionManager::m_thread_runner_stack = {};
+
+std::array<msg_t, 2> ConversionManager::m_mailbox_buffer;
+mailbox_t ConversionManager::m_mailbox = _MAILBOX_DATA(m_mailbox, m_mailbox_buffer.data(), m_mailbox_buffer.size());
+
+void ConversionManager::begin()
+{
+ m_thread_monitor = chThdCreateStatic(m_thread_monitor_stack.data(),
+ m_thread_monitor_stack.size(),
+ NORMALPRIO + 1,
+ threadMonitor,
+ nullptr);
+ auto runner_stack_end = &m_thread_runner_stack[CONVERSION_THREAD_STACK_SIZE];
+ m_thread_runner = chThdCreateStatic(m_thread_runner_entry_stack.data(),
+ m_thread_runner_entry_stack.size(),
+ HIGHPRIO,
+ threadRunnerEntry,
+ runner_stack_end);
+}
+
+void ConversionManager::start()
+{
+ Samples::Out.clear();
+ ADC::start(Samples::In.data(), Samples::In.size(), adcReadHandler);
+ DAC::start(0, Samples::Out.data(), Samples::Out.size());
+}
+
+void ConversionManager::startMeasured()
+{
+ Samples::Out.clear();
+ ADC::start(Samples::In.data(), Samples::In.size(), adcReadHandlerMeasure);
+ DAC::start(0, Samples::Out.data(), Samples::Out.size());
+}
+
+void ConversionManager::stop()
+{
+ DAC::stop(0);
+ ADC::stop();
+}
+
+thread_t *ConversionManager::getMonitorHandle()
+{
+ return m_thread_monitor;
+}
+
+void ConversionManager::abort()
+{
+ ELFManager::unload();
+ EM.add(Error::ConversionAborted);
+ run_status = RunStatus::Recovering;
+
+ // Confirm that the exception return thread is the algorithm...
+ uint32_t *psp;
+ asm("mrs %0, psp" : "=r" (psp));
+
+ bool isRunnerStack =
+ (uint32_t)psp >= reinterpret_cast<uint32_t>(m_thread_runner_stack.data()) &&
+ (uint32_t)psp <= reinterpret_cast<uint32_t>(m_thread_runner_stack.data() +
+ m_thread_runner_stack.size());
+
+ if (isRunnerStack)
+ {
+ // If it is, we can force the algorithm to exit by "resetting" its thread.
+ // We do this by rebuilding the thread's stacked exception return.
+ auto newpsp = reinterpret_cast<uint32_t *>(m_thread_runner_stack.data() +
+ m_thread_runner_stack.size() -
+ 8 * sizeof(uint32_t));
+ // Set the LR register to the thread's entry point.
+ newpsp[5] = reinterpret_cast<uint32_t>(threadRunner);
+ // Overwrite the instruction we'll return to with "bx lr" (jump to address in LR).
+ newpsp[6] = psp[6];
+ *reinterpret_cast<uint16_t *>(newpsp[6]) = 0x4770; // "bx lr"
+ // Keep PSR contents (bit set forces Thumb mode, just in case).
+ newpsp[7] = psp[7] | (1 << 24);
+ // Set the new stack pointer.
+ asm("msr psp, %0" :: "r" (newpsp));
+ }
+}
+
+void ConversionManager::threadMonitor(void *)
+{
+ while (1) {
+ msg_t message;
+ msg_t fetch = chMBFetchTimeout(&m_mailbox, &message, TIME_INFINITE);
+ if (fetch == MSG_OK)
+ chMsgSend(m_thread_runner, message);
+ }
+}
+
+void ConversionManager::threadRunnerEntry(void *stack)
+{
+ ELFManager::unload();
+ port_unprivileged_jump(reinterpret_cast<uint32_t>(threadRunner),
+ reinterpret_cast<uint32_t>(stack));
+}
+
+__attribute__((section(".convcode")))
+void ConversionManager::threadRunner(void *)
+{
+ while (1) {
+ // Sleep until we receive a mailbox message.
+ msg_t message;
+ asm("svc 0; mov %0, r0" : "=r" (message));
+
+ if (message != 0) {
+ auto samples = MSG_FOR_FIRST(message) ? Samples::In.data()
+ : Samples::In.middata();
+ auto size = Samples::In.size() / 2;
+
+ auto entry = ELFManager::loadedElf();
+ if (entry) {
+ // Below, we remember the stack pointer just in case the
+ // loaded algorithm messes things up.
+ uint32_t sp;
+
+ if (!MSG_FOR_MEASURE(message)) {
+ asm("mov %0, sp" : "=r" (sp));
+ samples = entry(samples, size);
+ asm("mov sp, %0" :: "r" (sp));
+ } else {
+ // Start execution timer:
+ asm("mov %0, sp; eor r0, r0; svc 2" : "=r" (sp));
+ samples = entry(samples, size);
+ // Stop execution timer:
+ asm("mov r0, #1; svc 2; mov sp, %0" :: "r" (sp));
+ }
+ }
+
+ // Update the sample out buffer with the transformed samples.
+ if (samples != nullptr) {
+ if (MSG_FOR_FIRST(message))
+ Samples::Out.modify(samples, size);
+ else
+ Samples::Out.midmodify(samples, size);
+ }
+ }
+ }
+}
+
+void ConversionManager::adcReadHandler(adcsample_t *buffer, size_t)
+{
+ chSysLockFromISR();
+
+ // If previous request hasn't been handled, then we're going too slow.
+ // We'll need to abort.
+ if (chMBGetUsedCountI(&m_mailbox) > 1) {
+ chMBResetI(&m_mailbox);
+ chMBResumeX(&m_mailbox);
+ chSysUnlockFromISR();
+ abort();
+ } else {
+ // Mark the modified samples as 'fresh' or ready for manipulation.
+ if (buffer == Samples::In.data()) {
+ Samples::In.setModified();
+ chMBPostI(&m_mailbox, MSG_CONVFIRST);
+ } else {
+ Samples::In.setMidmodified();
+ chMBPostI(&m_mailbox, MSG_CONVSECOND);
+ }
+ chSysUnlockFromISR();
+ }
+}
+
+void ConversionManager::adcReadHandlerMeasure(adcsample_t *buffer, size_t)
+{
+ chSysLockFromISR();
+ if (buffer == Samples::In.data()) {
+ Samples::In.setModified();
+ chMBPostI(&m_mailbox, MSG_CONVFIRST_MEASURE);
+ } else {
+ Samples::In.setMidmodified();
+ chMBPostI(&m_mailbox, MSG_CONVSECOND_MEASURE);
+ }
+ chSysUnlockFromISR();
+
+ ADC::setOperation(adcReadHandler);
+}
+
--- /dev/null
+/**
+ * @file conversion.hpp
+ * @brief Manages algorithm application (converts input samples to output).
+ *
+ * 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_CONVERSION_HPP
+#define STMDSP_CONVERSION_HPP
+
+#include "ch.h"
+#include "hal.h"
+
+#include <array>
+
+constexpr unsigned int CONVERSION_THREAD_STACK_SIZE =
+#if defined(TARGET_PLATFORM_H7)
+ 62 * 1024;
+#else
+ 15 * 1024;
+#endif
+
+class ConversionManager
+{
+public:
+ static void begin();
+
+ // Begins sample conversion.
+ static void start();
+ // Begins conversion with execution time measured.
+ static void startMeasured();
+ // Stops conversion.
+ static void stop();
+
+ static thread_t *getMonitorHandle();
+
+ // Internal only: Aborts a running conversion.
+ static void abort();
+
+private:
+ static void threadMonitor(void *);
+ static void threadRunnerEntry(void *stack);
+
+ static void threadRunner(void *);
+ static void adcReadHandler(adcsample_t *buffer, size_t);
+ static void adcReadHandlerMeasure(adcsample_t *buffer, size_t);
+
+ static thread_t *m_thread_monitor;
+ static thread_t *m_thread_runner;
+
+ static std::array<char, 1024> m_thread_monitor_stack;
+ static std::array<char, 128> m_thread_runner_entry_stack;
+ static std::array<char, CONVERSION_THREAD_STACK_SIZE> m_thread_runner_stack;
+
+ static std::array<msg_t, 2> m_mailbox_buffer;
+ static mailbox_t m_mailbox;
+};
+
+// Stores the measured execution time.
+extern time_measurement_t conversion_time_measurement;
+
+#endif // STMDSP_CONVERSION_HPP
+
+++ /dev/null
-#include "cordic.hpp"
-#include "hal.h"
-
-#if !defined(TARGET_PLATFORM_L4)
-namespace cordic {
-
-void init()
-{
- RCC->AHB2ENR |= RCC_AHB2ENR_CORDICEN;
-}
-
-static void prepare() {
- while (CORDIC->CSR & CORDIC_CSR_RRDY)
- asm("mov r0, %0" :: "r" (CORDIC->RDATA));
-}
-
-static uint32_t dtoq(double) {
- uint32_t res;
- asm("vcvt.s32.f64 d0, d0, #31;"
- "vmov %0, r5, d0"
- : "=r" (res));
- return res;
-}
-__attribute__((naked))
-static double qtod(uint32_t) {
- asm("eor r1, r1;"
- "vmov d0, r0, r1;"
- "vcvt.f64.s32 d0, d0, #31;"
- "bx lr");
- return 0;
-}
-__attribute__((naked))
-double mod(double, double) {
- asm("vdiv.f64 d2, d0, d1;"
- "vrintz.f64 d2;"
- "vmul.f64 d1, d1, d2;"
- "vsub.f64 d0, d0, d1;"
- "bx lr");
- return 0;
-}
-
-double cos(double x) {
- x = mod(x, 2 * math::PI) / math::PI;
- auto input = dtoq(x > 1. ? x - 2 : x);
-
- prepare();
- CORDIC->CSR = CORDIC_CSR_NARGS | CORDIC_CSR_NRES |
- (6 << CORDIC_CSR_PRECISION_Pos) |
- (0 << CORDIC_CSR_FUNC_Pos);
-
- CORDIC->WDATA = input;
- CORDIC->WDATA = input;
- while (!(CORDIC->CSR & CORDIC_CSR_RRDY));
-
- double cosx = qtod(CORDIC->RDATA) / x;
- [[maybe_unused]] auto sinx = CORDIC->RDATA;
- return cosx;
-}
-
-double sin(double x) {
- x = mod(x, 2 * math::PI) / math::PI;
- auto input = dtoq(x > 1. ? x - 2 : x);
-
- prepare();
- CORDIC->CSR = CORDIC_CSR_NARGS | CORDIC_CSR_NRES |
- (6 << CORDIC_CSR_PRECISION_Pos) |
- (1 << CORDIC_CSR_FUNC_Pos);
-
- CORDIC->WDATA = input;
- CORDIC->WDATA = input;
- while (!(CORDIC->CSR & CORDIC_CSR_RRDY));
-
- double sinx = qtod(CORDIC->RDATA) / x;
- [[maybe_unused]] auto cosx = CORDIC->RDATA;
- return sinx;
-}
-
-double tan(double x) {
- x = mod(x, 2 * math::PI) / math::PI;
- auto input = dtoq(x > 1. ? x - 2 : x);
-
- prepare();
- CORDIC->CSR = CORDIC_CSR_NARGS | CORDIC_CSR_NRES |
- (6 << CORDIC_CSR_PRECISION_Pos) |
- (1 << CORDIC_CSR_FUNC_Pos);
-
- CORDIC->WDATA = input;
- CORDIC->WDATA = input;
- while (!(CORDIC->CSR & CORDIC_CSR_RRDY));
-
- double sinx = qtod(CORDIC->RDATA) / x;
- double tanx = sinx * x / qtod(CORDIC->RDATA);
- return tanx;
-}
-
-}
-#else // L4
-#include <cmath>
-namespace cordic {
-
-void init() {}
-
-float mod(float a, float b) {
- return a - (b * std::floor(a / b));
-}
-
-float cos(float x) { return std::cos(x); }
-float sin(float x) { return std::sin(x); }
-float tan(float x) { return std::tan(x); }
-
-}
-#endif
-
+++ /dev/null
-#ifndef CORDIC_HPP_
-#define CORDIC_HPP_
-
-namespace cordic {
- constexpr double PI = 3.1415926535L;
-
- void init();
-
-#if !defined(TARGET_PLATFORM_L4)
- double mod(double n, double d);
-
- double cos(double x);
- double sin(double x);
- double tan(double x);
-#else
- float mod(float n, float d);
-
- float cos(float x);
- float sin(float x);
- float tan(float x);
-#endif
-}
-
-#endif // CORDIC_HPP_
-
+++ /dev/null
-/**
- * @file dac.cpp
- * @brief Manages signal creation using the DAC.
- *
- * 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 "dac.hpp"
-#include "sclock.hpp"
-
-DACDriver *DAC::m_driver[2] = {
- &DACD1, &DACD2
-};
-
-const DACConfig DAC::m_config = {
- .init = 2048,
- .datamode = DAC_DHRM_12BIT_RIGHT,
- .cr = 0
-};
-
-static int dacIsDone = -1;
-static void dacEndCallback(DACDriver *dacd)
-{
- if (dacd == &DACD2)
- dacIsDone = dacIsBufferComplete(dacd) ? 1 : 0;
-}
-
-const DACConversionGroup DAC::m_group_config = {
- .num_channels = 1,
- .end_cb = dacEndCallback,
- .error_cb = nullptr,
-#if defined(TARGET_PLATFORM_H7)
- .trigger = 5 // TIM6_TRGO
-#elif defined(TARGET_PLATFORM_L4)
- .trigger = 0 // TIM6_TRGO
-#endif
-};
-
-void DAC::begin()
-{
- palSetPadMode(GPIOA, 4, PAL_STM32_MODE_ANALOG);
- palSetPadMode(GPIOA, 5, PAL_STM32_MODE_ANALOG);
-
- dacStart(m_driver[0], &m_config);
- dacStart(m_driver[1], &m_config);
-}
-
-void DAC::start(int channel, dacsample_t *buffer, size_t count)
-{
- if (channel >= 0 && channel < 2) {
- if (channel == 1)
- dacIsDone = -1;
- dacStartConversion(m_driver[channel], &m_group_config, buffer, count);
- SClock::start();
- }
-}
-
-int DAC::sigGenWantsMore()
-{
- return dacIsDone;
-}
-
-void DAC::stop(int channel)
-{
- if (channel >= 0 && channel < 2) {
- dacStopConversion(m_driver[channel]);
- SClock::stop();
- }
-}
-
+++ /dev/null
-/**
- * @file dac.hpp
- * @brief Manages signal creation using the DAC.
- *
- * 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_DAC_HPP_
-#define STMDSP_DAC_HPP_
-
-#include "hal.h"
-#undef DAC
-
-class DAC
-{
-public:
- static void begin();
-
- static void start(int channel, dacsample_t *buffer, size_t count);
- static void stop(int channel);
-
- static int sigGenWantsMore();
-
-private:
- static DACDriver *m_driver[2];
-
- static const DACConfig m_config;
- static const DACConversionGroup m_group_config;
-};
-
-#endif // STMDSP_DAC_HPP_
-
--- /dev/null
+/**
+ * @file elf.h
+ * @brief Defines ELF binary format info.
+ *
+ * Free to use, written by Clyne Sullivan.
+ */
+
+#ifndef STMDSP_ELF_HPP
+#define STMDSP_ELF_HPP
+
+#include <cstdint>
+
+#define EI_NIDENT 16
+
+#define PT_NULL 0
+#define PT_LOAD 1
+#define PT_DYNAMIC 2
+#define PT_INTERP 3
+#define PT_NOTE 4
+#define PT_SHLIB 5
+#define PT_PHDR 6
+#define PT_RESERVED 0x70000000
+
+#define ELF32_ST_BIND(i) ((i) >> 4)
+#define ELF32_ST_TYPE(i) ((i) & 0xF)
+#define ELF32_ST_INFO(b, t) (((b) << 4) + ((t) & 0xF))
+
+#define ELF32_R_SYM(i) ((i) >> 8)
+#define ELF32_R_TYPE(i) ((i) & 0xFF)
+#define ELF32_R_INFO(s, t) (((s) << 8) + ((t) & 0xFF))
+
+typedef uint32_t Elf32_Addr;
+typedef uint16_t Elf32_Half;
+typedef uint32_t Elf32_Off;
+typedef uint32_t Elf32_Sword;
+typedef uint32_t Elf32_Word;
+
+typedef struct {
+ unsigned char e_ident[EI_NIDENT];
+ Elf32_Half e_type;
+ Elf32_Half e_machine;
+ Elf32_Word e_version;
+ Elf32_Addr e_entry;
+ Elf32_Off e_phoff;
+ Elf32_Off e_shoff;
+ Elf32_Word e_flags;
+ Elf32_Half e_ehsize;
+ Elf32_Half e_phentsize;
+ Elf32_Half e_phnum;
+ Elf32_Half e_shentsize;
+ Elf32_Half e_shnum;
+ Elf32_Half e_shstrndx;
+} __attribute__((packed)) Elf32_Ehdr;
+
+typedef struct {
+ Elf32_Word sh_name;
+ Elf32_Word sh_type;
+ Elf32_Word sh_flags;
+ Elf32_Addr sh_addr;
+ Elf32_Off sh_offset;
+ Elf32_Word sh_size;
+ Elf32_Word sh_link;
+ Elf32_Word sh_info;
+ Elf32_Word sh_addralign;
+ Elf32_Word sh_entsize;
+} __attribute__((packed)) Elf32_Shdr;
+
+typedef struct {
+ Elf32_Word st_name;
+ Elf32_Addr st_value;
+ Elf32_Word st_size;
+ unsigned char st_info;
+ unsigned char st_other;
+ Elf32_Half st_shndx;
+} __attribute__((packed)) Elf32_Sym;
+
+typedef struct {
+ Elf32_Addr r_offset;
+ Elf32_Word r_info;
+} __attribute__((packed)) Elf32_Rel;
+
+typedef struct {
+ Elf32_Addr r_offset;
+ Elf32_Word r_info;
+ Elf32_Sword r_addend;
+} __attribute__((packed)) Elf32_Rela;
+
+typedef struct {
+ Elf32_Word p_type;
+ Elf32_Off p_offset;
+ Elf32_Addr p_vaddr;
+ Elf32_Addr p_paddr;
+ Elf32_Word p_filesz;
+ Elf32_Word p_memsz;
+ Elf32_Word p_flags;
+ Elf32_Word p_align;
+} __attribute__((packed)) Elf32_Phdr;
+
+#endif // STMDSP_ELF_HPP
+
+++ /dev/null
-/**
- * @file elf_format.cpp
- * @brief Defines ELF binary format info.
- *
- * Free to use, written by Clyne Sullivan.
- */
-
-#ifndef STMDSP_ELF_FORMAT_HPP_
-#define STMDSP_ELF_FORMAT_HPP_
-
-#include <cstdint>
-
-#define EI_NIDENT 16
-
-#define PT_NULL 0
-#define PT_LOAD 1
-#define PT_DYNAMIC 2
-#define PT_INTERP 3
-#define PT_NOTE 4
-#define PT_SHLIB 5
-#define PT_PHDR 6
-#define PT_RESERVED 0x70000000
-
-#define ELF32_ST_BIND(i) ((i) >> 4)
-#define ELF32_ST_TYPE(i) ((i) & 0xF)
-#define ELF32_ST_INFO(b, t) (((b) << 4) + ((t) & 0xF))
-
-#define ELF32_R_SYM(i) ((i) >> 8)
-#define ELF32_R_TYPE(i) ((i) & 0xFF)
-#define ELF32_R_INFO(s, t) (((s) << 8) + ((t) & 0xFF))
-
-typedef uint32_t Elf32_Addr;
-typedef uint16_t Elf32_Half;
-typedef uint32_t Elf32_Off;
-typedef uint32_t Elf32_Sword;
-typedef uint32_t Elf32_Word;
-
-typedef struct {
- unsigned char e_ident[EI_NIDENT];
- Elf32_Half e_type;
- Elf32_Half e_machine;
- Elf32_Word e_version;
- Elf32_Addr e_entry;
- Elf32_Off e_phoff;
- Elf32_Off e_shoff;
- Elf32_Word e_flags;
- Elf32_Half e_ehsize;
- Elf32_Half e_phentsize;
- Elf32_Half e_phnum;
- Elf32_Half e_shentsize;
- Elf32_Half e_shnum;
- Elf32_Half e_shstrndx;
-} __attribute__((packed)) Elf32_Ehdr;
-
-typedef struct {
- Elf32_Word sh_name;
- Elf32_Word sh_type;
- Elf32_Word sh_flags;
- Elf32_Addr sh_addr;
- Elf32_Off sh_offset;
- Elf32_Word sh_size;
- Elf32_Word sh_link;
- Elf32_Word sh_info;
- Elf32_Word sh_addralign;
- Elf32_Word sh_entsize;
-} __attribute__((packed)) Elf32_Shdr;
-
-typedef struct {
- Elf32_Word st_name;
- Elf32_Addr st_value;
- Elf32_Word st_size;
- unsigned char st_info;
- unsigned char st_other;
- Elf32_Half st_shndx;
-} __attribute__((packed)) Elf32_Sym;
-
-typedef struct {
- Elf32_Addr r_offset;
- Elf32_Word r_info;
-} __attribute__((packed)) Elf32_Rel;
-
-typedef struct {
- Elf32_Addr r_offset;
- Elf32_Word r_info;
- Elf32_Sword r_addend;
-} __attribute__((packed)) Elf32_Rela;
-
-typedef struct {
- Elf32_Word p_type;
- Elf32_Off p_offset;
- Elf32_Addr p_vaddr;
- Elf32_Addr p_paddr;
- Elf32_Word p_filesz;
- Elf32_Word p_memsz;
- Elf32_Word p_flags;
- Elf32_Word p_align;
-} __attribute__((packed)) Elf32_Phdr;
-
-#endif // STMDSP_ELF_FORMAT_HPP_
-
+++ /dev/null
-/**
- * @file elf_load.cpp
- * @brief Loads ELF binary data into memory for execution.
- *
- * 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 "elf_load.hpp"
-#include "elf_format.hpp"
-
-#include <algorithm>
-#include <cstring>
-
-static const unsigned char elf_header[] = { '\177', 'E', 'L', 'F' };
-
-template<typename T>
-constexpr static auto ptr_from_offset(void *base, uint32_t offset)
-{
- return reinterpret_cast<T>(reinterpret_cast<uint8_t *>(base) + offset);
-}
-
-namespace ELF {
-
-Entry load(void *elf_data)
-{
- // Check the ELF's header signature
- auto ehdr = reinterpret_cast<Elf32_Ehdr *>(elf_data);
- if (!std::equal(ehdr->e_ident, ehdr->e_ident + 4, elf_header))
- return nullptr;
-
- // Iterate through program header LOAD sections
- bool loaded = false;
- auto phdr = ptr_from_offset<Elf32_Phdr *>(elf_data, ehdr->e_phoff);
- for (Elf32_Half i = 0; i < ehdr->e_phnum; i++) {
- if (phdr->p_type == PT_LOAD) {
- if (phdr->p_filesz == 0) {
- std::memset(reinterpret_cast<void *>(phdr->p_vaddr),
- 0,
- phdr->p_memsz);
- } else {
- std::memcpy(reinterpret_cast<void *>(phdr->p_vaddr),
- ptr_from_offset<void *>(elf_data, phdr->p_offset),
- phdr->p_filesz);
- if (!loaded)
- loaded = true;
- }
- }
-
- phdr = ptr_from_offset<Elf32_Phdr *>(phdr, ehdr->e_phentsize);
- }
-
-
- return loaded ? reinterpret_cast<Entry>(ehdr->e_entry) : nullptr;
-}
-
-} // namespace ELF
-
+++ /dev/null
-/**
- * @file elf_load.hpp
- * @brief Loads ELF binary data into memory for execution.
- *
- * 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 ELF_LOAD_HPP_
-#define ELF_LOAD_HPP_
-
-#include <cstddef>
-#include <cstdint>
-
-namespace ELF
-{
- using Entry = uint16_t *(*)(uint16_t *, size_t);
-
- Entry load(void *elf_data);
-}
-
-#endif // ELF_LOAD_HPP_
-
--- /dev/null
+/**
+ * @file elfload.cpp
+ * @brief Loads ELF binary data into memory for execution.
+ *
+ * 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 "elfload.hpp"
+#include "elf.h"
+
+#include <algorithm>
+#include <cstring>
+
+__attribute__((section(".convdata")))
+ELFManager::EntryFunc ELFManager::m_entry = nullptr;
+std::array<unsigned char, MAX_ELF_FILE_SIZE> ELFManager::m_file_buffer = {};
+
+static const unsigned char elf_header[] = { '\177', 'E', 'L', 'F' };
+
+template<typename T>
+constexpr static auto ptr_from_offset(void *base, uint32_t offset)
+{
+ return reinterpret_cast<T>(reinterpret_cast<uint8_t *>(base) + offset);
+}
+
+ELFManager::EntryFunc ELFManager::loadFromInternalBuffer()
+{
+ auto elf_data = m_file_buffer.data();
+
+ // Check the ELF's header signature
+ auto ehdr = reinterpret_cast<Elf32_Ehdr *>(elf_data);
+ if (!std::equal(ehdr->e_ident, ehdr->e_ident + 4, elf_header))
+ return nullptr;
+
+ // Iterate through program header LOAD sections
+ bool loaded = false;
+ auto phdr = ptr_from_offset<Elf32_Phdr *>(elf_data, ehdr->e_phoff);
+ for (Elf32_Half i = 0; i < ehdr->e_phnum; i++) {
+ if (phdr->p_type == PT_LOAD) {
+ if (phdr->p_filesz == 0) {
+ std::memset(reinterpret_cast<void *>(phdr->p_vaddr),
+ 0,
+ phdr->p_memsz);
+ } else {
+ std::memcpy(reinterpret_cast<void *>(phdr->p_vaddr),
+ ptr_from_offset<void *>(elf_data, phdr->p_offset),
+ phdr->p_filesz);
+ if (!loaded)
+ loaded = true;
+ }
+ }
+
+ phdr = ptr_from_offset<Elf32_Phdr *>(phdr, ehdr->e_phentsize);
+ }
+
+
+ return loaded ? reinterpret_cast<ELFManager::EntryFunc>(ehdr->e_entry) : nullptr;
+}
+
--- /dev/null
+/**
+ * @file elfload.hpp
+ * @brief Loads ELF binary data into memory for execution.
+ *
+ * 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 ELF_LOAD_HPP_
+#define ELF_LOAD_HPP_
+
+#include "samplebuffer.hpp"
+
+#include <array>
+#include <cstddef>
+
+constexpr unsigned int MAX_ELF_FILE_SIZE = 16 * 1024;
+
+class ELFManager
+{
+public:
+ using EntryFunc = Sample *(*)(Sample *, size_t);
+
+ static EntryFunc loadFromInternalBuffer();
+
+ static EntryFunc loadedElf() {
+ return m_entry;
+ }
+
+ static unsigned char *fileBuffer() {
+ return m_file_buffer.data();
+ }
+
+ static void unload() {
+ m_entry = nullptr;
+ }
+
+private:
+ static EntryFunc m_entry;
+
+ static std::array<unsigned char, MAX_ELF_FILE_SIZE> m_file_buffer;
+};
+
+#endif // ELF_LOAD_HPP_
+
--- /dev/null
+/**
+ * @file error.cpp
+ * @brief Tracks and reports non-critical errors.
+ *
+ * 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 "error.hpp"
+
+ErrorManager EM;
+
+void ErrorManager::add(Error error)
+{
+ if (m_index < m_queue.size())
+ m_queue[m_index++] = error;
+}
+
+bool ErrorManager::assert(bool condition, Error error)
+{
+ if (!condition)
+ add(error);
+ return condition;
+}
+
+bool ErrorManager::hasError()
+{
+ return m_index > 0;
+}
+
+Error ErrorManager::pop()
+{
+ return m_index == 0 ? Error::None : m_queue[--m_index];
+}
+
-#include <array>
+/**
+ * @file error.hpp
+ * @brief Tracks and reports non-critical errors.
+ *
+ * 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_ERROR_HPP
+#define STMDSP_ERROR_HPP
-constexpr unsigned int MAX_ERROR_QUEUE_SIZE = 8;
+#include <array>
enum class Error : char
{
class ErrorManager
{
-public:
- void add(Error error) {
- if (m_index < m_queue.size())
- m_queue[m_index++] = error;
- }
-
- bool assert(bool condition, Error error) {
- if (!condition)
- add(error);
- return condition;
- }
+ constexpr static unsigned int MAX_ERROR_QUEUE_SIZE = 8;
- bool hasError() {
- return m_index > 0;
- }
-
- Error pop() {
- return m_index == 0 ? Error::None : m_queue[--m_index];
- }
+public:
+ void add(Error error);
+ bool assert(bool condition, Error error);
+ bool hasError();
+ Error pop();
private:
std::array<Error, MAX_ERROR_QUEUE_SIZE> m_queue;
unsigned int m_index = 0;
};
+extern ErrorManager EM;
+
+#endif // STMDSP_ERROR_HPP
+
--- /dev/null
+#include "handlers.hpp"
+
+#include "adc.hpp"
+#include "conversion.hpp"
+#include "cordic.hpp"
+#include "runstatus.hpp"
+
+extern "C" {
+
+__attribute__((naked))
+void port_syscall(struct port_extctx *ctxp, uint32_t n)
+{
+ switch (n) {
+
+ // Sleeps the current thread until a message is received.
+ // Used the algorithm runner to wait for new data.
+ case 0:
+ {
+ chSysLock();
+ chMsgWaitS();
+ auto monitor = ConversionManager::getMonitorHandle();
+ auto msg = chMsgGet(monitor);
+ chMsgReleaseS(monitor, MSG_OK);
+ chSysUnlock();
+ ctxp->r0 = msg;
+ }
+ break;
+
+ // Provides access to advanced math functions.
+ // A service call like this is required for some hardware targets that
+ // provide hardware-accelerated math computations (e.g. CORDIC).
+ case 1:
+ {
+ using mathcall = void (*)();
+ static mathcall funcs[3] = {
+ reinterpret_cast<mathcall>(cordic::sin),
+ reinterpret_cast<mathcall>(cordic::cos),
+ reinterpret_cast<mathcall>(cordic::tan),
+ };
+#if defined(PLATFORM_H7)
+ asm("vmov.f64 d0, %0, %1" :: "r" (ctxp->r1), "r" (ctxp->r2));
+ if (ctxp->r0 < 3) {
+ funcs[ctxp->r0]();
+ asm("vmov.f64 %0, %1, d0" : "=r" (ctxp->r1), "=r" (ctxp->r2));
+ } else {
+ asm("eor r0, r0; vmov.f64 d0, r0, r0");
+ }
+#else
+ asm("vmov.f32 s0, %0" :: "r" (ctxp->r1));
+ if (ctxp->r0 < 3) {
+ funcs[ctxp->r0]();
+ asm("vmov.f32 %0, s0" : "=r" (ctxp->r1));
+ } else {
+ asm("eor r0, r0; vmov.f32 s0, r0");
+ }
+#endif
+ }
+ break;
+
+ // Starts or stops precise cycle time measurement.
+ // Used to measure algorithm execution time.
+ case 2:
+ if (ctxp->r0 == 0) {
+ chTMStartMeasurementX(&conversion_time_measurement);
+ } else {
+ chTMStopMeasurementX(&conversion_time_measurement);
+ // Subtract measurement overhead from the result.
+ // Running an empty algorithm ("bx lr") takes 196 cycles as of 2/4/21.
+ // Only measures algorithm code time (loading args/storing result takes 9 cycles).
+ constexpr rtcnt_t measurement_overhead = 196 - 1;
+ if (conversion_time_measurement.last > measurement_overhead)
+ conversion_time_measurement.last -= measurement_overhead;
+ }
+ break;
+
+ // Reads one of the analog inputs made available for algorithm run-time input.
+ case 3:
+ ctxp->r0 = ADC::readAlt(ctxp->r0);
+ break;
+
+ //case 4:
+ // {
+ // const char *str = reinterpret_cast<const char *>(ctxp->r0);
+ // auto src = str;
+ // auto dst = userMessageBuffer;
+ // while (*src)
+ // *dst++ = *src++;
+ // *dst = '\0';
+ // userMessageSize = src - str;
+ // }
+ // break;
+ default:
+ while (1);
+ break;
+ }
+
+ asm("svc 0");
+ while (1);
+}
+
+__attribute__((naked))
+void MemManage_Handler()
+{
+ // 1. Get the stack pointer.
+ uint32_t lr;
+ asm("mov %0, lr" : "=r" (lr));
+
+ // 2. Recover from the fault.
+ ConversionManager::abort();
+
+ // 3. Return.
+ asm("mov lr, %0; bx lr" :: "r" (lr));
+}
+
+__attribute__((naked))
+void HardFault_Handler()
+{
+ // Get the stack pointer.
+ //uint32_t *stack;
+ uint32_t lr;
+ asm("mov %0, lr" : "=r" (lr));
+ /*asm("\
+ tst lr, #4; \
+ ite eq; \
+ mrseq %0, msp; \
+ mrsne %0, psp; \
+ mov %1, lr; \
+ " : "=r" (stack), "=r" (lr));*/
+
+ // If coming from the algorithm, attempt to recover; otherwise, give up.
+ if (run_status != RunStatus::Running && (lr & 4) != 0)
+ MemManage_Handler();
+
+ while (1);
+}
+
+} // extern "C"
+
--- /dev/null
+/**
+ * @file handlers.hpp
+ * @brief Interrupt service routine handlers.
+ *
+ * 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_HANDLERS_HPP
+#define STMDSP_HANDLERS_HPP
+
+#include "ch.h"
+
+extern "C" {
+
+__attribute__((naked))
+void port_syscall(struct port_extctx *ctxp, uint32_t n);
+
+__attribute__((naked))
+void MemManage_Handler();
+
+__attribute__((naked))
+void HardFault_Handler();
+
+}
+
+#endif // STMDSP_HANDLERS_HPP
+
#include "ch.h"
#include "hal.h"
-static_assert(sizeof(adcsample_t) == sizeof(uint16_t));
-static_assert(sizeof(dacsample_t) == sizeof(uint16_t));
-
#include "adc.hpp"
#include "cordic.hpp"
#include "dac.hpp"
-#include "elf_load.hpp"
#include "error.hpp"
-#include "samplebuffer.hpp"
#include "sclock.hpp"
#include "usbserial.hpp"
-#include <array>
-
-// Pin definitions
-//
-#if defined(TARGET_PLATFORM_L4)
-constexpr auto LINE_LED_GREEN = PAL_LINE(GPIOC_BASE, 10U);
-constexpr auto LINE_LED_YELLOW = PAL_LINE(GPIOC_BASE, 11U);
-constexpr auto LINE_LED_RED = PAL_LINE(GPIOC_BASE, 12U);
-#endif
-
-// Run status
-//
-enum class RunStatus : char
-{
- Idle = '1',
- Running,
- Recovering
-};
-static RunStatus run_status = RunStatus::Idle;
-
-// Conversion threads messaging
-//
-#define MSG_CONVFIRST (1)
-#define MSG_CONVSECOND (2)
-#define MSG_CONVFIRST_MEASURE (3)
-#define MSG_CONVSECOND_MEASURE (4)
-
-#define MSG_FOR_FIRST(m) (m & 1)
-#define MSG_FOR_MEASURE(m) (m > 2)
-
-static msg_t conversionMBBuffer[2];
-static MAILBOX_DECL(conversionMB, conversionMBBuffer, 2);
-
-// Sample input and output buffers
-//
-#if defined(TARGET_PLATFORM_H7)
-__attribute__((section(".convdata")))
-static SampleBuffer samplesIn (reinterpret_cast<Sample *>(0x38000000)); // 16k
-__attribute__((section(".convdata")))
-static SampleBuffer samplesOut (reinterpret_cast<Sample *>(0x30004000)); // 16k
-static SampleBuffer samplesSigGen (reinterpret_cast<Sample *>(0x30000000)); // 16k
-#else
-__attribute__((section(".convdata")))
-static SampleBuffer samplesIn (reinterpret_cast<Sample *>(0x20008000)); // 16k
-__attribute__((section(".convdata")))
-static SampleBuffer samplesOut (reinterpret_cast<Sample *>(0x2000C000)); // 16k
-static SampleBuffer samplesSigGen (reinterpret_cast<Sample *>(0x20010000)); // 16k
-#endif
-
-// Algorithm binary storage
-//
-constexpr unsigned int MAX_ELF_FILE_SIZE = 16 * 1024;
-static unsigned char elf_file_store[MAX_ELF_FILE_SIZE];
-__attribute__((section(".convdata")))
-static ELF::Entry elf_entry = nullptr;
+#include "runstatus.hpp"
+RunStatus run_status = RunStatus::Idle;
// Other variables
//
-static ErrorManager EM;
-static time_measurement_t conversion_time_measurement;
-static char userMessageBuffer[128];
-static unsigned char userMessageSize = 0;
+//static char userMessageBuffer[128];
+//static unsigned char userMessageSize = 0;
-// Functions
-//
-__attribute__((section(".convcode")))
-static void conversion_unprivileged_main();
-static void startThreads();
-static void mpuSetup();
-static void abortAlgorithmFromISR();
-static void signalOperate(adcsample_t *buffer, size_t count);
-static void signalOperateMeasure(adcsample_t *buffer, size_t count);
+#include "conversion.hpp"
+#include "communication.hpp"
+#include "monitor.hpp"
int main()
{
halInit();
chSysInit();
- SCB->CPACR |= 0xF << 20; // Enable FPU
- mpuSetup();
-
-#if defined(TARGET_PLATFORM_L4)
- palSetLineMode(LINE_LED_GREEN, PAL_MODE_OUTPUT_PUSHPULL);
- palSetLineMode(LINE_LED_YELLOW, PAL_MODE_OUTPUT_PUSHPULL);
- palSetLineMode(LINE_LED_RED, PAL_MODE_OUTPUT_PUSHPULL);
-#endif
-
+ // Init peripherials
ADC::begin();
DAC::begin();
SClock::begin();
SClock::setRate(SClock::Rate::R32K);
ADC::setRate(SClock::Rate::R32K);
- startThreads();
+ // Start our threads.
+ ConversionManager::begin();
+ CommunicationManager::begin();
+ Monitor::begin();
+
chThdExit(0);
return 0;
}
-static THD_FUNCTION(monitorThread, arg); // Runs status LEDs and allows debug halt.
-static THD_FUNCTION(conversionThreadMonitor, arg); // Monitors and manages algo. thread.
-static THD_FUNCTION(conversionThread, arg); // Algorithm thread (unprivileged).
-static THD_FUNCTION(communicationThread, arg); // Manages USB communications.
-
-// Need to hold some thread handles for mailbox usage.
-static thread_t *conversionThreadHandle = nullptr;
-__attribute__((section(".convdata")))
-static thread_t *conversionThreadMonitorHandle = nullptr;
-
-// The more stack for the algorithm, the merrier.
-constexpr unsigned int conversionThreadUPWASize =
-#if defined(TARGET_PLATFORM_H7)
- 62 * 1024;
-#else
- 15 * 1024;
-#endif
-
-__attribute__((section(".stacks")))
-static THD_WORKING_AREA(monitorThreadWA, 256);
-__attribute__((section(".stacks")))
-static THD_WORKING_AREA(conversionThreadMonitorWA, 1024);
-__attribute__((section(".stacks")))
-static THD_WORKING_AREA(conversionThreadWA, 128); // For entering unprivileged mode.
-__attribute__((section(".convdata")))
-static THD_WORKING_AREA(conversionThreadUPWA, conversionThreadUPWASize);
-__attribute__((section(".stacks")))
-static THD_WORKING_AREA(communicationThreadWA, 4096);
-
-void startThreads()
-{
- chThdCreateStatic(
- monitorThreadWA, sizeof(monitorThreadWA),
- LOWPRIO,
- monitorThread, nullptr);
- conversionThreadMonitorHandle = chThdCreateStatic(
- conversionThreadMonitorWA, sizeof(conversionThreadMonitorWA),
- NORMALPRIO + 1,
- conversionThreadMonitor, nullptr);
- auto conversionThreadUPWAEnd =
- reinterpret_cast<uint32_t>(conversionThreadUPWA) + conversionThreadUPWASize;
- conversionThreadHandle = chThdCreateStatic(
- conversionThreadWA, sizeof(conversionThreadWA),
- HIGHPRIO,
- conversionThread,
- reinterpret_cast<void *>(conversionThreadUPWAEnd));
- chThdCreateStatic(
- communicationThreadWA, sizeof(communicationThreadWA),
- NORMALPRIO,
- communicationThread, nullptr);
-}
-
-THD_FUNCTION(communicationThread, arg)
-{
- (void)arg;
- while (1) {
- if (USBSerial::isActive()) {
- // Attempt to receive a command packet
- if (unsigned char cmd[3]; USBSerial::read(&cmd[0], 1) > 0) {
- // Packet received, first byte represents the desired command/action
- switch (cmd[0]) {
-
- // 'a' - Read contents of ADC buffer.
- // 'A' - Write contents of ADC buffer.
- // 'B' - Set ADC/DAC buffer size.
- // 'd' - Read contents of DAC buffer.
- // 'D' - Set siggen size and write to its buffer.
- // 'E' - Load algorithm binary.
- // 'e' - Unload algorithm.
- // 'i' - Read "stmdsp" identifier string.
- // 'I' - Read status information.
- // 'M' - Begin conversion, measure algorithm execution time.
- // 'm' - Read last algorithm execution time.
- // 'R' - Begin conversion.
- // 'r' - Read or write sample rate.
- // 'S' - Stop conversion.
- // 's' - Get latest block of conversion results.
- // 't' - Get latest block of conversion input.
- // 'u' - Get user message.
- // 'W' - Start signal generator (siggen).
- // 'w' - Stop siggen.
-
- case 'a':
- USBSerial::write(samplesIn.bytedata(), samplesIn.bytesize());
- break;
- case 'A':
- USBSerial::read(samplesIn.bytedata(), samplesIn.bytesize());
- break;
-
- case 'B':
- if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle) &&
- EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize))
- {
- // count is multiplied by two since this command receives size of buffer
- // for each algorithm application.
- unsigned int count = (cmd[1] | (cmd[2] << 8)) * 2;
- if (EM.assert(count <= MAX_SAMPLE_BUFFER_SIZE, Error::BadParam)) {
- samplesIn.setSize(count);
- samplesOut.setSize(count);
- }
- }
- break;
-
- case 'd':
- USBSerial::write(samplesOut.bytedata(), samplesOut.bytesize());
- break;
- case 'D':
- if (EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize)) {
- unsigned int count = cmd[1] | (cmd[2] << 8);
- if (EM.assert(count <= MAX_SAMPLE_BUFFER_SIZE, Error::BadParam)) {
- if (run_status == RunStatus::Idle) {
- samplesSigGen.setSize(count * 2);
- USBSerial::read(
- reinterpret_cast<uint8_t *>(samplesSigGen.middata()),
- samplesSigGen.bytesize() / 2);
- } else if (run_status == RunStatus::Running) {
- int more;
- do {
- chThdSleepMicroseconds(10);
- more = DAC::sigGenWantsMore();
- } while (more == -1);
-
- USBSerial::read(reinterpret_cast<uint8_t *>(
- more == 0 ? samplesSigGen.data() : samplesSigGen.middata()),
- samplesSigGen.bytesize() / 2);
- }
- }
- }
- break;
-
- // 'E' - Reads in and loads the compiled conversion code binary from USB.
- case 'E':
- if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle) &&
- EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize))
- {
- // Only load the binary if it can fit in the memory reserved for it.
- unsigned int size = cmd[1] | (cmd[2] << 8);
- if (EM.assert(size < sizeof(elf_file_store), Error::BadUserCodeSize)) {
- USBSerial::read(elf_file_store, size);
- elf_entry = ELF::load(elf_file_store);
-
- EM.assert(elf_entry != nullptr, Error::BadUserCodeLoad);
- }
- }
- break;
-
- // 'e' - Unloads the currently loaded conversion code
- case 'e':
- elf_entry = nullptr;
- break;
-
- // 'i' - Sends an identifying string to confirm that this is the stmdsp device.
- case 'i':
-#if defined(TARGET_PLATFORM_H7)
- USBSerial::write(reinterpret_cast<const uint8_t *>("stmdsph"), 7);
-#else
- USBSerial::write(reinterpret_cast<const uint8_t *>("stmdspl"), 7);
-#endif
- break;
-
- // 'I' - Sends the current run status.
- case 'I':
- {
- unsigned char buf[2] = {
- static_cast<unsigned char>(run_status),
- static_cast<unsigned char>(EM.pop())
- };
- USBSerial::write(buf, sizeof(buf));
- }
- break;
-
- // 'M' - Begins continuous sampling, but measures the execution time of the first
- // sample processing. This duration can be later read through 'm'.
- case 'M':
- if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle)) {
- run_status = RunStatus::Running;
- samplesOut.clear();
- ADC::start(samplesIn.data(), samplesIn.size(), signalOperateMeasure);
- DAC::start(0, samplesOut.data(), samplesOut.size());
- }
- break;
-
- // 'm' - Returns the last measured sample processing time, presumably in processor
- // ticks.
- case 'm':
- USBSerial::write(reinterpret_cast<uint8_t *>(&conversion_time_measurement.last),
- sizeof(rtcnt_t));
- break;
-
- // 'R' - Begin continuous sampling/conversion of the ADC. Samples will go through
- // the conversion code, and will be sent out over the DAC.
- case 'R':
- if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle)) {
- run_status = RunStatus::Running;
- samplesOut.clear();
- ADC::start(samplesIn.data(), samplesIn.size(), signalOperate);
- DAC::start(0, samplesOut.data(), samplesOut.size());
- }
- break;
-
- case 'r':
- if (EM.assert(USBSerial::read(&cmd[1], 1) == 1, Error::BadParamSize)) {
- if (cmd[1] == 0xFF) {
- unsigned char r = SClock::getRate();
- USBSerial::write(&r, 1);
- } else {
- auto r = static_cast<SClock::Rate>(cmd[1]);
- SClock::setRate(r);
- ADC::setRate(r);
- }
- }
- break;
-
- // 'S' - Stops the continuous sampling/conversion.
- case 'S':
- if (run_status == RunStatus::Running) {
- DAC::stop(0);
- ADC::stop();
- run_status = RunStatus::Idle;
- }
- break;
-
- case 's':
- if (auto samps = samplesOut.modified(); samps != nullptr) {
- unsigned char buf[2] = {
- static_cast<unsigned char>(samplesOut.size() / 2 & 0xFF),
- static_cast<unsigned char>(((samplesOut.size() / 2) >> 8) & 0xFF)
- };
- USBSerial::write(buf, 2);
- unsigned int total = samplesOut.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 '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 'u':
- USBSerial::write(reinterpret_cast<uint8_t *>(userMessageBuffer), userMessageSize);
- break;
-
- case 'W':
- DAC::start(1, samplesSigGen.data(), samplesSigGen.size());
- break;
- case 'w':
- DAC::stop(1);
- break;
-
- default:
- break;
- }
- }
- }
-
- chThdSleepMicroseconds(100);
- }
-}
-
-THD_FUNCTION(conversionThreadMonitor, arg)
-{
- (void)arg;
- while (1) {
- msg_t message;
- if (chMBFetchTimeout(&conversionMB, &message, TIME_INFINITE) == MSG_OK)
- chMsgSend(conversionThreadHandle, message);
- }
-}
-
-THD_FUNCTION(conversionThread, stack)
-{
- elf_entry = nullptr;
- port_unprivileged_jump(reinterpret_cast<uint32_t>(conversion_unprivileged_main),
- reinterpret_cast<uint32_t>(stack));
-}
-
-THD_FUNCTION(monitorThread, arg)
-{
- (void)arg;
-
- palSetLineMode(LINE_BUTTON, PAL_MODE_INPUT_PULLUP);
- auto readButton = [] {
-#if defined(TARGET_PLATFORM_L4)
- return !palReadLine(LINE_BUTTON);
-#else
- return palReadLine(LINE_BUTTON);
-#endif
- };
-
- palClearLine(LINE_LED_RED);
- palClearLine(LINE_LED_YELLOW);
-
- while (1) {
- bool isidle = run_status == RunStatus::Idle;
- auto led = isidle ? LINE_LED_GREEN : LINE_LED_YELLOW;
- auto delay = isidle ? 500 : 250;
-
- palSetLine(led);
- chThdSleepMilliseconds(delay);
- palClearLine(led);
- chThdSleepMilliseconds(delay);
-
- if (run_status == RunStatus::Idle && readButton()) {
- palSetLine(LINE_LED_RED);
- palSetLine(LINE_LED_YELLOW);
- chSysLock();
- while (readButton())
- asm("nop");
- while (!readButton())
- asm("nop");
- chSysUnlock();
- palClearLine(LINE_LED_RED);
- palClearLine(LINE_LED_YELLOW);
- chThdSleepMilliseconds(500);
- }
-
- static bool erroron = false;
- if (auto err = EM.hasError(); err ^ erroron) {
- erroron = err;
- if (err)
- palSetLine(LINE_LED_RED);
- else
- palClearLine(LINE_LED_RED);
- }
- }
-}
-
-void conversion_unprivileged_main()
-{
- while (1) {
- msg_t message;
- asm("svc 0; mov %0, r0" : "=r" (message)); // sleep until next message
- if (message != 0) {
- auto samples = MSG_FOR_FIRST(message) ? samplesIn.data() : samplesIn.middata();
- auto size = samplesIn.size() / 2;
-
- if (elf_entry) {
- if (!MSG_FOR_MEASURE(message)) {
- // Remember the stack pointer in case the algorithm messes things up.
- uint32_t sp;
- asm("mov %0, sp" : "=r" (sp));
- samples = elf_entry(samples, size);
- asm("mov sp, %0" :: "r" (sp));
- } else {
- uint32_t sp;
- asm("mov %0, sp; eor r0, r0; svc 2" : "=r" (sp)); // start measurement
- samples = elf_entry(samples, size);
- asm("mov r0, #1; svc 2; mov sp, %0" :: "r" (sp)); // stop measurement
- }
- }
-
- if (samples != nullptr) {
- if (MSG_FOR_FIRST(message))
- samplesOut.modify(samples, size);
- else
- samplesOut.midmodify(samples, size);
- }
- }
- }
-}
-
-void mpuSetup()
-{
- // Set up MPU for user algorithm
-#if defined(TARGET_PLATFORM_H7)
- // Region 2: Data for algorithm thread
- // Region 3: Code for algorithm thread
- // Region 4: User algorithm code
- mpuConfigureRegion(MPU_REGION_2,
- 0x20000000,
- MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |
- MPU_RASR_SIZE_64K |
- MPU_RASR_ENABLE);
- mpuConfigureRegion(MPU_REGION_3,
- 0x0807F800,
- MPU_RASR_ATTR_AP_RO_RO | MPU_RASR_ATTR_NON_CACHEABLE |
- MPU_RASR_SIZE_2K |
- MPU_RASR_ENABLE);
- mpuConfigureRegion(MPU_REGION_4,
- 0x00000000,
- MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |
- MPU_RASR_SIZE_64K |
- MPU_RASR_ENABLE);
-#else
- // Region 2: Data for algorithm thread and ADC/DAC buffers
- // Region 3: Code for algorithm thread
- // Region 4: User algorithm code
- mpuConfigureRegion(MPU_REGION_2,
- 0x20008000,
- MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |
- MPU_RASR_SIZE_128K|
- MPU_RASR_ENABLE);
- mpuConfigureRegion(MPU_REGION_3,
- 0x0807F800,
- MPU_RASR_ATTR_AP_RO_RO | MPU_RASR_ATTR_NON_CACHEABLE |
- MPU_RASR_SIZE_2K |
- MPU_RASR_ENABLE);
- mpuConfigureRegion(MPU_REGION_4,
- 0x10000000,
- MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |
- MPU_RASR_SIZE_32K |
- MPU_RASR_ENABLE);
-#endif
-}
-
-void abortAlgorithmFromISR()
-{
- elf_entry = nullptr;
- EM.add(Error::ConversionAborted);
- run_status = RunStatus::Recovering;
-
- // Confirm that the exception return thread is the algorithm...
- uint32_t *psp;
- asm("mrs %0, psp" : "=r" (psp));
- if ((uint32_t)psp >= (uint32_t)conversionThreadUPWA &&
- (uint32_t)psp <= (uint32_t)conversionThreadUPWA + conversionThreadUPWASize)
- {
- // If it is, we can force the algorithm to exit by "resetting" its thread.
- // We do this by rebuilding the thread's stacked exception return.
- uint32_t *newpsp = reinterpret_cast<uint32_t *>(
- (char *)conversionThreadUPWA +
- conversionThreadUPWASize - 8 * sizeof(uint32_t));
- // Set the LR register to the thread's entry point.
- newpsp[5] = reinterpret_cast<uint32_t>(conversion_unprivileged_main);
- // Overwrite the instruction we'll return to with "bx lr" (jump to address in LR).
- newpsp[6] = psp[6];
- *reinterpret_cast<uint16_t *>(newpsp[6]) = 0x4770; // "bx lr"
- // Keep PSR contents (bit set forces Thumb mode, just in case).
- newpsp[7] = psp[7] | (1 << 24);
- // Set the new stack pointer.
- asm("msr psp, %0" :: "r" (newpsp));
- }
-}
-
-void signalOperate(adcsample_t *buffer, size_t)
-{
- chSysLockFromISR();
-
- if (chMBGetUsedCountI(&conversionMB) > 1) {
- chMBResetI(&conversionMB);
- chMBResumeX(&conversionMB);
- chSysUnlockFromISR();
- abortAlgorithmFromISR();
- } else {
- if (buffer == samplesIn.data()) {
- samplesIn.setModified();
- chMBPostI(&conversionMB, MSG_CONVFIRST);
- } else {
- samplesIn.setMidmodified();
- chMBPostI(&conversionMB, MSG_CONVSECOND);
- }
- chSysUnlockFromISR();
- }
-}
-
-void signalOperateMeasure(adcsample_t *buffer, [[maybe_unused]] size_t count)
-{
- chSysLockFromISR();
- if (buffer == samplesIn.data()) {
- samplesIn.setModified();
- chMBPostI(&conversionMB, MSG_CONVFIRST_MEASURE);
- } else {
- samplesIn.setMidmodified();
- chMBPostI(&conversionMB, MSG_CONVSECOND_MEASURE);
- }
- chSysUnlockFromISR();
-
- ADC::setOperation(signalOperate);
-}
-
-extern "C" {
-
-__attribute__((naked))
-void port_syscall(struct port_extctx *ctxp, uint32_t n)
-{
- switch (n) {
- case 0:
- {
- chSysLock();
- chMsgWaitS();
- auto msg = chMsgGet(conversionThreadMonitorHandle);
- chMsgReleaseS(conversionThreadMonitorHandle, MSG_OK);
- chSysUnlock();
- ctxp->r0 = msg;
- }
- break;
- case 1:
- {
- using mathcall = void (*)();
- static mathcall funcs[3] = {
- reinterpret_cast<mathcall>(cordic::sin),
- reinterpret_cast<mathcall>(cordic::cos),
- reinterpret_cast<mathcall>(cordic::tan),
- };
-#if defined(PLATFORM_H7)
- asm("vmov.f64 d0, %0, %1" :: "r" (ctxp->r1), "r" (ctxp->r2));
- if (ctxp->r0 < 3) {
- funcs[ctxp->r0]();
- asm("vmov.f64 %0, %1, d0" : "=r" (ctxp->r1), "=r" (ctxp->r2));
- } else {
- asm("eor r0, r0; vmov.f64 d0, r0, r0");
- }
-#else
- asm("vmov.f32 s0, %0" :: "r" (ctxp->r1));
- if (ctxp->r0 < 3) {
- funcs[ctxp->r0]();
- asm("vmov.f32 %0, s0" : "=r" (ctxp->r1));
- } else {
- asm("eor r0, r0; vmov.f32 s0, r0");
- }
-#endif
- }
- break;
- case 2:
- if (ctxp->r0 == 0) {
- chTMStartMeasurementX(&conversion_time_measurement);
- } else {
- chTMStopMeasurementX(&conversion_time_measurement);
- // Subtract measurement overhead from the result.
- // Running an empty algorithm ("bx lr") takes 196 cycles as of 2/4/21.
- // Only measures algorithm code time (loading args/storing result takes 9 cycles).
- constexpr rtcnt_t measurement_overhead = 196 - 1;
- if (conversion_time_measurement.last > measurement_overhead)
- conversion_time_measurement.last -= measurement_overhead;
- }
- break;
- case 3:
- ctxp->r0 = ADC::readAlt(ctxp->r0);
- break;
- case 4:
- {
- const char *str = reinterpret_cast<const char *>(ctxp->r0);
- auto src = str;
- auto dst = userMessageBuffer;
- while (*src)
- *dst++ = *src++;
- *dst = '\0';
- userMessageSize = src - str;
- }
- break;
- default:
- while (1);
- break;
- }
-
- asm("svc 0");
- while (1);
-}
-
-__attribute__((naked))
-void MemManage_Handler()
-{
- // 1. Get the stack pointer.
- uint32_t lr;
- asm("mov %0, lr" : "=r" (lr));
-
- // 2. Recover from the fault.
- abortAlgorithmFromISR();
-
- // 3. Return.
- asm("mov lr, %0; bx lr" :: "r" (lr));
-}
-
-__attribute__((naked))
-void HardFault_Handler()
-{
- // Get the stack pointer.
- //uint32_t *stack;
- uint32_t lr;
- asm("mov %0, lr" : "=r" (lr));
- /*asm("\
- tst lr, #4; \
- ite eq; \
- mrseq %0, msp; \
- mrsne %0, psp; \
- mov %1, lr; \
- " : "=r" (stack), "=r" (lr));*/
-
- // If coming from the algorithm, attempt to recover; otherwise, give up.
- if (run_status != RunStatus::Running && (lr & 4) != 0)
- MemManage_Handler();
-
- while (1);
-}
-
-} // extern "C"
-
--- /dev/null
+/**
+ * @file monitor.cpp
+ * @brief Manages the device monitoring thread (status LEDs, etc.).
+ *
+ * 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 "monitor.hpp"
+
+#include "error.hpp"
+#include "runstatus.hpp"
+
+#include "ch.h"
+#include "hal.h"
+
+void Monitor::begin()
+{
+ chThdCreateStatic(m_thread_stack.data(),
+ m_thread_stack.size(),
+ LOWPRIO,
+ threadMonitor,
+ nullptr);
+}
+
+void Monitor::threadMonitor(void *)
+{
+ palSetLineMode(LINE_BUTTON, PAL_MODE_INPUT_PULLUP);
+ auto readButton = [] {
+#ifdef TARGET_PLATFORM_L4
+ return !palReadLine(LINE_BUTTON);
+#else
+ return palReadLine(LINE_BUTTON);
+#endif
+ };
+
+ palClearLine(LINE_LED_RED);
+ palClearLine(LINE_LED_YELLOW);
+
+ while (1) {
+ bool isidle = run_status == RunStatus::Idle;
+ auto led = isidle ? LINE_LED_GREEN : LINE_LED_YELLOW;
+ auto delay = isidle ? 500 : 250;
+
+ palSetLine(led);
+ chThdSleepMilliseconds(delay);
+ palClearLine(led);
+ chThdSleepMilliseconds(delay);
+
+ if (run_status == RunStatus::Idle && readButton()) {
+ palSetLine(LINE_LED_RED);
+ palSetLine(LINE_LED_YELLOW);
+ chSysLock();
+ while (readButton())
+ asm("nop");
+ while (!readButton())
+ asm("nop");
+ chSysUnlock();
+ palClearLine(LINE_LED_RED);
+ palClearLine(LINE_LED_YELLOW);
+ chThdSleepMilliseconds(500);
+ }
+
+ static bool erroron = false;
+ if (auto err = EM.hasError(); err ^ erroron) {
+ erroron = err;
+ if (err)
+ palSetLine(LINE_LED_RED);
+ else
+ palClearLine(LINE_LED_RED);
+ }
+ }
+}
+
+__attribute__((section(".stacks")))
+std::array<char, 256> Monitor::m_thread_stack = {};
+
--- /dev/null
+/**
+ * @file monitor.hpp
+ * @brief Manages the device monitoring thread (status LEDs, etc.).
+ *
+ * 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_MONITOR_HPP
+#define STMDSP_MONITOR_HPP
+
+#include <array>
+
+class Monitor
+{
+public:
+ static void begin();
+
+private:
+ static void threadMonitor(void *);
+
+ static std::array<char, 256> m_thread_stack;
+};
+
+#endif // STMDSP_MONITOR_HPP
+
--- /dev/null
+/**
+ * @file adc.cpp
+ * @brief Manages signal reading through the ADC.
+ *
+ * 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 "adc.hpp"
+
+#if defined(TARGET_PLATFORM_L4)
+ADCDriver *ADC::m_driver = &ADCD1;
+ADCDriver *ADC::m_driver2 = &ADCD3;
+#else
+ADCDriver *ADC::m_driver = &ADCD3;
+//ADCDriver *ADC::m_driver2 = &ADCD1; // TODO
+#endif
+
+const ADCConfig ADC::m_config = {
+ .difsel = 0,
+#if defined(TARGET_PLATFORM_H7)
+ .calibration = 0,
+#endif
+};
+
+const ADCConfig ADC::m_config2 = {
+ .difsel = 0,
+#if defined(TARGET_PLATFORM_H7)
+ .calibration = 0,
+#endif
+};
+
+ADCConversionGroup ADC::m_group_config = {
+ .circular = true,
+ .num_channels = 1,
+ .end_cb = ADC::conversionCallback,
+ .error_cb = nullptr,
+ .cfgr = ADC_CFGR_EXTEN_RISING | ADC_CFGR_EXTSEL_SRC(13), /* TIM6_TRGO */
+ .cfgr2 = ADC_CFGR2_ROVSE | ADC_CFGR2_OVSR_1 | ADC_CFGR2_OVSS_0, // Oversampling 2x
+#if defined(TARGET_PLATFORM_H7)
+ .ccr = 0,
+ .pcsel = 0,
+ .ltr1 = 0, .htr1 = 4095,
+ .ltr2 = 0, .htr2 = 4095,
+ .ltr3 = 0, .htr3 = 4095,
+#else
+ .tr1 = ADC_TR(0, 4095),
+ .tr2 = ADC_TR(0, 4095),
+ .tr3 = ADC_TR(0, 4095),
+ .awd2cr = 0,
+ .awd3cr = 0,
+#endif
+ .smpr = {
+ ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_12P5), 0
+ },
+ .sqr = {
+ ADC_SQR1_SQ1_N(ADC_CHANNEL_IN5),
+ 0, 0, 0
+ },
+};
+
+static bool readAltDone = false;
+static void readAltCallback(ADCDriver *)
+{
+ readAltDone = true;
+}
+ADCConversionGroup ADC::m_group_config2 = {
+ .circular = false,
+ .num_channels = 2,
+ .end_cb = readAltCallback,
+ .error_cb = nullptr,
+ .cfgr = ADC_CFGR_EXTEN_RISING | ADC_CFGR_EXTSEL_SRC(13), /* TIM6_TRGO */
+ .cfgr2 = ADC_CFGR2_ROVSE | ADC_CFGR2_OVSR_1 | ADC_CFGR2_OVSS_0, // Oversampling 2x
+#if defined(TARGET_PLATFORM_H7)
+ .ccr = 0,
+ .pcsel = 0,
+ .ltr1 = 0, .htr1 = 4095,
+ .ltr2 = 0, .htr2 = 4095,
+ .ltr3 = 0, .htr3 = 4095,
+#else
+ .tr1 = ADC_TR(0, 4095),
+ .tr2 = ADC_TR(0, 4095),
+ .tr3 = ADC_TR(0, 4095),
+ .awd2cr = 0,
+ .awd3cr = 0,
+#endif
+ .smpr = {
+ ADC_SMPR1_SMP_AN1(ADC_SMPR_SMP_2P5) | ADC_SMPR1_SMP_AN2(ADC_SMPR_SMP_2P5), 0
+ },
+ .sqr = {
+ ADC_SQR1_SQ1_N(ADC_CHANNEL_IN1) | ADC_SQR1_SQ2_N(ADC_CHANNEL_IN2),
+ 0, 0, 0
+ },
+};
+
+adcsample_t *ADC::m_current_buffer = nullptr;
+size_t ADC::m_current_buffer_size = 0;
+ADC::Operation ADC::m_operation = nullptr;
+
+void ADC::begin()
+{
+#if defined(TARGET_PLATFORM_H7)
+ palSetPadMode(GPIOF, 3, PAL_MODE_INPUT_ANALOG);
+#else
+ palSetPadMode(GPIOA, 0, PAL_MODE_INPUT_ANALOG); // Algorithm in
+ palSetPadMode(GPIOC, 0, PAL_MODE_INPUT_ANALOG); // Potentiometer 1
+ palSetPadMode(GPIOC, 1, PAL_MODE_INPUT_ANALOG); // Potentiometer 2
+#endif
+
+ adcStart(m_driver, &m_config);
+ adcStart(m_driver2, &m_config2);
+}
+
+void ADC::start(adcsample_t *buffer, size_t count, Operation operation)
+{
+ m_current_buffer = buffer;
+ m_current_buffer_size = count;
+ m_operation = operation;
+
+ adcStartConversion(m_driver, &m_group_config, buffer, count);
+ SClock::start();
+}
+
+void ADC::stop()
+{
+ SClock::stop();
+ adcStopConversion(m_driver);
+
+ m_current_buffer = nullptr;
+ m_current_buffer_size = 0;
+ m_operation = nullptr;
+}
+
+adcsample_t ADC::readAlt(unsigned int id)
+{
+ if (id > 1)
+ return 0;
+ static adcsample_t result[16] = {};
+ readAltDone = false;
+ adcStartConversion(m_driver2, &m_group_config2, result, 8);
+ while (!readAltDone);
+ //__WFI();
+ adcStopConversion(m_driver2);
+ return result[id];
+}
+
+void ADC::setRate(SClock::Rate rate)
+{
+#if defined(TARGET_PLATFORM_H7)
+ std::array<std::array<uint32_t, 2>, 6> m_rate_presets = {{
+ // Rate PLL N PLL P
+ {/* 8k */ 80, 20},
+ {/* 16k */ 80, 10},
+ {/* 20k */ 80, 8},
+ {/* 32k */ 80, 5},
+ {/* 48k */ 96, 4},
+ {/* 96k */ 288, 10}
+ }};
+
+ auto& preset = m_rate_presets[static_cast<unsigned int>(rate)];
+ auto pllbits = (preset[0] << RCC_PLL2DIVR_N2_Pos) |
+ (preset[1] << RCC_PLL2DIVR_P2_Pos);
+
+ adcStop(m_driver);
+
+ // Adjust PLL2
+ RCC->CR &= ~(RCC_CR_PLL2ON);
+ while ((RCC->CR & RCC_CR_PLL2RDY) == RCC_CR_PLL2RDY);
+ auto pll2divr = RCC->PLL2DIVR &
+ ~(RCC_PLL2DIVR_N2_Msk | RCC_PLL2DIVR_P2_Msk);
+ pll2divr |= pllbits;
+ RCC->PLL2DIVR = pll2divr;
+ RCC->CR |= RCC_CR_PLL2ON;
+ while ((RCC->CR & RCC_CR_PLL2RDY) != RCC_CR_PLL2RDY);
+
+ m_group_config.smpr[0] = rate != SClock::Rate::R96K ? ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_12P5)
+ : ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_2P5);
+
+ adcStart(m_driver, &m_config);
+#elif defined(TARGET_PLATFORM_L4)
+ std::array<std::array<uint32_t, 3>, 6> m_rate_presets = {{
+ // Rate PLLSAI2N R SMPR
+ {/* 8k */ 8, 1, ADC_SMPR_SMP_12P5},
+ {/* 16k */ 16, 1, ADC_SMPR_SMP_12P5},
+ {/* 20k */ 20, 1, ADC_SMPR_SMP_12P5},
+ {/* 32k */ 32, 1, ADC_SMPR_SMP_12P5},
+ {/* 48k */ 24, 0, ADC_SMPR_SMP_12P5},
+ {/* 96k */ 73, 1, ADC_SMPR_SMP_6P5} // Technically 96.05263kS/s
+ }};
+
+ auto& preset = m_rate_presets[static_cast<int>(rate)];
+ auto pllnr = (preset[0] << RCC_PLLSAI2CFGR_PLLSAI2N_Pos) |
+ (preset[1] << RCC_PLLSAI2CFGR_PLLSAI2R_Pos);
+ auto smpr = preset[2];
+
+ // Adjust PLLSAI2
+ RCC->CR &= ~(RCC_CR_PLLSAI2ON);
+ while ((RCC->CR & RCC_CR_PLLSAI2RDY) == RCC_CR_PLLSAI2RDY);
+ RCC->PLLSAI2CFGR = (RCC->PLLSAI2CFGR & ~(RCC_PLLSAI2CFGR_PLLSAI2N_Msk | RCC_PLLSAI2CFGR_PLLSAI2R_Msk)) | pllnr;
+ RCC->CR |= RCC_CR_PLLSAI2ON;
+ while ((RCC->CR & RCC_CR_PLLSAI2RDY) != RCC_CR_PLLSAI2RDY);
+
+ m_group_config.smpr[0] = ADC_SMPR1_SMP_AN5(smpr);
+
+ // Set 2x oversampling
+ m_group_config.cfgr2 = ADC_CFGR2_ROVSE | ADC_CFGR2_OVSR_0 | ADC_CFGR2_OVSS_1;
+ m_group_config2.cfgr2 = ADC_CFGR2_ROVSE | ADC_CFGR2_OVSR_0 | ADC_CFGR2_OVSS_1;
+#endif
+}
+
+void ADC::setOperation(ADC::Operation operation)
+{
+ m_operation = operation;
+}
+
+void ADC::conversionCallback(ADCDriver *driver)
+{
+ if (m_operation != nullptr) {
+ auto half_size = m_current_buffer_size / 2;
+ if (adcIsBufferComplete(driver))
+ m_operation(m_current_buffer + half_size, half_size);
+ else
+ m_operation(m_current_buffer, half_size);
+ }
+}
+
--- /dev/null
+/**
+ * @file adc.hpp
+ * @brief Manages signal reading through the ADC.
+ *
+ * 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_ADC_HPP_
+#define STMDSP_ADC_HPP_
+
+#include "hal.h"
+#include "sclock.hpp"
+
+#include <array>
+
+class ADC
+{
+public:
+ using Operation = void (*)(adcsample_t *buffer, size_t count);
+
+ static void begin();
+
+ static void start(adcsample_t *buffer, size_t count, Operation operation);
+ static void stop();
+
+ static adcsample_t readAlt(unsigned int id);
+
+ static void setRate(SClock::Rate rate);
+ static void setOperation(Operation operation);
+
+private:
+ static ADCDriver *m_driver;
+ static ADCDriver *m_driver2;
+
+ static const ADCConfig m_config;
+ static const ADCConfig m_config2;
+ static ADCConversionGroup m_group_config;
+ static ADCConversionGroup m_group_config2;
+
+ static adcsample_t *m_current_buffer;
+ static size_t m_current_buffer_size;
+ static Operation m_operation;
+
+public:
+ static void conversionCallback(ADCDriver *);
+};
+
+#endif // STMDSP_ADC_HPP_
+
--- /dev/null
+#include "cordic.hpp"
+#include "hal.h"
+
+#if !defined(TARGET_PLATFORM_L4)
+namespace cordic {
+
+void init()
+{
+ RCC->AHB2ENR |= RCC_AHB2ENR_CORDICEN;
+}
+
+static void prepare() {
+ while (CORDIC->CSR & CORDIC_CSR_RRDY)
+ asm("mov r0, %0" :: "r" (CORDIC->RDATA));
+}
+
+static uint32_t dtoq(double) {
+ uint32_t res;
+ asm("vcvt.s32.f64 d0, d0, #31;"
+ "vmov %0, r5, d0"
+ : "=r" (res));
+ return res;
+}
+__attribute__((naked))
+static double qtod(uint32_t) {
+ asm("eor r1, r1;"
+ "vmov d0, r0, r1;"
+ "vcvt.f64.s32 d0, d0, #31;"
+ "bx lr");
+ return 0;
+}
+__attribute__((naked))
+double mod(double, double) {
+ asm("vdiv.f64 d2, d0, d1;"
+ "vrintz.f64 d2;"
+ "vmul.f64 d1, d1, d2;"
+ "vsub.f64 d0, d0, d1;"
+ "bx lr");
+ return 0;
+}
+
+double cos(double x) {
+ x = mod(x, 2 * math::PI) / math::PI;
+ auto input = dtoq(x > 1. ? x - 2 : x);
+
+ prepare();
+ CORDIC->CSR = CORDIC_CSR_NARGS | CORDIC_CSR_NRES |
+ (6 << CORDIC_CSR_PRECISION_Pos) |
+ (0 << CORDIC_CSR_FUNC_Pos);
+
+ CORDIC->WDATA = input;
+ CORDIC->WDATA = input;
+ while (!(CORDIC->CSR & CORDIC_CSR_RRDY));
+
+ double cosx = qtod(CORDIC->RDATA) / x;
+ [[maybe_unused]] auto sinx = CORDIC->RDATA;
+ return cosx;
+}
+
+double sin(double x) {
+ x = mod(x, 2 * math::PI) / math::PI;
+ auto input = dtoq(x > 1. ? x - 2 : x);
+
+ prepare();
+ CORDIC->CSR = CORDIC_CSR_NARGS | CORDIC_CSR_NRES |
+ (6 << CORDIC_CSR_PRECISION_Pos) |
+ (1 << CORDIC_CSR_FUNC_Pos);
+
+ CORDIC->WDATA = input;
+ CORDIC->WDATA = input;
+ while (!(CORDIC->CSR & CORDIC_CSR_RRDY));
+
+ double sinx = qtod(CORDIC->RDATA) / x;
+ [[maybe_unused]] auto cosx = CORDIC->RDATA;
+ return sinx;
+}
+
+double tan(double x) {
+ x = mod(x, 2 * math::PI) / math::PI;
+ auto input = dtoq(x > 1. ? x - 2 : x);
+
+ prepare();
+ CORDIC->CSR = CORDIC_CSR_NARGS | CORDIC_CSR_NRES |
+ (6 << CORDIC_CSR_PRECISION_Pos) |
+ (1 << CORDIC_CSR_FUNC_Pos);
+
+ CORDIC->WDATA = input;
+ CORDIC->WDATA = input;
+ while (!(CORDIC->CSR & CORDIC_CSR_RRDY));
+
+ double sinx = qtod(CORDIC->RDATA) / x;
+ double tanx = sinx * x / qtod(CORDIC->RDATA);
+ return tanx;
+}
+
+}
+#else // L4
+#include <cmath>
+namespace cordic {
+
+void init() {}
+
+float mod(float a, float b) {
+ return a - (b * std::floor(a / b));
+}
+
+float cos(float x) { return std::cos(x); }
+float sin(float x) { return std::sin(x); }
+float tan(float x) { return std::tan(x); }
+
+}
+#endif
+
--- /dev/null
+#ifndef CORDIC_HPP_
+#define CORDIC_HPP_
+
+namespace cordic {
+ constexpr double PI = 3.1415926535L;
+
+ void init();
+
+#if !defined(TARGET_PLATFORM_L4)
+ double mod(double n, double d);
+
+ double cos(double x);
+ double sin(double x);
+ double tan(double x);
+#else
+ float mod(float n, float d);
+
+ float cos(float x);
+ float sin(float x);
+ float tan(float x);
+#endif
+}
+
+#endif // CORDIC_HPP_
+
--- /dev/null
+/**
+ * @file dac.cpp
+ * @brief Manages signal creation using the DAC.
+ *
+ * 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 "dac.hpp"
+#include "sclock.hpp"
+
+DACDriver *DAC::m_driver[2] = {
+ &DACD1, &DACD2
+};
+
+const DACConfig DAC::m_config = {
+ .init = 2048,
+ .datamode = DAC_DHRM_12BIT_RIGHT,
+ .cr = 0
+};
+
+static int dacIsDone = -1;
+static void dacEndCallback(DACDriver *dacd)
+{
+ if (dacd == &DACD2)
+ dacIsDone = dacIsBufferComplete(dacd) ? 1 : 0;
+}
+
+const DACConversionGroup DAC::m_group_config = {
+ .num_channels = 1,
+ .end_cb = dacEndCallback,
+ .error_cb = nullptr,
+#if defined(TARGET_PLATFORM_H7)
+ .trigger = 5 // TIM6_TRGO
+#elif defined(TARGET_PLATFORM_L4)
+ .trigger = 0 // TIM6_TRGO
+#endif
+};
+
+void DAC::begin()
+{
+ palSetPadMode(GPIOA, 4, PAL_STM32_MODE_ANALOG);
+ palSetPadMode(GPIOA, 5, PAL_STM32_MODE_ANALOG);
+
+ dacStart(m_driver[0], &m_config);
+ dacStart(m_driver[1], &m_config);
+}
+
+void DAC::start(int channel, dacsample_t *buffer, size_t count)
+{
+ if (channel >= 0 && channel < 2) {
+ if (channel == 1)
+ dacIsDone = -1;
+ dacStartConversion(m_driver[channel], &m_group_config, buffer, count);
+ SClock::start();
+ }
+}
+
+int DAC::sigGenWantsMore()
+{
+ return dacIsDone;
+}
+
+void DAC::stop(int channel)
+{
+ if (channel >= 0 && channel < 2) {
+ dacStopConversion(m_driver[channel]);
+ SClock::stop();
+ }
+}
+
--- /dev/null
+/**
+ * @file dac.hpp
+ * @brief Manages signal creation using the DAC.
+ *
+ * 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_DAC_HPP_
+#define STMDSP_DAC_HPP_
+
+#include "hal.h"
+#undef DAC
+
+class DAC
+{
+public:
+ static void begin();
+
+ static void start(int channel, dacsample_t *buffer, size_t count);
+ static void stop(int channel);
+
+ static int sigGenWantsMore();
+
+private:
+ static DACDriver *m_driver[2];
+
+ static const DACConfig m_config;
+ static const DACConversionGroup m_group_config;
+};
+
+#endif // STMDSP_DAC_HPP_
+
--- /dev/null
+/*\r
+ ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\r
+\r
+ Licensed under the Apache License, Version 2.0 (the "License");\r
+ you may not use this file except in compliance with the License.\r
+ You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+*/\r
+\r
+#include "hal.h"\r
+\r
+/* Virtual serial port over USB.*/\r
+SerialUSBDriver SDU1;\r
+\r
+/*\r
+ * Endpoints to be used for USBD1.\r
+ */\r
+#define USBD1_DATA_REQUEST_EP 1\r
+#define USBD1_DATA_AVAILABLE_EP 1\r
+#define USBD1_INTERRUPT_REQUEST_EP 2\r
+\r
+/*\r
+ * USB Device Descriptor.\r
+ */\r
+static const uint8_t vcom_device_descriptor_data[18] = {\r
+ USB_DESC_DEVICE (0x0110, /* bcdUSB (1.1). */\r
+ 0x02, /* bDeviceClass (CDC). */\r
+ 0x00, /* bDeviceSubClass. */\r
+ 0x00, /* bDeviceProtocol. */\r
+ 0x40, /* bMaxPacketSize. */\r
+ 0x0483, /* idVendor (ST). */\r
+ 0x5740, /* idProduct. */\r
+ 0x0200, /* bcdDevice. */\r
+ 1, /* iManufacturer. */\r
+ 2, /* iProduct. */\r
+ 3, /* iSerialNumber. */\r
+ 1) /* bNumConfigurations. */\r
+};\r
+\r
+/*\r
+ * Device Descriptor wrapper.\r
+ */\r
+static const USBDescriptor vcom_device_descriptor = {\r
+ sizeof vcom_device_descriptor_data,\r
+ vcom_device_descriptor_data\r
+};\r
+\r
+/* Configuration Descriptor tree for a CDC.*/\r
+static const uint8_t vcom_configuration_descriptor_data[67] = {\r
+ /* Configuration Descriptor.*/\r
+ USB_DESC_CONFIGURATION(67, /* wTotalLength. */\r
+ 0x02, /* bNumInterfaces. */\r
+ 0x01, /* bConfigurationValue. */\r
+ 0, /* iConfiguration. */\r
+ 0xC0, /* bmAttributes (self powered). */\r
+ 50), /* bMaxPower (100mA). */\r
+ /* Interface Descriptor.*/\r
+ USB_DESC_INTERFACE (0x00, /* bInterfaceNumber. */\r
+ 0x00, /* bAlternateSetting. */\r
+ 0x01, /* bNumEndpoints. */\r
+ 0x02, /* bInterfaceClass (Communications\r
+ Interface Class, CDC section\r
+ 4.2). */\r
+ 0x02, /* bInterfaceSubClass (Abstract\r
+ Control Model, CDC section 4.3). */\r
+ 0x01, /* bInterfaceProtocol (AT commands,\r
+ CDC section 4.4). */\r
+ 0), /* iInterface. */\r
+ /* Header Functional Descriptor (CDC section 5.2.3).*/\r
+ USB_DESC_BYTE (5), /* bLength. */\r
+ USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */\r
+ USB_DESC_BYTE (0x00), /* bDescriptorSubtype (Header\r
+ Functional Descriptor. */\r
+ USB_DESC_BCD (0x0110), /* bcdCDC. */\r
+ /* Call Management Functional Descriptor. */\r
+ USB_DESC_BYTE (5), /* bFunctionLength. */\r
+ USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */\r
+ USB_DESC_BYTE (0x01), /* bDescriptorSubtype (Call Management\r
+ Functional Descriptor). */\r
+ USB_DESC_BYTE (0x00), /* bmCapabilities (D0+D1). */\r
+ USB_DESC_BYTE (0x01), /* bDataInterface. */\r
+ /* ACM Functional Descriptor.*/\r
+ USB_DESC_BYTE (4), /* bFunctionLength. */\r
+ USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */\r
+ USB_DESC_BYTE (0x02), /* bDescriptorSubtype (Abstract\r
+ Control Management Descriptor). */\r
+ USB_DESC_BYTE (0x02), /* bmCapabilities. */\r
+ /* Union Functional Descriptor.*/\r
+ USB_DESC_BYTE (5), /* bFunctionLength. */\r
+ USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */\r
+ USB_DESC_BYTE (0x06), /* bDescriptorSubtype (Union\r
+ Functional Descriptor). */\r
+ USB_DESC_BYTE (0x00), /* bMasterInterface (Communication\r
+ Class Interface). */\r
+ USB_DESC_BYTE (0x01), /* bSlaveInterface0 (Data Class\r
+ Interface). */\r
+ /* Endpoint 2 Descriptor.*/\r
+ USB_DESC_ENDPOINT (USBD1_INTERRUPT_REQUEST_EP|0x80,\r
+ 0x03, /* bmAttributes (Interrupt). */\r
+ 0x0008, /* wMaxPacketSize. */\r
+ 0xFF), /* bInterval. */\r
+ /* Interface Descriptor.*/\r
+ USB_DESC_INTERFACE (0x01, /* bInterfaceNumber. */\r
+ 0x00, /* bAlternateSetting. */\r
+ 0x02, /* bNumEndpoints. */\r
+ 0x0A, /* bInterfaceClass (Data Class\r
+ Interface, CDC section 4.5). */\r
+ 0x00, /* bInterfaceSubClass (CDC section\r
+ 4.6). */\r
+ 0x00, /* bInterfaceProtocol (CDC section\r
+ 4.7). */\r
+ 0x00), /* iInterface. */\r
+ /* Endpoint 3 Descriptor.*/\r
+ USB_DESC_ENDPOINT (USBD1_DATA_AVAILABLE_EP, /* bEndpointAddress.*/\r
+ 0x02, /* bmAttributes (Bulk). */\r
+ 0x0040, /* wMaxPacketSize. */\r
+ 0x00), /* bInterval. */\r
+ /* Endpoint 1 Descriptor.*/\r
+ USB_DESC_ENDPOINT (USBD1_DATA_REQUEST_EP|0x80, /* bEndpointAddress.*/\r
+ 0x02, /* bmAttributes (Bulk). */\r
+ 0x0040, /* wMaxPacketSize. */\r
+ 0x00) /* bInterval. */\r
+};\r
+\r
+/*\r
+ * Configuration Descriptor wrapper.\r
+ */\r
+static const USBDescriptor vcom_configuration_descriptor = {\r
+ sizeof vcom_configuration_descriptor_data,\r
+ vcom_configuration_descriptor_data\r
+};\r
+\r
+/*\r
+ * U.S. English language identifier.\r
+ */\r
+static const uint8_t vcom_string0[] = {\r
+ USB_DESC_BYTE(4), /* bLength. */\r
+ USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */\r
+ USB_DESC_WORD(0x0409) /* wLANGID (U.S. English). */\r
+};\r
+\r
+/*\r
+ * Vendor string.\r
+ */\r
+static const uint8_t vcom_string1[] = {\r
+ USB_DESC_BYTE(38), /* bLength. */\r
+ USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */\r
+ 'S', 0, 'T', 0, 'M', 0, 'i', 0, 'c', 0, 'r', 0, 'o', 0, 'e', 0,\r
+ 'l', 0, 'e', 0, 'c', 0, 't', 0, 'r', 0, 'o', 0, 'n', 0, 'i', 0,\r
+ 'c', 0, 's', 0\r
+};\r
+\r
+/*\r
+ * Device Description string.\r
+ */\r
+static const uint8_t vcom_string2[] = {\r
+ USB_DESC_BYTE(56), /* bLength. */\r
+ USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */\r
+ 'C', 0, 'h', 0, 'i', 0, 'b', 0, 'i', 0, 'O', 0, 'S', 0, '/', 0,\r
+ 'R', 0, 'T', 0, ' ', 0, 'V', 0, 'i', 0, 'r', 0, 't', 0, 'u', 0,\r
+ 'a', 0, 'l', 0, ' ', 0, 'C', 0, 'O', 0, 'M', 0, ' ', 0, 'P', 0,\r
+ 'o', 0, 'r', 0, 't', 0\r
+};\r
+\r
+/*\r
+ * Serial Number string.\r
+ */\r
+static const uint8_t vcom_string3[] = {\r
+ USB_DESC_BYTE(8), /* bLength. */\r
+ USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */\r
+ '0' + CH_KERNEL_MAJOR, 0,\r
+ '0' + CH_KERNEL_MINOR, 0,\r
+ '0' + CH_KERNEL_PATCH, 0\r
+};\r
+\r
+/*\r
+ * Strings wrappers array.\r
+ */\r
+static const USBDescriptor vcom_strings[] = {\r
+ {sizeof vcom_string0, vcom_string0},\r
+ {sizeof vcom_string1, vcom_string1},\r
+ {sizeof vcom_string2, vcom_string2},\r
+ {sizeof vcom_string3, vcom_string3}\r
+};\r
+\r
+/*\r
+ * Handles the GET_DESCRIPTOR callback. All required descriptors must be\r
+ * handled here.\r
+ */\r
+static const USBDescriptor *get_descriptor(USBDriver *usbp,\r
+ uint8_t dtype,\r
+ uint8_t dindex,\r
+ uint16_t lang) {\r
+\r
+ (void)usbp;\r
+ (void)lang;\r
+ switch (dtype) {\r
+ case USB_DESCRIPTOR_DEVICE:\r
+ return &vcom_device_descriptor;\r
+ case USB_DESCRIPTOR_CONFIGURATION:\r
+ return &vcom_configuration_descriptor;\r
+ case USB_DESCRIPTOR_STRING:\r
+ if (dindex < 4)\r
+ return &vcom_strings[dindex];\r
+ }\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ * @brief IN EP1 state.\r
+ */\r
+static USBInEndpointState ep1instate;\r
+\r
+/**\r
+ * @brief OUT EP1 state.\r
+ */\r
+static USBOutEndpointState ep1outstate;\r
+\r
+/**\r
+ * @brief EP1 initialization structure (both IN and OUT).\r
+ */\r
+static const USBEndpointConfig ep1config = {\r
+ USB_EP_MODE_TYPE_BULK,\r
+ NULL,\r
+ sduDataTransmitted,\r
+ sduDataReceived,\r
+ 0x0040,\r
+ 0x0040,\r
+ &ep1instate,\r
+ &ep1outstate,\r
+ 1,\r
+ NULL\r
+};\r
+\r
+/**\r
+ * @brief IN EP2 state.\r
+ */\r
+static USBInEndpointState ep2instate;\r
+\r
+/**\r
+ * @brief EP2 initialization structure (IN only).\r
+ */\r
+static const USBEndpointConfig ep2config = {\r
+ USB_EP_MODE_TYPE_INTR,\r
+ NULL,\r
+ sduInterruptTransmitted,\r
+ NULL,\r
+ 0x0010,\r
+ 0x0000,\r
+ &ep2instate,\r
+ NULL,\r
+ 1,\r
+ NULL\r
+};\r
+\r
+/*\r
+ * Handles the USB driver global events.\r
+ */\r
+static void usb_event(USBDriver *usbp, usbevent_t event) {\r
+ extern SerialUSBDriver SDU1;\r
+\r
+ switch (event) {\r
+ case USB_EVENT_ADDRESS:\r
+ return;\r
+ case USB_EVENT_CONFIGURED:\r
+ chSysLockFromISR();\r
+\r
+ /* Enables the endpoints specified into the configuration.\r
+ Note, this callback is invoked from an ISR so I-Class functions\r
+ must be used.*/\r
+ usbInitEndpointI(usbp, USBD1_DATA_REQUEST_EP, &ep1config);\r
+ usbInitEndpointI(usbp, USBD1_INTERRUPT_REQUEST_EP, &ep2config);\r
+\r
+ /* Resetting the state of the CDC subsystem.*/\r
+ sduConfigureHookI(&SDU1);\r
+\r
+ chSysUnlockFromISR();\r
+ return;\r
+ case USB_EVENT_RESET:\r
+ /* Falls into.*/\r
+ case USB_EVENT_UNCONFIGURED:\r
+ /* Falls into.*/\r
+ case USB_EVENT_SUSPEND:\r
+ chSysLockFromISR();\r
+\r
+ /* Disconnection event on suspend.*/\r
+ sduSuspendHookI(&SDU1);\r
+\r
+ chSysUnlockFromISR();\r
+ return;\r
+ case USB_EVENT_WAKEUP:\r
+ chSysLockFromISR();\r
+\r
+ /* Connection event on wakeup.*/\r
+ sduWakeupHookI(&SDU1);\r
+\r
+ chSysUnlockFromISR();\r
+ return;\r
+ case USB_EVENT_STALLED:\r
+ return;\r
+ }\r
+ return;\r
+}\r
+\r
+/*\r
+ * Handles the USB driver global events.\r
+ */\r
+static void sof_handler(USBDriver *usbp) {\r
+\r
+ (void)usbp;\r
+\r
+ osalSysLockFromISR();\r
+ sduSOFHookI(&SDU1);\r
+ osalSysUnlockFromISR();\r
+}\r
+\r
+/*\r
+ * USB driver configuration.\r
+ */\r
+const USBConfig usbcfg = {\r
+ usb_event,\r
+ get_descriptor,\r
+ sduRequestsHook,\r
+ sof_handler\r
+};\r
+\r
+/*\r
+ * Serial over USB driver configuration.\r
+ */\r
+const SerialUSBConfig serusbcfg = {\r
+#if defined(TARGET_PLATFORM_H7)\r
+ &USBD2,\r
+#else\r
+ &USBD1,\r
+#endif\r
+ USBD1_DATA_REQUEST_EP,\r
+ USBD1_DATA_AVAILABLE_EP,\r
+ USBD1_INTERRUPT_REQUEST_EP\r
+};\r
--- /dev/null
+/*\r
+ ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\r
+\r
+ Licensed under the Apache License, Version 2.0 (the "License");\r
+ you may not use this file except in compliance with the License.\r
+ You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+*/\r
+\r
+#ifndef USBCFG_H\r
+#define USBCFG_H\r
+\r
+#include "hal.h"\r
+\r
+extern const USBConfig usbcfg;\r
+extern SerialUSBConfig serusbcfg;\r
+extern SerialUSBDriver SDU1;\r
+\r
+#endif /* USBCFG_H */\r
+\r
+/** @} */\r
--- /dev/null
+/**
+ * @file usbserial.cpp
+ * @brief Wrapper for ChibiOS's SerialUSBDriver.
+ *
+ * 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 "usbserial.hpp"
+
+SerialUSBDriver *USBSerial::m_driver = &SDU1;
+
+void USBSerial::begin()
+{
+ palSetPadMode(GPIOA, 11, PAL_MODE_ALTERNATE(10));
+ palSetPadMode(GPIOA, 12, PAL_MODE_ALTERNATE(10));
+
+ sduObjectInit(m_driver);
+ sduStart(m_driver, &serusbcfg);
+
+ // Reconnect bus so device can re-enumerate on reset
+ usbDisconnectBus(serusbcfg.usbp);
+ chThdSleepMilliseconds(1500);
+ usbStart(serusbcfg.usbp, &usbcfg);
+ usbConnectBus(serusbcfg.usbp);
+}
+
+bool USBSerial::isActive()
+{
+ if (auto config = m_driver->config; config != nullptr) {
+ if (auto usbp = config->usbp; usbp != nullptr)
+ return usbp->state == USB_ACTIVE && !ibqIsEmptyI(&m_driver->ibqueue);
+ }
+
+ return false;
+}
+
+size_t USBSerial::read(unsigned char *buffer, size_t count)
+{
+ auto bss = reinterpret_cast<BaseSequentialStream *>(m_driver);
+ return streamRead(bss, buffer, count);
+}
+
+size_t USBSerial::write(const unsigned char *buffer, size_t count)
+{
+ auto bss = reinterpret_cast<BaseSequentialStream *>(m_driver);
+ return streamWrite(bss, buffer, count);
+}
+
--- /dev/null
+/**
+ * @file usbserial.hpp
+ * @brief Wrapper for ChibiOS's SerialUSBDriver.
+ *
+ * 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_USBSERIAL_HPP_
+#define STMDSP_USBSERIAL_HPP_
+
+#include "usbcfg.h"
+
+class USBSerial
+{
+public:
+ static void begin();
+
+ static bool isActive();
+
+ static size_t read(unsigned char *buffer, size_t count);
+ static size_t write(const unsigned char *buffer, size_t count);
+
+private:
+ static SerialUSBDriver *m_driver;
+};
+
+#endif // STMDSP_USBSERIAL_HPP_
+
--- /dev/null
+// Run status
+//
+enum class RunStatus : char
+{
+ Idle = '1',
+ Running,
+ Recovering
+};
+
+extern RunStatus run_status;
+
--- /dev/null
+/**
+ * @file samples.cpp
+ * @brief Provides sample buffers for inputs and outputs.
+ *
+ * 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 "samples.hpp"
+
+#include "ch.h"
+#include "hal.h"
+
+#include <cstdint>
+
+static_assert(sizeof(adcsample_t) == sizeof(uint16_t));
+static_assert(sizeof(dacsample_t) == sizeof(uint16_t));
+
+#if defined(TARGET_PLATFORM_H7)
+__attribute__((section(".convdata")))
+SampleBuffer Samples::In (reinterpret_cast<Sample *>(0x38000000)); // 16k
+__attribute__((section(".convdata")))
+SampleBuffer Samples::Out (reinterpret_cast<Sample *>(0x30004000)); // 16k
+SampleBuffer Samples::SigGen (reinterpret_cast<Sample *>(0x30000000)); // 16k
+#else
+__attribute__((section(".convdata")))
+SampleBuffer Samples::In (reinterpret_cast<Sample *>(0x20008000)); // 16k
+__attribute__((section(".convdata")))
+SampleBuffer Samples::Out (reinterpret_cast<Sample *>(0x2000C000)); // 16k
+SampleBuffer Samples::Generator (reinterpret_cast<Sample *>(0x20010000)); // 16k
+#endif
+
--- /dev/null
+/**
+ * @file samples.hpp
+ * @brief Provides sample buffers for inputs and outputs.
+ *
+ * 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_SAMPLES_HPP
+#define STMDSP_SAMPLES_HPP
+
+#include "samplebuffer.hpp"
+
+class Samples
+{
+public:
+ static SampleBuffer In;
+ static SampleBuffer Out;
+ static SampleBuffer Generator;
+};
+
+#endif // STMDSP_SAMPLES_HPP
+
+++ /dev/null
-/*\r
- ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\r
-\r
- Licensed under the Apache License, Version 2.0 (the "License");\r
- you may not use this file except in compliance with the License.\r
- You may obtain a copy of the License at\r
-\r
- http://www.apache.org/licenses/LICENSE-2.0\r
-\r
- Unless required by applicable law or agreed to in writing, software\r
- distributed under the License is distributed on an "AS IS" BASIS,\r
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- See the License for the specific language governing permissions and\r
- limitations under the License.\r
-*/\r
-\r
-#include "hal.h"\r
-\r
-/* Virtual serial port over USB.*/\r
-SerialUSBDriver SDU1;\r
-\r
-/*\r
- * Endpoints to be used for USBD1.\r
- */\r
-#define USBD1_DATA_REQUEST_EP 1\r
-#define USBD1_DATA_AVAILABLE_EP 1\r
-#define USBD1_INTERRUPT_REQUEST_EP 2\r
-\r
-/*\r
- * USB Device Descriptor.\r
- */\r
-static const uint8_t vcom_device_descriptor_data[18] = {\r
- USB_DESC_DEVICE (0x0110, /* bcdUSB (1.1). */\r
- 0x02, /* bDeviceClass (CDC). */\r
- 0x00, /* bDeviceSubClass. */\r
- 0x00, /* bDeviceProtocol. */\r
- 0x40, /* bMaxPacketSize. */\r
- 0x0483, /* idVendor (ST). */\r
- 0x5740, /* idProduct. */\r
- 0x0200, /* bcdDevice. */\r
- 1, /* iManufacturer. */\r
- 2, /* iProduct. */\r
- 3, /* iSerialNumber. */\r
- 1) /* bNumConfigurations. */\r
-};\r
-\r
-/*\r
- * Device Descriptor wrapper.\r
- */\r
-static const USBDescriptor vcom_device_descriptor = {\r
- sizeof vcom_device_descriptor_data,\r
- vcom_device_descriptor_data\r
-};\r
-\r
-/* Configuration Descriptor tree for a CDC.*/\r
-static const uint8_t vcom_configuration_descriptor_data[67] = {\r
- /* Configuration Descriptor.*/\r
- USB_DESC_CONFIGURATION(67, /* wTotalLength. */\r
- 0x02, /* bNumInterfaces. */\r
- 0x01, /* bConfigurationValue. */\r
- 0, /* iConfiguration. */\r
- 0xC0, /* bmAttributes (self powered). */\r
- 50), /* bMaxPower (100mA). */\r
- /* Interface Descriptor.*/\r
- USB_DESC_INTERFACE (0x00, /* bInterfaceNumber. */\r
- 0x00, /* bAlternateSetting. */\r
- 0x01, /* bNumEndpoints. */\r
- 0x02, /* bInterfaceClass (Communications\r
- Interface Class, CDC section\r
- 4.2). */\r
- 0x02, /* bInterfaceSubClass (Abstract\r
- Control Model, CDC section 4.3). */\r
- 0x01, /* bInterfaceProtocol (AT commands,\r
- CDC section 4.4). */\r
- 0), /* iInterface. */\r
- /* Header Functional Descriptor (CDC section 5.2.3).*/\r
- USB_DESC_BYTE (5), /* bLength. */\r
- USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */\r
- USB_DESC_BYTE (0x00), /* bDescriptorSubtype (Header\r
- Functional Descriptor. */\r
- USB_DESC_BCD (0x0110), /* bcdCDC. */\r
- /* Call Management Functional Descriptor. */\r
- USB_DESC_BYTE (5), /* bFunctionLength. */\r
- USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */\r
- USB_DESC_BYTE (0x01), /* bDescriptorSubtype (Call Management\r
- Functional Descriptor). */\r
- USB_DESC_BYTE (0x00), /* bmCapabilities (D0+D1). */\r
- USB_DESC_BYTE (0x01), /* bDataInterface. */\r
- /* ACM Functional Descriptor.*/\r
- USB_DESC_BYTE (4), /* bFunctionLength. */\r
- USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */\r
- USB_DESC_BYTE (0x02), /* bDescriptorSubtype (Abstract\r
- Control Management Descriptor). */\r
- USB_DESC_BYTE (0x02), /* bmCapabilities. */\r
- /* Union Functional Descriptor.*/\r
- USB_DESC_BYTE (5), /* bFunctionLength. */\r
- USB_DESC_BYTE (0x24), /* bDescriptorType (CS_INTERFACE). */\r
- USB_DESC_BYTE (0x06), /* bDescriptorSubtype (Union\r
- Functional Descriptor). */\r
- USB_DESC_BYTE (0x00), /* bMasterInterface (Communication\r
- Class Interface). */\r
- USB_DESC_BYTE (0x01), /* bSlaveInterface0 (Data Class\r
- Interface). */\r
- /* Endpoint 2 Descriptor.*/\r
- USB_DESC_ENDPOINT (USBD1_INTERRUPT_REQUEST_EP|0x80,\r
- 0x03, /* bmAttributes (Interrupt). */\r
- 0x0008, /* wMaxPacketSize. */\r
- 0xFF), /* bInterval. */\r
- /* Interface Descriptor.*/\r
- USB_DESC_INTERFACE (0x01, /* bInterfaceNumber. */\r
- 0x00, /* bAlternateSetting. */\r
- 0x02, /* bNumEndpoints. */\r
- 0x0A, /* bInterfaceClass (Data Class\r
- Interface, CDC section 4.5). */\r
- 0x00, /* bInterfaceSubClass (CDC section\r
- 4.6). */\r
- 0x00, /* bInterfaceProtocol (CDC section\r
- 4.7). */\r
- 0x00), /* iInterface. */\r
- /* Endpoint 3 Descriptor.*/\r
- USB_DESC_ENDPOINT (USBD1_DATA_AVAILABLE_EP, /* bEndpointAddress.*/\r
- 0x02, /* bmAttributes (Bulk). */\r
- 0x0040, /* wMaxPacketSize. */\r
- 0x00), /* bInterval. */\r
- /* Endpoint 1 Descriptor.*/\r
- USB_DESC_ENDPOINT (USBD1_DATA_REQUEST_EP|0x80, /* bEndpointAddress.*/\r
- 0x02, /* bmAttributes (Bulk). */\r
- 0x0040, /* wMaxPacketSize. */\r
- 0x00) /* bInterval. */\r
-};\r
-\r
-/*\r
- * Configuration Descriptor wrapper.\r
- */\r
-static const USBDescriptor vcom_configuration_descriptor = {\r
- sizeof vcom_configuration_descriptor_data,\r
- vcom_configuration_descriptor_data\r
-};\r
-\r
-/*\r
- * U.S. English language identifier.\r
- */\r
-static const uint8_t vcom_string0[] = {\r
- USB_DESC_BYTE(4), /* bLength. */\r
- USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */\r
- USB_DESC_WORD(0x0409) /* wLANGID (U.S. English). */\r
-};\r
-\r
-/*\r
- * Vendor string.\r
- */\r
-static const uint8_t vcom_string1[] = {\r
- USB_DESC_BYTE(38), /* bLength. */\r
- USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */\r
- 'S', 0, 'T', 0, 'M', 0, 'i', 0, 'c', 0, 'r', 0, 'o', 0, 'e', 0,\r
- 'l', 0, 'e', 0, 'c', 0, 't', 0, 'r', 0, 'o', 0, 'n', 0, 'i', 0,\r
- 'c', 0, 's', 0\r
-};\r
-\r
-/*\r
- * Device Description string.\r
- */\r
-static const uint8_t vcom_string2[] = {\r
- USB_DESC_BYTE(56), /* bLength. */\r
- USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */\r
- 'C', 0, 'h', 0, 'i', 0, 'b', 0, 'i', 0, 'O', 0, 'S', 0, '/', 0,\r
- 'R', 0, 'T', 0, ' ', 0, 'V', 0, 'i', 0, 'r', 0, 't', 0, 'u', 0,\r
- 'a', 0, 'l', 0, ' ', 0, 'C', 0, 'O', 0, 'M', 0, ' ', 0, 'P', 0,\r
- 'o', 0, 'r', 0, 't', 0\r
-};\r
-\r
-/*\r
- * Serial Number string.\r
- */\r
-static const uint8_t vcom_string3[] = {\r
- USB_DESC_BYTE(8), /* bLength. */\r
- USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */\r
- '0' + CH_KERNEL_MAJOR, 0,\r
- '0' + CH_KERNEL_MINOR, 0,\r
- '0' + CH_KERNEL_PATCH, 0\r
-};\r
-\r
-/*\r
- * Strings wrappers array.\r
- */\r
-static const USBDescriptor vcom_strings[] = {\r
- {sizeof vcom_string0, vcom_string0},\r
- {sizeof vcom_string1, vcom_string1},\r
- {sizeof vcom_string2, vcom_string2},\r
- {sizeof vcom_string3, vcom_string3}\r
-};\r
-\r
-/*\r
- * Handles the GET_DESCRIPTOR callback. All required descriptors must be\r
- * handled here.\r
- */\r
-static const USBDescriptor *get_descriptor(USBDriver *usbp,\r
- uint8_t dtype,\r
- uint8_t dindex,\r
- uint16_t lang) {\r
-\r
- (void)usbp;\r
- (void)lang;\r
- switch (dtype) {\r
- case USB_DESCRIPTOR_DEVICE:\r
- return &vcom_device_descriptor;\r
- case USB_DESCRIPTOR_CONFIGURATION:\r
- return &vcom_configuration_descriptor;\r
- case USB_DESCRIPTOR_STRING:\r
- if (dindex < 4)\r
- return &vcom_strings[dindex];\r
- }\r
- return NULL;\r
-}\r
-\r
-/**\r
- * @brief IN EP1 state.\r
- */\r
-static USBInEndpointState ep1instate;\r
-\r
-/**\r
- * @brief OUT EP1 state.\r
- */\r
-static USBOutEndpointState ep1outstate;\r
-\r
-/**\r
- * @brief EP1 initialization structure (both IN and OUT).\r
- */\r
-static const USBEndpointConfig ep1config = {\r
- USB_EP_MODE_TYPE_BULK,\r
- NULL,\r
- sduDataTransmitted,\r
- sduDataReceived,\r
- 0x0040,\r
- 0x0040,\r
- &ep1instate,\r
- &ep1outstate,\r
- 1,\r
- NULL\r
-};\r
-\r
-/**\r
- * @brief IN EP2 state.\r
- */\r
-static USBInEndpointState ep2instate;\r
-\r
-/**\r
- * @brief EP2 initialization structure (IN only).\r
- */\r
-static const USBEndpointConfig ep2config = {\r
- USB_EP_MODE_TYPE_INTR,\r
- NULL,\r
- sduInterruptTransmitted,\r
- NULL,\r
- 0x0010,\r
- 0x0000,\r
- &ep2instate,\r
- NULL,\r
- 1,\r
- NULL\r
-};\r
-\r
-/*\r
- * Handles the USB driver global events.\r
- */\r
-static void usb_event(USBDriver *usbp, usbevent_t event) {\r
- extern SerialUSBDriver SDU1;\r
-\r
- switch (event) {\r
- case USB_EVENT_ADDRESS:\r
- return;\r
- case USB_EVENT_CONFIGURED:\r
- chSysLockFromISR();\r
-\r
- /* Enables the endpoints specified into the configuration.\r
- Note, this callback is invoked from an ISR so I-Class functions\r
- must be used.*/\r
- usbInitEndpointI(usbp, USBD1_DATA_REQUEST_EP, &ep1config);\r
- usbInitEndpointI(usbp, USBD1_INTERRUPT_REQUEST_EP, &ep2config);\r
-\r
- /* Resetting the state of the CDC subsystem.*/\r
- sduConfigureHookI(&SDU1);\r
-\r
- chSysUnlockFromISR();\r
- return;\r
- case USB_EVENT_RESET:\r
- /* Falls into.*/\r
- case USB_EVENT_UNCONFIGURED:\r
- /* Falls into.*/\r
- case USB_EVENT_SUSPEND:\r
- chSysLockFromISR();\r
-\r
- /* Disconnection event on suspend.*/\r
- sduSuspendHookI(&SDU1);\r
-\r
- chSysUnlockFromISR();\r
- return;\r
- case USB_EVENT_WAKEUP:\r
- chSysLockFromISR();\r
-\r
- /* Connection event on wakeup.*/\r
- sduWakeupHookI(&SDU1);\r
-\r
- chSysUnlockFromISR();\r
- return;\r
- case USB_EVENT_STALLED:\r
- return;\r
- }\r
- return;\r
-}\r
-\r
-/*\r
- * Handles the USB driver global events.\r
- */\r
-static void sof_handler(USBDriver *usbp) {\r
-\r
- (void)usbp;\r
-\r
- osalSysLockFromISR();\r
- sduSOFHookI(&SDU1);\r
- osalSysUnlockFromISR();\r
-}\r
-\r
-/*\r
- * USB driver configuration.\r
- */\r
-const USBConfig usbcfg = {\r
- usb_event,\r
- get_descriptor,\r
- sduRequestsHook,\r
- sof_handler\r
-};\r
-\r
-/*\r
- * Serial over USB driver configuration.\r
- */\r
-const SerialUSBConfig serusbcfg = {\r
-#if defined(TARGET_PLATFORM_H7)\r
- &USBD2,\r
-#else\r
- &USBD1,\r
-#endif\r
- USBD1_DATA_REQUEST_EP,\r
- USBD1_DATA_AVAILABLE_EP,\r
- USBD1_INTERRUPT_REQUEST_EP\r
-};\r
+++ /dev/null
-/*\r
- ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\r
-\r
- Licensed under the Apache License, Version 2.0 (the "License");\r
- you may not use this file except in compliance with the License.\r
- You may obtain a copy of the License at\r
-\r
- http://www.apache.org/licenses/LICENSE-2.0\r
-\r
- Unless required by applicable law or agreed to in writing, software\r
- distributed under the License is distributed on an "AS IS" BASIS,\r
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- See the License for the specific language governing permissions and\r
- limitations under the License.\r
-*/\r
-\r
-#ifndef USBCFG_H\r
-#define USBCFG_H\r
-\r
-#include "hal.h"\r
-\r
-extern const USBConfig usbcfg;\r
-extern SerialUSBConfig serusbcfg;\r
-extern SerialUSBDriver SDU1;\r
-\r
-#endif /* USBCFG_H */\r
-\r
-/** @} */\r
+++ /dev/null
-/**
- * @file usbserial.cpp
- * @brief Wrapper for ChibiOS's SerialUSBDriver.
- *
- * 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 "usbserial.hpp"
-
-SerialUSBDriver *USBSerial::m_driver = &SDU1;
-
-void USBSerial::begin()
-{
- palSetPadMode(GPIOA, 11, PAL_MODE_ALTERNATE(10));
- palSetPadMode(GPIOA, 12, PAL_MODE_ALTERNATE(10));
-
- sduObjectInit(m_driver);
- sduStart(m_driver, &serusbcfg);
-
- // Reconnect bus so device can re-enumerate on reset
- usbDisconnectBus(serusbcfg.usbp);
- chThdSleepMilliseconds(1500);
- usbStart(serusbcfg.usbp, &usbcfg);
- usbConnectBus(serusbcfg.usbp);
-}
-
-bool USBSerial::isActive()
-{
- if (auto config = m_driver->config; config != nullptr) {
- if (auto usbp = config->usbp; usbp != nullptr)
- return usbp->state == USB_ACTIVE && !ibqIsEmptyI(&m_driver->ibqueue);
- }
-
- return false;
-}
-
-size_t USBSerial::read(unsigned char *buffer, size_t count)
-{
- auto bss = reinterpret_cast<BaseSequentialStream *>(m_driver);
- return streamRead(bss, buffer, count);
-}
-
-size_t USBSerial::write(const unsigned char *buffer, size_t count)
-{
- auto bss = reinterpret_cast<BaseSequentialStream *>(m_driver);
- return streamWrite(bss, buffer, count);
-}
-
+++ /dev/null
-/**
- * @file usbserial.hpp
- * @brief Wrapper for ChibiOS's SerialUSBDriver.
- *
- * 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_USBSERIAL_HPP_
-#define STMDSP_USBSERIAL_HPP_
-
-#include "usbcfg.h"
-
-class USBSerial
-{
-public:
- static void begin();
-
- static bool isActive();
-
- static size_t read(unsigned char *buffer, size_t count);
- static size_t write(const unsigned char *buffer, size_t count);
-
-private:
- static SerialUSBDriver *m_driver;
-};
-
-#endif // STMDSP_USBSERIAL_HPP_
-