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


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: )

3) The main routine, startread explained:


 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.



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.


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).


  • 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.


#include <stdio.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' ) ;




            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;





          *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;



              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)




                        useccountervalue = *mscounteradr;

                        elapsed=((double) (useccountervalue - useccounterstart)) / MICROSECONDS_PER_SEC;

                        double freq=count/elapsed;

                        unsigned int deltabits = bitchanges;




                        //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';




                                      bitON = '0';





                                  bitON = '0';

                                  changedresults[bit] = '0';



                             onresults[bit] = bitON;

                             bit ++;

                             if (bit<16)



                                  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);




                             printf ("   %s  %s\n", changedresults, onresults);



                   }    //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);


          if (_Stop)





          return 0;



int main(int argc, char **argv)





     //       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");




          int i=0;

          while (i<argc)


              if (argv[i][0]=='-')


                   char *sw =argv[i];

                   switch (sw[1])


                        case 'o':


                             no =atoi(argv[i]);



                        case 'c':


                             nc =atoi(argv[i]);



                        case 's':


                             singlestates =atoi(argv[i]);



                        case 'd':


                             debounce =atoi(argv[i]);



                        case 'f':


                             frequency_ms =atoi(argv[i]);



                        case 'm':









     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();




     return startread (nc, no, singlestates, debounce, frequency_ms, max);     //debounce all