Closed Davidinos closed 4 years ago
Thanks for the info. But actually you ask a xenomai question. There are examples where SOEM runs on bare metal hardware and has maximum jitter below 1us. The issue is with your hardware and OS support. No matter how clever xenomai is written, it can not do much to improve basic design flaws in hardware and drivers. Coding for real time is fundamentally different from what is available in common OS concepts.
Off my soapbox now. What you need to do is instrument your code to find the timing issues. Log your PDO thread timing in relation to the reference slave clock. When possible also log the different time paths from starting the cycle to sending data, to receiving data. The more fine grained the more informative. Store the log in histogram format, that way you can keep it in memory and still catch millions of events.
Hello Arthur, thank you for your answer. Your point is very clear and probably we'll move to bare metal implementation to reach our goal. I have one last question, the only modification that I applied to xenomai implementation regards the osal.c and osal.h since the time base functions are different. None other files needs to be changed right ? (consider we didn't change the nic..). Thank you for your help.
Correct. That is the whole point of osal (operating system abstraction layer). Each target need to implement the optimum OS version of the timing and threading primitives. SOEM is not critical about the specific performance of the implementation, but off course the total performance will suffer from an inefficient implementation.
Thank you very much Arthur.
Goodmorning, I write you to have your opinion about the code reported below. I'm using a Beaglebone black (1GHz single core) with Xenomai Cobalt patch and DC enabled at 1ms. Do you find anything wrong? I ask you because with our tests we see that sometimes it loses the sync.. Besides I wonder if I can improve the jitter In attachment the wireshark result with a cycle time 1ms. 1msCycleTime.zip
Thank you.
`/** \file
include
include
include
include <sys/time.h>
include
include
include
include
include
include
include
include
include <sys/mman.h>
include <alchemy/task.h>
include <alchemy/timer.h>
include <alchemy/sem.h>
include <boilerplate/trace.h>
include <xenomai/init.h>
//#include "ethercat.h"
include "includeSOEM/ethercat.h"
define NSEC_PER_SEC 1000000000
define EC_TIMEOUTMON 500
struct sched_param schedp; char IOmap[4096]; RT_TASK thread1, thread2; RT_SEM sem_desc; struct timeval tv, t1, t2; int dorun = 0; int deltat, tmax = 0; int64 toff, gl_delta; int DCdiff; int os; uint8 ob; uint16 ob2; uint8 *digout = 0; int expectedWKC; boolean needlf; volatile int wkc; boolean inOP; uint8 currentgroup = 0;
int64 maxDelta = 0;
define SYNC0_SIGNAL_PERIOD_NS 1000000
define stack64k (64 * 1024)
define SEM_INIT 0 / Initial semaphore count /
define SEM_MODE S_FIFO / Wait by FIFO order /
define ETHCAT_CHECK_LOOP 10000000 //Expressed in ticks
/*****
*****/
static int slave_dc_config(uint16 slave) { ec_dcsync0(slave, TRUE, SYNC0_SIGNAL_PERIOD_NS, 10); return 0; }
void catch_signal(int sig) { dorun=0; usleep(5e5); rt_task_delete(&thread1); rt_task_delete(&thread2); exit(1); }
/*****
*****/
void redtest(char ifname, char ifname2) { int cnt, i, j, oloop, iloop;
printf("Starting Redundant test\n");
/ initialise SOEM, bind socket to ifname / // if (ec_init_redundant(ifname, ifname2)) if (ec_init(ifname)) { printf("ec_init on %s succeeded.\n",ifname); / find and auto-config slaves / if ( ec_config_init(FALSE) > 0 ) { / configure DC options for every DC capable slave found in the list / ec_configdc();
} 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); gl_delta = delta; // Max delta found maxDelta = (maxDelta > gl_delta) ? maxDelta : gl_delta; }
/*****
*****/
void ecatthread(void *ptr) { RTIME cycle_ns; RTIME cur_time, cur_cycle_cnt, cycle_time, remain_time; RTIME dc_remain_time, rt_ts;
toff = 0; dorun = 0; maxDelta = 0;
/ Now, wait for a semaphore unit... / rt_sem_p(&sem_desc, TM_INFINITE);
ec_send_processdata();
cycle_ns = (int)ptr 1000; // cycletime in ns cur_time = rt_timer_read(); // get current master time cur_cycle_cnt = cur_time / cycle_ns; // calcualte number of cycles has passed cycle_time = cur_cycle_cnt cycle_ns; remain_time = cur_time % cycle_ns; // remain time to next cycle, test only
rt_printf("cycle_cnt=%lld\n", cur_cycle_cnt); rt_printf("remain_time=%lld\n", remain_time);
wkc = ec_receive_processdata(EC_TIMEOUTRET); // get reference DC time
dc_remain_time = ec_DCtime % cycle_ns; rt_ts = cycle_time + dc_remain_time;
rt_printf("dc remain_time=%lld\n", dc_remain_time); rt_task_sleep_until(rt_ts);
while(1) { rt_ts += (RTIME) (cycle_ns + toff); rt_task_sleep_until(rt_ts);
} }
/*****
*****/
void ecatcheck( void *ptr ) { int slave;
}
/*****
*****/
int main(int argc, char *argv[]) { // Handle Signals to close threads signal(SIGTERM, catch_signal); signal(SIGINT, catch_signal);
// Block memory mlockall(MCL_CURRENT | MCL_FUTURE);
printf("SOEM (Simple Open EtherCAT Master)\nRedundancy test\n");
if (argc > 3) { int ctime = 0; //Create a semaphore rt_sem_create(&sem_desc, "MySemaphore", SEM_INIT, SEM_MODE);
} else { printf("Usage: red_test ifname1 ifname2 cycletime\nifname = eth0 for example\ncycletime in us\n"); }
printf("End program\n");
return (0); }`