├── libraries └── arducat │ ├── 工作记录.txt │ ├── EtherCAT SSC License V1.1.pdf │ ├── ethercat.cpp │ ├── ecatcoe.h │ ├── ecatappl.h │ ├── mailbox.h │ ├── ecatcoe.cpp │ ├── objdef.h │ ├── esc.h │ ├── ecatslv.h │ ├── ethercat.h │ ├── coeappl.cpp │ ├── sdoserv.h │ ├── ecatappl.cpp │ └── hw.cpp ├── EtherCATSlave ├── SSC-Device.xls ├── Example_MiscDevice.xls ├── SlaveGeneraor.cfg ├── LICENSE ├── EthercatType.py ├── SlaveGenerator.py ├── SlaveReader.py ├── PDOMapping.py ├── ODGenerator.py └── XmlGenerator.py ├── .gitignore └── README.md /libraries/arducat/工作记录.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethercat-diy/arducat/HEAD/libraries/arducat/工作记录.txt -------------------------------------------------------------------------------- /EtherCATSlave/SSC-Device.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethercat-diy/arducat/HEAD/EtherCATSlave/SSC-Device.xls -------------------------------------------------------------------------------- /EtherCATSlave/Example_MiscDevice.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethercat-diy/arducat/HEAD/EtherCATSlave/Example_MiscDevice.xls -------------------------------------------------------------------------------- /EtherCATSlave/SlaveGeneraor.cfg: -------------------------------------------------------------------------------- 1 | -i SSC-Device.xls 2 | -c EtherCATSlave.ino 3 | -x C:\TwinCAT\3.1\Config\Io\EtherCAT\SlaveDevice.xml -------------------------------------------------------------------------------- /libraries/arducat/EtherCAT SSC License V1.1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethercat-diy/arducat/HEAD/libraries/arducat/EtherCAT SSC License V1.1.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.ino 3 | *.xml 4 | / 5 | 6 | !libraries/arduinocat/ 7 | !EtherCATSlave/ 8 | !EtherCATSlave/SSC-Template.xml 9 | !EtherCATSlave/EtherCATSlave.ino -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # arducat 2 | The ArduCAT is an Arduino “At Heart” board aiming to ease the EtherCAT slave device development with the Arduino technology. The board provides 2 100BASE-TX ports for EtherCAT real-time data link which is ideal for PC-based automation. Using ATMega2560 processor and LAN9252 adapter, it is compatible with Arduino Mega board, having the same pin definition. The switching regulator on board can source up to 2A current at 5V or 3.3V with mere heat dissipation. The open source software library and slave stack code generation tool that come with the board support the EtherCAT PDO and CoE function, which make developers concentrate on the application code, simplifying the work on hardware and protocol stack. 3 | 4 | -------------------------------------------------------------------------------- /EtherCATSlave/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 CXMICROWAVE 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /libraries/arducat/ethercat.cpp: -------------------------------------------------------------------------------- 1 | #include "ethercat.h" 2 | 3 | const UINT16 Ethercat::cBitMask[16] = {0x0000,0x0001,0x0003,0x0007,0x000F,0x001F,0x003F,0x007F,0x00FF,0x01FF,0x03FF,0x07FF,0x0FFF,0x1FFF,0x3FFF,0x7FFF}; 4 | const UINT32 MBXMEM Ethercat::cAbortCode[] = 5 | { 6 | ABORT_NOERROR, 7 | ABORT_TOGGLE_BIT_NOT_CHANGED, 8 | ABORT_SDO_PROTOCOL_TIMEOUT, 9 | ABORT_COMMAND_SPECIFIER_UNKNOWN, 10 | ABORT_OUT_OF_MEMORY, 11 | ABORT_UNSUPPORTED_ACCESS, 12 | ABORT_WRITE_ONLY_ENTRY, 13 | ABORT_READ_ONLY_ENTRY, 14 | ABORT_OBJECT_NOT_EXISTING, 15 | ABORT_OBJECT_CANT_BE_PDOMAPPED, 16 | ABORT_MAPPED_OBJECTS_EXCEED_PDO, 17 | ABORT_PARAM_IS_INCOMPATIBLE, 18 | ABORT_INTERNAL_DEVICE_INCOMPATIBILITY, 19 | ABORT_HARDWARE_ERROR, 20 | ABORT_PARAM_LENGTH_ERROR, 21 | ABORT_PARAM_LENGTH_TOO_LONG, 22 | ABORT_PARAM_LENGTH_TOO_SHORT, 23 | ABORT_SUBINDEX_NOT_EXISTING, 24 | ABORT_VALUE_EXCEEDED, 25 | ABORT_VALUE_TOO_GREAT, 26 | ABORT_VALUE_TOO_SMALL, 27 | ABORT_MAX_VALUE_IS_LESS_THAN_MIN_VALUE, 28 | ABORT_GENERAL_ERROR, 29 | ABORT_DATA_CANNOT_BE_READ_OR_STORED, 30 | ABORT_DATA_CANNOT_BE_READ_OR_STORED_BECAUSE_OF_LOCAL_CONTROL, 31 | ABORT_DATA_CANNOT_BE_READ_OR_STORED_IN_THIS_STATE, 32 | ABORT_NO_OBJECT_DICTIONARY_IS_PRESENT, 33 | ABORT_ENTRY_CANT_BE_WRITTEN_SI0_NOT_0 34 | }; 35 | CHAR OBJMEM Ethercat::aSubindexDesc[13] = "SubIndex 000"; 36 | 37 | Ethercat::Ethercat() 38 | { 39 | ObjDicList = NULL; 40 | aSubindexDesc[13]; 41 | 42 | } 43 | 44 | Ethercat::~Ethercat() 45 | { 46 | HW_Release(); 47 | } 48 | 49 | Ethercat ethercat; 50 | -------------------------------------------------------------------------------- /EtherCATSlave/EthercatType.py: -------------------------------------------------------------------------------- 1 | 2 | dictEthercatType = { 3 | 'BOOL': ('UINT16', 'DEFTYPE_BOOLEAN', 1), 4 | 'BYTE': ('UINT16', 'DEFTYPE_UNSIGNED8', 1), 5 | 'BIT1': ('UINT16', 'DEFTYPE_BIT1', 1), 6 | 'BIT2': ('UINT16', 'DEFTYPE_BIT2', 2), 7 | 'BIT3': ('UINT16', 'DEFTYPE_BIT3', 3), 8 | 'BIT4': ('UINT16', 'DEFTYPE_BIT4', 4), 9 | 'BIT5': ('UINT16', 'DEFTYPE_BIT5', 5), 10 | 'BIT6': ('UINT16', 'DEFTYPE_BIT6', 6), 11 | 'BIT7': ('UINT16', 'DEFTYPE_BIT7', 7), 12 | 'BIT8': ('UINT16', 'DEFTYPE_BIT8', 8), 13 | 'SINT': ('INT16', 'DEFTYPE_INTEGER8', 8), 14 | 'INT' : ('INT16', 'DEFTYPE_INTEGER16', 16), 15 | 'INT24':('INT32', 'DEFTYPE_INTEGER24', 24), 16 | 'DINT': ('INT32', 'DEFTYPE_INTEGER32', 32), 17 | 'INT40':('long long', 'DEFTYPE_INTEGER40', 40), 18 | 'INT48':('long long', 'DEFTYPE_INTEGER48', 48), 19 | 'INT56':('long long', 'DEFTYPE_INTEGER56', 56), 20 | 'LINT': ('long long', 'DEFTYPE_INTEGER64', 64), 21 | 'USINT': ('UINT16', 'DEFTYPE_UNSIGNED8', 8), 22 | 'UINT' : ('UINT16', 'DEFTYPE_UNSIGNED16', 16), 23 | 'UINT24':('UINT32', 'DEFTYPE_UNSIGNED24', 24), 24 | 'UDINT': ('UINT32', 'DEFTYPE_UNSIGNED32', 32), 25 | 'UINT40':('unsinged long long','DEFTYPE_UNSIGNED40', 40), 26 | 'UINT48':('unsinged long long','DEFTYPE_UNSIGNED48', 48), 27 | 'UINT56':('unsinged long long','DEFTYPE_UNSIGNED56', 56), 28 | 'ULINT': ('unsinged long long','DEFTYPE_UNSIGNED64', 64), 29 | 'REAL': ('float', 'DEFTYPE_REAL32', 32), 30 | 'LREAL':('double', 'DEFTYPE_REAL64', 64), 31 | } 32 | #The variable length types are not supported. Eg.STRING(n) 33 | 34 | dictAccessType = { 35 | 'RO' : 'ACCESS_READ', 36 | 'RW' : 'ACCESS_READ|ACCESS_WRITE', 37 | }; 38 | 39 | dictPdoDirType = { 40 | '' : 'OBJACCESS_NOPDOMAPPING', 41 | 'rx' : 'OBJACCESS_RXPDOMAPPING', 42 | 'tx' : 'OBJACCESS_TXPDOMAPPING', 43 | }; 44 | -------------------------------------------------------------------------------- /libraries/arducat/ecatcoe.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------- 2 | ------ 3 | ------ ecatcoe.h 4 | ------ ------ 5 | -----------------------------------------------------------------------------------------*/ 6 | 7 | #ifndef _ECATCOE_H_ 8 | 9 | #define _ECATCOE_H_ 10 | 11 | /*----------------------------------------------------------------------------------------- 12 | ------ 13 | ------ Includes 14 | ------ 15 | -----------------------------------------------------------------------------------------*/ 16 | 17 | #include "mailbox.h" 18 | 19 | /*----------------------------------------------------------------------------------------- 20 | ------ 21 | ------ Defines and Types 22 | ------ 23 | -----------------------------------------------------------------------------------------*/ 24 | 25 | /*///////////////////////////////////////////////////////////////////////////////////////// 26 | // 27 | // Error Codes 28 | */ 29 | 30 | #define ERROR_COEINVALIDSERVICE 0x01 31 | #define ERROR_COENOTSUPPORTED 0x02 32 | 33 | /*///////////////////////////////////////////////////////////////////////////////////////// 34 | // 35 | // COE services 36 | */ 37 | 38 | #define COESERVICE_EMERGENCY 0x01 39 | #define COESERVICE_SDOREQUEST 0x02 40 | #define COESERVICE_SDORESPONSE 0x03 41 | #define COESERVICE_TXPDO 0x04 42 | #define COESERVICE_RXPDO 0x05 43 | #define COESERVICE_TXPDOREMREQ 0x06 44 | #define COESERVICE_RXPDOREMREQ 0x07 45 | #define COESERVICE_SDOINFO 0x08 46 | 47 | /*///////////////////////////////////////////////////////////////////////////////////////// 48 | // 49 | // COE Structures 50 | */ 51 | 52 | typedef UINT16 TCOEHEADER; 53 | #define COEHEADER_COESERVICESHIFT 12 54 | #define COEHEADER_COESERVICEMASK 0xF000 55 | 56 | #define COE_HEADER_SIZE 2 57 | 58 | typedef struct MBX_STRUCT_PACKED_START 59 | { 60 | TMBXHEADER MbxHeader; 61 | TCOEHEADER CoeHeader; 62 | UINT16 Data[((MAX_MBX_DATA_SIZE)-(COE_HEADER_SIZE)) >> 1]; 63 | }MBX_STRUCT_PACKED_END 64 | TCOEMBX; 65 | 66 | 67 | 68 | #endif //_ECATCOE_H_ 69 | 70 | 71 | -------------------------------------------------------------------------------- /EtherCATSlave/SlaveGenerator.py: -------------------------------------------------------------------------------- 1 | 2 | strFileInput = "SSC-Device.xls" 3 | strFileCodeOutput = "ethercat.ino" 4 | strFileXmlOutput = "SlaveDevice.xml" 5 | 6 | def parseParameters(params): 7 | global strFileInput,strFileCodeOutput,strFileXmlOutput 8 | for i in range(0, len(params)): 9 | if params[i] == "-h" or params[i] == "/h": 10 | print """Usage: SlaveGeneraor 11 | \t[-i Input FileName = "SSC-Device.xls"] 12 | \t[-c Code FileName = "ethercat.ino"] 13 | \t[-x Xml FileName= "SlaveDevice.xml"] 14 | If the command has no parameters, it check SlaveGenerator.cfg for parameters.""" 15 | quit() 16 | elif params[i] == "-i": 17 | strFileInput = params[i+1] 18 | elif params[i] == "-c": 19 | strFileCodeOutput = params[i+1] 20 | elif params[i] == "-x": 21 | strFileXmlOutput = params[i+1] 22 | 23 | 24 | #Read Input parameters 25 | import os 26 | if os.path.exists('SlaveGeneraor.cfg'): 27 | fConfig = open('SlaveGeneraor.cfg') 28 | params = fConfig.read() 29 | params = params.split() 30 | parseParameters(params) 31 | 32 | import sys 33 | parseParameters(sys.argv) 34 | 35 | #Generate code 36 | from SlaveReader import SlaveReader 37 | from SlaveReader import SlaveInfoReader 38 | from ODGenerator import ODGenerator 39 | from PDOMapping import GenerateRxPdoMappingProg 40 | from PDOMapping import GenerateTxPdoMappingProg 41 | from XmlGenerator import XmlGenerator 42 | 43 | print "Parsing ", strFileInput, "..." 44 | listSlaveDict = SlaveReader(strFileInput) 45 | dictSlaveInfo = SlaveInfoReader(strFileInput) 46 | strApplicationObjDic = ODGenerator(listSlaveDict) 47 | strOutputMapping = GenerateRxPdoMappingProg(listSlaveDict) 48 | strInputMapping = GenerateTxPdoMappingProg(listSlaveDict) 49 | 50 | f = open("ethercat_template.txt") 51 | strFile = f.read() 52 | f.close() 53 | dictMapping = {\ 54 | "ApplicationObjDic" :strApplicationObjDic, 55 | "OutputMapping" :strOutputMapping, 56 | "InputMapping" :strInputMapping, 57 | } 58 | dictMapping.update(dictSlaveInfo) 59 | strFile = strFile%dictMapping 60 | 61 | f = open(strFileCodeOutput,'w') 62 | f.write(strFile) 63 | f.close() 64 | print 'Arduino code file "',strFileCodeOutput,'" generated.' 65 | 66 | XmlGenerator(strFileXmlOutput,dictSlaveInfo,listSlaveDict) 67 | print 'ESI file "',strFileXmlOutput,'" generated.' 68 | 69 | print "Press ENTER to continue..." 70 | raw_input() 71 | -------------------------------------------------------------------------------- /libraries/arducat/ecatappl.h: -------------------------------------------------------------------------------- 1 | /** 2 | \ingroup ecatappl 3 | \file ecatappl.h 4 | \brief Definition. 5 | */ 6 | /*----------------------------------------------------------------------------------------- 7 | ------ 8 | ------ Description 9 | ------ 10 | ------ ecatappl.h 11 | ------ 12 | ------ EtherCAT Slave Application 13 | ------ ------ 14 | -----------------------------------------------------------------------------------------*/ 15 | 16 | #ifndef _ECATAPPL_H_ 17 | #define _ECATAPPL_H_ 18 | 19 | /*----------------------------------------------------------------------------------------- 20 | ------ 21 | ------ Includes 22 | ------ 23 | -----------------------------------------------------------------------------------------*/ 24 | 25 | #include "ecat_def.h" 26 | 27 | /*----------------------------------------------------------------------------------------- 28 | ------ 29 | ------ Defines and Types 30 | ------ 31 | -----------------------------------------------------------------------------------------*/ 32 | /*Set to unsigned short to handle bit entries correct*/ 33 | #define BOOLEAN(x) unsigned short(x):1 34 | #define BIT1(x) unsigned short(x):1 35 | #define BIT2(x) unsigned short(x):2 36 | #define BIT3(x) unsigned short(x):3 37 | #define BIT4(x) unsigned short(x):4 38 | #define BIT5(x) unsigned short(x):5 39 | #define BIT6(x) unsigned short(x):6 40 | #define BIT7(x) unsigned short(x):7 41 | #define BIT8(x) unsigned short(x):8 42 | #define ALIGN0(x) 43 | #define ALIGN1(x) unsigned short(x):1; 44 | #define ALIGN2(x) unsigned short(x):2; 45 | #define ALIGN3(x) unsigned short(x):3; 46 | #define ALIGN4(x) unsigned short(x):4; 47 | #define ALIGN5(x) unsigned short(x):5; 48 | #define ALIGN6(x) unsigned short(x):6; 49 | #define ALIGN7(x) unsigned short(x):7; 50 | #define ALIGN8(x) unsigned short(x):8; 51 | #define ALIGN9(x) unsigned short x1:1; unsigned short(x):8; 52 | #define ALIGN10(x) unsigned short x1:2; unsigned short(x):8; 53 | #define ALIGN11(x) unsigned short x1:3; unsigned short(x):8; 54 | #define ALIGN12(x) unsigned short x1:4; unsigned short(x):8; 55 | #define ALIGN13(x) unsigned short x1:5; unsigned short(x):8; 56 | #define ALIGN14(x) unsigned short x1:6; unsigned short(x):8; 57 | #define ALIGN15(x) unsigned short x1:7; unsigned short(x):8; 58 | 59 | 60 | /*----------------------------------------------------------------------------------------- 61 | ------ 62 | ------ type definitions 63 | ------ 64 | -----------------------------------------------------------------------------------------*/ 65 | 66 | #endif //_ECATAPPL_H_ 67 | 68 | -------------------------------------------------------------------------------- /libraries/arducat/mailbox.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------- 2 | ------ 3 | ------ Description 4 | ------ 5 | ------ mailbox.h 6 | ------ 7 | ------ EtherCAT Mailbox 8 | ------ ------ 9 | -----------------------------------------------------------------------------------------*/ 10 | 11 | #ifndef _MAILBOX_H_ 12 | #define _MAILBOX_H_ 13 | 14 | /*----------------------------------------------------------------------------------------- 15 | ------ 16 | ------ Includes 17 | ------ 18 | -----------------------------------------------------------------------------------------*/ 19 | 20 | #include "esc.h" 21 | #include "ecatslv.h" 22 | 23 | /*----------------------------------------------------------------------------------------- 24 | ------ 25 | ------ Defines and Types 26 | ------ 27 | -----------------------------------------------------------------------------------------*/ 28 | 29 | /////////////////////////////////////////////////////////////// 30 | // 31 | // General 32 | // 33 | #define MBX_TYPE_AOE 1 34 | #define MBX_TYPE_EOE 2 35 | #define MBX_TYPE_COE 3 36 | #define MBX_TYPE_FOE 4 37 | #define MBX_TYPE_SOE 5 38 | #define MBX_TYPE_VOE 15 39 | 40 | #define EMCY_SERVICE ((UINT8) 0x0001) 41 | #define COE_SERVICE ((UINT8) 0x0002) 42 | #define SOE_SERVICE ((UINT8) 0x0004) 43 | #define EOE_SERVICE ((UINT8) 0x0008) 44 | #define AOE_SERVICE ((UINT8) 0x0010) 45 | #define VOE_SERVICE ((UINT8) 0x0020) 46 | #define FOE_SERVICE ((UINT8) 0x0040) 47 | #define FRAGMENTS_FOLLOW ((UINT8) 0x0080) 48 | 49 | #ifndef DISABLE_MBX_INT 50 | #define DISABLE_MBX_INT 51 | #endif 52 | #ifndef ENABLE_MBX_INT 53 | #define ENABLE_MBX_INT 54 | #endif 55 | 56 | #ifndef ENTER_MBX_CRITICAL 57 | #define ENTER_MBX_CRITICAL 58 | #endif 59 | 60 | #ifndef LEAVE_MBX_CRITICAL 61 | #define LEAVE_MBX_CRITICAL 62 | #endif 63 | 64 | #ifndef MAX_MBX_QUEUE_SIZE 65 | #define MAX_MBX_QUEUE_SIZE 10 66 | #endif 67 | 68 | /////////////////////////////////////////////////////////////// 69 | // 70 | // Command Codes for the mailbox type 0 71 | // 72 | 73 | #define MBXSERVICE_MBXERRORCMD 0x01 74 | 75 | /////////////////////////////////////////////////////////////// 76 | // 77 | // Error Codes for a mailbox error response 78 | // 79 | 80 | #define MBXERR_SYNTAX 0x01 81 | #define MBXERR_UNSUPPORTEDPROTOCOL 0x02 82 | #define MBXERR_INVALIDCHANNEL 0x03 83 | #define MBXERR_SERVICENOTSUPPORTED 0x04 84 | #define MBXERR_INVALIDHEADER 0x05 85 | #define MBXERR_SIZETOOSHORT 0x06 86 | #define MBXERR_NOMOREMEMORY 0x07 87 | #define MBXERR_INVALIDSIZE 0x08 88 | #define MBXERR_SERVICEINWORK 0x09 89 | 90 | /*--------------------------------------------------------------------------------- 91 | ------ 92 | ------ Data Types 93 | ------ 94 | ---------------------------------------------------------------------------------*/ 95 | typedef struct MBX_STRUCT_PACKED_START 96 | { 97 | UINT16 Length; 98 | UINT16 Address; 99 | 100 | UINT8 Flags[2]; 101 | #define MBX_OFFS_TYPE 1 102 | #define MBX_OFFS_COUNTER 1 103 | #define MBX_MASK_TYPE 0x0F 104 | #define MBX_MASK_COUNTER 0xF0 105 | #define MBX_SHIFT_TYPE 0 106 | #define MBX_SHIFT_COUNTER 4 107 | }MBX_STRUCT_PACKED_END 108 | TMBXHEADER; 109 | 110 | #define MBX_HEADER_SIZE SIZEOF(TMBXHEADER) 111 | 112 | #define MAX_MBX_DATA_SIZE (MAX_MBX_SIZE - MBX_HEADER_SIZE) 113 | 114 | typedef struct MBX_STRUCT_PACKED_START 115 | { 116 | TMBXHEADER MbxHeader; 117 | UINT16 Data[(MAX_MBX_DATA_SIZE >> 1)]; 118 | }MBX_STRUCT_PACKED_END 119 | TMBX; 120 | 121 | /* ECATCHANGE_START(V5.01) MBX3*/ 122 | #define MBX_BUFFER_SIZE (MBX_HEADER_SIZE + MAX_MBX_DATA_SIZE ) 123 | /* ECATCHANGE_END(V5.01) MBX3*/ 124 | 125 | typedef struct 126 | { 127 | UINT16 firstInQueue; 128 | UINT16 lastInQueue; 129 | UINT16 maxQueueSize; 130 | TMBX MBXMEM * queue[(MAX_MBX_QUEUE_SIZE)+1]; 131 | } TMBXQUEUE; 132 | 133 | 134 | #endif //_MAILBOX_H_ 135 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /EtherCATSlave/SlaveReader.py: -------------------------------------------------------------------------------- 1 | def SlaveReader(filename): 2 | #Read the xlsx file 3 | import xlrd 4 | from EthercatType import dictEthercatType 5 | book = xlrd.open_workbook(filename) 6 | sh = book.sheet_by_index(0) 7 | print filename, " has", sh.nrows,"rows and", sh.ncols, "cols" 8 | 9 | 10 | #Initialize the dictionary list to return 11 | listSlaveInfo = []; 12 | 13 | #Parsing 14 | for row in range(17,sh.nrows): 15 | #Extract info 16 | strIndex = sh.cell_value(rowx=row, colx=1) 17 | strObjectCode = sh.cell_value(rowx=row, colx=2) 18 | intSI = sh.cell_value(rowx=row, colx=3) 19 | strDataType = sh.cell_value(rowx=row, colx=4) 20 | strName = sh.cell_value(rowx=row, colx=5) 21 | strDefault = sh.cell_value(rowx=row, colx=6) 22 | strAccess = sh.cell_value(rowx=row, colx=11) 23 | strPdoDir = sh.cell_value(rowx=row, colx=12) 24 | strDescription = sh.cell_value(rowx=row, colx=14) 25 | #Skip the empty items 26 | if (len(strIndex)<4 or strIndex[0:2]!="0x") and intSI=="": 27 | continue; 28 | #Generate empty dictionary 29 | dictSlaveInfo = {}; 30 | #Check Index 31 | if len(strIndex)<4: 32 | if len(listSlaveInfo)==0: 33 | print "Error: Row", row, ". Index should not be empty." 34 | return []; 35 | else: 36 | strIndex = listSlaveInfo[-1]['Index']; 37 | #Check Object Code 38 | if intSI!="" and listSlaveInfo!=[]: 39 | strObjectCode = listSlaveInfo[-1]['ObjectCode'];#Inherit the main index 40 | if len(strIndex)>=4 and (strObjectCode!="VARIABLE") and (strObjectCode!="ARRAY") and (strObjectCode!="RECORD"): 41 | print "Error: Row", row, ". Wrong object code." 42 | return []; 43 | #Check SubIndex 44 | if strObjectCode == "VARIABLE": 45 | intSI=="" 46 | elif strObjectCode == "RECORD": 47 | if len(listSlaveInfo)==0: 48 | intSI = 0; 49 | elif listSlaveInfo[-1]['Index'] != strIndex: 50 | intSI = 0; 51 | elif listSlaveInfo[-1]['Entry'][-1]['SI']+1 != intSI: 52 | print "Error: Row", row, ". Wrong sub-index." 53 | return []; 54 | intSI = int(intSI) 55 | elif strObjectCode == "ARRAY": 56 | if len(listSlaveInfo)==0: 57 | intSI = 0; 58 | elif listSlaveInfo[-1]['Index'] != strIndex: 59 | intSI = 0; 60 | print "Note: Row", row, "The length of array is determined by the default value of subindex 0" 61 | #Check Type 62 | if strDataType not in dictEthercatType: 63 | print "Error: Row", row, "Unrecognized data type ",strDataType 64 | return []; 65 | #Check Default 66 | if strDefault == "": 67 | strDefault = "0" 68 | if strObjectCode == "ARRAY" and strDefault<=0 and intSI==0: 69 | print "Error: Row", row, ". Wrong array length." 70 | #Check Access 71 | if strAccess != "RO" and strAccess != "RW": 72 | print "Error: Row", row, ". Access code should be either RO or RW." 73 | return []; 74 | #Check PDO direction 75 | if strPdoDir != "" and strPdoDir != "rx" and strPdoDir != "tx": 76 | print "Error: Row", row, ". PDO direction should be empty, rx or tx." 77 | return []; 78 | 79 | dictEntry = { 80 | 'SI' : intSI, 81 | 'DataType' : strDataType, 82 | 'Name' : strName, 83 | 'Default' : strDefault, 84 | 'Access' : strAccess, 85 | 'PdoDirection': strPdoDir, 86 | 'Description': strDescription 87 | }; 88 | 89 | if strObjectCode== 'VARIABLE' or intSI == 0: 90 | dictSlaveInfo['Index'] = strIndex 91 | dictSlaveInfo['ObjectCode'] = strObjectCode 92 | dictSlaveInfo['Entry'] = [dictEntry] 93 | listSlaveInfo.append(dictSlaveInfo) 94 | else: 95 | listSlaveInfo[-1]['Entry'].append(dictEntry) 96 | if strObjectCode== 'RECORD': 97 | listSlaveInfo[-1]['Entry'][0]['Default'] = str(intSI) 98 | return listSlaveInfo 99 | 100 | def SlaveInfoReader(filename): 101 | #Read the xlsx file 102 | import xlrd 103 | from EthercatType import dictEthercatType 104 | book = xlrd.open_workbook(filename) 105 | sh = book.sheet_by_index(0) 106 | 107 | dictInfo = {} 108 | dictInfo['Device Profile'] = sh.cell_value(rowx=0, colx=2) 109 | dictInfo['Module Profile'] = sh.cell_value(rowx=1, colx=2) 110 | dictInfo['Vendor ID'] = sh.cell_value(rowx=2, colx=2) 111 | dictInfo['Vendor Name'] = sh.cell_value(rowx=3, colx=2) 112 | dictInfo['Product Code'] = sh.cell_value(rowx=4, colx=2) 113 | dictInfo['Revision Number'] = sh.cell_value(rowx=5, colx=2) 114 | dictInfo['Serial Number'] = sh.cell_value(rowx=6, colx=2) 115 | dictInfo['Device Name'] = sh.cell_value(rowx=7, colx=2) 116 | dictInfo['Device Name Len'] = len(sh.cell_value(rowx=7, colx=2)) 117 | dictInfo['HW Version'] = sh.cell_value(rowx=8, colx=2) 118 | dictInfo['HW Version Len'] = len(sh.cell_value(rowx=8, colx=2)) 119 | dictInfo['SW Version'] = sh.cell_value(rowx=9, colx=2) 120 | dictInfo['SW Version Len'] = len(sh.cell_value(rowx=9, colx=2)) 121 | dictInfo['Group Type'] = sh.cell_value(rowx=10, colx=2) 122 | dictInfo['Group Name'] = sh.cell_value(rowx=11, colx=2) 123 | 124 | return dictInfo 125 | #Test bench 126 | #listSlaveInfo = SlaveReader("SSC-Device.xlsx") 127 | #for item in listSlaveInfo: 128 | # print item 129 | -------------------------------------------------------------------------------- /libraries/arducat/ecatcoe.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | \defgroup ecatcoe ecatcoe.c: CoE (CAN application layer over EtherCAT) functions 3 | \brief This file contains the CoE mailbox interface\n 4 | \brief Changes to version V4.40: 5 | \brief V5.0 SDO7: "SDOS_SdoInfoInd()" never return pending SDO Info service. Delete "NOERROR_INWORK" handling. 6 | \brief Changes to version V4.08:\n 7 | \brief V4.40 SDO1: add initial value for "nSdoInfoFragmentsLeft" 8 | \brief V4.40 MBX6: change return value if no mailbox buffer is available 9 | \brief V4.08 MBX 1: If the switch MAILBOX_QUEUE was set, we have to put all SDO Info Responses in the Send Queue\n 10 | 11 | \version 5.0 12 | */ 13 | 14 | //--------------------------------------------------------------------------------------- 15 | /** 16 | \ingroup ecatcoe 17 | \file ecatcoe.c 18 | \brief Implementation. 19 | */ 20 | //--------------------------------------------------------------------------------------- 21 | 22 | /*--------------------------------------------------------------------------------------- 23 | ------ 24 | ------ Includes 25 | ------ 26 | ---------------------------------------------------------------------------------------*/ 27 | 28 | #include "ecat_def.h" 29 | 30 | 31 | #include "ethercat.h" 32 | /*--------------------------------------------------------------------------------------- 33 | ------ 34 | ------ internal Types and Defines 35 | ------ 36 | ---------------------------------------------------------------------------------------*/ 37 | 38 | #define ECATCOE 0x4300 39 | #define ECATCOEMAX 0x02 40 | 41 | /*--------------------------------------------------------------------------------------- 42 | ------ 43 | ------ static variables 44 | ------ 45 | ---------------------------------------------------------------------------------------*/ 46 | 47 | /*--------------------------------------------------------------------------------------- 48 | ------ 49 | ------ static functions 50 | ------ 51 | ---------------------------------------------------------------------------------------*/ 52 | 53 | /*--------------------------------------------------------------------------------------- 54 | ------ 55 | ------ functions 56 | ------ 57 | ---------------------------------------------------------------------------------------*/ 58 | 59 | /** 60 | \addtogroup ecatcoe 61 | @{ 62 | */ 63 | 64 | ///////////////////////////////////////////////////////////////////////////////////////// 65 | /** 66 | 67 | \brief This function intialize the CoE Interface. 68 | *//////////////////////////////////////////////////////////////////////////////////////// 69 | 70 | void Ethercat::COE_Init(void) 71 | { 72 | pCoeSendStored = 0; 73 | nSdoInfoFragmentsLeft = 0; 74 | } 75 | 76 | ///////////////////////////////////////////////////////////////////////////////////////// 77 | /** 78 | \param pCoeMbx Pointer to the received mailbox data from the master. 79 | 80 | \return result of the operation (0 (success) or mailbox error code (MBXERR_.... defined in 81 | mailbox.h)) 82 | 83 | \brief This function is called when a CoE (CAN application layer over EtherCAT) service is received from 84 | the master. 85 | *//////////////////////////////////////////////////////////////////////////////////////// 86 | 87 | UINT8 Ethercat::COE_ServiceInd(TCOEMBX MBXMEM *pCoeMbx) 88 | { 89 | UINT8 result = 0; 90 | 91 | switch ((pCoeMbx->CoeHeader & COEHEADER_COESERVICEMASK) >> COEHEADER_COESERVICESHIFT) 92 | { 93 | case COESERVICE_SDOREQUEST: 94 | /* SDO-Request received, call SDOS_SdoInd to process the SDO-Request 95 | if an existing SDO-Stack shall be used, the corresponding function 96 | should be called */ 97 | result = SDOS_SdoInd( (TINITSDOMBX MBXMEM *) pCoeMbx ); 98 | break; 99 | 100 | case COESERVICE_SDOINFO: 101 | /* SDO-Information Request received, call SDOS_SdoInfoInd to process the SDO-Request */ 102 | result = SDOS_SdoInfoInd( (TSDOINFORMATION MBXMEM *) pCoeMbx ); 103 | /*NOERROR_INWORK is never returned by SDOS_SdoInfoInd() => delete return code handling*/ 104 | break; 105 | 106 | case COESERVICE_EMERGENCY: 107 | case COESERVICE_SDORESPONSE: 108 | case COESERVICE_TXPDO: 109 | case COESERVICE_RXPDO: 110 | case COESERVICE_TXPDOREMREQ: 111 | case COESERVICE_RXPDOREMREQ: 112 | /* these CoE services are not supported yet */ 113 | result = MBXERR_SERVICENOTSUPPORTED; 114 | break; 115 | 116 | default: 117 | result = MBXERR_INVALIDHEADER; 118 | break; 119 | } 120 | return result; 121 | } 122 | 123 | ///////////////////////////////////////////////////////////////////////////////////////// 124 | /** 125 | \param pMbx Pointer to the free mailbox to sent. 126 | 127 | \brief This function is called when a CoE service to be sent is stored and can 128 | \brief be put in the send mailbox. 129 | *//////////////////////////////////////////////////////////////////////////////////////// 130 | 131 | void Ethercat::COE_ContinueInd(TMBX MBXMEM * pMbx) 132 | { 133 | if (pCoeSendStored) 134 | { 135 | /* send the stored CoE service which could not be sent before */ 136 | MBX_MailboxSendReq(pCoeSendStored, 0); 137 | pCoeSendStored = 0; 138 | } 139 | else 140 | { 141 | /* send the next fragment of the last CoE service (only for SDO-Information possible) */ 142 | /* in mailbox queue mode pMbx is always 0, so a mailbox buffer shall be get */ 143 | pMbx = (TMBX MBXMEM *) APPL_AllocMailboxBuffer(SIZEOF(TMBX)); 144 | /* it shall be checked if a valid pointer was returned */ 145 | if ( pMbx != NULL ) 146 | { 147 | /* copy the stored SDO-Info-Header in the request */ 148 | MBXMEMCPY(pMbx, aSdoInfoHeader, SDO_INFO_HEADER_BYTE_SIZE); 149 | /* call SDOS_SdoInfoInd to generate and send the next fragment */ 150 | SDOS_SdoInfoInd( (TSDOINFORMATION MBXMEM *) pMbx ); 151 | } 152 | } 153 | } 154 | 155 | /** @} */ 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /EtherCATSlave/PDOMapping.py: -------------------------------------------------------------------------------- 1 | 2 | def GeneratePdoMapping(listSlaveInfo,rxtx,strEntryPrefix,strPdoObjPrefix): 3 | from EthercatType import dictEthercatType, dictAccessType, dictPdoDirType 4 | #Get items start with 0x6xxx and with 'tx' attribute 5 | #Generate list {RxPdoObj: [(Index,SI,length,intBitOffset)]} with intBitOffset = 0 temporaly 6 | dictPdoMapping = {} 7 | for dictItem in listSlaveInfo: 8 | if len(dictItem['Index'])!=6 or dictItem['Index'][0:3]!=strEntryPrefix: 9 | continue 10 | strPdoMappingObj = strPdoObjPrefix+dictItem['Index'][3:5] 11 | if dictItem['ObjectCode'] == "VARIABLE": 12 | if dictItem['Entry'][0]['PdoDirection'] != rxtx: 13 | continue 14 | dictMappingEntry = {'Index':dictItem['Index'],'SI':0,'Length':dictEthercatType[dictItem['Entry'][0]['DataType']][2],'BitOffset':0} 15 | dictMappingEntry['Name'] = dictItem['Entry'][0]['Name'] 16 | if dictMappingEntry['Name']=="": 17 | dictMappingEntry['Name'] = "Obj"+dictItem['Index'][2:] 18 | dictMappingEntry['DataType'] = dictItem['Entry'][0]['DataType'] 19 | if strPdoMappingObj in dictPdoMapping: 20 | dictPdoMapping[strPdoMappingObj].append(dictMappingEntry) 21 | else: 22 | dictPdoMapping[strPdoMappingObj] = [dictMappingEntry] 23 | if dictItem['ObjectCode'] == "RECORD": 24 | for dictEntry in dictItem['Entry']: 25 | if dictEntry['PdoDirection'] != rxtx: 26 | continue 27 | dictMappingEntry = {'Index':dictItem['Index'],'SI':dictEntry['SI'],'Length':dictEthercatType[dictEntry['DataType']][2],'BitOffset':0} 28 | dictMappingEntry['Name'] = dictEntry['Name'] 29 | if dictMappingEntry['Name']=="": 30 | dictMappingEntry['Name'] = "SubIndex "+"%03d"%dictEntry['SI'] 31 | dictMappingEntry['DataType'] = dictEntry['DataType'] 32 | if strPdoMappingObj in dictPdoMapping: 33 | dictPdoMapping[strPdoMappingObj].append(dictMappingEntry) 34 | else: 35 | dictPdoMapping[strPdoMappingObj] = [dictMappingEntry] 36 | if dictItem['ObjectCode'] == "ARRAY": 37 | if dictItem['Entry'][1]['PdoDirection'] != rxtx: 38 | continue 39 | for intSI in range(1,int(dictItem['Entry'][0]['Default'])+1): 40 | dictMappingEntry = {'Index':dictItem['Index'],'SI':intSI,'Length':dictEthercatType[dictItem['Entry'][1]['DataType']][2],'BitOffset':0} 41 | dictMappingEntry['Name'] = "SubIndex "+"%03d"%intSI 42 | dictMappingEntry['DataType'] = dictItem['Entry'][1]['DataType'] 43 | if strPdoMappingObj in dictPdoMapping: 44 | dictPdoMapping[strPdoMappingObj].append(dictMappingEntry) 45 | else: 46 | dictPdoMapping[strPdoMappingObj] = [dictMappingEntry] 47 | #Sort 48 | dictPdoMapping = sorted(dictPdoMapping.iteritems(), key=lambda asd:asd[0], reverse = False) 49 | #Generate BitOffset and Dummy items 50 | dictPdoMappingPost = {} 51 | for itemPdoMapping in dictPdoMapping: 52 | strPdoMappingObj = itemPdoMapping[0] 53 | listMappingEntries = itemPdoMapping[1] 54 | dictPdoMappingPost[strPdoMappingObj] = [] 55 | bitOffset = int(0) 56 | listMappingEntriesPost = [] 57 | for dictMappingEntry in listMappingEntries: 58 | #Align PDO mapping to word 59 | if (bitOffset%16!=0) and (bitOffset%16+dictMappingEntry['Length']>16 or \ 60 | dictMappingEntry['Length']>=16) : 61 | dictDummyEntry = {'Index':"0x0000",'SI':0,'Length':16-(bitOffset%16),'BitOffset':bitOffset} 62 | dictPdoMappingPost[strPdoMappingObj].append(dictDummyEntry) 63 | bitOffset = (bitOffset/16+1)*16 64 | #Add to bit Offset 65 | dictMappingEntry['BitOffset'] = bitOffset 66 | dictPdoMappingPost[strPdoMappingObj].append(dictMappingEntry) 67 | bitOffset = bitOffset+dictMappingEntry['Length'] 68 | if bitOffset%16!=0: 69 | dictDummyEntry = {'Index':"0x0000",'SI':0,'Length':16-(bitOffset%16),'BitOffset':bitOffset} 70 | dictPdoMappingPost[strPdoMappingObj].append(dictDummyEntry) 71 | bitOffset = (bitOffset/16+1)*16 72 | dictPdoMapping = dictPdoMappingPost 73 | return dictPdoMapping 74 | 75 | 76 | def GenerateTxPdoMapping(listSlaveInfo): 77 | return GeneratePdoMapping(listSlaveInfo,"tx","0x6","0x1A") 78 | 79 | def GenerateRxPdoMapping(listSlaveInfo): 80 | return GeneratePdoMapping(listSlaveInfo,"rx","0x7","0x16") 81 | 82 | def GeneratePdoMappingProg(listSlaveInfo,rxtx): 83 | if rxtx=="rx": 84 | dictPdoMapping = GenerateRxPdoMapping(listSlaveInfo) 85 | cmdWordsCopy = '\t\t\t\t*((UINT16*)&(%(strVarName)s)+%(intWord)d)' 86 | cmdWordsCopy = cmdWordsCopy+' = *(pData+%(WordOffset)d);\n' 87 | cmdMskShift = '\t\t\t\tu16Temp = *(pData+%(WordOffset)d);\n' 88 | cmdMskShift = cmdMskShift+'\t\t\t\t%(strVarName)s = (u16Temp&0x%(intMask)x)>>(%(intOffset)d);\n' 89 | elif rxtx=="tx": 90 | dictPdoMapping = GenerateTxPdoMapping(listSlaveInfo) 91 | cmdWordsCopy = '\t\t\t\t*(pData+%(WordOffset)d)' 92 | cmdWordsCopy = cmdWordsCopy+' = *((UINT16*)&(%(strVarName)s)+%(intWord)d);\n' 93 | cmdMskShift = '\t\t\t\t*(pData+%(WordOffset)d) &= ~0x%(intMask)x;\n' 94 | cmdMskShift = cmdMskShift+'\t\t\t\tu16Temp = %(strVarName)s;\n' 95 | cmdMskShift = cmdMskShift+'\t\t\t\t*(pData+%(WordOffset)d) |= (u16Temp' 96 | cmdMskShift = cmdMskShift+'<<(%(intOffset)d))&0x%(intMask)x;\n' 97 | strResult = '' 98 | for itemPdoMapping in dictPdoMapping.iteritems(): 99 | bitEnd = int(0) 100 | strResult = strResult+"\t\t\tcase "+itemPdoMapping[0]+':\n' 101 | for dictEntry in itemPdoMapping[1]: 102 | #Skip dummy objects 103 | if dictEntry['Index'] == "0x0000": 104 | continue; 105 | #Get destination variable 106 | if dictEntry['SI'] == 0: 107 | strVarName = "Obj"+dictEntry['Index'] 108 | else: 109 | strVarName = "Obj"+dictEntry['Index']+".SubIndex"\ 110 | +str(dictEntry['SI']) 111 | #Copy word variables 112 | if dictEntry['Length'] >= 16: 113 | for intWord in range(0,dictEntry['Length']/16): 114 | strResult = strResult+cmdWordsCopy%{'strVarName':strVarName,\ 115 | 'intWord':intWord,\ 116 | 'WordOffset':dictEntry['BitOffset']/16+intWord} 117 | #Copy bits variables 118 | else: 119 | intMask = int(2**(dictEntry['Length'])-1)<<(dictEntry['BitOffset']%16) 120 | strResult = strResult+cmdMskShift%{'strVarName':strVarName,\ 121 | 'intMask':intMask,\ 122 | 'intOffset':dictEntry['BitOffset']%16,\ 123 | 'WordOffset':dictEntry['BitOffset']/16} 124 | bitEnd = dictEntry['BitOffset']+dictEntry['Length'] 125 | strResult = strResult+'\t\t\t\tpData += '+str((bitEnd+15)/16)+';\n' 126 | strResult = strResult+'\t\t\t\tbreak;\n' 127 | return strResult 128 | 129 | def GenerateRxPdoMappingProg(listSlaveInfo): 130 | return GeneratePdoMappingProg(listSlaveInfo,"rx") 131 | 132 | def GenerateTxPdoMappingProg(listSlaveInfo): 133 | return GeneratePdoMappingProg(listSlaveInfo,"tx") 134 | -------------------------------------------------------------------------------- /libraries/arducat/objdef.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------- 2 | ------ 3 | ------ Description 4 | ------ 5 | ------ objdef.h 6 | ------ 7 | ------ CANopen over EtherCAT object dictionary 8 | ------ ------ 9 | -----------------------------------------------------------------------------------------*/ 10 | 11 | #ifndef _OBJDEF_H_ 12 | #define _OBJDEF_H_ 13 | 14 | /*----------------------------------------------------------------------------------------- 15 | ------ 16 | ------ Includes 17 | ------ 18 | -----------------------------------------------------------------------------------------*/ 19 | 20 | #include "sdoserv.h" 21 | 22 | /*----------------------------------------------------------------------------------------- 23 | ------ 24 | ------ Defines and Types 25 | ------ 26 | -----------------------------------------------------------------------------------------*/ 27 | 28 | /*///////////////////////////////////////////////////////////////////////////////////////// 29 | // 30 | // Standard Types 31 | */ 32 | 33 | #define DEFTYPE_NULL 0x0000 34 | #define DEFTYPE_BOOLEAN 0x0001 35 | #define DEFTYPE_INTEGER8 0x0002 36 | #define DEFTYPE_INTEGER16 0x0003 37 | #define DEFTYPE_INTEGER32 0x0004 38 | #define DEFTYPE_UNSIGNED8 0x0005 39 | #define DEFTYPE_UNSIGNED16 0x0006 40 | #define DEFTYPE_UNSIGNED32 0x0007 41 | #define DEFTYPE_REAL32 0x0008 42 | #define DEFTYPE_VISIBLESTRING 0x0009 43 | #define DEFTYPE_OCTETSTRING 0x000A 44 | #define DEFTYPE_UNICODE_STRING 0x000B 45 | #define DEFTYPE_TIME_OF_DAY 0x000C 46 | #define DEFTYPE_TIME_DIFFERENCE 0x000D 47 | #define DEFTYPE_DOMAIN 0x000F 48 | #define DEFTYPE_INTEGER24 0x0010 49 | #define DEFTYPE_REAL64 0x0011 50 | #define DEFTYPE_INTEGER40 0x0012 51 | #define DEFTYPE_INTEGER48 0x0013 52 | #define DEFTYPE_INTEGER56 0x0014 53 | #define DEFTYPE_INTEGER64 0x0015 54 | #define DEFTYPE_UNSIGNED24 0x0016 55 | #define DEFTYPE_UNSIGNED40 0x0018 56 | #define DEFTYPE_UNSIGNED48 0x0019 57 | #define DEFTYPE_UNSIGNED56 0x001A 58 | #define DEFTYPE_UNSIGNED64 0x001B 59 | #define DEFTYPE_SAFETY 0x001C 60 | #define DEFTYPE_PDOMAPPING 0x0021 61 | #define DEFTYPE_IDENTITY 0x0023 62 | #define DEFTYPE_COMMAND 0x0025 63 | #define DEFTYPE_PDOCOMPAR 0x0027 64 | #define DEFTYPE_ENUM 0x0028 65 | #define DEFTYPE_SMPAR 0x0029 66 | #define DEFTYPE_RECORD 0x002A 67 | #define DEFTYPE_BACKUP 0x002B 68 | #define DEFTYPE_MDP 0x002C 69 | #define DEFTYPE_FSOEFRAME 0x002E 70 | #define DEFTYPE_FSOECOMMPAR 0x002F 71 | #define DEFTYPE_BIT1 0x0030 72 | #define DEFTYPE_BIT2 0x0031 73 | #define DEFTYPE_BIT3 0x0032 74 | #define DEFTYPE_BIT4 0x0033 75 | #define DEFTYPE_BIT5 0x0034 76 | #define DEFTYPE_BIT6 0x0035 77 | #define DEFTYPE_BIT7 0x0036 78 | #define DEFTYPE_BIT8 0x0037 79 | #define DEFTYPE_ERRORHANDLING 0x0038 80 | #define DEFTYPE_DIAGHISTORY 0x0039 81 | #define DEFTYPE_SYNCSTATUS 0x003A 82 | #define DEFTYPE_SYNCSETTINGS 0x003B 83 | #define DEFTYPE_CYCLICTIMES 0x003C 84 | 85 | #define BYTELEN_UNKNOWN 0x0000 86 | #define BYTELEN_BOOLEAN 0x0001 87 | #define BYTELEN_INTEGER8 0x0001 88 | #define BYTELEN_INTEGER16 0x0002 89 | #define BYTELEN_INTEGER32 0x0004 90 | #define BYTELEN_UNSIGNED8 0x0001 91 | #define BYTELEN_UNSIGNED16 0x0002 92 | #define BYTELEN_UNSIGNED32 0x0004 93 | #define BYTELEN_REAL32 0x0004 94 | #define BYTELEN_VISIBLESTRING 0x0001 95 | #define BYTELEN_OCTETSTRING 0x0001 96 | #define BYTELEN_REAL64 0x0008 97 | #define BYTELEN_UNSIGNED24 0x0003 98 | #define BYTELEN_UNSIGNED40 0x0005 99 | #define BYTELEN_UNSIGNED48 0x0006 100 | #define BYTELEN_UNSIGNED56 0x0007 101 | #define BYTELEN_UNSIGNED64 0x0008 102 | 103 | #define BITLEN_BOOLEAN 0x0001 104 | #define BITLEN_INTEGER8 0x0008 105 | #define BITLEN_INTEGER16 0x0010 106 | #define BITLEN_INTEGER32 0x0020 107 | #define BITLEN_UNSIGNED8 0x0008 108 | #define BITLEN_UNSIGNED16 0x0010 109 | #define BITLEN_UNSIGNED32 0x0020 110 | #define BITLEN_REAL32 0x0020 111 | #define BITLEN_REAL64 0x0040 112 | #define BITLEN_UNSIGNED24 0x0018 113 | #define BITLEN_UNSIGNED40 0x0028 114 | #define BITLEN_UNSIGNED48 0x0030 115 | #define BITLEN_UNSIGNED56 0x0038 116 | #define BITLEN_UNSIGNED64 0x0040 117 | 118 | #define SYNCTYPE_FREERUN 0x0000 119 | #define SYNCTYPE_SYNCHRON 0x0001 120 | #define SYNCTYPE_DCSYNC0 0x0002 121 | #define SYNCTYPE_DCSYNC1 0x0003 122 | #define SYNCTYPE_SM2INT 0x0022 123 | #define SYNCTYPE_SM3INT 0x0023 124 | #define SYNCTYPE_FREERUNSUPP 0x0001 125 | #define SYNCTYPE_SYNCHRONSUPP 0x0002 126 | #define SYNCTYPE_DCSYNC0SUPP 0x0004 127 | #define SYNCTYPE_DCSYNC1SUPP 0x0008 128 | #define SYNCTYPE_TIMESVARIABLE 0x4000 129 | #define SYNCTYPE_FASTMODE 0x8000 130 | 131 | 132 | #define IS_PDO_ASSIGN(x) ((x >= 0x1C10) && (x <= 0x1C2F)) 133 | #define IS_RX_PDO(x) (((x) >= 0x1600) && ((x) <= 0x17FF)) 134 | #define IS_TX_PDO(x) (((x) >= 0x1A00) && ((x) <= 0x1BFF)) 135 | /*///////////////////////////////////////////////////////////////////////////////////////// 136 | // 137 | // Object dictionary entry structure 138 | */ 139 | 140 | typedef struct OBJ_ENTRY 141 | { 142 | struct OBJ_ENTRY *pPrev; //previous entry(object) in the object dictionary list 143 | struct OBJ_ENTRY *pNext; //next entry(object) in the object dictionary list 144 | 145 | UINT16 Index; 146 | TSDOINFOOBJDESC ObjDesc; 147 | OBJCONST TSDOINFOENTRYDESC OBJMEM *pEntryDesc; 148 | OBJCONST UCHAR OBJMEM *pName; 149 | void MBXMEM *pVarPtr; 150 | UINT8 (* Read)( UINT16 Index, UINT8 Subindex, UINT32 Size, UINT16 MBXMEM * pData, UINT8 bCompleteAccess ); 151 | UINT8 (* Write)( UINT16 Index, UINT8 Subindex, UINT32 Size, UINT16 MBXMEM * pData, UINT8 bCompleteAccess ); 152 | UINT16 NonVolatileOffset; 153 | } 154 | TOBJECT; 155 | 156 | /*///////////////////////////////////////////////////////////////////////////////////////// 157 | // 158 | // type definitions of objects 159 | // 160 | */ 161 | 162 | typedef struct OBJ_STRUCT_PACKED_START 163 | { 164 | UINT16 subindex0; 165 | UINT16 u16SyncType; //SI 1 166 | UINT32 u32CycleTime; //SI 2 167 | UINT32 u32ShiftTime; //SI 3 168 | UINT16 u16SyncTypesSupported; //SI 4 169 | UINT32 u32MinCycleTime; //SI 5 170 | UINT32 u32CalcAndCopyTime; //SI 6 171 | UINT32 u32Reserved; //SI 7 172 | UINT16 u16GetCycleTime; //SI 8 173 | UINT32 u32DelayTime; //SI 9 174 | UINT32 u32Sync0CycleTime; //SI 10 175 | UINT32 u32SmEventMissedCounter; //SI 11 176 | UINT32 u32CycleExceededCounter; //SI 12 177 | UINT32 u32ShiftTooShortCounter; // SI 13 178 | UINT16 u16SyncError; // SI 32 179 | }OBJ_STRUCT_PACKED_END 180 | TSYNCMANPAR; 181 | 182 | typedef struct OBJ_STRUCT_PACKED_START 183 | { 184 | UINT16 syncFailedCounter; 185 | }OBJ_STRUCT_PACKED_END 186 | TCYCLEDIAG; 187 | 188 | typedef struct OBJ_STRUCT_PACKED_START { 189 | UINT16 u16SubIndex0; 190 | UINT32 u32LocalErrorReaction; 191 | UINT32 u32SyncErrorCounterLimit; 192 | } OBJ_STRUCT_PACKED_END 193 | TOBJ10F1; 194 | 195 | #endif //_OBJDEF_H_ 196 | 197 | 198 | -------------------------------------------------------------------------------- /libraries/arducat/esc.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------- 2 | ------ 3 | ------ Description 4 | ------ 5 | ------ esc.h 6 | ------ 7 | ------ EtherCAT Slave Controller 8 | ------ ------ 9 | -----------------------------------------------------------------------------------------*/ 10 | 11 | #ifndef _ESC_H_ 12 | #define _ESC_H_ 13 | 14 | /*----------------------------------------------------------------------------------------- 15 | ------ 16 | ------ Includes 17 | ------ 18 | -----------------------------------------------------------------------------------------*/ 19 | 20 | #include "ecat_def.h" 21 | 22 | 23 | /*----------------------------------------------------------------------------------------- 24 | ------ 25 | ------ Defines and Types 26 | ------ 27 | -----------------------------------------------------------------------------------------*/ 28 | 29 | /////////////////////////////////////////////////////////////// 30 | // 31 | // Standard Data Types 32 | // 33 | 34 | #define SIZEOF_SM_REGISTER 8 //Each SyncManger has 8Byte Configuration/Status Registers 35 | #define MAX_NO_OF_SYNC_MAN 16 //Maximum Number of SyncManager channels supported by an ESC 36 | 37 | #ifndef BL_PAGE_SIZE 38 | #define BL_PAGE_SIZE 512 39 | #endif 40 | 41 | 42 | 43 | 44 | /////////////////////////////////////////////////////////////// 45 | // 46 | // ESC Offsets (Detailed Information about the registers are located in the ESC Datasheets e.g. ET1100 Datasheet http://www.beckhoff.com/english.asp?download/ethercat_development_products.htm?id=71003127100387) 47 | // 48 | #define ESC_INFO_OFFSET 0x0000 //ESC information registers startoffset 49 | 50 | #define ESC_COMM_INFO_OFFSET 0x0004 //Communication information registers startoffset 51 | 52 | #define ESC_SM_CHANNELS_MASK 0xFF00 //Mask based on ESC offset "ESC_COMM_INFO_OFFSET" 53 | #define ESC_SM_CHANNELS_SHIFT 8 //Bit shift based on ESC offset "ESC_COMM_INFO_OFFSET" 54 | 55 | #define ESC_FEATURE_ADDRESS_OFFSET 0x0008 56 | /* ECATCHANGE_START(V5.01) ESC3*/ 57 | #define ESC_SLAVE_ADDRESS_OFFSET 0x0010 //Register Description: Address used for node addressing (FPxx commands) 58 | /* ECATCHANGE_START(V5.01) ESC3*/ 59 | 60 | 61 | #define ESC_AL_CONTROL_OFFSET 0x0120 //Register Description: Initiate State Transition of the Device State Machine 62 | #define ESC_AL_STATUS_OFFSET 0x0130 //Register Description: Actual State of the Device State Machine 63 | #define ESC_AL_STATUS_CODE_OFFSET 0x0134 //Register Description: AL Status Code 64 | 65 | #define ESC_RUN_LED_OVERRIDE 0x0138 //Register Description: Set Ecat Run indication via ESC. (not all ESC types support this feature) 66 | #define ESC_ERROR_LED_OVERRIDE 0x0139 //Register Description: Set Ecat Error indication via ESC. (not all ESC types support this feature) 67 | 68 | #define ESC_PDI_CONTROL_OFFSET 0x0140 //Register Description: Specifies the process data interface 69 | 70 | #define ESC_AL_EVENTMASK_OFFSET 0x0204 //Register Description: AL Event masking of the AL Event Request register Events for mapping to PDI IRQ signal 71 | #define ESC_AL_EVENT_OFFSET 0x0220 //Register Description: "Mirror" register for ESC events 72 | 73 | #define ESC_WD_DIVIDER_OFFSET 0x0400 //Register Description: Number of 25 MHz tics (minus 2) that represents the basic watchdog increment. (Default value is 100us = 2498) 74 | 75 | #define ESC_PD_WD_TIME 0x0420 //Register Description: Number of basic watchdog increments (Default value with Watchdog divider 100us means 100ms Watchdog) 76 | #define ESC_PD_WD_STATE 0x0440 //Register Description: Watchdog Status of Process Data (triggered by SyncManagers) 77 | #define ESC_PD_WD_TRIGGER_MASK 0x0001 //Trigger state of the process data watchdog 78 | 79 | #define ESC_EEPROM_CONFIG_OFFSET 0x0500 //Register Description: EEPROM Access Configuration 80 | /*EEPROM config and access state bit mask (based on "ESC_EEPROM_CONFIG_OFFSET") - START*/ 81 | #define ESC_EEPROM_ASSIGN_TO_PDI_MASK 0x0001 //Description (0x500.0): PDI has EEPROM control 82 | #define ESC_EEPROM_LOCKED_BY_PDI_MASK 0x0100 //Description (0x500.8): PDI locked EEPROM access 83 | /*EEPROM config and access state bit mask (based on "ESC_EEPROM_CONFIG_OFFSET") - END*/ 84 | 85 | #define ESC_EEPROM_CONTROL_OFFSET 0x0502 86 | /* EEPROM command and status bit masks (based on "ESC_EEPROM_CONTROL_OFFSET") - START*/ 87 | #define ESC_EEPROM_SUPPORTED_READBYTES_MASK 0x0040 //Description (0x502.6): Supported number of EEPROM read bytes: 0-> 4 Bytes; 1 -> 8 Bytes 88 | #define ESC_EEPROM_CMD_MASK 0x0700 //Description (0x502.8:10): Command bit mask 89 | #define ESC_EEPROM_CMD_READ_MASK 0x0100 //Description (0x502.8): Currently executed read command 90 | #define ESC_EEPROM_CMD_WRITE_MASK 0x0200 //Description (0x502.9): Initialize Write Command 91 | #define ESC_EEPROM_CMD_RELOAD_MASK 0x0400 //Description (0x502.10): Trigger EEPROM reload 92 | #define ESC_EEPROM_ERROR_MASK 0x7800 //Description : Mask all EEPROM error bits; Checksum error (0x0502.11); EEPROM not loaded (0x0502.12); Missing EEPROM Acknowledge (0x0502.13); Write Error (0x0502.14) 93 | #define ESC_EEPROM_ERROR_CRC 0x0800 //Description (0x502.11): EEPROM CRC Error 94 | #define ESC_EEPROM_ERROR_CMD_ACK 0x2000 //Description (0x502.13): EEPROM Busy 95 | #define ESC_EEPROM_BUSY_MASK 0x8000 //Description (0x502.15): EEPROM Busy 96 | /* EEPROM command and status bit masks (based on "ESC_EEPROM_CONTROL_OFFSET") - END*/ 97 | #define ESC_EEPROM_ADDRESS_OFFSET 0x0504 98 | #define ESC_EEPROM_DATA_OFFSET 0x0508 99 | 100 | #define ESC_SYNCMAN_REG_OFFSET 0x0800 //Register Description: Start address of the SyncManager Configuration/Staus registers 101 | #define ESC_SYNCMAN_CONTROL_OFFSET 0x0804 //Register Description: SyncManager Setting Register 102 | 103 | 104 | #define ESC_SYNCMAN_ACTIVE_OFFSET 0x0806 //Register Description: SyncManager Activation Register 105 | 106 | 107 | #define ESC_SYSTEMTIME_OFFSET 0x0910 //Register Description: Local copy of the System Time 108 | 109 | #define ESC_DC_UNIT_CONTROL_OFFSET 0x0980 //Register Description: Control registers for Cycle and Sync Unit (can be predefined with the "AssignActivate" Element in the device description, for further information see ETG.2000) 110 | #define ESC_DC_SYNC_UNIT_ACTIVE_MASK 0x0100 //Description (0x980.8): Sync Out Unit is activated 111 | #define ESC_DC_SYNC0_ACTIVE_MASK 0x0200 //Description (0x980.9): Sync0 generation is activated 112 | #define ESC_DC_SYNC1_ACTIVE_MASK 0x0400 //Description (0x980.10): Sync1 generation is activated 113 | 114 | #define ESC_DC_SYNC0_CYCLETIME_OFFSET 0x09A0 //Register Description: 32Bit Time between two consecutive SYNC0 pulses in ns 115 | #define ESC_DC_SYNC1_CYCLETIME_OFFSET 0x09A4 //Register Description: 32Bit Time between two consecutive SYNC1 pulses in ns 116 | 117 | 118 | 119 | /*--------------------------------------------------------------------------------- 120 | ------ ------ 121 | ------ Typdefinitionen ------ 122 | ------ ------ 123 | ---------------------------------------------------------------------------------*/ 124 | 125 | /**************************************************************** 126 | ** 127 | ** Sync Manager 128 | */ 129 | 130 | typedef struct STRUCT_PACKED_START 131 | { 132 | 133 | UINT16 PhysicalStartAddress; 134 | UINT16 Length; 135 | 136 | UINT16 Settings[2]; 137 | 138 | /*Defines to access : 139 | * - SM Control register 0x0804 140 | * - SM Status register 0x0805 141 | * 142 | * all defines are based on 0x0804 143 | */ 144 | 145 | /*SyncManger control (0x0804) access*/ 146 | #define SM_SETTING_CONTROL_OFFSET 0 //Offset to value of register 0x0804 147 | #define SM_SETTING_MODE_MASK 0x0002 148 | #define SM_SETTING_MODE_THREE_BUFFER_VALUE 0x0000 149 | #define SM_SETTING_MODE_ONE_BUFFER_VALUE 0x0002 150 | #define SM_SETTING_DIRECTION_MASK 0x000C 151 | #define SM_SETTING_DIRECTION_READ_VALUE 0x0000 152 | #define SM_SETTING_DIRECTION_WRITE_VALUE 0x0004 153 | #define SM_SETTING_WATCHDOG_VALUE 0x0040 154 | 155 | /*SyncManger status (0x0805) access*/ 156 | #define SM_STATUS_MBX_BUFFER_FULL 0x0800 //Indicates in one buffer mode if buffer was completely written 157 | 158 | /*Defines to access : 159 | * - SM Active register 0x0806 160 | * - SM PDI Control register 0x0807 161 | * 162 | * all defines are based on 0x0806 163 | */ 164 | 165 | /*SyncManger active (0x0806) access*/ 166 | #define SM_SETTING_ACTIVATE_OFFSET 1 //Offset to value of register 0x0806 167 | #define SM_SETTING_ENABLE_VALUE 0x0001 168 | #define SM_SETTING_REPAET_REQ_MASK 0x0002 169 | #define SM_SETTING_REPEAT_REQ_SHIFT 0 170 | 171 | /*SyncManger PDI Control (0x0807) access*/ 172 | #define SM_SETTING_PDI_DISABLE 0x0100 //Bit0 of register 0x0807 (if 1 SM is disabled from PDI) 173 | #define SM_SETTING_REPEAT_ACK 0x0200 //Bit1 of register 0x0807 174 | }STRUCT_PACKED_END 175 | TSYNCMAN; 176 | 177 | 178 | #define SYNCMAN_REG_SIZE 8 //"TSYNCMAN" structure size in bytes 179 | 180 | 181 | #endif //_ESC_H_ 182 | 183 | 184 | -------------------------------------------------------------------------------- /EtherCATSlave/ODGenerator.py: -------------------------------------------------------------------------------- 1 | from PDOMapping import GeneratePdoMapping 2 | 3 | def GeneratePdoMappingString(listSlaveInfo,rxtx,strEntryPrefix,strPdoObjPrefix): 4 | dictPdoMapping = GeneratePdoMapping(listSlaveInfo,rxtx,strEntryPrefix,strPdoObjPrefix) 5 | strPdoMapping = "" 6 | strAppObjDic = "" 7 | #Generate PDO Mapping Object 8 | for itemPdoMapping in dictPdoMapping.iteritems(): 9 | #Generate Remark 10 | strRemark = "/****************************************************\n" 11 | strRemark = strRemark+"** Object"+itemPdoMapping[0]+'\n' 12 | strRemark = strRemark+"****************************************************/\n" 13 | #Generate entry description 14 | strEntryDesc = 'OBJCONST TSDOINFOENTRYDESC OBJMEM '+'asEntryDesc'+itemPdoMapping[0] 15 | strEntryDesc = strEntryDesc+'[] = {\n' 16 | strEntryDesc = strEntryDesc+'\t{DEFTYPE_UNSIGNED16, 0x10, ACCESS_READ },\n' 17 | strEntryDesc = strEntryDesc+'\t{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},\n'*len(itemPdoMapping[1]) 18 | strEntryDesc = strEntryDesc+'};' 19 | #Generate Obj name 20 | if rxtx == "rx": 21 | strObjName = 'OBJCONST UCHAR OBJMEM aName'+itemPdoMapping[0]+'[] = "Output Mapping ' 22 | elif rxtx == "tx": 23 | strObjName = 'OBJCONST UCHAR OBJMEM aName'+itemPdoMapping[0]+'[] = "Input Mapping ' 24 | strObjName = strObjName+itemPdoMapping[0][4:6] 25 | strObjName = strObjName+'\\000\\377";' 26 | #Generate var declaration 27 | strVarDecl = "typedef struct OBJ_STRUCT_PACKED_START {\n" 28 | strVarDecl = strVarDecl+"\tUINT16 u16SubIndex0;\n" 29 | strVarDecl = strVarDecl+"\tUINT32 aEntries["+str(len(itemPdoMapping[1]))+"];\n" 30 | strVarDecl = strVarDecl+"} OBJ_STRUCT_PACKED_END\n" 31 | strVarDecl = strVarDecl+"TOBJ"+itemPdoMapping[0]+';\n' 32 | strVarDecl = strVarDecl+"TOBJ"+itemPdoMapping[0]+" Obj"+itemPdoMapping[0]+' __attribute__ ((aligned (2)))\n={' 33 | strVarDecl = strVarDecl+str(len(itemPdoMapping[1]))+', {' 34 | for dictEntry in itemPdoMapping[1]: 35 | strVarDecl = strVarDecl+str(dictEntry['Index'])+("%02d"%dictEntry['SI'])+\ 36 | ("%02x"%dictEntry['Length'])+',' 37 | strVarDecl = strVarDecl+'}};' 38 | strPdoMapping = strPdoMapping+strRemark+strVarDecl+'\n'+strEntryDesc+'\n'+strObjName+'\n' 39 | #Generate AppObjDic of PDO mapping 40 | strAppObjDic = strAppObjDic+"\t{NULL,NULL,"+itemPdoMapping[0] 41 | strAppObjDic = strAppObjDic+",{DEFTYPE_PDOMAPPING, 1 | (OBJCODE_ARR << 8)}, asEntryDesc" 42 | strAppObjDic = strAppObjDic+itemPdoMapping[0]+', aName'+itemPdoMapping[0]+', &Obj'+itemPdoMapping[0] 43 | strAppObjDic = strAppObjDic+', NULL, NULL, 0x0000 },\n' 44 | #Generate PDO Assignment 45 | #Generate Remark 46 | strRemark = "/****************************************************\n" 47 | if rxtx == "rx": 48 | strRemark = strRemark+"** Object0x1C12\n" 49 | elif rxtx == "tx": 50 | strRemark = strRemark+"** Object0x1C13\n" 51 | strRemark = strRemark+"****************************************************/\n" 52 | #Generate Obj name 53 | if rxtx == "rx": 54 | strPdoAssignIndex = '0x1C12' 55 | strPdoAssignVarName = 'sRxPDOassign' 56 | elif rxtx == "tx": 57 | strPdoAssignIndex = '0x1C13' 58 | strPdoAssignVarName = 'sTxPDOassign' 59 | strObjName = 'OBJCONST UCHAR OBJMEM aName'+strPdoAssignIndex+'[] = "'+rxtx.upper()+'PDO assign";' 60 | #Generate var declaration 61 | strVarDecl = "typedef struct OBJ_STRUCT_PACKED_START {\n" 62 | strVarDecl = strVarDecl+"\tUINT16 u16SubIndex0;\n" 63 | strVarDecl = strVarDecl+"\tUINT16 aEntries["+str(len(dictPdoMapping))+"];\n" 64 | strVarDecl = strVarDecl+"} OBJ_STRUCT_PACKED_END\n" 65 | strVarDecl = strVarDecl+"TOBJ"+strPdoAssignIndex+';\n' 66 | strVarDecl = strVarDecl+"TOBJ"+strPdoAssignIndex+' '+strPdoAssignVarName+' __attribute__ ((aligned (2))) \n={' 67 | strVarDecl = strVarDecl+str(len(dictPdoMapping))+', {' 68 | strVarDecl = strVarDecl+','.join(dictPdoMapping.keys()) 69 | strVarDecl = strVarDecl+'}};' 70 | strPdoMapping = strPdoMapping+strRemark+strVarDecl+'\n'+strObjName+'\n' 71 | #Generate AppObjDic of PDO assignment 72 | strAppObjDic = strAppObjDic+"\t{NULL,NULL,"+strPdoAssignIndex 73 | strAppObjDic = strAppObjDic+",{DEFTYPE_UNSIGNED16, "+str(len(dictPdoMapping)) 74 | strAppObjDic = strAppObjDic+" | (OBJCODE_ARR << 8)}, asPDOAssignEntryDesc" 75 | strAppObjDic = strAppObjDic+', aName'+strPdoAssignIndex+', &'+strPdoAssignVarName 76 | strAppObjDic = strAppObjDic+', NULL, NULL, 0x0000 },\n' 77 | return (strPdoMapping,strAppObjDic) 78 | 79 | 80 | 81 | def GenerateTxPdoMappingString(listSlaveInfo): 82 | return GeneratePdoMappingString(listSlaveInfo,"tx","0x6","0x1A") 83 | 84 | def GenerateRxPdoMappingString(listSlaveInfo): 85 | return GeneratePdoMappingString(listSlaveInfo,"rx","0x7","0x16") 86 | 87 | 88 | 89 | def ODGenerator(listSlaveInfo): 90 | from EthercatType import dictEthercatType, dictAccessType, dictPdoDirType 91 | strDecl = "" 92 | #Generate PdoMappingString 93 | (strDeclTxPdo,strAppObjDicTxPdo) = GenerateTxPdoMappingString(listSlaveInfo); 94 | (strDeclRxPdo,strAppObjDicRxPdo) = GenerateRxPdoMappingString(listSlaveInfo); 95 | strDecl = strDecl+strDeclRxPdo; 96 | strDecl = strDecl+strDeclTxPdo; 97 | strAppObjDic = "\n/****************************************************\n" 98 | strAppObjDic = strAppObjDic+"** ApplicationObjDic\n" 99 | strAppObjDic = strAppObjDic+"****************************************************/\n" 100 | strAppObjDic = strAppObjDic+"TOBJECT OBJMEM ApplicationObjDic[] = {\n" 101 | strAppObjDic = strAppObjDic+strAppObjDicTxPdo 102 | strAppObjDic = strAppObjDic+strAppObjDicRxPdo 103 | for dictItem in listSlaveInfo: 104 | if dictItem['ObjectCode'] == "VARIABLE": 105 | dictEntry = dictItem['Entry'][0] 106 | #Generate var declaration 107 | strVarDecl = dictEthercatType[dictEntry['DataType']][0] 108 | strVarDecl = strVarDecl+' '+"Obj"+dictItem['Index']+' __attribute__ ((aligned (2)))= ' 109 | strVarDecl = strVarDecl+str(dictEntry['Default'])+';' 110 | #Generate entry description 111 | strEntryDesc = 'OBJCONST TSDOINFOENTRYDESC OBJMEM '+'asEntryDesc'+dictItem['Index']+ ' = {' 112 | strEntryDesc = strEntryDesc+dictEthercatType[dictEntry['DataType']][1] 113 | strEntryDesc = strEntryDesc+', '+str(dictEthercatType[dictEntry['DataType']][2])+', ' 114 | strEntryDesc = strEntryDesc+dictAccessType[dictEntry['Access']]+'|' 115 | strEntryDesc = strEntryDesc+dictPdoDirType[dictEntry['PdoDirection']] 116 | strEntryDesc = strEntryDesc+'};' 117 | #Generate Obj name 118 | strObjName = 'OBJCONST UCHAR OBJMEM aName'+dictItem['Index']+'[] = "'+dictEntry['Name']+'";' 119 | elif dictItem['ObjectCode'] == "RECORD": 120 | #Generate entry description 121 | strEntryDesc = 'OBJCONST TSDOINFOENTRYDESC OBJMEM '+'asEntryDesc'+dictItem['Index'] 122 | strEntryDesc = strEntryDesc+'['+str(len(dictItem['Entry']))+'] = {\n' 123 | for dictEntry in dictItem['Entry']: 124 | strEntryDesc = strEntryDesc+'\t{' 125 | strEntryDesc = strEntryDesc+dictEthercatType[dictEntry['DataType']][1] 126 | strEntryDesc = strEntryDesc+', '+str(dictEthercatType[dictEntry['DataType']][2])+', ' 127 | strEntryDesc = strEntryDesc+dictAccessType[dictEntry['Access']]+'|' 128 | strEntryDesc = strEntryDesc+dictPdoDirType[dictEntry['PdoDirection']] 129 | strEntryDesc = strEntryDesc+'},\n' 130 | strEntryDesc = strEntryDesc+'};' 131 | #Generate Obj name 132 | strObjName = 'OBJCONST UCHAR OBJMEM aName'+dictItem['Index']+'[] = "' 133 | for dictEntry in dictItem['Entry']: 134 | strObjName = strObjName+dictEntry['Name']+'\\000' 135 | strObjName = strObjName+'\\377";' 136 | #Generate var declaration 137 | strVarDecl = "typedef struct OBJ_STRUCT_PACKED_START {\n" 138 | bLastBitField = 0 139 | for dictEntry in dictItem['Entry']: 140 | strVarDecl = strVarDecl+"\t"+ dictEthercatType[dictEntry['DataType']][0] 141 | strVarDecl = strVarDecl+' '+"SubIndex"+str(dictEntry['SI']) 142 | if dictEthercatType[dictEntry['DataType']][2]<16: 143 | strVarDecl = strVarDecl+':'+str(dictEthercatType[dictEntry['DataType']][2]) 144 | if bLastBitField == 0: 145 | strVarDecl = strVarDecl+' __attribute__ ((aligned (2)))' 146 | bLastBitField = 1 147 | else: 148 | strVarDecl = strVarDecl+' __attribute__ ((aligned (2)))' 149 | bLastBitField = 0 150 | strVarDecl = strVarDecl+';\n' 151 | strVarDecl = strVarDecl+"} OBJ_STRUCT_PACKED_END\n" 152 | strVarDecl = strVarDecl+"TOBJ"+dictItem['Index']+';\n' 153 | strVarDecl = strVarDecl+"TOBJ"+dictItem['Index']+" Obj"+dictItem['Index']+'\n={' 154 | for dictEntry in dictItem['Entry']: 155 | strVarDecl = strVarDecl+str(dictEntry['Default'])+',' 156 | strVarDecl = strVarDecl+'};' 157 | elif dictItem['ObjectCode'] == "ARRAY": 158 | #Generate entry description 159 | strEntryDesc = 'OBJCONST TSDOINFOENTRYDESC OBJMEM '+'asEntryDesc'+dictItem['Index'] 160 | strEntryDesc = strEntryDesc+'['+str(len(dictItem['Entry']))+'] = {\n' 161 | for dictEntry in dictItem['Entry']: 162 | strEntryDesc = strEntryDesc+'\t{' 163 | strEntryDesc = strEntryDesc+dictEthercatType[dictEntry['DataType']][1] 164 | strEntryDesc = strEntryDesc+', '+str(dictEthercatType[dictEntry['DataType']][2])+', ' 165 | strEntryDesc = strEntryDesc+dictAccessType[dictEntry['Access']]+'|' 166 | strEntryDesc = strEntryDesc+dictPdoDirType[dictEntry['PdoDirection']] 167 | strEntryDesc = strEntryDesc+'},\n' 168 | strEntryDesc = strEntryDesc+'};' 169 | #Generate Obj name 170 | strObjName = 'OBJCONST UCHAR OBJMEM aName'+dictItem['Index']+'[] = "' 171 | strObjName = strObjName+dictItem['Entry'][0]['Name']+'";' 172 | #Generate var declaration 173 | strVarDecl = "typedef struct OBJ_STRUCT_PACKED_START {\n" 174 | strVarDecl = strVarDecl+"\t"+ dictEthercatType[dictItem['Entry'][0]['DataType']][0] 175 | strVarDecl = strVarDecl+' '+"SubIndex0"+' __attribute__ ((aligned (2)));\n' 176 | for intSI in range(1,int(dictItem['Entry'][0]['Default'])+1): 177 | strVarDecl = strVarDecl+"\t"+ dictEthercatType[dictItem['Entry'][1]['DataType']][0]\ 178 | +' '+"SubIndex"+str(intSI) 179 | if dictEthercatType[dictItem['Entry'][1]['DataType']][2]<16: 180 | strVarDecl = strVarDecl+':'+str(dictEthercatType[dictItem['Entry'][1]['DataType']][2]) 181 | if intSI==1 or dictEthercatType[dictItem['Entry'][1]['DataType']][2]>=16: 182 | strVarDecl = strVarDecl+' __attribute__ ((aligned (2)))' 183 | strVarDecl = strVarDecl+';\n' 184 | strVarDecl = strVarDecl+"} OBJ_STRUCT_PACKED_END\n" 185 | strVarDecl = strVarDecl+"TOBJ"+dictItem['Index']+';\n' 186 | strVarDecl = strVarDecl+"TOBJ"+dictItem['Index']+" Obj"+dictItem['Index']+'\n={' 187 | strVarDecl = strVarDecl+str(dictItem['Entry'][0]['Default'])+',' 188 | strVarDecl = strVarDecl+(str(dictItem['Entry'][1]['Default'])+',')*int(dictItem['Entry'][0]['Default']) 189 | strVarDecl = strVarDecl+'};' 190 | #Generate Remark 191 | strRemark = "/****************************************************\n" 192 | strRemark = strRemark+"** Object"+dictItem['Index']+'\n' 193 | strRemark = strRemark+"****************************************************/\n" 194 | strEntry = strRemark+strVarDecl+'\n'+strEntryDesc+'\n'+strObjName+'\n' 195 | #Append to declaration string 196 | strDecl = strDecl+strEntry 197 | #Generate ApplicationObjDic Item 198 | strObjDicItem = "\t{NULL, NULL, "+dictItem['Index']+', ' 199 | if dictItem['ObjectCode'] == "VARIABLE": 200 | strObjDicItem = strObjDicItem+'{'+dictEthercatType[dictItem['Entry'][0]['DataType']][1] 201 | strObjDicItem = strObjDicItem+', 0|(OBJCODE_VAR<<8)}, '+'&' 202 | elif dictItem['ObjectCode'] == "RECORD": 203 | strObjDicItem = strObjDicItem+'{DEFTYPE_RECORD' 204 | strObjDicItem = strObjDicItem+', '+str(len(dictItem['Entry'])-1)+'|(OBJCODE_REC<<8)}, ' 205 | elif dictItem['ObjectCode'] == "ARRAY": 206 | strObjDicItem = strObjDicItem+'{DEFTYPE_RECORD' 207 | strObjDicItem = strObjDicItem+', '+str(dictItem['Entry'][0]['Default'])+'|(OBJCODE_ARR<<8)}, ' 208 | strObjDicItem = strObjDicItem+'asEntryDesc'+dictItem['Index']+', ' 209 | strObjDicItem = strObjDicItem+'aName'+dictItem['Index']+', ' 210 | strObjDicItem = strObjDicItem+'&Obj'+dictItem['Index']+', ' 211 | strObjDicItem = strObjDicItem+'NULL , NULL , 0x0000 },\n' 212 | strAppObjDic = strAppObjDic+strObjDicItem 213 | strAppObjDic = strAppObjDic+"\t{NULL,NULL, 0xFFFF, {0, 0}, NULL, NULL, NULL, NULL}};\n" 214 | return strDecl+strAppObjDic 215 | 216 | 217 | 218 | 219 | 220 | -------------------------------------------------------------------------------- /libraries/arducat/ecatslv.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------- 2 | ------ 3 | ------ ecatslv.h 4 | ------ 5 | -----------------------------------------------------------------------------------------*/ 6 | 7 | #ifndef _ECATSLV_H_ 8 | #define _ECATSLV_H_ 9 | 10 | /*----------------------------------------------------------------------------------------- 11 | ------ 12 | ------ Includes 13 | ------ 14 | -----------------------------------------------------------------------------------------*/ 15 | 16 | #include "ecat_def.h" 17 | 18 | #include "esc.h" 19 | 20 | 21 | 22 | /*----------------------------------------------------------------------------------------- 23 | ------ 24 | ------ Defines and Types 25 | ------ 26 | -----------------------------------------------------------------------------------------*/ 27 | 28 | /////////////////////////////////////////////////////////////// 29 | // 30 | // General 31 | // 32 | 33 | #ifndef OBJGETNEXTSTR 34 | /* the old definition was not working with all compilers */ 35 | /* old: #define OBJGETNEXTSTR(p) ( (OBJCONST CHAR OBJMEM * )( ((UINT32) p) + OBJSTRLEN( (OBJCONST CHAR OBJMEM *) p ) + 1 ) ) */ 36 | #define OBJGETNEXTSTR(p) ( (OBJCONST CHAR OBJMEM * )( &((p)[OBJSTRLEN( (OBJCONST CHAR OBJMEM *) (p) ) + 1]) ) ) 37 | #endif 38 | 39 | #ifndef LO_BYTE 40 | #define LO_BYTE 0 41 | #endif 42 | 43 | #ifndef HI_BYTE 44 | #define HI_BYTE 1 45 | #endif 46 | 47 | #ifndef LOLO_BYTE 48 | #define LOLO_BYTE 0 49 | #endif 50 | 51 | #ifndef LOHI_BYTE 52 | #define LOHI_BYTE 1 53 | #endif 54 | 55 | #ifndef HILO_BYTE 56 | #define HILO_BYTE 2 57 | #endif 58 | 59 | #ifndef HIHI_BYTE 60 | #define HIHI_BYTE 3 61 | #endif 62 | 63 | #ifndef LO_WORD 64 | #define LO_WORD 0 65 | #endif 66 | 67 | #ifndef HI_WORD 68 | #define HI_WORD 1 69 | #endif 70 | 71 | #ifndef SWAPWORD 72 | #define SWAPWORD(x) (x) 73 | #endif 74 | 75 | #ifndef SWAPDWORD 76 | #define SWAPDWORD(x) (x) 77 | #endif 78 | 79 | #ifndef LOBYTE 80 | #define LOBYTE(x) ((x)&0xFF) 81 | #endif 82 | 83 | #ifndef HIBYTE 84 | #define HIBYTE(x) (((x)&0xFF00)>>8) 85 | #endif 86 | 87 | #ifndef LOLOBYTE 88 | #define LOLOBYTE(x) ((x)&0xFF) 89 | #endif 90 | 91 | #ifndef LOHIBYTE 92 | #define LOHIBYTE(x) (((x)&0xFF00)>>8) 93 | #endif 94 | 95 | #ifndef HILOBYTE 96 | #define HILOBYTE(x) (((x)&0xFF0000)>>16) 97 | #endif 98 | 99 | #ifndef HIHIBYTE 100 | #define HIHIBYTE(x) (((x)&0xFF000000)>>24) 101 | #endif 102 | 103 | #ifndef LOWORD 104 | #define LOWORD(x) ((x)&0xFFFF) 105 | #endif 106 | 107 | #ifndef HIWORD 108 | #define HIWORD(x) (((x)&0xFFFF0000)>>16) 109 | #endif 110 | 111 | #ifndef BIT2BYTE 112 | #define BIT2BYTE(x) (((x)+7)>>3) 113 | #endif 114 | 115 | #ifndef BYTE2BIT 116 | #define BYTE2BIT(x) ((x)<<3) 117 | #endif 118 | 119 | #ifndef BIT2WORD 120 | #define BIT2WORD(x) (((x)+15)>>4) 121 | #endif 122 | 123 | #ifndef BYTE2WORD 124 | #define BYTE2WORD(x) (((x)+1)>>1) 125 | #endif 126 | 127 | #ifndef ROUNDUPBYTE2WORD 128 | #define ROUNDUPBYTE2WORD(x) ((((x)+1)>>1)<<1) 129 | #endif 130 | 131 | /*///////////////////////////////////////////////////////////////////////////////////////// 132 | // 133 | // State defines 134 | */ 135 | 136 | #define STATE_INIT ((UINT8) 0x01) 137 | #define STATE_PREOP ((UINT8) 0x02) 138 | #define STATE_BOOT ((UINT8) 0x03) 139 | #define STATE_SAFEOP ((UINT8) 0x04) 140 | #define STATE_OP ((UINT8) 0x08) 141 | 142 | #define STATE_MASK ((UINT8) 0x0F) 143 | #define STATE_CHANGE ((UINT8) 0x10) 144 | #define STATE_DEVID ((UINT8) 0x20) 145 | 146 | #define BOOT_2_INIT (((STATE_BOOT) << 4) | (STATE_INIT)) 147 | 148 | #define INIT_2_BOOT (((STATE_INIT) << 4) | (STATE_BOOT)) 149 | #define PREOP_2_BOOT (((STATE_PREOP) << 4) | (STATE_BOOT)) 150 | #define SAFEOP_2_BOOT (((STATE_SAFEOP) << 4) | (STATE_BOOT)) 151 | #define OP_2_BOOT (((STATE_OP) << 4) | (STATE_BOOT)) 152 | 153 | #define INIT_2_INIT (((STATE_INIT) << 4) | (STATE_INIT)) 154 | #define INIT_2_PREOP (((STATE_INIT) << 4) | (STATE_PREOP)) 155 | #define INIT_2_SAFEOP (((STATE_INIT) << 4) | (STATE_SAFEOP)) 156 | #define INIT_2_OP (((STATE_INIT) << 4) | (STATE_OP)) 157 | 158 | #define PREOP_2_INIT (((STATE_PREOP) << 4) | (STATE_INIT)) 159 | #define PREOP_2_PREOP (((STATE_PREOP) << 4) | (STATE_PREOP)) 160 | #define PREOP_2_SAFEOP (((STATE_PREOP) << 4) | (STATE_SAFEOP)) 161 | #define PREOP_2_OP (((STATE_PREOP) << 4) | (STATE_OP)) 162 | 163 | #define SAFEOP_2_INIT (((STATE_SAFEOP) << 4) | (STATE_INIT)) 164 | #define SAFEOP_2_PREOP (((STATE_SAFEOP) << 4) | (STATE_PREOP)) 165 | #define SAFEOP_2_SAFEOP (((STATE_SAFEOP) << 4) | (STATE_SAFEOP)) 166 | #define SAFEOP_2_OP (((STATE_SAFEOP) << 4) |( STATE_OP)) 167 | 168 | #define OP_2_INIT (((STATE_OP) << 4) | (STATE_INIT)) 169 | #define OP_2_PREOP (((STATE_OP) << 4) | (STATE_PREOP)) 170 | #define OP_2_SAFEOP (((STATE_OP) << 4) | (STATE_SAFEOP)) 171 | #define OP_2_OP (((STATE_OP) << 4) | (STATE_OP)) 172 | 173 | /*///////////////////////////////////////////////////////////////////////////////////////// 174 | // 175 | // ESM transition error codes 176 | */ 177 | 178 | #define SYNCMANCHODDADDRESS 0x00 179 | #define SYNCMANCHADDRESS 0x01 180 | #define SYNCMANCHSIZE 0x02 181 | #define SYNCMANCHSETTINGS 0x03 182 | #define ERROR_SYNCMANCH(code, channel) ((code)+((channel)<<2)) 183 | #define ERROR_SYNCMANCHODDADDRESS(channel) ((SYNCMANCHODDADDRESS)+((channel)<<2)) 184 | #define ERROR_SYNCMANCHADDRESS(channel) ((SYNCMANCHADDRESS)+((channel)<<2)) 185 | #define ERROR_SYNCMANCHSIZE(channel) ((SYNCMANCHSIZE)+((channel)<<2)) 186 | #define ERROR_SYNCMANCHSETTINGS(channel) ((SYNCMANCHSETTINGS)+((channel)<<2)) 187 | #define ERROR_SYNCTYPES 0x80 188 | #define ERROR_DCSYNCCONTROL 0x81 189 | #define ERROR_DCSYNC0CYCLETIME 0x82 190 | #define ERROR_DCSYNC1CYCLETIME 0x83 191 | #define ERROR_DCCYCLEPARAMETER 0x84 192 | #define ERROR_DCLATCHCONTROL 0x85 193 | 194 | #define ERROR_INVALIDSTATE 0xF0 195 | #define ERROR_NOMEMORY 0xF1 196 | #define ERROR_OBJECTDICTIONARY 0xF2 197 | #define ERROR_NOSYNCMANACCESS 0xF3 198 | #define ERROR_NOOFRXPDOS 0xF4 199 | #define ERROR_NOOFTXPDOS 0xF5 200 | #define ERROR_STATECHANGE 0xF6 201 | 202 | #define NOERROR_NOSTATECHANGE 0xFE 203 | #define NOERROR_INWORK 0xFF 204 | 205 | #define EMCY_SM_ERRORCODE 0xA000 206 | #define EMCY_SM_DEVICESPECIFIC 0xFF00 207 | 208 | /*///////////////////////////////////////////////////////////////////////////////////////// 209 | // 210 | // AL Status Codes 211 | */ 212 | 213 | #define ALSTATUSCODE_NOERROR 0x0000 214 | #define ALSTATUSCODE_UNSPECIFIEDERROR 0x0001 215 | #define ALSTATUSCODE_NOMEMORY 0x0002 216 | #define ALSTATUSCODE_INVALIDALCONTROL 0x0011 217 | #define ALSTATUSCODE_UNKNOWNALCONTROL 0x0012 218 | #define ALSTATUSCODE_BOOTNOTSUPP 0x0013 219 | #define ALSTATUSCODE_NOVALIDFIRMWARE 0x0014 220 | #define ALSTATUSCODE_INVALIDMBXCFGINBOOT 0x0015 221 | #define ALSTATUSCODE_INVALIDMBXCFGINPREOP 0x0016 222 | #define ALSTATUSCODE_INVALIDSMCFG 0x0017 223 | #define ALSTATUSCODE_NOVALIDINPUTS 0x0018 224 | #define ALSTATUSCODE_NOVALIDOUTPUTS 0x0019 225 | #define ALSTATUSCODE_SYNCERROR 0x001A 226 | #define ALSTATUSCODE_SMWATCHDOG 0x001B 227 | #define ALSTATUSCODE_SYNCTYPESNOTCOMPATIBLE 0x001C 228 | #define ALSTATUSCODE_INVALIDSMOUTCFG 0x001D 229 | #define ALSTATUSCODE_INVALIDSMINCFG 0x001E 230 | #define ALSTATUSCODE_INVALIDWDCFG 0x001F 231 | #define ALSTATUSCODE_WAITFORCOLDSTART 0x0020 232 | #define ALSTATUSCODE_WAITFORINIT 0x0021 233 | #define ALSTATUSCODE_WAITFORPREOP 0x0022 234 | #define ALSTATUSCODE_WAITFORSAFEOP 0x0023 235 | #define ALSTATUSCODE_INVALIDINPUTMAPPING 0x0024 236 | #define ALSTATUSCODE_INVALIDOUTPUTMAPPING 0x0025 237 | #define ALSTATUSCODE_INCONSISTENTSETTINGS 0x0026 238 | #define ALSTATUSCODE_FREERUNNOTSUPPORTED 0x0027 239 | #define ALSTATUSCODE_SYNCHRONNOTSUPPORTED 0x0028 240 | #define ALSTATUSCODE_FREERUNNEEDS3BUFFERMODE 0x0029 241 | #define ALSTATUSCODE_BACKGROUNDWATCHDOG 0x002A 242 | #define ALSTATUSCODE_NOVALIDINPUTSANDOUTPUTS 0x002B 243 | #define ALSTATUSCODE_FATALSYNCERROR 0x002C 244 | #define ALSTATUSCODE_NOSYNCERROR 0x002D 245 | #define ALSTATUSCODE_DCINVALIDSYNCCFG 0x0030 246 | #define ALSTATUSCODE_DCINVALIDLATCHCFG 0x0031 247 | #define ALSTATUSCODE_DCPLLSYNCERROR 0x0032 248 | #define ALSTATUSCODE_DCSYNCIOERROR 0x0033 249 | #define ALSTATUSCODE_DCSYNCMISSEDERROR 0x0034 250 | #define ALSTATUSCODE_DCINVALIDSYNCCYCLETIME 0x0035 251 | #define ALSTATUSCODE_DCSYNC0CYCLETIME 0x0036 252 | #define ALSTATUSCODE_DCSYNC1CYCLETIME 0x0037 253 | #define ALSTATUSCODE_MBX_AOE 0x0041 254 | #define ALSTATUSCODE_MBX_EOE 0x0042 255 | #define ALSTATUSCODE_MBX_COE 0x0043 256 | #define ALSTATUSCODE_MBX_FOE 0x0044 257 | #define ALSTATUSCODE_MBX_SOE 0x0045 258 | #define ALSTATUSCODE_MBX_VOE 0x004F 259 | #define ALSTATUSCODE_EE_NOACCESS 0x0050 260 | #define ALSTATUSCODE_EE_ERROR 0x0051 261 | 262 | /*///////////////////////////////////////////////////////////////////////////////////////// 263 | // 264 | // AL event masks 265 | */ 266 | 267 | #define AL_CONTROL_EVENT ((UINT16) 0x01) 268 | #define SYNC0_EVENT ((UINT16) 0x04) 269 | #define SYNC1_EVENT ((UINT16) 0x08) 270 | #define SM_CHANGE_EVENT ((UINT16) 0x10) 271 | #define EEPROM_CMD_PENDING ((UINT16) 0x20) 272 | 273 | #ifndef MAX_PD_SYNC_MAN_CHANNELS 274 | #define MAX_PD_SYNC_MAN_CHANNELS 2 275 | #endif 276 | #define MAX_NUMBER_OF_SYNCMAN ((MAX_PD_SYNC_MAN_CHANNELS)+2) 277 | 278 | #define MAILBOX_WRITE 0 279 | #define MAILBOX_READ 1 280 | #define PROCESS_DATA_OUT 2 281 | #define PROCESS_DATA_IN 3 282 | 283 | #define MAILBOX_WRITE_EVENT ((UINT16) 0x0100) 284 | #define MAILBOX_READ_EVENT ((UINT16) 0x0200) 285 | #define PROCESS_OUTPUT_EVENT ((UINT16) 0x0400) 286 | #define PROCESS_INPUT_EVENT ((UINT16) 0x0800) 287 | 288 | /*///////////////////////////////////////////////////////////////////////////////////////// 289 | // 290 | // Codes for LED 291 | // bit 7: invert flag 292 | // bit 6: toggle (if toggle == 1 and number of flashes == 0 => infinite toggle) 293 | // bit 5: fast toggle flag (50ms cycle) 294 | // bit 4-0: number of flashes 295 | */ 296 | #define LED_OFF 0x00 297 | #define LED_FLICKERING 0x60 298 | #define LED_BLINKING 0x40 299 | #define LED_SINGLEFLASH 0x41 300 | #define LED_DOUBLEFLASH 0x42 301 | #define LED_INVERT_DOUBLEFLASH 0xC2 302 | #define LED_ON 0x01 303 | 304 | 305 | /*///////////////////////////////////////////////////////////////////////////////////////// 306 | // 307 | // Addresses 308 | */ 309 | #define MEMORY_START_ADDRESS 0x1000 310 | 311 | /*///////////////////////////////////////////////////////////////////////////////////////// 312 | // 313 | // Overwrites 314 | */ 315 | 316 | #ifndef DC_SYNC_ACTIVE 317 | #define DC_SYNC_ACTIVE ESC_DC_SYNC0_ACTIVE_MASK 318 | #endif 319 | #ifndef DC_EVENT_MASK 320 | #define DC_EVENT_MASK PROCESS_OUTPUT_EVENT 321 | #endif 322 | 323 | /*///////////////////////////////////////////////////////////////////////////////////////// 324 | // 325 | // Data Types 326 | */ 327 | #define MAX_SM_EVENT_MISSED 4 328 | 329 | #endif //_ECATSLV_H_ 330 | 331 | /*----------------------------------------------------------------------------------------- 332 | ------ 333 | ------ global variables 334 | ------ 335 | -----------------------------------------------------------------------------------------*/ 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | -------------------------------------------------------------------------------- /libraries/arducat/ethercat.h: -------------------------------------------------------------------------------- 1 | #ifndef ETHERCAT_H 2 | #define ETHERCAT_H 3 | 4 | #include "Arduino.h" 5 | 6 | #include "ecat_def.h" 7 | #include "esc.h" 8 | #include "ecatslv.h" 9 | #include "ecatappl.h" 10 | #include "ecatcoe.h" 11 | #include "mailbox.h" 12 | #include "sdoserv.h" 13 | #include "objdef.h" 14 | 15 | //==========================HWDIFINES==================================== 16 | #define ECAT_TIMER_INC_P_MS 1000 //1000 micros/ms 17 | 18 | /*-------------------------------------------------------------------------------------- 19 | ------ 20 | ------ internal Types and Defines 21 | ------ 22 | --------------------------------------------------------------------------------------*/ 23 | 24 | typedef union 25 | { 26 | unsigned short Word; 27 | unsigned char Byte[2]; 28 | } UBYTETOWORD; 29 | 30 | typedef union 31 | { 32 | UINT8 Byte[2]; 33 | UINT16 Word; 34 | } 35 | UALEVENT; 36 | 37 | 38 | class Ethercat 39 | { 40 | public: 41 | Ethercat(); 42 | ~Ethercat(); 43 | private: 44 | //=========================ECATSLV_H================================= 45 | BOOL bEcatOutputUpdateRunning; // indicates the OP state, will be set in StartOutputHandler 46 | // and reset in StopOutputHandler 47 | BOOL bEcatInputUpdateRunning; // indicates the SAFEOP or OP state, will be set in StartInputHandler 48 | // and reset in Stop InputHandler 49 | BOOL bEcatFirstOutputsReceived; // indicates if outputs were received (SM2-event) 50 | // or inputs were read (SM3-event, if the output size is 0), 51 | // has to be set by the application and reset in StopOutputHandler 52 | BOOL bWdTrigger; // indicates that the SM2 WD trigger bit (0x814 bit6) is set. 53 | BOOL bDcSyncActive; // indicates that the Distributed Clocks synchronization is active, 54 | INT16 EsmTimeoutCounter; // Counter used to detect an ESM timeout. -1 indicates a deactivated counter and 0 is expired 55 | BOOL bDcRunning; //indicates if Sync0 events are received 56 | BOOL bSmSyncToggle; //this variable is set to false in the PDI Isr and to true in der corresponding Sync event 57 | BOOL bPllRunning; //set to true if Esc/Sync0 sequence is valid 58 | INT16 i16WaitForPllRunningTimeout; //the time bPllRunnig shall be true while state change from SafeOp to Op 59 | INT16 i16WaitForPllRunningCnt; 60 | UINT16 Sync0WdCounter; 61 | UINT16 Sync0WdValue; 62 | BOOL bEscIntEnabled; // indicates that the ESC interrupt is enabled (SM2/3 or SYNC0/1-event), 63 | // will be set in StartInputHandler and reset in StopInputHandler 64 | BOOL b3BufferMode; // indicates that inputs and outputs are running in 3-Buffer-Mode 65 | BOOL bLocalErrorFlag; // contains the information if the application has a local error 66 | UINT16 u16LocalErrorCode; //reason for local error 67 | BOOL bApplEsmPending; //indicates if the local application ESM function need to be called from Al_ConntrolRes (is true if NOERR_INWORK is returned by generic ESM function) 68 | BOOL bEcatWaitForAlControlRes; // contains the information that the state machine waits for an acknowledge 69 | // for the last AL_ControlInd from the application/generic stack 70 | UINT16 nEcatStateTrans; 71 | UINT8 u8EcatErrorLed; 72 | UINT8 u8EcatRunLed; 73 | UINT16 nPdInputSize; // contains the input size (SM3 size), has to be written by the application 74 | UINT16 nPdOutputSize; // contains the output size (SM3 size), has to be written by the application 75 | UINT8 nMaxSyncMan; // contains the maximum number of Sync Manager channels, will be initialized in ECAT_Main 76 | UINT8 nAlStatus; // contains the actual AL Status, will be written in AL_ControlInd 77 | UINT16 EcatWdValue; // contains the value of the watchdog in 100us, will be written in StartInputHandler. 78 | UINT16 nEscAddrOutputData; // contains the SM2 address 79 | UINT16 nEscAddrInputData; // contains the SM3 address 80 | void SetALStatus(UINT8 alStatus, UINT16 alStatusCode); 81 | void AL_ControlInd(UINT8 alControl, UINT16 alStatusCode); 82 | void DC_CheckWatchdog(void); 83 | void CheckIfEcatError(void); 84 | void ECAT_Init(void); 85 | void ECAT_StateChange(UINT8 alStatus, UINT16 alStatusCode); 86 | void ECAT_Main(void); 87 | 88 | //===============================ECATAPPL_H==================================== 89 | BOOL bEcatWaitForInputUpdate; 90 | BOOL bEtherCATRunLed; 91 | BOOL bEtherCATErrorLed; 92 | BOOL bRunApplication; 93 | public: 94 | UINT16 MainInit(void); 95 | void MainLoop(void); 96 | void PDI_Isr(void); 97 | void Sync0_Isr(void); 98 | private: 99 | void ECAT_CheckTimer(void); 100 | void ECAT_Application(void); 101 | void PDO_ResetOutputs(void); 102 | void PDO_ReadInputs(void); 103 | void PDO_InputMapping(void); 104 | void PDO_OutputMapping(void); 105 | void ECAT_SetLedIndication(void); 106 | void CalcSMCycleTime(void); 107 | 108 | //========================MAILBOX_H============================== 109 | BOOL bReceiveMbxIsLocked; 110 | BOOL bSendMbxIsFull; 111 | BOOL bMbxRunning; 112 | BOOL bMbxRepeatToggle; 113 | UINT16 u16SendMbxSize; 114 | UINT16 u16ReceiveMbxSize; 115 | UINT16 u16EscAddrReceiveMbx; 116 | UINT16 u16EscAddrSendMbx; 117 | UINT8 u8MbxWriteCounter; 118 | UINT8 u8MbxReadCounter; 119 | TMBX MBXMEM asMbx[2]; 120 | UINT8 u8MailboxSendReqStored; 121 | TMBX MBXMEM * psWriteMbx; 122 | TMBX MBXMEM * psReadMbx; 123 | TMBX MBXMEM * psRepeatMbx; 124 | TMBX MBXMEM * psStoreMbx; 125 | TMBXQUEUE MBXMEM sMbxSendQueue; 126 | TMBXQUEUE MBXMEM sMbxReceiveQueue; 127 | void MBX_Init(void); 128 | UINT8 MBX_StartMailboxHandler(void); 129 | void MBX_StopMailboxHandler(void); 130 | void MBX_MailboxWriteInd(TMBX MBXMEM *pMbx); 131 | void MBX_MailboxReadInd(void); 132 | void MBX_MailboxRepeatReq(void); 133 | UINT8 MBX_MailboxSendReq(TMBX MBXMEM * pMbx, UINT8 flags); 134 | void MBX_CheckAndCopyMailbox(void); 135 | UINT8 MBX_CopyToSendMailbox(TMBX MBXMEM *pMbx); 136 | void MBX_Main(void); 137 | 138 | 139 | //==========================OBJ_H================================== 140 | TCYCLEDIAG sCycleDiag; 141 | OBJCONST TOBJECT OBJMEM * OBJ_GetObjectHandle( UINT16 index ); 142 | UINT16 OBJ_GetObjectLength( UINT16 index, UINT8 subindex, OBJCONST TOBJECT OBJMEM * pObjEntry, UINT8 bCompleteAccess); 143 | UINT16 OBJ_GetNoOfObjects(UINT8 listType); 144 | UINT16 OBJ_GetObjectList(UINT16 listType, UINT16 *pIndex, UINT16 size, UINT16 MBXMEM *pData,UINT8 *pAbort); 145 | UINT16 OBJ_GetDesc( UINT16 index, UINT8 subindex, OBJCONST TOBJECT OBJMEM * pObjEntry, UINT16 MBXMEM * pData ); 146 | OBJCONST TSDOINFOENTRYDESC OBJMEM * OBJ_GetEntryDesc(OBJCONST TOBJECT OBJMEM * pObjEntry, UINT8 Subindex); 147 | OBJCONST TSDOINFOOBJDESC OBJMEM * OBJ_GetObjDesc(OBJCONST TOBJECT OBJMEM * pObjEntry); 148 | UINT16 OBJ_GetEntryOffset(UINT8 subindex, OBJCONST TOBJECT OBJMEM * pObjEntry); 149 | UINT8 OBJ_Read( UINT16 index, UINT8 subindex, UINT32 completeSize, OBJCONST TOBJECT OBJMEM * pObjEntry, UINT16 MBXMEM * pData, UINT8 bCompleteAccess ); 150 | UINT8 OBJ_Write( UINT16 index, UINT8 subindex, UINT32 completeSize, OBJCONST TOBJECT OBJMEM * pObjEntry, UINT16 MBXMEM * pData, UINT8 bCompleteAccess ); 151 | void COE_WriteBackupEntry(UINT8 subindex, OBJCONST TOBJECT OBJMEM * pObjEntry); 152 | public: 153 | TSYNCMANPAR MBXMEM sSyncManOutPar; 154 | TSYNCMANPAR MBXMEM sSyncManInPar; 155 | private: 156 | //========================ECATCOE_H================================ 157 | TMBX MBXMEM * VARMEM pCoeSendStored; /* if the mailbox service could not be sent (or stored), 158 | the CoE service will be stored in this variable 159 | and will be sent automatically from the mailbox handler 160 | (COE_ContinueInd) when the send mailbox will be read 161 | the next time from the master */ 162 | void COE_Init(void); 163 | UINT8 COE_ServiceInd(TCOEMBX MBXMEM *pCoeMbx); 164 | void COE_ContinueInd(TMBX MBXMEM * pMbx); 165 | 166 | //======================SDOSERVE_H================================= 167 | //Member variables 168 | /* ECATCHANGE_START(V5.01) SDO6*/ 169 | #define SDO_PENDING_WRITE 0x1 170 | #define SDO_PENDING_SEG_WRITE 0x2 171 | #define SDO_PENDING_READ 0x3 172 | #define SDO_PENDING_SEG_READ 0x4 173 | UINT8 u8PendingSdo; 174 | BOOL bStoreCompleteAccess; 175 | UINT8 u8StoreSubindex; 176 | UINT16 u16StoreIndex; 177 | UINT32 u32StoreDataSize; 178 | UINT16 MBXMEM *pStoreData; 179 | UINT8 (* pSdoPendFunc)( UINT16 Index, UINT8 Subindex, UINT32 Size, UINT16 MBXMEM * pData, UINT8 bCompleteAccess ); 180 | /* ECATCHANGE_END(V5.01) SDO6*/ 181 | UINT16 VARMEM * VARMEM pSdoSegData; 182 | #define SDO_INFO_HEADER_BYTE_SIZE ((SIZEOF_SDOINFOSTRUCT)+(SIZEOF_SDOINFOLISTHEAD)) 183 | MEM_ADDR VARMEM aSdoInfoHeader[GET_MEM_SIZE(SDO_INFO_HEADER_BYTE_SIZE)]; 184 | UINT16 VARMEM nSdoInfoFragmentsLeft; 185 | //Functions 186 | UINT8 SDOS_SdoInfoInd(TSDOINFORMATION MBXMEM *pSdoInfoInd); 187 | UINT8 SDOS_SdoInd(TINITSDOMBX MBXMEM *pSdoMbx); 188 | void SDOS_SdoRes(UINT8 abort, UINT32 objLength, UINT16 MBXMEM *pData); 189 | void SdoRes(UINT8 abort, UINT8 command, UINT8 completeAccess, UINT16 dataSize, UINT32 objLength, TINITSDOMBX MBXMEM *pSdoRes); 190 | //=======================COEAPPL_H================================= 191 | OBJCONST TOBJECT OBJMEM * COE_GetObjectDictionary(void); 192 | void COE_ObjInit(void); 193 | void COE_Main(void); 194 | UINT16 COE_ObjDictionaryInit(void); 195 | UINT16 COE_AddObjectToDic(TOBJECT OBJMEM * pNewObjEntry); 196 | void COE_RemoveDicEntry(UINT16 index); 197 | void COE_ClearObjDictionary(void); 198 | UINT16 AddObjectsToObjDictionary(TOBJECT OBJMEM * pObjEntry); 199 | 200 | //====================HARDWARE_H=================================== 201 | #define ESC_RD 0x02 ///< read access to ESC 202 | #define ESC_WR 0x04 ///< write access to ESC 203 | unsigned long lastTime; 204 | unsigned long inline HW_GetTimer() 205 | { return micros()-lastTime; } //auto overflow 206 | void inline HW_ClearTimer() 207 | { lastTime = micros(); } 208 | 209 | public: 210 | UINT8 HW_Init(void); 211 | 212 | private: 213 | void HW_Release(void); 214 | UINT16 HW_GetALEventRegister(void); 215 | UINT16 HW_GetALEventRegister_Isr(void); 216 | void HW_ResetALEventMask(UINT16 intMask); 217 | void HW_SetALEventMask(UINT16 intMask); 218 | void HW_SetLed(UINT8 RunLed,UINT8 ErrLed); 219 | 220 | void HW_EscRead( MEM_ADDR * pData, UINT16 Address, UINT16 Len ); 221 | void inline HW_EscReadWord( UINT16& WordValue, UINT16 Address) 222 | { HW_EscRead(((MEM_ADDR *)&(WordValue)),((UINT16)(Address)),2); } 223 | void inline HW_EscReadDWord( UINT32& DWordValue, UINT16 Address) 224 | { HW_EscRead(((MEM_ADDR *)&(DWordValue)),((UINT16)(Address)),4); } 225 | void inline HW_EscReadMbxMem(MEM_ADDR *pData, UINT16 Address,UINT16 Len) 226 | { HW_EscRead(((MEM_ADDR *)(pData)),((UINT16)(Address)),(Len)); } 227 | 228 | 229 | void HW_EscReadIsr( MEM_ADDR *pData, UINT16 Address, UINT16 Len ); 230 | void inline HW_EscReadWordIsr(UINT16 &WordValue, UINT16 Address) 231 | { HW_EscReadIsr(((MEM_ADDR *)&(WordValue)),((UINT16)(Address)),2); } 232 | void inline HW_EscReadDWordIsr(UINT32 &DWordValue, UINT16 Address) 233 | { HW_EscReadIsr(((MEM_ADDR *)&(DWordValue)),((UINT16)(Address)),4); } 234 | 235 | void HW_EscWrite( MEM_ADDR *pData, UINT16 Address, UINT16 Len ); 236 | void inline HW_EscWriteWord(UINT16 WordValue, UINT16 Address) 237 | { HW_EscWrite(((MEM_ADDR *)&(WordValue)),((UINT16)(Address)),2); } 238 | void inline HW_EscWriteDWord(UINT32 DWordValue, UINT16 Address) 239 | { HW_EscWrite(((MEM_ADDR *)&(DWordValue)),((UINT16)(Address)),4); } 240 | void inline HW_EscWriteMbxMem(MEM_ADDR *pData,UINT16 Address,UINT16 Len) 241 | { HW_EscWrite(((MEM_ADDR *)(pData)),((UINT16)(Address)),(Len)); } 242 | 243 | void HW_EscWriteIsr( MEM_ADDR *pData, UINT16 Address, UINT16 Len ); 244 | void inline HW_EscWriteWordIsr(UINT16 WordValue, UINT16 Address) 245 | { HW_EscWriteIsr(((MEM_ADDR *)&(WordValue)),((UINT16)(Address)),2); } 246 | void inline HW_EscWriteDWordIsr(UINT32 DWordValue, UINT16 Address) 247 | { HW_EscWriteIsr(((MEM_ADDR *)&(DWordValue)),((UINT16)(Address)),4); } 248 | 249 | void HW_DisableSyncManChannel(UINT8 channel); 250 | void HW_EnableSyncManChannel(UINT8 channel); 251 | TSYNCMAN ESCMEM *HW_GetSyncMan(UINT8 channel); 252 | 253 | //=====================COEAPPL_C========================== 254 | TOBJECT OBJMEM * ObjDicList; 255 | //====================ECATAPPL_C========================== 256 | /*variables only required to calculate values for SM Synchronisation objects (0x1C3x)*/ 257 | UINT16 u16BusCycleCntMs; //used to calculate the bus cycle time in Ms 258 | UINT32 StartTimerCnt; //variable to store the timer register value when get cycle time was triggered 259 | BOOL bCycleTimeMeasurementStarted; // indicates if the bus cycle measurement is started 260 | #if MAX_PD_OUTPUT_SIZE > 0 261 | UINT16 aPdOutputData[(MAX_PD_OUTPUT_SIZE>>1)]; 262 | #endif 263 | #if MAX_PD_INPUT_SIZE > 0 264 | UINT16 aPdInputData[(MAX_PD_INPUT_SIZE>>1)]; 265 | #endif 266 | //===================ECATSLV_C============================ 267 | //VARVOLATILE UINT16 u16dummy; 268 | UINT16 u16dummy; 269 | UINT16 u16ALEventMask; 270 | UINT8 CheckSmSettings(UINT8 maxChannel); 271 | UINT16 StartInputHandler(void); 272 | UINT16 StartOutputHandler(void); 273 | void StopOutputHandler(void); 274 | void StopInputHandler(void); 275 | void AL_ControlRes(void); 276 | //===================OBJDEF_C============================= 277 | static const UINT16 cBitMask[16]; 278 | static CHAR OBJMEM aSubindexDesc[13]; 279 | void OBJ_CopyNumberToString(UCHAR MBXMEM *pStr, UINT8 Number); 280 | //==================MAILBOX_C============================= 281 | UINT8 PutInMbxQueue(TMBX MBXMEM * pMbx, TMBXQUEUE MBXMEM * pQueue); 282 | TMBX MBXMEM * GetOutOfMbxQueue(TMBXQUEUE MBXMEM * pQueue); 283 | UINT8 MailboxServiceInd(TMBX MBXMEM *pMbx); 284 | //===================SDOSERVE_C=========================== 285 | static const UINT32 MBXMEM cAbortCode[]; 286 | UINT16 VARMEM nSdoInfoIndex; 287 | OBJCONST TOBJECT OBJMEM * VARMEM pSdoInfoObjEntry; 288 | 289 | TINITSDOMBX MBXMEM * VARMEM pSdoResStored; 290 | static BOOL VARMEM bSdoInWork; 291 | 292 | UINT8 VARMEM nSdoSegService; 293 | UINT8 VARMEM bSdoSegFollows; 294 | UINT8 VARMEM bSdoSegAccess; 295 | UINT16 VARMEM nSdoSegIndex; 296 | UINT8 VARMEM nSdoSegSubindex; 297 | UINT32 VARMEM nSdoSegBytesToHandle; 298 | UINT8 VARMEM bSdoSegLastToggle; 299 | UINT32 VARMEM nSdoSegCompleteSize; 300 | OBJCONST TOBJECT OBJMEM * VARMEM pSdoSegObjEntry; 301 | UINT8 SdoDownloadSegmentInd( TDOWNLOADSDOSEGREQMBX MBXMEM * pSdoInd ); 302 | UINT8 SdoUploadSegmentInd( TUPLOADSDOSEGREQMBX MBXMEM * pSdoInd ); 303 | //===================HARDWARE_C============================ 304 | UALEVENT EscALEvent; //contains the content of the ALEvent register (0x220), this variable is updated on each Access to the Esc 305 | UINT16 nAlEventMask; //current ALEventMask (content of register 0x204:0x205) 306 | TSYNCMAN TmpSyncMan; 307 | void GetInterruptRegister(void); 308 | void ISR_GetInterruptRegister(void); 309 | void AddressingEsc( UINT16 Address, UINT8 Command ); 310 | void ISR_AddressingEsc( UINT16 Address, UINT8 Command ); 311 | //==============Application handlers======================== 312 | void APPL_Application(void); 313 | 314 | void APPL_AckErrorInd(UINT16 stateTrans); 315 | UINT16 APPL_StartMailboxHandler(void); 316 | UINT16 APPL_StopMailboxHandler(void); 317 | UINT16 APPL_StartInputHandler(UINT16 *pIntMask); 318 | UINT16 APPL_StopInputHandler(void); 319 | UINT16 APPL_StartOutputHandler(void); 320 | UINT16 APPL_StopOutputHandler(void); 321 | 322 | UINT16 APPL_GenerateMapping(UINT16 *pInputSize,UINT16 *pOutputSize); 323 | void APPL_InputMapping(UINT16* pData); 324 | void APPL_OutputMapping(UINT16* pData); 325 | }; 326 | 327 | extern Ethercat ethercat; 328 | 329 | 330 | #endif 331 | -------------------------------------------------------------------------------- /libraries/arducat/coeappl.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | \defgroup coeappl coeappl.c: CoE (CAN application layer over EtherCAT) - application interface 3 | \brief This file contains an example for CoE services and the CoE object dictionary\n 4 | \brief Changes to version V5.0: 5 | \brief V5.01 APPL3: Include library demo application 6 | \brief V5.01 COE1: Remove alignment entry (SI33) from objects 0x1C32/0x1C33 7 | \brief V5.01 EL9800 1: Read/Write functions for setting object 0x8020 moved to el9800appl.c 8 | \brief V5.01 EL9800 2: Add TxPdo Parameter object 0x1802 9 | \brief V5.01 SDO6: Update SDO response interface handling. (used if the object access function returns "ABORTIDX_WORKING" and SDO_RES_INTERFACE is active) 10 | \brief Changes to version V4.40: 11 | \brief V5.0 TEST1: Add test application. See Application Note ET9300 for more details. 12 | \brief V5.0 COE1: Add reserved Subindex7 to object 0x1C32 and 0x1C33. 13 | \brief V5.0 COE4: Create object dictionary on device startup. 14 | \brief V5.0 COE6: Update entry descriptions for object with the code ARRAY. 15 | \brief V5.0 ECAT2: Reference to application specific object dictionary is set to "ApplicationObjDic". 16 | \brief Changes to version V4.30:\n 17 | \brief V4.40 COE 6: change dynamic object dictionary handling 18 | \brief V4.40 CiA402 2: enable freerun Sync Mode for CiA402 Device (The motion controller application will only be triggered if DC Synchronisation is disabled) 19 | \brief V4.40 COE5: move identification and synchronisation object values to ecat_def.h 20 | \brief V4.40 DIAG1: Add diagnosis object 0x10F3 21 | \brief V4.40 OBJ3: Update the global object dictionary pointer if a new list head is added 22 | \brief V4.40 COE4: Prevent access NULL pointer 23 | \brief V4.40 SYNC1: Initialize 0x1C3x objects 24 | \brief V4.40 HW1: Add (adapt) objects for FC1100 support 25 | \brief Changes to version V4.20:\n 26 | \brief V4.30 OBJ 3: Handle object dictionary in double link list 27 | \brief V4.30 OBJ 2: General EtherCAT device objects are moved from chnappl.h to coeappl.c 28 | \brief V4.30 SYNC: set 0x1C32:05; 0x1C33:05 (minCyleTime) to MIN_PD_CYCLE_TIME 29 | (should be set dynamic if required) 30 | \brief V4.30 PDO: rename PDO specific function calls 31 | (PDO specific functions are moved to ecatappl.c) 32 | \brief V4.20 PIC24: Add EL9800_4 (PIC24) required source code 33 | \brief V4.11 ECAT 1: C166: MinDelayTime renamed in CalcAndCopyTime\n 34 | \brief Changes to version V4.07:\n 35 | \brief V4.10 ECAT 1: To save and restore arrays in the object directory. 36 | \brief The code to get the size of an array entry was changed.\n 37 | \brief V4.10 COE 1: The SM-Parameter were extended\n 38 | \brief Changes to version V4.06:\n 39 | \brief V4.07 COEAPPL 1: The PDO numbers were adapted according top the modular device profile, 40 | \brief the PDO numbers increment now with every module (in chnappl.h) 41 | \brief V4.07 COEAPPL 2: The example is working for the NIOS with the evaluation board DBC2C20 V1.2 42 | \brief which is available by Altera\n 43 | \brief V4.07 ECAT 1: The sources for SPI and MCI were merged (in ecat_def.h 44 | \brief set the switch MCI_HW to 1 when using the MCI, 45 | \brief set the switch SPI_HW to 1 when using the SPI\n 46 | \brief Changes to version V4.03:\n 47 | \brief V4.04 SDO 1: The SDO interface was changed in that way that a SDO response 48 | \brief could be sent by the application to a later time. In that case 49 | \brief the functions OBJ_Read and OBJ_Write shall return the value 50 | \brief ABORTIDX_WORKING. To send the SDO response the new function SDOS_SdoRes 51 | \brief has to be called by the application. While waiting for the call 52 | \brief of SDOS_SdoRes the SDO interface will answer to another SDO request 53 | \brief with the error MBXERR_SERVICEINWORK in the mailbox protocol. 54 | \brief In this example the reading and writing of object 0x8020 is delayed, 55 | \brief the function SDOS_SdoRes will be called from COE_Main. 56 | \brief Changes to version V4.00:\n 57 | \brief V4.01 COEAPPL 1: The object dictionary and the mapping functions are now defined 58 | \brief in chnappl.h (chnappl.inc made problems with some developing tools)\n 59 | \brief Changes to version V3.20:\n 60 | \brief V4.00 COEAPPL 1: The object dictionary and the mapping functions are now defined 61 | \brief in chnappl.c and were adapted to the modular device profile\n 62 | \brief V4.00 COEAPPL 2: The handling of backup parameters was included according to 63 | \brief the EtherCAT Guidelines and Protocol Enhancements Specification\n 64 | \brief V4.00 COEAPPL 3: The handling of the TxPDO Toggle was included according to 65 | \brief the EtherCAT Guidelines and Protocol Enhancements Specification\n 66 | \brief V4.00 COEAPPL 4: The handling of the TxPDO State was included according to 67 | \brief the EtherCAT Guidelines and Protocol Enhancements Specification\n 68 | \brief V4.00 ECAT 1: The handling of the Sync Manager Parameter was included according to 69 | \brief the EtherCAT Guidelines and Protocol Enhancements Specification\n 70 | 71 | 72 | \version 5.01 73 | */ 74 | 75 | //--------------------------------------------------------------------------------------- 76 | /** 77 | \ingroup coeappl 78 | \file coeappl.c 79 | \brief Implementation. 80 | */ 81 | //--------------------------------------------------------------------------------------- 82 | 83 | /*----------------------------------------------------------------------------------------- 84 | ------ 85 | ------ Includes 86 | ------ 87 | -----------------------------------------------------------------------------------------*/ 88 | 89 | //#include "ecat_def.h" 90 | 91 | //#include "objdef.h" 92 | 93 | #include "ethercat.h" 94 | 95 | 96 | /*----------------------------------------------------------------------------------------- 97 | ------ 98 | ------ Backup Parameter 99 | ------ 100 | -----------------------------------------------------------------------------------------*/ 101 | /* 102 | extern const UINT32 VENDOR_ID; 103 | extern const UINT32 PRODUCT_CODE; 104 | extern const UINT32 REVISION_NUMBER; 105 | extern const UINT32 DEVICE_PROFILE_TYPE; 106 | extern const char DEVICE_NAME[]; 107 | extern const UINT8 DEVICE_NAME_LEN; 108 | extern const char DEVICE_HW_VERSION[]; 109 | extern const UINT8 DEVICE_HW_VERSION_LEN; 110 | extern const char DEVICE_SW_VERSION[]; 111 | extern const UINT8 DEVICE_SW_VERSION_LEN; 112 | */ 113 | extern TOBJECT OBJMEM GenObjDic[]; 114 | extern TOBJECT OBJMEM ApplicationObjDic[]; 115 | 116 | 117 | /*----------------------------------------------------------------------------------------- 118 | ------ 119 | ------ Functions 120 | ------ 121 | -----------------------------------------------------------------------------------------*/ 122 | /** 123 | \addtogroup coeappl 124 | @{ 125 | */ 126 | 127 | 128 | ///////////////////////////////////////////////////////////////////////////////////////// 129 | /** 130 | 131 | \brief returns the pointer to the object dictionary 132 | *//////////////////////////////////////////////////////////////////////////////////////// 133 | 134 | OBJCONST TOBJECT OBJMEM * Ethercat::COE_GetObjectDictionary(void) 135 | { 136 | return (OBJCONST TOBJECT OBJMEM *) ObjDicList; 137 | } 138 | 139 | 140 | ///////////////////////////////////////////////////////////////////////////////////////// 141 | /** 142 | \brief This function initialize the several objects 143 | *//////////////////////////////////////////////////////////////////////////////////////// 144 | 145 | void Ethercat::COE_ObjInit(void) 146 | { 147 | 148 | /* initialize the Sync Manager Output parameter object 0x1C32 */ 149 | 150 | sSyncManOutPar.subindex0 = 32; 151 | /* 152 | subindex 1 contains the actual synchronization mode, it could be written 153 | from the master to switch between ECAT FreeRun and ECAT Synchron Mode 154 | if the slave supports both modes, 155 | in DC mode (selected by the DC registers) this value will be overwritten 156 | with SYNCTYPE_DCSYNC0 or SYNCTYPE_DCSYNC1 */ 157 | /*default mode is ECAT Synchron Mode */ 158 | sSyncManOutPar.u16SyncType = SYNCTYPE_SYNCHRON; 159 | /* subindex 2 contains the cycle time of the application, 160 | in ECAT FreeRun mode it could be used for a timer interrupt to run the application, 161 | in ECAT Synchron mode it could be written from the master with its local cycle time 162 | that the slave can check if this cycle time is supported, 163 | in DC Mode this value will be overwritten with the DC cycle time register */ 164 | sSyncManOutPar.u32CycleTime = 0; 165 | /* only for DC Mode important: the subindex 3 contains the time shift between the 166 | SYNC0 (SYNC1) signal and when the outputs are put to the hardware to allow the 167 | master a very exactly calculation of delay times 168 | (in this example we will make an online measurement in the ESC Interrupt Routine) */ 169 | sSyncManOutPar.u32ShiftTime = 0; 170 | /* the subindex 4 contains the supported synchronization types */ 171 | sSyncManOutPar.u16SyncTypesSupported = SYNCTYPE_TIMESVARIABLE /* the execution times depend on the connected modules */ 172 | | SYNCTYPE_FREERUNSUPP /* ECAT FreeRun Mode is supported */ 173 | | SYNCTYPE_SYNCHRONSUPP /* ECAT Synchron Mode is supported */ 174 | | SYNCTYPE_DCSYNC0SUPP /* DC Sync0 Mode is supported */ 175 | ; 176 | /* subindex 5 contains the minimum cycle time the slave is able to support, 177 | will be calculated dynamically because it depends on the connected modules 178 | (in this example we will make an online measurement in the ESC Interrupt Routine). 179 | For the sample application this value is set to MIN_PD_CYCLE_TIME */ 180 | sSyncManOutPar.u32MinCycleTime = MIN_PD_CYCLE_TIME; 181 | /* only for DC Mode important: subindex 6 contains the minimum delay time the slave 182 | needs after receiving the SM2-event before the SYNC0(SYNC1) can be received without delays 183 | will be calculated dynamically because it depends on the connected modules 184 | (in this example we will make an online measurement in the ESC Interrupt Routine) */ 185 | sSyncManOutPar.u32CalcAndCopyTime = (PD_OUTPUT_CALC_AND_COPY_TIME); 186 | 187 | /*subindex 8: trigger cycle time measurement*/ 188 | sSyncManOutPar.u16GetCycleTime = 0; 189 | 190 | /*subindex 9: time from start driving outputs until outputs are valid*/ 191 | sSyncManOutPar.u32DelayTime = (PD_OUTPUT_DELAY_TIME); 192 | 193 | /*subindex 32: indicates if a synchronisation error has occurred*/ 194 | sSyncManOutPar.u16SyncError = 0; 195 | 196 | /* initialize the Sync Manager Input parameter object 0x1C33 */ 197 | sSyncManInPar.subindex0 = 32; 198 | /* default mode is ECAT Synchron Mode, if output size > 0 the inputs are updated with the SM2-event */ 199 | sSyncManInPar.u16SyncType = SYNCTYPE_SM2INT; 200 | /* subindex 2: same as 0x1C32:02 */ 201 | sSyncManInPar.u32CycleTime = sSyncManOutPar.u32CycleTime; 202 | /* only for DC Mode important: subindex 3 contains the time shift between the 203 | SYNC0 (SYNC1) signal and when the inputs are got to the hardware to allow the 204 | master a very exactly calculation of delay times, 205 | will be calculated dynamically because it depends on the connected modules 206 | (in this example we will make an online measurement in the ESC Interrupt Routine) */ 207 | sSyncManInPar.u32ShiftTime = 0; 208 | /* subindex 4: same as 0x1C32:04 */ 209 | sSyncManInPar.u16SyncTypesSupported = sSyncManOutPar.u16SyncTypesSupported; 210 | /* subindex 5: same as 0x1C32:05 */ 211 | sSyncManInPar.u32MinCycleTime = sSyncManOutPar.u32MinCycleTime; 212 | /* subindex 6: delay read inputs, calculation and copy to SM buffer*/ 213 | sSyncManInPar.u32CalcAndCopyTime = (PD_INPUT_CALC_AND_COPY_TIME); 214 | /*subindex 8: trigger cycle time measurement*/ 215 | sSyncManInPar.u16GetCycleTime = 0; 216 | /*subindex 9: delay to prepare input latch*/ 217 | sSyncManInPar.u32DelayTime = (PD_INPUT_DELAY_TIME); 218 | 219 | /*subindex 32: incremented if a synchronisation error has occurred*/ 220 | sSyncManInPar.u16SyncError = 0; 221 | 222 | { 223 | UINT16 result = COE_ObjDictionaryInit(); 224 | if(result != 0) 225 | { 226 | /*clear already linked objects*/ 227 | COE_ClearObjDictionary(); 228 | } 229 | } 230 | 231 | /* ECATCHANGE_START(V5.01) SDO6*/ 232 | u8PendingSdo = 0; 233 | bStoreCompleteAccess = FALSE; 234 | u16StoreIndex = 0; 235 | u8StoreSubindex = 0; 236 | u32StoreDataSize = 0; 237 | pStoreData = NULL; 238 | pSdoPendFunc = NULL; 239 | /* ECATCHANGE_END(V5.01) SDO6*/ 240 | 241 | pSdoSegData = NULL; 242 | } 243 | 244 | ///////////////////////////////////////////////////////////////////////////////////////// 245 | /** 246 | \return 0 object successful added to object dictionary 247 | ALSTATUSCODE_XX add object failed 248 | 249 | \brief This function adds an object to the object dictionary 250 | *//////////////////////////////////////////////////////////////////////////////////////// 251 | UINT16 Ethercat::COE_AddObjectToDic(TOBJECT OBJMEM * pNewObjEntry) 252 | { 253 | if(pNewObjEntry != NULL) 254 | { 255 | if(ObjDicList == NULL) 256 | { 257 | /* Object dictionary is empty */ 258 | ObjDicList = pNewObjEntry; 259 | ObjDicList->pNext = NULL; 260 | ObjDicList->pPrev = NULL; 261 | return 0; 262 | } 263 | else if(ObjDicList->Index > pNewObjEntry->Index) 264 | { 265 | /*insert new object dictionary head*/ 266 | pNewObjEntry->pPrev = NULL; 267 | pNewObjEntry->pNext = ObjDicList; 268 | ObjDicList->pPrev = pNewObjEntry; 269 | ObjDicList = pNewObjEntry; 270 | return 0; 271 | } 272 | else 273 | { 274 | TOBJECT OBJMEM * pDicEntry = ObjDicList; 275 | while(pDicEntry != NULL) 276 | { 277 | if(pDicEntry->Index == pNewObjEntry->Index) 278 | { 279 | /*object already exists in object dictionary*/ 280 | return ALSTATUSCODE_UNSPECIFIEDERROR; 281 | } 282 | else if(pDicEntry->Index > pNewObjEntry->Index) 283 | { 284 | pNewObjEntry->pPrev = pDicEntry->pPrev; 285 | pNewObjEntry->pNext = pDicEntry; 286 | 287 | if(pDicEntry->pPrev != NULL) 288 | pDicEntry->pPrev->pNext = pNewObjEntry; 289 | 290 | pDicEntry->pPrev = pNewObjEntry; 291 | 292 | return 0; 293 | } 294 | else if(pDicEntry->pNext == NULL) 295 | { 296 | /*Last entry reached => add object to list tail*/ 297 | pDicEntry->pNext = pNewObjEntry; 298 | pNewObjEntry->pPrev = pDicEntry; 299 | pNewObjEntry->pNext = NULL; 300 | return 0; 301 | } 302 | pDicEntry = pDicEntry->pNext; 303 | } 304 | } 305 | } 306 | return ALSTATUSCODE_UNSPECIFIEDERROR; 307 | } 308 | ///////////////////////////////////////////////////////////////////////////////////////// 309 | /** 310 | 311 | \brief This function adds an object to the object dictionary 312 | *//////////////////////////////////////////////////////////////////////////////////////// 313 | void Ethercat::COE_RemoveDicEntry(UINT16 index) 314 | { 315 | TOBJECT OBJMEM * pDicEntry = ObjDicList; 316 | 317 | while(pDicEntry != NULL) 318 | { 319 | if(pDicEntry->Index == index) 320 | { 321 | TOBJECT OBJMEM *pPrevEntry = pDicEntry->pPrev; 322 | TOBJECT OBJMEM *pNextEntry = pDicEntry->pNext; 323 | 324 | if(pPrevEntry != NULL) 325 | pPrevEntry->pNext = pNextEntry; 326 | 327 | if(pNextEntry != NULL) 328 | pNextEntry->pPrev = pPrevEntry; 329 | 330 | pDicEntry->pPrev = NULL; 331 | pDicEntry->pNext = NULL; 332 | /*Update Object dictionary pointer if list head was removed*/ 333 | if(pDicEntry->Index == ObjDicList->Index) 334 | { 335 | ObjDicList = pNextEntry; 336 | } 337 | return; 338 | } 339 | 340 | pDicEntry = pDicEntry->pNext; 341 | } 342 | } 343 | ///////////////////////////////////////////////////////////////////////////////////////// 344 | /** 345 | 346 | \brief This function clear the object dictionary 347 | *//////////////////////////////////////////////////////////////////////////////////////// 348 | void Ethercat::COE_ClearObjDictionary(void) 349 | { 350 | TOBJECT OBJMEM * pObjEntry = (TOBJECT OBJMEM *) ObjDicList; 351 | UINT16 Index = 0; 352 | 353 | while(pObjEntry != NULL) 354 | { 355 | Index = pObjEntry->Index; 356 | pObjEntry = pObjEntry->pNext; 357 | 358 | COE_RemoveDicEntry(Index); 359 | } 360 | ObjDicList = NULL; 361 | } 362 | 363 | 364 | UINT16 Ethercat::AddObjectsToObjDictionary(TOBJECT OBJMEM * pObjEntry) 365 | { 366 | UINT16 result = 0; 367 | TOBJECT OBJMEM * pEntry = (TOBJECT OBJMEM *)pObjEntry; 368 | 369 | while(pEntry->Index != 0xFFFF) 370 | { 371 | result = COE_AddObjectToDic(pEntry); 372 | 373 | if(result != 0) 374 | return result; 375 | 376 | pEntry++; 377 | } 378 | 379 | return result; 380 | 381 | } 382 | ///////////////////////////////////////////////////////////////////////////////////////// 383 | /** 384 | \return 0 object dictionary created successful 385 | ALSTATUSCODE_XX create object dictionary failed 386 | 387 | \brief This function initialize the object dictionary 388 | *//////////////////////////////////////////////////////////////////////////////////////// 389 | UINT16 Ethercat::COE_ObjDictionaryInit(void) 390 | { 391 | UINT16 result = 0; 392 | 393 | /*Reset object dictionary pointer*/ 394 | ObjDicList = NULL; 395 | 396 | result = AddObjectsToObjDictionary((TOBJECT OBJMEM *) GenObjDic); 397 | 398 | if(result != 0) 399 | return result; 400 | if(ApplicationObjDic != NULL) 401 | { 402 | result = AddObjectsToObjDictionary((TOBJECT OBJMEM *) ApplicationObjDic); 403 | } 404 | 405 | return result; 406 | } 407 | 408 | 409 | ///////////////////////////////////////////////////////////////////////////////////////// 410 | /** 411 | 412 | \brief is called for background calculations which should not influence the 413 | ECAT_Application in synchronous modes 414 | *//////////////////////////////////////////////////////////////////////////////////////// 415 | 416 | void Ethercat::COE_Main(void) 417 | { 418 | /* ECATCHANGE_START(V5.01) SDO6*/ 419 | UINT8 abort = 0; 420 | if(pSdoPendFunc != NULL) 421 | { 422 | abort = pSdoPendFunc(u16StoreIndex,u8StoreSubindex,u32StoreDataSize,pStoreData,bStoreCompleteAccess); 423 | 424 | if(abort != ABORTIDX_WORKING) 425 | { 426 | switch(u8PendingSdo) 427 | { 428 | case SDO_PENDING_SEG_WRITE: 429 | if(pSdoSegData) 430 | { 431 | /* the allocated buffer can be released */ 432 | FREEMEM( (UINT16 VARMEM *) pSdoSegData ); 433 | pSdoSegData = NULL; 434 | } 435 | case SDO_PENDING_WRITE: 436 | /*send SDO Download Response*/ 437 | SDOS_SdoRes(abort, 0, NULL); 438 | break; 439 | 440 | case SDO_PENDING_SEG_READ: 441 | case SDO_PENDING_READ: 442 | /* send SDO upload response */ 443 | SDOS_SdoRes(abort, u32StoreDataSize, pStoreData); 444 | break; 445 | 446 | } 447 | 448 | u8PendingSdo = 0; 449 | u16StoreIndex = 0; 450 | u8StoreSubindex = 0; 451 | u32StoreDataSize = 0; 452 | pStoreData = NULL; 453 | bStoreCompleteAccess = 0; 454 | pSdoPendFunc = NULL; 455 | } 456 | } 457 | /* ECATCHANGE_END(V5.01) SDO6*/ 458 | } 459 | /** @} */ 460 | 461 | 462 | 463 | -------------------------------------------------------------------------------- /EtherCATSlave/XmlGenerator.py: -------------------------------------------------------------------------------- 1 | import xml.etree.ElementTree as ET 2 | from EthercatType import dictEthercatType 3 | from PDOMapping import GeneratePdoMapping 4 | from PDOMapping import GenerateRxPdoMapping 5 | from PDOMapping import GenerateTxPdoMapping 6 | 7 | 8 | def hexStrToEtg(strHex): 9 | if strHex[0:2] == "0x": 10 | result = "#x"+strHex[2:] 11 | else: 12 | result = strHex 13 | return result 14 | 15 | 16 | def intToHexLsb(intInput): 17 | hexMsb = "%08x"%intInput 18 | hexLsb = hexMsb[6:8]+hexMsb[4:6]+hexMsb[2:4]+hexMsb[0:2] 19 | return hexLsb.upper() 20 | 21 | def searchElementDataType(eDataTypes,strDataType): 22 | for eDataType in eDataTypes: 23 | if eDataType[0].text == strDataType: 24 | return eDataType 25 | 26 | def addDataType(eDataTypes,strDataType): 27 | if searchElementDataType(eDataTypes,strDataType)==None: 28 | eDataType = ET.SubElement(eDataTypes, 'DataType') 29 | eDataTypeName = ET.SubElement(eDataType, 'Name') 30 | eDataTypeName.text = strDataType 31 | eDataTypeNameSize = ET.SubElement(eDataType, 'BitSize') 32 | eDataTypeNameSize.text = str(dictEthercatType[strDataType][2]) 33 | 34 | 35 | def fillDictionaryString(eDataTypes,eObject,strContent): 36 | intStringLength = len(strContent) 37 | strStringType = "STRING("+str(len(strContent))+")" 38 | eObject[2].text = strStringType 39 | if searchElementDataType(eDataTypes,strStringType)==None: 40 | eStringType = ET.SubElement(eDataTypes, 'DataType') 41 | eStringTypeName = ET.SubElement(eStringType, 'Name') 42 | eStringTypeName.text = strStringType 43 | eStringTypeSize = ET.SubElement(eStringType, 'BitSize') 44 | eStringTypeSize.text = str(8*intStringLength) 45 | asciiString = strContent.encode('ascii') 46 | ordString = ("".join(["%02x"%(ord(ch)) for ch in asciiString])).upper() 47 | eObject[4][0].text = ordString 48 | return eDataTypes 49 | 50 | def GeneratePdoObjects(root,listSlaveDict): 51 | eDataTypes = root[1][1][0][5][1][0] 52 | eObjects = root[1][1][0][5][1][1] 53 | eDT1C12ARR = eDataTypes[9] 54 | eDT1C12 = eDataTypes[10] 55 | eDT1C13ARR = eDataTypes[10] 56 | eDT1C13 = eDataTypes[11] 57 | eObj1C12 = eObjects[8] 58 | eObj1C13 = eObjects[9] 59 | #{PdoObj: [(Index,SI,length,intBitOffset)]} 60 | txPdoMapping = GenerateTxPdoMapping(listSlaveDict) 61 | rxPdoMapping = GenerateRxPdoMapping(listSlaveDict) 62 | #Generate SyncManager Assignment 63 | eDT1C12ARR = eDataTypes[9] 64 | eDT1C12 = eDataTypes[10] 65 | eDT1C13ARR = eDataTypes[11] 66 | eDT1C13 = eDataTypes[12] 67 | eObj1C12 = eObjects[8] 68 | eObj1C13 = eObjects[9] 69 | eDT1C12ARR[3][1].text = str(len(rxPdoMapping.keys())) 70 | eDT1C13ARR[3][1].text = str(len(txPdoMapping.keys())) 71 | eDT1C12ARR[2].text = str(len(rxPdoMapping.keys())*16) 72 | eDT1C13ARR[2].text = str(len(txPdoMapping.keys())*16) 73 | eDT1C12[1].text = str(len(rxPdoMapping.keys())*16+16) 74 | eDT1C13[1].text = str(len(txPdoMapping.keys())*16+16) 75 | eDT1C12[3][2].text = str(len(rxPdoMapping.keys())*16) 76 | eDT1C13[3][2].text = str(len(txPdoMapping.keys())*16) 77 | eObj1C12[3].text = eDT1C12[1].text 78 | eObj1C13[3].text = eDT1C13[1].text 79 | eObj1C12[4][0][1][0].text = intToHexLsb(len(rxPdoMapping.keys())) 80 | eObj1C13[4][0][1][0].text = intToHexLsb(len(txPdoMapping.keys())) 81 | intIdx = 1 82 | for strMappingObj in rxPdoMapping.keys(): 83 | eSubItem = ET.SubElement(eObj1C12[4],"SubItem") 84 | eSubName = ET.SubElement(eSubItem, 'Name') 85 | eSubInfo = ET.SubElement(eSubItem, 'Info') 86 | eSubDefault = ET.SubElement(eSubInfo, 'DefaultData') 87 | eSubName.text = "SubIndex "+"%03d"%intIdx 88 | eSubDefault.text = intToHexLsb(int(strMappingObj,0)) 89 | intIdx = intIdx+1 90 | intIdx = 1 91 | for strMappingObj in txPdoMapping.keys(): 92 | eSubItem = ET.SubElement(eObj1C13[4],"SubItem") 93 | eSubName = ET.SubElement(eSubItem, 'Name') 94 | eSubInfo = ET.SubElement(eSubItem, 'Info') 95 | eSubDefault = ET.SubElement(eSubInfo, 'DefaultData') 96 | eSubName.text = "SubIndex "+"%03d"%intIdx 97 | eSubDefault.text = intToHexLsb(int(strMappingObj,0)) 98 | intIdx = intIdx+1 99 | #Generate Pdo Objects 100 | pdoMapping = txPdoMapping 101 | pdoMapping.update(rxPdoMapping) 102 | idxInsert = 7 103 | idxRxPdo = 0 104 | idxTxPdo = 0 105 | for pdoObject in pdoMapping.iteritems(): 106 | #Generate DataType 107 | eDataType = ET.Element("DataType") 108 | eDTName = ET.Element("Name") 109 | eDTName.text = "DT"+pdoObject[0][2:] 110 | eDataType.append(eDTName) 111 | eDTSize = ET.SubElement(eDataType, 'BitSize') 112 | eDTSize.text = str(16+32*len(pdoObject[1])) 113 | eDTSubItem = ET.SubElement(eDataType, 'SubItem') 114 | eDTSubIdx = ET.SubElement(eDTSubItem, 'SubIdx') 115 | eDTSubIdx.text = "0" 116 | eDTSubName = ET.SubElement(eDTSubItem, 'Name') 117 | eDTSubName.text = "SubIndex 000" 118 | eDTSubType = ET.SubElement(eDTSubItem, 'Type') 119 | eDTSubType.text = "UINT" 120 | eDTSubSize = ET.SubElement(eDTSubItem, 'BitSize') 121 | eDTSubSize.text = "16" 122 | eDTBitOffs = ET.SubElement(eDTSubItem, 'BitOffs') 123 | eDTBitOffs.text = "0" 124 | eAccess = ET.Element("Access") 125 | eAccess.text = "ro" 126 | eFlags = ET.Element("Flags") 127 | eFlags.append(eAccess) 128 | eDTSubItem.append(eFlags) 129 | intBitOffs = int(16) 130 | intIdx = int(1) 131 | #Generate Object 132 | eObject = ET.Element("Object") 133 | eObjIndex = ET.SubElement(eObject, 'Index') 134 | eObjIndex.text = hexStrToEtg(pdoObject[0]) 135 | eObjName = ET.SubElement(eObject, 'Name') 136 | if pdoObject[0][0:4]=="0x1A": 137 | eObjName.text = "Input mapping "+str(idxRxPdo) 138 | idxRxPdo = idxRxPdo+1 139 | else: 140 | eObjName.text = "Output mapping "+str(idxTxPdo) 141 | idxTxPdo = idxTxPdo+1 142 | eObjType = ET.SubElement(eObject, 'Type') 143 | eObjType.text = eDTName.text 144 | eObjSize = ET.SubElement(eObject, 'BitSize') 145 | eObjSize.text = eDTSize.text 146 | eObjInfo = ET.SubElement(eObject, 'Info') 147 | eSubItem = ET.SubElement(eObjInfo, 'SubItem') 148 | eSubName = ET.SubElement(eSubItem, 'Name') 149 | eSubName.text = "SubIndex 000" 150 | eSubInfo = ET.SubElement(eSubItem, 'Info') 151 | eSubDefault = ET.SubElement(eSubInfo, 'DefaultData') 152 | eSubDefault.text = intToHexLsb(len(pdoObject[1])) 153 | for pdoEntry in pdoObject[1]: 154 | eDTSubItem = ET.SubElement(eDataType, 'SubItem') 155 | eDTSubIdx = ET.SubElement(eDTSubItem, 'SubIdx') 156 | eDTSubIdx.text = str(intIdx) 157 | eDTSubName = ET.SubElement(eDTSubItem, 'Name') 158 | eDTSubName.text = "SubIndex "+"%03d"%intIdx 159 | eDTSubType = ET.SubElement(eDTSubItem, 'Type') 160 | eDTSubType.text = "UDINT" 161 | eDTSubSize = ET.SubElement(eDTSubItem, 'BitSize') 162 | eDTSubSize.text = "32" 163 | eDTBitOffs = ET.SubElement(eDTSubItem, 'BitOffs') 164 | eDTBitOffs.text = str(intBitOffs) 165 | eDTSubItem.append(eFlags) 166 | eSubItem = ET.SubElement(eObjInfo, 'SubItem') 167 | eSubName = ET.SubElement(eSubItem, 'Name') 168 | eSubName.text = eDTSubName.text 169 | eSubInfo = ET.SubElement(eSubItem, 'Info') 170 | eSubDefault = ET.SubElement(eSubInfo, 'DefaultData') 171 | eSubDefault.text = intToHexLsb(int(str(pdoEntry['Index'])\ 172 | +("%02d"%pdoEntry['SI'])+("%02x"%pdoEntry['Length']),0)) 173 | intIdx = intIdx+1 174 | intBitOffs = intBitOffs+32 175 | eObjects.insert(idxInsert,eObject) 176 | eDataTypes.insert(idxInsert,eDataType) 177 | idxInsert = idxInsert+1 178 | #Genenrate Pdo Elements 179 | idxInsert = 13 180 | idxRxPdo = 0 181 | idxTxPdo = 0 182 | for pdoObject in pdoMapping.iteritems(): 183 | ePdo = ET.Element("Pdo") 184 | ePdo.set('Fixed', 'true') 185 | ePdoIndex = ET.SubElement(ePdo, 'Index') 186 | ePdoIndex.text = hexStrToEtg(pdoObject[0]) 187 | ePdoName = ET.SubElement(ePdo, 'Name') 188 | if pdoObject[0][0:4] == "0x1A": 189 | ePdo.tag = "TxPdo" 190 | ePdo.set('Sm','3') 191 | ePdoName.text = "Input mapping "+str(idxRxPdo) 192 | idxRxPdo = idxRxPdo+1 193 | elif pdoObject[0][0:4] == "0x16": 194 | ePdo.tag = "RxPdo" 195 | ePdo.set('Sm','2') 196 | ePdoName.text = "Output mapping "+str(idxTxPdo) 197 | idxTxPdo = idxTxPdo+1 198 | for pdoEntry in pdoObject[1]: 199 | ePdoEntry = ET.SubElement(ePdo, 'Entry') 200 | eEntryIdx = ET.SubElement(ePdoEntry, 'Index') 201 | eEntryIdx.text = hexStrToEtg(pdoEntry['Index']) 202 | eEntrySubIdx = ET.SubElement(ePdoEntry, 'SubIndex') 203 | eEntrySubIdx.text = str(pdoEntry['SI']) 204 | eEntryBitLen = ET.SubElement(ePdoEntry, 'BitLen') 205 | eEntryBitLen.text = str(pdoEntry['Length']) 206 | if int(pdoEntry['Index'],0)!=0: 207 | eEntryName = ET.SubElement(ePdoEntry, 'Name') 208 | eEntryName.text = pdoEntry['Name'] 209 | eEntryDataType = ET.SubElement(ePdoEntry, 'DataType') 210 | eEntryDataType.text = pdoEntry['DataType'] 211 | root[1][1][0].insert(idxInsert,ePdo) 212 | idxInsert = idxInsert+1 213 | return root 214 | 215 | 216 | 217 | 218 | def XmlGenerator(strXmlFile,dictSlaveInfo,listSlaveDict): 219 | #Read template 220 | tree = ET.parse('SSC-Template.xml') 221 | root = tree.getroot() 222 | #General infomation 223 | root[0][0].text = hexStrToEtg(dictSlaveInfo['Vendor ID']) 224 | root[0][1].text = hexStrToEtg(dictSlaveInfo['Vendor Name']) 225 | root[1][1][0][0].set('ProductCode',hexStrToEtg(dictSlaveInfo['Product Code'])) 226 | root[1][1][0][0].set('RevisionNo',hexStrToEtg(dictSlaveInfo['Revision Number'])) 227 | root[1][1][0][0].text = dictSlaveInfo['Device Name'] 228 | root[1][1][0][1].text = dictSlaveInfo['Device Name'] 229 | root[1][1][0][2].text = dictSlaveInfo['Device Name'] 230 | root[1][1][0][5][0][0].text = str(int(dictSlaveInfo['Device Profile'])) 231 | root[1][0][0][0].text = dictSlaveInfo['Group Type'] 232 | root[1][0][0][1].text = dictSlaveInfo['Group Name'] 233 | root[1][0][0][2].text = dictSlaveInfo['Group Name'] 234 | root[1][1][0][4].text = dictSlaveInfo['Group Type'] 235 | #General Object Dictionary Existed Items 236 | eDataTypes = root[1][1][0][5][1][0] 237 | eObjects = root[1][1][0][5][1][1] 238 | #--Device Profile-- 239 | eObjects[0][4][0].text = intToHexLsb(int(dictSlaveInfo['Device Profile'])) 240 | #--Device Name-- 241 | eDataTypes = fillDictionaryString(eDataTypes,eObjects[2],dictSlaveInfo['Device Name']) 242 | #--HW version-- 243 | eDataTypes = fillDictionaryString(eDataTypes,eObjects[3],dictSlaveInfo['HW Version']) 244 | #--SW version-- 245 | eDataTypes = fillDictionaryString(eDataTypes,eObjects[4],dictSlaveInfo['SW Version']) 246 | #--Identity-- 247 | eObjects[5][4][1][1][0].text = intToHexLsb(int(dictSlaveInfo['Vendor ID'],0)) 248 | eObjects[5][4][2][1][0].text = intToHexLsb(int(dictSlaveInfo['Product Code'],0)) 249 | eObjects[5][4][3][1][0].text = intToHexLsb(int(dictSlaveInfo['Revision Number'],0)) 250 | eObjects[5][4][4][1][0].text = intToHexLsb(int(dictSlaveInfo['Serial Number'],0)) 251 | #General Application Dictionary 252 | for dictSlaveInfo in listSlaveDict: 253 | #{Index, ObjectCode, [{SI,DataType,Name,Default,Access,PdoDir,Description}]} 254 | eObject = ET.SubElement(eObjects, 'Object') 255 | eObjIndex = ET.SubElement(eObject, 'Index') 256 | eObjIndex.text = hexStrToEtg(dictSlaveInfo['Index']) 257 | eObjName = ET.SubElement(eObject, 'Name') 258 | eObjName.text = dictSlaveInfo['Entry'][0]['Name'] 259 | if eObjName.text=="": 260 | eObjName.text = "Obj"+dictSlaveInfo['Index'][2:] 261 | if dictSlaveInfo['ObjectCode']=="VARIABLE": 262 | eObjType = ET.SubElement(eObject, 'Type') 263 | eObjType.text = dictSlaveInfo['Entry'][0]['DataType'] 264 | #If the data type does not exist, add it to datatype list 265 | addDataType(eDataTypes,eObjType.text) 266 | eObjBitSize = ET.SubElement(eObject, 'BitSize') 267 | eObjBitSize.text = str(dictEthercatType[dictSlaveInfo['Entry'][0]['DataType']][2]) 268 | if dictSlaveInfo['Entry'][0]['Default'] != "" and \ 269 | dictSlaveInfo['Entry'][0]['Default'] != None: 270 | eObjInfo = ET.SubElement(eObject, 'Info') 271 | eObjDefault = ET.SubElement(eObjInfo, 'DefaultData') 272 | eObjDefault.text = intToHexLsb(int(dictSlaveInfo['Entry'][0]['Default'])) 273 | elif dictSlaveInfo['ObjectCode']=="RECORD": 274 | eObjType = ET.SubElement(eObject, 'Type') 275 | eObjType.text = "DT"+dictSlaveInfo['Index'][2:] 276 | eDataType = ET.SubElement(eDataTypes, 'DataType') 277 | eObjSize = ET.SubElement(eObject, 'BitSize') 278 | eDTName = ET.SubElement(eDataType, 'Name') 279 | eDTName.text = eObjType.text 280 | eDTSize = ET.SubElement(eDataType, 'BitSize') 281 | eSubInfo = ET.SubElement(eObject, 'Info') 282 | bitOffset = int(0) 283 | for dictEntry in dictSlaveInfo['Entry']: 284 | eDTSubItem = ET.SubElement(eDataType, 'SubItem') 285 | eSubIdx = ET.SubElement(eDTSubItem, 'SubIdx') 286 | eSubIdx.text = str(dictEntry['SI']) 287 | eSubName = ET.SubElement(eDTSubItem, 'Name') 288 | if dictEntry['Name']=="": 289 | eSubName.text = "SubIndex "+"%03d"%dictEntry['SI'] 290 | else: 291 | eSubName.text = dictEntry['Name'] 292 | eSubType = ET.SubElement(eDTSubItem, 'Type') 293 | eSubType.text = dictEntry['DataType'] 294 | #If the data type does not exist, add it to datatype list 295 | addDataType(eDataTypes,eSubType.text) 296 | eSubSize = ET.SubElement(eDTSubItem, 'BitSize') 297 | intSubSize = dictEthercatType[dictEntry['DataType']][2] 298 | eSubSize.text = str(intSubSize) 299 | #16 bit padding 300 | if bitOffset%16!=0 and intSubSize<16 and (bitOffset%16+intSubSize>16): 301 | bitOffset = (bitOffset/16+1)*16 302 | eSubBitOffset = ET.SubElement(eDTSubItem, 'BitOffs') 303 | eSubBitOffset.text = str(bitOffset) 304 | bitOffset = bitOffset+intSubSize 305 | eSubFlags = ET.SubElement(eDTSubItem, 'Flags') 306 | eSubAccess = ET.SubElement(eSubFlags, 'Access') 307 | eSubAccess.text = dictEntry['Access'].lower() 308 | eSubItem = ET.SubElement(eSubInfo, 'SubItem') 309 | eSubItemName = ET.SubElement(eSubItem, 'Name') 310 | eSubItemName.text = eSubName.text 311 | eSubItemInfo = ET.SubElement(eSubItem, 'Info') 312 | eSubDefault = ET.SubElement(eSubItemInfo, 'DefaultData') 313 | eSubDefault.text = intToHexLsb(int(dictEntry['Default'])) 314 | eDTSize.text = str((bitOffset+7)/8*8) 315 | eObjSize.text = eDTSize.text 316 | elif dictSlaveInfo['ObjectCode']=="ARRAY": 317 | #Generate ARRAY TYPE 318 | eDataType = ET.SubElement(eDataTypes, 'DataType') 319 | eDTName = ET.SubElement(eDataType, 'Name') 320 | eDTName.text = "DT"+dictSlaveInfo['Index'][2:]+"ARR" 321 | eDTBaseType = ET.SubElement(eDataType, 'BaseType') 322 | eDTBaseType.text = dictSlaveInfo['Entry'][1]['DataType'] 323 | #If the data type does not exist, add it to datatype list 324 | addDataType(eDataTypes,eDTBaseType.text) 325 | eDTSize = ET.SubElement(eDataType, 'BitSize') 326 | eDTSize.text = str(dictEthercatType[dictSlaveInfo['Entry'][1]['DataType']][2]\ 327 | *int(dictSlaveInfo['Entry'][0]['Default'])) 328 | eDTArray = ET.SubElement(eDataType, 'ArrayInfo') 329 | eDTLBound = ET.SubElement(eDTArray, 'LBound') 330 | eDTLBound.text = "1" 331 | eDTElements = ET.SubElement(eDTArray, 'Elements') 332 | eDTElements.text = dictSlaveInfo['Entry'][0]['Default'] 333 | #Generate Data TYPE 334 | eDataType = ET.SubElement(eDataTypes, 'DataType') 335 | eDTName = ET.SubElement(eDataType, 'Name') 336 | eDTName.text = "DT"+dictSlaveInfo['Index'][2:] 337 | eDTSize = ET.SubElement(eDataType, 'BitSize') 338 | eDTSize.text = str(dictEthercatType[dictSlaveInfo['Entry'][0]['DataType']][2]\ 339 | +dictEthercatType[dictSlaveInfo['Entry'][1]['DataType']][2]\ 340 | *int(dictSlaveInfo['Entry'][0]['Default'])) 341 | eDTSubItem = ET.SubElement(eDataType, 'SubItem') 342 | eDTSubIdx = ET.SubElement(eDTSubItem, 'SubIdx') 343 | eDTSubIdx.text = "0" 344 | eDTName = ET.SubElement(eDTSubItem, 'Name') 345 | eDTName.text = "SubIndex 000" 346 | eDTType = ET.SubElement(eDTSubItem, 'Type') 347 | eDTType.text = dictSlaveInfo['Entry'][0]['DataType'] 348 | addDataType(eDataTypes,eDTType.text) 349 | eDTSize = ET.SubElement(eDTSubItem, 'BitSize') 350 | eDTSize.text = str(dictEthercatType[dictSlaveInfo['Entry'][0]['DataType']][2]) 351 | eDTBitOffs = ET.SubElement(eDTSubItem, 'BitOffs') 352 | eDTBitOffs.text = "0" 353 | eDTSubFlags = ET.SubElement(eDTSubItem, 'Flags') 354 | eDTSubAccess = ET.SubElement(eDTSubFlags, 'Access') 355 | eDTSubAccess.text = dictSlaveInfo['Entry'][0]['Access'].lower() 356 | eDTSubItem = ET.SubElement(eDataType, 'SubItem') 357 | eDTName = ET.SubElement(eDTSubItem, 'Name') 358 | eDTName.text = "Elements" 359 | eDTType = ET.SubElement(eDTSubItem, 'Type') 360 | eDTType.text = "DT"+dictSlaveInfo['Index'][2:]+"ARR" 361 | eDTSize = ET.SubElement(eDTSubItem, 'BitSize') 362 | eDTSize.text = str(dictEthercatType[dictSlaveInfo['Entry'][1]['DataType']][2]\ 363 | *int(dictSlaveInfo['Entry'][0]['Default'])) 364 | eDTBitOffs = ET.SubElement(eDTSubItem, 'BitOffs') 365 | eDTBitOffs.text = "16" 366 | eDTSubFlags = ET.SubElement(eDTSubItem, 'Flags') 367 | eDTSubAccess = ET.SubElement(eDTSubFlags, 'Access') 368 | eDTSubAccess.text = dictSlaveInfo['Entry'][1]['Access'].lower() 369 | #Generate Object 370 | eObjType = ET.SubElement(eObject, 'Type') 371 | eObjType.text = "DT"+dictSlaveInfo['Index'][2:] 372 | eObjSize = ET.SubElement(eObject, 'BitSize') 373 | eObjSize.text = eDTSize.text 374 | eSubInfo = ET.SubElement(eObject, 'Info') 375 | eSubItem = ET.SubElement(eSubInfo, 'SubItem') 376 | eSubItemName = ET.SubElement(eSubItem, 'Name') 377 | eSubItemName.text = "SubIndex 000" 378 | eSubItemInfo = ET.SubElement(eSubItem, 'Info') 379 | eSubDefault = ET.SubElement(eSubItemInfo, 'DefaultData') 380 | eSubDefault.text = intToHexLsb(int(dictSlaveInfo['Entry'][0]['Default'])) 381 | for idx in range(1,int(dictSlaveInfo['Entry'][0]['Default'])+1): 382 | eSubItem = ET.SubElement(eSubInfo, 'SubItem') 383 | eSubItemName = ET.SubElement(eSubItem, 'Name') 384 | eSubItemName.text = "SubIndex "+"%03d"%idx 385 | eSubItemInfo = ET.SubElement(eSubItem, 'Info') 386 | eSubDefault = ET.SubElement(eSubItemInfo, 'DefaultData') 387 | eSubDefault.text = intToHexLsb(int(dictSlaveInfo['Entry'][1]['Default'])) 388 | eObjFlags = ET.SubElement(eObject, 'Flags') 389 | eObjAccess = ET.SubElement(eObjFlags, 'Access') 390 | eObjAccess.text = dictSlaveInfo['Entry'][0]['Access'].lower() 391 | #Generate Pdo Mapping 392 | root = GeneratePdoObjects(root,listSlaveDict) 393 | 394 | #Output Xml file 395 | tree.write(strXmlFile) 396 | 397 | 398 | -------------------------------------------------------------------------------- /libraries/arducat/sdoserv.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------- 2 | ------ 3 | ------ Description 4 | ------ 5 | ------ sdoserv.h 6 | ------ 7 | ------ SDO-Server definitions 8 | ------ ------ 9 | -----------------------------------------------------------------------------------------*/ 10 | 11 | #ifndef _SDOSERV_H_ 12 | #define _SDOSERV_H_ 13 | 14 | /*----------------------------------------------------------------------------------------- 15 | ------ 16 | ------ Includes 17 | ------ 18 | -----------------------------------------------------------------------------------------*/ 19 | 20 | #include "ecatcoe.h" 21 | 22 | /*----------------------------------------------------------------------------------------- 23 | ------ 24 | ------ Defines and Types 25 | ------ 26 | -----------------------------------------------------------------------------------------*/ 27 | 28 | /*///////////////////////////////////////////////////////////////////////////////////////// 29 | // 30 | // Error Codes 31 | */ 32 | 33 | #define ERROR_SDOINVALIDCOMMAND 0x1101 34 | #define ERROR_SDOINVALIDHEADER 0x1102 35 | #define ERROR_SDONOTSUPPORTED 0x1103 36 | 37 | /*///////////////////////////////////////////////////////////////////////////////////////// 38 | // 39 | // SDO services 40 | */ 41 | 42 | #define SDOHEADER_SIZEINDICATOR ((UINT8) 0x01) 43 | #define SDOHEADER_TRANSFERTYPE ((UINT8) 0x02) 44 | #define SDOHEADER_DATASETSIZE ((UINT8) 0x0C) 45 | #define SDOHEADER_COMPLETEACCESS ((UINT8) 0x10) 46 | #define SDOHEADER_COMMAND ((UINT8) 0xE0) 47 | #define SDOHEADERSHIFT_SIZEINDICATOR ((UINT8) 0) 48 | #define SDOHEADERSHIFT_TRANSFERTYPE ((UINT8) 1) 49 | #define SDOHEADERSHIFT_DATASETSIZE ((UINT8) 2) 50 | #define SDOHEADERSHIFT_INDEXACCESS ((UINT8) 4) 51 | #define SDOHEADERSHIFT_COMMAND ((UINT8) 5) 52 | #define SDOSERVICE_SIZEINDICATOR ((UINT8) 0x01) 53 | #define SDOSERVICE_TRANSFERTYPE ((UINT8) 0x02) 54 | #define SDOSERVICE_DATASETSIZESHIFT ((UINT8) 2) 55 | #define SDOSERVICE_COMMANDSHIFT ((UINT8) 5) 56 | #define SDOSERVICE_INITIATEDOWNLOADREQ ((UINT8) (0x01 << (SDOHEADERSHIFT_COMMAND))) 57 | #define SDOSERVICE_INITIATEDOWNLOADRES ((UINT8) (0x03 << (SDOHEADERSHIFT_COMMAND))) 58 | #define SDOSERVICE_DOWNLOADSEGMENTREQ ((UINT8) (0x00 << (SDOHEADERSHIFT_COMMAND))) 59 | #define SDOSERVICE_DOWNLOADSEGMENTRES ((UINT8) (0x01 << (SDOHEADERSHIFT_COMMAND))) 60 | #define SDOSERVICE_INITIATEUPLOADREQ ((UINT8) (0x02 << (SDOHEADERSHIFT_COMMAND))) 61 | #define SDOSERVICE_INITIATEUPLOADRES ((UINT8) (0x02 << (SDOHEADERSHIFT_COMMAND))) 62 | #define SDOSERVICE_UPLOADSEGMENTREQ ((UINT8) (0x03 << (SDOHEADERSHIFT_COMMAND))) 63 | #define SDOSERVICE_UPLOADSEGMENTRES ((UINT8) (0x00 << (SDOHEADERSHIFT_COMMAND))) 64 | #define SDOSERVICE_ABORTTRANSFER ((UINT8) (((UINT8) 0x04) << (SDOHEADERSHIFT_COMMAND))) 65 | 66 | /////////////////////////////////////////////////////////////////////////////////////////// 67 | /*///////////////////////////////////////////////////////////////////////////////////////// 68 | // 69 | // SDO structures 70 | */ 71 | 72 | /////////////////////////////////////////////////////////////////////////////////////////// 73 | // BASIC structures: 74 | 75 | // initial service header: 76 | typedef struct MBX_STRUCT_PACKED_START 77 | { 78 | UINT8 Sdo[4]; 79 | #define SDOHEADER_COMMANDOFFSET 0 80 | #define SDOHEADER_INDEXLOOFFSET 1 81 | #define SDOHEADER_INDEXHIOFFSET 2 82 | #define SDOHEADER_SUBINDEXOFFSET 3 83 | #define SDOHEADER_COMMANDMASK 0xFF 84 | #define SDOHEADER_INDEXLOSHIFT 0 85 | #define SDOHEADER_INDEXHIMASK 0xFF 86 | #define SDOHEADER_SUBINDEXSHIFT 0 87 | }MBX_STRUCT_PACKED_END 88 | TINITSDOHEADER; 89 | 90 | #define INITSDO_HEADER_SIZE SIZEOF(TINITSDOHEADER) 91 | 92 | // complete initial service header: 93 | typedef struct MBX_STRUCT_PACKED_START 94 | { 95 | TMBXHEADER MbxHeader; 96 | TCOEHEADER CoeHeader; 97 | TINITSDOHEADER SdoHeader; 98 | }MBX_STRUCT_PACKED_END 99 | TINITSDOMBX; 100 | 101 | #define MAX_EXPEDITED_DATA 4 102 | #define MIN_SEGMENTED_DATA ((UINT16) 7) 103 | #define EXPEDITED_FRAME_SIZE ( COE_HEADER_SIZE + INITSDO_HEADER_SIZE + (MAX_EXPEDITED_DATA) ) 104 | #define DOWNLOAD_NORM_REQ_SIZE ( COE_HEADER_SIZE + INITSDO_HEADER_SIZE + 4 ) 105 | /* HBu 06.02.06: names of defines changed */ 106 | /* HBu 21.03.06: the SDO_Download-Response has to have always 8 bytes */ 107 | #define DOWNLOAD_NORM_RES_SIZE ( COE_HEADER_SIZE + INITSDO_HEADER_SIZE + 4 ) 108 | #define UPLOAD_NORM_RES_SIZE ( COE_HEADER_SIZE + INITSDO_HEADER_SIZE + 4 ) 109 | #define SEGMENT_NORM_HEADER_SIZE ( COE_HEADER_SIZE + 1 ) 110 | #define SEGMENT_NORM_RES_SIZE ( (SEGMENT_NORM_HEADER_SIZE) + (MIN_SEGMENTED_DATA) ) 111 | #define SEGMENT_MBX_SIZE ( MBX_HEADER_SIZE + (SEGMENT_NORM_HEADER_SIZE) ) 112 | 113 | // segmented service header with data: 114 | typedef struct MBX_STRUCT_PACKED_START 115 | { 116 | UINT8 SegHeader; 117 | #define SEGHEADER_MASK ((UINT8) 0xFF) 118 | #define SEGHEADER_NOMOREFOLLOWS ((UINT8) 0x01) 119 | #define SEGHEADER_SEGDATASIZE ((UINT8) 0x0E) 120 | #define SEGHEADER_TOGGLE ((UINT8) 0x10) 121 | #define SEGHEADER_COMMAND ((UINT8) 0xE0) 122 | #define SEGHEADERSHIFT_SEGDATASIZE ((UINT8) 1) 123 | #define SEGHEADERSHIFT_TOGGLE ((UINT8) 4) 124 | #define SEGHEADERSHIFT_COMMAND ((UINT8) 5) 125 | UINT8 Data[(MAX_MBX_DATA_SIZE)-(SEGMENT_NORM_HEADER_SIZE)]; 126 | }MBX_STRUCT_PACKED_END 127 | TSDOSEGHEADERDATA; 128 | 129 | /////////////////////////////////////////////////////////////////////////////////////////// 130 | // DOWNLOAD service structures: 131 | 132 | // expedited download request: 133 | typedef struct MBX_STRUCT_PACKED_START 134 | { 135 | TMBXHEADER MbxHeader; 136 | TCOEHEADER CoeHeader; 137 | TINITSDOHEADER SdoHeader; 138 | UINT16 Data[(MAX_EXPEDITED_DATA) >> 1]; 139 | }MBX_STRUCT_PACKED_END 140 | TINITSDODOWNLOADEXPREQMBX; 141 | 142 | // normal download request: 143 | typedef struct MBX_STRUCT_PACKED_START 144 | { 145 | TMBXHEADER MbxHeader; 146 | TCOEHEADER CoeHeader; 147 | TINITSDOHEADER SdoHeader; 148 | UINT16 CompleteSize[2]; 149 | UINT16 Data[(((MAX_MBX_DATA_SIZE)-(DOWNLOAD_NORM_REQ_SIZE)) >> 1)]; 150 | }MBX_STRUCT_PACKED_END 151 | TINITSDODOWNLOADNORMREQMBX; 152 | 153 | // expedited and normal download response: 154 | typedef struct MBX_STRUCT_PACKED_START 155 | { 156 | TMBXHEADER MbxHeader; 157 | TCOEHEADER CoeHeader; 158 | TINITSDOHEADER SdoHeader; 159 | }MBX_STRUCT_PACKED_END 160 | TINITSDODOWNLOADRESMBX; 161 | 162 | // segmented download request: 163 | typedef struct MBX_STRUCT_PACKED_START 164 | { 165 | TMBXHEADER MbxHeader; 166 | TCOEHEADER CoeHeader; 167 | TSDOSEGHEADERDATA SdoHeader; // data is included in header ! 168 | }MBX_STRUCT_PACKED_END 169 | TDOWNLOADSDOSEGREQMBX; 170 | 171 | // segmented download response: 172 | typedef struct MBX_STRUCT_PACKED_START 173 | { 174 | TMBXHEADER MbxHeader; 175 | TCOEHEADER CoeHeader; 176 | UINT8 SegHeader; 177 | }MBX_STRUCT_PACKED_END 178 | TDOWNLOADSDOSEGRESMBX; 179 | 180 | 181 | /////////////////////////////////////////////////////////////////////////////////////////// 182 | // UPLOAD service structures: 183 | 184 | // expedited and normal upload request: 185 | typedef struct MBX_STRUCT_PACKED_START 186 | { 187 | TMBXHEADER MbxHeader; 188 | TCOEHEADER CoeHeader; 189 | TINITSDOHEADER SdoHeader; 190 | }MBX_STRUCT_PACKED_END 191 | TINITSDOUPLOADREQMBX; 192 | 193 | // expedited upload response: 194 | typedef struct MBX_STRUCT_PACKED_START 195 | { 196 | TMBXHEADER MbxHeader; // 6 bytes 197 | TCOEHEADER CoeHeader; // 2 bytes 198 | TINITSDOHEADER SdoHeader; // 4 bytes 199 | UINT16 Data[((MAX_EXPEDITED_DATA) >> 1)]; 200 | }MBX_STRUCT_PACKED_END 201 | TINITSDOUPLOADEXPRESMBX; 202 | 203 | // normal upload response: 204 | typedef struct MBX_STRUCT_PACKED_START 205 | { 206 | TMBXHEADER MbxHeader; 207 | TCOEHEADER CoeHeader; 208 | TINITSDOHEADER SdoHeader; 209 | UINT16 CompleteSize[2]; 210 | UINT16 Data[(((MAX_MBX_DATA_SIZE)-(UPLOAD_NORM_RES_SIZE)) >> 1)]; 211 | }MBX_STRUCT_PACKED_END 212 | TINITSDOUPLOADNORMRESMBX; 213 | 214 | // segmented upload request: 215 | typedef struct MBX_STRUCT_PACKED_START 216 | { 217 | TMBXHEADER MbxHeader; 218 | TCOEHEADER CoeHeader; 219 | UINT8 SegHeader; 220 | }MBX_STRUCT_PACKED_END 221 | TUPLOADSDOSEGREQMBX; 222 | 223 | // segmented upload response: 224 | typedef struct MBX_STRUCT_PACKED_START 225 | { 226 | TMBXHEADER MbxHeader; 227 | TCOEHEADER CoeHeader; 228 | TSDOSEGHEADERDATA SdoHeader; // data is included in header ! 229 | }MBX_STRUCT_PACKED_END 230 | TUPLOADSDOSEGRESMBX; 231 | 232 | /////////////////////////////////////////////////////////////////////////////////////////// 233 | // ABORT service structure and defines: 234 | 235 | // abort request: 236 | typedef struct MBX_STRUCT_PACKED_START 237 | { 238 | TMBXHEADER MbxHeader; 239 | TCOEHEADER CoeHeader; 240 | TINITSDOHEADER SdoHeader; 241 | UINT32 AbortCode; 242 | }MBX_STRUCT_PACKED_END 243 | TABORTSDOTRANSFERREQMBX; 244 | 245 | #define ABORT_NORM_RES_SIZE (SIZEOF(TABORTSDOTRANSFERREQMBX) - SIZEOF(TMBXHEADER)) 246 | 247 | /*///////////////////////////////////////////////////////////////////////////////////////// 248 | // 249 | // Abort codes 250 | */ 251 | 252 | #define ABORTIDX_TOGGLE_BIT_NOT_CHANGED 0x01 253 | #define ABORTIDX_SDO_PROTOCOL_TIMEOUT 0x02 254 | #define ABORTIDX_COMMAND_SPECIFIER_UNKNOWN 0x03 255 | #define ABORTIDX_OUT_OF_MEMORY 0x04 256 | #define ABORTIDX_UNSUPPORTED_ACCESS 0x05 257 | #define ABORTIDX_WRITE_ONLY_ENTRY 0x06 258 | #define ABORTIDX_READ_ONLY_ENTRY 0x07 259 | #define ABORTIDX_OBJECT_NOT_EXISTING 0x08 260 | #define ABORTIDX_OBJECT_CANT_BE_PDOMAPPED 0x09 261 | #define ABORTIDX_MAPPED_OBJECTS_EXCEED_PDO 0x0A 262 | #define ABORTIDX_PARAM_IS_INCOMPATIBLE 0x0B 263 | #define ABORTIDX_INTERNAL_DEVICE_INCOMPATIBILITY 0x0C 264 | #define ABORTIDX_HARDWARE_ERROR 0x0D 265 | #define ABORTIDX_PARAM_LENGTH_ERROR 0x0E 266 | #define ABORTIDX_PARAM_LENGTH_TOO_LONG 0x0F 267 | #define ABORTIDX_PARAM_LENGTH_TOO_SHORT 0x10 268 | #define ABORTIDX_SUBINDEX_NOT_EXISTING 0x11 269 | #define ABORTIDX_VALUE_EXCEEDED 0x12 270 | #define ABORTIDX_VALUE_TOO_GREAT 0x13 271 | #define ABORTIDX_VALUE_TOO_SMALL 0x14 272 | #define ABORTIDX_MAX_VALUE_IS_LESS_THAN_MIN_VALUE 0x15 273 | #define ABORTIDX_GENERAL_ERROR 0x16 274 | #define ABORTIDX_DATA_CANNOT_BE_READ_OR_STORED 0x17 275 | #define ABORTIDX_DATA_CANNOT_BE_ACCESSED_BECAUSE_OF_LOCAL_CONTROL 0x18 276 | #define ABORTIDX_IN_THIS_STATE_DATA_CANNOT_BE_READ_OR_STORED 0x19 277 | #define ABORTIDX_NO_OBJECT_DICTIONARY_IS_PRESENT 0x1A 278 | #define ABORTIDX_ENTRY_CANT_BE_WRITTEN_SI0_NOT_0 0x1B 279 | #define ABORTIDX_WORKING 0xFF 280 | 281 | #define ABORT_NOERROR 0x00000000 282 | #define ABORT_TOGGLE_BIT_NOT_CHANGED 0x05030000 283 | #define ABORT_SDO_PROTOCOL_TIMEOUT 0x05040000 284 | #define ABORT_COMMAND_SPECIFIER_UNKNOWN 0x05040001 285 | #define ABORT_OUT_OF_MEMORY 0x05040005 286 | #define ABORT_UNSUPPORTED_ACCESS 0x06010000 287 | #define ABORT_WRITE_ONLY_ENTRY 0x06010001 288 | #define ABORT_READ_ONLY_ENTRY 0x06010002 289 | #define ABORT_ENTRY_CANT_BE_WRITTEN_SI0_NOT_0 0x06010003 290 | #define ABORT_OBJECT_NOT_EXISTING 0x06020000 291 | #define ABORT_OBJECT_CANT_BE_PDOMAPPED 0x06040041 292 | #define ABORT_MAPPED_OBJECTS_EXCEED_PDO 0x06040042 293 | #define ABORT_PARAM_IS_INCOMPATIBLE 0x06040043 294 | #define ABORT_INTERNAL_DEVICE_INCOMPATIBILITY 0x06040047 295 | #define ABORT_HARDWARE_ERROR 0x06060000 296 | #define ABORT_PARAM_LENGTH_ERROR 0x06070010 297 | #define ABORT_PARAM_LENGTH_TOO_LONG 0x06070012 298 | #define ABORT_PARAM_LENGTH_TOO_SHORT 0x06070013 299 | #define ABORT_SUBINDEX_NOT_EXISTING 0x06090011 300 | #define ABORT_VALUE_EXCEEDED 0x06090030 301 | #define ABORT_VALUE_TOO_GREAT 0x06090031 302 | #define ABORT_VALUE_TOO_SMALL 0x06090032 303 | #define ABORT_MAX_VALUE_IS_LESS_THAN_MIN_VALUE 0x06090036 304 | #define ABORT_GENERAL_ERROR 0x08000000 305 | #define ABORT_DATA_CANNOT_BE_READ_OR_STORED 0x08000020 306 | #define ABORT_DATA_CANNOT_BE_READ_OR_STORED_BECAUSE_OF_LOCAL_CONTROL 0x08000021 307 | #define ABORT_DATA_CANNOT_BE_READ_OR_STORED_IN_THIS_STATE 0x08000022 308 | #define ABORT_NO_OBJECT_DICTIONARY_IS_PRESENT 0x08000023 309 | 310 | /*///////////////////////////////////////////////////////////////////////////////////////// 311 | // 312 | // SDO Information services 313 | */ 314 | 315 | #define SDOINFOSERVICE_OBJDICTIONARYLIST_Q 0x01 316 | #define SDOINFOSERVICE_OBJDICTIONARYLIST_S 0x02 317 | #define SDOINFOSERVICE_OBJDESCRIPTION_Q 0x03 318 | #define SDOINFOSERVICE_OBJDESCRIPTION_S 0x04 319 | #define SDOINFOSERVICE_ENTRYDESCRIPTION_Q 0x05 320 | #define SDOINFOSERVICE_ENTRYDESCRIPTION_S 0x06 321 | #define SDOINFOSERVICE_ERROR_Q 0x07 322 | #define SDOINFOSERVICE_INCOMPLETE 0x80 323 | 324 | /*///////////////////////////////////////////////////////////////////////////////////////// 325 | // 326 | // SDO Information service structures: 327 | */ 328 | 329 | // SDO Information / Object Dictionary Lists: 330 | typedef struct MBX_STRUCT_PACKED_START 331 | { 332 | UINT16 ListType; 333 | #define INFO_LIST_TYPE_LENGTH 0 334 | #define INFO_LIST_TYPE_ALL 1 335 | #define INFO_LIST_TYPE_RXPDO 2 336 | #define INFO_LIST_TYPE_TXPDO 3 337 | #define INFO_LIST_TYPE_BACKUP 4 338 | #define INFO_LIST_TYPE_SET 5 339 | #define INFO_LIST_TYPE_MAX 5 340 | }MBX_STRUCT_PACKED_END 341 | TSDOINFOLIST; 342 | 343 | 344 | typedef struct MBX_STRUCT_PACKED_START 345 | { 346 | UINT16 DataType; // refer to data type index 347 | UINT16 ObjFlags; 348 | /*Object Code and MaxSubindex will be set manually in the object dictionary. The value is always created in little endian format*/ 349 | #define OBJFLAGS_MAXSUBINDEXMASK 0x00FF 350 | #define OBJFLAGS_MAXSUBINDEXSHIFT 0 351 | #define OBJFLAGS_OBJCODEMASK 0x0F00 352 | #define OBJFLAGS_OBJCODESHIFT 8 353 | 354 | #define OBJCODE_VAR 0x07 355 | #define OBJCODE_ARR 0x08 356 | #define OBJCODE_REC 0x09 357 | // char Name[]; // rest of mailbox data 358 | }MBX_STRUCT_PACKED_END 359 | TSDOINFOOBJDESC; 360 | 361 | #define SDO_INFO_OBJ_DESC_SIZE SIZEOF(TSDOINFOOBJDESC) 362 | 363 | typedef struct MBX_STRUCT_PACKED_START 364 | { 365 | UINT16 Index; 366 | TSDOINFOOBJDESC Res; 367 | }MBX_STRUCT_PACKED_END 368 | TSDOINFOOBJ; 369 | 370 | #define SDO_INFO_OBJ_DESC_RES_SIZE SIZEOF(TSDOINFOOBJ) 371 | 372 | // SDO Information / Entry Description: 373 | typedef struct MBX_STRUCT_PACKED_START 374 | { 375 | UINT16 DataType; // refer to data type index 376 | UINT16 BitLength; 377 | UINT16 ObjAccess; // Bit 0: Read Access in Pre-Op 378 | // Bit 1: Read Access in Safe-Op 379 | // Bit 2: Read Access in Op 380 | // Bit 3: Write Access in Pre-Op 381 | // Bit 4: Write Access in Safe-Op 382 | // Bit 5: Write Access in Op 383 | // Bit 6: mappable in RxPDO 384 | // Bit 7: mappable in TxPDO 385 | // Bit 8: entry will be included in backup 386 | // Bit 9: entry will be included in settings 387 | 388 | #define ACCESS_READWRITE 0x003F 389 | #define ACCESS_READ 0x0007 390 | #define ACCESS_READ_PREOP 0x0001 391 | #define ACCESS_READ_SAFEOP 0x0002 392 | #define ACCESS_READ_OP 0x0004 393 | #define ACCESS_WRITE 0x0038 394 | #define ACCESS_WRITE_PREOP 0x0008 395 | #define ACCESS_WRITE_SAFEOP 0x0010 396 | #define ACCESS_WRITE_OP 0x0020 397 | #define OBJACCESS_NOPDOMAPPING 0x0000 398 | #define OBJACCESS_RXPDOMAPPING 0x0040 399 | #define OBJACCESS_TXPDOMAPPING 0x0080 400 | #define OBJACCESS_BACKUP 0x0100 401 | #define OBJACCESS_SETTINGS 0x0200 402 | #define OBJACCESS_SAFEINPUTS 0x0400 403 | #define OBJACCESS_SAFEOUTPUTS 0x0800 404 | #define OBJACCESS_SAFEPARAMETER 0x1000 405 | // UINT16 UnitType; // optional if bit3 of valueInfo 406 | // UINT8 DefaultValue[]; // optional if bit4 of valueInfo 407 | // UINT8 MinValue[]; // optional if bit5 of valueInfo 408 | // UINT8 MaxValue[]; // optional if bit6 of valueInfo 409 | // char Desc[]; // rest of mailbox data 410 | }MBX_STRUCT_PACKED_END 411 | TSDOINFOENTRYDESC; 412 | 413 | typedef struct MBX_STRUCT_PACKED_START 414 | { 415 | UINT16 Index; 416 | 417 | UINT16 Info; 418 | #define ENTRY_MASK_SUBINDEX 0x00FF 419 | #define ENTRY_SUBINDEX_SHIFT 0 420 | #define ENTRY_MASK_VALUEINFO 0xFF00 421 | #define ENTRY_VALUEINFO_SHIFT 8 422 | 423 | TSDOINFOENTRYDESC Res; 424 | }MBX_STRUCT_PACKED_END 425 | TSDOINFOENTRY; 426 | 427 | // SDO Information / Error: 428 | typedef struct MBX_STRUCT_PACKED_START 429 | { 430 | UINT32 ErrorCode; 431 | // char ErrorText[]; 432 | }MBX_STRUCT_PACKED_END 433 | TSDOINFOERROR; 434 | 435 | #define SDO_INFO_ERROR_SIZE SIZEOF(TSDOINFOERROR) 436 | 437 | 438 | // SDO Information / Header 439 | typedef struct MBX_STRUCT_PACKED_START 440 | { 441 | UINT16 InfoHead; 442 | #define INFOHEAD_OPCODE_MASK 0x007F 443 | #define INFOHEAD_OPCODE_SHIFT 0 444 | #define INFOHEADER_INCOMPLETE_MASK 0x0080 445 | #define INFOHEADER_INCOMPLETE_SHIFT 0 //the incomplete value "SDOINFOSERVICE_INCOMPLETE" is defined as a UINT8 => no shift required 446 | 447 | UINT16 FragmentsLeft; 448 | 449 | union MBX_STRUCT_PACKED_START 450 | { 451 | TSDOINFOLIST List; 452 | TSDOINFOOBJ Obj; 453 | TSDOINFOENTRY Entry; 454 | TSDOINFOERROR Error; 455 | UINT16 Data[1]; 456 | }MBX_STRUCT_PACKED_END 457 | Data; 458 | }MBX_STRUCT_PACKED_END 459 | TSDOINFOHEADER; 460 | 461 | 462 | // SDO Information / complete structure 463 | typedef struct MBX_STRUCT_PACKED_START 464 | { 465 | TMBXHEADER MbxHeader; 466 | TCOEHEADER CoeHeader; 467 | TSDOINFOHEADER SdoHeader; 468 | }MBX_STRUCT_PACKED_END 469 | TSDOINFORMATION; 470 | 471 | #define SIZEOF_SDOINFOHEAD 4 472 | #define SIZEOF_SDOINFO ( (COE_HEADER_SIZE) + (SIZEOF_SDOINFOHEAD )) 473 | #define SIZEOF_SDOINFOSTRUCT ( (MBX_HEADER_SIZE) + (COE_HEADER_SIZE) + (SIZEOF_SDOINFOHEAD) ) 474 | #define SIZEOF_SDOINFOLISTHEAD 2 475 | #define SIZEOF_SDOINFOLISTSTRUCT (( COE_HEADER_SIZE) + (SIZEOF_SDOINFOHEAD) + (SIZEOF_SDOINFOLISTHEAD) ) 476 | #define SIZEOF_SDOINFOOBJSTRUCT ( (COE_HEADER_SIZE) + (SIZEOF_SDOINFOHEAD) + (SDO_INFO_OBJ_DESC_RES_SIZE) ) 477 | #define SIZEOF_SDOINFOENTRYREQHEAD 4 478 | #define SIZEOF_SDOINFOENTRYREQSTRUCT ( (COE_HEADER_SIZE) + (SIZEOF_SDOINFOHEAD) + (SIZEOF_SDOINFOENTRYREQHEAD) ) 479 | #define SIZEOF_SDOINFOERRORSTRUCT ( (COE_HEADER_SIZE) + (SIZEOF_SDOINFOHEAD) + (SDO_INFO_ERROR_SIZE) ) 480 | 481 | 482 | 483 | #endif //_SDOSRV_H_ 484 | 485 | 486 | -------------------------------------------------------------------------------- /libraries/arducat/ecatappl.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | \defgroup ecatappl ecatappl: EtherCAT Slave - application interface 3 | \brief This file contains the EtherCAT State Machine and Process Data interface.\n 4 | \brief Changes to version V5.0: 5 | \brief V5.01 APPL3: Include library demo application 6 | \brief V5.01 ESC1: Change ESC access function (if EEPROM Emulation is active) 7 | \brief V5.01 ESC2: Add missed value swapping 8 | \brief Changes to version V4.40: 9 | \brief V5.0 TEST1: Add test application. See Application Note ET9300 for more details. 10 | \brief V5.0 ECAT2: Application specific functions are moved to application files. 11 | \brief V5.0 ECAT3: Global dummy variables used for dummy ESC operations. 12 | \brief V5.0 ESC1: ESC 32Bit Access added. 13 | \brief V5.0 ESC3: Add EEPROM emulation support. 14 | \brief V5.0 ESM3: Handling pending ESM transitions. 15 | \brief V5.0 ESC5: Enhance EEPROM access handling. 16 | \brief V5.0 PDO1: AL Event flags are not rechecked in PDO_OutputMappping(). (Already checked before call function) 17 | \brief V5.0 SYNC1: Add missed SM event indication (0x1C32/0x1C33 SI11). 18 | \brief Changes to version V4.30:\n 19 | \brief V4.40 DIAG1: add diagnosis message support 20 | \brief 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. 21 | \brief V4.40 PDO2: Generic process data length calculation 22 | \brief V4.40 ECAT2: call cyclic CheckIfLocalError() to check the local flags 23 | \brief V4.40 HW0: Generic hardware access functions. Add Function (PDI_Isr()), content merged from spihw.c and mcihw.c. 24 | \brief V4.40 WD1: define (ESC_SM_WD_SUPPORTED) to choose ESC SyncManager watchdog or local watchdog 25 | \brief V4.40 ESM2: Change state transition behaviour from SafeOP to OP 26 | \brief V4.40 TIMER1: Change bus cycle time calculation and trigger of ECAT_CheckTimer() if ECAT_TIMER_INT is reset 27 | \brief V4.40 HW1: Add support for fc1100 hardware 28 | \brief Changes to version V4.20:\n 29 | \brief V4.30 EL9800: EL9800_x cyclic application is moved to el9800.c 30 | \brief V4.30 OBJ 3: add object dictionary initialization 31 | \brief V4.30 SYNC: add CalcSMCycleTime() (calculation of bus cycle time); change synchronisation control functions 32 | \brief V4.30 PDO: include PDO specific functions (moved from coeappl.c). 33 | \brief xxx_InputMapping(); xxx_OutputMapping(); xxx_ReadInputs(); xxx_ResetOutputs(); xxx_Application() 34 | \brief V4.30 CiA402: Add CiA402_StateMachine() and CiA402_Application() call 35 | \brief V4.20 DC 1: Add DC pending Statemachine handling 36 | \brief V4.20 PIC24: Add EL9800_4 (PIC24) required source code 37 | \brief V4.20 LED 1: Modified LED Handling 38 | \brief V4.11 APPL 1: The checkWatchdog() function should not called in checkTimer() if this function is triggered by an Interrupt\n 39 | \brief Changes to version V4.08:\n 40 | \brief V4.10 LED 1: The handling of the EtherCAT-Error-LED was added\n 41 | \brief V4.10 AOE 3: The AoE fragment size has to be initialized during the state transition 42 | \brief from INIT to PREOP\n 43 | \brief Changes to version V4.07:\n 44 | \brief V4.08 LED 1: The handling of the EtherCAT-LED can be (de-)selected by the switch LEDS_SUPPORTED 45 | \brief because the ET1100 and ET1200 have an output port which could be connected directly.\n 46 | \brief Changes to version V4.01:\n 47 | \brief V4.02 ECAT 1: The watchdog timer variables shall be initialized.\n 48 | \brief Changes to version V4.00:\n 49 | \brief V4.01 APPL 1: If the application is running in synchron mode and no SM event 50 | \brief is received, the application should be called from the main loop\n 51 | \brief V4.01 APPL 2: In FreeRun mode the output should only be copied if the slave is in OP\n 52 | \brief Changes to version V3.20:\n 53 | \brief V4.00 APPL 1: The watchdog checking should be done by a microcontroller 54 | \brief timer because the watchdog trigger of the ESC will be reset too 55 | \brief if only a part of the sync manager data is written\n 56 | \brief V4.00 APPL 2: The setting of EtherCAT state LEDs were included\n 57 | \brief V4.00 APPL 3: The outputs should be reset to a safe state, 58 | \brief when the state OP is left\n 59 | \brief V4.00 APPL 4: An example for the EEPROM access through the ESC is shown in 60 | \brief the function APPL_StartMailboxHandler\n 61 | \brief V4.00 APPL 5: The inputs should be read once when the state transition 62 | \brief from PREOP to SAFEOP is made\n 63 | \brief V4.00 APPL 6: The main function was split in MainInit and MainLoop\n 64 | 65 | 66 | \version 5.01 67 | */ 68 | 69 | //--------------------------------------------------------------------------------------- 70 | /** 71 | \ingroup ecatappl 72 | \file ecatappl.c 73 | \brief Implementation. 74 | */ 75 | //--------------------------------------------------------------------------------------- 76 | 77 | /*----------------------------------------------------------------------------------------- 78 | ------ 79 | ------ Includes 80 | ------ 81 | -----------------------------------------------------------------------------------------*/ 82 | 83 | //#define _ECATAPPL_ 1 84 | //#include "ecatappl.h" 85 | //#undef _ECATAPPL_ 86 | //#define _ECATAPPL_ 0 87 | 88 | //#include "objdef.h" 89 | //#include "ecatslv.h" 90 | //#include "el9800appl.h" 91 | #include "ethercat.h" 92 | 93 | 94 | 95 | /*-------------------------------------------------------------------------------------- 96 | ------ 97 | ------ local Types and Defines 98 | ------ 99 | --------------------------------------------------------------------------------------*/ 100 | 101 | 102 | /*----------------------------------------------------------------------------------------- 103 | ------ 104 | ------ local variables and constants 105 | ------ 106 | -----------------------------------------------------------------------------------------*/ 107 | 108 | 109 | /*variables are declared in ecatslv.c*/ //move to class var 110 | // extern VARVOLATILE UINT16 u16dummy; 111 | /*----------------------------------------------------------------------------------------- 112 | ------ 113 | ------ local functions 114 | ------ 115 | -----------------------------------------------------------------------------------------*/ 116 | 117 | /*----------------------------------------------------------------------------------------- 118 | ------ 119 | ------ Functions 120 | ------ 121 | -----------------------------------------------------------------------------------------*/ 122 | 123 | /** 124 | \addtogroup ecatappl 125 | @{ 126 | */ 127 | ///////////////////////////////////////////////////////////////////////////////////////// 128 | /** 129 | \brief This function will copies the inputs from the local memory to the ESC memory 130 | to the hardware 131 | *//////////////////////////////////////////////////////////////////////////////////////// 132 | void Ethercat::PDO_InputMapping() 133 | { 134 | APPL_InputMapping((UINT16*)aPdInputData); 135 | HW_EscWriteIsr(((MEM_ADDR *) aPdInputData), nEscAddrInputData, nPdInputSize ); 136 | } 137 | 138 | ///////////////////////////////////////////////////////////////////////////////////////// 139 | /** 140 | \brief This function will copies the outputs from the ESC memory to the local memory 141 | to the hardware. This function is only called in case of an SM2 142 | (output process data) event. 143 | *//////////////////////////////////////////////////////////////////////////////////////// 144 | void Ethercat::PDO_OutputMapping() 145 | { 146 | 147 | HW_EscReadIsr(((MEM_ADDR *)aPdOutputData), nEscAddrOutputData, nPdOutputSize ); 148 | 149 | APPL_OutputMapping((UINT16*) aPdOutputData); 150 | } 151 | 152 | 153 | ///////////////////////////////////////////////////////////////////////////////////////// 154 | /** 155 | \brief This function shall be called every 1ms. 156 | \brief If the switch ECAT_TIMER_INT is 0, the watchdog control is implemented without using 157 | \brief interrupts. In this case a local timer register is checked every ECAT_Main cycle 158 | \brief and the function is triggered if 1 ms is elapsed 159 | *//////////////////////////////////////////////////////////////////////////////////////// 160 | 161 | void Ethercat::ECAT_CheckTimer(void) 162 | { 163 | if(sSyncManOutPar.u32CycleTime == 0) 164 | { 165 | u16BusCycleCntMs++; 166 | } 167 | 168 | /*decrement the state transition timeout counter*/ 169 | if(bEcatWaitForAlControlRes && (EsmTimeoutCounter > 0)) 170 | { 171 | EsmTimeoutCounter--; 172 | } 173 | 174 | ECAT_SetLedIndication(); 175 | 176 | DC_CheckWatchdog(); 177 | } 178 | 179 | void Ethercat::PDI_Isr(void) 180 | { 181 | /* get the AL event register */ 182 | UINT16 ALEvent = HW_GetALEventRegister_Isr(); 183 | ALEvent = SWAPWORD(ALEvent); 184 | 185 | if ( ALEvent & PROCESS_OUTPUT_EVENT ) 186 | { 187 | if(bDcSyncActive && bDcRunning) 188 | { 189 | bSmSyncToggle = FALSE; 190 | } 191 | if(sSyncManOutPar.u32SmEventMissedCounter > 0) 192 | sSyncManOutPar.u32SmEventMissedCounter--; 193 | 194 | /*calculate the cycle time if device is in SM Sync mode and Cycle time was not calculated yet*/ 195 | if ( !bDcSyncActive && bEscIntEnabled) 196 | { 197 | if(sSyncManOutPar.u16GetCycleTime == 1) 198 | { 199 | /*get bus cycle time triggered */ 200 | sSyncManOutPar.u32CycleTime = 0; 201 | sSyncManInPar.u32CycleTime = 0; 202 | u16BusCycleCntMs = 0; 203 | bCycleTimeMeasurementStarted = TRUE; 204 | sSyncManOutPar.u16GetCycleTime = 0; 205 | StartTimerCnt = (UINT32) HW_GetTimer(); 206 | } 207 | else 208 | { 209 | if(bCycleTimeMeasurementStarted == TRUE) 210 | { 211 | UINT32 CurTimerCnt = (UINT32)HW_GetTimer(); 212 | 213 | #if ECAT_TIMER_INC_P_MS 214 | sSyncManOutPar.u32CycleTime = (UINT32)u16BusCycleCntMs * 1000000 + (((INT32)(CurTimerCnt-StartTimerCnt))*1000000/ECAT_TIMER_INC_P_MS); //get elapsed cycle time in ns 215 | #else 216 | sSyncManOutPar.u32CycleTime = 0; 217 | #endif 218 | sSyncManInPar.u32CycleTime = sSyncManOutPar.u32CycleTime ; 219 | u16BusCycleCntMs = 0; 220 | StartTimerCnt = 0; 221 | bCycleTimeMeasurementStarted = FALSE; 222 | 223 | 224 | /* CiA402 Motion controller cycle time is only set if DC Synchronisation is active*/ 225 | } 226 | } 227 | } 228 | /* Outputs were updated, set flag for watchdog monitoring */ 229 | bEcatFirstOutputsReceived = TRUE; 230 | if ( bEcatOutputUpdateRunning ) 231 | { 232 | /* slave is in OP, update the outputs */ 233 | PDO_OutputMapping(); 234 | } 235 | else 236 | { 237 | HW_EscReadWordIsr(u16dummy,nEscAddrOutputData); 238 | HW_EscReadWordIsr(u16dummy,(nEscAddrOutputData+nPdOutputSize-2)); 239 | } 240 | } 241 | 242 | /*if DC Mode is enabled ECAT_Application is called from Sync0Isr*/ 243 | 244 | if ( !bDcSyncActive && bEscIntEnabled ) //only set in SM Sync mode 245 | { 246 | /* ECAT synchron Mode is active */ 247 | if ( ALEvent & (PROCESS_OUTPUT_EVENT | PROCESS_INPUT_EVENT) ) 248 | { 249 | /* Application is synchronized to SM2-event (output size > 0) 250 | or SM3-event (output size = 0, in that case PDO_InputMapping will be called 251 | from ECAT_Application to acknowledge the SM3-event) */ 252 | ECAT_Application(); 253 | } 254 | } 255 | /*if next SM event was triggered during runtime increment cycle exceed counter*/ 256 | ALEvent = HW_GetALEventRegister_Isr(); 257 | ALEvent = SWAPWORD(ALEvent); 258 | 259 | if ( ALEvent & PROCESS_OUTPUT_EVENT ) 260 | { 261 | sSyncManOutPar.u32CycleExceededCounter++; 262 | sSyncManInPar.u32CycleExceededCounter = sSyncManOutPar.u32CycleExceededCounter; 263 | } 264 | } 265 | 266 | void Ethercat::Sync0_Isr(void) 267 | { 268 | bDcRunning = TRUE; 269 | Sync0WdCounter = 0; 270 | 271 | /*if SM Sync toggle is true no SM event received between two Sync events*/ 272 | if(bSmSyncToggle) 273 | { 274 | sSyncManOutPar.u32SmEventMissedCounter = sSyncManOutPar.u32SmEventMissedCounter + 3; 275 | } 276 | bSmSyncToggle = TRUE; 277 | 278 | if(bDcSyncActive) 279 | { 280 | /* Application is synchronized to SYNC0 event*/ 281 | ECAT_Application(); 282 | } 283 | } 284 | ///////////////////////////////////////////////////////////////////////////////////////// 285 | /** 286 | 287 | \brief This function shall called within a 1ms cycle. 288 | Set Run and Error Led depending on the Ledstate 289 | 290 | *//////////////////////////////////////////////////////////////////////////////////////// 291 | void Ethercat::ECAT_SetLedIndication(void) 292 | { 293 | static UINT16 ms = 0; 294 | static UINT16 RunCounter = 0; 295 | static UINT16 ErrorCounter = 0; 296 | 297 | static UINT8 u8PrevErrorLed = LED_OFF ; 298 | static UINT8 u8PrevRunLed = LED_OFF ; 299 | 300 | // this code should be called every ms in average 301 | if ( bEcatOutputUpdateRunning ) 302 | { 303 | // in OP the EtherCAT state LED is always 1 and ErrorLED is 0 304 | bEtherCATRunLed = TRUE; 305 | bEtherCATErrorLed = FALSE; 306 | } 307 | else 308 | { 309 | ms++; 310 | if(ms == 50 || ms == 100 ||ms == 150 ||ms == 200) //set flickering LED if required 311 | { 312 | /*Set run Led State*/ 313 | switch ( nAlStatus & STATE_MASK) 314 | { 315 | case STATE_INIT: 316 | // in INIT the EtherCAT state LED is off 317 | u8EcatRunLed = LED_OFF; 318 | break; 319 | case STATE_PREOP: 320 | // in PREOP the EtherCAT state LED toggles every 200 ms 321 | u8EcatRunLed = LED_BLINKING; 322 | break; 323 | case STATE_SAFEOP: 324 | // in SAFEOP the EtherCAT state LED is 200 ms on and 1s off 325 | u8EcatRunLed = LED_SINGLEFLASH; 326 | break; 327 | case STATE_OP: 328 | u8EcatRunLed = LED_ON; 329 | break; 330 | case STATE_BOOT: 331 | u8EcatRunLed = LED_FLICKERING; 332 | break; 333 | default: 334 | u8EcatRunLed = LED_OFF; 335 | break; 336 | }//switch nAlStatus 337 | 338 | /*Calculate current Run LED state*/ 339 | if(u8EcatRunLed & 0x20 || ms == 200) //if fast flag or slow cycle event 340 | { 341 | UINT8 NumFlashes = (u8EcatRunLed & 0x1F)+((u8EcatRunLed & 0x1F)-1); //total number 342 | /*generate LED code*/ 343 | if(u8EcatRunLed != u8PrevRunLed) //state changed start with active LED 344 | { 345 | if(u8EcatRunLed & 0x80) //invert flag enable? 346 | bEtherCATRunLed = FALSE; 347 | else 348 | bEtherCATRunLed = TRUE; 349 | 350 | RunCounter = 1; 351 | } 352 | else //second and following LED cycle 353 | { 354 | if(u8EcatRunLed & 0x40) //toggle LED bit on 355 | { 356 | bEtherCATRunLed = !bEtherCATRunLed; 357 | 358 | if(NumFlashes) //NumFlashes defined => limited LED toggle 359 | { 360 | RunCounter++; 361 | 362 | if(RunCounter > NumFlashes) //toggle led finished 363 | { 364 | if(u8EcatRunLed & 0x80) //invert flag enable? 365 | bEtherCATRunLed = TRUE; 366 | else 367 | bEtherCATRunLed = FALSE; 368 | 369 | if(RunCounter > (NumFlashes+5)) //toggle time + 5 cycles low 370 | RunCounter = 0; 371 | } 372 | } 373 | } 374 | else 375 | bEtherCATRunLed = (u8EcatRunLed & 0x01); 376 | } 377 | u8PrevRunLed = u8EcatRunLed; 378 | } 379 | 380 | /*Calculate current Error LED state*/ 381 | if(u8EcatErrorLed & 0x20 || ms == 200) //if fast flag or slow cycle event 382 | { 383 | UINT8 NumFlashes = (u8EcatErrorLed & 0x1F)+((u8EcatErrorLed & 0x1F)-1); //total number 384 | /*generate LED code*/ 385 | if(u8EcatErrorLed != u8PrevErrorLed) //state changed start with active LED 386 | { 387 | if(u8EcatErrorLed & 0x80) //invert flag enable? 388 | bEtherCATErrorLed = FALSE; 389 | else 390 | bEtherCATErrorLed = TRUE; 391 | 392 | ErrorCounter = 1; 393 | } 394 | else //second and following LED cycle 395 | { 396 | if(u8EcatErrorLed & 0x40) //toggle LED bit on 397 | { 398 | bEtherCATErrorLed = !bEtherCATErrorLed; 399 | 400 | if(NumFlashes) //NumFlashes defined => limited LED toggle 401 | { 402 | ErrorCounter++; 403 | 404 | if(ErrorCounter > NumFlashes) //toggle led finished 405 | { 406 | if(u8EcatErrorLed & 0x80) //invert flag enable? 407 | bEtherCATErrorLed = TRUE; 408 | else 409 | bEtherCATErrorLed = FALSE; 410 | 411 | if(ErrorCounter > (NumFlashes+5)) //toggle time + 5 cycles low 412 | ErrorCounter = 0; 413 | } 414 | } 415 | } 416 | else 417 | bEtherCATErrorLed = (u8EcatErrorLed & 0x01); 418 | } 419 | 420 | u8PrevErrorLed = u8EcatErrorLed; 421 | } 422 | 423 | if(ms == 200) 424 | ms = 0; 425 | } 426 | } 427 | /* set the EtherCAT-LED */ 428 | HW_SetLed(((UINT8)bEtherCATRunLed),((UINT8)bEtherCATErrorLed)); 429 | } 430 | ///////////////////////////////////////////////////////////////////////////////////////// 431 | /** 432 | \param pObjectDictionary Pointer to application specific object dictionary. 433 | NULL if no specific object are available. 434 | \return 0 if initialization was successful 435 | 436 | \brief This function initialize the EtherCAT Sample Code 437 | 438 | *//////////////////////////////////////////////////////////////////////////////////////// 439 | 440 | UINT16 Ethercat::MainInit() 441 | { 442 | UINT16 Error = 0; 443 | /*Hardware init function need to be called from the application layer*/ 444 | /* initialize the EtherCAT Slave Interface */ 445 | ECAT_Init(); 446 | /* initialize the objects */ 447 | COE_ObjInit(); 448 | 449 | 450 | /*Timer initialization*/ 451 | u16BusCycleCntMs = 0; 452 | StartTimerCnt = 0; 453 | bCycleTimeMeasurementStarted = FALSE; 454 | 455 | /*Application Init need to be called from the application layer*/ 456 | return Error; 457 | } 458 | 459 | ///////////////////////////////////////////////////////////////////////////////////////// 460 | /** 461 | 462 | \brief This function shall be called cyclically from main 463 | 464 | *//////////////////////////////////////////////////////////////////////////////////////// 465 | 466 | void Ethercat::MainLoop(void) 467 | { 468 | /* FreeRun-Mode: bEscIntEnabled = FALSE, bDcSyncActive = FALSE 469 | Synchron-Mode: bEscIntEnabled = TRUE, bDcSyncActive = FALSE 470 | DC-Mode: bEscIntEnabled = FALSE, bDcSyncActive = TRUE */ 471 | if ( (!bEscIntEnabled || !bEcatFirstOutputsReceived) /* SM-Synchronous, but not SM-event received */ 472 | && !bDcSyncActive /* DC-Synchronous */ 473 | ) 474 | { 475 | /* if the application is running in ECAT Synchron Mode the function ECAT_Application is called 476 | from the ESC interrupt routine (in mcihw.c or spihw.c), 477 | in ECAT Synchron Mode it should be additionally checked, if the SM-event is received 478 | at least once (bEcatFirstOutputsReceived = 1), otherwise no interrupt is generated 479 | and the function ECAT_Application has to be called here (with interrupts disabled, 480 | because the SM-event could be generated while executing ECAT_Application) */ 481 | if ( !bEscIntEnabled ) 482 | { 483 | /* application is running in ECAT FreeRun Mode, 484 | first we have to check, if outputs were received */ 485 | UINT16 ALEvent = HW_GetALEventRegister(); 486 | ALEvent = SWAPWORD(ALEvent); 487 | 488 | if ( ALEvent & PROCESS_OUTPUT_EVENT ) 489 | { 490 | /* set the flag for the state machine behaviour */ 491 | bEcatFirstOutputsReceived = TRUE; 492 | if ( bEcatOutputUpdateRunning ) 493 | { 494 | /* update the outputs */ 495 | PDO_OutputMapping(); 496 | } 497 | } 498 | else if ( nPdOutputSize == 0 ) 499 | { 500 | /* if no outputs are transmitted, the watchdog must be reset, when the inputs were read */ 501 | if ( ALEvent & PROCESS_INPUT_EVENT ) 502 | { 503 | /* Outputs were updated, set flag for watchdog monitoring */ 504 | bEcatFirstOutputsReceived = TRUE; 505 | } 506 | } 507 | } 508 | 509 | noInterrupts(); 510 | ECAT_Application(); 511 | interrupts(); 512 | } 513 | 514 | /* there is no interrupt routine for the hardware timer so check the timer register if the desired cycle elapsed*/ 515 | { 516 | UINT16 CurTimer = HW_GetTimer(); 517 | 518 | if(CurTimer>= ECAT_TIMER_INC_P_MS) 519 | { 520 | ECAT_CheckTimer(); 521 | 522 | HW_ClearTimer(); 523 | } 524 | } 525 | 526 | /* call EtherCAT functions */ 527 | //cx 528 | // Serial.println("Mainloop"); 529 | ECAT_Main(); 530 | 531 | /* call lower prior application part */ 532 | COE_Main(); 533 | CheckIfEcatError(); 534 | 535 | } 536 | 537 | /*The main function was moved to the application files.*/ 538 | ///////////////////////////////////////////////////////////////////////////////////////// 539 | /** 540 | \brief ECAT_Application (prev. SSC versions "COE_Application") 541 | this function calculates and the physical process signals and triggers the input mapping 542 | *//////////////////////////////////////////////////////////////////////////////////////// 543 | void Ethercat::ECAT_Application(void) 544 | { 545 | { 546 | APPL_Application(); 547 | } 548 | 549 | if ( bEcatInputUpdateRunning ) 550 | { 551 | /* EtherCAT slave is at least in SAFE-OPERATIONAL, update inputs */ 552 | PDO_InputMapping(); 553 | } 554 | 555 | } 556 | 557 | /*The PDO generation and mapping functions are moved to the application files*/ 558 | /*PDO_ResetOutputs() is removed. Resetting the outputs can be done in the specific ESM function */ 559 | 560 | 561 | 562 | 563 | /** @} */ 564 | 565 | 566 | -------------------------------------------------------------------------------- /libraries/arducat/hw.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | \defgroup el9800hw el9800hw.c: EL9800 hardware abstraction 3 | \brief Hardware access implementation for EL9800 onboard PIC18/PIC24 connected via SPI to ESC\n 4 | \brief Changes to version V5.0: 5 | \brief V5.01 HW1: Invalid ESC access function was used 6 | \brief Changes to version V4.40: 7 | \brief V5.0 ESC4: Save SM disable/Enable. Operation may be pending due to frame handling. 8 | \brief Changes to version V4.30:\n 9 | \brief V4.40 : File renamed from spihw.c to el9800hw.c 10 | \brief Changes to version V4.20:\n 11 | \brief V4.30 ESM: if mailbox Syncmanger is disabled and bMbxRunning is true the SyncManger settings need to be revalidate 12 | \brief V4.30 EL9800: EL9800_x hardware initialization is moved to el9800.c 13 | \brief V4.30 SYNC: change synchronisation control function. Add usage of 0x1C32:12 [SM missed counter]. 14 | \brief Calculate bus cycle time (0x1C32:02 ; 0x1C33:02) CalcSMCycleTime() 15 | \brief V4.30 PDO: rename PDO specific functions (COE_xxMapping -> PDO_xxMapping and COE_Application -> ECAT_Application) 16 | \brief V4.30 ESC: change requested address in GetInterruptRegister() to prevent acknowledge events. 17 | \brief (e.g. reading an SM config register acknowledge SM change event) 18 | \brief GENERIC: renamed several variables to identify used SPI if multiple interfaces are available 19 | \brief V4.20 MBX 1: Add Mailbox queue support 20 | \brief V4.20 SPI 1: include SPI RxBuffer dummy read 21 | \brief V4.20 DC 1: Add Sync0 Handling 22 | \brief V4.20 PIC24: Add EL9800_4 (PIC24) required source code 23 | \brief V4.08 ECAT 3: The AlStatusCode is changed as parameter of the function AL_ControlInd\n 24 | \brief Changes to version V4.02:\n 25 | \brief V4.03 SPI 1: In ISR_GetInterruptRegister the NOP-command should be used.\n 26 | \brief Changes to version V4.01:\n 27 | \brief V4.02 SPI 1: In HW_OutputMapping the variable u16OldTimer shall not be set, 28 | \brief otherwise the watchdog might exceed too early.\n 29 | \brief Changes to version V4.00:\n 30 | \brief V4.01 SPI 1: DI and DO were changed (DI is now an input for the uC, DO is now an output for the uC)\n 31 | \brief V4.01 SPI 2: The SPI has to operate with Late-Sample = FALSE on the Eva-Board\n 32 | \brief Changes to version V3.20:\n 33 | \brief V4.00 ECAT 1: The handling of the Sync Manager Parameter was included according to 34 | \brief the EtherCAT Guidelines and Protocol Enhancements Specification\n 35 | \brief V4.00 APPL 1: The watchdog checking should be done by a microcontroller 36 | \brief timer because the watchdog trigger of the ESC will be reset too 37 | \brief if only a part of the sync manager data is written\n 38 | \brief V4.00 APPL 4: The EEPROM access through the ESC is added\n 39 | 40 | 41 | \version 5.01 42 | */ 43 | 44 | //--------------------------------------------------------------------------------------- 45 | /** 46 | \ingroup el9800hw 47 | \file el9800hw.c 48 | \brief Implementation. 49 | */ 50 | 51 | //--------------------------------------------------------------------------------------- 52 | /** 53 | \addtogroup el9800hw 54 | @{ 55 | */ 56 | //--------------------------------------------------------------------------------------- 57 | 58 | /*-------------------------------------------------------------------------------------- 59 | ------ 60 | ------ Includes 61 | ------ 62 | --------------------------------------------------------------------------------------*/ 63 | #include "ecat_def.h" 64 | 65 | 66 | 67 | #include "ethercat.h" 68 | #include 69 | 70 | /*-------------------------------------------------------------------------------------- 71 | --------------------Ethercat pin definitions-------------------------------------------- 72 | SSEL = PH2 73 | SIRQ = PE6(INT6) 74 | SYNC = PE7(INT7) 75 | 76 | 77 | ----------------------------------------------------------------------------------------*/ 78 | 79 | 80 | /*----------------------------------------------------------------------------------------- 81 | ------ 82 | ------ SPI defines/macros 83 | ------ 84 | -----------------------------------------------------------------------------------------*/ 85 | 86 | #define WAIT_SPI_IF //spi.end();//while( SPI1_IF );//while( !SPI1_IF ); 87 | #define SELECT_SPI PORTH &= ~(1<<2);//digitalWrite(15, LOW);//{(LPC_GPIO3->DATA) &= ~(SPI_ACTIVE);} 88 | #define DESELECT_SPI PORTH |= (1<<2);//digitalWrite(15, HIGH);//{(LPC_GPIO3->DATA) |= (SPI_DEACTIVE);} 89 | #define INIT_SSPIF //{(SPI1_IF)=0;} 90 | #define SPI1_CON0_VALUE 0x000000C7//0x027E 91 | #define SPI1_CON0_VALUE_16BIT 0x000000CF//0x047E 92 | #define SPI1_CON1_VALUE 0x00000002//Write at end 93 | #define SPI1_CPSR_VALUE 0x00000002//Write at end 94 | #define SPI_DEACTIVE (1<<4) 95 | #define SPI_ACTIVE (1<<4) 96 | 97 | 98 | /*----------------------------------------------------------------------------------------- 99 | ------ 100 | ------ Global Interrupt setting 101 | ------ 102 | -----------------------------------------------------------------------------------------*/ 103 | 104 | #define DISABLE_GLOBAL_INT noInterrupts(); 105 | #define ENABLE_GLOBAL_INT interrupts(); 106 | #define DISABLE_AL_EVENT_INT DISABLE_GLOBAL_INT 107 | #define ENABLE_AL_EVENT_INT ENABLE_GLOBAL_INT 108 | 109 | 110 | /*----------------------------------------------------------------------------------------- 111 | ------ 112 | ------ ESC Interrupt 113 | ------ 114 | -----------------------------------------------------------------------------------------*/ 115 | 116 | #define INIT_ESC_INT attachInterrupt(6,SIRQ_IRQHandler,LOW ); 117 | 118 | 119 | 120 | /*----------------------------------------------------------------------------------------- 121 | ------ 122 | ------ SYNC0 Interrupt 123 | ------ 124 | -----------------------------------------------------------------------------------------*/ 125 | 126 | #define INIT_SYNC0_INT //{(LPC_GPIO1->IE)|=(1<<9);(LPC_GPIO1->IS)|=(1<<9);(LPC_GPIO1->IEV)&=~(1<<9);NVIC_EnableIRQ(EINT1_IRQn);} 127 | #define DISABLE_SYNC0_INT detachInterrupt(7);// {(LPC_GPIO1->IE)&=~(1<<9);}//disable interrupt source INT3 128 | #define ENABLE_SYNC0_INT attachInterrupt(7,SYNC_IRQHandler,LOW );// {(LPC_GPIO1->IE)|=(1<<9);} //enable interrupt source INT3 129 | 130 | /*----------------------------------------------------------------------------------------- 131 | ------ 132 | ------ Hardware timer 133 | ------ 134 | -----------------------------------------------------------------------------------------*/ 135 | 136 | 137 | #define INIT_ECAT_TIMER 138 | 139 | 140 | #define STOP_ECAT_TIMER 141 | 142 | #define START_ECAT_TIMER 143 | 144 | 145 | 146 | /*-------------------------------------------------------------------------------------- 147 | ------ 148 | ------ internal Variables 149 | ------ 150 | --------------------------------------------------------------------------------------*/ 151 | //UALEVENT EscALEvent; //contains the content of the ALEvent register (0x220), this variable is updated on each Access to the Esc 152 | //UINT16 nAlEventMask; //current ALEventMask (content of register 0x204:0x205) 153 | //TSYNCMAN TmpSyncMan; 154 | 155 | /*-------------------------------------------------------------------------------------- 156 | ------ 157 | ------ internal functions 158 | ------ 159 | --------------------------------------------------------------------------------------*/ 160 | void SIRQ_IRQHandler(); 161 | void SYNC_IRQHandler(); 162 | 163 | #define CMD_SERIAL_WRITE 0x02 164 | #define CMD_SERIAL_READ 0x03 165 | 166 | UINT32 SPIReadDWord (UINT16 Address) 167 | { 168 | UINT32 Val; 169 | //Assert CS line 170 | SELECT_SPI; 171 | //Write Command 172 | SPI.transfer(CMD_SERIAL_READ); 173 | //Write Address 174 | SPI.transfer(*((UINT8*)&Address+1)); 175 | SPI.transfer(*((UINT8*)&Address+0)); 176 | //Read Bytes 177 | *((UINT8*)&Val+0) = SPI.transfer(0xFF); 178 | *((UINT8*)&Val+1) = SPI.transfer(0xFF); 179 | *((UINT8*)&Val+2) = SPI.transfer(0xFF); 180 | *((UINT8*)&Val+3) = SPI.transfer(0xFF); 181 | //De-Assert CS line 182 | DESELECT_SPI; 183 | 184 | 185 | return Val; 186 | } 187 | 188 | void SPIWriteDWord (UINT16 Address, UINT32 Val) 189 | { 190 | //Assert CS line 191 | SELECT_SPI; 192 | //Write Command 193 | SPI.transfer(CMD_SERIAL_WRITE); 194 | //Write Address 195 | SPI.transfer(*((UINT8*)&Address+1)); 196 | SPI.transfer(*((UINT8*)&Address+0)); 197 | //Write Bytes 198 | SPI.transfer(*((UINT8*)&Val+0)); 199 | SPI.transfer(*((UINT8*)&Val+1)); 200 | SPI.transfer(*((UINT8*)&Val+2)); 201 | SPI.transfer(*((UINT8*)&Val+3)); 202 | 203 | //De-Assert CS line 204 | DESELECT_SPI; 205 | } 206 | 207 | #define ESC_CSR_CMD_REG 0x304 208 | #define ESC_CSR_DATA_REG 0x300 209 | #define ESC_WRITE_BYTE 0x80 210 | #define ESC_READ_BYTE 0xC0 211 | #define ESC_CSR_BUSY 0x80 212 | void SPIReadDRegister(UINT8 *ReadBuffer, UINT16 Address, UINT8 Count) 213 | { 214 | UINT32 param32_1 = 0; 215 | UINT8 i = 0; 216 | UINT16 wAddr = Address; 217 | 218 | *((UINT8*)¶m32_1+0) = *((UINT8*)&wAddr+0); 219 | *((UINT8*)¶m32_1+1) = *((UINT8*)&wAddr+1); 220 | *((UINT8*)¶m32_1+2) = Count; 221 | *((UINT8*)¶m32_1+3) = ESC_READ_BYTE; 222 | 223 | SPIWriteDWord (ESC_CSR_CMD_REG, param32_1); 224 | 225 | do 226 | { 227 | param32_1 = SPIReadDWord (ESC_CSR_CMD_REG); 228 | 229 | }while(*((UINT8*)¶m32_1+3) & ESC_CSR_BUSY); 230 | 231 | param32_1 = SPIReadDWord (ESC_CSR_DATA_REG); 232 | 233 | 234 | for(i=0;i 359 | //Write 0x5c - 0x00000001 360 | data = 0x00000001; 361 | SPIWriteDWord (0x5C, data); 362 | SPIReadDWord(0x58); 363 | 364 | INIT_SYNC0_INT 365 | // ENABLE_SYNC0_INT; 366 | 367 | INIT_ECAT_TIMER; 368 | START_ECAT_TIMER; 369 | 370 | /* enable all interrupts */ 371 | ENABLE_GLOBAL_INT; 372 | 373 | return 0; 374 | } 375 | 376 | 377 | ///////////////////////////////////////////////////////////////////////////////////////// 378 | /** 379 | \brief This function shall be implemented if hardware resources need to be release 380 | when the sample application stops 381 | *//////////////////////////////////////////////////////////////////////////////////////// 382 | void Ethercat::HW_Release(void) 383 | { 384 | SPI.end(); 385 | } 386 | 387 | ///////////////////////////////////////////////////////////////////////////////////////// 388 | /** 389 | \return first two Bytes of ALEvent register (0x220) 390 | 391 | \brief This function gets the current content of ALEvent register 392 | *//////////////////////////////////////////////////////////////////////////////////////// 393 | UINT16 Ethercat::HW_GetALEventRegister(void) 394 | { 395 | GetInterruptRegister(); 396 | return EscALEvent.Word; 397 | } 398 | 399 | ///////////////////////////////////////////////////////////////////////////////////////// 400 | /** 401 | \return first two Bytes of ALEvent register (0x220) 402 | 403 | \brief The SPI PDI requires an extra ESC read access functions from interrupts service routines. 404 | The behaviour is equal to "HW_GetALEventRegister()" 405 | *//////////////////////////////////////////////////////////////////////////////////////// 406 | UINT16 Ethercat::HW_GetALEventRegister_Isr(void) 407 | { 408 | ISR_GetInterruptRegister(); 409 | return EscALEvent.Word; 410 | } 411 | 412 | ///////////////////////////////////////////////////////////////////////////////////////// 413 | /** 414 | \param intMask interrupt mask (disabled interrupt shall be zero) 415 | 416 | \brief This function makes an logical and with the AL Event Mask register (0x204) 417 | *//////////////////////////////////////////////////////////////////////////////////////// 418 | void Ethercat::HW_ResetALEventMask(UINT16 intMask) 419 | { 420 | UINT16 mask; 421 | 422 | HW_EscReadWord(mask, ESC_AL_EVENTMASK_OFFSET); 423 | 424 | mask &= intMask; 425 | DISABLE_AL_EVENT_INT; 426 | HW_EscWriteWord(mask, ESC_AL_EVENTMASK_OFFSET); 427 | HW_EscReadWord(nAlEventMask, ESC_AL_EVENTMASK_OFFSET); 428 | ENABLE_AL_EVENT_INT; 429 | } 430 | 431 | ///////////////////////////////////////////////////////////////////////////////////////// 432 | /** 433 | \param intMask interrupt mask (enabled interrupt shall be one) 434 | 435 | \brief This function makes an logical or with the AL Event Mask register (0x204) 436 | *//////////////////////////////////////////////////////////////////////////////////////// 437 | void Ethercat::HW_SetALEventMask(UINT16 intMask) 438 | { 439 | UINT16 mask; 440 | 441 | HW_EscReadWord(mask, ESC_AL_EVENTMASK_OFFSET); 442 | 443 | mask |= intMask; 444 | DISABLE_AL_EVENT_INT; 445 | HW_EscWriteWord(mask, ESC_AL_EVENTMASK_OFFSET); 446 | HW_EscReadWord(nAlEventMask, ESC_AL_EVENTMASK_OFFSET); 447 | ENABLE_AL_EVENT_INT; 448 | } 449 | 450 | ///////////////////////////////////////////////////////////////////////////////////////// 451 | /** 452 | \param RunLed desired EtherCAT Run led state 453 | \param ErrLed desired EtherCAT Error led state 454 | 455 | \brief This function updates the EtherCAT run and error led 456 | *//////////////////////////////////////////////////////////////////////////////////////// 457 | void Ethercat::HW_SetLed(UINT8 RunLed,UINT8 ErrLed) 458 | { 459 | // LED_ECATGREEN = RunLed; 460 | // LED_ECATRED = ErrLed; 461 | } 462 | ///////////////////////////////////////////////////////////////////////////////////////// 463 | /** 464 | \param pData Pointer to a byte array which holds data to write or saves read data. 465 | \param Address EtherCAT ASIC address ( upper limit is 0x1FFF ) for access. 466 | \param Len Access size in Bytes. 467 | 468 | \brief This function operates the SPI read access to the EtherCAT ASIC. 469 | *//////////////////////////////////////////////////////////////////////////////////////// 470 | void Ethercat::HW_EscRead( MEM_ADDR *pData, UINT16 Address, UINT16 Len ) 471 | { 472 | UINT16 i; 473 | UINT8 *pTmpData = (UINT8 *)pData; 474 | 475 | /* loop for all bytes to be read */ 476 | while ( Len > 0 ) 477 | { 478 | 479 | i= (Len > 4) ? 4 : Len; 480 | 481 | if(Address & 01) 482 | { 483 | i=1; 484 | } 485 | else if (Address & 02) 486 | { 487 | i= (i&1) ? 1:2; 488 | } 489 | else if (i == 03) 490 | { 491 | i=1; 492 | } 493 | 494 | DISABLE_AL_EVENT_INT; 495 | 496 | SPIReadDRegister(pTmpData,Address,i); 497 | 498 | ENABLE_AL_EVENT_INT; 499 | 500 | Len -= i; 501 | pTmpData += i; 502 | Address += i; 503 | } 504 | } 505 | 506 | ///////////////////////////////////////////////////////////////////////////////////////// 507 | /** 508 | \param pData Pointer to a byte array which holds data to write or saves read data. 509 | \param Address EtherCAT ASIC address ( upper limit is 0x1FFF ) for access. 510 | \param Len Access size in Bytes. 511 | 512 | \brief The SPI PDI requires an extra ESC read access functions from interrupts service routines. 513 | The behaviour is equal to "HW_EscRead()" 514 | *//////////////////////////////////////////////////////////////////////////////////////// 515 | void Ethercat::HW_EscReadIsr( MEM_ADDR *pData, UINT16 Address, UINT16 Len ) 516 | { 517 | UINT16 i; 518 | UINT8 *pTmpData = (UINT8 *)pData; 519 | 520 | /* send the address and command to the ESC */ 521 | 522 | /* loop for all bytes to be read */ 523 | while ( Len > 0 ) 524 | { 525 | 526 | i= (Len>4) ? 4:Len; 527 | 528 | if(Address & 01) 529 | { 530 | i=1; 531 | } 532 | else if (Address & 02) 533 | { 534 | i= (i&1) ? 1:2; 535 | } 536 | else if (i == 03) 537 | { 538 | i=1; 539 | } 540 | 541 | SPIReadDRegister(pTmpData, Address,i); 542 | 543 | Len -= i; 544 | pTmpData += i; 545 | Address += i; 546 | } 547 | } 548 | 549 | ///////////////////////////////////////////////////////////////////////////////////////// 550 | /** 551 | \param pData Pointer to a byte array which holds data to write or saves write data. 552 | \param Address EtherCAT ASIC address ( upper limit is 0x1FFF ) for access. 553 | \param Len Access size in Bytes. 554 | 555 | \brief This function operates the SPI write access to the EtherCAT ASIC. 556 | *//////////////////////////////////////////////////////////////////////////////////////// 557 | void Ethercat::HW_EscWrite( MEM_ADDR *pData, UINT16 Address, UINT16 Len ) 558 | { 559 | UINT16 i; 560 | UINT8 *pTmpData = (UINT8 *)pData; 561 | 562 | 563 | /* loop for all bytes to be written */ 564 | while ( Len ) 565 | { 566 | 567 | i= (Len>4) ? 4:Len; 568 | 569 | if(Address & 01) 570 | { 571 | i=1; 572 | } 573 | else if (Address & 02) 574 | { 575 | i= (i&1) ? 1:2; 576 | } 577 | else if (i == 03) 578 | { 579 | i=1; 580 | } 581 | //DISABLE_AL_EVENT_INT; 582 | DISABLE_AL_EVENT_INT; 583 | /* start transmission */ 584 | SPIWriteRegister(pTmpData, Address, i); 585 | 586 | ENABLE_AL_EVENT_INT; 587 | 588 | /* next address */ 589 | Len -= i; 590 | pTmpData += i; 591 | Address += i; 592 | 593 | } 594 | } 595 | 596 | ///////////////////////////////////////////////////////////////////////////////////////// 597 | /** 598 | \param pData Pointer to a byte array which holds data to write or saves write data. 599 | \param Address EtherCAT ASIC address ( upper limit is 0x1FFF ) for access. 600 | \param Len Access size in Bytes. 601 | 602 | \brief The SPI PDI requires an extra ESC write access functions from interrupts service routines. 603 | The behaviour is equal to "HW_EscWrite()" 604 | *//////////////////////////////////////////////////////////////////////////////////////// 605 | void Ethercat::HW_EscWriteIsr( MEM_ADDR *pData, UINT16 Address, UINT16 Len ) 606 | { 607 | UINT16 i ; 608 | UINT8 *pTmpData = (UINT8 *)pData; 609 | 610 | 611 | /* loop for all bytes to be written */ 612 | while ( Len ) 613 | { 614 | 615 | i= (Len > 4) ? 4 : Len; 616 | 617 | if(Address & 01) 618 | { 619 | i=1; 620 | } 621 | else if (Address & 02) 622 | { 623 | i= (i&1) ? 1:2; 624 | } 625 | else if (i == 03) 626 | { 627 | i=1; 628 | } 629 | 630 | /* start transmission */ 631 | SPIWriteRegister(pTmpData, Address, i); 632 | 633 | /* next address */ 634 | Len -= i; 635 | pTmpData += i; 636 | Address += i; 637 | } 638 | } 639 | 640 | 641 | 642 | ///////////////////////////////////////////////////////////////////////////////////////// 643 | /** 644 | \param channel Sync Manager channel 645 | 646 | \brief This function disables a Sync Manager channel 647 | *//////////////////////////////////////////////////////////////////////////////////////// 648 | void Ethercat::HW_DisableSyncManChannel(UINT8 channel) 649 | { 650 | UINT16 Offset; 651 | //The register 0x806 is only readable from PDI => writing 0 is valid 652 | UINT16 smStatus = SM_SETTING_PDI_DISABLE; 653 | Offset = (ESC_SYNCMAN_ACTIVE_OFFSET + (SIZEOF_SM_REGISTER*channel)); 654 | 655 | HW_EscWriteWord(smStatus,Offset); 656 | 657 | /*wait until SyncManager is disabled*/ 658 | do 659 | { 660 | HW_EscReadWord(smStatus, Offset); 661 | }while(!(smStatus & SM_SETTING_PDI_DISABLE)); 662 | } 663 | 664 | ///////////////////////////////////////////////////////////////////////////////////////// 665 | /** 666 | \param channel Sync Manager channel 667 | 668 | \brief This function enables a Sync Manager channel 669 | *//////////////////////////////////////////////////////////////////////////////////////// 670 | void Ethercat::HW_EnableSyncManChannel(UINT8 channel) 671 | { 672 | UINT16 Offset; 673 | //The register 0x806 is only readable from PDI => writing 0 is valid 674 | UINT16 smStatus = 0x0000; 675 | Offset = (ESC_SYNCMAN_ACTIVE_OFFSET + (SIZEOF_SM_REGISTER*channel)); 676 | 677 | HW_EscWriteWord(smStatus,Offset); 678 | 679 | /*wait until SyncManager is enabled*/ 680 | do 681 | { 682 | HW_EscReadWord(smStatus,Offset); 683 | }while((smStatus & SM_SETTING_PDI_DISABLE)); 684 | } 685 | 686 | ///////////////////////////////////////////////////////////////////////////////////////// 687 | /** 688 | \param channel requested Sync Manger channel information 689 | 690 | \return Pointer to the SYNC Manager channel description 691 | 692 | \brief This function is called to read the SYNC Manager channel descriptions of the 693 | process data SYNC Managers. 694 | *//////////////////////////////////////////////////////////////////////////////////////// 695 | 696 | TSYNCMAN ESCMEM * Ethercat::HW_GetSyncMan(UINT8 channel) 697 | { 698 | // get a temporary structure of the Sync Manager: 699 | HW_EscRead( (MEM_ADDR *) &TmpSyncMan, ESC_SYNCMAN_REG_OFFSET + (channel * SYNCMAN_REG_SIZE), SYNCMAN_REG_SIZE ); 700 | 701 | return &TmpSyncMan; 702 | } 703 | 704 | 705 | ///////////////////////////////////////////////////////////////////////////////////////// 706 | /** 707 | \brief Interrupt service routine for the PDI interrupt from the EtherCAT Slave Controller 708 | *//////////////////////////////////////////////////////////////////////////////////////// 709 | 710 | //void __attribute__ ((__interrupt__, no_auto_psv)) EscIsr & SYNC0(void) 711 | void SIRQ_IRQHandler() 712 | { 713 | ethercat.PDI_Isr(); 714 | } 715 | void SYNC_IRQHandler() 716 | { 717 | ethercat.Sync0_Isr(); 718 | } 719 | 720 | 721 | 722 | 723 | 724 | --------------------------------------------------------------------------------