OpenEtherCATsociety / SOEM

Simple Open Source EtherCAT Master
Other
1.3k stars 668 forks source link

machine1 can OP run,machine2 cant OP,same code #698

Open zouzhe1 opened 1 year ago

zouzhe1 commented 1 year ago

1 introduce

My program runs in Raspberry Pi, and the motor can rotate normally.

But if the same program is placed in X86 or Jetson, the motor will not rotate and an error message will be reported as follows.

I think this issue may be related to time.

My servo is the SV630N of inovance,only 1 servo

2,The error is as follows:

SOEM (Simple Open EtherCAT Master)
Redundancy test
# /dev/cpu_dma_latency set to 0us
Starting Redundant test,mode=8
ec_init on eth0 succeeded.
1 slaves found and configured.
BOBO2 Drive setup
Slaves mapped, state to SAFE_OP.
Slave:1 Name:InoSV630N Output size:152bits Input size:200bits State: 4 delay:0.1
Calculated workcounter 3
Request operational state for all slaves
Not all slaves reached operational state.
Slave 1 State=0x14 StatusCode=0x0027 : Freerun not supported

Request safe operational state for all slaves
End driver test, close socket
End program

3,error wireshark file is here:

PCAPfile230418.zip

4,slaveinfo

SOEM (Simple Open EtherCAT Master)
Slaveinfo
Starting slaveinfo
ec_init on eth0 succeeded.
1 slaves found and configured.
Calculated workcounter 3

Slave:1
 Name:InoSV630N
 Output size: 152bits
 Input size: 200bits
 State: 4
 Delay: 0[ns]
 Has DC: 1
 DCParentport:0
 Activeports:1.0.0.0
 Configured address: 1001
 Man: 00100000 ID: 000c0112 Rev: 00010000
 SM0 A:1000 L: 256 F:00010026 Type:1
 SM1 A:1400 L: 256 F:00010022 Type:2
 SM2 A:1800 L:  19 F:00010064 Type:3
 SM3 A:1c00 L:  25 F:00010020 Type:4
 FMMU0 Ls:00000000 Ll:  19 Lsb:0 Leb:7 Ps:1800 Psb:0 Ty:02 Act:01
 FMMU1 Ls:00000013 Ll:  25 Lsb:0 Leb:7 Ps:1c00 Psb:0 Ty:01 Act:01
 FMMUfunc 0:1 1:2 2:3 3:0
 MBX length wr: 256 rd: 256 MBX protocols : 06
 CoE details: 0d FoE details: 00 EoE details: 02 SoE details: 00
 Ebus current: 0[mA]
 only LRD/LWR:0
End slaveinfo, close socket
End program
ArthurKetels commented 1 year ago

There are multiple things wrong in your application.

You can not go from pre-OP to OP without first starting cyclic PDO transfers. Then wait until master and all slaves are synchronized (by measuring the time differences). Then request state change to OP. While still doing cyclic PDO check if all slaves go to OP. Then start control.

It seems that the slave detects that the master PDO cycle is not synchronous ("at all" or "not enough") with the SYNC0 signal, and therefore complains that freerun is not supported.

zouzhe1 commented 1 year ago

@ArthurKetels You're great. Based on the PCAP file, you can see such a detailed problem. I don't even know how to look at Wireshark to find the problem.

This problem stuck with me for a long time, Thank you for taking a look at my program.

Here is my program:

