Initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
garaz.sh
|
||||||
39
Makefile
Normal file
39
Makefile
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
CC = gcc
|
||||||
|
STD_CFLAGS = -Wall -std=gnu99 -c -O2
|
||||||
|
|
||||||
|
# Enable ARM-specific options only on ARM, and compilation of the app only on ARM
|
||||||
|
UNAME := $(shell uname -m)
|
||||||
|
PCPUI := $(shell cat /proc/cpuinfo | grep Revision | cut -c16-)
|
||||||
|
|
||||||
|
# Determine the hardware platform. Below, pi1 stands for the RaspberryPi 1 (the original one),
|
||||||
|
# and pi2 stands for both the RaspberryPi 2 and 3.
|
||||||
|
ifeq ($(UNAME), armv6l)
|
||||||
|
CFLAGS = $(STD_CFLAGS) -march=armv6 -mtune=arm1176jzf-s -mfloat-abi=hard -mfpu=vfp -ffast-math -DRASPI=1
|
||||||
|
TARGET = pi1
|
||||||
|
else ifeq ($(PCPUI), 11)
|
||||||
|
CFLAGS = $(STD_CFLAGS) -march=armv7-a -mtune=arm1176jzf-s -mfloat-abi=hard -mfpu=vfp -ffast-math -DRASPI=4
|
||||||
|
TARGET = pi4
|
||||||
|
else ifeq ($(UNAME), armv7l)
|
||||||
|
CFLAGS = $(STD_CFLAGS) -march=armv7-a -mtune=arm1176jzf-s -mfloat-abi=hard -mfpu=vfp -ffast-math -DRASPI=2
|
||||||
|
TARGET = pi2
|
||||||
|
else
|
||||||
|
CFLAGS = $(STD_CFLAGS)
|
||||||
|
TARGET = other
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(TARGET), other)
|
||||||
|
|
||||||
|
app: pitxft.o mailbox.o
|
||||||
|
$(CC) -o pitxft mailbox.o pitxft.o -lm
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
mailbox.o: mailbox.c mailbox.h
|
||||||
|
$(CC) $(CFLAGS) mailbox.c
|
||||||
|
|
||||||
|
pitxft.o: pitxft.c mailbox.h
|
||||||
|
$(CC) $(CFLAGS) pitxft.c
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o
|
||||||
268
fm_mpx.c
Normal file
268
fm_mpx.c
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
/*
|
||||||
|
PiFmAdv - Advanced FM transmitter for the Raspberry Pi
|
||||||
|
Copyright (C) 2017 Miegl
|
||||||
|
|
||||||
|
See https://github.com/Miegl/PiFmAdv
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sndfile.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "rds.h"
|
||||||
|
|
||||||
|
#define PI 3.14159265359
|
||||||
|
|
||||||
|
#define FIR_HALF_SIZE 30
|
||||||
|
#define FIR_SIZE (2*FIR_HALF_SIZE-1)
|
||||||
|
|
||||||
|
size_t length;
|
||||||
|
|
||||||
|
// coefficients of the low-pass FIR filter
|
||||||
|
double low_pass_fir[FIR_HALF_SIZE];
|
||||||
|
|
||||||
|
double carrier_38[] = {0.0, 0.8660254037844386, 0.8660254037844388, 1.2246467991473532e-16, -0.8660254037844384, -0.8660254037844386};
|
||||||
|
double carrier_19[] = {0.0, 0.5, 0.8660254037844386, 1.0, 0.8660254037844388, 0.5, 1.2246467991473532e-16, -0.5, -0.8660254037844384, -1.0, -0.8660254037844386, -0.5};
|
||||||
|
|
||||||
|
int phase_38 = 0;
|
||||||
|
int phase_19 = 0;
|
||||||
|
|
||||||
|
double downsample_factor;
|
||||||
|
|
||||||
|
double *audio_buffer;
|
||||||
|
int audio_index = 0;
|
||||||
|
int audio_len = 0;
|
||||||
|
double audio_pos;
|
||||||
|
|
||||||
|
double fir_buffer_mono[FIR_SIZE] = {0};
|
||||||
|
double fir_buffer_stereo[FIR_SIZE] = {0};
|
||||||
|
int fir_index = 0;
|
||||||
|
int channels;
|
||||||
|
|
||||||
|
double *last_buffer_val;
|
||||||
|
double preemphasis_prewarp;
|
||||||
|
double preemphasis_coefficient;
|
||||||
|
|
||||||
|
SNDFILE *inf;
|
||||||
|
|
||||||
|
double *alloc_empty_buffer(size_t length) {
|
||||||
|
double *p = malloc(length * sizeof(double));
|
||||||
|
if(p == NULL) return NULL;
|
||||||
|
|
||||||
|
bzero(p, length * sizeof(double));
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int fm_mpx_open(char *filename, size_t len, int cutoff_freq, int preemphasis_corner_freq, int srate, int nochan) {
|
||||||
|
length = len;
|
||||||
|
|
||||||
|
if(filename != NULL) {
|
||||||
|
// Open the input file
|
||||||
|
SF_INFO sfinfo;
|
||||||
|
|
||||||
|
if(filename[0] == '-' && srate > 0 && nochan > 0) {
|
||||||
|
printf("Using stdin for raw audio input at %d Hz.\n",srate);
|
||||||
|
sfinfo.samplerate = srate;
|
||||||
|
sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE;
|
||||||
|
sfinfo.channels = nochan;
|
||||||
|
sfinfo.frames = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stdin or file on the filesystem?
|
||||||
|
if(filename[0] == '-') {
|
||||||
|
if(!(inf = sf_open_fd(fileno(stdin), SFM_READ, &sfinfo, 0))) {
|
||||||
|
fprintf(stderr, "Error: could not open stdin for audio input.\n");
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
printf("Using stdin for audio input.\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(!(inf = sf_open(filename, SFM_READ, &sfinfo))) {
|
||||||
|
fprintf(stderr, "Error: could not open input file %s.\n", filename);
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
printf("Using audio file: %s\n", filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int in_samplerate = sfinfo.samplerate;
|
||||||
|
downsample_factor = 228000. / in_samplerate;
|
||||||
|
|
||||||
|
printf("Input: %d Hz, upsampling factor: %.2f\n", in_samplerate, downsample_factor);
|
||||||
|
|
||||||
|
channels = sfinfo.channels;
|
||||||
|
if(channels > 1) {
|
||||||
|
printf("%d channels, generating stereo multiplex.\n", channels);
|
||||||
|
} else {
|
||||||
|
printf("1 channel, monophonic operation.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the preemphasis
|
||||||
|
last_buffer_val = (double*) malloc(sizeof(double)*channels);
|
||||||
|
for(int i=0; i<channels; i++) last_buffer_val[i] = 0;
|
||||||
|
|
||||||
|
preemphasis_prewarp = tan(PI*preemphasis_corner_freq/in_samplerate);
|
||||||
|
preemphasis_coefficient = (1.0 + (1.0 - preemphasis_prewarp)/(1.0 + preemphasis_prewarp))/2.0;
|
||||||
|
printf("Created preemphasis with cutoff at %.1i Hz\n", preemphasis_corner_freq);
|
||||||
|
|
||||||
|
// Create the low-pass FIR filter
|
||||||
|
if(in_samplerate < cutoff_freq) cutoff_freq = in_samplerate;
|
||||||
|
// Here we divide this coefficient by two because it will be counted twice
|
||||||
|
// when applying the filter
|
||||||
|
low_pass_fir[FIR_HALF_SIZE-1] = 2 * cutoff_freq / 228000 /2;
|
||||||
|
|
||||||
|
// Only store half of the filter since it is symmetric
|
||||||
|
for(int i=1; i<FIR_HALF_SIZE; i++) {
|
||||||
|
low_pass_fir[FIR_HALF_SIZE-1-i] =
|
||||||
|
sin(2 * PI * cutoff_freq * i / 228000) / (PI * i) // sinc
|
||||||
|
* (.54 - .46 * cos(2*PI * (i+FIR_HALF_SIZE) / (2*FIR_HALF_SIZE))); // Hamming window
|
||||||
|
}
|
||||||
|
printf("Created low-pass FIR filter for audio channels, with cutoff at %.1i Hz\n", cutoff_freq);
|
||||||
|
|
||||||
|
audio_pos = downsample_factor;
|
||||||
|
audio_buffer = alloc_empty_buffer(length * channels);
|
||||||
|
if(audio_buffer == NULL) return -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
inf = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// samples provided by this function are in 0..10: they need to be divided by
|
||||||
|
// 10 after.
|
||||||
|
int fm_mpx_get_samples(double *mpx_buffer, double *rds_buffer, float mpx, int rds, int wait) {
|
||||||
|
if(inf == NULL) {
|
||||||
|
if(rds) get_rds_samples(mpx_buffer, length);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
if(rds) get_rds_samples(rds_buffer, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0; i<length; i++) {
|
||||||
|
if(audio_pos >= downsample_factor) {
|
||||||
|
audio_pos -= downsample_factor;
|
||||||
|
|
||||||
|
if(audio_len <= channels) {
|
||||||
|
for(int j=0; j<2; j++) { // one retry
|
||||||
|
audio_len = sf_read_double(inf, audio_buffer, length);
|
||||||
|
if (audio_len < 0) {
|
||||||
|
fprintf(stderr, "Error reading audio\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(audio_len == 0) {
|
||||||
|
if( sf_seek(inf, 0, SEEK_SET) < 0 ) {
|
||||||
|
if(wait) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Could not rewind in audio file, terminating\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//apply preemphasis
|
||||||
|
int k;
|
||||||
|
int l;
|
||||||
|
double tmp;
|
||||||
|
for(k=0; k<audio_len; k+=channels) {
|
||||||
|
for(l=0; l<channels; l++) {
|
||||||
|
tmp = audio_buffer[k+l];
|
||||||
|
audio_buffer[k+l] = audio_buffer[k+l] - preemphasis_coefficient*last_buffer_val[l];
|
||||||
|
last_buffer_val[l] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
audio_index = 0;
|
||||||
|
} else {
|
||||||
|
audio_index += channels;
|
||||||
|
audio_len -= channels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// First store the current sample(s) into the FIR filter's ring buffer
|
||||||
|
if(channels == 0) {
|
||||||
|
fir_buffer_mono[fir_index] = audio_buffer[audio_index];
|
||||||
|
} else {
|
||||||
|
// In stereo operation, generate sum and difference signals
|
||||||
|
fir_buffer_mono[fir_index] =
|
||||||
|
audio_buffer[audio_index] + audio_buffer[audio_index+1];
|
||||||
|
fir_buffer_stereo[fir_index] =
|
||||||
|
audio_buffer[audio_index] - audio_buffer[audio_index+1];
|
||||||
|
}
|
||||||
|
fir_index++;
|
||||||
|
if(fir_index >= FIR_SIZE) fir_index = 0;
|
||||||
|
|
||||||
|
// Now apply the FIR low-pass filter
|
||||||
|
|
||||||
|
/* As the FIR filter is symmetric, we do not multiply all
|
||||||
|
the coefficients independently, but two-by-two, thus reducing
|
||||||
|
the total number of multiplications by a factor of two
|
||||||
|
*/
|
||||||
|
double out_mono = 0;
|
||||||
|
double out_stereo = 0;
|
||||||
|
int ifbi = fir_index; // ifbi = increasing FIR Buffer Index
|
||||||
|
int dfbi = fir_index; // dfbi = decreasing FIR Buffer Index
|
||||||
|
for(int fi=0; fi<FIR_HALF_SIZE; fi++) { // fi = Filter Index
|
||||||
|
dfbi--;
|
||||||
|
if(dfbi < 0) dfbi = FIR_SIZE-1;
|
||||||
|
out_mono +=
|
||||||
|
low_pass_fir[fi] *
|
||||||
|
(fir_buffer_mono[ifbi] + fir_buffer_mono[dfbi]);
|
||||||
|
if(channels > 1) {
|
||||||
|
out_stereo +=
|
||||||
|
low_pass_fir[fi] *
|
||||||
|
(fir_buffer_stereo[ifbi] + fir_buffer_stereo[dfbi]);
|
||||||
|
}
|
||||||
|
ifbi++;
|
||||||
|
if(ifbi >= FIR_SIZE) ifbi = 0;
|
||||||
|
}
|
||||||
|
// End of FIR filter
|
||||||
|
|
||||||
|
if (channels>1) {
|
||||||
|
mpx_buffer[i] =
|
||||||
|
((mpx-2)/2) * out_mono +
|
||||||
|
((mpx-2)/2) * carrier_38[phase_38] * out_stereo +
|
||||||
|
1 * carrier_19[phase_19];
|
||||||
|
|
||||||
|
if (rds) {
|
||||||
|
mpx_buffer[i] += rds_buffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
phase_19++;
|
||||||
|
phase_38++;
|
||||||
|
if(phase_19 >= 12) phase_19 = 0;
|
||||||
|
if(phase_38 >= 6) phase_38 = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mpx_buffer[i] =
|
||||||
|
(mpx-1) * out_mono;
|
||||||
|
|
||||||
|
if (rds) {
|
||||||
|
mpx_buffer[i] += rds_buffer[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
audio_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int fm_mpx_close() {
|
||||||
|
if(sf_close(inf) ) {
|
||||||
|
fprintf(stderr, "Error closing audio file");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(audio_buffer != NULL) free(audio_buffer);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
10
fm_mpx.h
Normal file
10
fm_mpx.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
PiFmAdv - Advanced FM transmitter for the Raspberry Pi
|
||||||
|
Copyright (C) 2017 Miegl
|
||||||
|
|
||||||
|
See https://github.com/Miegl/PiFmAdv
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int fm_mpx_open(char *filename, size_t len, int cutoff_freq, int preemphasis_corner_freq, int srate, int nochan);
|
||||||
|
extern int fm_mpx_get_samples(double *mpx_buffer, double *rds_buffer, float mpx, int rds, int wait);
|
||||||
|
extern int fm_mpx_close();
|
||||||
262
mailbox.c
Normal file
262
mailbox.c
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2012, Broadcom Europe Ltd.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* 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.
|
||||||
|
* 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 <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
#include "mailbox.h"
|
||||||
|
|
||||||
|
#define PAGE_SIZE (4*1024)
|
||||||
|
|
||||||
|
void *mapmem(unsigned base, unsigned size)
|
||||||
|
{
|
||||||
|
int mem_fd;
|
||||||
|
unsigned offset = base % PAGE_SIZE;
|
||||||
|
base = base - offset;
|
||||||
|
size = size + offset;
|
||||||
|
/* open /dev/mem */
|
||||||
|
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
|
||||||
|
printf("can't open /dev/mem\nThis program should be run as root. Try prefixing command with: sudo\n");
|
||||||
|
exit (-1);
|
||||||
|
}
|
||||||
|
void *mem = mmap(
|
||||||
|
0,
|
||||||
|
size,
|
||||||
|
PROT_READ|PROT_WRITE,
|
||||||
|
MAP_SHARED/*|MAP_FIXED*/,
|
||||||
|
mem_fd,
|
||||||
|
base);
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("base=0x%x, mem=%p\n", base, mem);
|
||||||
|
#endif
|
||||||
|
if (mem == MAP_FAILED) {
|
||||||
|
printf("mmap error %d\n", (int)mem);
|
||||||
|
exit (-1);
|
||||||
|
}
|
||||||
|
close(mem_fd);
|
||||||
|
return (char *)mem + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unmapmem(void *addr, unsigned size)
|
||||||
|
{
|
||||||
|
const intptr_t offset = (intptr_t)addr % PAGE_SIZE;
|
||||||
|
addr = (char *)addr - offset;
|
||||||
|
size = size + offset;
|
||||||
|
int s = munmap(addr, size);
|
||||||
|
if (s != 0) {
|
||||||
|
printf("munmap error %d\n", s);
|
||||||
|
exit (-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* use ioctl to send mbox property message
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int mbox_property(int file_desc, void *buf)
|
||||||
|
{
|
||||||
|
int ret_val = ioctl(file_desc, IOCTL_MBOX_PROPERTY, buf);
|
||||||
|
|
||||||
|
if (ret_val < 0) {
|
||||||
|
printf("ioctl_set_msg failed:%d\n", ret_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
unsigned *p = buf; int i; unsigned size = *(unsigned *)buf;
|
||||||
|
for (i=0; i<size/4; i++)
|
||||||
|
printf("%04x: 0x%08x\n", i*sizeof *p, p[i]);
|
||||||
|
#endif
|
||||||
|
return ret_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned mem_alloc(int file_desc, unsigned size, unsigned align, unsigned flags)
|
||||||
|
{
|
||||||
|
int i=0;
|
||||||
|
unsigned p[32];
|
||||||
|
p[i++] = 0; // size
|
||||||
|
p[i++] = 0x00000000; // process request
|
||||||
|
|
||||||
|
p[i++] = 0x3000c; // (the tag id)
|
||||||
|
p[i++] = 12; // (size of the buffer)
|
||||||
|
p[i++] = 12; // (size of the data)
|
||||||
|
p[i++] = size; // (num bytes? or pages?)
|
||||||
|
p[i++] = align; // (alignment)
|
||||||
|
p[i++] = flags; // (MEM_FLAG_L1_NONALLOCATING)
|
||||||
|
|
||||||
|
p[i++] = 0x00000000; // end tag
|
||||||
|
p[0] = i*sizeof *p; // actual size
|
||||||
|
|
||||||
|
mbox_property(file_desc, p);
|
||||||
|
return p[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned mem_free(int file_desc, unsigned handle)
|
||||||
|
{
|
||||||
|
int i=0;
|
||||||
|
unsigned p[32];
|
||||||
|
p[i++] = 0; // size
|
||||||
|
p[i++] = 0x00000000; // process request
|
||||||
|
|
||||||
|
p[i++] = 0x3000f; // (the tag id)
|
||||||
|
p[i++] = 4; // (size of the buffer)
|
||||||
|
p[i++] = 4; // (size of the data)
|
||||||
|
p[i++] = handle;
|
||||||
|
|
||||||
|
p[i++] = 0x00000000; // end tag
|
||||||
|
p[0] = i*sizeof *p; // actual size
|
||||||
|
|
||||||
|
mbox_property(file_desc, p);
|
||||||
|
return p[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned mem_lock(int file_desc, unsigned handle)
|
||||||
|
{
|
||||||
|
int i=0;
|
||||||
|
unsigned p[32];
|
||||||
|
p[i++] = 0; // size
|
||||||
|
p[i++] = 0x00000000; // process request
|
||||||
|
|
||||||
|
p[i++] = 0x3000d; // (the tag id)
|
||||||
|
p[i++] = 4; // (size of the buffer)
|
||||||
|
p[i++] = 4; // (size of the data)
|
||||||
|
p[i++] = handle;
|
||||||
|
|
||||||
|
p[i++] = 0x00000000; // end tag
|
||||||
|
p[0] = i*sizeof *p; // actual size
|
||||||
|
|
||||||
|
mbox_property(file_desc, p);
|
||||||
|
return p[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned mem_unlock(int file_desc, unsigned handle)
|
||||||
|
{
|
||||||
|
int i=0;
|
||||||
|
unsigned p[32];
|
||||||
|
p[i++] = 0; // size
|
||||||
|
p[i++] = 0x00000000; // process request
|
||||||
|
|
||||||
|
p[i++] = 0x3000e; // (the tag id)
|
||||||
|
p[i++] = 4; // (size of the buffer)
|
||||||
|
p[i++] = 4; // (size of the data)
|
||||||
|
p[i++] = handle;
|
||||||
|
|
||||||
|
p[i++] = 0x00000000; // end tag
|
||||||
|
p[0] = i*sizeof *p; // actual size
|
||||||
|
|
||||||
|
mbox_property(file_desc, p);
|
||||||
|
return p[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned execute_code(int file_desc, unsigned code, unsigned r0, unsigned r1, unsigned r2, unsigned r3, unsigned r4, unsigned r5)
|
||||||
|
{
|
||||||
|
int i=0;
|
||||||
|
unsigned p[32];
|
||||||
|
p[i++] = 0; // size
|
||||||
|
p[i++] = 0x00000000; // process request
|
||||||
|
|
||||||
|
p[i++] = 0x30010; // (the tag id)
|
||||||
|
p[i++] = 28; // (size of the buffer)
|
||||||
|
p[i++] = 28; // (size of the data)
|
||||||
|
p[i++] = code;
|
||||||
|
p[i++] = r0;
|
||||||
|
p[i++] = r1;
|
||||||
|
p[i++] = r2;
|
||||||
|
p[i++] = r3;
|
||||||
|
p[i++] = r4;
|
||||||
|
p[i++] = r5;
|
||||||
|
|
||||||
|
p[i++] = 0x00000000; // end tag
|
||||||
|
p[0] = i*sizeof *p; // actual size
|
||||||
|
|
||||||
|
mbox_property(file_desc, p);
|
||||||
|
return p[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned qpu_enable(int file_desc, unsigned enable)
|
||||||
|
{
|
||||||
|
int i=0;
|
||||||
|
unsigned p[32];
|
||||||
|
|
||||||
|
p[i++] = 0; // size
|
||||||
|
p[i++] = 0x00000000; // process request
|
||||||
|
|
||||||
|
p[i++] = 0x30012; // (the tag id)
|
||||||
|
p[i++] = 4; // (size of the buffer)
|
||||||
|
p[i++] = 4; // (size of the data)
|
||||||
|
p[i++] = enable;
|
||||||
|
|
||||||
|
p[i++] = 0x00000000; // end tag
|
||||||
|
p[0] = i*sizeof *p; // actual size
|
||||||
|
|
||||||
|
mbox_property(file_desc, p);
|
||||||
|
return p[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned execute_qpu(int file_desc, unsigned num_qpus, unsigned control, unsigned noflush, unsigned timeout) {
|
||||||
|
int i=0;
|
||||||
|
unsigned p[32];
|
||||||
|
|
||||||
|
p[i++] = 0; // size
|
||||||
|
p[i++] = 0x00000000; // process request
|
||||||
|
p[i++] = 0x30011; // (the tag id)
|
||||||
|
p[i++] = 16; // (size of the buffer)
|
||||||
|
p[i++] = 16; // (size of the data)
|
||||||
|
p[i++] = num_qpus;
|
||||||
|
p[i++] = control;
|
||||||
|
p[i++] = noflush;
|
||||||
|
p[i++] = timeout; // ms
|
||||||
|
|
||||||
|
p[i++] = 0x00000000; // end tag
|
||||||
|
p[0] = i*sizeof *p; // actual size
|
||||||
|
|
||||||
|
mbox_property(file_desc, p);
|
||||||
|
return p[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
int mbox_open() {
|
||||||
|
int file_desc;
|
||||||
|
|
||||||
|
// open a char device file used for communicating with kernel mbox driver
|
||||||
|
file_desc = open(DEVICE_FILE_NAME, 0);
|
||||||
|
if (file_desc < 0) {
|
||||||
|
printf("Can't open device file: %s\n", DEVICE_FILE_NAME);
|
||||||
|
printf("Try creating a device file with: sudo mknod %s c %d 0\n", DEVICE_FILE_NAME, MAJOR_NUM);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
return file_desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mbox_close(int file_desc) {
|
||||||
|
close(file_desc);
|
||||||
|
}
|
||||||
47
mailbox.h
Normal file
47
mailbox.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2012, Broadcom Europe Ltd.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* 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.
|
||||||
|
* 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 <linux/ioctl.h>
|
||||||
|
|
||||||
|
#define MAJOR_NUM 100
|
||||||
|
#define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *)
|
||||||
|
#define DEVICE_FILE_NAME "/dev/vcio"
|
||||||
|
|
||||||
|
int mbox_open();
|
||||||
|
void mbox_close(int file_desc);
|
||||||
|
|
||||||
|
unsigned get_version(int file_desc);
|
||||||
|
unsigned mem_alloc(int file_desc, unsigned size, unsigned align, unsigned flags);
|
||||||
|
unsigned mem_free(int file_desc, unsigned handle);
|
||||||
|
unsigned mem_lock(int file_desc, unsigned handle);
|
||||||
|
unsigned mem_unlock(int file_desc, unsigned handle);
|
||||||
|
void *mapmem(unsigned base, unsigned size);
|
||||||
|
void unmapmem(void *addr, unsigned size);
|
||||||
|
|
||||||
|
unsigned execute_code(int file_desc, unsigned code, unsigned r0, unsigned r1, unsigned r2, unsigned r3, unsigned r4, unsigned r5);
|
||||||
|
unsigned execute_qpu(int file_desc, unsigned num_qpus, unsigned control, unsigned noflush, unsigned timeout);
|
||||||
|
unsigned qpu_enable(int file_desc, unsigned enable);
|
||||||
753
pitxft.c
Normal file
753
pitxft.c
Normal file
@@ -0,0 +1,753 @@
|
|||||||
|
/*
|
||||||
|
PiTxFt - Raspberry Pi ft transmitter
|
||||||
|
Copyright (C) 2021 Miegl
|
||||||
|
|
||||||
|
See https://github.com/Miegl/PiTxFt
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
#include "mailbox.h"
|
||||||
|
|
||||||
|
#define MBFILE DEVICE_FILE_NAME // From mailbox.h
|
||||||
|
|
||||||
|
#if (RASPI) == 1 // Original Raspberry Pi 1
|
||||||
|
#define PERIPH_VIRT_BASE 0x20000000
|
||||||
|
#define PERIPH_PHYS_BASE 0x7e000000
|
||||||
|
#define DRAM_PHYS_BASE 0x40000000
|
||||||
|
#define MEM_FLAG 0x0c
|
||||||
|
#define CLOCK_BASE 19.2e6
|
||||||
|
#define DMA_CHANNEL 14
|
||||||
|
#elif (RASPI) == 2 // Raspberry Pi 2 & 3
|
||||||
|
#define PERIPH_VIRT_BASE 0x3f000000
|
||||||
|
#define PERIPH_PHYS_BASE 0x7e000000
|
||||||
|
#define DRAM_PHYS_BASE 0xc0000000
|
||||||
|
#define MEM_FLAG 0x04
|
||||||
|
#define CLOCK_BASE 19.2e6
|
||||||
|
#define DMA_CHANNEL 14
|
||||||
|
#elif (RASPI) == 4 // Raspberry Pi 4
|
||||||
|
#define PERIPH_VIRT_BASE 0xfe000000
|
||||||
|
#define PERIPH_PHYS_BASE 0x7e000000
|
||||||
|
#define DRAM_PHYS_BASE 0xc0000000
|
||||||
|
#define MEM_FLAG 0x04
|
||||||
|
#define CLOCK_BASE 54.0e6
|
||||||
|
#define DMA_CHANNEL 6
|
||||||
|
#else
|
||||||
|
#error Unknown Raspberry Pi version (variable RASPI)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DMA_BASE_OFFSET 0x00007000
|
||||||
|
#define PWM_BASE_OFFSET 0x0020C000
|
||||||
|
#define PWM_LEN 0x28
|
||||||
|
#define CLK_BASE_OFFSET 0x00101000
|
||||||
|
#define CLK_LEN 0x1300
|
||||||
|
#define GPIO_BASE_OFFSET 0x00200000
|
||||||
|
#define GPIO_LEN 0x100
|
||||||
|
#define PCM_BASE_OFFSET 0x00203000
|
||||||
|
#define PCM_LEN 0x24
|
||||||
|
#define PAD_BASE_OFFSET 0x00100000
|
||||||
|
#define PAD_LEN (0x40/4) //0x64
|
||||||
|
|
||||||
|
#define DMA_VIRT_BASE (PERIPH_VIRT_BASE + DMA_BASE_OFFSET)
|
||||||
|
#define PWM_VIRT_BASE (PERIPH_VIRT_BASE + PWM_BASE_OFFSET)
|
||||||
|
#define CLK_VIRT_BASE (PERIPH_VIRT_BASE + CLK_BASE_OFFSET)
|
||||||
|
#define GPIO_VIRT_BASE (PERIPH_VIRT_BASE + GPIO_BASE_OFFSET)
|
||||||
|
#define PAD_VIRT_BASE (PERIPH_VIRT_BASE + PAD_BASE_OFFSET)
|
||||||
|
#define PCM_VIRT_BASE (PERIPH_VIRT_BASE + PCM_BASE_OFFSET)
|
||||||
|
|
||||||
|
#define PWM_PHYS_BASE (PERIPH_PHYS_BASE + PWM_BASE_OFFSET)
|
||||||
|
#define PCM_PHYS_BASE (PERIPH_PHYS_BASE + PCM_BASE_OFFSET)
|
||||||
|
#define GPIO_PHYS_BASE (PERIPH_PHYS_BASE + GPIO_BASE_OFFSET)
|
||||||
|
|
||||||
|
// GPIO
|
||||||
|
#define GPFSEL0 (0x00/4)
|
||||||
|
#define GPFSEL1 (0x04/4)
|
||||||
|
#define GPFSEL2 (0x08/4)
|
||||||
|
#define GPPUD (0x94/4)
|
||||||
|
#define GPPUDCLK0 (0x98/4)
|
||||||
|
#define GPPUDCLK1 (0x9C/4)
|
||||||
|
|
||||||
|
#define CORECLK_CNTL (0x08/4)
|
||||||
|
#define CORECLK_DIV (0x0c/4)
|
||||||
|
#define GPCLK_CNTL (0x70/4)
|
||||||
|
#define GPCLK_DIV (0x74/4)
|
||||||
|
#define EMMCCLK_CNTL (0x1C0/4)
|
||||||
|
#define EMMCCLK_DIV (0x1C4/4)
|
||||||
|
|
||||||
|
#define CM_LOCK (0x114/4)
|
||||||
|
#define CM_LOCK_FLOCKA (1<<8)
|
||||||
|
#define CM_LOCK_FLOCKB (1<<9)
|
||||||
|
#define CM_LOCK_FLOCKC (1<<10)
|
||||||
|
#define CM_LOCK_FLOCKD (1<<11)
|
||||||
|
#define CM_LOCK_FLOCKH (1<<12)
|
||||||
|
|
||||||
|
#define CM_PLLA (0x104/4)
|
||||||
|
#define CM_PLLC (0x108/4)
|
||||||
|
#define CM_PLLD (0x10c/4)
|
||||||
|
#define CM_PLLH (0x110/4)
|
||||||
|
#define CM_PLLB (0x170/4)
|
||||||
|
|
||||||
|
#define A2W_PLLA_ANA0 (0x1010/4)
|
||||||
|
#define A2W_PLLC_ANA0 (0x1030/4)
|
||||||
|
#define A2W_PLLD_ANA0 (0x1050/4)
|
||||||
|
#define A2W_PLLH_ANA0 (0x1070/4)
|
||||||
|
#define A2W_PLLB_ANA0 (0x10f0/4)
|
||||||
|
#define A2W_PLL_KA_SHIFT 7
|
||||||
|
#define A2W_PLL_KI_SHIFT 19
|
||||||
|
#define A2W_PLL_KP_SHIFT 15
|
||||||
|
|
||||||
|
#define PLLA_CTRL (0x1100/4)
|
||||||
|
#define PLLA_FRAC (0x1200/4)
|
||||||
|
#define PLLA_DSI0 (0x1300/4)
|
||||||
|
#define PLLA_CORE (0x1400/4)
|
||||||
|
#define PLLA_PER (0x1500/4)
|
||||||
|
#define PLLA_CCP2 (0x1600/4)
|
||||||
|
|
||||||
|
#define PLLB_CTRL (0x11e0/4)
|
||||||
|
#define PLLB_FRAC (0x12e0/4)
|
||||||
|
#define PLLB_ARM (0x13e0/4)
|
||||||
|
#define PLLB_SP0 (0x14e0/4)
|
||||||
|
#define PLLB_SP1 (0x15e0/4)
|
||||||
|
#define PLLB_SP2 (0x16e0/4)
|
||||||
|
|
||||||
|
#define PLLC_CTRL (0x1120/4)
|
||||||
|
#define PLLC_FRAC (0x1220/4)
|
||||||
|
#define PLLC_CORE2 (0x1320/4)
|
||||||
|
#define PLLC_CORE1 (0x1420/4)
|
||||||
|
#define PLLC_PER (0x1520/4)
|
||||||
|
#define PLLC_CORE0 (0x1620/4)
|
||||||
|
|
||||||
|
#define PLLD_CTRL (0x1140/4)
|
||||||
|
#define PLLD_FRAC (0x1240/4)
|
||||||
|
#define PLLD_DSI0 (0x1340/4)
|
||||||
|
#define PLLD_CORE (0x1440/4)
|
||||||
|
#define PLLD_PER (0x1540/4)
|
||||||
|
#define PLLD_DSI1 (0x1640/4)
|
||||||
|
|
||||||
|
#define PLLH_CTRL (0x1160/4)
|
||||||
|
#define PLLH_FRAC (0x1260/4)
|
||||||
|
#define PLLH_AUX (0x1360/4)
|
||||||
|
#define PLLH_RCAL (0x1460/4)
|
||||||
|
#define PLLH_PIX (0x1560/4)
|
||||||
|
#define PLLH_STS (0x1660/4)
|
||||||
|
|
||||||
|
// PWM
|
||||||
|
#define PWM_CTL (0x00/4)
|
||||||
|
#define PWM_DMAC (0x08/4)
|
||||||
|
#define PWM_RNG1 (0x10/4)
|
||||||
|
#define PWM_RNG2 (0x20/4)
|
||||||
|
#define PWM_FIFO (0x18/4)
|
||||||
|
|
||||||
|
#define PWMCLK_CNTL 40
|
||||||
|
#define PWMCLK_DIV 41
|
||||||
|
|
||||||
|
#define PWMCTL_PWEN1 (1<<0)
|
||||||
|
#define PWMCTL_MODE1 (1<<1)
|
||||||
|
#define PWMCTL_RPTL1 (1<<2)
|
||||||
|
#define PWMCTL_POLA1 (1<<4)
|
||||||
|
#define PWMCTL_USEF1 (1<<5)
|
||||||
|
#define PWMCTL_CLRF (1<<6)
|
||||||
|
#define PWMCTL_MSEN1 (1<<7)
|
||||||
|
#define PWMCTL_PWEN2 (1<<8)
|
||||||
|
#define PWMCTL_MODE2 (1<<9)
|
||||||
|
#define PWMCTL_RPTL2 (1<<10)
|
||||||
|
#define PWMCTL_USEF2 (1<<13)
|
||||||
|
#define PWMCTL_MSEN2 (1<<15)
|
||||||
|
|
||||||
|
#define PWMDMAC_ENAB (1<<31)
|
||||||
|
#define PWMDMAC_THRSHLD ((15<<8)|(15<<0))
|
||||||
|
|
||||||
|
// PCM
|
||||||
|
#define PCM_CS_A (0x00/4)
|
||||||
|
#define PCM_FIFO_A (0x04/4)
|
||||||
|
#define PCM_MODE_A (0x08/4)
|
||||||
|
#define PCM_RXC_A (0x0c/4)
|
||||||
|
#define PCM_TXC_A (0x10/4)
|
||||||
|
#define PCM_DREQ_A (0x14/4)
|
||||||
|
#define PCM_INTEN_A (0x18/4)
|
||||||
|
#define PCM_INT_STC_A (0x1c/4)
|
||||||
|
#define PCM_GRAY (0x20/4)
|
||||||
|
|
||||||
|
#define PCMCLK_CNTL 38
|
||||||
|
#define PCMCLK_DIV 39
|
||||||
|
|
||||||
|
// PAD
|
||||||
|
#define GPIO_PAD_0_27 (0x2C/4)
|
||||||
|
#define GPIO_PAD_28_45 (0x30/4)
|
||||||
|
#define GPIO_PAD_46_52 (0x34/4)
|
||||||
|
|
||||||
|
// DMA
|
||||||
|
#define DMA_CHANNEL_MAX 14
|
||||||
|
#define DMA_CHANNEL_SIZE 0x100
|
||||||
|
|
||||||
|
#define BCM2708_DMA_ACTIVE (1<<0)
|
||||||
|
#define BCM2708_DMA_END (1<<1)
|
||||||
|
#define BCM2708_DMA_INT (1<<2)
|
||||||
|
#define BCM2708_DMA_WAIT_RESP (1<<3)
|
||||||
|
#define BCM2708_DMA_D_DREQ (1<<6)
|
||||||
|
#define BCM2708_DMA_DST_IGNOR (1<<7)
|
||||||
|
#define BCM2708_DMA_SRC_INC (1<<8)
|
||||||
|
#define BCM2708_DMA_SRC_IGNOR (1<<11)
|
||||||
|
#define BCM2708_DMA_NO_WIDE_BURSTS (1<<26)
|
||||||
|
#define BCM2708_DMA_DISDEBUG (1<<28)
|
||||||
|
#define BCM2708_DMA_ABORT (1<<30)
|
||||||
|
#define BCM2708_DMA_RESET (1<<31)
|
||||||
|
#define BCM2708_DMA_PER_MAP(x) ((x)<<16)
|
||||||
|
#define BCM2708_DMA_PRIORITY(x) ((x)&0xf << 16)
|
||||||
|
#define BCM2708_DMA_PANIC_PRIORITY(x) ((x)&0xf << 20)
|
||||||
|
|
||||||
|
#define DMA_CS (0x00/4)
|
||||||
|
#define DMA_CONBLK_AD (0x04/4)
|
||||||
|
#define DMA_DEBUG (0x20/4)
|
||||||
|
|
||||||
|
#define DMA_CS_RESET (1<<31)
|
||||||
|
#define DMA_CS_ABORT (1<<30)
|
||||||
|
#define DMA_CS_DISDEBUG (1<<29)
|
||||||
|
#define DMA_CS_WAIT_FOR_OUTSTANDING_WRITES (1<<28)
|
||||||
|
#define DMA_CS_INT (1<<2)
|
||||||
|
#define DMA_CS_END (1<<1)
|
||||||
|
#define DMA_CS_ACTIVE (1<<0)
|
||||||
|
#define DMA_CS_PRIORITY(x) ((x)&0xf << 16)
|
||||||
|
#define DMA_CS_PANIC_PRIORITY(x) ((x)&0xf << 20)
|
||||||
|
|
||||||
|
#define DREQ_PCM_TX 2
|
||||||
|
#define DREQ_PCM_RX 3
|
||||||
|
#define DREQ_SMI 4
|
||||||
|
#define DREQ_PWM 5
|
||||||
|
#define DREQ_SPI_TX 6
|
||||||
|
#define DREQ_SPI_RX 7
|
||||||
|
#define DREQ_SPI_SLAVE_TX 8
|
||||||
|
#define DREQ_SPI_SLAVE_RX 9
|
||||||
|
|
||||||
|
#define MEM_FLAG_DISCARDABLE (1 << 0) /* can be resized to 0 at any time. Use for cached data */
|
||||||
|
#define MEM_FLAG_NORMAL (0 << 2) /* normal allocating alias. Don't use from ARM */
|
||||||
|
#define MEM_FLAG_DIRECT (1 << 2) /* 0xC alias uncached */
|
||||||
|
#define MEM_FLAG_COHERENT (2 << 2) /* 0x8 alias. Non-allocating in L2 but coherent */
|
||||||
|
#define MEM_FLAG_L1_NONALLOCATING (MEM_FLAG_DIRECT | MEM_FLAG_COHERENT) /* Allocating in L2 */
|
||||||
|
#define MEM_FLAG_ZERO (1 << 4) /* initialise buffer to all zeros */
|
||||||
|
#define MEM_FLAG_NO_INIT (1 << 5) /* don't initialise (default is initialise to all ones */
|
||||||
|
#define MEM_FLAG_HINT_PERMALOCK (1 << 6) /* Likely to be locked for long periods of time. */
|
||||||
|
|
||||||
|
#define BUS_TO_PHYS(x) ((x)&~0xC0000000)
|
||||||
|
|
||||||
|
#define PAGE_SIZE 4096
|
||||||
|
#define PAGE_SHIFT 12
|
||||||
|
#define NUM_PAGES ((sizeof(struct control_data_s) + PAGE_SIZE - 1) >> PAGE_SHIFT)
|
||||||
|
|
||||||
|
#define NUM_SAMPLES 64000
|
||||||
|
#define NUM_CBS (NUM_SAMPLES * 2)
|
||||||
|
|
||||||
|
|
||||||
|
struct __attribute__((__packed__)) ft {
|
||||||
|
double frequency;
|
||||||
|
uint32_t duration;
|
||||||
|
uint32_t padding;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t info, src, dst, length, stride, next, pad[2];
|
||||||
|
} dma_cb_t;
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
int handle; /* From mbox_open() */
|
||||||
|
unsigned mem_ref; /* From mem_alloc() */
|
||||||
|
unsigned bus_addr; /* From mem_lock() */
|
||||||
|
uint8_t *virt_addr; /* From mapmem() */
|
||||||
|
} mbox;
|
||||||
|
|
||||||
|
struct control_data_s {
|
||||||
|
dma_cb_t cb[NUM_CBS];
|
||||||
|
uint32_t sample[NUM_SAMPLES];
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct control_data_s *ctl;
|
||||||
|
|
||||||
|
static volatile uint32_t *pwm_reg;
|
||||||
|
static volatile uint32_t *clk_reg;
|
||||||
|
static volatile uint32_t *dma_reg;
|
||||||
|
static volatile uint32_t *gpio_reg;
|
||||||
|
static volatile uint32_t *pcm_reg;
|
||||||
|
static volatile uint32_t *pad_reg;
|
||||||
|
|
||||||
|
static void udelay(int us)
|
||||||
|
{
|
||||||
|
struct timespec ts = { 0, us * 1000 };
|
||||||
|
nanosleep(&ts, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void terminate(int num)
|
||||||
|
{
|
||||||
|
// Stop outputting and generating the clock.
|
||||||
|
if (clk_reg && gpio_reg && mbox.virt_addr) {
|
||||||
|
// Set GPIOs to be an output (instead of ALT FUNC 0, which is the clock).
|
||||||
|
gpio_reg[0] = (gpio_reg[0] & ~(7 << 12)) | (1 << 12); //GPIO4
|
||||||
|
udelay(10);
|
||||||
|
gpio_reg[2] = (gpio_reg[2] & ~(7 << 0)) | (1 << 0); //GPIO20
|
||||||
|
udelay(10);
|
||||||
|
gpio_reg[3] = (gpio_reg[3] & ~(7 << 6)) | (1 << 6); //GPIO32
|
||||||
|
udelay(10);
|
||||||
|
//gpio_reg[3] = (gpio_reg[3] & ~(7 << 12)) | (1 << 12); //GPIO34 - Doesn't work on Pi 3, 3B+, Zero W
|
||||||
|
//udelay(10);
|
||||||
|
|
||||||
|
// Disable the clock generator.
|
||||||
|
clk_reg[GPCLK_CNTL] = 0x5A;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dma_reg && mbox.virt_addr) {
|
||||||
|
dma_reg[DMA_CS] = BCM2708_DMA_RESET;
|
||||||
|
udelay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mbox.virt_addr != NULL) {
|
||||||
|
unmapmem(mbox.virt_addr, NUM_PAGES * PAGE_SIZE);
|
||||||
|
mem_unlock(mbox.handle, mbox.mem_ref);
|
||||||
|
mem_free(mbox.handle, mbox.mem_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Terminating: cleanly deactivated the DMA engine and killed the carrier.\n");
|
||||||
|
|
||||||
|
exit(num);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fatal(char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
fprintf(stderr,"ERROR: ");
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
terminate(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void warn(char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
fprintf(stderr,"WARNING: ");
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t mem_virt_to_phys(void *virt)
|
||||||
|
{
|
||||||
|
uint32_t offset = (uint8_t *)virt - mbox.virt_addr;
|
||||||
|
|
||||||
|
return mbox.bus_addr + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t mem_phys_to_virt(uint32_t phys)
|
||||||
|
{
|
||||||
|
return phys - (uint32_t)mbox.bus_addr + (uint32_t)mbox.virt_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *map_peripheral(uint32_t base, uint32_t len)
|
||||||
|
{
|
||||||
|
int fd = open("/dev/mem", O_RDWR | O_SYNC);
|
||||||
|
void * vaddr;
|
||||||
|
|
||||||
|
if (fd < 0)
|
||||||
|
fatal("Failed to open /dev/mem: %m.\n");
|
||||||
|
vaddr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, base);
|
||||||
|
if (vaddr == MAP_FAILED)
|
||||||
|
fatal("Failed to map peripheral at 0x%08x: %m.\n", base);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return vaddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int tx(uint32_t carrier_freq, int divider, char *ft_file, float ppm, int power, int gpio, bool repeat) {
|
||||||
|
// Catch only important signals
|
||||||
|
for (int i = 0; i < 25; i++) {
|
||||||
|
struct sigaction sa;
|
||||||
|
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sa.sa_handler = terminate;
|
||||||
|
sigaction(i, &sa, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fd = NULL;
|
||||||
|
|
||||||
|
if(ft_file != NULL) {
|
||||||
|
if(ft_file[0] == '-') {
|
||||||
|
if(!(fd = freopen(NULL, "rb", stdin))) {
|
||||||
|
fatal("Error: could not reopen stdin.\n");
|
||||||
|
} else {
|
||||||
|
printf("Using stdin for input.\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(!(fd = fopen(ft_file, "rb"))) {
|
||||||
|
fatal("Error: could not open input file %s.\n", ft_file);
|
||||||
|
} else {
|
||||||
|
printf("Using input file: %s\n", ft_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fatal("Error: no input file specified.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_reg = map_peripheral(DMA_VIRT_BASE, (DMA_CHANNEL_SIZE * (DMA_CHANNEL_MAX + 1)));
|
||||||
|
dma_reg = dma_reg + ((DMA_CHANNEL_SIZE / sizeof(int)) * (DMA_CHANNEL));
|
||||||
|
pwm_reg = map_peripheral(PWM_VIRT_BASE, PWM_LEN);
|
||||||
|
clk_reg = map_peripheral(CLK_VIRT_BASE, CLK_LEN);
|
||||||
|
gpio_reg = map_peripheral(GPIO_VIRT_BASE, GPIO_LEN);
|
||||||
|
pcm_reg = map_peripheral(PCM_VIRT_BASE, PCM_LEN);
|
||||||
|
pad_reg = map_peripheral(PAD_VIRT_BASE, PAD_LEN);
|
||||||
|
uint32_t freq_ctl;
|
||||||
|
|
||||||
|
// Use the mailbox interface to the VC to ask for physical memory.
|
||||||
|
mbox.handle = mbox_open();
|
||||||
|
if (mbox.handle < 0)
|
||||||
|
fatal("Failed to open mailbox. Check kernel support for vcio / BCM2708 mailbox.\n");
|
||||||
|
printf("Allocating physical memory: size = %d, ", NUM_PAGES * PAGE_SIZE);
|
||||||
|
if(!(mbox.mem_ref = mem_alloc(mbox.handle, NUM_PAGES * PAGE_SIZE, PAGE_SIZE, MEM_FLAG))) {
|
||||||
|
fatal("Could not allocate memory.\n");
|
||||||
|
}
|
||||||
|
printf("mem_ref = %u, ", mbox.mem_ref);
|
||||||
|
if(!(mbox.bus_addr = mem_lock(mbox.handle, mbox.mem_ref))) {
|
||||||
|
fatal("Could not lock memory.\n");
|
||||||
|
}
|
||||||
|
printf("bus_addr = %x, ", mbox.bus_addr);
|
||||||
|
if(!(mbox.virt_addr = mapmem(BUS_TO_PHYS(mbox.bus_addr), NUM_PAGES * PAGE_SIZE))) {
|
||||||
|
fatal("Could not map memory.\n");
|
||||||
|
}
|
||||||
|
printf("virt_addr = %p\n", mbox.virt_addr);
|
||||||
|
|
||||||
|
clk_reg[GPCLK_CNTL] = (0x5a<<24) | (1<<4) | (4);
|
||||||
|
udelay(100);
|
||||||
|
|
||||||
|
clk_reg[CM_PLLA] = 0x5A00022A; // Enable PLLA_PER
|
||||||
|
udelay(100);
|
||||||
|
|
||||||
|
int ana[4];
|
||||||
|
for (int i = 3; i >= 0; i--)
|
||||||
|
{
|
||||||
|
ana[i] = clk_reg[(A2W_PLLA_ANA0) + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
ana[1]&=~(1<<14);
|
||||||
|
for (int i = 3; i >= 0; i--)
|
||||||
|
{
|
||||||
|
clk_reg[(A2W_PLLA_ANA0) + i] = (0x5A << 24) | ana[i];
|
||||||
|
}
|
||||||
|
udelay(100);
|
||||||
|
|
||||||
|
clk_reg[PLLA_CORE] = (0x5a<<24) | (1<<8); // Disable
|
||||||
|
clk_reg[PLLA_PER] = 0x5A000001; // Div
|
||||||
|
udelay(100);
|
||||||
|
|
||||||
|
|
||||||
|
// Adjust PLLA frequency
|
||||||
|
freq_ctl = (unsigned int)(((carrier_freq*divider)/CLOCK_BASE*((double)(1<<20))));
|
||||||
|
clk_reg[PLLA_CTRL] = (0x5a<<24) | (0x21<<12) | (freq_ctl>>20); // Integer part
|
||||||
|
freq_ctl&=0xFFFFF;
|
||||||
|
clk_reg[PLLA_FRAC] = (0x5a<<24) | (freq_ctl&0xFFFFC); // Fractional part
|
||||||
|
udelay(100);
|
||||||
|
|
||||||
|
if ((clk_reg[CM_LOCK] & CM_LOCK_FLOCKA) > 0)
|
||||||
|
printf("Master PLLA Locked\n");
|
||||||
|
else
|
||||||
|
printf("Warning: Master PLLA NOT Locked\n");
|
||||||
|
|
||||||
|
// Program GPCLK integer division
|
||||||
|
//int clktmp;
|
||||||
|
//clktmp = clk_reg[GPCLK_CNTL];
|
||||||
|
//clk_reg[GPCLK_CNTL] = (0xF0F&clktmp) | (0x5a<<24); // Clear run
|
||||||
|
//udelay(100);
|
||||||
|
clk_reg[GPCLK_DIV] = (0x5a<<24) | (divider<<12);
|
||||||
|
udelay(100);
|
||||||
|
clk_reg[GPCLK_CNTL] = (0x5a<<24) | (4); // Source = PLLA (4)
|
||||||
|
udelay(100);
|
||||||
|
clk_reg[GPCLK_CNTL] = (0x5a<<24) | (1<<4) | (4); // Run, Source = PLLA (4)
|
||||||
|
udelay(100);
|
||||||
|
|
||||||
|
// Drive Strength: 0 = 2mA, 7 = 16mA. Ref: https://www.scribd.com/doc/101830961/GPIO-Pads-Control2
|
||||||
|
pad_reg[GPIO_PAD_0_27] = 0x5a000018 + power;
|
||||||
|
pad_reg[GPIO_PAD_28_45] = 0x5a000018 + power;
|
||||||
|
udelay(100);
|
||||||
|
|
||||||
|
int reg = gpio / 10;
|
||||||
|
int shift = (gpio % 10) * 3;
|
||||||
|
int mode;
|
||||||
|
if(gpio == 20) {
|
||||||
|
mode = 2;
|
||||||
|
} else {
|
||||||
|
mode = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GPIO needs to be ALT FUNC 0 to output the clock
|
||||||
|
gpio_reg[reg] = (gpio_reg[reg] & ~(7 << shift)) | (mode << shift);
|
||||||
|
udelay(100);
|
||||||
|
|
||||||
|
ctl = (struct control_data_s *) mbox.virt_addr;
|
||||||
|
dma_cb_t *cbp = ctl->cb;
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||||
|
ctl->sample[i] = 0x5a << 24 | freq_ctl; // Silence
|
||||||
|
// Write a frequency sample
|
||||||
|
cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP;
|
||||||
|
cbp->src = mem_virt_to_phys(ctl->sample + i);
|
||||||
|
cbp->dst = PERIPH_PHYS_BASE + (PLLA_FRAC<<2) + CLK_BASE_OFFSET;
|
||||||
|
cbp->length = 4;
|
||||||
|
cbp->stride = 0;
|
||||||
|
cbp->next = mem_virt_to_phys(cbp + 1);
|
||||||
|
cbp++;
|
||||||
|
// Delay
|
||||||
|
cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(5);
|
||||||
|
cbp->src = mem_virt_to_phys(mbox.virt_addr);
|
||||||
|
cbp->dst = PERIPH_PHYS_BASE + (PWM_FIFO<<2) + PWM_BASE_OFFSET;
|
||||||
|
cbp->length = 4;
|
||||||
|
cbp->stride = 0;
|
||||||
|
cbp->next = mem_virt_to_phys(cbp + 1);
|
||||||
|
cbp++;
|
||||||
|
}
|
||||||
|
cbp--;
|
||||||
|
cbp->next = mem_virt_to_phys(mbox.virt_addr);
|
||||||
|
|
||||||
|
// Here we define the rate at which we want to update the GPCLK control register
|
||||||
|
double srdivider = (((double)carrier_freq*divider/1e3)/(2*200*(1.+ppm/1.e6)));
|
||||||
|
uint32_t idivider = (uint32_t)srdivider;
|
||||||
|
uint32_t fdivider = (uint32_t)((srdivider - idivider)*pow(2, 12));
|
||||||
|
|
||||||
|
printf("PPM correction is %.4f, divider is %.4f (%d + %d*2^-12).\n", ppm, srdivider, idivider, fdivider);
|
||||||
|
|
||||||
|
pwm_reg[PWM_CTL] = 0;
|
||||||
|
udelay(100);
|
||||||
|
clk_reg[PWMCLK_CNTL] = (0x5a<<24) | (4); // Source = PLLA & disable
|
||||||
|
udelay(100);
|
||||||
|
clk_reg[PWMCLK_DIV] = (0x5a<<24) | (idivider<<12) | fdivider;
|
||||||
|
udelay(100);
|
||||||
|
clk_reg[PWMCLK_CNTL] = (0x5a<<24) | (1<<9) | (1<<4) | (4); // Source = PLLA, enable, MASH setting 1
|
||||||
|
udelay(100);
|
||||||
|
pwm_reg[PWM_RNG1] = 2;
|
||||||
|
udelay(100);
|
||||||
|
pwm_reg[PWM_DMAC] = PWMDMAC_ENAB | PWMDMAC_THRSHLD;
|
||||||
|
udelay(100);
|
||||||
|
pwm_reg[PWM_CTL] = PWMCTL_CLRF;
|
||||||
|
udelay(100);
|
||||||
|
pwm_reg[PWM_CTL] = PWMCTL_USEF1 | PWMCTL_MODE1 | PWMCTL_PWEN1 | PWMCTL_MSEN1;
|
||||||
|
//pwm_reg[PWM_CTL] = PWMCTL_USEF1 | PWMCTL_PWEN1;
|
||||||
|
udelay(100);
|
||||||
|
|
||||||
|
// Initialise the DMA
|
||||||
|
dma_reg[DMA_CS] = BCM2708_DMA_RESET;
|
||||||
|
udelay(100);
|
||||||
|
dma_reg[DMA_CS] = BCM2708_DMA_INT | BCM2708_DMA_END;
|
||||||
|
dma_reg[DMA_CONBLK_AD] = mem_virt_to_phys(ctl->cb);
|
||||||
|
dma_reg[DMA_DEBUG] = 7; // clear debug error flags
|
||||||
|
dma_reg[DMA_CS] = BCM2708_DMA_PRIORITY(15) | BCM2708_DMA_PANIC_PRIORITY(15) | BCM2708_DMA_DISDEBUG | BCM2708_DMA_ACTIVE;
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t last_cb = (uint32_t)ctl->cb;
|
||||||
|
|
||||||
|
struct ft* ft_data = NULL;
|
||||||
|
size_t ft_len = 500;
|
||||||
|
size_t ft_index = 0;
|
||||||
|
ft_data = calloc(ft_len, sizeof(struct ft));
|
||||||
|
|
||||||
|
//while(ft_index += fread(&ft_data[ft_index], sizeof(struct ft), ft_len - ft_index, fd) == (ft_len - ft_index)) {
|
||||||
|
// ft_len *= 2;
|
||||||
|
// ft_data = reallocarray(ft_data, ft_len, sizeof(struct ft));
|
||||||
|
//}
|
||||||
|
|
||||||
|
while(fread(&ft_data[ft_index++], sizeof(struct ft), 1, fd) > 0) {
|
||||||
|
if(ft_index == ft_len - 1) {
|
||||||
|
ft_len *= 2;
|
||||||
|
ft_data = reallocarray(ft_data, ft_len, sizeof(struct ft));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double *buffer;
|
||||||
|
size_t buffer_len = 0;
|
||||||
|
size_t buffer_index = 0;
|
||||||
|
for(size_t i = 0; i < ft_index; i++) {
|
||||||
|
buffer_len += (uint64_t)ft_data[i].duration*200000/1000000000;
|
||||||
|
}
|
||||||
|
buffer = calloc(buffer_len, sizeof(double));
|
||||||
|
for(size_t i = 0; i < ft_index; i++) {
|
||||||
|
for(size_t j = 0; j < ((uint64_t)ft_data[i].duration*200000/1000000000); j++) {
|
||||||
|
buffer[buffer_index++] = ft_data[i].frequency;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer_index = 0;
|
||||||
|
|
||||||
|
printf("Starting to transmit on %3.1f MHz.\n", carrier_freq/1e6);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
uint32_t cur_cb = (int)mem_phys_to_virt(dma_reg[DMA_CONBLK_AD]);
|
||||||
|
int last_sample = (last_cb - (int)mbox.virt_addr) / (sizeof(dma_cb_t) * 2);
|
||||||
|
int this_sample = (cur_cb - (int)mbox.virt_addr) / (sizeof(dma_cb_t) * 2);
|
||||||
|
int free_slots = this_sample - last_sample;
|
||||||
|
|
||||||
|
if (free_slots < 0)
|
||||||
|
free_slots += NUM_SAMPLES;
|
||||||
|
|
||||||
|
while (free_slots >= 1) {
|
||||||
|
// Get more baseband samples if necessary
|
||||||
|
|
||||||
|
if(repeat && (buffer_index == buffer_len)) buffer_index = 0;
|
||||||
|
|
||||||
|
uint32_t freqval = (uint32_t)(((buffer[buffer_index++]*divider)/CLOCK_BASE*((double)(1<<20))));
|
||||||
|
freqval &= 0xFFFFF;
|
||||||
|
|
||||||
|
ctl->sample[last_sample++] = (0x5A << 24 | freqval);
|
||||||
|
if (last_sample == NUM_SAMPLES)
|
||||||
|
last_sample = 0;
|
||||||
|
|
||||||
|
free_slots -= 1;
|
||||||
|
}
|
||||||
|
last_cb = (uint32_t)mbox.virt_addr + last_sample * sizeof(dma_cb_t) * 2;
|
||||||
|
|
||||||
|
usleep(5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
int opt;
|
||||||
|
|
||||||
|
char *ft_file = NULL;
|
||||||
|
uint32_t carrier_freq = 87600000;
|
||||||
|
float ppm = 0;
|
||||||
|
int divc = 0;
|
||||||
|
int power = 7;
|
||||||
|
int gpio = 4;
|
||||||
|
bool repeat = false;
|
||||||
|
|
||||||
|
const char *short_opt = "i:f:p:d:w:g:rh";
|
||||||
|
struct option long_opt[] =
|
||||||
|
{
|
||||||
|
{"input", required_argument, NULL, 'i'},
|
||||||
|
{"freq", required_argument, NULL, 'f'},
|
||||||
|
{"ppm", required_argument, NULL, 'p'},
|
||||||
|
{"div", required_argument, NULL, 'd'},
|
||||||
|
{"power", required_argument, NULL, 'w'},
|
||||||
|
{"gpio", required_argument, NULL, 'g'},
|
||||||
|
{"repeat", no_argument, NULL, 'r'},
|
||||||
|
|
||||||
|
{"help", no_argument, NULL, 'h'},
|
||||||
|
{ 0, 0, 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1)
|
||||||
|
{
|
||||||
|
switch(opt)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'i': //input file
|
||||||
|
ft_file = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'f': //freq
|
||||||
|
carrier_freq = 1e6 * atof(optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'p': //ppm
|
||||||
|
ppm = atof(optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'd': //div
|
||||||
|
divc = atoi(optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'w': //power
|
||||||
|
power = atoi(optarg);
|
||||||
|
if(power < 0 || power > 7)
|
||||||
|
fatal("Output power has to be set in range of 0 - 7\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'g': //gpio
|
||||||
|
gpio = atoi(optarg);
|
||||||
|
if(gpio != 4 && gpio != 20 && gpio != 32) // && gpio != 34)
|
||||||
|
fatal("Available GPIO pins: 4,20,32\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'r': //repeat
|
||||||
|
repeat = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'h': //help
|
||||||
|
fatal("Help:\n"
|
||||||
|
"Syntax: pitxft [--input (-i) file] [--freq (-f) frequency] [--ppm (-p) ppm-error] ...\n");
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ':':
|
||||||
|
fatal("%s: option '-%c' requires an argument\n", argv[0], optopt);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '?':
|
||||||
|
default:
|
||||||
|
fatal("%s: option '-%c' is invalid. See -h (--help)\n", argv[0], optopt);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double xtal_freq_recip=1.0/CLOCK_BASE;
|
||||||
|
int divider, best_divider = 0;
|
||||||
|
int min_int_multiplier, max_int_multiplier;
|
||||||
|
int int_multiplier;
|
||||||
|
double frac_multiplier;
|
||||||
|
int fom, best_fom = 0;
|
||||||
|
int solution_count = 0;
|
||||||
|
for(divider = 2; divider < 50; divider += 1)
|
||||||
|
{
|
||||||
|
if(carrier_freq * divider > 1400e6) break;
|
||||||
|
|
||||||
|
max_int_multiplier=((int)((double)(carrier_freq + 10 + (1 * 1000)) * divider * xtal_freq_recip));
|
||||||
|
min_int_multiplier=((int)((double)(carrier_freq - 10 - (1 * 1000)) * divider * xtal_freq_recip));
|
||||||
|
if(min_int_multiplier != max_int_multiplier) continue;
|
||||||
|
|
||||||
|
solution_count++;
|
||||||
|
fom = 0;
|
||||||
|
|
||||||
|
if(carrier_freq * divider > 900e6) fom++; // Prefer frequencies close to 1.0 Ghz
|
||||||
|
if(carrier_freq * divider < 1100e6) fom++;
|
||||||
|
|
||||||
|
if(carrier_freq * divider > 800e6) fom++;
|
||||||
|
if(carrier_freq * divider < 1200e6) fom++;
|
||||||
|
|
||||||
|
frac_multiplier = ((double)(carrier_freq) * divider * xtal_freq_recip);
|
||||||
|
int_multiplier = (int)frac_multiplier;
|
||||||
|
frac_multiplier = frac_multiplier - int_multiplier;
|
||||||
|
if((frac_multiplier > 0.2) && (frac_multiplier < 0.8)) fom++; // Prefer mulipliers away from integer boundaries
|
||||||
|
|
||||||
|
if(fom > best_fom) // Best match so far
|
||||||
|
{
|
||||||
|
best_fom = fom;
|
||||||
|
best_divider = divider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(divc) {
|
||||||
|
best_divider = divc;
|
||||||
|
}
|
||||||
|
else if(!solution_count & !best_divider) {
|
||||||
|
fatal("No tuning solution found. You can specify the divider manually by setting the -div parameter.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Carrier: %3.2f Mhz, VCO: %4.1f MHz, Multiplier: %f, Divider: %d\n", carrier_freq/1e6, (double)carrier_freq * best_divider / 1e6, carrier_freq * best_divider * xtal_freq_recip, best_divider);
|
||||||
|
|
||||||
|
int errcode = tx(carrier_freq, best_divider, ft_file, ppm, power, gpio, repeat);
|
||||||
|
|
||||||
|
terminate(errcode);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user