/********** ACCURATE TIMER for REAL TIME CONTROL *** This C program illustrates accurate timing on a Raspberry Pi by sending a 50kHz signal to a GPIO pin with a jitter of about 0.1 microseconds. It uses the processor's 1MHz timer and disables interrupts. It includes GPIO setup and read/write code. Compiled from console with gcc under the standard Debian distribution. Tested with a keyboard and HDMI monitor attached, and X Windows not started. **************************************************/ /*********** TIMER CODE example ******* unsigned int timend; setup() // initialise system // call only once interrupts(0); // disable interrupts timend = *timer + 200; // Set up 200 microsecond delay // Maximum possible delay // is 7FFFFFF or about 35 minutes while((((*timer)-timend) & 0x80000000) != 0); // delay loop // This works even if *timer // overflows to zero during the delay, // or if the while test misses the exact // termination when (*timer-timend) == 0. // Jitter in delay about 1 microsceond. // Can be reduced to about 0.1 microsecond // by synchronising the timend set // instruction to a change in *timer // if interrupts are not disabled // the delay can occasionally be // 2ms (or more) longer than requested // and is routinely out by up to 0.1ms interrupts(1); // re-enable interrupts *************************************************/ #include #include #include #include #include #include #include #define GPIO_BASE 0x20200000 #define TIMER_BASE 0x20003000 #define INT_BASE 0x2000B000 volatile unsigned *gpio,*gpset,*gpclr,*gpin,*timer,*intrupt; /******************** GPIO read/write *******************/ // outputs CLK = GPIO 17 = connector pin 11 // DAT = GPIO 14 = connector pin 8 // code example // CLKHI; #define CLKHI *gpset = (1 << 17) // GPIO 17 #define CLKLO *gpclr = (1 << 17) #define DATHI *gpset = (1 << 14) // GPIO 14 #define DATLO *gpclr = (1 << 14) // inputs P3 = GPIO 3 = connector pin 5 (Rev 2 board) // P2 = GPIO 2 = connector pin 3 (Rev 2 board) // ESC = GPIO 18 = connector pin 12 // code examples // if(P2IN == 0) // if(P2IN != 0) // n = P2INBIT; // 0 or 1 #define ESCIN (*gpin & (1 << 18)) // GPIO 18 #define P2IN (*gpin & (1 << 2)) // GPIO 2 #define P3IN (*gpin & (1 << 3) // GPIO 3 #define P2INBIT ((*gpin >> 2) & 1) // GPIO 2 #define P3INBIT ((*gpin >> 3) & 1) // GPIO 3 /******************* END GPIO ****************/ int setup(void); int interrupts(int flag); uint32_t **parray; main() { FILE *fp = fopen("jd70_2_6mid_1.seq", "r"); int n, getout; unsigned int timend; sleep(1); setup(); interrupts(0); getout = 0; timend = *timer + 10000; while (fgets(inbuff, sizeof(inbuff), fp)) { int lineno; float y0, y1; float x0, x1; int bcount; int w, bit; sscanf(strtok(inbuff, " "), "%d", &lineno); sscanf(strtok(0, " "), "%f", &y0); sscanf(strtok(0, " "), "%f", &y1); sscanf(strtok(0, " "), "%f", &x0); sscanf(strtok(0, " "), "%f", &x1); sscanf(strtok(0, " "), "%d", &bcount); for (w = 0; w < ceil(bcount/32.); w++) { sscanf(strtok(0, " "), "%x", &parray[w]); for (bit = 0; bit < 32; ++bit) { timend += 10000; if (parray[w] & (1 << bit)) { while ((((*timer) - timend) & 0x80000000) != 0) { CLKHI; } timend += 10000; while ((((*timer) - timend) & 0x80000000)) { CLKLO; } } else { while ((((*timer) - timend) & 0x80000000)) { CLKLO; if (ESCIN == 0) { getout = 1; } } timend += 10000; while ((((*timer) - timend) & 0x80000000)) { CLKLO; if (ESCIN == 0) { getout = 1; } } } } } } interrupts(1); // re-enable interrupts fclose(fp); return; } /******************** INTERRUPTS ************* Is this safe? Dunno, but it works interrupts(0) disable interrupts interrupts(1) re-enable interrupts return 1 = OK 0 = error with message print Uses intrupt pointer set by setup() Does not disable FIQ which seems to cause a system crash Avoid calling immediately after keyboard input or key strokes will not be dealt with properly *******************************************/ int interrupts(int flag) { static unsigned int sav132 = 0; static unsigned int sav133 = 0; static unsigned int sav134 = 0; if(flag == 0) // disable { if(sav132 != 0) { // Interrupts already disabled so avoid printf return(0); } if( (*(intrupt+128) | *(intrupt+129) | *(intrupt+130)) != 0) { printf("Pending interrupts\n"); // may be OK but probably return(0); // better to wait for the } // pending interrupts to // clear sav134 = *(intrupt+134); *(intrupt+137) = sav134; sav132 = *(intrupt+132); // save current interrupts *(intrupt+135) = sav132; // disable active interrupts sav133 = *(intrupt+133); *(intrupt+136) = sav133; } else // flag = 1 enable { if(sav132 == 0) { printf("Interrupts not disabled\n"); return(0); } *(intrupt+132) = sav132; // restore saved interrupts *(intrupt+133) = sav133; *(intrupt+134) = sav134; sav132 = 0; // indicates interrupts enabled } return(1); } /***************** SETUP **************** Sets up five GPIO pins as described in comments Sets timer and interrupt pointers for future use Does not disable interrupts return 1 = OK 0 = error with message print Also opens file to read hexadecimal values from to create the bit pattern for each individual row. ************************************/ int setup() { int memfd; unsigned int timend; void *gpio_map,*timer_map,*int_map; memfd = open("/dev/mem",O_RDWR|O_SYNC); if(memfd < 0) { printf("Mem open error\n"); return(0); } gpio_map = mmap(NULL,4096,PROT_READ|PROT_WRITE, MAP_SHARED,memfd,GPIO_BASE); timer_map = mmap(NULL,4096,PROT_READ|PROT_WRITE, MAP_SHARED,memfd,TIMER_BASE); int_map = mmap(NULL,4096,PROT_READ|PROT_WRITE, MAP_SHARED,memfd,INT_BASE); close(memfd); if(gpio_map == MAP_FAILED || timer_map == MAP_FAILED || int_map == MAP_FAILED) { printf("Map failed\n"); return(0); } // interrupt pointer intrupt = (volatile unsigned *)int_map; // timer pointer timer = (volatile unsigned *)timer_map; ++timer; // timer lo 4 bytes // timer hi 4 bytes available via *(timer+1) // GPIO pointers gpio = (volatile unsigned *)gpio_map; gpset = gpio + 7; // set bit register offset 28 gpclr = gpio + 10; // clr bit register gpin = gpio + 13; // read all bits register // setup GPIO 2/3 = inputs have pull ups on board // control reg = gpio + 0 = pin/10 // GPIO 2 shift 3 bits by 6 = (pin rem 10) * 3 // GPIO 3 shift 3 bits by 9 = (pin rem 10) * 3 *gpio &= ~(7 << 6); // GPIO 2 3 bits = 000 input *gpio &= ~(7 << 9); // GPIO 3 3 bits = 000 input // setup GPIO 18 = input *(gpio+1) &= ~(7 << 24); // GPIO 18 input // enable pull up on GPIO 18 *(gpio+37) = 2; // PUD = 2 pull up // = 0 disable pull up/down // = 1 pull down timend = *timer+2; // 2us delay while( (((*timer)-timend) & 0x80000000) != 0); *(gpio+38) = (1 << 18); // PUDCLK bit set clocks PUD=2 to GPIO 18 timend = *timer+2; // 2us delay while( (((*timer)-timend) & 0x80000000) != 0); *(gpio+37) = 0; // zero PUD *(gpio+38) = 0; // zero PUDCLK // finished pull up enable // GPIO 14/17 = outputs // control reg = gpio + 1 = pin/10 // GPIO 14 shift 3 bits by 12 = (pin rem 10) * 3 // GPIO 17 shift 3 bits by 17 = (pin rem 10) * 3 *(gpio+1) &= ~(7 << 12); // GPIO 14 zero 3 bits *(gpio+1) |= (1 << 12); // 3 bits = 001 output *(gpio+1) &= ~(7 << 21); // GPIO 17 zero 3 bits *(gpio+1) |= (1 << 21); // 3 bits = 001 output return(1); } /**************** PULL UPS ********* pull up register PUD = gpio+37 clock register PUDCLK = gpio+38 1. set PUD = 0 disable pull up/down 1 enable pull down 2 enable pull up 3 reserved *(gpio+37) = 2 to pull up 2. wait 150 cycles 3. set bit of GPIO pin in PUDCLK so for GPIO 3 *(gpio+38) = 8 to clock PUD into GPIO 3 only 4. wait 150 cycles 5. write 0 to PUD 6. write 0 to PUDCLK ************ END ************************/ char inbuff[1000];