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:
cbb
2026-08-09 03:27:21 +08:00
parent 8e5da18100
commit a2d0b28b3d
49 changed files with 13258 additions and 554 deletions
+780
View File
@@ -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;
}
}
}
/** @} */
+630
View File
@@ -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 */
}
/** @} */
+180
View File
@@ -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
+929
View File
@@ -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