ebox.c

Go to the documentation of this file.
00001 /** \file
00002  * \brief Example code for Simple Open EtherCAT master
00003  *
00004  * Usage : ebox [ifname] [cycletime]
00005  * ifname is NIC interface, f.e. eth0
00006  * cycletime in us, f.e. 500
00007  *
00008  * This test is specifically build for the E/BOX.
00009  *
00010  * (c)Arthur Ketels 2011 
00011  */
00012 
00013 #include <stdio.h>
00014 #include <stdlib.h>
00015 #include <sys/time.h>
00016 #include <unistd.h>
00017 #include <sched.h>
00018 #include <string.h>
00019 #include <sys/time.h>
00020 #include <time.h>
00021 #include <pthread.h>
00022 #include <math.h>
00023 
00024 #include "ethercattype.h"
00025 #include "nicdrv.h"
00026 #include "ethercatbase.h"
00027 #include "ethercatmain.h"
00028 #include "ethercatcoe.h"
00029 #include "ethercatconfig.h"
00030 #include "ethercatdc.h"
00031 
00032 #define NSEC_PER_SEC 1000000000
00033 
00034 typedef struct PACKED
00035 {
00036     uint8         status;
00037     uint8         counter;
00038     uint8         din;
00039     int32         ain[2];
00040     uint32      tsain;
00041     int32     enc[2];
00042 } in_EBOXt; 
00043 
00044 typedef struct PACKED
00045 {
00046     uint8         counter;
00047     int16         stream[100];
00048 } in_EBOX_streamt;  
00049 
00050 typedef struct PACKED
00051 {
00052     uint8         control;
00053     uint8         dout;
00054     int16     aout[2];
00055     uint16      pwmout[2];
00056 } out_EBOXt;  
00057 
00058 typedef struct PACKED
00059 {
00060     uint8         control;
00061 } out_EBOX_streamt; 
00062 
00063 // total samples to capture
00064 #define MAXSTREAM 200000
00065 // sample interval in ns, here 8us -> 125kHz
00066 // maximum data rate for E/BOX v1.0.1 is around 150kHz
00067 #define SYNC0TIME 8000
00068 
00069 struct sched_param schedp;
00070 char IOmap[4096];
00071 pthread_t thread1;
00072 struct timeval tv,t1,t2;
00073 int dorun = 0;
00074 int deltat, tmax=0;
00075 int64 toff;
00076 int DCdiff;
00077 int os;
00078 uint32 ob;
00079 int16 ob2;
00080 uint8 ob3;
00081 pthread_cond_t      cond  = PTHREAD_COND_INITIALIZER;
00082 pthread_mutex_t     mutex = PTHREAD_MUTEX_INITIALIZER;
00083 int64 integral=0;
00084 uint32 cyclecount;
00085 in_EBOX_streamt   *in_EBOX;
00086 out_EBOX_streamt  *out_EBOX;
00087 double    ain[2];
00088 int     ainc;
00089 int     streampos;
00090 int16   stream1[MAXSTREAM];
00091 int16   stream2[MAXSTREAM];
00092 
00093 int output_cvs(char *fname, int length)
00094 {
00095     FILE *fp;
00096  
00097   int  i;
00098 
00099     fp = fopen(fname, "w");
00100     if(fp == NULL) 
00101         return 0;
00102     for (i = 0; i < length; i++)
00103     {
00104       fprintf(fp, "%d %d %d\n", i, stream1[i], stream2[i]);
00105     }
00106   fclose(fp);
00107   return 1;
00108 }
00109 
00110 void eboxtest(char *ifname)
00111 {
00112   int cnt, i;
00113   
00114   printf("Starting E/BOX test\n");
00115   
00116   /* initialise SOEM, bind socket to ifname */
00117   if (ec_init(ifname))
00118   { 
00119     printf("ec_init on %s succeeded.\n",ifname);
00120     /* find and auto-config slaves */
00121     if ( ec_config_init(FALSE) > 0 )
00122     {
00123       printf("%d slaves found and configured.\n",ec_slavecount);
00124 
00125       // check if first slave is an E/BOX
00126       if (( ec_slavecount >= 1 ) &&
00127          (strcmp(ec_slave[1].name,"E/BOX") == 0))
00128       {
00129         // reprogram PDO mapping to set slave in stream mode
00130         // this can only be done in pre-OP state
00131         os=sizeof(ob2); ob2 = 0x1601;
00132         ec_SDOwrite(1,0x1c12,01,FALSE,os,&ob2,EC_TIMEOUTRXM); 
00133         os=sizeof(ob2); ob2 = 0x1a01;
00134         ec_SDOwrite(1,0x1c13,01,FALSE,os,&ob2,EC_TIMEOUTRXM); 
00135       }
00136 
00137       ec_config_map(&IOmap);
00138 
00139       ec_configdc();
00140 
00141       /* wait for all slaves to reach SAFE_OP state */
00142       ec_statecheck(0, EC_STATE_SAFE_OP,  EC_TIMEOUTSTATE);
00143       
00144       /* configure DC options for every DC capable slave found in the list */
00145       printf("DC capable : %d\n",ec_configdc());
00146       
00147       /* check configuration */
00148       if (( ec_slavecount >= 1 ) &&
00149          (strcmp(ec_slave[1].name,"E/BOX") == 0)
00150          )
00151       {
00152         printf("E/BOX found.\n");
00153 
00154       /* connect struct pointers to slave I/O pointers */
00155       in_EBOX = (in_EBOX_streamt*) ec_slave[1].inputs;
00156       out_EBOX = (out_EBOX_streamt*) ec_slave[1].outputs;
00157 
00158       /* read indevidual slave state and store in ec_slave[] */
00159       ec_readstate();
00160       for(cnt = 1; cnt <= ec_slavecount ; cnt++)
00161       {
00162         printf("Slave:%d Name:%s Output size:%3dbits Input size:%3dbits State:%2d delay:%d.%d\n",
00163              cnt, ec_slave[cnt].name, ec_slave[cnt].Obits, ec_slave[cnt].Ibits,
00164              ec_slave[cnt].state, (int)ec_slave[cnt].pdelay, ec_slave[cnt].hasdc);
00165       }
00166       printf("Request operational state for all slaves\n");
00167 
00168       /* send one processdata cycle to init SM in slaves */ 
00169       ec_send_processdata();
00170       ec_receive_processdata(EC_TIMEOUTRET);
00171         
00172       ec_slave[0].state = EC_STATE_OPERATIONAL;
00173       /* request OP state for all slaves */
00174       ec_writestate(0);
00175       /* wait for all slaves to reach OP state */
00176       ec_statecheck(0, EC_STATE_OPERATIONAL,  EC_TIMEOUTSTATE);
00177       if (ec_slave[0].state == EC_STATE_OPERATIONAL )
00178       {
00179         printf("Operational state reached for all slaves.\n");
00180         ain[0] = 0;
00181         ain[1] = 0;
00182         ainc = 0;
00183         dorun = 1;
00184         usleep(100000); // wait for linux to sync on DC
00185         ec_dcsync0(1, TRUE, SYNC0TIME, 0); // SYNC0 on slave 1
00186         /* acyclic loop 20ms */
00187         for(i = 1; i <= 200; i++)
00188         {
00189           /* read DC difference register for slave 2 */
00190 //          ec_FPRD(ec_slave[1].configadr, ECT_REG_DCSYSDIFF, sizeof(DCdiff), &DCdiff, EC_TIMEOUTRET);
00191 //          if(DCdiff<0) { DCdiff = - (int32)((uint32)DCdiff & 0x7ffffff); }
00192           printf("PD cycle %5d DCtime %12lld Cnt:%3d Data: %6d %6d %6d %6d %6d %6d %6d %6d \n",
00193                cyclecount, ec_DCtime, in_EBOX->counter, in_EBOX->stream[0], in_EBOX->stream[1],
00194                  in_EBOX->stream[2], in_EBOX->stream[3], in_EBOX->stream[4], in_EBOX->stream[5],
00195                  in_EBOX->stream[98], in_EBOX->stream[99]);
00196           usleep(20000);
00197         }
00198         dorun = 0;
00199 //        printf("\nCnt %d : Ain0 = %f  Ain2 = %f\n", ainc, ain[0] / ainc, ain[1] / ainc);
00200       }
00201       else
00202       {
00203         printf("Not all slaves reached operational state.\n");
00204       }     
00205       }
00206       else
00207       {
00208         printf("E/BOX not found in slave configuration.\n");
00209       } 
00210       ec_dcsync0(1, FALSE, 8000, 0); // SYNC0 off
00211       printf("Request safe operational state for all slaves\n");
00212       ec_slave[0].state = EC_STATE_SAFE_OP;
00213       /* request SAFE_OP state for all slaves */
00214       ec_writestate(0);
00215       /* wait for all slaves to reach state */
00216       ec_statecheck(0, EC_STATE_SAFE_OP,  EC_TIMEOUTSTATE);
00217       ec_slave[0].state = EC_STATE_PRE_OP;
00218       /* request SAFE_OP state for all slaves */
00219       ec_writestate(0);
00220       /* wait for all slaves to reach state */
00221       ec_statecheck(0, EC_STATE_PRE_OP,  EC_TIMEOUTSTATE);
00222       if (( ec_slavecount >= 1 ) &&
00223          (strcmp(ec_slave[1].name,"E/BOX") == 0))
00224       {
00225         // restore PDO to standard mode
00226         // this can only be done is pre-op state
00227         os=sizeof(ob2); ob2 = 0x1600;
00228         ec_SDOwrite(1,0x1c12,01,FALSE,os,&ob2,EC_TIMEOUTRXM); 
00229         os=sizeof(ob2); ob2 = 0x1a00;
00230         ec_SDOwrite(1,0x1c13,01,FALSE,os,&ob2,EC_TIMEOUTRXM); 
00231       }
00232       printf("Streampos %d\n", streampos);
00233       output_cvs("stream.txt", streampos);
00234     }
00235     else
00236     {
00237       printf("No slaves found!\n");
00238     }
00239     printf("End E/BOX, close socket\n");
00240     /* stop SOEM, close socket */
00241     ec_close();
00242   }
00243   else
00244   {
00245     printf("No socket connection on %s\nExcecute as root\n",ifname);
00246   } 
00247 } 
00248 
00249 /* add ns to timespec */
00250 void add_timespec(struct timespec *ts, int64 addtime)
00251 {
00252   int64 sec, nsec;
00253   
00254   nsec = addtime % NSEC_PER_SEC;
00255   sec = (addtime - nsec) / NSEC_PER_SEC;
00256   ts->tv_sec += sec;
00257   ts->tv_nsec += nsec;
00258   if ( ts->tv_nsec > NSEC_PER_SEC ) 
00259   { 
00260     nsec = ts->tv_nsec % NSEC_PER_SEC;
00261     ts->tv_sec += (ts->tv_nsec - nsec) / NSEC_PER_SEC;
00262     ts->tv_nsec = nsec;
00263   } 
00264 } 
00265 
00266 /* PI calculation to get linux time synced to DC time */
00267 void ec_sync(int64 reftime, int64 cycletime , int64 *offsettime)
00268 {
00269   int64 delta;
00270   /* set linux sync point 50us later than DC sync, just as example */
00271   delta = (reftime - 50000) % cycletime;
00272   if(delta> (cycletime /2)) { delta= delta - cycletime; }
00273   if(delta>0){ integral++; }
00274   if(delta<0){ integral--; }
00275   *offsettime = -(delta / 100) - (integral /20);
00276 } 
00277 
00278 /* RT EtherCAT thread */
00279 void ecatthread( void *ptr )
00280 {
00281   struct timespec   ts;
00282   struct timeval    tp;
00283   int rc;
00284   int ht;
00285   int i;
00286   int pcounter = 0;
00287   int64 cycletime;
00288   
00289     rc = pthread_mutex_lock(&mutex);
00290   rc =  gettimeofday(&tp, NULL);
00291 
00292     /* Convert from timeval to timespec */
00293     ts.tv_sec  = tp.tv_sec;
00294   ht = (tp.tv_usec / 1000) + 1; /* round to nearest ms */
00295     ts.tv_nsec = ht * 1000000;
00296   cycletime = *(int*)ptr * 1000; /* cycletime in ns */
00297   toff = 0;
00298   dorun = 0;
00299   while(1)
00300   { 
00301     /* calculate next cycle start */
00302     add_timespec(&ts, cycletime + toff);
00303     /* wait to cycle start */
00304     rc = pthread_cond_timedwait(&cond, &mutex, &ts);
00305     if (dorun>0)
00306     {
00307       rc =  gettimeofday(&tp, NULL);
00308 
00309       ec_send_processdata();
00310 
00311       ec_receive_processdata(EC_TIMEOUTRET);
00312 
00313       cyclecount++;
00314 
00315       
00316       if((in_EBOX->counter != pcounter) && (streampos < (MAXSTREAM - 1)))
00317       {
00318         // check if we have timing problems in master
00319         // if so, overwrite stream data so it shows up clearly in plots.
00320         if(in_EBOX->counter > (pcounter + 1))
00321           for(i = 0 ; i < 50 ; i++)
00322           {
00323             stream1[streampos]   = 20000;
00324             stream2[streampos++] = -20000;
00325           }
00326         else
00327           for(i = 0 ; i < 50 ; i++)
00328           {
00329             stream1[streampos]   = in_EBOX->stream[i * 2];
00330             stream2[streampos++] = in_EBOX->stream[(i * 2) + 1];
00331           }
00332         pcounter = in_EBOX->counter;
00333       }
00334                 
00335       /* calulate toff to get linux time and DC synced */
00336       ec_sync(ec_DCtime, cycletime, &toff);
00337     } 
00338   }  
00339 }
00340 
00341 int main(int argc, char *argv[])
00342 {
00343   int iret1;
00344     int ctime;
00345   struct sched_param    param;
00346   int                   policy = SCHED_OTHER;
00347   
00348   printf("SOEM (Simple Open EtherCAT Master)\nE/BOX test\n");
00349   
00350   memset(&schedp, 0, sizeof(schedp));
00351   /* do not set priority above 49, otherwise sockets are starved */
00352   schedp.sched_priority = 30;
00353   sched_setscheduler(0, SCHED_FIFO, &schedp);
00354 
00355   do
00356   {
00357     usleep(1000);
00358   }
00359   while (dorun);
00360   
00361   if (argc > 1)
00362   {   
00363     dorun = 1;
00364     if( argc > 2)
00365       ctime = atoi(argv[2]);
00366     else
00367       ctime = 1000; // 1ms cycle time
00368     /* create RT thread */
00369     iret1 = pthread_create( &thread1, NULL, (void *) &ecatthread, (void*) &ctime);  
00370     memset(&param, 0, sizeof(param));
00371     /* give it higher priority */
00372       param.sched_priority = 40;
00373       iret1 = pthread_setschedparam(thread1, policy, &param);
00374 
00375     /* start acyclic part */
00376     eboxtest(argv[1]);
00377   }
00378   else
00379   {
00380     printf("Usage: ebox ifname [cycletime]\nifname = eth0 for example\ncycletime in us\n");
00381   } 
00382   
00383   schedp.sched_priority = 0;
00384   sched_setscheduler(0, SCHED_OTHER, &schedp);
00385 
00386   printf("End program\n");
00387   return (0);
00388 }
Generated by  doxygen 1.6.3