diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/.gitignore b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/.gitignore new file mode 100755 index 0000000..fb3ca12 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/.gitignore @@ -0,0 +1,6 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch +.vscode/settings.json diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/.vscode/extensions.json b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/.vscode/extensions.json new file mode 100755 index 0000000..080e70d --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/include/README b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/include/README new file mode 100755 index 0000000..45496b1 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/include/extend32to64.h b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/include/extend32to64.h new file mode 100755 index 0000000..b6c3b0d --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/include/extend32to64.h @@ -0,0 +1,14 @@ +#ifndef EXTEND32TO64 +#define EXTEND32TO54 + +#include + +class extend32to64 +{ +public: + int64_t previousTimeValue = 0; + const uint64_t ONE_PERIOD = 4294967296; // almost UINT32_MAX; + const uint64_t HALF_PERIOD = 2147483648; // Half of that + int64_t extendTime(uint32_t in); +}; +#endif diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/README b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/README new file mode 100755 index 0000000..8c9c29c --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/MetalMusings_EaserCAT_6000.xml b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/MetalMusings_EaserCAT_6000.xml new file mode 100755 index 0000000..d83d547 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/MetalMusings_EaserCAT_6000.xml @@ -0,0 +1,1137 @@ + + + + 2730 + MetalMusings + + + + + MachineControl + Incremental encoder + + + + + EaserCAT 6000 + MetalMusings EaserCAT 6000 + MachineControl + + 5001 + 0 + + + + DT1018 + 144 + + 0 + Max SubIndex + USINT + 8 + 0 + + ro + + + + 1 + Vendor ID + UDINT + 32 + 16 + + ro + + + + 2 + Product Code + UDINT + 32 + 48 + + ro + + + + 3 + Revision Number + UDINT + 32 + 80 + + ro + + + + 4 + Serial Number + UDINT + 32 + 112 + + ro + + + + + DT1600 + 144 + + 0 + Max SubIndex + USINT + 8 + 0 + + ro + + + + 1 + New array subitem + UDINT + 32 + 16 + + ro + + + + 2 + New array subitem + UDINT + 32 + 48 + + ro + + + + 3 + New array subitem + UDINT + 32 + 80 + + ro + + + + 4 + New array subitem + UDINT + 32 + 112 + + ro + + + + + DT1A00 + 400 + + 0 + Max SubIndex + USINT + 8 + 0 + + ro + + + + 1 + New array subitem + UDINT + 32 + 16 + + ro + + + + 2 + New array subitem + UDINT + 32 + 48 + + ro + + + + 3 + New array subitem + UDINT + 32 + 80 + + ro + + + + 4 + New array subitem + UDINT + 32 + 112 + + ro + + + + 5 + New array subitem + UDINT + 32 + 144 + + ro + + + + 6 + New array subitem + UDINT + 32 + 176 + + ro + + + + 7 + New array subitem + UDINT + 32 + 208 + + ro + + + + 8 + New array subitem + UDINT + 32 + 240 + + ro + + + + 9 + New array subitem + UDINT + 32 + 272 + + ro + + + + 10 + New array subitem + UDINT + 32 + 304 + + ro + + + + 11 + New array subitem + UDINT + 32 + 336 + + ro + + + + 12 + New array subitem + UDINT + 32 + 368 + + ro + + + + + DT1A01 + 48 + + 0 + Max SubIndex + USINT + 8 + 0 + + ro + + + + 1 + Velocity + UDINT + 32 + 16 + + ro + + + + + DT1C00ARR + USINT + 32 + + 1 + 4 + + + + DT1C00 + 48 + + 0 + Max SubIndex + USINT + 8 + 0 + + ro + + + + Elements + DT1C00ARR + 32 + 16 + + ro + + + + + DT1C12ARR + UINT + 16 + + 1 + 1 + + + + DT1C12 + 32 + + 0 + Max SubIndex + USINT + 8 + 0 + + ro + + + + Elements + DT1C12ARR + 16 + 16 + + ro + + + + + DT1C13ARR + UINT + 32 + + 1 + 2 + + + + DT1C13 + 48 + + 0 + Max SubIndex + USINT + 8 + 0 + + ro + + + + Elements + DT1C13ARR + 32 + 16 + + ro + + + + + DT6000ARR + BOOL + 12 + + 1 + 12 + + + + DT6000 + 28 + + 0 + Max SubIndex + USINT + 8 + 0 + + ro + + + + Elements + DT6000ARR + 12 + 16 + + ro + T + + + + + DT7000ARR + BOOL + 4 + + 1 + 4 + + + + DT7000 + 20 + + 0 + Max SubIndex + USINT + 8 + 0 + + ro + + + + Elements + DT7000ARR + 4 + 16 + + ro + R + + + + + UDINT + 32 + + + STRING(26) + 208 + + + STRING(5) + 40 + + + USINT + 8 + + + UINT + 16 + + + REAL + 32 + + + BOOL + 1 + + + + + #x1000 + Device Type + UDINT + 32 + + 5001 + + + ro + m + + + + #x1008 + Device Name + STRING(26) + 208 + + MetalMusings EaserCAT 6000 + + + ro + + + + #x1009 + Hardware Version + STRING(5) + 40 + + 0.0.1 + + + ro + o + + + + #x100A + Software Version + STRING(5) + 40 + + 0.0.1 + + + ro + + + + #x1018 + Identity Object + DT1018 + 144 + + + Max SubIndex + + 4 + + + + Vendor ID + + 2730 + + + + Product Code + + 13294767 + + + + Revision Number + + 3 + + + + Serial Number + + 1 + + + + + ro + + + + #x1600 + Output + DT1600 + 144 + + + Max SubIndex + + 4 + + + + New array subitem + + #x70000101 + + + + New array subitem + + #x70000201 + + + + New array subitem + + #x70000301 + + + + New array subitem + + #x70000401 + + + + + ro + + + + #x1A00 + Input + DT1A00 + 400 + + + Max SubIndex + + 12 + + + + New array subitem + + #x60000101 + + + + New array subitem + + #x60000201 + + + + New array subitem + + #x60000301 + + + + New array subitem + + #x60000401 + + + + New array subitem + + #x60000501 + + + + New array subitem + + #x60000601 + + + + New array subitem + + #x60000701 + + + + New array subitem + + #x60000801 + + + + New array subitem + + #x60000901 + + + + New array subitem + + #x60000a01 + + + + New array subitem + + #x60000b01 + + + + New array subitem + + #x60000c01 + + + + + ro + + + + #x1A01 + Velocity + DT1A01 + 48 + + + Max SubIndex + + 1 + + + + Velocity + + #x60010020 + + + + + ro + + + + #x1C00 + Sync Manager Communication Type + DT1C00 + 48 + + + Max SubIndex + + 4 + + + + Communications Type SM0 + + 1 + + + + Communications Type SM1 + + 2 + + + + Communications Type SM2 + + 3 + + + + Communications Type SM3 + + 4 + + + + + ro + + + + #x1C12 + Sync Manager 2 PDO Assignment + DT1C12 + 32 + + + Max SubIndex + + 1 + + + + PDO Mapping + + #x1600 + + + + + ro + + + + #x1C13 + Sync Manager 3 PDO Assignment + DT1C13 + 48 + + + Max SubIndex + + 2 + + + + PDO Mapping + + #x1A00 + + + + PDO Mapping + + #x1A01 + + + + + ro + + + + #x2000 + VelocityScale + REAL + 32 + + 0 + + + ro + + + + #x6000 + Input + DT6000 + 28 + + + Max SubIndex + + 12 + + + + New array subitem + + 0 + + + + New array subitem + + 0 + + + + New array subitem + + 0 + + + + New array subitem + + 0 + + + + New array subitem + + 0 + + + + New array subitem + + 0 + + + + New array subitem + + 0 + + + + New array subitem + + 0 + + + + New array subitem + + 0 + + + + New array subitem + + 0 + + + + New array subitem + + 0 + + + + New array subitem + + 0 + + + + + ro + + + + #x6001 + Velocity + REAL + 32 + + 0 + + + ro + T + + + + #x7000 + Output + DT7000 + 20 + + + Max SubIndex + + 4 + + + + New array subitem + + 0 + + + + New array subitem + + 0 + + + + New array subitem + + 0 + + + + New array subitem + + 0 + + + + + ro + + + + + + Outputs + Inputs + MBoxState + MBoxOut + MBoxIn + Outputs + Inputs + + #x1600 + Output + + #x7000 + #x1 + 1 + New array subitem + BOOL + + + #x7000 + #x2 + 1 + New array subitem + BOOL + + + #x7000 + #x3 + 1 + New array subitem + BOOL + + + #x7000 + #x4 + 1 + New array subitem + BOOL + + + + #x1A00 + Input + + #x6000 + #x1 + 1 + New array subitem + BOOL + + + #x6000 + #x2 + 1 + New array subitem + BOOL + + + #x6000 + #x3 + 1 + New array subitem + BOOL + + + #x6000 + #x4 + 1 + New array subitem + BOOL + + + #x6000 + #x5 + 1 + New array subitem + BOOL + + + #x6000 + #x6 + 1 + New array subitem + BOOL + + + #x6000 + #x7 + 1 + New array subitem + BOOL + + + #x6000 + #x8 + 1 + New array subitem + BOOL + + + #x6000 + #x9 + 1 + New array subitem + BOOL + + + #x6000 + #xa + 1 + New array subitem + BOOL + + + #x6000 + #xb + 1 + New array subitem + BOOL + + + #x6000 + #xc + 1 + New array subitem + BOOL + + + + #x1A01 + Velocity + + #x6001 + #x0 + 32 + Velocity + REAL + + + + + + + + SM-Synchron + SM-Synchron + #x000 + + + DC + DC-Synchron + #x300 + + + + 2048 + 80060344640000 + + + + + \ No newline at end of file diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/ecat_options.h b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/ecat_options.h new file mode 100755 index 0000000..6f10889 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/ecat_options.h @@ -0,0 +1,42 @@ +#ifndef __ECAT_OPTIONS_H__ +#define __ECAT_OPTIONS_H__ + +#define USE_FOE 0 +#define USE_EOE 0 + +#define MBXSIZE 512 +#define MBXSIZEBOOT 512 +#define MBXBUFFERS 3 + +#define MBX0_sma 0x1000 +#define MBX0_sml MBXSIZE +#define MBX0_sme MBX0_sma+MBX0_sml-1 +#define MBX0_smc 0x26 +#define MBX1_sma MBX0_sma+MBX0_sml +#define MBX1_sml MBXSIZE +#define MBX1_sme MBX1_sma+MBX1_sml-1 +#define MBX1_smc 0x22 + +#define MBX0_sma_b 0x1000 +#define MBX0_sml_b MBXSIZEBOOT +#define MBX0_sme_b MBX0_sma_b+MBX0_sml_b-1 +#define MBX0_smc_b 0x26 +#define MBX1_sma_b MBX0_sma_b+MBX0_sml_b +#define MBX1_sml_b MBXSIZEBOOT +#define MBX1_sme_b MBX1_sma_b+MBX1_sml_b-1 +#define MBX1_smc_b 0x22 + +#define SM2_sma 0x1600 +#define SM2_smc 0x24 +#define SM2_act 1 +#define SM3_sma 0x1A00 +#define SM3_smc 0x20 +#define SM3_act 1 + +#define MAX_MAPPINGS_SM2 4 +#define MAX_MAPPINGS_SM3 13 + +#define MAX_RXPDO_SIZE 512 +#define MAX_TXPDO_SIZE 512 + +#endif /* __ECAT_OPTIONS_H__ */ diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/eeprom.bin b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/eeprom.bin new file mode 100755 index 0000000..60cff24 Binary files /dev/null and b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/eeprom.bin differ diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/eeprom.hex b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/eeprom.hex new file mode 100755 index 0000000..d157a2e --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/eeprom.hex @@ -0,0 +1,65 @@ +:2000000080060344640000000000000000001400AA0A0000AFDCCA0003000000010000008E +:20002000000000000000000000000000000000000010000200120002040000000000000096 +:200040000000000000000000000000000000000000000000000000000000000000000000A0 +:20006000000000000000000000000000000000000000000000000000000000000F00010070 +:200080000A002000040D456173657243415420363030300E4D616368696E65436F6E7472AE +:2000A0006F6C06494D474342591A4D6574616C4D7573696E677320456173657243415420A9 +:2000C000363030301E00100002030104001F000000000000000000001100000000000000F2 +:2000E000000000000000000028000200010203002900100000100002260001010012000249 +:20010000220001020016000024000103001A000020000104FFFFFFFFFFFFFFFFFFFFFFFF49 +:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF +:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF +:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F +:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F +:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F +:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F +:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F +:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE +:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE +:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE +:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E +:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E +:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E +:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E +:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E +:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD +:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD +:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD +:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D +:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D +:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D +:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D +:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D +:20040000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC +:20042000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC +:20044000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC +:20046000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C +:20048000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C +:2004A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C +:2004C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C +:2004E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C +:20050000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB +:20052000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDB +:20054000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBB +:20056000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9B +:20058000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7B +:2005A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B +:2005C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3B +:2005E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1B +:20060000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA +:20062000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDA +:20064000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBA +:20066000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9A +:20068000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7A +:2006A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A +:2006C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3A +:2006E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1A +:20070000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9 +:20072000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9 +:20074000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9 +:20076000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF99 +:20078000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF79 +:2007A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF59 +:2007C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF39 +:2007E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF19 +:00000001FF \ No newline at end of file diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/esi.json b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/esi.json new file mode 100755 index 0000000..8ab241d --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/esi.json @@ -0,0 +1,225 @@ +{ + "form": { + "VendorName": "MetalMusings", + "VendorID": "0xaaa", + "ProductCode": "0xcadcaf", + "ProfileNo": "5001", + "RevisionNumber": "0x003", + "SerialNumber": "0x001", + "HWversion": "0.0.1", + "SWversion": "0.0.1", + "EEPROMsize": "2048", + "RxMailboxOffset": "0x1000", + "TxMailboxOffset": "0x1200", + "MailboxSize": "512", + "SM2Offset": "0x1600", + "SM3Offset": "0x1A00", + "TextGroupType": "MachineControl", + "TextGroupName5": "Incremental encoder", + "ImageName": "IMGCBY", + "TextDeviceType": "EaserCAT 6000", + "TextDeviceName": "MetalMusings EaserCAT 6000", + "Port0Physical": "Y", + "Port1Physical": "Y", + "Port2Physical": " ", + "Port3Physical": " ", + "ESC": "LAN9252", + "SPImode": "3", + "CoeDetailsEnableSDO": "EnableSDO", + "CoeDetailsEnableSDOInfo": "EnableSDOInfo", + "CoeDetailsEnablePDOAssign": "EnablePDOAssign", + "CoeDetailsEnablePDOConfiguration": "EnablePDOConfiguration", + "CoeDetailsEnableUploadAtStartup": "EnableUploadAtStartup", + "CoeDetailsEnableSDOCompleteAccess": "EnableSDOCompleteAccess" + }, + "od": { + "sdo": { + "2000": { + "otype": "VAR", + "name": "VelocityScale", + "access": "RO", + "dtype": "REAL32", + "value": "0", + "isSDOitem": true, + "data": "&Obj.VelocityScale" + }, + "A": { + "otype": "RECORD", + "name": "Error Settings", + "access": "RO", + "items": [ + { + "name": "Max SubIndex" + }, + { + "name": "New record subitem", + "dtype": "UNSIGNED8" + } + ] + } + }, + "txpdo": { + "600": { + "otype": "VAR", + "name": "EncFrequency", + "access": "RO", + "pdo_mappings": [ + "txpdo" + ], + "dtype": "INTEGER32", + "value": "0", + "data": "&Obj.EncFrequency" + }, + "6000": { + "otype": "ARRAY", + "name": "Input", + "access": "RO", + "items": [ + { + "name": "Max SubIndex" + }, + { + "name": "New array subitem", + "data": "&Obj.Input[0]", + "value": "0" + }, + { + "name": "New array subitem", + "value": "0", + "data": "&Obj.Input[1]" + }, + { + "name": "New array subitem", + "value": "0", + "data": "&Obj.Input[2]" + }, + { + "name": "New array subitem", + "value": "0", + "data": "&Obj.Input[3]" + }, + { + "name": "New array subitem", + "value": "0", + "data": "&Obj.Input[4]" + }, + { + "name": "New array subitem", + "value": "0", + "data": "&Obj.Input[5]" + }, + { + "name": "New array subitem", + "value": "0", + "data": "&Obj.Input[6]" + }, + { + "name": "New array subitem", + "value": "0", + "data": "&Obj.Input[7]" + }, + { + "name": "New array subitem", + "value": "0", + "data": "&Obj.Input[8]" + }, + { + "name": "New array subitem", + "value": "0", + "data": "&Obj.Input[9]" + }, + { + "name": "New array subitem", + "value": "0", + "data": "&Obj.Input[10]" + }, + { + "name": "New array subitem", + "value": "0", + "data": "&Obj.Input[11]" + } + ], + "pdo_mappings": [ + "txpdo" + ], + "dtype": "BOOLEAN" + }, + "6001": { + "otype": "VAR", + "name": "Velocity", + "access": "RO", + "pdo_mappings": [ + "txpdo" + ], + "dtype": "REAL32", + "value": "0", + "data": "&Obj.Velocity" + } + }, + "rxpdo": { + "7000": { + "otype": "ARRAY", + "name": "Output", + "access": "RO", + "items": [ + { + "name": "Max SubIndex" + }, + { + "name": "New array subitem", + "data": "&Obj.Output[0]" + }, + { + "name": "New array subitem", + "value": "0", + "data": "&Obj.Output[1]" + }, + { + "name": "New array subitem", + "value": "0", + "data": "&Obj.Output[2]" + }, + { + "name": "New array subitem", + "value": "0", + "data": "&Obj.Output[3]" + } + ], + "pdo_mappings": [ + "rxpdo" + ], + "dtype": "BOOLEAN" + }, + "60664": { + "otype": "VAR", + "name": "ActualPosition", + "access": "RO", + "pdo_mappings": [ + "rxpdo" + ], + "dtype": "INTEGER32", + "value": "0" + } + } + }, + "dc": [ + { + "Name": "SM-Synchron", + "Description": "SM-Synchron", + "AssignActivate": "#x000", + "Sync0cycleTime": "0", + "Sync0shiftTime": "0", + "Sync1cycleTime": "0", + "Sync1shiftTime": "0" + }, + { + "Name": "DC", + "Description": "DC-Synchron", + "AssignActivate": "#x300", + "Sync0cycleTime": "0", + "Sync0shiftTime": "0", + "Sync1cycleTime": "0", + "Sync1shiftTime": "0" + } + ] +} \ No newline at end of file diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/objectlist.c b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/objectlist.c new file mode 100755 index 0000000..6a74bed --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/objectlist.c @@ -0,0 +1,198 @@ +#include "esc_coe.h" +#include "utypes.h" +#include + + +static const char acName1000[] = "Device Type"; +static const char acName1008[] = "Device Name"; +static const char acName1009[] = "Hardware Version"; +static const char acName100A[] = "Software Version"; +static const char acName1018[] = "Identity Object"; +static const char acName1018_00[] = "Max SubIndex"; +static const char acName1018_01[] = "Vendor ID"; +static const char acName1018_02[] = "Product Code"; +static const char acName1018_03[] = "Revision Number"; +static const char acName1018_04[] = "Serial Number"; +static const char acName1600[] = "Output"; +static const char acName1600_00[] = "Max SubIndex"; +static const char acName1600_01[] = "New array subitem"; +static const char acName1600_02[] = "New array subitem"; +static const char acName1600_03[] = "New array subitem"; +static const char acName1600_04[] = "New array subitem"; +static const char acName1A00[] = "Input"; +static const char acName1A00_00[] = "Max SubIndex"; +static const char acName1A00_01[] = "New array subitem"; +static const char acName1A00_02[] = "New array subitem"; +static const char acName1A00_03[] = "New array subitem"; +static const char acName1A00_04[] = "New array subitem"; +static const char acName1A00_05[] = "New array subitem"; +static const char acName1A00_06[] = "New array subitem"; +static const char acName1A00_07[] = "New array subitem"; +static const char acName1A00_08[] = "New array subitem"; +static const char acName1A00_09[] = "New array subitem"; +static const char acName1A00_0a[] = "New array subitem"; +static const char acName1A00_0b[] = "New array subitem"; +static const char acName1A00_0c[] = "New array subitem"; +static const char acName1A01[] = "Velocity"; +static const char acName1A01_00[] = "Max SubIndex"; +static const char acName1A01_01[] = "Velocity"; +static const char acName1C00[] = "Sync Manager Communication Type"; +static const char acName1C00_00[] = "Max SubIndex"; +static const char acName1C00_01[] = "Communications Type SM0"; +static const char acName1C00_02[] = "Communications Type SM1"; +static const char acName1C00_03[] = "Communications Type SM2"; +static const char acName1C00_04[] = "Communications Type SM3"; +static const char acName1C12[] = "Sync Manager 2 PDO Assignment"; +static const char acName1C12_00[] = "Max SubIndex"; +static const char acName1C12_01[] = "PDO Mapping"; +static const char acName1C13[] = "Sync Manager 3 PDO Assignment"; +static const char acName1C13_00[] = "Max SubIndex"; +static const char acName1C13_01[] = "PDO Mapping"; +static const char acName1C13_02[] = "PDO Mapping"; +static const char acName2000[] = "VelocityScale"; +static const char acName6000[] = "Input"; +static const char acName6000_00[] = "Max SubIndex"; +static const char acName6000_01[] = "New array subitem"; +static const char acName6000_02[] = "New array subitem"; +static const char acName6000_03[] = "New array subitem"; +static const char acName6000_04[] = "New array subitem"; +static const char acName6000_05[] = "New array subitem"; +static const char acName6000_06[] = "New array subitem"; +static const char acName6000_07[] = "New array subitem"; +static const char acName6000_08[] = "New array subitem"; +static const char acName6000_09[] = "New array subitem"; +static const char acName6000_0a[] = "New array subitem"; +static const char acName6000_0b[] = "New array subitem"; +static const char acName6000_0c[] = "New array subitem"; +static const char acName6001[] = "Velocity"; +static const char acName7000[] = "Output"; +static const char acName7000_00[] = "Max SubIndex"; +static const char acName7000_01[] = "New array subitem"; +static const char acName7000_02[] = "New array subitem"; +static const char acName7000_03[] = "New array subitem"; +static const char acName7000_04[] = "New array subitem"; + +const _objd SDO1000[] = +{ + {0x0, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1000, 5001, NULL}, +}; +const _objd SDO1008[] = +{ + {0x0, DTYPE_VISIBLE_STRING, 208, ATYPE_RO, acName1008, 0, "MetalMusings EaserCAT 6000"}, +}; +const _objd SDO1009[] = +{ + {0x0, DTYPE_VISIBLE_STRING, 40, ATYPE_RO, acName1009, 0, "0.0.1"}, +}; +const _objd SDO100A[] = +{ + {0x0, DTYPE_VISIBLE_STRING, 40, ATYPE_RO, acName100A, 0, "0.0.1"}, +}; +const _objd SDO1018[] = +{ + {0x00, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1018_00, 4, NULL}, + {0x01, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1018_01, 2730, NULL}, + {0x02, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1018_02, 13294767, NULL}, + {0x03, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1018_03, 3, NULL}, + {0x04, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1018_04, 1, &Obj.serial}, +}; +const _objd SDO1600[] = +{ + {0x00, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1600_00, 4, NULL}, + {0x01, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1600_01, 0x70000101, NULL}, + {0x02, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1600_02, 0x70000201, NULL}, + {0x03, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1600_03, 0x70000301, NULL}, + {0x04, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1600_04, 0x70000401, NULL}, +}; +const _objd SDO1A00[] = +{ + {0x00, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1A00_00, 12, NULL}, + {0x01, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1A00_01, 0x60000101, NULL}, + {0x02, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1A00_02, 0x60000201, NULL}, + {0x03, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1A00_03, 0x60000301, NULL}, + {0x04, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1A00_04, 0x60000401, NULL}, + {0x05, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1A00_05, 0x60000501, NULL}, + {0x06, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1A00_06, 0x60000601, NULL}, + {0x07, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1A00_07, 0x60000701, NULL}, + {0x08, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1A00_08, 0x60000801, NULL}, + {0x09, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1A00_09, 0x60000901, NULL}, + {0x0a, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1A00_0a, 0x60000a01, NULL}, + {0x0b, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1A00_0b, 0x60000b01, NULL}, + {0x0c, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1A00_0c, 0x60000c01, NULL}, +}; +const _objd SDO1A01[] = +{ + {0x00, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1A01_00, 1, NULL}, + {0x01, DTYPE_UNSIGNED32, 32, ATYPE_RO, acName1A01_01, 0x60010020, NULL}, +}; +const _objd SDO1C00[] = +{ + {0x00, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1C00_00, 4, NULL}, + {0x01, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1C00_01, 1, NULL}, + {0x02, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1C00_02, 2, NULL}, + {0x03, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1C00_03, 3, NULL}, + {0x04, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1C00_04, 4, NULL}, +}; +const _objd SDO1C12[] = +{ + {0x00, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1C12_00, 1, NULL}, + {0x01, DTYPE_UNSIGNED16, 16, ATYPE_RO, acName1C12_01, 0x1600, NULL}, +}; +const _objd SDO1C13[] = +{ + {0x00, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName1C13_00, 2, NULL}, + {0x01, DTYPE_UNSIGNED16, 16, ATYPE_RO, acName1C13_01, 0x1A00, NULL}, + {0x02, DTYPE_UNSIGNED16, 16, ATYPE_RO, acName1C13_02, 0x1A01, NULL}, +}; +const _objd SDO2000[] = +{ + {0x0, DTYPE_REAL32, 32, ATYPE_RO, acName2000, 0x00000000, &Obj.VelocityScale}, +}; +const _objd SDO6000[] = +{ + {0x00, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName6000_00, 12, NULL}, + {0x01, DTYPE_BOOLEAN, 1, ATYPE_RO | ATYPE_TXPDO, acName6000_01, 0, &Obj.Input[0]}, + {0x02, DTYPE_BOOLEAN, 1, ATYPE_RO | ATYPE_TXPDO, acName6000_02, 0, &Obj.Input[1]}, + {0x03, DTYPE_BOOLEAN, 1, ATYPE_RO | ATYPE_TXPDO, acName6000_03, 0, &Obj.Input[2]}, + {0x04, DTYPE_BOOLEAN, 1, ATYPE_RO | ATYPE_TXPDO, acName6000_04, 0, &Obj.Input[3]}, + {0x05, DTYPE_BOOLEAN, 1, ATYPE_RO | ATYPE_TXPDO, acName6000_05, 0, &Obj.Input[4]}, + {0x06, DTYPE_BOOLEAN, 1, ATYPE_RO | ATYPE_TXPDO, acName6000_06, 0, &Obj.Input[5]}, + {0x07, DTYPE_BOOLEAN, 1, ATYPE_RO | ATYPE_TXPDO, acName6000_07, 0, &Obj.Input[6]}, + {0x08, DTYPE_BOOLEAN, 1, ATYPE_RO | ATYPE_TXPDO, acName6000_08, 0, &Obj.Input[7]}, + {0x09, DTYPE_BOOLEAN, 1, ATYPE_RO | ATYPE_TXPDO, acName6000_09, 0, &Obj.Input[8]}, + {0x0a, DTYPE_BOOLEAN, 1, ATYPE_RO | ATYPE_TXPDO, acName6000_0a, 0, &Obj.Input[9]}, + {0x0b, DTYPE_BOOLEAN, 1, ATYPE_RO | ATYPE_TXPDO, acName6000_0b, 0, &Obj.Input[10]}, + {0x0c, DTYPE_BOOLEAN, 1, ATYPE_RO | ATYPE_TXPDO, acName6000_0c, 0, &Obj.Input[11]}, +}; +const _objd SDO6001[] = +{ + {0x0, DTYPE_REAL32, 32, ATYPE_RO | ATYPE_TXPDO, acName6001, 0x00000000, &Obj.Velocity}, +}; +const _objd SDO7000[] = +{ + {0x00, DTYPE_UNSIGNED8, 8, ATYPE_RO, acName7000_00, 4, NULL}, + {0x01, DTYPE_BOOLEAN, 1, ATYPE_RO | ATYPE_RXPDO, acName7000_01, 0, &Obj.Output[0]}, + {0x02, DTYPE_BOOLEAN, 1, ATYPE_RO | ATYPE_RXPDO, acName7000_02, 0, &Obj.Output[1]}, + {0x03, DTYPE_BOOLEAN, 1, ATYPE_RO | ATYPE_RXPDO, acName7000_03, 0, &Obj.Output[2]}, + {0x04, DTYPE_BOOLEAN, 1, ATYPE_RO | ATYPE_RXPDO, acName7000_04, 0, &Obj.Output[3]}, +}; + +const _objectlist SDOobjects[] = +{ + {0x1000, OTYPE_VAR, 0, 0, acName1000, SDO1000}, + {0x1008, OTYPE_VAR, 0, 0, acName1008, SDO1008}, + {0x1009, OTYPE_VAR, 0, 0, acName1009, SDO1009}, + {0x100A, OTYPE_VAR, 0, 0, acName100A, SDO100A}, + {0x1018, OTYPE_RECORD, 4, 0, acName1018, SDO1018}, + {0x1600, OTYPE_RECORD, 4, 0, acName1600, SDO1600}, + {0x1A00, OTYPE_RECORD, 12, 0, acName1A00, SDO1A00}, + {0x1A01, OTYPE_RECORD, 1, 0, acName1A01, SDO1A01}, + {0x1C00, OTYPE_ARRAY, 4, 0, acName1C00, SDO1C00}, + {0x1C12, OTYPE_ARRAY, 1, 0, acName1C12, SDO1C12}, + {0x1C13, OTYPE_ARRAY, 2, 0, acName1C13, SDO1C13}, + {0x2000, OTYPE_VAR, 0, 0, acName2000, SDO2000}, + {0x6000, OTYPE_ARRAY, 12, 0, acName6000, SDO6000}, + {0x6001, OTYPE_VAR, 0, 0, acName6001, SDO6001}, + {0x7000, OTYPE_ARRAY, 4, 0, acName7000, SDO7000}, + {0xffff, 0xff, 0xff, 0xff, NULL, NULL} +}; diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/utypes.h b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/utypes.h new file mode 100755 index 0000000..0590ed8 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes-esi/utypes.h @@ -0,0 +1,30 @@ +#ifndef __UTYPES_H__ +#define __UTYPES_H__ + +#include "cc.h" + +/* Object dictionary storage */ + +typedef struct +{ + /* Identity */ + + uint32_t serial; + + /* Inputs */ + + uint8_t Input[12]; + float Velocity; + + /* Outputs */ + + uint8_t Output[4]; + + /* Parameters */ + + float VelocityScale; +} _Objects; + +extern _Objects Obj; + +#endif /* __UTYPES_H__ */ diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/CMakeLists.txt b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/CMakeLists.txt new file mode 100755 index 0000000..d39dfd9 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/CMakeLists.txt @@ -0,0 +1,29 @@ + +# NOTE: add headers to make them show up in an IDE +add_library (soes + esc.c + esc.h + esc_coe.c + esc_coe.h + esc_foe.c + esc_foe.h + esc_eoe.c + esc_eoe.h + esc_eep.c + esc_eep.h + ecat_slv.c + ecat_slv.h + options.h + ${HAL_SOURCES} + ) + +include_directories(${HAL_INCLUDES}) + +install (TARGETS soes DESTINATION bin) +install (FILES + esc.h + esc_coe.h + esc_foe.h + esc_eoe.h + esc_eep.h + DESTINATION include) diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/Doxyfile b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/Doxyfile new file mode 100755 index 0000000..3c634b2 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/Doxyfile @@ -0,0 +1,1742 @@ +# Doxyfile 1.7.3 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = SOES + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = v1.0.0 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = YES + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = . \ + ../os_al/rtl_slavedemo \ + ../os_hw/rtl_spi \ + doc/tutorial.txt \ + doc/soes.dox \ + + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.vhd \ + *.vhdl \ + *.C \ + *.CC \ + *.C++ \ + *.II \ + *.I++ \ + *.H \ + *.HH \ + *.H++ \ + *.CS \ + *.PHP \ + *.PHP3 \ + *.M \ + *.MM \ + *.PY \ + *.F90 \ + *.F \ + *.VHD \ + *.VHDL \ + *.inc + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = doc/images + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [0,1..20]) +# that doxygen will group on one line in the generated HTML documentation. +# Note that a value of 0 will completely suppress the enum values from +# appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the +# mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax +# before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = NO + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will write a font called Helvetica to the output +# directory and reference it in all dot files that doxygen generates. +# When you want a differently looking font you can specify the font name +# using DOT_FONTNAME. You need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, svg, gif or svg. +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 1000 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = YES + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/cc.h b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/cc.h new file mode 100755 index 0000000..b3067b0 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/cc.h @@ -0,0 +1,88 @@ +/* + * Licensed under the GNU General Public License version 2 with exceptions. See + * LICENSE file in the project root for full license information + */ + +#ifndef CC_H +#define CC_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include +#include +#ifdef __linux__ + #include +#else + #include +#endif + +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +#ifndef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + +#define CC_PACKED_BEGIN +#define CC_PACKED_END +#define CC_PACKED __attribute__((packed)) + +#ifdef __rtk__ +#define CC_ASSERT(exp) ASSERT (exp) +#else +#define CC_ASSERT(exp) assert (exp) +#endif +#define CC_STATIC_ASSERT(exp) _Static_assert (exp, "") + +#define CC_DEPRECATED __attribute__((deprecated)) + +#define CC_SWAP32(x) __builtin_bswap32 (x) +#define CC_SWAP16(x) __builtin_bswap16 (x) + +#define CC_ATOMIC_SET(var,val) __atomic_store_n(&var,val,__ATOMIC_SEQ_CST) +#define CC_ATOMIC_GET(var) __atomic_load_n(&var,__ATOMIC_SEQ_CST) +#define CC_ATOMIC_ADD(var,val) __atomic_add_fetch(&var,val,__ATOMIC_SEQ_CST) +#define CC_ATOMIC_SUB(var,val) __atomic_sub_fetch(&var,val,__ATOMIC_SEQ_CST) +#define CC_ATOMIC_AND(var,val) __atomic_and_fetch(&var,val,__ATOMIC_SEQ_CST) +#define CC_ATOMIC_OR(var,val) __atomic_or_fetch(&var,val,__ATOMIC_SEQ_CST) + +#if BYTE_ORDER == BIG_ENDIAN +#define htoes(x) CC_SWAP16 (x) +#define htoel(x) CC_SWAP32 (x) +#else +#define htoes(x) (x) +#define htoel(x) (x) +#endif + +#define etohs(x) htoes (x) +#define etohl(x) htoel (x) + +#if BYTE_ORDER == LITTLE_ENDIAN +#define EC_LITTLE_ENDIAN +#else +#define EC_BIG_ENDIAN +#endif + +#ifdef ESC_DEBUG +#ifdef __rtk__ +#include +#define DPRINT(...) rprintp ("soes: "__VA_ARGS__) +#else +#include +#define DPRINT(...) printf ("soes: "__VA_ARGS__) +#endif +#else +#define DPRINT(...) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CC_H */ diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/doc/images/esi_pdo.png b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/doc/images/esi_pdo.png new file mode 100755 index 0000000..1b21c36 Binary files /dev/null and b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/doc/images/esi_pdo.png differ diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/doc/images/sii_pdo.png b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/doc/images/sii_pdo.png new file mode 100755 index 0000000..7298379 Binary files /dev/null and b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/doc/images/sii_pdo.png differ diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/doc/soes.dox b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/doc/soes.dox new file mode 100755 index 0000000..58d9a7a --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/doc/soes.dox @@ -0,0 +1,50 @@ +/** + * \mainpage Simple Open EtherCAT Slave or SOES + * + * \section start Tutorial + * For a tutorial on SOES See tutorial.txt + * + * \section overview Overview + * SOES is an EtherCAT slave stack written in c. Its purpose is to learn and + * to use. All users are invited to study the source to get an understanding + * how an EtherCAT slave function + * + * Features as of 1.0.0 : + * - Address offset based HAL for easy ESC read/write access via any interface + * - Mailbox with data link layer + * - CoE + * - Object dictionary + * - SDO read and write for all sizes including segmented transfers + * - Easy portable C-code suited for embedded applications + * - Fixed PDO mapping + * - FoE with bootstrap template + * - Support for Little and Big endian targets. + * - Polling for interrupts + * + * \section legal Legal notice + * SOES Simple Open EtherCAT Slave \n + * Copyright (C) 2007-2013 Arthur Ketels \n + * Copyright (C) 2012-2013 rt-labs \n + * + * SOES is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * SOES is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * As a special exception, if other files instantiate templates or use macros + * or inline functions from this file, or you compile this file and link it + * with other works to produce a work based on this file, this file does not + * by itself cause the resulting work to be covered by the GNU General Public + * License. However the source code for this file must still be made available + * in accordance with section (3) of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. + * + * The EtherCAT Technology, the trade name and logo "EtherCAT" are the intellectual + * property of, and protected by Beckhoff Automation GmbH. + */ diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/doc/tutorial.txt b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/doc/tutorial.txt new file mode 100755 index 0000000..53e1b1c --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/doc/tutorial.txt @@ -0,0 +1,476 @@ +/** \file + +\section general General + +The SOES is a library that provides the Micro Controller user application with + the means to access the EtherCAT fieldbus communication environment: + - EtherCAT State Machine + - Mailbox Interfaces + - Protocols + - CoE + - FoE + bootstrap template + +Support for mailbox and protocols are typical examples when you need a slave +stack to control the Application Layer of EtherCAT. The PDI used for such +applications is either SPI or some Micro Controller Interface + +The following sections show some basic examples on how to get the SOES up +and running, as well as a lightweight example on howto design your slave. +Since all code is local to the application or global variables, it is possible + to tweak and optimize when possible. + +Our target Application: + - Inputs 40bit + - One button 8bit + - One encoder value 32bit + - Outputs 8bit + - LED 8bit + - Parameters + - Encoder settings + - Slave commands + - Reset counter + +Now to translate and implement on top of SOES. + +First look on the start up code. This example shows how to add a +main function that will be called by startup code. In this example +main's only purpose is to spawn two new tasks. One that executes +SOES and one that control the ERROR LED. Some ESCs provided a pin +for the RUN LED, some even for the ERROR LED, if it don't you can +control them from the slave Micro Controller. We'll focus on the "soes" task. + +\code + +int main (void) +{ + rprintp ("SOES (Simple Open EtherCAT Slave)\nsoes test\n"); + + /* task_spawn ("led_run", led_error, 15, 512, NULL); / + task_spawn ("led_error", led_error, 15, 512, NULL); + task_spawn ("soes", soes, 9, 1024, NULL); + + return (0); +} + +\endcode + +\section configuration Configuration +The function soes is our EtherCAT slave device and can be split in 3 parts. +Hardware Init, Software Init and Application loop. We will start with the +Hardware Init. + + - Hardware Init + - esc_reset, special function used for ESC reset if no Physical + EEPROM used. This is local to the Application not part of the generic + ESC handling. + - ESC_init, initialise SPI communication or similar + - Wait for ESC is started, waiting for SPI to be up and running, + we'll query the ESC register DL status if EEPROM loaded OK and PDI + operational, eg. SPI OK. + +\code + +void soes (void *arg) +{ + TXPDOsize = SM3_sml = sizeTXPDO (); + RXPDOsize = SM2_sml = sizeRXPDO (); + + esc_reset (); + ESC_init ((void *)spi_name); + + task_delay (tick_from_ms (200)); + + // wait until ESC is started up + while ((ESCvar.DLstatus & 0x0001) == 0) + { + ESC_read (ESCREG_DLSTATUS, (void *) &ESCvar.DLstatus, + sizeof (ESCvar.DLstatus)); + ESCvar.DLstatus = etohs (ESCvar.DLstatus); + } + + // reset ESC to init state + ESC_ALstatus (ESCinit); +... +} + +\endcode + + - Software Init + - Reset the slave into Init state by writing AL Status register. + - Clear Errors by writing AL Status Code register. + - Stop the Application Layer, basically we disable the SyncManagers + that implicitly block the data exchange. + +\code + +void soes (void *arg) +{ +... + while ((ESCvar.DLstatus & 0x0001) == 0) + { + ESC_read (ESCREG_DLSTATUS, (void *) &ESCvar.DLstatus, + sizeof (ESCvar.DLstatus)); + ESCvar.DLstatus = etohs (ESCvar.DLstatus); + } + + // reset ESC to init state + ESC_ALstatus (ESCinit); + ESC_ALerror (ALERR_NONE); + ESC_stopmbx (); + ESC_stopinput (); + ESC_stopoutput (); + + // application run loop + while (1) +... +} +\endcode + + - Application loop + - ALevent handling, ALevent hold information on changes in ALControl or + SyncManagers. ALControl for state changes and SyncManagers for changes written + by EtherCAT in local memory mapped to active SyncManagers + - ESC_state for state handling, such as state step up or down with correct + error and acknowledge response. + - Mailbox handler, generic support of mailboxes used by all Application Layer + protocols. + - On mailbox actions we'll also check if we need to use a specific protocol + handler to handle the incoming or outgoing Mailbox data. + +Up until the now we're using the SOES protocol stack without any application specific +calls. Next up we'll look at the application Code, here named DIG_process (). + +\code + +void soes (void *arg) +{ +... + // application run loop + while (1) + { + if((ESCvar.ALstatus & 0x0f) == ESCinit) + { + txpdomap = DEFAULTTXPDOMAP; + rxpdomap = DEFAULTRXPDOMAP; + txpdoitems = DEFAULTTXPDOITEMS; + rxpdoitems = DEFAULTTXPDOITEMS; + } + ESC_read (ESCREG_LOCALTIME, (void *) &ESCvar.Time, sizeof (ESCvar.Time)); + ESCvar.Time = etohl (ESCvar.Time); + + ESC_state (); + if (ESC_mbxprocess ()) + { + ESC_coeprocess (); + ESC_foeprocess (); + ESC_xoeprocess (); + } + DIG_process (); + }; +} + +\endcode + +\section application Application +The function DIG_process is the User part of the application and could be joined +by more cyclic User functions for executing other parts of the application. +The example code can be split in 2 parts + - Outputs + - Start by evaluating if we're in a state supporting update of outputs, eg. + Operational state. + - If we're in OP we can read the current PDO data in the 3-buffer SyncManager + mapped to the output SM, the default is SyncManager2, we read the ESC RAM address + of SM2 and store it at the local address of the local variable Wb.LED. + We'll read RXPDOsize bytes to trigger a complete SyncManager read. + - After local variables have been refreshed we basically write the local + PDO variables to the User application, ex. a GPIO. + - Basically this is the API of the SOES toward the User Application. + - This function also include a watchdog mechanism, if triggered it will + shutdown the outputs and trigger a state changes to safe operational. + AlError is updated with cause of error to inform the Master. + +\code + +void RXPDO_update (void) +{ + ESC_read (SM2_sma, &Wb.LED, RXPDOsize); +} + +void DIG_process (void) +{ + if (App.state & APPSTATE_OUTPUT) + { + if (ESCvar.ALevent & ESCREG_ALEVENT_SM2) // SM2 trigger ? + { + RXPDO_update (); + reset_wd (); + gpio_set(GPIO_LED, Wb.LED & BIT(0)); + + } + if (!wd_cnt) + { + ESC_stopoutput (); + // watchdog, invalid outputs + ESC_ALerror (ALERR_WATCHDOG); + // goto safe-op with error bit set + ESC_ALstatus (ESCsafeop | ESCerror); + wd_trigger = 1; + } + } + else + { + reset_wd (); + } +... +} + +\endcode + + - Inputs + - Is handled a bit simpler than outputs but in reverse order, the slave will + continue update the inputs in state Safe Operational. + - Here we first read User application data from ex. GPIO and then write to + local PDO variables. + - After the local variables have been refreshed we write those to the current + PDO data 3-buffer SyncManager mapped to the input SM, the default is SyncManager3. + This way we update the ESC RAM address with User Application data accessed by + the EtherCAT master. + +\code + +void TXPDO_update (void) +{ + ESC_write (SM3_sma, &Rb.button, TXPDOsize); +} + +void DIG_process (void) +{ +... + Rb.button = gpio_get(GPIO_WAKEUP); + Cb.reset_counter++; + Rb.encoder = Cb.reset_counter; + + TXPDO_update (); +} + +\endcode + +\section ApplicationdataProcessdata ApplicationdataProcessdata +To run application data through EtherCAT processdata we need to describe for +the fieldbus what data we have and will read/write. For this we have 3 objects, +the ESI file, SII-EEPROM and CoE Object Dictionary. The first 2 are mandatory +and the third is a very convenient way of describing complex slaves. + +Our strategy is to keep the ESI file and the SII-EEPROM as thin as possible to +avoid duplication of data that need to be maintained. Both will hold the bare +minimum of mandatory + optional data to pass CTT. Optional data will be +included to tell EtherCAT that detailed information can be retrieved via CoE +from the OD stored in the slave it self. + +\section SII-EEPROM SII-EEPROM +Snapshot from SII information matrix from EtherCAT communication slides. + +\image html sii_pdo.png "Our target slave is Fixed PDO and OD" +\image latex sii_pdo.png "Our target slave is Fixed PDO and OD" width=15cm + + +\section ESI-file ESI-file +Snapshot from ESI tree from EtherCAT communication slides. + +\image html esi_pdo.png "mandatory and optional ESI data" +\image latex esi_pdo.png "mandatory and optional ESI data" width=15cm + +To briefly give a hint what are describe in the ESI and SII we're listing +a set of included elements marked M for mandatory and O for optional. + +\code + - Vendor (M) , Describes the identity. + - Id (M), Hex, EtherCAT Vendor ID, OD 1018.01 + - Name (M), NameType, Expedient vendor name + - Descriptions (M), Describes the EtherCAT device(s) using elements. + - Groups (M), Similar devices can be assigned to one group. + - Group (M), One group groups similar devices with slightly different features + - Type (M), A reference handle corresponding to the GroupType value in Description:Devices:Device:Group + - Name (M), Name for this group show by a configuration tool + - Devices (M), Element devices may describe one or several devices with their EtherCAT features such as SyncManagers, FMMUs and Dictionaries + - Device (O), Holds all information about the device like syncmanagers and FMMU, object dictionary, data types and the PDO mapping and assign description + - Device ATT: Physics (M),string, Physics at individual ports + - Type (M), Device identity + - Type ATT:ProductCode="#x98123467" + - Type ATT:RevisionNo="#x00000001" + - Name (M), Detailed name of device shown by a configuration tool (not used for identification) + - GroupType (M), Reference to a group (described in element Groups) to which this device should be assigned to. Name of the handle used in element Groups:Group:Type + - Fmmu (O), String to describe function, Outputs -> RxPDO, Inputs -> TxPDO , MBoxState -> FMMU is used to poll Input Mailbox + - Sm (O), Description of SyncManager including start address and direction. + - MBoxOut Mailbox Data Master -> Slave + - MBoxIn Mailbox Data Slave -> Master + - Outputs Process Data Master -> Slave + - Inputs Process Data Slave -> master + - Sm ATT:DefaultSize="128" , Size + - Sm ATT:StartAddress="#x1000" , Start address + - Sm ATT:ControlByte="#x26" , Settings , Bit [1][0] = 10, Operation mode Mailbox, 00 Buffered 3. + - Sm ATT:Enable="1", Enabled + - Mailbox (O), Description of available mailbox protocols + - Mailbox ATT: DataLinkLayer="true", Support of Mailbox Data Link Layer is mandatory. + - CoE (O), Device support CoE + - CoE (O) ATT: SdoInfo="true" , SDO Information Service + - CoE (O) ATT: CompleteAccess="false" , SDO complete access not supported + - CoE (O) ATT: PdoUpload="true", PDO description uploaded from the slave's object dictionary and SyncManager length calculated based on the same + - Dc (O), describes synchronization modes supported by the device. + - OpMode (O), Definition of supported operation modes + - Name (M), Internal Handle of operation mode + - Desc (O(M)), description of operation mode, recommended, Free Run (no sync), SM Synchronous, DC Synchronous + - AssignActive (M), Value of Latch and Sync Control registers + - Eeprom (O, use is mandatory) + - Data (M) + Or + - ByteSize (M), Byte Size of connected EEPROM device + - ConfigData (M), First 7 words of EEPROM, Configuration Areas + - BootStrap (O), Start address and length of mailboxes for BootStrap +\endcode + +So to describe the application we use CoE and Object Dictionary. The mapping between +Object Dictionary and the User Application are done via local variables defined as +user types. The Object Dictionary itself is stored in a matrix where all +the indexes are listed. Every index then have a submatrix with its subindex. + +\section ObjectDictionary ObjectDictionary +The Object Dictionary used as example follow the CANopen DS301 ranges. + + - 0x0000 - 0x0FFF, Data Type Area + - 0x1000 - 0x1FFF, Communication Area + - 0x2000 - 0x5FFF, Manufacture specific area + - 0x6000 - 0x6FFF, Input area + - 0x7000 - 0x7FFF, Output area + - 0x8000 - 0x8FFF, Configuration area + - 0x9000 - 0x9FFF, Information area + - 0xA000 - 0xAFFF, Diagnosis Area + - 0xB000 - 0xBFFF, Service Transfer Area + - 0xC000 - 0xEFFF, Reserved Area + - 0xF000 - 0xFFFF, Device Area + +RxPDO , 0x1600 - 0x17FF +TxPDO , 0x1A00 - 0x1BFF + +Example, on how the the OD index are linked. +Top index, SyncManagers Communication Types. In index 0 the 0x04 indicates we have 4 +SyncManagers defined. Every SyncManager is assigned a type, in index 1-4, we have standard +settings SM0 = 1, SM1 = 2, SM2 = 3, SM3 = 4 from ETG 1000.6, 5.6.7.4. + - 0, Unused + - 1, MailBox Receive, master to slave + - 2, MailBox Send, slave to master + - 3, Processdata output, master to slave + - 4, Processdata input, slave to master + +\code +objectlist.h +FLASHSTORE _objectlist SDOobjects[] = +... + {0x1C00, OTYPE_ARRAY, 4, 0, &acName1C00[0], &SDO1C00[0]}, + +FLASHSTORE _objd SDO1C00[] = +{ {0x00, DTYPE_UNSIGNED8, 8, ATYPE_R, &acNameNOE[0], 0x04, nil}, + {0x01, DTYPE_UNSIGNED8, 8, ATYPE_R, &acName1C00_01[0], 0x01, nil}, + {0x02, DTYPE_UNSIGNED8, 8, ATYPE_R, &acName1C00_02[0], 0x02, nil}, + {0x03, DTYPE_UNSIGNED8, 8, ATYPE_R, &acName1C00_03[0], 0x03, nil}, + {0x04, DTYPE_UNSIGNED8, 8, ATYPE_R, &acName1C00_04[0], 0x04, nil} + +\endcode + +SyncManagers channels 0-31 are listed in SDO1C10-SDO1C2F. If we look +at SyncManager channel 2, we see. + + - Type 3, Processdata output, master to slave + +It got one RxPDO index 0x1600 connected, and the submatrix for 0x1600 link +one PDO object index 0x7000 subindex 1. The output object 0x70000108 give +you the index 0x7000, subindex 1 and PDO object length 1(byte). + +\code +objectlist.h +FLASHSTORE _objd SDO1C12[] = +{ {0x00, DTYPE_UNSIGNED8, 8, ATYPE_R, &acNameNOE[0], 0x01, nil}, + {0x01, DTYPE_UNSIGNED16, 16, ATYPE_R, &acNameMO[0], 0x1600, nil} +}; + +FLASHSTORE _objd SDO1600[] = +{ {0x00, DTYPE_UNSIGNED8, 8, ATYPE_R, &acNameNOE[0], 0x01, nil}, + {0x01, DTYPE_UNSIGNED32, 32, ATYPE_R, &acNameMO[0], 0x70000108, nil} +}; + +\endcode + +At PDO level we make the connection between the local application and +the object dictionary. For all subindex in the PDO the last element +is the address to the local variable. + +\code + +objectlist.h +FLASHSTORE _objd SDO7000[] = +{ {0x00, DTYPE_UNSIGNED8, 8, ATYPE_R, &acNameNOE[0], 0x01, nil}, + {0x01, DTYPE_UNSIGNED8, 8, ATYPE_RW, &acName7000_01[0], 0, &(Wb.LED)} +}; + +utypes.h +typedef struct +{ + uint8 LED; +} _Wbuffer; + +\endcode + +Beside SyncManager to PDO mapping we also have mandatory data as + +0x1000 Device Type +0x1018 Object Identity +0x10C0 SyncManager Communication Type, as we used as top index when +figuring out our PDOs in the Object Dictionary. + +For a complete description of the object dictionary you can get guidance +by the ETG1000.6 EcatAlProtocols + +A useful feature is the Asynchronous use of SDO parameters. In the example +we have Parameter set holding an encoder scale value, just for show we also +have a read only mirror value of the encoder scale. Parameters defined as a +RW SDO parameter and can be written/read by SDO Download or Upload. In +addition there is a ESC_objecthandler Hook on SDO Download where you can +add additional logic, ex. we execute the mirror of the encoder scale value +by assigning the encoder scale value to the mirror. + +\code + +objectlist.h +FLASHSTORE _objd SDO7100[] = +{ {0x00, DTYPE_UNSIGNED8, 8, ATYPE_R, &acNameNOE[0], 0x02, nil}, + {0x01, DTYPE_UNSIGNED32, 32, ATYPE_RW, &acName7100_01[0], 0, &(encoder_scale)}, + {0x02, DTYPE_UNSIGNED32, 32, ATYPE_R, &acName7100_02[0], 0, &(encoder_scale_mirror)} +}; + +soes.c +void ESC_objecthandler (uint16 index, uint8 subindex) +{ + switch (index) + { +... + case 0x7100: + { + switch (subindex) + { + case 0x01: + { + encoder_scale_mirror = encoder_scale; + break; + } + } + break; + } +... +} + +\endcode +This tutorial is just one way of doing it. +Enjoy and happy coding! + +Andreas Karlsson, rt-labs AB, www.rt-labs.com + */ diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/ecat_slv.c b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/ecat_slv.c new file mode 100755 index 0000000..2ffce08 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/ecat_slv.c @@ -0,0 +1,386 @@ +/* + * Licensed under the GNU General Public License version 2 with exceptions. See + * LICENSE file in the project root for full license information + */ +#include +#include "esc.h" +#include "esc_coe.h" +#include "esc_foe.h" +#include "esc_eoe.h" +#include "ecat_slv.h" + +#define IS_RXPDO(index) ((index) >= 0x1600 && (index) < 0x1800) +#define IS_TXPDO(index) ((index) >= 0x1A00 && (index) < 0x1C00) + +/* Global variables used by the stack */ +uint8_t MBX[MBXBUFFERS * MAX(MBXSIZE, MBXSIZEBOOT)]; +_MBXcontrol MBXcontrol[MBXBUFFERS]; +_SMmap SMmap2[MAX_MAPPINGS_SM2]; +_SMmap SMmap3[MAX_MAPPINGS_SM3]; +_ESCvar ESCvar; + +/* Private variables */ +static volatile int watchdog; + +#if MAX_MAPPINGS_SM2 > 0 +static uint8_t rxpdo[MAX_RXPDO_SIZE] __attribute__((aligned(8))); +#else +extern uint8_t *rxpdo; +#endif + +#if MAX_MAPPINGS_SM3 > 0 +static uint8_t txpdo[MAX_TXPDO_SIZE] __attribute__((aligned(8))); +#else +extern uint8_t *txpdo; +#endif + +/** Function to pre-qualify the incoming SDO download. + * + * @param[in] index = index of SDO download request to check + * @param[in] sub-index = sub-index of SDO download request to check + * @return SDO abort code, or 0 on success + */ +uint32_t ESC_download_pre_objecthandler(uint16_t index, + uint8_t subindex, + void *data, + size_t size, + uint16_t flags) +{ + if (IS_RXPDO(index) || + IS_TXPDO(index) || + index == RX_PDO_OBJIDX || + index == TX_PDO_OBJIDX) + { + uint8_t minSub = ((flags & COMPLETE_ACCESS_FLAG) == 0) ? 0 : 1; + if (subindex > minSub && COE_maxSub(index) != 0) + { + return ABORT_SUBINDEX0_NOT_ZERO; + } + } + + if (ESCvar.pre_object_download_hook) + { + return (ESCvar.pre_object_download_hook)(index, + subindex, + data, + size, + flags); + } + + return 0; +} + +/** Hook called from the slave stack SDO Download handler to act on + * user specified Index and Sub-index. + * + * @param[in] index = index of SDO download request to handle + * @param[in] sub-index = sub-index of SDO download request to handle + * @return SDO abort code, or 0 on success + */ +uint32_t ESC_download_post_objecthandler(uint16_t index, uint8_t subindex, uint16_t flags) +{ + if (ESCvar.post_object_download_hook != NULL) + { + return (ESCvar.post_object_download_hook)(index, subindex, flags); + } + + return 0; +} + +/** Function to pre-qualify the incoming SDO upload. + * + * @param[in] index = index of SDO upload request to handle + * @param[in] sub-index = sub-index of SDO upload request to handle + * @return SDO abort code, or 0 on success + */ +uint32_t ESC_upload_pre_objecthandler(uint16_t index, + uint8_t subindex, + void *data, + size_t size, + uint16_t flags) +{ + if (ESCvar.pre_object_upload_hook != NULL) + { + return (ESCvar.pre_object_upload_hook)(index, + subindex, + data, + size, + flags); + } + + return 0; +} + +/** Hook called from the slave stack SDO Upload handler to act on + * user specified Index and Sub-index. + * + * @param[in] index = index of SDO upload request to handle + * @param[in] sub-index = sub-index of SDO upload request to handle + * @return SDO abort code, or 0 on success + */ +uint32_t ESC_upload_post_objecthandler(uint16_t index, uint8_t subindex, uint16_t flags) +{ + if (ESCvar.post_object_upload_hook != NULL) + { + return (ESCvar.post_object_upload_hook)(index, subindex, flags); + } + + return 0; +} + +/** Hook called from the slave stack ESC_stopoutputs to act on state changes + * forcing us to stop outputs. Here we can set them to a safe state. + */ +void APP_safeoutput(void) +{ + DPRINT("APP_safeoutput\n"); + + if (ESCvar.safeoutput_override != NULL) + { + (ESCvar.safeoutput_override)(); + } +} + +/** Write local process data to Sync Manager 3, Master Inputs. + */ +void TXPDO_update(void) +{ + if (ESCvar.txpdo_override != NULL) + { + (ESCvar.txpdo_override)(); + } + else + { + if (MAX_MAPPINGS_SM3 > 0) + { + COE_pdoPack(txpdo, ESCvar.sm3mappings, SMmap3); + } + ESC_write(ESC_SM3_sma, txpdo, ESCvar.ESC_SM3_sml); + } +} + +/** Read Sync Manager 2 to local process data, Master Outputs. + */ +void RXPDO_update(void) +{ + if (ESCvar.rxpdo_override != NULL) + { + (ESCvar.rxpdo_override)(); + } + else + { + ESC_read(ESC_SM2_sma, rxpdo, ESCvar.ESC_SM2_sml); + if (MAX_MAPPINGS_SM2 > 0) + { + COE_pdoUnpack(rxpdo, ESCvar.sm2mappings, SMmap2); + } + } +} + +/* Set the watchdog count value, don't have any affect when using + * HW watchdog 0x4xx + * + * @param[in] watchdogcnt = new watchdog count value + */ +void APP_setwatchdog(int watchdogcnt) +{ + CC_ATOMIC_SET(ESCvar.watchdogcnt, watchdogcnt); +} + +/* Function to update local I/O, call read ethercat outputs, call + * write ethercat inputs. Implement watch-dog counter to count-out if we have + * made state change affecting the App.state. + */ + +void DIG_process(uint16_t ALEvent, uint8_t flags) +{ + /* Handle watchdog */ + if ((flags & DIG_PROCESS_WD_FLAG) > 0) + { + if (CC_ATOMIC_GET(watchdog) > 0) + { + CC_ATOMIC_SUB(watchdog, 1); + } + + if ((CC_ATOMIC_GET(watchdog) <= 0) && + ((CC_ATOMIC_GET(ESCvar.App.state) & APPSTATE_OUTPUT) > 0)) + { + DPRINT("DIG_process watchdog expired\n"); + ESC_ALstatusgotoerror((ESCsafeop | ESCerror), ALERR_WATCHDOG); + } + else if (((CC_ATOMIC_GET(ESCvar.App.state) & APPSTATE_OUTPUT) == 0)) + { + CC_ATOMIC_SET(watchdog, ESCvar.watchdogcnt); + } + } + + /* Handle Outputs */ + if ((flags & DIG_PROCESS_OUTPUTS_FLAG) > 0) + { + if (((CC_ATOMIC_GET(ESCvar.App.state) & APPSTATE_OUTPUT) > 0) && + (ALEvent & ESCREG_ALEVENT_SM2)) + { + RXPDO_update(); + CC_ATOMIC_SET(watchdog, ESCvar.watchdogcnt); + /* Set outputs */ + cb_set_outputs(); + } + else if (ALEvent & ESCREG_ALEVENT_SM2) + { + RXPDO_update(); + } + } + + /* Call application */ + if ((flags & DIG_PROCESS_APP_HOOK_FLAG) > 0) + { + /* Call application callback if set */ + if (ESCvar.application_hook != NULL) + { + (ESCvar.application_hook)(); + } + } + + /* Handle Inputs */ + if ((flags & DIG_PROCESS_INPUTS_FLAG) > 0) + { + if (CC_ATOMIC_GET(ESCvar.App.state) > 0) + { + /* Update inputs */ + cb_get_inputs(); + TXPDO_update(); + } + } +} + +/* + * Handler for SM change, SM0/1, AL CONTROL and EEPROM events, the application + * control what interrupts that should be served and re-activated with + * event mask argument + */ +void ecat_slv_worker(uint32_t event_mask) +{ + do + { + /* Check the state machine */ + ESC_state(); + /* Check the SM activation event */ + ESC_sm_act_event(); + + /* Check mailboxes */ + while ((ESC_mbxprocess() > 0) || (ESCvar.txcue > 0)) + { + ESC_coeprocess(); +#if USE_FOE + ESC_foeprocess(); +#endif +#if USE_EOE + ESC_eoeprocess(); +#endif + ESC_xoeprocess(); + } +#if USE_EOE + ESC_eoeprocess_tx(); +#endif + /* Call emulated eeprom handler if set */ + if (ESCvar.esc_hw_eep_handler != NULL) + { + (ESCvar.esc_hw_eep_handler)(); + } + + CC_ATOMIC_SET(ESCvar.ALevent, ESC_ALeventread()); + + } while (ESCvar.ALevent & event_mask); + + ESC_ALeventmaskwrite(ESC_ALeventmaskread() | event_mask); +} + +/* + * Polling function. It should be called periodically for an application + * when only SM2/DC interrupt is active. + * Read and handle events for the EtherCAT state, status, mailbox and eeprom. + */ +void ecat_slv_poll(void) +{ + /* Read local time from ESC*/ + ESC_read(ESCREG_LOCALTIME, (void *)&ESCvar.Time, sizeof(ESCvar.Time)); + ESCvar.Time = etohl(ESCvar.Time); + + /* Check the state machine */ + ESC_state(); + /* Check the SM activation event */ + ESC_sm_act_event(); + + /* Check mailboxes */ + if (ESC_mbxprocess()) + { + ESC_coeprocess(); +#if USE_FOE + ESC_foeprocess(); +#endif +#if USE_EOE + ESC_eoeprocess(); +#endif + ESC_xoeprocess(); + } +#if USE_EOE + ESC_eoeprocess_tx(); +#endif + + /* Call emulated eeprom handler if set */ + if (ESCvar.esc_hw_eep_handler != NULL) + { + (ESCvar.esc_hw_eep_handler)(); + } +} + +/* + * Poll all events in a free-run application + */ +void ecat_slv(void) +{ + ecat_slv_poll(); + DIG_process(ESC_ALeventread(), DIG_PROCESS_WD_FLAG | DIG_PROCESS_OUTPUTS_FLAG | + DIG_PROCESS_APP_HOOK_FLAG | DIG_PROCESS_INPUTS_FLAG); +} + +/* + * Initialize the slave stack. + */ +void ecat_slv_init(esc_cfg_t *config) +{ + /* Init watchdog */ + watchdog = config->watchdog_cnt; + + /* Call stack configuration */ + ESC_config(config); + /* Call HW init */ + ESC_init(config); + + /* wait until ESC is started up */ + while ((ESCvar.DLstatus & 0x0001) == 0) + { + ESC_read(ESCREG_DLSTATUS, (void *)&ESCvar.DLstatus, + sizeof(ESCvar.DLstatus)); + ESCvar.DLstatus = etohs(ESCvar.DLstatus); + } + +#if USE_FOE + /* Init FoE */ + FOE_init(); +#endif + +#if USE_EOE + /* Init EoE */ + EOE_init(); +#endif + + /* reset ESC to init state */ + ESC_ALstatus(ESCinit); + ESC_ALerror(ALERR_NONE); + ESC_stopmbx(); + ESC_stopinput(); + ESC_stopoutput(); + /* Init Object Dictionary default values */ + COE_initDefaultValues(); +} diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/ecat_slv.h b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/ecat_slv.h new file mode 100755 index 0000000..5f6b516 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/ecat_slv.h @@ -0,0 +1,69 @@ +/* + * Licensed under the GNU General Public License version 2 with exceptions. See + * LICENSE file in the project root for full license information + */ + +#ifndef __ECAT_SLV_H__ +#define __ECAT_SLV_H__ + +#include "ecat_options.h" +#include "esc.h" + +/** + * This function is called when to get input values + */ +void cb_get_inputs(); + +/** + * This function is called when to set outputs values + */ +void cb_set_outputs(); + +/** Set the watchdog count value + * + * @param[in] watchdogcnt = new watchdog count value + */ +void APP_setwatchdog(int watchdogcnt); + +#define DIG_PROCESS_INPUTS_FLAG 0x01 +#define DIG_PROCESS_OUTPUTS_FLAG 0x02 +#define DIG_PROCESS_WD_FLAG 0x04 +#define DIG_PROCESS_APP_HOOK_FLAG 0x08 +/** Implements the watch-dog counter to count if we should make a state change + * due to missing incoming SM2 events. Updates local I/O and run the application + * in the following order, call read EtherCAT outputs, execute user provided + * application hook and call write EtherCAT inputs. + * + * @param[in] flags = User input what to execute + */ +void DIG_process(uint16_t ALEvent, uint8_t flags); + +/** + * Handler for SM change, SM0/1, AL CONTROL and EEPROM events, the application + * control what interrupts that should be served and re-activated with + * event mask argument + * + * @param[in] event_mask = Event mask for interrupts to serve and re-activate + * after served + */ +void ecat_slv_worker(uint32_t event_mask); + +/** + * Poll SM0/1, EEPROM and AL CONTROL events in a SM/DC synchronization + * application + */ +void ecat_slv_poll(void); + +/** + * Poll all events in a free-run application + */ +void ecat_slv(void); + +/** + * Initialize the slave stack + * + * @param[in] config = User input how to configure the stack + */ +void ecat_slv_init(esc_cfg_t *config); + +#endif /* __ECAT_SLV_H__ */ diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc.c b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc.c new file mode 100755 index 0000000..582c1e0 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc.c @@ -0,0 +1,1179 @@ +/* + * Licensed under the GNU General Public License version 2 with exceptions. See + * LICENSE file in the project root for full license information + */ +#include +#include +#include "esc.h" +#include "esc_coe.h" +#include "esc_foe.h" + +/** \file + * \brief + * Base EtherCAT functions for handling the Data Link Layer and Malilboxes + * + * + * State machine and mailbox support. + */ + +/** Write AL Status Code to the ESC. + * + * @param[in] errornumber = Write an by EtherCAT specified Error number register 0x134 AL Status Code + */ +void ESC_ALerror (uint16_t errornumber) +{ + uint16_t dummy; + ESCvar.ALerror = errornumber; + dummy = htoes (errornumber); + ESC_write (ESCREG_ALERROR, &dummy, sizeof (dummy)); +} + +/** Write AL Status to the ESC. + * + * @param[in] status = Write current slave status to register 0x130 AL Status + * reflecting actual state and error indication if present + */ +void ESC_ALstatus (uint8_t status) +{ + uint16_t dummy; + ESCvar.ALstatus = status; + dummy = htoes ((uint16_t) status); + ESC_write (ESCREG_ALSTATUS, &dummy, sizeof (dummy)); +} + +/** Write AL Status and AL Status code to the ESC. + * Call pre- and poststate change hook + * + * @param[in] status = Write current slave status to register 0x130 AL Status + * reflecting actual state and error indication if present + * @param[in] errornumber = Write an by EtherCAT specified Error number + * register 0x134 AL Status Code + */ +void ESC_ALstatusgotoerror (uint8_t status, uint16_t errornumber) +{ + uint8_t an, as; + + if(status & ESCop) + { + /* Erroneous input, ignore */ + return; + } + /* Mask error ack of current state */ + as = ESCvar.ALstatus & ESCREG_AL_ERRACKMASK; + an = as; + /* Set the state transition, new state in high bits and old in bits */ + as = ((status & ESCREG_AL_ERRACKMASK) << 4) | (as & 0x0f); + /* Call post state change hook case it have been configured */ + if (ESCvar.pre_state_change_hook != NULL) + { + ESCvar.pre_state_change_hook (&as, &an); + } + /* Stop outputs if active */ + if ((CC_ATOMIC_GET(ESCvar.App.state) & APPSTATE_OUTPUT) > 0) + { + ESC_stopoutput(); + } + ESC_ALerror(errornumber); + ESC_ALstatus(status); + an = status; + /* Call post state change hook case it have been configured */ + if (ESCvar.post_state_change_hook != NULL) + { + ESCvar.post_state_change_hook (&as, &an); + } +} + +/** Write ALeventMask register 0x204. + * + * @param[in] n = AL Event Mask + */ +void ESC_ALeventmaskwrite (uint32_t mask) +{ + uint32_t aleventmask; + aleventmask = htoel(mask); + ESC_write (ESCREG_ALEVENTMASK, &aleventmask, sizeof(aleventmask)); +} + +/** Read AleventMask register 0x204. + * + * @return value of register AL Event Mask + */ +uint32_t ESC_ALeventmaskread (void) +{ + uint32_t aleventmask; + + ESC_read (ESCREG_ALEVENTMASK, &aleventmask, sizeof(aleventmask)); + return htoel(aleventmask); +} + +/** Write ALevent register 0x220. + * + * @param[in] n = AL Event Mask + */ +void ESC_ALeventwrite (uint32_t event) +{ + uint32_t alevent; + alevent = htoel(event); + ESC_write (ESCREG_ALEVENT, &alevent, sizeof(alevent)); +} + +/** Read Alevent register 0x220. + * + * @return value of register AL Event Mask + */ +uint32_t ESC_ALeventread (void) +{ + uint32_t alevent; + ESC_read (ESCREG_ALEVENT, &alevent, sizeof(alevent)); + return htoel(alevent); +} + +/** Read SM Activate register 0x806(+ offset to SyncManager n) to acknowledge a + * Sync Manager event Bit 3 in ALevent. The result is not used. + * + * @param[in] n = Read Sync Manager no. n + */ +void ESC_SMack (uint8_t n) +{ + uint8_t dummy; + ESC_read (ESCREG_SM0ACTIVATE + (n << 3), &dummy, 1); +} + +/** Read SM Status register 0x805(+ offset to SyncManager n) and save the + * result in global variable ESCvar.SM[n]. + * + * @param[in] n = Read Sync Manager no. n + */ +void ESC_SMstatus (uint8_t n) +{ + _ESCsm2 *sm; + sm = (_ESCsm2 *)&ESCvar.SM[n]; + ESC_read (ESCREG_SM0STATUS + (n << 3), &(sm->Status), 1); +} + +/** Write ESCvar.SM[n] data to ESC PDI control register 0x807(+ offset to SyncManager n). + * + * @param[in] n = Write to Sync Manager no. n + */ +void ESC_SMwritepdi (uint8_t n) +{ + _ESCsm2 *sm; + sm = (_ESCsm2 *)&ESCvar.SM[n]; + ESC_write (ESCREG_SM0PDI + (n << 3), &(sm->ActPDI), 1); +} + +/** Write 0 to Bit0 in SM PDI control register 0x807(+ offset to SyncManager n) to Activate the Sync Manager n. + * + * @param[in] n = Write to Sync Manager no. n + */ +void ESC_SMenable (uint8_t n) +{ + _ESCsm2 *sm; + sm = (_ESCsm2 *)&ESCvar.SM[n]; + sm->ActPDI &= ~ESCREG_SMENABLE_BIT; + ESC_SMwritepdi (n); +} +/** Write 1 to Bit0 in SM PDI control register 0x807(+ offset to SyncManager n) to De-activte the Sync Manager n. + * + * @param[in] n = Write to Sync Manager no. n + */ +void ESC_SMdisable (uint8_t n) +{ + _ESCsm2 *sm; + sm = (_ESCsm2 *)&ESCvar.SM[n]; + sm->ActPDI |= ESCREG_SMENABLE_BIT; + ESC_SMwritepdi (n); +} +/** Read Configured Station Address register 0x010 assigned by the Master. + * + */ +void ESC_address (void) +{ + ESC_read (ESCREG_ADDRESS, (void *) &ESCvar.address, sizeof (ESCvar.address)); + ESCvar.address = etohs (ESCvar.address); +} + +/** Read Watchdog Status register 0x440. Result Bit0 0= Expired, 1= Active or disabled. + * + * @return value of register Watchdog Status. + */ +uint8_t ESC_WDstatus (void) +{ + uint16_t wdstatus; + ESC_read (ESCREG_WDSTATUS, &wdstatus, 2); + wdstatus = etohs (wdstatus); + return (uint8_t) wdstatus; +} + +/** Read SYNC Out Unit activation registers 0x981 + * + * @return value of register Activation. + */ +uint8_t ESC_SYNCactivation (void) +{ + uint8_t activation; + ESC_read (ESCREG_SYNC_ACT, &activation, sizeof(activation)); + return activation; +} + +/** Read SYNC0 cycle time + * + * @return value of register SYNC0 cycle time + */ +uint32_t ESC_SYNC0cycletime (void) +{ + uint32_t cycletime; + ESC_read (ESCREG_SYNC0_CYCLE_TIME, &cycletime, sizeof(cycletime)); + cycletime = etohl (cycletime); + return cycletime; +} + +/** Read SYNC1 cycle time + * + * @return value of register SYNC1 cycle time + */ +uint32_t ESC_SYNC1cycletime (void) +{ + uint32_t cycletime; + ESC_read (ESCREG_SYNC1_CYCLE_TIME, &cycletime, 4); + cycletime = etohl (cycletime); + return cycletime; +} + + +/** Validate the DC values if the SYNC unit is activated. + * + * @return = 0 if OK, else ERROR code to be set by caller. + */ +uint16_t ESC_checkDC (void) +{ + uint16_t ret = 0; + + uint8_t sync_act = ESC_SYNCactivation(); + /* Do we need to check sync settings? */ + if((sync_act & (ESCREG_SYNC_ACT_ACTIVATED | ESCREG_SYNC_AUTO_ACTIVATED)) > 0) + { + /* Trigger a by the application given DC check handler, return error if + * non is given + */ + ret = ALERR_DCINVALIDSYNCCFG; + if(ESCvar.esc_check_dc_handler != NULL) + { + ret = (ESCvar.esc_check_dc_handler)(); + } + } + else + { + ESCvar.dcsync = 0; + ESCvar.synccounter = 0; + } + + return ret; +} + +/** Check mailbox status by reading all SyncManager 0 and 1 data. The read values + * are compared with local definitions for SM Physical Address, SM Length and SM Control. + * If we check fails we disable Mailboxes by disabling SyncManager 0 and 1 and return + * state Init with Error flag set. + * + * @param[in] state = Current state request read from ALControl 0x0120 + * @return if all Mailbox values is correct we return incoming state request, otherwise + * we return state Init with Error flag set. + */ +uint8_t ESC_checkmbx (uint8_t state) +{ + _ESCsm2 *SM; + ESC_read (ESCREG_SM0, (void *) &ESCvar.SM[0], sizeof (ESCvar.SM[0])); + ESC_read (ESCREG_SM1, (void *) &ESCvar.SM[1], sizeof (ESCvar.SM[1])); + SM = (_ESCsm2 *) & ESCvar.SM[0]; + if ((etohs (SM->PSA) != ESC_MBX0_sma) || (etohs (SM->Length) != ESC_MBX0_sml) + || (SM->Command != ESC_MBX0_smc) || (ESCvar.SM[0].ECsm == 0)) + { + ESCvar.SMtestresult = SMRESULT_ERRSM0; + ESC_SMdisable (0); + ESC_SMdisable (1); + return (uint8_t) (ESCinit | ESCerror); //fail state change + } + SM = (_ESCsm2 *) & ESCvar.SM[1]; + if ((etohs (SM->PSA) != ESC_MBX1_sma) || (etohs (SM->Length) != ESC_MBX1_sml) + || (SM->Command != ESC_MBX1_smc) || (ESCvar.SM[1].ECsm == 0)) + { + ESCvar.SMtestresult = SMRESULT_ERRSM1; + ESC_SMdisable (0); + ESC_SMdisable (1); + return ESCinit | ESCerror; //fail state change + } + return state; +} +/** Try to start mailboxes for current ALControl state request by enabling SyncManager 0 and 1. + * If all mailbox settings is correct we return incoming state request, otherwise + * we return state Init with Error flag set and update local ALerror with code 0x16 Invalid + * mailbox configuration. + * + * @param[in] state = Current state request read from ALControl 0x0120 + * @return if all Mailbox values is correct we return incoming state, otherwise + * we return state Init with Error flag set. + */ +uint8_t ESC_startmbx (uint8_t state) +{ + /* Assign SM settings */ + ESCvar.activembxsize = MBXSIZE; + ESCvar.activemb0 = &ESCvar.mb[0]; + ESCvar.activemb1 = &ESCvar.mb[1]; + + + ESC_SMenable (0); + ESC_SMenable (1); + ESC_SMstatus (0); + ESC_SMstatus (1); + if ((state = ESC_checkmbx (state)) & ESCerror) + { + ESC_ALerror (ALERR_INVALIDMBXCONFIG); + ESCvar.MBXrun = 0; + } + else + { + ESCvar.toggle = ESCvar.SM[1].ECrep; //sync repeat request toggle state + ESCvar.MBXrun = 1; + } + return state; +} + +/** Try to start bootstrap mailboxes for current ALControl state request by enabling SyncManager 0 and 1. + * If all mailbox settings is correct we return incoming state request, otherwise + * we return state Init with Error flag set and update local ALerror with code 0x16 Invalid + * mailbox configuration. + * + * @param[in] state = Current state request read from ALControl 0x0120 + * @return if all Mailbox values is correct we return incoming state, otherwise + * we return state Init with Error flag set. + */ +uint8_t ESC_startmbxboot (uint8_t state) +{ + /* Assign SM settings */ + ESCvar.activembxsize = MBXSIZEBOOT; + ESCvar.activemb0 = &ESCvar.mbboot[0]; + ESCvar.activemb1 = &ESCvar.mbboot[1]; + + ESC_SMenable (0); + ESC_SMenable (1); + ESC_SMstatus (0); + ESC_SMstatus (1); + if ((state = ESC_checkmbx (state)) & ESCerror) + { + ESC_ALerror (ALERR_INVALIDBOOTMBXCONFIG); + ESCvar.MBXrun = 0; + } + else + { + ESCvar.toggle = ESCvar.SM[1].ECrep; //sync repeat request toggle state + ESCvar.MBXrun = 1; + } + return state; +} + +/** Stop mailboxes by disabling SyncManager 0 and 1. Clear local mailbox variables + * stored in ESCvar. + */ +void ESC_stopmbx (void) +{ + uint8_t n; + ESCvar.MBXrun = 0; + ESC_SMdisable (0); + ESC_SMdisable (1); + for (n = 0; n < ESC_MBXBUFFERS; n++) + { + MBXcontrol[n].state = MBXstate_idle; + } + ESCvar.mbxoutpost = 0; + ESCvar.mbxbackup = 0; + ESCvar.xoe = 0; + ESCvar.mbxfree = 1; + ESCvar.toggle = 0; + ESCvar.mbxincnt = 0; + ESCvar.segmented = 0; + ESCvar.frags = 0; + ESCvar.fragsleft = 0; + ESCvar.txcue = 0; + ESCvar.index = 0; + ESCvar.subindex = 0; + ESCvar.flags = 0; +} + +/** Read Receive mailbox and store data in local ESCvar.MBX variable. + * Combined function for bootstrap and other states. State check decides + * which one to read. + */ +void ESC_readmbx (void) +{ + _MBX *MB = (_MBX *)&MBX[0]; + uint16_t length; + + ESC_read (ESC_MBX0_sma, MB, ESC_MBXHSIZE); + length = etohs (MB->header.length); + + if (length > (ESC_MBX0_sml - ESC_MBXHSIZE)) + { + length = ESC_MBX0_sml - ESC_MBXHSIZE; + } + ESC_read (ESC_MBX0_sma + ESC_MBXHSIZE, MB->b, length); + if (length + ESC_MBXHSIZE < ESC_MBX0_sml) + { + ESC_read (ESC_MBX0_sme, &length, 1); + } + + MBXcontrol[0].state = MBXstate_inclaim; +} +/** Write local mailbox buffer ESCvar.MBX[n] to Send mailbox. + * Combined function for bootstrap and other states. State check decides + * which one to write. + * + * @param[in] n = Which local mailbox buffer n to send. + */ +void ESC_writembx (uint8_t n) +{ + _MBXh *MBh = (_MBXh *)&MBX[n * ESC_MBXSIZE]; + uint8_t dummy = 0; + uint16_t length; + length = etohs (MBh->length); + + if (length > (ESC_MBX1_sml - ESC_MBXHSIZE)) + { + length = ESC_MBX1_sml - ESC_MBXHSIZE; + } + ESC_write (ESC_MBX1_sma, MBh, ESC_MBXHSIZE + length); + if (length + ESC_MBXHSIZE < ESC_MBX1_sml) + { + ESC_write (ESC_MBX1_sme, &dummy, 1); + } + + ESCvar.mbxfree = 0; +} + +/** TBD + */ +void ESC_ackmbxread (void) +{ + uint8_t dummy = 0; + + ESC_write (ESC_MBX1_sma, &dummy, 1); + ESCvar.mbxfree = 1; +} + +/** Allocate and prepare a mailbox buffer. Take the first Idle buffer from the End. + * Set Mailbox control state to be used for outbox and fill the mailbox buffer with + * address master and mailbox next CNT value between 1-7. + * + * @return The index of Mailbox buffer prepared for outbox. IF no buffer is available return 0. + */ +uint8_t ESC_claimbuffer (void) +{ + _MBXh *MBh; + uint8_t n = ESC_MBXBUFFERS - 1; + while ((n > 0) && (MBXcontrol[n].state)) + { + n--; + } + if (n) + { + MBXcontrol[n].state = MBXstate_outclaim; + MBh = (_MBXh *)&MBX[n * ESC_MBXSIZE]; + ESCvar.mbxcnt++; + ESCvar.mbxcnt = (ESCvar.mbxcnt & 0x07); + if (ESCvar.mbxcnt == 0) + { + ESCvar.mbxcnt = 1; + } + MBh->address = htoes (0x0000); // destination is master + MBh->channel = 0; + MBh->priority = 0; + MBh->mbxcnt = ESCvar.mbxcnt; + ESCvar.txcue++; + } + return n; +} + +/** Look for any present requests for posting to the outbox. + * + * @return the index of Mailbox buffer ready to be posted. + */ +uint8_t ESC_outreqbuffer (void) +{ + uint8_t n = ESC_MBXBUFFERS - 1; + while ((n > 0) && (MBXcontrol[n].state != MBXstate_outreq)) + { + n--; + } + return n; +} +/** Allocate and prepare a mailbox buffer for sending an error message. Take the first Idle + * buffer from the end. Set Mailbox control state to be used for outbox and fill the mailbox + * buffer with error information. + * + * @param[in] n = Error number to be sent in mailbox error message. + */ +void MBX_error (uint16_t error) +{ + uint8_t MBXout; + _MBXerr *mbxerr; + MBXout = ESC_claimbuffer (); + if (MBXout) + { + mbxerr = (_MBXerr *) &MBX[MBXout * ESC_MBXSIZE]; + mbxerr->mbxheader.length = htoes ((uint16_t) 0x04); + mbxerr->mbxheader.mbxtype = MBXERR; + mbxerr->type = htoes ((uint16_t) 0x01); + mbxerr->detail = htoes (error); + MBXcontrol[MBXout].state = MBXstate_outreq; + } +} + +/** Mailbox routine for implementing the low-level part of the mailbox protocol + * used by Application Layers running on-top of mailboxes. It takes care of sending + * a mailbox, re-sending a mailbox, reading a mailbox and handles a mailbox full event. + * + * @return =0 if nothing to do. =1 if something to be handled by mailbox protocols. + */ +uint8_t ESC_mbxprocess (void) +{ + uint8_t mbxhandle = 0; + _MBXh *MBh = (_MBXh *)&MBX[0]; + + if (ESCvar.MBXrun == 0) + { + /* nothing to do */ + return 0; + } + + /* SM0/1 access */ + if (ESCvar.ALevent & (ESCREG_ALEVENT_SM0 | ESCREG_ALEVENT_SM1)) + { + ESC_SMstatus (0); + ESC_SMstatus (1); + } + + /* outmbx read by master */ + if (ESCvar.mbxoutpost && (ESCvar.ALevent & ESCREG_ALEVENT_SM1)) + { + ESC_ackmbxread (); + /* dispose old backup */ + if (ESCvar.mbxbackup) + { + MBXcontrol[ESCvar.mbxbackup].state = MBXstate_idle; + } + /* if still to do */ + if (MBXcontrol[ESCvar.mbxoutpost].state == MBXstate_again) + { + ESC_writembx (ESCvar.mbxoutpost); + } + /* create new backup */ + MBXcontrol[ESCvar.mbxoutpost].state = MBXstate_backup; + ESCvar.mbxbackup = ESCvar.mbxoutpost; + ESCvar.mbxoutpost = 0; + /* Do we have any ongoing protocol transfers, return 1 */ + if(ESCvar.xoe > 0) + { + return 1; + } + return 0; + } + + /* repeat request */ + if (ESCvar.SM[1].ECrep != ESCvar.toggle) + { + if (ESCvar.mbxoutpost || ESCvar.mbxbackup) + { + /* if outmbx empty */ + if (ESCvar.mbxoutpost == 0) + { + /* use backup mbx */ + ESC_writembx (ESCvar.mbxbackup); + } + else + { + /* reset mailbox */ + ESC_SMdisable (1); + /* have to resend later */ + MBXcontrol[ESCvar.mbxoutpost].state = MBXstate_again; + /* activate mailbox */ + ESC_SMenable (1); + /* use backup mbx */ + ESC_writembx (ESCvar.mbxbackup); + } + ESCvar.toggle = ESCvar.SM[1].ECrep; + ESCvar.SM[1].PDIrep = ESCvar.toggle; + ESC_SMwritepdi (1); + } + return 0; + } + + /* if the outmailbox is free check if we have something to send */ + if (ESCvar.txcue && (ESCvar.mbxfree || !ESCvar.SM[1].MBXstat)) + { + /* check out request mbx */ + mbxhandle = ESC_outreqbuffer (); + /* outmbx empty and outreq mbx available */ + if (mbxhandle) + { + ESC_writembx (mbxhandle); + /* Refresh SM status */ + ESC_SMstatus (1); + /* change state */ + MBXcontrol[mbxhandle].state = MBXstate_outpost; + ESCvar.mbxoutpost = mbxhandle; + if (ESCvar.txcue) + { + ESCvar.txcue--; + } + } + } + + /* read mailbox if full and no xoe in progress */ + if ((ESCvar.SM[0].MBXstat != 0) && (MBXcontrol[0].state == 0) + && (ESCvar.mbxoutpost == 0) && (ESCvar.xoe == 0)) + { + ESC_readmbx (); + ESCvar.SM[0].MBXstat = 0; + if (etohs (MBh->length) == 0) + { + MBX_error (MBXERR_INVALIDHEADER); + /* drop mailbox */ + MBXcontrol[0].state = MBXstate_idle; + } + if ((MBh->mbxcnt != 0) && (MBh->mbxcnt == ESCvar.mbxincnt)) + { + /* drop mailbox */ + MBXcontrol[0].state = MBXstate_idle; + } + ESCvar.mbxincnt = MBh->mbxcnt; + return 1; + } + + return 0; +} +/** Handler for incorrect or unsupported mailbox data. Write error response + * in Mailbox. + */ +void ESC_xoeprocess (void) +{ + _MBXh *mbh; + if (ESCvar.MBXrun == 0) + { + return; + } + if ((ESCvar.xoe == 0) && (MBXcontrol[0].state == MBXstate_inclaim)) + { + mbh = (_MBXh *) &MBX[0]; + if ((mbh->mbxtype == 0) || (etohs (mbh->length) == 0)) + { + MBX_error (MBXERR_INVALIDHEADER); + } + else + { + MBX_error (MBXERR_UNSUPPORTEDPROTOCOL); + } + /* mailbox type not supported, drop mailbox */ + MBXcontrol[0].state = MBXstate_idle; + } +} + +/** Validate the values of Sync Manager 2 & 3 that the current ESC values is + * equal to configured and calculated local values. + * + * @param[in] state = Requested state. + * @return = incoming state request if every thing checks out OK. = state (PREOP | ERROR) if something isn't correct. + */ +uint8_t ESC_checkSM23 (uint8_t state) +{ + _ESCsm2 *SM; + ESC_read (ESCREG_SM2, (void *) &ESCvar.SM[2], sizeof (ESCvar.SM[2])); + SM = (_ESCsm2 *) & ESCvar.SM[2]; + if ((etohs (SM->PSA) != ESC_SM2_sma) || (etohs (SM->Length) != ESCvar.ESC_SM2_sml) + || (SM->Command != ESC_SM2_smc) || !(SM->ActESC & ESC_SM2_act)) + { + ESCvar.SMtestresult = SMRESULT_ERRSM2; + /* fail state change */ + return (ESCpreop | ESCerror); + } + if ((ESC_SM2_sma + (etohs (SM->Length) * 3)) > ESC_SM3_sma) + { + ESCvar.SMtestresult = SMRESULT_ERRSM2; + /* SM2 overlaps SM3, fail state change */ + return (ESCpreop | ESCerror); + } + ESC_read (ESCREG_SM3, (void *) &ESCvar.SM[3], sizeof (ESCvar.SM[3])); + SM = (_ESCsm2 *) & ESCvar.SM[3]; + if ((etohs (SM->PSA) != ESC_SM3_sma) || (etohs (SM->Length) != ESCvar.ESC_SM3_sml) + || (SM->Command != ESC_SM3_smc) || !(SM->ActESC & ESC_SM3_act)) + { + ESCvar.SMtestresult = SMRESULT_ERRSM3; + /* fail state change */ + return (ESCpreop | ESCerror); + } + return state; +} + +/** Function trying to enable start updating the process data inputs. It calls the check SM 2 & 3 + * routine, based on the result from there if enables or disables the Input SyncManager, in addition + * it updates the ALStatusCode case something didn't pass the check. + * + * @param[in] state = Requested state. + * @return = state, incoming state request if every thing checks out OK. =state (PREOP | ERROR) if something isn't correct. + */ +uint8_t ESC_startinput (uint8_t state) +{ + + state = ESC_checkSM23 (state); + + if (state != (ESCpreop | ESCerror)) + { + ESC_SMenable (3); + CC_ATOMIC_SET(ESCvar.App.state, APPSTATE_INPUT); + } + else + { + ESC_SMdisable (2); + ESC_SMdisable (3); + if (ESCvar.SMtestresult & SMRESULT_ERRSM3) + { + ESC_ALerror (ALERR_INVALIDINPUTSM); + } + else + { + ESC_ALerror (ALERR_INVALIDOUTPUTSM); + } + } + + /* Exit here if polling */ + if (ESCvar.use_interrupt == 0) + { + return state; + } + + if (state != (ESCpreop | ESCerror)) + { + uint16_t dc_check_result; + dc_check_result = ESC_checkDC(); + if(dc_check_result > 0) + { + ESC_ALerror (dc_check_result); + state = (ESCpreop | ESCerror); + + ESC_SMdisable (2); + ESC_SMdisable (3); + CC_ATOMIC_SET(ESCvar.App.state, APPSTATE_IDLE); + } + else + { + if (ESCvar.esc_hw_interrupt_enable != NULL) + { + if(ESCvar.dcsync > 0) + { + ESCvar.esc_hw_interrupt_enable(ESCREG_ALEVENT_DC_SYNC0 | + ESCREG_ALEVENT_SM2); + } + else + { + ESCvar.esc_hw_interrupt_enable(ESCREG_ALEVENT_SM2); + } + } + } + } + + return state; +} + +/** Unconditional stop of updating inputs by disabling Sync Manager 2 & 3. + * Set the App.state to APPSTATE_IDLE. + * + */ +void ESC_stopinput (void) +{ + CC_ATOMIC_SET(ESCvar.App.state, APPSTATE_IDLE); + ESC_SMdisable (3); + ESC_SMdisable (2); + + /* Call interrupt disable hook case it have been configured */ + if ((ESCvar.use_interrupt != 0) && + (ESCvar.esc_hw_interrupt_disable != NULL)) + { + ESCvar.esc_hw_interrupt_disable (ESCREG_ALEVENT_DC_SYNC0 | + ESCREG_ALEVENT_SM2); + } +} + + +/** Unconditional start of updating outputs by enabling Sync Manager 2. + * Set the App.state to APPSTATE_OUTPUT. + * + * @param[in] state = Not used. + * @return = state unchanged. + * + */ +uint8_t ESC_startoutput (uint8_t state) +{ + + ESC_SMenable (2); + CC_ATOMIC_OR(ESCvar.App.state, APPSTATE_OUTPUT); + return state; + +} + +/** Unconditional stop of updating outputs by disabling Sync Manager 2. + * Set the App.state to APPSTATE_INPUT. Call application hook APP_safeoutput + * letting the user to set safe state values on outputs. + * + */ +void ESC_stopoutput (void) +{ + CC_ATOMIC_AND(ESCvar.App.state, APPSTATE_INPUT); + ESC_SMdisable (2); + APP_safeoutput (); +} + +/** The state handler acting on SyncManager Activation BIT(4) + * events in the Al Event Request register 0x220. + * + */ +void ESC_sm_act_event (void) +{ + uint8_t ac, an, as, ax, ax23; + + /* Have at least on Sync Manager changed */ + if ((ESCvar.ALevent & ESCREG_ALEVENT_SMCHANGE) == 0) + { + /* nothing to do */ + return; + } + + /* Mask state request bits + Error ACK */ + ac = ESCvar.ALcontrol & ESCREG_AL_STATEMASK; + as = ESCvar.ALstatus & ESCREG_AL_STATEMASK; + an = as; + if (((ac & ESCerror) || (ac == ESCinit))) + { + /* if error bit confirmed reset */ + ac &= ESCREG_AL_ERRACKMASK; + an &= ESCREG_AL_ERRACKMASK; + } + /* Enter SM changed handling for all steps but Init and Boot when Mailboxes + * is up and running + */ + if ((as & ESCREG_AL_ALLBUTINITMASK) && + ((as == ESCboot) == 0) && ESCvar.MBXrun) + { + /* Validate Sync Managers, reading the Activation register will + * acknowledge the SyncManager Activation event making us enter + * this execution path. + */ + ax = ESC_checkmbx (as); + ax23 = ESC_checkSM23 (as); + if ((an & ESCerror) && ((ac & ESCerror) == 0)) + { + /* if in error then stay there */ + } + /* Have we been forced to step down to INIT we will stop mailboxes, + * update AL Status Code and exit ESC_state + */ + else if (ax == (ESCinit | ESCerror)) + { + /* If we have activated Inputs and Outputs we need to disable them */ + if (CC_ATOMIC_GET(ESCvar.App.state)) + { + ESC_stopoutput (); + ESC_stopinput (); + } + /* Stop mailboxes and update ALStatus code */ + ESC_stopmbx (); + ESC_ALerror (ALERR_INVALIDMBXCONFIG); + ESCvar.MBXrun = 0; + ESC_ALstatus (ax); + return; + } + /* Have we been forced to step down to PREOP we will stop inputs + * and outputs, update AL Status Code and exit ESC_state + */ + else if (CC_ATOMIC_GET(ESCvar.App.state) && (ax23 == (ESCpreop | ESCerror))) + { + ESC_stopoutput (); + ESC_stopinput (); + if (ESCvar.SMtestresult & SMRESULT_ERRSM3) + { + ESC_ALerror (ALERR_INVALIDINPUTSM); + } + else + { + ESC_ALerror (ALERR_INVALIDOUTPUTSM); + } + ESC_ALstatus (ax23); + } + } + else + { + ESC_SMack (0); + ESC_SMack (1); + ESC_SMack (2); + ESC_SMack (3); + ESC_SMack (4); + ESC_SMack (5); + ESC_SMack (6); + ESC_SMack (7); + } +} +/** The state handler acting on ALControl Bit(0) + * events in the Al Event Request register 0x220. + * + */ +void ESC_state (void) +{ + uint8_t ac, an, as; + + /* Do we have a state change request pending */ + if (ESCvar.ALevent & ESCREG_ALEVENT_CONTROL) + { + ESC_read (ESCREG_ALCONTROL, (void *) &ESCvar.ALcontrol, + sizeof (ESCvar.ALcontrol)); + ESCvar.ALcontrol = etohs (ESCvar.ALcontrol); + } + else + { + /* nothing to do */ + return; + } + /* Mask state request bits + Error ACK */ + ac = ESCvar.ALcontrol & ESCREG_AL_STATEMASK; + as = ESCvar.ALstatus & ESCREG_AL_STATEMASK; + an = as; + if (((ac & ESCerror) || (ac == ESCinit))) + { + /* if error bit confirmed reset */ + ac &= ESCREG_AL_ERRACKMASK; + an &= ESCREG_AL_ERRACKMASK; + } + + /* Error state not acked, leave original */ + if ((an & ESCerror) && ((ac & ESCerror) == 0)) + { + return; + } + + /* Mask high bits ALcommand, low bits ALstatus */ + as = (ac << 4) | (as & 0x0f); + + /* Call post state change hook case it have been configured */ + if (ESCvar.pre_state_change_hook != NULL) + { + ESCvar.pre_state_change_hook (&as, &an); + } + + /* Switch through the state change requested via AlControl from + * current state read in AL status + */ + switch (as) + { + case INIT_TO_INIT: + case PREOP_TO_PREOP: + case OP_TO_OP: + { + break; + } + case INIT_TO_PREOP: + { + /* get station address */ + ESC_address (); + an = ESC_startmbx (ac); + break; + } + case INIT_TO_BOOT: + case BOOT_TO_BOOT: + { + /* get station address */ + ESC_address (); + an = ESC_startmbxboot (ac); + break; + } + case INIT_TO_SAFEOP: + case INIT_TO_OP: + { + an = ESCinit | ESCerror; + ESC_ALerror (ALERR_INVALIDSTATECHANGE); + break; + } + case OP_TO_INIT: + { + ESC_stopoutput (); + ESC_stopinput (); + ESC_stopmbx (); + an = ESCinit; + break; + } + case SAFEOP_TO_INIT: + { + ESC_stopinput (); + ESC_stopmbx (); + an = ESCinit; + break; + } + case PREOP_TO_INIT: + { + ESC_stopmbx (); + an = ESCinit; + break; + } + case BOOT_TO_INIT: + { + ESC_stopmbx (); + an = ESCinit; + break; + } + case PREOP_TO_BOOT: + case BOOT_TO_PREOP: + case BOOT_TO_SAFEOP: + case BOOT_TO_OP: + { + an = ESCpreop | ESCerror; + ESC_ALerror (ALERR_INVALIDSTATECHANGE); + break; + } + case PREOP_TO_SAFEOP: + case SAFEOP_TO_SAFEOP: + { + ESCvar.ESC_SM2_sml = sizeOfPDO (RX_PDO_OBJIDX, &ESCvar.sm2mappings, + SMmap2, MAX_MAPPINGS_SM2); + if (ESCvar.sm2mappings < 0) + { + an = ESCpreop | ESCerror; + ESC_ALerror (ALERR_INVALIDOUTPUTSM); + break; + } + + ESCvar.ESC_SM3_sml = sizeOfPDO (TX_PDO_OBJIDX, &ESCvar.sm3mappings, + SMmap3, MAX_MAPPINGS_SM3); + if (ESCvar.sm3mappings < 0) + { + an = ESCpreop | ESCerror; + ESC_ALerror (ALERR_INVALIDINPUTSM); + break; + } + + an = ESC_startinput (ac); + if (an == ac) + { + ESC_SMenable (2); + } + break; + } + case PREOP_TO_OP: + { + an = ESCpreop | ESCerror; + ESC_ALerror (ALERR_INVALIDSTATECHANGE); + break; + } + case OP_TO_PREOP: + { + ESC_stopoutput (); + ESC_stopinput (); + an = ESCpreop; + break; + } + case SAFEOP_TO_PREOP: + { + ESC_stopinput (); + an = ESCpreop; + break; + } + case SAFEOP_TO_BOOT: + { + an = ESCsafeop | ESCerror; + ESC_ALerror (ALERR_INVALIDSTATECHANGE); + break; + } + case SAFEOP_TO_OP: + { + an = ESC_startoutput (ac); + break; + } + case OP_TO_BOOT: + { + an = ESCsafeop | ESCerror; + ESC_ALerror (ALERR_INVALIDSTATECHANGE); + ESC_stopoutput (); + break; + } + case OP_TO_SAFEOP: + { + an = ESCsafeop; + ESC_stopoutput (); + break; + } + default: + { + if (an == ESCop) + { + ESC_stopoutput (); + an = ESCsafeop; + } + if (as == ESCsafeop) + { + ESC_stopinput (); + } + an |= ESCerror; + ESC_ALerror (ALERR_UNKNOWNSTATE); + break; + } + } + + /* Call post state change hook case it have been configured */ + if (ESCvar.post_state_change_hook != NULL) + { + ESCvar.post_state_change_hook (&as, &an); + } + + if (!(an & ESCerror) && (ESCvar.ALerror)) + { + /* clear error */ + ESC_ALerror (ALERR_NONE); + } + + ESC_ALstatus (an); + DPRINT ("state %x\n", an); +} +/** Function copying the application configuration variable + * data to the stack local variable. + * + * @param[in] cfg = Pointer to the Application configuration variable + * holding application specific details. Data is copied. + */ +void ESC_config (esc_cfg_t * cfg) +{ + static sm_cfg_t mb0 = {MBX0_sma, MBX0_sml, MBX0_sme, MBX0_smc, 0}; + static sm_cfg_t mb1 = {MBX1_sma, MBX1_sml, MBX1_sme, MBX1_smc, 0}; + static sm_cfg_t mbboot0 = {MBX0_sma_b, MBX0_sml_b, MBX0_sme_b, MBX0_smc_b, 0}; + static sm_cfg_t mbboot1 = {MBX1_sma_b, MBX1_sml_b, MBX1_sme_b, MBX1_smc_b, 0}; + + /* Configure stack */ + ESCvar.use_interrupt = cfg->use_interrupt; + ESCvar.watchdogcnt = cfg->watchdog_cnt; + + ESCvar.mb[0] = mb0; + ESCvar.mb[1] = mb1; + ESCvar.mbboot[0] = mbboot0; + ESCvar.mbboot[1] = mbboot1; + + ESCvar.skip_default_initialization = cfg->skip_default_initialization; + ESCvar.set_defaults_hook = cfg->set_defaults_hook; + ESCvar.pre_state_change_hook = cfg->pre_state_change_hook; + ESCvar.post_state_change_hook = cfg->post_state_change_hook; + ESCvar.application_hook = cfg->application_hook; + ESCvar.safeoutput_override = cfg->safeoutput_override; + ESCvar.pre_object_download_hook = cfg->pre_object_download_hook; + ESCvar.post_object_download_hook = cfg->post_object_download_hook; + ESCvar.pre_object_upload_hook = cfg->pre_object_upload_hook; + ESCvar.post_object_upload_hook = cfg->post_object_upload_hook; + ESCvar.rxpdo_override = cfg->rxpdo_override; + ESCvar.txpdo_override = cfg->txpdo_override; + ESCvar.esc_hw_interrupt_enable = cfg->esc_hw_interrupt_enable; + ESCvar.esc_hw_interrupt_disable = cfg->esc_hw_interrupt_disable; + ESCvar.esc_hw_eep_handler = cfg->esc_hw_eep_handler; + ESCvar.esc_check_dc_handler = cfg->esc_check_dc_handler; +} diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc.h b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc.h new file mode 100755 index 0000000..2d08e61 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc.h @@ -0,0 +1,769 @@ +/* + * Licensed under the GNU General Public License version 2 with exceptions. See + * LICENSE file in the project root for full license information + */ + +/** \file + * \brief + * Headerfile for esc.h + */ + +#ifndef __esc__ +#define __esc__ + +#include +#include +#include +#include "ecat_options.h" + +#define ESCREG_ADDRESS 0x0010 +#define ESCREG_DLSTATUS 0x0110 +#define ESCREG_ALCONTROL 0x0120 +#define ESCREG_ALSTATUS 0x0130 +#define ESCREG_ALERROR 0x0134 +#define ESCREG_ALEVENTMASK 0x0204 +#define ESCREG_ALEVENT 0x0220 +#define ESCREG_ALEVENT_SM_MASK 0x0310 +#define ESCREG_ALEVENT_SMCHANGE 0x0010 +#define ESCREG_ALEVENT_CONTROL 0x0001 +#define ESCREG_ALEVENT_DC_LATCH 0x0002 +#define ESCREG_ALEVENT_DC_SYNC0 0x0004 +#define ESCREG_ALEVENT_DC_SYNC1 0x0008 +#define ESCREG_ALEVENT_EEP 0x0020 +#define ESCREG_ALEVENT_WD 0x0040 +#define ESCREG_ALEVENT_SM0 0x0100 +#define ESCREG_ALEVENT_SM1 0x0200 +#define ESCREG_ALEVENT_SM2 0x0400 +#define ESCREG_ALEVENT_SM3 0x0800 +#define ESCREG_WDSTATUS 0x0440 +#define ESCREG_EECONTSTAT 0x0502 +#define ESCREG_EEDATA 0x0508 +#define ESCREG_SM0 0x0800 +#define ESCREG_SM0STATUS (ESCREG_SM0 + 5) +#define ESCREG_SM0ACTIVATE (ESCREG_SM0 + 6) +#define ESCREG_SM0PDI (ESCREG_SM0 + 7) +#define ESCREG_SM1 (ESCREG_SM0 + 0x08) +#define ESCREG_SM2 (ESCREG_SM0 + 0x10) +#define ESCREG_SM3 (ESCREG_SM0 + 0x18) +#define ESCREG_LOCALTIME 0x0910 +#define ESCREG_LOCALTIME_OFFSET 0x0920 +#define ESCREG_SYNC_ACT 0x0981 +#define ESCREG_SYNC_ACT_ACTIVATED 0x01 +#define ESCREG_SYNC_SYNC0_EN 0x02 +#define ESCREG_SYNC_SYNC1_EN 0x04 +#define ESCREG_SYNC_AUTO_ACTIVATED 0x08 +#define ESCREG_SYNC0_CYCLE_TIME 0x09A0 +#define ESCREG_SYNC1_CYCLE_TIME 0x09A4 +#define ESCREG_SMENABLE_BIT 0x01 +#define ESCREG_AL_STATEMASK 0x001f +#define ESCREG_AL_ALLBUTINITMASK 0x0e +#define ESCREG_AL_ERRACKMASK 0x0f + +#define SYNCTYPE_SUPPORT_FREERUN 0x01 +#define SYNCTYPE_SUPPORT_SYNCHRON 0x02 +#define SYNCTYPE_SUPPORT_DCSYNC0 0x04 +#define SYNCTYPE_SUPPORT_DCSYNC1 0x08 +#define SYNCTYPE_SUPPORT_SUBCYCLE 0x10 + +#define ESCinit 0x01 +#define ESCpreop 0x02 +#define ESCboot 0x03 +#define ESCsafeop 0x04 +#define ESCop 0x08 +#define ESCerror 0x10 + +#define INIT_TO_INIT 0x11 +#define INIT_TO_PREOP 0x21 +#define INIT_TO_BOOT 0x31 +#define INIT_TO_SAFEOP 0x41 +#define INIT_TO_OP 0x81 +#define PREOP_TO_INIT 0x12 +#define PREOP_TO_PREOP 0x22 +#define PREOP_TO_BOOT 0x32 +#define PREOP_TO_SAFEOP 0x42 +#define PREOP_TO_OP 0x82 +#define BOOT_TO_INIT 0x13 +#define BOOT_TO_PREOP 0x23 +#define BOOT_TO_BOOT 0x33 +#define BOOT_TO_SAFEOP 0x43 +#define BOOT_TO_OP 0x83 +#define SAFEOP_TO_INIT 0x14 +#define SAFEOP_TO_PREOP 0x24 +#define SAFEOP_TO_BOOT 0x34 +#define SAFEOP_TO_SAFEOP 0x44 +#define SAFEOP_TO_OP 0x84 +#define OP_TO_INIT 0x18 +#define OP_TO_PREOP 0x28 +#define OP_TO_BOOT 0x38 +#define OP_TO_SAFEOP 0x48 +#define OP_TO_OP 0x88 + +#define ALERR_NONE 0x0000 +#define ALERR_UNSPECIFIEDERROR 0x0001 +#define ALERR_NOMEMORY 0x0002 +#define ALERR_INVALIDSTATECHANGE 0x0011 +#define ALERR_UNKNOWNSTATE 0x0012 +#define ALERR_BOOTNOTSUPPORTED 0x0013 +#define ALERR_NOVALIDFIRMWARE 0x0014 +#define ALERR_INVALIDBOOTMBXCONFIG 0x0015 +#define ALERR_INVALIDMBXCONFIG 0x0016 +#define ALERR_INVALIDSMCONFIG 0x0017 +#define ALERR_NOVALIDINPUTS 0x0018 +#define ALERR_NOVALIDOUTPUTS 0x0019 +#define ALERR_SYNCERROR 0x001A +#define ALERR_WATCHDOG 0x001B +#define ALERR_INVALIDSYNCMANAGERTYP 0x001C +#define ALERR_INVALIDOUTPUTSM 0x001D +#define ALERR_INVALIDINPUTSM 0x001E +#define ALERR_INVALIDWDTCFG 0x001F +#define ALERR_SLAVENEEDSCOLDSTART 0x0020 +#define ALERR_SLAVENEEDSINIT 0x0021 +#define ALERR_SLAVENEEDSPREOP 0x0022 +#define ALERR_SLAVENEEDSSAFEOP 0x0023 +#define ALERR_INVALIDINPUTMAPPING 0x0024 +#define ALERR_INVALIDOUTPUTMAPPING 0x0025 +#define ALERR_INCONSISTENTSETTINGS 0x0026 +#define ALERR_FREERUNNOTSUPPORTED 0x0027 +#define ALERR_SYNCNOTSUPPORTED 0x0028 +#define ALERR_FREERUNNEEDS3BUFFMODE 0x0029 +#define ALERR_BACKGROUNDWATCHDOG 0x002A +#define ALERR_NOVALIDINPUTSOUTPUTS 0x002B +#define ALERR_FATALSYNCERROR 0x002C +#define ALERR_NOSYNCERROR 0x002D +#define ALERR_INVALIDINPUTFMMUCFG 0x002E +#define ALERR_DCINVALIDSYNCCFG 0x0030 +#define ALERR_INVALIDDCLATCHCFG 0x0031 +#define ALERR_PLLERROR 0x0032 +#define ALERR_DCSYNCIOERROR 0x0033 +#define ALERR_DCSYNCTIMEOUT 0x0034 +#define ALERR_DCSYNCCYCLETIME 0x0035 +#define ALERR_DCSYNC0CYCLETIME 0x0036 +#define ALERR_DCSYNC1CYCLETIME 0x0037 +#define ALERR_MBXAOE 0x0041 +#define ALERR_MBXEOE 0x0042 +#define ALERR_MBXCOE 0x0043 +#define ALERR_MBXFOE 0x0044 +#define ALERR_MBXSOE 0x0045 +#define ALERR_MBXVOE 0x004F +#define ALERR_EEPROMNOACCESS 0x0050 +#define ALERR_EEPROMERROR 0x0051 +#define ALERR_SLAVERESTARTEDLOCALLY 0x0060 +#define ALERR_DEVICEIDVALUEUPDATED 0x0061 +#define ALERR_APPLCTRLAVAILABLE 0x00f0 +#define ALERR_UNKNOWN 0xffff + +#define MBXERR_SYNTAX 0x0001 +#define MBXERR_UNSUPPORTEDPROTOCOL 0x0002 +#define MBXERR_INVALIDCHANNEL 0x0003 +#define MBXERR_SERVICENOTSUPPORTED 0x0004 +#define MBXERR_INVALIDHEADER 0x0005 +#define MBXERR_SIZETOOSHORT 0x0006 +#define MBXERR_NOMOREMEMORY 0x0007 +#define MBXERR_INVALIDSIZE 0x0008 + +#define ABORT_NOTOGGLE 0x05030000 +#define ABORT_TRANSFER_TIMEOUT 0x05040000 +#define ABORT_UNKNOWN 0x05040001 +#define ABORT_INVALID_BLOCK_SIZE 0x05040002 +#define ABORT_INVALID_SEQUENCE_NUMBER 0x05040003 +#define ABORT_BLOCK_CRC_ERROR 0x05040004 +#define ABORT_OUT_OF_MEMORY 0x05040005 +#define ABORT_UNSUPPORTED 0x06010000 +#define ABORT_WRITEONLY 0x06010001 +#define ABORT_READONLY 0x06010002 +#define ABORT_SUBINDEX0_NOT_ZERO 0x06010003 +#define ABORT_CA_NOT_SUPPORTED 0x06010004 +#define ABORT_EXCEEDS_MBOX_SIZE 0x06010005 +#define ABORT_SDO_DOWNLOAD_BLOCKED 0x06010006 +#define ABORT_NOOBJECT 0x06020000 +#define ABORT_MAPPING_OBJECT_ERROR 0x06040041 +#define ABORT_MAPPING_LENGTH_ERROR 0x06040042 +#define ABORT_GENERAL_PARAMETER_ERROR 0x06040043 +#define ABORT_GENERAL_DEVICE_ERROR 0x06040047 +#define ABORT_HARDWARE_ERROR 0x06060000 +#define ABORT_TYPEMISMATCH 0x06070010 +#define ABORT_DATATYPE_TOO_HIGH 0x06070012 +#define ABORT_DATATYPE_TOO_LOW 0x06070013 +#define ABORT_NOSUBINDEX 0x06090011 +#define ABORT_VALUE_EXCEEDED 0x06090030 +#define ABORT_VALUE_TOO_HIGH 0x06090031 +#define ABORT_VALUE_TOO_LOW 0x06090032 +#define ABORT_MODULE_LIST_MISMATCH 0x06090033 +#define ABORT_MAX_VAL_LESS_THAN_MIN_VAL 0x06090036 +#define ABORT_RESOURCE_NOT_AVAILABLE 0x060A0023 +#define ABORT_GENERALERROR 0x08000000 +#define ABORT_DATA_STORE_ERROR 0x08000020 +#define ABORT_DATA_STORE_LOCAL_ERROR 0x08000021 +#define ABORT_NOTINTHISSTATE 0x08000022 +#define ABORT_OBJECT_DICTIONARY_ERROR 0x08000023 +#define ABORT_NO_DATA_AVAILABLE 0x08000024 + +#define MBXstate_idle 0x00 +#define MBXstate_inclaim 0x01 +#define MBXstate_outclaim 0x02 +#define MBXstate_outreq 0x03 +#define MBXstate_outpost 0x04 +#define MBXstate_backup 0x05 +#define MBXstate_again 0x06 + +#define COE_DEFAULTLENGTH 0x0a +#define COE_HEADERSIZE 0x0a +#define COE_SEGMENTHEADERSIZE 0x03 +#define COE_SDOREQUEST 0x02 +#define COE_SDORESPONSE 0x03 +#define COE_SDOINFORMATION 0x08 +#define COE_COMMAND_SDOABORT 0x80 +#define COE_COMMAND_UPLOADREQUEST 0x40 +#define COE_COMMAND_UPLOADRESPONSE 0x40 +#define COE_COMMAND_UPLOADSEGMENT 0x00 +#define COE_COMMAND_UPLOADSEGREQ 0x60 +#define COE_COMMAND_DOWNLOADREQUEST 0x20 +#define COE_COMMAND_DOWNLOADRESPONSE 0x60 +#define COE_COMMAND_DOWNLOADSEGREQ 0x00 +#define COE_COMMAND_DOWNLOADSEGRESP 0x20 +#define COE_COMMAND_LASTSEGMENTBIT 0x01 +#define COE_SIZE_INDICATOR 0x01 +#define COE_EXPEDITED_INDICATOR 0x02 +#define COE_COMPLETEACCESS 0x10 +#define COE_TOGGLEBIT 0x10 +#define COE_INFOERROR 0x07 +#define COE_GETODLISTRESPONSE 0x02 +#define COE_GETODRESPONSE 0x04 +#define COE_ENTRYDESCRIPTIONRESPONSE 0x06 +#define COE_VALUEINFO_ACCESS 0x01 +#define COE_VALUEINFO_OBJECT 0x02 +#define COE_VALUEINFO_MAPPABLE 0x04 +#define COE_VALUEINFO_TYPE 0x08 +#define COE_VALUEINFO_DEFAULT 0x10 +#define COE_VALUEINFO_MINIMUM 0x20 +#define COE_VALUEINFO_MAXIMUM 0x40 +#define COE_MINIMUM_LENGTH 8 + +#define MBXERR 0x00 +#define MBXAOE 0x01 +#define MBXEOE 0x02 +#define MBXCOE 0x03 +#define MBXFOE 0x04 +#define MBXODL 0x10 +#define MBXOD 0x20 +#define MBXED 0x30 +#define MBXSEU 0x40 +#define MBXSED 0x50 + +#define SMRESULT_ERRSM0 0x01 +#define SMRESULT_ERRSM1 0x02 +#define SMRESULT_ERRSM2 0x04 +#define SMRESULT_ERRSM3 0x08 + +#define FOE_ERR_NOTDEFINED 0x8000 +#define FOE_ERR_NOTFOUND 0x8001 +#define FOE_ERR_ACCESS 0x8002 +#define FOE_ERR_DISKFULL 0x8003 +#define FOE_ERR_ILLEGAL 0x8004 +#define FOE_ERR_PACKETNO 0x8005 +#define FOE_ERR_EXISTS 0x8006 +#define FOE_ERR_NOUSER 0x8007 +#define FOE_ERR_BOOTSTRAPONLY 0x8008 +#define FOE_ERR_NOTINBOOTSTRAP 0x8009 +#define FOE_ERR_NORIGHTS 0x800A +#define FOE_ERR_PROGERROR 0x800B +#define FOE_ERR_CHECKSUM 0x800C + +#define FOE_OP_RRQ 1 +#define FOE_OP_WRQ 2 +#define FOE_OP_DATA 3 +#define FOE_OP_ACK 4 +#define FOE_OP_ERR 5 +#define FOE_OP_BUSY 6 + +#define FOE_READY 0 +#define FOE_WAIT_FOR_ACK 1 +#define FOE_WAIT_FOR_FINAL_ACK 2 +#define FOE_WAIT_FOR_DATA 3 + +#define EOE_RESULT_SUCCESS 0x0000 +#define EOE_RESULT_UNSPECIFIED_ERROR 0x0001 +#define EOE_RESULT_UNSUPPORTED_FRAME_TYPE 0x0002 +#define EOE_RESULT_NO_IP_SUPPORT 0x0201 +#define EOE_RESULT_NO_DHCP_SUPPORT 0x0202 +#define EOE_RESULT_NO_FILTER_SUPPORT 0x0401 + +#define APPSTATE_IDLE 0x00 +#define APPSTATE_INPUT 0x01 +#define APPSTATE_OUTPUT 0x02 + +#define PREALLOC_FACTOR 3 +#define PREALLOC_BUFFER_SIZE (PREALLOC_FACTOR * MBXSIZE) + +typedef struct sm_cfg +{ + uint16_t cfg_sma; + uint16_t cfg_sml; + uint16_t cfg_sme; + uint8_t cfg_smc; + uint8_t cfg_smact; +}sm_cfg_t; + +typedef struct esc_cfg +{ + void * user_arg; + int use_interrupt; + int watchdog_cnt; + bool skip_default_initialization; + void (*set_defaults_hook) (void); + void (*pre_state_change_hook) (uint8_t * as, uint8_t * an); + void (*post_state_change_hook) (uint8_t * as, uint8_t * an); + void (*application_hook) (void); + void (*safeoutput_override) (void); + uint32_t (*pre_object_download_hook) (uint16_t index, + uint8_t subindex, + void * data, + size_t size, + uint16_t flags); + uint32_t (*post_object_download_hook) (uint16_t index, + uint8_t subindex, + uint16_t flags); + uint32_t (*pre_object_upload_hook) (uint16_t index, + uint8_t subindex, + void * data, + size_t size, + uint16_t flags); + uint32_t (*post_object_upload_hook) (uint16_t index, + uint8_t subindex, + uint16_t flags); + void (*rxpdo_override) (void); + void (*txpdo_override) (void); + void (*esc_hw_interrupt_enable) (uint32_t mask); + void (*esc_hw_interrupt_disable) (uint32_t mask); + void (*esc_hw_eep_handler) (void); + uint16_t (*esc_check_dc_handler) (void); +} esc_cfg_t; + +typedef struct +{ + uint8_t state; +} _App; + +// Attention! this struct is always little-endian +CC_PACKED_BEGIN +typedef struct CC_PACKED +{ + uint16_t PSA; + uint16_t Length; + +#if defined(EC_LITTLE_ENDIAN) + uint8_t Mode:2; + uint8_t Direction:2; + uint8_t IntECAT:1; + uint8_t IntPDI:1; + uint8_t WTE:1; + uint8_t R1:1; + + uint8_t IntW:1; + uint8_t IntR:1; + uint8_t R2:1; + uint8_t MBXstat:1; + uint8_t BUFstat:2; + uint8_t R3:2; + + uint8_t ECsm:1; + uint8_t ECrep:1; + uint8_t ECr4:4; + uint8_t EClatchEC:1; + uint8_t EClatchPDI:1; + + uint8_t PDIsm:1; + uint8_t PDIrep:1; + uint8_t PDIr5:6; +#endif + +#if defined(EC_BIG_ENDIAN) + uint8_t R1:1; + uint8_t WTE:1; + uint8_t IntPDI:1; + uint8_t IntECAT:1; + uint8_t Direction:2; + uint8_t Mode:2; + + uint8_t R3:2; + uint8_t BUFstat:2; + uint8_t MBXstat:1; + uint8_t R2:1; + uint8_t IntR:1; + uint8_t IntW:1; + + uint8_t EClatchPDI:1; + uint8_t EClatchEC:1; + uint8_t ECr4:4; + uint8_t ECrep:1; + uint8_t ECsm:1; + + uint8_t PDIr5:6; + uint8_t PDIrep:1; + uint8_t PDIsm:1; +#endif +} _ESCsm; +CC_PACKED_END + +/* Attention! this struct is always little-endian */ +CC_PACKED_BEGIN +typedef struct CC_PACKED +{ + uint16_t PSA; + uint16_t Length; + uint8_t Command; + uint8_t Status; + uint8_t ActESC; + uint8_t ActPDI; +} _ESCsm2; +CC_PACKED_END + +CC_PACKED_BEGIN +typedef struct CC_PACKED +{ + uint16_t PSA; + uint16_t Length; + uint8_t Command; +} _ESCsmCompact; +CC_PACKED_END + +typedef struct +{ + /* Configuration input is saved so the user variable may go out-of-scope */ + int use_interrupt; + sm_cfg_t mb[2]; + sm_cfg_t mbboot[2]; + bool skip_default_initialization; + void (*set_defaults_hook) (void); + void (*pre_state_change_hook) (uint8_t * as, uint8_t * an); + void (*post_state_change_hook) (uint8_t * as, uint8_t * an); + void (*application_hook) (void); + void (*safeoutput_override) (void); + uint32_t (*pre_object_download_hook) (uint16_t index, + uint8_t subindex, + void * data, + size_t size, + uint16_t flags); + uint32_t (*post_object_download_hook) (uint16_t index, + uint8_t subindex, + uint16_t flags); + uint32_t (*pre_object_upload_hook) (uint16_t index, + uint8_t subindex, + void * data, + size_t size, + uint16_t flags); + uint32_t (*post_object_upload_hook) (uint16_t index, + uint8_t subindex, + uint16_t flags); + void (*rxpdo_override) (void); + void (*txpdo_override) (void); + void (*esc_hw_interrupt_enable) (uint32_t mask); + void (*esc_hw_interrupt_disable) (uint32_t mask); + void (*esc_hw_eep_handler) (void); + uint16_t (*esc_check_dc_handler) (void); + uint8_t MBXrun; + size_t activembxsize; + sm_cfg_t * activemb0; + sm_cfg_t * activemb1; + uint16_t ESC_SM2_sml; + uint16_t ESC_SM3_sml; + uint8_t dcsync; + uint16_t synccounterlimit; + uint16_t ALstatus; + uint16_t ALcontrol; + uint16_t ALerror; + uint16_t DLstatus; + uint16_t address; + uint8_t mbxcnt; + uint8_t mbxincnt; + uint8_t mbxoutpost; + uint8_t mbxbackup; + uint8_t xoe; + uint8_t txcue; + uint8_t mbxfree; + uint8_t segmented; + void *data; + uint16_t entries; + uint16_t frags; + uint16_t fragsleft; + uint16_t index; + uint8_t subindex; + uint16_t flags; + + uint8_t toggle; + + int sm2mappings; + int sm3mappings; + + uint8_t SMtestresult; + uint32_t PrevTime; + _ESCsm SM[4]; + /* Volatile since it may be read from ISR */ + volatile int watchdogcnt; + volatile uint32_t Time; + volatile uint16_t ALevent; + volatile int8_t synccounter; + volatile _App App; + uint8_t mbxdata[PREALLOC_BUFFER_SIZE]; +} _ESCvar; + +CC_PACKED_BEGIN +typedef struct CC_PACKED +{ + uint16_t length; + uint16_t address; + +#if defined(EC_LITTLE_ENDIAN) + uint8_t channel:6; + uint8_t priority:2; + + uint8_t mbxtype:4; + uint8_t mbxcnt:4; +#endif + +#if defined(EC_BIG_ENDIAN) + uint8_t priority:2; + uint8_t channel:6; + + uint8_t mbxcnt:4; + uint8_t mbxtype:4; +#endif +} _MBXh; +CC_PACKED_END + +CC_PACKED_BEGIN +typedef struct CC_PACKED +{ + _MBXh header; + uint8_t b[0]; +} _MBX; +CC_PACKED_END + +CC_PACKED_BEGIN +typedef struct CC_PACKED +{ + uint16_t numberservice; +} _COEh; +CC_PACKED_END + +CC_PACKED_BEGIN +typedef struct CC_PACKED +{ +#if defined(EC_LITTLE_ENDIAN) + uint8_t opcode:7; + uint8_t incomplete:1; +#endif + +#if defined(EC_BIG_ENDIAN) + uint8_t incomplete:1; + uint8_t opcode:7; +#endif + + uint8_t reserved; + uint16_t fragmentsleft; +} _INFOh; +CC_PACKED_END + +CC_PACKED_BEGIN +typedef struct CC_PACKED +{ + _MBXh mbxheader; + uint16_t type; + uint16_t detail; +} _MBXerr; +CC_PACKED_END + +CC_PACKED_BEGIN +typedef struct CC_PACKED +{ + _MBXh mbxheader; + _COEh coeheader; + uint8_t command; + uint16_t index; + uint8_t subindex; + uint32_t size; +} _COEsdo; +CC_PACKED_END + +CC_PACKED_BEGIN +typedef struct CC_PACKED +{ + _MBXh mbxheader; + _COEh coeheader; + _INFOh infoheader; + uint16_t index; + uint16_t datatype; + uint8_t maxsub; + uint8_t objectcode; + char name; +} _COEobjdesc; +CC_PACKED_END + +CC_PACKED_BEGIN +typedef struct CC_PACKED +{ + _MBXh mbxheader; + _COEh coeheader; + _INFOh infoheader; + uint16_t index; + uint8_t subindex; + uint8_t valueinfo; + uint16_t datatype; + uint16_t bitlength; + uint16_t access; + char name; +} _COEentdesc; +CC_PACKED_END + +CC_PACKED_BEGIN +typedef struct CC_PACKED +{ + uint8_t opcode; + uint8_t reserved; + union + { + uint32_t password; + uint32_t packetnumber; + uint32_t errorcode; + }; +} _FOEh; +CC_PACKED_END + +CC_PACKED_BEGIN +typedef struct CC_PACKED +{ + _MBXh mbxheader; + _FOEh foeheader; + union + { + char filename[0]; + uint8_t data[0]; + char errortext[0]; + }; +} _FOE; +CC_PACKED_END + +CC_PACKED_BEGIN +typedef struct CC_PACKED +{ + uint16_t frameinfo1; + union + { + uint16_t frameinfo2; + uint16_t result; + }; +} _EOEh; +CC_PACKED_END + +CC_PACKED_BEGIN +typedef struct CC_PACKED +{ + _MBXh mbxheader; + _EOEh eoeheader; + uint8_t data[0]; +} _EOE; +CC_PACKED_END + +/* state definition in mailbox + * 0 : idle + * 1 : claimed for inbox + * 2 : claimed for outbox + * 3 : request post outbox + * 4 : outbox posted not send + * 5 : backup outbox + * 6 : mailbox needs to be transmitted again + */ +typedef struct +{ + uint8_t state; +} _MBXcontrol; + +/* Stack reference to application configuration of the ESC */ +#define ESC_MBXSIZE (ESCvar.activembxsize) +#define ESC_MBX0_sma (ESCvar.activemb0->cfg_sma) +#define ESC_MBX0_sml (ESCvar.activemb0->cfg_sml) +#define ESC_MBX0_sme (ESCvar.activemb0->cfg_sme) +#define ESC_MBX0_smc (ESCvar.activemb0->cfg_smc) +#define ESC_MBX1_sma (ESCvar.activemb1->cfg_sma) +#define ESC_MBX1_sml (ESCvar.activemb1->cfg_sml) +#define ESC_MBX1_sme (ESCvar.activemb1->cfg_sme) +#define ESC_MBX1_smc (ESCvar.activemb1->cfg_smc) +#define ESC_MBXBUFFERS (MBXBUFFERS) +#define ESC_SM2_sma (SM2_sma) +#define ESC_SM2_smc (SM2_smc) +#define ESC_SM2_act (SM2_act) +#define ESC_SM3_sma (SM3_sma) +#define ESC_SM3_smc (SM3_smc) +#define ESC_SM3_act (SM3_act) + +#define ESC_MBXHSIZE sizeof(_MBXh) +#define ESC_MBXDSIZE (ESC_MBXSIZE - ESC_MBXHSIZE) +#define ESC_FOEHSIZE sizeof(_FOEh) +#define ESC_FOE_DATA_SIZE (ESC_MBXSIZE - (ESC_MBXHSIZE +ESC_FOEHSIZE)) +#define ESC_EOEHSIZE sizeof(_EOEh) +#define ESC_EOE_DATA_SIZE (ESC_MBXSIZE - (ESC_MBXHSIZE +ESC_EOEHSIZE)) + +void ESC_config (esc_cfg_t * cfg); +void ESC_ALerror (uint16_t errornumber); +void ESC_ALeventwrite (uint32_t event); +uint32_t ESC_ALeventread (void); +void ESC_ALeventmaskwrite (uint32_t mask); +uint32_t ESC_ALeventmaskread (void); +void ESC_ALstatus (uint8_t status); +void ESC_ALstatusgotoerror (uint8_t status, uint16_t errornumber); +void ESC_SMstatus (uint8_t n); +uint8_t ESC_WDstatus (void); +uint8_t ESC_claimbuffer (void); +uint8_t ESC_startmbx (uint8_t state); +void ESC_stopmbx (void); +void MBX_error (uint16_t error); +uint8_t ESC_mbxprocess (void); +void ESC_xoeprocess (void); +uint8_t ESC_startinput (uint8_t state); +void ESC_stopinput (void); +uint8_t ESC_startoutput (uint8_t state); +void ESC_stopoutput (void); +void ESC_state (void); +void ESC_sm_act_event (void); + +/* From hardware file */ +void ESC_read (uint16_t address, void *buf, uint16_t len); +void ESC_write (uint16_t address, void *buf, uint16_t len); +void ESC_init (const esc_cfg_t * cfg); +void ESC_reset (void); + +/* From application */ +extern void APP_safeoutput (); +extern _ESCvar ESCvar; +extern _MBXcontrol MBXcontrol[]; +extern uint8_t MBX[]; +extern _SMmap SMmap2[]; +extern _SMmap SMmap3[]; + +/* ATOMIC operations are used when running interrupt driven */ +#ifndef CC_ATOMIC_SET +#define CC_ATOMIC_SET(var,val) (var = val) +#endif + +#ifndef CC_ATOMIC_GET +#define CC_ATOMIC_GET(var) (var) +#endif + +#ifndef CC_ATOMIC_ADD +#define CC_ATOMIC_ADD(var,val) (var += val) +#endif + +#ifndef CC_ATOMIC_SUB +#define CC_ATOMIC_SUB(var,val) (var -= val) +#endif + +#ifndef CC_ATOMIC_AND +#define CC_ATOMIC_AND(var,val) (var &= val) +#endif + +#ifndef CC_ATOMIC_OR +#define CC_ATOMIC_OR(var,val) (var |= val) +#endif + + +#endif diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_coe.c b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_coe.c new file mode 100755 index 0000000..58fe954 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_coe.c @@ -0,0 +1,1783 @@ +/* + * Licensed under the GNU General Public License version 2 with exceptions. See + * LICENSE file in the project root for full license information + */ + + /** \file + * \brief + * CAN over EtherCAT (CoE) module. + * + * SDO read / write and SDO service functions + */ + +#include +#include +#include +#include +#include +#include "esc.h" +#include "esc_coe.h" + +#define BITS2BYTES(b) ((b + 7) >> 3) + +/* Fetch value from object dictionary */ +#define OBJ_VALUE_FETCH(v, o) \ + ((o).data ? *(__typeof__ (v) *)(o).data : (__typeof__ (v))(o).value) + +#define SDO_COMMAND(byte) \ + (byte & 0xe0) +#define SDO_COMPLETE_ACCESS(byte) \ + ((byte & COE_COMPLETEACCESS) == COE_COMPLETEACCESS) + +#define READ_ACCESS(access, state) \ + (((access & ATYPE_Rpre) && (state == ESCpreop)) || \ + ((access & ATYPE_Rsafe) && (state == ESCsafeop)) || \ + ((access & ATYPE_Rop) && (state == ESCop))) + +#define WRITE_ACCESS(access, state) \ + (((access & ATYPE_Wpre) && (state == ESCpreop)) || \ + ((access & ATYPE_Wsafe) && (state == ESCsafeop)) || \ + ((access & ATYPE_Wop) && (state == ESCop))) + +typedef enum { UPLOAD, DOWNLOAD } load_t; + +/** Search for an object sub-index. + * + * @param[in] nidx = local array index of object we want to find sub-index to + * @param[in] subindex = value on sub-index of object we want to locate + * @return local array index if we succeed, -1 if we didn't find the index. + */ +int16_t SDO_findsubindex (int16_t nidx, uint8_t subindex) +{ + const _objd *objd; + int16_t n = 0; + uint8_t maxsub; + objd = SDOobjects[nidx].objdesc; + maxsub = SDOobjects[nidx].maxsub; + + /* Since most objects contain all subindexes (i.e. are not sparse), + * check the most likely scenario first + */ + if ((subindex <= maxsub) && ((objd + subindex)->subindex == subindex)) + { + return subindex; + } + + while (((objd + n)->subindex < subindex) && (n < maxsub)) + { + n++; + } + if ((objd + n)->subindex != subindex) + { + return -1; + } + return n; +} + +/** Search for an object index matching the wanted value in the Object List. + * + * @param[in] index = value on index of object we want to locate + * @return local array index if we succeed, -1 if we didn't find the index. + */ +int32_t SDO_findobject (uint16_t index) +{ + int32_t n = 0; + while (SDOobjects[n].index < index) + { + n++; + } + if (SDOobjects[n].index != index) + { + return -1; + } + return n; +} + +/** + * Calculate the size in Bytes of RxPDO or TxPDOs by adding the + * objects in SyncManager SDO 1C1x. + * + * A list of mapped objects is created for fast lookup of + * dynamically mapped process data. The max size of the list (@a + * max_mappings) can be set to 0 if dynamic processdata is not + * supported. + * + * The output variable @a nmappings is set to 0 if dynamic processdata + * is not supported. It is set to the number of mapped objects if + * dynamic processdata is supported, or -1 if the mapping was + * incorrect. + + * @param[in] index = SM index + * @param[out] nmappings = number of mapped objects in SM, or -1 if + * mapping is invalid + * @param[out] mappings = list of mapped objects in SM + * @param[out] max_mappings = max number of mapped objects in SM + * @return size of RxPDO or TxPDOs in Bytes. + */ +uint16_t sizeOfPDO (uint16_t index, int * nmappings, _SMmap * mappings, + int max_mappings) +{ + uint16_t offset = 0, hobj; + uint8_t si, sic, c; + int16_t nidx; + const _objd *objd; + const _objd *objd1c1x; + int mapIx = 0; + + if ((index != RX_PDO_OBJIDX) && (index != TX_PDO_OBJIDX)) + { + return 0; + } + + nidx = SDO_findobject (index); + if(nidx < 0) + { + return 0; + } + + objd1c1x = SDOobjects[nidx].objdesc; + + si = OBJ_VALUE_FETCH (si, objd1c1x[0]); + if (si) + { + for (sic = 1; sic <= si; sic++) + { + hobj = OBJ_VALUE_FETCH (hobj, objd1c1x[sic]); + nidx = SDO_findobject (hobj); + if (nidx >= 0) + { + uint8_t maxsub; + + objd = SDOobjects[nidx].objdesc; + maxsub = OBJ_VALUE_FETCH (maxsub, objd[0]); + + for (c = 1; c <= maxsub; c++) + { + uint32_t value = OBJ_VALUE_FETCH (value, objd[c]); + uint8_t bitlength = value & 0xFF; + + if (max_mappings > 0) + { + uint16_t index = value >> 16; + uint8_t subindex = (value >> 8) & 0xFF; + const _objd * mapping; + + if (mapIx == max_mappings) + { + /* Too many mapped objects */ + *nmappings = -1; + return 0; + } + + DPRINT ("%04x:%02x @ %d\n", index, subindex, offset); + + if (index == 0 && subindex == 0) + { + /* Padding element */ + mapping = NULL; + } + else + { + nidx = SDO_findobject (index); + if (nidx >= 0) + { + int16_t nsub; + + nsub = SDO_findsubindex (nidx, subindex); + if (nsub < 0) + { + /* Mapped subindex does not exist */ + *nmappings = -1; + return 0; + } + + mapping = &SDOobjects[nidx].objdesc[nsub]; + } + else + { + /* Mapped index does not exist */ + *nmappings = -1; + return 0; + } + } + + mappings[mapIx].obj = mapping; + mappings[mapIx++].offset = offset; + } + + offset += bitlength; + } + } + } + } + + if (max_mappings > 0) + { + *nmappings = mapIx; + } + else + { + *nmappings = 0; + } + + return BITS2BYTES (offset); +} + +/** Copy to mailbox. + * + * @param[in] source = pointer to source + * @param[in] dest = pointer to destination + * @param[in] size = Size to copy + */ +static void copy2mbx (void *source, void *dest, uint16_t size) +{ + memcpy (dest, source, size); +} + +/** Function for sending an SDO Abort reply. + * + * @param[in] index = index of object causing abort reply + * @param[in] sub-index = sub-index of object causing abort reply + * @param[in] abortcode = abort code to send in reply + */ +void SDO_abort (uint16_t index, uint8_t subindex, uint32_t abortcode) +{ + uint8_t MBXout; + _COEsdo *coeres; + MBXout = ESC_claimbuffer (); + if (MBXout) + { + coeres = (_COEsdo *) &MBX[MBXout * ESC_MBXSIZE]; + coeres->mbxheader.length = htoes (COE_DEFAULTLENGTH); + coeres->mbxheader.mbxtype = MBXCOE; + coeres->coeheader.numberservice = + htoes ((0 & 0x01f) | (COE_SDOREQUEST << 12)); + coeres->index = htoes (index); + coeres->subindex = subindex; + coeres->command = COE_COMMAND_SDOABORT; + coeres->size = htoel (abortcode); + MBXcontrol[MBXout].state = MBXstate_outreq; + } +} + +static void set_state_idle(uint16_t index, + uint8_t subindex, + uint32_t abortcode) +{ + if (abortcode != 0) + { + SDO_abort (index, subindex, abortcode); + } + + MBXcontrol[0].state = MBXstate_idle; + ESCvar.xoe = 0; +} + +/** Function for responding on requested SDO Upload, sending the content + * requested in a free Mailbox buffer. Depending of size of data expedited, + * normal or segmented transfer is used. On error an SDO Abort will be sent. + */ +static void SDO_upload (void) +{ + _COEsdo *coesdo, *coeres; + uint16_t index; + uint8_t subindex; + int16_t nidx, nsub; + uint8_t MBXout; + uint32_t size; + uint8_t dss; + uint32_t abort = 1; + const _objd *objd; + coesdo = (_COEsdo *) &MBX[0]; + index = etohs (coesdo->index); + subindex = coesdo->subindex; + nidx = SDO_findobject (index); + if (nidx >= 0) + { + nsub = SDO_findsubindex (nidx, subindex); + if (nsub >= 0) + { + objd = SDOobjects[nidx].objdesc; + uint8_t access = (objd + nsub)->flags & 0x3f; + uint8_t state = ESCvar.ALstatus & 0x0f; + if (!READ_ACCESS(access, state)) + { + set_state_idle (index, subindex, ABORT_WRITEONLY); + return; + } + MBXout = ESC_claimbuffer (); + if (MBXout) + { + coeres = (_COEsdo *) &MBX[MBXout * ESC_MBXSIZE]; + coeres->mbxheader.length = htoes (COE_DEFAULTLENGTH); + coeres->mbxheader.mbxtype = MBXCOE; + coeres->coeheader.numberservice = + htoes ((0 & 0x01f) | (COE_SDORESPONSE << 12)); + size = (objd + nsub)->bitlength; + /* expedited bits used calculation */ + dss = 0x0c; + if (size > 8) + { + dss = 0x08; + } + if (size > 16) + { + dss = 0x04; + } + if (size > 24) + { + dss = 0x00; + } + coeres->index = htoes (index); + coeres->subindex = subindex; + if (size <= 32) + { + /* expedited response i.e. length<=4 bytes */ + coeres->command = COE_COMMAND_UPLOADRESPONSE + + COE_SIZE_INDICATOR + COE_EXPEDITED_INDICATOR + dss; + /* convert bits to bytes */ + size = BITS2BYTES(size); + void *dataptr = ((objd + nsub)->data) ? + (objd + nsub)->data : (void *)&((objd + nsub)->value); + abort = ESC_upload_pre_objecthandler (index, subindex, + dataptr, size, (objd + nsub)->flags); + if (abort == 0) + { + if ((objd + nsub)->data == NULL) + { + /* use constant value */ + coeres->size = htoel ((objd + nsub)->value); + } + else + { + /* use dynamic data */ + copy2mbx ((objd + nsub)->data, &(coeres->size), size); + } + } + else + { + SDO_abort (index, subindex, abort); + } + } + else + { + /* normal response i.e. length>4 bytes */ + coeres->command = COE_COMMAND_UPLOADRESPONSE + + COE_SIZE_INDICATOR; + /* convert bits to bytes */ + size = BITS2BYTES(size); + /* set total size in bytes */ + ESCvar.frags = size; + coeres->size = htoel (size); + if ((size + COE_HEADERSIZE) > ESC_MBXDSIZE) + { + /* segmented transfer needed */ + /* limit to mailbox size */ + size = ESC_MBXDSIZE - COE_HEADERSIZE; + /* number of bytes done */ + ESCvar.fragsleft = size; + /* signal segmented transfer */ + ESCvar.segmented = MBXSEU; + ESCvar.data = (objd + nsub)->data; + ESCvar.flags = (objd + nsub)->flags; + } + else + { + ESCvar.segmented = 0; + } + coeres->mbxheader.length = htoes (COE_HEADERSIZE + size); + abort = ESC_upload_pre_objecthandler (index, subindex, + (objd + nsub)->data, ESCvar.frags, (objd + nsub)->flags); + if (abort == 0) + { + /* use dynamic data */ + copy2mbx ((objd + nsub)->data, (&(coeres->size)) + 1, size); + } + else + { + SDO_abort (index, subindex, abort); + } + } + if ((abort == 0) && (ESCvar.segmented == 0)) + { + abort = ESC_upload_post_objecthandler (index, subindex, + (objd + nsub)->flags); + if (abort != 0) + { + SDO_abort (index, subindex, abort); + } + } + MBXcontrol[MBXout].state = MBXstate_outreq; + } + } + else + { + SDO_abort (index, subindex, ABORT_NOSUBINDEX); + } + } + else + { + SDO_abort (index, subindex, ABORT_NOOBJECT); + } + MBXcontrol[0].state = MBXstate_idle; + ESCvar.xoe = 0; +} + +static uint32_t complete_access_get_variables(_COEsdo *coesdo, uint16_t *index, + uint8_t *subindex, int16_t *nidx, + int16_t *nsub) +{ + *index = etohs (coesdo->index); + *subindex = coesdo->subindex; + + /* A Complete Access must start with Subindex 0 or Subindex 1 */ + if (*subindex > 1) + { + return ABORT_UNSUPPORTED; + } + + *nidx = SDO_findobject (*index); + if (*nidx < 0) + { + return ABORT_NOOBJECT; + } + + *nsub = SDO_findsubindex (*nidx, *subindex); + if (*nsub < 0) + { + return ABORT_NOSUBINDEX; + } + + return 0; +} + +static uint32_t complete_access_subindex_loop(const _objd *objd, + int16_t nidx, + int16_t nsub, + uint8_t *mbxdata, + load_t load_type, + uint16_t max_bytes) +{ + /* Objects with dynamic entries cannot be accessed with Complete Access */ + if ((objd->datatype == DTYPE_VISIBLE_STRING) || + (objd->datatype == DTYPE_OCTET_STRING) || + (objd->datatype == DTYPE_UNICODE_STRING)) + { + return ABORT_CA_NOT_SUPPORTED; + } + + uint32_t size = 0; + + while (nsub <= SDOobjects[nidx].maxsub) + { + uint8_t bitlen = (objd + nsub)->bitlength; + void *ul_source = ((objd + nsub)->data != NULL) ? + (objd + nsub)->data : (void *)&((objd + nsub)->value); + uint8_t bitoffset = size % 8; + uint8_t access = (objd + nsub)->flags & 0x3f; + uint8_t state = ESCvar.ALstatus & 0x0f; + + if ((bitlen % 8) == 0) + { + if (bitoffset != 0) + { + /* move on to next byte boundary */ + size += (8 - bitoffset); + } + if (mbxdata != NULL) + { + /* copy a non-bit data type to a byte boundary */ + if (load_type == UPLOAD) + { + if (READ_ACCESS(access, state)) + { + memcpy(&mbxdata[BITS2BYTES(size)], ul_source, + BITS2BYTES(bitlen)); + } + else + { + /* return zeroes for upload of WO objects */ + memset(&mbxdata[BITS2BYTES(size)], 0, BITS2BYTES(bitlen)); + } + } + /* download of RO objects shall be ignored */ + else if (WRITE_ACCESS(access, state)) + { + memcpy((objd + nsub)->data, &mbxdata[BITS2BYTES(size)], + BITS2BYTES(bitlen)); + } + } + } + else if ((load_type == UPLOAD) && (mbxdata != NULL)) + { + /* copy a bit data type into correct position */ + uint8_t bitmask = (1 << bitlen) - 1; + if (READ_ACCESS(access, state)) + { + mbxdata[BITS2BYTES(size)] |= + (*(uint8_t *)ul_source & bitmask) << bitoffset; + } + else + { + mbxdata[BITS2BYTES(size)] &= ~(bitmask << bitoffset); + } + } + + /* Subindex 0 is padded to 16 bit */ + size += (nsub == 0) ? 16 : bitlen; + nsub++; + + if ((max_bytes > 0) && (BITS2BYTES(size) >= max_bytes)) + { + break; + } + } + + return size; +} + +static void init_coesdo(_COEsdo *coesdo, + uint8_t sdoservice, + uint8_t command, + uint16_t index, + uint8_t subindex) +{ + coesdo->mbxheader.length = htoes(COE_DEFAULTLENGTH); + coesdo->mbxheader.mbxtype = MBXCOE; + coesdo->coeheader.numberservice = htoes(sdoservice << 12); + coesdo->command = command; + coesdo->index = htoes(index); + coesdo->subindex = subindex; +} + +/** Function for responding on requested SDO Upload with Complete Access, + * sending the content requested in a free Mailbox buffer. Depending of + * size of data expedited, normal or segmented transfer is used. + * On error an SDO Abort will be sent. + */ +static void SDO_upload_complete_access (void) +{ + _COEsdo *coesdo = (_COEsdo *) &MBX[0]; + uint16_t index; + uint8_t subindex; + int16_t nidx, nsub; + uint32_t abortcode = complete_access_get_variables + (coesdo, &index, &subindex, &nidx, &nsub); + if (abortcode != 0) + { + set_state_idle (index, subindex, abortcode); + return; + } + + uint8_t MBXout = ESC_claimbuffer (); + if (MBXout == 0) + { + /* It is a bad idea to call SDO_abort when ESC_claimbuffer fails, + * because SDO_abort will also call ESC_claimbuffer ... + */ + set_state_idle (index, subindex, 0); + return; + } + + const _objd *objd = SDOobjects[nidx].objdesc; + + /* loop through the subindexes to get the total size */ + uint32_t size = complete_access_subindex_loop(objd, nidx, nsub, NULL, UPLOAD, 0); + if (size > 0xffff) + { + /* 'size' is in this case actually an abort code */ + set_state_idle (index, subindex, size); + return; + } + + /* check that upload data fits in the preallocated buffer */ + if ((size + PREALLOC_FACTOR * COE_HEADERSIZE) > PREALLOC_BUFFER_SIZE) + { + set_state_idle (index, subindex, ABORT_GENERALERROR); + return; + } + + abortcode = ESC_upload_pre_objecthandler(index, subindex, + objd->data, BITS2BYTES(size), objd->flags | COMPLETE_ACCESS_FLAG); + if (abortcode != 0) + { + SDO_abort (index, subindex, abortcode); + } + + /* copy subindex data into the preallocated buffer */ + complete_access_subindex_loop(objd, nidx, nsub, ESCvar.mbxdata, UPLOAD, 0); + + _COEsdo *coeres = (_COEsdo *) &MBX[MBXout * ESC_MBXSIZE]; + init_coesdo(coeres, COE_SDORESPONSE, + COE_COMMAND_UPLOADRESPONSE | COE_COMPLETEACCESS | COE_SIZE_INDICATOR, + index, subindex); + + /* expedited bits used calculation */ + uint8_t dss = (size > 24) ? 0 : (4 * (3 - ((size - 1) >> 3))); + + /* convert bits to bytes */ + size = BITS2BYTES(size); + + ESCvar.segmented = 0; + + if (size <= 4) + { + /* expedited response, i.e. length <= 4 bytes */ + coeres->command |= (COE_EXPEDITED_INDICATOR | dss); + memcpy(&(coeres->size), ESCvar.mbxdata, size); + } + else + { + /* normal response, i.e. length > 4 bytes */ + coeres->size = htoel (size); + + if ((size + COE_HEADERSIZE) > ESC_MBXDSIZE) + { + /* segmented transfer needed */ + /* set total size in bytes */ + ESCvar.frags = size; + /* limit to mailbox size */ + size = ESC_MBXDSIZE - COE_HEADERSIZE; + /* number of bytes done */ + ESCvar.fragsleft = size; + /* signal segmented transfer */ + ESCvar.segmented = MBXSEU; + ESCvar.data = ESCvar.mbxdata; + ESCvar.flags = COMPLETE_ACCESS_FLAG; + } + + coeres->mbxheader.length = htoes (COE_HEADERSIZE + size); + memcpy((&(coeres->size)) + 1, ESCvar.mbxdata, size); + } + + if (ESCvar.segmented == 0) + { + abortcode = ESC_upload_post_objecthandler (index, subindex, + objd->flags | COMPLETE_ACCESS_FLAG); + + if (abortcode != 0) + { + SDO_abort (index, subindex, abortcode); + } + } + + MBXcontrol[MBXout].state = MBXstate_outreq; + + set_state_idle (index, subindex, 0); +} + +/** Function for handling the following SDO Upload if previous SDOUpload + * response was flagged it needed to be segmented. + */ +static void SDO_uploadsegment (void) +{ + _COEsdo *coesdo, *coeres; + uint8_t MBXout; + uint32_t size, offset, abort; + coesdo = (_COEsdo *) &MBX[0]; + MBXout = ESC_claimbuffer (); + if (MBXout) + { + coeres = (_COEsdo *) &MBX[MBXout * ESC_MBXSIZE]; + offset = ESCvar.fragsleft; + size = ESCvar.frags - ESCvar.fragsleft; + uint8_t command = COE_COMMAND_UPLOADSEGMENT + + (coesdo->command & COE_TOGGLEBIT); /* copy toggle bit */ + init_coesdo(coeres, COE_SDORESPONSE, command, + coesdo->index, coesdo->subindex); + if ((size + COE_SEGMENTHEADERSIZE) > ESC_MBXDSIZE) + { + /* more segmented transfer needed */ + /* limit to mailbox size */ + size = ESC_MBXDSIZE - COE_SEGMENTHEADERSIZE; + /* number of bytes done */ + ESCvar.fragsleft += size; + coeres->mbxheader.length = htoes (COE_SEGMENTHEADERSIZE + size); + } + else + { + /* last segment */ + ESCvar.segmented = 0; + ESCvar.frags = 0; + ESCvar.fragsleft = 0; + coeres->command += COE_COMMAND_LASTSEGMENTBIT; + if (size >= 7) + { + coeres->mbxheader.length = htoes (COE_SEGMENTHEADERSIZE + size); + } + else + { + coeres->command += (7 - size) << 1; + coeres->mbxheader.length = htoes (COE_DEFAULTLENGTH); + } + } + copy2mbx ((uint8_t *) ESCvar.data + offset, (&(coeres->command)) + 1, + size); /* copy to mailbox */ + + if (ESCvar.segmented == 0) + { + abort = ESC_upload_post_objecthandler (etohs (coesdo->index), + coesdo->subindex, ESCvar.flags); + if (abort != 0) + { + SDO_abort (etohs (coesdo->index), coesdo->subindex, abort); + } + } + + MBXcontrol[MBXout].state = MBXstate_outreq; + } + MBXcontrol[0].state = MBXstate_idle; + ESCvar.xoe = 0; +} + + +/** Function for handling incoming requested SDO Download, validating the + * request and sending an response. On error an SDO Abort will be sent. + */ +static void SDO_download (void) +{ + _COEsdo *coesdo, *coeres; + uint16_t index; + uint8_t subindex; + int16_t nidx, nsub; + uint8_t MBXout; + uint16_t size, actsize; + const _objd *objd; + uint32_t *mbxdata; + uint32_t abort; + + coesdo = (_COEsdo *) &MBX[0]; + index = etohs (coesdo->index); + subindex = coesdo->subindex; + nidx = SDO_findobject (index); + if (nidx >= 0) + { + nsub = SDO_findsubindex (nidx, subindex); + if (nsub >= 0) + { + objd = SDOobjects[nidx].objdesc; + uint8_t access = (objd + nsub)->flags & 0x3f; + uint8_t state = ESCvar.ALstatus & 0x0f; + if (WRITE_ACCESS(access, state)) + { + /* expedited? */ + if (coesdo->command & COE_EXPEDITED_INDICATOR) + { + size = 4 - ((coesdo->command & 0x0c) >> 2); + mbxdata = &(coesdo->size); + } + else + { + /* normal download */ + size = (etohl (coesdo->size) & 0xffff); + mbxdata = (&(coesdo->size)) + 1; + } + actsize = BITS2BYTES((objd + nsub)->bitlength); + if (actsize != size) + { + /* entries with data types VISIBLE_STRING, OCTET_STRING, + * UNICODE_STRING, ARRAY_OF_INT, ARRAY_OF_SINT, + * ARRAY_OF_DINT, and ARRAY_OF_UDINT may have flexible length + */ + uint16_t type = (objd + nsub)->datatype; + if (type == DTYPE_VISIBLE_STRING) + { + /* pad with zeroes up to the maximum size of the entry */ + memset((objd + nsub)->data + size, 0, actsize - size); + } + else if ((type != DTYPE_OCTET_STRING) && + (type != DTYPE_UNICODE_STRING) && + (type != DTYPE_ARRAY_OF_INT) && + (type != DTYPE_ARRAY_OF_SINT) && + (type != DTYPE_ARRAY_OF_DINT) && + (type != DTYPE_ARRAY_OF_UDINT)) + { + set_state_idle (index, subindex, ABORT_TYPEMISMATCH); + return; + } + } + abort = ESC_download_pre_objecthandler ( + index, + subindex, + mbxdata, + size, + (objd + nsub)->flags + ); + if (abort == 0) + { + if ((size > 4) && + (size > (coesdo->mbxheader.length - COE_HEADERSIZE))) + { + size = coesdo->mbxheader.length - COE_HEADERSIZE; + /* signal segmented transfer */ + ESCvar.segmented = MBXSED; + ESCvar.data = (objd + nsub)->data + size; + ESCvar.index = index; + ESCvar.subindex = subindex; + ESCvar.flags = (objd + nsub)->flags; + } + else + { + ESCvar.segmented = 0; + } + copy2mbx (mbxdata, (objd + nsub)->data, size); + MBXout = ESC_claimbuffer (); + if (MBXout) + { + coeres = (_COEsdo *) &MBX[MBXout * ESC_MBXSIZE]; + coeres->mbxheader.length = htoes (COE_DEFAULTLENGTH); + coeres->mbxheader.mbxtype = MBXCOE; + coeres->coeheader.numberservice = + htoes ((0 & 0x01f) | (COE_SDORESPONSE << 12)); + coeres->index = htoes (index); + coeres->subindex = subindex; + coeres->command = COE_COMMAND_DOWNLOADRESPONSE; + coeres->size = htoel (0); + MBXcontrol[MBXout].state = MBXstate_outreq; + } + if (ESCvar.segmented == 0) + { + /* external object write handler */ + abort = ESC_download_post_objecthandler (index, subindex, (objd + nsub)->flags); + if (abort != 0) + { + SDO_abort (index, subindex, abort); + } + } + } + else + { + SDO_abort (index, subindex, abort); + } + } + else + { + if (access == ATYPE_RWpre) + { + SDO_abort (index, subindex, ABORT_NOTINTHISSTATE); + } + else + { + SDO_abort (index, subindex, ABORT_READONLY); + } + } + } + else + { + SDO_abort (index, subindex, ABORT_NOSUBINDEX); + } + } + else + { + SDO_abort (index, subindex, ABORT_NOOBJECT); + } + MBXcontrol[0].state = MBXstate_idle; + ESCvar.xoe = 0; +} + +/** Function for handling incoming requested SDO Download with Complete Access, + * validating the request and sending a response. On error an SDO Abort will + * be sent. + */ +static void SDO_download_complete_access (void) +{ + _COEsdo *coesdo = (_COEsdo *) &MBX[0]; + uint16_t index; + uint8_t subindex; + int16_t nidx, nsub; + uint32_t abortcode = complete_access_get_variables + (coesdo, &index, &subindex, &nidx, &nsub); + if (abortcode != 0) + { + set_state_idle (index, subindex, abortcode); + return; + } + + uint16_t bytes; + uint32_t *mbxdata = &(coesdo->size); + + if (coesdo->command & COE_EXPEDITED_INDICATOR) + { + /* expedited download */ + bytes = 4 - ((coesdo->command & 0x0c) >> 2); + } + else + { + /* normal download */ + bytes = (etohl (coesdo->size) & 0xffff); + mbxdata++; + } + + const _objd *objd = SDOobjects[nidx].objdesc; + + /* loop through the subindexes to get the total size */ + uint32_t size = complete_access_subindex_loop(objd, nidx, nsub, NULL, DOWNLOAD, 0); + if (size > 0xffff) + { + /* 'size' is in this case actually an abort code */ + set_state_idle (index, subindex, size); + return; + } + /* The document ETG.1020 S (R) V1.3.0, chapter 12.2, states that + * "The SDO Download Complete Access data length shall always match + * the full current object size (defined by SubIndex0)". + * But EtherCAT Conformance Test Tool doesn't follow this rule for some test + * cases, which is the reason to here only check for 'less than or equal'. + */ + else if (bytes <= BITS2BYTES(size)) + { + abortcode = ESC_download_pre_objecthandler(index, subindex, mbxdata, + size, objd->flags | COMPLETE_ACCESS_FLAG); + if (abortcode != 0) + { + set_state_idle (index, subindex, abortcode); + return; + } + + /* copy download data to subindexes */ + complete_access_subindex_loop(objd, nidx, nsub, (uint8_t *)mbxdata, DOWNLOAD, bytes); + + abortcode = ESC_download_post_objecthandler(index, subindex, + objd->flags | COMPLETE_ACCESS_FLAG); + if (abortcode != 0) + { + set_state_idle (index, subindex, abortcode); + return; + } + } + else + { + set_state_idle (index, subindex, ABORT_TYPEMISMATCH); + return; + } + + uint8_t MBXout = ESC_claimbuffer (); + if (MBXout > 0) + { + _COEsdo *coeres = (_COEsdo *) &MBX[MBXout * ESC_MBXSIZE]; + init_coesdo(coeres, COE_SDORESPONSE, + COE_COMMAND_DOWNLOADRESPONSE | COE_COMPLETEACCESS, + index, subindex); + + coeres->size = 0; + MBXcontrol[MBXout].state = MBXstate_outreq; + } + + set_state_idle (index, subindex, 0); +} + +static void SDO_downloadsegment (void) +{ + _COEsdo *coesdo = (_COEsdo *) &MBX[0]; + uint8_t MBXout = ESC_claimbuffer (); + if (MBXout) + { + _COEsdo *coeres = (_COEsdo *) &MBX[MBXout * ESC_MBXSIZE]; + uint32_t size = coesdo->mbxheader.length - 3; + if (size == 7) + { + size = 7 - ((coesdo->command >> 1) & 7); + } + uint8_t command = COE_COMMAND_DOWNLOADSEGRESP + + (coesdo->command & COE_TOGGLEBIT); /* copy toggle bit */ + init_coesdo(coeres, COE_SDORESPONSE, command, 0, 0); + + uint32_t *mbxdata = (uint32_t *)&(coesdo->index); /* data pointer */ + copy2mbx (mbxdata, (uint8_t *)ESCvar.data, size); + + if (coesdo->command & COE_COMMAND_LASTSEGMENTBIT) + { + /* last segment */ + ESCvar.segmented = 0; + + /* external object write handler */ + uint32_t abort = ESC_download_post_objecthandler + (ESCvar.index, ESCvar.subindex, ESCvar.flags); + if (abort != 0) + { + set_state_idle (ESCvar.index, ESCvar.subindex, abort); + return; + } + } + else + { + /* more segmented transfer needed: increase offset */ + ESCvar.data += size; + } + + MBXcontrol[MBXout].state = MBXstate_outreq; + } + + set_state_idle (0, 0, 0); +} + +/** Function for sending an SDO Info Error reply. + * + * @param[in] abortcode = = abort code to send in reply + */ +static void SDO_infoerror (uint32_t abortcode) +{ + uint8_t MBXout; + _COEobjdesc *coeres; + MBXout = ESC_claimbuffer (); + if (MBXout) + { + coeres = (_COEobjdesc *) &MBX[MBXout * ESC_MBXSIZE]; + coeres->mbxheader.length = htoes ((uint16_t) COE_HEADERSIZE); + coeres->mbxheader.mbxtype = MBXCOE; + coeres->coeheader.numberservice = + htoes ((0 & 0x01f) | (COE_SDOINFORMATION << 12)); + /* SDO info error request */ + coeres->infoheader.opcode = COE_INFOERROR; + coeres->infoheader.incomplete = 0; + coeres->infoheader.reserved = 0x00; + coeres->infoheader.fragmentsleft = 0; + coeres->index = (uint16_t)htoel (abortcode); + coeres->datatype = (uint16_t)(htoel (abortcode) >> 16); + MBXcontrol[MBXout].state = MBXstate_outreq; + MBXcontrol[0].state = MBXstate_idle; + ESCvar.xoe = 0; + } +} + +#define ODLISTSIZE ((ESC_MBX1_sml - ESC_MBXHSIZE - sizeof(_COEh) - sizeof(_INFOh) - 2) & 0xfffe) + +/** Function for handling incoming requested SDO Get OD List, validating the + * request and sending an response. On error an SDO Info Error will be sent. + */ +static void SDO_getodlist (void) +{ + uint16_t frags; + uint8_t MBXout = 0; + uint16_t entries = 0; + uint16_t i, n; + uint16_t *p; + _COEobjdesc *coel, *coer; + + while (SDOobjects[entries].index != 0xffff) + { + entries++; + } + ESCvar.entries = entries; + frags = ((entries << 1) + ODLISTSIZE - 1); + frags /= ODLISTSIZE; + coer = (_COEobjdesc *) &MBX[0]; + /* check for unsupported opcodes */ + if (etohs (coer->index) > 0x01) + { + SDO_infoerror (ABORT_UNSUPPORTED); + } + else + { + MBXout = ESC_claimbuffer (); + } + if (MBXout) + { + coel = (_COEobjdesc *) &MBX[MBXout * ESC_MBXSIZE]; + coel->mbxheader.mbxtype = MBXCOE; + coel->coeheader.numberservice = + htoes ((0 & 0x01f) | (COE_SDOINFORMATION << 12)); + coel->infoheader.opcode = COE_GETODLISTRESPONSE; + /* number of objects request */ + if (etohs (coer->index) == 0x00) + { + coel->index = htoes ((uint16_t) 0x00); + coel->infoheader.incomplete = 0; + coel->infoheader.reserved = 0x00; + coel->infoheader.fragmentsleft = htoes ((uint16_t) 0); + MBXcontrol[0].state = MBXstate_idle; + ESCvar.xoe = 0; + ESCvar.frags = frags; + ESCvar.fragsleft = frags - 1; + p = &(coel->datatype); + *p = htoes (entries); + p++; + *p = 0; + p++; + *p = 0; + p++; + *p = 0; + p++; + *p = 0; + coel->mbxheader.length = htoes (0x08 + (5 << 1)); + } + /* only return all objects */ + if (etohs (coer->index) == 0x01) + { + if (frags > 1) + { + coel->infoheader.incomplete = 1; + ESCvar.xoe = MBXCOE + MBXODL; + n = ODLISTSIZE >> 1; + } + else + { + coel->infoheader.incomplete = 0; + MBXcontrol[0].state = MBXstate_idle; + ESCvar.xoe = 0; + n = entries; + } + coel->infoheader.reserved = 0x00; + ESCvar.frags = frags; + ESCvar.fragsleft = frags - 1; + coel->infoheader.fragmentsleft = htoes (ESCvar.fragsleft); + coel->index = htoes ((uint16_t) 0x01); + + p = &(coel->datatype); + for (i = 0; i < n; i++) + { + *p = htoes (SDOobjects[i].index); + p++; + } + + coel->mbxheader.length = htoes (0x08 + (n << 1)); + } + MBXcontrol[MBXout].state = MBXstate_outreq; + } +} +/** Function for continuing sending left overs from previous requested + * SDO Get OD List, validating the request and sending an response. + */ +static void SDO_getodlistcont (void) +{ + uint8_t MBXout; + uint16_t i, n, s; + uint16_t *p; + _COEobjdesc *coel; + + MBXout = ESC_claimbuffer (); + if (MBXout) + { + coel = (_COEobjdesc *) &MBX[MBXout * ESC_MBXSIZE]; + coel->mbxheader.mbxtype = MBXCOE; + coel->coeheader.numberservice = + htoes ((0 & 0x01f) | (COE_SDOINFORMATION << 12)); + coel->infoheader.opcode = COE_GETODLISTRESPONSE; + s = (ESCvar.frags - ESCvar.fragsleft) * (ODLISTSIZE >> 1); + if (ESCvar.fragsleft > 1) + { + coel->infoheader.incomplete = 1; + n = s + (ODLISTSIZE >> 1); + } + else + { + coel->infoheader.incomplete = 0; + MBXcontrol[0].state = MBXstate_idle; + ESCvar.xoe = 0; + n = ESCvar.entries; + } + coel->infoheader.reserved = 0x00; + ESCvar.fragsleft--; + coel->infoheader.fragmentsleft = htoes (ESCvar.fragsleft); + /* pointer 2 bytes back to exclude index */ + p = &(coel->index); + for (i = s; i < n; i++) + { + *p = htoes (SDOobjects[i].index); + p++; + } + coel->mbxheader.length = htoes (0x06 + ((n - s) << 1)); + MBXcontrol[MBXout].state = MBXstate_outreq; + } +} + +/** Function for handling incoming requested SDO Get Object Description, + * validating the request and sending an response. On error an + * SDO Info Error will be sent. + */ +static void SDO_getod (void) +{ + uint8_t MBXout; + uint16_t index; + int32_t nidx; + uint8_t *d; + const uint8_t *s; + uint8_t n = 0; + _COEobjdesc *coer, *coel; + coer = (_COEobjdesc *) &MBX[0]; + index = etohs (coer->index); + nidx = SDO_findobject (index); + if (nidx >= 0) + { + MBXout = ESC_claimbuffer (); + if (MBXout) + { + coel = (_COEobjdesc *) &MBX[MBXout * ESC_MBXSIZE]; + coel->mbxheader.mbxtype = MBXCOE; + coel->coeheader.numberservice = + htoes ((0 & 0x01f) | (COE_SDOINFORMATION << 12)); + coel->infoheader.opcode = COE_GETODRESPONSE; + coel->infoheader.incomplete = 0; + coel->infoheader.reserved = 0x00; + coel->infoheader.fragmentsleft = htoes (0); + coel->index = htoes (index); + if (SDOobjects[nidx].objtype == OTYPE_VAR) + { + int32_t nsub = SDO_findsubindex (nidx, 0); + const _objd *objd = SDOobjects[nidx].objdesc; + coel->datatype = htoes ((objd + nsub)->datatype); + coel->maxsub = SDOobjects[nidx].maxsub; + } + else if (SDOobjects[nidx].objtype == OTYPE_ARRAY) + { + int32_t nsub = SDO_findsubindex (nidx, 0); + const _objd *objd = SDOobjects[nidx].objdesc; + coel->datatype = htoes ((objd + nsub)->datatype); + coel->maxsub = SDOobjects[nidx].objdesc->value; + } + else + { + coel->datatype = htoes (0); + coel->maxsub = SDOobjects[nidx].objdesc->value; + } + coel->objectcode = SDOobjects[nidx].objtype; + s = (uint8_t *) SDOobjects[nidx].name; + d = (uint8_t *) &(coel->name); + while (*s && (n < (ESC_MBXDSIZE - 0x0c))) + { + *d = *s; + n++; + s++; + d++; + } + *d = *s; + coel->mbxheader.length = htoes ((uint16_t) 0x0c + n); + MBXcontrol[MBXout].state = MBXstate_outreq; + MBXcontrol[0].state = MBXstate_idle; + ESCvar.xoe = 0; + } + } + else + { + SDO_infoerror (ABORT_NOOBJECT); + } +} + +/** Function for handling incoming requested SDO Get Entry Description, + * validating the request and sending an response. On error an + * SDO Info Error will be sent. + */ +static void SDO_geted (void) +{ + uint8_t MBXout; + uint16_t index; + int32_t nidx, nsub; + uint8_t subindex; + uint8_t *d; + const uint8_t *s; + const _objd *objd; + uint8_t n = 0; + _COEentdesc *coer, *coel; + coer = (_COEentdesc *) &MBX[0]; + index = etohs (coer->index); + subindex = coer->subindex; + nidx = SDO_findobject (index); + if (nidx >= 0) + { + nsub = SDO_findsubindex (nidx, subindex); + if (nsub >= 0) + { + objd = SDOobjects[nidx].objdesc; + MBXout = ESC_claimbuffer (); + if (MBXout) + { + coel = (_COEentdesc *) &MBX[MBXout * ESC_MBXSIZE]; + coel->mbxheader.mbxtype = MBXCOE; + coel->coeheader.numberservice = + htoes ((0 & 0x01f) | (COE_SDOINFORMATION << 12)); + coel->infoheader.opcode = COE_ENTRYDESCRIPTIONRESPONSE; + coel->infoheader.incomplete = 0; + coel->infoheader.reserved = 0x00; + coel->infoheader.fragmentsleft = htoes ((uint16_t) 0); + coel->index = htoes (index); + coel->subindex = subindex; + coel->valueinfo = COE_VALUEINFO_ACCESS + + COE_VALUEINFO_OBJECT + COE_VALUEINFO_MAPPABLE; + coel->datatype = htoes ((objd + nsub)->datatype); + coel->bitlength = htoes ((objd + nsub)->bitlength); + coel->access = htoes ((objd + nsub)->flags); + s = (uint8_t *) (objd + nsub)->name; + d = (uint8_t *) &(coel->name); + while (*s && (n < (ESC_MBXDSIZE - 0x10))) + { + *d = *s; + n++; + s++; + d++; + } + *d = *s; + coel->mbxheader.length = htoes ((uint16_t) 0x10 + n); + MBXcontrol[MBXout].state = MBXstate_outreq; + MBXcontrol[0].state = MBXstate_idle; + ESCvar.xoe = 0; + } + } + else + { + SDO_infoerror (ABORT_NOSUBINDEX); + } + } + else + { + SDO_infoerror (ABORT_NOOBJECT); + } +} + +/** Main CoE function checking the status on current mailbox buffers carrying + * data, distributing the mailboxes to appropriate CoE functions. + * On Error an MBX_error or SDO Abort will be sent depending on error cause. + */ +void ESC_coeprocess (void) +{ + _MBXh *mbh; + _COEsdo *coesdo; + _COEobjdesc *coeobjdesc; + uint8_t service; + if (ESCvar.MBXrun == 0) + { + return; + } + if (!ESCvar.xoe && (MBXcontrol[0].state == MBXstate_inclaim)) + { + mbh = (_MBXh *) &MBX[0]; + if (mbh->mbxtype == MBXCOE) + { + if (etohs (mbh->length) < COE_MINIMUM_LENGTH) + { + MBX_error (MBXERR_INVALIDSIZE); + } + else + { + ESCvar.xoe = MBXCOE; + } + } + } + if ((ESCvar.xoe == (MBXCOE + MBXODL)) && (!ESCvar.mbxoutpost)) + { + /* continue get OD list */ + SDO_getodlistcont (); + } + if (ESCvar.xoe == MBXCOE) + { + coesdo = (_COEsdo *) &MBX[0]; + coeobjdesc = (_COEobjdesc *) &MBX[0]; + service = etohs (coesdo->coeheader.numberservice) >> 12; + if (service == COE_SDOREQUEST) + { + if ((SDO_COMMAND(coesdo->command) == COE_COMMAND_UPLOADREQUEST) + && (etohs (coesdo->mbxheader.length) == COE_HEADERSIZE)) + { + /* initiate SDO upload request */ + if (SDO_COMPLETE_ACCESS(coesdo->command)) + { + SDO_upload_complete_access (); + } + else + { + SDO_upload (); + } + } + else if (((coesdo->command & 0xef) == COE_COMMAND_UPLOADSEGREQ) + && (etohs (coesdo->mbxheader.length) == COE_HEADERSIZE) + && (ESCvar.segmented == MBXSEU)) + { + /* SDO upload segment request */ + SDO_uploadsegment (); + } + else if (SDO_COMMAND(coesdo->command) == COE_COMMAND_DOWNLOADREQUEST) + { + /* initiate SDO download request */ + if (SDO_COMPLETE_ACCESS(coesdo->command)) + { + SDO_download_complete_access (); + } + else + { + SDO_download (); + } + } + else if (SDO_COMMAND(coesdo->command) == COE_COMMAND_DOWNLOADSEGREQ) + { + /* SDO download segment request */ + SDO_downloadsegment (); + } + } + /* initiate SDO get OD list */ + else + { + if ((service == COE_SDOINFORMATION) + && (coeobjdesc->infoheader.opcode == 0x01)) + { + SDO_getodlist (); + } + /* initiate SDO get OD */ + else + { + if ((service == COE_SDOINFORMATION) + && (coeobjdesc->infoheader.opcode == 0x03)) + { + SDO_getod (); + } + /* initiate SDO get ED */ + else + { + if ((service == COE_SDOINFORMATION) + && (coeobjdesc->infoheader.opcode == 0x05)) + { + SDO_geted (); + } + else + { + /* COE not recognised above */ + if (ESCvar.xoe == MBXCOE) + { + if (service == 0) + { + MBX_error (MBXERR_INVALIDHEADER); + } + else + { + SDO_abort (etohs (coesdo->index), coesdo->subindex, ABORT_UNSUPPORTED); + } + MBXcontrol[0].state = MBXstate_idle; + ESCvar.xoe = 0; + } + } + } + } + } + } +} + +/** + * Get value from bitmap + * + * This function gets a value from a bitmap. + * + * @param[in] bitmap = bitmap containing value + * @param[in] offset = start offset + * @param[in] length = number of bits to get + * @return bitslice value + */ +static uint64_t COE_bitsliceGet (uint64_t * bitmap, int offset, int length) +{ + const int word_offset = offset / 64; + const int bit_offset = offset % 64; + const uint64_t mask = (length == 64) ? UINT64_MAX : (1ULL << length) - 1; + uint64_t w0; + uint64_t w1 = 0; + + /* Get the least significant word */ + w0 = bitmap[word_offset]; + w0 = w0 >> bit_offset; + + /* Get the most significant word, if required */ + if (length + bit_offset > 64) + { + w1 = bitmap[word_offset + 1]; + w1 = w1 << (64 - bit_offset); + } + + w0 = (w1 | w0); + return (w0 & mask); +} + +/** + * Set value in bitmap + * + * This function sets a value in a bitmap. + * + * @param[in] bitmap = bitmap to contain value + * @param[in] offset = start offset + * @param[in] length = number of bits to set + * @param[in] value = value to set + */ +static void COE_bitsliceSet (uint64_t * bitmap, int offset, int length, + uint64_t value) +{ + const int word_offset = offset / 64; + const int bit_offset = offset % 64; + const uint64_t mask = (length == 64) ? UINT64_MAX : (1ULL << length) - 1; + const uint64_t mask0 = mask << bit_offset; + uint64_t v0 = value << bit_offset; + uint64_t w0 = bitmap[word_offset]; + + /* Set the least significant word */ + w0 = (w0 & ~mask0) | (v0 & mask0); + bitmap[word_offset] = w0; + + /* Set the most significant word, if required */ + if (length + bit_offset > 64) + { + const uint64_t mask1 = mask >> (64 - bit_offset); + uint64_t v1 = value >> (64 - bit_offset); + uint64_t w1 = bitmap[word_offset + 1]; + + w1 = (w1 & ~mask1) | (v1 & mask1); + bitmap[word_offset + 1] = w1; + } +} + +/** + * Get object value + * + * This function atomically gets an object value. + * + * @param[in] obj = object description + * @return object value + */ +static uint64_t COE_getValue (const _objd * obj) +{ + uint64_t value = 0; + + /* TODO: const data */ + + switch(obj->datatype) + { + case DTYPE_BIT1: + case DTYPE_BIT2: + case DTYPE_BIT3: + case DTYPE_BIT4: + case DTYPE_BIT5: + case DTYPE_BIT6: + case DTYPE_BIT7: + case DTYPE_BIT8: + case DTYPE_BOOLEAN: + case DTYPE_UNSIGNED8: + case DTYPE_INTEGER8: + case DTYPE_BITARR8: + value = *(uint8_t *)obj->data; + break; + + case DTYPE_UNSIGNED16: + case DTYPE_INTEGER16: + case DTYPE_BITARR16: + value = *(uint16_t *)obj->data; + break; + + case DTYPE_REAL32: + case DTYPE_UNSIGNED32: + case DTYPE_INTEGER32: + case DTYPE_BITARR32: + value = *(uint32_t *)obj->data; + break; + + case DTYPE_REAL64: + case DTYPE_UNSIGNED64: + case DTYPE_INTEGER64: + /* FIXME: must be atomic */ + value = *(uint64_t *)obj->data; + break; + + default: + CC_ASSERT (0); + } + + return value; +} + +/** + * Set object value + * + * This function atomically sets an object value. + * + * @param[in] obj = object description + * @param[in] value = new value + */ +static void COE_setValue (const _objd * obj, uint64_t value) +{ + switch(obj->datatype) + { + case DTYPE_BIT1: + case DTYPE_BIT2: + case DTYPE_BIT3: + case DTYPE_BIT4: + case DTYPE_BIT5: + case DTYPE_BIT6: + case DTYPE_BIT7: + case DTYPE_BIT8: + case DTYPE_BOOLEAN: + case DTYPE_UNSIGNED8: + case DTYPE_INTEGER8: + case DTYPE_BITARR8: + *(uint8_t *)obj->data = value & UINT8_MAX; + break; + + case DTYPE_UNSIGNED16: + case DTYPE_INTEGER16: + case DTYPE_BITARR16: + *(uint16_t *)obj->data = value & UINT16_MAX; + break; + + case DTYPE_REAL32: + case DTYPE_UNSIGNED32: + case DTYPE_INTEGER32: + case DTYPE_BITARR32: + *(uint32_t *)obj->data = value & UINT32_MAX; + break; + + case DTYPE_REAL64: + case DTYPE_UNSIGNED64: + case DTYPE_INTEGER64: + /* FIXME: must be atomic */ + *(uint64_t *)obj->data = value; + break; + + default: + DPRINT ("ignored\n"); + break; + } +} + +/** + * Init default values for SDO objects + */ +void COE_initDefaultValues (void) +{ + int i; + const _objd *objd; + int n; + uint8_t maxsub; + + /* Let application decide if initialization will be skipped */ + if (ESCvar.skip_default_initialization) + { + return; + } + + /* Set default values from object descriptor */ + for (n = 0; SDOobjects[n].index != 0xffff; n++) + { + objd = SDOobjects[n].objdesc; + maxsub = SDOobjects[n].maxsub; + + i = 0; + do + { + if (objd[i].data != NULL) + { + COE_setValue (&objd[i], objd[i].value); + DPRINT ("%04x:%02x = %x\n", SDOobjects[n].index, objd[i].subindex, objd[i].value); + } + } while (objd[i++].subindex < maxsub); + } + + /* Let application override default values */ + if (ESCvar.set_defaults_hook != NULL) + { + ESCvar.set_defaults_hook(); + } +} + +/** + * Pack process data + * + * This function reads mapped objects and constructs the process data + * inputs (TXPDO). + * + * @param[in] buffer = input process data + * @param[in] nmappings = number of mappings in sync manager + * @param[in] mappings = list of mapped objects in sync manager + */ +void COE_pdoPack (uint8_t * buffer, int nmappings, _SMmap * mappings) +{ + int ix; + + /* Check that buffer is aligned on 64-bit boundary */ + CC_ASSERT (((uintptr_t)buffer & 0x07) == 0); + + for (ix = 0; ix < nmappings; ix++) + { + const _objd * obj = mappings[ix].obj; + uint16_t offset = mappings[ix].offset; + + if (obj != NULL) + { + if (obj->bitlength > 64) + { + memcpy ( + &buffer[BITS2BYTES (offset)], + obj->data, + BITS2BYTES (obj->bitlength) + ); + } + else + { + /* Atomically get object value */ + uint64_t value = COE_getValue (obj); + COE_bitsliceSet ( + (uint64_t *)buffer, + offset, + obj->bitlength, + value + ); + } + } + } +} + +/** + * Unpack process data + * + * This function unpacks process data output (RXPDO) and writes to the + * mapped objects. + * + * @param[in] buffer = output process data + * @param[in] nmappings = number of mappings in sync manager + * @param[in] mappings = list of mapped objects in sync manager + */ +void COE_pdoUnpack (uint8_t * buffer, int nmappings, _SMmap * mappings) +{ + int ix; + + /* Check that buffer is aligned on 64-bit boundary */ + CC_ASSERT (((uintptr_t)buffer & 0x07) == 0); + + for (ix = 0; ix < nmappings; ix++) + { + const _objd * obj = mappings[ix].obj; + uint16_t offset = mappings[ix].offset; + + if (obj != NULL) + { + if (obj->bitlength > 64) + { + memcpy ( + obj->data, + &buffer[BITS2BYTES (offset)], + BITS2BYTES (obj->bitlength) + ); + } + else + { + /* Atomically set object value */ + uint64_t value = COE_bitsliceGet ( + (uint64_t *)buffer, + offset, + obj->bitlength + ); + COE_setValue (obj, value); + } + } + } +} + +/** + * Fetch max subindex + * + * This function fetches the value of subindex 0 (max subindex). + * + * @param[in] index = object index + */ +uint8_t COE_maxSub (uint16_t index) +{ + int nidx; + uint8_t maxsub; + + nidx = SDO_findobject (index); + if (nidx == -1) + return 0; + + maxsub = OBJ_VALUE_FETCH (maxsub, SDOobjects[nidx].objdesc[0]); + return maxsub; +} diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_coe.h b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_coe.h new file mode 100755 index 0000000..689750c --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_coe.h @@ -0,0 +1,135 @@ +/* + * Licensed under the GNU General Public License version 2 with exceptions. See + * LICENSE file in the project root for full license information + */ + + /** \file + * \brief + * Headerfile for esc_coe.c + */ + +#ifndef __esc_coe__ +#define __esc_coe__ + +#include + +CC_PACKED_BEGIN +typedef struct CC_PACKED +{ + uint16_t subindex; + uint16_t datatype; + uint16_t bitlength; + uint16_t flags; + const char *name; + uint32_t value; + void *data; +} _objd; +CC_PACKED_END + +CC_PACKED_BEGIN +typedef struct CC_PACKED +{ + uint16_t index; + uint16_t objtype; + uint8_t maxsub; + uint8_t pad1; + const char *name; + const _objd *objdesc; +} _objectlist; +CC_PACKED_END + +typedef struct +{ + const _objd * obj; + uint16_t offset; +} _SMmap; + +#define OBJH_READ 0 +#define OBJH_WRITE 1 + +#define OTYPE_DOMAIN 0x0002 +#define OTYPE_DEFTYPE 0x0005 +#define OTYPE_DEFSTRUCT 0x0006 +#define OTYPE_VAR 0x0007 +#define OTYPE_ARRAY 0x0008 +#define OTYPE_RECORD 0x0009 + +#define DTYPE_BOOLEAN 0x0001 +#define DTYPE_INTEGER8 0x0002 +#define DTYPE_INTEGER16 0x0003 +#define DTYPE_INTEGER32 0x0004 +#define DTYPE_UNSIGNED8 0x0005 +#define DTYPE_UNSIGNED16 0x0006 +#define DTYPE_UNSIGNED32 0x0007 +#define DTYPE_REAL32 0x0008 +#define DTYPE_VISIBLE_STRING 0x0009 +#define DTYPE_OCTET_STRING 0x000A +#define DTYPE_UNICODE_STRING 0x000B +#define DTYPE_INTEGER24 0x0010 +#define DTYPE_UNSIGNED24 0x0016 +#define DTYPE_INTEGER64 0x0015 +#define DTYPE_UNSIGNED64 0x001B +#define DTYPE_REAL64 0x0011 +#define DTYPE_PDO_MAPPING 0x0021 +#define DTYPE_IDENTITY 0x0023 +#define DTYPE_BITARR8 0x002D +#define DTYPE_BITARR16 0x002E +#define DTYPE_BITARR32 0x002F +#define DTYPE_BIT1 0x0030 +#define DTYPE_BIT2 0x0031 +#define DTYPE_BIT3 0x0032 +#define DTYPE_BIT4 0x0033 +#define DTYPE_BIT5 0x0034 +#define DTYPE_BIT6 0x0035 +#define DTYPE_BIT7 0x0036 +#define DTYPE_BIT8 0x0037 +#define DTYPE_ARRAY_OF_INT 0x0260 +#define DTYPE_ARRAY_OF_SINT 0x0261 +#define DTYPE_ARRAY_OF_DINT 0x0262 +#define DTYPE_ARRAY_OF_UDINT 0x0263 + +#define ATYPE_Rpre 0x01 +#define ATYPE_Rsafe 0x02 +#define ATYPE_Rop 0x04 +#define ATYPE_Wpre 0x08 +#define ATYPE_Wsafe 0x10 +#define ATYPE_Wop 0x20 +#define ATYPE_RXPDO 0x40 +#define ATYPE_TXPDO 0x80 + +#define ATYPE_RO (ATYPE_Rpre | ATYPE_Rsafe | ATYPE_Rop) +#define ATYPE_WO (ATYPE_Wpre | ATYPE_Wsafe | ATYPE_Wop) +#define ATYPE_RW (ATYPE_RO | ATYPE_WO) +#define ATYPE_RWpre (ATYPE_Wpre | ATYPE_RO) + +#define TX_PDO_OBJIDX 0x1c13 +#define RX_PDO_OBJIDX 0x1c12 + +#define COMPLETE_ACCESS_FLAG (1 << 15) + +void ESC_coeprocess (void); +int16_t SDO_findsubindex (int16_t nidx, uint8_t subindex); +int32_t SDO_findobject (uint16_t index); +uint16_t sizeOfPDO (uint16_t index, int * nmappings, _SMmap * sm, int max_mappings); +void SDO_abort (uint16_t index, uint8_t subindex, uint32_t abortcode); +void COE_initDefaultValues (void); + +void COE_pdoPack (uint8_t * buffer, int nmappings, _SMmap * sm); +void COE_pdoUnpack (uint8_t * buffer, int nmappings, _SMmap * sm); +uint8_t COE_maxSub (uint16_t index); + +extern uint32_t ESC_download_post_objecthandler (uint16_t index, uint8_t subindex, uint16_t flags); +extern uint32_t ESC_download_pre_objecthandler (uint16_t index, + uint8_t subindex, + void * data, + size_t size, + uint16_t flags); +extern uint32_t ESC_upload_pre_objecthandler (uint16_t index, + uint8_t subindex, + void * data, + size_t size, + uint16_t flags); +extern uint32_t ESC_upload_post_objecthandler (uint16_t index, uint8_t subindex, uint16_t flags); +extern const _objectlist SDOobjects[]; + +#endif diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_eep.c b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_eep.c new file mode 100755 index 0000000..8736ac0 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_eep.c @@ -0,0 +1,80 @@ +/* + * Licensed under the GNU General Public License version 2 with exceptions. See + * LICENSE file in the project root for full license information + */ + + /** \file + * \brief + * ESI EEPROM emulator module. + */ + +#include "cc.h" +#include "esc.h" +#include "esc_eep.h" + +#include + +static uint8_t eep_buf[8]; + +/** EPP periodic task of ESC side EEPROM emulation. + * + */ +void EEP_process (void) +{ + eep_stat_t stat; + + /* check for eeprom event */ + if ((ESCvar.ALevent & ESCREG_ALEVENT_EEP) == 0) { + return; + } + + while (1) { + /* read eeprom status */ + ESC_read (ESCREG_EECONTSTAT, &stat, sizeof (eep_stat_t)); + stat.contstat.reg = etohs(stat.contstat.reg); + stat.addr = etohl(stat.addr); + + /* check busy flag, exit if job finished */ + if (!stat.contstat.bits.busy) { + return; + } + + /* clear error bits */ + stat.contstat.bits.csumErr = 0; + stat.contstat.bits.eeLoading = 0; + stat.contstat.bits.ackErr = 0; + stat.contstat.bits.wrErr = 0; + + /* process commands */ + switch (stat.contstat.bits.cmdReg) { + case EEP_CMD_IDLE: + break; + + case EEP_CMD_READ: + case EEP_CMD_RELOAD: + /* handle read request */ + if (EEP_read (stat.addr * sizeof(uint16_t), eep_buf, EEP_READ_SIZE) != 0) { + stat.contstat.bits.ackErr = 1; + } else { + ESC_write (ESCREG_EEDATA, eep_buf, EEP_READ_SIZE); + } + break; + + case EEP_CMD_WRITE: + /* handle write request */ + ESC_read (ESCREG_EEDATA, eep_buf, EEP_WRITE_SIZE); + if (EEP_write (stat.addr * sizeof(uint16_t), eep_buf, EEP_WRITE_SIZE) != 0) { + stat.contstat.bits.ackErr = 1; + } + break; + + default: + stat.contstat.bits.ackErr = 1; + } + + /* acknowledge command */ + stat.contstat.reg = htoes(stat.contstat.reg); + ESC_write (ESCREG_EECONTSTAT, &stat.contstat.reg, sizeof(uint16_t)); + } +} + diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_eep.h b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_eep.h new file mode 100755 index 0000000..b94f3d4 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_eep.h @@ -0,0 +1,77 @@ +/* + * Licensed under the GNU General Public License version 2 with exceptions. See + * LICENSE file in the project root for full license information + */ + +/** \file + * \brief + * Headerfile for esc_eep.c + */ + +#ifndef __esc_eep__ +#define __esc_eep__ + +#include +#include "esc.h" + +/* EEPROM commands */ +#define EEP_CMD_IDLE 0x0 +#define EEP_CMD_READ 0x1 +#define EEP_CMD_WRITE 0x2 +#define EEP_CMD_RELOAD 0x3 + +/* read/write size */ +#define EEP_READ_SIZE 8 +#define EEP_WRITE_SIZE 2 + +/* CONSTAT register content */ +typedef struct CC_PACKED +{ + union { + uint16_t reg; + struct { + uint8_t wrEnable:1; + uint8_t reserved:4; + uint8_t eeEmulated:1; + uint8_t eightByteRead:1; + uint8_t twoByteAddr:1; + + uint8_t cmdReg:3; + uint8_t csumErr:1; + uint8_t eeLoading:1; + uint8_t ackErr:1; + uint8_t wrErr:1; + uint8_t busy:1; + } bits; + } contstat; + + uint32_t addr; +} eep_stat_t; + +/** + * ECAT EEPROM configuration area data structure + */ +typedef union eep_config +{ + struct + { + uint16_t pdi_control; + uint16_t pdi_configuration; + uint16_t sync_impulse_len; + uint16_t pdi_configuration2; + uint16_t configured_station_alias; + uint8_t reserved[4]; + uint16_t checksum; + }; + uint32_t dword[4]; /**< Four 32 bit double word equivalent to 8 16 bit configuration area word. */ +}eep_config_t; + +/* periodic task */ +void EEP_process (void); + +/* From hardware file */ +void EEP_init (void); +int8_t EEP_read (uint32_t addr, uint8_t *data, uint16_t size); +int8_t EEP_write (uint32_t addr, uint8_t *data, uint16_t size); + +#endif diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_eoe.c b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_eoe.c new file mode 100755 index 0000000..6332cf8 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_eoe.c @@ -0,0 +1,1066 @@ +/* + * Licensed under the GNU General Public License version 2 with exceptions. See + * LICENSE file in the project root for full license information + */ + + /** \file + * \brief + * Ethernet over EtherCAT (EoE) module. + */ + +#include +#include +#include "esc.h" +#include "esc_eoe.h" + + +#if defined(EC_BIG_ENDIAN) +#define EOE_HTONS(x) (x) +#define EOE_NTOHS(x) (x) +#define EOE_HTONL(x) (x) +#define EOE_NTOHL(x) (x) +#else +#define EOE_HTONS(x) ((((x) & 0x00ffUL) << 8) | (((x) & 0xff00UL) >> 8)) +#define EOE_NTOHS(x) EOE_HTONS(x) +#define EOE_HTONL(x) ((((x) & 0x000000ffUL) << 24) | \ + (((x) & 0x0000ff00UL) << 8) | \ + (((x) & 0x00ff0000UL) >> 8) | \ + (((x) & 0xff000000UL) >> 24)) +#define EOE_NTOHL(x) EOE_HTONL(x) +#endif /* #if defined(EC_BIG_ENDIAN) */ + +#define EOE_MAKEU32(a,b,c,d) (((uint32_t)((a) & 0xff) << 24) | \ + ((uint32_t)((b) & 0xff) << 16) | \ + ((uint32_t)((c) & 0xff) << 8) | \ + (uint32_t)((d) & 0xff)) + +/** Get one byte from the 4-byte address */ +#define eoe_ip4_addr1(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[0]) +#define eoe_ip4_addr2(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[1]) +#define eoe_ip4_addr3(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[2]) +#define eoe_ip4_addr4(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[3]) + +/** Set an IP address given by the four byte-parts */ +#define EOE_IP4_ADDR_TO_U32(ipaddr,a,b,c,d) \ + (ipaddr)->addr = EOE_HTONL(EOE_MAKEU32(a,b,c,d)) + +/** Header frame info 1 */ +#define EOE_HDR_FRAME_TYPE_OFFSET 0 +#define EOE_HDR_FRAME_TYPE (0xF << 0) +#define EOE_HDR_FRAME_TYPE_SET(x) (((x) & 0xF) << 0) +#define EOE_HDR_FRAME_TYPE_GET(x) (((x) >> 0) & 0xF) +#define EOE_HDR_FRAME_PORT_OFFSET 4 +#define EOE_HDR_FRAME_PORT (0xF << 4) +#define EOE_HDR_FRAME_PORT_SET(x) (((x) & 0xF) << 4) +#define EOE_HDR_FRAME_PORT_GET(x) (((x) >> 4) & 0xF) +#define EOE_HDR_LAST_FRAGMENT_OFFSET 8 +#define EOE_HDR_LAST_FRAGMENT (0x1 << 8) +#define EOE_HDR_LAST_FRAGMENT_SET(x) (((x) & 0x1) << 8) +#define EOE_HDR_LAST_FRAGMENT_GET(x) (((x) >> 8) & 0x1) +#define EOE_HDR_TIME_APPEND_OFFSET 9 +#define EOE_HDR_TIME_APPEND (0x1 << 9) +#define EOE_HDR_TIME_APPEND_SET(x) (((x) & 0x1) << 9) +#define EOE_HDR_TIME_APPEND_GET(x) (((x) >> 9) & 0x1) +#define EOE_HDR_TIME_REQUEST_OFFSET 10 +#define EOE_HDR_TIME_REQUEST (0x1 << 10) +#define EOE_HDR_TIME_REQUEST_SET(x) (((x) & 0x1) << 10) +#define EOE_HDR_TIME_REQUEST_GET(x) (((x) >> 10) & 0x1) + +/** Header frame info 2 */ +#define EOE_HDR_FRAG_NO_OFFSET 0 +#define EOE_HDR_FRAG_NO (0x3F << 0) +#define EOE_HDR_FRAG_NO_SET(x) (((x) & 0x3F) << 0) +#define EOE_HDR_FRAG_NO_GET(x) (((x) >> 0) & 0x3F) +#define EOE_HDR_FRAME_OFFSET_OFFSET 6 +#define EOE_HDR_FRAME_OFFSET (0x3F << 6) +#define EOE_HDR_FRAME_OFFSET_SET(x) (((x) & 0x3F) << 6) +#define EOE_HDR_FRAME_OFFSET_GET(x) (((x) >> 6) & 0x3F) +#define EOE_HDR_FRAME_NO_OFFSET 12 +#define EOE_HDR_FRAME_NO (0xF << 12) +#define EOE_HDR_FRAME_NO_SET(x) (((x) & 0xF) << 12) +#define EOE_HDR_FRAME_NO_GET(x) (((x) >> 12) & 0xF) + +/** EOE param */ +#define EOE_PARAM_OFFSET 4 +#define EOE_PARAM_MAC_INCLUDE (0x1 << 0) +#define EOE_PARAM_IP_INCLUDE (0x1 << 1) +#define EOE_PARAM_SUBNET_IP_INCLUDE (0x1 << 2) +#define EOE_PARAM_DEFAULT_GATEWAY_INCLUDE (0x1 << 3) +#define EOE_PARAM_DNS_IP_INCLUDE (0x1 << 4) +#define EOE_PARAM_DNS_NAME_INCLUDE (0x1 << 5) + +/** EoE frame types */ +#define EOE_FRAG_DATA 0 +#define EOE_INIT_RESP_TIMESTAMP 1 +#define EOE_INIT_REQ 2 /* Spec SET IP REQ */ +#define EOE_INIT_RESP 3 /* Spec SET IP RESP */ +#define EOE_SET_ADDR_FILTER_REQ 4 +#define EOE_SET_ADDR_FILTER_RESP 5 +#define EOE_GET_IP_PARAM_REQ 6 +#define EOE_GET_IP_PARAM_RESP 7 +#define EOE_GET_ADDR_FILTER_REQ 8 +#define EOE_GET_ADDR_FILTER_RESP 9 + +/** Define number of ports available.(Only one is supported currently */ +#define EOE_NUMBER_OF_PORTS 1 +#define EOE_PORT_INDEX(x) ((x > 0) ? (x - 1) : 0) +/** DNS length according to ETG 1000.6 */ +#define EOE_DNS_NAME_LENGTH 32 +/** Ethernet address length not including VLAN */ +#define EOE_ETHADDR_LENGTH 6 +/** IPv4 address length */ +#define EOE_IP4_LENGTH sizeof(uint32_t) + +/** EOE ip4 address in network order */ +struct eoe_ip4_addr { + uint32_t addr; +}; +typedef struct eoe_ip4_addr eoe_ip4_addr_t; + +/** EOE ethernet address */ +CC_PACKED_BEGIN +typedef struct CC_PACKED eoe_ethaddr +{ + uint8_t addr[EOE_ETHADDR_LENGTH]; +} eoe_ethaddr_t; +CC_PACKED_END + +typedef struct +{ + /** Pointer to current RX buffer to fill */ + eoe_pbuf_t rxebuf; + /** Pointer to current TX buff to Send */ + eoe_pbuf_t txebuf; + + /** Current RX fragment number */ + uint8_t rxfragmentno; + /** Complete RX frame size of current frame */ + uint16_t rxframesize; + /** Current RX data offset in frame */ + uint16_t rxframeoffset; + /** Current RX frame number */ + uint16_t rxframeno; + + /** Current TX fragment number */ + uint8_t txfragmentno; + /** Complete TX frame size of current frame */ + uint16_t txframesize; + /** Current TX data offset in frame */ + uint16_t txframeoffset; +} _EOEvar; + +/** EoE IP request structure */ +typedef struct eoe_param +{ + uint8_t mac_set:1; + uint8_t ip_set:1; + uint8_t subnet_set:1; + uint8_t default_gateway_set:1; + uint8_t dns_ip_set:1; + uint8_t dns_name_set:1; + eoe_ethaddr_t mac; + eoe_ip4_addr_t ip; + eoe_ip4_addr_t subnet; + eoe_ip4_addr_t default_gateway; + eoe_ip4_addr_t dns_ip; + char dns_name[EOE_DNS_NAME_LENGTH]; +} eoe_param_t; + +/** Main EoE status data array. Structure gets filled with current information + * variables during EoE receive and send operations. + */ +static _EOEvar EOEvar; + +/** Main FoE configuration pointer data array. Structure is allocated and filled + * by the application defining what preferences it requires. + */ +static eoe_cfg_t * eoe_cfg; + +/** Local EoE variable holding cached IP information values. + * To be set or read from the user application, eg. TCP/IP stack. + */ +static eoe_param_t nic_ports[EOE_NUMBER_OF_PORTS]; + +/** Local init/reset functions on frame receive init */ +static void EOE_init_rx (); +/** Local init/reset functions on frame send completion */ +static void EOE_init_tx (); + +/** EoE utility function to convert uint32 to eoe ip bytes. + * @param[in] ip = ip in uint32 + * @param[out] byte_ip = eoe ip 4th octet, 3ed octet, 2nd octet, 1st octet + */ +static void EOE_ip_uint32_to_byte (eoe_ip4_addr_t * ip, uint8_t * byte_ip) +{ + byte_ip[3] = eoe_ip4_addr1(ip); /* 1st octet */ + byte_ip[2] = eoe_ip4_addr2(ip); /* 2nd octet */ + byte_ip[1] = eoe_ip4_addr3(ip); /* 3ed octet */ + byte_ip[0] = eoe_ip4_addr4(ip); /* 4th octet */ +} + +/** EoE utility function to convert eoe ip bytes to uint32. + * @param[in] byte_ip = eoe ip 4th octet, 3ed octet, 2nd octet, 1st octet + * @param[out] ip = ip in uint32 + */ +static void EOE_ip_byte_to_uint32 (uint8_t * byte_ip, eoe_ip4_addr_t * ip) +{ + EOE_IP4_ADDR_TO_U32(ip, + byte_ip[3], /* 1st octet */ + byte_ip[2], /* 2nd octet */ + byte_ip[1], /* 3ed octet */ + byte_ip[0]) ;/* 4th octet */ +} + +/** Get EoE cached MAC address + * + * @param[in] port = get MAC for port + * @param[out] mac = variable to store mac in, should fit EOE_ETHADDR_LENGTH + * @return 0= if we succeed, -1 if not set + */ +int EOE_get_mac(uint8_t port, uint8_t mac[]) +{ + int ret = -1; + int port_ix; + + if(port < EOE_NUMBER_OF_PORTS) + { + port_ix = EOE_PORT_INDEX(port); + if(nic_ports[port_ix].mac_set) + { + memcpy(mac, nic_ports[port_ix].mac.addr, + sizeof(nic_ports[port_ix].mac)); + nic_ports[port_ix].mac_set = 1; + ret = 0; + } + } + return ret; +} + +/** Set EoE cached MAC address + * + * @param[in] port = get MAC for port + * @param[in] mac = mac address to store + * @return 0= if we succeed, else -1. + */ +int EOE_ecat_set_mac(uint8_t port, uint8_t mac[]) +{ + int ret = -1; + int port_ix; + if(port < EOE_NUMBER_OF_PORTS) + { + port_ix = EOE_PORT_INDEX(port); + memcpy(nic_ports[port_ix].mac.addr, mac, + sizeof(nic_ports[port_ix].mac)); + ret = 0; + } + return ret; +} + +/** Get EoE cached ip address + * + * @param[in] port = get ip address for port + * @param[out] ip = variable to store ip in + * @return 0= if we succeed, -1 if not set + */ +int EOE_ecat_get_ip(uint8_t port, uint32_t * ip) +{ + int ret = -1; + int port_ix; + if(port < EOE_NUMBER_OF_PORTS) + { + port_ix = EOE_PORT_INDEX(port); + if(nic_ports[port_ix].ip_set) + { + *ip = EOE_NTOHL(nic_ports[port_ix].ip.addr); + ret = 0; + } + } + return ret; +} + +/** Set EoE cached ip address + * + * @param[in] port = get ip for port + * @param[in] ip = ip address to store + * @return 0= if we succeed, else -1. + */ +int EOE_ecat_set_ip(uint8_t port, uint32_t ip) +{ + int ret = -1; + int port_ix; + if(port < EOE_NUMBER_OF_PORTS) + { + port_ix = EOE_PORT_INDEX(port); + nic_ports[port_ix].ip.addr = EOE_HTONL(ip); + nic_ports[port_ix].ip_set = 1; + ret = 0; + } + return ret; +} + +/** Get EoE cached subnet ip address + * + * @param[in] port = get ip address for port + * @param[out] subnet = variable to store ip in + * @return 0= if we succeed, -1 if not set + */ +int EOE_ecat_get_subnet(uint8_t port, uint32_t * subnet) +{ + int ret = -1; + int port_ix; + if(port < EOE_NUMBER_OF_PORTS) + { + port_ix = EOE_PORT_INDEX(port); + if(nic_ports[port_ix].subnet_set) + { + *subnet = EOE_NTOHL(nic_ports[port_ix].subnet.addr); + ret = 0; + } + } + return ret; +} + +/** Set EoE cached subnet ip address + * + * @param[in] port = get ip for port + * @param[in] subnet = ip address to store + * @return 0= if we succeed, else -1. + */ +int EOE_ecat_set_subnet(uint8_t port, uint32_t subnet) +{ + int ret = -1; + int port_ix; + if(port < EOE_NUMBER_OF_PORTS) + { + port_ix = EOE_PORT_INDEX(port); + nic_ports[port_ix].subnet.addr = EOE_HTONL(subnet); + nic_ports[port_ix].subnet_set = 1; + ret = 0; + } + return ret; +} + +/** Get EoE cached default gateway ip address + * + * @param[in] port = get ip address for port + * @param[out] default_gateway = variable to store ip in + * @return 0= if we succeed, -1 if not set + */ +int EOE_ecat_get_gateway(uint8_t port, uint32_t * default_gateway) +{ + int ret = -1; + int port_ix; + if(port < EOE_NUMBER_OF_PORTS) + { + port_ix = EOE_PORT_INDEX(port); + if(nic_ports[port_ix].default_gateway_set) + { + *default_gateway = + EOE_NTOHL(nic_ports[port_ix].default_gateway.addr); + ret = 0; + } + } + return ret; +} + +/** Set EoE cached default gateway ip address + * + * @param[in] port = get ip for port + * @param[in] default_gateway = ip address to store + * @return 0= if we succeed, else -1. + */ +int EOE_ecat_set_gateway(uint8_t port, uint32_t default_gateway) +{ + int ret = -1; + int port_ix; + if(port < EOE_NUMBER_OF_PORTS) + { + port_ix = EOE_PORT_INDEX(port); + nic_ports[port_ix].default_gateway.addr = + EOE_HTONL(default_gateway); + nic_ports[port_ix].default_gateway_set = 1; + ret = 0; + } + return ret; +} + +/** Get EoE cached dns ip address + * + * @param[in] port = get ip address for port + * @param[out] dns_ip = variable to store ip in + * @return 0= if we succeed, -1 if not set + */ +int EOE_ecat_get_dns_ip(uint8_t port, uint32_t * dns_ip) +{ + int ret = -1; + int port_ix; + if(port < EOE_NUMBER_OF_PORTS) + { + port_ix = EOE_PORT_INDEX(port); + if(nic_ports[port_ix].dns_ip_set) + { + *dns_ip = EOE_NTOHL(nic_ports[port_ix].dns_ip.addr); + ret = 0; + } + } + return ret; +} + +/** Set EoE cached dns ip address + * + * @param[in] port = get ip for port + * @param[in] dns_ip = ip address to store + * @return 0= if we succeed, else -1. + */ +int EOE_ecat_set_dns_ip(uint8_t port, uint32_t dns_ip) +{ + int ret = -1; + int port_ix; + if(port < EOE_NUMBER_OF_PORTS) + { + port_ix = EOE_PORT_INDEX(port); + nic_ports[port_ix].dns_ip.addr = EOE_HTONL(dns_ip); + nic_ports[port_ix].dns_ip_set = 1; + ret = 0; + } + return ret; +} + +/** Get EoE cached dns name + * + * @param[in] port = get dns name for port + * @param[out] dns_name = variable to store dns name in + * @return 0= if we succeed, -1 if not set + */ +int EOE_ecat_get_dns_name(uint8_t port, char * dns_name) +{ + int ret = -1; + int port_ix; + if(port < EOE_NUMBER_OF_PORTS) + { + port_ix = EOE_PORT_INDEX(port); + if(nic_ports[port_ix].dns_name_set) + { + memcpy(dns_name, + nic_ports[port_ix].dns_name, + sizeof(nic_ports[port_ix].dns_name)); + ret = 0; + } + } + return ret; +} + +/** Set EoE cached dns name + * + * @param[in] port = get dns name for port + * @param[in] dns_name = dns name to store + * @return 0= if we succeed, else -1. + */ +int EOE_ecat_set_dns_name(uint8_t port, char * dns_name) +{ + int ret = -1; + int port_ix; + + if(port < EOE_NUMBER_OF_PORTS) + { + port_ix = EOE_PORT_INDEX(port); + memcpy(nic_ports[port_ix].dns_name, + dns_name, + sizeof(nic_ports[port_ix].dns_name)); + nic_ports[port_ix].dns_name_set = 1; + ret = 0; + } + return ret; +} + +/** Function for sending an simple EOE response frame. + * + * @param[in] frametype1 = frame type of response + * @param[in] result = result code + */ +static void EOE_no_data_response (uint16_t frameinfo1, uint16_t result) +{ + _EOE *eoembx; + uint8_t mbxhandle; + + /* Send back a response packet. */ + mbxhandle = ESC_claimbuffer (); + if (mbxhandle) + { + eoembx = (_EOE *) &MBX[mbxhandle * ESC_MBXSIZE]; + eoembx->mbxheader.length = htoes (ESC_EOEHSIZE); + eoembx->mbxheader.mbxtype = MBXEOE; + eoembx->eoeheader.frameinfo1 = htoes(frameinfo1); + eoembx->eoeheader.result = htoes(result); + MBXcontrol[mbxhandle].state = MBXstate_outreq; + } +} + +/** EoE get IP param request handler. Will send a get IP param response. + */ +static void EOE_get_ip (void) +{ + _EOE *req_eoembx; + _EOE *eoembx; + uint8_t mbxhandle; + uint16_t frameinfo1; + uint8_t port; + uint8_t flags; + uint8_t data_offset; + int port_ix; + + req_eoembx = (_EOE *) &MBX[0]; + frameinfo1 = etohs(req_eoembx->eoeheader.frameinfo1); + port = EOE_HDR_FRAME_PORT_GET(frameinfo1); + data_offset = EOE_PARAM_OFFSET; + flags = 0; + + if(port > EOE_NUMBER_OF_PORTS) + { + DPRINT("Invalid port\n"); + /* Return error response on given port */ + EOE_no_data_response((EOE_HDR_FRAME_PORT_SET(port) | + EOE_INIT_RESP | + EOE_HDR_LAST_FRAGMENT), + EOE_RESULT_UNSPECIFIED_ERROR); + return; + } + + /* Refresh settings if needed */ + if(eoe_cfg->load_eth_settings != NULL) + { + (void)eoe_cfg->load_eth_settings(); + } + + /* Send back an response packet. */ + mbxhandle = ESC_claimbuffer (); + if (mbxhandle) + { + eoembx = (_EOE *) &MBX[mbxhandle * ESC_MBXSIZE]; + eoembx->mbxheader.mbxtype = MBXEOE; + MBXcontrol[mbxhandle].state = MBXstate_outreq; + eoembx->eoeheader.frameinfo1 = + htoes(EOE_HDR_FRAME_TYPE_SET(EOE_GET_IP_PARAM_RESP) | + EOE_HDR_FRAME_PORT_SET(port) | + EOE_HDR_LAST_FRAGMENT); + eoembx->eoeheader.frameinfo2 = 0; + + /* include mac in get ip request */ + port_ix = EOE_PORT_INDEX(port); + if(nic_ports[port_ix].mac_set) + { + flags |= EOE_PARAM_MAC_INCLUDE; + memcpy(&eoembx->data[data_offset] , + nic_ports[port_ix].mac.addr, + EOE_ETHADDR_LENGTH); + /* Add size of mac address */ + data_offset += EOE_ETHADDR_LENGTH; + + } + /* include ip in get ip request */ + if(nic_ports[port_ix].ip_set) + { + flags |= EOE_PARAM_IP_INCLUDE; + EOE_ip_uint32_to_byte(&nic_ports[port_ix].ip, + &eoembx->data[data_offset]); + /* Add size of uint32 IP address */ + data_offset += EOE_IP4_LENGTH; + } + + /* include subnet in get ip request */ + if(nic_ports[port_ix].subnet_set) + { + flags |= EOE_PARAM_SUBNET_IP_INCLUDE; + EOE_ip_uint32_to_byte(&nic_ports[port_ix].subnet, + &eoembx->data[data_offset]); + /* Add size of uint32 IP address */ + data_offset += EOE_IP4_LENGTH; + } + + /* include default gateway in get ip request */ + if(nic_ports[port_ix].default_gateway_set) + { + flags |= EOE_PARAM_DEFAULT_GATEWAY_INCLUDE; + EOE_ip_uint32_to_byte(&nic_ports[port_ix].default_gateway, + &eoembx->data[data_offset]); + /* Add size of uint32 IP address */ + data_offset += EOE_IP4_LENGTH; + } + /* include dns ip in get ip request */ + if(nic_ports[port_ix].dns_ip_set) + { + flags |= EOE_PARAM_DNS_IP_INCLUDE; + EOE_ip_uint32_to_byte(&nic_ports[port_ix].dns_ip, + &eoembx->data[data_offset]); + /* Add size of uint32 IP address */ + data_offset += EOE_IP4_LENGTH; + } + + /* include dns name in get ip request */ + if(nic_ports[port_ix].dns_name_set) + { + /* TwinCAT include EOE_DNS_NAME_LENGTH chars even if name is shorter */ + flags |= EOE_PARAM_DNS_NAME_INCLUDE; + memcpy(&eoembx->data[data_offset], + nic_ports[port_ix].dns_name, + EOE_DNS_NAME_LENGTH); + /* Add size of dns name length */ + data_offset += EOE_DNS_NAME_LENGTH; + } + + eoembx->data[0] = flags; + eoembx->mbxheader.length = htoes (ESC_EOEHSIZE + data_offset); + } +} + +/** EoE set IP param request handler. Will send a set IP param response. + */ +static void EOE_set_ip (void) +{ + _EOE *eoembx; + uint16_t eoedatasize; + uint16_t frameinfo1; + uint8_t port; + uint8_t flags; + uint8_t data_offset; + uint16_t result; + int port_ix; + + eoembx = (_EOE *) &MBX[0]; + eoedatasize = etohs(eoembx->mbxheader.length) - ESC_EOEHSIZE; + frameinfo1 = etohs(eoembx->eoeheader.frameinfo1); + port = EOE_HDR_FRAME_PORT_GET(frameinfo1); + flags = eoembx->data[0]; + data_offset = EOE_PARAM_OFFSET; + + if(port > EOE_NUMBER_OF_PORTS) + { + DPRINT("Invalid port\n"); + /* Return error response on given port */ + EOE_no_data_response((EOE_HDR_FRAME_PORT_SET(port) | + EOE_INIT_RESP | + EOE_HDR_LAST_FRAGMENT), + EOE_RESULT_UNSPECIFIED_ERROR); + return; + } + + /* mac included in set ip request? */ + port_ix = EOE_PORT_INDEX(port); + if(flags & EOE_PARAM_MAC_INCLUDE) + { + memcpy(&nic_ports[port_ix].mac.addr, + &eoembx->data[data_offset], + EOE_ETHADDR_LENGTH); + nic_ports[port_ix].mac_set = 1; + /* Add size of mac address */ + data_offset += EOE_ETHADDR_LENGTH; + } + /* ip included in set ip request? */ + if(flags & EOE_PARAM_IP_INCLUDE) + { + EOE_ip_byte_to_uint32(&eoembx->data[data_offset], + &nic_ports[port_ix].ip); + nic_ports[port_ix].ip_set = 1; + /* Add size of uint32 IP address */ + data_offset += EOE_IP4_LENGTH; + } + /* subnet included in set ip request? */ + if(flags & EOE_PARAM_SUBNET_IP_INCLUDE) + { + EOE_ip_byte_to_uint32(&eoembx->data[data_offset], + &nic_ports[port_ix].subnet); + nic_ports[port_ix].subnet_set = 1; + /* Add size of uint32 IP address */ + data_offset += EOE_IP4_LENGTH; + } + /* default gateway included in set ip request? */ + if(flags & EOE_PARAM_DEFAULT_GATEWAY_INCLUDE) + { + EOE_ip_byte_to_uint32(&eoembx->data[data_offset], + &nic_ports[port_ix].default_gateway); + nic_ports[port_ix].default_gateway_set = 1; + /* Add size of uint32 IP address */ + data_offset += EOE_IP4_LENGTH; + } + /* dns ip included in set ip request? */ + if(flags & EOE_PARAM_DNS_IP_INCLUDE) + { + EOE_ip_byte_to_uint32(&eoembx->data[data_offset], + &nic_ports[port_ix].dns_ip); + nic_ports[port_ix].dns_ip_set = 1; + /* Add size of uint32 IP address */ + data_offset += EOE_IP4_LENGTH; + } + /* dns name included in set ip request? */ + if(flags & EOE_PARAM_DNS_NAME_INCLUDE) + { + uint16_t dns_len = MIN((eoedatasize - data_offset), EOE_DNS_NAME_LENGTH); + memcpy(nic_ports[port_ix].dns_name, + &eoembx->data[data_offset], + dns_len); + nic_ports[port_ix].dns_name_set = 1; + data_offset += dns_len; /* expected 1- EOE_DNS_NAME_LENGTH; */ + } + + if(data_offset > eoedatasize) + { + result = MBXERR_SIZETOOSHORT; + } + else + { + /* Application specific store settings function. From there + * you typically set the IP for the TCP/IP stack */ + if(eoe_cfg->store_ethernet_settings != NULL) + { + result = eoe_cfg->store_ethernet_settings(); + } + else + { + result = EOE_RESULT_NO_IP_SUPPORT; + } + } + EOE_no_data_response((EOE_HDR_FRAME_PORT_SET(port) | + EOE_INIT_RESP | + EOE_HDR_LAST_FRAGMENT), + result); +} + +/** EoE receive fragment handler. + */ +static void EOE_receive_fragment (void) +{ + _EOE *eoembx; + eoembx = (_EOE *) &MBX[0]; + uint16_t eoedatasize = etohs(eoembx->mbxheader.length) - ESC_EOEHSIZE; + uint16_t frameinfo1 = etohs(eoembx->eoeheader.frameinfo1); + uint16_t frameinfo2 = etohs(eoembx->eoeheader.frameinfo2); + + /* Capture error case */ + if(EOEvar.rxfragmentno != EOE_HDR_FRAG_NO_GET(frameinfo2)) + { + DPRINT("Unexpected fragment number %d, expected: %d\n", + EOE_HDR_FRAG_NO_GET(frameinfo2), EOEvar.rxfragmentno); + /* Clean up existing saved data */ + if(EOEvar.rxfragmentno != 0) + { + EOE_init_rx(); + } + /* Skip fragment if not start of new frame */ + if(EOE_HDR_FRAG_NO_GET(frameinfo2) > 0) + { + return; + } + } + + /* Start of new frame at fragment 0 */ + if(EOEvar.rxfragmentno == 0) + { + EOEvar.rxframesize = (EOE_HDR_FRAME_OFFSET_GET(frameinfo2) << 5); + + if(EOEvar.rxebuf.payload != NULL) + { + EOEvar.rxebuf.len = EOEvar.rxframesize; + EOEvar.rxframeoffset = 0; + EOEvar.rxframeno = EOE_HDR_FRAME_NO_GET(frameinfo2); + } + } + /* In frame fragment received */ + else + { + uint16_t offset = (EOE_HDR_FRAME_OFFSET_GET(frameinfo2) << 5); + /* Validate received fragment */ + if(EOEvar.rxframeno != EOE_HDR_FRAME_NO_GET(frameinfo2)) + { + DPRINT("Unexpected frame number %d, expected: %d\n", + EOE_HDR_FRAME_NO_GET(frameinfo2), EOEvar.rxframeno); + EOE_init_rx (); + return; + } + else if(EOEvar.rxframeoffset != offset) + { + DPRINT("Unexpected frame offset %d, expected: %d\n", + offset, EOEvar.rxframeoffset); + EOE_init_rx (); + return; + } + } + + /* Check so allocated buffer is sufficient */ + if ((EOEvar.rxframeoffset + eoedatasize) <= EOEvar.rxframesize) + { + memcpy((uint8_t *)(EOEvar.rxebuf.payload + EOEvar.rxframeoffset), + eoembx->data, + eoedatasize); + EOEvar.rxframeoffset += eoedatasize; + EOEvar.rxfragmentno++; + } + else + { + DPRINT("Size of data exceed available buffer size\n"); + EOE_init_rx (); + return; + } + + if(EOE_HDR_LAST_FRAGMENT_GET(frameinfo1)) + { + /* Remove time stamp, TODO support for time stamp? */ + if(EOE_HDR_TIME_APPEND_GET(frameinfo1)) + { + EOEvar.rxframeoffset -= 4; + } + EOEvar.rxebuf.len = EOEvar.rxframeoffset; + eoe_cfg->handle_recv_buffer(EOE_HDR_FRAME_PORT_GET(frameinfo1), + &EOEvar.rxebuf); + /* Pass ownership of buf to receive function */ + EOEvar.rxebuf.payload = NULL; + EOE_init_rx (); + } +} + +/** EoE send fragment handler. + */ +static void EOE_send_fragment () +{ + _EOE *eoembx; + uint8_t mbxhandle; + int len; + int len_to_send; + uint16_t frameinfo1; + uint16_t frameinfo2; + static uint8_t frameno = 0; + + /* Do we have a current transfer on-going */ + if(EOEvar.txebuf.payload == NULL) + { + /* Fetch a buffer if available */ + len = eoe_cfg->fetch_send_buffer(0, &EOEvar.txebuf); + if(len > 0) + { + EOEvar.txframesize = len; + } + else + { + return; + } + } + + /* Process the frame if we can get a free mailbox */ + mbxhandle = ESC_claimbuffer (); + if (mbxhandle) + { + len_to_send = EOEvar.txframesize - EOEvar.txframeoffset; + if((len_to_send + ESC_EOEHSIZE + ESC_MBXHSIZE) > ESC_MBXSIZE) + { + /* Adjust to len in whole 32 octet blocks to fit specification*/ + len_to_send = + (((ESC_MBXSIZE - ESC_EOEHSIZE - ESC_MBXHSIZE) >> 5) << 5); + } + + /* TODO: port handling? */ + if(len_to_send == (EOEvar.txframesize - EOEvar.txframeoffset)) + { + frameinfo1 = EOE_HDR_LAST_FRAGMENT_SET(1); + } + else + { + frameinfo1 = 0; + } + + /* Set fragment number */ + frameinfo2 = EOE_HDR_FRAG_NO_SET(EOEvar.txfragmentno); + + /* Set complete size for fragment 0 or offset for in frame fragments */ + if(EOEvar.txfragmentno > 0) + { + frameinfo2 |= (EOE_HDR_FRAME_OFFSET_SET((EOEvar.txframeoffset >> 5))); + } + else + { + frameinfo2 |= + (EOE_HDR_FRAME_OFFSET_SET(((EOEvar.txframesize + 31) >> 5))); + frameno++; + } + + /* Set frame number */ + frameinfo2 = frameinfo2 | EOE_HDR_FRAME_NO_SET(frameno); + + eoembx = (_EOE *) &MBX[mbxhandle * ESC_MBXSIZE]; + eoembx->mbxheader.length = htoes (len_to_send + ESC_EOEHSIZE); + eoembx->mbxheader.mbxtype = MBXEOE; + eoembx->eoeheader.frameinfo1 = htoes(frameinfo1); + eoembx->eoeheader.frameinfo2 = htoes(frameinfo2); + + /* Copy data to mailbox */ + memcpy(eoembx->data, + &EOEvar.txebuf.payload[EOEvar.txframeoffset], + len_to_send); + MBXcontrol[mbxhandle].state = MBXstate_outreq; + + /* Did we complete the frame? */ + if(len_to_send == (EOEvar.txframesize - EOEvar.txframeoffset)) + { + EOE_init_tx (); + } + else + { + EOEvar.txframeoffset += len_to_send; + EOEvar.txfragmentno += 1; + } + if(eoe_cfg->fragment_sent_event != NULL) + { + eoe_cfg->fragment_sent_event(); + } + } +} + +/** Initialize by clearing all current status variables and fetch new buffer. + */ +static void EOE_init_rx () +{ + /* Reset RX transfer status variables */ + EOEvar.rxfragmentno = 0; + EOEvar.rxframesize = 0; + EOEvar.rxframeoffset = 0; + EOEvar.rxframeno = 0; + + /* Fetch buffer */ + if(EOEvar.rxebuf.payload == NULL) + { + if(eoe_cfg->get_buffer != NULL) + { + /* TODO: verify size VS buffer size */ + eoe_cfg->get_buffer(&EOEvar.rxebuf); + } + } +} + +/** Initialize by clearing all current status variables and release old buffer. + */ +static void EOE_init_tx () +{ + /* Reset TX transfer status variables */ + EOEvar.txfragmentno = 0; + EOEvar.txframesize = 0; + EOEvar.txframeoffset = 0; + + /* Release what seems as an abandoned buffer */ + if((EOEvar.txebuf.payload != NULL)) + { + if(eoe_cfg->free_buffer != NULL) + { + eoe_cfg->free_buffer(&EOEvar.txebuf); + EOEvar.txebuf.pbuf = NULL; + EOEvar.txebuf.payload = NULL; + EOEvar.txebuf.len = 0; + } + } +} + +/** Initialize by clearing all current status variables. + */ +void EOE_init () +{ + DPRINT("EOE_init\n"); + EOE_init_tx (); + EOE_init_rx (); +} + +/** Function copying the application configuration variable + * to the EoE module local pointer variable. + * + * @param[in] cfg = Pointer to by the Application static declared + * configuration variable holding application specific details. + */ +void EOE_config (eoe_cfg_t * cfg) +{ + eoe_cfg = cfg; +} + +/** Main EoE receive function checking the status on current mailbox buffers + * carrying data, distributing the mailboxes to appropriate EOE functions + * depending on requested frametype. + */ +void ESC_eoeprocess (void) +{ + _MBXh *mbh; + _EOE *eoembx; + uint16_t frameinfo1; + + if (ESCvar.MBXrun == 0) + { + return; + } + if (!ESCvar.xoe && (MBXcontrol[0].state == MBXstate_inclaim)) + { + mbh = (_MBXh *) &MBX[0]; + if (mbh->mbxtype == MBXEOE) + { + ESCvar.xoe = MBXEOE; + } + } + if (ESCvar.xoe == MBXEOE) + { + eoembx = (_EOE *) &MBX[0]; + /* Verify the size of the file data. */ + if (etohs (eoembx->mbxheader.length) < ESC_EOEHSIZE) + { + EOE_no_data_response ( + EOE_INIT_RESP | EOE_HDR_LAST_FRAGMENT, + MBXERR_SIZETOOSHORT); + } + else + { + frameinfo1 = etohs(eoembx->eoeheader.frameinfo1); + switch (EOE_HDR_FRAME_TYPE_GET(frameinfo1)) + { + case EOE_FRAG_DATA: + { + EOE_receive_fragment (); + break; + } + case EOE_INIT_REQ: + { + EOE_set_ip (); + break; + } + case EOE_GET_IP_PARAM_REQ: + { + EOE_get_ip (); + break; + } + case EOE_INIT_RESP_TIMESTAMP: + case EOE_INIT_RESP: + case EOE_SET_ADDR_FILTER_REQ: + case EOE_SET_ADDR_FILTER_RESP: + case EOE_GET_IP_PARAM_RESP: + case EOE_GET_ADDR_FILTER_REQ: + case EOE_GET_ADDR_FILTER_RESP: + default: + { + DPRINT("EOE_RESULT_UNSUPPORTED_TYPE\n"); + EOE_no_data_response ((EOE_HDR_FRAME_PORT & frameinfo1) | + (EOE_HDR_FRAME_TYPE & frameinfo1) | + EOE_HDR_LAST_FRAGMENT, + EOE_RESULT_UNSUPPORTED_FRAME_TYPE); + break; + } + } + } + MBXcontrol[0].state = MBXstate_idle; + ESCvar.xoe = 0; + } +} +/** EoE function to send a fragment. + * NOTE: Not thread safe, should be called from the SOES task sequential + * with other mailbox functions. Add support for threading by adding + * a thread safe application fetch function, example a mailbox with buffers + * to send, posted by TCP/IP stack and fetched by SOES task. + */ +void ESC_eoeprocess_tx (void) +{ + if (ESCvar.MBXrun == 0) + { + return; + } + EOE_send_fragment (); +} diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_eoe.h b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_eoe.h new file mode 100755 index 0000000..78bd74b --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_eoe.h @@ -0,0 +1,68 @@ +/* + * Licensed under the GNU General Public License version 2 with exceptions. See + * LICENSE file in the project root for full license information + */ + +/** \file + * \brief + * Headerfile for esc_eoe.c + */ + +#ifndef __esc_eoe__ +#define __esc_eoe__ + +#include + +typedef struct eoe_pbuf +{ + /** Pointer to frame buffer type used by a TCP/IP stack. (Not mandatory) */ + void * pbuf; + /** Pointer to frame buffer to send or read from */ + uint8_t * payload; + /** Length of data in frame buffer */ + size_t len; +} eoe_pbuf_t; + +typedef struct eoe_cfg +{ + /** Callback function to get a frame buffer for storage of received frame */ + void (*get_buffer) (eoe_pbuf_t * ebuf); + /** Callback function to free a frame buffer */ + void (*free_buffer) (eoe_pbuf_t * ebuf); + /** Callback function to read local settings and update EtherCAT variables + * to be delivered to the EtherCAT Master + */ + int (*load_eth_settings) (void); + /** Callback function to read settings provided by the EtherCAT master + * and store to local settings. + */ + int (*store_ethernet_settings) (void); + /** Callback to frame receive function in TCP(IP stack, + * caller should free the buffer + * */ + void (*handle_recv_buffer) (uint8_t port, eoe_pbuf_t * ebuf); + /** Callback to fetch a buffer to send */ + int (*fetch_send_buffer) (uint8_t port, eoe_pbuf_t * ebuf); + /** Callback to notify the application fragment sent */ + void (*fragment_sent_event) (void); +} eoe_cfg_t; + +int EOE_ecat_get_mac (uint8_t port, uint8_t mac[]); +int EOE_ecat_get_ip (uint8_t port, uint32_t * ip); +int EOE_ecat_get_subnet (uint8_t port, uint32_t * subnet); +int EOE_ecat_get_gateway (uint8_t port, uint32_t * default_gateway); +int EOE_ecat_get_dns_ip (uint8_t port, uint32_t * dns_ip); +int EOE_ecat_get_dns_name (uint8_t port, char * dns_name); +int EOE_ecat_set_mac (uint8_t port, uint8_t mac[]); +int EOE_ecat_set_ip (uint8_t port, uint32_t ip); +int EOE_ecat_set_subnet (uint8_t port, uint32_t subnet); +int EOE_ecat_set_gateway (uint8_t port, uint32_t default_gateway); +int EOE_ecat_set_dns_ip (uint8_t port, uint32_t dns_ip); +int EOE_ecat_set_dns_name (uint8_t port, char * dns_name); + +void EOE_config (eoe_cfg_t * cfg); +void EOE_init (void); +void ESC_eoeprocess (void); +void ESC_eoeprocess_tx (void); + +#endif diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_foe.c b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_foe.c new file mode 100755 index 0000000..475f460 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_foe.c @@ -0,0 +1,627 @@ +/* + * Licensed under the GNU General Public License version 2 with exceptions. See + * LICENSE file in the project root for full license information + */ + + /** \file + * \brief + * File over EtherCAT (FoE) module. + */ + + +#include +#include "esc.h" +#include "esc_foe.h" +#include + + /** \file + * \brief + * File over EtherCAT (FoE) module. + * + * FOE read / write and FOE service functions + */ + +//define if FOE_read should be supported +//#define FOE_READ_SUPPORTED + +/** Variable holding current filename read at FOE Open. + */ +static char foe_file_name[FOE_FN_MAX + 1]; + + +/** Main FoE configuration pointer data array. Structure is allocated and filled + * by the application defining what preferences it requires. + */ +static foe_cfg_t * foe_cfg; +/** Pointer to current file configuration item used by FoE. + */ +static foe_file_cfg_t * foe_file; +/** Main FoE status data array. Structure gets filled with current status + * variables during FoE usage. + */ +static _FOEvar FOEvar; + +/** Validate a write or read request by checking filename and password. + * + * @param[in] name = Filename + * @param[in] num_chars = Length of filename + * @param[in] pass = Numeric variable of password + * @param[in] op = Request op-code + * @return 0= if we succeed, FOE_ERR_NOTFOUND something wrong with filename or + * password + */ +static int FOE_fopen (char *name, uint8_t num_chars, uint32_t pass, uint8_t op) +{ + uint32_t i; + + /* Unpack the file name into characters we can look at. */ + if (num_chars > FOE_FN_MAX) + { + num_chars = FOE_FN_MAX; + } + + for (i = 0; i < num_chars; i++) + { + foe_file_name[i] = name[i]; + } + foe_file_name[i] = '\0'; + + /* Figure out what file they're talking about. */ + for (i = 0; i < foe_cfg->n_files; i++) + { + if (0 == strncmp (foe_file_name, foe_cfg->files[i].name, num_chars)) + { + if (pass != foe_cfg->files[i].filepass) + { + return FOE_ERR_NORIGHTS; + } + + if (op == FOE_OP_WRQ && + (foe_cfg->files[i].write_only_in_boot) && + (ESCvar.ALstatus != ESCboot)) + { + return FOE_ERR_NOTINBOOTSTRAP; + } + + foe_file = &foe_cfg->files[i]; + foe_file->address_offset = 0; + foe_file->total_size = 0; + switch (op) + { + case FOE_OP_RRQ: + { + FOEvar.fposition = 0; + FOEvar.fend = foe_cfg->files[i].max_data; + return 0; + } + case FOE_OP_WRQ: + { + FOEvar.fposition = 0; + FOEvar.fend = foe_cfg->files[i].max_data; + return 0; + } + } + } + } + + return FOE_ERR_NOTFOUND; +} + +#ifdef FOE_READ_SUPPORTED +/** Function writing local data to mailbox buffer to be sent as next FoE frame. + * It will try to fill the Mailbox buffer available if there is enough data + * left to read. + * + * @param[in] data = pointer to buffer + * @param[in] maxlength = max length of data possible to read, controlled by + * Mailbox - FoE and Mailbox frame headers. + + * @return Number of copied bytes. + */ +static uint16_t FOE_fread (uint8_t * data, uint16_t maxlength) +{ + uint16_t ncopied = 0; + + while (maxlength && (FOEvar.fend - FOEvar.fposition)) + { + maxlength--; + *(data++) = foe_cfg->fbuffer[FOEvar.fposition++]; + ncopied++; + } + + return ncopied; +} +#endif + +/** Function reading mailbox buffer to local buffer to be handled by + * application write hook. Ex. flash routine used by software update. + * It will consume the buffer and call the write hook every time the configured + * flush buffer limit is reached. + * + * + * @param[in] data = Pointer to buffer + * @param[in] length = Length of data to read + + * @return Number of copied bytes. + */ +static uint16_t FOE_fwrite (uint8_t *data, uint16_t length) +{ + uint16_t ncopied = 0; + uint32_t failed = 0; + + DPRINT("FOE_fwrite\n"); + FOEvar.fprevposition = FOEvar.fposition; + while (length && (FOEvar.fend - FOEvar.fposition) && !failed) + { + length--; + foe_cfg->fbuffer[FOEvar.fbufposition++] = *(data++); + if(FOEvar.fbufposition >= foe_cfg->buffer_size) + { + failed = foe_file->write_function (foe_file, foe_cfg->fbuffer, FOEvar.fbufposition); + FOEvar.fbufposition = 0; + foe_file->address_offset += foe_cfg->buffer_size; + } + FOEvar.fposition++; + ncopied++; + } + + foe_file->total_size += ncopied; + + DPRINT("FOE_fwrite END with : %d\n",ncopied); + return ncopied; +} + + +/** Function handling the final FOE_fwrite when we close up regardless + * if we have filled the buffers or not. + * + * @return Number of copied bytes on success, 0= if failed. + */ +static uint32_t FOE_fclose (void) +{ + uint32_t failed = 0; + + DPRINT("FOE_fclose\n"); + + failed = foe_file->write_function (foe_file, foe_cfg->fbuffer, FOEvar.fbufposition); + foe_file->address_offset += FOEvar.fbufposition; + FOEvar.fbufposition = 0; + + return failed; +} + +/** Initialize by clearing all current status variables. + * + */ +void FOE_init () +{ + DPRINT("FOE_init\n"); + FOEvar.foepacket = 0; + FOEvar.foestate = FOE_READY; + FOEvar.fposition = 0; + FOEvar.fprevposition = 0; + FOEvar.fbufposition = 0; +} + +/** Function for sending an FOE abort frame. + * + * @param[in] code = abort code + */ +static void FOE_abort (uint32_t code) +{ + _FOE *foembx; + uint8_t mbxhandle; + + if (code) + { + /* Send back an error packet. */ + mbxhandle = ESC_claimbuffer (); + if (mbxhandle) + { + foembx = (_FOE *) &MBX[mbxhandle * ESC_MBXSIZE]; + foembx->mbxheader.length = htoes (ESC_FOEHSIZE); /* Don't bother with error text for now. */ + foembx->mbxheader.mbxtype = MBXFOE; + foembx->foeheader.opcode = FOE_OP_ERR; + foembx->foeheader.errorcode = htoel (code); + MBXcontrol[mbxhandle].state = MBXstate_outreq; + } + /* Nothing we can do if we can't get an outbound mailbox. */ + } + DPRINT("FOE_abort: 0x%X\n", code); + FOE_init (); +} + +#ifdef FOE_READ_SUPPORTED +/** Sends an FoE data frame, returning the number of data bytes + * written or an error number. + * Error numbers will be greater than FOE_DATA_SIZE. + + * @param[in] data = pointer to buffer + * @param[in] length = length of data to read + + * @return Number of data bytes written or an error number. Error numbers + * will be greater than FOE_DATA_SIZE. + */ +static int FOE_send_data_packet () +{ + _FOE *foembx; + uint16_t data_len; + uint8_t mbxhandle; + + mbxhandle = ESC_claimbuffer (); + if (mbxhandle) + { + foembx = (_FOE *) &MBX[mbxhandle * ESC_MBXSIZE]; + data_len = FOE_fread (foembx->data, ESC_FOE_DATA_SIZE); + foembx->foeheader.opcode = FOE_OP_DATA; + foembx->foeheader.packetnumber = htoel (FOEvar.foepacket); + FOEvar.foepacket++; + foembx->mbxheader.length = htoes (data_len + ESC_FOEHSIZE); + foembx->mbxheader.mbxtype = MBXFOE; + /* Mark the outbound mailbox as filled. */ + MBXcontrol[mbxhandle].state = MBXstate_outreq; + return data_len; + } + else + { + return FOE_ERR_PROGERROR; + } +} +#endif + +/** Sends an FoE ack data frame. + + * @return 0= or error number. + */ +static int FOE_send_ack () +{ + _FOE *foembx; + uint8_t mbxhandle; + + mbxhandle = ESC_claimbuffer (); + if (mbxhandle) + { + DPRINT("FOE_send_ack\n"); + foembx = (_FOE *) &MBX[mbxhandle * ESC_MBXSIZE]; + foembx->mbxheader.length = htoes (ESC_FOEHSIZE); + foembx->mbxheader.mbxtype = MBXFOE; + foembx->foeheader.opcode = FOE_OP_ACK; + foembx->foeheader.packetnumber = htoel (FOEvar.foepacket); + FOEvar.foepacket++; + MBXcontrol[mbxhandle].state = MBXstate_outreq; + return 0; + } + else + { + DPRINT("ERROR:FOE_send_ack\n"); + return FOE_ERR_PROGERROR; + } +} + +/* Handlers for various FoE states. */ + +#ifdef FOE_READ_SUPPORTED +/** FoE read request handler. Starts with Initialize, Open and Sending one frame. + * When first frame have been sent we will send data from Ack. + * On error we will send FOE Abort. + * + */ +static void FOE_read () +{ + _FOE *foembx; + uint32_t data_len; + uint32_t password; + int res; + + if (FOEvar.foestate != FOE_READY) + { + FOE_abort (FOE_ERR_ILLEGAL); + return; + } + + FOE_init (); + foembx = (_FOE *) &MBX[0]; + /* Get the length of the file name in octets. */ + data_len = etohs (foembx->mbxheader.length) - ESC_FOEHSIZE; + password = etohl (foembx->foeheader.password); + + res = FOE_fopen (foembx->filename, data_len, password, FOE_OP_RRQ); + if (res == 0) + { + FOEvar.foepacket = 1; + /* + * Attempt to send the packet + */ + res = FOE_send_data_packet (); + if (res <= (int)ESC_FOE_DATA_SIZE) + { + FOEvar.foestate = FOE_WAIT_FOR_ACK; + } + else + { + FOE_abort (res); + } + } + else + { + FOE_abort (res); + } +} + +/** FoE data ack handler. Will continue sending next frame until finished. + * On error we will send FOE Abort. + */ +static void FOE_ack () +{ + int res; + + /* Make sure we're able to take this. */ + if (FOEvar.foestate == FOE_WAIT_FOR_FINAL_ACK) + { + /* Move us back to ready state. */ + FOE_init (); + return; + } + else if (FOEvar.foestate != FOE_WAIT_FOR_ACK) + { + FOE_abort (FOE_ERR_ILLEGAL); + return; + } + res = FOE_send_data_packet (); + if (res < (int)ESC_FOE_DATA_SIZE) + { + FOEvar.foestate = FOE_WAIT_FOR_FINAL_ACK; + } + else if (res >= FOE_ERR_NOTDEFINED) + { + FOE_abort (FOE_ERR_PROGERROR); + } +} +#endif + +/** FoE write request handler. Starts with Initialize, Open and Ack that we can/will + * receive data. On error we will send FOE Abort. + * + */ +static void FOE_write () +{ + _FOE *foembx; + uint32_t data_len; + uint32_t password; + int res; + + if (FOEvar.foestate != FOE_READY) + { + FOE_abort (FOE_ERR_ILLEGAL); + return; + } + + FOE_init (); + foembx = (_FOE *) &MBX[0]; + data_len = etohs (foembx->mbxheader.length) - ESC_FOEHSIZE; + password = etohl (foembx->foeheader.password); + + /* Get an address we can write the file to, if possible. */ + res = FOE_fopen (foembx->filename, data_len, password, FOE_OP_WRQ); + DPRINT("%s %sOK, file \"%s\"\n", __func__, (res == 0) ? "" : "N", foe_file_name); + if (res == 0) + { + res = FOE_send_ack (); + if (res) + { + FOE_abort (res); + } + else + { + FOEvar.foestate = FOE_WAIT_FOR_DATA; + } + } + else + { + FOE_abort (res); + } +} +/** FoE data request handler. Validates and reads data until we're finished. Every + * read frame followed by an Ack frame. On error we will send FOE Abort. + * + */ +static void FOE_data () +{ + _FOE *foembx; + uint32_t packet; + uint16_t data_len, ncopied; + int res; + + if(FOEvar.foestate != FOE_WAIT_FOR_DATA) + { + FOE_abort(FOE_ERR_ILLEGAL); + return; + } + + foembx = (_FOE*)&MBX[0]; + data_len = etohs(foembx->mbxheader.length) - ESC_FOEHSIZE; + packet = etohl(foembx->foeheader.packetnumber); + + if (packet != FOEvar.foepacket) + { + DPRINT("FOE_data packet error, packet: %d, foeheader.packet: %d\n",packet,FOEvar.foepacket); + FOE_abort (FOE_ERR_PACKETNO); + } + else if (data_len == 0) + { + DPRINT("FOE_data completed\n"); + FOE_fclose (); + res = FOE_send_ack (); + FOE_init (); + } + else if (FOEvar.fposition + data_len > FOEvar.fend) + { + DPRINT("FOE_data disk full\n"); + FOE_abort (FOE_ERR_DISKFULL); + } + else + { + ncopied = FOE_fwrite (foembx->data, data_len); + if (!ncopied) + { + DPRINT("FOE_data no copied\n"); + FOE_abort (FOE_ERR_PROGERROR); + } + else if (data_len == ESC_FOE_DATA_SIZE) + { + DPRINT("FOE_data data_len == FOE_DATA_SIZE\n"); + if (ncopied != data_len) + { + DPRINT("FOE_data only %d of %d copied\n",ncopied, data_len); + FOE_abort (FOE_ERR_PROGERROR); + } + res = FOE_send_ack (); + if (res) + { + FOE_abort (res); + } + } + else + { + if ((ncopied != data_len) || FOE_fclose ()) + { + DPRINT("FOE_fclose failed to write extra buffer\n"); + FOE_abort (FOE_ERR_PROGERROR); + } + else + { + DPRINT("FOE_data completed\n"); + res = FOE_send_ack (); + FOE_init (); + } + } + } +} + +#ifdef FOE_READ_SUPPORTED +/** FoE read request busy handler. Send an Ack of last frame again. On error + * we will send FOE Abort. + * + */ +static void FOE_busy () +{ + /* Only valid if we're servicing a read request. */ + if (FOEvar.foestate != FOE_WAIT_FOR_ACK) + { + FOE_abort (FOE_ERR_ILLEGAL); + } + else + { + /* Send the last part again. */ + FOEvar.fposition = FOEvar.fprevposition; + FOEvar.foepacket--; + FOE_ack (); + } +} +#endif + +/** FoE error requesthandler. Send an FOE Abort. + * + */ +static void FOE_error () +{ + /* Master panic! abort the transfer. */ + FOE_abort (0); +} + +/** Function copying the application configuration variable + * to the FoE module local pointer variable. + * + * @param[in] cfg = Pointer to by the Application static declared + * configuration variable holding application specific details. + */ +void FOE_config (foe_cfg_t * cfg) +{ + foe_cfg = cfg; +} + +/** Main FoE function checking the status on current mailbox buffers carrying + * data, distributing the mailboxes to appropriate FOE functions depending + * on requested opcode. + * On Error an FoE Error or FoE Abort will be sent. + */ +void ESC_foeprocess (void) +{ + _MBXh *mbh; + _FOE *foembx; + + if (ESCvar.MBXrun == 0) + { + return; + } + if (!ESCvar.xoe && (MBXcontrol[0].state == MBXstate_inclaim)) + { + mbh = (_MBXh *) &MBX[0]; + if (mbh->mbxtype == MBXFOE) + { + ESCvar.xoe = MBXFOE; + } + } + if (ESCvar.xoe == MBXFOE) + { + foembx = (_FOE *) &MBX[0]; + /* Verify the size of the file data. */ + if (etohs (foembx->mbxheader.length) < ESC_FOEHSIZE) + { + FOE_abort (MBXERR_SIZETOOSHORT); + } + else + { + switch (foembx->foeheader.opcode) + { + case FOE_OP_WRQ: + { + DPRINT("FOE_OP_WRQ\n"); + FOE_write (); + break; + } + case FOE_OP_DATA: + { + DPRINT("FOE_OP_DATA\n"); + FOE_data (); + break; + } +#ifdef FOE_READ_SUPPORTED + case FOE_OP_RRQ: + { + DPRINT("FOE_OP_RRQ\n"); + FOE_read (); + break; + } + case FOE_OP_ACK: + { + DPRINT("FOE_OP_ACK\n"); + FOE_ack (); + break; + } + + case FOE_OP_BUSY: + { + DPRINT("FOE_OP_BUSY\n"); + FOE_busy (); + break; + } +#endif + case FOE_OP_ERR: + { + DPRINT("FOE_OP_ERR\n"); + FOE_error (); + break; + } + default: + { + DPRINT("FOE_ERR_NOTDEFINED\n"); + FOE_abort (FOE_ERR_NOTDEFINED); + break; + } + } + } + MBXcontrol[0].state = MBXstate_idle; + ESCvar.xoe = 0; + } +} diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_foe.h b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_foe.h new file mode 100755 index 0000000..71472c9 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/esc_foe.h @@ -0,0 +1,77 @@ +/* + * Licensed under the GNU General Public License version 2 with exceptions. See + * LICENSE file in the project root for full license information + */ + +/** \file + * \brief + * Headerfile for esc_foe.c + */ + +#ifndef __esc_foe__ +#define __esc_foe__ + +#include + +/** Maximum number of characters allowed in a file name. */ +#define FOE_FN_MAX 31 + +typedef struct foe_file_cfg foe_file_cfg_t; +struct foe_file_cfg +{ + /** Name of file to receive from master */ + const char * name; + /** Size of file,sizeof data we can recv */ + uint32_t max_data; + /** Where to store the data initially */ + uint32_t dest_start_address; + /** Current address during write of file */ + uint32_t address_offset; + /** Calculated size of file received */ + uint32_t total_size; + /** FoE password */ + uint32_t filepass; + /** This file can be written only in BOOT state. Intended for FW files */ + uint8_t write_only_in_boot; + /** for feature use */ + uint32_t padding:24; + /** Pointer to application foe write function */ + uint32_t (*write_function) (foe_file_cfg_t * self, uint8_t * data, size_t length); +}; + +typedef struct foe_cfg +{ + /** Allocate static in caller func to fit buffer_size */ + uint8_t * fbuffer; + /** Buffer size before we flush to destination */ + uint32_t buffer_size; + /** Number of files used in firmware update */ + uint32_t n_files; + /** Pointer to files configured to be used by FoE */ + foe_file_cfg_t * files; +} foe_cfg_t; + +typedef struct CC_PACKED +{ + /** Current FoE state, ex. Waiting for ACK, Waiting for DATA */ + uint8_t foestate; + /** Current file buffer position, evaluated against foe file buffer size + * when to flush + */ + uint16_t fbufposition; + /** Frame number in read or write sequence */ + uint32_t foepacket; + /** Current position in file to be handled by FoE request */ + uint32_t fposition; + /** Previous position in file to be handled by FoE request */ + uint32_t fprevposition; + /** End position of allocated disk space for FoE requested file */ + uint32_t fend; +} _FOEvar; + +/* Initializes FoE state. */ +void FOE_config (foe_cfg_t * cfg); +void FOE_init (void); +void ESC_foeprocess (void); + +#endif diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/hal/arduino-lan9252/esc_hw.c b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/hal/arduino-lan9252/esc_hw.c new file mode 100755 index 0000000..a9f0982 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/hal/arduino-lan9252/esc_hw.c @@ -0,0 +1,458 @@ +/* + * Licensed under the GNU General Public License version 2 with exceptions. See + * LICENSE file in the project root for full license information + */ + + /** \file + * \brief + * ESC hardware layer functions for LAN9252. + * + * Function to read and write commands to the ESC. Used to read/write ESC + * registers and memory. + */ +#include "esc.h" +#include "spi.h" +#include + + +#define O_RDWR 1 + +#define BIT(x) 1 << (x) + +#define ESC_CMD_SERIAL_WRITE 0x02 +#define ESC_CMD_SERIAL_READ 0x03 +#define ESC_CMD_FAST_READ 0x0B +#define ESC_CMD_RESET_SQI 0xFF + +#define ESC_CMD_FAST_READ_DUMMY 1 +#define ESC_CMD_ADDR_INC BIT(6) + +#define ESC_PRAM_RD_FIFO_REG 0x000 +#define ESC_PRAM_WR_FIFO_REG 0x020 +#define ESC_PRAM_RD_ADDR_LEN_REG 0x308 +#define ESC_PRAM_RD_CMD_REG 0x30C +#define ESC_PRAM_WR_ADDR_LEN_REG 0x310 +#define ESC_PRAM_WR_CMD_REG 0x314 + +#define ESC_PRAM_CMD_BUSY BIT(31) +#define ESC_PRAM_CMD_ABORT BIT(30) + +#define ESC_PRAM_CMD_CNT(x) ((x >> 8) & 0x1F) +#define ESC_PRAM_CMD_AVAIL BIT(0) + +#define ESC_PRAM_SIZE(x) ((x) << 16) +#define ESC_PRAM_ADDR(x) ((x) << 0) + +#define ESC_CSR_DATA_REG 0x300 +#define ESC_CSR_CMD_REG 0x304 + +#define ESC_CSR_CMD_BUSY BIT(31) +#define ESC_CSR_CMD_READ (BIT(31) | BIT(30)) +#define ESC_CSR_CMD_WRITE BIT(31) +#define ESC_CSR_CMD_SIZE(x) (x << 16) + +#define ESC_RESET_CTRL_REG 0x1F8 +#define ESC_RESET_CTRL_RST BIT(6) +#define ESC_DIGITAL_RST 0x00000001 + +#define ESC_ID_REV_REG 0x050 +#define LAN9252_ID_REV 0x9252 + +#define ESC_IRQ_CFG_REG 0x054 +#define ESC_INT_EN_REG 0x05C +#define ESC_BYTE_TEST_REG 0x064 +#define ESC_TEST_VALUE 0x87654321 + +#define ESC_HW_CFG_REG 0x074 +#define ESC_READY BIT(27) + + +static int lan9252 = -1; + +/* lan9252 singel write */ +static void lan9252_write_32 (uint16_t address, uint32_t val) +{ + uint8_t data[7]; + + data[0] = ESC_CMD_SERIAL_WRITE; + data[1] = ((address >> 8) & 0xFF); + data[2] = (address & 0xFF); + data[3] = (val & 0xFF); + data[4] = ((val >> 8) & 0xFF); + data[5] = ((val >> 16) & 0xFF); + data[6] = ((val >> 24) & 0xFF); + + /* Select device. */ + spi_select (lan9252); + /* Write data */ + write (lan9252, data, sizeof(data)); + /* Un-select device. */ + spi_unselect (lan9252); +} + +/* lan9252 single read */ +static uint32_t lan9252_read_32 (uint32_t address) +{ + uint8_t data[4]; + uint8_t result[4]; + + data[0] = ESC_CMD_FAST_READ; + data[1] = ((address >> 8) & 0xFF); + data[2] = (address & 0xFF); + data[3] = ESC_CMD_FAST_READ_DUMMY; + + /* Select device. */ + spi_select (lan9252); + /* Read data */ + write (lan9252, data, sizeof(data)); + read (lan9252, result, sizeof(result)); + /* Un-select device. */ + spi_unselect (lan9252); + + return ((result[3] << 24) | + (result[2] << 16) | + (result[1] << 8) | + result[0]); +} + +/* ESC read CSR function */ +static void ESC_read_csr (uint16_t address, void *buf, uint16_t len) +{ + uint32_t value; + + value = (ESC_CSR_CMD_READ | ESC_CSR_CMD_SIZE(len) | address); + lan9252_write_32(ESC_CSR_CMD_REG, value); + + do + { + value = lan9252_read_32(ESC_CSR_CMD_REG); + } while(value & ESC_CSR_CMD_BUSY); + + value = lan9252_read_32(ESC_CSR_DATA_REG); + memcpy(buf, (uint8_t *)&value, len); +} + +/* ESC write CSR function */ +static void ESC_write_csr (uint16_t address, void *buf, uint16_t len) +{ + uint32_t value; + + memcpy((uint8_t*)&value, buf,len); + lan9252_write_32(ESC_CSR_DATA_REG, value); + value = (ESC_CSR_CMD_WRITE | ESC_CSR_CMD_SIZE(len) | address); + lan9252_write_32(ESC_CSR_CMD_REG, value); + + do + { + value = lan9252_read_32(ESC_CSR_CMD_REG); + } while(value & ESC_CSR_CMD_BUSY); +} + +/* ESC read process data ram function */ +/*static*/ void ESC_read_pram (uint16_t address, void *buf, uint16_t len) +{ + uint32_t value; + uint8_t * temp_buf = (uint8_t *)buf; + uint16_t byte_offset = 0; + uint8_t fifo_cnt, first_byte_position, temp_len, data[4]; + + value = ESC_PRAM_CMD_ABORT; + lan9252_write_32(ESC_PRAM_RD_CMD_REG, value); + + do + { + value = lan9252_read_32(ESC_PRAM_RD_CMD_REG); + }while(value & ESC_PRAM_CMD_BUSY); + + value = ESC_PRAM_SIZE(len) | ESC_PRAM_ADDR(address); + lan9252_write_32(ESC_PRAM_RD_ADDR_LEN_REG, value); + + value = ESC_PRAM_CMD_BUSY; + lan9252_write_32(ESC_PRAM_RD_CMD_REG, value); + + do + { + value = lan9252_read_32(ESC_PRAM_RD_CMD_REG); + }while((value & ESC_PRAM_CMD_AVAIL) == 0); + + /* Fifo count */ + fifo_cnt = ESC_PRAM_CMD_CNT(value); + + /* Read first value from FIFO */ + value = lan9252_read_32(ESC_PRAM_RD_FIFO_REG); + fifo_cnt--; + + /* Find out first byte position and adjust the copy from that + * according to LAN9252 datasheet and MicroChip SDK code + */ + first_byte_position = (address & 0x03); + temp_len = ((4 - first_byte_position) > len) ? len : (4 - first_byte_position); + + memcpy(temp_buf ,((uint8_t *)&value + first_byte_position), temp_len); + len -= temp_len; + byte_offset += temp_len; + + /* Select device. */ + spi_select (lan9252); + /* Send command and address for fifo read */ + data[0] = ESC_CMD_FAST_READ; + data[1] = ((ESC_PRAM_RD_FIFO_REG >> 8) & 0xFF); + data[2] = (ESC_PRAM_RD_FIFO_REG & 0xFF); + data[3] = ESC_CMD_FAST_READ_DUMMY; + write (lan9252, data, sizeof(data)); + + /* Continue reading until we have read len */ + while(len > 0) + { + temp_len = (len > 4) ? 4: len; + /* Always read 4 byte */ + read (lan9252, (temp_buf + byte_offset), sizeof(uint32_t)); + + fifo_cnt--; + len -= temp_len; + byte_offset += temp_len; + } + /* Un-select device. */ + spi_unselect (lan9252); +} + +/* ESC write process data ram function */ +/* static */ void ESC_write_pram (uint16_t address, void *buf, uint16_t len) +{ + uint32_t value; + uint8_t * temp_buf = (uint8_t *)buf; + uint16_t byte_offset = 0; + uint8_t fifo_cnt, first_byte_position, temp_len, data[3]; + + value = ESC_PRAM_CMD_ABORT; + lan9252_write_32(ESC_PRAM_WR_CMD_REG, value); + + do + { + value = lan9252_read_32(ESC_PRAM_WR_CMD_REG); + }while(value & ESC_PRAM_CMD_BUSY); + + value = ESC_PRAM_SIZE(len) | ESC_PRAM_ADDR(address); + lan9252_write_32(ESC_PRAM_WR_ADDR_LEN_REG, value); + + value = ESC_PRAM_CMD_BUSY; + lan9252_write_32(ESC_PRAM_WR_CMD_REG, value); + + do + { + value = lan9252_read_32(ESC_PRAM_WR_CMD_REG); + }while((value & ESC_PRAM_CMD_AVAIL) == 0); + + /* Fifo count */ + fifo_cnt = ESC_PRAM_CMD_CNT(value); + + /* Find out first byte position and adjust the copy from that + * according to LAN9252 datasheet + */ + first_byte_position = (address & 0x03); + temp_len = ((4 - first_byte_position) > len) ? len : (4 - first_byte_position); + + memcpy(((uint8_t *)&value + first_byte_position), temp_buf, temp_len); + + /* Write first value from FIFO */ + lan9252_write_32(ESC_PRAM_WR_FIFO_REG, value); + + len -= temp_len; + byte_offset += temp_len; + fifo_cnt--; + + /* Select device. */ + spi_select (lan9252); + /* Send command and address for incrementing write */ + data[0] = ESC_CMD_SERIAL_WRITE; + data[1] = ((ESC_PRAM_WR_FIFO_REG >> 8) & 0xFF); + data[2] = (ESC_PRAM_WR_FIFO_REG & 0xFF); + write (lan9252, data, sizeof(data)); + + /* Continue reading until we have read len */ + while(len > 0) + { + temp_len = (len > 4) ? 4 : len; + value = 0; + memcpy((uint8_t *)&value, (temp_buf + byte_offset), temp_len); + /* Always write 4 byte */ + write (lan9252, (uint8_t *)&value, sizeof(value)); + + fifo_cnt--; + len -= temp_len; + byte_offset += temp_len; + } + /* Un-select device. */ + spi_unselect (lan9252); +} + + +/** ESC read function used by the Slave stack. + * + * @param[in] address = address of ESC register to read + * @param[out] buf = pointer to buffer to read in + * @param[in] len = number of bytes to read + */ +void ESC_read (uint16_t address, void *buf, uint16_t len) +{ + /* Select Read function depending on address, process data ram or not */ + if (address >= 0x1000) + { + ESC_read_pram(address, buf, len); + } + else + { + uint16_t size; + uint8_t *temp_buf = (uint8_t *)buf; + + while(len > 0) + { + /* We write maximum 4 bytes at the time */ + size = (len > 4) ? 4 : len; + /* Make size aligned to address according to LAN9252 datasheet + * Table 12-14 EtherCAT CSR Address VS size and MicroChip SDK code + */ + /* If we got an odd address size is 1 , 01b 11b is captured */ + if(address & BIT(0)) + { + size = 1; + } + /* If address 1xb and size != 1 and 3 , allow size 2 else size 1 */ + else if (address & BIT(1)) + { + size = (size & BIT(0)) ? 1 : 2; + } + /* size 3 not valid */ + else if (size == 3) + { + size = 1; + } + /* else size is kept AS IS */ + ESC_read_csr(address, temp_buf, size); + + /* next address */ + len -= size; + temp_buf += size; + address += size; + } + } + /* To mimic the ET1100 always providing AlEvent on every read or write */ + ESC_read_csr(ESCREG_ALEVENT,(void *)&ESCvar.ALevent,sizeof(ESCvar.ALevent)); + ESCvar.ALevent = etohs (ESCvar.ALevent); + +} + +/** ESC write function used by the Slave stack. + * + * @param[in] address = address of ESC register to write + * @param[out] buf = pointer to buffer to write from + * @param[in] len = number of bytes to write + */ +void ESC_write (uint16_t address, void *buf, uint16_t len) +{ + /* Select Write function depending on address, process data ram or not */ + if (address >= 0x1000) + { + ESC_write_pram(address, buf, len); + } + else + { + uint16_t size; + uint8_t *temp_buf = (uint8_t *)buf; + + while(len > 0) + { + /* We write maximum 4 bytes at the time */ + size = (len > 4) ? 4 : len; + /* Make size aligned to address according to LAN9252 datasheet + * Table 12-14 EtherCAT CSR Address VS size and MicroChip SDK code + */ + /* If we got an odd address size is 1 , 01b 11b is captured */ + if(address & BIT(0)) + { + size = 1; + } + /* If address 1xb and size != 1 and 3 , allow size 2 else size 1 */ + else if (address & BIT(1)) + { + size = (size & BIT(0)) ? 1 : 2; + } + /* size 3 not valid */ + else if (size == 3) + { + size = 1; + } + /* else size is kept AS IS */ + ESC_write_csr(address, temp_buf, size); + + /* next address */ + len -= size; + temp_buf += size; + address += size; + } + } + + /* To mimic the ET1x00 always providing AlEvent on every read or write */ + ESC_read_csr(ESCREG_ALEVENT,(void *)&ESCvar.ALevent,sizeof(ESCvar.ALevent)); + ESCvar.ALevent = etohs (ESCvar.ALevent); +} + +/* Un-used due to evb-lan9252-digio not havning any possability to + * reset except over SPI. + */ +void ESC_reset (void) +{ + +} + + +uint8_t ESC_IsLAN9252() +{ + volatile uint32_t value; + uint16_t detectedChip, revision; + + /* Read */ + value = lan9252_read_32(ESC_ID_REV_REG); + detectedChip = value >> 16; + revision = value & 0xFF; + + return detectedChip == LAN9252_ID_REV + && revision >= 1; +} + + +void ESC_init (const esc_cfg_t * config) +{ + uint32_t value; + + spi_setup(); + + /* Reset the ecat core here due to evb-lan9252-digio not having any GPIO + * for that purpose. + */ + lan9252_write_32(ESC_RESET_CTRL_REG, ESC_DIGITAL_RST); + do + { + value = lan9252_read_32(ESC_RESET_CTRL_REG); + } + while(value & ESC_RESET_CTRL_RST); + + /* Read test register */ + do + { + value = lan9252_read_32(ESC_BYTE_TEST_REG); + } + while (value != ESC_TEST_VALUE); + + /* Check Ready flag */ + do + { + value = lan9252_read_32(ESC_HW_CFG_REG); + } + while ((value & ESC_READY) == 0); + + if(!ESC_IsLAN9252()) + { + while (1); + } +} diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/hal/arduino-lan9252/spi.cpp b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/hal/arduino-lan9252/spi.cpp new file mode 100755 index 0000000..8752019 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/hal/arduino-lan9252/spi.cpp @@ -0,0 +1,66 @@ +#include "spi.hpp" +// #include +#include + + +char SCS = ESC_GPIO_Pin_CS; + + +void spi_setup(void) +{ + SPI.begin(); + pinMode(SCS, OUTPUT); + spi_unselect(0); + delay(100); + SPI.beginTransaction(SPISettings(SPIX_ESC_SPEED, MSBFIRST, SPI_MODE0)); + +} + +void spi_select (int8_t board) +{ + // Soft CSN + #if SCS_ACTIVE_POLARITY == SCS_LOW + digitalWrite(SCS, LOW); + #endif +} + +void spi_unselect (int8_t board) +{ + // Soft CSN + #if SCS_ACTIVE_POLARITY == SCS_LOW + digitalWrite(SCS, HIGH); + #endif +} + +inline static uint8_t spi_transfer_byte(uint8_t byte) +{ + return SPI.transfer(byte); + // AVR will need handling last byte transfer difference, + // but then again they pobably wont even fit EtherCAT stack in RAM + // so no need to care for now +} + +void write (int8_t board, uint8_t *data, uint8_t size) +{ + for(int i = 0; i < size; ++i) + { + spi_transfer_byte(data[i]); + } +} + +void read (int8_t board, uint8_t *result, uint8_t size) +{ + for(int i = 0; i < size; ++i) + { + result[i] = spi_transfer_byte(DUMMY_BYTE); + } +} + + +void spi_bidirectionally_transfer (int8_t board, uint8_t *result, uint8_t *data, uint8_t size) +{ + for(int i = 0; i < size; ++i) + { + result[i] = spi_transfer_byte(data[i]); + } +} diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/hal/arduino-lan9252/spi.h b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/hal/arduino-lan9252/spi.h new file mode 100755 index 0000000..7413d47 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/hal/arduino-lan9252/spi.h @@ -0,0 +1,15 @@ +#ifndef SRC_APP_SPI_H_ +#define SRC_APP_SPI_H_ + +#include + + +void spi_setup(void); +void spi_select (int8_t board); +void spi_unselect (int8_t board); +void write (int8_t board, uint8_t *data, uint8_t size); +void read (int8_t board, uint8_t *result, uint8_t size); +void spi_bidirectionally_transfer (int8_t board, uint8_t *result, uint8_t *data, uint8_t size); + + +#endif /* SRC_APP_SPI_H_ */ diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/hal/arduino-lan9252/spi.hpp b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/hal/arduino-lan9252/spi.hpp new file mode 100755 index 0000000..297656c --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/lib/soes/hal/arduino-lan9252/spi.hpp @@ -0,0 +1,25 @@ +#ifndef SRC_APP_SPI_H_ +#define SRC_APP_SPI_H_ + +#include + +#define SCS_LOW 0 +#define SCS_HIGH 1 +#define SCS_ACTIVE_POLARITY SCS_LOW + +#define SPIX_ESC SPI1 +//#define SPIX_ESC_SPEED 18000000 +#define SPIX_ESC_SPEED 50000000 +#define ESC_GPIO_Pin_CS PC4 + +#define DUMMY_BYTE 0xFF + +extern "C" void spi_setup(void); +extern "C" void spi_select (int8_t board); +extern "C" void spi_unselect (int8_t board); +extern "C" void write (int8_t board, uint8_t *data, uint8_t size); +extern "C" void read (int8_t board, uint8_t *result, uint8_t size); +extern "C" void spi_bidirectionally_transfer (int8_t board, uint8_t *result, uint8_t *data, uint8_t size); + + +#endif /* SRC_APP_SPI_H_ */ diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/platformio.ini b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/platformio.ini new file mode 100755 index 0000000..06c389b --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/platformio.ini @@ -0,0 +1,23 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:genericSTM32F407VGT6] +framework = arduino +platform = ststm32 +board = genericSTM32F407VGT6 +upload_protocol = stlink +debug_tool = stlink +debug_build_flags = -O0 -g -ggdb +monitor_port = COM3 +monitor_filters = send_on_enter, time, colorize, log2file +monitor_speed = 115200 +build_flags = -Wl,--no-warn-rwx-segment +lib_deps = + SPI diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/src/extend32to64.cpp b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/src/extend32to64.cpp new file mode 100755 index 0000000..0486008 --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/src/extend32to64.cpp @@ -0,0 +1,18 @@ +#include "extend32to64.h" + +// Extend from 32-bit to 64-bit precision +int64_t extend32to64::extendTime(uint32_t in) +{ + int64_t c64 = (int64_t)in - HALF_PERIOD; // remove half period to determine (+/-) sign of the wrap + int64_t dif = (c64 - previousTimeValue); // core concept: prev + (current - prev) = current + + // wrap difference from -HALF_PERIOD to HALF_PERIOD. modulo prevents differences after the wrap from having an incorrect result + int64_t mod_dif = ((dif + HALF_PERIOD) % ONE_PERIOD) - HALF_PERIOD; + if (dif < int64_t(-HALF_PERIOD)) + mod_dif += ONE_PERIOD; // account for mod of negative number behavior in C + + int64_t unwrapped = previousTimeValue + mod_dif; + previousTimeValue = unwrapped; // load previous value + + return unwrapped + HALF_PERIOD; // remove the shift we applied at the beginning, and return +} \ No newline at end of file diff --git a/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/src/main.cpp b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/src/main.cpp new file mode 100755 index 0000000..10236fb --- /dev/null +++ b/Cards/EaserCAT-6000-THCAD-reader+Digital-IO/Firmware/src/main.cpp @@ -0,0 +1,237 @@ +#include +#include +extern "C" +{ +#include "ecat_slv.h" +#include "utypes.h" +}; +_Objects Obj; + +#include "extend32to64.h" +extend32to64 longTime; +volatile uint64_t irqTime = 0; + +HardwareSerial Serial1(PA10, PA9); + +uint8_t inputPin[] = {PD15, PD14, PD13, PD12, PD11, PD10, PD9, PD8, PB15, PB14, PB13, PB12}; +uint8_t outputPin[] = {PE10, PE9, PE8, PE7}; + +#include "HardwareTimer.h" +// NOTE This mod in the beginning of HardwareTimer.cpp for 32-bit precision +////// //#define MAX_RELOAD ((1 << 16) - 1) // Currently even 32b timers are used as 16b to have generic behavior +////// #define MAX_RELOAD 0xFFFFFFFF + +#define THCAD_PIN PA0 +// PA0 is connected to Timer 2, a 32-bit timer + +uint32_t channel; +volatile uint32_t FrequencyMeasured, LastCapture = 0, CurrentCapture; +uint32_t input_freq = 0; +volatile uint32_t rolloverCompareCount = 0; +HardwareTimer *EncoderTimer; +void InputCapture_IT_callback(void); +void Rollover_IT_callback(void); + +volatile uint16_t ALEventIRQ; // ALEvent that caused the interrupt + +void cb_set_outputs(void) // Get Master outputs, slave inputs, first operation +{ + // Update digital pins + for (int i = 0; i < sizeof(outputPin); i++) + { + digitalWrite(outputPin[i], Obj.Output[i]); + } +} + +void cb_get_inputs(void) // Set Master inputs, slave outputs, last operation +{ + for (int i = 0; i < sizeof(inputPin); i++) + Obj.Input[i] = digitalRead(inputPin[i]); + + Obj.Velocity = Obj.VelocityScale * FrequencyMeasured; +} + +void ESC_interrupt_enable(uint32_t mask); +void ESC_interrupt_disable(uint32_t mask); +uint16_t dc_checker(void); +void sync0Handler(void); + +static esc_cfg_t config = + { + .user_arg = NULL, + .use_interrupt = 1, + .watchdog_cnt = 150, + .set_defaults_hook = NULL, + .pre_state_change_hook = NULL, + .post_state_change_hook = NULL, + .application_hook = NULL, + .safeoutput_override = NULL, + .pre_object_download_hook = NULL, + .post_object_download_hook = NULL, + .rxpdo_override = NULL, + .txpdo_override = NULL, + .esc_hw_interrupt_enable = ESC_interrupt_enable, + .esc_hw_interrupt_disable = ESC_interrupt_disable, + .esc_hw_eep_handler = NULL, + .esc_check_dc_handler = dc_checker, +}; + +volatile byte serveIRQ = 0; + +volatile uint32_t globalIRQ = 0; +void globalInt(void) +{ + globalIRQ++; +} + +void setup(void) +{ + Serial1.begin(115200); + + for (int i = 0; i < sizeof(inputPin); i++) + pinMode(inputPin[i], INPUT_PULLDOWN); + for (int i = 0; i < sizeof(outputPin); i++) + { + pinMode(outputPin[i], OUTPUT); + digitalWrite(outputPin[i], LOW); + } + + // Automatically retrieve TIM instance and channel associated to pin + // This is used to be compatible with all STM32 series automatically. + TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(THCAD_PIN), PinMap_PWM); + channel = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(THCAD_PIN), PinMap_PWM)); + + EncoderTimer = new HardwareTimer(Instance); + + // Configure rising edge detection to measure frequency + EncoderTimer->setMode(channel, TIMER_INPUT_CAPTURE_RISING, THCAD_PIN); + + // With a PrescalerFactor = 1, the minimum frequency value to measure is : TIM counter clock / CCR MAX + // = (SystemCoreClock) / 65535 + // Example on Nucleo_L476RG with systemClock at 80MHz, the minimum frequency is around 1,2 khz + // To reduce minimum frequency, it is possible to increase prescaler. But this is at a cost of precision. + // The maximum frequency depends on processing of the interruption and thus depend on board used + // Example on Nucleo_L476RG with systemClock at 80MHz the interruption processing is around 4,5 microseconds and thus Max frequency is around 220kHz + uint32_t PrescalerFactor = 1; + EncoderTimer->setPrescaleFactor(PrescalerFactor); + EncoderTimer->setOverflow(0xFFFFFFF0); // Max Period value to have the largest possible time to detect rising edge and avoid timer rollover + EncoderTimer->attachInterrupt(channel, InputCapture_IT_callback); + EncoderTimer->attachInterrupt(Rollover_IT_callback); + EncoderTimer->resume(); + + // Compute this scale factor only once + input_freq = EncoderTimer->getTimerClkFreq() / EncoderTimer->getPrescaleFactor(); + + ecat_slv_init(&config); + attachInterrupt(digitalPinToInterrupt(PC0), globalInt, RISING); +} + +void loop(void) +{ +#if 0 // Sync 0 mode + uint64_t dTime; + if (serveIRQ) + { + DIG_process(ALEventIRQ, DIG_PROCESS_WD_FLAG | DIG_PROCESS_OUTPUTS_FLAG | + DIG_PROCESS_APP_HOOK_FLAG | DIG_PROCESS_INPUTS_FLAG); + serveIRQ = 0; + ESCvar.PrevTime = ESCvar.Time; + ecat_slv_poll(); + } + dTime = longTime.extendTime(micros()) - irqTime; + if (dTime > 5000) // Don't run ecat_slv_poll when expecting to serve interrupt + ecat_slv_poll(); +#else // Freerun mode + ecat_slv(); + +#endif +} + +void sync0Handler(void) +{ + ALEventIRQ = ESC_ALeventread(); + // if (ALEventIRQ & ESCREG_ALEVENT_SM2) + { + irqTime = longTime.extendTime(micros()); + serveIRQ = 1; + } +} + +// Enable SM2 interrupts +void ESC_interrupt_enable(uint32_t mask) +{ + // Enable interrupt for SYNC0 or SM2 or SM3 + uint32_t user_int_mask = ESCREG_ALEVENT_DC_SYNC0 | ESCREG_ALEVENT_SM2 | ESCREG_ALEVENT_SM3; + if (mask & user_int_mask) + { + ESC_ALeventmaskwrite(ESC_ALeventmaskread() | (mask & user_int_mask)); + ESC_ALeventmaskwrite(ESC_ALeventmaskread() & ~(ESCREG_ALEVENT_DC_SYNC0 | ESCREG_ALEVENT_SM3)); + attachInterrupt(digitalPinToInterrupt(PC3), sync0Handler, RISING); + + // Set LAN9252 interrupt pin driver as push-pull active high + uint32_t bits = 0x00000111; + ESC_write(0x54, &bits, 4); + + // Enable LAN9252 interrupt + bits = 0x00000001; + ESC_write(0x5c, &bits, 4); + } +} + +// Disable SM2 interrupts +void ESC_interrupt_disable(uint32_t mask) +{ + // Enable interrupt for SYNC0 or SM2 or SM3 + // uint32_t user_int_mask = ESCREG_ALEVENT_DC_SYNC0 | ESCREG_ALEVENT_SM2 | ESCREG_ALEVENT_SM3; + uint32_t user_int_mask = ESCREG_ALEVENT_SM2; + + if (mask & user_int_mask) + { + // Disable interrupt from SYNC0 + ESC_ALeventmaskwrite(ESC_ALeventmaskread() & ~(mask & user_int_mask)); + detachInterrupt(digitalPinToInterrupt(PC3)); + // Disable LAN9252 interrupt + uint32_t bits = 0x00000000; + ESC_write(0x5c, &bits, 4); + } +} + +extern "C" uint32_t ESC_SYNC0cycletime(void); + +// Setup of DC +uint16_t dc_checker(void) +{ + // Indicate we run DC + ESCvar.dcsync = 1; + return 0; +} + +void InputCapture_IT_callback(void) +{ + CurrentCapture = EncoderTimer->getCaptureCompare(channel); + + /* frequency computation */ + if (CurrentCapture > LastCapture) + { + FrequencyMeasured = input_freq / (CurrentCapture - LastCapture); + } + else if (CurrentCapture <= LastCapture) + { + /* 0xFFFFFFFF is max overflow value */ + FrequencyMeasured = input_freq / (0xFFFFFFFF + CurrentCapture - LastCapture); + } + LastCapture = CurrentCapture; + rolloverCompareCount = 0; +} + +/* In case of timer rollover, frequency is to low to be measured set value to 0 + To reduce minimum frequency, it is possible to increase prescaler. But this is at a cost of precision. */ +void Rollover_IT_callback(void) +{ + rolloverCompareCount++; + + if (rolloverCompareCount > 1) + { + FrequencyMeasured = 0; + } +}