一.系统环境说明
- 主站系统:
ubuntu22.04 - Igh版本:gitlab上的最新版本1.6.0,https://gitlab.com/etherlab.org/ethercat
- 系统的实时内核:
preempt-rt 该内核安装非常方便,
- 注册https://ubuntu.com/账号
- 在个人账号的订阅下可以看到Token, 可以免费安装5台设备
https://ubuntu.com/pro/dashboard -
ua attach <免费的TOEKN>
- 确保你在使用至少 27.8 版本的 ubuntu-advantage-tools 软件包,可通过此命令查看当前版本:
ua version
要在 Ubuntu 22.04 (Jammy Jellyfish) 中升级 ubuntu-advantage-tools 到 27.8,请运行命令:
sudo apt install ubuntu-advantage-tools=27.8~22.04.1
要启用测试版实时内核,可运行:
ua enable realtime-kernel --beta
需要验证实时性能,可以使用cyclitest,主要看max的值,经过14个小时时间,其最大值的延迟在230us左右,满足8ms为周期的控制
sudo cyclictest -p 99 -S -i 8000 -m
[sudo] lm 的密码:
/dev/cpu_dma_latency set to 0us
policy: fifo: loadavg: 10.26 9.47 9.38 7/1308 105547
T: 0 (91316) P:99 I:8000 C:6638053 Min: 2 Act: 73 Avg: 30 Max: 193
T: 1 (91317) P:99 I:8500 C:6247579 Min: 2 Act: 72 Avg: 27 Max: 166
T: 2 (91318) P:99 I:9000 C:5900491 Min: 3 Act: 74 Avg: 29 Max: 230
T: 3 (91319) P:99 I:9500 C:5589938 Min: 3 Act: 6 Avg: 28 Max: 155
lm@lm-X550JK:~$ stress-ng -c 4 --cpu-method fft --timerfd-freq 1000000 -t 24h
stress-ng: info: [92633] setting to a 86400 second (1 day, 0.00 secs) run per stressor
stress-ng: info: [92633] dispatching hogs: 4 cpu
^Cstress-ng: info: [92633] successful run completed in 52186.24s (14 hours, 29 mins, 46.24 secs)
- 控制对象:
7个支持DC模式的电机 - 参考代码:
- 官方例程:dc_user,该例程以主站为参考时钟;
- 官方例程:rtai_rtdm_dc,该例程以从站为参考时钟,但其内核环境是RTAI
二.igh使用配置
ecrt_request_master(0);
ecrt_master_create_domain(master);
sc[i] = ecrt_master_slave_config(master, a[i], p[i], VID_PID)
ecrt_slave_config_pdos(sc[i], EC_END, device_syncs)
ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs)
ecrt_slave_config_sdo16(sc[i], 0x1c32, 1, 2);
ecrt_slave_config_sdo16(sc[i], 0x1c33, 1, 2);
ecrt_slave_config_dc(sc[i], 0x0300, cycle_ns, cycle_ns/4, 0, 0);
ecrt_master_activate(master)
ecrt_domain_data(domain1)
三.ethercat DC同步
3.1 dc同步原理
用EtherCAT的分布式时钟(DC)功能使从站设备同步指的是,总线中的第一个DC从站被定义为基准时钟,EtherCAT主站将基准时钟的时间分配至所有的从站。因此,EtherCAT主站周期性发送一个ARMW命令,以此读取存储在时钟主站的ESC(EtherCAT从站控制器)上适当的寄存器中的总线时间,并将这个值写入DC-从站相应的寄存器中。这些DC从站通过整合在ESC中的一个控制器来更新他们的本地时间。为保证请求的精度(可以接收低于1us的值),特殊从站之间的EtherCAT帧延迟必须得到额外的补偿。 对于每个从站来说,一个帧从发送到接受的这段时间将被测量。然后,根据总线拓扑结构,主站计算从站之间的延迟,并将相应延迟补偿值写入到ESC中的寄存器0x928里。
ESC控制器的DC单元提供两个数字输出信号,SYNC0和SYNC1。频率一般对应于EtherCAT总线时钟的SYNC脉冲通常都是基于总线时间生成的。举个例子:如果EtherCAT主站用1ms发送周期性的I/O数据,SYNC脉冲频率通常也设置为1kHz。这些SYNC信号在从站一侧,一方面是可作为一个数字输出信号(例如要激活从站硬件组件),另一方面作为从站软件中断源。也就是说,很明显的要在SYNC脉冲被释放之前为所有从站提供新的信号。要实现这一目标,新的周期性I/O数据到达和SYNC脉冲之间的相隔时间的必须保证最小化。
通常情况下,EtherCAT主站协议栈通过在其控制硬件(例如嵌入式x86PC中的8254计时器)中的硬件计时器来发送循环输出数据。如果系统运行周期为1kHz,那么8254计时器以及负责生成同步脉冲的从站计时器都应设置为1kHz。但8254计时器和从站计时器都不能准确的运行1kHz的周期率。实际上,这两个计时器之间有些偏差,因此,在主站里发送周期性I/O数据和从站中的一系列SYNC脉冲之间可以实现非恒定间隔。为了控制一个定义好的常量值的间隔,EtherCAT主站必须要与DC时钟主站同步(就是主站上的第一个DC从站)。这一机制被称为分布式时钟主站同步(DCM)。 它可以以两种不同的方式实现: 重新调整用来执行EtherCAT主站的硬件中所使用的物理计时器(例如8254定时器)(主站转换);重新调整DC时钟主站的总线时间时间(总线转换)。 DCM控制器周期性计算EtherCAT主站时间和DC时钟主站时间之间的差异。根据设定值(即SYNC脉冲到主站中计时器中断的距离),PI控制器算法计算重新调整的值。当使用"主站转换"时,重新调整值会影响EtherCAT主站的物理计时器(例如8254);而当使用的"总线转换"时,它则会影响DC时钟主站的寄存器0x920。 EtherCAT主站ClassA支持这两种方法(主站转换和总线转换)。 主站与从站的同步是EtherCAT最具挑战性的特点之一。
3.2 同步模式
3.2.1 free run
从站的过程数据处理,由内部事件触发,与主站循环无关
1、每个从站的定时周期都不一样 2、每个定时周期到的时候,每个从站去执行自己的程序,<比如检查通道上有没有合适的新的输入数据,有的话就令其输出有效(Output valid)或者检查有没有需要输入的数据,将其放到对应的同步管理器通道上(input prepare)让主站取走> 3、对于Free Run 模式而言,好比每个人都有自己的手表,但是如果没有一起对时的话。手表的本身的时间是不一样的(可能表和表之间存在时间差,一个8点一个10点),那么在这种情况下,公司如果要求9点上班,那么每个人到公司的时间都是自己手表上的9点,但是不是统一的9点,他们到达公司的时间是不一样,所以Free Run模式没有任何的同步性可言
3.2.2 SM-Synchronous
从站的过程数据处理,由接收到携带过程数据的周期性数据帧时所产生的硬件中断触发
1、一般而言,如果EtherCAT总线通讯时的同步模式不是DC模式,那么就是SM同步模式了, 2、SM(Sync Manager同步管理器)指的是同步管理器的同步,它的触发方式是通过SM Event,也就是我们的数据帧在到达对应的从站的时候,会触发一个叫做同步管理器事件的信号(即SM Event),当从站接受到这个信号的时候,会进入到对应的中断服务例程(即中断保存当期任务,去执行插入的中断例程,这里指线程中断处理相关数据,这也是SM Event和 Free Run的不同之处),即SM是通过中断服务例程来处理对应的数据。 3、从站检测到SM Event事件信号的时候会进入到中断服务例程去处理相应的数据(比如把输出数据有效,然后把输入数据放到同步管理器的通道上让主站取走) 4、由于SM同步模式是根据数据帧到达特定从站的时候来触发SM Event事件信号来进行同步,那么对于一个特地给的帧来说,它到达每一个从站的时间必然是不同的,当系统很庞大的时候,每个从站接收到数据帧的时间就会相差很大,越在后边的从站接收到数据帧的时间就越晚,它的同步效果就越差。 5、手表举例:10个人10个表,10个人开始对表,从0开始计时,当上一个人完成了对表计时之后,才通知到下一个人进行对表计时,依次传递,那么到最后的第10个人它开始计时的时间是最晚的。完成对时之后,如果公司要求是10点上班,每个人按照自己手表上的10点到达,那么第10个人一定是最晚的。
3.2.3 DC-Synchronous 简单
从站的过程数据处理,由基于分布时钟和系统时间的硬件中断触发
1、Sync0 Event和SM Event事件信号是类似的,他们都是一个中断事件信号,对于一个从站而言,如果中断信号触发了而且中断屏蔽寄存器没有屏蔽掉该中断信号,那么从站就会进入到中断服务例程 2、中断服务例程就是说从站从主循环中跳出,暂停并保留主程序状态,然后执行中断服务例程这一部分内容,比如说周期性数据的输出,周期性数据的实时性比较强,那么中断信号可以满足这种比较强的实时性需求,不会因为其他信号来耽误它的操作,其它的中断信号会进入等待状态直到前一个中断信号被恢复,即进入到恢复现场阶段. 3、不同于SM Event的地方是Sync0 Event是根据我们自己设定的延时时间触发而不是帧到达时候才触发 4、注意,虽然简单DC同步机制中没有用到SM Event事件信号,但是它依旧是存在的,因为只要当Frame帧到达从站的时候就会触发SM Event事件信号,这里只是不用到该信号来进行触发而已,(但是应该明白不管是哪一种同步机制,SM Event都是存在的,当从站接受到数据帧的时候都会产生SM Event事件信号)
3.2.4 DC-Synchronous 优化
1、优化的DC模式同时使用了SM Event事件信号和Sync0 Event事件信号,而简单DC同步机制只是使用Sync0 Event这一种事件信号进行同步,无论使用哪一种同步模式,只要当数据帧Frame到达从站的时候都会触发对应的SM Event事件信号。 2、在这种优化的DC模式中,当对应的SM Event事件信号触发后,从站会进入到中断服务例程进行数据的处理,把数据帧Frame中对应的所需数据进行计算,然后复制到管理器通道对应的用户区域,等待Sync0 Event同步信号触发之后让从站取走,然后SM Event中断完成,恢复现场,然后等待Sync0 Event信号的触发到来,也就是同步信号的触发,可以看到由于之前SM Event中断中已经完成了前期数据的处理,当Sync0 Event同步信号触发时,程序进入到中断服务例程,就只需要很短的一段Output Delay Time,马上就进入到Output Valid(输出有效)状态。 3、这种优化版的DC同步机制相对于之前的简单DC模式,优点就是Output Delay Time输出延时没有那么长了,主要是因为第二种优化的DC模式利用SM Event事件信号的触发完成了前期从数据帧中对应从站数据的计算和复制到管理器通道上,所以当Sync0 Event同步信号触发后,只需要从管理器通道取走已经计算并复制好了的数据,进入到输出有效Output Valid的延时时间自然就比较短了,而第一种简单的DC模式只是使用到了Sync0 Event同步信号,所以当Sync0 Event触发后,需要在Output Delay Time时间内完成数据的复制和计算,所以简单DC模式下的Output Delay Time延时时间就会比较长。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pYHXkibk-1666254718808)(/image/sync3.png)] 1、当数据帧Frame依次到达每一个从站的时候,EtherCAT的机制会触发SM Event事件信号,(数据帧依次发送到各个从站的时间是一个慢慢变长的延时时间,这是硬件上必然发生无法改善的事情)。 2、然后从站会把数据帧上从站需要的数据进行计算并复制到同步管理器通道上,然后会进行中断事件恢复, 3、主程序等待一个DC Sync Signal同步事件中断信号(也就是Sync0 Event),这个Sync0 Event同步事件信号是所以从站同时触发的,就像规定的9点上班一样<这里的前提是主站已经通过发送帧对各个从站完成了对表计时步骤,各个从站的时间是同步一致的>。 4、当Sync0 Event事件信号同步触发时候,由于之前已经完成了数据的计算和复制,那么此时的输出抖动会非常小,约为15ns,(输出抖动jitter和主站有关,在SM Event事件之前由主站触发),同时同步性能也会非常好(Output Delay time会很短,Output Valid输出有效很快触发)。 5、在Output Valid输出有效触发之后,从站会等待一个Input Latch信号,它可以是Sync1 Event事件信号,也可以是Sync0 Event事件信号触发后一段固定的延时时间,这取决于我们在TwinCAT上的设置情况。
3.3 DC同步丢帧
1、问题描述:在使用DC模式的时候会出现一种同步丢帧的情况,就是说数据帧在到达尾端从站之前,所有从站的Sync0 Event同步事件信号就已经触发了,也就是说数据帧传输的太慢,可能还来不及到达尾端从站,但是它的同步事件信号已经触发了,而此时从站却从管理器通道上获取不到数据帧中对应的数据,从站就会判断数据帧丢失了,这就是同步丢帧的问题。 这种问题一般在考前的从站中发生较少,当一个系统较大时,尾端的从站接收到数据帧的时间也比较晚,因为存在物理传输时间,所以越后面的从站接收到的数据帧时间就越晚,虽然Sync0 Event事件信号在完成DC对时之后是同步触发的,但是数据帧的传递时间是依次递增的,如果我们刚开始留出的偏移时间(shift time)不够大的话就有可能在尾端从站发生同步丢帧的情况 2、解决方案:把第一个从站和主站之间的偏移时间调大,可以在TwinCAT中对Shift Time进行调整。(由于Sync0 Event是同步触发的,只需要设置好第一个从站和主站之间的Shift time,后面的从站和主站的Shift Time也都是这个值)。
3.4 同步模式细分
3.5 对象0x1c32
![在这里插入图片描述](https://img-blog.csdnimg.cn/a300acefc7e3492990330d520b7e65fb.png#pic_cente
同步管理器2 0x1c32 0x1c32 output ox1c33 input
0x1c32:01h
{
rw
uint16
value:0,1,2,3
}
0x1c32:02h
{
ro
uint32
value:ns
}
0x1c32:03h
{
ro/rw
uint32
value:ns
}
0x1c32:04h
{
ro
uint16
value
}
0x1c32:05h
{
ro
uint32
value
}
0x1c32:06h
{
ro
uint32
}
0x1c32:07h
{
ro
uint32
}
0x1c32:08h
{
ro
}
0x1c32:09h
{
ro
uint32
}
0x1c32:10h
{
ro
uint32
}
3.6 igh ethercat官方例程的说明
-
dc_user 以主站为参考时钟,实现方式是:ecrt_master_sync_reference_clock 这个函数将最近一次从ecrt_master_application_time 传入的时间戳发送给参考时钟 ecrt_master_sync_slave_clocks 这个函数的作用是将参考时钟发送给所有从站,以实现dc时钟同步 -
rtai_rtdm_dc 以从站为参考时钟
二.以主站时钟为参考
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/mman.h>
#include "ecrt.h"
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <math.h>
#include <iostream>
extern "C"
{
#include "ipipe_64.h"
}
#define pi 3.14159265
#define N 1500
#define M 3
#define CLOCK_TO_USE CLOCK_MONOTONIC
static unsigned int cycle_ns = 8000000;
static ec_master_t *master = NULL;
static ec_master_state_t master_state = {};
static ec_domain_t *domain1 = NULL;
static ec_domain_state_t domain1_state = {};
static ec_slave_config_t *sc[7] = {};
static ec_slave_config_state_t sc_state[7];
static ec_slave_config_state_t sc_zero_state = {};
static ec_slave_config_t *sc_5 = NULL;
static uint8_t *domain1_pd = NULL;
static ec_slave_config_t *sc_dig_out_01 = NULL;
#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;
#if SYNC_MASTER_TO_REF
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;
#endif
static int64_t system_time_base = 0LL;
static uint64_t wakeup_time = 0LL;
static uint64_t overruns = 0LL;
static unsigned int off_dig_out0 = 0;
#define PANASONIC_6 0,6
#define PANASONIC_5 0,5
#define PANASONIC_4 0,4
#define PANASONIC_3 0,3
#define PANASONIC_2 0,2
#define PANASONIC_1 0,1
#define PANASONIC_0 0,0
#define num_ 7
uint16_t a[7]={0};
uint16_t p[7]={0,1,2,3,4,5,6};
#define VID_PID 0x000000ab,0x00001030
float t;
static struct{
unsigned int operation_mode[7];
unsigned int ctrl_word[7];
unsigned int target_velocity[7];
unsigned int target_position[7];
unsigned int status_word[7];
unsigned int current_velocity[7];
unsigned int current_position[7];
unsigned int data_input[7];
unsigned int data_input1[7];
unsigned int mode_operation[7];
unsigned int error_code[7];
unsigned int profile_velocity[7];
unsigned int profile_acceleration[7];
unsigned int profile_deccleration[7];
unsigned int target_turque[7];
unsigned int current_turque[7];
unsigned int max_acceleration[7];
unsigned int max_deccleration[7];
unsigned int max_velocity[7];
unsigned int current_taque[7];
unsigned int dangqian_dianliu[7];
unsigned int current_dianliu[7];
unsigned int target_taque[7];
}offset;
const static ec_pdo_entry_reg_t domain1_regs[] = {
{PANASONIC_0, VID_PID, 0x6040, 0, &offset.ctrl_word[0]},
{PANASONIC_0, VID_PID, 0x6060, 0, &offset.operation_mode[0] },
{PANASONIC_0, VID_PID, 0x607A, 0, &offset.target_position[0]},
{PANASONIC_0, VID_PID, 0x6041, 0, &offset.status_word[0]},
{PANASONIC_0, VID_PID, 0x6064, 0, &offset.current_position[0]},
{PANASONIC_0, VID_PID, 0x606C, 0, &offset.current_velocity[0]},
{PANASONIC_0, VID_PID, 0x6061, 0, &offset.mode_operation[0]},
{PANASONIC_0, VID_PID, 0x603F, 0, &offset.error_code[0]},
{PANASONIC_1, VID_PID, 0x6040, 0, &offset.ctrl_word[1]},
{PANASONIC_1, VID_PID, 0x6060, 0, &offset.operation_mode[1] },
{PANASONIC_1, VID_PID, 0x607A, 0, &offset.target_position[1]},
{PANASONIC_1, VID_PID, 0x6041, 0, &offset.status_word[1]},
{PANASONIC_1, VID_PID, 0x6064, 0, &offset.current_position[1]},
{PANASONIC_1, VID_PID, 0x606C, 0, &offset.current_velocity[1]},
{PANASONIC_1, VID_PID, 0x6061, 0, &offset.mode_operation[1]},
{PANASONIC_1, VID_PID, 0x603F, 0, &offset.error_code[1]},
{PANASONIC_2, VID_PID, 0x6040, 0, &offset.ctrl_word[2]},
{PANASONIC_2, VID_PID, 0x6060, 0, &offset.operation_mode[2] },
{PANASONIC_2, VID_PID, 0x607A, 0, &offset.target_position[2]},
{PANASONIC_2, VID_PID, 0x6041, 0, &offset.status_word[2]},
{PANASONIC_2, VID_PID, 0x6064, 0, &offset.current_position[2]},
{PANASONIC_2, VID_PID, 0x606C, 0, &offset.current_velocity[2]},
{PANASONIC_2, VID_PID, 0x6061, 0, &offset.mode_operation[2]},
{PANASONIC_2, VID_PID, 0x603F, 0, &offset.error_code[2]},
{PANASONIC_3, VID_PID, 0x6040, 0, &offset.ctrl_word[3]},
{PANASONIC_3, VID_PID, 0x6060, 0, &offset.operation_mode[3] },
{PANASONIC_3, VID_PID, 0x607A, 0, &offset.target_position[3]},
{PANASONIC_3, VID_PID, 0x6041, 0, &offset.status_word[3]},
{PANASONIC_3, VID_PID, 0x6064, 0, &offset.current_position[3]},
{PANASONIC_3, VID_PID, 0x606C, 0, &offset.current_velocity[3]},
{PANASONIC_3, VID_PID, 0x6061, 0, &offset.mode_operation[3]},
{PANASONIC_3, VID_PID, 0x603F, 0, &offset.error_code[3]},
{PANASONIC_4, VID_PID, 0x6040, 0, &offset.ctrl_word[4]},
{PANASONIC_4, VID_PID, 0x6060, 0, &offset.operation_mode[4] },
{PANASONIC_4, VID_PID, 0x607A, 0, &offset.target_position[4]},
{PANASONIC_4, VID_PID, 0x6041, 0, &offset.status_word[4]},
{PANASONIC_4, VID_PID, 0x6064, 0, &offset.current_position[4]},
{PANASONIC_4, VID_PID, 0x606C, 0, &offset.current_velocity[4]},
{PANASONIC_4, VID_PID, 0x6061, 0, &offset.mode_operation[4]},
{PANASONIC_4, VID_PID, 0x603F, 0, &offset.error_code[4]},
{PANASONIC_5, VID_PID, 0x6040, 0, &offset.ctrl_word[5]},
{PANASONIC_5, VID_PID, 0x6060, 0, &offset.operation_mode[5]},
{PANASONIC_5, VID_PID, 0x607A, 0, &offset.target_position[5]},
{PANASONIC_5, VID_PID, 0x6041, 0, &offset.status_word[5]},
{PANASONIC_5, VID_PID, 0x6064, 0, &offset.current_position[5]},
{PANASONIC_5, VID_PID, 0x606C, 0, &offset.current_velocity[5]},
{PANASONIC_5, VID_PID, 0x6061, 0, &offset.mode_operation[5]},
{PANASONIC_5, VID_PID, 0x603F, 0, &offset.error_code[5]},
{PANASONIC_6, VID_PID, 0x6040, 0, &offset.ctrl_word[6]},
{PANASONIC_6, VID_PID, 0x6060, 0, &offset.operation_mode[6] },
{PANASONIC_6, VID_PID, 0x607A, 0, &offset.target_position[6]},
{PANASONIC_6, VID_PID, 0x6041, 0, &offset.status_word[6]},
{PANASONIC_6, VID_PID, 0x6064, 0, &offset.current_position[6]},
{PANASONIC_6, VID_PID, 0x606C, 0, &offset.current_velocity[6]},
{PANASONIC_6, VID_PID, 0x6061, 0, &offset.mode_operation[6]},
{PANASONIC_6, VID_PID, 0x603F, 0, &offset.error_code[6]},
{}
};
static ec_pdo_entry_info_t device_pdo_entries[] = {
{0x6040, 0x00, 16},
{0x6060, 0x00, 8 },
{0x607A, 0x00, 32},
{0x6041, 0x00, 16},
{0x6064, 0x00, 32},
{0x606C, 0x00, 32},
{0x6061, 0x00, 8 },
{0x603F, 0x00, 16},
};
static ec_pdo_info_t device_pdos[] = {
{0x1600, 3, device_pdo_entries + 0 },
{0x1A00, 5, device_pdo_entries + 3 }
};
static ec_sync_info_t device_syncs[] = {
{ 0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE },
{ 1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE },
{ 2, EC_DIR_OUTPUT, 1, device_pdos + 0, EC_WD_DISABLE },
{ 3, EC_DIR_INPUT, 1, device_pdos + 1, EC_WD_DISABLE },
{ 0xFF}
};
uint64_t system_time_ns(void)
{
RTIME time = rt_get_time_ns();
if (system_time_base > time) {
return time;
}
else {
return time - system_time_base;
}
}
RTIME system2count(uint64_t time)
{
RTIME ret;
if ((system_time_base < 0) &&
((uint64_t) (-system_time_base) > time)) {
ret = time;
}
else {
ret = time + system_time_base;
}
return nano2count(ret);
}
#define FREQUENCY 125
#define NSEC_PER_SEC (1000000000L)
#define PERIOD_NS (NSEC_PER_SEC / FREQUENCY)
const struct timespec cycletime = {0, PERIOD_NS};
static unsigned int sync_ref_counter = 0;
#define DIFF_NS(A, B) (((B).tv_sec - (A).tv_sec) * NSEC_PER_SEC + \
(B).tv_nsec - (A).tv_nsec)
#define TIMESPEC2NS(T) ((uint64_t) (T).tv_sec * NSEC_PER_SEC + (T).tv_nsec)
struct timespec timespec_add(struct timespec time1, struct timespec time2)
{
struct timespec result;
if ((time1.tv_nsec + time2.tv_nsec) >= NSEC_PER_SEC) {
result.tv_sec = time1.tv_sec + time2.tv_sec + 1;
result.tv_nsec = time1.tv_nsec + time2.tv_nsec - NSEC_PER_SEC;
} else {
result.tv_sec = time1.tv_sec + time2.tv_sec;
result.tv_nsec = time1.tv_nsec + time2.tv_nsec;
}
return result;
}
void sync_distributed_clocks(void)
{
#if SYNC_MASTER_TO_REF
uint32_t ref_time = 0;
uint64_t prev_app_time = dc_time_ns;
#endif
dc_time_ns = system_time_ns();
#if SYNC_MASTER_TO_REF
ecrt_master_reference_clock_time(master, &ref_time);
dc_diff_ns = (uint32_t) prev_app_time - ref_time;
#else
ecrt_master_sync_reference_clock_to(master, dc_time_ns);
#endif
ecrt_master_sync_slave_clocks(master);
}
#define sign(val) \
({ typeof (val) _val = (val); \
((_val > 0) - (_val < 0)); })
void update_master_clock(void)
{
#if SYNC_MASTER_TO_REF
int32_t delta = dc_diff_ns - prev_dc_diff_ns;
prev_dc_diff_ns = dc_diff_ns;
dc_diff_ns =
((dc_diff_ns + (cycle_ns / 2)) % cycle_ns) - (cycle_ns / 2);
if (dc_started) {
dc_diff_total_ns += dc_diff_ns;
dc_delta_total_ns += delta;
dc_filter_idx++;
if (dc_filter_idx >= DC_FILTER_CNT) {
dc_adjust_ns +=
((dc_delta_total_ns + (DC_FILTER_CNT / 2)) / DC_FILTER_CNT);
dc_adjust_ns += sign(dc_diff_total_ns / DC_FILTER_CNT);
if (dc_adjust_ns < -1000) {
dc_adjust_ns = -1000;
}
if (dc_adjust_ns > 1000) {
dc_adjust_ns = 1000;
}
dc_diff_total_ns = 0LL;
dc_delta_total_ns = 0LL;
dc_filter_idx = 0;
}
system_time_base += dc_adjust_ns + sign(dc_diff_ns);
}
else {
dc_started = (dc_diff_ns != 0);
if (dc_started) {
dc_start_time_ns = dc_time_ns;
}
}
#endif
}
void rt_check_domain_state(void)
{
ec_domain_state_t ds = {};
ecrt_domain_state(domain1, &ds);
if (ds.working_counter != domain1_state.working_counter) {
printf("Domain1: WC %u.\n", ds.working_counter);
}
if (ds.wc_state != domain1_state.wc_state) {
printf("Domain1: State %u.\n", ds.wc_state);
}
domain1_state = ds;
}
void rt_check_master_state(void)
{
ec_master_state_t ms;
ecrt_master_state(master, &ms);
printf("-----------------------------------------\n");
printf("%u slave(s).\n", ms.slaves_responding);
printf("master AL states: 0x%02X.\n", ms.al_states);
printf("Link is %s.\n", ms.link_up ? "up" : "down");
master_state = ms;
}
void check_slave_config_states(ec_slave_config_t *sc, int i)
{
ec_slave_config_state_t s;
ecrt_slave_config_state(sc, &s);
printf("slave[%d]: State 0x%02X.\n", i, s.al_state);
printf("slave[%d]: %s.\n", i, s.online ? "online" : "offline");
printf("slave[%d]: %soperational.\n", i, s.operational ? "" : "Not ");
sc_state[i] = s;
}
void wait_period(void)
{
while (1)
{
RTIME wakeup_count = system2count(wakeup_time);
RTIME current_count = rt_get_time();
if ((wakeup_count < current_count)
|| (wakeup_count > current_count + (50 * cycle_ns))) {
}
struct timespec wakeupTime;
wakeupTime.tv_sec= cycle_ns / NSEC_PER_SEC;
std::cout<< "wakeupTime.tv_sec: " <<wakeupTime.tv_sec<<std::endl;
wakeupTime.tv_nsec= cycle_ns % NSEC_PER_SEC;
int s =clock_nanosleep(CLOCK_TO_USE, 0, &wakeupTime, NULL);
if (s != 0) {
if (s == EINTR)
printf("Interrupted by signal handler\n");
else
printf("clock_nanosleep:errno=%d\n",s);
}
break;
}
ecrt_master_application_time(master, wakeup_time);
wakeup_time += cycle_ns;
}
int32_t current_pos[7], target_pos[7];
int16_t error_co[7];
int32_t follow_err[7];
bool clear_err_zero = true;
bool _release_ = false;
int32_t current_pos_5, target_pos_5;
int16_t error_co_5;
int32_t follow_err_5;
bool state_ok = false;
int slave_op_num = 0;
void my_cyclic(void)
{
static uint16_t command[7] = {0x004F,0x004F,0x004F,0x004F,0x004F,0x004F,0x004F};
int cycle_counter = 0;
unsigned int blink = 0;
uint16_t status[7];
uint32_t target_position = 0;
int once=0;
uint32_t positions[N];
int i,dir=0;
wakeup_time = system_time_ns() + 10 * cycle_ns;
bool kk=0;
int k1 = 10000;
struct timespec wakeupTime, time;
clock_gettime(CLOCK_TO_USE, &wakeupTime);
while (1)
{
wakeupTime = timespec_add(wakeupTime, cycletime);
clock_nanosleep(CLOCK_TO_USE, TIMER_ABSTIME, &wakeupTime, NULL);
ecrt_master_application_time(master, TIMESPEC2NS(wakeupTime));
ecrt_master_receive(master);
ecrt_domain_process(domain1);
cycle_counter++;
if(!(cycle_counter % 500))
{
cycle_counter = 0;
rt_check_domain_state();
rt_check_master_state();
for(int i = 0; i < num_; i++)
{
check_slave_config_states(sc[i], i);
printf("*****```*****\n");
}
}
if(master_state.al_states == 8 && sc_state[0].al_state == 8 && sc_state[1].al_state == 8
&& sc_state[2].al_state == 8 && sc_state[3].al_state == 8
&& sc_state[4].al_state == 8 && sc_state[5].al_state == 8
&& sc_state[6].al_state == 8)
{
state_ok = true;
}
if(state_ok == true)
{
for(int i = 0; i < num_; i++)
{
current_pos[i] = EC_READ_S32(domain1_pd + offset.current_position[i]);
status[i] = EC_READ_U16(domain1_pd + offset.status_word[i]);
error_co[i] = EC_READ_S16(domain1_pd + offset.error_code[i]);
}
for (int i = 0; i < num_; i++)
{
if( (status[i] & command[i]) == 0x0040 )
{
EC_WRITE_U16(domain1_pd + offset.ctrl_word[i], 0x0006 );
current_pos[i]=EC_READ_S32(domain1_pd+offset.current_position[i]);
EC_WRITE_S32(domain1_pd+offset.target_position[i],current_pos[i]);
target_pos[i] = current_pos[i];
EC_WRITE_S8(domain1_pd + offset.operation_mode[i], 8);
command[i] = 0x006F;
}
else if( (status[i] & command[i]) == 0x0021)
{
EC_WRITE_U16(domain1_pd + offset.ctrl_word[i], 0x0007 );
EC_WRITE_S32(domain1_pd+offset.target_position[i],current_pos[i]);
command[i] = 0x006F;
printf("command[%d] = 0x0007", i);
}
else if( (status[i] & command[i]) == 0x0023)
{
EC_WRITE_U16(domain1_pd + offset.ctrl_word[i], 0x000f );
command[i] = 0x006F;
}
}
for(int i = 0; i < num_; i++)
{
if( (status[i] & command[i]) == 0x0027)
{
}
}
}
if(_release_)
{
k1--;
printf("Release time k1 = %d\n", k1);
for(int i = 0; i < num_; i++)
{
break;
}
if(k1 == 0)
{
break;
}
break;
}
if (sync_ref_counter) {
sync_ref_counter--;
} else {
sync_ref_counter = 1;
clock_gettime(CLOCK_TO_USE, &time);
ecrt_master_sync_reference_clock_to(master, TIMESPEC2NS(time));
}
ecrt_master_sync_slave_clocks(master);
ecrt_domain_queue(domain1);
ecrt_master_send(master);
}
}
void signal_handler(int sig)
{
printf("get the ctrl-c signal : %d\n\n", sig);
_release_ = true;
usleep(1000000);
}
int main(int argc, char *argv[])
{
int ret;
int i = 0;
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1)
{
perror("mlockall failed");
return -1;
}
printf("Requesting master...\n");
master = ecrt_request_master(0);
if (!master) {
return -1;
}
domain1 = ecrt_master_create_domain(master);
if (!domain1) {
return -1;
}
printf("Creating slave configurations...\n");
for(i = 0; i <num_; i++ )
{
if (!(sc[i] = ecrt_master_slave_config(master, a[i], p[i], VID_PID)))
{
fprintf(stderr, "Failed to get slave %d configuration.\n", i);
return -1;
}
if (ecrt_slave_config_pdos(sc[i], EC_END, device_syncs))
{
fprintf(stderr, "Failed to configure slave %d PDOs.\n", i);
return -1;
}
}
if (ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs)) {
fprintf(stderr, "slave PDO entry registration failed!\n");
return -1;
}
dc_start_time_ns = system_time_ns();
dc_time_ns = dc_start_time_ns;
for(i = 0; i < num_; i++)
{
ecrt_slave_config_sdo16(sc[i], 0x1c32, 1, 2);
ecrt_slave_config_sdo16(sc[i], 0x1c33, 1, 2);
ecrt_slave_config_dc(sc[i], 0x0300, cycle_ns, cycle_ns/4, 0, 0);
}
printf("Activating master...\n");
if (ecrt_master_activate(master)) {
return -1;
}
if (!(domain1_pd = ecrt_domain_data(domain1))) {
fprintf(stderr, "Failed to get domain data pointer.\n");
return -1;
}
struct sched_param param;
param.sched_priority = 98;
printf("Using priority %i.\n", param.sched_priority);
if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
puts("ERROR IN SETTING THE SCHEDULER");
perror("errno");
return -1;
}
my_cyclic();
printf("End of Program\n");
ecrt_release_master(master);
return 0;
}
以主站为参考时钟,会出现
lm:~$sudo dmesg
[280374.468550] r8169 0000:04:00.1 enp4s0f1: Link is Down
[280374.468991] EtherCAT 0: Link state of ecm0 changed to DOWN.
[281905.622543] r8169 0000:04:00.1 enp4s0f1: Link is Up - 100Mbps/Full - flow control rx/tx
[281905.624451] EtherCAT 0: Link state of ecm0 changed to UP.
[281905.626454] EtherCAT 0: 7 slave(s) responding on main device.
[281905.626458] EtherCAT 0: Slave states on main device: PREOP.
[281905.626611] EtherCAT 0: Scanning bus.
[281905.736375] EtherCAT WARNING 0: 27 datagrams TIMED OUT!
[281905.736380] EtherCAT WARNING 0: 199 datagrams UNMATCHED!
[281906.004788] EtherCAT 0: Bus scanning completed in 378 ms.
[281906.004792] EtherCAT 0: Using slave 0 as DC reference clock.
[281907.460478] EtherCAT WARNING 0: 7 datagrams TIMED OUT!
[281907.460483] EtherCAT WARNING 0: 7 datagrams UNMATCHED!
[281908.828153] EtherCAT WARNING 0: 3 datagrams TIMED OUT!
[281908.828158] EtherCAT WARNING 0: 3 datagrams UNMATCHED!
[281912.095492] EtherCAT WARNING 0: 2 datagrams TIMED OUT!
[281912.095496] EtherCAT WARNING 0: 2 datagrams UNMATCHED!
[281916.932601] EtherCAT WARNING 0: 2 datagrams TIMED OUT!
[281916.932615] EtherCAT WARNING 0: 2 datagrams UNMATCHED!
[281920.965582] EtherCAT WARNING 0: 2 datagrams TIMED OUT!
[281920.965596] EtherCAT WARNING 0: 2 datagrams UNMATCHED!
[281922.539623] EtherCAT WARNING 0: 1 datagram TIMED OUT!
[281922.539637] EtherCAT WARNING 0: 1 datagram UNMATCHED!
[281923.890630] EtherCAT WARNING 0: 6 datagrams TIMED OUT!
[281923.890644] EtherCAT WARNING 0: 6 datagrams UNMATCHED!
[281927.266652] EtherCAT WARNING 0: 2 datagrams TIMED OUT!
[281927.266657] EtherCAT WARNING 0: 2 datagrams UNMATCHED!
[281936.043685] EtherCAT WARNING 0: 2 datagrams TIMED OUT!
[281936.043690] EtherCAT WARNING 0: 2 datagrams UNMATCHED!
[281936.670449] EtherCAT: Requesting master 0...
[281936.670452] EtherCAT: Successfully requested master 0.
[281936.670729] EtherCAT 0: Domain0: Logical address 0x00000000, 140 byte, expected working counter 21.
[281936.670732] EtherCAT 0: Datagram domain0-0-main: Logical offset 0x00000000, 140 byte, type LRW at 00000000d781fa5f.
[281936.670772] EtherCAT 0: Master thread exited.
[281936.670775] EtherCAT 0: Starting EtherCAT-OP thread.
[281937.043712] EtherCAT WARNING 0: 1 datagram TIMED OUT!
[281937.043717] EtherCAT WARNING 0: 2 datagrams UNMATCHED!
[281942.822919] EtherCAT WARNING 0-0: Slave did not sync after 5008 ms.
[281942.878914] EtherCAT 0: Domain 0: Working counter changed to 2/21.
[281943.886923] EtherCAT 0: Domain 0: 3 working counter changes - now 6/21.
[281949.630973] EtherCAT WARNING 0-2: Slave did not sync after 5000 ms.
[281949.670970] EtherCAT 0: Domain 0: Working counter changed to 8/21.
[281950.678980] EtherCAT 0: Domain 0: Working counter changed to 9/21.
[281952.717962] EtherCAT 0: Domain 0: Working counter changed to 0/21.
[281952.806997] EtherCAT WARNING: Datagram 00000000d781fa5f (domain0-0-main) was SKIPPED 7 times.
[281953.046809] EtherCAT WARNING 0: 17 datagrams UNMATCHED!
[281953.719003] EtherCAT 0: Domain 0: Working counter changed to 9/21.
[281955.359033] EtherCAT WARNING 0-3: Slave did not sync after 5000 ms.
[281955.399037] EtherCAT 0: Domain 0: Working counter changed to 11/21.
[281956.407019] EtherCAT 0: Domain 0: Working counter changed to 12/21.
[281961.495061] EtherCAT WARNING 0-4: Slave did not sync after 5000 ms.
[281961.551058] EtherCAT 0: Domain 0: Working counter changed to 15/21.
[281962.559076] EtherCAT 0: Domain 0: Working counter changed to 18/21.
[281968.271133] EtherCAT WARNING 0-6: Slave did not sync after 5000 ms.
[281968.311135] EtherCAT 0: Domain 0: Working counter changed to 21/21.
[281968.337007] EtherCAT 0: Slave states on main device: OP.
超时5000ms,即从站同步较慢 后续没有出现同步错误的提示 该方法的主要内容:
- 配置从站的dc方式,0x0300是从站DC的激活字,可以从电机厂家给的XML文件的
<dc> 标签得到 ecrt_slave_config_dc(sc[i], 0x0300, cycle_ns, cycle_ns/4, 0, 0);//8ms 激活字对应的是从站的0x980和0x981寄存器的内容
0x0300对应寄存器0x980 = 0x00,0x981 = 0x03,即SYNC输出单元控制和激活sync0. 0x0700对应寄存器0x980 = 0x00,0x981 = 0x07,即SYNC输出单元控制和激活sync0、sync1. 这里需要根据从站来决定 2. 将主站时钟同步到参考时钟,该函数将主站的时钟同步到参考时钟(注意,此时参考时钟依然是默认的第一个带dc的slave,但是该时钟的数值被主站修改了,这样间接的实现以主站为参考时钟) ecrt_master_sync_reference_clock_to(master, TIMESPEC2NS(time));//将主站时钟同步到参考时钟 ecrt_master_sync_slave_clocks(master);//将参考时钟发送给所有的从站,以实现dc同步
三.以从站为参考时钟
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/mman.h>
#include "ecrt.h"
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <math.h>
#include <iostream>
extern "C"
{
#include "ipipe_64.h"
}
#define pi 3.14159265
#define N 1500
#define M 3
#define CLOCK_TO_USE CLOCK_MONOTONIC
#define NSEC_PER_SEC 1000000000
static unsigned int cycle_ns = 8000000;
static ec_master_t *master = NULL;
static ec_master_state_t master_state = {};
static ec_domain_t *domain1 = NULL;
static ec_domain_state_t domain1_state = {};
static ec_slave_config_t *sc[7] = {};
static ec_slave_config_state_t sc_state[7];
static ec_slave_config_state_t sc_zero_state = {};
static ec_slave_config_t *sc_5 = NULL;
static uint8_t *domain1_pd = NULL;
static ec_slave_config_t *sc_dig_out_01 = NULL;
#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;
#if SYNC_MASTER_TO_REF
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;
#endif
static int64_t system_time_base = 0LL;
static uint64_t wakeup_time = 0LL;
static uint64_t overruns = 0LL;
static unsigned int off_dig_out0 = 0;
#define PANASONIC_6 0,6
#define PANASONIC_5 0,5
#define PANASONIC_4 0,4
#define PANASONIC_3 0,3
#define PANASONIC_2 0,2
#define PANASONIC_1 0,1
#define PANASONIC_0 0,0
#define num_ 7
uint16_t a[7]={0};
uint16_t p[7]={0,1,2,3,4,5,6};
#define VID_PID 0x000000ab,0x00001030
float t;
static struct{
unsigned int operation_mode[7];
unsigned int ctrl_word[7];
unsigned int target_velocity[7];
unsigned int target_position[7];
unsigned int status_word[7];
unsigned int current_velocity[7];
unsigned int current_position[7];
unsigned int data_input[7];
unsigned int data_input1[7];
unsigned int mode_operation[7];
unsigned int error_code[7];
unsigned int profile_velocity[7];
unsigned int profile_acceleration[7];
unsigned int profile_deccleration[7];
unsigned int target_turque[7];
unsigned int current_turque[7];
unsigned int max_acceleration[7];
unsigned int max_deccleration[7];
unsigned int max_velocity[7];
unsigned int current_taque[7];
unsigned int dangqian_dianliu[7];
unsigned int current_dianliu[7];
unsigned int target_taque[7];
}offset;
const static ec_pdo_entry_reg_t domain1_regs[] = {
{PANASONIC_0, VID_PID, 0x6040, 0, &offset.ctrl_word[0]},
{PANASONIC_0, VID_PID, 0x6060, 0, &offset.operation_mode[0] },
{PANASONIC_0, VID_PID, 0x607A, 0, &offset.target_position[0]},
{PANASONIC_0, VID_PID, 0x6041, 0, &offset.status_word[0]},
{PANASONIC_0, VID_PID, 0x6064, 0, &offset.current_position[0]},
{PANASONIC_0, VID_PID, 0x606C, 0, &offset.current_velocity[0]},
{PANASONIC_0, VID_PID, 0x6061, 0, &offset.mode_operation[0]},
{PANASONIC_0, VID_PID, 0x603F, 0, &offset.error_code[0]},
{PANASONIC_1, VID_PID, 0x6040, 0, &offset.ctrl_word[1]},
{PANASONIC_1, VID_PID, 0x6060, 0, &offset.operation_mode[1] },
{PANASONIC_1, VID_PID, 0x607A, 0, &offset.target_position[1]},
{PANASONIC_1, VID_PID, 0x6041, 0, &offset.status_word[1]},
{PANASONIC_1, VID_PID, 0x6064, 0, &offset.current_position[1]},
{PANASONIC_1, VID_PID, 0x606C, 0, &offset.current_velocity[1]},
{PANASONIC_1, VID_PID, 0x6061, 0, &offset.mode_operation[1]},
{PANASONIC_1, VID_PID, 0x603F, 0, &offset.error_code[1]},
{PANASONIC_2, VID_PID, 0x6040, 0, &offset.ctrl_word[2]},
{PANASONIC_2, VID_PID, 0x6060, 0, &offset.operation_mode[2] },
{PANASONIC_2, VID_PID, 0x607A, 0, &offset.target_position[2]},
{PANASONIC_2, VID_PID, 0x6041, 0, &offset.status_word[2]},
{PANASONIC_2, VID_PID, 0x6064, 0, &offset.current_position[2]},
{PANASONIC_2, VID_PID, 0x606C, 0, &offset.current_velocity[2]},
{PANASONIC_2, VID_PID, 0x6061, 0, &offset.mode_operation[2]},
{PANASONIC_2, VID_PID, 0x603F, 0, &offset.error_code[2]},
{PANASONIC_3, VID_PID, 0x6040, 0, &offset.ctrl_word[3]},
{PANASONIC_3, VID_PID, 0x6060, 0, &offset.operation_mode[3] },
{PANASONIC_3, VID_PID, 0x607A, 0, &offset.target_position[3]},
{PANASONIC_3, VID_PID, 0x6041, 0, &offset.status_word[3]},
{PANASONIC_3, VID_PID, 0x6064, 0, &offset.current_position[3]},
{PANASONIC_3, VID_PID, 0x606C, 0, &offset.current_velocity[3]},
{PANASONIC_3, VID_PID, 0x6061, 0, &offset.mode_operation[3]},
{PANASONIC_3, VID_PID, 0x603F, 0, &offset.error_code[3]},
{PANASONIC_4, VID_PID, 0x6040, 0, &offset.ctrl_word[4]},
{PANASONIC_4, VID_PID, 0x6060, 0, &offset.operation_mode[4] },
{PANASONIC_4, VID_PID, 0x607A, 0, &offset.target_position[4]},
{PANASONIC_4, VID_PID, 0x6041, 0, &offset.status_word[4]},
{PANASONIC_4, VID_PID, 0x6064, 0, &offset.current_position[4]},
{PANASONIC_4, VID_PID, 0x606C, 0, &offset.current_velocity[4]},
{PANASONIC_4, VID_PID, 0x6061, 0, &offset.mode_operation[4]},
{PANASONIC_4, VID_PID, 0x603F, 0, &offset.error_code[4]},
{PANASONIC_5, VID_PID, 0x6040, 0, &offset.ctrl_word[5]},
{PANASONIC_5, VID_PID, 0x6060, 0, &offset.operation_mode[5]},
{PANASONIC_5, VID_PID, 0x607A, 0, &offset.target_position[5]},
{PANASONIC_5, VID_PID, 0x6041, 0, &offset.status_word[5]},
{PANASONIC_5, VID_PID, 0x6064, 0, &offset.current_position[5]},
{PANASONIC_5, VID_PID, 0x606C, 0, &offset.current_velocity[5]},
{PANASONIC_5, VID_PID, 0x6061, 0, &offset.mode_operation[5]},
{PANASONIC_5, VID_PID, 0x603F, 0, &offset.error_code[5]},
{PANASONIC_6, VID_PID, 0x6040, 0, &offset.ctrl_word[6]},
{PANASONIC_6, VID_PID, 0x6060, 0, &offset.operation_mode[6] },
{PANASONIC_6, VID_PID, 0x607A, 0, &offset.target_position[6]},
{PANASONIC_6, VID_PID, 0x6041, 0, &offset.status_word[6]},
{PANASONIC_6, VID_PID, 0x6064, 0, &offset.current_position[6]},
{PANASONIC_6, VID_PID, 0x606C, 0, &offset.current_velocity[6]},
{PANASONIC_6, VID_PID, 0x6061, 0, &offset.mode_operation[6]},
{PANASONIC_6, VID_PID, 0x603F, 0, &offset.error_code[6]},
{}
};
static ec_pdo_entry_info_t device_pdo_entries[] = {
{0x6040, 0x00, 16},
{0x6060, 0x00, 8 },
{0x607A, 0x00, 32},
{0x6041, 0x00, 16},
{0x6064, 0x00, 32},
{0x606C, 0x00, 32},
{0x6061, 0x00, 8 },
{0x603F, 0x00, 16},
};
static ec_pdo_info_t device_pdos[] = {
{0x1600, 3, device_pdo_entries + 0 },
{0x1A00, 5, device_pdo_entries + 3 }
};
static ec_sync_info_t device_syncs[] = {
{ 0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE },
{ 1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE },
{ 2, EC_DIR_OUTPUT, 1, device_pdos + 0, EC_WD_DISABLE },
{ 3, EC_DIR_INPUT, 1, device_pdos + 1, EC_WD_DISABLE },
{ 0xFF}
};
#define TIMESPEC2NS(T) ((uint64_t) (T).tv_sec * NSEC_PER_SEC + (T).tv_nsec)
uint64_t system_time_ns(void)
{
RTIME time;
struct timespec time_spc;
clock_gettime(CLOCK_TO_USE, &time_spc);
time = TIMESPEC2NS(time_spc);
if (system_time_base > time) {
return time;
}
else {
return time - system_time_base;
}
}
RTIME system2count(uint64_t time)
{
RTIME ret;
if ((system_time_base < 0) &&
((uint64_t) (-system_time_base) > time)) {
ret = time;
}
else {
ret = time + system_time_base;
printf("\nret:%lld\n",ret);
}
return nano2count(ret);
}
RTIME system2count_change(uint64_t time)
{
RTIME ret;
if ((system_time_base < 0) &&
((uint64_t) (-system_time_base) > time)) {
ret = time;
}
else {
ret = time + system_time_base;
printf("\nret:%lld\n",ret);
}
return ret;
}
void sync_distributed_clocks(void)
{
#if SYNC_MASTER_TO_REF
uint32_t ref_time = 0;
uint64_t prev_app_time = dc_time_ns;
#endif
dc_time_ns = system_time_ns();
#if SYNC_MASTER_TO_REF
ecrt_master_reference_clock_time(master, &ref_time);
printf("ref_time:%d\n", ref_time);
dc_diff_ns = (uint32_t) prev_app_time - ref_time;
printf("dc_diff_ns:%d\n",dc_diff_ns);
#else
ecrt_master_sync_reference_clock_to(master, dc_time_ns);
#endif
ecrt_master_sync_slave_clocks(master);
}
#define sign(val) \
({ typeof (val) _val = (val); \
((_val > 0) - (_val < 0)); })
void update_master_clock(void)
{
#if SYNC_MASTER_TO_REF
int32_t delta = dc_diff_ns - prev_dc_diff_ns;
prev_dc_diff_ns = dc_diff_ns;
dc_diff_ns =
((dc_diff_ns + (cycle_ns / 2)) % cycle_ns) - (cycle_ns / 2);
if (dc_started) {
dc_diff_total_ns += dc_diff_ns;
dc_delta_total_ns += delta;
dc_filter_idx++;
if (dc_filter_idx >= DC_FILTER_CNT) {
dc_adjust_ns +=
((dc_delta_total_ns + (DC_FILTER_CNT / 2)) / DC_FILTER_CNT);
dc_adjust_ns += sign(dc_diff_total_ns / DC_FILTER_CNT);
if (dc_adjust_ns < -(0.001 * cycle_ns)) {
dc_adjust_ns = -0.001 * cycle_ns;
}
if (dc_adjust_ns > (0.001 * cycle_ns)) {
dc_adjust_ns = 0.001 * cycle_ns;
}
dc_diff_total_ns = 0LL;
dc_delta_total_ns = 0LL;
dc_filter_idx = 0;
}
system_time_base += dc_adjust_ns + sign(dc_diff_ns);
}
else {
dc_started = (dc_diff_ns != 0);
if (dc_started) {
dc_start_time_ns = dc_time_ns;
}
}
#endif
}
void rt_check_domain_state(void)
{
ec_domain_state_t ds = {};
ecrt_domain_state(domain1, &ds);
if (ds.working_counter != domain1_state.working_counter) {
printf("Domain1: WC %u.\n", ds.working_counter);
}
if (ds.wc_state != domain1_state.wc_state) {
printf("Domain1: State %u.\n", ds.wc_state);
}
domain1_state = ds;
}
void rt_check_master_state(void)
{
ec_master_state_t ms;
ecrt_master_state(master, &ms);
if (ms.slaves_responding != master_state.slaves_responding) {
printf("%u slave(s).\n", ms.slaves_responding);
}
if (ms.al_states != master_state.al_states) {
printf("AL states: 0x%02X.\n", ms.al_states);
}
if (ms.link_up != master_state.link_up) {
printf("Link is %s.\n", ms.link_up ? "up" : "down");
}
master_state = ms;
}
void check_slave_config_states(ec_slave_config_t *sc, int i)
{
ec_slave_config_state_t s;
ecrt_slave_config_state(sc, &s);
printf("slave[%d]: State 0x%02X.\n", i, s.al_state);
printf("slave[%d]: %s.\n", i, s.online ? "online" : "offline");
printf("slave[%d]: %soperational.\n", i, s.operational ? "" : "Not ");
sc_state[i] = s;
}
struct timespec timespec_add(struct timespec time1, struct timespec time2)
{
struct timespec result;
if ((time1.tv_nsec + time2.tv_nsec) >= NSEC_PER_SEC) {
result.tv_sec = time1.tv_sec + time2.tv_sec + 1;
result.tv_nsec = time1.tv_nsec + time2.tv_nsec - NSEC_PER_SEC;
} else {
result.tv_sec = time1.tv_sec + time2.tv_sec;
result.tv_nsec = time1.tv_nsec + time2.tv_nsec;
}
return result;
}
void wait_period(void)
{
while (1)
{
RTIME wakeup_time_change = system2count_change(wakeup_time);
struct timespec wakeupTime;
wakeupTime.tv_sec= wakeup_time_change / NSEC_PER_SEC;
wakeupTime.tv_nsec= wakeup_time_change % NSEC_PER_SEC;
printf("\nwakeupTime is :%lds,%ldns\n", wakeupTime.tv_sec, wakeupTime.tv_nsec);
int s = clock_nanosleep(CLOCK_TO_USE, TIMER_ABSTIME, &wakeupTime, NULL);
printf("\n s is :%d\n", s);
if (s != 0) {
if (s == EINTR)
printf("Interrupted by signal handler\n");
else
printf("clock_nanosleep:errno=%d\n",s);
}
break;
}
ecrt_master_application_time(master, wakeup_time);
wakeup_time += cycle_ns;
}
int32_t current_pos[7], target_pos[7];
int16_t error_co[7];
int32_t follow_err[7];
bool clear_err_zero = true;
bool _release_ = false;
int32_t current_pos_5, target_pos_5;
int16_t error_co_5;
int32_t follow_err_5;
bool state_ok = false;
void *my_cyclic(void *data)
{
static uint16_t command[7] = {0x004F,0x004F,0x004F,0x004F,0x004F,0x004F,0x004F};
int cycle_counter = 0;
unsigned int blink = 0;
uint16_t status[7];
uint32_t target_position = 0;
int once=0;
uint32_t positions[N];
int i,dir=0;
wakeup_time = system_time_ns() + 10 * cycle_ns;
bool kk=0;
int k1 = 10000;
while (1) {
wait_period();
cycle_counter++;
ecrt_master_receive(master);
ecrt_domain_process(domain1);
if(!(cycle_counter % 500))
{
cycle_counter = 0;
rt_check_domain_state();
rt_check_master_state();
for(int i = 0; i < num_; i++)
{
check_slave_config_states(sc[i], i);
printf("*****```*****\n");
}
}
if(master_state.al_states == 8 && sc_state[0].al_state == 8 && sc_state[1].al_state == 8
&& sc_state[2].al_state == 8 && sc_state[3].al_state == 8
&& sc_state[4].al_state == 8 && sc_state[5].al_state == 8
&& sc_state[6].al_state == 8)
{
state_ok = true;
}
if(state_ok == true)
{
for(int i = 0; i < num_; i++)
{
current_pos[i] = EC_READ_S32(domain1_pd + offset.current_position[i]);
status[i] = EC_READ_U16(domain1_pd + offset.status_word[i]);
error_co[i] = EC_READ_S16(domain1_pd + offset.error_code[i]);
printf("-------------------------------\n");
printf("status[%d]:%x\n", i, status[i]);
printf("current_position[%d]:%d\n", i, current_pos[i]);
printf("target_position[%d]:%d\n", i, target_pos[i]);
printf("error_code[%d]:%x\n", i, error_co[i]);
printf("mode[%d]:%d\n", i, EC_READ_S8(domain1_pd + offset.mode_operation[i]));
printf("kk = %d\n", kk);
printf("-------------------------------\n");
}
for (int i = 0; i < num_; i++)
{
if( (status[i] & command[i]) == 0x0040 )
{
EC_WRITE_U16(domain1_pd + offset.ctrl_word[i], 0x0006 );
current_pos[i]=EC_READ_S32(domain1_pd+offset.current_position[i]);
EC_WRITE_S32(domain1_pd+offset.target_position[i],current_pos[i]);
target_pos[i] = current_pos[i];
EC_WRITE_S8(domain1_pd + offset.operation_mode[i], 8);
command[i] = 0x006F;
printf("command[%d] = 0x0006", i);
}
else if( (status[i] & command[i]) == 0x0021)
{
EC_WRITE_U16(domain1_pd + offset.ctrl_word[i], 0x0007 );
EC_WRITE_S32(domain1_pd+offset.target_position[i],current_pos[i]);
command[i] = 0x006F;
printf("command[%d] = 0x0007", i);
}
else if( (status[i] & command[i]) == 0x0023)
{
EC_WRITE_U16(domain1_pd + offset.ctrl_word[i], 0x000f );
command[i] = 0x006F;
printf("command[%d] = 0x000f", i);
}
}
for(int i = 0; i < num_; i++)
{
if( (status[i] & command[i]) == 0x0027)
{
printf("\n slave[%d] is enabled\n", i);
}
}
}
if(_release_)
{
k1--;
printf("Release time k1 = %d\n", k1);
for(int i = 0; i < num_; i++)
{
break;
}
if(k1 == 0)
{
break;
}
break;
}
ecrt_domain_queue(domain1);
sync_distributed_clocks();
ecrt_master_send(master);
update_master_clock();
}
return NULL;
}
void signal_handler(int sig)
{
printf("get the ctrl-c signal : %d\n\n", sig);
_release_ = true;
usleep(1000000);
}
int main(int argc, char *argv[])
{
int ret;
int i = 0;
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
perror("mlockall failed");
return -1;
}
printf("Requesting master...\n");
master = ecrt_request_master(0);
if (!master) {
return -1;
}
domain1 = ecrt_master_create_domain(master);
if (!domain1) {
return -1;
}
printf("Creating slave configurations...\n");
for(i = 0; i <num_; i++ )
{
if (!(sc[i] = ecrt_master_slave_config(master, a[i], p[i], VID_PID)))
{
fprintf(stderr, "Failed to get slave %d configuration.\n", i);
return -1;
}
if (ecrt_slave_config_pdos(sc[i], EC_END, device_syncs))
{
fprintf(stderr, "Failed to configure slave %d PDOs.\n", i);
return -1;
}
}
if (ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs)) {
fprintf(stderr, "slave PDO entry registration failed!\n");
return -1;
}
dc_start_time_ns = system_time_ns();
dc_time_ns = dc_start_time_ns;
printf("1.dc_time_ns:%ld\n", dc_time_ns);
for(i = 0; i < num_; i++)
{
ecrt_slave_config_sdo16(sc[i], 0x1c32, 1, 2);
ecrt_slave_config_sdo16(sc[i], 0x1c33, 1, 2);
ecrt_slave_config_dc(sc[i], 0x0300, cycle_ns, cycle_ns/2, 0, 0);
}
ret = ecrt_master_select_reference_clock(master, sc[0]);
if (ret < 0) {
fprintf(stderr, "Failed to select reference clock: %s\n",
strerror(-ret));
return ret;
}
printf("Activating master...\n");
if (ecrt_master_activate(master)) {
return -1;
}
if (!(domain1_pd = ecrt_domain_data(domain1))) {
fprintf(stderr, "Failed to get domain data pointer.\n");
return -1;
}
struct sched_param param = {};
pthread_attr_t attr;
pthread_t thread;
int ret_;
ret_ = pthread_attr_init(&attr);
if (ret_) {
printf("init pthread attributes failed\n");
goto out;
}
ret_ = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);
if (ret_) {
printf("pthread setstacksize failed\n");
goto out;
}
ret_ = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
if (ret_) {
printf("pthread setschedpolicy failed\n");
goto out;
}
param.sched_priority = 98;
ret_ = pthread_attr_setschedparam(&attr, ¶m);
if (ret_){
printf("pthread setschedparam failed\n");
goto out;
}
ret_ = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
if (ret_) {
printf("pthread setinheritsched failed\n");
goto out;
}
usleep(1000000/125);
ret_ = pthread_create(&thread, &attr, my_cyclic, NULL);
if (ret_) {
printf("create pthread failed\n");
goto out;
}
ret_ = pthread_join(thread, NULL);
out:
printf("End of Program\n");
ecrt_release_master(master);
return 0;
}
- 更改睡眠时钟,例程的是rt相关,这里作了修改
即 system2count_change() 返回值修改为ns时间,原例是有计数时钟所以返回计数 system_time_ns() 修改,clock_gettime获取时间 - 以从站为参考
ret = ecrt_master_select_reference_clock(master, sc[0]); - 获取参考时钟的时间
ecrt_master_reference_clock_time(master, &ref_time);//获取参考时钟的低32位 - 计算主站时钟与参考时钟的偏差,即主站的时钟偏移
dc_diff_ns = (uint32_t) prev_app_time - ref_time; - 使从站同步参考时钟
ecrt_master_sync_slave_clocks(master); - 计算时钟漂移
int32_t delta = dc_diff_ns - prev_dc_diff_ns; - 归一化处理,因为是为保证相位同步
dc_diff_ns = ((dc_diff_ns + (cycle_ns / 2)) % cycle_ns) - (cycle_ns / 2); - 后续对偏移进行滤波操作,最后将偏差添加到基时间
结果如下
[285162.618427] EtherCAT: Requesting master 0...
[285162.618433] EtherCAT: Successfully requested master 0.
[285162.618828] EtherCAT 0: Application selected DC reference clock config (0-0) set by application.
[285162.618834] EtherCAT 0: Using slave 0 as application selected DC reference clock.
[285162.618836] EtherCAT 0: Using slave 0 as DC reference clock.
[285162.618866] EtherCAT 0: Domain0: Logical address 0x00000000, 140 byte, expected working counter 21.
[285162.618870] EtherCAT 0: Datagram domain0-0-main: Logical offset 0x00000000, 140 byte, type LRW at 000000001de6bb29.
[285162.618899] EtherCAT 0: Master thread exited.
[285162.618902] EtherCAT 0: Starting EtherCAT-OP thread.
[285162.619003] EtherCAT WARNING 0: 1 datagram TIMED OUT!
[285162.619007] EtherCAT WARNING 0: 2 datagrams UNMATCHED!
[285163.907391] EtherCAT 0: Domain 0: Working counter changed to 2/21.
[285164.915418] EtherCAT 0: Domain 0: 3 working counter changes - now 6/21.
[285165.923446] EtherCAT 0: Domain 0: 2 working counter changes - now 9/21.
[285171.011438] EtherCAT 0: Domain 0: Working counter changed to 11/21.
[285172.018424] EtherCAT 0: Domain 0: Working counter changed to 12/21.
[285173.025458] EtherCAT 0: Domain 0: Working counter changed to 15/21.
[285174.032442] EtherCAT 0: Domain 0: Working counter changed to 18/21.
[285174.433663] EtherCAT 0: Slave states on main device: OP.
[285175.039456] EtherCAT 0: Domain 0: Working counter changed to 21/21.
解决了超时问题,未出现同步错误
|