void test_driver(char *ifname, int mode)
{
   int cnt, i, j ;
   BO2_Drive_Inputs *iptr;
   BO2_Drive_Outputs *optr;
   struct sched_param schedp;
   cpu_set_t mask;
   pthread_t thread;
   int ht;
   int chk = 2000;
   int64 cycletime;
   struct timespec ts, tnow;

   CPU_ZERO(&mask);
   CPU_SET(2, &mask);
   thread = pthread_self();
   pthread_setaffinity_np(thread, sizeof(mask), &mask);

   memset(&schedp, 0, sizeof(schedp));
   schedp.sched_priority = 99; /* 设置优先级为99,即RT */
   sched_setscheduler(0, SCHED_FIFO, &schedp);

   printf("Starting Redundant test,mode=%d\n",mode);

   /* initialise SOEM, bind socket to ifname */
   if (ec_init(ifname))
   {
      printf("ec_init on %s succeeded.\n", ifname);
      /* find and auto-config slaves */
      if (ec_config_init(FALSE) > 0)
      {
         printf("%d slaves found and configured.\n", ec_slavecount);
         /* wait for all slaves to reach SAFE_OP state */

         int slave_ix;
         for (slave_ix = 1; slave_ix <= ec_slavecount; slave_ix++)
         {
            ec_slavet *slave = &ec_slave[slave_ix];
            slave->PO2SOconfig = bobo2_drive_setup;
         }

         /* configure DC options for every DC capable slave found in the list */
         ec_config_map(&IOmap); // 此处调用drive_setup函数,进行PDO映射表设置
         ec_configdc(); // 设置同步时钟,该函数必须在设置pdo映射之后;

         // setup dc for devices
         for (slave_ix = 1; slave_ix <= ec_slavecount; slave_ix++)
         {
            ec_dcsync0(slave_ix, TRUE, 4000000U, 20000U);//循环时间,偏移时间
            //ec_dcsync01(slave_ix, TRUE, 4000000U, 8000000U, 20000U);
         }

         printf("Slaves mapped, state to SAFE_OP.\n");
         ec_statecheck(0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE);

         /* read indevidual slave state and store in ec_slave[] */
         ec_readstate();
         for (cnt = 1; cnt <= ec_slavecount; cnt++)
         {
            printf("Slave:%d Name:%s Output size:%3dbits Input size:%3dbits State:%2d delay:%d.%d\n",
                   cnt, ec_slave[cnt].name, ec_slave[cnt].Obits, ec_slave[cnt].Ibits,
                   ec_slave[cnt].state, (int)ec_slave[cnt].pdelay, ec_slave[cnt].hasdc);
         }
         expectedWKC = (ec_group[0].outputsWKC * 2) + ec_group[0].inputsWKC;
         printf("Calculated workcounter %d\n", expectedWKC);

         printf("Request operational state for all slaves\n");
         /* activate cyclic process data */
         /* wait for all slaves to reach OP state */
         ec_slave[0].state = EC_STATE_OPERATIONAL;
         /* request OP state for all slaves */
         ec_writestate(0);

         clock_gettime(CLOCK_MONOTONIC, &ts);
         ht = (ts.tv_nsec / 1000000) + 1; /* round to nearest ms */
         ts.tv_nsec = ht * 1000000;
         cycletime = 4000 * 1000; /* cycletime in ns */

         /* 对PDO进行初始化 */
         for (i = 0; i < ec_slavecount; i++)
         {
            optr = (BO2_Drive_Outputs *)ec_slave[i + 1].outputs;
            if(optr == NULL)
            {
               printf("optr is NULL.\n");
            }
            optr->Controlword = 0;
            optr->TargetPos = 0;
            optr->TargetVel = 0;
            optr->TargetTor = 0;
            optr->ModeOp = 0;
            optr->TouchProbe = 0;
            optr->MaxVel = 0;
         }

         do
         {
            /* wait to cycle start */
            clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL);
            if (ec_slave[0].hasdc)
            {
               /* calulate toff to get linux time and DC synced */
               ec_sync(ec_DCtime, cycletime, &toff);
            }
            wkc = ec_receive_processdata(EC_TIMEOUTRET);
            ec_send_processdata();
            add_timespec(&ts, cycletime + toff);
         } while (chk-- && (wkc != expectedWKC));
         /* 此处与SOEM官方例程不一样,因为ec_statecheck函数消耗的时间较多,有可能超过循环周期 */

         if (wkc == expectedWKC)
         {
            printf("Operational state reached for all slaves.\n");
            inOP = TRUE;
            cnt = 0;
            while (cnt<10)
            {
               /* 计算下一周期唤醒时间 */
               add_timespec(&ts, cycletime + toff);
               /* wait to cycle start */
               clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL);
               clock_gettime(CLOCK_MONOTONIC, &tnow);
               if (ec_slave[0].hasdc)
               {
                  /* calulate toff to get linux time and DC synced */
                  ec_sync(ec_DCtime, cycletime, &toff);
               }
               wkc = ec_receive_processdata(EC_TIMEOUTRET);
               diff = calcdiff_ns(tnow, ts);

               switch (state)
               {
               case STATE_RESET: /* 对驱动器清除故障 */
                  for (i = 0; i < ec_slavecount; i++)
                  {
                     //if(iptr->)
                     optr = (BO2_Drive_Outputs *)ec_slave[i + 1].outputs;
                     optr->Controlword = 128;printf("run state=1\n");
                  }
                  state = STATE_INIT;//state = 0;
                  break;
               case STATE_INIT /* 初始化驱动器 */:
                  for (i = 0; i < ec_slavecount; i++)
                  {
                     optr = (BO2_Drive_Outputs *)ec_slave[i + 1].outputs;
                     iptr = (BO2_Drive_Inputs *)ec_slave[i + 1].inputs;
                     optr->Controlword = 0;
                     optr->TargetPos = iptr->ActualPos;printf("run state=2\n");
                  }
                  state = STATE_PREREADY;
                  break;
               case STATE_PREREADY:
                  printf("into state=3 status=%d,calc status=%x\n",iptr->Statusword,(iptr->Statusword & 0x004f));
                  if((iptr->Statusword & 0x004f) != 0x0040){printf("into Failed state=3\n");break;}
                  for (i = 0; i < ec_slavecount; i++)
                  {
                     optr = (BO2_Drive_Outputs *)ec_slave[i + 1].outputs;
                     optr->Controlword = 6;printf("run state=3\n");
                  }
                  state = STATE_READY;
                  break;
               case STATE_READY /* 系统转为准备使能状态 */:
                  printf("into state=4 status=%d,calc status=%x\n",iptr->Statusword,(iptr->Statusword & 0x006f));
                  if((iptr->Statusword & 0x006f) != 0x0021){printf("into Failed state=4\n");break;}
                  for (i = 0; i < ec_slavecount; i++)
                  {
                     optr = (BO2_Drive_Outputs *)ec_slave[i + 1].outputs;
                     optr->Controlword = 7;
                     optr->ModeOp = 8;printf("run state=4\n");
                  }
                  state = STATE_ENABLE;
                  break;
               case STATE_ENABLE /* 驱动器使能 */:
                  printf("into state=5 status=%d,calc status=%x\n",iptr->Statusword,(iptr->Statusword & 0x006f));
                  if((iptr->Statusword & 0x006f) != 0x0023){printf("into Failed state=5\n");break;}
                  for (i = 0; i < ec_slavecount; i++)
                  {
                     optr = (BO2_Drive_Outputs *)ec_slave[i + 1].outputs;
                     optr->Controlword = 15;printf("run state=5\n");
                  }
                  state = STATE_RUN;
                  break;
               case STATE_RUN /* 电机运行转起来了*/:
                  printf("into state=9 status=%d,calc status=%x\n",iptr->Statusword,(iptr->Statusword & 0x006f));
                  if((iptr->Statusword & 0x006f) != 0x0027){printf("into Failed state=9\n");state = STATE_ENABLE;break;}
                  for (i = 0; i < ec_slavecount; i++)
                  {
                     optr = (BO2_Drive_Outputs *)ec_slave[i + 1].outputs;
                     optr->Controlword = 31;/*1F*/printf("run state=9\n");
                     iptr = (BO2_Drive_Inputs *)ec_slave[i + 1].inputs;
                     optr->TargetPos=iptr->ActualPos + 100;
                  }
                  break;
               case STATE_DISABLE:
               /* 使电机失能并在10个循环之后退出循环 */
                  for (i = 0; i < ec_slavecount; i++)
                  {
                     optr = (BO2_Drive_Outputs *)ec_slave[i + 1].outputs;
                     optr->ModeOp = 0;
                     optr->TargetVel = 0;
                     optr->Controlword = 6;printf("run state=6 \n");
                  }
                  cnt ++;
                  break;
               default:
                  break;
               }
               ec_send_processdata();
               cycle++;

               avg += diff;
               if (diff > maxt)
                  maxt = diff;

               for (j = 0; j < 1; j++)
               {
                  iptr = (BO2_Drive_Inputs *)ec_slave[j + 1].inputs;
                  optr = (BO2_Drive_Outputs *)ec_slave[j + 1].outputs;
                  printf("  %d: CW: %d, status: %d, pos: %d", j + 1, optr->Controlword, iptr->Statusword, iptr->ActualPos);
               }
               printf(", MaxLatency: %lu, avg: %lu    ,cycle:%lu\r", maxt, avg / cycle,  cycle);
               fflush(stdout);
            }
            dorun = 0;
         }
         else /* ECAT进入OP失败 */
         {
            printf("Not all slaves reached operational state.\n");
            ec_readstate();
            for (i = 1; i <= ec_slavecount; i++)
            {
               if (ec_slave[i].state != EC_STATE_OPERATIONAL)
               {
                  printf("Slave %d State=0x%2.2x StatusCode=0x%4.4x : %s\n",
                         i, ec_slave[i].state, ec_slave[i].ALstatuscode, ec_ALstatuscode2string(ec_slave[i].ALstatuscode));
               }
            }
         }
         /* 断开ECAT通讯 */
         printf("\nRequest safe operational state for all slaves\n");
         ec_slave[0].state = EC_STATE_SAFE_OP;
         /* request SAFE_OP state for all slaves */
         ec_writestate(0);
         ec_slave[0].state = EC_STATE_PRE_OP;
         ec_writestate(0);
         ec_slave[0].state = EC_STATE_INIT;
         ec_writestate(0);
         ec_readstate();
         if (ec_statecheck(0, EC_STATE_SAFE_OP, 1000) == EC_STATE_INIT)
         {
            printf("ECAT changed into state init\n");
         }
      }
      else
      {
         printf("No slaves found!\n");
      }
      printf("End driver test, close socket\n");
      /* stop SOEM, close socket */
      ec_close();
   }
   else
   {
      printf("No socket connection on %s\nExcecute as root\n", ifname);
   }
}

