簡單來說,使用DC的目的是爲了使不同的從站在同一時刻產生sync0信號,爲此,Etherlab及應用程序需要完成以下工作:
(1) 計算從站之間的傳輸延時
(2) 計算從站本地時鐘和系統時鐘的初始偏移量
(3) 設置DC週期時間
(4) 設置sync0啓動時間
(5) 使能DC
(6) 時鐘同步
1、計算傳輸延時
在檢測到總線拓撲發生變化後,發送一個廣播寫命令,寫所有從站端口0的接收時間寄存器0x0900:
fsm_master.c
void ec_fsm_master_state_clear_addresses(
ec_fsm_master_t *fsm /**< Master state machine. */
)
{
......
EC_MASTER_DBG(master, 1, "Sending broadcast-write"
" to measure transmission delays on %s link.\n",
ec_device_names[fsm->dev_idx != 0]);
ec_datagram_bwr(datagram, 0x0900, 1);//廣播寫0x0900寄存器
ec_datagram_zero(datagram);
fsm->datagram->device_index = fsm->dev_idx;
fsm->retries = EC_FSM_RETRIES; //
fsm->state = ec_fsm_master_state_dc_measure_delays;
}
完成從站掃描後開始計算總線拓撲和傳輸延時:
fsm_master.c -> ec_fsm_master_state_scan_slave() -> ec_master_calc_dc(master)
/** Distributed-clocks calculations.
*/
void ec_master_calc_dc(
ec_master_t *master /**< EtherCAT master. */
)
{
// find DC reference clock
ec_master_find_dc_ref_clock(master); //把第一個支持DC的從站作爲參考時鐘
// calculate bus topology
ec_master_calc_topology(master); //根據數據鏈路狀態寄存器0x0110-0x0111,計算總線拓撲
ec_master_calc_transmission_delays(master); //計算傳輸延時
}
2 、計算初始偏移量
讀取從站本地系統時間,以及與參考時鐘的偏差:
fsm_master.c
void ec_fsm_master_enter_write_system_times(
ec_fsm_master_t *fsm /**< Master state machine. */
)
{
ec_master_t *master = fsm->master;
if (master->has_app_time) {
while (fsm->slave < master->slaves + master->slave_count) {
if (!fsm->slave->base_dc_supported
|| !fsm->slave->has_dc_system_time) {
fsm->slave++;
continue;
}
EC_SLAVE_DBG(fsm->slave, 1, "Checking system time offset.\n");
// read DC system time (0x0910, 64 bit) //本地系統時間
// gap (64 bit) //gap是數據幀處理單元接收時間
// and time offset (0x0920, 64 bit) //本地時間和系統時間的偏差
ec_datagram_fprd(fsm->datagram, fsm->slave->station_address,
0x0910, 24);
......
}
} else {
......
}
根據主站時間、從站本地時間及偏差,計算從站時間和主站時間的初始偏移量:
fsm_master.c
void ec_fsm_master_state_dc_read_offset(
ec_fsm_master_t *fsm /**< Master state machine. */
)
{
......
system_time = EC_READ_U64(datagram->data); // 0x0910, 本地系統時間
old_offset = EC_READ_U64(datagram->data + 16); // 0x0920,本地時間和系統時間的偏差
jiffies_since_read = jiffies - datagram->jiffies_sent;
//計算從站本地時間和主站時間(master->app_time)的初始偏移量
if (slave->base_dc_range == EC_DC_32) {
new_offset = ec_fsm_master_dc_offset32(fsm,
system_time, old_offset, jiffies_since_read);
} else {
new_offset = ec_fsm_master_dc_offset64(fsm,
system_time, old_offset, jiffies_since_read);
}
// set DC system time offset and transmission delay
ec_datagram_fpwr(datagram, slave->station_address, 0x0920, 12);
EC_WRITE_U64(datagram->data, new_offset);
EC_WRITE_U32(datagram->data + 8, slave->transmission_delay); //0x0928-0x092B,從站和參考時鐘的傳輸延時
fsm->datagram->device_index = slave->device_index;
fsm->retries = EC_FSM_RETRIES;
fsm->state = ec_fsm_master_state_dc_write_offset;
}
3、設置DC週期時間
應用程序設置DC週期、偏移量、使能控制字:
// configure SYNC signals for this slave
ecrt_slave_config_dc(sc, 0x0300, PERIOD_NS, 440000, 0, 0); //使能sync0, 週期1ms,偏移量440us
將DC週期寫入0x09A0寄存器:
fsm_slave_config.c
void ec_fsm_slave_config_enter_dc_cycle(
ec_fsm_slave_config_t *fsm /**< slave state machine */
)
{
......
if (config->dc_assign_activate) {
......
// set DC cycle times
ec_datagram_fpwr(datagram, slave->station_address, 0x09A0, 8);
EC_WRITE_U32(datagram->data, config->dc_sync[0].cycle_time);
EC_WRITE_U32(datagram->data + 4, config->dc_sync[1].cycle_time);
fsm->retries = EC_FSM_RETRIES;
fsm->state = ec_fsm_slave_config_state_dc_cycle;
}
......
}
4、計算sync0啓動時間
除了將從站間的系統時間同步,還需要sync0產生的相位一致,從站間的sync0才能保持同步。
計算sync0啓動時間在Fsm_slave_config.c文件的ec_fsm_slave_config_state_dc_sync_check()函數中,
每個從站配置時都會執行一次:
void ec_fsm_slave_config_state_dc_sync_check(
ec_fsm_slave_config_t *fsm /**< slave state machine */
)
{
......
// set DC start time
start_time = master->app_time + EC_DC_START_OFFSET; // now + X ns
// FIXME use slave's local system time here?
if (sync0->cycle_time) {
// find correct phase
if (master->has_app_time) {
u64 diff, start;
u32 remainder;
diff = start_time - master->app_start_time;
remainder = do_div(diff, sync0->cycle_time); //取餘數
start = start_time +
sync0->cycle_time - remainder + sync0->shift_time; //相位補償
......
start_time = start; //補償相位以後的啓動時間
} else {
EC_SLAVE_WARN(slave, "No application time supplied."
" Cyclic start time will not be in phase.\n");
}
}
ec_datagram_fpwr(datagram, slave->station_address, 0x0990, 8); //將sync0啓動時間寫入從站寄存器
EC_WRITE_U64(datagram->data, start_time);
fsm->retries = EC_FSM_RETRIES;
fsm->state = ec_fsm_slave_config_state_dc_start;
}
其中,啓動時間往後延時EC_DC_START_OFFSET,是爲了避免使能DC後,從站的本地系統時間已經超過sync0啓動時間,sync0將不會產生.
啓動時間往前推移remainder是爲了不同從站的sync0啓動時間相差整數倍DC週期時間。
5、使能DC
寫0x0981寄存器,激活SYNC0信號:
fsm_slave_config.c
void ec_fsm_slave_config_state_dc_start(
ec_fsm_slave_config_t *fsm /**< slave state machine */
)
{
......
EC_SLAVE_DBG(slave, 1, "Setting DC AssignActivate to 0x%04x.\n",
config->dc_assign_activate);
// assign sync unit to EtherCAT or PDI
ec_datagram_fpwr(datagram, slave->station_address, 0x0980, 2);
EC_WRITE_U16(datagram->data, config->dc_assign_activate);
fsm->retries = EC_FSM_RETRIES;
fsm->state = ec_fsm_slave_config_state_dc_assign;
}
6、時鐘同步
從站間時鐘同步:
ecrt_master_sync_slave_clocks(master);
主從時鐘同步:
ecrt_master_sync_reference_clock(master); //將主站時間master->app_time寫入參考時鐘從站。
同步報文: