kopia lustrzana https://github.com/JamesP6000/PiCW
Seems to be working. Forgot -DRPIx in makefile :(
rodzic
55858ceb8f
commit
fd4e52a2b8
411
PiCW.cpp
411
PiCW.cpp
|
@ -52,17 +52,66 @@
|
||||||
|
|
||||||
#include "mailbox.h"
|
#include "mailbox.h"
|
||||||
|
|
||||||
|
// Note on accessing memory in RPi:
|
||||||
|
//
|
||||||
|
// There are 3 (yes three) address spaces in the Pi:
|
||||||
|
// Physical addresses
|
||||||
|
// These are the actual address locations of the RAM and are equivalent
|
||||||
|
// to offsets into /dev/mem.
|
||||||
|
// The peripherals (DMA engine, PWM, etc.) are located at physical
|
||||||
|
// address 0x2000000 for RPi1 and 0x3F000000 for RPi2/3.
|
||||||
|
// Virtual addresses
|
||||||
|
// These are the addresses that a program sees and can read/write to.
|
||||||
|
// Addresses 0x00000000 through 0xBFFFFFFF are the addresses available
|
||||||
|
// to a program running in user space.
|
||||||
|
// Addresses 0xC0000000 and above are available only to the kernel.
|
||||||
|
// The peripherals start at address 0xF2000000 in virtual space but
|
||||||
|
// this range is only accessible by the kernel. The kernel could directly
|
||||||
|
// 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
|
||||||
|
// memory range.
|
||||||
|
// Bus addresses
|
||||||
|
// This is a different (virtual?) address space that also maps onto
|
||||||
|
// physical memory.
|
||||||
|
// The peripherals start at address 0x7E000000 of the bus address space.
|
||||||
|
// The DRAM is also available in bus address space in 4 different locations:
|
||||||
|
// 0x00000000 "L1 and L2 cached alias"
|
||||||
|
// 0x40000000 "L2 cache coherent (non allocating)"
|
||||||
|
// 0x80000000 "L2 cache (only)"
|
||||||
|
// 0xC0000000 "Direct, uncached access"
|
||||||
|
//
|
||||||
|
// Accessing peripherals from user space (virtual addresses):
|
||||||
|
// The technique used in this program is that mmap is used to map portions of
|
||||||
|
// /dev/mem to an arbitrary virtual address. For example, to access the
|
||||||
|
// GPIO's, the gpio range of addresses in /dev/mem (physical addresses) are
|
||||||
|
// mapped to a kernel chosen virtual address. After the mapping has been
|
||||||
|
// set up, writing to the kernel chosen virtual address will actually
|
||||||
|
// write to the GPIO addresses in physical memory.
|
||||||
|
//
|
||||||
|
// Accessing RAM from DMA engine
|
||||||
|
// The DMA engine is programmed by accessing the peripheral registers but
|
||||||
|
// must use bus addresses to access memory. Thus, to use the DMA engine to
|
||||||
|
// move memory from one virtual address to another virtual address, one needs
|
||||||
|
// to first find the physical addresses that corresponds to the virtual
|
||||||
|
// addresses. Then, one needs to find the bus addresses that corresponds to
|
||||||
|
// those physical addresses. Finally, the DMA engine can be programmed. i.e.
|
||||||
|
// DMA engine access should use addresses starting with 0xC.
|
||||||
|
//
|
||||||
|
// The perhipherals in the Broadcom documentation are described using their bus
|
||||||
|
// addresses and structures are created and calculations performed in this
|
||||||
|
// 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
|
||||||
#define MARK std::cout << "Currently in file: " << __FILE__ << " line: " << __LINE__ << std::endl
|
#define MARK std::cout << "Currently in file: " << __FILE__ << " line: " << __LINE__ << std::endl
|
||||||
|
|
||||||
// PLLD clock frequency.
|
// PLLD clock frequency.
|
||||||
// There seems to be a 2.5ppm offset between the NTP measured frequency
|
// For RPi1, after NTP converges, these is a 2.5 PPM difference between
|
||||||
// error and the frequency error measured by a frequency counter. This fixed
|
// the PPM correction reported by NTP and the actual frequency offset of
|
||||||
// PPM offset is compensated for here.
|
// the crystal. This 2.5 PPM offset is not present in the RPi2 and RPi3.
|
||||||
// This PPM correction is not needed for RPI2/3.
|
// This 2.5 PPM offset is compensated for here, but only for the RPi1.
|
||||||
#ifdef RPI2
|
#ifdef RPI2
|
||||||
#define F_PLLD_CLK (500000000.0*(1-0.000e-6))
|
#define F_PLLD_CLK (500000000.0)
|
||||||
#else
|
#else
|
||||||
#define F_PLLD_CLK (500000000.0*(1-2.500e-6))
|
#define F_PLLD_CLK (500000000.0*(1-2.500e-6))
|
||||||
#endif
|
#endif
|
||||||
|
@ -73,25 +122,28 @@
|
||||||
#define F_PWM_CLK_INIT (31156186.6125761)
|
#define F_PWM_CLK_INIT (31156186.6125761)
|
||||||
|
|
||||||
// Choose proper base address depending on RPI1/RPI2 setting from makefile.
|
// Choose proper base address depending on RPI1/RPI2 setting from makefile.
|
||||||
|
// PERI_BASE_PHYS is the base address of the peripherals, in physical
|
||||||
|
// address space.
|
||||||
#ifdef RPI2
|
#ifdef RPI2
|
||||||
#define BCM2708_PERI_BASE 0x3f000000
|
#define PERI_BASE_PHYS 0x3f000000
|
||||||
#define MEM_FLAG 0x04
|
#define MEM_FLAG 0x04
|
||||||
//#pragma message "Raspberry Pi 2/3 detected."
|
|
||||||
#else
|
#else
|
||||||
#define BCM2708_PERI_BASE 0x20000000
|
#define PERI_BASE_PHYS 0x20000000
|
||||||
#define MEM_FLAG 0x0c
|
#define MEM_FLAG 0x0c
|
||||||
//#pragma message "Raspberry Pi 1 detected."
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define PAGE_SIZE (4*1024)
|
#define PAGE_SIZE (4*1024)
|
||||||
#define BLOCK_SIZE (4*1024)
|
#define BLOCK_SIZE (4*1024)
|
||||||
|
|
||||||
// This must be declared global so that it can be used by the atexit
|
// 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
|
||||||
|
// 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
|
||||||
// function.
|
// function.
|
||||||
volatile unsigned *allof7e = NULL;
|
volatile unsigned *peri_base_virt = NULL;
|
||||||
|
|
||||||
// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y)
|
// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y)
|
||||||
#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
|
//#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
|
||||||
//#define OUT_GPIO(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3))
|
//#define OUT_GPIO(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3))
|
||||||
//#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
|
//#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
|
||||||
|
|
||||||
|
@ -99,23 +151,30 @@ volatile unsigned *allof7e = NULL;
|
||||||
//#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0
|
//#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0
|
||||||
//#define GPIO_GET *(gpio+13) // sets bits which are 1 ignores bits which are 0
|
//#define GPIO_GET *(gpio+13) // sets bits which are 1 ignores bits which are 0
|
||||||
|
|
||||||
#define ACCESS(base) *(volatile int*)((long int)allof7e+base-0x7e000000)
|
// Given an address in the bus address space of the peripherals, this
|
||||||
#define SETBIT(base, bit) ACCESS(base) |= 1<<bit
|
// macro calculates the appropriate virtual address to use to access
|
||||||
#define CLRBIT(base, bit) ACCESS(base) &= ~(1<<bit)
|
// the requested bus address space. It does this by first subtracting
|
||||||
|
// 0x7e000000 from the supplied bus address to calculate the offset into
|
||||||
|
// the peripheral address space. Then, this offset is added to peri_base_virt
|
||||||
|
// Which is the base address of the peripherals, in virtual address space.
|
||||||
|
#define ACCESS_BUS_ADDR(buss_addr) *(volatile int*)((long int)peri_base_virt+(buss_addr)-0x7e000000)
|
||||||
|
// Given a bus address in the peripheral address space, set or clear a bit.
|
||||||
|
#define SETBIT_BUS_ADDR(base, bit) ACCESS_BUS_ADDR(base) |= 1<<bit
|
||||||
|
#define CLRBIT_BUS_ADDR(base, bit) ACCESS_BUS_ADDR(base) &= ~(1<<bit)
|
||||||
|
|
||||||
#define GPIO_VIRT_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */
|
// The following are all bus addresses.
|
||||||
#define DMA_VIRT_BASE (BCM2708_PERI_BASE + 0x7000)
|
#define GPIO_BUS_BASE (0x7E200000)
|
||||||
|
#define CM_GP0CTL_BUS (0x7e101070)
|
||||||
#define GPIO_PHYS_BASE (0x7E200000)
|
#define CM_GP0DIV_BUS (0x7e101074)
|
||||||
#define CM_GP0CTL (0x7e101070)
|
#define PADS_GPIO_0_27_BUS (0x7e10002c)
|
||||||
#define CM_GP0DIV (0x7e101074)
|
#define CLK_BUS_BASE (0x7E101000)
|
||||||
#define PADS_GPIO_0_27 (0x7e10002c)
|
#define DMA_BUS_BASE (0x7E007000)
|
||||||
#define CLK_PHYS_BASE (0x7E101000)
|
#define PWM_BUS_BASE (0x7e20C000) /* PWM controller */
|
||||||
#define DMA_PHYS_BASE (0x7E007000)
|
|
||||||
#define PWM_PHYS_BASE (0x7e20C000) /* PWM controller */
|
|
||||||
|
|
||||||
|
// Convert from a bus address to a physical address.
|
||||||
#define BUS_TO_PHYS(x) ((x)&~0xC0000000)
|
#define BUS_TO_PHYS(x) ((x)&~0xC0000000)
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
@ -127,6 +186,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;
|
||||||
|
@ -139,53 +199,86 @@ 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* p; // physical address
|
void* b; // bus address
|
||||||
void* v; // virtual address
|
void* v; // virtual address
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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; /* 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; /* 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
|
||||||
|
// are saved in the mbox structure.
|
||||||
|
void allocMemPool(unsigned numpages) {
|
||||||
|
// Allocate space.
|
||||||
mbox.mem_ref = mem_alloc(mbox.handle, 4096*numpages, 4096, MEM_FLAG);
|
mbox.mem_ref = mem_alloc(mbox.handle, 4096*numpages, 4096, MEM_FLAG);
|
||||||
|
// Lock down the allocated space and return its bus address.
|
||||||
mbox.bus_addr = mem_lock(mbox.handle, 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);
|
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;
|
mbox.pool_size=numpages;
|
||||||
|
// How many of the created pages have actually been used.
|
||||||
mbox.pool_cnt=0;
|
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);
|
//printf("allocMemoryPool bus_addr=%x virt_addr=%x mem_ref=%x\n",mbox.bus_addr,(unsigned)mbox.virt_addr,mbox.mem_ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getRealMemPageFromPool(void ** vAddr, void **pAddr)
|
// Returns the virtual and bus address (NOT physical address!) of another
|
||||||
{
|
// page in the pool.
|
||||||
|
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);
|
||||||
*pAddr = (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) //it will be 0 by default as in .bss
|
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) {
|
||||||
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() {
|
||||||
|
// Check if mapping has been set up yet.
|
||||||
|
if (peri_base_virt==NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Disable the clock (in case it's already running) by reading current
|
||||||
|
// settings and only clearing the enable bit.
|
||||||
|
auto settings=ACCESS_BUS_ADDR(CM_GP0CTL_BUS);
|
||||||
|
// Clear enable bit and add password
|
||||||
|
settings=(settings&0x7EF)|0x5A000000;
|
||||||
|
// Disable
|
||||||
|
ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = *((int*)&settings);
|
||||||
|
// Wait for clock to not be busy.
|
||||||
|
while (true) {
|
||||||
|
if (!(ACCESS_BUS_ADDR(CM_GP0CTL_BUS)&(1<<7))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Transmit tone tone_freq for tsym seconds.
|
// Transmit tone tone_freq for tsym seconds.
|
||||||
//
|
//
|
||||||
// TODO:
|
// TODO:
|
||||||
|
@ -234,47 +327,45 @@ void txSym(
|
||||||
// Configure the transmission for this iteration
|
// Configure the transmission for this iteration
|
||||||
// Set GPIO pin to transmit f0
|
// Set GPIO pin to transmit f0
|
||||||
bufPtr++;
|
bufPtr++;
|
||||||
MARK;
|
ACCESS_BUS_ADDR(DMA_BUS_BASE+0x20) = 0x31401234;
|
||||||
while( ACCESS(DMA_PHYS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].p)) usleep(100);
|
while( ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].b)) usleep(100);
|
||||||
((struct CB*)(instrs[bufPtr].v))->SOURCE_AD = (long int)constPage.p + f0_idx*4;
|
((struct CB*)(instrs[bufPtr].v))->SOURCE_AD = (long int)constPage.b + f0_idx*4;
|
||||||
|
|
||||||
// Wait for n_f0 PWM clocks
|
// Wait for n_f0 PWM clocks
|
||||||
bufPtr++;
|
bufPtr++;
|
||||||
MARK;
|
while( ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].b)) usleep(100);
|
||||||
while( ACCESS(DMA_PHYS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].p)) usleep(100);
|
|
||||||
((struct CB*)(instrs[bufPtr].v))->TXFR_LEN = n_f0;
|
((struct CB*)(instrs[bufPtr].v))->TXFR_LEN = n_f0;
|
||||||
|
|
||||||
// Set GPIO pin to transmit f1
|
// Set GPIO pin to transmit f1
|
||||||
bufPtr++;
|
bufPtr++;
|
||||||
MARK;
|
while( ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].b)) usleep(100);
|
||||||
while( ACCESS(DMA_PHYS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].p)) usleep(100);
|
((struct CB*)(instrs[bufPtr].v))->SOURCE_AD = (long int)constPage.b + f1_idx*4;
|
||||||
((struct CB*)(instrs[bufPtr].v))->SOURCE_AD = (long int)constPage.p + f1_idx*4;
|
|
||||||
|
|
||||||
// Wait for n_f1 PWM clocks
|
// Wait for n_f1 PWM clocks
|
||||||
bufPtr=(bufPtr+1) % (1024);
|
bufPtr=(bufPtr+1) % (1024);
|
||||||
MARK;
|
while( ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].b)) {
|
||||||
while( ACCESS(DMA_PHYS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].p)) {
|
|
||||||
std::cout << ACCESS(DMA_PHYS_BASE + 0x04 /* CurBlock*/) << std::endl;
|
|
||||||
usleep(100);
|
usleep(100);
|
||||||
}
|
}
|
||||||
MARK;
|
|
||||||
((struct CB*)(instrs[bufPtr].v))->TXFR_LEN = n_f1;
|
((struct CB*)(instrs[bufPtr].v))->TXFR_LEN = n_f1;
|
||||||
MARK;
|
|
||||||
|
|
||||||
// Update counters
|
// Update counters
|
||||||
n_pwmclk_transmitted+=n_pwmclk;
|
n_pwmclk_transmitted+=n_pwmclk;
|
||||||
n_f0_transmitted+=n_f0;
|
n_f0_transmitted+=n_f0;
|
||||||
MARK;
|
|
||||||
}
|
}
|
||||||
MARK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void unSetupDMA(){
|
void unSetupDMA(){
|
||||||
|
// Check if mapping has been set up yet.
|
||||||
|
if (peri_base_virt==NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
//printf("exiting\n");
|
//printf("exiting\n");
|
||||||
struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS(DMA_PHYS_BASE));
|
struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS_BUS_ADDR(DMA_BUS_BASE));
|
||||||
DMA0->CS =1<<31; // reset dma controller
|
DMA0->CS =1<<31; // reset dma controller
|
||||||
|
disable_clock();
|
||||||
|
/*
|
||||||
// Turn off GPIO clock
|
// Turn off GPIO clock
|
||||||
ACCESS(CM_GP0CTL) =
|
ACCESS_BUS_ADDR(CM_GP0CTL_BUS) =
|
||||||
// PW
|
// PW
|
||||||
(0x5a<<24) |
|
(0x5a<<24) |
|
||||||
// MASH
|
// MASH
|
||||||
|
@ -290,11 +381,14 @@ void unSetupDMA(){
|
||||||
// SRC
|
// SRC
|
||||||
(6<<0)
|
(6<<0)
|
||||||
;
|
;
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
void handSig(const int h) {
|
void handSig(const int h) {
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
double bit_trunc(
|
double bit_trunc(
|
||||||
const double & d,
|
const double & d,
|
||||||
|
@ -344,17 +438,17 @@ void setupDMA(
|
||||||
struct PageInfo & instrPage,
|
struct PageInfo & instrPage,
|
||||||
struct PageInfo instrs[]
|
struct PageInfo instrs[]
|
||||||
){
|
){
|
||||||
atexit(unSetupDMA);
|
//atexit(unSetupDMA);
|
||||||
atexit(deallocMemPool);
|
//atexit(deallocMemPool);
|
||||||
signal (SIGINT, handSig);
|
//signal (SIGINT, handSig);
|
||||||
signal (SIGTERM, handSig);
|
//signal (SIGTERM, handSig);
|
||||||
signal (SIGHUP, handSig);
|
//signal (SIGHUP, handSig);
|
||||||
signal (SIGQUIT, handSig);
|
//signal (SIGQUIT, handSig);
|
||||||
|
|
||||||
allocMemPool(1025);
|
allocMemPool(1025);
|
||||||
|
|
||||||
// Allocate a page of ram for the constants
|
// Allocate a page of ram for the constants
|
||||||
getRealMemPageFromPool(&constPage.v, &constPage.p);
|
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
|
||||||
|
@ -362,7 +456,7 @@ void setupDMA(
|
||||||
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.p);
|
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
|
||||||
|
@ -372,68 +466,69 @@ void setupDMA(
|
||||||
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].p = (void*)((long int)instrPage.p + sizeof(struct CB)*i);
|
instrs[instrCnt].b = (void*)((long int)instrPage.b + sizeof(struct CB)*i);
|
||||||
instr0->SOURCE_AD = (unsigned long int)constPage.p+2048;
|
instr0->SOURCE_AD = (unsigned long int)constPage.b+2048;
|
||||||
instr0->DEST_AD = PWM_PHYS_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.p + 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;
|
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].p;
|
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].p;
|
((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(CLK_PHYS_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(CLK_PHYS_BASE + 41*4 /*PWMCLK_DIV*/) = 0x5A002800;
|
//ACCESS_BUS_ADDR(CLK_BUS_BASE + 41*4 /*PWMCLK_DIV*/) = 0x5A002800;
|
||||||
ACCESS(CLK_PHYS_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(CLK_PHYS_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(PWM_PHYS_BASE + 0x0 /* CTRL*/) = 0;
|
ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x0 /* CTRL*/) = 0;
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
ACCESS(PWM_PHYS_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(PWM_PHYS_BASE + 0x10)=32;
|
ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x10)=32;
|
||||||
ACCESS(PWM_PHYS_BASE + 0x20)=32;
|
ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x20)=32;
|
||||||
ACCESS(PWM_PHYS_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(PWM_PHYS_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(DMA_PHYS_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.p);
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
//
|
//
|
||||||
// Set up memory regions to access GPIO
|
// Set up memory regions to access GPIO
|
||||||
//
|
//
|
||||||
void setup_io(
|
void setup_io(
|
||||||
int & mem_fd,
|
int & mem_fd
|
||||||
char * & gpio_mem,
|
//char * & gpio_mem,
|
||||||
char * & gpio_map,
|
//char * & gpio_map,
|
||||||
volatile unsigned * & gpio
|
//volatile unsigned * & gpio
|
||||||
) {
|
) {
|
||||||
/* open /dev/mem */
|
/* open /dev/mem */
|
||||||
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
|
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
|
||||||
|
@ -471,7 +566,9 @@ void setup_io(
|
||||||
// Always use volatile pointer!
|
// Always use volatile pointer!
|
||||||
gpio = (volatile unsigned *)gpio_map;
|
gpio = (volatile unsigned *)gpio_map;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
// Not sure why this function is needed as this code only uses GPIO4 and
|
// Not sure why this function is needed as this code only uses GPIO4 and
|
||||||
// this function sets gpio 7 through 11 as input...
|
// this function sets gpio 7 through 11 as input...
|
||||||
void setup_gpios(
|
void setup_gpios(
|
||||||
|
@ -489,11 +586,12 @@ void setup_gpios(
|
||||||
|
|
||||||
// Set GPIO pins 7-11 to output
|
// Set GPIO pins 7-11 to output
|
||||||
for (g=7; g<=11; g++) {
|
for (g=7; g<=11; g++) {
|
||||||
INP_GPIO(g); // must use INP_GPIO before we can use OUT_GPIO
|
//INP_GPIO(g); // must use INP_GPIO before we can use OUT_GPIO
|
||||||
//OUT_GPIO(g);
|
//OUT_GPIO(g);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void print_usage() {
|
void print_usage() {
|
||||||
std::cout << "Usage:" << std::endl;
|
std::cout << "Usage:" << std::endl;
|
||||||
|
@ -715,12 +813,10 @@ void tone_main(
|
||||||
int bufPtr=0;
|
int bufPtr=0;
|
||||||
|
|
||||||
while (!terminate) {
|
while (!terminate) {
|
||||||
MARK;
|
|
||||||
// Read the current values of the atomics.
|
// Read the current values of the atomics.
|
||||||
double freq_new=freq;
|
double freq_new=freq;
|
||||||
double ppm_new=ppm;
|
double ppm_new=ppm;
|
||||||
|
|
||||||
MARK;
|
|
||||||
// Update table if necessary.
|
// Update table if necessary.
|
||||||
if (
|
if (
|
||||||
(ppm_new!=ppm_old) ||
|
(ppm_new!=ppm_old) ||
|
||||||
|
@ -729,13 +825,11 @@ void tone_main(
|
||||||
) {
|
) {
|
||||||
setupDMATab(freq_new,F_PLLD_CLK*(1-ppm_new/1e6),dma_table_freq,constPage);
|
setupDMATab(freq_new,F_PLLD_CLK*(1-ppm_new/1e6),dma_table_freq,constPage);
|
||||||
}
|
}
|
||||||
MARK;
|
|
||||||
|
|
||||||
// Transmit for a small amount of time before checking for updates to
|
// Transmit for a small amount of time before checking for updates to
|
||||||
// frequency or PPM.
|
// frequency or PPM.
|
||||||
double tx_time_secs=1.0;
|
double tx_time_secs=1.0;
|
||||||
tone_thread_ready=true;
|
tone_thread_ready=true;
|
||||||
MARK;
|
|
||||||
txSym(
|
txSym(
|
||||||
terminate,
|
terminate,
|
||||||
freq_new,
|
freq_new,
|
||||||
|
@ -746,7 +840,6 @@ void tone_main(
|
||||||
constPage,
|
constPage,
|
||||||
bufPtr
|
bufPtr
|
||||||
);
|
);
|
||||||
MARK;
|
|
||||||
|
|
||||||
freq_old=freq_new;
|
freq_old=freq_new;
|
||||||
ppm_old=ppm_new;
|
ppm_old=ppm_new;
|
||||||
|
@ -846,7 +939,7 @@ void set_current(
|
||||||
}
|
}
|
||||||
if (value==0) {
|
if (value==0) {
|
||||||
// Turn off output
|
// Turn off output
|
||||||
ACCESS(CM_GP0CTL) =
|
ACCESS_BUS_ADDR(CM_GP0CTL_BUS) =
|
||||||
// PW
|
// PW
|
||||||
(0x5a<<24) |
|
(0x5a<<24) |
|
||||||
// MASH
|
// MASH
|
||||||
|
@ -864,9 +957,9 @@ void set_current(
|
||||||
;
|
;
|
||||||
} else {
|
} else {
|
||||||
// Set drive strength
|
// Set drive strength
|
||||||
ACCESS(PADS_GPIO_0_27) = 0x5a000018 + ((value - 1)&0x7);
|
ACCESS_BUS_ADDR(PADS_GPIO_0_27_BUS) = 0x5a000018 + ((value - 1)&0x7);
|
||||||
// Turn on output
|
// Turn on output
|
||||||
ACCESS(CM_GP0CTL) =
|
ACCESS_BUS_ADDR(CM_GP0CTL_BUS) =
|
||||||
// PW
|
// PW
|
||||||
(0x5a<<24) |
|
(0x5a<<24) |
|
||||||
// MASH
|
// MASH
|
||||||
|
@ -1129,6 +1222,7 @@ void morse_table_init(
|
||||||
morse_table['@']=".--.-.";
|
morse_table['@']=".--.-.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
@ -1143,20 +1237,106 @@ void open_mbox() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void unlinkmbox() {
|
// Called when exiting or when a signal is received.
|
||||||
|
void cleanup() {
|
||||||
|
disable_clock();
|
||||||
|
unSetupDMA();
|
||||||
|
deallocMemPool();
|
||||||
unlink(DEVICE_FILE_NAME);
|
unlink(DEVICE_FILE_NAME);
|
||||||
unlink(LOCAL_DEVICE_FILE_NAME);
|
unlink(LOCAL_DEVICE_FILE_NAME);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
// Called when a signal is received. Automatically calls cleanup().
|
||||||
|
void cleanupAndExit(int sig) {
|
||||||
|
std::cerr << "Exiting with error; caught signal: " << sig << std::endl;
|
||||||
|
cleanup();
|
||||||
|
ABORT(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
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. This is done by giving our
|
||||||
|
//process a high priority. Note, must run as super-user for this to work.
|
||||||
|
struct sched_param sp;
|
||||||
|
sp.sched_priority=priority;
|
||||||
|
int ret = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp);
|
||||||
|
if (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.
|
||||||
|
#if 0
|
||||||
|
void setup_peri_base_virt(
|
||||||
|
volatile unsigned * & peri_base_virt
|
||||||
|
) {
|
||||||
|
int mem_fd;
|
||||||
|
// open /dev/mem
|
||||||
|
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
|
||||||
|
std::cerr << "Error: can't open /dev/mem" << std::endl;
|
||||||
|
ABORT (-1);
|
||||||
|
}
|
||||||
|
std::cout << "peri_base_virt: " << std::hex << (unsigned int)peri_base_virt << std::dec << std::endl;
|
||||||
|
peri_base_virt = (unsigned *)mmap(
|
||||||
|
NULL,
|
||||||
|
0x01000000, //len
|
||||||
|
PROT_READ|PROT_WRITE,
|
||||||
|
MAP_SHARED,
|
||||||
|
mem_fd,
|
||||||
|
PERI_BASE_PHYS //base
|
||||||
|
);
|
||||||
|
std::cout << "peri_base_virt: " << std::hex << (unsigned int)peri_base_virt << std::dec << std::endl;
|
||||||
|
if (peri_base_virt==MAP_FAILED) {
|
||||||
|
std::cerr << "Error: peri_base_virt mmap error!" << std::endl;
|
||||||
|
ABORT(-1);
|
||||||
|
}
|
||||||
|
close(mem_fd);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
void setup_peri_base_virt(
|
||||||
|
volatile unsigned * & peri_base_virt
|
||||||
|
) {
|
||||||
|
int mem_fd;
|
||||||
|
// open /dev/mem
|
||||||
|
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
|
||||||
|
std::cerr << "Error: can't open /dev/mem" << std::endl;
|
||||||
|
ABORT (-1);
|
||||||
|
}
|
||||||
|
peri_base_virt = (unsigned *)mmap(
|
||||||
|
NULL,
|
||||||
|
0x01000000, //len
|
||||||
|
PROT_READ|PROT_WRITE,
|
||||||
|
MAP_SHARED,
|
||||||
|
mem_fd,
|
||||||
|
PERI_BASE_PHYS //base
|
||||||
|
);
|
||||||
|
if (peri_base_virt==MAP_FAILED) {
|
||||||
|
std::cerr << "Error: peri_base_virt mmap error!" << std::endl;
|
||||||
|
ABORT(-1);
|
||||||
|
}
|
||||||
|
close(mem_fd);
|
||||||
|
}
|
||||||
|
|
||||||
int main(const int argc, char * const argv[]) {
|
int main(const int argc, char * const argv[]) {
|
||||||
|
//catch all signals (like ctrl+c, ctrl+z, ...) to ensure DMA is disabled
|
||||||
|
/*
|
||||||
|
for (int i = 0; i < 64; i++) {
|
||||||
|
struct sigaction sa;
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sa.sa_handler = cleanupAndExit;
|
||||||
|
sigaction(i, &sa, NULL);
|
||||||
|
}
|
||||||
|
atexit(cleanup);
|
||||||
|
setSchedPriority(30);
|
||||||
|
*/
|
||||||
|
|
||||||
#ifdef RPI1
|
#ifdef RPI1
|
||||||
std::cout << "Detected Raspberry Pi version 1" << std::endl;
|
std::cout << "Detected Raspberry Pi version 1" << std::endl;
|
||||||
#else
|
#else
|
||||||
std::cout << "Detected Raspberry Pi version 2/3" << std::endl;
|
std::cout << "Detected Raspberry Pi version 2/3" << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
atexit(unlinkmbox);
|
|
||||||
|
|
||||||
// Parse arguments
|
// Parse arguments
|
||||||
double freq_init;
|
double freq_init;
|
||||||
double wpm_init;
|
double wpm_init;
|
||||||
|
@ -1178,32 +1358,15 @@ int main(const int argc, char * const argv[]) {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Initial configuration
|
// Initial configuration
|
||||||
int mem_fd;
|
|
||||||
char *gpio_mem, *gpio_map;
|
|
||||||
volatile unsigned *gpio = NULL;
|
|
||||||
setup_io(mem_fd,gpio_mem,gpio_map,gpio);
|
|
||||||
setup_gpios(gpio);
|
|
||||||
allof7e = (unsigned *)mmap(
|
|
||||||
NULL,
|
|
||||||
0x002FFFFF, //len
|
|
||||||
PROT_READ|PROT_WRITE,
|
|
||||||
MAP_SHARED,
|
|
||||||
mem_fd,
|
|
||||||
BCM2708_PERI_BASE //base
|
|
||||||
);
|
|
||||||
if ((long int)allof7e==-1) {
|
|
||||||
std::cerr << "Error: mmap error!" << std::endl;
|
|
||||||
ABORT(-1);
|
|
||||||
}
|
|
||||||
open_mbox();
|
|
||||||
|
|
||||||
// Configure GPIO4
|
|
||||||
SETBIT(GPIO_PHYS_BASE , 14);
|
|
||||||
CLRBIT(GPIO_PHYS_BASE , 13);
|
|
||||||
CLRBIT(GPIO_PHYS_BASE , 12);
|
|
||||||
struct PageInfo constPage;
|
struct PageInfo constPage;
|
||||||
struct PageInfo instrPage;
|
struct PageInfo instrPage;
|
||||||
struct PageInfo instrs[1024];
|
struct PageInfo instrs[1024];
|
||||||
|
setup_peri_base_virt(peri_base_virt);
|
||||||
|
open_mbox();
|
||||||
|
// Configure GPIO4
|
||||||
|
SETBIT_BUS_ADDR(GPIO_BUS_BASE , 14);
|
||||||
|
CLRBIT_BUS_ADDR(GPIO_BUS_BASE , 13);
|
||||||
|
CLRBIT_BUS_ADDR(GPIO_BUS_BASE , 12);
|
||||||
setupDMA(constPage,instrPage,instrs);
|
setupDMA(constPage,instrPage,instrs);
|
||||||
|
|
||||||
// Morse code table.
|
// Morse code table.
|
||||||
|
@ -1271,7 +1434,6 @@ int main(const int argc, char * const argv[]) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
}
|
}
|
||||||
|
|
||||||
MARK;
|
|
||||||
// Wait for queue to be emptied.
|
// Wait for queue to be emptied.
|
||||||
while (true) {
|
while (true) {
|
||||||
{
|
{
|
||||||
|
@ -1282,7 +1444,6 @@ int main(const int argc, char * const argv[]) {
|
||||||
}
|
}
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
}
|
}
|
||||||
MARK;
|
|
||||||
|
|
||||||
// Wait for final character to be transmitted.
|
// Wait for final character to be transmitted.
|
||||||
while (am_thread_busy) {
|
while (am_thread_busy) {
|
||||||
|
@ -1290,19 +1451,15 @@ int main(const int argc, char * const argv[]) {
|
||||||
}
|
}
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
|
|
||||||
MARK;
|
|
||||||
// Terminate subthreads
|
// Terminate subthreads
|
||||||
terminate_am_thread=true;
|
terminate_am_thread=true;
|
||||||
terminate_tone_thread=true;
|
terminate_tone_thread=true;
|
||||||
MARK;
|
|
||||||
if (am_thread.joinable()) {
|
if (am_thread.joinable()) {
|
||||||
am_thread.join();
|
am_thread.join();
|
||||||
}
|
}
|
||||||
MARK;
|
|
||||||
if (tone_thread.joinable()) {
|
if (tone_thread.joinable()) {
|
||||||
tone_thread.join();
|
tone_thread.join();
|
||||||
}
|
}
|
||||||
MARK;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
4
makefile
4
makefile
|
@ -8,8 +8,8 @@ all: PiCW
|
||||||
mailbox.o: mailbox.c mailbox.h
|
mailbox.o: mailbox.c mailbox.h
|
||||||
g++ -c -Wall -lm mailbox.c
|
g++ -c -Wall -lm mailbox.c
|
||||||
|
|
||||||
PiCW: PiCW.cpp mailbox.o
|
PiCW: PiCW.cpp mailbox.o mailbox.h
|
||||||
g++ -D_GLIBCXX_DEBUG -std=c++11 -Wall -Werror -fmax-errors=5 -lm mailbox.o PiCW.cpp -pthread -oPiCW
|
g++ -D_GLIBCXX_DEBUG -std=c++11 -Wall -Werror -fmax-errors=5 -lm $(pi_version_flag) mailbox.o PiCW.cpp -pthread -oPiCW
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
-rm PiCW
|
-rm PiCW
|
||||||
|
|
Ładowanie…
Reference in New Issue