From fd4e52a2b8bd855829fe187c27141637990a16f9 Mon Sep 17 00:00:00 2001 From: James Peroulas Date: Tue, 28 Feb 2017 06:41:39 +0000 Subject: [PATCH] Seems to be working. Forgot -DRPIx in makefile :( --- PiCW.cpp | 579 +++++++++++++++++++++++++++++++++++-------------------- makefile | 4 +- 2 files changed, 370 insertions(+), 213 deletions(-) diff --git a/PiCW.cpp b/PiCW.cpp index 056cdfc..68ca577 100644 --- a/PiCW.cpp +++ b/PiCW.cpp @@ -52,17 +52,66 @@ #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) // Used for debugging #define MARK std::cout << "Currently in file: " << __FILE__ << " line: " << __LINE__ << std::endl // PLLD clock frequency. -// There seems to be a 2.5ppm offset between the NTP measured frequency -// error and the frequency error measured by a frequency counter. This fixed -// PPM offset is compensated for here. -// This PPM correction is not needed for RPI2/3. +// For RPi1, after NTP converges, these is a 2.5 PPM difference between +// the PPM correction reported by NTP and the actual frequency offset of +// the crystal. This 2.5 PPM offset is not present in the RPi2 and RPi3. +// This 2.5 PPM offset is compensated for here, but only for the RPi1. #ifdef RPI2 -#define F_PLLD_CLK (500000000.0*(1-0.000e-6)) +#define F_PLLD_CLK (500000000.0) #else #define F_PLLD_CLK (500000000.0*(1-2.500e-6)) #endif @@ -73,25 +122,28 @@ #define F_PWM_CLK_INIT (31156186.6125761) // 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 -#define BCM2708_PERI_BASE 0x3f000000 +#define PERI_BASE_PHYS 0x3f000000 #define MEM_FLAG 0x04 -//#pragma message "Raspberry Pi 2/3 detected." #else -#define BCM2708_PERI_BASE 0x20000000 +#define PERI_BASE_PHYS 0x20000000 #define MEM_FLAG 0x0c -//#pragma message "Raspberry Pi 1 detected." #endif #define PAGE_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. -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) -#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 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_GET *(gpio+13) // sets bits which are 1 ignores bits which are 0 -#define ACCESS(base) *(volatile int*)((long int)allof7e+base-0x7e000000) -#define SETBIT(base, bit) ACCESS(base) |= 1<=mbox.pool_size) { - std::cerr << "Error: unable to allocated more pages!" << std::endl; - ABORT(-1); - } - unsigned offset = mbox.pool_cnt*4096; - *vAddr = (void*)(((unsigned)mbox.virt_addr) + offset); - *pAddr = (void*)(((unsigned)mbox.bus_addr) + offset); - //printf("getRealMemoryPageFromPool bus_addr=%x virt_addr=%x\n", (unsigned)*pAddr,(unsigned)*vAddr); - mbox.pool_cnt++; +// 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) { + std::cerr << "Error: unable to allocated more pages!" << std::endl; + ABORT(-1); + } + unsigned offset = mbox.pool_cnt*4096; + *vAddr = (void*)(((unsigned)mbox.virt_addr) + offset); + *bAddr = (void*)(((unsigned)mbox.bus_addr) + offset); + //printf("getRealMemoryPageFromPool bus_addr=%x virt_addr=%x\n", (unsigned)*pAddr,(unsigned)*vAddr); + mbox.pool_cnt++; } -void deallocMemPool() -{ - if(mbox.virt_addr) //it will be 0 by default as in .bss - { - unmapmem(mbox.virt_addr, mbox.pool_size*4096); - mem_unlock(mbox.handle, mbox.mem_ref); - mem_free(mbox.handle, mbox.mem_ref); +// Free the memory pool +void deallocMemPool() { + if(mbox.virt_addr!=NULL) { + unmapmem(mbox.virt_addr, mbox.pool_size*4096); + } + if (mbox.mem_ref!=0) { + mem_unlock(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. @@ -234,67 +327,68 @@ void txSym( // Configure the transmission for this iteration // Set GPIO pin to transmit f0 bufPtr++; - MARK; - while( ACCESS(DMA_PHYS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].p)) usleep(100); - ((struct CB*)(instrs[bufPtr].v))->SOURCE_AD = (long int)constPage.p + f0_idx*4; + ACCESS_BUS_ADDR(DMA_BUS_BASE+0x20) = 0x31401234; + 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.b + f0_idx*4; // Wait for n_f0 PWM clocks bufPtr++; - MARK; - 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))->TXFR_LEN = n_f0; // Set GPIO pin to transmit f1 bufPtr++; - MARK; - while( ACCESS(DMA_PHYS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].p)) usleep(100); - ((struct CB*)(instrs[bufPtr].v))->SOURCE_AD = (long int)constPage.p + f1_idx*4; + 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.b + f1_idx*4; // Wait for n_f1 PWM clocks bufPtr=(bufPtr+1) % (1024); - MARK; - while( ACCESS(DMA_PHYS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].p)) { - std::cout << ACCESS(DMA_PHYS_BASE + 0x04 /* CurBlock*/) << std::endl; + while( ACCESS_BUS_ADDR(DMA_BUS_BASE + 0x04 /* CurBlock*/) == (long int)(instrs[bufPtr].b)) { usleep(100); } - MARK; ((struct CB*)(instrs[bufPtr].v))->TXFR_LEN = n_f1; - MARK; // Update counters n_pwmclk_transmitted+=n_pwmclk; n_f0_transmitted+=n_f0; - MARK; } - MARK; } void unSetupDMA(){ - //printf("exiting\n"); - struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS(DMA_PHYS_BASE)); - DMA0->CS =1<<31; // reset dma controller - // Turn off GPIO clock - ACCESS(CM_GP0CTL) = - // PW - (0x5a<<24) | - // MASH - (1<<9) | - // Flip - (0<<8) | - // Busy - (0<<7) | - // Kill - (0<<5) | - // Enable - (0<<4) | - // SRC - (6<<0) - ; + // Check if mapping has been set up yet. + if (peri_base_virt==NULL) { + return; + } + //printf("exiting\n"); + struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS_BUS_ADDR(DMA_BUS_BASE)); + DMA0->CS =1<<31; // reset dma controller + disable_clock(); +/* + // Turn off GPIO clock + ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = + // PW + (0x5a<<24) | + // MASH + (1<<9) | + // Flip + (0<<8) | + // Busy + (0<<7) | + // Kill + (0<<5) | + // Enable + (0<<4) | + // SRC + (6<<0) + ; +*/ } +/* void handSig(const int h) { exit(0); } +*/ double bit_trunc( const double & d, @@ -344,96 +438,97 @@ void setupDMA( struct PageInfo & instrPage, struct PageInfo instrs[] ){ - atexit(unSetupDMA); - atexit(deallocMemPool); - signal (SIGINT, handSig); - signal (SIGTERM, handSig); - signal (SIGHUP, handSig); - signal (SIGQUIT, handSig); + //atexit(unSetupDMA); + //atexit(deallocMemPool); + //signal (SIGINT, handSig); + //signal (SIGTERM, handSig); + //signal (SIGHUP, handSig); + //signal (SIGQUIT, handSig); - allocMemPool(1025); + allocMemPool(1025); - // Allocate a page of ram for the constants - getRealMemPageFromPool(&constPage.v, &constPage.p); + // Allocate a page of ram for the constants + getRealMemPageFromPool(&constPage.v, &constPage.b); - // Create 1024 instructions allocating one page at a time. - // Even instructions target the GP0 Clock divider - // Odd instructions target the PWM FIFO - int instrCnt = 0; - while (instrCnt<1024) { - // Allocate a page of ram for the instructions - getRealMemPageFromPool(&instrPage.v, &instrPage.p); + // Create 1024 instructions allocating one page at a time. + // Even instructions target the GP0 Clock divider + // Odd instructions target the PWM FIFO + int instrCnt = 0; + while (instrCnt<1024) { + // Allocate a page of ram for the instructions + getRealMemPageFromPool(&instrPage.v, &instrPage.b); - // make copy instructions - // Only create as many instructions as will fit in the recently - // allocated page. If not enough space for all instructions, the - // next loop will allocate another page. - struct CB* instr0= (struct CB*)instrPage.v; - int 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].p = (void*)((long int)instrPage.p + sizeof(struct CB)*i); - instr0->SOURCE_AD = (unsigned long int)constPage.p+2048; - instr0->DEST_AD = PWM_PHYS_BASE+0x18 /* FIF1 */; - instr0->TXFR_LEN = 4; - instr0->STRIDE = 0; - //instr0->NEXTCONBK = (int)instrPage.p + sizeof(struct CB)*(i+1); - instr0->TI = (1/* DREQ */<<6) | (5 /* PWM */<<16) | (1<<26/* no wide*/) ; - instr0->RES1 = 0; - instr0->RES2 = 0; + // make copy instructions + // Only create as many instructions as will fit in the recently + // allocated page. If not enough space for all instructions, the + // next loop will allocate another page. + struct CB* instr0= (struct CB*)instrPage.v; + int 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].b = (void*)((long int)instrPage.b + sizeof(struct CB)*i); + instr0->SOURCE_AD = (unsigned long int)constPage.b+2048; + instr0->DEST_AD = PWM_BUS_BASE+0x18 /* FIF1 */; + instr0->TXFR_LEN = 4; + instr0->STRIDE = 0; + //instr0->NEXTCONBK = (int)instrPage.b + sizeof(struct CB)*(i+1); + instr0->TI = (1/* DREQ */<<6) | (5 /* PWM */<<16) | (1<<26/* no wide*/) ; + instr0->RES1 = 0; + instr0->RES2 = 0; - // Shouldn't this be (instrCnt%2) ??? - if (i%2) { - instr0->DEST_AD = CM_GP0DIV; - instr0->STRIDE = 4; - instr0->TI = (1<<26/* no wide*/) ; - } + // Shouldn't this be (instrCnt%2) ??? + if (i%2) { + instr0->DEST_AD = CM_GP0DIV_BUS; + instr0->STRIDE = 4; + instr0->TI = (1<<26/* no wide*/) ; + } - if (instrCnt!=0) ((struct CB*)(instrs[instrCnt-1].v))->NEXTCONBK = (long int)instrs[instrCnt].p; - instr0++; - instrCnt++; - } - } - // Create a circular linked list of instructions - ((struct CB*)(instrs[1023].v))->NEXTCONBK = (long int)instrs[0].p; + if (instrCnt!=0) ((struct CB*)(instrs[instrCnt-1].v))->NEXTCONBK = (long int)instrs[instrCnt].b; + instr0++; + instrCnt++; + } + } + // Create a circular linked list of instructions + ((struct CB*)(instrs[1023].v))->NEXTCONBK = (long int)instrs[0].b; - // set up a clock for the PWM - ACCESS(CLK_PHYS_BASE + 40*4 /*PWMCLK_CNTL*/) = 0x5A000026; // Source=PLLD and disable - usleep(1000); - //ACCESS(CLK_PHYS_BASE + 41*4 /*PWMCLK_DIV*/) = 0x5A002800; - ACCESS(CLK_PHYS_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 - usleep(1000); + // set up a clock for the PWM + ACCESS_BUS_ADDR(CLK_BUS_BASE + 40*4 /*PWMCLK_CNTL*/) = 0x5A000026; // Source=PLLD and disable + usleep(1000); + //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 + 40*4 /*PWMCLK_CNTL*/) = 0x5A000016; // Source=PLLD and enable + usleep(1000); - // set up pwm - ACCESS(PWM_PHYS_BASE + 0x0 /* CTRL*/) = 0; - usleep(1000); - ACCESS(PWM_PHYS_BASE + 0x4 /* status*/) = -1; // clear errors - usleep(1000); - // Range should default to 32, but it is set at 2048 after reset on my RPi. - ACCESS(PWM_PHYS_BASE + 0x10)=32; - ACCESS(PWM_PHYS_BASE + 0x20)=32; - ACCESS(PWM_PHYS_BASE + 0x0 /* CTRL*/) = -1; //(1<<13 /* Use fifo */) | (1<<10 /* repeat */) | (1<<9 /* serializer */) | (1<<8 /* enable ch */) ; - usleep(1000); - ACCESS(PWM_PHYS_BASE + 0x8 /* DMAC*/) = (1<<31 /* DMA enable */) | 0x0707; + // set up pwm + ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x0 /* CTRL*/) = 0; + usleep(1000); + ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x4 /* status*/) = -1; // clear errors + usleep(1000); + // 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 + 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 */) ; + usleep(1000); + ACCESS_BUS_ADDR(PWM_BUS_BASE + 0x8 /* DMAC*/) = (1<<31 /* DMA enable */) | 0x0707; - //activate dma - struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS(DMA_PHYS_BASE)); - DMA0->CS =1<<31; // reset - DMA0->CONBLK_AD=0; - DMA0->TI=0; - DMA0->CONBLK_AD = (unsigned long int)(instrPage.p); - DMA0->CS =(1<<0)|(255 <<16); // enable bit = 0, clear end flag = 1, prio=19-16 + //activate dma + struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS_BUS_ADDR(DMA_BUS_BASE)); + DMA0->CS =1<<31; // reset + DMA0->CONBLK_AD=0; + DMA0->TI=0; + DMA0->CONBLK_AD = (unsigned long int)(instrPage.b); + 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 // void setup_io( - int & mem_fd, - char * & gpio_mem, - char * & gpio_map, - volatile unsigned * & gpio + int & mem_fd + //char * & gpio_mem, + //char * & gpio_map, + //volatile unsigned * & gpio ) { /* open /dev/mem */ if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) { @@ -471,7 +566,9 @@ void setup_io( // Always use volatile pointer! gpio = (volatile unsigned *)gpio_map; } +#endif +#if 0 // Not sure why this function is needed as this code only uses GPIO4 and // this function sets gpio 7 through 11 as input... void setup_gpios( @@ -489,11 +586,12 @@ void setup_gpios( // Set GPIO pins 7-11 to output 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); } } +#endif void print_usage() { std::cout << "Usage:" << std::endl; @@ -715,12 +813,10 @@ void tone_main( int bufPtr=0; while (!terminate) { - MARK; // Read the current values of the atomics. double freq_new=freq; double ppm_new=ppm; - MARK; // Update table if necessary. if ( (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); } - MARK; // Transmit for a small amount of time before checking for updates to // frequency or PPM. double tx_time_secs=1.0; tone_thread_ready=true; - MARK; txSym( terminate, freq_new, @@ -746,7 +840,6 @@ void tone_main( constPage, bufPtr ); - MARK; freq_old=freq_new; ppm_old=ppm_new; @@ -846,7 +939,7 @@ void set_current( } if (value==0) { // Turn off output - ACCESS(CM_GP0CTL) = + ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = // PW (0x5a<<24) | // MASH @@ -864,9 +957,9 @@ void set_current( ; } else { // 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 - ACCESS(CM_GP0CTL) = + ACCESS_BUS_ADDR(CM_GP0CTL_BUS) = // PW (0x5a<<24) | // MASH @@ -1129,6 +1222,7 @@ void morse_table_init( morse_table['@']=".--.-."; } +// Create the mbox special files and open mbox. void open_mbox() { unlink(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(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[]) { + //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 std::cout << "Detected Raspberry Pi version 1" << std::endl; #else std::cout << "Detected Raspberry Pi version 2/3" << std::endl; #endif - atexit(unlinkmbox); - // Parse arguments double freq_init; double wpm_init; @@ -1178,32 +1358,15 @@ int main(const int argc, char * const argv[]) { ); // 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 instrPage; 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); // Morse code table. @@ -1271,7 +1434,6 @@ int main(const int argc, char * const argv[]) { std::this_thread::sleep_for(std::chrono::milliseconds(200)); } - MARK; // Wait for queue to be emptied. while (true) { { @@ -1282,7 +1444,6 @@ int main(const int argc, char * const argv[]) { } std::this_thread::sleep_for(std::chrono::milliseconds(100)); } - MARK; // Wait for final character to be transmitted. while (am_thread_busy) { @@ -1290,19 +1451,15 @@ int main(const int argc, char * const argv[]) { } std::cout << std::endl; - MARK; // Terminate subthreads terminate_am_thread=true; terminate_tone_thread=true; - MARK; if (am_thread.joinable()) { am_thread.join(); } - MARK; if (tone_thread.joinable()) { tone_thread.join(); } - MARK; return 0; } diff --git a/makefile b/makefile index 6e57a74..380cdb8 100644 --- a/makefile +++ b/makefile @@ -8,8 +8,8 @@ all: PiCW mailbox.o: mailbox.c mailbox.h g++ -c -Wall -lm mailbox.c -PiCW: PiCW.cpp mailbox.o - g++ -D_GLIBCXX_DEBUG -std=c++11 -Wall -Werror -fmax-errors=5 -lm mailbox.o PiCW.cpp -pthread -oPiCW +PiCW: PiCW.cpp mailbox.o mailbox.h + g++ -D_GLIBCXX_DEBUG -std=c++11 -Wall -Werror -fmax-errors=5 -lm $(pi_version_flag) mailbox.o PiCW.cpp -pthread -oPiCW clean: -rm PiCW