2015-12-16 20:30:58 +00:00
|
|
|
/*
|
|
|
|
fm_transmitter - use Raspberry Pi as FM transmitter
|
|
|
|
|
2019-01-05 03:06:16 +00:00
|
|
|
Copyright (c) 2019, Marcin Kondej
|
2015-12-16 20:30:58 +00:00
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
See https://github.com/markondej/fm_transmitter
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without modification, are
|
|
|
|
permitted provided that the following conditions are met:
|
|
|
|
|
|
|
|
1. Redistributions of source code must retain the above copyright notice, this list
|
|
|
|
of conditions and the following disclaimer.
|
|
|
|
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright notice, this
|
|
|
|
list of conditions and the following disclaimer in the documentation and/or other
|
|
|
|
materials provided with the distribution.
|
|
|
|
|
|
|
|
3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
|
|
used to endorse or promote products derived from this software without specific
|
|
|
|
prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
|
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
|
|
|
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
|
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
|
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
|
|
|
|
WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "transmitter.h"
|
2019-01-05 03:06:16 +00:00
|
|
|
#include "preemp.h"
|
2018-12-29 02:18:27 +00:00
|
|
|
#include "error_reporter.h"
|
2019-01-05 03:06:16 +00:00
|
|
|
#include "mailbox.h"
|
2018-12-29 02:18:27 +00:00
|
|
|
#include <bcm_host.h>
|
2015-12-16 20:30:58 +00:00
|
|
|
#include <sstream>
|
|
|
|
#include <cmath>
|
2016-12-07 12:34:19 +00:00
|
|
|
#include <fcntl.h>
|
2015-12-16 20:30:58 +00:00
|
|
|
#include <sys/mman.h>
|
|
|
|
|
|
|
|
using std::ostringstream;
|
2018-12-29 02:18:27 +00:00
|
|
|
using std::vector;
|
2015-12-16 20:30:58 +00:00
|
|
|
|
2019-01-05 03:06:16 +00:00
|
|
|
#define PERIPHERALS_PHYS_BASE 0x7E000000
|
2019-01-07 01:52:33 +00:00
|
|
|
#define BCM2835_PERI_VIRT_BASE 0x20000000
|
2019-09-12 06:49:15 +00:00
|
|
|
#define BCM2838_PERI_VIRT_BASE 0xFE000000
|
2019-01-05 03:06:16 +00:00
|
|
|
#define DMA0_BASE_OFFSET 0x00007000
|
|
|
|
#define DMA15_BASE_OFFSET 0x00E05000
|
|
|
|
#define CLK0_BASE_OFFSET 0x00101070
|
|
|
|
#define PWMCLK_BASE_OFFSET 0x001010A0
|
|
|
|
#define GPIO_BASE_OFFSET 0x00200000
|
|
|
|
#define PWM_BASE_OFFSET 0x0020C000
|
|
|
|
#define TIMER_BASE_OFFSET 0x00003000
|
|
|
|
|
2019-01-07 01:52:33 +00:00
|
|
|
#define BCM2837_MEM_FLAG 0x04
|
|
|
|
#define BCM2835_MEM_FLAG 0x0C
|
|
|
|
#define PAGE_SIZE 4096
|
|
|
|
|
2019-01-05 03:06:16 +00:00
|
|
|
#define BUFFER_TIME 1000000
|
2019-01-07 01:52:33 +00:00
|
|
|
#define PWM_WRITES_PER_SAMPLE 10
|
2019-01-05 03:06:16 +00:00
|
|
|
#define PWM_CHANNEL_RANGE 32
|
|
|
|
|
|
|
|
struct TimerRegisters {
|
|
|
|
unsigned ctlStatus;
|
|
|
|
unsigned low;
|
|
|
|
unsigned high;
|
|
|
|
unsigned c0;
|
|
|
|
unsigned c1;
|
|
|
|
unsigned c2;
|
|
|
|
unsigned c3;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ClockRegisters {
|
|
|
|
unsigned ctl;
|
|
|
|
unsigned div;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct PWMRegisters {
|
|
|
|
unsigned ctl;
|
|
|
|
unsigned status;
|
|
|
|
unsigned dmaConf;
|
2019-01-10 10:19:16 +00:00
|
|
|
unsigned reserved0;
|
2019-01-05 03:06:16 +00:00
|
|
|
unsigned chn1Range;
|
|
|
|
unsigned chn1Data;
|
|
|
|
unsigned fifoIn;
|
2019-01-10 10:19:16 +00:00
|
|
|
unsigned reserved1;
|
2019-01-05 03:06:16 +00:00
|
|
|
unsigned chn2Range;
|
|
|
|
unsigned chn2Data;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DMAControllBlock {
|
|
|
|
unsigned transferInfo;
|
|
|
|
unsigned srcAddress;
|
|
|
|
unsigned dstAddress;
|
|
|
|
unsigned transferLen;
|
|
|
|
unsigned stride;
|
2019-01-07 01:52:33 +00:00
|
|
|
unsigned nextCbAddress;
|
2019-01-05 03:06:16 +00:00
|
|
|
unsigned reserved0;
|
|
|
|
unsigned reserved1;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DMARegisters {
|
|
|
|
unsigned ctlStatus;
|
|
|
|
unsigned cbAddress;
|
|
|
|
unsigned transferInfo;
|
|
|
|
unsigned srcAddress;
|
|
|
|
unsigned dstAddress;
|
|
|
|
unsigned transferLen;
|
|
|
|
unsigned stride;
|
2019-01-07 01:52:33 +00:00
|
|
|
unsigned nextCbAddress;
|
2019-01-05 03:06:16 +00:00
|
|
|
unsigned debug;
|
|
|
|
};
|
2015-12-16 20:30:58 +00:00
|
|
|
|
2018-12-30 08:34:27 +00:00
|
|
|
bool Transmitter::transmitting = false;
|
2019-01-05 03:06:16 +00:00
|
|
|
bool Transmitter::clockInitialized = false;
|
|
|
|
bool Transmitter::preserveCarrier = false;
|
|
|
|
void *Transmitter::peripherals = NULL;
|
2018-12-30 08:34:27 +00:00
|
|
|
|
2019-01-05 03:06:16 +00:00
|
|
|
Transmitter::Transmitter()
|
2019-01-07 13:56:06 +00:00
|
|
|
: memSize(0)
|
2015-12-16 20:30:58 +00:00
|
|
|
{
|
|
|
|
int memFd;
|
|
|
|
if ((memFd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
|
|
|
|
throw ErrorReporter("Cannot open /dev/mem (permission denied)");
|
|
|
|
}
|
|
|
|
|
2019-09-12 06:49:15 +00:00
|
|
|
peripherals = mmap(NULL, getPeripheralSize(), PROT_READ | PROT_WRITE, MAP_SHARED, memFd, getPeripheralVirtAddress());
|
2015-12-16 20:30:58 +00:00
|
|
|
close(memFd);
|
|
|
|
if (peripherals == MAP_FAILED) {
|
|
|
|
throw ErrorReporter("Cannot obtain access to peripherals (mmap error)");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Transmitter::~Transmitter()
|
|
|
|
{
|
2019-09-12 06:49:15 +00:00
|
|
|
munmap(peripherals, getPeripheralSize());
|
2015-12-16 20:30:58 +00:00
|
|
|
}
|
|
|
|
|
2019-01-05 03:06:16 +00:00
|
|
|
Transmitter &Transmitter::getInstance()
|
2015-12-16 20:30:58 +00:00
|
|
|
{
|
|
|
|
static Transmitter instance;
|
2019-01-05 03:06:16 +00:00
|
|
|
return instance;
|
2015-12-16 20:30:58 +00:00
|
|
|
}
|
|
|
|
|
2019-01-07 13:56:06 +00:00
|
|
|
bool Transmitter::allocateMemory(unsigned size)
|
|
|
|
{
|
|
|
|
if (memSize) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
mBoxFd = mbox_open();
|
|
|
|
memSize = size;
|
|
|
|
if (memSize % PAGE_SIZE) {
|
|
|
|
memSize = (memSize / PAGE_SIZE + 1) * PAGE_SIZE;
|
|
|
|
}
|
2019-09-12 06:49:15 +00:00
|
|
|
memHandle = mem_alloc(mBoxFd, size, PAGE_SIZE, (getPeripheralVirtAddress() == BCM2835_PERI_VIRT_BASE) ? BCM2835_MEM_FLAG : BCM2837_MEM_FLAG);
|
2019-01-07 13:56:06 +00:00
|
|
|
if (!memHandle) {
|
|
|
|
mbox_close(mBoxFd);
|
|
|
|
memSize = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
memAddress = mem_lock(mBoxFd, memHandle);
|
|
|
|
memAllocated = mapmem(memAddress & ~0xC0000000, memSize);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Transmitter::freeMemory()
|
|
|
|
{
|
|
|
|
unmapmem(memAllocated, memSize);
|
|
|
|
mem_unlock(mBoxFd, memHandle);
|
|
|
|
mem_free(mBoxFd, memHandle);
|
|
|
|
|
|
|
|
mbox_close(mBoxFd);
|
|
|
|
memSize = 0;
|
|
|
|
}
|
|
|
|
|
2019-09-12 06:49:15 +00:00
|
|
|
unsigned Transmitter::getPeripheralVirtAddress()
|
|
|
|
{
|
|
|
|
return (bcm_host_get_peripheral_size() == BCM2838_PERI_VIRT_BASE) ? BCM2838_PERI_VIRT_BASE : bcm_host_get_peripheral_address();
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned Transmitter::getPeripheralSize()
|
|
|
|
{
|
|
|
|
unsigned size = bcm_host_get_peripheral_size();
|
|
|
|
if (size == BCM2838_PERI_VIRT_BASE) {
|
|
|
|
size = 0x01000000;
|
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2019-01-10 11:44:09 +00:00
|
|
|
unsigned Transmitter::getMemoryAddress(volatile void *object)
|
2019-01-07 13:56:06 +00:00
|
|
|
{
|
|
|
|
return (memSize) ? memAddress + ((unsigned)object - (unsigned)memAllocated) : 0x00000000;
|
|
|
|
}
|
|
|
|
|
2019-01-10 10:51:55 +00:00
|
|
|
unsigned Transmitter::getPeripheralAddress(volatile void *object) {
|
2019-01-10 10:19:16 +00:00
|
|
|
return PERIPHERALS_PHYS_BASE + ((unsigned)object - (unsigned)peripherals);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *Transmitter::getPeripheral(unsigned offset) {
|
|
|
|
return (void *)((unsigned)peripherals + offset);
|
|
|
|
}
|
|
|
|
|
2019-01-07 16:50:34 +00:00
|
|
|
void Transmitter::play(WaveReader &reader, double frequency, double bandwidth, unsigned char dmaChannel, bool preserveCarrierOnExit)
|
2015-12-16 20:30:58 +00:00
|
|
|
{
|
2016-12-14 23:55:21 +00:00
|
|
|
if (transmitting) {
|
2015-12-16 20:30:58 +00:00
|
|
|
throw ErrorReporter("Cannot play, transmitter already in use");
|
|
|
|
}
|
|
|
|
|
2016-12-14 23:55:21 +00:00
|
|
|
transmitting = true;
|
2019-01-05 03:06:16 +00:00
|
|
|
preserveCarrier = preserveCarrierOnExit;
|
2016-12-07 12:17:26 +00:00
|
|
|
|
2019-01-05 03:06:16 +00:00
|
|
|
PCMWaveHeader header = reader.getHeader();
|
|
|
|
unsigned bufferSize = (unsigned)((unsigned long long)header.sampleRate * BUFFER_TIME / 1000000);
|
|
|
|
vector<Sample> *samples = reader.getSamples(bufferSize, transmitting);
|
|
|
|
if (samples == NULL) {
|
2018-12-29 02:18:27 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-01-05 03:06:16 +00:00
|
|
|
bool eof = samples->size() < bufferSize;
|
2018-12-29 02:18:27 +00:00
|
|
|
|
2019-01-08 08:54:44 +00:00
|
|
|
unsigned clockDivisor = (unsigned)round((500 << 12) / frequency);
|
|
|
|
unsigned divisorRange = clockDivisor - (unsigned)round((500 << 12) / (frequency + 0.0005 * bandwidth));
|
2019-01-07 13:56:06 +00:00
|
|
|
bool isError = false;
|
|
|
|
string errorMessage;
|
2015-12-16 20:30:58 +00:00
|
|
|
|
2019-01-05 03:06:16 +00:00
|
|
|
if (dmaChannel != 0xFF) {
|
|
|
|
if (dmaChannel > 15) {
|
2019-01-07 01:52:33 +00:00
|
|
|
delete samples;
|
2019-01-07 13:56:06 +00:00
|
|
|
throw ErrorReporter("DMA channel number out of range (0 - 15)");
|
2019-01-05 03:06:16 +00:00
|
|
|
}
|
|
|
|
|
2019-02-04 02:09:33 +00:00
|
|
|
if (!allocateMemory(sizeof(unsigned) * (bufferSize + 1) + sizeof(DMAControllBlock) * (2 * bufferSize))) {
|
2019-01-07 01:52:33 +00:00
|
|
|
delete samples;
|
2019-01-05 03:06:16 +00:00
|
|
|
throw ErrorReporter("Cannot allocate memory");
|
|
|
|
}
|
|
|
|
|
2019-01-07 13:56:06 +00:00
|
|
|
volatile ClockRegisters *clk0 = (ClockRegisters *)getPeripheral(CLK0_BASE_OFFSET);
|
|
|
|
volatile unsigned *gpio = (unsigned *)getPeripheral(GPIO_BASE_OFFSET);
|
2019-01-05 03:06:16 +00:00
|
|
|
if (!clockInitialized) {
|
|
|
|
clk0->ctl = (0x5A << 24) | 0x06;
|
|
|
|
usleep(1000);
|
|
|
|
clk0->div = (0x5A << 24) | clockDivisor;
|
|
|
|
clk0->ctl = (0x5A << 24) | (0x01 << 9) | (0x01 << 4) | 0x06;
|
2019-01-07 13:56:06 +00:00
|
|
|
*gpio = (*gpio & 0xFFFF8FFF) | (0x01 << 14);
|
2019-01-05 03:06:16 +00:00
|
|
|
clockInitialized = true;
|
|
|
|
}
|
|
|
|
|
2019-01-07 13:56:06 +00:00
|
|
|
volatile ClockRegisters *pwmClk = (ClockRegisters *)getPeripheral(PWMCLK_BASE_OFFSET);
|
2019-01-07 01:52:33 +00:00
|
|
|
float pwmClkFreq = PWM_WRITES_PER_SAMPLE * PWM_CHANNEL_RANGE * header.sampleRate / 1000000;
|
2019-01-05 03:06:16 +00:00
|
|
|
pwmClk->ctl = (0x5A << 24) | 0x06;
|
|
|
|
usleep(1000);
|
2019-01-07 01:52:33 +00:00
|
|
|
pwmClk->div = (0x5A << 24) | (unsigned)((500 << 12) / pwmClkFreq);
|
2019-01-05 03:06:16 +00:00
|
|
|
pwmClk->ctl = (0x5A << 24) | (0x01 << 4) | 0x06;
|
|
|
|
|
2019-01-07 13:56:06 +00:00
|
|
|
volatile PWMRegisters *pwm = (PWMRegisters *)getPeripheral(PWM_BASE_OFFSET);
|
2019-01-05 03:06:16 +00:00
|
|
|
pwm->ctl = 0x00;
|
|
|
|
usleep(1000);
|
|
|
|
pwm->status = 0x01FC;
|
|
|
|
pwm->ctl = (0x01 << 6);
|
|
|
|
usleep(1000);
|
2019-01-07 01:52:33 +00:00
|
|
|
pwm->chn1Range = PWM_CHANNEL_RANGE;
|
2019-01-05 03:06:16 +00:00
|
|
|
pwm->dmaConf = (0x01 << 31) | 0x0707;
|
|
|
|
pwm->ctl = (0x01 << 5) | (0x01 << 2) | 0x01;
|
|
|
|
|
2019-01-07 13:56:06 +00:00
|
|
|
float value;
|
2019-02-04 12:09:27 +00:00
|
|
|
unsigned i, cbOffset = 0;
|
2019-01-05 03:06:16 +00:00
|
|
|
#ifndef NO_PREEMP
|
2019-01-07 01:52:33 +00:00
|
|
|
PreEmp preEmp(header.sampleRate);
|
2019-01-05 03:06:16 +00:00
|
|
|
#endif
|
2019-01-07 01:52:33 +00:00
|
|
|
|
2019-01-10 11:44:09 +00:00
|
|
|
volatile DMAControllBlock *dmaCb = (DMAControllBlock *)memAllocated;
|
2019-02-04 02:09:33 +00:00
|
|
|
volatile unsigned *clkDiv = (unsigned *)((unsigned)dmaCb + 2 * sizeof(DMAControllBlock) * bufferSize);
|
|
|
|
volatile unsigned *pwmFifoData = (unsigned *)((unsigned)clkDiv + sizeof(unsigned) * bufferSize);
|
2019-01-05 03:06:16 +00:00
|
|
|
for (i = 0; i < bufferSize; i++) {
|
|
|
|
value = (*samples)[i].getMonoValue();
|
|
|
|
#ifndef NO_PREEMP
|
|
|
|
value = preEmp.filter(value);
|
|
|
|
#endif
|
2019-01-07 16:50:34 +00:00
|
|
|
clkDiv[i] = (0x5A << 24) | (clockDivisor - (int)round(value * divisorRange));
|
2019-02-04 12:09:27 +00:00
|
|
|
dmaCb[cbOffset].transferInfo = (0x01 << 26) | (0x01 << 3);
|
|
|
|
dmaCb[cbOffset].srcAddress = getMemoryAddress(&clkDiv[i]);
|
|
|
|
dmaCb[cbOffset].dstAddress = getPeripheralAddress(&clk0->div);
|
|
|
|
dmaCb[cbOffset].transferLen = sizeof(unsigned);
|
|
|
|
dmaCb[cbOffset].stride = 0;
|
|
|
|
dmaCb[cbOffset].nextCbAddress = getMemoryAddress(&dmaCb[cbOffset + 1]);
|
|
|
|
cbOffset++;
|
|
|
|
|
|
|
|
dmaCb[cbOffset].transferInfo = (0x01 << 26) | (0x05 << 16) | (0x01 << 6) | (0x01 << 3);
|
|
|
|
dmaCb[cbOffset].srcAddress = getMemoryAddress(pwmFifoData);
|
|
|
|
dmaCb[cbOffset].dstAddress = getPeripheralAddress(&pwm->fifoIn);
|
|
|
|
dmaCb[cbOffset].transferLen = sizeof(unsigned) * PWM_WRITES_PER_SAMPLE;
|
|
|
|
dmaCb[cbOffset].stride = 0;
|
|
|
|
dmaCb[cbOffset].nextCbAddress = getMemoryAddress((i < bufferSize - 1) ? &dmaCb[cbOffset + 1] : dmaCb);
|
|
|
|
cbOffset++;
|
2019-01-05 03:06:16 +00:00
|
|
|
}
|
2019-02-06 00:57:12 +00:00
|
|
|
*pwmFifoData = 0x00;
|
2019-01-05 03:06:16 +00:00
|
|
|
delete samples;
|
|
|
|
|
2019-01-07 13:56:06 +00:00
|
|
|
volatile DMARegisters *dma = (DMARegisters *)getPeripheral((dmaChannel < 15) ? DMA0_BASE_OFFSET + dmaChannel * 0x100 : DMA15_BASE_OFFSET);
|
2019-01-05 03:06:16 +00:00
|
|
|
dma->ctlStatus = (0x01 << 31);
|
|
|
|
usleep(1000);
|
|
|
|
dma->ctlStatus = (0x01 << 2) | (0x01 << 1);
|
2019-01-10 10:19:16 +00:00
|
|
|
dma->cbAddress = getMemoryAddress(dmaCb);
|
2019-01-05 03:06:16 +00:00
|
|
|
dma->ctlStatus = (0xFF << 16) | 0x01;
|
|
|
|
|
2019-01-08 08:54:44 +00:00
|
|
|
usleep(BUFFER_TIME / 4);
|
2019-01-05 03:06:16 +00:00
|
|
|
|
2019-01-07 01:52:33 +00:00
|
|
|
try {
|
2019-01-05 03:06:16 +00:00
|
|
|
while (!eof && transmitting) {
|
2019-01-07 01:52:33 +00:00
|
|
|
samples = reader.getSamples(bufferSize, transmitting);
|
2019-01-05 03:06:16 +00:00
|
|
|
if (samples == NULL) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
eof = samples->size() < bufferSize;
|
2019-02-04 12:09:27 +00:00
|
|
|
cbOffset = 0;
|
2019-01-05 03:06:16 +00:00
|
|
|
for (i = 0; i < samples->size(); i++) {
|
|
|
|
value = (*samples)[i].getMonoValue();
|
|
|
|
#ifndef NO_PREEMP
|
|
|
|
value = preEmp.filter(value);
|
|
|
|
#endif
|
2019-01-10 11:07:59 +00:00
|
|
|
while (i == ((dma->cbAddress - getMemoryAddress(dmaCb)) / (2 * sizeof(DMAControllBlock)))) {
|
2019-02-06 00:57:12 +00:00
|
|
|
usleep(1000);
|
2019-01-05 03:06:16 +00:00
|
|
|
}
|
2019-01-07 16:50:34 +00:00
|
|
|
clkDiv[i] = (0x5A << 24) | (clockDivisor - (int)round(value * divisorRange));
|
2019-02-04 12:09:27 +00:00
|
|
|
cbOffset += 2;
|
2019-01-05 03:06:16 +00:00
|
|
|
}
|
|
|
|
delete samples;
|
|
|
|
}
|
2019-01-07 01:52:33 +00:00
|
|
|
} catch (ErrorReporter &error) {
|
|
|
|
preserveCarrier = false;
|
|
|
|
errorMessage = error.what();
|
|
|
|
isError = true;
|
|
|
|
}
|
|
|
|
|
2019-02-04 12:09:27 +00:00
|
|
|
cbOffset -= 2;
|
|
|
|
dmaCb[cbOffset].nextCbAddress = 0x00;
|
2019-01-07 01:52:33 +00:00
|
|
|
while (dma->cbAddress != 0x00) {
|
2019-02-06 00:57:12 +00:00
|
|
|
usleep(1000);
|
2019-01-07 01:52:33 +00:00
|
|
|
}
|
|
|
|
|
2019-01-05 03:06:16 +00:00
|
|
|
dma->ctlStatus = (0x01 << 31);
|
|
|
|
pwm->ctl = 0x00;
|
|
|
|
|
2019-01-07 13:56:06 +00:00
|
|
|
freeMemory();
|
2019-01-05 03:06:16 +00:00
|
|
|
transmitting = false;
|
|
|
|
|
|
|
|
if (!preserveCarrier) {
|
|
|
|
clk0->ctl = (0x5A << 24) | 0x06;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
unsigned sampleOffset = 0;
|
|
|
|
vector<Sample> *buffer = samples;
|
|
|
|
|
2019-01-07 16:50:34 +00:00
|
|
|
void *transmitterParams[5] = {
|
2019-01-05 03:06:16 +00:00
|
|
|
(void *)&buffer,
|
|
|
|
(void *)&sampleOffset,
|
|
|
|
(void *)&clockDivisor,
|
2019-01-07 16:50:34 +00:00
|
|
|
(void *)&divisorRange,
|
2019-01-05 03:06:16 +00:00
|
|
|
(void *)&header.sampleRate
|
|
|
|
};
|
|
|
|
|
|
|
|
pthread_t thread;
|
|
|
|
int returnCode = pthread_create(&thread, NULL, &Transmitter::transmit, (void *)transmitterParams);
|
|
|
|
if (returnCode) {
|
|
|
|
delete samples;
|
|
|
|
ostringstream oss;
|
|
|
|
oss << "Cannot create transmitter thread (code: " << returnCode << ")";
|
|
|
|
throw ErrorReporter(oss.str());
|
|
|
|
}
|
|
|
|
|
2019-01-08 08:54:44 +00:00
|
|
|
usleep(BUFFER_TIME / 2);
|
2015-12-16 20:30:58 +00:00
|
|
|
|
2019-01-05 03:06:16 +00:00
|
|
|
try {
|
|
|
|
while (!eof && transmitting) {
|
2016-12-14 23:55:21 +00:00
|
|
|
if (buffer == NULL) {
|
2019-01-05 03:06:16 +00:00
|
|
|
if (!reader.setSampleOffset(sampleOffset + bufferSize)) {
|
2016-12-19 17:02:24 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-01-05 03:06:16 +00:00
|
|
|
samples = reader.getSamples(bufferSize, transmitting);
|
|
|
|
if (samples == NULL) {
|
2016-12-14 23:55:21 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-01-05 03:06:16 +00:00
|
|
|
eof = samples->size() < bufferSize;
|
|
|
|
buffer = samples;
|
2016-12-14 23:55:21 +00:00
|
|
|
}
|
2019-01-08 08:54:44 +00:00
|
|
|
usleep(BUFFER_TIME / 2);
|
2016-12-13 01:43:28 +00:00
|
|
|
}
|
2015-12-16 20:30:58 +00:00
|
|
|
}
|
2019-01-05 03:06:16 +00:00
|
|
|
catch (ErrorReporter &error) {
|
|
|
|
preserveCarrier = false;
|
|
|
|
errorMessage = error.what();
|
|
|
|
isError = true;
|
|
|
|
}
|
|
|
|
transmitting = false;
|
|
|
|
pthread_join(thread, NULL);
|
2019-01-07 15:59:44 +00:00
|
|
|
if (buffer != NULL) {
|
|
|
|
delete buffer;
|
|
|
|
}
|
2019-01-07 13:56:06 +00:00
|
|
|
}
|
|
|
|
if (isError) {
|
|
|
|
throw ErrorReporter(errorMessage);
|
2018-12-29 02:18:27 +00:00
|
|
|
}
|
|
|
|
}
|
2015-12-16 20:30:58 +00:00
|
|
|
|
2019-01-05 03:06:16 +00:00
|
|
|
void *Transmitter::transmit(void *params)
|
2015-12-16 20:30:58 +00:00
|
|
|
{
|
2019-01-05 03:06:16 +00:00
|
|
|
vector<Sample> **buffer = (vector<Sample> **)((void **)params)[0];
|
|
|
|
unsigned *sampleOffset = (unsigned *)((void **)params)[1];
|
|
|
|
unsigned *clockDivisor = (unsigned *)((void **)params)[2];
|
2019-01-07 16:50:34 +00:00
|
|
|
unsigned *divisorRange = (unsigned *)((void **)params)[3];
|
|
|
|
unsigned *sampleRate = (unsigned *)((void **)params)[4];
|
2018-12-29 02:18:27 +00:00
|
|
|
|
|
|
|
unsigned offset, length, prevOffset;
|
2019-01-05 03:06:16 +00:00
|
|
|
vector<Sample> *samples = NULL;
|
|
|
|
unsigned long long start;
|
2016-12-13 00:37:54 +00:00
|
|
|
float value;
|
2015-12-16 20:30:58 +00:00
|
|
|
|
|
|
|
#ifndef NO_PREEMP
|
2019-01-05 03:06:16 +00:00
|
|
|
PreEmp preEmp(*sampleRate);
|
2015-12-16 20:30:58 +00:00
|
|
|
#endif
|
|
|
|
|
2019-01-07 13:56:06 +00:00
|
|
|
volatile ClockRegisters *clk0 = (ClockRegisters *)getPeripheral(CLK0_BASE_OFFSET);
|
|
|
|
volatile unsigned *gpio = (unsigned *)getPeripheral(GPIO_BASE_OFFSET);
|
2019-01-05 03:06:16 +00:00
|
|
|
if (!clockInitialized) {
|
|
|
|
clk0->ctl = (0x5A << 24) | 0x06;
|
|
|
|
usleep(1000);
|
|
|
|
clk0->div = (0x5A << 24) | *clockDivisor;
|
|
|
|
clk0->ctl = (0x5A << 24) | (0x01 << 9) | (0x01 << 4) | 0x06;
|
|
|
|
*gpio = (*gpio & 0xFFFF8FFF) | (0x01 << 14);
|
|
|
|
clockInitialized = true;
|
|
|
|
}
|
2016-12-06 03:29:47 +00:00
|
|
|
|
2019-01-07 13:56:06 +00:00
|
|
|
volatile TimerRegisters *timer = (TimerRegisters *)getPeripheral(TIMER_BASE_OFFSET);
|
|
|
|
unsigned long long current = *(unsigned long long *)&timer->low;
|
2019-01-05 03:06:16 +00:00
|
|
|
unsigned long long playbackStart = current;
|
2015-12-16 20:30:58 +00:00
|
|
|
|
2018-12-30 08:34:27 +00:00
|
|
|
while (transmitting) {
|
2016-12-06 03:24:52 +00:00
|
|
|
start = current;
|
2018-12-29 02:18:27 +00:00
|
|
|
|
2018-12-30 08:34:27 +00:00
|
|
|
while ((*buffer == NULL) && transmitting) {
|
2016-02-02 16:11:08 +00:00
|
|
|
usleep(1);
|
2019-01-07 13:56:06 +00:00
|
|
|
current = *(unsigned long long *)&timer->low;
|
2015-12-16 20:30:58 +00:00
|
|
|
}
|
2018-12-30 08:34:27 +00:00
|
|
|
if (!transmitting) {
|
2015-12-16 20:30:58 +00:00
|
|
|
break;
|
|
|
|
}
|
2018-12-29 02:18:27 +00:00
|
|
|
|
2019-01-05 03:06:16 +00:00
|
|
|
samples = *buffer;
|
|
|
|
length = samples->size();
|
|
|
|
*buffer = NULL;
|
2015-12-16 20:30:58 +00:00
|
|
|
|
2019-01-05 03:06:16 +00:00
|
|
|
*sampleOffset = (current - playbackStart) * (*sampleRate) / 1000000;
|
|
|
|
offset = (current - start) * (*sampleRate) / 1000000;
|
2015-12-16 20:30:58 +00:00
|
|
|
|
|
|
|
while (true) {
|
|
|
|
if (offset >= length) {
|
|
|
|
break;
|
|
|
|
}
|
2018-12-29 02:18:27 +00:00
|
|
|
prevOffset = offset;
|
2019-01-05 03:06:16 +00:00
|
|
|
value = (*samples)[offset].getMonoValue();
|
2015-12-16 20:30:58 +00:00
|
|
|
#ifndef NO_PREEMP
|
2019-01-05 03:06:16 +00:00
|
|
|
value = preEmp.filter(value);
|
2015-12-16 20:30:58 +00:00
|
|
|
#endif
|
2019-01-07 16:50:34 +00:00
|
|
|
clk0->div = (0x5A << 24) | (*clockDivisor - (int)round(value * (*divisorRange)));
|
2018-12-29 02:18:27 +00:00
|
|
|
while (offset == prevOffset) {
|
2019-01-05 03:06:16 +00:00
|
|
|
usleep(1); // asm("nop");
|
2019-01-07 13:56:06 +00:00
|
|
|
current = *(unsigned long long *)&timer->low;
|
2019-01-05 03:06:16 +00:00
|
|
|
offset = (current - start) * (*sampleRate) / 1000000;
|
2015-12-16 20:30:58 +00:00
|
|
|
}
|
|
|
|
}
|
2019-01-05 03:06:16 +00:00
|
|
|
delete samples;
|
2015-12-16 20:30:58 +00:00
|
|
|
}
|
|
|
|
|
2019-01-05 03:06:16 +00:00
|
|
|
if (!preserveCarrier) {
|
|
|
|
clk0->ctl = (0x5A << 24) | 0x06;
|
|
|
|
}
|
2015-12-16 20:30:58 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Transmitter::stop()
|
|
|
|
{
|
2019-01-05 03:06:16 +00:00
|
|
|
preserveCarrier = false;
|
|
|
|
transmitting = false;
|
2015-12-16 20:30:58 +00:00
|
|
|
}
|