NOTES:1) I am not a C programmerThis will be obvious when you look at the code. My background is C#, .NET. If you have any suggestions for improvements, please, let me know. 2) The preliminary documentation on the DIO pinouts is incorrect. Pins 2 and 16 are not available. Pin 2 is ground, pin 16 is 3.3V (see the schematics page: http://www.embeddedarm.com/documentation/ts-7800-schematic.pdf ) 3) The main routine, startread explained:
Description:The idea is to read the DIO inputs, watch for changes and trigger the results. For most edge devices (or the ones I need, rainbuckets and flow meters) I want to count the pulses as the device goes back to open (state value =1). So, for example, a pulse is generated which is for a flow, I only want to know when the pulse goes away, and that's one count. FOr this device, I watch it as a normallyopen device. Some devices, like reed switches on a door, you'd want to know when the door is closed and when it opens again. For this, you read it as a singlestate device. Usage:Example: dio -o 1 -c 21980 -s 0 -d 21981 -f 25 -m 3000 -o 1 Watch for changes to state open (value=1) on pin 1 -c 21980 Watch for changes to state closed, on 55DC (all available pins, except 1) -s 0 Do not watch for any single state changes -d 21981 Debounce on all available pins (55DD) -f 25 Sleep for 25 ms between watchs. Means the fastest frequency I'll see is 40hz (which is 20hz on no and nc circuits, because we only count every other change). -m 3000 stop after 3000 changes (pulse count pairs for nc, no and pulses for ss). Improvements:
Code:#include <stdio.h> #include<unistd.h> #include<sys/types.h> #include<sys/mman.h> #include<fcntl.h> #include<string.h> #include<signal.h> #include <sys/times.h> #include <time.h>
struct sigaction osa;
#define DIOBASE 0xE8000000
static unsigned char _Stop = 0;
void bypass_sigint(int sig_no) { _Stop = 1; printf("STOP CALLED. \n"); }
void setupSIGInterupt() { struct sigaction sa,osa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = &bypass_sigint; sigaction(SIGINT, &sa,&osa); } void print_bits( unsigned short int number ) { while( number ) { if( (number & 0x01) ) { putchar( '1' ) ; } else { putchar( '0' ) ; }
number = number >> 1 ; } }
/* * Returns all bits that trigger results (0 if none) * allinputs should be normallyopen | normallyclosed | singlestates * * no_trigger, nc_trigger are the bits that are a subset of normallyclosed / normallyopen that changed to trigger state * for no we watch for change back to OPEN (1), nc we watch for state change back to CLOSED (0) * single_trigger are the bits, a subset of singlestates, that changed and single_value is the current value for those bits. * * normallyopen items trigger when the state goes back to Open (i.e., =1). A button would trigger upon RELEASE * normallyclosed items trigger when the state goes back to Closed (i.e., =0), a button would trigger upon PRESS * */ unsigned int computechanges(unsigned int diovalue, unsigned int olddiovalue, unsigned int allinputs, unsigned int normallyopen, unsigned int normallyclosed, unsigned int singlestates, unsigned int *no_trigger, unsigned int *nc_trigger, unsigned int *single_trigger, unsigned int *single_value)
{
unsigned int delta = olddiovalue^diovalue; //XOR
if (delta & allinputs) { *single_trigger= delta & singlestates; unsigned int nc_changes = delta & normallyclosed; unsigned int no_changes = delta & normallyopen;
//in our bits if (*single_trigger) { *single_value = singlestates & diovalue; } else *single_value = 0;
if (nc_changes) { //watching for a 0 (went back to closed, to watch for 0 must NOT, i.e., ~, the diovalue) //bits changed //result is normallyclosed & delta & newvalue; *nc_trigger = nc_changes & ~diovalue; } else *nc_trigger = 0;
if (no_changes) { //watching for a 1 for new value (i.e., went back to open) *no_trigger = no_changes & diovalue; } else *no_trigger = 0;
} else { *no_trigger = 0; *nc_trigger = 0; *single_value = 0; *single_trigger = 0; }
return *single_trigger | *nc_trigger | *no_trigger; }
int startread (unsigned short int normallyclosed, unsigned short int normallyopen, unsigned short int singlestates, unsigned short int debounce, unsigned char frequency_ms, unsigned int max) { unsigned short int allinputs = normallyclosed | normallyopen | singlestates; volatile unsigned int *PDATA, *PDIRECTION, *GPIOBDB;
//unsigned char state, state2; //bottom 8 pins, top 8 pins unsigned char *iostart; int fd = open("/dev/mem", O_RDWR|O_SYNC); iostart = mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, DIOBASE);
//READ THE usecond counter unsigned int counteradr = 0xE8000040; unsigned char *mscounteraddr; mscounteraddr = mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, counteradr & 0xfffff000); //page
unsigned int *mscounteradr, useccounterstart, useccountervalue; mscounteradr = (unsigned int *)(mscounteraddr + (counteradr & 0xfff)); useccounterstart = *mscounteradr; //mscounter microsecond counter
//DATA STARTS HERE PDATA = (unsigned int *)(iostart + 0x04); //starting address of DIO read, E800 0000 + 4 = E800 0004
//SET THE DIRECTION OF THE IO LINES, by setting the outputs for each requiered pin to 1 we ensure tri-state functionality //PDIRECTION = (unsigned int *)(iostart + 0x14); //port b direction
PDIRECTION = (unsigned int *)(iostart + 0x8); //0xE800 0008, the output register
GPIOBDB = (unsigned int *)(iostart + 0xC4); // debounce on port b
//set all outputs (allinputs) to 1's //OLD: >>> *PDIRECTION = 0xf0; //upper nibble output, lower nibble input ... I don't know what this is about? //first, read the current (especially b/c these lines are used by other things, like LCD, etc... unsigned int outputvalue = *PDIRECTION; outputvalue |= allinputs; //make sure you set all bits high for the required inputs *PDIRECTION = outputvalue; //keeps existing 1's, adds any new 1's
//set debounce //debounce only those bits you're watching, leave other bits as they were unsigned int existingdebounce = *GPIOBDB; *GPIOBDB = existingdebounce | (debounce & allinputs);
unsigned short int diovalue = *PDATA; //only need 16 bits unsigned short int olddiovalue = diovalue; printf ("Press buttons on DIO inputs. Ctrl-C to Quit (or wait %d changes).\n", max);
int count = 0; double elapsed=0; unsigned int MICROSECONDS_PER_SEC = 1000000; //1,000,000 unsigned char changed=0;
//1ms = 1000usec unsigned int sleepTime = frequency_ms * 1000;
while (!_Stop && count<=max) //(state & 0x01) { diovalue = *PDATA; changed=0;
if (diovalue != olddiovalue) { //change occurred //now, must determine if it was in our bits to watch unsigned int no_trigger, nc_trigger, single_trigger, single_value, bitchanges; bitchanges = computechanges (diovalue, olddiovalue, allinputs, normallyopen, normallyclosed, singlestates, &no_trigger, &nc_trigger, &single_trigger, &single_value);
//make sure changed bits are in the list of bits we want to watch if (bitchanges) { printf("bitchanges:"); print_bits(bitchanges); useccountervalue = *mscounteradr; elapsed=((double) (useccountervalue - useccounterstart)) / MICROSECONDS_PER_SEC; double freq=count/elapsed; unsigned int deltabits = bitchanges;
count++; //now determine if
unsigned int diovaluebits = diovalue;
unsigned char bit =0; char changedresults[17] = ""; char onresults[17] = ""; char bitON = '0';
while (bit<16) { int thisbit = deltabits & 0x01; bitON = '0';
if (thisbit) { changedresults[bit] = 'X'; //this bit changed //determine if it's closed or open if (diovaluebits & 0x01) { bitON = 'X'; } else { bitON = '0'; } } else { bitON = '0'; changedresults[bit] = '0'; }
onresults[bit] = bitON; bit ++; if (bit<16) { diovaluebits>>=1; deltabits >>= 1; } }
//other than single state objects, b/c you're
only printing on even counts, the DISPLAYED value of diovalue //will likely remain constant (since you're not seeing the change) if (count %10 == 0){ //which pins changed printf (" %s %s Freq:%f, Count:%d, State:%X,%X,%X in [%f]\n", changedresults, onresults, freq, count, olddiovalue,diovalue, bitchanges, elapsed); } else { printf (" %s %s\n", changedresults, onresults); } changed=1; } //if bitchanges olddiovalue = diovalue; } usleep(sleepTime); //1msec } useccountervalue = *mscounteradr; elapsed=((double) (useccountervalue - useccounterstart)) / MICROSECONDS_PER_SEC; printf ("\nDONE, Count=%d in [%f] seconds.\n", count, elapsed); close(fd); if (_Stop) { sigaction(SIGINT,&osa,NULL); kill(0,SIGINT); } return 0; }
int main(int argc, char **argv) {
setupSIGInterupt();
// usable pins 1=usable //bits 0101 0101 1101 1101 == 0x55DD //pin ^--16 ^ ^ ^^--1 // | | --2 =grnd // | --6=spi_frame // --10=spi_MISO, 12=SPI_MOSI, 14=SPI_CLK, 16= 3.3V // all available are hi via 2.2K, except, 4, 8 (20K-150K)
unsigned short int nc, no, singlestates, debounce; unsigned char frequency_ms;
nc= 0; //nc watches means the state is 0, triggers on 0's //a button would trigger when you PRESS the button, for example
no = 0x55DD; //all but pine 1. 0x55DD; //all availablle, undefined ports = 0x55DD //no means the state is normally 1 (open drain), triggers on 0's //a button would trigger when you RELEASE the button
singlestates = 0x4000; //pin 15 (bit 14), door latch, notify on open and close debounce = 0; frequency_ms = 5;
unsigned int max =3000;
if (argc == 0) { printf("Usage bit values for: -c:normally closed, -o:normally open, -s:singlestates, -d:debounce, -f:frequency in ms.\nExample: -c 55DD -o 0 -s 0 -d 0 -f 25"); } else { int i=0; while (i<argc) { if (argv[i][0]=='-') { char *sw =argv[i]; switch (sw[1]) { case 'o': i++; no =atoi(argv[i]); break;
case 'c': i++; nc =atoi(argv[i]); break;
case 's': i++; singlestates =atoi(argv[i]); break;
case 'd': i++; debounce =atoi(argv[i]); break;
case 'f': i++; frequency_ms =atoi(argv[i]); break;
case 'm': i++; max=atoi(argv[i]);
} } i++; } } printf("Starting:NC=%X, NO=%X,
SS=%X, Debounce=%X, Freq(ms)=%d, Max=%d\nENTER TO BEGIN:", nc, no, singlestates, debounce,frequency_ms, max); char ch; ch = getchar(); while( ch != '\n' ) ch = getchar();
printf("\n");
return startread (nc, no, singlestates, debounce, frequency_ms, max); //debounce all } |