// // rpc.c - customization of a useful utility for real-time // process control on a raspberry pi. // // author: brendan.pratt at uconn.edu // version: may 4, 2015 // acknowledgement: based on original code for B+ hardware // written by petzval // reference url: https://www.raspberrypi.org/forums/viewtopic=php?t=52393 // /********** 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; volatile int *timer = setup_rtc() // 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 #define GPIO_BASE 0x20200000 #define TIMER_BASE 0x20003000 #define INT_BASE 0x2000B000 volatile unsigned int *gpio; volatile unsigned int *gpset; volatile unsigned int *gpclr; volatile unsigned int *gpin; volatile unsigned int *timer; volatile unsigned int *intrupt; int interrupts(int flag) { //******************** 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. //******************************************* 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; } volatile int *setup_rtc() { //***************** SETUP ***************** // Sets timer and interrupt pointers for future use, // but does not disable interrupts. // return timer pointer = OK // 0 = error with message print //***************************************** int memfd; unsigned int timend; void *gpio_map; void *timer_map; void *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 return timer; } int gpio_config_input(int pinno, int pud) { // Configures the GPIO interface to configure pinno for input // and enable pull-up on that pin or not // pud = 0 disable pull up/down // 1 enable pull down // 2 enable pull up int gpio_addr = pinno / 10; int gpio_offset = (pinno % 10) * 3; if (pinno < 2 || pinno > 27) return 0; *(gpio + gpio_addr) &= ~(7 << gpio_offset); return 1; } int gpio_config_output(int pinno) { // Configures the GPIO interface to configure pinno for output int gpio_addr = pinno / 10; int gpio_offset = (pinno % 10) * 3; if (pinno < 2 || pinno > 27) return 0; *(gpio + gpio_addr) |= (1 << gpio_offset); return 1; } int gpio_read_input(int pinno) { // Reads the state of gpio input gpio, should have bee configured // using gpio_config_input(pinno) if (pinno < 2 || pinno > 27) return 0; return (*gpin >> pinno) & 1; } int gpio_write_output(int pinno, int value) { // Configures the GPIO interface to configure pinno for output if (pinno < 2 || pinno > 27) return 0; else if (value) *gpset = (1 << pinno); else *gpclr = (1 << pinno); return 1; }