/* PI calculation to get linux time synced to DC time */
void ec_sync(int64 reftime, int64 cycletime, int64 *offsettime)
{
   static int64 integral = 0;
   int64 delta;
   /* set linux sync point 50us later than DC sync, just as example */
   delta = (reftime - 50000) % cycletime;
   if (delta > (cycletime / 2))
   {
      delta = delta - cycletime;
   }
   if (delta > 0)
   {
      integral++;
   }
   if (delta < 0)
   {
      integral--;
   }
   *offsettime = -(delta / 100) - (integral / 20);
}
zouzhe1 commented 1 year ago

@ArthurKetels When running, my drive will also report an error, and the query manual says: no synchronization signal, the mechanism of generation: When the servo communication switches to the OP state, the MCU does not receive the synchronization signal.

zouzhe1 commented 1 year ago

@ArthurKetels Hello, can you help me take a look? This problem has been holding me back for half a year and I haven't figured out how to solve it. Is that the sentence 'ec'_ dcsync0(slave_ix, TRUE, 4000000U, 20000U); ? How much should the cycle time of dcsync0 and the cycle time of my tes function be set separately? Are they related? Thank you very much! Thank you very much!

Liwhitehead666 commented 9 months ago

hello,are you solved the“freerun” problem,can we talk about this. I just stucked here in the sv660n.