feat(project): 更新项目配置并增加ESC EEPROM功能
- 添加新的SPI命令文件:app_spi_cmd_can_burst.cpp 和 app_spi_cmd_exmc_diag.cpp - 移除旧的SPI命令文件:app_spi_cmd_ota_wait.cpp, app_spi_cmd_ping.cpp, app_spi_cmd_can_bridge.cpp, app_spi_cmd_can_feedback.cpp, app_spi_cmd_id_snapshot.cpp, app_spi_cmd_adc_feedback.cpp, app_spi_cmd_motor_state.cpp, app_spi_cmd_motor_version.cpp, app_spi_cmd_motor_ota.cpp - 新增SSC EtherCAT从站协议栈相关文件组,包含头文件和源文件 - 添加ESC EEPROM操作相关的头文件和源文件 - 修正HBI寄存器EXMC偏移地址映射错误 - 实现8位、16位、32位ESC寄存器读写函数 - 改进EEPROM初始化逻辑,添加重试机制和诊断功能
This commit is contained in:
@@ -0,0 +1,780 @@
|
||||
/**
|
||||
\addtogroup CoE CAN Application Profile over EtherCAT
|
||||
@{
|
||||
*/
|
||||
|
||||
/**
|
||||
\file coeappl.c
|
||||
\author EthercatSSC@beckhoff.com
|
||||
\brief Implementation
|
||||
This file contains an example for CoE services and the CoE object dictionary
|
||||
|
||||
\version 5.11
|
||||
|
||||
<br>Changes to version V5.10:<br>
|
||||
V5.11 COE1: update invalid end entry in the object dictionaries (error with some compilers)<br>
|
||||
V5.11 COE3: change 0x10F3.2 (Sync Error limit) from UINT32 to UINT16 (according to the ETG.1020)<br>
|
||||
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
|
||||
V5.11 ECAT4: enhance SM/Sync monitoring for input/output only slaves<br>
|
||||
<br>Changes to version V5.01:<br>
|
||||
V5.10 COE1: Define one entry description for all 0x1C3x objects and change data type of SI11,12,13 to UINT16 (according ETG.1020)<br>
|
||||
V5.10 COE2: Change return value of Get ObjectSize to UINT32<br>
|
||||
Change object size to UINT32<br>
|
||||
V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)<br>
|
||||
Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings<br>
|
||||
Update 0x1C3x entries<br>
|
||||
<br>Changes to version V5.0:<br>
|
||||
V5.01 APPL3: Include library demo application<br>
|
||||
V5.01 COE1: Remove alignment entry (SI33) from objects 0x1C32/0x1C33<br>
|
||||
V5.01 EL9800 1: Read/Write functions for setting object 0x8020 moved to el9800appl.c<br>
|
||||
V5.01 EL9800 2: Add TxPdo Parameter object 0x1802<br>
|
||||
V5.01 SDO6: Update SDO response interface handling. (used if the object access function returns "ABORTIDX_WORKING" and SDO_RES_INTERFACE is active)<br>
|
||||
<br>Changes to version V4.40:<br>
|
||||
V5.0 TEST1: Add test application. See Application Note ET9300 for more details.<br>
|
||||
V5.0 COE1: Add reserved Subindex7 to object 0x1C32 and 0x1C33.<br>
|
||||
V5.0 COE4: Create object dictionary on device startup.<br>
|
||||
V5.0 COE6: Update entry descriptions for object with the code ARRAY.<br>
|
||||
V5.0 ECAT2: Reference to application specific object dictionary is set to "ApplicationObjDic".<br>
|
||||
<br>Changes to version V4.30:<br>
|
||||
V4.40 COE 6: change dynamic object dictionary handling<br>
|
||||
V4.40 CiA402 2: enable freerun Sync Mode for CiA402 Device (The motion controller application will only be triggered if DC Synchronisation is disabled)<br>
|
||||
V4.40 COE5: move identification and synchronisation object values to ecat_def.h<br>
|
||||
V4.40 DIAG1: Add diagnosis object 0x10F3<br>
|
||||
V4.40 OBJ3: Update the global object dictionary pointer if a new list head is added<br>
|
||||
V4.40 COE4: Prevent access NULL pointer<br>
|
||||
V4.40 SYNC1: Initialize 0x1C3x objects<br>
|
||||
V4.40 HW1: Add (adapt) objects for FC1100 support<br>
|
||||
<br>Changes to version V4.20:<br>
|
||||
V4.30 OBJ 3: Handle object dictionary in double link list<br>
|
||||
V4.30 OBJ 2: General EtherCAT device objects are moved from chnappl.h to coeappl.c<br>
|
||||
V4.30 SYNC: set 0x1C32:05; 0x1C33:05 (minCyleTime) to MIN_PD_CYCLE_TIME
|
||||
(should be set dynamic if required)<br>
|
||||
V4.30 PDO: rename PDO specific function calls
|
||||
(PDO specific functions are moved to ecatappl.c)<br>
|
||||
V4.20 PIC24: Add EL9800_4 (PIC24) required source code<br>
|
||||
V4.11 ECAT 1: C166: MinDelayTime renamed in CalcAndCopyTime<br>
|
||||
<br>Changes to version V4.07:<br>
|
||||
V4.10 ECAT 1: To save and restore arrays in the object directory.<br>
|
||||
The code to get the size of an array entry was changed.<br>
|
||||
V4.10 COE 1: The SM-Parameter were extended<br>
|
||||
<br>Changes to version V4.06:<br>
|
||||
V4.07 COEAPPL 1: The PDO numbers were adapted according top the modular device profile,<br>
|
||||
the PDO numbers increment now with every module (in chnappl.h)<br>
|
||||
V4.07 COEAPPL 2: The example is working for the NIOS with the evaluation board DBC2C20 V1.2<br>
|
||||
which is available by Altera<br>
|
||||
V4.07 ECAT 1: The sources for SPI and MCI were merged (in ecat_def.h<br>
|
||||
set the switch MCI_HW to 1 when using the MCI,<br>
|
||||
set the switch SPI_HW to 1 when using the SPI<br>
|
||||
<br>Changes to version V4.03:<br>
|
||||
V4.04 SDO 1: The SDO interface was changed in that way that a SDO response<br>
|
||||
could be sent by the application to a later time. In that case<br>
|
||||
the functions OBJ_Read and OBJ_Write shall return the value<br>
|
||||
ABORTIDX_WORKING. To send the SDO response the new function SDOS_SdoRes<br>
|
||||
has to be called by the application. While waiting for the call<br>
|
||||
of SDOS_SdoRes the SDO interface will answer to another SDO request<br>
|
||||
with the error MBXERR_SERVICEINWORK in the mailbox protocol.<br>
|
||||
In this example the reading and writing of object 0x8020 is delayed,<br>
|
||||
the function SDOS_SdoRes will be called from COE_Main.<br>
|
||||
<br>Changes to version V4.00:<br>
|
||||
V4.01 COEAPPL 1: The object dictionary and the mapping functions are now defined<br>
|
||||
in chnappl.h (chnappl.inc made problems with some developing tools)<br>
|
||||
<br>Changes to version V3.20:<br>
|
||||
V4.00 COEAPPL 1: The object dictionary and the mapping functions are now defined<br>
|
||||
in chnappl.c and were adapted to the modular device profile<br>
|
||||
V4.00 COEAPPL 2: The handling of backup parameters was included according to<br>
|
||||
the EtherCAT Guidelines and Protocol Enhancements Specification<br>
|
||||
V4.00 COEAPPL 3: The handling of the TxPDO Toggle was included according to<br>
|
||||
the EtherCAT Guidelines and Protocol Enhancements Specification<br>
|
||||
V4.00 COEAPPL 4: The handling of the TxPDO State was included according to<br>
|
||||
the EtherCAT Guidelines and Protocol Enhancements Specification<br>
|
||||
V4.00 ECAT 1: The handling of the Sync Manager Parameter was included according to<br>
|
||||
the EtherCAT Guidelines and Protocol Enhancements Specification
|
||||
*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Includes
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#include "ecat_def.h"
|
||||
|
||||
|
||||
#include "ecatslv.h"
|
||||
|
||||
#define _COEAPPL_ 1
|
||||
#include "coeappl.h"
|
||||
#undef _COEAPPL_
|
||||
/* ECATCHANGE_START(V5.11) ECAT10*/
|
||||
/*remove definition of _COEAPPL_ (#ifdef is used in coeappl.h)*/
|
||||
/* ECATCHANGE_END(V5.11) ECAT10*/
|
||||
|
||||
#define _OBJD_
|
||||
|
||||
/*Add Application specific Objects*/
|
||||
#include "el9800appl.h"
|
||||
|
||||
|
||||
#undef _OBJD_
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Backup Parameter
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Generic EtherCAT device object
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/*---------------------------------------------
|
||||
- 0x1000
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x1000 (Device type) variable to handle the object data
|
||||
*/
|
||||
UINT32 u32Devicetype = (DEVICE_PROFILE_TYPE);
|
||||
|
||||
/**
|
||||
* \brief 0x1000 (Device type) entry description
|
||||
*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM sEntryDesc0x1000 = {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ};
|
||||
|
||||
/**
|
||||
* \brief 0x1000 (Device type) object name
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x1000[] = "Device type";
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- 0x1001
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x1001 (Error Register) variable to handle the object data
|
||||
*/
|
||||
UINT16 u16ErrorRegister = 0x0;
|
||||
|
||||
/**
|
||||
* \brief 0x1001 (Error Register) entry description
|
||||
*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM sEntryDesc0x1001 = {DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ};
|
||||
|
||||
/**
|
||||
* \brief 0x1001 (Error Register) object name
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x1001[] = "Error register";
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- 0x1008
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x1008 (Device name) variable to handle the object data
|
||||
*/
|
||||
CHAR acDevicename[] = DEVICE_NAME;
|
||||
|
||||
/**
|
||||
* \brief 0x1008 (Device name) entry description
|
||||
*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM sEntryDesc0x1008 = {DEFTYPE_VISIBLESTRING, BYTE2BIT(DEVICE_NAME_LEN), ACCESS_READ};
|
||||
|
||||
/**
|
||||
* \brief 0x1008 (Device name) object name
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x1008[] = "Device name";
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- 0x1009
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x1009 (Hardware version) variable to handle the object data
|
||||
*/
|
||||
CHAR acHardwareversion[] = DEVICE_HW_VERSION;
|
||||
|
||||
/**
|
||||
* \brief 0x1009 (Hardware version) entry description
|
||||
*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM sEntryDesc0x1009 = {DEFTYPE_VISIBLESTRING,BYTE2BIT(DEVICE_HW_VERSION_LEN), ACCESS_READ};
|
||||
|
||||
/**
|
||||
* \brief 0x1009 (Hardware version) object name
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x1009[] = "Hardware version";
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- 0x100A
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x100A (Software version) variable to handle the object data
|
||||
*/
|
||||
CHAR acSoftwareversion[] = DEVICE_SW_VERSION;
|
||||
|
||||
/**
|
||||
* \brief 0x1009 (Hardware version) entry description
|
||||
*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM sEntryDesc0x100A = {DEFTYPE_VISIBLESTRING,BYTE2BIT(DEVICE_SW_VERSION_LEN), ACCESS_READ};
|
||||
|
||||
/**
|
||||
* \brief 0x1009 (Hardware version) object name
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x100A[] = "Software version";
|
||||
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- 0x1018
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x1018 (Identity) object structure
|
||||
*/
|
||||
typedef struct OBJ_STRUCT_PACKED_START {
|
||||
UINT16 u16SubIndex0; /**< \brief SubIndex 0*/
|
||||
UINT32 u32VendorID; /**< \brief EtherCAT vendor ID */
|
||||
UINT32 u32Productcode; /**< \brief Product code*/
|
||||
UINT32 u32Revision; /**< \brief Revision number*/
|
||||
UINT32 u32Serialnumber; /**< \brief Serial number*/
|
||||
} OBJ_STRUCT_PACKED_END
|
||||
TOBJ1018;
|
||||
|
||||
/**
|
||||
* \brief 0x1018 (Identity) variable to handle the object data
|
||||
*/
|
||||
TOBJ1018 sIdentity = {4, (VENDOR_ID), (PRODUCT_CODE), (REVISION_NUMBER), (SERIAL_NUMBER)};
|
||||
|
||||
/**
|
||||
* \brief 0x1018 (Identity) entry descriptions
|
||||
*
|
||||
* Subindex 000
|
||||
* SubIndex 001: Vendor ID
|
||||
* SubIndex 002: Product code
|
||||
* SubIndex 003: Revision
|
||||
* SubIndex 004: Serial number
|
||||
*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x1018[] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x8, ACCESS_READ },
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}};
|
||||
|
||||
/**
|
||||
* \brief 0x1018 (Identity) object and entry names
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x1018[] = "Identity\000Vendor ID\000Product code\000Revision\000Serial number\000\377";
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- 0x10F1
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x10F1 (Error settings) entry description
|
||||
* Subindex 000
|
||||
* SubIndex 001: Local Error Reaction
|
||||
* SubIndex 002: Sync Error Counter Limit
|
||||
*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x10F1[] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x8, ACCESS_READ },
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READWRITE},
|
||||
/*ECATCHANGE_START(V5.11) COE3*/
|
||||
{DEFTYPE_UNSIGNED16, 0x10, ACCESS_READWRITE}};
|
||||
/*ECATCHANGE_END(V5.11) COE3*/
|
||||
|
||||
/**
|
||||
* \brief 0x10F1 (Error settings) object and entry names
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x10F1[] = "Error Settings\000Local Error Reaction\000Sync Error Counter Limit\000\377";
|
||||
|
||||
|
||||
//object declaration and initialization in objdef.h
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- 0x1C00
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x1C00 (Sync manager type) object structure
|
||||
*/
|
||||
typedef struct OBJ_STRUCT_PACKED_START {
|
||||
UINT16 u16SubIndex0; /**< \brief SubIndex 0*/
|
||||
UINT8 aEntries[4]; /**< \brief Entry buffer (one for each SyncManager)*/
|
||||
} OBJ_STRUCT_PACKED_END
|
||||
TOBJ1C00;
|
||||
|
||||
/**
|
||||
* \brief 0x1C00 (Sync manager type) variable to handle the object data
|
||||
*
|
||||
* SI 0 : 4 (4 SyncManagers available)
|
||||
* SI1 (SM0): Mailbox Out (0x1)
|
||||
* SI2 (SM1): Mailbox In (0x2)
|
||||
* SI3 (SM2): Process data Out (0x3)
|
||||
* SI4 (SM3): Process data In (0x4)
|
||||
*
|
||||
*/
|
||||
TOBJ1C00 sSyncmanagertype = {0x04, {0x01, 0x02, 0x03, 0x04}};
|
||||
|
||||
|
||||
/**
|
||||
* \brief 0x1C00 (Sync manager type) entry descriptions
|
||||
*
|
||||
* The object is an array so only the first entry is described (which is used for all entries).
|
||||
*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x1C00[2] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ}};
|
||||
|
||||
/**
|
||||
* \brief 0x1C00 (Sync manager type) object name
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x1C00[] = "Sync manager type";
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- 0x1C3x
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x1C3x (SyncManager parameter) entry description
|
||||
*
|
||||
* The entry descriptions are used for 0x1C32 and 0x1C33
|
||||
*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x1C3x[] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x8, ACCESS_READ }, /* Subindex 000 */
|
||||
{DEFTYPE_UNSIGNED16, 0x10, (ACCESS_READ | ACCESS_WRITE_PREOP)}, /* SubIndex 001: Synchronization Type */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* SubIndex 002: Cycle Time */
|
||||
{0x0000, 0x20, 0}, /* SubIndex 003: Shift Time (not supported)*/
|
||||
{DEFTYPE_UNSIGNED16, 0x10, ACCESS_READ}, /* SubIndex 004: Synchronization Types supported */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* SubIndex 005: Minimum Cycle Time */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* SubIndex 006: Calc and Copy Time */
|
||||
{0x0000, 0x20, 0}, /* Subindex 007 Minimum Delay Time (not supported)*/
|
||||
{DEFTYPE_UNSIGNED16, 0x10, ACCESS_READWRITE}, /* SubIndex 008: Get Cycle Time */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* SubIndex 009: Delay Time */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READWRITE}, /* SubIndex 010: Sync0 Cycle Time */
|
||||
{DEFTYPE_UNSIGNED16, 0x10, ACCESS_READ}, /* SubIndex 011: SM-Event Missed */
|
||||
{DEFTYPE_UNSIGNED16, 0x10, ACCESS_READ}, /* SubIndex 012: Cycle Time Too Small */
|
||||
{0x0000, 0x10, 0}, /* SubIndex 013: Shift Too Short Counter (not supported)*/
|
||||
{0x0000, 0x10, 0}, /* Subindex 014: RxPDO Toggle Failed (not supported)*/
|
||||
{0x0000, 0x20, 0}, /* Subindex 015: Minimum Cycle Distance (not supported)*/
|
||||
{0x0000, 0x20, 0}, /* Subindex 016: Maximum Cycle Distance (not supported)*/
|
||||
{0x0000, 0x20, 0}, /* Subindex 017: Minimum SM Sync Distance (not supported)*/
|
||||
{0x0000, 0x20, 0}, /* Subindex 018: Maximum SM Sync Distance (not supported)*/
|
||||
{0x0000, 0, 0}, /* Subindex 019 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 020 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 021 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 022 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 023 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 024 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 025 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 026 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 027 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 028 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 029 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 030 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 031 doesn't exist */
|
||||
{DEFTYPE_BOOLEAN, 0x01, ACCESS_READ}}; /* SubIndex 032: Sync error */
|
||||
|
||||
/*ECATCHANGE_START(V5.11) ECAT4*/
|
||||
/**
|
||||
* \brief 0x1C32 (SyncManager 2 parameter) object and entry names
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x1C32[] = "SM output parameter\000Synchronization Type\000Cycle Time\000\000Synchronization Types supported\000Minimum Cycle Time\000Calc and Copy Time\000\000Get Cycle Time\000Delay Time\000Sync0 Cycle Time\000SM-Event Missed\000Cycle Time Too Small\000Shift Time Too Short\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000Sync Error\000\377";
|
||||
/*ECATCHANGE_END(V5.11) ECAT4*/
|
||||
|
||||
/*ECATCHANGE_START(V5.11) ECAT4*/
|
||||
/**
|
||||
* \brief 0x1C33 (SyncManager 3 parameter) object and entry names
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x1C33[] = "SM input parameter\000Synchronization Type\000Cycle Time\000\000Synchronization Types supported\000Minimum Cycle Time\000Calc and Copy Time\000\000Get Cycle Time\000Delay Time\000Sync0 Cycle Time\000SM-Event Missed\000Cycle Time Too Small\000Shift Time Too Short\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000Sync Error\000\377";
|
||||
|
||||
/******************************************************************************
|
||||
** Object Dictionary
|
||||
******************************************************************************/
|
||||
/**
|
||||
* \brief Object dictionary pointer
|
||||
*/
|
||||
TOBJECT OBJMEM * ObjDicList = NULL;
|
||||
|
||||
/**
|
||||
* \brief List of generic application independent objects
|
||||
*/
|
||||
TOBJECT OBJMEM GenObjDic[] = {
|
||||
/* Object 0x1000 */
|
||||
{NULL,NULL, 0x1000, {DEFTYPE_UNSIGNED32, 0 | (OBJCODE_VAR << 8)}, &sEntryDesc0x1000, aName0x1000, &u32Devicetype, NULL, NULL, 0x0000 },
|
||||
/* Object 0x1001 */
|
||||
{NULL,NULL, 0x1001, {DEFTYPE_UNSIGNED8, 0 | (OBJCODE_VAR << 8)}, &sEntryDesc0x1001, aName0x1001, &u16ErrorRegister, NULL, NULL, 0x0000 },
|
||||
/* Object 0x1008 */
|
||||
{NULL,NULL, 0x1008, {DEFTYPE_VISIBLESTRING, 0 | (OBJCODE_VAR << 8)}, &sEntryDesc0x1008, aName0x1008, acDevicename, NULL, NULL, 0x0000 },
|
||||
/* Object 0x1009 */
|
||||
{NULL,NULL, 0x1009, {DEFTYPE_VISIBLESTRING, 0 | (OBJCODE_VAR << 8)}, &sEntryDesc0x1009, aName0x1009, acHardwareversion, NULL, NULL, 0x0000 },
|
||||
/* Object 0x100A */
|
||||
{NULL,NULL, 0x100A, {DEFTYPE_VISIBLESTRING, 0 | (OBJCODE_VAR << 8)}, &sEntryDesc0x100A, aName0x100A, acSoftwareversion, NULL, NULL, 0x0000 },
|
||||
/* Object 0x1018 */
|
||||
{NULL,NULL, 0x1018, {DEFTYPE_IDENTITY, 4 | (OBJCODE_REC << 8)}, asEntryDesc0x1018, aName0x1018, &sIdentity, NULL, NULL, 0x0000 },
|
||||
/* Object 0x10F1 */
|
||||
{NULL,NULL, 0x10F1, {DEFTYPE_RECORD, 2 | (OBJCODE_REC << 8)}, asEntryDesc0x10F1, aName0x10F1, &sErrorSettings, NULL, NULL, 0x0000 },
|
||||
/* Object 0x1C00 */
|
||||
{NULL,NULL, 0x1C00, {DEFTYPE_UNSIGNED8, 4 | (OBJCODE_ARR << 8)}, asEntryDesc0x1C00, aName0x1C00, &sSyncmanagertype, NULL, NULL, 0x0000 },
|
||||
/* Object 0x1C32 */
|
||||
{NULL,NULL, 0x1C32, {DEFTYPE_SMPAR, 32 | (OBJCODE_REC << 8)}, asEntryDesc0x1C3x, aName0x1C32, &sSyncManOutPar, NULL, NULL, 0x0000 },
|
||||
/* Object 0x1C33 */
|
||||
{NULL,NULL, 0x1C33, {DEFTYPE_SMPAR, 32 | (OBJCODE_REC << 8)}, asEntryDesc0x1C3x, aName0x1C33, &sSyncManInPar, NULL, NULL, 0x0000 },
|
||||
|
||||
/*end of entries*/
|
||||
/*ECATCHANGE_START(V5.11) COE1*/
|
||||
{NULL,NULL, 0xFFFF, {0, 0}, NULL, NULL, NULL, NULL, NULL, 0x000}};
|
||||
/*ECATCHANGE_END(V5.11) COE1*/
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Functions
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
|
||||
\brief returns the pointer to the object dictionary
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
OBJCONST TOBJECT OBJMEM * COE_GetObjectDictionary(void)
|
||||
{
|
||||
return (OBJCONST TOBJECT OBJMEM *) ObjDicList;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function initialize the several objects
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void COE_ObjInit(void)
|
||||
{
|
||||
|
||||
/* initialize the Sync Manager Output parameter object 0x1C32 */
|
||||
|
||||
sSyncManOutPar.subindex0 = 32;
|
||||
/*
|
||||
subindex 1 contains the actual synchronization mode, it could be written
|
||||
from the master to switch between ECAT FreeRun and ECAT Synchron Mode
|
||||
if the slave supports both modes,
|
||||
in DC mode (selected by the DC registers) this value will be overwritten
|
||||
with SYNCTYPE_DCSYNC0 or SYNCTYPE_DCSYNC1 */
|
||||
/*default mode is ECAT Synchron Mode */
|
||||
sSyncManOutPar.u16SyncType = SYNCTYPE_FREERUN;
|
||||
|
||||
/* subindex 2 contains the cycle time of the application,
|
||||
in ECAT FreeRun mode it could be used for a timer interrupt to run the application,
|
||||
in ECAT Synchron mode it could be written from the master with its local cycle time
|
||||
that the slave can check if this cycle time is supported,
|
||||
in DC Mode this value will be overwritten with the DC cycle time register */
|
||||
sSyncManOutPar.u32CycleTime = 0;
|
||||
/* only for DC Mode important: the subindex 3 contains the time shift between the
|
||||
SYNC0 (SYNC1) signal and when the outputs are put to the hardware to allow the
|
||||
master a very exactly calculation of delay times*/
|
||||
sSyncManOutPar.u32ShiftTime = 0;
|
||||
|
||||
/* the subindex 4 contains the supported synchronization types */
|
||||
|
||||
sSyncManOutPar.u16SyncTypesSupported = SYNCTYPE_FREERUNSUPP /* ECAT FreeRun Mode is supported */
|
||||
| SYNCTYPE_TIMESVARIABLE /* the execution times depend on the connected modules */
|
||||
| SYNCTYPE_SYNCHRONSUPP /* ECAT Synchron Mode is supported */
|
||||
| SYNCTYPE_DCSYNC0SUPP /* DC Sync0 Mode is supported */
|
||||
| SYNCTYPE_DCSYNC1SUPP /* DC Sync1 Mode is supported */
|
||||
| SYNCTYPE_SUBCYCLESUPP /*Subordinated application cycles supported*/
|
||||
;
|
||||
|
||||
/* subindex 5 contains the minimum cycle time the slave is able to support,
|
||||
will be calculated dynamically because it depends on the connected modules
|
||||
(in this example we will make an online measurement in the ESC Interrupt Routine).
|
||||
For the sample application this value is set to MIN_PD_CYCLE_TIME */
|
||||
sSyncManOutPar.u32MinCycleTime = MIN_PD_CYCLE_TIME;
|
||||
/* only for DC Mode important: subindex 6 contains the minimum delay time the slave
|
||||
needs after receiving the SM2-event before the SYNC0(SYNC1) can be received without delays
|
||||
will be calculated dynamically because it depends on the connected modules
|
||||
(in this example we will make an online measurement in the ESC Interrupt Routine) */
|
||||
sSyncManOutPar.u32CalcAndCopyTime = (PD_OUTPUT_CALC_AND_COPY_TIME);
|
||||
|
||||
/*subindex 8: trigger cycle time measurement*/
|
||||
sSyncManOutPar.u16GetCycleTime = 0;
|
||||
|
||||
/*subindex 9: time from start driving outputs until outputs are valid*/
|
||||
sSyncManOutPar.u32DelayTime = (PD_OUTPUT_DELAY_TIME);
|
||||
|
||||
/*subindex 32: indicates if a synchronisation error has occurred*/
|
||||
sSyncManOutPar.u8SyncError = 0;
|
||||
|
||||
/*ECATCHANGE_START(V5.11) ECAT4*/
|
||||
/* initialize the Sync Manager Input parameter object 0x1C33 */
|
||||
sSyncManInPar.subindex0 = 32;
|
||||
/* default mode is ECAT Synchron Mode, if output size > 0 the inputs are updated with the SM2-event */
|
||||
sSyncManInPar.u16SyncType = SYNCTYPE_FREERUN;
|
||||
|
||||
/* subindex 2: same as 0x1C32:02 */
|
||||
sSyncManInPar.u32CycleTime = sSyncManOutPar.u32CycleTime;
|
||||
/* only for DC Mode important: subindex 3 contains the time shift between the
|
||||
SYNC0 (SYNC1) signal and when the inputs are got to the hardware to allow the
|
||||
master a very exactly calculation of delay times,
|
||||
will be calculated dynamically because it depends on the connected modules
|
||||
(in this example we will make an online measurement in the ESC Interrupt Routine) */
|
||||
sSyncManInPar.u32ShiftTime = 0;
|
||||
/* subindex 4: same as 0x1C32:04 */
|
||||
sSyncManInPar.u16SyncTypesSupported = sSyncManOutPar.u16SyncTypesSupported;
|
||||
|
||||
/* subindex 5: same as 0x1C32:05 */
|
||||
sSyncManInPar.u32MinCycleTime = MIN_PD_CYCLE_TIME;
|
||||
/* subindex 6: delay read inputs, calculation and copy to SM buffer*/
|
||||
sSyncManInPar.u32CalcAndCopyTime = (PD_INPUT_CALC_AND_COPY_TIME);
|
||||
/*subindex 8: trigger cycle time measurement*/
|
||||
sSyncManInPar.u16GetCycleTime = 0;
|
||||
/*subindex 9: delay to prepare input latch*/
|
||||
sSyncManInPar.u32DelayTime = (PD_INPUT_DELAY_TIME);
|
||||
|
||||
/*subindex 32: incremented if a synchronisation error has occurred*/
|
||||
sSyncManInPar.u8SyncError = 0;
|
||||
/*ECATCHANGE_END(V5.11) ECAT4*/
|
||||
|
||||
/*Indicate no user specified Sync mode*/
|
||||
bSyncSetByUser = FALSE;
|
||||
|
||||
{
|
||||
UINT16 result = COE_ObjDictionaryInit();
|
||||
if(result != 0)
|
||||
{
|
||||
/*clear already linked objects*/
|
||||
COE_ClearObjDictionary();
|
||||
}
|
||||
}
|
||||
|
||||
u8PendingSdo = 0;
|
||||
bStoreCompleteAccess = FALSE;
|
||||
u16StoreIndex = 0;
|
||||
u8StoreSubindex = 0;
|
||||
u32StoreDataSize = 0;
|
||||
pStoreData = NULL;
|
||||
pSdoPendFunc = NULL;
|
||||
|
||||
pSdoSegData = NULL;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\return 0 object successful added to object dictionary
|
||||
ALSTATUSCODE_XX add object failed
|
||||
|
||||
\brief This function adds an object to the object dictionary
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
UINT16 COE_AddObjectToDic(TOBJECT OBJMEM * pNewObjEntry)
|
||||
{
|
||||
if(pNewObjEntry != NULL)
|
||||
{
|
||||
if(ObjDicList == NULL)
|
||||
{
|
||||
/* Object dictionary is empty */
|
||||
ObjDicList = pNewObjEntry;
|
||||
ObjDicList->pNext = NULL;
|
||||
ObjDicList->pPrev = NULL;
|
||||
return 0;
|
||||
}
|
||||
else if(ObjDicList->Index > pNewObjEntry->Index)
|
||||
{
|
||||
/*insert new object dictionary head*/
|
||||
pNewObjEntry->pPrev = NULL;
|
||||
pNewObjEntry->pNext = ObjDicList;
|
||||
ObjDicList->pPrev = pNewObjEntry;
|
||||
ObjDicList = pNewObjEntry;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
TOBJECT OBJMEM * pDicEntry = ObjDicList;
|
||||
while(pDicEntry != NULL)
|
||||
{
|
||||
if(pDicEntry->Index == pNewObjEntry->Index)
|
||||
{
|
||||
/*object already exists in object dictionary*/
|
||||
return ALSTATUSCODE_UNSPECIFIEDERROR;
|
||||
}
|
||||
else if(pDicEntry->Index > pNewObjEntry->Index)
|
||||
{
|
||||
pNewObjEntry->pPrev = pDicEntry->pPrev;
|
||||
pNewObjEntry->pNext = pDicEntry;
|
||||
|
||||
if(pDicEntry->pPrev != NULL)
|
||||
pDicEntry->pPrev->pNext = pNewObjEntry;
|
||||
|
||||
pDicEntry->pPrev = pNewObjEntry;
|
||||
|
||||
return 0;
|
||||
}
|
||||
else if(pDicEntry->pNext == NULL)
|
||||
{
|
||||
/*Last entry reached => add object to list tail*/
|
||||
pDicEntry->pNext = pNewObjEntry;
|
||||
pNewObjEntry->pPrev = pDicEntry;
|
||||
pNewObjEntry->pNext = NULL;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*The new object index is smaller than the current index. Get next object handle.*/
|
||||
pDicEntry = pDicEntry->pNext;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ALSTATUSCODE_UNSPECIFIEDERROR;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
|
||||
\brief This function removes an object to the object dictionary
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
void COE_RemoveDicEntry(UINT16 index)
|
||||
{
|
||||
TOBJECT OBJMEM * pDicEntry = ObjDicList;
|
||||
|
||||
while(pDicEntry != NULL)
|
||||
{
|
||||
if(pDicEntry->Index == index)
|
||||
{
|
||||
TOBJECT OBJMEM *pPrevEntry = pDicEntry->pPrev;
|
||||
TOBJECT OBJMEM *pNextEntry = pDicEntry->pNext;
|
||||
|
||||
if(pPrevEntry != NULL)
|
||||
pPrevEntry->pNext = pNextEntry;
|
||||
|
||||
if(pNextEntry != NULL)
|
||||
pNextEntry->pPrev = pPrevEntry;
|
||||
|
||||
pDicEntry->pPrev = NULL;
|
||||
pDicEntry->pNext = NULL;
|
||||
/*Update Object dictionary pointer if list head was removed*/
|
||||
if(pDicEntry->Index == ObjDicList->Index)
|
||||
{
|
||||
ObjDicList = pNextEntry;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
pDicEntry = pDicEntry->pNext;
|
||||
}
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
|
||||
\brief This function clear the object dictionary
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
void COE_ClearObjDictionary(void)
|
||||
{
|
||||
TOBJECT OBJMEM * pObjEntry = (TOBJECT OBJMEM *) ObjDicList;
|
||||
UINT16 Index = 0;
|
||||
|
||||
while(pObjEntry != NULL)
|
||||
{
|
||||
Index = pObjEntry->Index;
|
||||
pObjEntry = pObjEntry->pNext;
|
||||
|
||||
COE_RemoveDicEntry(Index);
|
||||
}
|
||||
ObjDicList = NULL;
|
||||
}
|
||||
|
||||
|
||||
UINT16 AddObjectsToObjDictionary(TOBJECT OBJMEM * pObjEntry)
|
||||
{
|
||||
UINT16 result = 0;
|
||||
TOBJECT OBJMEM * pEntry = (TOBJECT OBJMEM *)pObjEntry;
|
||||
|
||||
while(pEntry->Index != 0xFFFF)
|
||||
{
|
||||
result = COE_AddObjectToDic(pEntry);
|
||||
|
||||
if(result != 0)
|
||||
return result;
|
||||
|
||||
pEntry++;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\return 0 object dictionary created successful
|
||||
ALSTATUSCODE_XX create object dictionary failed
|
||||
|
||||
\brief This function initialize the object dictionary
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
UINT16 COE_ObjDictionaryInit(void)
|
||||
{
|
||||
UINT16 result = 0;
|
||||
|
||||
/*Reset object dictionary pointer*/
|
||||
ObjDicList = NULL;
|
||||
|
||||
result = AddObjectsToObjDictionary((TOBJECT OBJMEM *) GenObjDic);
|
||||
|
||||
if(result != 0)
|
||||
return result;
|
||||
if(ApplicationObjDic != NULL)
|
||||
{
|
||||
result = AddObjectsToObjDictionary((TOBJECT OBJMEM *) ApplicationObjDic);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
|
||||
\brief is called for background calculations which should not influence the
|
||||
ECAT_Application in synchronous modes
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void COE_Main(void)
|
||||
{
|
||||
UINT8 abort = 0;
|
||||
if(pSdoPendFunc != NULL)
|
||||
{
|
||||
abort = pSdoPendFunc(u16StoreIndex,u8StoreSubindex,u32StoreDataSize,pStoreData,bStoreCompleteAccess);
|
||||
|
||||
if(abort != ABORTIDX_WORKING)
|
||||
{
|
||||
switch(u8PendingSdo)
|
||||
{
|
||||
case SDO_PENDING_SEG_WRITE:
|
||||
if(pSdoSegData)
|
||||
{
|
||||
/* the allocated buffer can be released */
|
||||
FREEMEM( (UINT16 VARMEM *) pSdoSegData );
|
||||
pSdoSegData = NULL;
|
||||
}
|
||||
case SDO_PENDING_WRITE:
|
||||
/*send SDO Download Response*/
|
||||
SDOS_SdoRes(abort, 0, NULL);
|
||||
break;
|
||||
|
||||
case SDO_PENDING_SEG_READ:
|
||||
case SDO_PENDING_READ:
|
||||
/* send SDO upload response */
|
||||
SDOS_SdoRes(abort, u32StoreDataSize, pStoreData);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
u8PendingSdo = 0;
|
||||
u16StoreIndex = 0;
|
||||
u8StoreSubindex = 0;
|
||||
u32StoreDataSize = 0;
|
||||
pStoreData = NULL;
|
||||
bStoreCompleteAccess = 0;
|
||||
pSdoPendFunc = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,630 @@
|
||||
/**
|
||||
\addtogroup EcatAppl EtherCAT application
|
||||
@{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
\file ecatappl.c
|
||||
\author EthercatSSC@beckhoff.com
|
||||
\brief Implementation
|
||||
This file contains the Process Data interface
|
||||
|
||||
\version 5.11
|
||||
|
||||
<br>Changes to version V5.10.1:<br>
|
||||
V5.11 COE3: change 0x10F3.2 (Sync Error limit) from UINT32 to UINT16 (according to the ETG.1020)<br>
|
||||
V5.11 ECAT1: update EEPROM access reset operation<br>
|
||||
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
|
||||
V5.11 ECAT11: create application interface function pointer, add eeprom emulation interface functions<br>
|
||||
V5.11 ECAT2: update EEPROM access retry cycle (add 10ms delay between two retry cycles)<br>
|
||||
V5.11 ECAT3: handle bus cycle calculation for input/output only devices and create warning diag message only if calculation failed<br>
|
||||
V5.11 ECAT4: enhance SM/Sync monitoring for input/output only slaves<br>
|
||||
V5.11 ECAT6: add function to calculate bus cycle time<br>
|
||||
V5.11 ECAT8: call PDO_InputMapping only once if DC is enabled and COE is not supported<br>
|
||||
V5.11 EEPROM1: fix compiler error during pEEPROM pointer initialization<br>
|
||||
V5.11 EEPROM2: write Station alias value to EEPROM data register on EEPROM reload command<br>
|
||||
V5.11 EEPROM3: clear EEPROM error bits<br>
|
||||
V5.11 EEPROM4: prevent the variable in the EEPROM busy loop to be removed by the compiler<br>
|
||||
V5.11 ESM7: "add Sync define for 0x22 (""SYNCTYPE_SM2_SYNCHRON""), support value 0x22 for 0x1C33.1 (SM2 sync)"<br>
|
||||
<br>Changes to version V5.01:<br>
|
||||
V5.10 COE1: Define one entry description for all 0x1C3x objects and change data type of SI11,12,13 to UINT16 (according ETG.1020)<br>
|
||||
V5.10 ECAT1: Correct calculation of blinking and flashing sequence<br>
|
||||
V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)<br>
|
||||
Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings<br>
|
||||
Update 0x1C3x entries<br>
|
||||
V5.10 ECAT2: Prevent EEPROM data null pointer access (if the pointer is null an command error is set)<br>
|
||||
EEPROM emulation return command error if unknown command was received<br>
|
||||
V5.10 ECAT4: Update alignment marco for 8 to 15 bit alignments (16 and 32 Bit controllers)<br>
|
||||
Bugfix calculate LED blink frequency<br>
|
||||
V5.10 ECAT7: Add "bInitFinished" to indicate if the initialization is complete<br>
|
||||
V5.10 HW2: Change HW_GetTimer return value to UINT32<br>
|
||||
<br>Changes to version V5.0:<br>
|
||||
V5.01 APPL3: Include library demo application<br>
|
||||
V5.01 ESC1: Change ESC access function (if EEPROM Emulation is active)<br>
|
||||
V5.01 ESC2: Add missed value swapping<br>
|
||||
<br>Changes to version V4.40:<br>
|
||||
V5.0 TEST1: Add test application. See Application Note ET9300 for more details.<br>
|
||||
V5.0 ECAT2: Application specific functions are moved to application files.<br>
|
||||
V5.0 ECAT3: Global dummy variables used for dummy ESC operations.<br>
|
||||
V5.0 ESC1: ESC 32Bit Access added.<br>
|
||||
V5.0 ESC3: Add EEPROM emulation support.<br>
|
||||
V5.0 ESM3: Handling pending ESM transitions.<br>
|
||||
V5.0 ESC5: Enhance EEPROM access handling.<br>
|
||||
V5.0 PDO1: AL Event flags are not rechecked in PDO_OutputMappping(). (Already checked before call function)<br>
|
||||
V5.0 SYNC1: Add missed SM event indication (0x1C32/0x1C33 SI11).<br>
|
||||
<br>Changes to version V4.30:<br>
|
||||
V4.40 DIAG1: add diagnosis message support<br>
|
||||
V4.40 PDO1: merge content of HW_InputMapping (spihw.c/mcihw.c) to PDO_InputMapping. merge content of HW_OutputMapping (spihw.c/mcihw.c) to PDO_OutputMapping.<br>
|
||||
V4.40 PDO2: Generic process data length calculation<br>
|
||||
V4.40 ECAT2: call cyclic CheckIfLocalError() to check the local flags<br>
|
||||
V4.40 HW0: Generic hardware access functions. Add Function (PDI_Isr()), content merged from spihw.c and mcihw.c.<br>
|
||||
V4.40 WD1: define (ESC_SM_WD_SUPPORTED) to choose ESC SyncManager watchdog or local watchdog<br>
|
||||
V4.40 ESM2: Change state transition behaviour from SafeOP to OP<br>
|
||||
V4.40 TIMER1: Change bus cycle time calculation and trigger of ECAT_CheckTimer() if ECAT_TIMER_INT is reset<br>
|
||||
V4.40 HW1: Add support for fc1100 hardware<br>
|
||||
<br>Changes to version V4.20:<br>
|
||||
V4.30 EL9800: EL9800_x cyclic application is moved to el9800.c<br>
|
||||
V4.30 OBJ 3: add object dictionary initialization<br>
|
||||
V4.30 SYNC: add CalcSMCycleTime() (calculation of bus cycle time); change synchronisation control functions<br>
|
||||
V4.30 PDO: include PDO specific functions (moved from coeappl.c).<br>
|
||||
xxx_InputMapping(); xxx_OutputMapping(); xxx_ReadInputs(); xxx_ResetOutputs(); xxx_Application()<br>
|
||||
V4.30 CiA402: Add CiA402_StateMachine() and CiA402_Application() call<br>
|
||||
V4.20 DC 1: Add DC pending Statemachine handling<br>
|
||||
V4.20 PIC24: Add EL9800_4 (PIC24) required source code<br>
|
||||
V4.20 LED 1: Modified LED Handling<br>
|
||||
V4.11 APPL 1: The checkWatchdog() function should not called in checkTimer() if this function is triggered by an Interrupt<br>
|
||||
<br>Changes to version V4.08:<br>
|
||||
V4.10 LED 1: The handling of the EtherCAT-Error-LED was added<br>
|
||||
V4.10 AOE 3: The AoE fragment size has to be initialized during the state transition<br>
|
||||
from INIT to PREOP<br>
|
||||
<br>Changes to version V4.07:<br>
|
||||
V4.08 LED 1: The handling of the EtherCAT-LED can be (de-)selected by the switch LEDS_SUPPORTED<br>
|
||||
because the ET1100 and ET1200 have an output port which could be connected directly.<br>
|
||||
<br>Changes to version V4.01:<br>
|
||||
V4.02 ECAT 1: The watchdog timer variables shall be initialized.<br>
|
||||
<br>Changes to version V4.00:<br>
|
||||
V4.01 APPL 1: If the application is running in synchron mode and no SM event<br>
|
||||
is received, the application should be called from the main loop<br>
|
||||
V4.01 APPL 2: In FreeRun mode the output should only be copied if the slave is in OP<br>
|
||||
<br>Changes to version V3.20:<br>
|
||||
V4.00 APPL 1: The watchdog checking should be done by a microcontroller<br>
|
||||
timer because the watchdog trigger of the ESC will be reset too<br>
|
||||
if only a part of the sync manager data is written<br>
|
||||
V4.00 APPL 2: The setting of EtherCAT state LEDs were included<br>
|
||||
V4.00 APPL 3: The outputs should be reset to a safe state,<br>
|
||||
when the state OP is left<br>
|
||||
V4.00 APPL 4: An example for the EEPROM access through the ESC is shown in<br>
|
||||
the function APPL_StartMailboxHandler<br>
|
||||
V4.00 APPL 5: The inputs should be read once when the state transition<br>
|
||||
from PREOP to SAFEOP is made<br>
|
||||
V4.00 APPL 6: The main function was split in MainInit and MainLoop
|
||||
*/
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Includes
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
#include "ecatslv.h"
|
||||
|
||||
#define _ECATAPPL_ 1
|
||||
#include "ecatappl.h"
|
||||
#undef _ECATAPPL_
|
||||
/* ECATCHANGE_START(V5.11) ECAT10*/
|
||||
/*remove definition of _ECATAPPL_ (#ifdef is used in ecatappl.h)*/
|
||||
/* ECATCHANGE_END(V5.11) ECAT10*/
|
||||
|
||||
#include "coeappl.h"
|
||||
|
||||
|
||||
/* ECATCHANGE_START(V5.11) ECAT11*/
|
||||
#define _APPL_INTERFACE_ 1
|
||||
#include "applInterface.h"
|
||||
#undef _APPL_INTERFACE_
|
||||
/* ECATCHANGE_END(V5.11) ECAT11*/
|
||||
|
||||
#include "el9800appl.h"
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------------------
|
||||
------
|
||||
------ local Types and Defines
|
||||
------
|
||||
--------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#ifndef ECAT_TIMER_INC_P_MS
|
||||
/**
|
||||
* \todo Define the timer ticks per ms
|
||||
*/
|
||||
#warning "Define the timer ticks per ms"
|
||||
#endif /* #ifndef ECAT_TIMER_INC_P_MS */
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ local variables and constants
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
/*variables only required to calculate values for SM Synchronisation objects (0x1C3x)*/
|
||||
UINT16 u16BusCycleCntMs; //used to calculate the bus cycle time in Ms
|
||||
UINT32 StartTimerCnt; //variable to store the timer register value when get cycle time was triggered
|
||||
BOOL bCycleTimeMeasurementStarted; // indicates if the bus cycle measurement is started
|
||||
|
||||
UINT16 aPdOutputData[(MAX_PD_OUTPUT_SIZE>>1)];
|
||||
UINT16 aPdInputData[(MAX_PD_INPUT_SIZE>>1)];
|
||||
|
||||
/*variables are declared in ecatslv.c*/
|
||||
extern VARVOLATILE UINT16 u16dummy;
|
||||
BOOL bInitFinished = FALSE; /** < \brief indicates if the initialization is finished*/
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ local functions
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Functions
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function will copies the inputs from the local memory to the ESC memory
|
||||
to the hardware
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
void PDO_InputMapping(void)
|
||||
{
|
||||
APPL_InputMapping((UINT16*)aPdInputData);
|
||||
HW_EscWriteIsr(((MEM_ADDR *) aPdInputData), nEscAddrInputData, nPdInputSize );
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function will copies the outputs from the ESC memory to the local memory
|
||||
to the hardware. This function is only called in case of an SM2
|
||||
(output process data) event.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
void PDO_OutputMapping(void)
|
||||
{
|
||||
|
||||
HW_EscReadIsr(((MEM_ADDR *)aPdOutputData), nEscAddrOutputData, nPdOutputSize );
|
||||
|
||||
APPL_OutputMapping((UINT16*) aPdOutputData);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function shall be called every 1ms.
|
||||
\brief If the switch ECAT_TIMER_INT is 0, the watchdog control is implemented without using
|
||||
\brief interrupts. In this case a local timer register is checked every ECAT_Main cycle
|
||||
\brief and the function is triggered if 1 ms is elapsed
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ECAT_CheckTimer(void)
|
||||
{
|
||||
if(sSyncManOutPar.u32CycleTime == 0)
|
||||
{
|
||||
u16BusCycleCntMs++;
|
||||
}
|
||||
|
||||
/*decrement the state transition timeout counter*/
|
||||
if(bEcatWaitForAlControlRes && (EsmTimeoutCounter > 0))
|
||||
{
|
||||
EsmTimeoutCounter--;
|
||||
}
|
||||
|
||||
|
||||
|
||||
DC_CheckWatchdog();
|
||||
}
|
||||
|
||||
/*ECATCHANGE_START(V5.11) ECAT6*/
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function is called from the PDI_Isr and is used to calculate the bus cycle time
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
void HandleBusCycleCalculation(void)
|
||||
{
|
||||
/*calculate the cycle time if device is in SM Sync mode and Cycle time was not calculated yet*/
|
||||
if ( !bDcSyncActive && bEscIntEnabled)
|
||||
{
|
||||
BOOL bTiggerCalcCycleTime = FALSE;
|
||||
|
||||
if(sSyncManOutPar.u16GetCycleTime == 1)
|
||||
bTiggerCalcCycleTime = TRUE;
|
||||
if(bTiggerCalcCycleTime)
|
||||
{
|
||||
/*get bus cycle time triggered */
|
||||
sSyncManOutPar.u32CycleTime = 0;
|
||||
sSyncManOutPar.u16GetCycleTime = 0;
|
||||
|
||||
sSyncManInPar.u32CycleTime = 0;
|
||||
sSyncManInPar.u16GetCycleTime = 0;
|
||||
|
||||
u16BusCycleCntMs = 0;
|
||||
bCycleTimeMeasurementStarted = TRUE;
|
||||
StartTimerCnt = (UINT32) HW_GetTimer();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(bCycleTimeMeasurementStarted == TRUE)
|
||||
{
|
||||
UINT32 CurTimerCnt = (UINT32)HW_GetTimer();
|
||||
/*ECATCHANGE_START(V5.11) ECAT3*/
|
||||
UINT32 CalcCycleTime = 0;
|
||||
|
||||
|
||||
#if ECAT_TIMER_INC_P_MS
|
||||
CalcCycleTime = (UINT32)u16BusCycleCntMs * 1000000 + (((INT32)(CurTimerCnt-StartTimerCnt))*1000000/ECAT_TIMER_INC_P_MS); //get elapsed cycle time in ns
|
||||
#endif
|
||||
|
||||
/*ECATCHANGE_START(V5.11) ECAT4*/
|
||||
sSyncManOutPar.u32CycleTime = CalcCycleTime;
|
||||
/*ECATCHANGE_END(V5.11) ECAT4*/
|
||||
sSyncManInPar.u32CycleTime = CalcCycleTime;
|
||||
u16BusCycleCntMs = 0;
|
||||
StartTimerCnt = 0;
|
||||
bCycleTimeMeasurementStarted = FALSE;
|
||||
|
||||
/*ECATCHANGE_END(V5.11) ECAT3*/
|
||||
/* CiA402 Motion controller cycle time is only set if DC Synchronisation is active*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*ECATCHANGE_END(V5.11) ECAT6*/
|
||||
|
||||
void PDI_Isr(void)
|
||||
{
|
||||
if(bEscIntEnabled)
|
||||
{
|
||||
/* get the AL event register */
|
||||
UINT16 ALEvent = HW_GetALEventRegister_Isr();
|
||||
ALEvent = SWAPWORD(ALEvent);
|
||||
|
||||
if ( ALEvent & PROCESS_OUTPUT_EVENT )
|
||||
{
|
||||
if(bDcRunning && bDcSyncActive)
|
||||
{
|
||||
/* Reset SM/Sync0 counter. Will be incremented on every Sync0 event*/
|
||||
u16SmSync0Counter = 0;
|
||||
}
|
||||
if(sSyncManOutPar.u16SmEventMissedCounter > 0)
|
||||
sSyncManOutPar.u16SmEventMissedCounter--;
|
||||
|
||||
|
||||
/*ECATCHANGE_START(V5.11) ECAT6*/
|
||||
//calculate the bus cycle time if required
|
||||
HandleBusCycleCalculation();
|
||||
/*ECATCHANGE_END(V5.11) ECAT6*/
|
||||
|
||||
/* Outputs were updated, set flag for watchdog monitoring */
|
||||
bEcatFirstOutputsReceived = TRUE;
|
||||
|
||||
|
||||
/*
|
||||
handle output process data event
|
||||
*/
|
||||
if ( bEcatOutputUpdateRunning )
|
||||
{
|
||||
/* slave is in OP, update the outputs */
|
||||
PDO_OutputMapping();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Just acknowledge the process data event in the INIT,PreOP and SafeOP state */
|
||||
HW_EscReadWordIsr(u16dummy,nEscAddrOutputData);
|
||||
HW_EscReadWordIsr(u16dummy,(nEscAddrOutputData+nPdOutputSize-2));
|
||||
}
|
||||
}
|
||||
|
||||
/*ECATCHANGE_START(V5.11) ECAT4*/
|
||||
if (( ALEvent & PROCESS_INPUT_EVENT ) && (nPdOutputSize == 0))
|
||||
{
|
||||
//calculate the bus cycle time if required
|
||||
HandleBusCycleCalculation();
|
||||
}
|
||||
/*ECATCHANGE_END(V5.11) ECAT4*/
|
||||
|
||||
/*
|
||||
Call ECAT_Application() in SM Sync mode
|
||||
*/
|
||||
if (sSyncManOutPar.u16SyncType == SYNCTYPE_SM_SYNCHRON)
|
||||
{
|
||||
/* The Application is synchronized to process data Sync Manager event*/
|
||||
ECAT_Application();
|
||||
}
|
||||
|
||||
if ( bEcatInputUpdateRunning
|
||||
/*ECATCHANGE_START(V5.11) ESM7*/
|
||||
&& ((sSyncManInPar.u16SyncType == SYNCTYPE_SM_SYNCHRON) || (sSyncManInPar.u16SyncType == SYNCTYPE_SM2_SYNCHRON))
|
||||
/*ECATCHANGE_END(V5.11) ESM7*/
|
||||
)
|
||||
{
|
||||
/* EtherCAT slave is at least in SAFE-OPERATIONAL, update inputs */
|
||||
PDO_InputMapping();
|
||||
}
|
||||
|
||||
/*
|
||||
Check if cycle exceed
|
||||
*/
|
||||
/*if next SM event was triggered during runtime increment cycle exceed counter*/
|
||||
ALEvent = HW_GetALEventRegister_Isr();
|
||||
ALEvent = SWAPWORD(ALEvent);
|
||||
|
||||
if ( ALEvent & PROCESS_OUTPUT_EVENT )
|
||||
{
|
||||
sSyncManOutPar.u16CycleExceededCounter++;
|
||||
sSyncManInPar.u16CycleExceededCounter = sSyncManOutPar.u16CycleExceededCounter;
|
||||
|
||||
/* Acknowledge the process data event*/
|
||||
HW_EscReadWordIsr(u16dummy,nEscAddrOutputData);
|
||||
HW_EscReadWordIsr(u16dummy,(nEscAddrOutputData+nPdOutputSize-2));
|
||||
}
|
||||
} //if(bEscIntEnabled)
|
||||
}
|
||||
|
||||
void Sync0_Isr(void)
|
||||
{
|
||||
|
||||
Sync0WdCounter = 0;
|
||||
|
||||
if(bDcSyncActive)
|
||||
{
|
||||
|
||||
if ( bEcatInputUpdateRunning )
|
||||
{
|
||||
LatchInputSync0Counter++;
|
||||
}
|
||||
|
||||
/*ECATCHANGE_START(V5.11) ECAT4*/
|
||||
if(u16SmSync0Value > 0)
|
||||
{
|
||||
/* Check if Sm-Sync sequence is invalid */
|
||||
if (u16SmSync0Counter > u16SmSync0Value)
|
||||
{
|
||||
/*ECATCHANGE_START(V5.11) COE3*/
|
||||
if ((nPdOutputSize > 0) && (sSyncManOutPar.u16SmEventMissedCounter <= sErrorSettings.u16SyncErrorCounterLimit))
|
||||
{
|
||||
/*ECATCHANGE_END(V5.11) COE3*/
|
||||
sSyncManOutPar.u16SmEventMissedCounter = sSyncManOutPar.u16SmEventMissedCounter + 3;
|
||||
}
|
||||
|
||||
/*ECATCHANGE_START(V5.11) COE3*/
|
||||
if ((nPdInputSize > 0) && (nPdOutputSize == 0) && (sSyncManInPar.u16SmEventMissedCounter <= sErrorSettings.u16SyncErrorCounterLimit))
|
||||
{
|
||||
/*ECATCHANGE_END(V5.11) COE3*/
|
||||
sSyncManInPar.u16SmEventMissedCounter = sSyncManInPar.u16SmEventMissedCounter + 3;
|
||||
}
|
||||
|
||||
} // if (u16SmSync0Counter > u16SmSync0Value)
|
||||
|
||||
|
||||
if ((nPdOutputSize == 0) && (nPdInputSize > 0))
|
||||
{
|
||||
/* Input only with DC, check if the last input data was read*/
|
||||
UINT16 ALEvent = HW_GetALEventRegister_Isr();
|
||||
ALEvent = SWAPWORD(ALEvent);
|
||||
|
||||
if ((ALEvent & PROCESS_INPUT_EVENT) == 0)
|
||||
{
|
||||
/* no input data was read by the master, increment the sm missed counter*/
|
||||
u16SmSync0Counter++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Reset SM/Sync0 counter*/
|
||||
u16SmSync0Counter = 0;
|
||||
|
||||
sSyncManInPar.u16SmEventMissedCounter = 0;
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
u16SmSync0Counter++;
|
||||
}
|
||||
}//SM -Sync monitoring enabled
|
||||
/*ECATCHANGE_END(V5.11) ECAT4*/
|
||||
|
||||
|
||||
if(!bEscIntEnabled && bEcatOutputUpdateRunning)
|
||||
{
|
||||
/* Output mapping was not done by the PDI ISR */
|
||||
PDO_OutputMapping();
|
||||
}
|
||||
|
||||
/* Application is synchronized to SYNC0 event*/
|
||||
ECAT_Application();
|
||||
|
||||
if ( bEcatInputUpdateRunning
|
||||
&& (LatchInputSync0Value > 0) && (LatchInputSync0Value == LatchInputSync0Counter) ) /* Inputs shall be latched on a specific Sync0 event */
|
||||
{
|
||||
/* EtherCAT slave is at least in SAFE-OPERATIONAL, update inputs */
|
||||
PDO_InputMapping();
|
||||
|
||||
if(LatchInputSync0Value == 1)
|
||||
{
|
||||
/* if inputs are latched on every Sync0 event (otherwise the counter is reset on the next Sync1 event) */
|
||||
LatchInputSync0Counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Sync1_Isr(void)
|
||||
{
|
||||
Sync1WdCounter = 0;
|
||||
|
||||
if ( bEcatInputUpdateRunning
|
||||
&& (sSyncManInPar.u16SyncType == SYNCTYPE_DCSYNC1)
|
||||
&& (LatchInputSync0Value == 0)) /* Inputs are latched on Sync1 (LatchInputSync0Value == 0), if LatchInputSync0Value > 0 inputs are latched with Sync0 */
|
||||
{
|
||||
/* EtherCAT slave is at least in SAFE-OPERATIONAL, update inputs */
|
||||
PDO_InputMapping();
|
||||
}
|
||||
|
||||
/* Reset Sync0 latch counter (to start next Sync0 latch cycle) */
|
||||
LatchInputSync0Counter = 0;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
|
||||
\brief This function shall called within a 1ms cycle.
|
||||
Set Run and Error Led depending on the Led state
|
||||
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\param pObjectDictionary Pointer to application specific object dictionary.
|
||||
NULL if no specific object are available.
|
||||
\return 0 if initialization was successful
|
||||
|
||||
\brief This function initialize the EtherCAT Sample Code
|
||||
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
UINT16 MainInit(void)
|
||||
{
|
||||
UINT16 Error = 0;
|
||||
/*Hardware init function need to be called from the application layer*/
|
||||
|
||||
/*ECATCHANGE_START(V5.11) EEPROM1*/
|
||||
#ifdef SET_EEPROM_PTR
|
||||
SET_EEPROM_PTR
|
||||
#endif
|
||||
/*ECATCHANGE_END(V5.11) EEPROM1*/
|
||||
|
||||
/* initialize the EtherCAT Slave Interface */
|
||||
ECAT_Init();
|
||||
/* initialize the objects */
|
||||
COE_ObjInit();
|
||||
|
||||
|
||||
/*Timer initialization*/
|
||||
u16BusCycleCntMs = 0;
|
||||
StartTimerCnt = 0;
|
||||
bCycleTimeMeasurementStarted = FALSE;
|
||||
|
||||
/*indicate that the slave stack initialization finished*/
|
||||
bInitFinished = TRUE;
|
||||
|
||||
/*Application Init need to be called from the application layer*/
|
||||
return Error;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
|
||||
\brief This function shall be called cyclically from main
|
||||
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MainLoop(void)
|
||||
{
|
||||
/*return if initialization not finished */
|
||||
if(bInitFinished == FALSE)
|
||||
return;
|
||||
|
||||
|
||||
|
||||
/* FreeRun-Mode: bEscIntEnabled = FALSE, bDcSyncActive = FALSE
|
||||
Synchron-Mode: bEscIntEnabled = TRUE, bDcSyncActive = FALSE
|
||||
DC-Mode: bEscIntEnabled = TRUE, bDcSyncActive = TRUE */
|
||||
if (
|
||||
(!bEscIntEnabled || !bEcatFirstOutputsReceived) /* SM-Synchronous, but not SM-event received */
|
||||
&& !bDcSyncActive /* DC-Synchronous */
|
||||
)
|
||||
{
|
||||
/* if the application is running in ECAT Synchron Mode the function ECAT_Application is called
|
||||
from the ESC interrupt routine (in mcihw.c or spihw.c),
|
||||
in ECAT Synchron Mode it should be additionally checked, if the SM-event is received
|
||||
at least once (bEcatFirstOutputsReceived = 1), otherwise no interrupt is generated
|
||||
and the function ECAT_Application has to be called here (with interrupts disabled,
|
||||
because the SM-event could be generated while executing ECAT_Application) */
|
||||
if ( !bEscIntEnabled )
|
||||
{
|
||||
/* application is running in ECAT FreeRun Mode,
|
||||
first we have to check, if outputs were received */
|
||||
UINT16 ALEvent = HW_GetALEventRegister();
|
||||
ALEvent = SWAPWORD(ALEvent);
|
||||
|
||||
if ( ALEvent & PROCESS_OUTPUT_EVENT )
|
||||
{
|
||||
/* set the flag for the state machine behaviour */
|
||||
bEcatFirstOutputsReceived = TRUE;
|
||||
if ( bEcatOutputUpdateRunning )
|
||||
{
|
||||
/* update the outputs */
|
||||
PDO_OutputMapping();
|
||||
}
|
||||
}
|
||||
else if ( nPdOutputSize == 0 )
|
||||
{
|
||||
/* if no outputs are transmitted, the watchdog must be reset, when the inputs were read */
|
||||
if ( ALEvent & PROCESS_INPUT_EVENT )
|
||||
{
|
||||
/* Outputs were updated, set flag for watchdog monitoring */
|
||||
bEcatFirstOutputsReceived = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DISABLE_ESC_INT();
|
||||
ECAT_Application();
|
||||
|
||||
if ( bEcatInputUpdateRunning )
|
||||
{
|
||||
/* EtherCAT slave is at least in SAFE-OPERATIONAL, update inputs */
|
||||
PDO_InputMapping();
|
||||
}
|
||||
ENABLE_ESC_INT();
|
||||
}
|
||||
#if !ECAT_TIMER_INT
|
||||
/* there is no interrupt routine for the hardware timer so check the timer register if the desired cycle elapsed*/
|
||||
{
|
||||
UINT32 CurTimer = (UINT32)HW_GetTimer();
|
||||
|
||||
if(CurTimer>= ECAT_TIMER_INC_P_MS)
|
||||
{
|
||||
ECAT_CheckTimer();
|
||||
|
||||
HW_ClearTimer();
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* call EtherCAT functions */
|
||||
ECAT_Main();
|
||||
|
||||
/* call lower prior application part */
|
||||
COE_Main();
|
||||
CheckIfEcatError();
|
||||
|
||||
}
|
||||
|
||||
/*The main function was moved to the application files.*/
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief ECAT_Application (prev. SSC versions "COE_Application")
|
||||
this function calculates and the physical process signals and triggers the input mapping
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
void ECAT_Application(void)
|
||||
{
|
||||
|
||||
APPL_Application();
|
||||
|
||||
/* PDO Input mapping is called from the specific trigger ISR */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** @} */
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/**
|
||||
\addtogroup CoE CAN Application Profile over EtherCAT
|
||||
@{
|
||||
*/
|
||||
|
||||
/**
|
||||
\file ecatcoe.c
|
||||
\author EthercatSSC@beckhoff.com
|
||||
\brief Implementation
|
||||
This file contains the CoE mailbox interface
|
||||
|
||||
\version 5.11
|
||||
|
||||
<br>Changes to version V5.0:<br>
|
||||
V5.11 COE4: "change prototype of ""COE_ContinueInd()"" return <> 0 if a failure occurred"<br>
|
||||
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
|
||||
V5.11 TEST6: add test object to trigger Slave-to-Slave communication<br>
|
||||
V5.11 TEST7: add test behaviour to send an emergency on every SDO request (in SafeOP)<br>
|
||||
<br>Changes to version V4.40:<br>
|
||||
V5.0 SDO7: "SDOS_SdoInfoInd()" never return pending SDO Info service. Delete "NOERROR_INWORK" handling.<br>
|
||||
<br>Changes to version V4.08:<br>
|
||||
V4.40 SDO1: add initial value for "nSdoInfoFragmentsLeft"<br>
|
||||
V4.40 MBX6: change return value if no mailbox buffer is available<br>
|
||||
V4.08 MBX 1: If the switch MAILBOX_QUEUE was set, we have to put all SDO Info Responses in the Send Queue
|
||||
*/
|
||||
|
||||
/*---------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Includes
|
||||
------
|
||||
---------------------------------------------------------------------------------------*/
|
||||
|
||||
#include "ecat_def.h"
|
||||
|
||||
|
||||
#include "sdoserv.h"
|
||||
#define _ECATCOE_ 1
|
||||
#include "ecatcoe.h"
|
||||
#undef _ECATCOE_
|
||||
/* ECATCHANGE_START(V5.11) ECAT10*/
|
||||
/*remove definition of _ECATCOE_ (#ifdef is used in ecatcoe.h)*/
|
||||
/* ECATCHANGE_END(V5.11) ECAT10*/
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------------------
|
||||
------
|
||||
------ internal Types and Defines
|
||||
------
|
||||
---------------------------------------------------------------------------------------*/
|
||||
|
||||
#define ECATCOE 0x4300
|
||||
#define ECATCOEMAX 0x02
|
||||
|
||||
/*---------------------------------------------------------------------------------------
|
||||
------
|
||||
------ static variables
|
||||
------
|
||||
---------------------------------------------------------------------------------------*/
|
||||
|
||||
/*---------------------------------------------------------------------------------------
|
||||
------
|
||||
------ static functions
|
||||
------
|
||||
---------------------------------------------------------------------------------------*/
|
||||
|
||||
/*---------------------------------------------------------------------------------------
|
||||
------
|
||||
------ functions
|
||||
------
|
||||
---------------------------------------------------------------------------------------*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
|
||||
\brief This function intialize the CoE Interface.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void COE_Init(void)
|
||||
{
|
||||
pCoeSendStored = 0;
|
||||
nSdoInfoFragmentsLeft = 0;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\param pCoeMbx Pointer to the received mailbox data from the master.
|
||||
|
||||
\return result of the operation (0 (success) or mailbox error code (MBXERR_.... defined in
|
||||
mailbox.h))
|
||||
|
||||
\brief This function is called when a CoE (CAN application layer over EtherCAT) service is received from
|
||||
the master.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
UINT8 COE_ServiceInd(TCOEMBX MBXMEM *pCoeMbx)
|
||||
{
|
||||
UINT8 result = 0;
|
||||
|
||||
switch ((pCoeMbx->CoeHeader & COEHEADER_COESERVICEMASK) >> COEHEADER_COESERVICESHIFT)
|
||||
{
|
||||
case COESERVICE_SDOREQUEST:
|
||||
/* SDO-Request received, call SDOS_SdoInd to process the SDO-Request
|
||||
if an existing SDO-Stack shall be used, the corresponding function
|
||||
should be called */
|
||||
result = SDOS_SdoInd( (TINITSDOMBX MBXMEM *) pCoeMbx );
|
||||
break;
|
||||
|
||||
case COESERVICE_SDOINFO:
|
||||
/* SDO-Information Request received, call SDOS_SdoInfoInd to process the SDO-Request */
|
||||
result = SDOS_SdoInfoInd( (TSDOINFORMATION MBXMEM *) pCoeMbx );
|
||||
/*NOERROR_INWORK is never returned by SDOS_SdoInfoInd() => delete return code handling*/
|
||||
break;
|
||||
|
||||
|
||||
case COESERVICE_SDORESPONSE:
|
||||
case COESERVICE_EMERGENCY:
|
||||
case COESERVICE_TXPDO:
|
||||
case COESERVICE_RXPDO:
|
||||
case COESERVICE_TXPDOREMREQ:
|
||||
case COESERVICE_RXPDOREMREQ:
|
||||
/* these CoE services are not supported yet */
|
||||
result = MBXERR_SERVICENOTSUPPORTED;
|
||||
break;
|
||||
|
||||
default:
|
||||
result = MBXERR_INVALIDHEADER;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\param pMbx Pointer to the free mailbox to sent.
|
||||
|
||||
\return result of the operation (0 (success)
|
||||
|
||||
\brief This function is called when a CoE service to be sent is stored and can
|
||||
\brief be put in the send mailbox.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*ECATCHANGE_START(V5.11) COE4*/
|
||||
UINT8 COE_ContinueInd(TMBX MBXMEM * pMbx)
|
||||
/*ECATCHANGE_END(V5.11) COE4*/
|
||||
{
|
||||
if (pCoeSendStored)
|
||||
{
|
||||
/* send the stored CoE service which could not be sent before */
|
||||
MBX_MailboxSendReq(pCoeSendStored, 0);
|
||||
pCoeSendStored = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* send the next fragment of the last CoE service (only for SDO-Information possible) */
|
||||
/* in mailbox queue mode pMbx is always 0, so a mailbox buffer shall be get */
|
||||
pMbx = (TMBX MBXMEM *) APPL_AllocMailboxBuffer(SIZEOF(TMBX));
|
||||
/* it shall be checked if a valid pointer was returned */
|
||||
/*ECATCHANGE_START(V5.11) COE4*/
|
||||
if (pMbx == NULL)
|
||||
{
|
||||
return MBXERR_NOMOREMEMORY;
|
||||
}
|
||||
else
|
||||
/*ECATCHANGE_END(V5.11) COE4*/
|
||||
{
|
||||
/* copy the stored SDO-Info-Header in the request */
|
||||
MBXMEMCPY(pMbx, aSdoInfoHeader, SDO_INFO_HEADER_BYTE_SIZE);
|
||||
/* call SDOS_SdoInfoInd to generate and send the next fragment */
|
||||
SDOS_SdoInfoInd( (TSDOINFORMATION MBXMEM *) pMbx );
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,929 @@
|
||||
/**
|
||||
\addtogroup Mailbox Mailbox Functions
|
||||
@{
|
||||
*/
|
||||
|
||||
/**
|
||||
\file mailbox.c
|
||||
\author EthercatSSC@beckhoff.com
|
||||
\brief Implementation
|
||||
|
||||
<br>Description of the mailbox buffer handling (MAILBOX_QUEUE = 0):<br>
|
||||
There are two mailbox buffer for sending and receiving mailbox services.<br>
|
||||
Normal operation:<br>
|
||||
When starting the mailbox handler psWriteMbx contains mailbox buffer 1,<br>
|
||||
psReadMbx, psRepeatMbx and psStoreMbx are 0.<br>
|
||||
In this state a repeat request would be ignored because there was no service sent yet.<br>
|
||||
When the first mailbox service is sent (in MBX_CopyToSendMailbox), psWriteMbx gets mailbox buffer 2<br>
|
||||
and psReadMbx gets the sent mailbox buffer 1, psRepeatMbx and psStoreMbx are still 0.<br>
|
||||
When the first mailbox service was read from the master, the sent mailbox buffer 1 is stored<br>
|
||||
in psRepeatMbx (in MBX_MailboxReadInd).<br>
|
||||
After that psReadMbx gets always the actual sent mailbox buffer, psWriteMbx is set to 0 (another<br>
|
||||
received mailbox service from the master will not be handled until the sent mailbox service was read<br>
|
||||
and MBX_MailboxReadInd was called).<br>
|
||||
When the mailbox service is read, psWriteMbx gets the Buffer of psRepeatMbx and psRepeatMbx gets the <br>
|
||||
buffer of psReadMbx.<br>
|
||||
Repeat Request from the master:<br>
|
||||
When a Repeat from the master is requested (MBX_MailboxRepeatReq), there are three different possibilities:<br>
|
||||
1. no mailbox service was sent since the mailbox handler was started (psRepeatMbx = 0): nothing to do<br>
|
||||
2. the acknowledge of the last sent mailbox service was received (in MBX_MailboxReadInd) (bSendMbxIsFull = 0):<br>
|
||||
the last sent mailbox service (psRepeatMbx) will be sent again (in MBX_CopyToSendMailbox) and stored in psReadMbx,<br>
|
||||
psRepeatMbx will be set to 0<br>
|
||||
3. the acknowledge of the last sent mailbox service was not received (psReadMbx and psRepeatMbx contain different buffers,<br>
|
||||
psReadMbx is still in the mailbox (because MBX_MailboxReadInd is not called yet, bSendMbxIsFull = 1): <br>
|
||||
psReadMbx will be deleted in the mailbox (call of DisableSyncManChannel and EnableSyncManChannel) and<br>
|
||||
stored in psStoreMbx, psRepeatMbx will be sent again (in MBX_CopyToSendMailbox) and stored in psReadMbx,<br>
|
||||
psRepeatMbx will be set to 0.<br>
|
||||
When the repeated mailbox service was sent (call of MBX_MailboxReadInd), psReadMbx will be stored in psRepeatMbx<br>
|
||||
and psStoreMbx will be sent (in MBX_CopyToSendMailbox) and stored in psReadMbx, psStoreMbx will be set to 0.<br>
|
||||
|
||||
<br>Description of the mailbox buffer handling (MAILBOX_QUEUE = 1):<br>
|
||||
There are two mailbox buffer for sending and receiving mailbox services.<br>
|
||||
Normal operation (psWriteMbx is only used for local storage):<br>
|
||||
When starting the mailbox handler psReadMbx, psRepeatMbx and psStoreMbx are 0.<br>
|
||||
In this state a repeat request would be ignored because there was no service sent yet.<br>
|
||||
When a mailbox service is received from the master (in MBX_CheckAndCopyMailbox) a mailbox buffer<br>
|
||||
will be get with APPL_AllocMailboxBuffer and the corresponding protocol service function will<br>
|
||||
be called (in MBX_WriteMailboxInd). This buffer shall be used for the protocol service response.<br>
|
||||
When the first mailbox service is sent (in MBX_CopyToSendMailbox), psReadMbx gets the sent mailbox buffer,<br>
|
||||
psRepeatMbx and psStoreMbx are still 0.<br>
|
||||
When the first mailbox service was read from the master, the sent mailbox buffer (psReadMbx) is stored<br>
|
||||
in psRepeatMbx (in MBX_MailboxReadInd).<br>
|
||||
After that psReadMbx gets always the actual sent mailbox buffer (in MBX_CopyToSendMailbox)<br>
|
||||
When the mailbox service is read, psRepeatMbx is returned (with APPL_FreeMailboxBuffer) and psRepeatMbx gets the <br>
|
||||
buffer of psReadMbx.<br>
|
||||
Repeat Request from the master:<br>
|
||||
When a Repeat from the master is requested (MBX_MailboxRepeatReq), there are three different possibilities:<br>
|
||||
1. no mailbox service was sent since the mailbox handler was started (psRepeatMbx = 0): nothing to do<br>
|
||||
2. the acknowledge of the last sent mailbox service was received (in MBX_MailboxReadInd) (bSendMbxIsFull = 0):<br>
|
||||
the last sent mailbox service (psRepeatMbx) will be sent again (in MBX_CopyToSendMailbox) and stored in psReadMbx,<br>
|
||||
psRepeatMbx will be set to 0<br>
|
||||
3. the acknowledge of the last sent mailbox service was not received (psReadMbx and psRepeatMbx contain different buffers,<br>
|
||||
psReadMbx is still in the mailbox (because MBX_MailboxReadInd is not called yet, bSendMbxIsFull = 1): <br>
|
||||
psReadMbx will be deleted in the mailbox (call of DisableSyncManChannel and EnableSyncManChannel) and<br>
|
||||
stored in psStoreMbx, psRepeatMbx will be sent again (in MBX_CopyToSendMailbox) and stored in psReadMbx,<br>
|
||||
psRepeatMbx will be set to 0.<br>
|
||||
When the repeated mailbox service was sent (call of MBX_MailboxReadInd), psReadMbx will be stored in psRepeatMbx<br>
|
||||
and psStoreMbx will be sent (in MBX_CopyToSendMailbox) and stored in psReadMbx, psStoreMbx will be set to 0.
|
||||
|
||||
\version 5.11
|
||||
|
||||
<br>Changes to version V5.10:<br>
|
||||
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
|
||||
V5.11 ECAT7: add missing big endian swapping<br>
|
||||
V5.11 HW1: "move hardware independent functions ""HW_DisableSyncManChannel()"", ""HW_EnableSyncManChannel()"", ""HW_GetSyncMan()"", ""HW_ResetALEventMask()"", ""HW_SetALEventMask()"" to ecatalv.c"<br>
|
||||
V5.11 MBX2: "check in IP if enough dynamic memory is available to handle mailbox communication, if it is not the case the mbx error ""no memory"" is returned on any mbx request"<br>
|
||||
V5.11 MBX3: set application triggered emergency and EoE data to pending if no mailbox queue is supported and another mailbox request is currently handled, Handle only one mailbox request at a time (in case that MAILBPX_QUEUE is disabled)<br>
|
||||
V5.11 TEST4: add new mailbox test behaviour (the master mailbox cnt shall be incremented by 1 and the slave mailbox cnt is alternating)<br>
|
||||
<br>Changes to version V5.01:<br>
|
||||
V5.10 EOE3: Change local send frame pending indication variable to a global variable (it need to be resetted if the mailbox is stopped and a frame is pending)<br>
|
||||
V5.10 ESC6: Update SyncManager register size (only for 8Bit ESC access)<br>
|
||||
V5.10 MBX2: Allocate only memory for current configured mailbox size (max supported size was allocated before)<br>
|
||||
<br>Changes to version V5.0:<br>
|
||||
V5.01 ESC2: Add missed value swapping<br>
|
||||
V5.01 MBX2: Return an error if length specified in mailbnox header is too large.<br>
|
||||
<br>Changes to version V4.40:<br>
|
||||
V5.0 ECAT3: Global dummy variables used for dummy ESC operations.<br>
|
||||
V5.0 ESC1: ESC 32Bit Access added.<br>
|
||||
V5.0 MBX2: Prevent double swapping (MBX repeat service) of MBX length field.<br>
|
||||
V5.0 MBX4: Unlock SM buffer based on SM state.<br>
|
||||
<br>Changes to version V4.11:<br>
|
||||
V4.40 MBX8: Handle pending mailbox data even if mailbox queue is enabled<br>
|
||||
V4.40 MBX7: Reset SyncManger 1 Repeat Acknowledge bit if local variable is reset<br>
|
||||
V4.40 EMCY2: Send queued emergency messages<br>
|
||||
V4.40 BOOT2: Support only FoE in Bootstrap state<br>
|
||||
V4.40 MBX5: Prevent multiple free buffer<br>
|
||||
V4.40 MBX6: change return value if no mailbox buffer is available<br>
|
||||
<br>Changes to version V4.08:<br>
|
||||
V4.11 MBX 1: free some dynamic memory to avoid memory leaks in the mailbox queue<br>
|
||||
<br>Changes to version V4.07:<br>
|
||||
V4.10 MBX 1: check Mailbox message buffer and print emergency messages<br>
|
||||
during state change Init -> PreOP<br>
|
||||
<br>Changes to version V4.07:<br>
|
||||
V4.08 MBX 1: The mailbox queue was added (switch MAILBOX_QUEUE), if the<br>
|
||||
mailbox should be handled in the ESC-Interrupt Service Routine<br>
|
||||
the defines ENTER_MBX_CRITICAL and LEAVE_MBX_CRITICAL could be used<br>
|
||||
to disable the interrupts or change the priority<br>
|
||||
V4.08 AOE 1: AoE was added<br>
|
||||
<br>Changes to version V4.06:<br>
|
||||
V4.07 ECAT 1: The sources for SPI and MCI were merged (in ecat_def.h<br>
|
||||
set the switch MCI_HW to 1 when using the MCI,<br>
|
||||
set the switch SPI_HW to 1 when using the SPI
|
||||
*/
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Includes
|
||||
------
|
||||
---------------------------------------------------------------------------------------*/
|
||||
|
||||
#include "ecat_def.h"
|
||||
|
||||
|
||||
#include "ecatslv.h"
|
||||
|
||||
|
||||
#define _MAILBOX_ 1
|
||||
#include "mailbox.h"
|
||||
#undef _MAILBOX_
|
||||
/* ECATCHANGE_START(V5.11) ECAT10*/
|
||||
/*remove definition of _MAILBOX_ (#ifdef is used in mailbox.h)*/
|
||||
/* ECATCHANGE_END(V5.11) ECAT10*/
|
||||
|
||||
#include "ecatcoe.h"
|
||||
|
||||
/*--------------------------------------------------------------------------------------
|
||||
------
|
||||
------ internal Types and Defines
|
||||
------
|
||||
--------------------------------------------------------------------------------------*/
|
||||
|
||||
/*--------------------------------------------------------------------------------------
|
||||
------
|
||||
------ internal Variables
|
||||
------
|
||||
--------------------------------------------------------------------------------------*/
|
||||
/*variables are declared in ecatslv.c*/
|
||||
extern VARVOLATILE UINT16 u16dummy;
|
||||
|
||||
/* ECATCHANGE_START(V5.11) MBX2*/
|
||||
BOOL bNoMbxMemoryAvailable; /**< \brief Indicates if enough dynamic memory is available to handle at least one mailbox datagram */
|
||||
/* ECATCHANGE_END(V5.11) MBX2*/
|
||||
/*--------------------------------------------------------------------------------------
|
||||
------
|
||||
------ internal functions
|
||||
------
|
||||
--------------------------------------------------------------------------------------*/
|
||||
|
||||
/*---------------------------------------------------------------------------------------
|
||||
------
|
||||
------ functions
|
||||
------
|
||||
---------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PutInMbxQueue
|
||||
//
|
||||
|
||||
UINT8 PutInMbxQueue(TMBX MBXMEM * pMbx, TMBXQUEUE MBXMEM * pQueue)
|
||||
{
|
||||
UINT16 lastInQueue;
|
||||
ENTER_MBX_CRITICAL;
|
||||
|
||||
|
||||
lastInQueue = pQueue->lastInQueue+1;
|
||||
if (lastInQueue == pQueue->maxQueueSize)
|
||||
{
|
||||
// Umbruch der Queue
|
||||
lastInQueue = 0;
|
||||
}
|
||||
|
||||
if (pQueue->firstInQueue == lastInQueue)
|
||||
{
|
||||
// Ueberlauf der Queue -> letztes Element wieder herausnehmen
|
||||
LEAVE_MBX_CRITICAL;
|
||||
return MBXERR_NOMOREMEMORY;
|
||||
}
|
||||
|
||||
pQueue->queue[pQueue->lastInQueue] = pMbx;
|
||||
pQueue->lastInQueue = lastInQueue;
|
||||
|
||||
LEAVE_MBX_CRITICAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// GetOutOfMbxQueue
|
||||
//
|
||||
|
||||
TMBX MBXMEM * GetOutOfMbxQueue(TMBXQUEUE MBXMEM * pQueue)
|
||||
{
|
||||
TMBX MBXMEM * pMbx;
|
||||
ENTER_MBX_CRITICAL;
|
||||
|
||||
if (pQueue->firstInQueue != pQueue->lastInQueue)
|
||||
{
|
||||
// Queue ist nicht leer
|
||||
UINT16 firstInQueue = pQueue->firstInQueue;
|
||||
pMbx = pQueue->queue[firstInQueue];
|
||||
firstInQueue++;
|
||||
pQueue->firstInQueue = firstInQueue;
|
||||
if (pQueue->firstInQueue == pQueue->maxQueueSize)
|
||||
{
|
||||
// Umbruch der Queue
|
||||
pQueue->firstInQueue = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
pMbx = 0;
|
||||
|
||||
|
||||
LEAVE_MBX_CRITICAL;
|
||||
|
||||
return pMbx;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function intialize the Mailbox Interface.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MBX_Init(void)
|
||||
{
|
||||
u16ReceiveMbxSize = MIN_MBX_SIZE;
|
||||
u16SendMbxSize = MAX_MBX_SIZE;
|
||||
u16EscAddrReceiveMbx = MIN_MBX_WRITE_ADDRESS;
|
||||
u16EscAddrSendMbx = MIN_MBX_READ_ADDRESS;
|
||||
|
||||
sMbxReceiveQueue.firstInQueue = 0;
|
||||
sMbxReceiveQueue.lastInQueue = 0;
|
||||
sMbxReceiveQueue.maxQueueSize = MAX_MBX_QUEUE_SIZE;
|
||||
sMbxSendQueue.firstInQueue = 0;
|
||||
sMbxSendQueue.lastInQueue = 0;
|
||||
sMbxSendQueue.maxQueueSize = MAX_MBX_QUEUE_SIZE;
|
||||
psWriteMbx = NULL;
|
||||
|
||||
|
||||
psRepeatMbx = NULL;
|
||||
psReadMbx = NULL;
|
||||
psStoreMbx = NULL;
|
||||
|
||||
bMbxRepeatToggle = FALSE;
|
||||
/*Reset Repeat acknowledge bit of SyncManager1 (0x80F bit 2)*/
|
||||
{
|
||||
UINT16 sm1Activate = 0;
|
||||
HW_EscReadWord(sm1Activate,(ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER));
|
||||
sm1Activate &= SWAPWORD(~0x0200);
|
||||
HW_EscWriteWord(sm1Activate,(ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER));
|
||||
}
|
||||
bMbxRunning = FALSE;
|
||||
bSendMbxIsFull = FALSE;
|
||||
bReceiveMbxIsLocked = FALSE;
|
||||
u8MailboxSendReqStored = 0;
|
||||
u8MbxWriteCounter = 0;
|
||||
u8MbxReadCounter = 0;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
|
||||
\brief This function includes the state transition from INIT to
|
||||
\brief PRE-OPERATIONAL in the EtherCAT Slave corresponding to
|
||||
\brief local management service Start Mailbox Handler
|
||||
\brief it is checked if the mailbox areas overlaps each other
|
||||
\brief and the Sync Manager channels 0 and 1 are enabled.
|
||||
\brief This function shall only be called if mailbox is supported.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
UINT16 MBX_StartMailboxHandler(void)
|
||||
{
|
||||
UINT16 result = 0;
|
||||
/* get address of the receive mailbox sync manager (SM0) */
|
||||
/*ECATCHANGE_START(V5.11) HW1*/
|
||||
TSYNCMAN ESCMEM * pSyncMan = (TSYNCMAN ESCMEM *)GetSyncMan(MAILBOX_WRITE);
|
||||
/*ECATCHANGE_END(V5.11) HW1*/
|
||||
/* store size of the receive mailbox */
|
||||
u16ReceiveMbxSize = pSyncMan->Length;
|
||||
/* store the address of the receive mailbox */
|
||||
u16EscAddrReceiveMbx = pSyncMan->PhysicalStartAddress;
|
||||
|
||||
/* get address of the send mailbox sync manager (SM1) */
|
||||
/*ECATCHANGE_START(V5.11) HW1*/
|
||||
pSyncMan =(TSYNCMAN ESCMEM *) GetSyncMan(MAILBOX_READ);
|
||||
/*ECATCHANGE_END(V5.11) HW1*/
|
||||
|
||||
/* store the size of the send mailbox */
|
||||
u16SendMbxSize = pSyncMan->Length;
|
||||
/* store the address of the send mailbox */
|
||||
u16EscAddrSendMbx = pSyncMan->PhysicalStartAddress;
|
||||
|
||||
// HBu 02.05.06: it should be checked if there are overlaps in the sync manager areas
|
||||
if ((u16EscAddrReceiveMbx + u16ReceiveMbxSize) > u16EscAddrSendMbx && (u16EscAddrReceiveMbx < (u16EscAddrSendMbx + u16SendMbxSize)))
|
||||
{
|
||||
return ALSTATUSCODE_INVALIDMBXCFGINPREOP;
|
||||
}
|
||||
|
||||
/* enable the receive mailbox sync manager channel */
|
||||
/*ECATCHANGE_START(V5.11) HW1*/
|
||||
EnableSyncManChannel(MAILBOX_WRITE);
|
||||
/* enable the send mailbox sync manager channel */
|
||||
EnableSyncManChannel(MAILBOX_READ);
|
||||
/*ECATCHANGE_END(V5.11) HW1*/
|
||||
|
||||
/* ECATCHANGE_START(V5.11) MBX2*/
|
||||
psWriteMbx = (TMBX MBXMEM *) APPL_AllocMailboxBuffer(u16ReceiveMbxSize);
|
||||
if(psWriteMbx == NULL)
|
||||
{
|
||||
bNoMbxMemoryAvailable = TRUE;
|
||||
|
||||
//check if at least enough memory for an mailbox error is available (other wise stop the state transition)
|
||||
psWriteMbx = (TMBX MBXMEM *) APPL_AllocMailboxBuffer(10); /* a mailbox error datagram length*/
|
||||
if(psWriteMbx == NULL)
|
||||
{
|
||||
result = ALSTATUSCODE_NOMEMORY;
|
||||
}
|
||||
|
||||
APPL_FreeMailboxBuffer(psWriteMbx);
|
||||
}
|
||||
else
|
||||
{
|
||||
bNoMbxMemoryAvailable = FALSE;
|
||||
APPL_FreeMailboxBuffer(psWriteMbx);
|
||||
}
|
||||
/* ECATCHANGE_END(V5.11) MBX2*/
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
|
||||
\brief This function includes the state transition from
|
||||
\brief PRE-OPERATIONAL to INIT in the EtherCAT Slave corresponding to
|
||||
\brief local management service Stop Mailbox Handler
|
||||
\brief the Sync Manager channels 0 and 1 are disabled
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MBX_StopMailboxHandler(void)
|
||||
{
|
||||
TMBX MBXMEM * pMbx;
|
||||
|
||||
/* mailbox handler is stopped */
|
||||
bMbxRunning = FALSE;
|
||||
/* disable the receive mailbox sync manager channel */
|
||||
/*ECATCHANGE_START(V5.11) HW1*/
|
||||
DisableSyncManChannel(MAILBOX_WRITE);
|
||||
/* disable the send mailbox sync manager channel */
|
||||
DisableSyncManChannel(MAILBOX_READ);
|
||||
/*ECATCHANGE_END(V5.11) HW1*/
|
||||
/* initialize variables again */
|
||||
|
||||
|
||||
if (psRepeatMbx != NULL)
|
||||
APPL_FreeMailboxBuffer(psRepeatMbx);
|
||||
|
||||
if (psStoreMbx != NULL && psStoreMbx != psRepeatMbx)
|
||||
APPL_FreeMailboxBuffer(psStoreMbx);
|
||||
|
||||
if (psReadMbx != NULL && psReadMbx != psRepeatMbx && psReadMbx != psStoreMbx)
|
||||
APPL_FreeMailboxBuffer(psReadMbx);
|
||||
|
||||
|
||||
psRepeatMbx = NULL;
|
||||
psReadMbx = NULL;
|
||||
psStoreMbx = NULL;
|
||||
|
||||
bMbxRepeatToggle = FALSE;
|
||||
/*Reset Repeat acknowledge bit of SyncManager1 (0x080F bit 2)*/
|
||||
{
|
||||
UINT16 sm1Activate = 0;
|
||||
HW_EscReadWord(sm1Activate,(ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER));
|
||||
sm1Activate &= SWAPWORD(~0x0200);
|
||||
HW_EscWriteWord(sm1Activate,(ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER));
|
||||
}
|
||||
bSendMbxIsFull = FALSE;
|
||||
bReceiveMbxIsLocked = FALSE;
|
||||
u8MailboxSendReqStored = 0;
|
||||
u8MbxWriteCounter = 0;
|
||||
u8MbxReadCounter = 0;
|
||||
|
||||
do
|
||||
{
|
||||
pMbx = GetOutOfMbxQueue(&sMbxReceiveQueue);
|
||||
if (pMbx)
|
||||
{
|
||||
APPL_FreeMailboxBuffer(pMbx);
|
||||
pMbx = NULL;
|
||||
}
|
||||
} while (pMbx != NULL);
|
||||
do
|
||||
{
|
||||
pMbx = GetOutOfMbxQueue(&sMbxSendQueue);
|
||||
if (pMbx)
|
||||
{
|
||||
APPL_FreeMailboxBuffer(pMbx);
|
||||
pMbx = NULL;
|
||||
}
|
||||
} while (pMbx != NULL);
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\param pMbx Pointer to the received Mailbox command from Master.
|
||||
|
||||
\brief The function checks the mailbox header for the requested service and calls the
|
||||
\brief corresponding XXXX_ServiceInd-function
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
UINT8 MailboxServiceInd(TMBX MBXMEM *pMbx)
|
||||
{
|
||||
UINT8 result;
|
||||
|
||||
|
||||
switch ( (pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] & MBX_MASK_TYPE) >> MBX_SHIFT_TYPE )
|
||||
{
|
||||
case MBX_TYPE_COE:
|
||||
/* CoE datagram received */
|
||||
result = COE_ServiceInd((TCOEMBX MBXMEM *) pMbx);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
result = MBXERR_UNSUPPORTEDPROTOCOL;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\param pMbx Pointer to the received Mailbox command from Master.
|
||||
|
||||
\brief This function is called when the Master has written the Receive-Mailbox.
|
||||
\brief It will only be called if the send mailbox is empty, that a response for the
|
||||
\brief mailbox service could be stored.
|
||||
\brief The function checks the mailbox header for the requested service and calls the
|
||||
\brief corresponding XXXX_ServiceInd-function
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MBX_MailboxWriteInd(TMBX MBXMEM *pMbx)
|
||||
{
|
||||
UINT8 result = 0;
|
||||
UINT8 mbxCounter = pMbx->MbxHeader.Flags[MBX_OFFS_COUNTER] >> MBX_SHIFT_COUNTER;
|
||||
UINT16 MbxLen = SWAPWORD(pMbx->MbxHeader.Length);
|
||||
|
||||
if(MbxLen > MAX_MBX_SIZE)
|
||||
{
|
||||
/* Mailbox error response: size specified in mailbox header too large*/
|
||||
pMbx->MbxHeader.Length = 4;
|
||||
pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] &= ~MBX_MASK_TYPE;
|
||||
pMbx->Data[0] = SWAPWORD(MBXSERVICE_MBXERRORCMD);
|
||||
pMbx->Data[1] = SWAPWORD(MBXERR_INVALIDSIZE);
|
||||
MBX_MailboxSendReq(pMbx, 0);
|
||||
}
|
||||
else
|
||||
/* if the mailbox datagram counter (Bit 4-6 of Byte 5 of the mailbox header) is unequal zero,
|
||||
the master supports the mailbox data link layer,
|
||||
in that case a repeated mailbox write request will be detected, if the counter is unequal zero
|
||||
and unchanged */
|
||||
if ( mbxCounter == 0 || mbxCounter != u8MbxWriteCounter )
|
||||
{
|
||||
/* new mailbox service received */
|
||||
/* mbxCounter = 0: old EtherCAT master */
|
||||
/* new MBX service received, store the new mailbox counter */
|
||||
u8MbxWriteCounter = mbxCounter;
|
||||
|
||||
{
|
||||
/* check the protocol type and call the XXXX_ServiceInd-function */
|
||||
|
||||
result = PutInMbxQueue(pMbx, &sMbxReceiveQueue);
|
||||
}
|
||||
|
||||
if ( result != 0 )
|
||||
{
|
||||
/* Mailbox error response: type 0 (mailbox service protocol) */
|
||||
pMbx->MbxHeader.Length = 4;
|
||||
pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] &= ~MBX_MASK_TYPE;
|
||||
pMbx->Data[0] = SWAPWORD(MBXSERVICE_MBXERRORCMD);
|
||||
pMbx->Data[1] = SWAPWORD(result);
|
||||
MBX_MailboxSendReq(pMbx, 0);
|
||||
}
|
||||
}
|
||||
// the mailbox buffer has to be freed here
|
||||
else
|
||||
{
|
||||
APPL_FreeMailboxBuffer(pMbx);
|
||||
pMbx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function is called when the Master has read the Send-Mailbox.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MBX_MailboxReadInd(void)
|
||||
{
|
||||
bSendMbxIsFull = FALSE;
|
||||
// HBu 02.05.06: the pointer psRepeatMbx is only free if there is no stored
|
||||
// mailbox service from the last repeat
|
||||
if ( psRepeatMbx && psStoreMbx == NULL )
|
||||
{
|
||||
/* the last sent service is not stored for repeat any longer */
|
||||
APPL_FreeMailboxBuffer(psRepeatMbx);
|
||||
psRepeatMbx = NULL;
|
||||
}
|
||||
|
||||
/* the actual sent service has to be stored for repeat */
|
||||
psRepeatMbx = psReadMbx;
|
||||
|
||||
if ( psStoreMbx )
|
||||
{
|
||||
/* there was a buffer stored */
|
||||
MBX_CopyToSendMailbox(psStoreMbx);
|
||||
/* no more buffer to be stored any more */
|
||||
psStoreMbx = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
TMBX MBXMEM *pMbx = GetOutOfMbxQueue(&sMbxSendQueue);
|
||||
if (pMbx)
|
||||
{
|
||||
MBX_CopyToSendMailbox(pMbx);
|
||||
}
|
||||
}
|
||||
|
||||
if ( u8MailboxSendReqStored )
|
||||
{
|
||||
/* there are mailbox services stored to be sent */
|
||||
if ( u8MailboxSendReqStored & COE_SERVICE )
|
||||
{
|
||||
UINT8 result = 0;
|
||||
/* reset the flag indicating that CoE service to be sent was stored */
|
||||
u8MailboxSendReqStored &= ~COE_SERVICE;
|
||||
|
||||
/* call CoE function that will send the stored CoE service */
|
||||
result = COE_ContinueInd(psWriteMbx);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
/*Set the pending CoE indication is an error occurred during the continue indication*/
|
||||
u8MailboxSendReqStored |= COE_SERVICE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function is called if the Master has requested a resending of the last
|
||||
\brief sent mailbox
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MBX_MailboxRepeatReq(void)
|
||||
{
|
||||
if (psRepeatMbx)
|
||||
{
|
||||
TMBX MBXMEM *pMbx = psRepeatMbx;
|
||||
/* send mailbox service stored for repeat */
|
||||
/* HBu 13.10.06: if a repeat request is received (again) before the previously repeated mailbox telegram
|
||||
was read from the master (psStoreMbx != NULL) the next mailbox telegram to be sent is still in the
|
||||
read mailbox so it has not to updated exchanged */
|
||||
ENTER_MBX_CRITICAL;
|
||||
|
||||
if (bSendMbxIsFull && psStoreMbx == NULL)
|
||||
{
|
||||
/* mailbox is full, take the buffer off */
|
||||
/*ECATCHANGE_START(V5.11) HW1*/
|
||||
DisableSyncManChannel(MAILBOX_READ);
|
||||
|
||||
/* store the buffer to be sent next */
|
||||
psStoreMbx = psReadMbx;
|
||||
/* enable the mailbox again */
|
||||
EnableSyncManChannel(MAILBOX_READ);
|
||||
/*ECATCHANGE_END(V5.11) HW1*/
|
||||
|
||||
/* HBu 15.02.06: flag has to be reset otherwise the mailbox service
|
||||
will not be copied by MBX_CopyToSendMailbox */
|
||||
bSendMbxIsFull = FALSE;
|
||||
}
|
||||
|
||||
|
||||
MBX_CopyToSendMailbox(pMbx);
|
||||
// HBu 17.06.06: psRepeatMbx has to be set to 0, when it was repeated, otherwise it would be returned twice
|
||||
// to the empty queue (MAILBOX_QUEUE=1) or a buffer get lost, if the the next repeat request will happen before
|
||||
// the repeated buffer was read
|
||||
psRepeatMbx = NULL;
|
||||
LEAVE_MBX_CRITICAL;
|
||||
}
|
||||
|
||||
// Repeat was finished, toggle the acknowledge bit
|
||||
bMbxRepeatToggle = !bMbxRepeatToggle;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\param pMbx Pointer to a Mailbox command to be sent (read by the Master)
|
||||
\param flags Bit 0-6: mailbox protocol type:
|
||||
0x01 - emergency service
|
||||
0x02 - CoE service
|
||||
0x04 - SoE service
|
||||
0x80 - EoE service
|
||||
0x10 - AoE service
|
||||
0x20 - VoE service
|
||||
Bit 7: 0 - no more fragments to be sent for the requested mailbox service
|
||||
1 - additional fragments to be sent for the mailbox service, the
|
||||
corresponding XXXX_ContinueInd-function will be called to get
|
||||
the next fragment
|
||||
|
||||
\return 0: Success - mailbox command could be stored in the send mailbox
|
||||
1: Failed - mailbox command could not be stored in the send mailbox, the
|
||||
XXXX_ContinueInd service will be called when the mailbox was
|
||||
read from the master to
|
||||
|
||||
\brief This function puts a new Mailbox service in the Send Mailbox
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
UINT8 MBX_MailboxSendReq( TMBX MBXMEM * pMbx, UINT8 flags )
|
||||
{
|
||||
UINT8 result = 0;
|
||||
|
||||
/* HBu 06.02.06: in INIT-state a mailbox send request shall be refused */
|
||||
if ( (nAlStatus & STATE_MASK) == STATE_INIT )
|
||||
return ERROR_INVALIDSTATE;
|
||||
|
||||
|
||||
ENTER_MBX_CRITICAL;
|
||||
DISABLE_MBX_INT;
|
||||
|
||||
/* the counter in the mailbox header has to be incremented with every new mailbox service to be sent
|
||||
if the mailbox data link layer is supported (software switch MAILBOX_REPEAT_SUPPORTED set)*/
|
||||
pMbx->MbxHeader.Flags[MBX_OFFS_COUNTER] &= ~MBX_MASK_COUNTER;
|
||||
/* HBu 13.02.06: Repeat-Counter was incremented too much if the mailbox service could not be sent */
|
||||
/* u8MbxCounter holds the actual counter for the mailbox header, only the values
|
||||
1-7 are allowed if the mailbox data link layer is supported */
|
||||
if ( (u8MbxReadCounter & 0x07) == 0 )
|
||||
u8MbxReadCounter = 1;
|
||||
|
||||
pMbx->MbxHeader.Flags[MBX_OFFS_COUNTER] |= u8MbxReadCounter << MBX_SHIFT_COUNTER;
|
||||
|
||||
/* try to copy the mailbox command in the ESC */
|
||||
if ( MBX_CopyToSendMailbox(pMbx) != 0 )
|
||||
{
|
||||
/* no success, send mailbox was full, set flag */
|
||||
result = PutInMbxQueue(pMbx, &sMbxSendQueue);
|
||||
if (result != 0)
|
||||
flags |= FRAGMENTS_FOLLOW;
|
||||
else
|
||||
u8MbxReadCounter++;
|
||||
}
|
||||
/* HBu 13.02.06: Repeat-Counter was incremented too much if the mailbox service could not be sent */
|
||||
else
|
||||
{
|
||||
u8MbxReadCounter++;
|
||||
}
|
||||
|
||||
if ( flags & FRAGMENTS_FOLLOW )
|
||||
{
|
||||
/* store the mailbox service that the corresponding XXX_ContinueInd function will
|
||||
be called when the send mailbox will have been read by the master because there
|
||||
are mailbox commands to be sent for this service */
|
||||
u8MailboxSendReqStored |= (flags & ((UINT8) ~FRAGMENTS_FOLLOW));
|
||||
}
|
||||
|
||||
ENABLE_MBX_INT;
|
||||
LEAVE_MBX_CRITICAL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function is used to check if the received mailbox command can be processed.
|
||||
|
||||
Also the contents of the Receive Mailbox will be copied in the variable sMbx.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MBX_CheckAndCopyMailbox( void )
|
||||
{
|
||||
UINT16 mbxLen;
|
||||
|
||||
/* get the size of the received mailbox command and acknowledge the event*/
|
||||
HW_EscReadWord(mbxLen,u16EscAddrReceiveMbx);
|
||||
|
||||
/* the size has to be swapped here, all other bytes of the mailbox service will be swapped later */
|
||||
mbxLen = SWAPWORD(mbxLen);
|
||||
|
||||
/* ECATCHANGE_START(V5.11) MBX2*/
|
||||
if(bNoMbxMemoryAvailable == TRUE)
|
||||
{
|
||||
/* Return a no memory error in case of any mailbox request*/
|
||||
TMBX MBXMEM *pMbx = (TMBX MBXMEM *) APPL_AllocMailboxBuffer(10); /* a mailbox error datagram length*/
|
||||
|
||||
if(pMbx != NULL)
|
||||
{
|
||||
HMEMSET(pMbx,0x00,10);
|
||||
|
||||
/* Mailbox error response: type 0 (mailbox service protocol) */
|
||||
pMbx->MbxHeader.Length = 4;
|
||||
pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] &= ~(MBX_MASK_TYPE);
|
||||
pMbx->Data[0] = SWAPWORD(MBXSERVICE_MBXERRORCMD);
|
||||
pMbx->Data[1] = SWAPWORD(MBXERR_NOMOREMEMORY);
|
||||
MBX_MailboxSendReq(pMbx, 0);
|
||||
|
||||
APPL_FreeMailboxBuffer(pMbx);
|
||||
}
|
||||
}
|
||||
/* ECATCHANGE_END(V5.11) MBX2*/
|
||||
/* the length of the mailbox data is in the first two bytes of the mailbox,
|
||||
so the length of the mailbox header has to be added */
|
||||
mbxLen += MBX_HEADER_SIZE;
|
||||
|
||||
/* in this example there are only two mailbox buffers available in the firmware (one for processing and
|
||||
one to stored the last sent response for a possible repeat request), so a
|
||||
received mailbox service can only be processed if a free buffer is available */
|
||||
if ( ( bSendMbxIsFull ) /* a received mailbox service will not be processed
|
||||
as long as the send mailbox is still full
|
||||
(waits to be read from the master) */
|
||||
||( u8MailboxSendReqStored ) /* a mailbox service to be sent is still stored
|
||||
so the received mailbox service will not be processed
|
||||
until all stored mailbox services are sent */
|
||||
)
|
||||
{
|
||||
/* set flag that the processing of the mailbox service will be checked in the
|
||||
function MBX_Main (called from ECAT_Main) */
|
||||
bReceiveMbxIsLocked = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* received mailbox command can be processed, reset flag */
|
||||
bReceiveMbxIsLocked = FALSE;
|
||||
|
||||
/* if the read mailbox size is too big for the buffer, set the copy size to the maximum buffer size, otherwise
|
||||
memory could be overwritten,
|
||||
the evaluation of the mailbox size will be done in the mailbox protocols called from MBX_WriteMailboxInd */
|
||||
|
||||
if (mbxLen > u16ReceiveMbxSize)
|
||||
mbxLen = u16ReceiveMbxSize;
|
||||
|
||||
psWriteMbx = (TMBX MBXMEM *) APPL_AllocMailboxBuffer(u16ReceiveMbxSize);
|
||||
|
||||
/* if there is no more memory for mailbox buffer, the mailbox should not be read */
|
||||
if (psWriteMbx == NULL)
|
||||
{
|
||||
/* set flag that the processing of the mailbox service will be checked in the
|
||||
function MBX_Main (called from ECAT_Main) */
|
||||
bReceiveMbxIsLocked = TRUE;
|
||||
return;
|
||||
}
|
||||
/* copy the mailbox header and data*/
|
||||
HW_EscReadMbxMem((MEM_ADDR MBXMEM *) psWriteMbx,u16EscAddrReceiveMbx,mbxLen);
|
||||
|
||||
|
||||
{
|
||||
/*Read Control and Status of SyncManager 0 to check if the buffer is unlocked*/
|
||||
VARVOLATILE UINT16 smstate = 0x00;
|
||||
HW_EscReadWord(smstate,ESC_SYNCMAN_CONTROL_OFFSET);
|
||||
/*ECATCHANGE_START(V5.11) ECAT7*/
|
||||
smstate = SWAPWORD(smstate);
|
||||
/*ECATCHANGE_END(V5.11) ECAT7*/
|
||||
|
||||
if(smstate & SM_STATUS_MBX_BUFFER_FULL)
|
||||
{
|
||||
/*Unlock the mailbox SyncManger buffer*/
|
||||
u16dummy = 0;
|
||||
HW_EscReadWord(u16dummy,(u16EscAddrReceiveMbx + u16ReceiveMbxSize - 2));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* in MBX_MailboxWriteInd the mailbox protocol will be processed */
|
||||
MBX_MailboxWriteInd( psWriteMbx );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\param pMbx Pointer to the Mailbox command to be send to the master.
|
||||
|
||||
\brief This function copies data to the Send Mailbox.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
UINT8 MBX_CopyToSendMailbox( TMBX MBXMEM *pMbx )
|
||||
{
|
||||
if ( (nAlStatus & STATE_MASK) == STATE_INIT)
|
||||
{
|
||||
/* the mailbox is disabled if the slave is in the INIT state */
|
||||
return( ERROR_INVALIDSTATE );
|
||||
}
|
||||
|
||||
|
||||
if ( !bMbxRunning )
|
||||
{
|
||||
/* the mailbox is disabled if the slave is in the INIT state */
|
||||
return( ERROR_INVALIDSTATE );
|
||||
}
|
||||
|
||||
if ( bSendMbxIsFull )
|
||||
{
|
||||
/* mailbox service cannot be sent because the send mailbox is still full */
|
||||
return MBXERR_NOMOREMEMORY;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* the variable mbxSize contains the size of the mailbox data to be sent */
|
||||
UINT16 mbxSize = pMbx->MbxHeader.Length;
|
||||
HW_EscWriteMbxMem((MEM_ADDR *)pMbx, u16EscAddrSendMbx, (mbxSize + MBX_HEADER_SIZE));
|
||||
/* ECATCHANGE_HW(V5.10) HW1*/
|
||||
|
||||
{
|
||||
/*Read Control and Status of SyncManager 1 to check if the buffer is still marked as empty*/
|
||||
VARVOLATILE UINT16 smstate = 0x00;
|
||||
HW_EscReadWord(smstate,(ESC_SYNCMAN_CONTROL_OFFSET + SIZEOF_SM_REGISTER));
|
||||
/*ECATCHANGE_START(V5.11) ECAT7*/
|
||||
smstate = SWAPWORD(smstate);
|
||||
/*ECATCHANGE_END(V5.11) ECAT7*/
|
||||
|
||||
if(!(smstate & SM_STATUS_MBX_BUFFER_FULL))
|
||||
{
|
||||
UINT8 BytesLeft = u16SendMbxSize - (mbxSize + MBX_HEADER_SIZE);
|
||||
|
||||
/*Write last Byte to trigger mailbox full flag*/
|
||||
/*Read last 2 Bytes and write them again (required if low Byte of the WORD were written before)*/
|
||||
u16dummy = 0;
|
||||
if(BytesLeft < 2)
|
||||
{
|
||||
/*The last 2Bytes are overlapping the already written buffer*/
|
||||
|
||||
/*Get the valid 16Bit address*/
|
||||
UINT32 LastDataAddress = ((mbxSize + MBX_HEADER_SIZE)/2)*2;
|
||||
|
||||
/*Copy the buffer to overwrite*/
|
||||
MEMCPY((UINT16 *)&u16dummy,(((UINT8 *)pMbx) + LastDataAddress),(2 - BytesLeft));
|
||||
}
|
||||
|
||||
HW_EscWriteWord(u16dummy,(u16EscAddrSendMbx + u16SendMbxSize - 2));
|
||||
}
|
||||
}
|
||||
|
||||
/* store last send mailbox service for a possible repeat
|
||||
one buffer includes the last send service (psRepeatMbx),
|
||||
the other one the actual service to be sent (psReadMbx),
|
||||
there is no buffer available for a mailbox receive service
|
||||
until the last sent buffer was read from the master
|
||||
the exception is after the INIT2PREOP transition, in that
|
||||
case there is no last sent service (psReadMbx = 0) */
|
||||
if ( psReadMbx )
|
||||
psWriteMbx = NULL;
|
||||
else
|
||||
{
|
||||
/* only the first time after the INIT2PREOP-transition */
|
||||
psWriteMbx = &asMbx[1];
|
||||
}
|
||||
// HBu 17.06.06: psRepeatMbx was already updated in MBX_MailboxReadInd
|
||||
// psRepeatMbx = psReadMbx;
|
||||
psReadMbx = pMbx;
|
||||
|
||||
/* set flag that send mailbox is full now */
|
||||
bSendMbxIsFull = TRUE;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function is called cyclically to check if a received Mailbox service was
|
||||
stored.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MBX_Main(void)
|
||||
{
|
||||
TMBX MBXMEM *pMbx = NULL;
|
||||
|
||||
do
|
||||
{
|
||||
UINT8 result = 0;
|
||||
|
||||
pMbx = GetOutOfMbxQueue(&sMbxReceiveQueue);
|
||||
if ( pMbx )
|
||||
result = MailboxServiceInd(pMbx);
|
||||
|
||||
if ( result != 0 )
|
||||
{
|
||||
/* Mailbox error response: type 0 (mailbox service protocol) */
|
||||
pMbx->MbxHeader.Length = 4;
|
||||
pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] &= ~(MBX_MASK_TYPE);
|
||||
pMbx->Data[0] = SWAPWORD(MBXSERVICE_MBXERRORCMD);
|
||||
pMbx->Data[1] = SWAPWORD(result);
|
||||
MBX_MailboxSendReq(pMbx, 0);
|
||||
}
|
||||
}
|
||||
while ( pMbx != NULL );
|
||||
|
||||
|
||||
|
||||
if ( bReceiveMbxIsLocked )
|
||||
{
|
||||
/* the work on the receive mailbox is locked, check if it can be unlocked (if all
|
||||
mailbox commands has been sent */
|
||||
MBX_CheckAndCopyMailbox();
|
||||
}
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user