2nd pot; some doc; smooth WAV playback

master
Clyne 3 years ago
parent d2f50fb925
commit f1773b634e

@ -49,16 +49,16 @@ const char *makefile_text_l4 =
#endif
"arm-none-eabi-g++ -x c++ -Os -std=c++20 -fno-exceptions -fno-rtti "
"-mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mtune=cortex-m4 "
"-nostartfiles -I$1/cmsis"
"-nostartfiles -I$1/cmsis "
"-Wl,-Ttext-segment=0x10000000 -Wl,-zmax-page-size=512 -Wl,-eprocess_data_entry "
"$0 -o $0.o" NEWLINE
COPY " $0.o $0.orig.o" NEWLINE
"arm-none-eabi-strip -s -S --strip-unneeded $0.o" NEWLINE
"arm-none-eabi-objcopy --remove-section .ARM.attributes "
COPY " $0.o $0.orig.o" NEWLINE
"arm-none-eabi-strip -s -S --strip-unneeded $0.o" NEWLINE
"arm-none-eabi-objcopy --remove-section .ARM.attributes "
"--remove-section .comment "
"--remove-section .noinit "
"$0.o" NEWLINE
"arm-none-eabi-size $0.o" NEWLINE;
"arm-none-eabi-size $0.o" NEWLINE;
// $0 = buffer size
const char *file_header_h7 = R"cpp(
@ -168,11 +168,21 @@ asm("vsqrt.f32 s0, s0; bx lr");
return 0;
}
auto readalt() {
auto readpot1() {
Sample s;
asm("push {r4-r6}; svc 3; mov %0, r0; pop {r4-r6}" : "=&r"(s));
asm("push {r4-r6}; eor r0, r0; svc 3; mov %0, r0; pop {r4-r6}" : "=&r"(s));
return s;
}
auto readpot2() {
Sample s;
asm("push {r4-r6}; mov r0, #1; svc 3; mov %0, r0; pop {r4-r6}" : "=&r"(s));
return s;
}
void puts(const char *s) {
// 's' will already be in r0.
asm("push {r4-r6}; svc 4; pop {r4-r6}");
}
// End stmdspgui header code

@ -48,18 +48,16 @@ void MainFrame::onRunStart(wxCommandEvent& ce)
m_device->continuous_start_measure();
m_timer_performance->StartOnce(1000);
} else {
if (m_device->is_siggening() && m_wav_clip) {
// TODO Confirm need for factor of 500
m_timer_wavclip->Start(m_device->get_buffer_size() * 500 /
srateNums[m_rate_select->GetSelection()]);
} else if (m_conv_result_log) {
m_timer_record->Start(m_device->get_buffer_size() /
srateNums[m_rate_select->GetSelection()] *
800 / 1000);
} else if (m_run_draw_samples->IsChecked()) {
m_timer_record->Start(m_device->get_buffer_size() /
srateNums[m_rate_select->GetSelection()]);
}
auto reqSpeedExact =
m_device->get_buffer_size()
/ static_cast<float>(srateNums[m_rate_select->GetSelection()])
* 1000.f * 0.5f;
int reqSpeed = reqSpeedExact;
if (m_device->is_siggening() && m_wav_clip)
m_timer_wavclip->Start(reqSpeed);
if (m_conv_result_log || m_run_draw_samples->IsChecked())
m_timer_record->Start(reqSpeed);
m_device->continuous_start();
}

