-/**\r
- * @file main.cpp\r
- * @brief Program entry point.\r
- *\r
- * Copyright (C) 2020 Clyne Sullivan\r
- *\r
- * Distributed under the GNU GPL v3 or later. You should have received a copy of\r
- * the GNU General Public License along with this program.\r
- * If not, see <https://www.gnu.org/licenses/>.\r
- */\r
-\r
-#include "ch.h"\r
-#include "hal.h"\r
-\r
-static_assert(sizeof(adcsample_t) == sizeof(uint16_t));\r
-static_assert(sizeof(dacsample_t) == sizeof(uint16_t));\r
-\r
-#include "common.hpp"\r
-#include "error.hpp"\r
-\r
-#include "adc.hpp"\r
-#include "dac.hpp"\r
-#include "elf_load.hpp"\r
-#include "sclock.hpp"\r
-#include "usbserial.hpp"\r
-\r
-#include <array>\r
-\r
-constexpr unsigned int MAX_ELF_FILE_SIZE = 8 * 1024;\r
-\r
-enum class RunStatus : char\r
-{\r
- Idle = '1',\r
- Running\r
-};\r
-static RunStatus run_status = RunStatus::Idle;\r
-\r
-#define MSG_CONVFIRST (1)\r
-#define MSG_CONVSECOND (2)\r
-#define MSG_CONVFIRST_MEASURE (3)\r
-#define MSG_CONVSECOND_MEASURE (4)\r
-\r
-#define MSG_FOR_FIRST(m) (m & 1)\r
-#define MSG_FOR_MEASURE(m) (m > 2)\r
-\r
-static msg_t conversionMBBuffer[4];\r
-static MAILBOX_DECL(conversionMB, conversionMBBuffer, 4);\r
-\r
-static THD_WORKING_AREA(conversionThreadWA, 2048);\r
-static THD_FUNCTION(conversionThread, arg);\r
-\r
-static time_measurement_t conversion_time_measurement;\r
-\r
-static ErrorManager EM;\r
-\r
-static SampleBuffer samplesIn (reinterpret_cast<Sample *>(0x38000000));\r
-static SampleBuffer samplesOut (reinterpret_cast<Sample *>(0x38002000));\r
-#ifdef ENABLE_SIGGEN\r
-static SampleBuffer samplesSigGen;\r
-#endif\r
-\r
-static unsigned char elf_file_store[MAX_ELF_FILE_SIZE];\r
-static ELF::Entry elf_entry = nullptr;\r
-\r
-static void signal_operate(adcsample_t *buffer, size_t count);\r
-static void signal_operate_measure(adcsample_t *buffer, size_t count);\r
-static void main_loop();\r
-\r
-static THD_WORKING_AREA(waThread1, 128);\r
-static THD_FUNCTION(Thread1, arg);\r
-\r
-int main()\r
-{\r
- // Initialize the RTOS\r
- halInit();\r
- chSysInit();\r
-\r
- //SCB_DisableDCache();\r
-\r
- // Enable FPU\r
- SCB->CPACR |= 0xF << 20;\r
-\r
- ADC::begin();\r
- DAC::begin();\r
- SClock::begin();\r
- USBSerial::begin();\r
-\r
- SClock::setRate(SClock::Rate::R32K);\r
- ADC::setRate(SClock::Rate::R32K);\r
-\r
- // Start the conversion manager thread\r
- chTMObjectInit(&conversion_time_measurement);\r
- chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1,\r
- nullptr);\r
- chThdCreateStatic(conversionThreadWA, sizeof(conversionThreadWA),\r
- NORMALPRIO, conversionThread, nullptr);\r
-\r
- main_loop();\r
-}\r
-\r
-void main_loop()\r
-{\r
-\r
- while (1) {\r
- if (USBSerial::isActive()) {\r
- // Attempt to receive a command packet\r
- if (unsigned char cmd[3]; USBSerial::read(&cmd[0], 1) > 0) {\r
- // Packet received, first byte represents the desired command/action\r
- switch (cmd[0]) {\r
-\r
- case 'a':\r
- USBSerial::write(samplesIn.bytedata(), samplesIn.bytesize());\r
- break;\r
- case 'A':\r
- USBSerial::read(samplesIn.bytedata(), samplesIn.bytesize());\r
- break;\r
-\r
- case 'B':\r
- if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle) &&\r
- EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize))\r
- {\r
- unsigned int count = (cmd[1] | (cmd[2] << 8)) * 2;\r
- if (EM.assert(count <= MAX_SAMPLE_BUFFER_SIZE, Error::BadParam)) {\r
- samplesIn.setSize(count);\r
- samplesOut.setSize(count);\r
- }\r
- }\r
- break;\r
-\r
- case 'd':\r
- USBSerial::write(samplesOut.bytedata(), samplesOut.bytesize());\r
- break;\r
-#ifdef ENABLE_SIGGEN\r
- case 'D':\r
- if (EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize)) {\r
- unsigned int count = cmd[1] | (cmd[2] << 8);\r
- if (EM.assert(count <= MAX_SAMPLE_BUFFER_SIZE, Error::BadParam)) {\r
- samplesSigGen.setSize(count);\r
- USBSerial::read(samplesSigGen.bytedata(), samplesSigGen.bytesize());\r
- }\r
- }\r
- break;\r
-#endif\r
-\r
- // 'E' - Reads in and loads the compiled conversion code binary from USB.\r
- case 'E':\r
- if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle) &&\r
- EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize))\r
- {\r
- // Only load the binary if it can fit in the memory reserved for it.\r
- unsigned int size = cmd[1] | (cmd[2] << 8);\r
- if (EM.assert(size < sizeof(elf_file_store), Error::BadUserCodeSize)) {\r
- USBSerial::read(elf_file_store, size);\r
- elf_entry = ELF::load(elf_file_store);\r
-\r
- EM.assert(elf_entry != nullptr, Error::BadUserCodeLoad);\r
- }\r
- }\r
- break;\r
-\r
- // 'e' - Unloads the currently loaded conversion code\r
- case 'e':\r
- elf_entry = nullptr;\r
- break;\r
-\r
- // 'i' - Sends an identifying string to confirm that this is the stmdsp device.\r
- case 'i':\r
- USBSerial::write(reinterpret_cast<const uint8_t *>("stmdsp"), 6);\r
- break;\r
-\r
- // 'I' - Sends the current run status.\r
- case 'I':\r
- {\r
- unsigned char buf[2] = {\r
- static_cast<unsigned char>(run_status),\r
- static_cast<unsigned char>(EM.pop())\r
- };\r
- USBSerial::write(buf, sizeof(buf));\r
- }\r
- break;\r
-\r
- // 'M' - Begins continuous sampling, but measures the execution time of the first\r
- // sample processing. This duration can be later read through 'm'.\r
- case 'M':\r
- if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle)) {\r
- run_status = RunStatus::Running;\r
- samplesOut.clear();\r
- ADC::start(samplesIn.data(), samplesIn.size(), signal_operate_measure);\r
- DAC::start(0, samplesOut.data(), samplesOut.size());\r
- }\r
- break;\r
-\r
- // 'm' - Returns the last measured sample processing time, presumably in processor\r
- // ticks.\r
- case 'm':\r
- USBSerial::write(reinterpret_cast<uint8_t *>(&conversion_time_measurement.last),\r
- sizeof(rtcnt_t));\r
- break;\r
-\r
- // 'R' - Begin continuous sampling/conversion of the ADC. Samples will go through\r
- // the conversion code, and will be sent out over the DAC.\r
- case 'R':\r
- if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle)) {\r
- run_status = RunStatus::Running;\r
- samplesOut.clear();\r
- ADC::start(samplesIn.data(), samplesIn.size(), signal_operate);\r
- DAC::start(0, samplesOut.data(), samplesOut.size());\r
- }\r
- break;\r
-\r
- case 'r':\r
- if (EM.assert(USBSerial::read(&cmd[1], 1) == 1, Error::BadParamSize)) {\r
- if (cmd[1] == 0xFF) {\r
- unsigned char r = SClock::getRate();\r
- USBSerial::write(&r, 1);\r
- } else {\r
- auto r = static_cast<SClock::Rate>(cmd[1]);\r
- SClock::setRate(r);\r
- ADC::setRate(r);\r
- }\r
- }\r
- break;\r
-\r
- // 'S' - Stops the continuous sampling/conversion.\r
- case 'S':\r
- if (run_status == RunStatus::Running) {\r
- DAC::stop(0);\r
- ADC::stop();\r
- run_status = RunStatus::Idle;\r
- }\r
- break;\r
-\r
- case 's':\r
- if (auto samps = samplesOut.modified(); samps != nullptr) {\r
- unsigned char buf[2] = {\r
- static_cast<unsigned char>(samplesOut.size() / 2 & 0xFF),\r
- static_cast<unsigned char>(((samplesOut.size() / 2) >> 8) & 0xFF)\r
- };\r
- USBSerial::write(buf, 2);\r
- unsigned int total = samplesOut.bytesize() / 2;\r
- unsigned int offset = 0;\r
- unsigned char unused;\r
- while (total > 512) {\r
- USBSerial::write(reinterpret_cast<uint8_t *>(samps) + offset, 512);\r
- while (USBSerial::read(&unused, 1) == 0);\r
- offset += 512;\r
- total -= 512;\r
- }\r
- USBSerial::write(reinterpret_cast<uint8_t *>(samps) + offset, total);\r
- while (USBSerial::read(&unused, 1) == 0);\r
- } else {\r
- USBSerial::write(reinterpret_cast<const uint8_t *>("\0\0"), 2);\r
- }\r
- break;\r
-\r
-#ifdef ENABLE_SIGGEN\r
- case 'W':\r
- DAC::start(1, samplesSigGen.data(), samplesSigGen.size());\r
- break;\r
- case 'w':\r
- DAC::stop(1);\r
- break;\r
-#endif\r
-\r
- default:\r
- break;\r
- }\r
- }\r
- }\r
-\r
- chThdSleepMicroseconds(100);\r
- }\r
-}\r
-\r
-void conversion_abort()\r
-{\r
- elf_entry = nullptr;\r
- DAC::stop(0);\r
- ADC::stop();\r
- EM.add(Error::ConversionAborted);\r
-}\r
-\r
-THD_FUNCTION(conversionThread, arg)\r
-{\r
- (void)arg;\r
-\r
- while (1) {\r
- msg_t message;\r
- if (chMBFetchTimeout(&conversionMB, &message, TIME_INFINITE) == MSG_OK) {\r
- auto samples = MSG_FOR_FIRST(message) ? samplesIn.data() : samplesIn.middata();\r
- auto size = samplesIn.size() / 2;\r
-\r
- if (elf_entry) {\r
- if (!MSG_FOR_MEASURE(message)) {\r
- samples = elf_entry(samples, size);\r
- } else {\r
- chTMStartMeasurementX(&conversion_time_measurement);\r
- samples = elf_entry(samples, size);\r
- chTMStopMeasurementX(&conversion_time_measurement);\r
- } \r
- }\r
-\r
- if (MSG_FOR_FIRST(message))\r
- samplesOut.modify(samples, size); \r
- else\r
- samplesOut.midmodify(samples, size); \r
- }\r
- }\r
-}\r
-\r
-void signal_operate(adcsample_t *buffer, size_t)\r
-{\r
- chSysLockFromISR();\r
-\r
- if (chMBGetUsedCountI(&conversionMB) > 1) {\r
- chSysUnlockFromISR();\r
- conversion_abort();\r
- } else {\r
- chMBPostI(&conversionMB, buffer == samplesIn.data() ? MSG_CONVFIRST : MSG_CONVSECOND);\r
- chSysUnlockFromISR();\r
- }\r
-}\r
-\r
-void signal_operate_measure(adcsample_t *buffer, [[maybe_unused]] size_t count)\r
-{\r
- chSysLockFromISR();\r
- chMBPostI(&conversionMB, buffer == samplesIn.data() ? MSG_CONVFIRST_MEASURE : MSG_CONVSECOND_MEASURE);\r
- chSysUnlockFromISR();\r
-\r
- ADC::setOperation(signal_operate);\r
-}\r
-\r
-THD_FUNCTION(Thread1, arg)\r
-{\r
- (void)arg;\r
- while (1) {\r
- palSetLine(LINE_LED1);\r
- chThdSleepMilliseconds(70);\r
- palSetLine(LINE_LED2);\r
- chThdSleepMilliseconds(70);\r
- palSetLine(LINE_LED3);\r
- chThdSleepMilliseconds(240);\r
- palClearLine(LINE_LED1);\r
- chThdSleepMilliseconds(70);\r
- palClearLine(LINE_LED2);\r
- chThdSleepMilliseconds(70);\r
- palClearLine(LINE_LED3);\r
- chThdSleepMilliseconds(240);\r
- }\r
-}\r
-\r
-extern "C" {\r
-\r
-__attribute__((naked))\r
-void HardFault_Handler()\r
-{\r
- while (1);\r
-// //asm("push {lr}");\r
-//\r
-// uint32_t *stack;\r
-// uint32_t lr;\r
-// asm("\\r
-// tst lr, #4; \\r
-// ite eq; \\r
-// mrseq %0, msp; \\r
-// mrsne %0, psp; \\r
-// mov %1, lr; \\r
-// " : "=r" (stack), "=r" (lr));\r
-// //stack++;\r
-// stack[7] |= (1 << 24); // Keep Thumb mode enabled\r
-//\r
-// conversion_abort();\r
-//\r
-// // TODO test lr and decide how to recover\r
-//\r
-// //if (run_status == RunStatus::Converting) {\r
-// stack[6] = stack[5]; // Escape from elf_entry code\r
-// //} else /*if (run_status == RunStatus::Recovered)*/ {\r
-// // stack[6] = (uint32_t)main_loop & ~1; // Return to safety\r
-// //}\r
-//\r
-// //asm("pop {lr}; bx lr");\r
-// asm("bx lr");\r
-}\r
-\r
-} // extern "C"\r
-\r
+/**
+ * @file main.cpp
+ * @brief Program entry point.
+ *
+ * Copyright (C) 2020 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 "ch.h"
+#include "hal.h"
+
+static_assert(sizeof(adcsample_t) == sizeof(uint16_t));
+static_assert(sizeof(dacsample_t) == sizeof(uint16_t));
+
+#include "common.hpp"
+#include "error.hpp"
+
+#include "adc.hpp"
+#include "dac.hpp"
+#include "elf_load.hpp"
+#include "sclock.hpp"
+#include "usbserial.hpp"
+
+#include <array>
+
+constexpr unsigned int MAX_ELF_FILE_SIZE = 8 * 1024;
+
+enum class RunStatus : char
+{
+ Idle = '1',
+ Running
+};
+static RunStatus run_status = RunStatus::Idle;
+
+#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[4];
+static MAILBOX_DECL(conversionMB, conversionMBBuffer, 4);
+
+static THD_WORKING_AREA(conversionThreadWA, 2048);
+static THD_FUNCTION(conversionThread, arg);
+
+static time_measurement_t conversion_time_measurement;
+
+static ErrorManager EM;
+
+static SampleBuffer samplesIn (reinterpret_cast<Sample *>(0x38000000)); // 16k
+static SampleBuffer samplesOut (reinterpret_cast<Sample *>(0x30004000)); // 16k
+static SampleBuffer samplesSigGen (reinterpret_cast<Sample *>(0x30000000)); // 16k
+
+static unsigned char elf_file_store[MAX_ELF_FILE_SIZE];
+static ELF::Entry elf_entry = nullptr;
+
+static void signal_operate(adcsample_t *buffer, size_t count);
+static void signal_operate_measure(adcsample_t *buffer, size_t count);
+static void main_loop();
+
+static THD_WORKING_AREA(waThread1, 128);
+static THD_FUNCTION(Thread1, arg);
+
+int main()
+{
+ // Initialize the RTOS
+ halInit();
+ chSysInit();
+
+ // Enable FPU
+ SCB->CPACR |= 0xF << 20;
+
+ ADC::begin();
+ DAC::begin();
+ SClock::begin();
+ USBSerial::begin();
+
+ SClock::setRate(SClock::Rate::R32K);
+ ADC::setRate(SClock::Rate::R32K);
+
+ chTMObjectInit(&conversion_time_measurement);
+ chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, nullptr);
+ chThdCreateStatic(conversionThreadWA, sizeof(conversionThreadWA),
+ NORMALPRIO, conversionThread, nullptr);
+
+ main_loop();
+}
+
+void main_loop()
+{
+
+ 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]) {
+
+ 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))
+ {
+ 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)) {
+ samplesSigGen.setSize(count);
+ USBSerial::read(samplesSigGen.bytedata(), samplesSigGen.bytesize());
+ }
+ }
+ 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':
+ USBSerial::write(reinterpret_cast<const uint8_t *>("stmdsp"), 6);
+ 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(), signal_operate_measure);
+ 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(), signal_operate);
+ 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 'W':
+ DAC::start(1, samplesSigGen.data(), samplesSigGen.size());
+ break;
+ case 'w':
+ DAC::stop(1);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ chThdSleepMicroseconds(100);
+ }
+}
+
+void conversion_abort()
+{
+ elf_entry = nullptr;
+ DAC::stop(0);
+ ADC::stop();
+ EM.add(Error::ConversionAborted);
+}
+
+THD_FUNCTION(conversionThread, arg)
+{
+ (void)arg;
+
+ while (1) {
+ msg_t message;
+ if (chMBFetchTimeout(&conversionMB, &message, TIME_INFINITE) == MSG_OK) {
+ auto samples = MSG_FOR_FIRST(message) ? samplesIn.data() : samplesIn.middata();
+ auto size = samplesIn.size() / 2;
+
+ if (elf_entry) {
+ if (!MSG_FOR_MEASURE(message)) {
+ samples = elf_entry(samples, size);
+ } else {
+ chTMStartMeasurementX(&conversion_time_measurement);
+ samples = elf_entry(samples, size);
+ chTMStopMeasurementX(&conversion_time_measurement);
+ }
+ }
+
+ if (MSG_FOR_FIRST(message))
+ samplesOut.modify(samples, size);
+ else
+ samplesOut.midmodify(samples, size);
+ }
+ }
+}
+
+void signal_operate(adcsample_t *buffer, size_t)
+{
+ chSysLockFromISR();
+
+ if (chMBGetUsedCountI(&conversionMB) > 1) {
+ chSysUnlockFromISR();
+ conversion_abort();
+ } else {
+ chMBPostI(&conversionMB, buffer == samplesIn.data() ? MSG_CONVFIRST : MSG_CONVSECOND);
+ chSysUnlockFromISR();
+ }
+}
+
+void signal_operate_measure(adcsample_t *buffer, [[maybe_unused]] size_t count)
+{
+ chSysLockFromISR();
+ chMBPostI(&conversionMB, buffer == samplesIn.data() ? MSG_CONVFIRST_MEASURE : MSG_CONVSECOND_MEASURE);
+ chSysUnlockFromISR();
+
+ ADC::setOperation(signal_operate);
+}
+
+THD_FUNCTION(Thread1, arg)
+{
+ (void)arg;
+
+ bool erroron = false;
+ 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 (auto err = EM.hasError(); err ^ erroron) {
+ erroron = err;
+ if (err)
+ palSetLine(LINE_LED_RED);
+ else
+ palClearLine(LINE_LED_RED);
+ }
+ }
+}
+
+extern "C" {
+
+__attribute__((naked))
+void HardFault_Handler()
+{
+ while (1);
+// //asm("push {lr}");
+//
+// uint32_t *stack;
+// uint32_t lr;
+// asm("\
+// tst lr, #4; \
+// ite eq; \
+// mrseq %0, msp; \
+// mrsne %0, psp; \
+// mov %1, lr; \
+// " : "=r" (stack), "=r" (lr));
+// //stack++;
+// stack[7] |= (1 << 24); // Keep Thumb mode enabled
+//
+// conversion_abort();
+//
+// // TODO test lr and decide how to recover
+//
+// //if (run_status == RunStatus::Converting) {
+// stack[6] = stack[5]; // Escape from elf_entry code
+// //} else /*if (run_status == RunStatus::Recovered)*/ {
+// // stack[6] = (uint32_t)main_loop & ~1; // Return to safety
+// //}
+//
+// //asm("pop {lr}; bx lr");
+// asm("bx lr");
+}
+
+} // extern "C"
+