Going into the kernel sounds like the only option I have left.
On 4/15/16, 9:59 AM, "Henry Bausley" <hbaus...@deltatau.com> wrote: > >Make sure you are not sending anything like >ecrt_master_sdo_download/ecrt_master_sdo_upload from additional threads or >anything else. > >We use xenomai but always in kernel mode. We can control up to 8kHz without >issues. > > > > >On Fri, 2016-04-15 at 00:16 +0200, Richard Hacker wrote: >> On 14.04.2016 23:51, Thomas Bitsky Jr wrote: >> > Over the course of the last few weeks, I have done the following: >> > - Built and installed a Xenomai 2.6.4 kernel. >> > - Switched x86 hardware >> > - Adapted the program to Xenomai >> > - Rewrote the program so that it is completely asynchronous >> > - Fixed the distributed clock code I had written. >> Woa, respect, that is crazy!! I was wondering what had happened to your >> experiments. >> >> > >> > The result has been a reduced latency, worst case latency of +/-10 >> > microseconds when not running the program. I am still running the user >> > space at a rate of 4000Hz. I am using the e1000e ethercat driver. >> > >> > >> > However, this did not solve my problem. When I run live with the EtherCAT >> > master, I get over runs, skipped packet and eventually a delay so long >> > that it causes the Synchronized Clock watchdog to barf. >> > >del >> > After scoping, I have confirmed that the delays are always in these two >> > functions: >> > >> > ecrt_master_receive >> > ecrt_master_send >> Now that is a good clue. Have you instrumented the time just before and >> just after these calls? Probably ;) >> >> The Intel chipset is designed for high throughput with as little CPU >> interaction as possible. It has things like interrupt throttling, >> buffers and the works - poison for any real time application. >> >> Although we have not experienced these issues and are successfully using >> this driver, we try to use the most dumb (!) network cards available: >> RTL8139 chipset is a good example. Paradoxically, they work best: when >> they receive a packet of data, it is available immediately; when a >> packet is sent to the card, it is transmitted immediately - no >> buffering, no throttling. >> >> > >> > In fact, I can comment out those functions but leave everything else >> > intact, including ecrt_domain_process, ecrt_domain_queue and even the >> > distributed clock functions, and the timing is near perfect. However, if I >> > add back in ecrt_master_receive/send, they return inconsistently. >> > >> > Can anyone tell me why ecrt_master_receive and ecrt_master_send would take >> > unreasonably long? Could this be a problem with calling those functions >> > from user space? Could it be a problem with the e1000e driver? I’m not >> > exactly sure where to go from here except to try moving the whole program >> > into the kernel, but, at this point, I’m not sure that will solve the >> > issue. >> > >> > Any insights or thoughts are greatly appreciated. >> > >> > Thanks! >> > Tom >> > >> > >> > >> > >> > >> > On 3/2/16, 10:04 AM, "Henry Bausley" <hbaus...@deltatau.com> wrote: >> > >> >> >> >> Are you using the dc_clock_adjust to modify your sleep time? ie. >> >> something like >> >> >> >> clock_nanosleep(CLOCK_MONOTONIC,TIMER_ABSTIME,&inst->wakeupTime,NULL); >> >> TIMESPEC_ADD_NS(inst->wakeupTime,1000000 + dc_clock_adjust); >> >> >> >> Does your inst->cycleNs include the clock adjustement? >> >> >> >> I have found the best debug tool for DC is an oscilloscope and a board >> >> that you probe the SOF and sync0 . If you see SOF wander with respect >> >> to sync0 it won't work correctly. If they don't you are good. >> >> >> >> On Wed, 2016-03-02 at 14:47 +0000, Thomas Bitsky Jr wrote: >> >>> Thanks for the response, Richard. >> >>> >> >>> CyclicTest on the hardware: >> >>> user@core:~$ sudo cyclictest --smp -p95 -m >> >>> # /dev/cpu_dma_latency set to 0us >> >>> policy: fifo: loadavg: 0.01 0.04 0.04 1/162 1547 >> >>> >> >>> T: 0 ( 1543) P:95 I:1000 C: 26093 Min: 6 Act: 30 Avg: 25 Max: >> >>> 56 >> >>> T: 1 ( 1544) P:95 I:1500 C: 17386 Min: 6 Act: 14 Avg: 23 Max: >> >>> 66 >> >>> T: 2 ( 1545) P:95 I:2000 C: 13036 Min: 5 Act: 28 Avg: 29 Max: >> >>> 57 >> >>> T: 3 ( 1546) P:95 I:2500 C: 10424 Min: 6 Act: 30 Avg: 29 Max: >> >>> 58 >> >>> >> >>> >> >>> >> >>> So, the max latency is definitely in the ballpark of what I was saying >> >>> last night. >> >>> >> >>> Setting cylictest with a break value fails almost instantly. >> >>> >> >>> root@core:/sys/kernel/debug/tracing# cyclictest --smp -p95 -f -b 200 >> >>> # /dev/cpu_dma_latency set to 0us >> >>> INFO: debugfs mountpoint: /sys/kernel/debug/tracing/ >> >>> policy: fifo: loadavg: 0.00 0.01 0.05 3/165 1703 >> >>> >> >>> T: 0 ( 1700) P:95 I:1000 C: 0 Min:1000000 Act: 0 Avg: 0 Max: >> >>> 0 >> >>> T: 1 ( 1701) P:95 I:1500 C: 0 Min:1000000 Act: 0 Avg: 0 Max: >> >>> 0 >> >>> T: 2 ( 0) P:95 I:2000 C: 0 Min:1000000 Act: 0 Avg: 0 Max: >> >>> 0 >> >>> T: 3 ( 1703) P:95 I:2500 C: 0 Min:1000000 Act: 0 Avg: 0 Max: >> >>> 0 >> >>> # Thread Ids: 01700 01701 01702 01703 >> >>> # Break thread: 1702 >> >>> # Break value: 217 >> >>> >> >>> >> >>> >> >>> [snip] >> >>> First things first: when you are running RT proggies, you need a stable >> >>> clock source and a preemptable kernel! >> >>> [/snip] >> >>> >> >>> >> >>> I am using RT Preempt: >> >>> # uname -a >> >>> Linux core 3.12.50-rt68 #1 SMP PREEMPT RT Mon Nov 23 18:17:14 CST 2015 >> >>> i686 i686 i686 GNU/Linux >> >>> >> >>> >> >>> Thanks! >> >>> Thomas C. Bitsky Jr. | Lead Developer >> >>> ADC | automateddesign.com <http://automateddesign.com/> >> >>> P: 630-783-1150 F: 630-783-1159 M: 630-632-6679 >> >>> >> >>> Follow ADC news and media: >> >>> Facebook <https://facebook.com/automateddesigncorp> | Twitter >> >>> <https://twitter.com/ADCSportsLogic> | YouTube >> >>> <https://www.youtube.com/user/ADCSportsLogic> >> >>> >> >>> >> >>> >> >>> >> >>> >> >>> >> >>> >> >>> >> >>> On 3/2/16, 5:30 AM, "etherlab-users on behalf of Richard Hacker" >> >>> <etherlab-users-boun...@etherlab.org on behalf of h...@igh.de> wrote: >> >>> >> >>>> Hi John, >> >>>> >> >>>> Skipped frames are either a timing or a hardware problem (faulty >> >>>> EtherCAT slaves, cabling, noise). Some slaves don't care, DC slaves >> >>>> puke! >> >>>> >> >>>> First things first: when you are running RT proggies, you need a stable >> >>>> clock source and a preemptable kernel! Even more so when you're running >> >>>> at 250us cycle time. Your jitter of 50us is roughly 20% of your cycle >> >>>> time, definitely not a neglegible fraction! So tackle that first. >> >>>> >> >>>> Download cyclictest and test that first: >> >>>> https://rt.wiki.kernel.org/index.php/Cyclictest >> >>>> >> >>>> Once your cyclic test is stable (~10us max jitter), you may start >> >>>> looking for delays in your code. >> >>>> >> >>>> Instrumate your loop and find out what is causing delays. Comment out >> >>>> large sections of code to start from a known good state (maybe doing >> >>>> EtherCAT IO exclusively and NOTHING else, no GUI, signal processing, >> >>>> etc) and reinsert code slowly to find the error. >> >>>> >> >>>> Do you have ANY system calls in your RT cycle, like sockets(), read() or >> >>>> write()? Do remember that system calls (including those to the master) >> >>>> may cause page faults that introduce delays. Only a very small subset of >> >>>> known and documented system calls can be used for RT. Notably read() and >> >>>> write() to pipes, shared memory (once set up of coarse), semaphores are >> >>>> OK. >> >>>> >> >>>> BTW: Needless to say that we have paid particular attention that the >> >>>> master does not cause any page faults or other delays once started in >> >>>> cyclic mode ;) >> >>>> >> >>>> Good luck! >> >>>> Richard >> >>>> >> >>>> On 02.03.2016 05:51, Thomas Bitsky Jr wrote: >> >>>>> >> >>>>> I’ve been continuing to work on this, but have had limited success. >> >>>>> While a distributed clock is technically running, it’s caused a few >> >>>>> other problems that I’ve been unable to correct. I think my main >> >>>>> problem >> >>>>> all stems from this: >> >>>>> >> >>>>> [36524.681778] EtherCAT 0: Domain 0: Working counter changed to 10/10. >> >>>>> [36524.681787] EtherCAT 0: Domain 2: Working counter changed to 1/1. >> >>>>> [36524.681792] EtherCAT 0: Domain 1: Working counter changed to 1/1. >> >>>>> [36525.858760] EtherCAT 0: Domain 0: Working counter changed to 0/10. >> >>>>> [36525.858810] EtherCAT 0: Domain 2: Working counter changed to 0/1. >> >>>>> [36525.858827] EtherCAT 0: Domain 1: Working counter changed to 0/1. >> >>>>> [36526.203067] EtherCAT WARNING: Datagram f185d88c (domain0-0-main) was >> >>>>> SKIPPED 2 times. >> >>>>> [36526.203099] EtherCAT WARNING: Datagram f185d90c (domain2-28-main) >> >>>>> was >> >>>>> SKIPPED 2 times. >> >>>>> [36526.203104] EtherCAT WARNING: Datagram f185d28c (domain1-22-main) >> >>>>> was >> >>>>> SKIPPED 2 times. >> >>>>> [36526.743379] EtherCAT WARNING 0: 12 datagrams UNMATCHED! >> >>>>> [36526.863556] EtherCAT 0: Domain 0: 5 working counter changes - now >> >>>>> 10/10. >> >>>>> [36526.863566] EtherCAT 0: Domain 2: 5 working counter changes - now >> >>>>> 1/1. >> >>>>> [36526.863572] EtherCAT 0: Domain 1: 5 working counter changes - now >> >>>>> 1/1. >> >>>>> >> >>>>> … and on and on and on… >> >>>>> >> >>>>> The AKD servo drive I’m using will run fine, no warnings, then suddenly >> >>>>> drop to an F125 fault and shut off. The is a frame synchronization >> >>>>> error. Basically, it’s saying that the sync0 frame isn’t received at a >> >>>>> consistent enough rate, so it faults out. >> >>>>> >> >>>>> My scan rate is 250 microseconds, and I admit there is jitter. It >> >>>>> varies >> >>>>> from as little as +/- 50 microseconds, though I’m not sure why. The >> >>>>> "ethercat master" command reports a steady 4000 frames/sec, but the >> >>>>> scoping part of my project records a timestamp, and I am seeing the +/- >> >>>>> 50 microseconds. >> >>>>> >> >>>>> My timing function is straight out of the EtherCAT master examples and >> >>>>> is also similar to methods I’ve seen in other cyclic task projects. The >> >>>>> whole cyclic task is below. Can anyone see what idiotic thing I must be >> >>>>> doing to get unmatched datagrams? >> >>>>> >> >>>>> #define TIMESPEC_ADD_NS(TS, NS)\ >> >>>>> (TS).tv_nsec += (NS);\ >> >>>>> while ( (TS).tv_nsec >= NANOS_PER_SEC ){\ >> >>>>> (TS).tv_nsec -= NANOS_PER_SEC;\ >> >>>>> (TS).tv_sec++; } >> >>>>> >> >>>>> #define TIMESPEC2NSEPOCH2000(T) \ >> >>>>> ((uint64_t) (((T).tv_sec - 946684800ULL) * 1000000000ULL) + >> >>>>> (T).tv_nsec) >> >>>>> >> >>>>> #define TON struct timespec >> >>>>> #define TON_ENDTIME(MS)\ >> >>>>> time_add_ns((MS) * NANOS_PER_MILLISEC) >> >>>>> >> >>>>> >> >>>>> static TON clockSyncTon_; >> >>>>> >> >>>>> >> >>>>> int >> >>>>> TON_ISDONE( struct timespec ts ) >> >>>>> { >> >>>>> struct timespec now; >> >>>>> clock_gettime(CLOCK_MONOTONIC, &now); >> >>>>> if ( now.tv_sec > ts.tv_sec ) >> >>>>> return 1; >> >>>>> else if ( now.tv_sec == ts.tv_sec >> >>>>> && now.tv_nsec >= ts.tv_nsec ) >> >>>>> return 1; >> >>>>> else >> >>>>> return 0; >> >>>>> } >> >>>>> >> >>>>> >> >>>>> static bool >> >>>>> wait_period(RtaiMain* inst) >> >>>>> { >> >>>>> >> >>>>> int rc; >> >>>>> bool done = false; >> >>>>> while ( !done && inst->doScan && runAll_ ) >> >>>>> { >> >>>>> rc = clock_nanosleep(CLOCK_MONOTONIC, >> >>>>> TIMER_ABSTIME, >> >>>>> &inst->wakeupTime, >> >>>>> NULL ); >> >>>>> >> >>>>> >> >>>>> if ( rc == EFAULT ) >> >>>>> { >> >>>>> return false; >> >>>>> } >> >>>>> else if ( rc == EINTR ) >> >>>>> { >> >>>>> continue; >> >>>>> } >> >>>>> else if ( rc == EINVAL ) >> >>>>> { >> >>>>> return false; >> >>>>> } >> >>>>> else >> >>>>> { >> >>>>> done = 1; >> >>>>> } >> >>>>> } >> >>>>> TIMESPEC_ADD_NS(inst->wakeupTime, inst->cycleNs); >> >>>>> return true; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> >> >>>>> static void >> >>>>> cyclic_task(RtaiMain* inst) >> >>>>> { >> >>>>> >> >>>>> clock_gettime(CLOCK_MONOTONIC ,&(inst->wakeupTime)); >> >>>>> /* start after one second */ >> >>>>> inst->wakeupTime.tv_sec++; >> >>>>> wait_period(inst); >> >>>>> while (runAll_ && inst->doScan) >> >>>>> { >> >>>>> // >> >>>>> // Trigger Fieldbus RX here. >> >>>>> // >> >>>>> // >> >>>>> ecrt_master_receive(master_); >> >>>>> >> >>>>> // record the time we received the data so other parts of the >> >>>>> program >> >>>>> // have an accurate time reading >> >>>>> globalTickTimeNs = ton_get_ns(); >> >>>>> >> >>>>> ecrt_domain_process(lrwDomainMgr_.domain); >> >>>>> ecrt_domain_process(noLrwWriteDomainMgr_.domain); >> >>>>> ecrt_domain_process(noLrwReadDomainMgr_.domain); >> >>>>> >> >>>>> if (counter_) >> >>>>> { >> >>>>> >> >>>>> counter_—; >> >>>>> } >> >>>>> else >> >>>>> { >> >>>>> counter_ = 4000; >> >>>>> >> >>>>> // check for master state >> >>>>> check_master_state(); >> >>>>> } >> >>>>> >> >>>>> >> >>>>> … tick sub systems >> >>>>> >> >>>>> >> >>>>> // >> >>>>> // Trigger Fieldbus TX. This should be the last step >> >>>>> // >> >>>>> // >> >>>>> ecrt_domain_queue(lrwDomainMgr_.domain); >> >>>>> ecrt_domain_queue(noLrwWriteDomainMgr_.domain); >> >>>>> ecrt_domain_queue(noLrwReadDomainMgr_.domain); >> >>>>> clock_gettime(CLOCK_REALTIME, &dcTime_ ); >> >>>>> ecrt_master_application_time( >> >>>>> master_, >> >>>>> TIMESPEC2NSEPOCH2000(dcTime_) ); >> >>>>> >> >>>>> >> >>>>> if ( TON_ISDONE(clockSyncTon_) ) >> >>>>> { >> >>>>> ecrt_master_sync_reference_clock(master_); >> >>>>> clockSyncTon_ = TON_ENDTIME(10);// milliseconds >> >>>>> } >> >>>>> ecrt_master_sync_slave_clocks(master_); >> >>>>> >> >>>>> // send EtherCAT data >> >>>>> ecrt_master_send(master_); >> >>>>> >> >>>>> >> >>>>> if ( !wait_period(inst) ) >> >>>>> { >> >>>>> PRINT( "%s Error with waiting! Stopping cyclic_task.\n", >> >>>>> __FUNCTION__ ); >> >>>>> inst->doScan = false; >> >>>>> } >> >>>>> } >> >>>>> >> >>>>> } >> >>>>> >> >>>>> >> >>>>> >> >>>>> >> >>>>> >> >>>>> >> >>>>> >> >>>>> >> >>>>> >> >>>>> >> >>>>> >> >>>>> >> >>>>> From: Graeme Foot <graeme.f...@touchcut.com >> >>>>> <mailto:graeme.f...@touchcut.com>> >> >>>>> Date: Sunday, February 21, 2016 at 4:07 PM >> >>>>> To: Thomas Bitsky <t...@automateddesign.com >> >>>>> <mailto:t...@automateddesign.com>> >> >>>>> Cc: "etherlab-users@etherlab.org <mailto:etherlab-users@etherlab.org>" >> >>>>> <etherlab-users@etherlab.org <mailto:etherlab-users@etherlab.org>> >> >>>>> Subject: RE: Distributed Clocks >> >>>>> >> >>>>> Hi, >> >>>>> >> >>>>> I build and patch against revision 2526, so don’t know what patches / >> >>>>> fixes have made it through to the latest release. However for my >> >>>>> revision I need fixes for reference clock selections and dc >> >>>>> synchronization issues. >> >>>>> >> >>>>> I’ve attached the dc related patches I use, but these are applied after >> >>>>> some other patches so you may get some conflicts or offsetting. >> >>>>> >> >>>>> *01 - Distributed Clock fixes and helpers.patch* >> >>>>> >> >>>>> This sorts out some ref slave issues, allowing a user selected ref >> >>>>> slave. It also adds some helper functions: >> >>>>> >> >>>>> - ecrt_master_setup_domain_memory() : this allows me to set up the >> >>>>> domain memory and do stuff with it before calling >> >>>>> ecrt_master_activate() >> >>>>> (for user space apps) >> >>>>> >> >>>>> - ecrt_master_deactivate_slaves() : this lets me deactivate the slaves >> >>>>> while still in realtime to avoid the slaves getting some shutdown sync >> >>>>> errors >> >>>>> >> >>>>> *02 - Distributed Clock fixes from Jun Yuan - dc sync issues.patch* >> >>>>> >> >>>>> This sorts out some timing issues to do with slave dc syncing. Without >> >>>>> it they can start syncing from one cycle out causing a large syncing >> >>>>> time overhead, one slave at a time. >> >>>>> >> >>>>> Regards, >> >>>>> >> >>>>> Graeme. >> >>>>> >> >>>>> *From:*Thomas Bitsky Jr [mailto:t...@automateddesign.com] >> >>>>> *Sent:* Sunday, 21 February 2016 10:27 a.m. >> >>>>> *To:* Graeme Foot >> >>>>> *Cc:* etherlab-users@etherlab.org <mailto:etherlab-users@etherlab.org> >> >>>>> *Subject:* Re: Distributed Clocks >> >>>>> >> >>>>> Graeme, >> >>>>> >> >>>>> Thank you so much for the detailed response. I'm away from my computer >> >>>>> right now, so I can't try out your advice, but I noticed you asked >> >>>>> about >> >>>>> patches. I am not running any patches; which should I get? >> >>>>> >> >>>>> Thanks! >> >>>>> Thomas Bitsky Jr >> >>>>> >> >>>>> On Feb 20, 2016, at 3:04 PM, Graeme Foot <graeme.f...@touchcut.com >> >>>>> <mailto:graeme.f...@touchcut.com>> wrote: >> >>>>> >> >>>>> Hi, >> >>>>> >> >>>>> The slave clocks get synced via the distributed clock system using >> >>>>> the master methods: ecrt_master_reference_clock_time(), >> >>>>> ecrt_master_sync_slave_clocks(), ecrt_master_application_time(), >> >>>>> ecrt_master_sync_reference_clock(). >> >>>>> >> >>>>> However each individual slave can choose (if it is capable of it) >> >>>>> whether to synchronize its reading and writing of data to a >> >>>>> particular point in time within the dc cycle. If that slave does >> >>>>> not choose to do so then the reading and writing of the data >> >>>>> occurs >> >>>>> at the time the EtherCAT frame goes past, resulting in a >> >>>>> progressive >> >>>>> update and application of data as the frame progresses through all >> >>>>> of the slaves. >> >>>>> >> >>>>> If a slave is registered to use the dc clock then it caches the >> >>>>> frame data until the sync0 interrupt so in theory all dc slaves >> >>>>> apply the data at the same time. It also means you don’t have to >> >>>>> worry about jitter as to the time the frame is sent over the wire. >> >>>>> The only thing is to choose a good sync0 time to ensure your >> >>>>> frames >> >>>>> are always sent out and have reached all of the slaves before the >> >>>>> sync0 time occurs, and that the next frame is not sent out before >> >>>>> the previous sync0 time occurs. >> >>>>> >> >>>>> In my application my cycle time is 1000us. I choose a sync0 time >> >>>>> of >> >>>>> 500us. I also send my frame as close as possible to the beginning >> >>>>> of the cycle. This gives the frame up to half the cycle time to >> >>>>> reach all of the slaves and then the other half of the cycle for >> >>>>> the >> >>>>> frame to return in time for the master receive call. I could >> >>>>> choose >> >>>>> a sync0 time later than 500us but I want it processed as soon as >> >>>>> possible after the frame is received while still allowing for a >> >>>>> bus >> >>>>> with a large number of terminals. >> >>>>> >> >>>>> So below where you are calculating loop_shift based on the current >> >>>>> time is wrong. Just choose a time within the dc cycle and use >> >>>>> that >> >>>>> value for all slaves. Note: the beginning of the dc cycle is in >> >>>>> phase with the first call to ecrt_master_application_time(), so >> >>>>> all >> >>>>> of your realtime looping should also be in phase with that >> >>>>> initial time. >> >>>>> >> >>>>> Note, when selecting a slave to be the DC reference slave you >> >>>>> should >> >>>>> generally choose the first slave on your bus, regardless of >> >>>>> whether >> >>>>> it will be (or can be) registered to use the dc synchronization. >> >>>>> At >> >>>>> the very least it must be before, or be the, first slave that will >> >>>>> be registered as a dc slave. >> >>>>> >> >>>>> Also note that some slaves clocks are not very stable and >> >>>>> shouldn’t >> >>>>> be used as the DC reference slave. My original testing was on a >> >>>>> Beckhoff CX1020 with a CX1100-0004, this could not be used as a >> >>>>> reference slave as its clock was not stable. >> >>>>> >> >>>>> I see you are configuring the slaves via ecrt_slave_config_*()then >> >>>>> calling ecrt_slave_config_pdos()at the end. If you call >> >>>>> ecrt_slave_config_pdos() at the beginning you don’t need all the >> >>>>> other config calls. However I hear AKD drives and some other >> >>>>> slaves >> >>>>> prefer explicit slave config calls but most slaves are happy with >> >>>>> just the ecrt_slave_config_reg_pdo_entry()methods. >> >>>>> >> >>>>> This also leads to another issue. One of the configured PDO items >> >>>>> is the “mode of operation” value (0x6060 0x00). You are also >> >>>>> configuring this with: ecrt_slave_config_sdo8( sc, 0x6060, 0, 8 ). >> >>>>> This value should be instead be set via the PDO value. Use >> >>>>> ecrt_slave_config_reg_pdo_entry()to get the offset to the value >> >>>>> and >> >>>>> set the value there. >> >>>>> >> >>>>> Sorry if that was a bit long but DC’s is not an easy topic to get >> >>>>> your head around. Here’s a bit of a summary: >> >>>>> >> >>>>> - You can choose which slaves get registered with >> >>>>> ecrt_slave_config_dc(). But each slave you want synced must get >> >>>>> its >> >>>>> own call to ecrt_slave_config_dc(). >> >>>>> >> >>>>> - If your yaskawa drives get to OP without >> >>>>> ecrt_slave_config_dc()then they should get to OP with >> >>>>> ecrt_slave_config_dc(). >> >>>>> >> >>>>> - The yaskawa drives require a very stable reference slave time, >> >>>>> which is why we sync the EtherCAT master to the reference slave >> >>>>> rather than the other way around. >> >>>>> >> >>>>> And some other comments: >> >>>>> >> >>>>> - Never use anEL9010 endcap module. These break the distributed >> >>>>> clock calculations. I don’t think they are available anymore >> >>>>> though. >> >>>>> >> >>>>> - There are some patches out there that fix various DC clock >> >>>>> issues, >> >>>>> are you using any of these? >> >>>>> >> >>>>> Regards, >> >>>>> >> >>>>> Graeme. >> >>>>> >> >>>>> *From:*Thomas Bitsky Jr [mailto:t...@automateddesign.com] >> >>>>> *Sent:* Sunday, 21 February 2016 7:15 a.m. >> >>>>> *To:* Graeme Foot; etherlab-users@etherlab.org >> >>>>> <mailto:etherlab-users@etherlab.org> >> >>>>> *Subject:* Re: Distributed Clocks >> >>>>> >> >>>>> [snip] >> >>>>> >> >>>>> I’ve never been able to get the EL7041 stepper modules to work in >> >>>>> dc >> >>>>> mode. >> >>>>> >> >>>>> [/snip] >> >>>>> >> >>>>> Is it all or nothing? I need the servo drives, the LVDT and the >> >>>>> EL3356 tied to a distributed clock. The EL7041 is optional for me. >> >>>>> >> >>>>> [snip] >> >>>>> >> >>>>> I don’t see in your code calls to ecrt_slave_config_dc(). >> >>>>> >> >>>>> For the yaskawa drive, during the config stage, I use the >> >>>>> following >> >>>>> calls… >> >>>>> >> >>>>> [/snip] >> >>>>> >> >>>>> Forgot to put that part; my bad. This is what I had for the >> >>>>> Yaskawa/AKD, although I was only doing it to one of the drives. I >> >>>>> thought I was supposed to set up one distributed clock, and it >> >>>>> became the master and handled the rest. Am I supposed to do this >> >>>>> for >> >>>>> all the cards, or do I select? >> >>>>> >> >>>>> Yaskawa(AKD drive code is pretty much the same): >> >>>>> >> >>>>> if (!(sc = ecrt_master_slave_config( >> >>>>> >> >>>>> master, >> >>>>> >> >>>>> slavePosDomain, >> >>>>> >> >>>>> slavePosIndex, >> >>>>> >> >>>>> vendorId, productCode))) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> return FALSE; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1C12, 0, 0 ); /* clear sm pdo >> >>>>> 0x1c12 */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1C13, 0, 0 ); /* clear sm pdo >> >>>>> 0x1c12 */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1A00, 0, 0 ); /* clear TxPDO0 */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1A01, 0, 0 ); /* clear TxPDO1 */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1A02, 0, 0 ); /* clear TxPDO2 */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1A03, 0, 0 ); /* clear TxPDO3 */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1600, 0, 0 ); /* number of var in >> >>>>> this >> >>>>> PDO */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1601, 0, 0 ); /* clear RxPdo 0x1601 >> >>>>> */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1602, 0, 0 ); /* clear RxPdo >> >>>>> 0x1602 */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1603, 0, 0 ); /* clear RxPdo >> >>>>> 0x1603 */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1A00, 0, 0 ); /* clear TxPDO0 */ >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1A00, 1, 0x60410010 ); // Status >> >>>>> word >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1A00, 2,0x60640020 );// Position >> >>>>> actual value, per encoder >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1A00, 3,0x60770010 );// Torque, >> >>>>> actual value >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1A00, 4,0x60F40020 );// Following >> >>>>> error, actual value >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1A00, 5,0x60610008 );// Modes of >> >>>>> operation display >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1A00, 6,0x00000008 );// GAP >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1A00, 7,0x60B90010 );// Touch probe >> >>>>> status >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1A00, 8, 0x60BA0020 ); // Touch >> >>>>> probe >> >>>>> 1 position >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1A00, 0, 8 ); /* pdo entries */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1A01, 0, 0 ); /* clear TxPDO1 */ >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1A01,1,0x60410010 ); // Status word >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1A01,2,0x60640020 );// Position >> >>>>> actual value, per encoder >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1A01, 0, 2 ); /* pdo entries */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1A02, 0, 0 ); /* clear TxPDO2 */ >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1A02,1,0x60410010 ); // Status word >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1A02,2,0x60640020 );// Position >> >>>>> actual value, per encoder >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1A02, 0, 2 ); /* pdo entries */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1A03, 0, 0 ); /* clear TxPDO2 */ >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1A03,1,0x60410010 ); // Status word >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1A03,2,0x60640020 );// Position >> >>>>> actual value, per encoder >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1A03,3,0x60770010 );// Torque, >> >>>>> actual >> >>>>> value >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1A03, 0, 3 ); /* pdo entries */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1600, 0, 0 ); /* clear entries */ >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1600, 1, 0x60400010 ); /* control >> >>>>> word */ >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1600, 2, 0x607A0020 ); /* target >> >>>>> position */ >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1600, 3, 0x60FF0020 ); /* target >> >>>>> velocity */ >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1600, 4, 0x60710010 ); /* target >> >>>>> torque */ >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1600, 5, 0x60720010 ); /* max >> >>>>> torque */ >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1600, 6, 0x60600008 ); /* modes of >> >>>>> operation */ >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1600, 7, 0x00000008 ); /* gap */ >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1600, 8, 0x60B80010 ); /* touch >> >>>>> probe function */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8(sc, 0x1600, 0, 8 ); /* pdo entries */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1601, 0, 0 ); /* clear entries */ >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1601, 1, 0x60400010 ); /* control >> >>>>> word */ >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1601, 2, 0x607A0020 ); /* target >> >>>>> position */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1601, 0, 2 ); /* pdo entries */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1602, 0, 0 ); /* clear entries */ >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1602, 1, 0x60400010 ); /* control >> >>>>> word */ >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1602, 2, 0x60FF0020 ); /* target >> >>>>> position */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1602, 0, 2 ); /* pdo entries */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1603, 0, 0 ); /* clear entries */ >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1603, 1, 0x60400010 ); /* control >> >>>>> word */ >> >>>>> >> >>>>> ecrt_slave_config_sdo32( sc, 0x1603, 2, 0x60710020 ); /* target >> >>>>> position */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1603, 0, 2 ); /* pdo entries */ >> >>>>> >> >>>>> ecrt_slave_config_sdo16( sc, 0x1C12, 1, 0x1601 ); /* download pdo >> >>>>> 1C12 index */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1C12, 0, 1 ); /* set number of >> >>>>> RxPDO */ >> >>>>> >> >>>>> ecrt_slave_config_sdo16( sc, 0x1C13, 1, 0x1A01 ); /* download pdo >> >>>>> 1C13 index */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x1C13, 0, 1 ); /* set number of >> >>>>> TxPDO */ >> >>>>> >> >>>>> // OPMODE >> >>>>> >> >>>>> // Yaskawa recommends 8 >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sc, 0x6060, 0, 8 ); >> >>>>> >> >>>>> unsigned char interpolationTime = 0xFF; >> >>>>> >> >>>>> // 250 >> >>>>> >> >>>>> unsigned char cycleExponent = 0xFA; >> >>>>> >> >>>>> // microseconds >> >>>>> >> >>>>> // globalSCanRate_us equals either 250 or 125. >> >>>>> >> >>>>> unsigned int us = globalScanRate_us; >> >>>>> >> >>>>> size_t i; >> >>>>> >> >>>>> for ( i=0;i<6, us > 0xFF;++i ) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> us /= 10; >> >>>>> >> >>>>> cycleExponent += 1; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> interpolationTime = us; >> >>>>> >> >>>>> ecrt_slave_config_sdo8( akd->sc_akd, 0x60C2, 1, interpolationTime >> >>>>> ); >> >>>>> /* Interpolation time */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( akd->sc_akd, 0x60C2, 2, cycleExponent ); >> >>>>> /* >> >>>>> Cycle exponent */ >> >>>>> >> >>>>> PRINT("Configuring PDOs...\n"); >> >>>>> >> >>>>> if (ecrt_slave_config_pdos(sc, EC_END, slave_syncs)) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> PRINT("Failed to configure Yaskawa Sigma PDOs.\n"); >> >>>>> >> >>>>> return FALSE; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> *struct timespec cur_time;* >> >>>>> >> >>>>> *clock_gettime(CLOCK_REALTIME, &cur_time);* >> >>>>> >> >>>>> *size_t loop_period = globalScanRate_us * 1000;* >> >>>>> >> >>>>> *if ( loop_period == 0 ) loop_period = 1;* >> >>>>> >> >>>>> *size_t loop_shift * >> >>>>> >> >>>>> *= loop_period - (cur_time.tv_nsec % loop_period);* >> >>>>> >> >>>>> *ecrt_slave_config_dc(* >> >>>>> >> >>>>> *sc, * >> >>>>> >> >>>>> *0x0300, * >> >>>>> >> >>>>> *loop_period, * >> >>>>> >> >>>>> *loop_shift, * >> >>>>> >> >>>>> *0, * >> >>>>> >> >>>>> *0);* >> >>>>> >> >>>>> For the EL3356, would I then? >> >>>>> >> >>>>> KL3356StrainGauge* sg = (KL3356StrainGauge*)slave->instance; >> >>>>> >> >>>>> printf( "Begin kl3356_ecConfigure...\n"); >> >>>>> >> >>>>> // >> >>>>> >> >>>>> // Create the slave configuration >> >>>>> >> >>>>> // >> >>>>> >> >>>>> if (!(sg->sc = ecrt_master_slave_config( >> >>>>> >> >>>>> master, >> >>>>> >> >>>>> slavePosDomain, slavePosIndex, // Bus position >> >>>>> >> >>>>> vendorId, productCode >> >>>>> >> >>>>> // Slave type >> >>>>> >> >>>>> ))) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> printf( >> >>>>> >> >>>>> "kl3356_ecConfigure -- Failed to get slave configuration.\n"); >> >>>>> >> >>>>> return FALSE; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> // >> >>>>> >> >>>>> // Register startup configuration for the hardware >> >>>>> >> >>>>> // >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sg->sc, 0x1C12, 0, 0 ); /* clear sm pdo >> >>>>> 0x1c12 */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sg->sc, 0x1C13, 0, 0 ); /* clear sm pdo >> >>>>> 0x1c12 */ >> >>>>> >> >>>>> ecrt_slave_config_sdo16( sg->sc, 0x1C12, 1, 0x1600 ); /* download >> >>>>> pdo 1C12 index */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sg->sc, 0x1C12, 0, 1 ); /* set number of >> >>>>> RxPDO */ >> >>>>> >> >>>>> ecrt_slave_config_sdo16( sg->sc, 0x1C13, 1, 0x1A00 ); /* download >> >>>>> pdo 1C13 index */ >> >>>>> >> >>>>> ecrt_slave_config_sdo16( sg->sc, 0x1C13, 2, 0x1A02 ); /* download >> >>>>> pdo 1C13 index */ >> >>>>> >> >>>>> ecrt_slave_config_sdo8( sg->sc, 0x1C13, 0, 2 ); /* set number of >> >>>>> TxPDO */ >> >>>>> >> >>>>> // >> >>>>> >> >>>>> // Configure the hardware's PDOs >> >>>>> >> >>>>> // >> >>>>> >> >>>>> if (ecrt_slave_config_pdos(sg->sc, EC_END, kl3356_syncs)) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> printf( >> >>>>> >> >>>>> "kl3356_ecConfigure -- Failed to configure PDOs.\n"); >> >>>>> >> >>>>> return FALSE; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> *struct timespec cur_time;* >> >>>>> >> >>>>> *clock_gettime(CLOCK_REALTIME, &cur_time);* >> >>>>> >> >>>>> *size_t loop_period = globalScanRate_us * 1000;* >> >>>>> >> >>>>> *if ( loop_period == 0 ) loop_period = 1;* >> >>>>> >> >>>>> *size_t loop_shift * >> >>>>> >> >>>>> *= loop_period - (cur_time.tv_nsec % loop_period);* >> >>>>> >> >>>>> *ecrt_slave_config_dc(* >> >>>>> >> >>>>> *s**g->sc, * >> >>>>> >> >>>>> *0x0300, * >> >>>>> >> >>>>> *loop_period, * >> >>>>> >> >>>>> *loop_shift, * >> >>>>> >> >>>>> *0, * >> >>>>> >> >>>>> *0);* >> >>>>> >> >>>>> Thanks! >> >>>>> >> >>>>> Thomas C. Bitsky Jr. | Lead Developer >> >>>>> >> >>>>> ADC | automateddesign.com <http://automateddesign.com/> >> >>>>> >> >>>>> P: 630-783-1150 F: 630-783-1159 M: 630-632-6679 >> >>>>> >> >>>>> Follow ADC news and media: >> >>>>> >> >>>>> Facebook <https://facebook.com/automateddesigncorp> | Twitter >> >>>>> <https://twitter.com/ADCSportsLogic> | YouTube >> >>>>> <https://www.youtube.com/user/ADCSportsLogic> >> >>>>> >> >>>>> *From: *Graeme Foot <graeme.f...@touchcut.com >> >>>>> <mailto:graeme.f...@touchcut.com>> >> >>>>> *Date: *Friday, February 19, 2016 at 7:24 PM >> >>>>> *To: *Thomas Bitsky <t...@automateddesign.com >> >>>>> <mailto:t...@automateddesign.com>>, "etherlab-users@etherlab.org >> >>>>> <mailto:etherlab-users@etherlab.org>" <etherlab-users@etherlab.org >> >>>>> <mailto:etherlab-users@etherlab.org>> >> >>>>> *Subject: *RE: Distributed Clocks >> >>>>> >> >>>>> Hi, >> >>>>> >> >>>>> I don’t see in your code calls to ecrt_slave_config_dc(). >> >>>>> >> >>>>> For the yaskawa drive, during the config stage, I use the >> >>>>> following >> >>>>> calls: >> >>>>> >> >>>>> // set interpolation time period (free run mode) >> >>>>> >> >>>>> // where 0x60C2 is time in seconds = (0x60C2, 0x01) x >> >>>>> 10^(0x60C2, 0x02) >> >>>>> >> >>>>> // eg period of 1ms: >> >>>>> >> >>>>> // (0x60C2, 0x01) = 1 >> >>>>> >> >>>>> // (0x60C2, 0x02) = -3 >> >>>>> >> >>>>> // => 1 x 10^(-3) = 0.001s >> >>>>> >> >>>>> ecrt_slave_config_sdo8(dev->slaveConfig, 0x60C2, 0x01, >> >>>>> (uint8_t)g_app.scanTimeMS); >> >>>>> >> >>>>> ecrt_slave_config_sdo8(dev->slaveConfig, 0x60C2, 0x02, >> >>>>> (int8_t)(-3)); >> >>>>> >> >>>>> // set up the distributed clock >> >>>>> >> >>>>> // 0x0000 = free run, 0x0300 = dc >> >>>>> >> >>>>> // (Supported DC cycle: 125us to 4ms (every 125us cycle)) >> >>>>> >> >>>>> ecrt_slave_config_dc(dev->slaveConfig, 0x0300, >> >>>>> g_app.scanTimeNS, 500000, 0, 0); >> >>>>> >> >>>>> 0x60C2 shouldn’t be necessary for dc mode, but I used it before I >> >>>>> had dc mode working and have never tried it without and it doesn’t >> >>>>> harm anything having it in. >> >>>>> >> >>>>> The second value that is being passed to the >> >>>>> ecrt_slave_config_dcmethod is a value that is written to the ESC >> >>>>> register 0x980. The Yaskawa SGDV doco says this value should be >> >>>>> 0x0000 for free run mode and 0x0300 for dc mode. Other ESC’s may >> >>>>> required different values. >> >>>>> >> >>>>> I’ve never been able to get the EL7041 stepper modules to work in >> >>>>> dc >> >>>>> mode. >> >>>>> >> >>>>> Graeme. >> >>>>> >> >>>>> *From:*etherlab-users [mailto:etherlab-users-boun...@etherlab.org] >> >>>>> *On Behalf Of *Thomas Bitsky Jr >> >>>>> *Sent:* Saturday, 20 February 2016 1:09 p.m. >> >>>>> *To:* etherlab-users@etherlab.org >> >>>>> <mailto:etherlab-users@etherlab.org> >> >>>>> *Subject:* [etherlab-users] Distributed Clocks >> >>>>> >> >>>>> Hello. >> >>>>> >> >>>>> I’ve been using the EtherCAT master for years to great success, >> >>>>> but >> >>>>> I’m stuck on a problem I can’t figure out that I think several >> >>>>> people here are doing successfully. I can’t implement distributed >> >>>>> clocks in my application. >> >>>>> >> >>>>> I am having the same problem on two systems I have up and running: >> >>>>> >> >>>>> SYSTEM ONE: >> >>>>> >> >>>>> EtherLAB Master 1.52, E1000E Driver, Scan Rate 4Khz, Ubuntu Server >> >>>>> 14.04LTS, RT-PREEMPT 3.12.50-rt68 >> >>>>> >> >>>>> alias=0, position=0, device=EK1100 >> >>>>> >> >>>>> alias=0, position=1, device=EL1104 >> >>>>> >> >>>>> alias=0, position=2, device=EL2004 >> >>>>> >> >>>>> alias=0, position=3, device=EL9510 >> >>>>> >> >>>>> alias=0, position=4, device=EL3356 >> >>>>> >> >>>>> alias=0, position=5, device=Kollmorgen AKD >> >>>>> >> >>>>> alias=0, position=6, device=MTS LVDT >> >>>>> >> >>>>> SYSTEM TWO: >> >>>>> >> >>>>> EtherLAB Master 1.52, E1000E Driver, Scan Rate 8Khz, Ubuntu Server >> >>>>> 14.04LTS, RT-PREEMPT 3.12.50-rt68 >> >>>>> >> >>>>> alias=0, position=0, device=EK1100 >> >>>>> >> >>>>> alias=0, position=1, device=EL3001 >> >>>>> >> >>>>> alias=0, position=2, device=EL1104 >> >>>>> >> >>>>> alias=0, position=3, device=EL1104 >> >>>>> >> >>>>> alias=0, position=4, device=EL1104 >> >>>>> >> >>>>> alias=0, position=5, device=EL2004 >> >>>>> >> >>>>> alias=0, position=6, device=EL2004 >> >>>>> >> >>>>> alias=0, position=7, device=EL9505 >> >>>>> >> >>>>> alias=0, position=8, device=EL7041 >> >>>>> >> >>>>> alias=0, position=9, device=EL7041 >> >>>>> >> >>>>> alias=0, position=10, device=EL7041 >> >>>>> >> >>>>> alias=0, position=11, device=EL7041 >> >>>>> >> >>>>> alias=0, position=12, device=EL7041 >> >>>>> >> >>>>> alias=0, position=13, device=EL7041 >> >>>>> >> >>>>> alias=0, position=14, device=EK1110 >> >>>>> >> >>>>> alias=1, position=0, device=SIGMA5-05 >> >>>>> >> >>>>> alias=2, position=0, device=Yaskawa SIGMA5-05 >> >>>>> >> >>>>> alias=3, position=0, device=Yaskawa SIGMA5-05 >> >>>>> >> >>>>> Both of the system are fully operational. However, for various >> >>>>> reasons, I need to implement distributed clocks on these systems. >> >>>>> I’ve never been able to get this to work. >> >>>>> >> >>>>> What follows is the code I used for both systems to try this. I >> >>>>> read >> >>>>> through examples on the mailing list, plus the examples, but I’m >> >>>>> not >> >>>>> seeing what I’m doing wrong. The result is always the same: all >> >>>>> the >> >>>>> fieldbus cards go into operation, but the servo drives won’t >> >>>>> because >> >>>>> of “bad configuration.” Take out the distributed clock code, and >> >>>>> they work fine. I’m getting away with it for now, but I do need >> >>>>> better clock resolution. >> >>>>> >> >>>>> The systems have an LRW domain, then a separate read domain and >> >>>>> write domain for the servo drive(s) for a total of three domains >> >>>>> (LRW, read, write). The yaskawa drives necessitate this. The scan >> >>>>> rate is usually 4Khz, but I have tried it at both 1Khz and 8Khz >> >>>>> and >> >>>>> gotten the same results. Everything about the implementation is >> >>>>> fairly straight-forward. There’s just something fundamental about >> >>>>> the DC configuration that I’m not understanding. >> >>>>> >> >>>>> Almost all the code below is taken right from the examples or the >> >>>>> message boards (thanks, everybody!). If anyone could tell me what >> >>>>> I’m going wrong or offer any insights, it’s greatly appreciated. >> >>>>> For >> >>>>> brevity, I tried to narrow it down to relevant parts, but please >> >>>>> let >> >>>>> me know any additional information or code I can provide. >> >>>>> >> >>>>> Thank you in advance, >> >>>>> >> >>>>> Tom >> >>>>> >> >>>>> ********************************************************** >> >>>>> >> >>>>> // EtherCAT distributed clock variables >> >>>>> >> >>>>> #define DC_FILTER_CNT 1024 >> >>>>> >> >>>>> #define SYNC_MASTER_TO_REF 1 >> >>>>> >> >>>>> static uint64_t dc_start_time_ns = 0LL; >> >>>>> >> >>>>> static uint64_t dc_time_ns = 0; >> >>>>> >> >>>>> static uint8_t dc_started = 0; >> >>>>> >> >>>>> static int32_t dc_diff_ns = 0; >> >>>>> >> >>>>> static int32_t prev_dc_diff_ns = 0; >> >>>>> >> >>>>> static int64_t dc_diff_total_ns = 0LL; >> >>>>> >> >>>>> static int64_t dc_delta_total_ns = 0LL; >> >>>>> >> >>>>> static int dc_filter_idx = 0; >> >>>>> >> >>>>> static int64_t dc_adjust_ns; >> >>>>> >> >>>>> static int64_t system_time_base = 0LL; >> >>>>> >> >>>>> static uint64_t wakeup_time = 0LL; >> >>>>> >> >>>>> static uint64_t overruns = 0LL; >> >>>>> >> >>>>> /** Get the time in ns for the current cpu, adjusted by >> >>>>> system_time_base. >> >>>>> >> >>>>> * >> >>>>> >> >>>>> * \attention Rather than calling rt_get_time_ns() directly, all >> >>>>> application >> >>>>> >> >>>>> * time calls should use this method instead. >> >>>>> >> >>>>> * >> >>>>> >> >>>>> * \ret The time in ns. >> >>>>> >> >>>>> */ >> >>>>> >> >>>>> uint64_t system_time_ns(void) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> struct timespec ts; >> >>>>> >> >>>>> clock_gettime(GLOBAL_CLOCK_TO_USE, &ts); >> >>>>> >> >>>>> return TIMESPEC2NS(ts); >> >>>>> >> >>>>> } >> >>>>> >> >>>>> static >> >>>>> >> >>>>> void sync_distributed_clocks(void) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> uint32_t ref_time = 0; >> >>>>> >> >>>>> uint64_t prev_app_time = dc_time_ns; >> >>>>> >> >>>>> dc_time_ns = system_time_ns(); >> >>>>> >> >>>>> // set master time in nano-seconds >> >>>>> >> >>>>> ecrt_master_application_time(master_, dc_time_ns); >> >>>>> >> >>>>> // get reference clock time to synchronize master cycle >> >>>>> >> >>>>> ecrt_master_reference_clock_time(master_, &ref_time); >> >>>>> >> >>>>> dc_diff_ns = (uint32_t) prev_app_time - ref_time; >> >>>>> >> >>>>> // call to sync slaves to ref slave >> >>>>> >> >>>>> ecrt_master_sync_slave_clocks(master_); >> >>>>> >> >>>>> } >> >>>>> >> >>>>> /** Return the sign of a number >> >>>>> >> >>>>> * >> >>>>> >> >>>>> * ie -1 for -ve value, 0 for 0, +1 for +ve value >> >>>>> >> >>>>> * >> >>>>> >> >>>>> * \retval the sign of the value >> >>>>> >> >>>>> */ >> >>>>> >> >>>>> #define sign(val) \ >> >>>>> >> >>>>> ({ typeof (val) _val = (val); \ >> >>>>> >> >>>>> ((_val > 0) - (_val < 0)); }) >> >>>>> >> >>>>> >> >>>>> /*****************************************************************************/ >> >>>>> >> >>>>> /** Update the master time based on ref slaves time diff >> >>>>> >> >>>>> * >> >>>>> >> >>>>> * called after the ethercat frame is sent to avoid time jitter >> >>>>> in >> >>>>> >> >>>>> * sync_distributed_clocks() >> >>>>> >> >>>>> */ >> >>>>> >> >>>>> static unsigned int cycle_ns = 1000000; // 1 millisecond >> >>>>> >> >>>>> void update_master_clock(void) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> // calc drift (via un-normalised time diff) >> >>>>> >> >>>>> int32_t delta = dc_diff_ns - prev_dc_diff_ns; >> >>>>> >> >>>>> prev_dc_diff_ns = dc_diff_ns; >> >>>>> >> >>>>> // normalise the time diff >> >>>>> >> >>>>> dc_diff_ns = >> >>>>> >> >>>>> ((dc_diff_ns + (cycle_ns / 2)) % cycle_ns) - (cycle_ns / >> >>>>> 2); >> >>>>> >> >>>>> // only update if primary master >> >>>>> >> >>>>> if (dc_started) { >> >>>>> >> >>>>> // add to totals >> >>>>> >> >>>>> dc_diff_total_ns += dc_diff_ns; >> >>>>> >> >>>>> dc_delta_total_ns += delta; >> >>>>> >> >>>>> dc_filter_idx++; >> >>>>> >> >>>>> if (dc_filter_idx >= DC_FILTER_CNT) { >> >>>>> >> >>>>> // add rounded delta average >> >>>>> >> >>>>> dc_adjust_ns += >> >>>>> >> >>>>> ((dc_delta_total_ns + (DC_FILTER_CNT / 2)) / >> >>>>> DC_FILTER_CNT); >> >>>>> >> >>>>> // and add adjustment for general diff (to pull in >> >>>>> drift) >> >>>>> >> >>>>> dc_adjust_ns += sign(dc_diff_total_ns / >> >>>>> DC_FILTER_CNT); >> >>>>> >> >>>>> // limit crazy numbers (0.1% of std cycle time) >> >>>>> >> >>>>> if (dc_adjust_ns < -1000) { >> >>>>> >> >>>>> dc_adjust_ns = -1000; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> if (dc_adjust_ns > 1000) { >> >>>>> >> >>>>> dc_adjust_ns = 1000; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> // reset >> >>>>> >> >>>>> dc_diff_total_ns = 0LL; >> >>>>> >> >>>>> dc_delta_total_ns = 0LL; >> >>>>> >> >>>>> dc_filter_idx = 0; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> // add cycles adjustment to time base (including a spot >> >>>>> adjustment) >> >>>>> >> >>>>> system_time_base += dc_adjust_ns + sign(dc_diff_ns); >> >>>>> >> >>>>> } >> >>>>> >> >>>>> else { >> >>>>> >> >>>>> dc_started = (dc_diff_ns != 0); >> >>>>> >> >>>>> if (dc_started) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> // record the time of this initial cycle >> >>>>> >> >>>>> dc_start_time_ns = dc_time_ns; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> } >> >>>>> >> >>>>> } >> >>>>> >> >>>>> struct timespec dcTime_; >> >>>>> >> >>>>> int >> >>>>> >> >>>>> ecatMain_process(void* lp) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> ecrt_master_receive(master_); >> >>>>> >> >>>>> clock_gettime(CLOCK_REALTIME, &dcTime_); >> >>>>> >> >>>>> ecrt_master_application_time(master_, TIMESPEC2NS(dcTime_)); >> >>>>> >> >>>>> ecrt_master_sync_reference_clock(master_); >> >>>>> >> >>>>> ecrt_master_sync_slave_clocks(master_); >> >>>>> >> >>>>> ecrt_domain_process(lrwDomainMgr_.domain); >> >>>>> >> >>>>> ecrt_domain_process(noLrwWriteDomainMgr_.domain); >> >>>>> >> >>>>> ecrt_domain_process(noLrwReadDomainMgr_.domain); >> >>>>> >> >>>>> … // handle my business >> >>>>> >> >>>>> // write application time to master >> >>>>> >> >>>>> clock_gettime(CLOCK_REALTIME, &dcTime_); >> >>>>> >> >>>>> ecrt_master_application_time(master_, TIMESPEC2NS(dcTime_)); >> >>>>> >> >>>>> if (sync_ref_counter_) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> sync_ref_counter_--; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> else >> >>>>> >> >>>>> { >> >>>>> >> >>>>> sync_ref_counter_ = 1; // sync every cycle >> >>>>> >> >>>>> ecrt_master_sync_reference_clock(master_); >> >>>>> >> >>>>> } >> >>>>> >> >>>>> // send process data >> >>>>> >> >>>>> ecrt_domain_queue(lrwDomainMgr_.domain); >> >>>>> >> >>>>> ecrt_domain_queue(noLrwWriteDomainMgr_.domain); >> >>>>> >> >>>>> ecrt_domain_queue(noLrwReadDomainMgr_.domain); >> >>>>> >> >>>>> // sync distributed clock just before master_send to set >> >>>>> >> >>>>> // most accurate master clock time >> >>>>> >> >>>>> sync_distributed_clocks(); >> >>>>> >> >>>>> // send EtherCAT data >> >>>>> >> >>>>> ecrt_master_send(master_); >> >>>>> >> >>>>> // update the master clock >> >>>>> >> >>>>> // Note: called after ecrt_master_send() to reduce time >> >>>>> >> >>>>> // jitter in the sync_distributed_clocks() call >> >>>>> >> >>>>> update_master_clock(); >> >>>>> >> >>>>> return 1; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> int >> >>>>> >> >>>>> ecatMain_start(void* lp) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> // >> >>>>> >> >>>>> // domain regs must end in a null entry >> >>>>> >> >>>>> // >> >>>>> >> >>>>> lrwDomainMgr_.domainRegs = realloc( >> >>>>> >> >>>>> lrwDomainMgr_.domainRegs, >> >>>>> >> >>>>> sizeof(ec_pdo_entry_reg_t) * (lrwDomainMgr_.size + 1) ); >> >>>>> >> >>>>> memset( >> >>>>> >> >>>>> &(lrwDomainMgr_.domainRegs[lrwDomainMgr_.size]), >> >>>>> >> >>>>> 0, >> >>>>> >> >>>>> sizeof(ec_pdo_entry_reg_t) ); >> >>>>> >> >>>>> noLrwReadDomainMgr_.domainRegs = realloc( >> >>>>> >> >>>>> noLrwReadDomainMgr_.domainRegs, >> >>>>> >> >>>>> sizeof(ec_pdo_entry_reg_t) * (noLrwReadDomainMgr_.size + 1) ); >> >>>>> >> >>>>> memset( >> >>>>> >> >>>>> &(noLrwReadDomainMgr_.domainRegs[noLrwReadDomainMgr_.size]), >> >>>>> >> >>>>> 0, >> >>>>> >> >>>>> sizeof(ec_pdo_entry_reg_t) ); >> >>>>> >> >>>>> noLrwWriteDomainMgr_.domainRegs = realloc( >> >>>>> >> >>>>> noLrwWriteDomainMgr_.domainRegs, >> >>>>> >> >>>>> sizeof(ec_pdo_entry_reg_t) * (noLrwWriteDomainMgr_.size + 1) ); >> >>>>> >> >>>>> memset( >> >>>>> >> >>>>> &(noLrwWriteDomainMgr_.domainRegs[noLrwWriteDomainMgr_.size]), >> >>>>> >> >>>>> 0, >> >>>>> >> >>>>> sizeof(ec_pdo_entry_reg_t) ); >> >>>>> >> >>>>> // >> >>>>> >> >>>>> // NOTE: the Output Domain must be registered with >> >>>>> >> >>>>> // ecrt_domain_reg_pdo_entry_list before the Input Domain >> >>>>> otherwise you >> >>>>> >> >>>>> // will not have any data exchanged even though the drive goes >> >>>>> into OP >> >>>>> >> >>>>> // mode. >> >>>>> >> >>>>> // >> >>>>> >> >>>>> PRINT("\nAttempting to register PDOs on WRITE ONLY domain...\n"); >> >>>>> >> >>>>> if (ecrt_domain_reg_pdo_entry_list( >> >>>>> >> >>>>> noLrwWriteDomainMgr_.domain, noLrwWriteDomainMgr_.domainRegs)) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> PRINT("WRITE ONLY PDO entry registration failed!\n"); >> >>>>> >> >>>>> return FALSE; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> PRINT("\nAttempting to register PDOs on READ ONLY domain...\n"); >> >>>>> >> >>>>> if (ecrt_domain_reg_pdo_entry_list( >> >>>>> >> >>>>> noLrwReadDomainMgr_.domain, noLrwReadDomainMgr_.domainRegs)) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> PRINT("READ ONLY PDO entry registration failed!\n"); >> >>>>> >> >>>>> return FALSE; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> // >> >>>>> >> >>>>> // And now we register the bi-directional domain. >> >>>>> >> >>>>> // >> >>>>> >> >>>>> PRINT("\nAttempting to register PDOs on LRW domain...\n"); >> >>>>> >> >>>>> if (ecrt_domain_reg_pdo_entry_list( >> >>>>> >> >>>>> lrwDomainMgr_.domain, lrwDomainMgr_.domainRegs)) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> PRINT("LRW PDO entry registration failed!\n"); >> >>>>> >> >>>>> return FALSE; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> /* >> >>>>> >> >>>>> * Finishes the configuration phase and prepares for cyclic >> >>>>> operation. >> >>>>> >> >>>>> * This function tells the master that the configuration phase >> >>>>> >> >>>>> * is finished and the realtime operation will begin. >> >>>>> >> >>>>> * The function allocates internal memory for the domains and >> >>>>> calculates >> >>>>> >> >>>>> * the logical FMMU addresses for domain members. >> >>>>> >> >>>>> * It tells the master state machine that the bus configuration is >> >>>>> >> >>>>> * now to be applied >> >>>>> >> >>>>> */ >> >>>>> >> >>>>> PRINT("\nAttempting to activate ECAT master...\n"); >> >>>>> >> >>>>> if (ecrt_master_activate(master_)) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> PRINT( >> >>>>> >> >>>>> "%s Failed to activate master!\n", >> >>>>> >> >>>>> __FUNCTION__ ); >> >>>>> >> >>>>> return FALSE; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> /* >> >>>>> >> >>>>> * Returns the domain's process data. >> >>>>> >> >>>>> */ >> >>>>> >> >>>>> PRINT( "%s getting LRW process data from master.\n", __FUNCTION__ >> >>>>> ); >> >>>>> >> >>>>> if (!(lrwDomainMgr_.processData >> >>>>> >> >>>>> = ecrt_domain_data(lrwDomainMgr_.domain))) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> PRINT( >> >>>>> >> >>>>> "%s set ecProcessData -- domain data is NULL!\n", >> >>>>> >> >>>>> __FUNCTION__ ); >> >>>>> >> >>>>> return FALSE; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> if (!(noLrwReadDomainMgr_.processData >> >>>>> >> >>>>> = ecrt_domain_data(noLrwReadDomainMgr_.domain))) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> PRINT( >> >>>>> >> >>>>> "%s set read ProcessData -- domain data is NULL!\n", >> >>>>> >> >>>>> __FUNCTION__ ); >> >>>>> >> >>>>> return FALSE; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> if (!(noLrwWriteDomainMgr_.processData >> >>>>> >> >>>>> = ecrt_domain_data(noLrwWriteDomainMgr_.domain))) >> >>>>> >> >>>>> { >> >>>>> >> >>>>> PRINT( >> >>>>> >> >>>>> "%s set write ProcessData -- domain data is NULL!\n", >> >>>>> >> >>>>> __FUNCTION__ ); >> >>>>> >> >>>>> return FALSE; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> … // blah blah blah >> >>>>> >> >>>>> doScan_ = TRUE; >> >>>>> >> >>>>> PRINT( "%s completed successfully.\n", __FUNCTION__ ); >> >>>>> >> >>>>> return TRUE; >> >>>>> >> >>>>> } >> >>>>> >> >>>>> >> >>>>> >> >>>>> _______________________________________________ >> >>>>> etherlab-users mailing list >> >>>>> etherlab-users@etherlab.org >> >>>>> http://lists.etherlab.org/mailman/listinfo/etherlab-users >> >>>>> >> >>>> _______________________________________________ >> >>>> etherlab-users mailing list >> >>>> etherlab-users@etherlab.org >> >>>> http://lists.etherlab.org/mailman/listinfo/etherlab-users >> >>> _______________________________________________ >> >>> etherlab-users mailing list >> >>> etherlab-users@etherlab.org >> >>> http://lists.etherlab.org/mailman/listinfo/etherlab-users >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> Outbound scan for Spam or Virus by Barracuda at Delta Tau >> >> >> >> > > > > _______________________________________________ etherlab-users mailing list etherlab-users@etherlab.org http://lists.etherlab.org/mailman/listinfo/etherlab-users