diff --git a/04_software/CMakeLists.txt b/04_software/CMakeLists.txt new file mode 100644 index 0000000..a7a743e --- /dev/null +++ b/04_software/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 3.10) +project(kyncat C) + +# Set C standard +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) + +# Set output directory for the compiled binary +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}") +set(CMAKE_BINARY_DIRECTORY "${CMAKE_SOURCE_DIR}") + +# SOEM Repository Info +set(SOEM_REPO "https://github.com/OpenEtherCATsociety/SOEM.git") +set(SOEM_PATH "${CMAKE_SOURCE_DIR}/SOEM") + +# Check if SOEM exists, if not, clone it +if(NOT EXISTS ${SOEM_PATH}/CMakeLists.txt) + message(STATUS "SOEM not found! Cloning from ${SOEM_REPO}...") + execute_process( + COMMAND git clone --depth 1 ${SOEM_REPO} ${SOEM_PATH} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + RESULT_VARIABLE GIT_CLONE_RESULT + ) + if(NOT GIT_CLONE_RESULT EQUAL 0) + message(FATAL_ERROR "Failed to clone SOEM repository!") + endif() +endif() + +# Include SOEM as a subdirectory (it has its own CMakeLists.txt) +add_subdirectory(${SOEM_PATH}) + +# Include directories for SOEM +include_directories( + ${SOEM_PATH}/soem + ${SOEM_PATH}/osal + ${SOEM_PATH}/osal/linux + ${SOEM_PATH}/oshw/linux +) + +# Define the source files for kyncat +set(SOURCES + main.c + slaveconfig.c +) + +set(HEADERS + slaveconfig.h +) + +# Create the executable and link against the SOEM static library +add_executable(kyncat ${SOURCES} ${HEADERS}) + +# Link SOEM library and required system libraries +target_link_libraries(kyncat soem pthread rt) + diff --git a/04_software/main.c b/04_software/main.c new file mode 100644 index 0000000..6d946d1 --- /dev/null +++ b/04_software/main.c @@ -0,0 +1,295 @@ +#include +#include +#include +#include "slaveconfig.h" +#include "ethercat.h" + +#define EC_TIMEOUTMON 100 + +char IOmap[4096]; +volatile int wkc; +OSAL_THREAD_HANDLE thread1; +int expectedWKC; +boolean needlf; +volatile int wkc; +uint8 currentgroup = 0; + + +/*! + * @brief Set the given Slave to given State + * @return 1 if No Error | 0 if Error + * */ +uint8_t setSlaveState(uint8_t slave_index, uint16_t ethercat_state ) +{ + //printf("SET STATE: %s - Slave[%d] \n",ec_slave[slave_index].name, slave_index); + ec_slave[slave_index].state = ethercat_state;// ec_slave[0] refers to a special virtual slave that represents all slaves + ec_writestate(slave_index); // Same as above + ec_statecheck(slave_index, ethercat_state, EC_TIMEOUTSTATE); + + if (ec_slave[slave_index].state == ethercat_state) { + { + // printf(" |- "); + // printState(ec_slave[slave_index].state); + } + return 1; + } else { + // printf(" |-> Failed\n"); + return 0; + } + return 0; +} + +/* +void searchForSlave() +{ + for (int i = 1; i <= ec_slavecount; i++) { + if (strcmp(ec_slave[i].name, "EL2008") == 0) { + el2008_slave = i; + printf("Found EL2008 at slave %d.\n", i); + } + } + + if (el2008_slave == -1) { + printf("EL2008 not found on the network.\n"); + } +} +*/ + + +void print_device_info() { + printf("--------------------------------------------------\n"); + printf("EtherCAT Device Info Panel\n"); + printf("--------------------------------------------------\n"); + + for (int i = 1; i <= ec_slavecount; i++) { + printf("Slave %d: %s\n", i, ec_slave[i].name); + printf(" Vendor ID: 0x%08X\n", ec_slave[i].eep_man); + printf(" Product Code: 0x%08X\n", ec_slave[i].eep_id); + printf(" Revision: 0x%08X\n", ec_slave[i].eep_rev); + printf(" "); + printState(ec_slave[i].state); + printf(" AL Status Code: 0x%04X\n", ec_slave[i].ALstatuscode); + + // Print Process Data (PDOs) + printf(" Process Data:\n"); + printf(" Inputs (%d bytes): ", ec_slave[i].Ibytes); + for (uint32_t j = 0; j < ec_slave[i].Ibytes; j++) { + printf("%02X ", ec_slave[i].inputs[j]); + } + printf("\n"); + + printf(" Outputs (%d bytes): ", ec_slave[i].Obytes); + for (uint32_t j = 0; j < ec_slave[i].Obytes; j++) { + printf("%02X ", ec_slave[i].outputs[j]); + } + printf("\n"); + + // Read available SDOs + printf(" SDO List:\n"); + for (int sdo_index = 0x1000; sdo_index <= 0x1FFF; sdo_index++) { + uint32_t sdo_value; + int size = sizeof(sdo_value); + if (ec_SDOread(i, sdo_index, 0x00, FALSE, &size, &sdo_value, EC_TIMEOUTSAFE) > 0) { + printf(" Index 0x%04X: Value 0x%08X\n", sdo_index, sdo_value); + } + } + printf("--------------------------------------------------\n"); + } +} + + +void setPin1Out() + { + uint8_t *outputs = (uint8_t *)ec_slave[3].outputs; + + // Print current output state before modification + printf("Current output state: 0x%02X\n", outputs[0]); + + // Set bit 0 (Channel 1 ON) + outputs[0] |= 1; + + // Send process data to the slave + ec_send_processdata(); + + wkc = ec_receive_processdata(EC_TIMEOUTRET); + + if (wkc >= expectedWKC) { + printf("Process data exchanged successfully.\n"); + } else { + printf("Process data exchange failed. WKC: %d, expectedWKC: %d\n", wkc, expectedWKC); + } + + // Print final output state + printf("Final output state: 0x%02X\n", outputs[0]); + + // Print slave state and AL status code + printState(ec_slave[3].state); +} + + +/*! + * @brief Initialising Distributed Clocks. you should initialize Distributed Clocks (DC) + * after all slaves are configured and mapped, + * but before transitioning them to OPERATIONAL (EC_STATE_OPERATIONAL). + * @return 1 if No Error | 0 if Error + * */ +uint8_t initDisributedClocks() +{ + printf("Init Distributed Clocks\n"); + if (ec_configdc()) { + printf(" |-> Succeeded \n"); + return 1; + }else + { + printf(" |-> Failed \n"); + return 0; + } +} + + +/*! + * @brief map process data objects (PDOs) of all detected EtherCAT slaves into a single memory buffer (IOmap). + * and groupe them into group[0] This will be important for the Slaves state intitalisation phase. + * @return 1 if No Error | 0 if Error + * */ +uint8_t mapSlavesGrouped() +{ + printf("Mapping Slaves\n"); + if(ec_config_map(&IOmap)){ + printf(" |-> Succeeded \n"); + for (uint8_t i = 1; i <= ec_slavecount; i++) { + printf(" |-> (%s) - ",ec_slave[i].name); + printState(ec_slave[i].state); + } + return 1; + }else + { + printf(" |-> Failed \n"); + return 0; + } +} + +/*! + * @brief Enumerate and init all slaves.> + * @return 1 if No Error | 0 if Error + * */ +uint8_t enumerateSlaves() +{ + printf("Detection and enumeration \n"); + if (ec_config_init(FALSE) > 0) { + printf(" |-> Succeeded \n"); + printf(" |-> Slave Count: %d \n",ec_slavecount); + return 1; + }else + { + printf(" |-> Failed \n"); + return 0; + } +} + +/*! + * @brief Initialising network atapter for communication with slaves + * @param char *Ifname is the interface name + * @return 1 if No Error | 0 if Error + * */ +uint8_t initNetworkAdapterSingle(char *ifname) +{ + printf("Init Network Interface < %s >\n", ifname); + if (ec_init(ifname)) { + printf(" |-> Succeeded \n"); + return 1; + }else + { + printf(" |-> Failed \n"); + return 0; + } +} + +void dataLoop() +{ + ec_send_processdata(); + wkc = ec_receive_processdata(EC_TIMEOUTRET); + if(wkc >= expectedWKC) + { + + } + osal_usleep(5000); +} + +int main(int argc, char *argv[]) { + if (argc > 1) { + // Init Network adapter + if(!initNetworkAdapterSingle(argv[1])){ + printf("Unable to Initiate Network Adapter -> Quitting"); + ec_close(); + return 0; + } + + if(!enumerateSlaves()){ + printf("Unable to Enumerate Slaves -> Quitting"); + ec_close(); + return 0; + } + + if(!mapSlavesGrouped()){ + printf("Unable to Map Slaves -> Quitting"); + ec_close(); + return 0; + } + + expectedWKC = (ec_group[0].outputsWKC * 2) + ec_group[0].inputsWKC; + printf("Calculated workcounter %d\n", expectedWKC); + + if(!initDisributedClocks()){ + printf("Unable to Initialise Distributed Clocks -> Quitting"); + ec_close(); + return 0; + } + + for (uint8_t i = 1; i <= ec_slavecount; i++) { + printf("-----------------------------------------------------------------------\n"); + print_device_info(); + printf("-----------------------------------------------------------------------\n"); + if(!setSlaveState(1,EC_STATE_INIT)){ + printf("Unable chnage Slave mode -> Quitting"); + ec_close(); + return 0; + } + if(!setSlaveState(i,EC_STATE_PRE_OP)){ + printf("Unable chnage Slave mode -> Quitting"); + ec_close(); + return 0; + } + if(!setSlaveState(i,EC_STATE_SAFE_OP)){ + printf("Unable chnage Slave mode -> Quitting"); + ec_close(); + return 0; + } + if(!setSlaveState(i,EC_STATE_OPERATIONAL)){ + printf("Unable chnage Slave mode -> Quitting"); + ec_close(); + } + printf("-----------------------------------------------------------------------\n"); + } + + setPin1Out(); + + for(uint16_t j = 0; j < 10000; j++) + { + ec_send_processdata(); + wkc = ec_receive_processdata(EC_TIMEOUTRET); + + if (wkc >= expectedWKC) { + } else { + printf("Process data exchange failed. WKC: %d, expectedWKC: %d\n", wkc, expectedWKC); + } + usleep(100); + } + + ec_close(); + } else { + printf("Usage : sudo %s need to be passed\n", argv[0]); + printf("Example : sudo %s eth0 \n", argv[0]); + } + return 0; +} diff --git a/04_software/slaveconfig.c b/04_software/slaveconfig.c new file mode 100644 index 0000000..b2b6552 --- /dev/null +++ b/04_software/slaveconfig.c @@ -0,0 +1,40 @@ +#include "slaveconfig.h" +#include + + +/*! + * @brief Prints the state in which the Slave is and also give basic infomations about that state. + * @param uint16_t state is the state flag of the slave which will be masked to ist 8 LSB's + * @link https://infosys.beckhoff.com/english.php?content=../content/1033/ethercatsystem/1036980875.html&id= + * @return void + * */ +void printState(uint16_t state) +{ + uint8_t state_masked = state & 0x0F; // Mask first byte + + switch (state_masked) { + case EC_STATE_INIT: + printf("State: INIT (0x%02X) - No communication yet.\n", state_masked); + break; + + case EC_STATE_PRE_OP: + printf("State: PRE-OP (0x%02X) - Configuration possible.\n", state_masked); + break; + + case EC_STATE_SAFE_OP: + printf("State: SAFE-OP (0x%02X) - Inputs work, outputs disabled.\n", state_masked); + break; + + case EC_STATE_OPERATIONAL: + printf("State: OPERATIONAL (0x%02X) - Fully active.\n", state_masked); + break; + + case EC_STATE_BOOT: + printf("State: BOOTSTRAP (0x%02X) - Firmware update mode.\n", state_masked); + break; + + default: + printf("State: UNKNOWN STATE (0x%02X)\n", state_masked); + break; + } +} diff --git a/04_software/slaveconfig.h b/04_software/slaveconfig.h new file mode 100644 index 0000000..a705e71 --- /dev/null +++ b/04_software/slaveconfig.h @@ -0,0 +1,30 @@ +/* + * 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 ethercatbase.c + */ + +#ifndef _slave_config_ +#define _slave_config_ + +#include "ethercat.h" + +/*! + * @brief Prints the state in which the Slave is and also give basic infomations about that state. + * @param uint16_t state is the state flag of the slave which will be masked to ist 8 LSB's + * @link https://infosys.beckhoff.com/english.php?content=../content/1033/ethercatsystem/1036980875.html&id= + * @return void + * */ +void printState(uint16_t state); + +/*! + * @brief Set the given Slave to given State + * @return 1 if No Error | 0 if Error + * */ +//uint8_t setSlaveState(uint8_t slave_index, uint16_t ethercat_state); + +#endif