/** * @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 . */ #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 "usbserial.hpp" #include 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(0x38000000)); static SampleBuffer samplesOut (reinterpret_cast(0x38002000)); #ifdef ENABLE_SIGGEN static SampleBuffer samplesSigGen; #endif 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(); //SCB_DisableDCache(); // Enable FPU SCB->CPACR |= 0xF << 20; ADC::begin(); DAC::begin(); USBSerial::begin(); // Start the conversion manager thread 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; #ifdef ENABLE_SIGGEN 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; #endif // '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("stmdsp"), 6); break; // 'I' - Sends the current run status. case 'I': { unsigned char buf[2] = { static_cast(run_status), static_cast(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(&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 = static_cast(ADC::getRate()); USBSerial::write(&r, 1); } else { ADC::setRate(static_cast(cmd[1])); } } 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(samplesOut.size() / 2 & 0xFF), static_cast(((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(samps) + offset, 512); while (USBSerial::read(&unused, 1) == 0); offset += 512; total -= 512; } USBSerial::write(reinterpret_cast(samps) + offset, total); while (USBSerial::read(&unused, 1) == 0); } else { USBSerial::write(reinterpret_cast("\0\0"), 2); } break; #ifdef ENABLE_SIGGEN case 'W': DAC::start(1, samplesSigGen.data(), samplesSigGen.size()); break; case 'w': DAC::stop(1); break; #endif 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; while (1) { palSetLine(LINE_LED1); chThdSleepMilliseconds(70); palSetLine(LINE_LED2); chThdSleepMilliseconds(70); palSetLine(LINE_LED3); chThdSleepMilliseconds(240); palClearLine(LINE_LED1); chThdSleepMilliseconds(70); palClearLine(LINE_LED2); chThdSleepMilliseconds(70); palClearLine(LINE_LED3); chThdSleepMilliseconds(240); } } 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"