/*
 *  Testing speed:
 *   Two pins of the 40-pin GPIO connector are connected with a cable.
 *   One pin is for reading, the second - for writing.
 *   The program changes state of the writing-pin to 1. Then waits when this state is sensed on
 *   the reading-pin. When the 1 is read from the reading-pin, then state of the writing-pin is
 *   changed again to 0, and the program waits untill the 0 is read from the reading-pin.
 *   The above sequence is repeated several times, and the time for the complete loop is measured.
 *   This gives good idea of how quickly signals can be generated by GPIO.
 * 
 *   The program initially sets both pins to input mode and displays message to connect the pins.
 *   Then waits for key-press, and starts the loop.
 *   This is in order to avoid damages by connecting posisbly active pins. 
 * 
 */
#include "ROCKPro64GPIO.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <stdint.h>   /* for uint64_t */
#include <time.h>

int main(int argc, char **argv) 
{
  int PinIn, PinOut;
  int i, RepetitionNumber;
  int RC;
  struct timespec T1;
  struct timespec T0;
  double X;


  if(argc < 3) 
  {
    fprintf(stderr, "\nUsage:\t%s { PinIn } { PinOut } [N]\n"
      "\tPinIn    : Pin in the 40-pin GPIO connection, which will be used to sense signal from PinOut.\n"
      "\tPinOut   : Pin in the 40-pin GPIO connection, which will be used to send signal to PinIn.\n"
       "\tN       : Number of repetition of 1-0 cycle. Default 1000.\n",
     argv[0]);
    exit(1);
	}
	PinIn = strtoul(argv[1], 0, 0);
	PinOut = strtoul(argv[2], 0, 0);
  if(argc > 3)  RepetitionNumber= strtoul(argv[3], 0, 0); else RepetitionNumber= 1000;

  RC= GPIOInit();
  if( RC != 0) return RC;
  RC= GPIOPinInit( PinIn, GPIOPinModeGPIO);
  if( RC != 0) return RC;
  RC= GPIOPinInit( PinOut, GPIOPinModeGPIO);
  if( RC != 0) return RC;
  RC= GPIOSetPinDirection( PinIn , GPIOPinDirectionInput );
  if( RC != 0) return RC;
  RC= GPIOSetPinDirection( PinOut , GPIOPinDirectionInput );
  if( RC != 0) return RC;
  RC= GPIOSetPullUpDown( PinIn , GPIOPullUpDownStateNormal );
  if( RC != 0) return RC;
  RC= GPIOSetPullUpDown( PinOut , GPIOPullUpDownStateNormal );
  if( RC != 0) return RC;
  
  fprintf( stdout, "\nConnect pin %d to pin %d and press any key\n", PinIn, PinOut);
  getchar();
  RC= GPIOSetPinDirection( PinOut , GPIOPinDirectionOutput );
  if( RC != 0) return RC;
  RC= GPIOSetPinState( PinOut, GPIOPinState0);
  if( RC != 0) return RC;
  fprintf( stdout, "Starting %d cycles with time measurement\n", RepetitionNumber);

  GPIOSetPinState( PinOut, GPIOPinState0);
  clock_gettime(CLOCK_MONOTONIC, &T0);

  for(i= 0; i<RepetitionNumber; i++)
  {
    RC= GPIOSetPinState( PinOut, GPIOPinState1);
    if( RC != 0) 
    {
      fprintf( stderr, "Error from GPIOSetPinState() to 1 at loop %d\n", i);
      return RC;
    }
    while( GPIOGetPinState( PinIn) != GPIOPinState1);
    RC= GPIOSetPinState( PinOut, GPIOPinState0);
    if( RC != 0) 
    {
      fprintf( stderr, "Error from GPIOSetPinState() to 0 at loop %d\n", i);
      return RC;
    }
    while( GPIOGetPinState( PinIn) != GPIOPinState0);
  }
  clock_gettime(CLOCK_MONOTONIC, &T1);
  X= (double)(T1.tv_sec- T0.tv_sec)*1000000.+ 
     (double)(T1.tv_nsec- T0.tv_nsec)/1000.;  /* Czas jest mierzony w mikrosekundach. */
  fprintf( stdout, "Time elapsed %f seconds.\n", (X/1000000.));
  X= X/((double) RepetitionNumber);
  fprintf( stdout, "Time of single change to 1 and the to 0: %f micro seconds.\n", X);
  X= 1/X;
  fprintf( stdout, "Frequency: %f MHz.\n", X);
  
  RC= GPIOSetPinState( PinOut, GPIOPinState0 );
  RC= GPIOSetPinDirection( PinOut , GPIOPinDirectionInput );
  GPIOPinRelease( PinOut);
  GPIOPinRelease( PinIn);

  return 0;
}

