kopia lustrzana https://github.com/markondej/fm_transmitter
Using mutex to synchronize threads
Using mutex to synchronize threadspull/91/head
commit
8d44b2f396
|
@ -35,6 +35,7 @@
|
||||||
#include "mailbox.h"
|
#include "mailbox.h"
|
||||||
#include <bcm_host.h>
|
#include <bcm_host.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
@ -120,7 +121,8 @@ struct AllocatedMemory {
|
||||||
bool Transmitter::transmitting = false;
|
bool Transmitter::transmitting = false;
|
||||||
volatile ClockRegisters *Transmitter::output = nullptr;
|
volatile ClockRegisters *Transmitter::output = nullptr;
|
||||||
uint32_t Transmitter::sampleOffset, Transmitter::clockDivisor, Transmitter::divisorRange, Transmitter::sampleRate;
|
uint32_t Transmitter::sampleOffset, Transmitter::clockDivisor, Transmitter::divisorRange, Transmitter::sampleRate;
|
||||||
std::vector<Sample> *Transmitter::loadedSamples;
|
std::vector<Sample> Transmitter::samples;
|
||||||
|
std::mutex Transmitter::samplesMutex;
|
||||||
void *Transmitter::peripherals;
|
void *Transmitter::peripherals;
|
||||||
|
|
||||||
Transmitter::Transmitter()
|
Transmitter::Transmitter()
|
||||||
|
@ -213,16 +215,16 @@ volatile PWMRegisters *Transmitter::initPwmController()
|
||||||
volatile ClockRegisters *pwmClk = reinterpret_cast<ClockRegisters *>(getPeripheralVirtAddress(PWMCLK_BASE_OFFSET));
|
volatile ClockRegisters *pwmClk = reinterpret_cast<ClockRegisters *>(getPeripheralVirtAddress(PWMCLK_BASE_OFFSET));
|
||||||
float pwmClkFreq = PWM_WRITES_PER_SAMPLE * PWM_CHANNEL_RANGE * sampleRate / 1000000;
|
float pwmClkFreq = PWM_WRITES_PER_SAMPLE * PWM_CHANNEL_RANGE * sampleRate / 1000000;
|
||||||
pwmClk->ctl = (0x5A << 24) | 0x06;
|
pwmClk->ctl = (0x5A << 24) | 0x06;
|
||||||
usleep(1000);
|
std::this_thread::sleep_for(std::chrono::microseconds(1000));
|
||||||
pwmClk->div = (0x5A << 24) | static_cast<uint32_t>(getSourceFreq() * (0x01 << 12) / pwmClkFreq);
|
pwmClk->div = (0x5A << 24) | static_cast<uint32_t>(getSourceFreq() * (0x01 << 12) / pwmClkFreq);
|
||||||
pwmClk->ctl = (0x5A << 24) | (0x01 << 4) | 0x06;
|
pwmClk->ctl = (0x5A << 24) | (0x01 << 4) | 0x06;
|
||||||
|
|
||||||
volatile PWMRegisters *pwm = reinterpret_cast<PWMRegisters *>(getPeripheralVirtAddress(PWM_BASE_OFFSET));
|
volatile PWMRegisters *pwm = reinterpret_cast<PWMRegisters *>(getPeripheralVirtAddress(PWM_BASE_OFFSET));
|
||||||
pwm->ctl = 0x00000000;
|
pwm->ctl = 0x00000000;
|
||||||
usleep(1000);
|
std::this_thread::sleep_for(std::chrono::microseconds(1000));
|
||||||
pwm->status = 0x01FC;
|
pwm->status = 0x01FC;
|
||||||
pwm->ctl = (0x01 << 6);
|
pwm->ctl = (0x01 << 6);
|
||||||
usleep(1000);
|
std::this_thread::sleep_for(std::chrono::microseconds(1000));
|
||||||
pwm->chn1Range = PWM_CHANNEL_RANGE;
|
pwm->chn1Range = PWM_CHANNEL_RANGE;
|
||||||
pwm->dmaConf = (0x01 << 31) | 0x0707;
|
pwm->dmaConf = (0x01 << 31) | 0x0707;
|
||||||
pwm->ctl = (0x01 << 5) | (0x01 << 2) | 0x01;
|
pwm->ctl = (0x01 << 5) | (0x01 << 2) | 0x01;
|
||||||
|
@ -238,7 +240,7 @@ volatile DMARegisters *Transmitter::startDma(AllocatedMemory &memory, volatile D
|
||||||
{
|
{
|
||||||
volatile DMARegisters *dma = reinterpret_cast<DMARegisters *>(getPeripheralVirtAddress((dmaChannel < 15) ? DMA0_BASE_OFFSET + dmaChannel * 0x100 : DMA15_BASE_OFFSET));
|
volatile DMARegisters *dma = reinterpret_cast<DMARegisters *>(getPeripheralVirtAddress((dmaChannel < 15) ? DMA0_BASE_OFFSET + dmaChannel * 0x100 : DMA15_BASE_OFFSET));
|
||||||
dma->ctlStatus = (0x01 << 31);
|
dma->ctlStatus = (0x01 << 31);
|
||||||
usleep(1000);
|
std::this_thread::sleep_for(std::chrono::microseconds(1000));
|
||||||
dma->ctlStatus = (0x01 << 2) | (0x01 << 1);
|
dma->ctlStatus = (0x01 << 2) | (0x01 << 1);
|
||||||
dma->cbAddress = getMemoryPhysAddress(memory, dmaCb);
|
dma->cbAddress = getMemoryPhysAddress(memory, dmaCb);
|
||||||
dma->ctlStatus = (0xFF << 16) | 0x01;
|
dma->ctlStatus = (0xFF << 16) | 0x01;
|
||||||
|
@ -255,7 +257,7 @@ volatile ClockRegisters *Transmitter::initClockOutput()
|
||||||
volatile ClockRegisters *clock = reinterpret_cast<ClockRegisters *>(getPeripheralVirtAddress(CLK0_BASE_OFFSET));
|
volatile ClockRegisters *clock = reinterpret_cast<ClockRegisters *>(getPeripheralVirtAddress(CLK0_BASE_OFFSET));
|
||||||
volatile uint32_t *gpio = reinterpret_cast<uint32_t *>(getPeripheralVirtAddress(GPIO_BASE_OFFSET));
|
volatile uint32_t *gpio = reinterpret_cast<uint32_t *>(getPeripheralVirtAddress(GPIO_BASE_OFFSET));
|
||||||
clock->ctl = (0x5A << 24) | 0x06;
|
clock->ctl = (0x5A << 24) | 0x06;
|
||||||
usleep(1000);
|
std::this_thread::sleep_for(std::chrono::microseconds(1000));
|
||||||
clock->div = (0x5A << 24) | clockDivisor;
|
clock->div = (0x5A << 24) | clockDivisor;
|
||||||
clock->ctl = (0x5A << 24) | (0x01 << 9) | (0x01 << 4) | 0x06;
|
clock->ctl = (0x5A << 24) | (0x01 << 9) | (0x01 << 4) | 0x06;
|
||||||
*gpio = (*gpio & 0xFFFF8FFF) | (0x01 << 14);
|
*gpio = (*gpio & 0xFFFF8FFF) | (0x01 << 14);
|
||||||
|
@ -293,7 +295,6 @@ void Transmitter::transmit(WaveReader &reader, float frequency, float bandwidth,
|
||||||
output = nullptr;
|
output = nullptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (dmaChannel != 0xFF) {
|
if (dmaChannel != 0xFF) {
|
||||||
transmitViaDma(reader, bufferSize, dmaChannel);
|
transmitViaDma(reader, bufferSize, dmaChannel);
|
||||||
|
@ -305,33 +306,35 @@ void Transmitter::transmit(WaveReader &reader, float frequency, float bandwidth,
|
||||||
finally();
|
finally();
|
||||||
throw catched;
|
throw catched;
|
||||||
}
|
}
|
||||||
|
|
||||||
finally();
|
finally();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Transmitter::transmitViaCpu(WaveReader &reader, uint32_t bufferSize)
|
void Transmitter::transmitViaCpu(WaveReader &reader, uint32_t bufferSize)
|
||||||
{
|
{
|
||||||
std::vector<Sample> samples = reader.getSamples(bufferSize, transmitting);
|
samples = reader.getSamples(bufferSize, transmitting);
|
||||||
if (!samples.size()) {
|
if (!samples.size()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleOffset = 0;
|
sampleOffset = 0;
|
||||||
loadedSamples = &samples;
|
|
||||||
|
|
||||||
bool eof = samples.size() < bufferSize;
|
bool eof = samples.size() < bufferSize;
|
||||||
std::thread txThread(Transmitter::transmitThread);
|
std::thread txThread(Transmitter::transmitThread);
|
||||||
|
|
||||||
usleep(BUFFER_TIME / 2);
|
std::this_thread::sleep_for(std::chrono::microseconds(BUFFER_TIME / 2));
|
||||||
|
bool wait = false;
|
||||||
|
|
||||||
auto finally = [&]() {
|
auto finally = [&]() {
|
||||||
transmitting = false;
|
transmitting = false;
|
||||||
txThread.join();
|
txThread.join();
|
||||||
|
samples.clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (!eof && transmitting) {
|
while (!eof && transmitting) {
|
||||||
if (loadedSamples == nullptr) {
|
if (wait) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::microseconds(BUFFER_TIME / 2));
|
||||||
|
}
|
||||||
|
std::lock_guard<std::mutex> locked(samplesMutex);
|
||||||
|
if (!samples.size()) {
|
||||||
if (!reader.setSampleOffset(sampleOffset + bufferSize)) {
|
if (!reader.setSampleOffset(sampleOffset + bufferSize)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -340,15 +343,13 @@ void Transmitter::transmitViaCpu(WaveReader &reader, uint32_t bufferSize)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
eof = samples.size() < bufferSize;
|
eof = samples.size() < bufferSize;
|
||||||
loadedSamples = &samples;
|
|
||||||
}
|
}
|
||||||
usleep(BUFFER_TIME / 2);
|
wait = true;
|
||||||
}
|
}
|
||||||
} catch (std::runtime_error &catched) {
|
} catch (std::runtime_error &catched) {
|
||||||
finally();
|
finally();
|
||||||
throw catched;
|
throw catched;
|
||||||
}
|
}
|
||||||
|
|
||||||
finally();
|
finally();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,7 +359,7 @@ void Transmitter::transmitViaDma(WaveReader &reader, uint32_t bufferSize, uint8_
|
||||||
throw std::runtime_error("DMA channel number out of range (0 - 15)");
|
throw std::runtime_error("DMA channel number out of range (0 - 15)");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Sample> samples = reader.getSamples(bufferSize, transmitting);
|
samples = reader.getSamples(bufferSize, transmitting);
|
||||||
if (!samples.size()) {
|
if (!samples.size()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -409,12 +410,12 @@ void Transmitter::transmitViaDma(WaveReader &reader, uint32_t bufferSize, uint8_
|
||||||
|
|
||||||
volatile DMARegisters *dma = startDma(dmaMemory, dmaCb, dmaChannel);
|
volatile DMARegisters *dma = startDma(dmaMemory, dmaCb, dmaChannel);
|
||||||
|
|
||||||
usleep(BUFFER_TIME / 4);
|
std::this_thread::sleep_for(std::chrono::microseconds(BUFFER_TIME / 4));
|
||||||
|
|
||||||
auto finally = [&]() {
|
auto finally = [&]() {
|
||||||
dmaCb[(cbOffset < 2 * bufferSize) ? cbOffset : 0].nextCbAddress = 0x00000000;
|
dmaCb[(cbOffset < 2 * bufferSize) ? cbOffset : 0].nextCbAddress = 0x00000000;
|
||||||
while (dma->cbAddress != 0x00000000) {
|
while (dma->cbAddress != 0x00000000) {
|
||||||
usleep(1000);
|
std::this_thread::sleep_for(std::chrono::microseconds(1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
closeDma(dma);
|
closeDma(dma);
|
||||||
|
@ -422,8 +423,8 @@ void Transmitter::transmitViaDma(WaveReader &reader, uint32_t bufferSize, uint8_
|
||||||
|
|
||||||
freeMemory(dmaMemory);
|
freeMemory(dmaMemory);
|
||||||
transmitting = false;
|
transmitting = false;
|
||||||
|
samples.clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (!eof && transmitting) {
|
while (!eof && transmitting) {
|
||||||
samples = reader.getSamples(bufferSize, transmitting);
|
samples = reader.getSamples(bufferSize, transmitting);
|
||||||
|
@ -438,7 +439,7 @@ void Transmitter::transmitViaDma(WaveReader &reader, uint32_t bufferSize, uint8_
|
||||||
value = preEmphasis.filter(value);
|
value = preEmphasis.filter(value);
|
||||||
#endif
|
#endif
|
||||||
while (i == ((dma->cbAddress - getMemoryPhysAddress(dmaMemory, dmaCb)) / (2 * sizeof(DMAControllBlock)))) {
|
while (i == ((dma->cbAddress - getMemoryPhysAddress(dmaMemory, dmaCb)) / (2 * sizeof(DMAControllBlock)))) {
|
||||||
usleep(1000);
|
std::this_thread::sleep_for(std::chrono::microseconds(1000));
|
||||||
}
|
}
|
||||||
clkDiv[i] = (0x5A << 24) | (clockDivisor - static_cast<int32_t>(round(value * divisorRange)));
|
clkDiv[i] = (0x5A << 24) | (clockDivisor - static_cast<int32_t>(round(value * divisorRange)));
|
||||||
cbOffset += 2;
|
cbOffset += 2;
|
||||||
|
@ -448,7 +449,6 @@ void Transmitter::transmitViaDma(WaveReader &reader, uint32_t bufferSize, uint8_
|
||||||
finally();
|
finally();
|
||||||
throw catched;
|
throw catched;
|
||||||
}
|
}
|
||||||
|
|
||||||
finally();
|
finally();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,32 +465,36 @@ void Transmitter::transmitThread()
|
||||||
while (transmitting) {
|
while (transmitting) {
|
||||||
uint64_t start = current;
|
uint64_t start = current;
|
||||||
|
|
||||||
while ((loadedSamples == nullptr) && transmitting) {
|
bool locked = samplesMutex.try_lock();
|
||||||
usleep(1);
|
while (!locked && transmitting) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||||
current = *(reinterpret_cast<volatile uint64_t *>(&timer->low));
|
current = *(reinterpret_cast<volatile uint64_t *>(&timer->low));
|
||||||
|
locked = samplesMutex.try_lock();
|
||||||
}
|
}
|
||||||
if (!transmitting) {
|
if (!transmitting) {
|
||||||
|
if (locked) {
|
||||||
|
samplesMutex.unlock();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
std::vector<Sample> loaded(std::move(samples));
|
||||||
std::vector<Sample> samples(*loadedSamples);
|
samplesMutex.unlock();
|
||||||
loadedSamples = nullptr;
|
|
||||||
|
|
||||||
sampleOffset = (current - playbackStart) * sampleRate / 1000000;
|
sampleOffset = (current - playbackStart) * sampleRate / 1000000;
|
||||||
uint32_t offset = (current - start) * sampleRate / 1000000;
|
uint32_t offset = (current - start) * sampleRate / 1000000;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (offset >= samples.size()) {
|
if (offset >= loaded.size()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
uint32_t prevOffset = offset;
|
uint32_t prevOffset = offset;
|
||||||
float value = samples[offset].getMonoValue();
|
float value = loaded[offset].getMonoValue();
|
||||||
#ifndef NO_PREEMP
|
#ifndef NO_PREEMP
|
||||||
value = preEmphasis.filter(value);
|
value = preEmphasis.filter(value);
|
||||||
#endif
|
#endif
|
||||||
output->div = (0x5A << 24) | (clockDivisor - static_cast<int32_t>(round(value * divisorRange)));
|
output->div = (0x5A << 24) | (clockDivisor - static_cast<int32_t>(round(value * divisorRange)));
|
||||||
while (offset == prevOffset) {
|
while (offset == prevOffset) {
|
||||||
usleep(1); // asm("nop");
|
std::this_thread::sleep_for(std::chrono::microseconds(1)); // asm("nop");
|
||||||
current = *(reinterpret_cast<volatile uint64_t *>(&timer->low));;
|
current = *(reinterpret_cast<volatile uint64_t *>(&timer->low));;
|
||||||
offset = (current - start) * sampleRate / 1000000;
|
offset = (current - start) * sampleRate / 1000000;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#define TRANSMITTER_HPP
|
#define TRANSMITTER_HPP
|
||||||
|
|
||||||
#include "wave_reader.hpp"
|
#include "wave_reader.hpp"
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
struct AllocatedMemory;
|
struct AllocatedMemory;
|
||||||
struct PWMRegisters;
|
struct PWMRegisters;
|
||||||
|
@ -77,7 +78,8 @@ class Transmitter
|
||||||
static bool transmitting;
|
static bool transmitting;
|
||||||
static uint32_t sampleOffset, clockDivisor, divisorRange, sampleRate;
|
static uint32_t sampleOffset, clockDivisor, divisorRange, sampleRate;
|
||||||
static volatile ClockRegisters *output;
|
static volatile ClockRegisters *output;
|
||||||
static std::vector<Sample> *loadedSamples;
|
static std::vector<Sample> samples;
|
||||||
|
static std::mutex samplesMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TRANSMITTER_HPP
|
#endif // TRANSMITTER_HPP
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
#include "wave_reader.hpp"
|
#include "wave_reader.hpp"
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
@ -115,7 +117,7 @@ std::vector<uint8_t> WaveReader::readData(uint32_t bytesToRead, bool headerBytes
|
||||||
data.resize(bytes);
|
data.resize(bytes);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
usleep(1);
|
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue