Reading DIO (Digital IO) Ports of the TS-7800

NOTES:

1) I am not a C programmer
    This 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:

Parameters:


 unsigned short int normallyclosed: bitmask bits for of edge devices that should trigger when the device goes back to CLOSED (i.e., reads as a 0) from an open.
 unsigned short int normallyoped:
bitmask of bits for edge devices that should trigger when the device goes back to OPEN (i.e., reads as a 1) from a closed.
 unsigned short int singlestates: bitmask of bits for devices that toggles should trigger (goes to 0 or 1).
 unsigned short int debounce: bitmask of bits whose debounce circuitry should be enabled.
 unsigned char frequency_ms:
frequency at which to read the edge devices. The fastest I've seen is 50hz = 20ms frequency (=1/hz * 1000).
This is really the sleep between reads in milliseconds.
 unsigned int max: Max number of reads before we end.

    

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:

  • Read the args as hex values!
  • Check that inputs are in range (max FFFF, but really, max is 55DD, because pins 2,6,10,12,14,16 are taken)
  • Make this a lib, add a callback function that gets called on trigger states, start the read in a separate thread.  Add an "END" method that ends the thread.


Code: 



#include <stdio.h>
#include
<stdlib.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

}

 


Comments