kopia lustrzana https://github.com/F5OEO/WsprryPi
Lots more comments. Defaults to NTP calibration.
rodzic
6de039b511
commit
67ca9cfcdf
|
@ -1,3 +1,3 @@
|
||||||
wspr
|
wspr
|
||||||
gpioclk
|
gpioclk
|
||||||
|
mailbox.o
|
||||||
|
|
551
wspr.cpp
551
wspr.cpp
|
@ -46,20 +46,20 @@
|
||||||
|
|
||||||
#include "mailbox.h"
|
#include "mailbox.h"
|
||||||
|
|
||||||
// Note on accessing memory in RPi
|
// Note on accessing memory in RPi:
|
||||||
//
|
//
|
||||||
// There are 3 types of addresses in the RPi:
|
// There are 3 (yes three) address spaces in the Pi:
|
||||||
// Physical addresses
|
// Physical addresses
|
||||||
// These are the actual address locations of the RAM and are equivalent
|
// These are the actual address locations of the RAM and are equivalent
|
||||||
// to offsets into /dev/mem.
|
// to offsets into /dev/mem.
|
||||||
// The peripherals (DMA engine, PWM, etc.) are located at physical
|
// The peripherals (DMA engine, PWM, etc.) are located at physical
|
||||||
// address 0x2000000 for RPi1 and 0x3f000000 for RPi2/3.
|
// address 0x2000000 for RPi1 and 0x3F000000 for RPi2/3.
|
||||||
// Virtual addresses
|
// Virtual addresses
|
||||||
// These are the addresses that a program sees and can read/write to.
|
// These are the addresses that a program sees and can read/write to.
|
||||||
// Addresses 0x00000000 through 0xbfffffff are the addresses available
|
// Addresses 0x00000000 through 0xBFFFFFFF are the addresses available
|
||||||
// to a program running in user space.
|
// to a program running in user space.
|
||||||
// Addresses 0xc0000000 and above are available only to the kernel.
|
// Addresses 0xC0000000 and above are available only to the kernel.
|
||||||
// The peripherals start at address 0xf2000000 in virtual space but
|
// The peripherals start at address 0xF2000000 in virtual space but
|
||||||
// this range is only accessible by the kernel. The kernel could directly
|
// this range is only accessible by the kernel. The kernel could directly
|
||||||
// access peripherals from virtual addresses. It is not clear to me my
|
// access peripherals from virtual addresses. It is not clear to me my
|
||||||
// a user space application running as 'root' does not have access to this
|
// a user space application running as 'root' does not have access to this
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
// Bus addresses
|
// Bus addresses
|
||||||
// This is a different (virtual?) address space that also maps onto
|
// This is a different (virtual?) address space that also maps onto
|
||||||
// physical memory.
|
// physical memory.
|
||||||
// The peripherals start at address 0x7e000000 of the bus address space.
|
// The peripherals start at address 0x7E000000 of the bus address space.
|
||||||
// The DRAM is also available in bus address space in 4 different locations:
|
// The DRAM is also available in bus address space in 4 different locations:
|
||||||
// 0x00000000 "L1 and L2 cached alias"
|
// 0x00000000 "L1 and L2 cached alias"
|
||||||
// 0x40000000 "L2 cache coherent (non allocating)"
|
// 0x40000000 "L2 cache coherent (non allocating)"
|
||||||
|
@ -83,17 +83,17 @@
|
||||||
// write to the GPIO addresses in physical memory.
|
// write to the GPIO addresses in physical memory.
|
||||||
//
|
//
|
||||||
// Accessing RAM from DMA engine
|
// Accessing RAM from DMA engine
|
||||||
// The DMA enginer must use bus addresses to access memory. Thus,
|
// The DMA engine is programmed by accessing the peripheral registers but
|
||||||
// to use the DMA engine to move memory from one virtual address to
|
// must use bus addresses to access memory. Thus, to use the DMA engine to
|
||||||
// another virtual address, one needs to first find the physical addresses
|
// move memory from one virtual address to another virtual address, one needs
|
||||||
// that corresponds to the virtual addresses. Then, one needs to find
|
// to first find the physical addresses that corresponds to the virtual
|
||||||
// the bus addresses that corresponds to those physical addresses. Finally,
|
// addresses. Then, one needs to find the bus addresses that corresponds to
|
||||||
// the DMA engine can be programmed. i.e. DMA engine access should use
|
// those physical addresses. Finally, the DMA engine can be programmed. i.e.
|
||||||
// addresses starting with 0xC.
|
// DMA engine access should use addresses starting with 0xC.
|
||||||
//
|
//
|
||||||
// The perhipherals in the Broadcom documentation are described using their
|
// The perhipherals in the Broadcom documentation are described using their bus
|
||||||
// bus addresses and calculations are performed in this program to figure
|
// addresses and structures are created and calculations performed in this
|
||||||
// out how to access them with virtual addresses.
|
// program to figure out how to access them with virtual addresses.
|
||||||
|
|
||||||
#define ABORT(a) exit(a)
|
#define ABORT(a) exit(a)
|
||||||
// Used for debugging
|
// Used for debugging
|
||||||
|
@ -138,7 +138,7 @@
|
||||||
|
|
||||||
// peri_base_virt is the base virtual address that a userspace program (this
|
// peri_base_virt is the base virtual address that a userspace program (this
|
||||||
// program) can use to read/write to the the physical addresses controlling
|
// program) can use to read/write to the the physical addresses controlling
|
||||||
// the peripherals.
|
// the peripherals. This address is mapped at runtime using mmap and /dev/mem.
|
||||||
// This must be declared global so that it can be called by the atexit
|
// This must be declared global so that it can be called by the atexit
|
||||||
// function.
|
// function.
|
||||||
volatile unsigned *peri_base_virt = NULL;
|
volatile unsigned *peri_base_virt = NULL;
|
||||||
|
@ -168,6 +168,7 @@ volatile unsigned *peri_base_virt = NULL;
|
||||||
|
|
||||||
typedef enum {WSPR,TONE} mode_type;
|
typedef enum {WSPR,TONE} mode_type;
|
||||||
|
|
||||||
|
// Structure used to control clock generator
|
||||||
struct GPCTL {
|
struct GPCTL {
|
||||||
char SRC : 4;
|
char SRC : 4;
|
||||||
char ENAB : 1;
|
char ENAB : 1;
|
||||||
|
@ -180,6 +181,7 @@ struct GPCTL {
|
||||||
char PASSWD : 8;
|
char PASSWD : 8;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Structure used to tell the DMA engine what to do
|
||||||
struct CB {
|
struct CB {
|
||||||
volatile unsigned int TI;
|
volatile unsigned int TI;
|
||||||
volatile unsigned int SOURCE_AD;
|
volatile unsigned int SOURCE_AD;
|
||||||
|
@ -191,6 +193,7 @@ struct CB {
|
||||||
volatile unsigned int RES2;
|
volatile unsigned int RES2;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// DMA engine status registers
|
||||||
struct DMAregs {
|
struct DMAregs {
|
||||||
volatile unsigned int CS;
|
volatile unsigned int CS;
|
||||||
volatile unsigned int CONBLK_AD;
|
volatile unsigned int CONBLK_AD;
|
||||||
|
@ -203,6 +206,7 @@ struct DMAregs {
|
||||||
volatile unsigned int DEBUG;
|
volatile unsigned int DEBUG;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Virtual and bus addresses of a page of physical memory.
|
||||||
struct PageInfo {
|
struct PageInfo {
|
||||||
void* b; // bus address
|
void* b; // bus address
|
||||||
void* v; // virtual address
|
void* v; // virtual address
|
||||||
|
@ -210,50 +214,58 @@ struct PageInfo {
|
||||||
|
|
||||||
// Must be global so that exit handlers can access this.
|
// Must be global so that exit handlers can access this.
|
||||||
static struct {
|
static struct {
|
||||||
int handle; /* From mbox_open() */
|
int handle; /* From mbox_open() */
|
||||||
unsigned mem_ref = 0; /* From mem_alloc() */
|
unsigned mem_ref = 0; /* From mem_alloc() */
|
||||||
unsigned bus_addr; /* From mem_lock() */
|
unsigned bus_addr; /* From mem_lock() */
|
||||||
unsigned char *virt_addr = NULL; /* From mapmem() */ //ha7ilm: originally uint8_t
|
unsigned char *virt_addr = NULL; /* From mapmem() */ //ha7ilm: originally uint8_t
|
||||||
unsigned pool_size;
|
unsigned pool_size;
|
||||||
unsigned pool_cnt;
|
unsigned pool_cnt;
|
||||||
} mbox;
|
} mbox;
|
||||||
|
|
||||||
void allocMemPool(unsigned numpages)
|
// Use the mbox interface to allocate a single chunk of memory to hold
|
||||||
{
|
// all the pages we will need. The bus address and the virtual address
|
||||||
mbox.mem_ref=mem_alloc(mbox.handle, 4096*numpages, 4096, MEM_FLAG);
|
// are saved in the mbox structure.
|
||||||
mbox.bus_addr = mem_lock(mbox.handle, mbox.mem_ref);
|
void allocMemPool(unsigned numpages) {
|
||||||
mbox.virt_addr = (unsigned char*)mapmem(BUS_TO_PHYS(mbox.bus_addr), 4096*numpages);
|
// Allocate space.
|
||||||
mbox.pool_size=numpages;
|
mbox.mem_ref = mem_alloc(mbox.handle, 4096*numpages, 4096, MEM_FLAG);
|
||||||
mbox.pool_cnt=0;
|
// Lock down the allocated space and return its bus address.
|
||||||
//printf("allocMemoryPool bus_addr=%x virt_addr=%x mem_ref=%x\n",mbox.bus_addr,(unsigned)mbox.virt_addr,mbox.mem_ref);
|
mbox.bus_addr = mem_lock(mbox.handle, mbox.mem_ref);
|
||||||
|
// Conert the bus address to a physical address and map this to virtual
|
||||||
|
// (aka user) space.
|
||||||
|
mbox.virt_addr = (unsigned char*)mapmem(BUS_TO_PHYS(mbox.bus_addr), 4096*numpages);
|
||||||
|
// The number of pages in the pool. Never changes!
|
||||||
|
mbox.pool_size=numpages;
|
||||||
|
// How many of the created pages have actually been used.
|
||||||
|
mbox.pool_cnt=0;
|
||||||
|
//printf("allocMemoryPool bus_addr=%x virt_addr=%x mem_ref=%x\n",mbox.bus_addr,(unsigned)mbox.virt_addr,mbox.mem_ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the virtual and bus address (NOT physical address!) of another
|
// Returns the virtual and bus address (NOT physical address!) of another
|
||||||
// page in the pool.
|
// page in the pool.
|
||||||
void getRealMemPageFromPool(void ** vAddr, void **bAddr)
|
void getRealMemPageFromPool(void ** vAddr, void **bAddr) {
|
||||||
{
|
if (mbox.pool_cnt>=mbox.pool_size) {
|
||||||
if (mbox.pool_cnt>=mbox.pool_size) {
|
std::cerr << "Error: unable to allocated more pages!" << std::endl;
|
||||||
std::cerr << "Error: unable to allocated more pages!" << std::endl;
|
ABORT(-1);
|
||||||
ABORT(-1);
|
}
|
||||||
}
|
unsigned offset = mbox.pool_cnt*4096;
|
||||||
unsigned offset = mbox.pool_cnt*4096;
|
*vAddr = (void*)(((unsigned)mbox.virt_addr) + offset);
|
||||||
*vAddr = (void*)(((unsigned)mbox.virt_addr) + offset);
|
*bAddr = (void*)(((unsigned)mbox.bus_addr) + offset);
|
||||||
*bAddr = (void*)(((unsigned)mbox.bus_addr) + offset);
|
//printf("getRealMemoryPageFromPool bus_addr=%x virt_addr=%x\n", (unsigned)*pAddr,(unsigned)*vAddr);
|
||||||
//printf("getRealMemoryPageFromPool bus_addr=%x virt_addr=%x\n", (unsigned)*pAddr,(unsigned)*vAddr);
|
mbox.pool_cnt++;
|
||||||
mbox.pool_cnt++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void deallocMemPool()
|
// Free the memory pool
|
||||||
{
|
void deallocMemPool() {
|
||||||
if(mbox.virt_addr!=NULL) {
|
if(mbox.virt_addr!=NULL) {
|
||||||
unmapmem(mbox.virt_addr, mbox.pool_size*4096);
|
unmapmem(mbox.virt_addr, mbox.pool_size*4096);
|
||||||
}
|
}
|
||||||
if (mbox.mem_ref!=0) {
|
if (mbox.mem_ref!=0) {
|
||||||
mem_unlock(mbox.handle, mbox.mem_ref);
|
mem_unlock(mbox.handle, mbox.mem_ref);
|
||||||
mem_free(mbox.handle, mbox.mem_ref);
|
mem_free(mbox.handle, mbox.mem_ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disable the PWM clock and wait for it to become 'not busy'.
|
||||||
void disable_clock() {
|
void disable_clock() {
|
||||||
// Disable the clock (in case it's already running) by reading current
|
// Disable the clock (in case it's already running) by reading current
|
||||||
// settings and only clearing the enable bit.
|
// settings and only clearing the enable bit.
|
||||||
|
@ -270,8 +282,8 @@ void disable_clock() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void txon()
|
// Turn on TX
|
||||||
{
|
void txon() {
|
||||||
// Set function select for GPIO4.
|
// Set function select for GPIO4.
|
||||||
// Fsel 000 => input
|
// Fsel 000 => input
|
||||||
// Fsel 001 => output
|
// Fsel 001 => output
|
||||||
|
@ -308,8 +320,8 @@ void txon()
|
||||||
ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = *((int*)&setupword);
|
ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = *((int*)&setupword);
|
||||||
}
|
}
|
||||||
|
|
||||||
void txoff()
|
// Turn transmitter on
|
||||||
{
|
void txoff() {
|
||||||
//struct GPCTL setupword = {6/*SRC*/, 0, 0, 0, 0, 1,0x5a};
|
//struct GPCTL setupword = {6/*SRC*/, 0, 0, 0, 0, 1,0x5a};
|
||||||
//ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = *((int*)&setupword);
|
//ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = *((int*)&setupword);
|
||||||
disable_clock();
|
disable_clock();
|
||||||
|
@ -391,6 +403,7 @@ void txSym(
|
||||||
//printf("<instrs[bufPtr]=%x %x>",(unsigned)instrs[bufPtr].v,(unsigned)instrs[bufPtr].b);
|
//printf("<instrs[bufPtr]=%x %x>",(unsigned)instrs[bufPtr].v,(unsigned)instrs[bufPtr].b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Turn off (reset) DMA engine
|
||||||
void unSetupDMA(){
|
void unSetupDMA(){
|
||||||
//cout << "Exiting!" << std::endl;
|
//cout << "Exiting!" << std::endl;
|
||||||
struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS_BUS_ADDR(DMA_BUS_BASE));
|
struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS_BUS_ADDR(DMA_BUS_BASE));
|
||||||
|
@ -398,10 +411,7 @@ void unSetupDMA(){
|
||||||
txoff();
|
txoff();
|
||||||
}
|
}
|
||||||
|
|
||||||
void handSig(const int h) {
|
// Truncate at bit lsb. i.e. set all bits less than lsb to zero.
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
double bit_trunc(
|
double bit_trunc(
|
||||||
const double & d,
|
const double & d,
|
||||||
const int & lsb
|
const int & lsb
|
||||||
|
@ -461,207 +471,215 @@ void setupDMATab(
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the memory structures needed by the DMA engine and perform initial
|
||||||
|
// clock configuration.
|
||||||
void setupDMA(
|
void setupDMA(
|
||||||
struct PageInfo & constPage,
|
struct PageInfo & constPage,
|
||||||
struct PageInfo & instrPage,
|
struct PageInfo & instrPage,
|
||||||
struct PageInfo instrs[]
|
struct PageInfo instrs[]
|
||||||
){
|
){
|
||||||
allocMemPool(1025);
|
allocMemPool(1025);
|
||||||
|
|
||||||
// Allocate a page of ram for the constants
|
// Allocate a page of ram for the constants
|
||||||
getRealMemPageFromPool(&constPage.v, &constPage.b);
|
getRealMemPageFromPool(&constPage.v, &constPage.b);
|
||||||
|
|
||||||
// Create 1024 instructions allocating one page at a time.
|
// Create 1024 instructions allocating one page at a time.
|
||||||
// Even instructions target the GP0 Clock divider
|
// Even instructions target the GP0 Clock divider
|
||||||
// Odd instructions target the PWM FIFO
|
// Odd instructions target the PWM FIFO
|
||||||
int instrCnt = 0;
|
int instrCnt = 0;
|
||||||
while (instrCnt<1024) {
|
while (instrCnt<1024) {
|
||||||
// Allocate a page of ram for the instructions
|
// Allocate a page of ram for the instructions
|
||||||
getRealMemPageFromPool(&instrPage.v, &instrPage.b);
|
getRealMemPageFromPool(&instrPage.v, &instrPage.b);
|
||||||
|
|
||||||
// make copy instructions
|
// make copy instructions
|
||||||
// Only create as many instructions as will fit in the recently
|
// Only create as many instructions as will fit in the recently
|
||||||
// allocated page. If not enough space for all instructions, the
|
// allocated page. If not enough space for all instructions, the
|
||||||
// next loop will allocate another page.
|
// next loop will allocate another page.
|
||||||
struct CB* instr0= (struct CB*)instrPage.v;
|
struct CB* instr0= (struct CB*)instrPage.v;
|
||||||
int i;
|
int i;
|
||||||
for (i=0; i<(signed)(4096/sizeof(struct CB)); i++) {
|
for (i=0; i<(signed)(4096/sizeof(struct CB)); i++) {
|
||||||
instrs[instrCnt].v = (void*)((long int)instrPage.v + sizeof(struct CB)*i);
|
instrs[instrCnt].v = (void*)((long int)instrPage.v + sizeof(struct CB)*i);
|
||||||
instrs[instrCnt].b = (void*)((long int)instrPage.b + sizeof(struct CB)*i);
|
instrs[instrCnt].b = (void*)((long int)instrPage.b + sizeof(struct CB)*i);
|
||||||
instr0->SOURCE_AD = (unsigned long int)constPage.b+2048;
|
instr0->SOURCE_AD = (unsigned long int)constPage.b+2048;
|
||||||
instr0->DEST_AD = PWM_BUS_BASE+0x18 /* FIF1 */;
|
instr0->DEST_AD = PWM_BUS_BASE+0x18 /* FIF1 */;
|
||||||
instr0->TXFR_LEN = 4;
|
instr0->TXFR_LEN = 4;
|
||||||
instr0->STRIDE = 0;
|
instr0->STRIDE = 0;
|
||||||
//instr0->NEXTCONBK = (int)instrPage.b + sizeof(struct CB)*(i+1);
|
//instr0->NEXTCONBK = (int)instrPage.b + sizeof(struct CB)*(i+1);
|
||||||
instr0->TI = (1/* DREQ */<<6) | (5 /* PWM */<<16) | (1<<26/* no wide*/) ;
|
instr0->TI = (1/* DREQ */<<6) | (5 /* PWM */<<16) | (1<<26/* no wide*/) ;
|
||||||
instr0->RES1 = 0;
|
instr0->RES1 = 0;
|
||||||
instr0->RES2 = 0;
|
instr0->RES2 = 0;
|
||||||
|
|
||||||
// Shouldn't this be (instrCnt%2) ???
|
// Shouldn't this be (instrCnt%2) ???
|
||||||
if (i%2) {
|
if (i%2) {
|
||||||
instr0->DEST_AD = CM_GP0DIV_BUS;
|
instr0->DEST_AD = CM_GP0DIV_BUS;
|
||||||
instr0->STRIDE = 4;
|
instr0->STRIDE = 4;
|
||||||
instr0->TI = (1<<26/* no wide*/) ;
|
instr0->TI = (1<<26/* no wide*/) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instrCnt!=0) ((struct CB*)(instrs[instrCnt-1].v))->NEXTCONBK = (long int)instrs[instrCnt].b;
|
if (instrCnt!=0) ((struct CB*)(instrs[instrCnt-1].v))->NEXTCONBK = (long int)instrs[instrCnt].b;
|
||||||
instr0++;
|
instr0++;
|
||||||
instrCnt++;
|
instrCnt++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Create a circular linked list of instructions
|
// Create a circular linked list of instructions
|
||||||
((struct CB*)(instrs[1023].v))->NEXTCONBK = (long int)instrs[0].b;
|
((struct CB*)(instrs[1023].v))->NEXTCONBK = (long int)instrs[0].b;
|
||||||
|
|
||||||
// set up a clock for the PWM
|
// set up a clock for the PWM
|
||||||
ACCESS_BUS_ADDR(CLK_BUS_BASE + 40*4 /*PWMCLK_CNTL*/) = 0x5A000026; // Source=PLLD and disable
|
ACCESS_BUS_ADDR(CLK_BUS_BASE + 40*4 /*PWMCLK_CNTL*/) = 0x5A000026; // Source=PLLD and disable
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
//ACCESS_BUS_ADDR(CLK_BUS_BASE + 41*4 /*PWMCLK_DIV*/) = 0x5A002800;
|
//ACCESS_BUS_ADDR(CLK_BUS_BASE + 41*4 /*PWMCLK_DIV*/) = 0x5A002800;
|
||||||
ACCESS_BUS_ADDR(CLK_BUS_BASE + 41*4 /*PWMCLK_DIV*/) = 0x5A002000; // set PWM div to 2, for 250MHz
|
ACCESS_BUS_ADDR(CLK_BUS_BASE + 41*4 /*PWMCLK_DIV*/) = 0x5A002000; // set PWM div to 2, for 250MHz
|
||||||
ACCESS_BUS_ADDR(CLK_BUS_BASE + 40*4 /*PWMCLK_CNTL*/) = 0x5A000016; // Source=PLLD and enable
|
ACCESS_BUS_ADDR(CLK_BUS_BASE + 40*4 /*PWMCLK_CNTL*/) = 0x5A000016; // Source=PLLD and enable
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
|
|
||||||
// set up pwm
|
// set up pwm
|
||||||
ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x0 /* CTRL*/) = 0;
|
ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x0 /* CTRL*/) = 0;
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x4 /* status*/) = -1; // clear errors
|
ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x4 /* status*/) = -1; // clear errors
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
// Range should default to 32, but it is set at 2048 after reset on my RPi.
|
// Range should default to 32, but it is set at 2048 after reset on my RPi.
|
||||||
ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x10)=32;
|
ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x10)=32;
|
||||||
ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x20)=32;
|
ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x20)=32;
|
||||||
ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x0 /* CTRL*/) = -1; //(1<<13 /* Use fifo */) | (1<<10 /* repeat */) | (1<<9 /* serializer */) | (1<<8 /* enable ch */) ;
|
ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x0 /* CTRL*/) = -1; //(1<<13 /* Use fifo */) | (1<<10 /* repeat */) | (1<<9 /* serializer */) | (1<<8 /* enable ch */) ;
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x8 /* DMAC*/) = (1<<31 /* DMA enable */) | 0x0707;
|
ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x8 /* DMAC*/) = (1<<31 /* DMA enable */) | 0x0707;
|
||||||
|
|
||||||
//activate dma
|
//activate dma
|
||||||
struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS_BUS_ADDR(DMA_BUS_BASE));
|
struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS_BUS_ADDR(DMA_BUS_BASE));
|
||||||
DMA0->CS =1<<31; // reset
|
DMA0->CS =1<<31; // reset
|
||||||
DMA0->CONBLK_AD=0;
|
DMA0->CONBLK_AD=0;
|
||||||
DMA0->TI=0;
|
DMA0->TI=0;
|
||||||
DMA0->CONBLK_AD = (unsigned long int)(instrPage.b);
|
DMA0->CONBLK_AD = (unsigned long int)(instrPage.b);
|
||||||
DMA0->CS =(1<<0)|(255 <<16); // enable bit = 0, clear end flag = 1, prio=19-16
|
DMA0->CS =(1<<0)|(255 <<16); // enable bit = 0, clear end flag = 1, prio=19-16
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert string to uppercase
|
// Convert string to uppercase
|
||||||
void to_upper(char *str)
|
void to_upper(
|
||||||
{ while(*str)
|
char *str
|
||||||
{
|
) {
|
||||||
*str = toupper(*str);
|
while(*str) {
|
||||||
str++;
|
*str = toupper(*str);
|
||||||
}
|
str++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode call, locator, and dBm into WSPR codeblock.
|
// Encode call, locator, and dBm into WSPR codeblock.
|
||||||
void wspr(const char* call, const char* l_pre, const char* dbm, unsigned char* symbols)
|
void wspr(
|
||||||
{
|
const char* call,
|
||||||
// pack prefix in nadd, call in n1, grid, dbm in n2
|
const char* l_pre,
|
||||||
char* c, buf[16];
|
const char* dbm,
|
||||||
strncpy(buf, call, 16);
|
unsigned char* symbols
|
||||||
c=buf;
|
) {
|
||||||
to_upper(c);
|
// pack prefix in nadd, call in n1, grid, dbm in n2
|
||||||
unsigned long ng,nadd=0;
|
char* c, buf[16];
|
||||||
|
strncpy(buf, call, 16);
|
||||||
|
c=buf;
|
||||||
|
to_upper(c);
|
||||||
|
unsigned long ng,nadd=0;
|
||||||
|
|
||||||
if(strchr(c, '/')){ //prefix-suffix
|
if(strchr(c, '/')){ //prefix-suffix
|
||||||
nadd=2;
|
nadd=2;
|
||||||
int i=strchr(c, '/')-c; //stroke position
|
int i=strchr(c, '/')-c; //stroke position
|
||||||
int n=strlen(c)-i-1; //suffix len, prefix-call len
|
int n=strlen(c)-i-1; //suffix len, prefix-call len
|
||||||
c[i]='\0';
|
c[i]='\0';
|
||||||
if(n==1) ng=60000-32768+(c[i+1]>='0'&&c[i+1]<='9'?c[i+1]-'0':c[i+1]==' '?38:c[i+1]-'A'+10); // suffix /A to /Z, /0 to /9
|
if(n==1) ng=60000-32768+(c[i+1]>='0'&&c[i+1]<='9'?c[i+1]-'0':c[i+1]==' '?38:c[i+1]-'A'+10); // suffix /A to /Z, /0 to /9
|
||||||
if(n==2) ng=60000+26+10*(c[i+1]-'0')+(c[i+2]-'0'); // suffix /10 to /99
|
if(n==2) ng=60000+26+10*(c[i+1]-'0')+(c[i+2]-'0'); // suffix /10 to /99
|
||||||
if(n>2){ // prefix EA8/, right align
|
if(n>2){ // prefix EA8/, right align
|
||||||
ng=(i<3?36:c[i-3]>='0'&&c[i-3]<='9'?c[i-3]-'0':c[i-3]-'A'+10);
|
ng=(i<3?36:c[i-3]>='0'&&c[i-3]<='9'?c[i-3]-'0':c[i-3]-'A'+10);
|
||||||
ng=37*ng+(i<2?36:c[i-2]>='0'&&c[i-2]<='9'?c[i-2]-'0':c[i-2]-'A'+10);
|
ng=37*ng+(i<2?36:c[i-2]>='0'&&c[i-2]<='9'?c[i-2]-'0':c[i-2]-'A'+10);
|
||||||
ng=37*ng+(i<1?36:c[i-1]>='0'&&c[i-1]<='9'?c[i-1]-'0':c[i-1]-'A'+10);
|
ng=37*ng+(i<1?36:c[i-1]>='0'&&c[i-1]<='9'?c[i-1]-'0':c[i-1]-'A'+10);
|
||||||
if(ng<32768) nadd=1; else ng=ng-32768;
|
if(ng<32768) nadd=1; else ng=ng-32768;
|
||||||
c=c+i+1;
|
c=c+i+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int i=(isdigit(c[2])?2:isdigit(c[1])?1:0); //last prefix digit of de-suffixed/de-prefixed callsign
|
||||||
|
int n=strlen(c)-i-1; //2nd part of call len
|
||||||
|
unsigned long n1;
|
||||||
|
n1=(i<2?36:c[i-2]>='0'&&c[i-2]<='9'?c[i-2]-'0':c[i-2]-'A'+10);
|
||||||
|
n1=36*n1+(i<1?36:c[i-1]>='0'&&c[i-1]<='9'?c[i-1]-'0':c[i-1]-'A'+10);
|
||||||
|
n1=10*n1+c[i]-'0';
|
||||||
|
n1=27*n1+(n<1?26:c[i+1]-'A');
|
||||||
|
n1=27*n1+(n<2?26:c[i+2]-'A');
|
||||||
|
n1=27*n1+(n<3?26:c[i+3]-'A');
|
||||||
|
|
||||||
|
//if(rand() % 2) nadd=0;
|
||||||
|
if(!nadd){
|
||||||
|
// Copy locator locally since it is declared const and we cannot modify
|
||||||
|
// its contents in-place.
|
||||||
|
char l[4];
|
||||||
|
strncpy(l, l_pre, 4);
|
||||||
|
to_upper(l); //grid square Maidenhead locator (uppercase)
|
||||||
|
ng=180*(179-10*(l[0]-'A')-(l[2]-'0'))+10*(l[1]-'A')+(l[3]-'0');
|
||||||
|
}
|
||||||
|
int p = atoi(dbm); //EIRP in dBm={0,3,7,10,13,17,20,23,27,30,33,37,40,43,47,50,53,57,60}
|
||||||
|
int corr[]={0,-1,1,0,-1,2,1,0,-1,1};
|
||||||
|
p=p>60?60:p<0?0:p+corr[p%10];
|
||||||
|
unsigned long n2=(ng<<7)|(p+64+nadd);
|
||||||
|
|
||||||
|
// pack n1,n2,zero-tail into 50 bits
|
||||||
|
char packed[11] = {
|
||||||
|
static_cast<char>(n1>>20),
|
||||||
|
static_cast<char>(n1>>12),
|
||||||
|
static_cast<char>(n1>>4),
|
||||||
|
static_cast<char>(((n1&0x0f)<<4)|((n2>>18)&0x0f)),
|
||||||
|
static_cast<char>(n2>>10),
|
||||||
|
static_cast<char>(n2>>2),
|
||||||
|
static_cast<char>((n2&0x03)<<6),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
// convolutional encoding K=32, r=1/2, Layland-Lushbaugh polynomials
|
||||||
|
int k = 0;
|
||||||
|
int j,s;
|
||||||
|
int nstate = 0;
|
||||||
|
unsigned char symbol[176];
|
||||||
|
for(j=0;j!=sizeof(packed);j++){
|
||||||
|
for(i=7;i>=0;i--){
|
||||||
|
unsigned long poly[2] = { 0xf2d05351L, 0xe4613c47L };
|
||||||
|
nstate = (nstate<<1) | ((packed[j]>>i)&1);
|
||||||
|
for(s=0;s!=2;s++){ //convolve
|
||||||
|
unsigned long n = nstate & poly[s];
|
||||||
|
int even = 0; // even := parity(n)
|
||||||
|
while(n){
|
||||||
|
even = 1 - even;
|
||||||
|
n = n & (n - 1);
|
||||||
|
}
|
||||||
|
symbol[k] = even;
|
||||||
|
k++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int i=(isdigit(c[2])?2:isdigit(c[1])?1:0); //last prefix digit of de-suffixed/de-prefixed callsign
|
// interleave symbols
|
||||||
int n=strlen(c)-i-1; //2nd part of call len
|
const unsigned char npr3[162] = {
|
||||||
unsigned long n1;
|
1,1,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,1,0,0,1,0,1,1,1,1,0,0,0,0,0,
|
||||||
n1=(i<2?36:c[i-2]>='0'&&c[i-2]<='9'?c[i-2]-'0':c[i-2]-'A'+10);
|
0,0,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1,1,0,0,1,1,0,1,0,0,0,1,1,0,1,0,
|
||||||
n1=36*n1+(i<1?36:c[i-1]>='0'&&c[i-1]<='9'?c[i-1]-'0':c[i-1]-'A'+10);
|
0,0,0,1,1,0,1,0,1,0,1,0,1,0,0,1,0,0,1,0,1,1,0,0,0,1,1,0,1,0,1,0,
|
||||||
n1=10*n1+c[i]-'0';
|
0,0,1,0,0,0,0,0,1,0,0,1,0,0,1,1,1,0,1,1,0,0,1,1,0,1,0,0,0,1,1,1,
|
||||||
n1=27*n1+(n<1?26:c[i+1]-'A');
|
0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0,0,0,0,1,1,0,1,0,1,1,0,0,0,1,1,0,
|
||||||
n1=27*n1+(n<2?26:c[i+2]-'A');
|
0,0 };
|
||||||
n1=27*n1+(n<3?26:c[i+3]-'A');
|
for(i=0;i!=162;i++){
|
||||||
|
// j0 := bit reversed_values_smaller_than_161[i]
|
||||||
//if(rand() % 2) nadd=0;
|
unsigned char j0;
|
||||||
if(!nadd){
|
p=-1;
|
||||||
// Copy locator locally since it is declared const and we cannot modify
|
for(k=0;p!=i;k++){
|
||||||
// its contents in-place.
|
for(j=0;j!=8;j++) // j0:=bit_reverse(k)
|
||||||
char l[4];
|
j0 = ((k>>j)&1)|(j0<<1);
|
||||||
strncpy(l, l_pre, 4);
|
if(j0<162)
|
||||||
to_upper(l); //grid square Maidenhead locator (uppercase)
|
p++;
|
||||||
ng=180*(179-10*(l[0]-'A')-(l[2]-'0'))+10*(l[1]-'A')+(l[3]-'0');
|
}
|
||||||
}
|
symbols[j0]=npr3[j0]|symbol[i]<<1; //interleave and add sync std::vector
|
||||||
int p = atoi(dbm); //EIRP in dBm={0,3,7,10,13,17,20,23,27,30,33,37,40,43,47,50,53,57,60}
|
}
|
||||||
int corr[]={0,-1,1,0,-1,2,1,0,-1,1};
|
|
||||||
p=p>60?60:p<0?0:p+corr[p%10];
|
|
||||||
unsigned long n2=(ng<<7)|(p+64+nadd);
|
|
||||||
|
|
||||||
// pack n1,n2,zero-tail into 50 bits
|
|
||||||
char packed[11] = {
|
|
||||||
static_cast<char>(n1>>20),
|
|
||||||
static_cast<char>(n1>>12),
|
|
||||||
static_cast<char>(n1>>4),
|
|
||||||
static_cast<char>(((n1&0x0f)<<4)|((n2>>18)&0x0f)),
|
|
||||||
static_cast<char>(n2>>10),
|
|
||||||
static_cast<char>(n2>>2),
|
|
||||||
static_cast<char>((n2&0x03)<<6),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
// convolutional encoding K=32, r=1/2, Layland-Lushbaugh polynomials
|
|
||||||
int k = 0;
|
|
||||||
int j,s;
|
|
||||||
int nstate = 0;
|
|
||||||
unsigned char symbol[176];
|
|
||||||
for(j=0;j!=sizeof(packed);j++){
|
|
||||||
for(i=7;i>=0;i--){
|
|
||||||
unsigned long poly[2] = { 0xf2d05351L, 0xe4613c47L };
|
|
||||||
nstate = (nstate<<1) | ((packed[j]>>i)&1);
|
|
||||||
for(s=0;s!=2;s++){ //convolve
|
|
||||||
unsigned long n = nstate & poly[s];
|
|
||||||
int even = 0; // even := parity(n)
|
|
||||||
while(n){
|
|
||||||
even = 1 - even;
|
|
||||||
n = n & (n - 1);
|
|
||||||
}
|
|
||||||
symbol[k] = even;
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// interleave symbols
|
|
||||||
const unsigned char npr3[162] = {
|
|
||||||
1,1,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,1,0,0,1,0,1,1,1,1,0,0,0,0,0,
|
|
||||||
0,0,1,0,0,1,0,1,0,0,0,0,0,0,1,0,1,1,0,0,1,1,0,1,0,0,0,1,1,0,1,0,
|
|
||||||
0,0,0,1,1,0,1,0,1,0,1,0,1,0,0,1,0,0,1,0,1,1,0,0,0,1,1,0,1,0,1,0,
|
|
||||||
0,0,1,0,0,0,0,0,1,0,0,1,0,0,1,1,1,0,1,1,0,0,1,1,0,1,0,0,0,1,1,1,
|
|
||||||
0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0,0,0,0,1,1,0,1,0,1,1,0,0,0,1,1,0,
|
|
||||||
0,0 };
|
|
||||||
for(i=0;i!=162;i++){
|
|
||||||
// j0 := bit reversed_values_smaller_than_161[i]
|
|
||||||
unsigned char j0;
|
|
||||||
p=-1;
|
|
||||||
for(k=0;p!=i;k++){
|
|
||||||
for(j=0;j!=8;j++) // j0:=bit_reverse(k)
|
|
||||||
j0 = ((k>>j)&1)|(j0<<1);
|
|
||||||
if(j0<162)
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
symbols[j0]=npr3[j0]|symbol[i]<<1; //interleave and add sync std::vector
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the system clock's minute to reach one second past 'minute'
|
// Wait for the system clock's minute to reach one second past 'minute'
|
||||||
void wait_every(int minute)
|
void wait_every(
|
||||||
{
|
int minute
|
||||||
|
) {
|
||||||
time_t t;
|
time_t t;
|
||||||
struct tm* ptm;
|
struct tm* ptm;
|
||||||
for(;;){
|
for(;;){
|
||||||
|
@ -685,8 +703,10 @@ void print_usage() {
|
||||||
std::cout << " -p --ppm ppm" << std::endl;
|
std::cout << " -p --ppm ppm" << std::endl;
|
||||||
std::cout << " Known PPM correction to 19.2MHz RPi nominal crystal frequency." << std::endl;
|
std::cout << " Known PPM correction to 19.2MHz RPi nominal crystal frequency." << std::endl;
|
||||||
std::cout << " -s --self-calibration" << std::endl;
|
std::cout << " -s --self-calibration" << std::endl;
|
||||||
std::cout << " Call ntp_adjtime() before every transmission to obtain the PPM error of the" << std::endl;
|
std::cout << " Check NTP before every transmission to obtain the PPM error of the" << std::endl;
|
||||||
std::cout << " crystal." << std::endl;
|
std::cout << " crystal (default setting!)." << std::endl;
|
||||||
|
std::cout << " -f --free-running" << std::endl;
|
||||||
|
std::cout << " Do not use NTP to correct frequency error of RPi crystal." << std::endl;
|
||||||
std::cout << " -r --repeat" << std::endl;
|
std::cout << " -r --repeat" << std::endl;
|
||||||
std::cout << " Repeatedly, and in order, transmit on all the specified command line freqs." << std::endl;
|
std::cout << " Repeatedly, and in order, transmit on all the specified command line freqs." << std::endl;
|
||||||
std::cout << " -x --terminate <n>" << std::endl;
|
std::cout << " -x --terminate <n>" << std::endl;
|
||||||
|
@ -731,7 +751,7 @@ void parse_commandline(
|
||||||
) {
|
) {
|
||||||
// Default values
|
// Default values
|
||||||
ppm=0;
|
ppm=0;
|
||||||
self_cal=false;
|
self_cal=true;
|
||||||
repeat=false;
|
repeat=false;
|
||||||
random_offset=false;
|
random_offset=false;
|
||||||
test_tone=NAN;
|
test_tone=NAN;
|
||||||
|
@ -743,6 +763,7 @@ void parse_commandline(
|
||||||
{"help", no_argument, 0, 'h'},
|
{"help", no_argument, 0, 'h'},
|
||||||
{"ppm", required_argument, 0, 'p'},
|
{"ppm", required_argument, 0, 'p'},
|
||||||
{"self-calibration", no_argument, 0, 's'},
|
{"self-calibration", no_argument, 0, 's'},
|
||||||
|
{"free-running", no_argument, 0, 'f'},
|
||||||
{"repeat", no_argument, 0, 'r'},
|
{"repeat", no_argument, 0, 'r'},
|
||||||
{"terminate", required_argument, 0, 'x'},
|
{"terminate", required_argument, 0, 'x'},
|
||||||
{"offset", no_argument, 0, 'o'},
|
{"offset", no_argument, 0, 'o'},
|
||||||
|
@ -751,10 +772,10 @@ void parse_commandline(
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
while (1) {
|
while (true) {
|
||||||
/* getopt_long stores the option index here. */
|
/* getopt_long stores the option index here. */
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
int c = getopt_long (argc, argv, "hp:srx:ot:n",
|
int c = getopt_long (argc, argv, "hp:sfrx:ot:n",
|
||||||
long_options, &option_index);
|
long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
@ -781,6 +802,9 @@ void parse_commandline(
|
||||||
case 's':
|
case 's':
|
||||||
self_cal=true;
|
self_cal=true;
|
||||||
break;
|
break;
|
||||||
|
case 'f':
|
||||||
|
self_cal=false;
|
||||||
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
repeat=true;
|
repeat=true;
|
||||||
break;
|
break;
|
||||||
|
@ -889,7 +913,6 @@ void parse_commandline(
|
||||||
center_freq_set.push_back(parsed_freq);
|
center_freq_set.push_back(parsed_freq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Check consistency among command line options.
|
// Check consistency among command line options.
|
||||||
if (ppm&&self_cal) {
|
if (ppm&&self_cal) {
|
||||||
std::cout << "Warning: ppm value is being ignored!" << std::endl;
|
std::cout << "Warning: ppm value is being ignored!" << std::endl;
|
||||||
|
@ -927,8 +950,7 @@ void parse_commandline(
|
||||||
std::cout << temp.str();
|
std::cout << temp.str();
|
||||||
temp.str("");
|
temp.str("");
|
||||||
if (self_cal) {
|
if (self_cal) {
|
||||||
temp << " ntp_adjtime() will be used to peridocially calibrate the transmission" << std::endl;
|
temp << " NTP will be used to peridocially calibrate the transmission frequency" << std::endl;
|
||||||
temp << " frequency" << std::endl;
|
|
||||||
} else if (ppm) {
|
} else if (ppm) {
|
||||||
temp << " PPM value to be used for all transmissions: " << ppm << std::endl;
|
temp << " PPM value to be used for all transmissions: " << ppm << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -950,7 +972,7 @@ void parse_commandline(
|
||||||
temp << std::setprecision(6) << std::fixed << "A test tone will be generated at frequency " << test_tone/1e6 << " MHz" << std::endl;
|
temp << std::setprecision(6) << std::fixed << "A test tone will be generated at frequency " << test_tone/1e6 << " MHz" << std::endl;
|
||||||
std::cout << temp.str();
|
std::cout << temp.str();
|
||||||
if (self_cal) {
|
if (self_cal) {
|
||||||
std::cout << "ntp_adjtime() will be used to calibrate the tone" << std::endl;
|
std::cout << "NTP will be used to calibrate the tone frequency" << std::endl;
|
||||||
} else if (ppm) {
|
} else if (ppm) {
|
||||||
std::cout << "PPM value to be used to generate the tone: " << ppm << std::endl;
|
std::cout << "PPM value to be used to generate the tone: " << ppm << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -1007,6 +1029,7 @@ void timeval_print(struct timeval *tv) {
|
||||||
printf("%s.%03ld", buffer, (tv->tv_usec+500)/1000);
|
printf("%s.%03ld", buffer, (tv->tv_usec+500)/1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the mbox special files and open mbox.
|
||||||
void open_mbox() {
|
void open_mbox() {
|
||||||
unlink(DEVICE_FILE_NAME);
|
unlink(DEVICE_FILE_NAME);
|
||||||
unlink(LOCAL_DEVICE_FILE_NAME);
|
unlink(LOCAL_DEVICE_FILE_NAME);
|
||||||
|
@ -1021,6 +1044,7 @@ void open_mbox() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called when exiting or when a signal is received.
|
||||||
void cleanup() {
|
void cleanup() {
|
||||||
disable_clock();
|
disable_clock();
|
||||||
unSetupDMA();
|
unSetupDMA();
|
||||||
|
@ -1029,23 +1053,27 @@ void cleanup() {
|
||||||
unlink(LOCAL_DEVICE_FILE_NAME);
|
unlink(LOCAL_DEVICE_FILE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called when a signal is received. Automatically calls cleanup().
|
||||||
void cleanupAndExit(int sig) {
|
void cleanupAndExit(int sig) {
|
||||||
|
std::cerr << "Exiting with error; caught signal: " << sig << std::endl;
|
||||||
cleanup();
|
cleanup();
|
||||||
printf("Exiting with error; caught signal: %i\n", sig);
|
ABORT(-1);
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSchedPriority(int priority) {
|
void setSchedPriority(int priority) {
|
||||||
//In order to get the best timing at a decent queue size, we want the kernel to avoid interrupting us for long durations.
|
//In order to get the best timing at a decent queue size, we want the kernel
|
||||||
//This is done by giving our process a high priority. Note, must run as super-user for this to work.
|
//to avoid interrupting us for long durations. This is done by giving our
|
||||||
|
//process a high priority. Note, must run as super-user for this to work.
|
||||||
struct sched_param sp;
|
struct sched_param sp;
|
||||||
sp.sched_priority=priority;
|
sp.sched_priority=priority;
|
||||||
int ret = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp);
|
int ret = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
printf("Warning: pthread_setschedparam (increase thread priority) returned non-zero: %i\n", ret);
|
std::cerr << "Warning: pthread_setschedparam (increase thread priority) returned non-zero: " << ret << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the memory map between virtual memory and the peripheral range
|
||||||
|
// of physical memory.
|
||||||
void setup_peri_base_virt(
|
void setup_peri_base_virt(
|
||||||
volatile unsigned * & peri_base_virt
|
volatile unsigned * & peri_base_virt
|
||||||
) {
|
) {
|
||||||
|
@ -1056,15 +1084,15 @@ void setup_peri_base_virt(
|
||||||
ABORT (-1);
|
ABORT (-1);
|
||||||
}
|
}
|
||||||
peri_base_virt = (unsigned *)mmap(
|
peri_base_virt = (unsigned *)mmap(
|
||||||
NULL,
|
NULL,
|
||||||
0x002FFFFF, //len
|
0x01000000, //len
|
||||||
PROT_READ|PROT_WRITE,
|
PROT_READ|PROT_WRITE,
|
||||||
MAP_SHARED,
|
MAP_SHARED,
|
||||||
mem_fd,
|
mem_fd,
|
||||||
PERI_BASE_PHYS //base
|
PERI_BASE_PHYS //base
|
||||||
);
|
);
|
||||||
if ((long int)peri_base_virt==-1) {
|
if ((long int)peri_base_virt==-1) {
|
||||||
std::cerr << "Error: mmap error!" << std::endl;
|
std::cerr << "Error: peri_base_virt mmap error!" << std::endl;
|
||||||
ABORT(-1);
|
ABORT(-1);
|
||||||
}
|
}
|
||||||
close(mem_fd);
|
close(mem_fd);
|
||||||
|
@ -1122,13 +1150,13 @@ int main(const int argc, char * const argv[]) {
|
||||||
int nbands=center_freq_set.size();
|
int nbands=center_freq_set.size();
|
||||||
|
|
||||||
// Initial configuration
|
// Initial configuration
|
||||||
|
struct PageInfo constPage;
|
||||||
|
struct PageInfo instrPage;
|
||||||
|
struct PageInfo instrs[1024];
|
||||||
setup_peri_base_virt(peri_base_virt);
|
setup_peri_base_virt(peri_base_virt);
|
||||||
// Set up DMA
|
// Set up DMA
|
||||||
open_mbox();
|
open_mbox();
|
||||||
txon();
|
txon();
|
||||||
struct PageInfo constPage;
|
|
||||||
struct PageInfo instrPage;
|
|
||||||
struct PageInfo instrs[1024];
|
|
||||||
setupDMA(constPage,instrPage,instrs);
|
setupDMA(constPage,instrPage,instrs);
|
||||||
txoff();
|
txoff();
|
||||||
|
|
||||||
|
@ -1267,6 +1295,7 @@ int main(const int argc, char * const argv[]) {
|
||||||
// Turn transmitter off
|
// Turn transmitter off
|
||||||
txoff();
|
txoff();
|
||||||
|
|
||||||
|
// End timestamp
|
||||||
gettimeofday(&tvEnd, NULL);
|
gettimeofday(&tvEnd, NULL);
|
||||||
std::cout << " TX ended at: ";
|
std::cout << " TX ended at: ";
|
||||||
timeval_print(&tvEnd);
|
timeval_print(&tvEnd);
|
||||||
|
|
Ładowanie…
Reference in New Issue