Created
December 13, 2013 17:56
-
-
Save rigid/7948348 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/********** 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 <stdio.h> | |
#include <stdlib.h> | |
#include <fcntl.h> | |
#include <sys/mman.h> | |
#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 15 = connector pin 10 | |
// DAT = GPIO 14 = connector pin 8 | |
// code example | |
// CLKHI; | |
#define CLKHI *gpset = (1 << 4) // GPIO 15 | |
#define CLKLO *gpclr = (1 << 4) | |
#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); | |
main() | |
{ | |
int n,getout; | |
unsigned int timend; | |
sleep(1); // 1 second delay | |
// When the program starts, the interrupt | |
// system may still be dealing with the | |
// last Enter keystroke. This gives it | |
// time to finish. | |
setup(); // setup GPIO, timer and interrupt pointers | |
interrupts(0); // Disable interrupts to ensure | |
// accurate timing. | |
// Re-enable via interrupts(1) as | |
// soon as accurate timing is no | |
// longer needed. | |
// screen output, keyboard input (and who | |
// knows what else) stop working until | |
// interrupts are re-enabled | |
// 50kHz signal to CLK output = GPIO 15 connector pin 10 | |
// 1000000 cycles = 20 seconds | |
// checks ESC input pin = GPIO 18 connector pin 12 | |
// if lo - loop terminates | |
getout = 0; | |
timend = *timer + 2; // set up 10us delay from | |
// current timer value | |
for(n = 0 ; n < 1000000 && getout == 0 ; ++n) | |
{ | |
// delay to timend | |
while( (((*timer)-timend) & 0x80000000) != 0); | |
CLKHI; // output GPIO 15 hi | |
// check input GPIO 18 pin | |
// which is pulled hi by setup() | |
// exit loop if lo | |
if(ESCIN == 0) | |
getout = 1; | |
// 10us delay | |
timend += 2; | |
while( (((*timer)-timend) & 0x80000000) != 0); | |
CLKLO; // output GPIO 15 lo | |
timend += 2; // 10us delay at start of next loop | |
} | |
interrupts(1); // re-enable interrupts | |
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 | |
************************************/ | |
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/15 = outputs | |
// control reg = gpio + 1 = pin/10 | |
// GPIO 14 shift 3 bits by 12 = (pin rem 10) * 3 | |
// GPIO 15 shift 3 bits by 15 = (pin rem 10) * 3 | |
*(gpio+1) &= ~(7 << 12); // GPIO 14 zero 3 bits | |
*(gpio+1) |= (1 << 12); // 3 bits = 001 output | |
*(gpio+1) &= ~(7 << 4); // GPIO 15 zero 3 bits | |
*(gpio+1) |= (1 << 4); // 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 ************************/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment