kopia lustrzana https://github.com/markondej/fm_transmitter
Fixed ARM64 bug, when not using DMA
rodzic
019f39c220
commit
ddfcf6a53c
|
@ -36,7 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
#include "mailbox.h"
|
#include "mailbox.hpp"
|
||||||
|
|
||||||
#define PAGE_SIZE (4*1024)
|
#define PAGE_SIZE (4*1024)
|
||||||
|
|
||||||
|
|
159
transmitter.cpp
159
transmitter.cpp
|
@ -51,7 +51,6 @@
|
||||||
#define BCM2711_PLLD_FREQ 750
|
#define BCM2711_PLLD_FREQ 750
|
||||||
|
|
||||||
#define GPIO_BASE_OFFSET 0x00200000
|
#define GPIO_BASE_OFFSET 0x00200000
|
||||||
#define TIMER_BASE_OFFSET 0x00003000
|
|
||||||
|
|
||||||
#define CLK0_BASE_OFFSET 0x00101070
|
#define CLK0_BASE_OFFSET 0x00101070
|
||||||
#define CLK1_BASE_OFFSET 0x00101078
|
#define CLK1_BASE_OFFSET 0x00101078
|
||||||
|
@ -100,16 +99,6 @@
|
||||||
#define BUFFER_TIME 1000000
|
#define BUFFER_TIME 1000000
|
||||||
#define PAGE_SIZE 4096
|
#define PAGE_SIZE 4096
|
||||||
|
|
||||||
struct TimerRegisters {
|
|
||||||
uint32_t ctlStatus;
|
|
||||||
uint32_t low;
|
|
||||||
uint32_t high;
|
|
||||||
uint32_t c0;
|
|
||||||
uint32_t c1;
|
|
||||||
uint32_t c2;
|
|
||||||
uint32_t c3;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ClockRegisters {
|
struct ClockRegisters {
|
||||||
uint32_t ctl;
|
uint32_t ctl;
|
||||||
uint32_t div;
|
uint32_t div;
|
||||||
|
@ -359,6 +348,10 @@ Transmitter::Transmitter()
|
||||||
}
|
}
|
||||||
|
|
||||||
Transmitter::~Transmitter() {
|
Transmitter::~Transmitter() {
|
||||||
|
std::unique_lock<std::mutex> lock(mtx);
|
||||||
|
cv.wait(lock, [&]() -> bool {
|
||||||
|
return !txThread.joinable() && !enable;
|
||||||
|
});
|
||||||
if (output != nullptr) {
|
if (output != nullptr) {
|
||||||
delete output;
|
delete output;
|
||||||
}
|
}
|
||||||
|
@ -386,6 +379,11 @@ void Transmitter::Transmit(WaveReader &reader, float frequency, float bandwidth,
|
||||||
delete output;
|
delete output;
|
||||||
output = nullptr;
|
output = nullptr;
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mtx);
|
||||||
|
enable = false;
|
||||||
|
}
|
||||||
|
cv.notify_all();
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
if (dmaChannel != 0xff) {
|
if (dmaChannel != 0xff) {
|
||||||
|
@ -408,66 +406,6 @@ void Transmitter::Stop()
|
||||||
cv.notify_all();
|
cv.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Transmitter::TransmitViaCpu(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange)
|
|
||||||
{
|
|
||||||
std::vector<Sample> samples;
|
|
||||||
unsigned sampleOffset = 0;
|
|
||||||
|
|
||||||
bool eof = false, stop = false, start = true;
|
|
||||||
|
|
||||||
std::thread transmitterThread(Transmitter::TransmitterThread, this, &output, sampleRate, clockDivisor, divisorRange, &sampleOffset, &samples, &stop);
|
|
||||||
|
|
||||||
auto finally = [&]() {
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mtx);
|
|
||||||
stop = true;
|
|
||||||
cv.notify_one();
|
|
||||||
}
|
|
||||||
transmitterThread.join();
|
|
||||||
samples.clear();
|
|
||||||
enable = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
while (!eof) {
|
|
||||||
std::unique_lock<std::mutex> lock(mtx);
|
|
||||||
if (!start) {
|
|
||||||
cv.wait(lock, [&]() -> bool {
|
|
||||||
return samples.empty() || !enable || stop;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
start = false;
|
|
||||||
}
|
|
||||||
if (!enable) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (stop) {
|
|
||||||
throw std::runtime_error("Transmitter thread has unexpectedly exited");
|
|
||||||
}
|
|
||||||
if (samples.empty()) {
|
|
||||||
if (!reader.SetSampleOffset(sampleOffset + bufferSize)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
lock.unlock();
|
|
||||||
samples = reader.GetSamples(bufferSize, enable, mtx);
|
|
||||||
lock.lock();
|
|
||||||
if (samples.empty()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
eof = samples.size() < bufferSize;
|
|
||||||
lock.unlock();
|
|
||||||
cv.notify_one();
|
|
||||||
} else {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (...) {
|
|
||||||
finally();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
finally();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transmitter::TransmitViaDma(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange, unsigned dmaChannel)
|
void Transmitter::TransmitViaDma(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange, unsigned dmaChannel)
|
||||||
{
|
{
|
||||||
if (dmaChannel > 15) {
|
if (dmaChannel > 15) {
|
||||||
|
@ -526,8 +464,6 @@ void Transmitter::TransmitViaDma(WaveReader &reader, ClockOutput &output, unsign
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
}
|
}
|
||||||
samples.clear();
|
samples.clear();
|
||||||
std::lock_guard<std::mutex> lock(mtx);
|
|
||||||
enable = false;
|
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
while (!eof) {
|
while (!eof) {
|
||||||
|
@ -559,14 +495,69 @@ void Transmitter::TransmitViaDma(WaveReader &reader, ClockOutput &output, unsign
|
||||||
finally();
|
finally();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Transmitter::TransmitViaCpu(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange)
|
||||||
|
{
|
||||||
|
std::vector<Sample> samples;
|
||||||
|
unsigned sampleOffset = 0;
|
||||||
|
|
||||||
|
bool eof = false, stop = false, start = true;
|
||||||
|
|
||||||
|
txThread = std::thread(Transmitter::TransmitterThread, this, &output, sampleRate, clockDivisor, divisorRange, &sampleOffset, &samples, &stop);
|
||||||
|
|
||||||
|
auto finally = [&]() {
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mtx);
|
||||||
|
stop = true;
|
||||||
|
}
|
||||||
|
cv.notify_all();
|
||||||
|
txThread.join();
|
||||||
|
samples.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (!eof) {
|
||||||
|
std::unique_lock<std::mutex> lock(mtx);
|
||||||
|
if (!start) {
|
||||||
|
cv.wait(lock, [&]() -> bool {
|
||||||
|
return samples.empty() || !enable || stop;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!enable) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (stop) {
|
||||||
|
throw std::runtime_error("Transmitter thread has unexpectedly exited");
|
||||||
|
}
|
||||||
|
if (samples.empty()) {
|
||||||
|
if (!reader.SetSampleOffset(sampleOffset + (start ? 0 : bufferSize))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lock.unlock();
|
||||||
|
samples = reader.GetSamples(bufferSize, enable, mtx);
|
||||||
|
lock.lock();
|
||||||
|
if (samples.empty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
eof = samples.size() < bufferSize;
|
||||||
|
lock.unlock();
|
||||||
|
cv.notify_all();
|
||||||
|
} else {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
start = false;
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
finally();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally();
|
||||||
|
}
|
||||||
|
|
||||||
void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output, unsigned sampleRate, unsigned clockDivisor, unsigned divisorRange, unsigned *sampleOffset, std::vector<Sample> *samples, bool *stop)
|
void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output, unsigned sampleRate, unsigned clockDivisor, unsigned divisorRange, unsigned *sampleOffset, std::vector<Sample> *samples, bool *stop)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
Peripherals &peripherals = Peripherals::GetInstance();
|
auto playbackStart = std::chrono::system_clock::now();
|
||||||
|
std::chrono::system_clock::time_point current, start;
|
||||||
volatile TimerRegisters *timer = reinterpret_cast<TimerRegisters *>(peripherals.GetVirtualAddress(TIMER_BASE_OFFSET));
|
|
||||||
uint64_t current = *(reinterpret_cast<volatile uint64_t *>(&timer->low));
|
|
||||||
uint64_t playbackStart = current, start = current;
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
std::vector<Sample> loadedSamples;
|
std::vector<Sample> loadedSamples;
|
||||||
|
@ -578,11 +569,11 @@ void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output,
|
||||||
if (*stop) {
|
if (*stop) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
start = current = *(reinterpret_cast<volatile uint64_t *>(&timer->low));
|
start = current = std::chrono::system_clock::now();
|
||||||
*sampleOffset = (current - playbackStart) * sampleRate / 1000000;
|
*sampleOffset = std::chrono::duration_cast<std::chrono::microseconds>(current - playbackStart).count() * sampleRate / 1000000;
|
||||||
loadedSamples = std::move(*samples);
|
loadedSamples = std::move(*samples);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
instance->cv.notify_one();
|
instance->cv.notify_all();
|
||||||
|
|
||||||
unsigned offset = 0;
|
unsigned offset = 0;
|
||||||
|
|
||||||
|
@ -595,8 +586,8 @@ void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output,
|
||||||
instance->output->SetDivisor(clockDivisor - static_cast<int>(round(value * divisorRange)));
|
instance->output->SetDivisor(clockDivisor - static_cast<int>(round(value * divisorRange)));
|
||||||
while (offset == prevOffset) {
|
while (offset == prevOffset) {
|
||||||
std::this_thread::yield(); // asm("nop");
|
std::this_thread::yield(); // asm("nop");
|
||||||
current = *(reinterpret_cast<volatile uint64_t *>(&timer->low));;
|
current = std::chrono::system_clock::now();
|
||||||
offset = (current - start) * sampleRate / 1000000;
|
offset = std::chrono::duration_cast<std::chrono::microseconds>(current - start).count() * sampleRate / 1000000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -604,6 +595,6 @@ void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output,
|
||||||
std::unique_lock<std::mutex> lock(instance->mtx);
|
std::unique_lock<std::mutex> lock(instance->mtx);
|
||||||
*stop = true;
|
*stop = true;
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
instance->cv.notify_one();
|
instance->cv.notify_all();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
#include "wave_reader.hpp"
|
#include "wave_reader.hpp"
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
class ClockOutput;
|
class ClockOutput;
|
||||||
|
|
||||||
|
@ -54,6 +55,7 @@ class Transmitter
|
||||||
static void TransmitterThread(Transmitter *instance, ClockOutput *output, unsigned sampleRate, unsigned clockDivisor, unsigned divisorRange, unsigned *sampleOffset, std::vector<Sample> *samples, bool *stop);
|
static void TransmitterThread(Transmitter *instance, ClockOutput *output, unsigned sampleRate, unsigned clockDivisor, unsigned divisorRange, unsigned *sampleOffset, std::vector<Sample> *samples, bool *stop);
|
||||||
|
|
||||||
std::condition_variable cv;
|
std::condition_variable cv;
|
||||||
|
std::thread txThread;
|
||||||
ClockOutput *output;
|
ClockOutput *output;
|
||||||
std::mutex mtx;
|
std::mutex mtx;
|
||||||
bool enable;
|
bool enable;
|
||||||
|
|
|
@ -171,6 +171,10 @@ std::vector<uint8_t> WaveReader::ReadData(unsigned bytesToRead, bool headerBytes
|
||||||
unsigned bytesRead = 0;
|
unsigned bytesRead = 0;
|
||||||
std::vector<uint8_t> data;
|
std::vector<uint8_t> data;
|
||||||
data.resize(bytesToRead);
|
data.resize(bytesToRead);
|
||||||
|
timeval timeout = {
|
||||||
|
.tv_sec = 1,
|
||||||
|
};
|
||||||
|
fd_set fds;
|
||||||
while (bytesRead < bytesToRead) {
|
while (bytesRead < bytesToRead) {
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mtx);
|
std::lock_guard<std::mutex> lock(mtx);
|
||||||
|
@ -191,7 +195,12 @@ std::vector<uint8_t> WaveReader::ReadData(unsigned bytesToRead, bool headerBytes
|
||||||
data.resize(bytesRead);
|
data.resize(bytesRead);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(STDIN_FILENO, &fds);
|
||||||
|
select(STDIN_FILENO + 1, &fds, nullptr, nullptr, &timeout);
|
||||||
|
if (FD_ISSET(STDIN_FILENO, &fds)) {
|
||||||
|
FD_CLR(STDIN_FILENO, &fds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue