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寫入參考時鐘從站。

同步報文:
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章