SOES/soes/esc.c

1365 lines
36 KiB
C

/*
* Licensed under the GNU General Public License version 2 with exceptions. See
* LICENSE file in the project root for full license information
*/
#include <string.h>
#include <cc.h>
#include "esc.h"
#include "esc_coe.h"
#include "esc_foe.h"
/** \file
* \brief
* Base EtherCAT functions for handling the Data Link Layer and Malilboxes
*
*
* State machine and mailbox support.
*/
/** Write AL Status Code to the ESC.
*
* @param[in] errornumber = Write an by EtherCAT specified Error number register 0x134 AL Status Code
*/
void ESC_ALerror (uint16_t errornumber)
{
uint16_t dummy;
ESCvar.ALerror = errornumber;
dummy = htoes (errornumber);
ESC_write (ESCREG_ALERROR, &dummy, sizeof (dummy));
}
/** Write AL Status to the ESC.
*
* @param[in] status = Write current slave status to register 0x130 AL Status
* reflecting actual state and error indication if present
*/
void ESC_ALstatus (uint8_t status)
{
uint16_t dummy;
ESCvar.ALstatus = status;
dummy = htoes ((uint16_t) status);
ESC_write (ESCREG_ALSTATUS, &dummy, sizeof (dummy));
}
/** Write AL Status and AL Status code to the ESC.
* Call pre- and poststate change hook
*
* @param[in] status = Write current slave status to register 0x130 AL Status
* reflecting actual state and error indication if present
* @param[in] errornumber = Write an by EtherCAT specified Error number
* register 0x134 AL Status Code
*/
void ESC_ALstatusgotoerror (uint8_t status, uint16_t errornumber)
{
uint8_t an, as;
if(status & ESCop)
{
/* Erroneous input, ignore */
return;
}
/* Mask error ack of current state */
as = ESCvar.ALstatus & ESCREG_AL_ERRACKMASK;
an = as;
/* Set the state transition, new state in high bits and old in bits */
as = (uint8_t)(((status & ESCREG_AL_ERRACKMASK) << 4) | (as & 0x0f));
/* Call post state change hook case it have been configured */
if (ESCvar.pre_state_change_hook != NULL)
{
ESCvar.pre_state_change_hook (&as, &an);
}
/* Stop outputs if active */
if ((CC_ATOMIC_GET(ESCvar.App.state) & APPSTATE_OUTPUT) > 0)
{
ESC_stopoutput();
}
ESC_ALerror(errornumber);
ESC_ALstatus(status);
an = status;
/* Call post state change hook case it have been configured */
if (ESCvar.post_state_change_hook != NULL)
{
ESCvar.post_state_change_hook (&as, &an);
}
}
/** Write ALeventMask register 0x204.
*
* @param[in] n = AL Event Mask
*/
void ESC_ALeventmaskwrite (uint32_t mask)
{
uint32_t aleventmask;
aleventmask = htoel(mask);
ESC_write (ESCREG_ALEVENTMASK, &aleventmask, sizeof(aleventmask));
}
/** Read AleventMask register 0x204.
*
* @return value of register AL Event Mask
*/
uint32_t ESC_ALeventmaskread (void)
{
uint32_t aleventmask;
ESC_read (ESCREG_ALEVENTMASK, &aleventmask, sizeof(aleventmask));
return htoel(aleventmask);
}
/** Write ALevent register 0x220.
*
* @param[in] n = AL Event Mask
*/
void ESC_ALeventwrite (uint32_t event)
{
uint32_t alevent;
alevent = htoel(event);
ESC_write (ESCREG_ALEVENT, &alevent, sizeof(alevent));
}
/** Read Alevent register 0x220.
*
* @return value of register AL Event Mask
*/
uint32_t ESC_ALeventread (void)
{
uint32_t alevent;
ESC_read (ESCREG_ALEVENT, &alevent, sizeof(alevent));
return htoel(alevent);
}
/** Read SM Activate register 0x806(+ offset to SyncManager n) to acknowledge a
* Sync Manager event Bit 3 in ALevent. The result is not used.
*
* @param[in] n = Read Sync Manager no. n
*/
void ESC_SMack (uint8_t n)
{
uint8_t dummy;
ESC_read ((uint16_t)(ESCREG_SM0ACTIVATE + (n << 3)), &dummy, 1);
}
/** Read SM Status register 0x805(+ offset to SyncManager n) and save the
* result in global variable ESCvar.SM[n].
*
* @param[in] n = Read Sync Manager no. n
*/
void ESC_SMstatus (uint8_t n)
{
_ESCsm2 *sm;
sm = (_ESCsm2 *)&ESCvar.SM[n];
ESC_read ((uint16_t)(ESCREG_SM0STATUS + (n << 3)), &(sm->Status), 1);
}
/** Write ESCvar.SM[n] data to ESC PDI control register 0x807(+ offset to SyncManager n).
*
* @param[in] n = Write to Sync Manager no. n
*/
void ESC_SMwritepdi (uint8_t n)
{
_ESCsm2 *sm;
sm = (_ESCsm2 *)&ESCvar.SM[n];
ESC_write ((uint16_t)(ESCREG_SM0PDI + (n << 3)), &(sm->ActPDI), 1);
}
/** Read ESC PDI control register 0x807(+ offset to SyncManager n) to ESCvar.SM[n] data.
*
* @param[in] n = Read from Sync Manager no. n
*/
void ESC_SMreadpdi (uint8_t n)
{
_ESCsm2* sm;
sm = (_ESCsm2*)&ESCvar.SM[n];
ESC_read ((uint16_t)(ESCREG_SM0PDI + (n << 3)), &(sm->ActPDI), 1);
}
/** Write 0 to Bit0 in SM PDI control register 0x807(+ offset to SyncManager n) to Activate the Sync Manager n.
*
* @param[in] n = Write to Sync Manager no. n
*/
void ESC_SMenable (uint8_t n)
{
_ESCsm2 *sm;
sm = (_ESCsm2 *)&ESCvar.SM[n];
sm->ActPDI &= (uint8_t)~ESCREG_SMENABLE_BIT;
ESC_SMwritepdi (n);
/* Read back wait until enabled */
do
{
ESC_SMreadpdi (n);
} while ((sm->ActPDI & ESCREG_SMENABLE_BIT) == 1);
}
/** Write 1 to Bit0 in SM PDI control register 0x807(+ offset to SyncManager n) to De-activte the Sync Manager n.
*
* @param[in] n = Write to Sync Manager no. n
*/
void ESC_SMdisable (uint8_t n)
{
_ESCsm2 *sm;
sm = (_ESCsm2 *)&ESCvar.SM[n];
sm->ActPDI |= ESCREG_SMENABLE_BIT;
ESC_SMwritepdi (n);
/* Read back wait until disabled */
do
{
ESC_SMreadpdi (n);
} while ((sm->ActPDI & ESCREG_SMENABLE_BIT) == 0);
}
/** Read Configured Station Address register 0x010 assigned by the Master.
*
*/
void ESC_address (void)
{
ESC_read (ESCREG_ADDRESS, (void *) &ESCvar.address, sizeof (ESCvar.address));
ESCvar.address = etohs (ESCvar.address);
}
/** Read Watchdog Status register 0x440. Result Bit0 0= Expired, 1= Active or disabled.
*
* @return value of register Watchdog Status.
*/
uint8_t ESC_WDstatus (void)
{
uint16_t wdstatus;
ESC_read (ESCREG_WDSTATUS, &wdstatus, 2);
wdstatus = etohs (wdstatus);
return (uint8_t) wdstatus;
}
/** Read SYNC Out Unit activation registers 0x981
*
* @return value of register Activation.
*/
uint8_t ESC_SYNCactivation (void)
{
uint8_t activation;
ESC_read (ESCREG_SYNC_ACT, &activation, sizeof(activation));
return activation;
}
/** Read SYNC0 cycle time
*
* @return value of register SYNC0 cycle time
*/
uint32_t ESC_SYNC0cycletime (void)
{
uint32_t cycletime;
ESC_read (ESCREG_SYNC0_CYCLE_TIME, &cycletime, sizeof(cycletime));
cycletime = etohl (cycletime);
return cycletime;
}
/** Read SYNC1 cycle time
*
* @return value of register SYNC1 cycle time
*/
uint32_t ESC_SYNC1cycletime (void)
{
uint32_t cycletime;
ESC_read (ESCREG_SYNC1_CYCLE_TIME, &cycletime, 4);
cycletime = etohl (cycletime);
return cycletime;
}
/** Validate the DC values if the SYNC unit is activated.
*
* @return = 0 if OK, else ERROR code to be set by caller.
*/
uint16_t ESC_checkDC (void)
{
uint16_t ret = 0;
uint8_t sync_act = ESC_SYNCactivation();
/* Do we need to check sync settings? */
if((sync_act & (ESCREG_SYNC_ACT_ACTIVATED | ESCREG_SYNC_AUTO_ACTIVATED)) > 0)
{
/* Trigger a by the application given DC check handler, return error if
* non is given
*/
ret = ALERR_DCINVALIDSYNCCFG;
if(ESCvar.esc_check_dc_handler != NULL)
{
ret = (ESCvar.esc_check_dc_handler)();
}
}
else
{
ESCvar.dcsync = 0;
ESCvar.synccounter = 0;
}
return ret;
}
/** Check mailbox status by reading all SyncManager 0 and 1 data. The read values
* are compared with local definitions for SM Physical Address, SM Length and SM Control.
* If we check fails we disable Mailboxes by disabling SyncManager 0 and 1 and return
* state Init with Error flag set.
*
* @param[in] state = Current state request read from ALControl 0x0120
* @return if all Mailbox values is correct we return incoming state request, otherwise
* we return state Init with Error flag set.
*/
uint8_t ESC_checkmbx (uint8_t state)
{
_ESCsm2 *SM;
ESC_read (ESCREG_SM0, (void *) &ESCvar.SM[0], sizeof (ESCvar.SM[0]));
ESC_read (ESCREG_SM1, (void *) &ESCvar.SM[1], sizeof (ESCvar.SM[1]));
SM = (_ESCsm2 *) & ESCvar.SM[0];
if ((etohs (SM->PSA) != ESC_MBX0_sma) || (etohs (SM->Length) != ESC_MBX0_sml)
|| (SM->Command != ESC_MBX0_smc) || (ESCvar.SM[0].ECsm == 0))
{
ESCvar.SMtestresult = SMRESULT_ERRSM0;
ESC_SMdisable (0);
ESC_SMdisable (1);
return (uint8_t) (ESCinit | ESCerror); //fail state change
}
SM = (_ESCsm2 *) & ESCvar.SM[1];
if ((etohs (SM->PSA) != ESC_MBX1_sma) || (etohs (SM->Length) != ESC_MBX1_sml)
|| (SM->Command != ESC_MBX1_smc) || (ESCvar.SM[1].ECsm == 0))
{
ESCvar.SMtestresult = SMRESULT_ERRSM1;
ESC_SMdisable (0);
ESC_SMdisable (1);
return ESCinit | ESCerror; //fail state change
}
return state;
}
/** Try to start mailboxes for current ALControl state request by enabling SyncManager 0 and 1.
* If all mailbox settings is correct we return incoming state request, otherwise
* we return state Init with Error flag set and update local ALerror with code 0x16 Invalid
* mailbox configuration.
*
* @param[in] state = Current state request read from ALControl 0x0120
* @return if all Mailbox values is correct we return incoming state, otherwise
* we return state Init with Error flag set.
*/
uint8_t ESC_startmbx (uint8_t state)
{
/* Assign SM settings */
ESCvar.activembxsize = MBXSIZE;
ESCvar.activemb0 = &ESCvar.mb[0];
ESCvar.activemb1 = &ESCvar.mb[1];
ESC_SMenable (0);
ESC_SMenable (1);
ESC_SMstatus (0);
ESC_SMstatus (1);
if ((state = ESC_checkmbx (state)) & ESCerror)
{
ESC_ALerror (ALERR_INVALIDMBXCONFIG);
ESCvar.MBXrun = 0;
}
else
{
ESCvar.toggle = ESCvar.SM[1].ECrep; //sync repeat request toggle state
ESCvar.SM[1].PDIrep = ESCvar.toggle & 0x1U;
ESC_SMwritepdi (1);
ESCvar.MBXrun = 1;
}
return state;
}
/** Try to start bootstrap mailboxes for current ALControl state request by enabling SyncManager 0 and 1.
* If all mailbox settings is correct we return incoming state request, otherwise
* we return state Init with Error flag set and update local ALerror with code 0x16 Invalid
* mailbox configuration.
*
* @param[in] state = Current state request read from ALControl 0x0120
* @return if all Mailbox values is correct we return incoming state, otherwise
* we return state Init with Error flag set.
*/
uint8_t ESC_startmbxboot (uint8_t state)
{
/* Assign SM settings */
ESCvar.activembxsize = MBXSIZEBOOT;
ESCvar.activemb0 = &ESCvar.mbboot[0];
ESCvar.activemb1 = &ESCvar.mbboot[1];
ESC_SMenable (0);
ESC_SMenable (1);
ESC_SMstatus (0);
ESC_SMstatus (1);
if ((state = ESC_checkmbx (state)) & ESCerror)
{
ESC_ALerror (ALERR_INVALIDBOOTMBXCONFIG);
ESCvar.MBXrun = 0;
}
else
{
ESCvar.toggle = ESCvar.SM[1].ECrep; //sync repeat request toggle state
ESCvar.SM[1].PDIrep = ESCvar.toggle & 0x1U;
ESC_SMwritepdi (1);
ESCvar.MBXrun = 1;
}
return state;
}
/** Stop mailboxes by disabling SyncManager 0 and 1. Clear local mailbox variables
* stored in ESCvar.
*/
void ESC_stopmbx (void)
{
uint8_t n;
ESCvar.MBXrun = 0;
ESC_SMdisable (0);
ESC_SMdisable (1);
for (n = 0; n < ESC_MBXBUFFERS; n++)
{
MBXcontrol[n].state = MBXstate_idle;
}
ESCvar.mbxoutpost = 0;
ESCvar.mbxbackup = 0;
ESCvar.xoe = 0;
ESCvar.mbxfree = 1;
ESCvar.toggle = 0;
ESCvar.mbxincnt = 0;
ESCvar.segmented = 0;
ESCvar.frags = 0;
ESCvar.fragsleft = 0;
ESCvar.txcue = 0;
ESCvar.index = 0;
ESCvar.subindex = 0;
ESCvar.flags = 0;
}
/** Read Receive mailbox and store data in local ESCvar.MBX variable.
* Combined function for bootstrap and other states. State check decides
* which one to read.
*/
void ESC_readmbx (void)
{
_MBX *MB = (_MBX *)&MBX[0];
uint16_t length;
ESC_read (ESC_MBX0_sma, MB, ESC_MBXHSIZE);
length = etohs (MB->header.length);
if (length > (ESC_MBX0_sml - ESC_MBXHSIZE))
{
length = (uint16_t)(ESC_MBX0_sml - ESC_MBXHSIZE);
}
ESC_read ((uint16_t)(ESC_MBX0_sma + ESC_MBXHSIZE), MB->b, length);
if (length + ESC_MBXHSIZE < ESC_MBX0_sml)
{
ESC_read (ESC_MBX0_sme, &length, 1);
}
MBXcontrol[0].state = MBXstate_inclaim;
}
/** Write local mailbox buffer ESCvar.MBX[n] to Send mailbox.
* Combined function for bootstrap and other states. State check decides
* which one to write.
*
* @param[in] n = Which local mailbox buffer n to send.
*/
void ESC_writembx (uint8_t n)
{
_MBXh *MBh = (_MBXh *)&MBX[n * ESC_MBXSIZE];
uint8_t dummy = 0;
uint16_t length;
length = etohs (MBh->length);
if (length > (ESC_MBX1_sml - ESC_MBXHSIZE))
{
length = (uint16_t)(ESC_MBX1_sml - ESC_MBXHSIZE);
}
ESC_write (ESC_MBX1_sma, MBh, (uint16_t)(ESC_MBXHSIZE + length));
if (length + ESC_MBXHSIZE < ESC_MBX1_sml)
{
ESC_write (ESC_MBX1_sme, &dummy, 1);
}
ESCvar.mbxfree = 0;
}
/** TBD
*/
void ESC_ackmbxread (void)
{
uint8_t dummy = 0;
ESC_write (ESC_MBX1_sma, &dummy, 1);
ESCvar.mbxfree = 1;
}
/** Allocate and prepare a mailbox buffer. Take the first Idle buffer from the End.
* Set Mailbox control state to be used for outbox and fill the mailbox buffer with
* address master and mailbox next CNT value between 1-7.
*
* @return The index of Mailbox buffer prepared for outbox. IF no buffer is available return 0.
*/
uint8_t ESC_claimbuffer (void)
{
_MBXh *MBh;
uint8_t n = ESC_MBXBUFFERS - 1;
while ((n > 0) && (MBXcontrol[n].state))
{
n--;
}
if (n)
{
MBXcontrol[n].state = MBXstate_outclaim;
MBh = (_MBXh *)&MBX[n * ESC_MBXSIZE];
ESCvar.mbxcnt++;
ESCvar.mbxcnt = (ESCvar.mbxcnt & 0x07);
if (ESCvar.mbxcnt == 0)
{
ESCvar.mbxcnt = 1;
}
MBh->address = htoes (0x0000); // destination is master
MBh->channel = 0;
MBh->priority = 0;
MBh->mbxcnt = ESCvar.mbxcnt & 0xFU;
ESCvar.txcue++;
}
return n;
}
/** Look for any present requests for posting to the outbox.
*
* @return the index of Mailbox buffer ready to be posted.
*/
uint8_t ESC_outreqbuffer (void)
{
uint8_t n = ESC_MBXBUFFERS - 1;
while ((n > 0) && (MBXcontrol[n].state != MBXstate_outreq))
{
n--;
}
return n;
}
/** Allocate and prepare a mailbox buffer for sending an error message. Take the first Idle
* buffer from the end. Set Mailbox control state to be used for outbox and fill the mailbox
* buffer with error information.
*
* @param[in] n = Error number to be sent in mailbox error message.
*/
void MBX_error (uint16_t error)
{
uint8_t MBXout;
_MBXerr *mbxerr;
MBXout = ESC_claimbuffer ();
if (MBXout)
{
mbxerr = (_MBXerr *) &MBX[MBXout * ESC_MBXSIZE];
mbxerr->mbxheader.length = htoes ((uint16_t) 0x04);
mbxerr->mbxheader.mbxtype = MBXERR;
mbxerr->type = htoes ((uint16_t) 0x01);
mbxerr->detail = htoes (error);
MBXcontrol[MBXout].state = MBXstate_outreq;
}
}
/** Mailbox routine for implementing the low-level part of the mailbox protocol
* used by Application Layers running on-top of mailboxes. It takes care of sending
* a mailbox, re-sending a mailbox, reading a mailbox and handles a mailbox full event.
*
* @return =0 if nothing to do. =1 if something to be handled by mailbox protocols.
*/
uint8_t ESC_mbxprocess (void)
{
uint8_t mbxhandle = 0;
_MBXh *MBh = (_MBXh *)&MBX[0];
if (ESCvar.MBXrun == 0)
{
/* nothing to do */
return 0;
}
/* SM0/1 access */
if (ESCvar.ALevent & (ESCREG_ALEVENT_SM0 | ESCREG_ALEVENT_SM1))
{
ESC_SMstatus (0);
ESC_SMstatus (1);
}
/* outmbx read by master */
if (ESCvar.mbxoutpost && (ESCvar.ALevent & ESCREG_ALEVENT_SM1))
{
ESC_ackmbxread ();
/* dispose old backup */
if (ESCvar.mbxbackup)
{
MBXcontrol[ESCvar.mbxbackup].state = MBXstate_idle;
}
/* if still to do */
if (MBXcontrol[ESCvar.mbxoutpost].state == MBXstate_again)
{
ESC_writembx (ESCvar.mbxoutpost);
}
/* create new backup */
MBXcontrol[ESCvar.mbxoutpost].state = MBXstate_backup;
ESCvar.mbxbackup = ESCvar.mbxoutpost;
ESCvar.mbxoutpost = 0;
/* Do we have any ongoing protocol transfers, return 1 */
if(ESCvar.xoe > 0)
{
return 1;
}
return 0;
}
/* repeat request */
if (ESCvar.SM[1].ECrep != ESCvar.toggle)
{
if (ESCvar.mbxoutpost || ESCvar.mbxbackup)
{
/* if outmbx empty */
if (ESCvar.mbxoutpost == 0)
{
/* use backup mbx */
ESC_writembx (ESCvar.mbxbackup);
}
else
{
/* reset mailbox */
ESC_SMdisable (1);
/* have to resend later */
MBXcontrol[ESCvar.mbxoutpost].state = MBXstate_again;
/* activate mailbox */
ESC_SMenable (1);
/* use backup mbx */
ESC_writembx (ESCvar.mbxbackup);
}
ESCvar.toggle = ESCvar.SM[1].ECrep;
ESCvar.SM[1].PDIrep = ESCvar.toggle & 0x1U;
ESC_SMwritepdi (1);
}
return 0;
}
/* if the outmailbox is free check if we have something to send */
if (ESCvar.txcue && (ESCvar.mbxfree || !ESCvar.SM[1].MBXstat))
{
/* check out request mbx */
mbxhandle = ESC_outreqbuffer ();
/* outmbx empty and outreq mbx available */
if (mbxhandle)
{
ESC_writembx (mbxhandle);
/* Refresh SM status */
ESC_SMstatus (1);
/* change state */
MBXcontrol[mbxhandle].state = MBXstate_outpost;
ESCvar.mbxoutpost = mbxhandle;
if (ESCvar.txcue)
{
ESCvar.txcue--;
}
}
}
/* read mailbox if full and no xoe in progress */
if ((ESCvar.SM[0].MBXstat != 0) && (MBXcontrol[0].state == 0)
&& (ESCvar.mbxoutpost == 0) && (ESCvar.xoe == 0))
{
ESC_readmbx ();
ESCvar.SM[0].MBXstat = 0;
if (etohs (MBh->length) == 0)
{
MBX_error (MBXERR_INVALIDHEADER);
/* drop mailbox */
MBXcontrol[0].state = MBXstate_idle;
}
if ((MBh->mbxcnt != 0) && (MBh->mbxcnt == ESCvar.mbxincnt))
{
/* drop mailbox */
MBXcontrol[0].state = MBXstate_idle;
}
ESCvar.mbxincnt = MBh->mbxcnt;
return 1;
}
return 0;
}
/** Handler for incorrect or unsupported mailbox data. Write error response
* in Mailbox.
*/
void ESC_xoeprocess (void)
{
_MBXh *mbh;
if (ESCvar.MBXrun == 0)
{
return;
}
if ((ESCvar.xoe == 0) && (MBXcontrol[0].state == MBXstate_inclaim))
{
mbh = (_MBXh *) &MBX[0];
if ((mbh->mbxtype == 0) || (etohs (mbh->length) == 0))
{
MBX_error (MBXERR_INVALIDHEADER);
}
else
{
MBX_error (MBXERR_UNSUPPORTEDPROTOCOL);
}
/* mailbox type not supported, drop mailbox */
MBXcontrol[0].state = MBXstate_idle;
}
}
/** Validate the values of Sync Manager 2 & 3 that the current ESC values is
* equal to configured and calculated local values.
*
* @param[in] state = Requested state.
* @return = incoming state request if every thing checks out OK. = state (PREOP | ERROR) if something isn't correct.
*/
uint8_t ESC_checkSM23 (uint8_t state)
{
_ESCsm2 *SM;
ESC_read (ESCREG_SM2, (void *) &ESCvar.SM[2], sizeof (ESCvar.SM[2]));
SM = (_ESCsm2 *) & ESCvar.SM[2];
/* Check SM settings */
if ((etohs (SM->PSA) != ESC_SM2_sma) ||
(SM->Command != ESC_SM2_smc))
{
ESCvar.SMtestresult = SMRESULT_ERRSM2;
/* fail state change */
return (ESCpreop | ESCerror);
}
/* Check run-time settings */
/* Check length */
else if (etohs (SM->Length) != ESCvar.ESC_SM2_sml)
{
ESCvar.SMtestresult = SMRESULT_ERRSM2;
/* fail state change */
return (ESCpreop | ESCerror);
}
/* SM disabled and (SM activated or length > 0) set by master */
else if (((ESC_SM2_act & ESCREG_SYNC_ACT_ACTIVATED) == 0) &&
((SM->ActESC & ESCREG_SYNC_ACT_ACTIVATED) || (ESCvar.ESC_SM2_sml > 0)))
{
ESCvar.SMtestresult = SMRESULT_ERRSM2;
/* fail state change */
return (ESCpreop | ESCerror);
}
/* SM enabled and (length > 0 but SM disabled) set by master */
else if (((ESC_SM2_act & ESCREG_SYNC_ACT_ACTIVATED) > 0) &&
((SM->ActESC & ESCREG_SYNC_ACT_ACTIVATED) == 0) &&
(ESCvar.ESC_SM2_sml > 0))
{
ESCvar.SMtestresult = SMRESULT_ERRSM2;
/* fail state change */
return (ESCpreop | ESCerror);
}
if ((ESC_SM2_sma + (etohs (SM->Length) * 3)) > ESC_SM3_sma)
{
ESCvar.SMtestresult = SMRESULT_ERRSM2;
/* SM2 overlaps SM3, fail state change */
return (ESCpreop | ESCerror);
}
ESC_read (ESCREG_SM3, (void *) &ESCvar.SM[3], sizeof (ESCvar.SM[3]));
SM = (_ESCsm2 *) & ESCvar.SM[3];
/* Check SM settings */
if ((etohs (SM->PSA) != ESC_SM3_sma) ||
(SM->Command != ESC_SM3_smc))
{
ESCvar.SMtestresult = SMRESULT_ERRSM3;
/* fail state change */
return (ESCpreop | ESCerror);
}
/* Check run-time settings */
/* Check length */
else if (etohs (SM->Length) != ESCvar.ESC_SM3_sml)
{
ESCvar.SMtestresult = SMRESULT_ERRSM3;
/* fail state change */
return (ESCpreop | ESCerror);
}
/* SM disabled and (SM activated or length > 0) set by master */
else if (((ESC_SM3_act & ESCREG_SYNC_ACT_ACTIVATED) == 0) &&
((SM->ActESC & ESCREG_SYNC_ACT_ACTIVATED) || (ESCvar.ESC_SM3_sml > 0)))
{
ESCvar.SMtestresult = SMRESULT_ERRSM3;
/* fail state change */
return (ESCpreop | ESCerror);
}
/* SM enabled and (length > 0 but SM disabled) set by master */
else if (((ESC_SM3_act & ESCREG_SYNC_ACT_ACTIVATED) > 0) &&
((SM->ActESC & ESCREG_SYNC_ACT_ACTIVATED) == 0) &&
(ESCvar.ESC_SM3_sml > 0))
{
ESCvar.SMtestresult = SMRESULT_ERRSM3;
/* fail state change */
return (ESCpreop | ESCerror);
}
return state;
}
/** Function trying to enable start updating the process data inputs. It calls the check SM 2 & 3
* routine, based on the result from there if enables or disables the Input SyncManager, in addition
* it updates the ALStatusCode case something didn't pass the check.
*
* @param[in] state = Requested state.
* @return = state, incoming state request if every thing checks out OK. =state (PREOP | ERROR) if something isn't correct.
*/
uint8_t ESC_startinput (uint8_t state)
{
state = ESC_checkSM23 (state);
if (state != (ESCpreop | ESCerror))
{
/* If inputs > 0 , enable SM3 */
if (ESCvar.ESC_SM3_sml > 0)
{
ESC_SMenable (3);
}
/* Go to state input regardless of any inputs present */
CC_ATOMIC_SET(ESCvar.App.state, APPSTATE_INPUT);
}
else
{
ESC_SMdisable (2);
ESC_SMdisable (3);
if (ESCvar.SMtestresult & SMRESULT_ERRSM3)
{
ESC_ALerror (ALERR_INVALIDINPUTSM);
}
else
{
ESC_ALerror (ALERR_INVALIDOUTPUTSM);
}
}
/* Exit here if polling */
if (ESCvar.use_interrupt == 0)
{
return state;
}
if (state != (ESCpreop | ESCerror))
{
uint16_t dc_check_result;
dc_check_result = ESC_checkDC();
if(dc_check_result > 0)
{
ESC_ALerror (dc_check_result);
state = (ESCpreop | ESCerror);
ESC_SMdisable (2);
ESC_SMdisable (3);
CC_ATOMIC_SET(ESCvar.App.state, APPSTATE_IDLE);
}
else
{
if (ESCvar.esc_hw_interrupt_enable != NULL)
{
uint32_t int_mask;
if (ESCvar.ESC_SM2_sml == 0)
{
int_mask = ESCREG_ALEVENT_SM3;
}
else
{
int_mask = ESCREG_ALEVENT_SM2;
}
if (ESCvar.dcsync > 0)
{
int_mask |= ESCREG_ALEVENT_DC_SYNC0;
}
ESCvar.esc_hw_interrupt_enable (int_mask);
}
}
}
return state;
}
/** Unconditional stop of updating inputs by disabling Sync Manager 2 & 3.
* Set the App.state to APPSTATE_IDLE.
*
*/
void ESC_stopinput (void)
{
CC_ATOMIC_SET(ESCvar.App.state, APPSTATE_IDLE);
ESC_SMdisable (3);
ESC_SMdisable (2);
/* Call interrupt disable hook case it have been configured */
if ((ESCvar.use_interrupt != 0) &&
(ESCvar.esc_hw_interrupt_disable != NULL))
{
ESCvar.esc_hw_interrupt_disable (ESCREG_ALEVENT_DC_SYNC0 |
ESCREG_ALEVENT_SM2 |
ESCREG_ALEVENT_SM3);
}
}
/** Unconditional start of updating outputs by enabling Sync Manager 2.
* Set the App.state to APPSTATE_OUTPUT.
*
* @param[in] state = Not used.
* @return = state unchanged.
*
*/
uint8_t ESC_startoutput (uint8_t state)
{
/* If outputs > 0 , enable SM2 */
if (ESCvar.ESC_SM2_sml > 0)
{
ESC_SMenable (2);
}
/* Go to state output regardless of any outputs present */
CC_ATOMIC_OR(ESCvar.App.state, APPSTATE_OUTPUT);
return state;
}
/** Unconditional stop of updating outputs by disabling Sync Manager 2.
* Set the App.state to APPSTATE_INPUT. Call application hook APP_safeoutput
* letting the user to set safe state values on outputs.
*
*/
void ESC_stopoutput (void)
{
CC_ATOMIC_AND(ESCvar.App.state, APPSTATE_INPUT);
ESC_SMdisable (2);
APP_safeoutput ();
}
/** The state handler acting on SyncManager Activation BIT(4)
* events in the Al Event Request register 0x220.
*
*/
void ESC_sm_act_event (void)
{
uint8_t ac, an, as, ax, ax23;
/* Have at least on Sync Manager changed */
if ((ESCvar.ALevent & ESCREG_ALEVENT_SMCHANGE) == 0)
{
/* nothing to do */
return;
}
/* Mask state request bits + Error ACK */
ac = ESCvar.ALcontrol & ESCREG_AL_STATEMASK;
as = ESCvar.ALstatus & ESCREG_AL_STATEMASK;
an = as;
if (((ac & ESCerror) || (ac == ESCinit)))
{
/* if error bit confirmed reset */
ac &= ESCREG_AL_ERRACKMASK;
an &= ESCREG_AL_ERRACKMASK;
}
/* Enter SM changed handling for all steps but Init and Boot when Mailboxes
* is up and running
*/
if ((as & ESCREG_AL_ALLBUTINITMASK) &&
((as == ESCboot) == 0) && ESCvar.MBXrun)
{
/* Validate Sync Managers, reading the Activation register will
* acknowledge the SyncManager Activation event making us enter
* this execution path.
*/
ax = ESC_checkmbx (as);
ax23 = ESC_checkSM23 (as);
if ((an & ESCerror) && ((ac & ESCerror) == 0))
{
/* if in error then stay there */
}
/* Have we been forced to step down to INIT we will stop mailboxes,
* update AL Status Code and exit ESC_state
*/
else if (ax == (ESCinit | ESCerror))
{
/* If we have activated Inputs and Outputs we need to disable them */
if (CC_ATOMIC_GET(ESCvar.App.state))
{
ESC_stopoutput ();
ESC_stopinput ();
}
/* Stop mailboxes and update ALStatus code */
ESC_stopmbx ();
ESC_ALerror (ALERR_INVALIDMBXCONFIG);
ESCvar.MBXrun = 0;
ESC_ALstatus (ax);
return;
}
/* Have we been forced to step down to PREOP we will stop inputs
* and outputs, update AL Status Code and exit ESC_state
*/
else if (CC_ATOMIC_GET(ESCvar.App.state) && (ax23 == (ESCpreop | ESCerror)))
{
ESC_stopoutput ();
ESC_stopinput ();
if (ESCvar.SMtestresult & SMRESULT_ERRSM3)
{
ESC_ALerror (ALERR_INVALIDINPUTSM);
}
else
{
ESC_ALerror (ALERR_INVALIDOUTPUTSM);
}
ESC_ALstatus (ax23);
}
}
else
{
ESC_SMack (0);
ESC_SMack (1);
ESC_SMack (2);
ESC_SMack (3);
ESC_SMack (4);
ESC_SMack (5);
ESC_SMack (6);
ESC_SMack (7);
}
}
static bool ESC_check_id_request (uint16_t ALcontrol, uint8_t * an)
{
if ((ALcontrol & ESCREG_AL_ID_REQUEST) != 0)
{
uint8_t state = ALcontrol & ESCREG_AL_ERRACKMASK;
if ((state != ESCboot) &&
((state < ESCsafeop) || (*an == ESCsafeop) || (*an == ESCop)))
{
uint16_t ALstatuscode;
ESC_read (ESCREG_ALERROR,
(void *)&ALstatuscode,
sizeof (ALstatuscode));
return (ALstatuscode == ALERR_NONE);
}
}
return false;
}
static uint8_t ESC_load_device_id (void)
{
uint16_t device_id;
if (ESCvar.get_device_id != NULL)
{
if (ESCvar.get_device_id (&device_id) != 0)
{
device_id = 0;
}
}
else
{
ESC_read (ESCREG_CONF_STATION_ALIAS,
(void *)&device_id,
sizeof (device_id));
}
if (device_id != 0)
{
/* Load the Device Identification Value to the AL Status Code register */
ESC_ALerror (device_id);
return ESCREG_AL_ID_REQUEST;
}
return 0;
}
#ifdef ESC_DEBUG
static char * ESC_state_to_string (uint8_t ESC_state)
{
switch (ESC_state)
{
case ESCinit: return "Init";
case ESCpreop: return "Pre-Operational";
case ESCboot: return "Bootstrap";
case ESCsafeop: return "Safe-Operational";
case ESCop: return "Operational";
case ESCerror: return "Error";
}
return "Unknown";
}
#endif
/** The state handler acting on ALControl Bit(0)
* events in the Al Event Request register 0x220.
*
*/
void ESC_state (void)
{
uint8_t ac, an, as;
/* Do we have a state change request pending */
if (ESCvar.ALevent & ESCREG_ALEVENT_CONTROL)
{
ESC_read (ESCREG_ALCONTROL, (void *) &ESCvar.ALcontrol,
sizeof (ESCvar.ALcontrol));
ESCvar.ALcontrol = etohs (ESCvar.ALcontrol);
}
else
{
/* nothing to do */
return;
}
/* Mask state request bits + Error ACK */
ac = ESCvar.ALcontrol & ESCREG_AL_STATEMASK;
as = ESCvar.ALstatus & ESCREG_AL_STATEMASK;
an = as;
if (((ac & ESCerror) || (ac == ESCinit)))
{
/* if error bit confirmed reset */
ac &= ESCREG_AL_ERRACKMASK;
an &= ESCREG_AL_ERRACKMASK;
}
/* Error state not acked, leave original */
if ((an & ESCerror) && ((ac & ESCerror) == 0))
{
return;
}
/* Mask high bits ALcommand, low bits ALstatus */
as = (uint8_t)((ac << 4) | (as & 0x0f));
/* Call post state change hook case it have been configured */
if (ESCvar.pre_state_change_hook != NULL)
{
ESCvar.pre_state_change_hook (&as, &an);
}
/* Switch through the state change requested via AlControl from
* current state read in AL status
*/
switch (as)
{
case INIT_TO_INIT:
case PREOP_TO_PREOP:
case OP_TO_OP:
{
break;
}
case INIT_TO_PREOP:
{
/* get station address */
ESC_address ();
an = ESC_startmbx (ac);
break;
}
case INIT_TO_BOOT:
case BOOT_TO_BOOT:
{
/* get station address */
ESC_address ();
an = ESC_startmbxboot (ac);
break;
}
case INIT_TO_SAFEOP:
case INIT_TO_OP:
{
an = ESCinit | ESCerror;
ESC_ALerror (ALERR_INVALIDSTATECHANGE);
break;
}
case OP_TO_INIT:
{
ESC_stopoutput ();
ESC_stopinput ();
ESC_stopmbx ();
an = ESCinit;
break;
}
case SAFEOP_TO_INIT:
{
ESC_stopinput ();
ESC_stopmbx ();
an = ESCinit;
break;
}
case PREOP_TO_INIT:
{
ESC_stopmbx ();
an = ESCinit;
break;
}
case BOOT_TO_INIT:
{
ESC_stopmbx ();
an = ESCinit;
break;
}
case PREOP_TO_BOOT:
case BOOT_TO_PREOP:
case BOOT_TO_SAFEOP:
case BOOT_TO_OP:
{
an = ESCpreop | ESCerror;
ESC_ALerror (ALERR_INVALIDSTATECHANGE);
break;
}
case PREOP_TO_SAFEOP:
case SAFEOP_TO_SAFEOP:
{
ESCvar.ESC_SM2_sml = sizeOfPDO (RX_PDO_OBJIDX, &ESCvar.sm2mappings,
SMmap2, MAX_MAPPINGS_SM2);
if (ESCvar.sm2mappings < 0)
{
an = ESCpreop | ESCerror;
ESC_ALerror (ALERR_INVALIDOUTPUTSM);
break;
}
ESCvar.ESC_SM3_sml = sizeOfPDO (TX_PDO_OBJIDX, &ESCvar.sm3mappings,
SMmap3, MAX_MAPPINGS_SM3);
if (ESCvar.sm3mappings < 0)
{
an = ESCpreop | ESCerror;
ESC_ALerror (ALERR_INVALIDINPUTSM);
break;
}
an = ESC_startinput (ac);
if (an == ac)
{
ESC_SMenable (2);
}
break;
}
case PREOP_TO_OP:
{
an = ESCpreop | ESCerror;
ESC_ALerror (ALERR_INVALIDSTATECHANGE);
break;
}
case OP_TO_PREOP:
{
ESC_stopoutput ();
ESC_stopinput ();
an = ESCpreop;
break;
}
case SAFEOP_TO_PREOP:
{
ESC_stopinput ();
an = ESCpreop;
break;
}
case SAFEOP_TO_BOOT:
{
an = ESCsafeop | ESCerror;
ESC_ALerror (ALERR_INVALIDSTATECHANGE);
break;
}
case SAFEOP_TO_OP:
{
an = ESC_startoutput (ac);
break;
}
case OP_TO_BOOT:
{
an = ESCsafeop | ESCerror;
ESC_ALerror (ALERR_INVALIDSTATECHANGE);
ESC_stopoutput ();
/* If no outputs present, we need to flag error using SM3 */
if (ESCvar.ESC_SM2_sml == 0 && ESCvar.ESC_SM3_sml > 0)
{
ESC_SMdisable (3);
}
break;
}
case OP_TO_SAFEOP:
{
an = ESCsafeop;
ESC_stopoutput ();
break;
}
default:
{
if (an == ESCop)
{
ESC_stopoutput ();
/* If no outputs present, we need to flag error using SM3 */
if (ESCvar.ESC_SM2_sml == 0 && ESCvar.ESC_SM3_sml > 0)
{
ESC_SMdisable (3);
}
an = ESCsafeop;
}
if (as == ESCsafeop)
{
ESC_stopinput ();
}
an |= ESCerror;
ESC_ALerror (ALERR_UNKNOWNSTATE);
break;
}
}
/* Call post state change hook case it have been configured */
if (ESCvar.post_state_change_hook != NULL)
{
ESCvar.post_state_change_hook (&as, &an);
}
if (!(an & ESCerror) && (ESCvar.ALerror))
{
/* clear error */
ESC_ALerror (ALERR_NONE);
}
if (ESC_check_id_request (ESCvar.ALcontrol, &an))
{
an |= ESC_load_device_id ();
}
ESC_ALstatus (an);
#ifdef ESC_DEBUG
DPRINT ("state %s\n", ESC_state_to_string (an & 0xF));
#endif
}
/** Function copying the application configuration variable
* data to the stack local variable.
*
* @param[in] cfg = Pointer to the Application configuration variable
* holding application specific details. Data is copied.
*/
void ESC_config (esc_cfg_t * cfg)
{
static sm_cfg_t mb0 = {MBX0_sma, MBX0_sml, MBX0_sme, MBX0_smc, 0};
static sm_cfg_t mb1 = {MBX1_sma, MBX1_sml, MBX1_sme, MBX1_smc, 0};
static sm_cfg_t mbboot0 = {MBX0_sma_b, MBX0_sml_b, MBX0_sme_b, MBX0_smc_b, 0};
static sm_cfg_t mbboot1 = {MBX1_sma_b, MBX1_sml_b, MBX1_sme_b, MBX1_smc_b, 0};
/* Configure stack */
ESCvar.use_interrupt = cfg->use_interrupt;
ESCvar.watchdogcnt = cfg->watchdog_cnt;
ESCvar.mb[0] = mb0;
ESCvar.mb[1] = mb1;
ESCvar.mbboot[0] = mbboot0;
ESCvar.mbboot[1] = mbboot1;
ESCvar.skip_default_initialization = cfg->skip_default_initialization;
ESCvar.set_defaults_hook = cfg->set_defaults_hook;
ESCvar.pre_state_change_hook = cfg->pre_state_change_hook;
ESCvar.post_state_change_hook = cfg->post_state_change_hook;
ESCvar.application_hook = cfg->application_hook;
ESCvar.safeoutput_override = cfg->safeoutput_override;
ESCvar.pre_object_download_hook = cfg->pre_object_download_hook;
ESCvar.post_object_download_hook = cfg->post_object_download_hook;
ESCvar.pre_object_upload_hook = cfg->pre_object_upload_hook;
ESCvar.post_object_upload_hook = cfg->post_object_upload_hook;
ESCvar.rxpdo_override = cfg->rxpdo_override;
ESCvar.txpdo_override = cfg->txpdo_override;
ESCvar.esc_hw_interrupt_enable = cfg->esc_hw_interrupt_enable;
ESCvar.esc_hw_interrupt_disable = cfg->esc_hw_interrupt_disable;
ESCvar.esc_hw_eep_handler = cfg->esc_hw_eep_handler;
ESCvar.esc_check_dc_handler = cfg->esc_check_dc_handler;
ESCvar.get_device_id = cfg->get_device_id;
}