DRAFT R2ROutput

This commit is contained in:
pschatzmann 2024-08-24 09:07:49 +02:00
parent 9538017752
commit 734b4354b8
2 changed files with 141 additions and 0 deletions

136
src/AudioLibs/R2ROutput.h Normal file
View File

@ -0,0 +1,136 @@
#pragma once
#include "AudioConfig.h"
#include "AudioTimer/AudioTimer.h"
#include "AudioTools/AudioLogger.h"
#include "AudioTools/AudioOutput.h"
#include "AudioTools/Buffers.h"
namespace audio_tools {
/**
* @brief R2R configuration
* @author Phil Schatzmann
* @copyright GPLv3
*/
class R2RConfig : public AudioInfo {
public:
Vector<int> channel1_pins{0};
Vector<int> channel2_pins{0};
};
/**
* @brief DRAFT implementation for Output to R2R DAC
* @ingroup io
* @author Phil Schatzmann
* @copyright GPLv3
*/
class R2ROutput : public AudioOutput {
public:
R2RConfig defaultConfig() {
R2RConfig r;
return r;
}
bool begin(R2RConfig c) {
cfg = c;
rcfg = c;
return begin();
}
bool begin() override {
if (cfg.channels == 0 || cfg.channels > 2) {
LOGE("channels is %d", cfg.channels);
return false;
}
if (rcfg.channel1_pins.size() == 0) {
LOGE("channel1_pins not defined");
return false;
}
if (cfg.channels == 2 &&
rcfg.channel2_pins.size() != rcfg.channel1_pins.size()) {
LOGE("channel2_pins not defined");
return false;
}
setupPins();
timer.setCallbackParameter(this);
return timer.begin(r2r_timer_callback, cfg.sample_rate, HZ);
}
size_t write(const uint8_t *data, size_t len) override {
size_t result = buffer.writeArray(data, len);
// activate output when buffer is full
if (!is_active && buffer.isFull()) {
is_active = true;
}
return result;
}
protected:
TimerAlarmRepeating timer;
NBuffer<uint8_t> buffer{1024, 2};
R2RConfig rcfg;
void setupPins() {
for (int j = 0; j < rcfg.channel1_pins.size(); j++) {
pinMode(rcfg.channel1_pins[j], OUTPUT);
}
for (int j = 0; j < rcfg.channel2_pins.size(); j++) {
pinMode(rcfg.channel2_pins[j], OUTPUT);
}
}
void writeValue(int channel) {
switch (cfg.bits_per_sample) {
case 8:
return writeValueT<int8_t>(channel);
case 16:
return writeValueT<int16_t>(channel);
case 24:
return writeValueT<int24_t>(channel);
case 32:
return writeValueT<int32_t>(channel);
}
}
template <typename T>
void writeValueT(int channel) {
// don't do anything if we do not have enough data
if (buffer.available()< sizeof(T)) return;
// get next value from buffer
T value = 0;
buffer.readArray((uint8_t *)&value, sizeof(T));
// convert to unsigned
unsigned uvalue = (int)value + NumberConverter::maxValueT<T>() + 1;
// scale value
uvalue = uvalue >> ((sizeof(T) * 8) - rcfg.channel1_pins.size());
// output pins
switch (channel) {
case 0:
for (int j = 0; j < rcfg.channel1_pins.size(); j++) {
digitalWrite(rcfg.channel1_pins[j], (uvalue >> j) & 1);
}
break;
case 1:
for (int j = 0; j < rcfg.channel2_pins.size(); j++) {
digitalWrite(rcfg.channel2_pins[j], (uvalue >> j) & 1);
}
break;
}
}
static void r2r_timer_callback(void *ptr) {
R2ROutput *self = (R2ROutput *)ptr;
if (self->is_active) {
// output channel 1
self->writeValue(0);
// output channel 2
if (self->cfg.channels == 2) self->writeValue(1);
};
}
};
} // namespace audio_tools

View File

@ -59,6 +59,11 @@ class TimerAlarmRepeatingDriverSTM32 : public TimerAlarmRepeatingDriverBase {
case US:
timer->setOverflow(time, MICROSEC_FORMAT); // 10 Hz
break;
case HZ:
// convert hz to time in us
uint64_t time_us = AudioTime::toTimeUs(time);
timer->setOverflow(time_us, MICROSEC_FORMAT); // 10 Hz
break;
}
timer->resume();
return true;