Etherlab源码解析----分布时钟DC

简单来说,使用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写入参考时钟从站。

同步报文:
这里写图片描述

展开阅读全文

没有更多推荐了,返回首页