/** LRW "logical memory read / write" primitive plus Clock Distribution. Blocking.
 * Frame consists of two datagrams, one LRW and one FPRMW.
 *
 * @param[in] LogAdr		= Logical memory address
 * @param[in] length		= length of databuffer
 * @param[in,out] data		= databuffer to write to and read from slave.
 * @param[in] DCrs			= Distributed Clock reference slave address.
 * @param[out] DCtime		= DC time read from reference slave.
 * @param[in] timeout		= timeout in us, standard is EC_TIMEOUTRET
 * @return Workcounter or EC_NOFRAME
 */
int ec_LRWDC(uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout)
{
    uint16 DCtO;
    uint8 idx;
	int wkc;
	uint64 DCtE;

    idx = ec_getindex();
	/* LRW in first datagram */
    ec_setupdatagram(&ec_txbuf[idx], EC_CMD_LRW, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
	/* FPRMW in second datagram */
	DCtE = htoell(*DCtime);
    DCtO = ec_adddatagram(&ec_txbuf[idx], EC_CMD_FRMW, idx, FALSE, DCrs, ECT_REG_DCSYSTIME, sizeof(DCtime), &DCtE);
    wkc = ec_srconfirm(idx, timeout);
    if ((wkc > 0) && (ec_rxbuf[idx][EC_CMDOFFSET] == EC_CMD_LRW))
    {
        memcpy(data, &ec_rxbuf[idx][EC_HEADERSIZE], length);
		memcpy(&wkc, &ec_rxbuf[idx][EC_HEADERSIZE + length], EC_WKCSIZE);
		memcpy(&DCtE, &ec_rxbuf[idx][DCtO], sizeof(*DCtime));
		*DCtime = etohll(DCtE);
    }
    ec_setbufstat(idx, EC_BUF_EMPTY);

    return wkc;
}
Example #2
0
/**
 * Set DC of slave to fire sync0 and sync1 at CyclTime interval with CyclShift offset.
 *
 * @param[in]  context        = context struct
 * @param [in] slave            Slave number.
 * @param [in] act              TRUE = active, FALSE = deactivated
 * @param [in] CyclTime0        Cycltime SYNC0 in ns.
 * @param [in] CyclTime1        Cycltime SYNC1 in ns. This time is a delta time in relation to
                                the SYNC0 fire. If CylcTime1 = 0 then SYNC1 fires a the same time
                                as SYNC0.
 * @param [in] CyclShift        CyclShift in ns.
 */
void ecx_dcsync01(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift)
{
    uint8 h, RA;
    uint16 slaveh;
    int64 t, t1;
    int32 tc;
    uint32 TrueCyclTime;

    /* Sync1 can be used as a multiple of Sync0, use true cycle time */
    TrueCyclTime = ((CyclTime1 / CyclTime0) + 1) * CyclTime0;

    slaveh = context->slavelist[slave].configadr;
    RA = 0;

    /* stop cyclic operation, ready for next trigger */
    (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET);
    if (act)
    {
        RA = 1 + 2 + 4;    /* act cyclic operation and sync0 + sync1 */
    }
    h = 0;
    (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCUC, sizeof(h), &h, EC_TIMEOUTRET); /* write access to ethercat */
    t1 = 0;
    (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCSYSTIME, sizeof(t1), &t1, EC_TIMEOUTRET); /* read local time of slave */
    t1 = etohll(t1);

    /* Calculate first trigger time, always a whole multiple of TrueCyclTime rounded up
    plus the shifttime (can be negative)
    This insures best sychronisation between slaves, slaves with the same CyclTime
    will sync at the same moment (you can use CyclShift to shift the sync) */
    if (CyclTime0 > 0)
    {
        t = ((t1 + SyncDelay) / TrueCyclTime) * TrueCyclTime + TrueCyclTime + CyclShift;
    }
    else
    {
        t = t1 + SyncDelay + CyclShift;
        /* first trigger at T1 + CyclTime + SyncDelay + CyclShift in ns */
    }
    t = htoell(t);
    (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSTART0, sizeof(t), &t, EC_TIMEOUTRET); /* SYNC0 start time */
    tc = htoel(CyclTime0);
    (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE0, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC0 cycle time */
    tc = htoel(CyclTime1);
    (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE1, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC1 cycle time */
    (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET); /* activate cyclic operation */

    // update ec_slave state
    context->slavelist[slave].DCactive = (uint8)act;
    context->slavelist[slave].DCshift = CyclShift;
    context->slavelist[slave].DCcycle = CyclTime0;
}
Example #3
0
/** LRW "logical memory read / write" primitive plus Clock Distribution. Blocking.
 * Frame consists of two datagrams, one LRW and one FPRMW.
 *
 * @param[in] port        = port context struct
 * @param[in]     LogAdr  = Logical memory address
 * @param[in]     length  = length of databuffer
 * @param[in,out] data    = databuffer to write to and read from slave.
 * @param[in]     DCrs    = Distributed Clock reference slave address.
 * @param[out]    DCtime  = DC time read from reference slave.
 * @param[in]     timeout = timeout in us, standard is EC_TIMEOUTRET
 * @return Workcounter or EC_NOFRAME
 */
int ecx_portt::LRWDC(uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout)
{
	uint8 idx = getindex();
	/* LRW in first datagram */
	setupdatagram(&(txbuf[idx]), EC_CMD_LRW, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data);
	/* FPRMW in second datagram */
	uint64 DCtE = htoell(*DCtime);
	uint16 DCtO = adddatagram(EC_CMD_FRMW, idx, FALSE, DCrs, ECT_REG_DCSYSTIME, sizeof(DCtime), &DCtE);
	int wkc = srconfirm(idx, timeout);
	if ((wkc > 0) && (rxbuf[idx].getCommand() == EC_CMD_LRW))
	{
		rxbuf[idx].copyHeader(data, length);
		wkc = rxbuf[idx].getWorkCounter(length);
		*DCtime = rxbuf[idx].getDCTime(DCtO);
		
	}
	setbufstat(idx, EC_BUF_EMPTY);

	return wkc;
}
Example #4
0
/**
 * Set DC of slave to fire sync0 at CyclTime interval with CyclShift offset.
 *
 * @param[in]  context        = context struct
 * @param [in] slave            Slave number.
 * @param [in] act              TRUE = active, FALSE = deactivated
 * @param [in] CyclTime         Cycltime in ns.
 * @param [in] CyclShift        CyclShift in ns.
 */
void ecx_dcsync0(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime, uint32 CyclShift)
{
   uint8 h, RA;
   uint16 wc, slaveh;
   int64 t, t1;
   int32 tc;

   slaveh = context->slavelist[slave].configadr;
   RA = 0;

   /* stop cyclic operation, ready for next trigger */
   wc = ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET);
   if (act)
   {
       RA = 1 + 2;    /* act cyclic operation and sync0, sync1 deactivated */
   }
   h = 0;
   wc = ecx_FPWR(context->port, slaveh, ECT_REG_DCCUC, sizeof(h), &h, EC_TIMEOUTRET); /* write access to ethercat */
   wc = ecx_FPRD(context->port, slaveh, ECT_REG_DCSYSTIME, sizeof(t1), &t1, EC_TIMEOUTRET); /* read local time of slave */
   t1 = etohll(t1);

   /* Calculate first trigger time, always a whole multiple of CyclTime rounded up
   plus the shifttime (can be negative)
   This insures best sychronisation between slaves, slaves with the same CyclTime
   will sync at the same moment (you can use CyclShift to shift the sync) */
   if (CyclTime > 0)
   {
       t = ((t1 + SyncDelay) / CyclTime) * CyclTime + CyclTime + CyclShift;
   }
   else
   {
      t = t1 + SyncDelay + CyclShift;
      /* first trigger at T1 + CyclTime + SyncDelay + CyclShift in ns */
   }
   t = htoell(t);
   wc = ecx_FPWR(context->port, slaveh, ECT_REG_DCSTART0, sizeof(t), &t, EC_TIMEOUTRET); /* SYNC0 start time */
   tc = htoel(CyclTime);
   wc = ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE0, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC0 cycle time */
   wc = ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET); /* activate cyclic operation */
}
Example #5
0
/**
 * Locate DC slaves, measure propagation delays.
 *
 * @param[in]  context        = context struct
 * @return boolean if slaves are found with DC
 */
boolean ecx_configdc(ecx_contextt *context)
{
    uint16 i, slaveh, parent, child;
    uint16 parenthold = 0;
    uint16 prevDCslave = 0;
    int32 ht, dt1, dt2, dt3;
    int64 hrt;
    uint8 entryport;
    int8 nlist;
    int8 plist[4];
    int32 tlist[4];
    ec_timet mastertime;
    uint64 mastertime64;

    context->slavelist[0].hasdc = FALSE;
    context->grouplist[0].hasdc = FALSE;
    ht = 0;

    ecx_BWR(context->port, 0, ECT_REG_DCTIME0, sizeof(ht), &ht, EC_TIMEOUTRET);  /* latch DCrecvTimeA of all slaves */
    mastertime = osal_current_time();
    mastertime.sec -= 946684800UL;  /* EtherCAT uses 2000-01-01 as epoch start instead of 1970-01-01 */
    mastertime64 = (((uint64)mastertime.sec * 1000000) + (uint64)mastertime.usec) * 1000;
    for (i = 1; i <= *(context->slavecount); i++)
    {
        context->slavelist[i].consumedports = context->slavelist[i].activeports;
        if (context->slavelist[i].hasdc)
        {
            if (!context->slavelist[0].hasdc)
            {
                context->slavelist[0].hasdc = TRUE;
                context->slavelist[0].DCnext = i;
                context->slavelist[i].DCprevious = 0;
                context->grouplist[0].hasdc = TRUE;
                context->grouplist[0].DCnext = i;
            }
            else
            {
                context->slavelist[prevDCslave].DCnext = i;
                context->slavelist[i].DCprevious = prevDCslave;
            }
            /* this branch has DC slave so remove parenthold */
            parenthold = 0;
            prevDCslave = i;
            slaveh = context->slavelist[i].configadr;
            (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME0, sizeof(ht), &ht, EC_TIMEOUTRET);
            context->slavelist[i].DCrtA = etohl(ht);
            /* 64bit latched DCrecvTimeA of each specific slave */
            (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCSOF, sizeof(hrt), &hrt, EC_TIMEOUTRET);
            /* use it as offset in order to set local time around 0 + mastertime */
            hrt = htoell(-etohll(hrt) + mastertime64);
            /* save it in the offset register */
            (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYSOFFSET, sizeof(hrt), &hrt, EC_TIMEOUTRET);
            (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME1, sizeof(ht), &ht, EC_TIMEOUTRET);
            context->slavelist[i].DCrtB = etohl(ht);
            (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME2, sizeof(ht), &ht, EC_TIMEOUTRET);
            context->slavelist[i].DCrtC = etohl(ht);
            (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME3, sizeof(ht), &ht, EC_TIMEOUTRET);
            context->slavelist[i].DCrtD = etohl(ht);

            /* make list of active ports and their time stamps */
            nlist = 0;
            if (context->slavelist[i].activeports & PORTM0)
            {
                plist[nlist] = 0;
                tlist[nlist] = context->slavelist[i].DCrtA;
                nlist++;
            }
            if (context->slavelist[i].activeports & PORTM3)
            {
                plist[nlist] = 3;
                tlist[nlist] = context->slavelist[i].DCrtD;
                nlist++;
            }
            if (context->slavelist[i].activeports & PORTM1)
            {
                plist[nlist] = 1;
                tlist[nlist] = context->slavelist[i].DCrtB;
                nlist++;
            }
            if (context->slavelist[i].activeports & PORTM2)
            {
                plist[nlist] = 2;
                tlist[nlist] = context->slavelist[i].DCrtC;
                nlist++;
            }
            /* entryport is port with the lowest timestamp */
            entryport = 0;
            if((nlist > 1) && (tlist[1] < tlist[entryport]))
            {
                entryport = 1;
            }
            if((nlist > 2) && (tlist[2] < tlist[entryport]))
            {
                entryport = 2;
            }
            if((nlist > 3) && (tlist[3] < tlist[entryport]))
            {
                entryport = 3;
            }
            entryport = plist[entryport];
            context->slavelist[i].entryport = entryport;
            /* consume entryport from activeports */
            context->slavelist[i].consumedports &= (uint8)~(1 << entryport);

            /* finding DC parent of current */
            parent = i;
            do
            {
                child = parent;
                parent = context->slavelist[parent].parent;
            }
            while (!((parent == 0) || (context->slavelist[parent].hasdc)));
            /* only calculate propagation delay if slave is not the first */
            if (parent > 0)
            {
                /* find port on parent this slave is connected to */
                context->slavelist[i].parentport = ecx_parentport(context, parent);
                if (context->slavelist[parent].topology == 1)
                {
                    context->slavelist[i].parentport = context->slavelist[parent].entryport;
                }

                dt1 = 0;
                dt2 = 0;
                /* delta time of (parentport - 1) - parentport */
                /* note: order of ports is 0 - 3 - 1 -2 */
                /* non active ports are skipped */
                dt3 = ecx_porttime(context, parent, context->slavelist[i].parentport) -
                      ecx_porttime(context, parent,
                                   ecx_prevport(context, parent, context->slavelist[i].parentport));
                /* current slave has children */
                /* those children's delays need to be subtracted */
                if (context->slavelist[i].topology > 1)
                {
                    dt1 = ecx_porttime(context, i,
                                       ecx_prevport(context, i, context->slavelist[i].entryport)) -
                          ecx_porttime(context, i, context->slavelist[i].entryport);
                }
                /* we are only interested in positive difference */
                if (dt1 > dt3) dt1 = -dt1;
                /* current slave is not the first child of parent */
                /* previous child's delays need to be added */
                if ((child - parent) > 1)
                {
                    dt2 = ecx_porttime(context, parent,
                                       ecx_prevport(context, parent, context->slavelist[i].parentport)) -
                          ecx_porttime(context, parent, context->slavelist[parent].entryport);
                }
                if (dt2 < 0) dt2 = -dt2;

                /* calculate current slave delay from delta times */
                /* assumption : forward delay equals return delay */
                context->slavelist[i].pdelay = ((dt3 - dt1) / 2) + dt2 +
                                               context->slavelist[parent].pdelay;
                ht = htoel(context->slavelist[i].pdelay);
                /* write propagation delay*/
                (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYSDELAY, sizeof(ht), &ht, EC_TIMEOUTRET);
            }
        }
        else
        {
            context->slavelist[i].DCrtA = 0;
            context->slavelist[i].DCrtB = 0;
            context->slavelist[i].DCrtC = 0;
            context->slavelist[i].DCrtD = 0;
            parent = context->slavelist[i].parent;
            /* if non DC slave found on first position on branch hold root parent */
            if ( (parent > 0) && (context->slavelist[parent].topology > 2))
                parenthold = parent;
            /* if branch has no DC slaves consume port on root parent */
            if ( parenthold && (context->slavelist[i].topology == 1))
            {
                ecx_parentport(context, parenthold);
                parenthold = 0;
            }
        }
    }

    return context->slavelist[0].hasdc;
}