SOES/soes/esc.c

960 lines
25 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.
*/
static esc_cfg_t * esc_cfg = NULL;
/** 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));
}
/** Read SM Status register 0x805(+ 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)
{
uint16_t dummy;
ESC_read (ESCREG_SM0STATUS + (n << 3), &dummy, 2);
}
/** 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;
uint16_t temp;
sm = (_ESCsm2 *) & ESCvar.SM[n];
ESC_read (ESCREG_SM0STATUS + (n << 3), &temp, 2);
#if defined(EC_LITTLE_ENDIAN)
sm->ActESC = temp >> 8;
sm->Status = temp;
#endif
#if defined(EC_BIG_ENDIAN)
sm->ActESC = temp;
sm->Status = temp >> 8;
#endif
}
/** 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 (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 &= ~ESCREG_SMENABLE_BIT;
ESC_SMwritepdi (n);
}
/** 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 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;
}
/** 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) != MBX0_sma) || (etohs (SM->Length) != MBX0_sml)
|| (SM->Command != 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) != MBX1_sma) || (etohs (SM->Length) != MBX1_sml)
|| (SM->Command != 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)
{
ESC_SMenable (0);
ESC_SMenable (1);
ESC_SMstatus (0);
ESC_SMstatus (1);
if ((state = ESC_checkmbx (state)) & ESCerror)
{
ESC_ALerror (ALERR_INVALIDMBXCONFIG);
MBXrun = 0;
}
else
{
ESCvar.toggle = ESCvar.SM[1].ECrep; //sync repeat request toggle state
MBXrun = 1;
}
return state;
}
/** Check boostrap 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_checkmbxboot (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) != MBX0_sma_b) || (etohs (SM->Length) != MBX0_sml_b)
|| (SM->Command != MBX0_smc_b) || (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) != MBX1_sma_b) || (etohs (SM->Length) != MBX1_sml_b)
|| (SM->Command != MBX1_smc_b) || (ESCvar.SM[1].ECsm == 0))
{
ESCvar.SMtestresult = SMRESULT_ERRSM1;
ESC_SMdisable (0);
ESC_SMdisable (1);
return (uint8_t) (ESCinit | ESCerror); //fail state change
}
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)
{
ESC_SMenable (0);
ESC_SMenable (1);
ESC_SMstatus (0);
ESC_SMstatus (1);
if ((state = ESC_checkmbxboot (state)) & ESCerror)
{
ESC_ALerror (ALERR_INVALIDBOOTMBXCONFIG);
MBXrun = 0;
}
else
{
ESCvar.toggle = ESCvar.SM[1].ECrep; //sync repeat request toggle state
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;
MBXrun = 0;
ESC_SMdisable (0);
ESC_SMdisable (1);
for (n = 0; n < 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;
}
/** 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[0];
uint16_t length;
if (ESCvar.ALstatus == ESCboot)
{
ESC_read (MBX0_sma_b, MB, MBXHSIZE);
length = etohs (MB->header.length);
if (length > (MBX0_sml_b - MBXHSIZE))
{
length = MBX0_sml_b - MBXHSIZE;
}
ESC_read (MBX0_sma_b + MBXHSIZE, &(MB->b[0]), length);
if (length + MBXHSIZE < MBX0_sml_b)
{
ESC_read (MBX0_sme_b, &length, 1);
}
}
else
{
ESC_read (MBX0_sma, MB, MBXHSIZE);
length = etohs (MB->header.length);
if (length > (MBX0_sml - MBXHSIZE))
{
length = MBX0_sml - MBXHSIZE;
}
ESC_read (MBX0_sma + MBXHSIZE, &(MB->b[0]), length);
if (length + MBXHSIZE < MBX0_sml)
{
ESC_read (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)
{
_MBX *MB = &MBX[n];
uint8_t dummy = 0;
uint16_t length;
length = etohs (MB->header.length);
if (ESCvar.ALstatus == ESCboot)
{
if (length > (MBX1_sml_b - MBXHSIZE))
{
length = MBX1_sml_b - MBXHSIZE;
}
ESC_write (MBX1_sma_b, MB, MBXHSIZE + length);
if (length + MBXHSIZE < MBX1_sml_b)
{
ESC_write (MBX1_sme_b, &dummy, 1);
}
}
else
{
if (length > (MBX1_sml - MBXHSIZE))
{
length = MBX1_sml - MBXHSIZE;
}
ESC_write (MBX1_sma, MB, MBXHSIZE + length);
if (length + MBXHSIZE < MBX1_sml)
{
ESC_write (MBX1_sme, &dummy, 1);
}
}
ESCvar.mbxfree = 0;
}
/** TBD
*/
void ESC_ackmbxread (void)
{
uint8_t dummy = 0;
if (ESCvar.ALstatus == ESCboot)
{
ESC_write (MBX1_sma_b, &dummy, 1);
}
else
{
ESC_write (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)
{
_MBX *MB;
uint8_t n = MBXBUFFERS - 1;
while ((n > 0) && (MBXcontrol[n].state))
{
n--;
}
if (n)
{
MBXcontrol[n].state = MBXstate_outclaim;
MB = &MBX[n];
ESCvar.mbxcnt = (++ESCvar.mbxcnt) & 0x07;
if (ESCvar.mbxcnt == 0)
{
ESCvar.mbxcnt = 1;
}
MB->header.address = htoes (0x0000); // destination is master
MB->header.channel = 0;
MB->header.priority = 0;
MB->header.mbxcnt = ESCvar.mbxcnt;
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 = 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];
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;
_MBX *MB = &MBX[0];
if (!MBXrun)
{
/* nothing to do */
return 0;
}
/* SM0/1 access or SMn change event */
if (ESCvar.ALevent & ESCREG_ALEVENT_SM_MASK)
{
ESC_SMstatus (0);
ESC_SMstatus (1);
}
/* outmbx read by master */
if (ESCvar.mbxoutpost && ESCvar.SM[1].IntR)
{
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)
{
/* 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;
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);
/* 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 && !MBXcontrol[0].state && !ESCvar.mbxoutpost
&& !ESCvar.xoe)
{
ESC_readmbx ();
ESCvar.SM[0].MBXstat = 0;
if (etohs (MB->header.length) == 0)
{
MBX_error (MBXERR_INVALIDHEADER);
/* drop mailbox */
MBXcontrol[0].state = MBXstate_idle;
}
if ((MB->header.mbxcnt != 0) && (MB->header.mbxcnt == ESCvar.mbxincnt))
{
/* drop mailbox */
MBXcontrol[0].state = MBXstate_idle;
}
ESCvar.mbxincnt = MB->header.mbxcnt;
return 1;
}
/* ack changes in non used SM */
if (ESCvar.ALevent & ESCREG_ALEVENT_SMCHANGE)
{
ESC_SMack (4);
ESC_SMack (5);
ESC_SMack (6);
ESC_SMack (7);
}
return 0;
}
/** Handler for incorrect or unsupported mailbox data. Write error response
* in Mailbox.
*/
void ESC_xoeprocess (void)
{
_MBXh *mbh;
if (!MBXrun)
{
return;
}
if (!ESCvar.xoe && (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]));
ESC_read (ESCREG_SM3, (void *) &ESCvar.SM[3], sizeof (ESCvar.SM[3]));
SM = (_ESCsm2 *) & ESCvar.SM[2];
if ((etohs (SM->PSA) != SM2_sma) || (etohs (SM->Length) != SM2_sml)
|| (SM->Command != SM2_smc) || !(SM->ActESC & SM2_act))
{
ESCvar.SMtestresult = SMRESULT_ERRSM2;
/* fail state change */
return (ESCpreop | ESCerror);
}
SM = (_ESCsm2 *) & ESCvar.SM[3];
if ((etohs (SM->PSA) != SM3_sma) || (etohs (SM->Length) != SM3_sml)
|| (SM->Command != SM3_smc) || !(SM->ActESC & SM3_act))
{
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))
{
ESC_SMenable (3);
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);
}
}
return state;
}
/** Unconditional stop of updating inputs by disabling Sync Manager 2 & 3.
* Set the App.state to APPSTATE_IDLE.
*
*/
void ESC_stopinput (void)
{
App.state = APPSTATE_IDLE;
ESC_SMdisable (3);
ESC_SMdisable (2);
}
/** 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)
{
ESC_SMenable (2);
App.state |= APPSTATE_OUTPUT;
return state;
}
/** Unconditional stop of updating outputs by disabling Sync Manager 2.
* Set the App.state to APPSTATE_ONPUT. Call application hook APP_safeoutput
* letting the user to set safe state values on outputs.
*
*/
void ESC_stopoutput (void)
{
App.state &= APPSTATE_INPUT;
ESC_SMdisable (2);
APP_safeoutput ();
}
/** The state handler acting on ALControl Bit(0) and SyncManager Activation BIT(4)
* events in the Al Event Request register 0x220.
*
*/
void ESC_state (void)
{
uint8_t ac, an, as, ax, ax23;
uint8_t handle_smchanged = 0;
/* 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);
}
/* Have at least on Sync Manager changed */
else if (ESCvar.ALevent & ESCREG_ALEVENT_SMCHANGE)
{
handle_smchanged = 1;
}
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;
}
/* Enter SM changed handling for all steps but Init and Boot when Mailboxes
* is up and running
*/
if (handle_smchanged && (as & ESCREG_AL_ALLBUTINITMASK) &&
!(as == ESCboot) && 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))
{
/* if in error then stay there */
return;
}
/* Have we been forced to step down to INIT we will stop mailboxes,
* update AL Status Code and exit ESC_state
*/
if (ax == (ESCinit | ESCerror))
{
/* If we have activated Inputs and Outputs we need to disable them */
if (App.state)
{
ESC_stopoutput ();
ESC_stopinput ();
}
/* Stop mailboxes and update ALStatus code */
ESC_stopmbx ();
ESC_ALerror (ALERR_INVALIDMBXCONFIG);
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
*/
if ((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);
return;
}
}
/* Error state not acked, leave original */
if ((an & ESCerror) && !(ac & ESCerror))
{
return;
}
/* Mask high bits ALcommand, low bits ALstatus */
as = (ac << 4) | (as & 0x0f);
/* Call post state change hook case it have been configured */
if ((esc_cfg != NULL) && esc_cfg->pre_state_change_hook)
{
esc_cfg->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 ();
}
case SAFEOP_TO_INIT:
{
ESC_stopinput ();
}
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:
{
SM2_sml = sizeRXPDO ();
SM3_sml = sizeTXPDO ();
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 ();
}
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 ();
break;
}
case OP_TO_SAFEOP:
{
an = ESCsafeop;
ESC_stopoutput ();
break;
}
default:
{
if (an == ESCop)
{
ESC_stopoutput ();
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 ((esc_cfg != NULL) && esc_cfg->post_state_change_hook)
{
esc_cfg->post_state_change_hook (&as, &an);
}
if (!(an & ESCerror) && (ESCvar.ALerror))
{
/* clear error */
ESC_ALerror (ALERR_NONE);
}
ESC_ALstatus (an);
}
/** Function copying the application configuration variable
* to the stack local pointer variable.
*
* @param[in] cfg = Pointer to by the Application static declared
* configuration variable holding application specific details. Ex. post- and
* pre state change hooks
*/
void ESC_config (esc_cfg_t * cfg)
{
esc_cfg = cfg;
}