@ -2,7 +2,7 @@
* @file adc.cpp
* @brief Manages signal reading through the ADC.
*
* Copyright (C) 2020 Clyne Sullivan
* 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.
@ -69,7 +69,7 @@ static void readAltCallback(ADCDriver *)
}
ADCConversionGroup ADC::m_group_config2 = {
.circular = false,
.num_channels = 1,
.num_channels = 2,
.end_cb = readAltCallback,
.error_cb = nullptr,
.cfgr = ADC_CFGR_EXTEN_RISING | ADC_CFGR_EXTSEL_SRC(13), /* TIM6_TRGO */
@ -88,10 +88,10 @@ ADCConversionGroup ADC::m_group_config2 = {
.awd3cr = 0,
#endif
.smpr = {
ADC_SMPR1_SMP_AN1(ADC_SMPR_SMP_12P5), 0
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_SQ1_N(ADC_CHANNEL_IN1) | ADC_SQR1_SQ2_N(ADC_CHANNEL_IN2),
0, 0, 0
},
};
@ -107,6 +107,7 @@ void ADC::begin()
#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);
@ -135,15 +136,15 @@ void ADC::stop()
adcsample_t ADC::readAlt(unsigned int id)
{
if (id != 0)
if (id > 1)
return 0;
static adcsample_t result[32] = {};
static adcsample_t result[16] = {};
readAltDone = false;
adcStartConversion(m_driver2, &m_group_config2, result, 32);
adcStartConversion(m_driver2, &m_group_config2, result, 8);
while (!readAltDone)
;
__WFI();
adcStopConversion(m_driver2);
return result[0];
return result[id];
}
void ADC::setRate(SClock::Rate rate)

@ -2,7 +2,7 @@
* @file adc.hpp
* @brief Manages signal reading through the ADC.
*
* Copyright (C) 2020 Clyne Sullivan
* 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.

@ -2,7 +2,7 @@
* @file dac.cpp
* @brief Manages signal creation using the DAC.
*
* Copyright (C) 2020 Clyne Sullivan
* 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.
@ -22,9 +22,16 @@ const DACConfig DAC::m_config = {
.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 = nullptr,
.end_cb = dacEndCallback,
.error_cb = nullptr,
#if defined(TARGET_PLATFORM_H7)
.trigger = 5 // TIM6_TRGO
@ -45,11 +52,18 @@ void DAC::begin()
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) {

@ -2,7 +2,7 @@
* @file dac.hpp
* @brief Manages signal creation using the DAC.
*
* Copyright (C) 2020 Clyne Sullivan
* 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.
@ -23,6 +23,8 @@ public:
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];

@ -2,7 +2,7 @@
* @file elf_load.cpp
* @brief Loads ELF binary data into memory for execution.
*
* Copyright (C) 2020 Clyne Sullivan
* 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.

@ -2,7 +2,7 @@
* @file elf_load.hpp
* @brief Loads ELF binary data into memory for execution.
*
* Copyright (C) 2020 Clyne Sullivan
* 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.

@ -2,7 +2,7 @@
* @file main.cpp
* @brief Program entry point.
*
* Copyright (C) 2020 Clyne Sullivan
* 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.
@ -26,8 +26,16 @@ static_assert(sizeof(dacsample_t) == sizeof(uint16_t));
#include <array>
constexpr unsigned int MAX_ELF_FILE_SIZE = 16 * 1024;
// 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',
@ -36,6 +44,8 @@ enum class RunStatus : char
};
static RunStatus run_status = RunStatus::Idle;
// Conversion threads messaging
//
#define MSG_CONVFIRST (1)
#define MSG_CONVSECOND (2)
#define MSG_CONVFIRST_MEASURE (3)
@ -44,43 +54,11 @@ static RunStatus run_status = RunStatus::Idle;
#define MSG_FOR_FIRST(m) (m & 1)
#define MSG_FOR_MEASURE(m) (m > 2)
static ErrorManager EM;
static msg_t conversionMBBuffer[2];
static MAILBOX_DECL(conversionMB, conversionMBBuffer, 2);
// Thread for LED status and wakeup hold
__attribute__((section(".stacks")))
static THD_WORKING_AREA(monitorThreadWA, 256);
static THD_FUNCTION(monitorThread, arg);
// Thread for managing the conversion task
__attribute__((section(".stacks")))
static THD_WORKING_AREA(conversionThreadMonitorWA, 1024);
static THD_FUNCTION(conversionThreadMonitor, arg);
static thread_t *conversionThreadHandle = nullptr;
// Thread for unprivileged algorithm execution
__attribute__((section(".stacks")))
static THD_WORKING_AREA(conversionThreadWA, 128); // All we do is enter unprivileged mode.
static THD_FUNCTION(conversionThread, arg);
constexpr unsigned int conversionThreadUPWASize =
#if defined(TARGET_PLATFORM_H7)
62 * 1024;
#else
15 * 1024;
#endif
__attribute__((section(".convdata")))
static THD_WORKING_AREA(conversionThreadUPWA, conversionThreadUPWASize);
__attribute__((section(".convdata")))
static thread_t *conversionThreadMonitorHandle = nullptr;
// Thread for USB monitoring
__attribute__((section(".stacks")))
static THD_WORKING_AREA(communicationThreadWA, 4096);
static THD_FUNCTION(communicationThread, arg);
static time_measurement_t conversion_time_measurement;
// Sample input and output buffers
//
#if defined(TARGET_PLATFORM_H7)
__attribute__((section(".convdata")))
static SampleBuffer samplesIn (reinterpret_cast<Sample *>(0x38000000)); // 16k
@ -95,35 +73,38 @@ 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;
// Other variables
//
static ErrorManager EM;
static time_measurement_t conversion_time_measurement;
static char userMessageBuffer[128];
static unsigned char userMessageSize = 0;
// Functions
//
__attribute__((section(".convcode")))
static void conversion_unprivileged_main();
static void mpu_setup();
static void startThreads();
static void mpuSetup();
static void abortAlgorithmFromISR();
static void signal_operate(adcsample_t *buffer, size_t count);
static void signal_operate_measure(adcsample_t *buffer, size_t count);
#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
static void signalOperate(adcsample_t *buffer, size_t count);
static void signalOperateMeasure(adcsample_t *buffer, size_t count);
int main()
{
// Initialize the RTOS
// Initialize ChibiOS
halInit();
chSysInit();
SCB->CPACR |= 0xF << 20; // Enable FPU
mpu_setup();
mpuSetup();
#if defined(TARGET_PLATFORM_L4)
palSetLineMode(LINE_LED_GREEN, PAL_MODE_OUTPUT_PUSHPULL);
@ -140,7 +121,42 @@ int main()
SClock::setRate(SClock::Rate::R32K);
ADC::setRate(SClock::Rate::R32K);
chTMObjectInit(&conversion_time_measurement);
startThreads();
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,
@ -149,19 +165,17 @@ int main()
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 *>(reinterpret_cast<uint32_t>(conversionThreadUPWA) +
conversionThreadUPWASize));
reinterpret_cast<void *>(conversionThreadUPWAEnd));
chThdCreateStatic(
communicationThreadWA, sizeof(communicationThreadWA),
NORMALPRIO,
communicationThread, nullptr);
chThdExit(0);
return 0;
}
THD_FUNCTION(communicationThread, arg)
@ -190,7 +204,7 @@ THD_FUNCTION(communicationThread, arg)
// 'S' - Stop conversion.
// 's' - Get latest block of conversion results.
// 't' - Get latest block of conversion input.
// 'u' - Get user message.
// 'u' - Get user message.
// 'W' - Start signal generator (siggen).
// 'w' - Stop siggen.
@ -222,8 +236,22 @@ THD_FUNCTION(communicationThread, arg)
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());
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;
@ -275,7 +303,7 @@ THD_FUNCTION(communicationThread, arg)
if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle)) {
run_status = RunStatus::Running;
samplesOut.clear();
ADC::start(samplesIn.data(), samplesIn.size(), signal_operate_measure);
ADC::start(samplesIn.data(), samplesIn.size(), signalOperateMeasure);
DAC::start(0, samplesOut.data(), samplesOut.size());
}
break;
@ -293,7 +321,7 @@ THD_FUNCTION(communicationThread, arg)
if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle)) {
run_status = RunStatus::Running;
samplesOut.clear();
ADC::start(samplesIn.data(), samplesIn.size(), signal_operate);
ADC::start(samplesIn.data(), samplesIn.size(), signalOperate);
DAC::start(0, samplesOut.data(), samplesOut.size());
}
break;
@ -488,7 +516,7 @@ void conversion_unprivileged_main()
}
}
void mpu_setup()
void mpuSetup()
{
// Set up MPU for user algorithm
#if defined(TARGET_PLATFORM_H7)
@ -561,7 +589,7 @@ void abortAlgorithmFromISR()
}
}
void signal_operate(adcsample_t *buffer, size_t)
void signalOperate(adcsample_t *buffer, size_t)
{
chSysLockFromISR();
@ -582,7 +610,7 @@ void signal_operate(adcsample_t *buffer, size_t)
}
}
void signal_operate_measure(adcsample_t *buffer, [[maybe_unused]] size_t count)
void signalOperateMeasure(adcsample_t *buffer, [[maybe_unused]] size_t count)
{
chSysLockFromISR();
if (buffer == samplesIn.data()) {
@ -594,7 +622,7 @@ void signal_operate_measure(adcsample_t *buffer, [[maybe_unused]] size_t count)
}
chSysUnlockFromISR();
ADC::setOperation(signal_operate);
ADC::setOperation(signalOperate);
}
extern "C" {
@ -654,7 +682,7 @@ void port_syscall(struct port_extctx *ctxp, uint32_t n)
}
break;
case 3:
ctxp->r0 = ADC::readAlt(0);
ctxp->r0 = ADC::readAlt(ctxp->r0);
break;
case 4:
{

@ -1,3 +1,14 @@
/**
* @file samplebuffer.cpp
* @brief Manages ADC/DAC buffer data.
*
* 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 "samplebuffer.hpp"
SampleBuffer::SampleBuffer(Sample *buffer) :

@ -1,3 +1,14 @@
/**
* @file samplebuffer.hpp
* @brief Manages ADC/DAC buffer data.
*
* 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 SAMPLEBUFFER_HPP_
#define SAMPLEBUFFER_HPP_

@ -1,3 +1,14 @@
/**
* @file sclock.cpp
* @brief Manages sampling rate clock speeds.
*
* 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 "sclock.hpp"
GPTDriver *SClock::m_timer = &GPTD6;

@ -1,3 +1,14 @@
/**
* @file sclock.hpp
* @brief Manages sampling rate clock speeds.
*
* 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 SCLOCK_HPP_
#define SCLOCK_HPP_

@ -2,7 +2,7 @@
* @file usbserial.cpp
* @brief Wrapper for ChibiOS's SerialUSBDriver.
*
* Copyright (C) 2020 Clyne Sullivan
* 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.
@ -32,7 +32,7 @@ bool USBSerial::isActive()
{
if (auto config = m_driver->config; config != nullptr) {
if (auto usbp = config->usbp; usbp != nullptr)
return usbp->state == USB_ACTIVE;
return usbp->state == USB_ACTIVE && !ibqIsEmptyI(&m_driver->ibqueue);
}
return false;

@ -2,7 +2,7 @@
* @file usbserial.hpp
* @brief Wrapper for ChibiOS's SerialUSBDriver.
*
* Copyright (C) 2020 Clyne Sullivan
* 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.

Loading…
Cancel
Save