/* * eth_ecat_commands.hpp * * Created on: May 2, 2023 * Author: algin */ #ifndef FREE_RTOS_ETHERNET_INDUSTRY_ETH_ECAT_COMMAND_HPP_ #define FREE_RTOS_ETHERNET_INDUSTRY_ETH_ECAT_COMMAND_HPP_ #include #include #include #include #include "ethernet_industry/ethercattype.hpp" #include "ethernet_industry/eth_ecat_types.h" namespace free_rtos { namespace address { // Slave addressing types using Position = int16_t; using Broadcast = uint16_t; using Node = uint16_t; using Logical = uint32_t; /* struct Position { Position(int16_t val): val_{val} { } Position() { } int16_t val_; }; struct Broadcast { Broadcast(uint16_t val): val_{val} { } Broadcast() { } uint16_t val_; }; struct Node { Node(uint16_t val): val_{val} { } Node() { } uint16_t val_; }; struct Logical { Logical(uint32_t val): val_{val} { } Logical() { } uint32_t val_; }; */ using SlaveTypes = std::tuple; // Register offset using Offset = uint16_t; using PositionAddress = std::tuple; using BroadcastAddress = std::tuple; using NodeAddress = std::tuple; using LogicalAddress = std::tuple; using Types = std::tuple; } // namespace address namespace command { enum class DIR_INDEX : uint16_t { RD = 0, WR, RW, RMW, NO = RMW }; enum class TYPE_INDEX : uint16_t { AP = 0, B, FP, L }; struct DirBase { }; template struct Dir : public DirBase { static constexpr DIR_INDEX dir = dir_index; }; using RD = Dir; using WR = Dir; using RW = Dir; using RMW = Dir; using NO = Dir; struct TypeBase { }; template struct Type : public TypeBase { using TAddress = typename std::tuple_element(type_index), address::Types>::type; using TSlaveAddress = typename std::tuple_element<0, TAddress>::type; static constexpr TYPE_INDEX type = type_index; static constexpr ec_cmdtype get_cmd(DIR_INDEX dir) { std::array, 4> commands = {{ {{EC_CMD_APRD, EC_CMD_APWR, EC_CMD_APRW, EC_CMD_ARMW}}, {{EC_CMD_BRD, EC_CMD_BWR, EC_CMD_BRW, EC_CMD_NOP}}, {{EC_CMD_FPRD, EC_CMD_FPWR, EC_CMD_FPRW, EC_CMD_FRMW}}, {{EC_CMD_LRD, EC_CMD_LWR, EC_CMD_LRW, EC_CMD_NOP}} }}; return commands[static_cast(type)][static_cast(dir)]; } }; using AP = Type; using B = Type; using FP = Type; using L = Type; class EcatCommandBase { public: EcatCommandBase(ec_cmdtype cmd) : cmd_{cmd} { } EcatCommandBase() : cmd_{ec_cmdtype::EC_CMD_NOP} { } uint8_t get_cmd() { return cmd_; } private: ec_cmdtype cmd_; }; template class EcatCommand : public EcatCommandBase { static_assert(std::is_base_of::value == true, "TypeT should be derived from CMD::TypeBase"); static_assert(std::is_base_of::value == true, "DirT should be derived from CMD::DirBase"); public: EcatCommand(typename TypeT::TAddress fields) : EcatCommandBase{TypeT::get_cmd(DirT::dir)} , address_{fields} { } EcatCommand() { } uint32_t get_address() { return address_.dword_; } private: template union Address { Address(FieldsT fields) : fields_{fields} { } Address() { } Address& operator=(const Address& other) { fields_ = other.fields_; return *this; } FieldsT fields_; uint32_t dword_; }; Address address_; }; /* * Position addressing * arg0: Each slave increments Position. Slave is addressed if Position = 0. * arg1: Local register or memory address of the ESC */ using APRD = EcatCommand; using APWR = EcatCommand; using APRW = EcatCommand; using ARMW = EcatCommand; /* * Broadcast addressing * arg0: Each slave increments Position (not used for addressing) * arg1: Local register or memory address of the ESC */ using NOP = EcatCommand; using BRD = EcatCommand; using BRW = EcatCommand; using BWR = EcatCommand; // Node addressing shortcuts /* * arg0: Slave is addressed if Address matches Configured Station Address or Configured Station Alias (if enabled). * arg1: Local register or memory address of the ESC */ using FPRD = EcatCommand; using FPRW = EcatCommand; using FPWR = EcatCommand; using FPMW = EcatCommand; /* * Logical addressing * arg0: Logical Address (configured by FMMUs) Slave is addressed if FMMU configuration matches Address. */ using LRD = EcatCommand; using LRW = EcatCommand; using LWR = EcatCommand; } // namespace command } #endif /* FREE_RTOS_ETHERNET_INDUSTRY_ETH_ECAT_COMMAND_HPP_ */