<pre name="code" class="cpp">/** \defgroup ecatslv ecatslv.c: EtherCAT State Machine \brief Changes to version V5.0: \brief V5.01 APPL3: Include library demo application \brief V5.01 ESC2: Add missed value swapping \brief V5.01 ESM1: Don't overwrite the error reason in case of an failed PS transition \brief V5.01 ESM2: Don't check the "appl trigger" flag in case on an regular transition to a lower state (OS, SP, PI). \brief V5.01 ESM3: Call Error acknowledge indication only if error was acknowledged by the master \brief V5.01 HW3: Update blink code of an SM watchdog error \brief Changes to version V4.42: \brief V5.0 ECAT1: Support Explicit Device ID. \brief V5.0 ECAT2: Application specific functions are moved to application files. \brief V5.0 ECAT3: Global dummy variables used for dummy ESC operations. \brief V5.0 ESC1: ESC 32Bit Access added. \brief V5.0 ESC2: Support ESC EtherCAT LED Indication. \brief V5.0 ESC3: Support EEPROM Emulation. \brief V5.0 ESM1: Update "LocalErrorFlag" handling. \brief V5.0 ESM2: Update Error Acknowledge by ALControl INIT (without error acknowledge) \brief V5.0 ESM3: Handle pending ESM transition \brief V5.0 ESM4: ECAT_StateChange() will only be called form application. In case of an communication error AL_ControlInd is called. \brief V5.0 MBX1: Support configuration without mailbox protocol support. \brief V5.0 TEST1: Add test application. See Application Note ET9300 for more details. \brief Changes to version V4.40:\n \brief V4.42 ESM1: Reset local error flag if master set the acknowledge bit (0x120.4) \brief Changes to version V4.30:\n \brief V4.40 ESM5: Enable output SyncManager if local error acknowledged \brief V4.40 HW0: Use common hardware access functions \brief V4.40 PDO3: Add support if only input process data is used \brief V4.40 ECAT4: Add read SM activation register to acknowledge SM Change event \brief V4.40 PDO2: Check if max process data size was exceed \brief V4.40 DIAG1: add diagnosis message support \brief V4.40 ESM4: Change Check WD setup; add define OP_PD_REQUIRED (defines if process data required in state change to OP) \brief V4.40 WD1: change WD behaviour depending if process data required in OP state \brief V4.40 MBX4: Change processing order of mailbox SyncManager flags \brief V4.40 ECAT1: Merge content of HW_Main (spihw.c /mcihw.c) to ECAT_Main \brief V4.40 ECAT2: Added CheckIfLocalError() to check local flags and set ALStatus /Al Status code if required. This function is called cyclic from MainLoop. \brief V4.40 ESM2: Add AL_ControlRes() to complete pending state requests. Change SafeOP to OP state response \brief V4.40 ESM1: Prevent double call of StopOutputHandler() \brief V4.40 BOOT1: Enable Mailbox SyncManger on state change to BOOT state (to enable FoE) \brief V4.40 ESM3: Change State machine behaviour according to ETG.1000 V1.0.2 (state change #26) \brief V4.40 LED1: Set error blink code \brief V4.40 TIMER1: Added DC_CheckWatchdog() triggered from ECAT_CheckTimer(). Change local Sync0 watchdog variables. Change bus cycle calculation \brief V4.40 WD1: Change check process data watchdog settings \brief Changes to version V4.20:\n \brief V4.30 OBJ 3: initialize the object dictionary in state change INIT->PREOP; clear object dictionary in state change PREOP->INIT \brief V4.30 SYNC: add 0x1C32:10; 0x1C33:10 (Sync0 cycle), change synchronisation control functionality \brief V4.30 CiA402: add CiA402_Init() call in state change from PREOP to SAFEOP if DC synchronisation is enabled, \brief else the Init function is called when bus cycle time is calculated [CalcSMCycleTime() ]. \brief trigger error handling if the EtherCAT state machine gets a transition from OP to an "lower" state \brief V4.20 ECAT 1: add LEGACY_MODE behaviour in ECAT_CheckWatchdog() \brief V4.20 DC 1: Add DC pending state machine handling and Dc watchdog functionality \brief V4.20 ESM 2: Add State transition from BOOT to INIT \brief V4.20 ESM 1: Non LEGACY_MODE State change handling \brief V4.11 Renamed the function parameter "code" of Function "SendSmFailedEmergency() to avoid \brief problems with some compilers"\n \brief V4.11 ECAT 1: Fixed a possible problem with state change Init -> SafeOP. The output syncmanager \brief was enabled by the state change-flag and not by the actual state\n \brief V4.11 LED 1: Clear the error LED during error acknowledgement\n \brief V4.11 ESC 1: fixed size of MBXHEADER in the TFOEMBX struct \n \brief Changes to version V4.08:\n \brief V4.10 ECAT 1: clear bEcatOutputsReceived in startMailboxhandler()\n \brief V4.10 ECAT 2: clear bEcatOutputsReceived in stopMailboxhandler()\n \brief V4.10 ECAT 3: when switching from INIT to BOOT the SM settings shall be checked\n \brief V4.10 ECAT 4: APPL_StartInputHandler shall always be called and bEcatInputUpdateRunning shall always be set \brief in StartInputHandler independent of the input size\n \brief V4.10 ECAT 5: AL_ControlInd: the error acknowledge behaviour was changed \brief according to the protocol enhancements and the conformance test\n \brief V4.10 ECAT 6: AL_ControlInd: if a state transitions failed the corresponding stop function is \brief called to get a consistent set of variables\n \brief V4.10 ECAT 7: the local application requested to leave the state OP so we have to disable the SM2 \brief and make the state change from OP to SAFEOP by calling StopOutputHandler\n \brief V4.10 ECAT 8: the AL Status Code has to be reset if the error was acknowledged by the master\n \brief V4.10 ECAT 9: ECAT_StateChange: when waiting for a State Change response from the application the \brief AL Status shall only be written if the final state was reached\n \brief Changes to version V4.07:\n \brief V4.08 ECAT 1: The watchdog value was not rounded up\n \brief V4.08 ECAT 2: The value of u16WdValue was not set 0 if the register 0x420 is 0\n \brief V4.08 ECAT 3: The AlStatusCode is changed as parameter of the function AL_ControlInd\n \brief V4.08 ECAT 4: In a state transition OP2PREOP, SAFEOP2INIT or OP2INIT is requested, \brief this was not working correctly if one of the application functions \brief APPL_StopInputHandler or APPL_StopOutputHandler were returning NOERROR_INWORK \brief (because only the first state transition was made in that case)\n \brief V4.08 AOE 1: AoE was added\n \brief Changes to version V4.06:\n \brief V4.07 ECAT 1: The sources for SPI and MCI were merged (in ecat_def.h \brief set the switch MCI_HW to 1 when using the MCI, \brief set the switch SPI_HW to 1 when using the SPI\n \brief Changes to version V4.00:\n \brief V4.01 ECAT 1: The Output sync Manager was not disabled when the state OP was left \brief by a local request (watchdog or io error)\n \brief V4.01 ECAT 2: APPL_StopOutputHandler returns an UINT16\n \brief V4.01 ECAT 3: TwinCAT compatibility mode: The state transition to OP is allowed when the \brief WD-Trigger-Bit of the SM2-Control-Byte (0x814.6) is FALSE, in that case the \brief watchdog will not be started before the outputs were received the first time\n \brief V4.01 ECAT 4: "else" was too much\n \brief Changes to version V3.20:\n \brief V4.00 ECAT 1: The handling of the Sync Manager Parameter was included according to \brief the EtherCAT Guidelines and Protocol Enhancements Specification\n \brief V4.00 ECAT 2: The output sync manager is initialized during the state transition \brief from PREOP to SAFEOP that the master can check if the slave could update \brief inputs and outputs before switching the slave to OP\n \brief behaviour according to the EtherCAT Guidelines and Protocol Enhancements Specification\n \brief V4.00 ECAT 3: The watchdog will be enabled in SAFE-OP that it can be checked if the last SM event \brief was received during the watchdog time before switching to OP\n \brief V4.00 ECAT 4: The function CheckSmChannelParameters is included in the function \brief CheckSmSettings to get a better overview\n \brief V4.00 ECAT 5: In synchronous mode the slave should support 1- and 3-buffer mode, 3-buffer mode \brief should be the standard setting, because the controlling if the process data was updated \brief should be done with the TxPDO Toggle, but the 1-buffer mode should be setable too, \brief that the master could easily check if all slaves are synchronous by checking the \brief the working counter (if the outputs were not read or the inputs were not written \brief the ESC of the slave would not increment the working counter with expected value \brief if the 1-buffer mode is running)\n \brief V4.00 ECAT 6: The function ECAT_StateChange was added, which the application should call if a local error \brief is detected (with the parameters alStatus = STATE_SAFEOP, alStatusCode = error code (> 0x1000)) \brief or gone (with the parameters alStatus = STATE_OP, alStatusCode = 0) \brief or if one of the functions APPL_StartMailboxHandler, APPL_StopMailboxHandler, APPL_StartInputHandler, \brief APPL_StopInputHandler, APPL_StartOutputHandler, APPL_StopOutputHandler has returned NOERROR_INWORK \brief to acknowledge the last state transition (with the parameters alStatus = new AL-Status, alStatusCode = \brief new AL-Status-Code)\n \brief V4.00 ECAT 7: The return values for the AL-StatusCode were changed to UINT16\n \version 5.01 */ //--------------------------------------------------------------------------------------- /** \ingroup ecatslv \file ecatslv.c \brief Implementation. */ //--------------------------------------------------------------------------------------- /*----------------------------------------------------------------------------------------- ------ ------ Includes ------ -----------------------------------------------------------------------------------------*/ #define _ECATSLV_ 1 #include "ecatslv.h" #undef _ECATSLV_ #define _ECATSLV_ 0 #include "mailbox.h" #include "ecatcoe.h" #include "ecatappl.h" #include "objdef.h" #include "coeappl.h" #include "el9800appl.h" /*-------------------------------------------------------------------------------------- ------ ------ local Types and Defines ------ --------------------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------------------- ------ ------ local variables and constants ------ -----------------------------------------------------------------------------------------*/ UINT16 u16ALEventMask; // Value which will be written to the 0x204 register (AL event mask) during the state transition PreOP to SafeOP /*Dummy variable to trigger read or writes events in the ESC*/ VARVOLATILE UINT16 u16dummy; /*----------------------------------------------------------------------------------------- ------ ------ local functions ------ -----------------------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------------------- ------ ------ functions ------ -----------------------------------------------------------------------------------------*/ /** \addtogroup ecatslv @{ */ ///////////////////////////////////////////////////////////////////////////////////////// /** \param maxChannel 被检查的最后一个SM \return 0: 成功或者返回AL的状态码 \brief 这个函数检查所有的SM通道 *//////////////////////////////////////////////////////////////////////////////////////// UINT8 CheckSmSettings(UINT8 maxChannel) { UINT8 i; UINT8 result = 0; TSYNCMAN ESCMEM *pSyncMan; UINT16 SMLength = 0; UINT16 SMAddress = 0; /* 检查接收邮箱的的SM参数(SM0) */ pSyncMan = HW_GetSyncMan(MAILBOX_WRITE); SMLength = pSyncMan->Length; SMAddress = pSyncMan->PhysicalStartAddress; if (!(pSyncMan->Settings[SM_SETTING_ACTIVATE_OFFSET] & SM_SETTING_ENABLE_VALUE)) /* 接收邮箱不能使能 */ result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; else if ( (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_DIRECTION_MASK) != SM_SETTING_DIRECTION_WRITE_VALUE) /* 接收邮箱由主站不能写*/ result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; else if ( (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_MODE_MASK) != SM_SETTING_MODE_ONE_BUFFER_VALUE ) /* 接收邮箱不是在一个缓存模式 */ result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; else if ( SMLength < MIN_MBX_SIZE ) /* 接收邮箱的大小太小 */ result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; else if ( SMLength > MAX_MBX_SIZE ) /* 接收邮箱大小太大 */ result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; else if ( SMAddress < MIN_MBX_WRITE_ADDRESS ) /* 接收邮箱的地址太小 */ result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; else if ( SMAddress > MAX_MBX_WRITE_ADDRESS) /* 接收邮箱地址太大 */ result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; if ( result == 0 )//则说明接收邮箱通过上面的检查 { /* 检查发送邮箱的SM参数(SM1) */ pSyncMan = HW_GetSyncMan(MAILBOX_READ); SMLength = pSyncMan->Length; SMAddress = pSyncMan->PhysicalStartAddress; if (!(pSyncMan->Settings[SM_SETTING_ACTIVATE_OFFSET] & SM_SETTING_ENABLE_VALUE)) /* 发送邮箱不使能 */ result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; else if ( (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_DIRECTION_MASK) != SM_SETTING_DIRECTION_READ_VALUE) /* 接收邮箱不能被主站读*/ result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; else if ( (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_MODE_MASK) != SM_SETTING_MODE_ONE_BUFFER_VALUE ) /* 接收邮箱不是一个缓冲区模式 */ result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; else if ( SMLength < MIN_MBX_SIZE ) /* 发送邮箱的大小太小 */ result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; else if ( SMLength > MAX_MBX_SIZE ) /* 发送邮箱的大小太大 */ result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; else if ( SMAddress < MIN_MBX_READ_ADDRESS ) /* 发送邮箱的地址太小 */ result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; else if ( SMAddress > MAX_MBX_READ_ADDRESS ) /* 发送邮箱的地址太大 */ result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; } if ( result == 0 && maxChannel > PROCESS_DATA_IN )//发送邮箱通过上面的检查 { /* 当离开这个函数的时候,b3BufferMode被设置,如果输入和输出运行在3个缓冲区模式 */ b3BufferMode = TRUE; /* 检查SM的参数给输入通道(SM的通道3) */ pSyncMan = HW_GetSyncMan(PROCESS_DATA_IN); <span style="white-space:pre"> </span>SMLength = pSyncMan->Length; <span style="white-space:pre"> </span>SMAddress = pSyncMan->PhysicalStartAddress; if ( (pSyncMan->Settings[SM_SETTING_ACTIVATE_OFFSET] & SM_SETTING_ENABLE_VALUE) != 0 && SMLength == 0 ) /* SM3的大小是等于0和SM3是激活的 */ result = SYNCMANCHSETTINGS+1; else if (pSyncMan->Settings[SM_SETTING_ACTIVATE_OFFSET] & SM_SETTING_ENABLE_VALUE) { /* SM3是激活的,输入大小是大于0的 */ if ( SMLength != nPdInputSize || nPdInputSize == 0 || SMLength > MAX_PD_INPUT_SIZE) /* 大小不想符合sizes don't match */ result = SYNCMANCHSIZE+1; else /* 大小相互符合sizes matches */ if ( (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_DIRECTION_MASK) == SM_SETTING_DIRECTION_READ_VALUE ) { /* settings match */ if ( ( ( nAlStatus == STATE_PREOP )&&( SMAddress >= MIN_PD_READ_ADDRESS )&&( SMAddress <= MAX_PD_READ_ADDRESS ) ) ||( ( nAlStatus != STATE_PREOP )&&( SMAddress == nEscAddrInputData ) ) ) { /* 地址符合addresses match */ if ( (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_MODE_MASK) == SM_SETTING_MODE_ONE_BUFFER_VALUE ) /* 如果输入输入运行在1个缓冲区的模式,重置b3BufferMode inputs are running in 1-Buffer-Mode, reset flag b3BufferMode */ b3BufferMode = FALSE; } else /* 输入地址是超越了允许的范围,或者已经在SAFEOP和OP之间改变input address is out of the allowed area or has changed in SAFEOP or OP */ result = SYNCMANCHADDRESS+1; } else /* 输入设置不相符合input settings do not match */ result = SYNCMANCHSETTINGS+1; } else if ( SMLength != 0 || nPdInputSize != 0 ) /* 输入大小不等于0,即使SM3的通道是不能使能input size is not zero although the SM3 channel is not enabled */ result = SYNCMANCHSIZE+1; if ( result != 0 )//则出错处理 { result = ALSTATUSCODE_INVALIDSMINCFG; } }//结束result==0&&maxChannel... if ( result == 0 && maxChannel > PROCESS_DATA_OUT ) { /* 检查SM参数给输入(SM2)check the Sync Manager Parameter for the Outputs (Sync Manager Channel 2) */ pSyncMan = HW_GetSyncMan(PROCESS_DATA_OUT); SMLength = pSyncMan->Length; SMAddress = pSyncMan->PhysicalStartAddress; if ( (pSyncMan->Settings[SM_SETTING_ACTIVATE_OFFSET] & SM_SETTING_ENABLE_VALUE) != 0 && SMLength == 0 ) /* SM2的大小是等于0或者SM2是使能the SM2 size is 0 and the SM2 is active */ result = SYNCMANCHSETTINGS+1; else if (pSyncMan->Settings[SM_SETTING_ACTIVATE_OFFSET] & SM_SETTING_ENABLE_VALUE) { /* 如果SM2的通道是激活的,输出的大小是比0大Sync Manager Channel 2 is active, output size has to greater 0 */ if ( SMLength == nPdOutputSize && nPdOutputSize != 0 && SMLength <= ((UINT16)MAX_PD_OUTPUT_SIZE)) { /* 大小相互符合sizes match */ if ( (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_DIRECTION_MASK) == SM_SETTING_DIRECTION_WRITE_VALUE ) { /* 设置符合要求settings match */ if ( ( ( nAlStatus == STATE_PREOP )&&( SMAddress >= MIN_PD_WRITE_ADDRESS )&&( SMAddress <= MAX_PD_WRITE_ADDRESS ) ) ||( ( nAlStatus != STATE_PREOP )&&( SMAddress == nEscAddrOutputData ) ) ) { /* 地址符合addresses match */ { /* 检查是否看门狗触发使能check, if watchdog trigger is enabled */ if (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_WATCHDOG_VALUE) { bWdTrigger = TRUE; } else { bWdTrigger = FALSE; } if ( (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_MODE_MASK) == SM_SETTING_MODE_ONE_BUFFER_VALUE ) /* 输出是运行在1个缓冲区的模式,重置标志位b3BufferMode */ b3BufferMode = FALSE; } } else /* 输出地址已经超越允许的范围或者在SAFEOP和OP里面转变output address is out of the allowed area or has changed in SAFEOP or OP */ result = SYNCMANCHADDRESS+1; } else /* 输出设置不相符合output settings do not match */ result = SYNCMANCHSETTINGS+1; } else /* 输出大小不相符合output sizes don't match */ result = SYNCMANCHSIZE+1; } else if ( SMLength != 0 || nPdOutputSize != 0 ) /* 输出大小不等于0,虽然SM2通道不使能output size is not zero although the SM2 channel is not enabled */ result = SYNCMANCHSIZE+1; if ( result != 0 )//出错状态 { result = ALSTATUSCODE_INVALIDSMOUTCFG; } }//对应上面的result==0&&maxChannel... if ( result == 0 ) { UINT8 SMActivate = 0; /* 其它SM通道的使能字节被读来响应SM-改变-中断the Enable-Byte of the rest of the SM channels has to be read to acknowledge the SM-Change-Interrupt */ for (i = maxChannel; i < nMaxSyncMan; i++) { pSyncMan = HW_GetSyncMan(i); SMActivate = pSyncMan->Settings[SM_SETTING_ACTIVATE_OFFSET]; } } return result; } ///////////////////////////////////////////////////////////////////////////////////////// /** \return AL Status Code (see ecatslv.h ALSTATUSCODE_....) \brief This function is called in case of the state transition from PREOP to SAFEOP. |brief the areas of the Sync Managers will be checked for overlapping, \brief the synchronization mode (Free Run, Synchron, Distributed Clocks) is selected, \brief the requested cycle time will be checked, the watchdog is started \brief and the AL Event Mask register will be set *//////////////////////////////////////////////////////////////////////////////////////// UINT16 StartInputHandler(void) { TSYNCMAN ESCMEM * pSyncMan; UINT16 dcControl; UINT32 cycleTimeSync0 = 0; UINT32 cycleTimeSync1 = 0; UINT16 wdiv; UINT16 wd; UINT16 nPdInputBuffer = 3; UINT16 nPdOutputBuffer = 3; u16ALEventMask = 0; bEcatFirstOutputsReceived = FALSE; /* get a pointer to the Sync Manager Channel 2 (Outputs) */ pSyncMan = HW_GetSyncMan(PROCESS_DATA_OUT); /* store the address of the Sync Manager Channel 2 (Outputs) */ nEscAddrOutputData = pSyncMan->PhysicalStartAddress; /* get the number of output buffer used for calculating the address areas */ if ( pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_MODE_ONE_BUFFER_VALUE ) nPdOutputBuffer = 1; /* get a pointer to the Sync Manager Channel 3 (Inputs) */ pSyncMan = HW_GetSyncMan(PROCESS_DATA_IN); /* store the address of the Sync Manager Channel 3 (Inputs) */ nEscAddrInputData = pSyncMan->PhysicalStartAddress; /* get the number of input buffer used for calculating the address areas */ if ( pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_MODE_ONE_BUFFER_VALUE ) nPdInputBuffer = 1; /* it has be checked if the Sync Manager memory areas for Inputs and Outputs will not overlap the Sync Manager memory areas for the Mailbox */ if ( ((nEscAddrInputData + nPdInputSize * nPdInputBuffer) > u16EscAddrSendMbx && (nEscAddrInputData < (u16EscAddrSendMbx + u16SendMbxSize))) ||((nEscAddrInputData + nPdInputSize * nPdInputBuffer) > u16EscAddrReceiveMbx && (nEscAddrInputData < (u16EscAddrReceiveMbx + u16ReceiveMbxSize))) ) { return ALSTATUSCODE_INVALIDSMINCFG; } if ( ((nEscAddrOutputData + nPdOutputSize * nPdOutputBuffer) > u16EscAddrSendMbx && (nEscAddrOutputData < (u16EscAddrSendMbx + u16SendMbxSize))) ||((nEscAddrOutputData + nPdOutputSize * nPdOutputBuffer) > u16EscAddrReceiveMbx && (nEscAddrOutputData < (u16EscAddrReceiveMbx + u16ReceiveMbxSize))) || ((nEscAddrOutputData + nPdOutputSize * nPdOutputBuffer) > nEscAddrInputData && (nEscAddrOutputData < (nEscAddrInputData + nPdInputSize))) ) { /* Sync Manager Channel 2 memory area (Outputs) overlaps the Sync Manager memory areas for the Mailbox or the Sync Manager Channel 3 memory area (Inputs) */ return ALSTATUSCODE_INVALIDSMOUTCFG; } /* check the DC-Registers */ /*Read registers 0x980:0x981 (corresponding masks are adapted)*/ HW_EscReadWord(dcControl, ESC_DC_UNIT_CONTROL_OFFSET); dcControl = SWAPWORD(dcControl); /* check if Distributed Clocks are enabled */ if ( dcControl & (ESC_DC_SYNC0_ACTIVE_MASK | ESC_DC_SYNC1_ACTIVE_MASK) ) { /* DC Mode is requested */ /* Distributed Clocks are enabled, check the correct SYNC0/SYNC1-setting, DC_SYNC_ACTIVE is set per default to SYNC0 (in ecatslv.h) but this can be overwritten in ecat_def.h */ if ( dcControl != (ESC_DC_SYNC_UNIT_ACTIVE_MASK | DC_SYNC_ACTIVE) ) { return ALSTATUSCODE_DCINVALIDSYNCCFG; } else { /* DC mask for the AL-Event-Mask-Register (0x204) DC_EVENT_MASK is set by default to PROCESS_OUTPUT_EVENT (optimized DC Cycle in the Synchronization document) but this can be overwritten in ecat_def.h */ u16ALEventMask = DC_EVENT_MASK; } /* slave is running in DC-mode */ bDcSyncActive = TRUE; // Cycletime for Sync0 HW_EscReadDWord(cycleTimeSync0, ESC_DC_SYNC0_CYCLETIME_OFFSET); /* ECATCHANGE_START(V5.01) ESC2*/ cycleTimeSync0 = SWAPDWORD(cycleTimeSync0); /* ECATCHANGE_END(V5.01) ESC2*/ // Cycletime for Sync1 HW_EscReadDWord(cycleTimeSync1, ESC_DC_SYNC1_CYCLETIME_OFFSET); /* ECATCHANGE_START(V5.01) ESC2*/ cycleTimeSync1 = SWAPDWORD(cycleTimeSync1); /* ECATCHANGE_END(V5.01) ESC2*/ } else { bDcSyncActive = FALSE; /* check entry 0x1C32:01, if free run or synchron synchronization mode is requested */ if ( sSyncManOutPar.u16SyncType != SYNCTYPE_FREERUN ) { /* ECAT Synchron Mode, the ESC interrupt is enabled */ bEscIntEnabled = TRUE; } else { /* ECAT FreeRun Mode, sync manager has to run in 3-Buffer Mode */ if ( !b3BufferMode ) /* 1-Buffer-Mode, refuse the state transition */ return ALSTATUSCODE_FREERUNNEEDS3BUFFERMODE; } } /* if ECAT Synchron Mode is active and the output size is zero, the ESC interrupt will be generated by the PROCESS_INPUT_EVENT */ if ( bEscIntEnabled && nPdOutputSize == 0 ) { /* Sync Manager Channel 3 event has to activated in the AL-Event mask register */ u16ALEventMask |= PROCESS_INPUT_EVENT; } /* check if the requested cycle time exceeds the minimum and maximum values of the cycle time (MIN_PDCYCLE_TIME and MAX_PD_CYCLE_TIME are defined in ecat_def.h) */ if(bDcSyncActive) { /*bus is synchronised to Sync1 => check if Sync1 cycle time is valid*/ if(cycleTimeSync1 != 0) { if (cycleTimeSync1 < MIN_PD_CYCLE_TIME || cycleTimeSync1 > MAX_PD_CYCLE_TIME) return ALSTATUSCODE_DCSYNC1CYCLETIME; } else { if ( cycleTimeSync0 != 0 && (cycleTimeSync0 < MIN_PD_CYCLE_TIME || cycleTimeSync0 > MAX_PD_CYCLE_TIME) ) return ALSTATUSCODE_DCSYNC0CYCLETIME; } } /* reset the error counter indicating synchronization problems */ sCycleDiag.syncFailedCounter = 0; /*get the watchdog time (register 0x420). if value is > 0 watchdog is active*/ HW_EscReadWord(wd, ESC_PD_WD_TIME); /* ECATCHANGE_START(V5.01) ESC2*/ wd = SWAPWORD(wd); /* ECATCHANGE_END(V5.01) ESC2*/ if (nPdOutputSize > 0 && wd != 0 ) { /*get watchdog divider (register 0x400)*/ HW_EscReadWord(wdiv, ESC_WD_DIVIDER_OFFSET); /* ECATCHANGE_START(V5.01) ESC2*/ wdiv = SWAPWORD(wdiv); /* ECATCHANGE_END(V5.01) ESC2*/ if ( wdiv != 0 ) { /* the ESC subtracts 2 in register 0x400 so it has to be added here */ UINT32 d = wdiv+2; d *= wd; /* store watchdog in ms in variable u16WdValue */ /* watchdog value has to be rounded up */ d += 24999; d /= 25000; EcatWdValue = (UINT16) d; } else { wd = 0; /* wd value has to be set to zero, if the wd is 0 */ EcatWdValue = 0; } } else { /* the watchdog is deactivated or slave has no output process data*/ wdiv = 0; EcatWdValue = 0; } if((EcatWdValue == 0 && bWdTrigger) || (EcatWdValue != 0 && !bWdTrigger)) { /* if the WD-Trigger in the Sync Manager Channel 2 Control-Byte is set (Bit 6 of Register 0x814) an error has to be returned */ return ALSTATUSCODE_INVALIDWDCFG; } if ( bEscIntEnabled && nPdOutputSize != 0 ) { /* ECAT synchron Mode is active, the Sync Manager Channel 2 event has to activated in the AL-Event mask register */ u16ALEventMask |= PROCESS_OUTPUT_EVENT; } /*The application ESM function is separated from this function to handle pending transitions*/ Sync0WdValue = 0; Sync0WdCounter = 0; bDcRunning = FALSE; bSmSyncToggle = FALSE; bPllRunning = FALSE; i16WaitForPllRunningTimeout = 0; sSyncManInPar.u16SyncError = 0; sSyncManOutPar.u16SyncError = 0; sSyncManOutPar.u32SmEventMissedCounter = 0; /* update the entries 0x1C32:01 and 0x1C33:01 */ if ( bDcSyncActive ) { /*calculate the Sync0 Watchdog counter value the minimum value is 1 ms if the sync0 cycle is greater 500us the Sync0 Wd value is 2*Sycn0 cycle */ if(cycleTimeSync0 == 0) { Sync0WdValue = 0; } else { UINT32 Sync0Cycle = cycleTimeSync0/100000; if(Sync0Cycle < 5) { /*Sync0 cycle less than 500us*/ Sync0WdValue = 1; } else { Sync0WdValue = (Sync0Cycle*2)/10; } } if(cycleTimeSync1 != 0) { /*the applications synchronised to Sync1*/ sSyncManOutPar.u32Sync0CycleTime = cycleTimeSync0; sSyncManInPar.u32Sync0CycleTime = cycleTimeSync0; sSyncManOutPar.u32CycleTime = cycleTimeSync1; sSyncManInPar.u32CycleTime = cycleTimeSync1; sSyncManOutPar.u16SyncType = SYNCTYPE_DCSYNC1; sSyncManInPar.u16SyncType = SYNCTYPE_DCSYNC1; } else { sSyncManOutPar.u32CycleTime = cycleTimeSync0; sSyncManInPar.u32CycleTime = cycleTimeSync0; /*the bus is synchronised with Sync0*/ sSyncManOutPar.u16SyncType = SYNCTYPE_DCSYNC0; sSyncManInPar.u16SyncType = SYNCTYPE_DCSYNC0; } } else if ( bEscIntEnabled ) { /* ECAT Synchron Mode */ sSyncManOutPar.u16SyncType = SYNCTYPE_SYNCHRON; sSyncManInPar.u16SyncType = SYNCTYPE_SM2INT; /*Trigger bus cycle calculation*/ sSyncManOutPar.u16GetCycleTime = 1; } else { /* ECAT FreeRun Mode */ sSyncManOutPar.u16SyncType = SYNCTYPE_FREERUN; sSyncManInPar.u16SyncType = SYNCTYPE_FREERUN; } /*CiA specific code is moved to CiA402 ESM functions*/ if(nPdOutputSize > 0) { HW_EnableSyncManChannel(PROCESS_DATA_OUT); } if(nPdInputSize > 0) { HW_EnableSyncManChannel(PROCESS_DATA_IN); } /*write initial input data*/ PDO_InputMapping(); return ALSTATUSCODE_NOERROR; } ///////////////////////////////////////////////////////////////////////////////////////// /** \return AL Status Code (see ecatslv.h ALSTATUSCODE_....) \brief This function is called in case of the state transition from SAFEOP to OP. \brief It will be checked if outputs had to be received before switching to OP \brief and the state transition would be refused if outputs are missing *//////////////////////////////////////////////////////////////////////////////////////// UINT16 StartOutputHandler(void) { UINT16 result = ALSTATUSCODE_NOERROR; if(bLocalErrorFlag) { /*Local error still exists => skip state request to OP and response with "u16LocalErrorCode"*/ return u16LocalErrorCode; } /*The application ESM function is separated from this function to handle pending transitions*/ /*DC synchronisation is active wait until pll is valid*/ if(bDcSyncActive) { i16WaitForPllRunningTimeout = 200; i16WaitForPllRunningCnt = 0; result = NOERROR_INWORK; } return result; } ///////////////////////////////////////////////////////////////////////////////////////// /** \brief This function is called in case of the state transition from OP to SAFEOP \brief the outputs can be set to an application specific safe state, \brief the state transition can be delayed by returning NOERROR_INWORK *//////////////////////////////////////////////////////////////////////////////////////// void StopOutputHandler(void) { /* reset the flags that outputs were received and that the slave is in OP */ bEcatFirstOutputsReceived = FALSE; bEcatOutputUpdateRunning = FALSE; /*The application ESM function is separated from this function to handle pending transitions*/ } ///////////////////////////////////////////////////////////////////////////////////////// /** \brief This function is called in case of the state transition from SAFEOP to PREOP *//////////////////////////////////////////////////////////////////////////////////////// void StopInputHandler(void) { if(nPdOutputSize > 0) { /* disable the Sync Manager Channel 2 (outputs) */ HW_DisableSyncManChannel(PROCESS_DATA_OUT); } if(nPdInputSize > 0) { /*disable Sync Manager 3 (inputs) if no outputs available*/ HW_DisableSyncManChannel(PROCESS_DATA_IN); } /* reset the events in the AL Event mask register (0x204) */ HW_ResetALEventMask( ~(SYNC0_EVENT | SYNC1_EVENT | PROCESS_INPUT_EVENT | PROCESS_OUTPUT_EVENT) ); /* reset the flags */ bEcatFirstOutputsReceived = FALSE; bEscIntEnabled = FALSE; /*The application ESM function is separated from this function to handle pending transitions*/ bDcSyncActive = FALSE; bDcRunning = FALSE; bPllRunning = FALSE; bSmSyncToggle = FALSE; i16WaitForPllRunningTimeout = 0; bWdTrigger = FALSE; bEcatInputUpdateRunning = FALSE; } ///////////////////////////////////////////////////////////////////////////////////////// /** \param alStatus New AL Status (written to register 0x130) \param alStatusCode New AL Status Code (written to register 0x134) \brief The function changes the state of the EtherCAT ASIC to the requested. *//////////////////////////////////////////////////////////////////////////////////////// void SetALStatus(UINT8 alStatus, UINT16 alStatusCode) { UINT16 Value = alStatusCode; /*update global status variable if required*/ if(nAlStatus != alStatus) //全局变量nAlStatus的状态值更新 { nAlStatus = alStatus; } if (alStatusCode != 0xFFFF) //如果AL状态不为0XFFFF { Value = SWAPWORD(Value); HW_EscWriteWord(Value,ESC_AL_STATUS_CODE_OFFSET); //对地址0X134写数据,更新应用层状态码 } Value = nAlStatus; //0X0001 Value = SWAPWORD(Value); //交换高低8位,原因,写函数先写高8位,后写低8位 HW_EscWriteWord(Value,ESC_AL_STATUS_OFFSET); //对地址0X130写数据 先写0X130,后写0X131 /*The Run LED state is set in Set LED Indication, only the Error LED blink code is set here,在此只设置故障显示led */ /*set Error blink code*/ //AL状态错误下的外设设置情况 if(alStatusCode == 0x00 || !(alStatus & STATE_CHANGE)) { u8EcatErrorLed = LED_OFF; //无状态或无状态切换,正确则灯暗 } else if((alStatusCode == ALSTATUSCODE_NOSYNCERROR) || (alStatusCode == ALSTATUSCODE_DCPLLSYNCERROR)) { u8EcatErrorLed = LED_SINGLEFLASH;// 单闪烁 } else if((alStatusCode == ALSTATUSCODE_SMWATCHDOG)) { /* ECATCHANGE_START(V5.01) HW3*/ u8EcatErrorLed = LED_DOUBLEFLASH; //双闪烁 /* ECATCHANGE_END(V5.01) HW3*/ } else { u8EcatErrorLed = LED_BLINKING; //单闪灯亮 } } ///////////////////////////////////////////////////////////////////////////////////////// /** \param alControl 请求的新的状态 \param alStatusCode 请求的状态码 \brief 这个函数处理EtherCAT状态机。它被调用 * 在AL控制时间发生(0x220的位0)触发,当主站写AL控制寄存器,其中alControl包含AL控制的内容(0x120)的时候 * 当SM改变事件(0x220)的第4位,当激活SYNCM y寄存器被主站写(从Ecat_Main函数),alControl包含实际的状态在(0x130)的位0到3 * 防止本地的看门狗溢出(从ECAT_Main函数),alControl包含一个新的请求的状态(SAFE_OP状态)的时候 *防止应用程序特殊的事件来改变EtherCAT的状态(从应用程序),alControl包含新的请求状态(INIT,PRE_OP和SAFE_OP状态)的时候 *//////////////////////////////////////////////////////////////////////////////////////// void AL_ControlInd(UINT8 alControl, UINT16 alStatusCode) { UINT16 result = 0; UINT8 bErrAck = 0; UINT8 stateTrans; /*deactivate ESM timeout counter*/ EsmTimeoutCounter = -1; bApplEsmPending = TRUE; /* 为了能被主站响应,重置错误标号 */ if ( alControl & STATE_CHANGE ) { bErrAck = 1; nAlStatus &= ~STATE_CHANGE; /*使能SM2,SM2被移到状态转换块。首先检查SM的设置.*/ } else if ( (nAlStatus & STATE_CHANGE) // HBu 17.04.08: the error has to be acknowledged before when sending the same (or a higher) state // (the error was acknowledged with the same state before independent of the acknowledge flag) /*Error Acknowledge with 0xX1 is allowed*/ && (alControl & STATE_MASK) != STATE_INIT ) /* the error flag (Bit 4) is set in the AL-Status and the ErrAck bit (Bit 4) is not set in the AL-Control, so the state cannot be set to a higher state and the new state request will be ignored */ return; else { nAlStatus &= STATE_MASK; } /* 产生一个变量给状态转换(位0-3:新的状态(AL控制),位4-7:旧的状态) */ alControl &= STATE_MASK; stateTrans = nAlStatus; stateTrans <<= 4; stateTrans += alControl; /* 根据状态转换,检查SYNCM的设置check the SYNCM settings depending on the state transition */ switch ( stateTrans ) { case INIT_2_PREOP: case OP_2_PREOP: case SAFEOP_2_PREOP: case PREOP_2_PREOP: /* 在预运行状态(PREOP),只有SYNCM给SYNCM0和SYNCM1(邮箱)的设置被检查,只要结果不等于0, 从站将会停留或者转换到INIT状态和设置AL-Status的ErrorInd 位(位4) */ result = CheckSmSettings(MAILBOX_READ+1); break; case PREOP_2_SAFEOP: /* 在检查SYNCM的设置给SYNCM2和SYNCM3(过程数据)之前, 需要的输入数据(nPdInputSize)和输出数据(nPdOutputSize)的长度可以被改变(通过PDO-Assign和PDO-Mapping)。 如果检查结果不等于0,从站将会停留在PREOP和设置AL-状态的错误位(位4) */ result = APPL_GenerateMapping(&nPdInputSize,&nPdOutputSize); if (result != 0) break; case SAFEOP_2_OP: case OP_2_SAFEOP: case SAFEOP_2_SAFEOP: case OP_2_OP: /* 在安全运行(SAFEOP)和运行(OP)阶段, SYNCM设置给所有的SYNCM将被检查, 如果检查结果不等于0,从站将会停留或者转换到PREOP和设置AL-Status的错误位(位4) */ result = CheckSmSettings(nMaxSyncMan); break; } if ( result == 0 ) { /* 如果result的结果等于0,将会执行对应的本地处理服务根据状态的转换execute the corresponding local management service(s) depending on the state transition */ nEcatStateTrans = 0; switch ( stateTrans ) { case INIT_2_BOOT : result = ALSTATUSCODE_BOOTNOTSUPP; break; case BOOT_2_INIT : result = ALSTATUSCODE_BOOTNOTSUPP; break; case INIT_2_PREOP : { /*在mailbox.c里面的函数MBX_StartMailboxHandler函数, 检查是否邮箱SM区域SM0和SM1是否存在相互的重叠? 如果结果是不等于0,则从站将停留在INIT状态和设置AL-Status的错误位(位4 */ result = MBX_StartMailboxHandler(); if (result == 0) { bApplEsmPending = FALSE; /* 此外,这里有一个另外得应用程序特别的检查(在ecatappl.c)如果状态在INIT到PREOP被转换,如果结果不等于0,从站将停留在INIT和设置AL-Status的错误代码(位4)additionally there could be an application specific check (in ecatappl.c) if the state transition from INIT to PREOP should be done if result is unequal 0, the slave will stay in INIT and sets the ErrorInd Bit (bit 4) of the AL-Status */ result = APPL_StartMailboxHandler(); if ( result == 0 ) { bMbxRunning = TRUE; } } if(result != 0 && result != NOERROR_INWORK) { /*停止APPL邮箱操作,如果APPL开始邮箱操作在之前被调用 Stop APPL Mbx handler if APPL Start Mbx handler was called before*/ if(!bApplEsmPending) APPL_StopMailboxHandler(); MBX_StopMailboxHandler(); } } break; case PREOP_2_SAFEOP: /* 开始输入操作(函数在上面被定义)start the input handler (function is defined above) */ result = StartInputHandler(); if ( result == 0 ) { bApplEsmPending = FALSE; result = APPL_StartInputHandler(&u16ALEventMask); if(result == 0) { /* initialize the AL Event Mask register (0x204) */ HW_SetALEventMask( u16ALEventMask ); bEcatInputUpdateRunning = TRUE; } } /*如果一个开始输入操作,返回一个错误,停止输入操作if one start input handler returned an error stop the input handler*/ if(result != 0 && result != NOERROR_INWORK) { if(!bApplEsmPending) { /*之前,APPL开始操作被调用,调用APPL停止操作Call only the APPL stop handler if the APPL start handler was called before*/ /*应用程序可以响应状态转换在函数APPL_StopInputHandle这个函数The application can react to the state transition in the function APPL_StopInputHandler */ /*ECATCHANGE_START(V5.01) ESM1*/ APPL_StopInputHandler(); /*ECATCHANGE_END(V5.01) ESM1*/ } StopInputHandler(); } break; case SAFEOP_2_OP: /* 开始输出操作(函数在上面被定义)start the output handler (function is defined above) */ result = StartOutputHandler(); if(result == 0) { bApplEsmPending = FALSE; result = APPL_StartOutputHandler(); if(result == 0) { /*设备是在运行状态Device is in OPERATINAL*/ bEcatOutputUpdateRunning = TRUE; } } if ( result != 0 && result != NOERROR_INWORK) { if(!bApplEsmPending) APPL_StopOutputHandler(); StopOutputHandler(); } break; case OP_2_SAFEOP: //* 停止输出操作(函数在上面定义)stop the output handler (function is defined above) */ /* ECATCHANGE_START(V5.01) ESM2*/ APPL_StopOutputHandler(); /* ECATCHANGE_END(V5.01) ESM2*/ StopOutputHandler(); break; case OP_2_PREOP: /* 停止输出操作(函数在上面定义)stop the output handler (function is defined above) */ /* ECATCHANGE_START(V5.01) ESM2*/ result = APPL_StopOutputHandler(); /* ECATCHANGE_END(V5.01) ESM2*/ StopOutputHandler(); if (result != 0) break; stateTrans = SAFEOP_2_PREOP; case SAFEOP_2_PREOP: /* 停止输出操作(函数在上面定义)stop the input handler (function is defined above) */ /* ECATCHANGE_START(V5.01) ESM2*/ APPL_StopInputHandler(); /* ECATCHANGE_END(V5.01) ESM2*/ StopInputHandler(); break; case OP_2_INIT: /* stop the output handler (function is defined above) */ /* ECATCHANGE_START(V5.01) ESM2*/ result = APPL_StopOutputHandler(); /* ECATCHANGE_END(V5.01) ESM2*/ StopOutputHandler(); if (result != 0) break; stateTrans = SAFEOP_2_INIT; case SAFEOP_2_INIT: /* stop the input handler (function is defined above) */ /* ECATCHANGE_START(V5.01) ESM2*/ result = APPL_StopInputHandler(); /* ECATCHANGE_END(V5.01) ESM2*/ StopInputHandler(); if (result != 0) break; stateTrans = PREOP_2_INIT; case PREOP_2_INIT: MBX_StopMailboxHandler(); result = APPL_StopMailboxHandler(); break; case INIT_2_INIT: case PREOP_2_PREOP: case SAFEOP_2_SAFEOP: case OP_2_OP: /*ECATCHANGE_START(V5.01) ESM3*/ if(bErrAck) APPL_AckErrorInd(stateTrans); /*ECATCHANGE_END(V5.01) ESM3*/ if(!bLocalErrorFlag) { /*没有本地错误标号,使能SMno local error flag is currently active, enable SM*/ if ( nAlStatus & (STATE_SAFEOP | STATE_OP)) { if(nPdOutputSize > 0) { HW_EnableSyncManChannel(PROCESS_DATA_OUT); } else { HW_EnableSyncManChannel(PROCESS_DATA_IN); } } } result = NOERROR_NOSTATECHANGE; break; case INIT_2_SAFEOP: case INIT_2_OP: case PREOP_2_OP: case PREOP_2_BOOT: case SAFEOP_2_BOOT: case OP_2_BOOT: result = ALSTATUSCODE_INVALIDALCONTROL; break; default: result = ALSTATUSCODE_UNKNOWNALCONTROL; break; } } else { /* 检查SM设置不成功,转换回到PREOP和INIT状态the checking of the sync manager settings was not successful switch back the state to PREOP or INIT */ switch (nAlStatus) { case STATE_OP: /* 停止输出操作(函数在上面定义)stop the output handler (function is defined above) */ /* ECATCHANGE_START(V5.01) ESM2*/ APPL_StopOutputHandler(); /* ECATCHANGE_END(V5.01) ESM2*/ StopOutputHandler(); case STATE_SAFEOP: /* stop the input handler (function is defined above) */ /* ECATCHANGE_END(V5.01) ESM2*/ APPL_StopInputHandler(); /* ECATCHANGE_END(V5.01) ESM2*/ StopInputHandler(); case STATE_SAFEOP: /* 停止输入操作(函数在上面定义)stop the input handler (function is defined above) */ /* ECATCHANGE_END(V5.01) ESM2*/ APPL_StopInputHandler(); /* ECATCHANGE_END(V5.01) ESM2*/ StopInputHandler(); case STATE_PREOP: if ( result == ALSTATUSCODE_INVALIDMBXCFGINPREOP ) { #if MAILBOX_SUPPORTED /* 邮箱的SM设置是错误的,转换回到INIT状态the mailbox sync manager settings were wrong, switch back to INIT */ MBX_StopMailboxHandler(); APPL_StopMailboxHandler(); #else /*禁止SM0Disable SM0 (邮箱输出)*/ HW_DisableSyncManChannel(MAILBOX_WRITE); /*禁止 SM1 (邮箱输入)*/ HW_DisableSyncManChannel(MAILBOX_READ); #endif nAlStatus = STATE_INIT; } else nAlStatus = STATE_PREOP; } } if ( result == NOERROR_INWORK ) { /* 状态转换仍然在工作,ECAT_SrateChange需要被调用从应用程序里面state transition is still in work ECAT_StateChange must be called from the application */ bEcatWaitForAlControlRes = TRUE; /* 状态转换需要被存储state transition has to be stored */ nEcatStateTrans = stateTrans; /*初始化ESM的超时计数(将被递减由本地的定时器,定时1ms的时间)Init ESM timeout counter (will be decremented with the local 1ms timer)*/ switch(nEcatStateTrans) { case INIT_2_PREOP: case INIT_2_BOOT: EsmTimeoutCounter = PREOPTIMEOUT; break; case PREOP_2_SAFEOP: case SAFEOP_2_OP: EsmTimeoutCounter = SAFEOP2OPTIMEOUT; break; default: EsmTimeoutCounter = 200; //设置常规的超时值为200msSet default timeout value to 200ms break; } EsmTimeoutCounter -= 50; //减去50ms从超时到响应,在主站进入超时之前subtract 50ms from the timeout to react before the master runs into a timeout. } else /* AL状态代码寄存器不应该被更改,如果函数被调用,如果SM转变时间或者同个状态的AL控制事件The AL Status Code register shall not be modified if the function is called in case of SM change event or an AL-Control event with the same state */ if ( alControl != (nAlStatus & STATE_MASK) ) { if ( (result != 0 || alStatusCode != 0) && ((alControl | nAlStatus) & STATE_OP) ) { /* 本地的应用程序需要请求离开状态OP,所以,我们需要禁止SM2和使状态转换从op到SAFEOP同个调用函数StopOutputHandler()the local application requested to leave the state OP so we have to disable the SM2 and make the state change from OP to SAFEOP by calling StopOutputHandler */ //如果输出更新仍然进行,需要执行StopOutputHandler()函数only execute StopOutputHandler() if Output update is still running if(bEcatOutputUpdateRunning) { /* ECATCHANGE_START(V5.01) ESM2*/ APPL_StopOutputHandler(); /* ECATCHANGE_END(V5.01) ESM2*/ StopOutputHandler(); } if(nPdOutputSize > 0) { /* 禁止Sync Manager通道2(输出)disable the Sync Manager Channel 2 (outputs) */ HW_DisableSyncManChannel(PROCESS_DATA_OUT); } else { /*禁止Sync Manager3(输入),如果没有输入变量disable Sync Manager 3 (inputs) if no outputs available*/ HW_DisableSyncManChannel(PROCESS_DATA_IN); } } if ( result != 0 ) { if ( nAlStatus == STATE_OP ) nAlStatus = STATE_SAFEOP; /* 保持失败的状态,如果AL状态代码应该被重置,如果一个成功的转换发生save the failed status to be able to decide, if the AL Status Code shall be reset in case of a coming successful state transition */ nAlStatus |= STATE_CHANGE; } else { /* 状态转换成功state transition was successful */ if ( alStatusCode != 0 ) { /* 来自用户的状态转换请求state change request from the user */ result = alStatusCode; alControl |= STATE_CHANGE; } /* 响应新的状态转换acknowledge the new state */ nAlStatus = alControl; } bEcatWaitForAlControlRes = FALSE; /* 写用户状态寄存器write the AL Status register */ SetALStatus(nAlStatus, result); } else { bEcatWaitForAlControlRes = FALSE; /* AL-Status应该被更新和AL-Status-Code应该被重置,如果错误码被响应AL-Status has to be updated and AL-Status-Code has to be reset if the the error bit was acknowledged */ SetALStatus(nAlStatus, 0); } #if CiA402_DEVICE if((stateTrans & 0x80) && !(stateTrans & STATE_OP)) //状态转换从运行状态到非运行状态state transition from OP to "not OP" { CiA402_LocalError(ERROR_COMMUNICATION); } #endif } ///////////////////////////////////////////////////////////////////////////////////////// /** \brief This function is called cyclic if a state transition is pending (bEcatWaitForAlControlRes == TRUE) \brief If the ESM timeout is expired the state transition will be rejected. Otherwise the application specific state transition function is called. \brief If the pending state transition is triggered by the application the transition need to be completed by the application (ECAT_StateChange()) *//////////////////////////////////////////////////////////////////////////////////////// void AL_ControlRes(void) { if(bEcatWaitForAlControlRes) { UINT16 result = 0; UINT8 Status = 0; UINT16 StatusCode = 0; if(EsmTimeoutCounter == 0) { Status = (UINT8)(nEcatStateTrans >> 4); /* ESM timeout expired*/ switch(nEcatStateTrans) { case INIT_2_PREOP: case INIT_2_BOOT: if(!bApplEsmPending) APPL_StopMailboxHandler(); MBX_StopMailboxHandler(); if(bLocalErrorFlag) { /*Set application specified error*/ StatusCode = u16LocalErrorCode; } else { /*Set unspecified error*/ StatusCode = ALSTATUSCODE_UNSPECIFIEDERROR; } break; case PREOP_2_SAFEOP: if(!bApplEsmPending) APPL_StopInputHandler(); StopInputHandler(); if(bLocalErrorFlag) { /*Set application specified error*/ StatusCode = u16LocalErrorCode; } else { /*Set unspecified error*/ StatusCode = ALSTATUSCODE_UNSPECIFIEDERROR; } break; case SAFEOP_2_OP: if(bDcSyncActive) { /*SafeOP to OP timeout expired check which AL status code need to be written*/ if(!bDcRunning) { /*no Sync0 signal received*/ StatusCode = ALSTATUSCODE_NOSYNCERROR; } else if(!bEcatFirstOutputsReceived) { /*no process data received*/ StatusCode = ALSTATUSCODE_SMWATCHDOG; } else { /*Pll is not valid*/ StatusCode = ALSTATUSCODE_DCPLLSYNCERROR; } } else { StatusCode = ALSTATUSCODE_SMWATCHDOG; } /*Stop handler on failed transition*/ if(StatusCode != 0) { if(!bApplEsmPending) APPL_StopOutputHandler(); StopOutputHandler(); } break; } } //ESM timeout else { /*Call application specific transition function and complete transition it the function returns 0*/ switch(nEcatStateTrans) { case INIT_2_PREOP: case INIT_2_BOOT: if(bApplEsmPending) { bApplEsmPending = FALSE; /*APPL_StartMailboxHandler() need to be called*/ result = APPL_StartMailboxHandler(); if(result == 0) { /*The application specific transition was successful => set active mailbox handler indication*/ bMbxRunning = TRUE; Status = (UINT8)(nEcatStateTrans & STATE_MASK); } else { /*The application specific transition failed. (In pending case the application need to complete the transition)*/ if(result != NOERROR_INWORK) { APPL_StopMailboxHandler(); MBX_StopMailboxHandler(); } } } break; case PREOP_2_SAFEOP: if(bApplEsmPending) { bApplEsmPending = FALSE; result = APPL_StartInputHandler(&u16ALEventMask); if(result == 0) { bEcatInputUpdateRunning = TRUE; Status = STATE_SAFEOP; } else { /*The application specific transition failed. (In pending case the application need to complete the transition)*/ if(result != NOERROR_INWORK) { APPL_StopInputHandler(); StopInputHandler(); } } } break; case SAFEOP_2_OP: if(bApplEsmPending) { if(bDcSyncActive) { if(i16WaitForPllRunningTimeout > 0 && i16WaitForPllRunningTimeout <= i16WaitForPllRunningCnt) { /*Pll sequence valid for 200ms (set in APPL_StartOutputHandler() ) acknowledge state transition to OP */ i16WaitForPllRunningTimeout = 0; i16WaitForPllRunningCnt = 0; result = APPL_StartOutputHandler(); if(result == 0) { /* Slave is OPERATIONAL */ bEcatOutputUpdateRunning = TRUE; Status = STATE_OP; } else { if(result != NOERROR_INWORK) { APPL_StopOutputHandler(); StopOutputHandler(); } } } } else { if(nPdOutputSize == 0 || bEcatFirstOutputsReceived) { result = APPL_StartOutputHandler(); if(result == 0) { /* Slave is OPERATIONAL */ bEcatOutputUpdateRunning = TRUE; Status = STATE_OP; } else { if(result != NOERROR_INWORK) { APPL_StopOutputHandler(); StopOutputHandler(); } } } } } break; }//Switch - transition } if(Status != 0) { /*Pending state transition finished => write AL Status and AL Status Code*/ bEcatWaitForAlControlRes = FALSE; if(StatusCode != 0) Status |= STATE_CHANGE; SetALStatus(Status,StatusCode); } }// Pending state transition (bEcatWaitForAlControlRes == true) } ///////////////////////////////////////////////////////////////////////////////////////// /** \brief This function checks the current Sync state and set the local flags //该函数主要是检测当前的同步状态,并设置标志位 The analyse of the local flags is handled in "CheckIfEcatError" //当ECAT错误时,检测该标志位 *//////////////////////////////////////////////////////////////////////////////////////// void DC_CheckWatchdog(void) //每1ms执行一次计数 { if(bDcSyncActive) //如果分布时钟同步使能,bDcSyncActive为使能标志位 { /*Check the Sync0 cycle if Sync0 Wd is enabled*/ if(Sync0WdValue > 0) { Sync0WdCounter ++; if(Sync0WdCounter > Sync0WdValue) { /*Sync0 watchdog expired*/ bDcRunning = FALSE; //看门狗同步越限 } } if(bDcRunning) { if(sSyncManOutPar.u32SmEventMissedCounter < sErrorSettings.u32SyncErrorCounterLimit) { bPllRunning = TRUE; /*Wait for PLL is active increment the Pll valid counter*/ if(i16WaitForPllRunningTimeout > 0) { i16WaitForPllRunningCnt++; } } else { bPllRunning = FALSE; sSyncManOutPar.u16SyncError = 1; /*Wait for PLL is active reset the Pll valid counter*/ if(i16WaitForPllRunningTimeout > 0) { i16WaitForPllRunningCnt = 0; } } } else { bPllRunning = FALSE; } } } ///////////////////////////////////////////////////////////////////////////////////////// /** \brief Checks communication and synchronisation variables and update AL status / AL status code if an error has occurred 检查通信和同步,如果发生了一个错误则更新AL状态/AL状态码 *//////////////////////////////////////////////////////////////////////////////////////// void CheckIfEcatError(void) { if(bDcSyncActive) { if(bEcatOutputUpdateRunning) { /*Slave is in OP state*/ if(!bDcRunning) { AL_ControlInd(STATE_SAFEOP, ALSTATUSCODE_FATALSYNCERROR); return; } else if(!bPllRunning) { AL_ControlInd(STATE_SAFEOP, ALSTATUSCODE_DCPLLSYNCERROR); return; } } } /*if the watchdog is enabled check the the process data watchdog in the ESC and set the AL status code 0x1B if the watchdog expired*/ if ( EcatWdValue != 0 ) { /*watchdog time is set => watchdog is active*/ UINT16 WdStatusOK = 0; HW_EscReadWord(WdStatusOK,ESC_PD_WD_STATE); /* ECATCHANGE_START(V5.01) ESC2*/ WdStatusOK = SWAPWORD(WdStatusOK); /* ECATCHANGE_END(V5.01) ESC2*/ if(!(WdStatusOK & ESC_PD_WD_TRIGGER_MASK)) { /*The device is in OP state*/ if(bEcatOutputUpdateRunning ) { AL_ControlInd(STATE_SAFEOP,ALSTATUSCODE_SMWATCHDOG); return; } else { bEcatFirstOutputsReceived = FALSE; } } } } ///////////////////////////////////////////////////////////////////////////////////////// /** \param alStatus: requested state \param alStatusCode: value for the AL-Status register \brief This function changes the state of the EtherCAT slave if the requested state is lower than the actual state, otherwise the error condition will be reset. *//////////////////////////////////////////////////////////////////////////////////////// void ECAT_StateChange(UINT8 alStatus, UINT16 alStatusCode) { UINT8 Status = alStatus; if(bEcatWaitForAlControlRes) { /*State transition is pending*/ if(bApplEsmPending) { /*The generic stack has currently control of the state transition. In case on an local error force ESM timeout*/ if(alStatusCode != 0) { bLocalErrorFlag = TRUE; u16LocalErrorCode = alStatusCode; EsmTimeoutCounter = 0; } } else { /*complete the state transition*/ if(alStatusCode != 0) { bLocalErrorFlag = TRUE; u16LocalErrorCode = alStatusCode; /*State transition failed due to local application reasons*/ switch(nEcatStateTrans) { case INIT_2_PREOP: case INIT_2_BOOT: APPL_StopMailboxHandler(); MBX_StopMailboxHandler(); break; case PREOP_2_SAFEOP: APPL_StopInputHandler(); StopInputHandler(); break; case SAFEOP_2_OP: APPL_StopOutputHandler(); StopOutputHandler(); break; } /*In case of a failed state transition the */ Status = (UINT8)(nEcatStateTrans >> 4); } else { /*State transition succeed*/ switch(nEcatStateTrans) { case INIT_2_PREOP: case INIT_2_BOOT: bMbxRunning = TRUE; break; case PREOP_2_SAFEOP: bEcatInputUpdateRunning = TRUE; break; case SAFEOP_2_OP: bEcatOutputUpdateRunning = TRUE; break; } /*In case of a failed state transition the */ Status = (UINT8)(nEcatStateTrans & STATE_MASK); } /*Pending state transition finished => write AL Status and AL Status Code*/ bEcatWaitForAlControlRes = FALSE; if(alStatusCode != 0) Status |= STATE_CHANGE; SetALStatus(Status,alStatusCode); }// state transition need to be completed by the local application }//State transition pending else { if ( alStatusCode != 0 ) { /* Local error has happened, we change the state if necessary */ bLocalErrorFlag = TRUE; u16LocalErrorCode = alStatusCode; if ( (alStatus & STATE_MASK) < (nAlStatus & STATE_MASK) ) { AL_ControlInd(alStatus, alStatusCode); } } else if (bLocalErrorFlag) { /*a local error is gone */ if ( (nAlStatus & STATE_MASK) == (STATE_SAFEOP | STATE_OP) ) { if(nPdOutputSize > 0) { /* we have to enable the output process data SyncManger (default: SM2), because it was disabled when switching back to SAFE-OP */ HW_EnableSyncManChannel(PROCESS_DATA_OUT); } else if (nPdInputSize > 0) { /* we have to enable the input process data SyncManger (default: SM3), because it was disabled when switching back to SAFE-OP */ HW_EnableSyncManChannel(PROCESS_DATA_IN); } } bLocalErrorFlag = FALSE; u16LocalErrorCode = 0x00; } } } ///////////////////////////////////////////////////////////////////////////////////////// /** \brief 初始化从站接口(SM、邮箱、全局变量、状态机、COE初始化) *//////////////////////////////////////////////////////////////////////////////////////// void ECAT_Init(void) { UINT8 i; //定义8位整形变量 /*Get Maximum Number of SyncManagers获得SM最大的数目*/ UINT16 TmpVar = 0; //定义16位整形变量 HW_EscReadWord(TmpVar, ESC_COMM_INFO_OFFSET); //读FMMU数 0-8个可配置 TmpVar = SWAPWORD(TmpVar); //交换字 高低8位互换 nMaxSyncMan = (UINT8) ((TmpVar & ESC_SM_CHANNELS_MASK)>> ESC_SM_CHANNELS_SHIFT); /*nMaxSyncMan变量中8位表示当前需要配置的SM通道数, ,用于下面使能代码 , 执行完后留下TmpVar的低8位*/ /* Sync Manager channels,初始化时将所有的SM通道屏蔽,在应用层中根据需要使能相应的SM通道*/ for (i = 0; i < nMaxSyncMan; i++) //不使能所有的单元 HW_DisableSyncManChannel(i); //每个SM通道操作不使能 /* initialize the mailbox handler */ MBX_Init(); //邮箱通信机制初始化 /* initialize variables */ bApplEsmPending = FALSE; //AL访问ESM标志 bEcatWaitForAlControlRes = FALSE; //ESCAT等待AL控制寄存器 bEcatFirstOutputsReceived = FALSE; //ESCAT输入输出正确标志mainloop函数中用 bEcatOutputUpdateRunning = FALSE; //运行模式下,输出更新运行标志mainloop函数中用 bEcatInputUpdateRunning = FALSE; //安全运行或运行模式下、输入更新运行标志mainloop函数中用 bWdTrigger = FALSE; //SM2看门狗使能不使能标志 EcatWdValue = 0; Sync0WdCounter = 0; Sync0WdValue = 0; bDcSyncActive = FALSE; //分布时钟同步使能,mainloop函数中用 bLocalErrorFlag = FALSE; u16LocalErrorCode = 0x00; //错误标志 u16ALEventMask = 0; //在预运行和安全运行状态下将被赋给0X204 /* initialize the AL Status register */ nAlStatus = STATE_INIT; //AL状态赋值0X01 初始化状态 SetALStatus(nAlStatus, 0); //初始化通信变量和外设灯控制情况 ,只设置al 故障状态指示led nEcatStateTrans = 0; //状态切换标志为0 u8EcatErrorLed = LED_OFF; //错误指示灯,运行状态错误指示灯初始值为0 bEscIntEnabled = FALSE; //ESC中断SM2/3使能标志,mainloop函数中用 /* initialize the COE part */ COE_Init(); //COE部分变量初始化 } ///////////////////////////////////////////////////////////////////////////////////////// /** \brief 这个函数被周期性得调用..主要完成非周期数据处理 *//////////////////////////////////////////////////////////////////////////////////////// void ECAT_Main(void) { UINT16 ALEventReg; UINT16 EscAlControl = 0x0000; UINT16 sm1Activate = SM_SETTING_ENABLE_VALUE;//0x0001//SM使能 /* 检查是否有服务存储在邮箱里面 */ MBX_Main(); if ( bMbxRunning )/*判断邮箱是否在使能状态*/ /* 从站至少工作在PREOP状态,邮箱才可以运行 */ /* 获得SM1(寄存器0x80E)的Activate-字节来检查是否重复请求被接收 */ { HW_EscReadWord(sm1Activate,(ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER)); //#define ESC_SYNCMAN_ACTIVE_OFFSET 0x0806 SM有效寄存器*/ //#define SIZEOF_SM_REGISTER 8 /**<每个SM有8位配置/状态寄存器.*/ //#define HW_EscReadWord(WordValue, Address) ((WordValue) = bsp_read_word(Address)) //即sm1Activate=bsp_read_word((ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER)); sm1Activate = SWAPWORD(sm1Activate); } /* Read AL Event-Register from ESC */ ALEventReg = HW_GetALEventRegister();/* 读0x0220寄存器*/ ALEventReg = SWAPWORD(ALEventReg); if ((ALEventReg & AL_CONTROL_EVENT) && !bEcatWaitForAlControlRes) { /* 读应用层状态控制寄存器, 得到主站发送的状态改变指令,同时复位应用层事件请求寄存器 的0位: 状态控制寄存器改变事件) */ HW_EscReadWord( EscAlControl, ESC_AL_CONTROL_OFFSET); EscAlControl = SWAPWORD(EscAlControl); /* 重置AL控制事件和SM转变事件(因为SM设置将会被检查在AL_ControlInd里面 )*/ ALEventReg &= ~((AL_CONTROL_EVENT) | (SM_CHANGE_EVENT)); AL_ControlInd((UINT8)EscAlControl, 0); /* in AL_ControlInd the state transition will be checked and done */ /* SM-Change-Event 也被操作 */ } if ( (ALEventReg & SM_CHANGE_EVENT) && !bEcatWaitForAlControlRes && (nAlStatus & STATE_CHANGE) == 0 && (nAlStatus & ~STATE_CHANGE) != STATE_INIT ) { /* SM的转换被设置(寄存器0x220里面的第4位),Sync Manager通道的当6位(使能,低字节寄存器的0x806,0x80E,0x816……)被写 */ ALEventReg &= ~(SM_CHANGE_EVENT); /* 带实际状态的AL_ControlInd函数被调用,所以,正确的SM设置将被检查 */ AL_ControlInd(nAlStatus & STATE_MASK, 0); } if(bEcatWaitForAlControlRes) { AL_ControlRes(); } /*<span style="white-space:pre"> </span>邮箱事件的处理顺序被改变防止竞争条件错误. SM1的激活字符(寄存器0x80E)被读,在读AL 事件寄存器之前. 1. 处理邮箱读事件 2. 处理重复计划请求 3. 处理邮箱写事件Handle Mailbox write event */ if ( bMbxRunning ) { /* 从站至少工作在PREOP状态,邮箱才可以运行 */ /* 获得SM1(寄存器0x80E)的Activate-字节来检查是否重复请求被接收 */ if(!(sm1Activate & SM_SETTING_ENABLE_VALUE)) AL_ControlInd(nAlStatus & STATE_MASK, 0); if ( ALEventReg & (MAILBOX_READ_EVENT) ) { /* SM 1 (邮箱读事件) 事件被设置,当邮箱由主站读的时候,来响应事件,邮箱里的第一个字节被写,通过写邮箱的第一个字节,邮箱也被锁 */ u16dummy = 0; HW_EscWriteWord(u16dummy,u16EscAddrSendMbx); /* 邮箱读事件在变量,ALEventReg应该被重置,在调用MBX_MailboxReadInd函数之前,一个新的邮箱数据包(如果可用)应该被存储在发送邮箱里面。 */ ALEventReg &= ~(MAILBOX_READ_EVENT); MBX_MailboxReadInd(); } DISABLE_MBX_INT; /* bMbxRepeatToggle 保持上个状态的Repeat位(Bit1) */ if ( ( (sm1Activate & SM_SETTING_REPAET_REQ_MASK) && !bMbxRepeatToggle ) ||( !(sm1Activate & SM_SETTING_REPAET_REQ_MASK) && bMbxRepeatToggle )) { /* 重复位(位1)被触发,这里有个一重复请求,在MBX_MailboxRepeatReq函数里,正确的响应将会再次放入发送邮箱 */ MBX_MailboxRepeatReq(); /* 响应重复请求在发送邮箱被更新,通过写入重复位RepeatAck Bit(第一位)在SM1的PDI控制字节(寄存器0x80F)acknowledge the repeat request after the send mailbox was updated by writing the Repeat Bit in the Repeat Ack Bit (Bit 1) of the PDI Ctrl-Byte of SM 1 (Register 0x80F) */ if(bMbxRepeatToggle) sm1Activate |= SM_SETTING_REPEAT_ACK; //set repeat acknowledge bit (bit 9) else sm1Activate &= ~SM_SETTING_REPEAT_ACK; //clear repeat acknowledge bit (bit 9) sm1Activate = SWAPWORD(sm1Activate); HW_EscWriteWord(sm1Activate,(ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER)); } ENABLE_MBX_INT; /* 重新加载ALEvent,因为它可以被改变由于SM的去使能,使能以至于重复请求 */ ALEventReg = HW_GetALEventRegister(); ALEventReg = SWAPWORD(ALEventReg); if ( ALEventReg & (MAILBOX_WRITE_EVENT) ) { /* SM 0 (邮箱写操作) 事件被设置,当邮箱由主站写来响应事件,邮箱的第一个字节可以被读,它将会在MBX_CheckAndCopyMailbox函数里面被操作event is set, when the mailbox was written from the master, to acknowledge the event the first byte of the mailbox has to be read, which will be done in MBX_CheckAndCopyMailbox */ /* 邮箱写事件在变量ALEventReg应该被重置,在调用MBX_CheckAndCopyMailbox之前,在哪里接收邮箱的数据包将会被处理 */ ALEventReg &= ~(MAILBOX_WRITE_EVENT); MBX_CheckAndCopyMailbox(); } } } /** @} */
时间: 2024-11-05 12:09:43