fm_transmitter/transmitter.cpp

488 wiersze
16 KiB
C++
Czysty Zwykły widok Historia

2015-12-16 20:30:58 +00:00
/*
fm_transmitter - use Raspberry Pi as FM transmitter
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"
#include "preemp.h"
#include "error_reporter.h"
#include "mailbox.h"
#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;
using std::vector;
2015-12-16 20:30:58 +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
#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
#define BUFFER_TIME 1000000
2019-01-07 01:52:33 +00:00
#define PWM_WRITES_PER_SAMPLE 10
#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;
unsigned chn1Range;
unsigned chn1Data;
unsigned fifoIn;
2019-01-10 10:19:16 +00:00
unsigned reserved1;
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;
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;
unsigned debug;
};
2015-12-16 20:30:58 +00:00
2018-12-30 08:34:27 +00:00
bool Transmitter::transmitting = false;
bool Transmitter::clockInitialized = false;
bool Transmitter::preserveCarrier = false;
void *Transmitter::peripherals = NULL;
2018-12-30 08:34:27 +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
}
Transmitter &Transmitter::getInstance()
2015-12-16 20:30:58 +00:00
{
static Transmitter instance;
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;
preserveCarrier = preserveCarrierOnExit;
2016-12-07 12:17:26 +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) {
return;
}
bool eof = samples->size() < bufferSize;
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
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-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;
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);
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);
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;
pwmClk->ctl = (0x5A << 24) | 0x06;
usleep(1000);
2019-01-07 01:52:33 +00:00
pwmClk->div = (0x5A << 24) | (unsigned)((500 << 12) / pwmClkFreq);
pwmClk->ctl = (0x5A << 24) | (0x01 << 4) | 0x06;
2019-01-07 13:56:06 +00:00
volatile PWMRegisters *pwm = (PWMRegisters *)getPeripheral(PWM_BASE_OFFSET);
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;
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;
#ifndef NO_PREEMP
2019-01-07 01:52:33 +00:00
PreEmp preEmp(header.sampleRate);
#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);
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-02-06 00:57:12 +00:00
*pwmFifoData = 0x00;
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);
dma->ctlStatus = (0x01 << 31);
usleep(1000);
dma->ctlStatus = (0x01 << 2) | (0x01 << 1);
2019-01-10 10:19:16 +00:00
dma->cbAddress = getMemoryAddress(dmaCb);
dma->ctlStatus = (0xFF << 16) | 0x01;
2019-01-08 08:54:44 +00:00
usleep(BUFFER_TIME / 4);
2019-01-07 01:52:33 +00:00
try {
while (!eof && transmitting) {
2019-01-07 01:52:33 +00:00
samples = reader.getSamples(bufferSize, transmitting);
if (samples == NULL) {
break;
}
eof = samples->size() < bufferSize;
2019-02-04 12:09:27 +00:00
cbOffset = 0;
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-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;
}
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
}
dma->ctlStatus = (0x01 << 31);
pwm->ctl = 0x00;
2019-01-07 13:56:06 +00:00
freeMemory();
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] = {
(void *)&buffer,
(void *)&sampleOffset,
(void *)&clockDivisor,
2019-01-07 16:50:34 +00:00
(void *)&divisorRange,
(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
try {
while (!eof && transmitting) {
2016-12-14 23:55:21 +00:00
if (buffer == NULL) {
if (!reader.setSampleOffset(sampleOffset + bufferSize)) {
2016-12-19 17:02:24 +00:00
break;
}
samples = reader.getSamples(bufferSize, transmitting);
if (samples == NULL) {
2016-12-14 23:55:21 +00:00
break;
}
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
}
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);
}
}
2015-12-16 20:30:58 +00:00
void *Transmitter::transmit(void *params)
2015-12-16 20:30:58 +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];
unsigned offset, length, prevOffset;
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
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);
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;
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-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;
}
samples = *buffer;
length = samples->size();
*buffer = NULL;
2015-12-16 20:30:58 +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;
}
prevOffset = offset;
value = (*samples)[offset].getMonoValue();
2015-12-16 20:30:58 +00:00
#ifndef NO_PREEMP
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)));
while (offset == prevOffset) {
usleep(1); // asm("nop");
2019-01-07 13:56:06 +00:00
current = *(unsigned long long *)&timer->low;
offset = (current - start) * (*sampleRate) / 1000000;
2015-12-16 20:30:58 +00:00
}
}
delete samples;
2015-12-16 20:30:58 +00:00
}
if (!preserveCarrier) {
clk0->ctl = (0x5A << 24) | 0x06;
}
2015-12-16 20:30:58 +00:00
return NULL;
}
void Transmitter::stop()
{
preserveCarrier = false;
transmitting = false;
2015-12-16 20:30:58 +00:00
}