├── LICENSE ├── README.md ├── examples └── slaveinfo │ └── slaveinfo.ino ├── library.properties └── src ├── SOEM.cpp ├── SOEM.h ├── osal ├── osal.c ├── osal.h └── osal_defs.h ├── oshw ├── nicdrv.c ├── nicdrv.h ├── oshw.c └── oshw.h ├── soem ├── ethercat.h ├── ethercatbase.c ├── ethercatbase.h ├── ethercatcoe.c ├── ethercatcoe.h ├── ethercatconfig.c ├── ethercatconfig.h ├── ethercatconfiglist.h ├── ethercatdc.c ├── ethercatdc.h ├── ethercateoe.c ├── ethercateoe.h ├── ethercatfoe.c ├── ethercatfoe.h ├── ethercatmain.c ├── ethercatmain.h ├── ethercatprint.c ├── ethercatprint.h ├── ethercatsoe.c ├── ethercatsoe.h └── ethercattype.h └── w5500 ├── w5500.cpp └── w5500.h /LICENSE: -------------------------------------------------------------------------------- 1 | Simple Open EtherCAT Master Library 2 | 3 | Copyright (C) 2005-2017 Speciaal Machinefabriek Ketels v.o.f. 4 | Copyright (C) 2005-2017 Arthur Ketels 5 | Copyright (C) 2008-2009 TU/e Technische Universiteit Eindhoven 6 | Copyright (C) 2009-2017 rt-labs AB, Sweden 7 | 8 | SOEM is free software; you can redistribute it and/or modify it under the terms 9 | of the GNU General Public License version 2 as published by the Free Software 10 | Foundation. 11 | 12 | SOEM is distributed in the hope that it will be useful, but WITHOUT ANY 13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 14 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | 16 | As a special exception, if other files instantiate templates or use macros or 17 | inline functions from this file, or you compile this file and link it with other 18 | works to produce a work based on this file, this file does not by itself cause 19 | the resulting work to be covered by the GNU General Public License. However the 20 | source code for this file must still be made available in accordance with 21 | section (3) of the GNU General Public License. 22 | 23 | This exception does not invalidate any other reasons why a work based on this 24 | file might be covered by the GNU General Public License. 25 | 26 | The EtherCAT Technology, the trade name and logo "EtherCAT" are the intellectual 27 | property of, and protected by Beckhoff Automation GmbH. You can use SOEM for the 28 | sole purpose of creating, using and/or selling or otherwise distributing an 29 | EtherCAT network master provided that an EtherCAT Master License is obtained 30 | from Beckhoff Automation GmbH. 31 | 32 | In case you did not receive a copy of the EtherCAT Master License along with 33 | SOEM write to Beckhoff Automation GmbH, Eiserstrasse 5, D-33415 Verl, Germany 34 | (www.beckhoff.com). 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple Open EtherCAT Master Library for Arduino 2 | 3 | This is SOEM (Simple Open EtherCAT Master) library ported for Arduino. 4 | 5 | It's just for educational or experimental purposes. 6 | 7 | The original SOEM is at https://github.com/OpenEtherCATsociety/SOEM 8 | 9 | ## Supported hardware 10 | 11 | * Arduino Due + Ethernet Shield 2 (W5500) 12 | * ESP32 + WIZ850io (W5500) 13 | * M5Stack + M5Stack LAN Module (W5500) 14 | * M5 ATOM Matrix + WIZ850io (W5500) 15 | * GR-SAKURA (Renesas RX63N) 16 | * GR-ROSE (Renesas RX65N) 17 | * chipKIT Max32 (PIC32MX) + Ethernet Shield 2 (W5500) 18 | 19 | ## limitations 20 | Many of MCUs don't have large memory, and has only one LAN port. SOEM4Arduino reduces memory usage, and does not support redundant LAN ports. 21 | 22 | Doe to reducing memory usage, some functions are limited. 23 | | item | constant name | original SOEM | SOEM4Arduino | 24 | | ---- | ---- | ---- | ---- | 25 | | max entries in Object Dictionary list | EC_MAXODLIST | 1024 | 64 | 26 | | max entries in Object Entry list | EC_MAXOELIST | 256 | 64 | 27 | | max number of slaves in array | EC_MAXSLAVE | 200 | 64 | 28 | | number of frame buffers | EC_MAXBUF | 16 | 2 | 29 | 30 | ## Notes for ESP32 31 | 32 | Connect ESP32 and W5500 as shown below. 33 | 34 | | ESP32-DevKitC | WIZ850io (W5500) | 35 | | ---- | ---- | 36 | | GND | GND | 37 | | 3V3 | 3.3V | 38 | | IO23 (VSPI MOSI) | MOSI | 39 | | IO19 (VSPI MISO) | MISO | 40 | | IO18 (VSPI SCK) | SCLK | 41 | | IO5 (VSPI SS) | SCSn | 42 | 43 | However, M5Stack's SS is not IO5 but IO26. 44 | 45 | ## Notes for M5 ATOM Matrix 46 | 47 | M5 ATOM Matrix's IO pins are remappable. The pin assignment is determined as follows. It's just for ease of wiring. 48 | 49 | | M5 ATOM Matrix | WIZ850io (W5500) | 50 | | ---- | ---- | 51 | | GND | GND | 52 | | 3V3 | 3.3V | 53 | | IO21 | MOSI | 54 | | IO19 | MISO | 55 | | IO25 | SCLK | 56 | | IO22 | SCSn | 57 | 58 | Please choose ESP32 Pico KIT as the board configuration, and use 115200 for the baud rate. 59 | 60 | ## Notes for GR-ROSE 61 | 62 | You must create a user task to use SOEM. 63 | 64 | GR-ROSE's main task (setup & loop) has only 512 byte stack. 65 | On the other hand, SOEM uses more than 3k byte local variables. 66 | Therefore the memory corruption will occur when you call SOEM's API on the main task. 67 | You must creat a task with more than 4k byte stack and then call SOEM's API on the task. 68 | Please see the sample sketch (slaveinfo.ino). 69 | 70 | ## Documentation of original SOEM 71 | See https://openethercatsociety.github.io/doc/soem/ 72 | 73 | ## Article about this library (written in Japanese) 74 | See https://lipoyang.hatenablog.com/entry/2019/08/13/125008 75 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=SOEM 2 | version=1.0.0 3 | author=Various 4 | maintainer=Bizan Nishimura 5 | sentence=EtherCAT Master. 6 | paragraph=EtherCAT Master. 7 | category=Communication 8 | url=https://github.com/lipoyang/SOEM4Arduino 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/SOEM.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "osal/osal_defs.h" // EC_DEBUG 5 | 6 | #if defined(__cplusplus) 7 | extern "C" { 8 | #endif 9 | // just for debug (not thread safe) 10 | void debug_print(const char* format, ...) 11 | { 12 | static char buff[1024]; // TODO non-reentrant 13 | 14 | va_list args; 15 | va_start( args, format ); 16 | vsnprintf( buff, sizeof(buff), format, args ); 17 | va_end(args); 18 | 19 | Serial.print(buff); 20 | } 21 | #ifdef __cplusplus 22 | } 23 | #endif 24 | 25 | #if defined(GRSAKURA) 26 | /************************************************** 27 | for GR-SAKURA 28 | **************************************************/ 29 | 30 | #include "rx63n/iodefine.h" 31 | //#include 32 | #if defined(__cplusplus) 33 | extern "C" { 34 | #endif 35 | #include "utility/driver/r_ether.h" 36 | #include "utility/driver/r_ether_local.h" 37 | #ifdef __cplusplus 38 | } 39 | #endif 40 | 41 | #define ETHER_BUFSIZE_MIN 60 42 | #define MY_MAC_ADDR 0x02,0x00,0x00,0x00,0x00,0x00 43 | static uint8_t myethaddr[6] = {MY_MAC_ADDR}; 44 | 45 | // polling Ethernet instead of interrupt. (TODO This is a bad trick.) 46 | static void ethernet_poll(void) 47 | { 48 | uint32_t status_ecsr = ETHERC.ECSR.LONG; 49 | uint32_t status_eesr = EDMAC.EESR.LONG; 50 | 51 | /* When the ETHERC status interrupt is generated */ 52 | if (status_eesr & EMAC_ECI_INT) 53 | { 54 | /* When the Magic Packet detection interrupt is generated */ 55 | if (status_ecsr & EMAC_MPD_INT) 56 | { 57 | //g_ether_MpdFlag = ETHER_FLAG_ON; 58 | } 59 | /* 60 | * Because each bit of the ECSR register is cleared when one is written, 61 | * the value read from the register is written and the bit is cleared. 62 | */ 63 | /* Clear all ETHERC status BFR, PSRTO, LCHNG, MPD, ICD */ 64 | ETHERC.ECSR.LONG = status_ecsr; 65 | } 66 | EDMAC.EESR.LONG = status_eesr; /* Clear EDMAC status bits */ 67 | } 68 | 69 | #ifdef __cplusplus 70 | extern "C" 71 | { 72 | #endif 73 | 74 | // (1) open 75 | // return: 0=SUCCESS 76 | int hal_ethernet_open(void) 77 | { 78 | int eth_ret; 79 | int result = R_ETHER_Open_ZC2(0, myethaddr); 80 | IEN(ETHER, EINT) = 0; 81 | 82 | do //TODO allow for timeout 83 | { 84 | eth_ret = R_Ether_CheckLink_ZC(0); 85 | delay(10); 86 | } 87 | while(R_ETHER_OK != eth_ret); 88 | 89 | do 90 | { 91 | R_ETHER_LinkProcess(); 92 | delay(10); 93 | } 94 | while(g_ether_TransferEnableFlag != ETHER_FLAG_ON); 95 | 96 | return result; 97 | } 98 | 99 | // (2) close 100 | void hal_ethernet_close(void) 101 | { 102 | R_ETHER_Close_ZC2(0); 103 | } 104 | 105 | // (3) send 106 | // return: 0=SUCCESS (!!! not sent size) 107 | int hal_ethernet_send(unsigned char *pucBuffer, int length) 108 | { 109 | int ret; 110 | uint8_t * pwrite_buffer; 111 | uint16_t write_buf_size; 112 | 113 | // get buffer to send 114 | ret = R_ETHER_Write_ZC2_GetBuf(0, (void **) &pwrite_buffer, &write_buf_size); 115 | if (R_ETHER_OK != ret) 116 | { 117 | //debug_print("R_ETHER_Write_ZC2_GetBuf failed!\n"); 118 | return -1; 119 | } 120 | // copy data to buffer if size enough 121 | if ((write_buf_size >= length) && (write_buf_size >= ETHER_BUFSIZE_MIN)) 122 | { 123 | memcpy(pwrite_buffer, pucBuffer, length); 124 | }else{ 125 | //debug_print("R_ETHER_Write_ZC2_GetBuf failed 2!\n"); 126 | return -2; 127 | } 128 | // minimum Ethernet frame size is 60 bytes. 129 | // zero padding and resize if under minimum. 130 | if (length < ETHER_BUFSIZE_MIN) 131 | { 132 | memset((pwrite_buffer + length), 0, (ETHER_BUFSIZE_MIN - length)); // padding 133 | length = ETHER_BUFSIZE_MIN; // resize 134 | } 135 | 136 | ret = R_ETHER_Write_ZC2_SetBuf(0, (uint16_t)length); 137 | if(ret != R_ETHER_OK){ 138 | //debug_print("R_ETHER_Write_ZC2_SetBuf failed!\n"); 139 | return -3; 140 | } 141 | 142 | //ret = R_ETHER_CheckWrite(0); 143 | //if(ret != R_ETHER_OK){ 144 | // return -4; 145 | //} 146 | 147 | return 0; 148 | } 149 | 150 | // (4) receive 151 | // return: received size 152 | int hal_ethernet_recv(unsigned char **data) 153 | { 154 | // polling Ethernet instead of interrupt. (TODO This is a bad trick.) 155 | ethernet_poll(); 156 | int result = R_ETHER_Read_ZC2(0, (void **)data); 157 | return result; 158 | } 159 | 160 | // release received buffer 161 | // don't forget call this after hal_ethernet_recv() 162 | void hal_ethernet_recv_rel(void) 163 | { 164 | R_ETHER_Read_ZC2_BufRelease(0); 165 | } 166 | 167 | #ifdef __cplusplus 168 | } 169 | #endif 170 | 171 | #elif defined(GRROSE) 172 | /************************************************** 173 | for GR-ROSE 174 | **************************************************/ 175 | 176 | #if defined(__cplusplus) 177 | #include "Arduino.h" 178 | extern "C" { 179 | #endif 180 | #include "FreeRTOS.h" 181 | #include "task.h" 182 | #include "r_ether_rx_if.h" 183 | #include "r_ether_rx_pinset.h" 184 | int32_t callback_ether_regist(void); 185 | #ifdef __cplusplus 186 | } 187 | #endif 188 | 189 | #define ETHER_BUFSIZE_MIN 60 190 | #define MY_MAC_ADDR 0x02,0x00,0x00,0x00,0x00,0x00 191 | static uint8_t myethaddr[6] = {MY_MAC_ADDR}; 192 | 193 | #ifdef __cplusplus 194 | extern "C" 195 | { 196 | #endif 197 | 198 | #if 0 199 | static TaskHandle_t my_ether_link_check_task_handle = 0; 200 | static void my_check_ether_link(void * pvParameters) 201 | { 202 | R_INTERNAL_NOT_USED(pvParameters); 203 | 204 | while(1) 205 | { 206 | vTaskDelay(1000); 207 | R_ETHER_LinkProcess(0); 208 | } 209 | } 210 | #endif 211 | 212 | void callback_ether(void * pparam); 213 | static int32_t my_callback_ether_regist(void) 214 | { 215 | ether_param_t param; 216 | ether_cb_t cb_func; 217 | 218 | int32_t ret; 219 | 220 | /* Set the callback function (LAN cable connect/disconnect event) */ 221 | cb_func.pcb_func = &callback_ether; 222 | param.ether_callback = cb_func; 223 | ret = R_ETHER_Control(CONTROL_SET_CALLBACK, param); 224 | if (ETHER_SUCCESS != ret) 225 | { 226 | return -1; 227 | } 228 | #if 0 229 | /* Set the callback function (Ether interrupt event) */ 230 | cb_func.pcb_int_hnd = &EINT_Trig_isr; 231 | param.ether_callback = cb_func; 232 | ret = R_ETHER_Control(CONTROL_SET_INT_HANDLER, param); 233 | if (ETHER_SUCCESS != ret) 234 | { 235 | return -1; 236 | } 237 | #endif 238 | return 0; 239 | } 240 | 241 | // (1) open 242 | // return: 0=SUCCESS 243 | int hal_ethernet_open(void) 244 | { 245 | ether_param_t param; 246 | ether_return_t eth_ret; 247 | 248 | R_ETHER_PinSet_ETHERC0_RMII(); 249 | R_ETHER_Initial(); 250 | my_callback_ether_regist(); 251 | int result = R_ETHER_Open_ZC2(0, myethaddr, ETHER_FLAG_OFF); 252 | param.channel = ETHER_CHANNEL_0; 253 | eth_ret = R_ETHER_Control(CONTROL_POWER_ON, param); // PHY mode settings, module stop cancellation 254 | 255 | do //TODO allow for timeout 256 | { 257 | eth_ret = R_ETHER_CheckLink_ZC(0); 258 | vTaskDelay(10); // TODO 259 | } 260 | while(ETHER_SUCCESS != eth_ret); 261 | 262 | R_ETHER_LinkProcess(0); 263 | 264 | #if 0 265 | long return_code; 266 | return_code = xTaskCreate(my_check_ether_link, 267 | "CHECK_ETHER_LINK_TIMER", 268 | 100, 269 | 0, 270 | configMAX_PRIORITIES, 271 | &my_ether_link_check_task_handle); 272 | if (pdFALSE == return_code) 273 | { 274 | return pdFALSE; 275 | } 276 | #endif 277 | return result; 278 | } 279 | 280 | // (2) close 281 | void hal_ethernet_close(void) 282 | { 283 | R_ETHER_Close_ZC2(0); 284 | } 285 | 286 | // (3) send 287 | // return: 0=SUCCESS (!!! not sent size) 288 | int hal_ethernet_send(unsigned char *pucBuffer, int length) 289 | { 290 | ether_return_t ret; 291 | uint8_t * pwrite_buffer; 292 | uint16_t write_buf_size; 293 | 294 | // get buffer to send data 295 | ret = R_ETHER_Write_ZC2_GetBuf(ETHER_CHANNEL_0, (void **) &pwrite_buffer, &write_buf_size); 296 | if (ETHER_SUCCESS != ret) 297 | { 298 | //debug_print("R_ETHER_Write_ZC2_GetBuf failed!\n"); 299 | return -1; 300 | } 301 | // copy data to buffer if size enough 302 | if ((write_buf_size >= length) && (write_buf_size >= ETHER_BUFSIZE_MIN)) 303 | { 304 | memcpy(pwrite_buffer, pucBuffer, length); 305 | }else{ 306 | return -2; 307 | } 308 | // minimum Ethernet frame size is 60 bytes. 309 | // zero padding and resize if under minimum. 310 | if (length < ETHER_BUFSIZE_MIN) 311 | { 312 | memset((pwrite_buffer + length), 0, (ETHER_BUFSIZE_MIN - length)); // padding 313 | length = ETHER_BUFSIZE_MIN; // resize 314 | } 315 | 316 | ret = R_ETHER_Write_ZC2_SetBuf(ETHER_CHANNEL_0, (uint16_t)length); 317 | if(ret != ETHER_SUCCESS){ 318 | return -3; 319 | } 320 | 321 | ret = R_ETHER_CheckWrite(ETHER_CHANNEL_0); 322 | if(ret != ETHER_SUCCESS){ 323 | return -4; 324 | } 325 | 326 | return 0; 327 | } 328 | 329 | // (4) receive 330 | // return: received size 331 | int hal_ethernet_recv(unsigned char **data) 332 | { 333 | int result = R_ETHER_Read_ZC2(ETHER_CHANNEL_0, (void **)data); 334 | #if 0 335 | int i; 336 | for(i=0;i0) hoge_print("\n"); 340 | #endif 341 | return result; 342 | } 343 | 344 | // release received buffer 345 | // don't forget call this after hal_ethernet_recv() 346 | void hal_ethernet_recv_rel(void) 347 | { 348 | R_ETHER_Read_ZC2_BufRelease(ETHER_CHANNEL_0); 349 | } 350 | 351 | #ifdef __cplusplus 352 | } 353 | #endif 354 | 355 | #else 356 | /************************************************** 357 | for Ethernet Shield (W5500) 358 | **************************************************/ 359 | 360 | #include 361 | #include "w5500/w5500.h" 362 | 363 | // W5500 RAW socket 364 | static SOCKET sock; 365 | // W5500 RAW socket buffer 366 | static unsigned char socketBuffer[1536]; 367 | 368 | #ifdef __cplusplus 369 | extern "C" 370 | { 371 | #endif 372 | 373 | // (1) open 374 | // return: 0=SUCCESS 375 | int hal_ethernet_open(void) 376 | { 377 | #if defined(ARDUINO_M5Stack_Core_ESP32) 378 | w5500.init(26); // M5Stack's SS is GPIO26. 379 | #elif defined(ESP32) 380 | if(strcmp(ARDUINO_BOARD, "ESP32_PICO") == 0){ 381 | w5500.init(25, 19, 21, 22); // for M5 ATOM Matrix 382 | }else{ 383 | w5500.init(5);// ESP32's SS is typically GPIO5. 384 | } 385 | #else 386 | // Ethernet Shield 2 387 | // disable nCS for SD Card 388 | pinMode(4, OUTPUT); 389 | digitalWrite(4, HIGH); 390 | 391 | w5500.init(); 392 | #endif 393 | w5500.writeSnMR(sock, SnMR::MACRAW); 394 | w5500.execCmdSn(sock, Sock_OPEN); 395 | return 0; 396 | } 397 | 398 | // (2) close 399 | void hal_ethernet_close(void) 400 | { 401 | // w5500 doesn't have close() or terminate() method 402 | w5500.swReset(); 403 | } 404 | 405 | // (3) send 406 | // return: 0=SUCCESS (!!! not sent size) 407 | int hal_ethernet_send(unsigned char *data, int len) 408 | { 409 | w5500.send_data_processing(sock, (byte *)data, len); 410 | w5500.execCmdSn(sock, Sock_SEND_MAC); 411 | return 0; 412 | } 413 | 414 | // (4) receive 415 | // return: received size 416 | int hal_ethernet_recv(unsigned char **data) 417 | { 418 | unsigned short packetSize; 419 | int res = w5500.getRXReceivedSize(sock); 420 | if(res > 0){ 421 | // first 2byte shows packet size 422 | w5500.recv_data_processing(sock, (byte*)socketBuffer, 2); 423 | w5500.execCmdSn(sock, Sock_RECV); 424 | // packet size 425 | packetSize = socketBuffer[0]; 426 | packetSize <<= 8; 427 | packetSize &= 0xFF00; 428 | packetSize |= (unsigned short)( (unsigned char)socketBuffer[1] & 0x00FF); 429 | packetSize -= 2; 430 | // get received data 431 | memset(socketBuffer, 0x00, 1536); 432 | w5500.recv_data_processing(sock, (byte *)socketBuffer, packetSize); 433 | w5500.execCmdSn(sock, Sock_RECV); 434 | *data = socketBuffer; 435 | } 436 | return packetSize; 437 | } 438 | 439 | #ifdef __cplusplus 440 | } 441 | #endif 442 | 443 | #endif // for Ethernet Shield (W5500) 444 | 445 | -------------------------------------------------------------------------------- /src/SOEM.h: -------------------------------------------------------------------------------- 1 | #include "soem/ethercat.h" 2 | 3 | // this is for printf() compatibility 4 | #ifndef ESP32 5 | #define printf(...) debug_print(__VA_ARGS__) 6 | #endif 7 | -------------------------------------------------------------------------------- /src/osal/osal.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | #include 7 | 8 | #if defined(GRROSE) 9 | #include "FreeRTOS.h" 10 | #include "task.h" 11 | #endif 12 | 13 | #include "osal.h" 14 | 15 | ec_timet osal_current_time (void) 16 | { 17 | uint32 ret = micros(); 18 | return ret; 19 | } 20 | 21 | void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff) 22 | { 23 | *diff = *end - *start; 24 | } 25 | 26 | void osal_timer_start (osal_timert *self, uint32 timeout_usec) 27 | { 28 | self->start_time = micros(); 29 | self->timeout = timeout_usec; 30 | } 31 | 32 | boolean osal_timer_is_expired (osal_timert *self) 33 | { 34 | uint32 now = micros(); 35 | uint32 elapsed = now - self->start_time; 36 | 37 | return (elapsed >= self->timeout); 38 | } 39 | 40 | int osal_usleep (uint32 usec) 41 | { 42 | #if defined(GRROSE) 43 | uint32 msec; 44 | msec = usec / 1000; 45 | usec = usec % 1000; 46 | vTaskDelay(msec); 47 | #else 48 | delayMicroseconds(usec); 49 | #endif 50 | return 0; 51 | } 52 | 53 | void *osal_malloc(size_t size) 54 | { 55 | return malloc(size); 56 | } 57 | 58 | void osal_free(void *ptr) 59 | { 60 | free(ptr); 61 | } 62 | 63 | int osal_thread_create(void *thandle, int stacksize, void *func, void *param) 64 | { 65 | #if defined(GRROSE) 66 | // TODO supports multi-task 67 | return 1; 68 | #else 69 | // not support multi-task 70 | return 1; 71 | #endif 72 | } 73 | 74 | int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param) 75 | { 76 | #if defined(GRROSE) 77 | // TODO supports multi-task 78 | return 1; 79 | #else 80 | // not support multi-task 81 | return 1; 82 | #endif 83 | } 84 | -------------------------------------------------------------------------------- /src/osal/osal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | #ifndef _osal_ 7 | #define _osal_ 8 | 9 | #ifdef __cplusplus 10 | extern "C" 11 | { 12 | #endif 13 | 14 | #include "osal_defs.h" 15 | #include 16 | #include 17 | 18 | /* General types */ 19 | 20 | #ifndef Arduino_h 21 | #ifdef __PIC32MX__ 22 | typedef uint8_t boolean; 23 | #else 24 | typedef bool boolean; 25 | #endif 26 | #endif 27 | #define TRUE 1 28 | #define FALSE 0 29 | typedef int8_t int8; 30 | typedef int16_t int16; 31 | typedef int32_t int32; 32 | typedef uint8_t uint8; 33 | typedef uint16_t uint16; 34 | typedef uint32_t uint32; 35 | typedef int64_t int64; 36 | typedef uint64_t uint64; 37 | typedef float float32; 38 | typedef double float64; 39 | 40 | // TODO ec_timet is not compatible with regular SOEM. 41 | #if 0 42 | typedef struct 43 | { 44 | uint32 sec; /*< Seconds elapsed since the Epoch (Jan 1, 1970) */ 45 | uint32 usec; /*< Microseconds elapsed since last second boundary */ 46 | } ec_timet; 47 | 48 | typedef struct osal_timer 49 | { 50 | ec_timet stop_time; 51 | } osal_timert; 52 | 53 | #else 54 | 55 | // elapsed time [usec] from start-up, not linux time 56 | typedef uint32 ec_timet; 57 | 58 | typedef struct osal_timer 59 | { 60 | ec_timet start_time; 61 | ec_timet timeout; 62 | } osal_timert; 63 | 64 | #endif 65 | 66 | void osal_timer_start(osal_timert * self, uint32 timeout_us); 67 | boolean osal_timer_is_expired(osal_timert * self); 68 | int osal_usleep(uint32 usec); 69 | ec_timet osal_current_time(void); 70 | void osal_time_diff(ec_timet *start, ec_timet *end, ec_timet *diff); 71 | int osal_thread_create(void *thandle, int stacksize, void *func, void *param); 72 | int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param); 73 | 74 | #ifdef __cplusplus 75 | } 76 | #endif 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /src/osal/osal_defs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | #ifndef _osal_defs_ 7 | #define _osal_defs_ 8 | 9 | #include 10 | 11 | #ifdef __cplusplus 12 | extern "C" 13 | { 14 | #endif 15 | 16 | // define if debug printf is needed 17 | //#define EC_DEBUG 18 | 19 | void debug_print(const char* format, ...); 20 | 21 | #ifdef EC_DEBUG 22 | #define EC_PRINT debug_print 23 | #else 24 | #define EC_PRINT(...) do {} while (0) 25 | #endif 26 | 27 | #ifndef PACKED 28 | #define PACKED_BEGIN 29 | #define PACKED __attribute__((__packed__)) 30 | #define PACKED_END 31 | #endif 32 | 33 | #define OSAL_THREAD_HANDLE HANDLE 34 | #define OSAL_THREAD_FUNC void 35 | #define OSAL_THREAD_FUNC_RT void 36 | 37 | #ifdef __cplusplus 38 | } 39 | #endif 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/oshw/nicdrv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * Headerfile for nicdrv.c 9 | */ 10 | 11 | #ifndef _nicdrvh_ 12 | #define _nicdrvh_ 13 | 14 | #if defined(GRROSE) 15 | #include "FreeRTOS.h" 16 | #include "semphr.h" 17 | #endif 18 | 19 | #ifdef __cplusplus 20 | extern "C" 21 | { 22 | #endif 23 | 24 | #define HAVE_REMOTE 25 | 26 | /** pointer structure to Tx and Rx stacks */ 27 | typedef struct 28 | { 29 | /** socket connection used */ 30 | int *sock; 31 | /** tx buffer */ 32 | ec_bufT (*txbuf)[EC_MAXBUF]; 33 | /** tx buffer lengths */ 34 | int (*txbuflength)[EC_MAXBUF]; 35 | /** temporary receive buffer */ 36 | ec_bufT *tempbuf; 37 | /** rx buffers */ 38 | ec_bufT (*rxbuf)[EC_MAXBUF]; 39 | /** rx buffer status fields */ 40 | int (*rxbufstat)[EC_MAXBUF]; 41 | /** received MAC source address (middle word) */ 42 | int (*rxsa)[EC_MAXBUF]; 43 | } ec_stackT; 44 | 45 | // secondary port is not supported 46 | // because Arduino usually has only one LAN port 47 | #if 0 48 | /** pointer structure to buffers for redundant port */ 49 | typedef struct 50 | { 51 | ec_stackT stack; 52 | int sockhandle; 53 | /** rx buffers */ 54 | ec_bufT rxbuf[EC_MAXBUF]; 55 | /** rx buffer status */ 56 | int rxbufstat[EC_MAXBUF]; 57 | /** rx MAC source address */ 58 | int rxsa[EC_MAXBUF]; 59 | /** temporary rx buffer */ 60 | ec_bufT tempinbuf; 61 | } ecx_redportt; 62 | #endif 63 | 64 | /** pointer structure to buffers, vars and mutexes for port instantiation */ 65 | typedef struct 66 | { 67 | ec_stackT stack; 68 | int sockhandle; 69 | /** rx buffers */ 70 | ec_bufT rxbuf[EC_MAXBUF]; 71 | /** rx buffer status */ 72 | int rxbufstat[EC_MAXBUF]; 73 | /** rx MAC source address */ 74 | int rxsa[EC_MAXBUF]; 75 | /** temporary rx buffer */ 76 | ec_bufT tempinbuf; 77 | /** temporary rx buffer status */ 78 | int tempinbufs; 79 | /** transmit buffers */ 80 | ec_bufT txbuf[EC_MAXBUF]; 81 | /** transmit buffer lengths */ 82 | int txbuflength[EC_MAXBUF]; 83 | /** temporary tx buffer */ 84 | ec_bufT txbuf2; 85 | /** temporary tx buffer length */ 86 | int txbuflength2; 87 | /** last used frame index */ 88 | int lastidx; 89 | /** current redundancy state */ 90 | int redstate; 91 | // secondary port is not supported 92 | // because Arduino usually has only one LAN port 93 | #if 0 94 | /** pointer to redundancy port and buffers */ 95 | ecx_redportt *redport; 96 | #endif 97 | #if defined(GRROSE) 98 | SemaphoreHandle_t getindex_mutex; 99 | SemaphoreHandle_t tx_mutex; 100 | SemaphoreHandle_t rx_mutex; 101 | #endif 102 | } ecx_portt; 103 | 104 | extern const uint16 priMAC[3]; 105 | extern const uint16 secMAC[3]; 106 | 107 | #ifdef EC_VER1 108 | extern ecx_portt ecx_port; 109 | 110 | // secondary port is not supported 111 | // because Arduino usually has only one LAN port 112 | #if 0 113 | extern ecx_redportt ecx_redport; 114 | #endif 115 | 116 | int ec_setupnic(const char * ifname, int secondary); 117 | int ec_closenic(void); 118 | void ec_setbufstat(int idx, int bufstat); 119 | int ec_getindex(void); 120 | int ec_outframe(int idx, int sock); 121 | int ec_outframe_red(int idx); 122 | int ec_waitinframe(int idx, int timeout); 123 | int ec_srconfirm(int idx,int timeout); 124 | #endif 125 | 126 | void ec_setupheader(void *p); 127 | int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary); 128 | int ecx_closenic(ecx_portt *port); 129 | void ecx_setbufstat(ecx_portt *port, int idx, int bufstat); 130 | int ecx_getindex(ecx_portt *port); 131 | int ecx_outframe(ecx_portt *port, int idx, int sock); 132 | int ecx_outframe_red(ecx_portt *port, int idx); 133 | int ecx_waitinframe(ecx_portt *port, int idx, int timeout); 134 | int ecx_srconfirm(ecx_portt *port, int idx,int timeout); 135 | 136 | #ifdef __cplusplus 137 | } 138 | #endif 139 | 140 | #endif 141 | -------------------------------------------------------------------------------- /src/oshw/oshw.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | #include 6 | #include "oshw.h" 7 | 8 | /** 9 | * Host to Network byte order (i.e. to big endian). 10 | * 11 | * Note that Ethercat uses little endian byte order, except for the Ethernet 12 | * header which is big endian as usual. 13 | */ 14 | uint16 oshw_htons (uint16 data) 15 | { 16 | uint16_t tmp = 0; 17 | tmp = (data & 0x00ff) << 8; 18 | tmp |= (data & 0xff00) >> 8; 19 | data = tmp; 20 | return data; 21 | } 22 | 23 | /** 24 | * Network (i.e. big endian) to Host byte order. 25 | * 26 | * Note that Ethercat uses little endian byte order, except for the Ethernet 27 | * header which is big endian as usual. 28 | */ 29 | uint16 oshw_ntohs (uint16 data) 30 | { 31 | uint16_t tmp = 0; 32 | tmp = (data & 0x00ff) << 8; 33 | tmp |= (data & 0xff00) >> 8; 34 | data = tmp; 35 | return data; 36 | } 37 | 38 | /* Create list over available network adapters. 39 | * @return First element in linked list of adapters 40 | */ 41 | ec_adaptert * oshw_find_adapters (void) 42 | { 43 | // Not implemented 44 | // because Arduino usually has only one LAN port 45 | ec_adaptert * ret_adapter = NULL; 46 | return ret_adapter; 47 | } 48 | 49 | /** Free memory allocated memory used by adapter collection. 50 | * @param[in] adapter = First element in linked list of adapters 51 | * EC_NOFRAME. 52 | */ 53 | void oshw_free_adapters (ec_adaptert * adapter) 54 | { 55 | // Not implemented 56 | // because Arduino usually has only one LAN port 57 | } 58 | -------------------------------------------------------------------------------- /src/oshw/oshw.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * Headerfile for ethercatbase.c 9 | */ 10 | 11 | #ifndef _oshw_ 12 | #define _oshw_ 13 | 14 | #ifdef __cplusplus 15 | extern "C" 16 | { 17 | #endif 18 | 19 | #include "../soem/ethercattype.h" 20 | #include "nicdrv.h" 21 | #include "../soem/ethercatmain.h" 22 | 23 | uint16 oshw_htons (uint16 hostshort); 24 | uint16 oshw_ntohs (uint16 networkshort); 25 | ec_adaptert * oshw_find_adapters (void); 26 | void oshw_free_adapters (ec_adaptert * adapter); 27 | 28 | #ifdef __cplusplus 29 | } 30 | #endif 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/soem/ethercat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * Headerfile for all ethercat headers 9 | */ 10 | 11 | #ifndef _EC_ETHERCAT_H 12 | #define _EC_ETHERCAT_H 13 | 14 | #include "ethercattype.h" 15 | #include "oshw/nicdrv.h" 16 | #include "ethercatbase.h" 17 | #include "ethercatmain.h" 18 | #include "ethercatdc.h" 19 | #include "ethercatcoe.h" 20 | #include "ethercatfoe.h" 21 | #include "ethercatsoe.h" 22 | #include "ethercateoe.h" 23 | #include "ethercatconfig.h" 24 | #include "ethercatprint.h" 25 | 26 | #endif /* _EC_ETHERCAT_H */ 27 | -------------------------------------------------------------------------------- /src/soem/ethercatbase.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * Base EtherCAT functions. 9 | * 10 | * Setting up a datagram in an ethernet frame. 11 | * EtherCAT datagram primitives, broadcast, auto increment, configured and 12 | * logical addressed data transfers. All base transfers are blocking, so 13 | * wait for the frame to be returned to the master or timeout. If this is 14 | * not acceptable build your own datagrams and use the functions from nicdrv.c. 15 | */ 16 | 17 | #include 18 | #include 19 | #include "oshw/oshw.h" 20 | #include "osal/osal.h" 21 | #include "ethercattype.h" 22 | #include "ethercatbase.h" 23 | 24 | /** Write data to EtherCAT datagram. 25 | * 26 | * @param[out] datagramdata = data part of datagram 27 | * @param[in] com = command 28 | * @param[in] length = length of databuffer 29 | * @param[in] data = databuffer to be copied into datagram 30 | */ 31 | static void ecx_writedatagramdata(void *datagramdata, ec_cmdtype com, uint16 length, const void * data) 32 | { 33 | if (length > 0) 34 | { 35 | switch (com) 36 | { 37 | case EC_CMD_NOP: 38 | /* Fall-through */ 39 | case EC_CMD_APRD: 40 | /* Fall-through */ 41 | case EC_CMD_FPRD: 42 | /* Fall-through */ 43 | case EC_CMD_BRD: 44 | /* Fall-through */ 45 | case EC_CMD_LRD: 46 | /* no data to write. initialise data so frame is in a known state */ 47 | memset(datagramdata, 0, length); 48 | break; 49 | default: 50 | memcpy(datagramdata, data, length); 51 | break; 52 | } 53 | } 54 | } 55 | 56 | /** Generate and set EtherCAT datagram in a standard ethernet frame. 57 | * 58 | * @param[in] port = port context struct 59 | * @param[out] frame = framebuffer 60 | * @param[in] com = command 61 | * @param[in] idx = index used for TX and RX buffers 62 | * @param[in] ADP = Address Position 63 | * @param[in] ADO = Address Offset 64 | * @param[in] length = length of datagram excluding EtherCAT header 65 | * @param[in] data = databuffer to be copied in datagram 66 | * @return always 0 67 | */ 68 | int ecx_setupdatagram(ecx_portt *port, void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data) 69 | { 70 | ec_comt *datagramP; 71 | uint8 *frameP; 72 | 73 | frameP = frame; 74 | /* Ethernet header is preset and fixed in frame buffers 75 | EtherCAT header needs to be added after that */ 76 | datagramP = (ec_comt*)&frameP[ETH_HEADERSIZE]; 77 | datagramP->elength = htoes(EC_ECATTYPE + EC_HEADERSIZE + length); 78 | datagramP->command = com; 79 | datagramP->index = idx; 80 | datagramP->ADP = htoes(ADP); 81 | datagramP->ADO = htoes(ADO); 82 | datagramP->dlength = htoes(length); 83 | ecx_writedatagramdata(&frameP[ETH_HEADERSIZE + EC_HEADERSIZE], com, length, data); 84 | /* set WKC to zero */ 85 | frameP[ETH_HEADERSIZE + EC_HEADERSIZE + length] = 0x00; 86 | frameP[ETH_HEADERSIZE + EC_HEADERSIZE + length + 1] = 0x00; 87 | /* set size of frame in buffer array */ 88 | port->txbuflength[idx] = ETH_HEADERSIZE + EC_HEADERSIZE + EC_WKCSIZE + length; 89 | 90 | return 0; 91 | } 92 | 93 | /** Add EtherCAT datagram to a standard ethernet frame with existing datagram(s). 94 | * 95 | * @param[in] port = port context struct 96 | * @param[out] frame = framebuffer 97 | * @param[in] com = command 98 | * @param[in] idx = index used for TX and RX buffers 99 | * @param[in] more = TRUE if still more datagrams to follow 100 | * @param[in] ADP = Address Position 101 | * @param[in] ADO = Address Offset 102 | * @param[in] length = length of datagram excluding EtherCAT header 103 | * @param[in] data = databuffer to be copied in datagram 104 | * @return Offset to data in rx frame, usefull to retrieve data after RX. 105 | */ 106 | int ecx_adddatagram(ecx_portt *port, void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data) 107 | { 108 | ec_comt *datagramP; 109 | uint8 *frameP; 110 | uint16 prevlength; 111 | 112 | frameP = frame; 113 | /* copy previous frame size */ 114 | prevlength = port->txbuflength[idx]; 115 | datagramP = (ec_comt*)&frameP[ETH_HEADERSIZE]; 116 | /* add new datagram to ethernet frame size */ 117 | datagramP->elength = htoes( etohs(datagramP->elength) + EC_HEADERSIZE + length ); 118 | /* add "datagram follows" flag to previous subframe dlength */ 119 | datagramP->dlength = htoes( etohs(datagramP->dlength) | EC_DATAGRAMFOLLOWS ); 120 | /* set new EtherCAT header position */ 121 | datagramP = (ec_comt*)&frameP[prevlength - EC_ELENGTHSIZE]; 122 | datagramP->command = com; 123 | datagramP->index = idx; 124 | datagramP->ADP = htoes(ADP); 125 | datagramP->ADO = htoes(ADO); 126 | if (more) 127 | { 128 | /* this is not the last datagram to add */ 129 | datagramP->dlength = htoes(length | EC_DATAGRAMFOLLOWS); 130 | } 131 | else 132 | { 133 | /* this is the last datagram in the frame */ 134 | datagramP->dlength = htoes(length); 135 | } 136 | ecx_writedatagramdata(&frameP[prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE], com, length, data); 137 | /* set WKC to zero */ 138 | frameP[prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE + length] = 0x00; 139 | frameP[prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE + length + 1] = 0x00; 140 | /* set size of frame in buffer array */ 141 | port->txbuflength[idx] = prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE + EC_WKCSIZE + length; 142 | 143 | /* return offset to data in rx frame 144 | 14 bytes smaller than tx frame due to stripping of ethernet header */ 145 | return prevlength + EC_HEADERSIZE - EC_ELENGTHSIZE - ETH_HEADERSIZE; 146 | } 147 | 148 | /** BRW "broadcast write" primitive. Blocking. 149 | * 150 | * @param[in] port = port context struct 151 | * @param[in] ADP = Address Position, normally 0 152 | * @param[in] ADO = Address Offset, slave memory address 153 | * @param[in] length = length of databuffer 154 | * @param[in] data = databuffer to be written to slaves 155 | * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET 156 | * @return Workcounter or EC_NOFRAME 157 | */ 158 | int ecx_BWR (ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout) 159 | { 160 | uint8 idx; 161 | int wkc; 162 | 163 | /* get fresh index */ 164 | idx = ecx_getindex (port); 165 | /* setup datagram */ 166 | ecx_setupdatagram (port, &(port->txbuf[idx]), EC_CMD_BWR, idx, ADP, ADO, length, data); 167 | /* send data and wait for answer */ 168 | wkc = ecx_srconfirm (port, idx, timeout); 169 | /* clear buffer status */ 170 | ecx_setbufstat (port, idx, EC_BUF_EMPTY); 171 | 172 | return wkc; 173 | } 174 | 175 | /** BRD "broadcast read" primitive. Blocking. 176 | * 177 | * @param[in] port = port context struct 178 | * @param[in] ADP = Address Position, normally 0 179 | * @param[in] ADO = Address Offset, slave memory address 180 | * @param[in] length = length of databuffer 181 | * @param[out] data = databuffer to put slave data in 182 | * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET 183 | * @return Workcounter or EC_NOFRAME 184 | */ 185 | int ecx_BRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout) 186 | { 187 | uint8 idx; 188 | int wkc; 189 | 190 | /* get fresh index */ 191 | idx = ecx_getindex(port); 192 | /* setup datagram */ 193 | ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_BRD, idx, ADP, ADO, length, data); 194 | /* send data and wait for answer */ 195 | wkc = ecx_srconfirm (port, idx, timeout); 196 | if (wkc > 0) 197 | { 198 | /* copy datagram to data buffer */ 199 | memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length); 200 | } 201 | /* clear buffer status */ 202 | ecx_setbufstat(port, idx, EC_BUF_EMPTY); 203 | 204 | return wkc; 205 | } 206 | 207 | /** APRD "auto increment address read" primitive. Blocking. 208 | * 209 | * @param[in] port = port context struct 210 | * @param[in] ADP = Address Position, each slave ++, slave that has 0 executes 211 | * @param[in] ADO = Address Offset, slave memory address 212 | * @param[in] length = length of databuffer 213 | * @param[out] data = databuffer to put slave data in 214 | * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET 215 | * @return Workcounter or EC_NOFRAME 216 | */ 217 | int ecx_APRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout) 218 | { 219 | int wkc; 220 | uint8 idx; 221 | 222 | idx = ecx_getindex(port); 223 | ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_APRD, idx, ADP, ADO, length, data); 224 | wkc = ecx_srconfirm(port, idx, timeout); 225 | if (wkc > 0) 226 | { 227 | memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length); 228 | } 229 | ecx_setbufstat(port, idx, EC_BUF_EMPTY); 230 | 231 | return wkc; 232 | } 233 | 234 | /** APRMW "auto increment address read, multiple write" primitive. Blocking. 235 | * 236 | * @param[in] port = port context struct 237 | * @param[in] ADP = Address Position, each slave ++, slave that has 0 reads, 238 | * following slaves write. 239 | * @param[in] ADO = Address Offset, slave memory address 240 | * @param[in] length = length of databuffer 241 | * @param[out] data = databuffer to put slave data in 242 | * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET 243 | * @return Workcounter or EC_NOFRAME 244 | */ 245 | int ecx_ARMW(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout) 246 | { 247 | int wkc; 248 | uint8 idx; 249 | 250 | idx = ecx_getindex(port); 251 | ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_ARMW, idx, ADP, ADO, length, data); 252 | wkc = ecx_srconfirm(port, idx, timeout); 253 | if (wkc > 0) 254 | { 255 | memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length); 256 | } 257 | ecx_setbufstat(port, idx, EC_BUF_EMPTY); 258 | 259 | return wkc; 260 | } 261 | 262 | /** FPRMW "configured address read, multiple write" primitive. Blocking. 263 | * 264 | * @param[in] port = port context struct 265 | * @param[in] ADP = Address Position, slave that has address reads, 266 | * following slaves write. 267 | * @param[in] ADO = Address Offset, slave memory address 268 | * @param[in] length = length of databuffer 269 | * @param[out] data = databuffer to put slave data in 270 | * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET 271 | * @return Workcounter or EC_NOFRAME 272 | */ 273 | int ecx_FRMW(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout) 274 | { 275 | int wkc; 276 | uint8 idx; 277 | 278 | idx = ecx_getindex(port); 279 | ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_FRMW, idx, ADP, ADO, length, data); 280 | wkc = ecx_srconfirm(port, idx, timeout); 281 | if (wkc > 0) 282 | { 283 | memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length); 284 | } 285 | ecx_setbufstat(port, idx, EC_BUF_EMPTY); 286 | 287 | return wkc; 288 | } 289 | 290 | /** APRDw "auto increment address read" word return primitive. Blocking. 291 | * 292 | * @param[in] port = port context struct 293 | * @param[in] ADP = Address Position, each slave ++, slave that has 0 reads. 294 | * @param[in] ADO = Address Offset, slave memory address 295 | * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET 296 | * @return word data from slave 297 | */ 298 | uint16 ecx_APRDw(ecx_portt *port, uint16 ADP, uint16 ADO, int timeout) 299 | { 300 | uint16 w; 301 | 302 | w = 0; 303 | ecx_APRD(port, ADP, ADO, sizeof(w), &w, timeout); 304 | 305 | return w; 306 | } 307 | 308 | /** FPRD "configured address read" primitive. Blocking. 309 | * 310 | * @param[in] port = port context struct 311 | * @param[in] ADP = Address Position, slave that has address reads. 312 | * @param[in] ADO = Address Offset, slave memory address 313 | * @param[in] length = length of databuffer 314 | * @param[out] data = databuffer to put slave data in 315 | * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET 316 | * @return Workcounter or EC_NOFRAME 317 | */ 318 | int ecx_FPRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout) 319 | { 320 | int wkc; 321 | uint8 idx; 322 | 323 | idx = ecx_getindex(port); 324 | ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_FPRD, idx, ADP, ADO, length, data); 325 | wkc = ecx_srconfirm(port, idx, timeout); 326 | if (wkc > 0) 327 | { 328 | memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length); 329 | } 330 | ecx_setbufstat(port, idx, EC_BUF_EMPTY); 331 | 332 | return wkc; 333 | } 334 | 335 | /** FPRDw "configured address read" word return primitive. Blocking. 336 | * 337 | * @param[in] port = port context struct 338 | * @param[in] ADP = Address Position, slave that has address reads. 339 | * @param[in] ADO = Address Offset, slave memory address 340 | * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET 341 | * @return word data from slave 342 | */ 343 | uint16 ecx_FPRDw(ecx_portt *port, uint16 ADP, uint16 ADO, int timeout) 344 | { 345 | uint16 w; 346 | 347 | w = 0; 348 | ecx_FPRD(port, ADP, ADO, sizeof(w), &w, timeout); 349 | return w; 350 | } 351 | 352 | /** APWR "auto increment address write" primitive. Blocking. 353 | * 354 | * @param[in] port = port context struct 355 | * @param[in] ADP = Address Position, each slave ++, slave that has 0 writes. 356 | * @param[in] ADO = Address Offset, slave memory address 357 | * @param[in] length = length of databuffer 358 | * @param[in] data = databuffer to write to slave. 359 | * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET 360 | * @return Workcounter or EC_NOFRAME 361 | */ 362 | int ecx_APWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout) 363 | { 364 | uint8 idx; 365 | int wkc; 366 | 367 | idx = ecx_getindex(port); 368 | ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_APWR, idx, ADP, ADO, length, data); 369 | wkc = ecx_srconfirm(port, idx, timeout); 370 | ecx_setbufstat(port, idx, EC_BUF_EMPTY); 371 | 372 | return wkc; 373 | } 374 | 375 | /** APWRw "auto increment address write" word primitive. Blocking. 376 | * 377 | * @param[in] port = port context struct 378 | * @param[in] ADP = Address Position, each slave ++, slave that has 0 writes. 379 | * @param[in] ADO = Address Offset, slave memory address 380 | * @param[in] data = word data to write to slave. 381 | * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET 382 | * @return Workcounter or EC_NOFRAME 383 | */ 384 | int ecx_APWRw(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 data, int timeout) 385 | { 386 | return ecx_APWR(port, ADP, ADO, sizeof(data), &data, timeout); 387 | } 388 | 389 | /** FPWR "configured address write" primitive. Blocking. 390 | * 391 | * @param[in] port = port context struct 392 | * @param[in] ADP = Address Position, slave that has address writes. 393 | * @param[in] ADO = Address Offset, slave memory address 394 | * @param[in] length = length of databuffer 395 | * @param[in] data = databuffer to write to slave. 396 | * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET 397 | * @return Workcounter or EC_NOFRAME 398 | */ 399 | int ecx_FPWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout) 400 | { 401 | int wkc; 402 | uint8 idx; 403 | 404 | idx = ecx_getindex(port); 405 | ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_FPWR, idx, ADP, ADO, length, data); 406 | wkc = ecx_srconfirm(port, idx, timeout); 407 | ecx_setbufstat(port, idx, EC_BUF_EMPTY); 408 | 409 | return wkc; 410 | } 411 | 412 | /** FPWR "configured address write" primitive. Blocking. 413 | * 414 | * @param[in] port = port context struct 415 | * @param[in] ADP = Address Position, slave that has address writes. 416 | * @param[in] ADO = Address Offset, slave memory address 417 | * @param[in] data = word to write to slave. 418 | * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET 419 | * @return Workcounter or EC_NOFRAME 420 | */ 421 | int ecx_FPWRw(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 data, int timeout) 422 | { 423 | return ecx_FPWR(port, ADP, ADO, sizeof(data), &data, timeout); 424 | } 425 | 426 | /** LRW "logical memory read / write" primitive. Blocking. 427 | * 428 | * @param[in] port = port context struct 429 | * @param[in] LogAdr = Logical memory address 430 | * @param[in] length = length of databuffer 431 | * @param[in,out] data = databuffer to write to and read from slave. 432 | * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET 433 | * @return Workcounter or EC_NOFRAME 434 | */ 435 | int ecx_LRW(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout) 436 | { 437 | uint8 idx; 438 | int wkc; 439 | 440 | idx = ecx_getindex(port); 441 | ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LRW, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data); 442 | wkc = ecx_srconfirm(port, idx, timeout); 443 | if ((wkc > 0) && (port->rxbuf[idx][EC_CMDOFFSET] == EC_CMD_LRW)) 444 | { 445 | memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length); 446 | } 447 | ecx_setbufstat(port, idx, EC_BUF_EMPTY); 448 | 449 | return wkc; 450 | } 451 | 452 | /** LRD "logical memory read" primitive. Blocking. 453 | * 454 | * @param[in] port = port context struct 455 | * @param[in] LogAdr = Logical memory address 456 | * @param[in] length = length of bytes to read from slave. 457 | * @param[out] data = databuffer to read from slave. 458 | * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET 459 | * @return Workcounter or EC_NOFRAME 460 | */ 461 | int ecx_LRD(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout) 462 | { 463 | uint8 idx; 464 | int wkc; 465 | 466 | idx = ecx_getindex(port); 467 | ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LRD, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data); 468 | wkc = ecx_srconfirm(port, idx, timeout); 469 | if ((wkc > 0) && (port->rxbuf[idx][EC_CMDOFFSET]==EC_CMD_LRD)) 470 | { 471 | memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length); 472 | } 473 | ecx_setbufstat(port, idx, EC_BUF_EMPTY); 474 | 475 | return wkc; 476 | } 477 | 478 | /** LWR "logical memory write" primitive. Blocking. 479 | * 480 | * @param[in] port = port context struct 481 | * @param[in] LogAdr = Logical memory address 482 | * @param[in] length = length of databuffer 483 | * @param[in] data = databuffer to write to slave. 484 | * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET 485 | * @return Workcounter or EC_NOFRAME 486 | */ 487 | int ecx_LWR(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout) 488 | { 489 | uint8 idx; 490 | int wkc; 491 | 492 | idx = ecx_getindex(port); 493 | ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LWR, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data); 494 | wkc = ecx_srconfirm(port, idx, timeout); 495 | ecx_setbufstat(port, idx, EC_BUF_EMPTY); 496 | 497 | return wkc; 498 | } 499 | 500 | /** LRW "logical memory read / write" primitive plus Clock Distribution. Blocking. 501 | * Frame consists of two datagrams, one LRW and one FPRMW. 502 | * 503 | * @param[in] port = port context struct 504 | * @param[in] LogAdr = Logical memory address 505 | * @param[in] length = length of databuffer 506 | * @param[in,out] data = databuffer to write to and read from slave. 507 | * @param[in] DCrs = Distributed Clock reference slave address. 508 | * @param[out] DCtime = DC time read from reference slave. 509 | * @param[in] timeout = timeout in us, standard is EC_TIMEOUTRET 510 | * @return Workcounter or EC_NOFRAME 511 | */ 512 | int ecx_LRWDC(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout) 513 | { 514 | uint16 DCtO; 515 | uint8 idx; 516 | int wkc; 517 | uint64 DCtE; 518 | 519 | idx = ecx_getindex(port); 520 | /* LRW in first datagram */ 521 | ecx_setupdatagram(port, &(port->txbuf[idx]), EC_CMD_LRW, idx, LO_WORD(LogAdr), HI_WORD(LogAdr), length, data); 522 | /* FPRMW in second datagram */ 523 | DCtE = htoell(*DCtime); 524 | DCtO = ecx_adddatagram(port, &(port->txbuf[idx]), EC_CMD_FRMW, idx, FALSE, DCrs, ECT_REG_DCSYSTIME, sizeof(DCtime), &DCtE); 525 | wkc = ecx_srconfirm(port, idx, timeout); 526 | if ((wkc > 0) && (port->rxbuf[idx][EC_CMDOFFSET] == EC_CMD_LRW)) 527 | { 528 | memcpy(data, &(port->rxbuf[idx][EC_HEADERSIZE]), length); 529 | memcpy(&wkc, &(port->rxbuf[idx][EC_HEADERSIZE + length]), EC_WKCSIZE); 530 | memcpy(&DCtE, &(port->rxbuf[idx][DCtO]), sizeof(*DCtime)); 531 | *DCtime = etohll(DCtE); 532 | } 533 | ecx_setbufstat(port, idx, EC_BUF_EMPTY); 534 | 535 | return wkc; 536 | } 537 | 538 | #ifdef EC_VER1 539 | int ec_setupdatagram(void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data) 540 | { 541 | return ecx_setupdatagram (&ecx_port, frame, com, idx, ADP, ADO, length, data); 542 | } 543 | 544 | int ec_adddatagram (void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data) 545 | { 546 | return ecx_adddatagram (&ecx_port, frame, com, idx, more, ADP, ADO, length, data); 547 | } 548 | 549 | int ec_BWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout) 550 | { 551 | return ecx_BWR (&ecx_port, ADP, ADO, length, data, timeout); 552 | } 553 | 554 | int ec_BRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout) 555 | { 556 | return ecx_BRD(&ecx_port, ADP, ADO, length, data, timeout); 557 | } 558 | 559 | int ec_APRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout) 560 | { 561 | return ecx_APRD(&ecx_port, ADP, ADO, length, data, timeout); 562 | } 563 | 564 | int ec_ARMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout) 565 | { 566 | return ecx_ARMW(&ecx_port, ADP, ADO, length, data, timeout); 567 | } 568 | 569 | int ec_FRMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout) 570 | { 571 | return ecx_FRMW(&ecx_port, ADP, ADO, length, data, timeout); 572 | } 573 | 574 | uint16 ec_APRDw(uint16 ADP, uint16 ADO, int timeout) 575 | { 576 | uint16 w; 577 | 578 | w = 0; 579 | ec_APRD(ADP, ADO, sizeof(w), &w, timeout); 580 | 581 | return w; 582 | } 583 | 584 | int ec_FPRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout) 585 | { 586 | return ecx_FPRD(&ecx_port, ADP, ADO, length, data, timeout); 587 | } 588 | 589 | uint16 ec_FPRDw(uint16 ADP, uint16 ADO, int timeout) 590 | { 591 | uint16 w; 592 | 593 | w = 0; 594 | ec_FPRD(ADP, ADO, sizeof(w), &w, timeout); 595 | return w; 596 | } 597 | 598 | int ec_APWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout) 599 | { 600 | return ecx_APWR(&ecx_port, ADP, ADO, length, data, timeout); 601 | } 602 | 603 | int ec_APWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout) 604 | { 605 | return ec_APWR(ADP, ADO, sizeof(data), &data, timeout); 606 | } 607 | 608 | int ec_FPWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout) 609 | { 610 | return ecx_FPWR(&ecx_port, ADP, ADO, length, data, timeout); 611 | } 612 | 613 | int ec_FPWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout) 614 | { 615 | return ec_FPWR(ADP, ADO, sizeof(data), &data, timeout); 616 | } 617 | 618 | int ec_LRW(uint32 LogAdr, uint16 length, void *data, int timeout) 619 | { 620 | return ecx_LRW(&ecx_port, LogAdr, length, data, timeout); 621 | } 622 | 623 | int ec_LRD(uint32 LogAdr, uint16 length, void *data, int timeout) 624 | { 625 | return ecx_LRD(&ecx_port, LogAdr, length, data, timeout); 626 | } 627 | 628 | int ec_LWR(uint32 LogAdr, uint16 length, void *data, int timeout) 629 | { 630 | return ecx_LWR(&ecx_port, LogAdr, length, data, timeout); 631 | } 632 | 633 | int ec_LRWDC(uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout) 634 | { 635 | return ecx_LRWDC(&ecx_port, LogAdr, length, data, DCrs, DCtime, timeout); 636 | } 637 | #endif 638 | -------------------------------------------------------------------------------- /src/soem/ethercatbase.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * Headerfile for ethercatbase.c 9 | */ 10 | 11 | #ifndef _ethercatbase_ 12 | #define _ethercatbase_ 13 | 14 | #include "oshw/nicdrv.h" 15 | 16 | #ifdef __cplusplus 17 | extern "C" 18 | { 19 | #endif 20 | 21 | int ecx_setupdatagram(ecx_portt *port, void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data); 22 | int ecx_adddatagram(ecx_portt *port, void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data); 23 | int ecx_BWR(ecx_portt *port, uint16 ADP,uint16 ADO,uint16 length,void *data,int timeout); 24 | int ecx_BRD(ecx_portt *port, uint16 ADP,uint16 ADO,uint16 length,void *data,int timeout); 25 | int ecx_APRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout); 26 | int ecx_ARMW(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout); 27 | int ecx_FRMW(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout); 28 | uint16 ecx_APRDw(ecx_portt *port, uint16 ADP, uint16 ADO, int timeout); 29 | int ecx_FPRD(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout); 30 | uint16 ecx_FPRDw(ecx_portt *port, uint16 ADP, uint16 ADO, int timeout); 31 | int ecx_APWRw(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 data, int timeout); 32 | int ecx_APWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout); 33 | int ecx_FPWRw(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 data, int timeout); 34 | int ecx_FPWR(ecx_portt *port, uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout); 35 | int ecx_LRW(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout); 36 | int ecx_LRD(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout); 37 | int ecx_LWR(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, int timeout); 38 | int ecx_LRWDC(ecx_portt *port, uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout); 39 | 40 | #ifdef EC_VER1 41 | int ec_setupdatagram(void *frame, uint8 com, uint8 idx, uint16 ADP, uint16 ADO, uint16 length, void *data); 42 | int ec_adddatagram(void *frame, uint8 com, uint8 idx, boolean more, uint16 ADP, uint16 ADO, uint16 length, void *data); 43 | int ec_BWR(uint16 ADP,uint16 ADO,uint16 length,void *data,int timeout); 44 | int ec_BRD(uint16 ADP,uint16 ADO,uint16 length,void *data,int timeout); 45 | int ec_APRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout); 46 | int ec_ARMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout); 47 | int ec_FRMW(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout); 48 | uint16 ec_APRDw(uint16 ADP, uint16 ADO, int timeout); 49 | int ec_FPRD(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout); 50 | uint16 ec_FPRDw(uint16 ADP, uint16 ADO, int timeout); 51 | int ec_APWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout); 52 | int ec_APWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout); 53 | int ec_FPWRw(uint16 ADP, uint16 ADO, uint16 data, int timeout); 54 | int ec_FPWR(uint16 ADP, uint16 ADO, uint16 length, void *data, int timeout); 55 | int ec_LRW(uint32 LogAdr, uint16 length, void *data, int timeout); 56 | int ec_LRD(uint32 LogAdr, uint16 length, void *data, int timeout); 57 | int ec_LWR(uint32 LogAdr, uint16 length, void *data, int timeout); 58 | int ec_LRWDC(uint32 LogAdr, uint16 length, void *data, uint16 DCrs, int64 *DCtime, int timeout); 59 | #endif 60 | 61 | #ifdef __cplusplus 62 | } 63 | #endif 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /src/soem/ethercatcoe.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * Headerfile for ethercatcoe.c 9 | */ 10 | 11 | #ifndef _ethercatcoe_ 12 | #define _ethercatcoe_ 13 | 14 | #ifdef __cplusplus 15 | extern "C" 16 | { 17 | #endif 18 | 19 | /** max entries in Object Description list */ 20 | #define EC_MAXODLIST 64 // TODO shrinked for memory reduction (original = 1024) 21 | 22 | /** max entries in Object Entry list */ 23 | #define EC_MAXOELIST 64 // TODO shrinked for memory reduction (original = 256) 24 | 25 | /* Storage for object description list */ 26 | typedef struct 27 | { 28 | /** slave number */ 29 | uint16 Slave; 30 | /** number of entries in list */ 31 | uint16 Entries; 32 | /** array of indexes */ 33 | uint16 Index[EC_MAXODLIST]; 34 | /** array of datatypes, see EtherCAT specification */ 35 | uint16 DataType[EC_MAXODLIST]; 36 | /** array of object codes, see EtherCAT specification */ 37 | uint8 ObjectCode[EC_MAXODLIST]; 38 | /** number of subindexes for each index */ 39 | uint8 MaxSub[EC_MAXODLIST]; 40 | /** textual description of each index */ 41 | char Name[EC_MAXODLIST][EC_MAXNAME+1]; 42 | } ec_ODlistt; 43 | 44 | /* storage for object list entry information */ 45 | typedef struct 46 | { 47 | /** number of entries in list */ 48 | uint16 Entries; 49 | /** array of value infos, see EtherCAT specification */ 50 | uint8 ValueInfo[EC_MAXOELIST]; 51 | /** array of value infos, see EtherCAT specification */ 52 | uint16 DataType[EC_MAXOELIST]; 53 | /** array of bit lengths, see EtherCAT specification */ 54 | uint16 BitLength[EC_MAXOELIST]; 55 | /** array of object access bits, see EtherCAT specification */ 56 | uint16 ObjAccess[EC_MAXOELIST]; 57 | /** textual description of each index */ 58 | char Name[EC_MAXOELIST][EC_MAXNAME+1]; 59 | } ec_OElistt; 60 | 61 | #ifdef EC_VER1 62 | void ec_SDOerror(uint16 Slave, uint16 Index, uint8 SubIdx, int32 AbortCode); 63 | int ec_SDOread(uint16 slave, uint16 index, uint8 subindex, 64 | boolean CA, int *psize, void *p, int timeout); 65 | int ec_SDOwrite(uint16 Slave, uint16 Index, uint8 SubIndex, 66 | boolean CA, int psize, void *p, int Timeout); 67 | int ec_RxPDO(uint16 Slave, uint16 RxPDOnumber , int psize, void *p); 68 | int ec_TxPDO(uint16 slave, uint16 TxPDOnumber , int *psize, void *p, int timeout); 69 | int ec_readPDOmap(uint16 Slave, int *Osize, int *Isize); 70 | int ec_readPDOmapCA(uint16 Slave, int Thread_n, int *Osize, int *Isize); 71 | int ec_readODlist(uint16 Slave, ec_ODlistt *pODlist); 72 | int ec_readODdescription(uint16 Item, ec_ODlistt *pODlist); 73 | int ec_readOEsingle(uint16 Item, uint8 SubI, ec_ODlistt *pODlist, ec_OElistt *pOElist); 74 | int ec_readOE(uint16 Item, ec_ODlistt *pODlist, ec_OElistt *pOElist); 75 | #endif 76 | 77 | void ecx_SDOerror(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIdx, int32 AbortCode); 78 | int ecx_SDOread(ecx_contextt *context, uint16 slave, uint16 index, uint8 subindex, 79 | boolean CA, int *psize, void *p, int timeout); 80 | int ecx_SDOwrite(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIndex, 81 | boolean CA, int psize, void *p, int Timeout); 82 | int ecx_RxPDO(ecx_contextt *context, uint16 Slave, uint16 RxPDOnumber , int psize, void *p); 83 | int ecx_TxPDO(ecx_contextt *context, uint16 slave, uint16 TxPDOnumber , int *psize, void *p, int timeout); 84 | int ecx_readPDOmap(ecx_contextt *context, uint16 Slave, int *Osize, int *Isize); 85 | int ecx_readPDOmapCA(ecx_contextt *context, uint16 Slave, int Thread_n, int *Osize, int *Isize); 86 | int ecx_readODlist(ecx_contextt *context, uint16 Slave, ec_ODlistt *pODlist); 87 | int ecx_readODdescription(ecx_contextt *context, uint16 Item, ec_ODlistt *pODlist); 88 | int ecx_readOEsingle(ecx_contextt *context, uint16 Item, uint8 SubI, ec_ODlistt *pODlist, ec_OElistt *pOElist); 89 | int ecx_readOE(ecx_contextt *context, uint16 Item, ec_ODlistt *pODlist, ec_OElistt *pOElist); 90 | 91 | #ifdef __cplusplus 92 | } 93 | #endif 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /src/soem/ethercatconfig.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * Headerfile for ethercatconfig.c 9 | */ 10 | 11 | #ifndef _ethercatconfig_ 12 | #define _ethercatconfig_ 13 | 14 | #ifdef __cplusplus 15 | extern "C" 16 | { 17 | #endif 18 | 19 | #define EC_NODEOFFSET 0x1000 20 | #define EC_TEMPNODE 0xffff 21 | 22 | #ifdef EC_VER1 23 | int ec_config_init(uint8 usetable); 24 | int ec_config_map(void *pIOmap); 25 | int ec_config_overlap_map(void *pIOmap); 26 | int ec_config_map_group(void *pIOmap, uint8 group); 27 | int ec_config_overlap_map_group(void *pIOmap, uint8 group); 28 | int ec_config(uint8 usetable, void *pIOmap); 29 | int ec_config_overlap(uint8 usetable, void *pIOmap); 30 | int ec_recover_slave(uint16 slave, int timeout); 31 | int ec_reconfig_slave(uint16 slave, int timeout); 32 | #endif 33 | 34 | int ecx_config_init(ecx_contextt *context, uint8 usetable); 35 | int ecx_config_map_group(ecx_contextt *context, void *pIOmap, uint8 group); 36 | int ecx_config_overlap_map_group(ecx_contextt *context, void *pIOmap, uint8 group); 37 | int ecx_recover_slave(ecx_contextt *context, uint16 slave, int timeout); 38 | int ecx_reconfig_slave(ecx_contextt *context, uint16 slave, int timeout); 39 | 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/soem/ethercatconfiglist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * DEPRECATED Configuration list of known EtherCAT slave devices. 9 | * 10 | * If a slave is found in this list it is configured according to the parameters 11 | * in the list. Otherwise the configuration info is read directly from the slave 12 | * EEPROM (SII or Slave Information Interface). 13 | */ 14 | 15 | #ifndef _ethercatconfiglist_ 16 | #define _ethercatconfiglist_ 17 | 18 | #ifdef __cplusplus 19 | extern "C" 20 | { 21 | #endif 22 | 23 | /* 24 | explanation of dev: 25 | 1: static device with no IO mapping ie EK1100 26 | 2: input device no mailbox ie simple IO device 27 | 3: output device no mailbox 28 | 4: input device with mailbox configuration 29 | 5: output device with mailbox configuration 30 | 6: input/output device no mailbox 31 | 7: input.output device with mailbox configuration 32 | */ 33 | #define EC_CONFIGEND 0xffffffff 34 | 35 | ec_configlist_t ec_configlist[] = { 36 | {/*Man=*/0x00000000,/*ID=*/0x00000000,/*Name=*/"" ,/*dtype=*/0,/*Ibits=*/ 0,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0}, 37 | {/*Man=*/0x00000002,/*ID=*/0x044c2c52,/*Name=*/"EK1100" ,/*dtype=*/1,/*Ibits=*/ 0,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0}, 38 | {/*Man=*/0x00000002,/*ID=*/0x03ea3052,/*Name=*/"EL1002" ,/*dtype=*/2,/*Ibits=*/ 2,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0}, 39 | {/*Man=*/0x00000002,/*ID=*/0x03ec3052,/*Name=*/"EL1004" ,/*dtype=*/2,/*Ibits=*/ 4,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0}, 40 | {/*Man=*/0x00000002,/*ID=*/0x03f43052,/*Name=*/"EL1012" ,/*dtype=*/2,/*Ibits=*/ 2,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0}, 41 | {/*Man=*/0x00000002,/*ID=*/0x03f63052,/*Name=*/"EL1014" ,/*dtype=*/2,/*Ibits=*/ 4,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0}, 42 | {/*Man=*/0x00000002,/*ID=*/0x03fa3052,/*Name=*/"EL1018" ,/*dtype=*/2,/*Ibits=*/ 8,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0}, 43 | {/*Man=*/0x00000002,/*ID=*/0x07d23052,/*Name=*/"EL2002" ,/*dtype=*/3,/*Ibits=*/ 0,/*Obits=*/ 2,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0}, 44 | {/*Man=*/0x00000002,/*ID=*/0x07d43052,/*Name=*/"EL2004" ,/*dtype=*/3,/*Ibits=*/ 0,/*Obits=*/ 4,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0}, 45 | {/*Man=*/0x00000002,/*ID=*/0x07d83052,/*Name=*/"EL2008" ,/*dtype=*/3,/*Ibits=*/ 0,/*Obits=*/ 8,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0}, 46 | {/*Man=*/0x00000002,/*ID=*/0x07f03052,/*Name=*/"EL2032" ,/*dtype=*/6,/*Ibits=*/ 2,/*Obits=*/ 2,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0}, 47 | {/*Man=*/0x00000002,/*ID=*/0x0c1e3052,/*Name=*/"EL3102" ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1}, 48 | {/*Man=*/0x00000002,/*ID=*/0x0c283052,/*Name=*/"EL3112" ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1}, 49 | {/*Man=*/0x00000002,/*ID=*/0x0c323052,/*Name=*/"EL3122" ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1}, 50 | {/*Man=*/0x00000002,/*ID=*/0x0c463052,/*Name=*/"EL3142" ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1}, 51 | {/*Man=*/0x00000002,/*ID=*/0x0c503052,/*Name=*/"EL3152" ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1}, 52 | {/*Man=*/0x00000002,/*ID=*/0x0c5a3052,/*Name=*/"EL3162" ,/*dtype=*/4,/*Ibits=*/48,/*Obits=*/ 0,/*SM2a*/0x1000,/*SM2f*/0x00000024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/0,/*FM1ac*/1}, 53 | {/*Man=*/0x00000002,/*ID=*/0x0fc03052,/*Name=*/"EL4032" ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1100,/*SM2f*/0x00010024,/*SM3a*/0x1180,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0}, 54 | {/*Man=*/0x00000002,/*ID=*/0x10063052,/*Name=*/"EL4102" ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0}, 55 | {/*Man=*/0x00000002,/*ID=*/0x10103052,/*Name=*/"EL4112" ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0}, 56 | {/*Man=*/0x00000002,/*ID=*/0x101a3052,/*Name=*/"EL4122" ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0}, 57 | {/*Man=*/0x00000002,/*ID=*/0x10243052,/*Name=*/"EL4132" ,/*dtype=*/5,/*Ibits=*/ 0,/*Obits=*/32,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00000022,/*FM0ac*/1,/*FM1ac*/0}, 58 | {/*Man=*/0x00000002,/*ID=*/0x13ed3052,/*Name=*/"EL5101" ,/*dtype=*/7,/*Ibits=*/40,/*Obits=*/24,/*SM2a*/0x1000,/*SM2f*/0x00010024,/*SM3a*/0x1100,/*SM3f*/0x00010020,/*FM0ac*/1,/*FM1ac*/1}, 59 | {/*Man=*/EC_CONFIGEND,/*ID=*/0x00000000,/*Name=*/"" ,/*dtype=*/0,/*Ibits=*/ 0,/*Obits=*/ 0,/*SM2a*/ 0,/*SM2f*/ 0,/*SM3a*/ 0,/*SM3f*/ 0,/*FM0ac*/0,/*FM1ac*/0} 60 | }; 61 | 62 | #ifdef __cplusplus 63 | } 64 | #endif 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /src/soem/ethercatdc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * Distributed Clock EtherCAT functions. 9 | * 10 | */ 11 | #include "oshw/oshw.h" 12 | #include "osal/osal.h" 13 | #include "ethercattype.h" 14 | #include "ethercatbase.h" 15 | #include "ethercatmain.h" 16 | #include "ethercatdc.h" 17 | 18 | #define PORTM0 0x01 19 | #define PORTM1 0x02 20 | #define PORTM2 0x04 21 | #define PORTM3 0x08 22 | 23 | /** 1st sync pulse delay in ns here 100ms */ 24 | #define SyncDelay ((int32)100000000) 25 | 26 | /** 27 | * Set DC of slave to fire sync0 at CyclTime interval with CyclShift offset. 28 | * 29 | * @param[in] context = context struct 30 | * @param [in] slave Slave number. 31 | * @param [in] act TRUE = active, FALSE = deactivated 32 | * @param [in] CyclTime Cycltime in ns. 33 | * @param [in] CyclShift CyclShift in ns. 34 | */ 35 | void ecx_dcsync0(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift) 36 | { 37 | uint8 h, RA; 38 | uint16 slaveh; 39 | int64 t, t1; 40 | int32 tc; 41 | 42 | slaveh = context->slavelist[slave].configadr; 43 | RA = 0; 44 | 45 | /* stop cyclic operation, ready for next trigger */ 46 | (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET); 47 | if (act) 48 | { 49 | RA = 1 + 2; /* act cyclic operation and sync0, sync1 deactivated */ 50 | } 51 | h = 0; 52 | (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCUC, sizeof(h), &h, EC_TIMEOUTRET); /* write access to ethercat */ 53 | t1 = 0; 54 | (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCSYSTIME, sizeof(t1), &t1, EC_TIMEOUTRET); /* read local time of slave */ 55 | t1 = etohll(t1); 56 | 57 | /* Calculate first trigger time, always a whole multiple of CyclTime rounded up 58 | plus the shifttime (can be negative) 59 | This insures best synchronization between slaves, slaves with the same CyclTime 60 | will sync at the same moment (you can use CyclShift to shift the sync) */ 61 | if (CyclTime > 0) 62 | { 63 | t = ((t1 + SyncDelay) / CyclTime) * CyclTime + CyclTime + CyclShift; 64 | } 65 | else 66 | { 67 | t = t1 + SyncDelay + CyclShift; 68 | /* first trigger at T1 + CyclTime + SyncDelay + CyclShift in ns */ 69 | } 70 | t = htoell(t); 71 | (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSTART0, sizeof(t), &t, EC_TIMEOUTRET); /* SYNC0 start time */ 72 | tc = htoel(CyclTime); 73 | (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE0, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC0 cycle time */ 74 | (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET); /* activate cyclic operation */ 75 | 76 | // update ec_slave state 77 | context->slavelist[slave].DCactive = (uint8)act; 78 | context->slavelist[slave].DCshift = CyclShift; 79 | context->slavelist[slave].DCcycle = CyclTime; 80 | } 81 | 82 | /** 83 | * Set DC of slave to fire sync0 and sync1 at CyclTime interval with CyclShift offset. 84 | * 85 | * @param[in] context = context struct 86 | * @param [in] slave Slave number. 87 | * @param [in] act TRUE = active, FALSE = deactivated 88 | * @param [in] CyclTime0 Cycltime SYNC0 in ns. 89 | * @param [in] CyclTime1 Cycltime SYNC1 in ns. This time is a delta time in relation to 90 | the SYNC0 fire. If CylcTime1 = 0 then SYNC1 fires a the same time 91 | as SYNC0. 92 | * @param [in] CyclShift CyclShift in ns. 93 | */ 94 | void ecx_dcsync01(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift) 95 | { 96 | uint8 h, RA; 97 | uint16 slaveh; 98 | int64 t, t1; 99 | int32 tc; 100 | uint32 TrueCyclTime; 101 | 102 | /* Sync1 can be used as a multiple of Sync0, use true cycle time */ 103 | TrueCyclTime = ((CyclTime1 / CyclTime0) + 1) * CyclTime0; 104 | 105 | slaveh = context->slavelist[slave].configadr; 106 | RA = 0; 107 | 108 | /* stop cyclic operation, ready for next trigger */ 109 | (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET); 110 | if (act) 111 | { 112 | RA = 1 + 2 + 4; /* act cyclic operation and sync0 + sync1 */ 113 | } 114 | h = 0; 115 | (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCUC, sizeof(h), &h, EC_TIMEOUTRET); /* write access to ethercat */ 116 | t1 = 0; 117 | (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCSYSTIME, sizeof(t1), &t1, EC_TIMEOUTRET); /* read local time of slave */ 118 | t1 = etohll(t1); 119 | 120 | /* Calculate first trigger time, always a whole multiple of TrueCyclTime rounded up 121 | plus the shifttime (can be negative) 122 | This insures best synchronization between slaves, slaves with the same CyclTime 123 | will sync at the same moment (you can use CyclShift to shift the sync) */ 124 | if (CyclTime0 > 0) 125 | { 126 | t = ((t1 + SyncDelay) / TrueCyclTime) * TrueCyclTime + TrueCyclTime + CyclShift; 127 | } 128 | else 129 | { 130 | t = t1 + SyncDelay + CyclShift; 131 | /* first trigger at T1 + CyclTime + SyncDelay + CyclShift in ns */ 132 | } 133 | t = htoell(t); 134 | (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSTART0, sizeof(t), &t, EC_TIMEOUTRET); /* SYNC0 start time */ 135 | tc = htoel(CyclTime0); 136 | (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE0, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC0 cycle time */ 137 | tc = htoel(CyclTime1); 138 | (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCCYCLE1, sizeof(tc), &tc, EC_TIMEOUTRET); /* SYNC1 cycle time */ 139 | (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYNCACT, sizeof(RA), &RA, EC_TIMEOUTRET); /* activate cyclic operation */ 140 | 141 | // update ec_slave state 142 | context->slavelist[slave].DCactive = (uint8)act; 143 | context->slavelist[slave].DCshift = CyclShift; 144 | context->slavelist[slave].DCcycle = CyclTime0; 145 | } 146 | 147 | /* latched port time of slave */ 148 | static int32 ecx_porttime(ecx_contextt *context, uint16 slave, uint8 port) 149 | { 150 | int32 ts; 151 | switch (port) 152 | { 153 | case 0: 154 | ts = context->slavelist[slave].DCrtA; 155 | break; 156 | case 1: 157 | ts = context->slavelist[slave].DCrtB; 158 | break; 159 | case 2: 160 | ts = context->slavelist[slave].DCrtC; 161 | break; 162 | case 3: 163 | ts = context->slavelist[slave].DCrtD; 164 | break; 165 | default: 166 | ts = 0; 167 | break; 168 | } 169 | return ts; 170 | } 171 | 172 | /* calculate previous active port of a slave */ 173 | static uint8 ecx_prevport(ecx_contextt *context, uint16 slave, uint8 port) 174 | { 175 | uint8 pport = port; 176 | uint8 aport = context->slavelist[slave].activeports; 177 | switch(port) 178 | { 179 | case 0: 180 | if(aport & PORTM2) 181 | pport = 2; 182 | else if (aport & PORTM1) 183 | pport = 1; 184 | else if (aport & PORTM3) 185 | pport = 3; 186 | break; 187 | case 1: 188 | if(aport & PORTM3) 189 | pport = 3; 190 | else if (aport & PORTM0) 191 | pport = 0; 192 | else if (aport & PORTM2) 193 | pport = 2; 194 | break; 195 | case 2: 196 | if(aport & PORTM1) 197 | pport = 1; 198 | else if (aport & PORTM3) 199 | pport = 3; 200 | else if (aport & PORTM0) 201 | pport = 0; 202 | break; 203 | case 3: 204 | if(aport & PORTM0) 205 | pport = 0; 206 | else if (aport & PORTM2) 207 | pport = 2; 208 | else if (aport & PORTM1) 209 | pport = 1; 210 | break; 211 | } 212 | return pport; 213 | } 214 | 215 | /* search unconsumed ports in parent, consume and return first open port */ 216 | static uint8 ecx_parentport(ecx_contextt *context, uint16 parent) 217 | { 218 | uint8 parentport = 0; 219 | uint8 b; 220 | /* search order is important, here 3 - 1 - 2 - 0 */ 221 | b = context->slavelist[parent].consumedports; 222 | if (b & PORTM3) 223 | { 224 | parentport = 3; 225 | b &= (uint8)~PORTM3; 226 | } 227 | else if (b & PORTM1) 228 | { 229 | parentport = 1; 230 | b &= (uint8)~PORTM1; 231 | } 232 | else if (b & PORTM2) 233 | { 234 | parentport = 2; 235 | b &= (uint8)~PORTM2; 236 | } 237 | else if (b & PORTM0) 238 | { 239 | parentport = 0; 240 | b &= (uint8)~PORTM0; 241 | } 242 | context->slavelist[parent].consumedports = b; 243 | return parentport; 244 | } 245 | 246 | /** 247 | * Locate DC slaves, measure propagation delays. 248 | * 249 | * @param[in] context = context struct 250 | * @return boolean if slaves are found with DC 251 | */ 252 | boolean ecx_configdc(ecx_contextt *context) 253 | { 254 | uint16 i, slaveh, parent, child; 255 | uint16 parenthold = 0; 256 | uint16 prevDCslave = 0; 257 | int32 ht, dt1, dt2, dt3; 258 | int64 hrt; 259 | uint8 entryport; 260 | int8 nlist; 261 | int8 plist[4]; 262 | int32 tlist[4]; 263 | // ec_timet mastertime; 264 | uint64 mastertime64; 265 | 266 | context->slavelist[0].hasdc = FALSE; 267 | context->grouplist[0].hasdc = FALSE; 268 | ht = 0; 269 | 270 | ecx_BWR(context->port, 0, ECT_REG_DCTIME0, sizeof(ht), &ht, EC_TIMEOUTRET); /* latch DCrecvTimeA of all slaves */ 271 | 272 | // TODO only use usec counter instead of real time clock 273 | #if 0 274 | mastertime = osal_current_time(); 275 | mastertime.sec -= 946684800UL; /* EtherCAT uses 2000-01-01 as epoch start instead of 1970-01-01 */ 276 | mastertime64 = (((uint64)mastertime.sec * 1000000) + (uint64)mastertime.usec) * 1000; 277 | #else 278 | mastertime64 = osal_current_time(); 279 | #endif 280 | for (i = 1; i <= *(context->slavecount); i++) 281 | { 282 | context->slavelist[i].consumedports = context->slavelist[i].activeports; 283 | if (context->slavelist[i].hasdc) 284 | { 285 | if (!context->slavelist[0].hasdc) 286 | { 287 | context->slavelist[0].hasdc = TRUE; 288 | context->slavelist[0].DCnext = i; 289 | context->slavelist[i].DCprevious = 0; 290 | context->grouplist[context->slavelist[i].group].hasdc = TRUE; 291 | context->grouplist[context->slavelist[i].group].DCnext = i; 292 | } 293 | else 294 | { 295 | context->slavelist[prevDCslave].DCnext = i; 296 | context->slavelist[i].DCprevious = prevDCslave; 297 | } 298 | /* this branch has DC slave so remove parenthold */ 299 | parenthold = 0; 300 | prevDCslave = i; 301 | slaveh = context->slavelist[i].configadr; 302 | (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME0, sizeof(ht), &ht, EC_TIMEOUTRET); 303 | context->slavelist[i].DCrtA = etohl(ht); 304 | /* 64bit latched DCrecvTimeA of each specific slave */ 305 | (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCSOF, sizeof(hrt), &hrt, EC_TIMEOUTRET); 306 | /* use it as offset in order to set local time around 0 + mastertime */ 307 | hrt = htoell(-etohll(hrt) + mastertime64); 308 | /* save it in the offset register */ 309 | (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYSOFFSET, sizeof(hrt), &hrt, EC_TIMEOUTRET); 310 | (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME1, sizeof(ht), &ht, EC_TIMEOUTRET); 311 | context->slavelist[i].DCrtB = etohl(ht); 312 | (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME2, sizeof(ht), &ht, EC_TIMEOUTRET); 313 | context->slavelist[i].DCrtC = etohl(ht); 314 | (void)ecx_FPRD(context->port, slaveh, ECT_REG_DCTIME3, sizeof(ht), &ht, EC_TIMEOUTRET); 315 | context->slavelist[i].DCrtD = etohl(ht); 316 | 317 | /* make list of active ports and their time stamps */ 318 | nlist = 0; 319 | if (context->slavelist[i].activeports & PORTM0) 320 | { 321 | plist[nlist] = 0; 322 | tlist[nlist] = context->slavelist[i].DCrtA; 323 | nlist++; 324 | } 325 | if (context->slavelist[i].activeports & PORTM3) 326 | { 327 | plist[nlist] = 3; 328 | tlist[nlist] = context->slavelist[i].DCrtD; 329 | nlist++; 330 | } 331 | if (context->slavelist[i].activeports & PORTM1) 332 | { 333 | plist[nlist] = 1; 334 | tlist[nlist] = context->slavelist[i].DCrtB; 335 | nlist++; 336 | } 337 | if (context->slavelist[i].activeports & PORTM2) 338 | { 339 | plist[nlist] = 2; 340 | tlist[nlist] = context->slavelist[i].DCrtC; 341 | nlist++; 342 | } 343 | /* entryport is port with the lowest timestamp */ 344 | entryport = 0; 345 | if((nlist > 1) && (tlist[1] < tlist[entryport])) 346 | { 347 | entryport = 1; 348 | } 349 | if((nlist > 2) && (tlist[2] < tlist[entryport])) 350 | { 351 | entryport = 2; 352 | } 353 | if((nlist > 3) && (tlist[3] < tlist[entryport])) 354 | { 355 | entryport = 3; 356 | } 357 | entryport = plist[entryport]; 358 | context->slavelist[i].entryport = entryport; 359 | /* consume entryport from activeports */ 360 | context->slavelist[i].consumedports &= (uint8)~(1 << entryport); 361 | 362 | /* finding DC parent of current */ 363 | parent = i; 364 | do 365 | { 366 | child = parent; 367 | parent = context->slavelist[parent].parent; 368 | } 369 | while (!((parent == 0) || (context->slavelist[parent].hasdc))); 370 | /* only calculate propagation delay if slave is not the first */ 371 | if (parent > 0) 372 | { 373 | /* find port on parent this slave is connected to */ 374 | context->slavelist[i].parentport = ecx_parentport(context, parent); 375 | if (context->slavelist[parent].topology == 1) 376 | { 377 | context->slavelist[i].parentport = context->slavelist[parent].entryport; 378 | } 379 | 380 | dt1 = 0; 381 | dt2 = 0; 382 | /* delta time of (parentport - 1) - parentport */ 383 | /* note: order of ports is 0 - 3 - 1 -2 */ 384 | /* non active ports are skipped */ 385 | dt3 = ecx_porttime(context, parent, context->slavelist[i].parentport) - 386 | ecx_porttime(context, parent, 387 | ecx_prevport(context, parent, context->slavelist[i].parentport)); 388 | /* current slave has children */ 389 | /* those children's delays need to be subtracted */ 390 | if (context->slavelist[i].topology > 1) 391 | { 392 | dt1 = ecx_porttime(context, i, 393 | ecx_prevport(context, i, context->slavelist[i].entryport)) - 394 | ecx_porttime(context, i, context->slavelist[i].entryport); 395 | } 396 | /* we are only interested in positive difference */ 397 | if (dt1 > dt3) dt1 = -dt1; 398 | /* current slave is not the first child of parent */ 399 | /* previous child's delays need to be added */ 400 | if ((child - parent) > 1) 401 | { 402 | dt2 = ecx_porttime(context, parent, 403 | ecx_prevport(context, parent, context->slavelist[i].parentport)) - 404 | ecx_porttime(context, parent, context->slavelist[parent].entryport); 405 | } 406 | if (dt2 < 0) dt2 = -dt2; 407 | 408 | /* calculate current slave delay from delta times */ 409 | /* assumption : forward delay equals return delay */ 410 | context->slavelist[i].pdelay = ((dt3 - dt1) / 2) + dt2 + 411 | context->slavelist[parent].pdelay; 412 | ht = htoel(context->slavelist[i].pdelay); 413 | /* write propagation delay*/ 414 | (void)ecx_FPWR(context->port, slaveh, ECT_REG_DCSYSDELAY, sizeof(ht), &ht, EC_TIMEOUTRET); 415 | } 416 | } 417 | else 418 | { 419 | context->slavelist[i].DCrtA = 0; 420 | context->slavelist[i].DCrtB = 0; 421 | context->slavelist[i].DCrtC = 0; 422 | context->slavelist[i].DCrtD = 0; 423 | parent = context->slavelist[i].parent; 424 | /* if non DC slave found on first position on branch hold root parent */ 425 | if ( (parent > 0) && (context->slavelist[parent].topology > 2)) 426 | parenthold = parent; 427 | /* if branch has no DC slaves consume port on root parent */ 428 | if ( parenthold && (context->slavelist[i].topology == 1)) 429 | { 430 | ecx_parentport(context, parenthold); 431 | parenthold = 0; 432 | } 433 | } 434 | } 435 | 436 | return context->slavelist[0].hasdc; 437 | } 438 | 439 | #ifdef EC_VER1 440 | void ec_dcsync0(uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift) 441 | { 442 | ecx_dcsync0(&ecx_context, slave, act, CyclTime, CyclShift); 443 | } 444 | 445 | void ec_dcsync01(uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift) 446 | { 447 | ecx_dcsync01(&ecx_context, slave, act, CyclTime0, CyclTime1, CyclShift); 448 | } 449 | 450 | boolean ec_configdc(void) 451 | { 452 | return ecx_configdc(&ecx_context); 453 | } 454 | #endif 455 | -------------------------------------------------------------------------------- /src/soem/ethercatdc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * Headerfile for ethercatdc.c 9 | */ 10 | 11 | #ifndef _EC_ECATDC_H 12 | #define _EC_ECATDC_H 13 | 14 | #ifdef __cplusplus 15 | extern "C" 16 | { 17 | #endif 18 | 19 | #ifdef EC_VER1 20 | boolean ec_configdc(); 21 | void ec_dcsync0(uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift); 22 | void ec_dcsync01(uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift); 23 | #endif 24 | 25 | boolean ecx_configdc(ecx_contextt *context); 26 | void ecx_dcsync0(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift); 27 | void ecx_dcsync01(ecx_contextt *context, uint16 slave, boolean act, uint32 CyclTime0, uint32 CyclTime1, int32 CyclShift); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | #endif /* _EC_ECATDC_H */ 34 | -------------------------------------------------------------------------------- /src/soem/ethercateoe.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * Ethernet over EtherCAT (EoE) module. 9 | * 10 | * Set / Get IP functions 11 | * Blocking send/receive Ethernet Frame 12 | * Read incoming EoE fragment to Ethernet Frame 13 | */ 14 | 15 | #include 16 | #include 17 | #include "osal/osal.h" 18 | #include "oshw/oshw.h" 19 | #include "ethercat.h" 20 | 21 | /** EoE utility function to convert uint32 to eoe ip bytes. 22 | * @param[in] ip = ip in uint32 23 | * @param[out] byte_ip = eoe ip 4th octet, 3ed octet, 2nd octet, 1st octet 24 | */ 25 | static void EOE_ip_uint32_to_byte(eoe_ip4_addr_t * ip, uint8_t * byte_ip) 26 | { 27 | byte_ip[3] = eoe_ip4_addr1(ip); /* 1st octet */ 28 | byte_ip[2] = eoe_ip4_addr2(ip); /* 2nd octet */ 29 | byte_ip[1] = eoe_ip4_addr3(ip); /* 3ed octet */ 30 | byte_ip[0] = eoe_ip4_addr4(ip); /* 4th octet */ 31 | } 32 | 33 | /** EoE utility function to convert eoe ip bytes to uint32. 34 | * @param[in] byte_ip = eoe ip 4th octet, 3ed octet, 2nd octet, 1st octet 35 | * @param[out] ip = ip in uint32 36 | */ 37 | static void EOE_ip_byte_to_uint32(uint8_t * byte_ip, eoe_ip4_addr_t * ip) 38 | { 39 | EOE_IP4_ADDR_TO_U32(ip, 40 | byte_ip[3], /* 1st octet */ 41 | byte_ip[2], /* 2nd octet */ 42 | byte_ip[1], /* 3ed octet */ 43 | byte_ip[0]); /* 4th octet */ 44 | } 45 | 46 | /** EoE fragment data handler hook. Should not block. 47 | * 48 | * @param[in] context = context struct 49 | * @param[in] hook = Pointer to hook function. 50 | * @return 1 51 | */ 52 | int ecx_EOEdefinehook(ecx_contextt *context, void *hook) 53 | { 54 | context->EOEhook = hook; 55 | return 1; 56 | } 57 | 58 | /** EoE EOE set IP, blocking. Waits for response from the slave. 59 | * 60 | * @param[in] context = Context struct 61 | * @param[in] slave = Slave number 62 | * @param[in] port = Port number on slave if applicable 63 | * @param[in] ipparam = IP parameter data to be sent 64 | * @param[in] Timeout = Timeout in us, standard is EC_TIMEOUTRXM 65 | * @return Workcounter from last slave response or returned result code 66 | */ 67 | int ecx_EOEsetIp(ecx_contextt *context, uint16 slave, uint8 port, eoe_param_t * ipparam, int timeout) 68 | { 69 | ec_EOEt *EOEp, *aEOEp; 70 | ec_mbxbuft MbxIn, MbxOut; 71 | uint16 frameinfo1, result; 72 | uint8 cnt, data_offset; 73 | uint8 flags = 0; 74 | int wkc; 75 | 76 | ec_clearmbx(&MbxIn); 77 | /* Empty slave out mailbox if something is in. Timout set to 0 */ 78 | wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0); 79 | ec_clearmbx(&MbxOut); 80 | aEOEp = (ec_EOEt *)&MbxIn; 81 | EOEp = (ec_EOEt *)&MbxOut; 82 | EOEp->mbxheader.address = htoes(0x0000); 83 | EOEp->mbxheader.priority = 0x00; 84 | data_offset = EOE_PARAM_OFFSET; 85 | 86 | /* get new mailbox count value, used as session handle */ 87 | cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt); 88 | context->slavelist[slave].mbx_cnt = cnt; 89 | 90 | EOEp->mbxheader.mbxtype = ECT_MBXT_EOE + (cnt << 4); /* EoE */ 91 | 92 | EOEp->frameinfo1 = htoes(EOE_HDR_FRAME_TYPE_SET(EOE_INIT_REQ) | 93 | EOE_HDR_FRAME_PORT_SET(port) | 94 | EOE_HDR_LAST_FRAGMENT); 95 | EOEp->frameinfo2 = 0; 96 | 97 | /* The EoE frame will include "empty" IP/DNS entries, makes wireshark happy. 98 | * Specification say they are optional, TwinCAT include empty entries. 99 | */ 100 | if (ipparam->mac_set) 101 | { 102 | flags |= EOE_PARAM_MAC_INCLUDE; 103 | memcpy(&EOEp->data[data_offset], ipparam->mac.addr, EOE_ETHADDR_LENGTH); 104 | } 105 | data_offset += EOE_ETHADDR_LENGTH; 106 | if (ipparam->ip_set) 107 | { 108 | flags |= EOE_PARAM_IP_INCLUDE; 109 | EOE_ip_uint32_to_byte(&ipparam->ip, &EOEp->data[data_offset]); 110 | } 111 | data_offset += 4; 112 | if (ipparam->subnet_set) 113 | { 114 | flags |= EOE_PARAM_SUBNET_IP_INCLUDE; 115 | EOE_ip_uint32_to_byte(&ipparam->subnet, &EOEp->data[data_offset]); 116 | } 117 | data_offset += 4; 118 | if (ipparam->default_gateway_set) 119 | { 120 | flags |= EOE_PARAM_DEFAULT_GATEWAY_INCLUDE; 121 | EOE_ip_uint32_to_byte(&ipparam->default_gateway, &EOEp->data[data_offset]); 122 | } 123 | data_offset += 4; 124 | if (ipparam->dns_ip_set) 125 | { 126 | flags |= EOE_PARAM_DNS_IP_INCLUDE; 127 | EOE_ip_uint32_to_byte(&ipparam->dns_ip, &EOEp->data[data_offset]); 128 | } 129 | data_offset += 4; 130 | if (ipparam->dns_name_set) 131 | { 132 | flags |= EOE_PARAM_DNS_NAME_INCLUDE; 133 | memcpy(&EOEp->data[data_offset], (void *)ipparam->dns_name, EOE_DNS_NAME_LENGTH); 134 | } 135 | data_offset += EOE_DNS_NAME_LENGTH; 136 | 137 | EOEp->mbxheader.length = htoes(EOE_PARAM_OFFSET + data_offset); 138 | EOEp->data[0] = flags; 139 | 140 | /* send EoE request to slave */ 141 | wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM); 142 | 143 | if (wkc > 0) /* succeeded to place mailbox in slave ? */ 144 | { 145 | /* clean mailboxbuffer */ 146 | ec_clearmbx(&MbxIn); 147 | /* read slave response */ 148 | wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout); 149 | if (wkc > 0) /* succeeded to read slave response ? */ 150 | { 151 | /* slave response should be FoE */ 152 | if ((aEOEp->mbxheader.mbxtype & 0x0f) == ECT_MBXT_EOE) 153 | { 154 | frameinfo1 = etohs(aEOEp->frameinfo1); 155 | result = etohs(aEOEp->result); 156 | if ((EOE_HDR_FRAME_TYPE_GET(frameinfo1) != EOE_INIT_RESP) || 157 | (result != EOE_RESULT_SUCCESS)) 158 | { 159 | wkc = -result; 160 | } 161 | } 162 | else 163 | { 164 | /* unexpected mailbox received */ 165 | wkc = -EC_ERR_TYPE_PACKET_ERROR; 166 | } 167 | } 168 | } 169 | return wkc; 170 | } 171 | 172 | /** EoE EOE get IP, blocking. Waits for response from the slave. 173 | * 174 | * @param[in] context = Context struct 175 | * @param[in] slave = Slave number 176 | * @param[in] port = Port number on slave if applicable 177 | * @param[out] ipparam = IP parameter data retrived from slave 178 | * @param[in] Timeout = Timeout in us, standard is EC_TIMEOUTRXM 179 | * @return Workcounter from last slave response or returned result code 180 | */ 181 | int ecx_EOEgetIp(ecx_contextt *context, uint16 slave, uint8 port, eoe_param_t * ipparam, int timeout) 182 | { 183 | ec_EOEt *EOEp, *aEOEp; 184 | ec_mbxbuft MbxIn, MbxOut; 185 | uint16 frameinfo1, eoedatasize; 186 | uint8 cnt, data_offset; 187 | uint8 flags = 0; 188 | int wkc; 189 | 190 | /* Empty slave out mailbox if something is in. Timout set to 0 */ 191 | wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0); 192 | ec_clearmbx(&MbxOut); 193 | aEOEp = (ec_EOEt *)&MbxIn; 194 | EOEp = (ec_EOEt *)&MbxOut; 195 | EOEp->mbxheader.address = htoes(0x0000); 196 | EOEp->mbxheader.priority = 0x00; 197 | data_offset = EOE_PARAM_OFFSET; 198 | 199 | /* get new mailbox count value, used as session handle */ 200 | cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt); 201 | context->slavelist[slave].mbx_cnt = cnt; 202 | 203 | EOEp->mbxheader.mbxtype = ECT_MBXT_EOE + (cnt << 4); /* EoE */ 204 | 205 | EOEp->frameinfo1 = htoes(EOE_HDR_FRAME_TYPE_SET(EOE_GET_IP_PARAM_REQ) | 206 | EOE_HDR_FRAME_PORT_SET(port) | 207 | EOE_HDR_LAST_FRAGMENT); 208 | EOEp->frameinfo2 = 0; 209 | 210 | EOEp->mbxheader.length = htoes(0x0004); 211 | EOEp->data[0] = flags; 212 | 213 | /* send EoE request to slave */ 214 | wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM); 215 | if (wkc > 0) /* succeeded to place mailbox in slave ? */ 216 | { 217 | /* clean mailboxbuffer */ 218 | ec_clearmbx(&MbxIn); 219 | /* read slave response */ 220 | wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout); 221 | if (wkc > 0) /* succeeded to read slave response ? */ 222 | { 223 | /* slave response should be FoE */ 224 | if ((aEOEp->mbxheader.mbxtype & 0x0f) == ECT_MBXT_EOE) 225 | { 226 | frameinfo1 = etohs(aEOEp->frameinfo1); 227 | eoedatasize = etohs(aEOEp->mbxheader.length) - 0x0004; 228 | if (EOE_HDR_FRAME_TYPE_GET(frameinfo1) != EOE_GET_IP_PARAM_RESP) 229 | { 230 | wkc = -EOE_RESULT_UNSUPPORTED_FRAME_TYPE; 231 | } 232 | else 233 | { 234 | /* The EoE frame will include "empty" IP/DNS entries, makes 235 | * wireshark happy. Specification say they are optional, TwinCAT 236 | * include empty entries. 237 | */ 238 | flags = aEOEp->data[0]; 239 | if (flags & EOE_PARAM_MAC_INCLUDE) 240 | { 241 | memcpy(ipparam->mac.addr, 242 | &aEOEp->data[data_offset], 243 | EOE_ETHADDR_LENGTH); 244 | ipparam->mac_set = 1; 245 | } 246 | data_offset += EOE_ETHADDR_LENGTH; 247 | if (flags & EOE_PARAM_IP_INCLUDE) 248 | { 249 | EOE_ip_byte_to_uint32(&aEOEp->data[data_offset], 250 | &ipparam->ip); 251 | ipparam->ip_set = 1; 252 | } 253 | data_offset += 4; 254 | if (flags & EOE_PARAM_SUBNET_IP_INCLUDE) 255 | { 256 | EOE_ip_byte_to_uint32(&aEOEp->data[data_offset], 257 | &ipparam->subnet); 258 | ipparam->subnet_set = 1; 259 | } 260 | data_offset += 4; 261 | if (flags & EOE_PARAM_DEFAULT_GATEWAY_INCLUDE) 262 | { 263 | EOE_ip_byte_to_uint32(&aEOEp->data[data_offset], 264 | &ipparam->default_gateway); 265 | ipparam->default_gateway_set = 1; 266 | } 267 | data_offset += 4; 268 | if (flags & EOE_PARAM_DNS_IP_INCLUDE) 269 | { 270 | EOE_ip_byte_to_uint32(&aEOEp->data[data_offset], 271 | &ipparam->dns_ip); 272 | ipparam->dns_ip_set = 1; 273 | } 274 | data_offset += 4; 275 | if (flags & EOE_PARAM_DNS_NAME_INCLUDE) 276 | { 277 | uint16_t dns_len; 278 | if ((eoedatasize - data_offset) < EOE_DNS_NAME_LENGTH) 279 | { 280 | dns_len = (eoedatasize - data_offset); 281 | } 282 | else 283 | { 284 | dns_len = EOE_DNS_NAME_LENGTH; 285 | } 286 | /* Assume ZERO terminated string */ 287 | memcpy(ipparam->dns_name, &aEOEp->data[data_offset], dns_len); 288 | ipparam->dns_name_set = 1; 289 | } 290 | data_offset += EOE_DNS_NAME_LENGTH; 291 | /* Something os not correct, flag the error */ 292 | if(data_offset > eoedatasize) 293 | { 294 | wkc = -EC_ERR_TYPE_MBX_ERROR; 295 | } 296 | } 297 | } 298 | else 299 | { 300 | /* unexpected mailbox received */ 301 | wkc = -EC_ERR_TYPE_PACKET_ERROR; 302 | } 303 | } 304 | } 305 | return wkc; 306 | } 307 | 308 | /** EoE ethernet buffer write, blocking. 309 | * 310 | * If the buffer is larger than the mailbox size then the buffer is sent in 311 | * several fragments. The function will split the buf data in fragments and 312 | * send them to the slave one by one. 313 | * 314 | * @param[in] context = context struct 315 | * @param[in] slave = Slave number 316 | * @param[in] port = Port number on slave if applicable 317 | * @param[in] psize = Size in bytes of parameter buffer. 318 | * @param[in] p = Pointer to parameter buffer 319 | * @param[in] Timeout = Timeout in us, standard is EC_TIMEOUTRXM 320 | * @return Workcounter from last slave transmission 321 | */ 322 | int ecx_EOEsend(ecx_contextt *context, uint16 slave, uint8 port, int psize, void *p, int timeout) 323 | { 324 | ec_EOEt *EOEp; 325 | ec_mbxbuft MbxOut; 326 | uint16 frameinfo1, frameinfo2; 327 | uint16 txframesize, txframeoffset; 328 | uint8 cnt, txfragmentno; 329 | boolean NotLast; 330 | int wkc, maxdata; 331 | const uint8 * buf = p; 332 | static uint8_t txframeno = 0; 333 | 334 | ec_clearmbx(&MbxOut); 335 | EOEp = (ec_EOEt *)&MbxOut; 336 | EOEp->mbxheader.address = htoes(0x0000); 337 | EOEp->mbxheader.priority = 0x00; 338 | /* data section=mailbox size - 6 mbx - 4 EoEh */ 339 | maxdata = context->slavelist[slave].mbx_l - 0x0A; 340 | txframesize = psize; 341 | txfragmentno = 0; 342 | txframeoffset = 0; 343 | NotLast = TRUE; 344 | 345 | do 346 | { 347 | txframesize = psize - txframeoffset; 348 | if (txframesize > maxdata) 349 | { 350 | /* Adjust to even 32-octect blocks */ 351 | txframesize = ((maxdata >> 5) << 5); 352 | } 353 | 354 | if (txframesize == (psize - txframeoffset)) 355 | { 356 | frameinfo1 = (EOE_HDR_LAST_FRAGMENT_SET(1) | EOE_HDR_FRAME_PORT_SET(port)); 357 | NotLast = FALSE; 358 | } 359 | else 360 | { 361 | frameinfo1 = EOE_HDR_FRAME_PORT_SET(port); 362 | } 363 | 364 | frameinfo2 = EOE_HDR_FRAG_NO_SET(txfragmentno); 365 | if (txfragmentno > 0) 366 | { 367 | frameinfo2 = frameinfo2 | (EOE_HDR_FRAME_OFFSET_SET((txframeoffset >> 5))); 368 | } 369 | else 370 | { 371 | frameinfo2 = frameinfo2 | (EOE_HDR_FRAME_OFFSET_SET(((psize + 31) >> 5))); 372 | txframeno++; 373 | } 374 | frameinfo2 = frameinfo2 | EOE_HDR_FRAME_NO_SET(txframeno); 375 | 376 | /* get new mailbox count value, used as session handle */ 377 | cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt); 378 | context->slavelist[slave].mbx_cnt = cnt; 379 | 380 | EOEp->mbxheader.length = htoes(4 + txframesize); /* no timestamp */ 381 | EOEp->mbxheader.mbxtype = ECT_MBXT_EOE + (cnt << 4); /* EoE */ 382 | 383 | EOEp->frameinfo1 = htoes(frameinfo1); 384 | EOEp->frameinfo2 = htoes(frameinfo2); 385 | 386 | memcpy(EOEp->data, &buf[txframeoffset], txframesize); 387 | 388 | /* send EoE request to slave */ 389 | wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, timeout); 390 | if ((NotLast == TRUE) && (wkc > 0)) 391 | { 392 | txframeoffset += txframesize; 393 | txfragmentno++; 394 | } 395 | } while ((NotLast == TRUE) && (wkc > 0)); 396 | 397 | return wkc; 398 | } 399 | 400 | 401 | /** EoE ethernet buffer read, blocking. 402 | * 403 | * If the buffer is larger than the mailbox size then the buffer is received 404 | * by several fragments. The function will assamble the fragments into 405 | * a complete Ethernet buffer. 406 | * 407 | * @param[in] context = context struct 408 | * @param[in] slave = Slave number 409 | * @param[in] port = Port number on slave if applicable 410 | * @param[in/out] psize = Size in bytes of parameter buffer. 411 | * @param[in] p = Pointer to parameter buffer 412 | * @param[in] timeout = Timeout in us, standard is EC_TIMEOUTRXM 413 | * @return Workcounter from last slave response or error code 414 | */ 415 | int ecx_EOErecv(ecx_contextt *context, uint16 slave, uint8 port, int * psize, void *p, int timeout) 416 | { 417 | ec_EOEt *aEOEp; 418 | ec_mbxbuft MbxIn; 419 | uint16 frameinfo1, frameinfo2, rxframesize, rxframeoffset, eoedatasize; 420 | uint8 rxfragmentno, rxframeno; 421 | boolean NotLast; 422 | int wkc, buffersize; 423 | uint8 * buf = p; 424 | 425 | ec_clearmbx(&MbxIn); 426 | aEOEp = (ec_EOEt *)&MbxIn; 427 | NotLast = TRUE; 428 | buffersize = *psize; 429 | rxfragmentno = 0; 430 | rxframeno = 0; 431 | rxframeoffset = 0; 432 | 433 | /* Hang for a while if nothing is in */ 434 | wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout); 435 | 436 | while ((wkc > 0) && (NotLast == TRUE)) 437 | { 438 | /* slave response should be FoE */ 439 | if ((aEOEp->mbxheader.mbxtype & 0x0f) == ECT_MBXT_EOE) 440 | { 441 | 442 | eoedatasize = etohs(aEOEp->mbxheader.length) - 0x00004; 443 | frameinfo1 = etohs(aEOEp->frameinfo1); 444 | frameinfo2 = etohs(aEOEp->frameinfo2); 445 | 446 | if (rxfragmentno != EOE_HDR_FRAG_NO_GET(frameinfo2)) 447 | { 448 | if (EOE_HDR_FRAG_NO_GET(frameinfo2) > 0) 449 | { 450 | wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA; 451 | /* Exit here*/ 452 | break; 453 | } 454 | } 455 | 456 | if (rxfragmentno == 0) 457 | { 458 | rxframeoffset = 0; 459 | rxframeno = EOE_HDR_FRAME_NO_GET(frameinfo2); 460 | rxframesize = (EOE_HDR_FRAME_OFFSET_GET(frameinfo2) << 5); 461 | if (rxframesize > buffersize) 462 | { 463 | wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA; 464 | /* Exit here*/ 465 | break; 466 | } 467 | if (port != EOE_HDR_FRAME_PORT_GET(frameinfo1)) 468 | { 469 | wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA; 470 | /* Exit here*/ 471 | break; 472 | } 473 | } 474 | else 475 | { 476 | if (rxframeno != EOE_HDR_FRAME_NO_GET(frameinfo2)) 477 | { 478 | wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA; 479 | /* Exit here*/ 480 | break; 481 | } 482 | else if (rxframeoffset != (EOE_HDR_FRAME_OFFSET_GET(frameinfo2) << 5)) 483 | { 484 | wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA; 485 | /* Exit here*/ 486 | break; 487 | } 488 | } 489 | 490 | if ((rxframeoffset + eoedatasize) <= buffersize) 491 | { 492 | memcpy(&buf[rxframeoffset], aEOEp->data, eoedatasize); 493 | rxframeoffset += eoedatasize; 494 | rxfragmentno++; 495 | } 496 | 497 | if (EOE_HDR_LAST_FRAGMENT_GET(frameinfo1)) 498 | { 499 | /* Remove timestamp */ 500 | if (EOE_HDR_TIME_APPEND_GET(frameinfo1)) 501 | { 502 | rxframeoffset -= 4; 503 | } 504 | NotLast = FALSE; 505 | *psize = rxframeoffset; 506 | } 507 | else 508 | { 509 | /* Hang for a while if nothing is in */ 510 | wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout); 511 | } 512 | } 513 | else 514 | { 515 | /* unexpected mailbox received */ 516 | wkc = -EC_ERR_TYPE_PACKET_ERROR; 517 | } 518 | } 519 | return wkc; 520 | } 521 | 522 | /** EoE mailbox fragment read 523 | * 524 | * Will take the data in incoming mailbox buffer and copy to destination 525 | * Ethernet frame buffer at given offset and update current fragment variables 526 | * 527 | * @param[in] MbxIn = Received mailbox containing fragment data 528 | * @param[in/out] rxfragmentno = Fragment number 529 | * @param[in/out] rxframesize = Frame size 530 | * @param[in/out] rxframeoffset = Frame offset 531 | * @param[in/out] rxframeno = Frame number 532 | * @param[in/out] psize = Size in bytes of frame buffer. 533 | * @param[out] p = Pointer to frame buffer 534 | * @return 0= if fragment OK, >0 if last fragment, <0 on error 535 | */ 536 | int ecx_EOEreadfragment( 537 | ec_mbxbuft * MbxIn, 538 | uint8 * rxfragmentno, 539 | uint16 * rxframesize, 540 | uint16 * rxframeoffset, 541 | uint16 * rxframeno, 542 | int * psize, 543 | void *p) 544 | { 545 | uint16 frameinfo1, frameinfo2, eoedatasize; 546 | int wkc; 547 | ec_EOEt * aEOEp; 548 | uint8 * buf; 549 | 550 | aEOEp = (ec_EOEt *)MbxIn; 551 | buf = p; 552 | wkc = 0; 553 | 554 | /* slave response should be EoE */ 555 | if ((aEOEp->mbxheader.mbxtype & 0x0f) == ECT_MBXT_EOE) 556 | { 557 | eoedatasize = etohs(aEOEp->mbxheader.length) - 0x00004; 558 | frameinfo1 = etohs(aEOEp->frameinfo1); 559 | frameinfo2 = etohs(aEOEp->frameinfo2); 560 | 561 | /* Retrive fragment number, is it what we expect? */ 562 | if (*rxfragmentno != EOE_HDR_FRAG_NO_GET(frameinfo2)) 563 | { 564 | /* If expected fragment number is not 0, reset working variables */ 565 | if (*rxfragmentno != 0) 566 | { 567 | *rxfragmentno = 0; 568 | *rxframesize = 0; 569 | *rxframeoffset = 0; 570 | *rxframeno = 0; 571 | } 572 | 573 | /* If incoming fragment number is not 0 we can't recover, exit */ 574 | if (EOE_HDR_FRAG_NO_GET(frameinfo2) > 0) 575 | { 576 | wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA; 577 | return wkc; 578 | } 579 | } 580 | 581 | /* Is it a new frame?*/ 582 | if (*rxfragmentno == 0) 583 | { 584 | *rxframesize = (EOE_HDR_FRAME_OFFSET_GET(frameinfo2) << 5); 585 | *rxframeoffset = 0; 586 | *rxframeno = EOE_HDR_FRAME_NO_GET(frameinfo2); 587 | } 588 | else 589 | { 590 | /* If we're inside a frame, make sure it is the same */ 591 | if (*rxframeno != EOE_HDR_FRAME_NO_GET(frameinfo2)) 592 | { 593 | *rxfragmentno = 0; 594 | *rxframesize = 0; 595 | *rxframeoffset = 0; 596 | *rxframeno = 0; 597 | wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA; 598 | return wkc; 599 | } 600 | else if (*rxframeoffset != (EOE_HDR_FRAME_OFFSET_GET(frameinfo2) << 5)) 601 | { 602 | *rxfragmentno = 0; 603 | *rxframesize = 0; 604 | *rxframeoffset = 0; 605 | *rxframeno = 0; 606 | wkc = -EC_ERR_TYPE_EOE_INVALID_RX_DATA; 607 | return wkc; 608 | } 609 | } 610 | 611 | /* Make sure we're inside expected frame size */ 612 | if (((*rxframeoffset + eoedatasize) <= *rxframesize) && 613 | ((*rxframeoffset + eoedatasize) <= *psize)) 614 | { 615 | memcpy(&buf[*rxframeoffset], aEOEp->data, eoedatasize); 616 | *rxframeoffset += eoedatasize; 617 | *rxfragmentno += 1; 618 | } 619 | 620 | /* Is it the last fragment */ 621 | if (EOE_HDR_LAST_FRAGMENT_GET(frameinfo1)) 622 | { 623 | /* Remove timestamp */ 624 | if (EOE_HDR_TIME_APPEND_GET(frameinfo1)) 625 | { 626 | *rxframeoffset -= 4; 627 | } 628 | *psize = *rxframeoffset; 629 | *rxfragmentno = 0; 630 | *rxframesize = 0; 631 | *rxframeoffset = 0; 632 | *rxframeno = 0; 633 | wkc = 1; 634 | } 635 | } 636 | else 637 | { 638 | /* unexpected mailbox received */ 639 | wkc = -EC_ERR_TYPE_PACKET_ERROR; 640 | } 641 | return wkc; 642 | } 643 | -------------------------------------------------------------------------------- /src/soem/ethercateoe.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * Headerfile for ethercatfoe.c 9 | */ 10 | 11 | #ifndef _ethercateoe_ 12 | #define _ethercateoe_ 13 | 14 | #ifdef __cplusplus 15 | extern "C" 16 | { 17 | #endif 18 | 19 | #include "ethercattype.h" 20 | 21 | /** DNS length according to ETG 1000.6 */ 22 | #define EOE_DNS_NAME_LENGTH 32 23 | /** Ethernet address length not including VLAN */ 24 | #define EOE_ETHADDR_LENGTH 6 25 | 26 | #define EOE_MAKEU32(a,b,c,d) (((uint32_t)((a) & 0xff) << 24) | \ 27 | ((uint32_t)((b) & 0xff) << 16) | \ 28 | ((uint32_t)((c) & 0xff) << 8) | \ 29 | (uint32_t)((d) & 0xff)) 30 | 31 | #if !defined(EC_BIG_ENDIAN) && defined(EC_LITTLE_ENDIAN) 32 | 33 | #define EOE_HTONS(x) ((((x) & 0x00ffUL) << 8) | (((x) & 0xff00UL) >> 8)) 34 | #define EOE_NTOHS(x) EOE_HTONS(x) 35 | #define EOE_HTONL(x) ((((x) & 0x000000ffUL) << 24) | \ 36 | (((x) & 0x0000ff00UL) << 8) | \ 37 | (((x) & 0x00ff0000UL) >> 8) | \ 38 | (((x) & 0xff000000UL) >> 24)) 39 | #define EOE_NTOHL(x) EOE_HTONL(x) 40 | #else 41 | #define EOE_HTONS(x) (x) 42 | #define EOE_NTOHS(x) (x) 43 | #define EOE_HTONL(x) (x) 44 | #define EOE_NTOHL(x) (x) 45 | #endif /* !defined(EC_BIG_ENDIAN) && defined(EC_LITTLE_ENDIAN) */ 46 | 47 | /** Get one byte from the 4-byte address */ 48 | #define eoe_ip4_addr1(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[0]) 49 | #define eoe_ip4_addr2(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[1]) 50 | #define eoe_ip4_addr3(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[2]) 51 | #define eoe_ip4_addr4(ipaddr) (((const uint8_t*)(&(ipaddr)->addr))[3]) 52 | 53 | /** Set an IP address given by the four byte-parts */ 54 | #define EOE_IP4_ADDR_TO_U32(ipaddr,a,b,c,d) \ 55 | (ipaddr)->addr = EOE_HTONL(EOE_MAKEU32(a,b,c,d)) 56 | 57 | /** Header frame info 1 */ 58 | #define EOE_HDR_FRAME_TYPE_OFFSET 0 59 | #define EOE_HDR_FRAME_TYPE (0xF << 0) 60 | #define EOE_HDR_FRAME_TYPE_SET(x) (((x) & 0xF) << 0) 61 | #define EOE_HDR_FRAME_TYPE_GET(x) (((x) >> 0) & 0xF) 62 | #define EOE_HDR_FRAME_PORT_OFFSET 4 63 | #define EOE_HDR_FRAME_PORT (0xF << 4) 64 | #define EOE_HDR_FRAME_PORT_SET(x) (((x) & 0xF) << 4) 65 | #define EOE_HDR_FRAME_PORT_GET(x) (((x) >> 4) & 0xF) 66 | #define EOE_HDR_LAST_FRAGMENT_OFFSET 8 67 | #define EOE_HDR_LAST_FRAGMENT (0x1 << 8) 68 | #define EOE_HDR_LAST_FRAGMENT_SET(x) (((x) & 0x1) << 8) 69 | #define EOE_HDR_LAST_FRAGMENT_GET(x) (((x) >> 8) & 0x1) 70 | #define EOE_HDR_TIME_APPEND_OFFSET 9 71 | #define EOE_HDR_TIME_APPEND (0x1 << 9) 72 | #define EOE_HDR_TIME_APPEND_SET(x) (((x) & 0x1) << 9) 73 | #define EOE_HDR_TIME_APPEND_GET(x) (((x) >> 9) & 0x1) 74 | #define EOE_HDR_TIME_REQUEST_OFFSET 10 75 | #define EOE_HDR_TIME_REQUEST (0x1 << 10) 76 | #define EOE_HDR_TIME_REQUEST_SET(x) (((x) & 0x1) << 10) 77 | #define EOE_HDR_TIME_REQUEST_GET(x) (((x) >> 10) & 0x1) 78 | 79 | /** Header frame info 2 */ 80 | #define EOE_HDR_FRAG_NO_OFFSET 0 81 | #define EOE_HDR_FRAG_NO (0x3F << 0) 82 | #define EOE_HDR_FRAG_NO_SET(x) (((x) & 0x3F) << 0) 83 | #define EOE_HDR_FRAG_NO_GET(x) (((x) >> 0) & 0x3F) 84 | #define EOE_HDR_FRAME_OFFSET_OFFSET 6 85 | #define EOE_HDR_FRAME_OFFSET (0x3F << 6) 86 | #define EOE_HDR_FRAME_OFFSET_SET(x) (((x) & 0x3F) << 6) 87 | #define EOE_HDR_FRAME_OFFSET_GET(x) (((x) >> 6) & 0x3F) 88 | #define EOE_HDR_FRAME_NO_OFFSET 12 89 | #define EOE_HDR_FRAME_NO (0xF << 12) 90 | #define EOE_HDR_FRAME_NO_SET(x) (((x) & 0xF) << 12) 91 | #define EOE_HDR_FRAME_NO_GET(x) (((x) >> 12) & 0xF) 92 | 93 | /** EOE param */ 94 | #define EOE_PARAM_OFFSET 4 95 | #define EOE_PARAM_MAC_INCLUDE (0x1 << 0) 96 | #define EOE_PARAM_IP_INCLUDE (0x1 << 1) 97 | #define EOE_PARAM_SUBNET_IP_INCLUDE (0x1 << 2) 98 | #define EOE_PARAM_DEFAULT_GATEWAY_INCLUDE (0x1 << 3) 99 | #define EOE_PARAM_DNS_IP_INCLUDE (0x1 << 4) 100 | #define EOE_PARAM_DNS_NAME_INCLUDE (0x1 << 5) 101 | 102 | /** EoE frame types */ 103 | #define EOE_FRAG_DATA 0 104 | #define EOE_INIT_RESP_TIMESTAMP 1 105 | #define EOE_INIT_REQ 2 /* Spec SET IP REQ */ 106 | #define EOE_INIT_RESP 3 /* Spec SET IP RESP */ 107 | #define EOE_SET_ADDR_FILTER_REQ 4 108 | #define EOE_SET_ADDR_FILTER_RESP 5 109 | #define EOE_GET_IP_PARAM_REQ 6 110 | #define EOE_GET_IP_PARAM_RESP 7 111 | #define EOE_GET_ADDR_FILTER_REQ 8 112 | #define EOE_GET_ADDR_FILTER_RESP 9 113 | 114 | /** EoE parameter result codes */ 115 | #define EOE_RESULT_SUCCESS 0x0000 116 | #define EOE_RESULT_UNSPECIFIED_ERROR 0x0001 117 | #define EOE_RESULT_UNSUPPORTED_FRAME_TYPE 0x0002 118 | #define EOE_RESULT_NO_IP_SUPPORT 0x0201 119 | #define EOE_RESULT_NO_DHCP_SUPPORT 0x0202 120 | #define EOE_RESULT_NO_FILTER_SUPPORT 0x0401 121 | 122 | 123 | /** EOE ip4 address in network order */ 124 | typedef struct eoe_ip4_addr { 125 | uint32_t addr; 126 | }eoe_ip4_addr_t; 127 | 128 | /** EOE ethernet address */ 129 | PACKED_BEGIN 130 | typedef struct PACKED eoe_ethaddr 131 | { 132 | uint8_t addr[EOE_ETHADDR_LENGTH]; 133 | } eoe_ethaddr_t; 134 | PACKED_END 135 | 136 | /** EoE IP request structure, storage only, no need to pack */ 137 | typedef struct eoe_param 138 | { 139 | uint8_t mac_set : 1; 140 | uint8_t ip_set : 1; 141 | uint8_t subnet_set : 1; 142 | uint8_t default_gateway_set : 1; 143 | uint8_t dns_ip_set : 1; 144 | uint8_t dns_name_set : 1; 145 | eoe_ethaddr_t mac; 146 | eoe_ip4_addr_t ip; 147 | eoe_ip4_addr_t subnet; 148 | eoe_ip4_addr_t default_gateway; 149 | eoe_ip4_addr_t dns_ip; 150 | char dns_name[EOE_DNS_NAME_LENGTH]; 151 | } eoe_param_t; 152 | 153 | /** EOE structure. 154 | * Used to interpret EoE mailbox packets. 155 | */ 156 | PACKED_BEGIN 157 | typedef struct PACKED 158 | { 159 | ec_mbxheadert mbxheader; 160 | uint16_t frameinfo1; 161 | union 162 | { 163 | uint16_t frameinfo2; 164 | uint16_t result; 165 | }; 166 | uint8 data[0]; 167 | } ec_EOEt; 168 | PACKED_END 169 | 170 | int ecx_EOEdefinehook(ecx_contextt *context, void *hook); 171 | int ecx_EOEsetIp(ecx_contextt *context, 172 | uint16 slave, 173 | uint8 port, 174 | eoe_param_t * ipparam, 175 | int timeout); 176 | int ecx_EOEgetIp(ecx_contextt *context, 177 | uint16 slave, 178 | uint8 port, 179 | eoe_param_t * ipparam, 180 | int timeout); 181 | int ecx_EOEsend(ecx_contextt *context, 182 | uint16 slave, 183 | uint8 port, 184 | int psize, 185 | void *p, 186 | int timeout); 187 | int ecx_EOErecv(ecx_contextt *context, 188 | uint16 slave, 189 | uint8 port, 190 | int * psize, 191 | void *p, 192 | int timeout); 193 | int ecx_EOEreadfragment( 194 | ec_mbxbuft * MbxIn, 195 | uint8 * rxfragmentno, 196 | uint16 * rxframesize, 197 | uint16 * rxframeoffset, 198 | uint16 * rxframeno, 199 | int * psize, 200 | void *p); 201 | 202 | #ifdef __cplusplus 203 | } 204 | #endif 205 | 206 | #endif 207 | -------------------------------------------------------------------------------- /src/soem/ethercatfoe.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * File over EtherCAT (FoE) module. 9 | * 10 | * SDO read / write and SDO service functions 11 | */ 12 | 13 | #include 14 | #include 15 | #include "osal/osal.h" 16 | #include "oshw/oshw.h" 17 | #include "ethercattype.h" 18 | #include "ethercatbase.h" 19 | #include "ethercatmain.h" 20 | #include "ethercatfoe.h" 21 | 22 | #define EC_MAXFOEDATA 512 23 | 24 | /** FOE structure. 25 | * Used for Read, Write, Data, Ack and Error mailbox packets. 26 | */ 27 | PACKED_BEGIN 28 | typedef struct PACKED 29 | { 30 | ec_mbxheadert MbxHeader; 31 | uint8 OpCode; 32 | uint8 Reserved; 33 | union 34 | { 35 | uint32 Password; 36 | uint32 PacketNumber; 37 | uint32 ErrorCode; 38 | }; 39 | union 40 | { 41 | char FileName[EC_MAXFOEDATA]; 42 | uint8 Data[EC_MAXFOEDATA]; 43 | char ErrorText[EC_MAXFOEDATA]; 44 | }; 45 | } ec_FOEt; 46 | PACKED_END 47 | 48 | /** FoE progress hook. 49 | * 50 | * @param[in] context = context struct 51 | * @param[in] hook = Pointer to hook function. 52 | * @return 1 53 | */ 54 | int ecx_FOEdefinehook(ecx_contextt *context, void *hook) 55 | { 56 | context->FOEhook = hook; 57 | return 1; 58 | } 59 | 60 | /** FoE read, blocking. 61 | * 62 | * @param[in] context = context struct 63 | * @param[in] slave = Slave number. 64 | * @param[in] filename = Filename of file to read. 65 | * @param[in] password = password. 66 | * @param[in,out] psize = Size in bytes of file buffer, returns bytes read from file. 67 | * @param[out] p = Pointer to file buffer 68 | * @param[in] timeout = Timeout per mailbox cycle in us, standard is EC_TIMEOUTRXM 69 | * @return Workcounter from last slave response 70 | */ 71 | int ecx_FOEread(ecx_contextt *context, uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout) 72 | { 73 | ec_FOEt *FOEp, *aFOEp; 74 | int wkc; 75 | int32 dataread = 0; 76 | int32 buffersize, packetnumber, prevpacket = 0; 77 | uint16 fnsize, maxdata, segmentdata; 78 | ec_mbxbuft MbxIn, MbxOut; 79 | uint8 cnt; 80 | boolean worktodo; 81 | 82 | buffersize = *psize; 83 | ec_clearmbx(&MbxIn); 84 | /* Empty slave out mailbox if something is in. Timeout set to 0 */ 85 | wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0); 86 | ec_clearmbx(&MbxOut); 87 | aFOEp = (ec_FOEt *)&MbxIn; 88 | FOEp = (ec_FOEt *)&MbxOut; 89 | fnsize = (uint16)strlen(filename); 90 | maxdata = context->slavelist[slave].mbx_l - 12; 91 | if (fnsize > maxdata) 92 | { 93 | fnsize = maxdata; 94 | } 95 | FOEp->MbxHeader.length = htoes(0x0006 + fnsize); 96 | FOEp->MbxHeader.address = htoes(0x0000); 97 | FOEp->MbxHeader.priority = 0x00; 98 | /* get new mailbox count value, used as session handle */ 99 | cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt); 100 | context->slavelist[slave].mbx_cnt = cnt; 101 | FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + (cnt << 4); /* FoE */ 102 | FOEp->OpCode = ECT_FOE_READ; 103 | FOEp->Password = htoel(password); 104 | /* copy filename in mailbox */ 105 | memcpy(&FOEp->FileName[0], filename, fnsize); 106 | /* send FoE request to slave */ 107 | wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM); 108 | if (wkc > 0) /* succeeded to place mailbox in slave ? */ 109 | { 110 | do 111 | { 112 | worktodo = FALSE; 113 | /* clean mailboxbuffer */ 114 | ec_clearmbx(&MbxIn); 115 | /* read slave response */ 116 | wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout); 117 | if (wkc > 0) /* succeeded to read slave response ? */ 118 | { 119 | /* slave response should be FoE */ 120 | if ((aFOEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_FOE) 121 | { 122 | if(aFOEp->OpCode == ECT_FOE_DATA) 123 | { 124 | segmentdata = etohs(aFOEp->MbxHeader.length) - 0x0006; 125 | packetnumber = etohl(aFOEp->PacketNumber); 126 | if ((packetnumber == ++prevpacket) && (dataread + segmentdata <= buffersize)) 127 | { 128 | memcpy(p, &aFOEp->Data[0], segmentdata); 129 | dataread += segmentdata; 130 | p = (uint8 *)p + segmentdata; 131 | if (segmentdata == maxdata) 132 | { 133 | worktodo = TRUE; 134 | } 135 | FOEp->MbxHeader.length = htoes(0x0006); 136 | FOEp->MbxHeader.address = htoes(0x0000); 137 | FOEp->MbxHeader.priority = 0x00; 138 | /* get new mailbox count value */ 139 | cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt); 140 | context->slavelist[slave].mbx_cnt = cnt; 141 | FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + (cnt << 4); /* FoE */ 142 | FOEp->OpCode = ECT_FOE_ACK; 143 | FOEp->PacketNumber = htoel(packetnumber); 144 | /* send FoE ack to slave */ 145 | wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM); 146 | if (wkc <= 0) 147 | { 148 | worktodo = FALSE; 149 | } 150 | if (context->FOEhook) 151 | { 152 | context->FOEhook(slave, packetnumber, dataread); 153 | } 154 | } 155 | else 156 | { 157 | /* FoE error */ 158 | wkc = -EC_ERR_TYPE_FOE_BUF2SMALL; 159 | } 160 | } 161 | else 162 | { 163 | if(aFOEp->OpCode == ECT_FOE_ERROR) 164 | { 165 | /* FoE error */ 166 | wkc = -EC_ERR_TYPE_FOE_ERROR; 167 | } 168 | else 169 | { 170 | /* unexpected mailbox received */ 171 | wkc = -EC_ERR_TYPE_PACKET_ERROR; 172 | } 173 | } 174 | } 175 | else 176 | { 177 | /* unexpected mailbox received */ 178 | wkc = -EC_ERR_TYPE_PACKET_ERROR; 179 | } 180 | *psize = dataread; 181 | } 182 | } while (worktodo); 183 | } 184 | 185 | return wkc; 186 | } 187 | 188 | /** FoE write, blocking. 189 | * 190 | * @param[in] context = context struct 191 | * @param[in] slave = Slave number. 192 | * @param[in] filename = Filename of file to write. 193 | * @param[in] password = password. 194 | * @param[in] psize = Size in bytes of file buffer. 195 | * @param[out] p = Pointer to file buffer 196 | * @param[in] timeout = Timeout per mailbox cycle in us, standard is EC_TIMEOUTRXM 197 | * @return Workcounter from last slave response 198 | */ 199 | int ecx_FOEwrite(ecx_contextt *context, uint16 slave, char *filename, uint32 password, int psize, void *p, int timeout) 200 | { 201 | ec_FOEt *FOEp, *aFOEp; 202 | int wkc; 203 | int32 packetnumber, sendpacket = 0; 204 | uint16 fnsize, maxdata; 205 | int segmentdata; 206 | ec_mbxbuft MbxIn, MbxOut; 207 | uint8 cnt; 208 | boolean worktodo, dofinalzero; 209 | int tsize; 210 | 211 | ec_clearmbx(&MbxIn); 212 | /* Empty slave out mailbox if something is in. Timeout set to 0 */ 213 | wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0); 214 | ec_clearmbx(&MbxOut); 215 | aFOEp = (ec_FOEt *)&MbxIn; 216 | FOEp = (ec_FOEt *)&MbxOut; 217 | dofinalzero = FALSE; 218 | fnsize = (uint16)strlen(filename); 219 | maxdata = context->slavelist[slave].mbx_l - 12; 220 | if (fnsize > maxdata) 221 | { 222 | fnsize = maxdata; 223 | } 224 | FOEp->MbxHeader.length = htoes(0x0006 + fnsize); 225 | FOEp->MbxHeader.address = htoes(0x0000); 226 | FOEp->MbxHeader.priority = 0x00; 227 | /* get new mailbox count value, used as session handle */ 228 | cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt); 229 | context->slavelist[slave].mbx_cnt = cnt; 230 | FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + (cnt << 4); /* FoE */ 231 | FOEp->OpCode = ECT_FOE_WRITE; 232 | FOEp->Password = htoel(password); 233 | /* copy filename in mailbox */ 234 | memcpy(&FOEp->FileName[0], filename, fnsize); 235 | /* send FoE request to slave */ 236 | wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM); 237 | if (wkc > 0) /* succeeded to place mailbox in slave ? */ 238 | { 239 | do 240 | { 241 | worktodo = FALSE; 242 | /* clean mailboxbuffer */ 243 | ec_clearmbx(&MbxIn); 244 | /* read slave response */ 245 | wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout); 246 | if (wkc > 0) /* succeeded to read slave response ? */ 247 | { 248 | /* slave response should be FoE */ 249 | if ((aFOEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_FOE) 250 | { 251 | switch (aFOEp->OpCode) 252 | { 253 | case ECT_FOE_ACK: 254 | { 255 | packetnumber = etohl(aFOEp->PacketNumber); 256 | if (packetnumber == sendpacket) 257 | { 258 | if (context->FOEhook) 259 | { 260 | context->FOEhook(slave, packetnumber, psize); 261 | } 262 | tsize = psize; 263 | if (tsize > maxdata) 264 | { 265 | tsize = maxdata; 266 | } 267 | if(tsize || dofinalzero) 268 | { 269 | worktodo = TRUE; 270 | dofinalzero = FALSE; 271 | segmentdata = tsize; 272 | psize -= segmentdata; 273 | /* if last packet was full size, add a zero size packet as final */ 274 | /* EOF is defined as packetsize < full packetsize */ 275 | if (!psize && (segmentdata == maxdata)) 276 | { 277 | dofinalzero = TRUE; 278 | } 279 | FOEp->MbxHeader.length = htoes(0x0006 + segmentdata); 280 | FOEp->MbxHeader.address = htoes(0x0000); 281 | FOEp->MbxHeader.priority = 0x00; 282 | /* get new mailbox count value */ 283 | cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt); 284 | context->slavelist[slave].mbx_cnt = cnt; 285 | FOEp->MbxHeader.mbxtype = ECT_MBXT_FOE + (cnt << 4); /* FoE */ 286 | FOEp->OpCode = ECT_FOE_DATA; 287 | sendpacket++; 288 | FOEp->PacketNumber = htoel(sendpacket); 289 | memcpy(&FOEp->Data[0], p, segmentdata); 290 | p = (uint8 *)p + segmentdata; 291 | /* send FoE data to slave */ 292 | wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM); 293 | if (wkc <= 0) 294 | { 295 | worktodo = FALSE; 296 | } 297 | } 298 | } 299 | else 300 | { 301 | /* FoE error */ 302 | wkc = -EC_ERR_TYPE_FOE_PACKETNUMBER; 303 | } 304 | break; 305 | } 306 | case ECT_FOE_BUSY: 307 | { 308 | /* resend if data has been send before */ 309 | /* otherwise ignore */ 310 | if (sendpacket) 311 | { 312 | if (!psize) 313 | { 314 | dofinalzero = TRUE; 315 | } 316 | psize += segmentdata; 317 | p = (uint8 *)p - segmentdata; 318 | --sendpacket; 319 | } 320 | break; 321 | } 322 | case ECT_FOE_ERROR: 323 | { 324 | /* FoE error */ 325 | if (aFOEp->ErrorCode == 0x8001) 326 | { 327 | wkc = -EC_ERR_TYPE_FOE_FILE_NOTFOUND; 328 | } 329 | else 330 | { 331 | wkc = -EC_ERR_TYPE_FOE_ERROR; 332 | } 333 | break; 334 | } 335 | default: 336 | { 337 | /* unexpected mailbox received */ 338 | wkc = -EC_ERR_TYPE_PACKET_ERROR; 339 | break; 340 | } 341 | } 342 | } 343 | else 344 | { 345 | /* unexpected mailbox received */ 346 | wkc = -EC_ERR_TYPE_PACKET_ERROR; 347 | } 348 | } 349 | } while (worktodo); 350 | } 351 | 352 | return wkc; 353 | } 354 | 355 | #ifdef EC_VER1 356 | int ec_FOEdefinehook(void *hook) 357 | { 358 | return ecx_FOEdefinehook(&ecx_context, hook); 359 | } 360 | 361 | int ec_FOEread(uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout) 362 | { 363 | return ecx_FOEread(&ecx_context, slave, filename, password, psize, p, timeout); 364 | } 365 | 366 | int ec_FOEwrite(uint16 slave, char *filename, uint32 password, int psize, void *p, int timeout) 367 | { 368 | return ecx_FOEwrite(&ecx_context, slave, filename, password, psize, p, timeout); 369 | } 370 | #endif 371 | -------------------------------------------------------------------------------- /src/soem/ethercatfoe.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * Headerfile for ethercatfoe.c 9 | */ 10 | 11 | #ifndef _ethercatfoe_ 12 | #define _ethercatfoe_ 13 | 14 | #ifdef __cplusplus 15 | extern "C" 16 | { 17 | #endif 18 | 19 | #ifdef EC_VER1 20 | int ec_FOEdefinehook(void *hook); 21 | int ec_FOEread(uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout); 22 | int ec_FOEwrite(uint16 slave, char *filename, uint32 password, int psize, void *p, int timeout); 23 | #endif 24 | 25 | int ecx_FOEdefinehook(ecx_contextt *context, void *hook); 26 | int ecx_FOEread(ecx_contextt *context, uint16 slave, char *filename, uint32 password, int *psize, void *p, int timeout); 27 | int ecx_FOEwrite(ecx_contextt *context, uint16 slave, char *filename, uint32 password, int psize, void *p, int timeout); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/soem/ethercatmain.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * Headerfile for ethercatmain.c 9 | */ 10 | 11 | #ifndef _ethercatmain_ 12 | #define _ethercatmain_ 13 | 14 | 15 | #ifdef __cplusplus 16 | extern "C" 17 | { 18 | #endif 19 | 20 | /** max. entries in EtherCAT error list */ 21 | #define EC_MAXELIST 64 22 | /** max. length of readable name in slavelist and Object Description List */ 23 | #define EC_MAXNAME 40 24 | /** max. number of slaves in array */ 25 | #define EC_MAXSLAVE 64 // TODO shrinked for memory reduction (original = 200) 26 | /** max. number of groups */ 27 | #define EC_MAXGROUP 2 28 | /** max. number of IO segments per group */ 29 | #define EC_MAXIOSEGMENTS 64 30 | /** max. mailbox size */ 31 | #define EC_MAXMBX 1486 32 | /** max. eeprom PDO entries */ 33 | #define EC_MAXEEPDO 0x200 34 | /** max. SM used */ 35 | #define EC_MAXSM 8 36 | /** max. FMMU used */ 37 | #define EC_MAXFMMU 4 38 | /** max. Adapter */ 39 | #define EC_MAXLEN_ADAPTERNAME 128 40 | /** define maximum number of concurrent threads in mapping */ 41 | #define EC_MAX_MAPT 1 42 | 43 | typedef struct ec_adapter ec_adaptert; 44 | struct ec_adapter 45 | { 46 | char name[EC_MAXLEN_ADAPTERNAME]; 47 | char desc[EC_MAXLEN_ADAPTERNAME]; 48 | ec_adaptert *next; 49 | }; 50 | 51 | /** record for FMMU */ 52 | PACKED_BEGIN 53 | typedef struct PACKED ec_fmmu 54 | { 55 | uint32 LogStart; 56 | uint16 LogLength; 57 | uint8 LogStartbit; 58 | uint8 LogEndbit; 59 | uint16 PhysStart; 60 | uint8 PhysStartBit; 61 | uint8 FMMUtype; 62 | uint8 FMMUactive; 63 | uint8 unused1; 64 | uint16 unused2; 65 | } ec_fmmut; 66 | PACKED_END 67 | 68 | /** record for sync manager */ 69 | PACKED_BEGIN 70 | typedef struct PACKED ec_sm 71 | { 72 | uint16 StartAddr; 73 | uint16 SMlength; 74 | uint32 SMflags; 75 | } ec_smt; 76 | PACKED_END 77 | 78 | PACKED_BEGIN 79 | typedef struct PACKED ec_state_status 80 | { 81 | uint16 State; 82 | uint16 Unused; 83 | uint16 ALstatuscode; 84 | } ec_state_status; 85 | PACKED_END 86 | 87 | #define ECT_MBXPROT_AOE 0x0001 88 | #define ECT_MBXPROT_EOE 0x0002 89 | #define ECT_MBXPROT_COE 0x0004 90 | #define ECT_MBXPROT_FOE 0x0008 91 | #define ECT_MBXPROT_SOE 0x0010 92 | #define ECT_MBXPROT_VOE 0x0020 93 | 94 | #define ECT_COEDET_SDO 0x01 95 | #define ECT_COEDET_SDOINFO 0x02 96 | #define ECT_COEDET_PDOASSIGN 0x04 97 | #define ECT_COEDET_PDOCONFIG 0x08 98 | #define ECT_COEDET_UPLOAD 0x10 99 | #define ECT_COEDET_SDOCA 0x20 100 | 101 | #define EC_SMENABLEMASK 0xfffeffff 102 | 103 | /** for list of ethercat slaves detected */ 104 | typedef struct ec_slave 105 | { 106 | /** state of slave */ 107 | uint16 state; 108 | /** AL status code */ 109 | uint16 ALstatuscode; 110 | /** Configured address */ 111 | uint16 configadr; 112 | /** Alias address */ 113 | uint16 aliasadr; 114 | /** Manufacturer from EEprom */ 115 | uint32 eep_man; 116 | /** ID from EEprom */ 117 | uint32 eep_id; 118 | /** revision from EEprom */ 119 | uint32 eep_rev; 120 | /** Interface type */ 121 | uint16 Itype; 122 | /** Device type */ 123 | uint16 Dtype; 124 | /** output bits */ 125 | uint16 Obits; 126 | /** output bytes, if Obits < 8 then Obytes = 0 */ 127 | uint32 Obytes; 128 | /** output pointer in IOmap buffer */ 129 | uint8 *outputs; 130 | /** startbit in first output byte */ 131 | uint8 Ostartbit; 132 | /** input bits */ 133 | uint16 Ibits; 134 | /** input bytes, if Ibits < 8 then Ibytes = 0 */ 135 | uint32 Ibytes; 136 | /** input pointer in IOmap buffer */ 137 | uint8 *inputs; 138 | /** startbit in first input byte */ 139 | uint8 Istartbit; 140 | /** SM structure */ 141 | ec_smt SM[EC_MAXSM]; 142 | /** SM type 0=unused 1=MbxWr 2=MbxRd 3=Outputs 4=Inputs */ 143 | uint8 SMtype[EC_MAXSM]; 144 | /** FMMU structure */ 145 | ec_fmmut FMMU[EC_MAXFMMU]; 146 | /** FMMU0 function */ 147 | uint8 FMMU0func; 148 | /** FMMU1 function */ 149 | uint8 FMMU1func; 150 | /** FMMU2 function */ 151 | uint8 FMMU2func; 152 | /** FMMU3 function */ 153 | uint8 FMMU3func; 154 | /** length of write mailbox in bytes, if no mailbox then 0 */ 155 | uint16 mbx_l; 156 | /** mailbox write offset */ 157 | uint16 mbx_wo; 158 | /** length of read mailbox in bytes */ 159 | uint16 mbx_rl; 160 | /** mailbox read offset */ 161 | uint16 mbx_ro; 162 | /** mailbox supported protocols */ 163 | uint16 mbx_proto; 164 | /** Counter value of mailbox link layer protocol 1..7 */ 165 | uint8 mbx_cnt; 166 | /** has DC capability */ 167 | boolean hasdc; 168 | /** Physical type; Ebus, EtherNet combinations */ 169 | uint8 ptype; 170 | /** topology: 1 to 3 links */ 171 | uint8 topology; 172 | /** active ports bitmap : ....3210 , set if respective port is active **/ 173 | uint8 activeports; 174 | /** consumed ports bitmap : ....3210, used for internal delay measurement **/ 175 | uint8 consumedports; 176 | /** slave number for parent, 0=master */ 177 | uint16 parent; 178 | /** port number on parent this slave is connected to **/ 179 | uint8 parentport; 180 | /** port number on this slave the parent is connected to **/ 181 | uint8 entryport; 182 | /** DC receivetimes on port A */ 183 | int32 DCrtA; 184 | /** DC receivetimes on port B */ 185 | int32 DCrtB; 186 | /** DC receivetimes on port C */ 187 | int32 DCrtC; 188 | /** DC receivetimes on port D */ 189 | int32 DCrtD; 190 | /** propagation delay */ 191 | int32 pdelay; 192 | /** next DC slave */ 193 | uint16 DCnext; 194 | /** previous DC slave */ 195 | uint16 DCprevious; 196 | /** DC cycle time in ns */ 197 | int32 DCcycle; 198 | /** DC shift from clock modulus boundary */ 199 | int32 DCshift; 200 | /** DC sync activation, 0=off, 1=on */ 201 | uint8 DCactive; 202 | /** link to config table */ 203 | uint16 configindex; 204 | /** link to SII config */ 205 | uint16 SIIindex; 206 | /** 1 = 8 bytes per read, 0 = 4 bytes per read */ 207 | uint8 eep_8byte; 208 | /** 0 = eeprom to master , 1 = eeprom to PDI */ 209 | uint8 eep_pdi; 210 | /** CoE details */ 211 | uint8 CoEdetails; 212 | /** FoE details */ 213 | uint8 FoEdetails; 214 | /** EoE details */ 215 | uint8 EoEdetails; 216 | /** SoE details */ 217 | uint8 SoEdetails; 218 | /** E-bus current */ 219 | int16 Ebuscurrent; 220 | /** if >0 block use of LRW in processdata */ 221 | uint8 blockLRW; 222 | /** group */ 223 | uint8 group; 224 | /** first unused FMMU */ 225 | uint8 FMMUunused; 226 | /** Boolean for tracking whether the slave is (not) responding, not used/set by the SOEM library */ 227 | boolean islost; 228 | /** registered configuration function PO->SO */ 229 | int (*PO2SOconfig)(uint16 slave); 230 | /** readable name */ 231 | char name[EC_MAXNAME + 1]; 232 | } ec_slavet; 233 | 234 | /** for list of ethercat slave groups */ 235 | typedef struct ec_group 236 | { 237 | /** logical start address for this group */ 238 | uint32 logstartaddr; 239 | /** output bytes, if Obits < 8 then Obytes = 0 */ 240 | uint32 Obytes; 241 | /** output pointer in IOmap buffer */ 242 | uint8 *outputs; 243 | /** input bytes, if Ibits < 8 then Ibytes = 0 */ 244 | uint32 Ibytes; 245 | /** input pointer in IOmap buffer */ 246 | uint8 *inputs; 247 | /** has DC capabillity */ 248 | boolean hasdc; 249 | /** next DC slave */ 250 | uint16 DCnext; 251 | /** E-bus current */ 252 | int16 Ebuscurrent; 253 | /** if >0 block use of LRW in processdata */ 254 | uint8 blockLRW; 255 | /** IO segments used */ 256 | uint16 nsegments; 257 | /** 1st input segment */ 258 | uint16 Isegment; 259 | /** Offset in input segment */ 260 | uint16 Ioffset; 261 | /** Expected workcounter outputs */ 262 | uint16 outputsWKC; 263 | /** Expected workcounter inputs */ 264 | uint16 inputsWKC; 265 | /** check slave states */ 266 | boolean docheckstate; 267 | /** IO segmentation list. Datagrams must not break SM in two. */ 268 | uint32 IOsegment[EC_MAXIOSEGMENTS]; 269 | } ec_groupt; 270 | 271 | /** SII FMMU structure */ 272 | typedef struct ec_eepromFMMU 273 | { 274 | uint16 Startpos; 275 | uint8 nFMMU; 276 | uint8 FMMU0; 277 | uint8 FMMU1; 278 | uint8 FMMU2; 279 | uint8 FMMU3; 280 | } ec_eepromFMMUt; 281 | 282 | /** SII SM structure */ 283 | typedef struct ec_eepromSM 284 | { 285 | uint16 Startpos; 286 | uint8 nSM; 287 | uint16 PhStart; 288 | uint16 Plength; 289 | uint8 Creg; 290 | uint8 Sreg; /* don't care */ 291 | uint8 Activate; 292 | uint8 PDIctrl; /* don't care */ 293 | } ec_eepromSMt; 294 | 295 | /** record to store rxPDO and txPDO table from eeprom */ 296 | typedef struct ec_eepromPDO 297 | { 298 | uint16 Startpos; 299 | uint16 Length; 300 | uint16 nPDO; 301 | uint16 Index[EC_MAXEEPDO]; 302 | uint16 SyncM[EC_MAXEEPDO]; 303 | uint16 BitSize[EC_MAXEEPDO]; 304 | uint16 SMbitsize[EC_MAXSM]; 305 | } ec_eepromPDOt; 306 | 307 | /** mailbox buffer array */ 308 | typedef uint8 ec_mbxbuft[EC_MAXMBX + 1]; 309 | 310 | /** standard ethercat mailbox header */ 311 | PACKED_BEGIN 312 | typedef struct PACKED ec_mbxheader 313 | { 314 | uint16 length; 315 | uint16 address; 316 | uint8 priority; 317 | uint8 mbxtype; 318 | } ec_mbxheadert; 319 | PACKED_END 320 | 321 | /** ALstatus and ALstatus code */ 322 | PACKED_BEGIN 323 | typedef struct PACKED ec_alstatus 324 | { 325 | uint16 alstatus; 326 | uint16 unused; 327 | uint16 alstatuscode; 328 | } ec_alstatust; 329 | PACKED_END 330 | 331 | /** stack structure to store segmented LRD/LWR/LRW constructs */ 332 | typedef struct ec_idxstack 333 | { 334 | uint8 pushed; 335 | uint8 pulled; 336 | uint8 idx[EC_MAXBUF]; 337 | void *data[EC_MAXBUF]; 338 | uint16 length[EC_MAXBUF]; 339 | } ec_idxstackT; 340 | 341 | /** ringbuf for error storage */ 342 | typedef struct ec_ering 343 | { 344 | int16 head; 345 | int16 tail; 346 | ec_errort Error[EC_MAXELIST + 1]; 347 | } ec_eringt; 348 | 349 | /** SyncManager Communication Type structure for CA */ 350 | PACKED_BEGIN 351 | typedef struct PACKED ec_SMcommtype 352 | { 353 | uint8 n; 354 | uint8 nu1; 355 | uint8 SMtype[EC_MAXSM]; 356 | } ec_SMcommtypet; 357 | PACKED_END 358 | 359 | /** SDO assign structure for CA */ 360 | PACKED_BEGIN 361 | typedef struct PACKED ec_PDOassign 362 | { 363 | uint8 n; 364 | uint8 nu1; 365 | uint16 index[256]; 366 | } ec_PDOassignt; 367 | PACKED_END 368 | 369 | /** SDO description structure for CA */ 370 | PACKED_BEGIN 371 | typedef struct PACKED ec_PDOdesc 372 | { 373 | uint8 n; 374 | uint8 nu1; 375 | uint32 PDO[256]; 376 | } ec_PDOdesct; 377 | PACKED_END 378 | 379 | /** Context structure , referenced by all ecx functions*/ 380 | struct ecx_context 381 | { 382 | /** port reference, may include red_port */ 383 | ecx_portt *port; 384 | /** slavelist reference */ 385 | ec_slavet *slavelist; 386 | /** number of slaves found in configuration */ 387 | int *slavecount; 388 | /** maximum number of slaves allowed in slavelist */ 389 | int maxslave; 390 | /** grouplist reference */ 391 | ec_groupt *grouplist; 392 | /** maximum number of groups allowed in grouplist */ 393 | int maxgroup; 394 | /** internal, reference to eeprom cache buffer */ 395 | uint8 *esibuf; 396 | /** internal, reference to eeprom cache map */ 397 | uint32 *esimap; 398 | /** internal, current slave for eeprom cache */ 399 | uint16 esislave; 400 | /** internal, reference to error list */ 401 | ec_eringt *elist; 402 | /** internal, reference to processdata stack buffer info */ 403 | ec_idxstackT *idxstack; 404 | /** reference to ecaterror state */ 405 | boolean *ecaterror; 406 | /** internal, position of DC datagram in process data packet */ 407 | uint16 DCtO; 408 | /** internal, length of DC datagram */ 409 | uint16 DCl; 410 | /** reference to last DC time from slaves */ 411 | int64 *DCtime; 412 | /** internal, SM buffer */ 413 | ec_SMcommtypet *SMcommtype; 414 | /** internal, PDO assign list */ 415 | ec_PDOassignt *PDOassign; 416 | /** internal, PDO description list */ 417 | ec_PDOdesct *PDOdesc; 418 | /** internal, SM list from eeprom */ 419 | ec_eepromSMt *eepSM; 420 | /** internal, FMMU list from eeprom */ 421 | ec_eepromFMMUt *eepFMMU; 422 | /** registered FoE hook */ 423 | int (*FOEhook)(uint16 slave, int packetnumber, int datasize); 424 | /** registered EoE hook */ 425 | int (*EOEhook)(struct ecx_context * context, uint16 slave, void * eoembx); 426 | }; 427 | typedef struct ecx_context ecx_contextt; 428 | 429 | #ifdef EC_VER1 430 | /** global struct to hold default master context */ 431 | extern ecx_contextt ecx_context; 432 | /** main slave data structure array */ 433 | extern ec_slavet ec_slave[EC_MAXSLAVE]; 434 | /** number of slaves found by configuration function */ 435 | extern int ec_slavecount; 436 | /** slave group structure */ 437 | extern ec_groupt ec_group[EC_MAXGROUP]; 438 | extern boolean EcatError; 439 | extern int64 ec_DCtime; 440 | 441 | void ec_pusherror(const ec_errort *Ec); 442 | boolean ec_poperror(ec_errort *Ec); 443 | boolean ec_iserror(void); 444 | void ec_packeterror(uint16 Slave, uint16 Index, uint8 SubIdx, uint16 ErrorCode); 445 | int ec_init(const char * ifname); 446 | // secondary port is not supported 447 | // because GR-SAKURA/ROSE has only one LAN port 448 | #if 0 449 | int ec_init_redundant(const char *ifname, char *if2name); 450 | #endif 451 | void ec_close(void); 452 | uint8 ec_siigetbyte(uint16 slave, uint16 address); 453 | int16 ec_siifind(uint16 slave, uint16 cat); 454 | void ec_siistring(char *str, uint16 slave, uint16 Sn); 455 | uint16 ec_siiFMMU(uint16 slave, ec_eepromFMMUt* FMMU); 456 | uint16 ec_siiSM(uint16 slave, ec_eepromSMt* SM); 457 | uint16 ec_siiSMnext(uint16 slave, ec_eepromSMt* SM, uint16 n); 458 | int ec_siiPDO(uint16 slave, ec_eepromPDOt* PDO, uint8 t); 459 | int ec_readstate(void); 460 | int ec_writestate(uint16 slave); 461 | uint16 ec_statecheck(uint16 slave, uint16 reqstate, int timeout); 462 | int ec_mbxempty(uint16 slave, int timeout); 463 | int ec_mbxsend(uint16 slave,ec_mbxbuft *mbx, int timeout); 464 | int ec_mbxreceive(uint16 slave, ec_mbxbuft *mbx, int timeout); 465 | void ec_esidump(uint16 slave, uint8 *esibuf); 466 | uint32 ec_readeeprom(uint16 slave, uint16 eeproma, int timeout); 467 | int ec_writeeeprom(uint16 slave, uint16 eeproma, uint16 data, int timeout); 468 | int ec_eeprom2master(uint16 slave); 469 | int ec_eeprom2pdi(uint16 slave); 470 | uint64 ec_readeepromAP(uint16 aiadr, uint16 eeproma, int timeout); 471 | int ec_writeeepromAP(uint16 aiadr, uint16 eeproma, uint16 data, int timeout); 472 | uint64 ec_readeepromFP(uint16 configadr, uint16 eeproma, int timeout); 473 | int ec_writeeepromFP(uint16 configadr, uint16 eeproma, uint16 data, int timeout); 474 | void ec_readeeprom1(uint16 slave, uint16 eeproma); 475 | uint32 ec_readeeprom2(uint16 slave, int timeout); 476 | int ec_send_processdata_group(uint8 group); 477 | int ec_send_overlap_processdata_group(uint8 group); 478 | int ec_receive_processdata_group(uint8 group, int timeout); 479 | int ec_send_processdata(void); 480 | int ec_send_overlap_processdata(void); 481 | int ec_receive_processdata(int timeout); 482 | #endif 483 | 484 | ec_adaptert * ec_find_adapters(void); 485 | void ec_free_adapters(ec_adaptert * adapter); 486 | uint8 ec_nextmbxcnt(uint8 cnt); 487 | void ec_clearmbx(ec_mbxbuft *Mbx); 488 | void ecx_pusherror(ecx_contextt *context, const ec_errort *Ec); 489 | boolean ecx_poperror(ecx_contextt *context, ec_errort *Ec); 490 | boolean ecx_iserror(ecx_contextt *context); 491 | void ecx_packeterror(ecx_contextt *context, uint16 Slave, uint16 Index, uint8 SubIdx, uint16 ErrorCode); 492 | int ecx_init(ecx_contextt *context, const char * ifname); 493 | // secondary port is not supported 494 | // because GR-SAKURA/ROSE has only one LAN port 495 | #if 0 496 | int ecx_init_redundant(ecx_contextt *context, ecx_redportt *redport, const char *ifname, char *if2name); 497 | #endif 498 | void ecx_close(ecx_contextt *context); 499 | uint8 ecx_siigetbyte(ecx_contextt *context, uint16 slave, uint16 address); 500 | int16 ecx_siifind(ecx_contextt *context, uint16 slave, uint16 cat); 501 | void ecx_siistring(ecx_contextt *context, char *str, uint16 slave, uint16 Sn); 502 | uint16 ecx_siiFMMU(ecx_contextt *context, uint16 slave, ec_eepromFMMUt* FMMU); 503 | uint16 ecx_siiSM(ecx_contextt *context, uint16 slave, ec_eepromSMt* SM); 504 | uint16 ecx_siiSMnext(ecx_contextt *context, uint16 slave, ec_eepromSMt* SM, uint16 n); 505 | int ecx_siiPDO(ecx_contextt *context, uint16 slave, ec_eepromPDOt* PDO, uint8 t); 506 | int ecx_readstate(ecx_contextt *context); 507 | int ecx_writestate(ecx_contextt *context, uint16 slave); 508 | uint16 ecx_statecheck(ecx_contextt *context, uint16 slave, uint16 reqstate, int timeout); 509 | int ecx_mbxempty(ecx_contextt *context, uint16 slave, int timeout); 510 | int ecx_mbxsend(ecx_contextt *context, uint16 slave,ec_mbxbuft *mbx, int timeout); 511 | int ecx_mbxreceive(ecx_contextt *context, uint16 slave, ec_mbxbuft *mbx, int timeout); 512 | void ecx_esidump(ecx_contextt *context, uint16 slave, uint8 *esibuf); 513 | uint32 ecx_readeeprom(ecx_contextt *context, uint16 slave, uint16 eeproma, int timeout); 514 | int ecx_writeeeprom(ecx_contextt *context, uint16 slave, uint16 eeproma, uint16 data, int timeout); 515 | int ecx_eeprom2master(ecx_contextt *context, uint16 slave); 516 | int ecx_eeprom2pdi(ecx_contextt *context, uint16 slave); 517 | uint64 ecx_readeepromAP(ecx_contextt *context, uint16 aiadr, uint16 eeproma, int timeout); 518 | int ecx_writeeepromAP(ecx_contextt *context, uint16 aiadr, uint16 eeproma, uint16 data, int timeout); 519 | uint64 ecx_readeepromFP(ecx_contextt *context, uint16 configadr, uint16 eeproma, int timeout); 520 | int ecx_writeeepromFP(ecx_contextt *context, uint16 configadr, uint16 eeproma, uint16 data, int timeout); 521 | void ecx_readeeprom1(ecx_contextt *context, uint16 slave, uint16 eeproma); 522 | uint32 ecx_readeeprom2(ecx_contextt *context, uint16 slave, int timeout); 523 | int ecx_send_overlap_processdata_group(ecx_contextt *context, uint8 group); 524 | int ecx_receive_processdata_group(ecx_contextt *context, uint8 group, int timeout); 525 | int ecx_send_processdata(ecx_contextt *context); 526 | int ecx_send_overlap_processdata(ecx_contextt *context); 527 | int ecx_receive_processdata(ecx_contextt *context, int timeout); 528 | int ecx_send_processdata_group(ecx_contextt *context, uint8 group); 529 | 530 | #ifdef __cplusplus 531 | } 532 | #endif 533 | 534 | #endif 535 | -------------------------------------------------------------------------------- /src/soem/ethercatprint.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * Module to convert EtherCAT errors to readable messages. 9 | * 10 | * SDO abort messages and AL status codes are used to relay slave errors to 11 | * the user application. This module converts the binary codes to readable text. 12 | * For the defined error codes see the EtherCAT protocol documentation. 13 | */ 14 | 15 | #include 16 | #include "oshw/oshw.h" 17 | #include "ethercattype.h" 18 | #include "ethercatmain.h" 19 | 20 | #define EC_MAXERRORNAME 127 21 | 22 | /** SDO error list type definition */ 23 | typedef struct 24 | { 25 | /** Error code returned from SDO */ 26 | uint32 errorcode; 27 | /** Readable error description */ 28 | char errordescription[EC_MAXERRORNAME + 1]; 29 | } ec_sdoerrorlist_t; 30 | 31 | /** AL status code list type definition */ 32 | typedef struct 33 | { 34 | /** AL status code */ 35 | uint16 ALstatuscode; 36 | /** Readable description */ 37 | char ALstatuscodedescription[EC_MAXERRORNAME + 1]; 38 | } ec_ALstatuscodelist_t; 39 | 40 | /** SoE error list type definition */ 41 | typedef struct 42 | { 43 | /** SoE error code */ 44 | uint16 errorcode; 45 | /** Readable description */ 46 | char errordescription[EC_MAXERRORNAME + 1]; 47 | } ec_soeerrorlist_t; 48 | 49 | /** MBX error list type definition */ 50 | typedef struct 51 | { 52 | /** MBX error code */ 53 | uint16 errorcode; 54 | /** Readable description */ 55 | char errordescription[EC_MAXERRORNAME + 1]; 56 | } ec_mbxerrorlist_t; 57 | 58 | char estring[EC_MAXERRORNAME]; 59 | 60 | /** SDO error list definition */ 61 | const ec_sdoerrorlist_t ec_sdoerrorlist[] = { 62 | {0x00000000, "No error" }, 63 | {0x05030000, "Toggle bit not changed" }, 64 | {0x05040000, "SDO protocol timeout" }, 65 | {0x05040001, "Client/Server command specifier not valid or unknown" }, 66 | {0x05040005, "Out of memory" }, 67 | {0x06010000, "Unsupported access to an object" }, 68 | {0x06010001, "Attempt to read to a write only object" }, 69 | {0x06010002, "Attempt to write to a read only object" }, 70 | {0x06010003, "Subindex can not be written, SI0 must be 0 for write access" }, 71 | {0x06010004, "SDO Complete access not supported for variable length objects" }, 72 | {0x06010005, "Object length exceeds mailbox size" }, 73 | {0x06010006, "Object mapped to RxPDO, SDO download blocked" }, 74 | {0x06020000, "The object does not exist in the object directory" }, 75 | {0x06040041, "The object can not be mapped into the PDO" }, 76 | {0x06040042, "The number and length of the objects to be mapped would exceed the PDO length" }, 77 | {0x06040043, "General parameter incompatibility reason" }, 78 | {0x06040047, "General internal incompatibility in the device" }, 79 | {0x06060000, "Access failed due to a hardware error" }, 80 | {0x06070010, "Data type does not match, length of service parameter does not match" }, 81 | {0x06070012, "Data type does not match, length of service parameter too high" }, 82 | {0x06070013, "Data type does not match, length of service parameter too low" }, 83 | {0x06090011, "Subindex does not exist" }, 84 | {0x06090030, "Value range of parameter exceeded (only for write access)" }, 85 | {0x06090031, "Value of parameter written too high" }, 86 | {0x06090032, "Value of parameter written too low" }, 87 | {0x06090036, "Maximum value is less than minimum value" }, 88 | {0x08000000, "General error" }, 89 | {0x08000020, "Data cannot be transferred or stored to the application" }, 90 | {0x08000021, "Data cannot be transferred or stored to the application because of local control" }, 91 | {0x08000022, "Data cannot be transferred or stored to the application because of the present device state" }, 92 | {0x08000023, "Object dictionary dynamic generation fails or no object dictionary is present" }, 93 | {0xffffffff, "Unknown" } 94 | }; 95 | 96 | /** AL status code list definition */ 97 | const ec_ALstatuscodelist_t ec_ALstatuscodelist[] = { 98 | {0x0000 , "No error" }, 99 | {0x0001 , "Unspecified error" }, 100 | {0x0002 , "No memory" }, 101 | {0x0011 , "Invalid requested state change" }, 102 | {0x0012 , "Unknown requested state" }, 103 | {0x0013 , "Bootstrap not supported" }, 104 | {0x0014 , "No valid firmware" }, 105 | {0x0015 , "Invalid mailbox configuration" }, 106 | {0x0016 , "Invalid mailbox configuration" }, 107 | {0x0017 , "Invalid sync manager configuration" }, 108 | {0x0018 , "No valid inputs available" }, 109 | {0x0019 , "No valid outputs" }, 110 | {0x001A , "Synchronization error" }, 111 | {0x001B , "Sync manager watchdog" }, 112 | {0x001C , "Invalid sync Manager types" }, 113 | {0x001D , "Invalid output configuration" }, 114 | {0x001E , "Invalid input configuration" }, 115 | {0x001F , "Invalid watchdog configuration" }, 116 | {0x0020 , "Slave needs cold start" }, 117 | {0x0021 , "Slave needs INIT" }, 118 | {0x0022 , "Slave needs PREOP" }, 119 | {0x0023 , "Slave needs SAFEOP" }, 120 | {0x0024 , "Invalid input mapping" }, 121 | {0x0025 , "Invalid output mapping" }, 122 | {0x0026 , "Inconsistent settings" }, 123 | {0x0027 , "Freerun not supported" }, 124 | {0x0028 , "Synchronisation not supported" }, 125 | {0x0029 , "Freerun needs 3buffer mode" }, 126 | {0x002A , "Background watchdog" }, 127 | {0x002B , "No valid Inputs and Outputs" }, 128 | {0x002C , "Fatal sync error" }, 129 | {0x002D , "No sync error" }, // was "Invalid Output FMMU Configuration" 130 | {0x002E , "Invalid input FMMU configuration" }, 131 | {0x0030 , "Invalid DC SYNC configuration" }, 132 | {0x0031 , "Invalid DC latch configuration" }, 133 | {0x0032 , "PLL error" }, 134 | {0x0033 , "DC sync IO error" }, 135 | {0x0034 , "DC sync timeout error" }, 136 | {0x0035 , "DC invalid sync cycle time" }, 137 | {0x0036 , "DC invalid sync0 cycle time" }, 138 | {0x0037 , "DC invalid sync1 cycle time" }, 139 | {0x0041 , "MBX_AOE" }, 140 | {0x0042 , "MBX_EOE" }, 141 | {0x0043 , "MBX_COE" }, 142 | {0x0044 , "MBX_FOE" }, 143 | {0x0045 , "MBX_SOE" }, 144 | {0x004F , "MBX_VOE" }, 145 | {0x0050 , "EEPROM no access" }, 146 | {0x0051 , "EEPROM error" }, 147 | {0x0060 , "Slave restarted locally" }, 148 | {0x0061 , "Device identification value updated" }, 149 | {0x00f0 , "Application controller available" }, 150 | {0xffff , "Unknown" } 151 | }; 152 | 153 | /** SoE error list definition */ 154 | const ec_soeerrorlist_t ec_soeerrorlist[] = { 155 | {0x0000, "No error" }, 156 | {0x1001, "No IDN" }, 157 | {0x1009, "Invalid access to element 1" }, 158 | {0x2001, "No Name" }, 159 | {0x2002, "Name transmission too short" }, 160 | {0x2003, "Name transmission too long" }, 161 | {0x2004, "Name cannot be changed (read only)" }, 162 | {0x2005, "Name is write-protected at this time" }, 163 | {0x3002, "Attribute transmission too short" }, 164 | {0x3003, "Attribute transmission too long" }, 165 | {0x3004, "Attribute cannot be changed (read only)" }, 166 | {0x3005, "Attribute is write-protected at this time" }, 167 | {0x4001, "No units" }, 168 | {0x4002, "Unit transmission too short" }, 169 | {0x4003, "Unit transmission too long" }, 170 | {0x4004, "Unit cannot be changed (read only)" }, 171 | {0x4005, "Unit is write-protected at this time" }, 172 | {0x5001, "No minimum input value" }, 173 | {0x5002, "Minimum input value transmission too short" }, 174 | {0x5003, "Minimum input value transmission too long" }, 175 | {0x5004, "Minimum input value cannot be changed (read only)" }, 176 | {0x5005, "Minimum input value is write-protected at this time" }, 177 | {0x6001, "No maximum input value" }, 178 | {0x6002, "Maximum input value transmission too short" }, 179 | {0x6003, "Maximum input value transmission too long" }, 180 | {0x6004, "Maximum input value cannot be changed (read only)" }, 181 | {0x6005, "Maximum input value is write-protected at this time" }, 182 | {0x7002, "Operation data transmission too short" }, 183 | {0x7003, "Operation data transmission too long" }, 184 | {0x7004, "Operation data cannot be changed (read only)" }, 185 | {0x7005, "Operation data is write-protected at this time (state)" }, 186 | {0x7006, "Operation data is smaller than the minimum input value" }, 187 | {0x7007, "Operation data is smaller than the maximum input value" }, 188 | {0x7008, "Invalid operation data:Configured IDN will not be supported" }, 189 | {0x7009, "Operation data write protected by a password" }, 190 | {0x700A, "Operation data is write protected, it is configured cyclically" }, 191 | {0x700B, "Invalid indirect addressing: (e.g., data container, list handling)" }, 192 | {0x700C, "Operation data is write protected, due to other settings" }, 193 | {0x700D, "Reserved" }, 194 | {0x7010, "Procedure command already active" }, 195 | {0x7011, "Procedure command not interruptible" }, 196 | {0x7012, "Procedure command at this time not executable (state)" }, 197 | {0x7013, "Procedure command not executable (invalid or false parameters)" }, 198 | {0x7014, "No data state" }, 199 | {0x8001, "No default value" }, 200 | {0x8002, "Default value transmission too long" }, 201 | {0x8004, "Default value cannot be changed, read only" }, 202 | {0x800A, "Invalid drive number" }, 203 | {0x800A, "General error" }, 204 | {0x800A, "No element addressed" }, 205 | {0xffff, "Unknown" } 206 | }; 207 | 208 | /** MBX error list definition */ 209 | const ec_mbxerrorlist_t ec_mbxerrorlist[] = { 210 | {0x0000, "No error" }, 211 | {0x0001, "Syntax of 6 octet Mailbox Header is wrong" }, 212 | {0x0002, "The mailbox protocol is not supported" }, 213 | {0x0003, "Channel Field contains wrong value"}, 214 | {0x0004, "The service is no supported"}, 215 | {0x0005, "Invalid mailbox header"}, 216 | {0x0006, "Length of received mailbox data is too short"}, 217 | {0x0007, "No more memory in slave"}, 218 | {0x0008, "The length of data is inconsistent"}, 219 | {0xffff, "Unknown"} 220 | }; 221 | 222 | /** Look up text string that belongs to SDO error code. 223 | * 224 | * @param[in] sdoerrorcode = SDO error code as defined in EtherCAT protocol 225 | * @return readable string 226 | */ 227 | const char* ec_sdoerror2string( uint32 sdoerrorcode) 228 | { 229 | int i = 0; 230 | 231 | while ( (ec_sdoerrorlist[i].errorcode != 0xffffffffUL) && 232 | (ec_sdoerrorlist[i].errorcode != sdoerrorcode) ) 233 | { 234 | i++; 235 | } 236 | 237 | return ec_sdoerrorlist[i].errordescription; 238 | } 239 | 240 | /** Look up text string that belongs to AL status code. 241 | * 242 | * @param[in] ALstatuscode = AL status code as defined in EtherCAT protocol 243 | * @return readable string 244 | */ 245 | char* ec_ALstatuscode2string( uint16 ALstatuscode) 246 | { 247 | int i = 0; 248 | 249 | while ( (ec_ALstatuscodelist[i].ALstatuscode != 0xffff) && 250 | (ec_ALstatuscodelist[i].ALstatuscode != ALstatuscode) ) 251 | { 252 | i++; 253 | } 254 | 255 | return (char *) ec_ALstatuscodelist[i].ALstatuscodedescription; 256 | } 257 | 258 | /** Look up text string that belongs to SoE error code. 259 | * 260 | * @param[in] errorcode = SoE error code as defined in EtherCAT protocol 261 | * @return readable string 262 | */ 263 | char* ec_soeerror2string( uint16 errorcode) 264 | { 265 | int i = 0; 266 | 267 | while ( (ec_soeerrorlist[i].errorcode != 0xffff) && 268 | (ec_soeerrorlist[i].errorcode != errorcode) ) 269 | { 270 | i++; 271 | } 272 | 273 | return (char *) ec_soeerrorlist[i].errordescription; 274 | } 275 | 276 | /** Look up text string that belongs to MBX error code. 277 | * 278 | * @param[in] errorcode = MBX error code as defined in EtherCAT protocol 279 | * @return readable string 280 | */ 281 | char* ec_mbxerror2string( uint16 errorcode) 282 | { 283 | int i = 0; 284 | 285 | while ( (ec_mbxerrorlist[i].errorcode != 0xffff) && 286 | (ec_mbxerrorlist[i].errorcode != errorcode) ) 287 | { 288 | i++; 289 | } 290 | 291 | return (char *) ec_mbxerrorlist[i].errordescription; 292 | } 293 | 294 | /** Look up error in ec_errorlist and convert to text string. 295 | * 296 | * @param[in] context = context struct 297 | * @return readable string 298 | */ 299 | char* ecx_elist2string(ecx_contextt *context) 300 | { 301 | ec_errort Ec; 302 | char timestr[20]; 303 | 304 | if (ecx_poperror(context, &Ec)) 305 | { 306 | // TODO only use usec counter instead of real time clock 307 | #if 0 308 | sprintf(timestr, "Time:%12.3f", Ec.Time.sec + (Ec.Time.usec / 1000000.0) ); 309 | #else 310 | sprintf(timestr, "Time:%10lu", Ec.Time ); 311 | #endif 312 | switch (Ec.Etype) 313 | { 314 | case EC_ERR_TYPE_SDO_ERROR: 315 | { 316 | sprintf(estring, "%s SDO slave:%d index:%4.4x.%2.2x error:%8.8x %s\n", 317 | timestr, Ec.Slave, Ec.Index, Ec.SubIdx, (unsigned)Ec.AbortCode, ec_sdoerror2string(Ec.AbortCode)); 318 | break; 319 | } 320 | case EC_ERR_TYPE_EMERGENCY: 321 | { 322 | sprintf(estring, "%s EMERGENCY slave:%d error:%4.4x\n", 323 | timestr, Ec.Slave, Ec.ErrorCode); 324 | break; 325 | } 326 | case EC_ERR_TYPE_PACKET_ERROR: 327 | { 328 | sprintf(estring, "%s PACKET slave:%d index:%4.4x.%2.2x error:%d\n", 329 | timestr, Ec.Slave, Ec.Index, Ec.SubIdx, Ec.ErrorCode); 330 | break; 331 | } 332 | case EC_ERR_TYPE_SDOINFO_ERROR: 333 | { 334 | sprintf(estring, "%s SDO slave:%d index:%4.4x.%2.2x error:%8.8x %s\n", 335 | timestr, Ec.Slave, Ec.Index, Ec.SubIdx, (unsigned)Ec.AbortCode, ec_sdoerror2string(Ec.AbortCode)); 336 | break; 337 | } 338 | case EC_ERR_TYPE_SOE_ERROR: 339 | { 340 | sprintf(estring, "%s SoE slave:%d IDN:%4.4x error:%4.4x %s\n", 341 | timestr, Ec.Slave, Ec.Index, (unsigned)Ec.AbortCode, ec_soeerror2string(Ec.ErrorCode)); 342 | break; 343 | } 344 | case EC_ERR_TYPE_MBX_ERROR: 345 | { 346 | sprintf(estring, "%s MBX slave:%d error:%4.4x %s\n", 347 | timestr, Ec.Slave, Ec.ErrorCode, ec_mbxerror2string(Ec.ErrorCode)); 348 | break; 349 | } 350 | default: 351 | { 352 | sprintf(estring, "%s error:%8.8x\n", 353 | timestr, (unsigned)Ec.AbortCode); 354 | break; 355 | } 356 | } 357 | return (char*) estring; 358 | } 359 | else 360 | { 361 | return ""; 362 | } 363 | } 364 | 365 | #ifdef EC_VER1 366 | char* ec_elist2string(void) 367 | { 368 | return ecx_elist2string(&ecx_context); 369 | } 370 | #endif 371 | -------------------------------------------------------------------------------- /src/soem/ethercatprint.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * Headerfile for ethercatprint.c 9 | */ 10 | 11 | #ifndef _ethercatprint_ 12 | #define _ethercatprint_ 13 | 14 | #ifdef __cplusplus 15 | extern "C" 16 | { 17 | #endif 18 | 19 | char* ec_sdoerror2string( uint32 sdoerrorcode); 20 | char* ec_ALstatuscode2string( uint16 ALstatuscode); 21 | char* ec_soeerror2string( uint16 errorcode); 22 | char* ecx_elist2string(ecx_contextt *context); 23 | 24 | #ifdef EC_VER1 25 | char* ec_elist2string(void); 26 | #endif 27 | 28 | #ifdef __cplusplus 29 | } 30 | #endif 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/soem/ethercatsoe.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * Servo over EtherCAT (SoE) Module. 9 | */ 10 | 11 | #include 12 | #include 13 | #include "osal/osal.h" 14 | #include "osal/osal.h" 15 | #include "ethercattype.h" 16 | #include "ethercatbase.h" 17 | #include "ethercatmain.h" 18 | #include "ethercatsoe.h" 19 | 20 | #define EC_SOE_MAX_DRIVES 8 21 | 22 | /** SoE (Servo over EtherCAT) mailbox structure */ 23 | PACKED_BEGIN 24 | typedef struct PACKED 25 | { 26 | ec_mbxheadert MbxHeader; 27 | uint8 opCode :3; 28 | uint8 incomplete :1; 29 | uint8 error :1; 30 | uint8 driveNo :3; 31 | uint8 elementflags; 32 | union 33 | { 34 | uint16 idn; 35 | uint16 fragmentsleft; 36 | }; 37 | } ec_SoEt; 38 | PACKED_END 39 | 40 | /** Report SoE error. 41 | * 42 | * @param[in] context = context struct 43 | * @param[in] Slave = Slave number 44 | * @param[in] idn = IDN that generated error 45 | * @param[in] Error = Error code, see EtherCAT documentation for list 46 | */ 47 | void ecx_SoEerror(ecx_contextt *context, uint16 Slave, uint16 idn, uint16 Error) 48 | { 49 | ec_errort Ec; 50 | 51 | memset(&Ec, 0, sizeof(Ec)); 52 | Ec.Time = osal_current_time(); 53 | Ec.Slave = Slave; 54 | Ec.Index = idn; 55 | Ec.SubIdx = 0; 56 | *(context->ecaterror) = TRUE; 57 | Ec.Etype = EC_ERR_TYPE_SOE_ERROR; 58 | Ec.ErrorCode = Error; 59 | ecx_pusherror(context, &Ec); 60 | } 61 | 62 | /** SoE read, blocking. 63 | * 64 | * The IDN object of the selected slave and DriveNo is read. If a response 65 | * is larger than the mailbox size then the response is segmented. The function 66 | * will combine all segments and copy them to the parameter buffer. 67 | * 68 | * @param[in] context = context struct 69 | * @param[in] slave = Slave number 70 | * @param[in] driveNo = Drive number in slave 71 | * @param[in] elementflags = Flags to select what properties of IDN are to be transferred. 72 | * @param[in] idn = IDN. 73 | * @param[in,out] psize = Size in bytes of parameter buffer, returns bytes read from SoE. 74 | * @param[out] p = Pointer to parameter buffer 75 | * @param[in] timeout = Timeout in us, standard is EC_TIMEOUTRXM 76 | * @return Workcounter from last slave response 77 | */ 78 | int ecx_SoEread(ecx_contextt *context, uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int *psize, void *p, int timeout) 79 | { 80 | ec_SoEt *SoEp, *aSoEp; 81 | uint16 totalsize, framedatasize; 82 | int wkc; 83 | uint8 *bp; 84 | uint8 *mp; 85 | uint16 *errorcode; 86 | ec_mbxbuft MbxIn, MbxOut; 87 | uint8 cnt; 88 | boolean NotLast; 89 | 90 | ec_clearmbx(&MbxIn); 91 | /* Empty slave out mailbox if something is in. Timeout set to 0 */ 92 | wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0); 93 | ec_clearmbx(&MbxOut); 94 | aSoEp = (ec_SoEt *)&MbxIn; 95 | SoEp = (ec_SoEt *)&MbxOut; 96 | SoEp->MbxHeader.length = htoes(sizeof(ec_SoEt) - sizeof(ec_mbxheadert)); 97 | SoEp->MbxHeader.address = htoes(0x0000); 98 | SoEp->MbxHeader.priority = 0x00; 99 | /* get new mailbox count value, used as session handle */ 100 | cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt); 101 | context->slavelist[slave].mbx_cnt = cnt; 102 | SoEp->MbxHeader.mbxtype = ECT_MBXT_SOE + (cnt << 4); /* SoE */ 103 | SoEp->opCode = ECT_SOE_READREQ; 104 | SoEp->incomplete = 0; 105 | SoEp->error = 0; 106 | SoEp->driveNo = driveNo; 107 | SoEp->elementflags = elementflags; 108 | SoEp->idn = htoes(idn); 109 | totalsize = 0; 110 | bp = p; 111 | mp = (uint8 *)&MbxIn + sizeof(ec_SoEt); 112 | NotLast = TRUE; 113 | /* send SoE request to slave */ 114 | wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM); 115 | if (wkc > 0) /* succeeded to place mailbox in slave ? */ 116 | { 117 | while (NotLast) 118 | { 119 | /* clean mailboxbuffer */ 120 | ec_clearmbx(&MbxIn); 121 | /* read slave response */ 122 | wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout); 123 | if (wkc > 0) /* succeeded to read slave response ? */ 124 | { 125 | /* slave response should be SoE, ReadRes */ 126 | if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) && 127 | (aSoEp->opCode == ECT_SOE_READRES) && 128 | (aSoEp->error == 0) && 129 | (aSoEp->driveNo == driveNo) && 130 | (aSoEp->elementflags == elementflags)) 131 | { 132 | framedatasize = etohs(aSoEp->MbxHeader.length) - sizeof(ec_SoEt) + sizeof(ec_mbxheadert); 133 | totalsize += framedatasize; 134 | /* Does parameter fit in parameter buffer ? */ 135 | if (totalsize <= *psize) 136 | { 137 | /* copy parameter data in parameter buffer */ 138 | memcpy(bp, mp, framedatasize); 139 | /* increment buffer pointer */ 140 | bp += framedatasize; 141 | } 142 | else 143 | { 144 | framedatasize -= totalsize - *psize; 145 | totalsize = *psize; 146 | /* copy parameter data in parameter buffer */ 147 | if (framedatasize > 0) memcpy(bp, mp, framedatasize); 148 | } 149 | 150 | if (!aSoEp->incomplete) 151 | { 152 | NotLast = FALSE; 153 | *psize = totalsize; 154 | } 155 | } 156 | /* other slave response */ 157 | else 158 | { 159 | NotLast = FALSE; 160 | if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) && 161 | (aSoEp->opCode == ECT_SOE_READRES) && 162 | (aSoEp->error == 1)) 163 | { 164 | mp = (uint8 *)&MbxIn + (etohs(aSoEp->MbxHeader.length) + sizeof(ec_mbxheadert) - sizeof(uint16)); 165 | errorcode = (uint16 *)mp; 166 | ecx_SoEerror(context, slave, idn, *errorcode); 167 | } 168 | else 169 | { 170 | ecx_packeterror(context, slave, idn, 0, 1); /* Unexpected frame returned */ 171 | } 172 | wkc = 0; 173 | } 174 | } 175 | else 176 | { 177 | NotLast = FALSE; 178 | ecx_packeterror(context, slave, idn, 0, 4); /* no response */ 179 | } 180 | } 181 | } 182 | return wkc; 183 | } 184 | 185 | /** SoE write, blocking. 186 | * 187 | * The IDN object of the selected slave and DriveNo is written. If a response 188 | * is larger than the mailbox size then the response is segmented. 189 | * 190 | * @param[in] context = context struct 191 | * @param[in] slave = Slave number 192 | * @param[in] driveNo = Drive number in slave 193 | * @param[in] elementflags = Flags to select what properties of IDN are to be transferred. 194 | * @param[in] idn = IDN. 195 | * @param[in] psize = Size in bytes of parameter buffer. 196 | * @param[out] p = Pointer to parameter buffer 197 | * @param[in] timeout = Timeout in us, standard is EC_TIMEOUTRXM 198 | * @return Workcounter from last slave response 199 | */ 200 | int ecx_SoEwrite(ecx_contextt *context, uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int psize, void *p, int timeout) 201 | { 202 | ec_SoEt *SoEp, *aSoEp; 203 | uint16 framedatasize, maxdata; 204 | int wkc; 205 | uint8 *mp; 206 | uint8 *hp; 207 | uint16 *errorcode; 208 | ec_mbxbuft MbxIn, MbxOut; 209 | uint8 cnt; 210 | boolean NotLast; 211 | 212 | ec_clearmbx(&MbxIn); 213 | /* Empty slave out mailbox if something is in. Timeout set to 0 */ 214 | wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, 0); 215 | ec_clearmbx(&MbxOut); 216 | aSoEp = (ec_SoEt *)&MbxIn; 217 | SoEp = (ec_SoEt *)&MbxOut; 218 | SoEp->MbxHeader.address = htoes(0x0000); 219 | SoEp->MbxHeader.priority = 0x00; 220 | SoEp->opCode = ECT_SOE_WRITEREQ; 221 | SoEp->error = 0; 222 | SoEp->driveNo = driveNo; 223 | SoEp->elementflags = elementflags; 224 | hp = p; 225 | mp = (uint8 *)&MbxOut + sizeof(ec_SoEt); 226 | maxdata = context->slavelist[slave].mbx_l - sizeof(ec_SoEt); 227 | NotLast = TRUE; 228 | while (NotLast) 229 | { 230 | framedatasize = psize; 231 | NotLast = FALSE; 232 | SoEp->idn = htoes(idn); 233 | SoEp->incomplete = 0; 234 | if (framedatasize > maxdata) 235 | { 236 | framedatasize = maxdata; /* segmented transfer needed */ 237 | NotLast = TRUE; 238 | SoEp->incomplete = 1; 239 | SoEp->fragmentsleft = psize / maxdata; 240 | } 241 | SoEp->MbxHeader.length = htoes(sizeof(ec_SoEt) - sizeof(ec_mbxheadert) + framedatasize); 242 | /* get new mailbox counter, used for session handle */ 243 | cnt = ec_nextmbxcnt(context->slavelist[slave].mbx_cnt); 244 | context->slavelist[slave].mbx_cnt = cnt; 245 | SoEp->MbxHeader.mbxtype = ECT_MBXT_SOE + (cnt << 4); /* SoE */ 246 | /* copy parameter data to mailbox */ 247 | memcpy(mp, hp, framedatasize); 248 | hp += framedatasize; 249 | psize -= framedatasize; 250 | /* send SoE request to slave */ 251 | wkc = ecx_mbxsend(context, slave, (ec_mbxbuft *)&MbxOut, EC_TIMEOUTTXM); 252 | if (wkc > 0) /* succeeded to place mailbox in slave ? */ 253 | { 254 | if (!NotLast || !ecx_mbxempty(context, slave, timeout)) 255 | { 256 | /* clean mailboxbuffer */ 257 | ec_clearmbx(&MbxIn); 258 | /* read slave response */ 259 | wkc = ecx_mbxreceive(context, slave, (ec_mbxbuft *)&MbxIn, timeout); 260 | if (wkc > 0) /* succeeded to read slave response ? */ 261 | { 262 | NotLast = FALSE; 263 | /* slave response should be SoE, WriteRes */ 264 | if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) && 265 | (aSoEp->opCode == ECT_SOE_WRITERES) && 266 | (aSoEp->error == 0) && 267 | (aSoEp->driveNo == driveNo) && 268 | (aSoEp->elementflags == elementflags)) 269 | { 270 | /* SoE write succeeded */ 271 | } 272 | /* other slave response */ 273 | else 274 | { 275 | if (((aSoEp->MbxHeader.mbxtype & 0x0f) == ECT_MBXT_SOE) && 276 | (aSoEp->opCode == ECT_SOE_READRES) && 277 | (aSoEp->error == 1)) 278 | { 279 | mp = (uint8 *)&MbxIn + (etohs(aSoEp->MbxHeader.length) + sizeof(ec_mbxheadert) - sizeof(uint16)); 280 | errorcode = (uint16 *)mp; 281 | ecx_SoEerror(context, slave, idn, *errorcode); 282 | } 283 | else 284 | { 285 | ecx_packeterror(context, slave, idn, 0, 1); /* Unexpected frame returned */ 286 | } 287 | wkc = 0; 288 | } 289 | } 290 | else 291 | { 292 | ecx_packeterror(context, slave, idn, 0, 4); /* no response */ 293 | } 294 | } 295 | } 296 | } 297 | return wkc; 298 | } 299 | 300 | /** SoE read AT and MTD mapping. 301 | * 302 | * SoE has standard indexes defined for mapping. This function 303 | * tries to read them and collect a full input and output mapping size 304 | * of designated slave. 305 | * 306 | * @param[in] context = context struct 307 | * @param[in] slave = Slave number 308 | * @param[out] Osize = Size in bits of output mapping (MTD) found 309 | * @param[out] Isize = Size in bits of input mapping (AT) found 310 | * @return >0 if mapping successful. 311 | */ 312 | int ecx_readIDNmap(ecx_contextt *context, uint16 slave, int *Osize, int *Isize) 313 | { 314 | int retVal = 0; 315 | int wkc; 316 | int psize; 317 | int driveNr; 318 | uint16 entries, itemcount; 319 | ec_SoEmappingt SoEmapping; 320 | ec_SoEattributet SoEattribute; 321 | 322 | *Isize = 0; 323 | *Osize = 0; 324 | for(driveNr = 0; driveNr < EC_SOE_MAX_DRIVES; driveNr++) 325 | { 326 | psize = sizeof(SoEmapping); 327 | /* read output mapping via SoE */ 328 | wkc = ecx_SoEread(context, slave, driveNr, EC_SOE_VALUE_B, EC_IDN_MDTCONFIG, &psize, &SoEmapping, EC_TIMEOUTRXM); 329 | if ((wkc > 0) && (psize >= 4) && ((entries = etohs(SoEmapping.currentlength) / 2) > 0) && (entries <= EC_SOE_MAXMAPPING)) 330 | { 331 | /* command word (uint16) is always mapped but not in list */ 332 | *Osize = 16; 333 | for (itemcount = 0 ; itemcount < entries ; itemcount++) 334 | { 335 | psize = sizeof(SoEattribute); 336 | /* read attribute of each IDN in mapping list */ 337 | wkc = ecx_SoEread(context, slave, driveNr, EC_SOE_ATTRIBUTE_B, SoEmapping.idn[itemcount], &psize, &SoEattribute, EC_TIMEOUTRXM); 338 | if ((wkc > 0) && (!SoEattribute.list)) 339 | { 340 | /* length : 0 = 8bit, 1 = 16bit .... */ 341 | *Osize += (int)8 << SoEattribute.length; 342 | } 343 | } 344 | } 345 | psize = sizeof(SoEmapping); 346 | /* read input mapping via SoE */ 347 | wkc = ecx_SoEread(context, slave, driveNr, EC_SOE_VALUE_B, EC_IDN_ATCONFIG, &psize, &SoEmapping, EC_TIMEOUTRXM); 348 | if ((wkc > 0) && (psize >= 4) && ((entries = etohs(SoEmapping.currentlength) / 2) > 0) && (entries <= EC_SOE_MAXMAPPING)) 349 | { 350 | /* status word (uint16) is always mapped but not in list */ 351 | *Isize = 16; 352 | for (itemcount = 0 ; itemcount < entries ; itemcount++) 353 | { 354 | psize = sizeof(SoEattribute); 355 | /* read attribute of each IDN in mapping list */ 356 | wkc = ecx_SoEread(context, slave, driveNr, EC_SOE_ATTRIBUTE_B, SoEmapping.idn[itemcount], &psize, &SoEattribute, EC_TIMEOUTRXM); 357 | if ((wkc > 0) && (!SoEattribute.list)) 358 | { 359 | /* length : 0 = 8bit, 1 = 16bit .... */ 360 | *Isize += (int)8 << SoEattribute.length; 361 | } 362 | } 363 | } 364 | } 365 | 366 | /* found some I/O bits ? */ 367 | if ((*Isize > 0) || (*Osize > 0)) 368 | { 369 | retVal = 1; 370 | } 371 | return retVal; 372 | } 373 | 374 | #ifdef EC_VER1 375 | int ec_SoEread(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int *psize, void *p, int timeout) 376 | { 377 | return ecx_SoEread(&ecx_context, slave, driveNo, elementflags, idn, psize, p, timeout); 378 | } 379 | 380 | int ec_SoEwrite(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int psize, void *p, int timeout) 381 | { 382 | return ecx_SoEwrite(&ecx_context, slave, driveNo, elementflags, idn, psize, p, timeout); 383 | } 384 | 385 | int ec_readIDNmap(uint16 slave, int *Osize, int *Isize) 386 | { 387 | return ecx_readIDNmap(&ecx_context, slave, Osize, Isize); 388 | } 389 | #endif 390 | -------------------------------------------------------------------------------- /src/soem/ethercatsoe.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * Headerfile for ethercatsoe.c 9 | */ 10 | 11 | #ifndef _ethercatsoe_ 12 | #define _ethercatsoe_ 13 | 14 | #ifdef __cplusplus 15 | extern "C" 16 | { 17 | #endif 18 | 19 | #define EC_SOE_DATASTATE_B 0x01 20 | #define EC_SOE_NAME_B 0x02 21 | #define EC_SOE_ATTRIBUTE_B 0x04 22 | #define EC_SOE_UNIT_B 0x08 23 | #define EC_SOE_MIN_B 0x10 24 | #define EC_SOE_MAX_B 0x20 25 | #define EC_SOE_VALUE_B 0x40 26 | #define EC_SOE_DEFAULT_B 0x80 27 | 28 | #define EC_SOE_MAXNAME 60 29 | #define EC_SOE_MAXMAPPING 64 30 | 31 | #define EC_IDN_MDTCONFIG 24 32 | #define EC_IDN_ATCONFIG 16 33 | 34 | /** SoE name structure */ 35 | PACKED_BEGIN 36 | typedef struct PACKED 37 | { 38 | /** current length in bytes of list */ 39 | uint16 currentlength; 40 | /** maximum length in bytes of list */ 41 | uint16 maxlength; 42 | char name[EC_SOE_MAXNAME]; 43 | } ec_SoEnamet; 44 | PACKED_END 45 | 46 | /** SoE list structure */ 47 | PACKED_BEGIN 48 | typedef struct PACKED 49 | { 50 | /** current length in bytes of list */ 51 | uint16 currentlength; 52 | /** maximum length in bytes of list */ 53 | uint16 maxlength; 54 | union 55 | { 56 | uint8 byte[8]; 57 | uint16 word[4]; 58 | uint32 dword[2]; 59 | uint64 lword[1]; 60 | }; 61 | } ec_SoElistt; 62 | PACKED_END 63 | 64 | /** SoE IDN mapping structure */ 65 | PACKED_BEGIN 66 | typedef struct PACKED 67 | { 68 | /** current length in bytes of list */ 69 | uint16 currentlength; 70 | /** maximum length in bytes of list */ 71 | uint16 maxlength; 72 | uint16 idn[EC_SOE_MAXMAPPING]; 73 | } ec_SoEmappingt; 74 | PACKED_END 75 | 76 | #define EC_SOE_LENGTH_1 0x00 77 | #define EC_SOE_LENGTH_2 0x01 78 | #define EC_SOE_LENGTH_4 0x02 79 | #define EC_SOE_LENGTH_8 0x03 80 | #define EC_SOE_TYPE_BINARY 0x00 81 | #define EC_SOE_TYPE_UINT 0x01 82 | #define EC_SOE_TYPE_INT 0x02 83 | #define EC_SOE_TYPE_HEX 0x03 84 | #define EC_SOE_TYPE_STRING 0x04 85 | #define EC_SOE_TYPE_IDN 0x05 86 | #define EC_SOE_TYPE_FLOAT 0x06 87 | #define EC_SOE_TYPE_PARAMETER 0x07 88 | 89 | /** SoE attribute structure */ 90 | PACKED_BEGIN 91 | typedef struct PACKED 92 | { 93 | /** evaluation factor for display purposes */ 94 | uint32 evafactor :16; 95 | /** length of IDN element(s) */ 96 | uint32 length :2; 97 | /** IDN is list */ 98 | uint32 list :1; 99 | /** IDN is command */ 100 | uint32 command :1; 101 | /** datatype */ 102 | uint32 datatype :3; 103 | uint32 reserved1 :1; 104 | /** decimals to display if float datatype */ 105 | uint32 decimals :4; 106 | /** write protected in pre-op */ 107 | uint32 wppreop :1; 108 | /** write protected in safe-op */ 109 | uint32 wpsafeop :1; 110 | /** write protected in op */ 111 | uint32 wpop :1; 112 | uint32 reserved2 :1; 113 | } ec_SoEattributet; 114 | PACKED_END 115 | 116 | #ifdef EC_VER1 117 | int ec_SoEread(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int *psize, void *p, int timeout); 118 | int ec_SoEwrite(uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int psize, void *p, int timeout); 119 | int ec_readIDNmap(uint16 slave, int *Osize, int *Isize); 120 | #endif 121 | 122 | int ecx_SoEread(ecx_contextt *context, uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int *psize, void *p, int timeout); 123 | int ecx_SoEwrite(ecx_contextt *context, uint16 slave, uint8 driveNo, uint8 elementflags, uint16 idn, int psize, void *p, int timeout); 124 | int ecx_readIDNmap(ecx_contextt *context, uint16 slave, int *Osize, int *Isize); 125 | 126 | #ifdef __cplusplus 127 | } 128 | #endif 129 | 130 | #endif -------------------------------------------------------------------------------- /src/soem/ethercattype.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the GNU General Public License version 2 with exceptions. See 3 | * LICENSE file in the project root for full license information 4 | */ 5 | 6 | /** \file 7 | * \brief 8 | * General typedefs and defines for EtherCAT. 9 | * 10 | * Defines that could need optimisation for specific applications 11 | * are the EC_TIMEOUTxxx. Assumptions for the standard settings are a 12 | * standard linux PC or laptop and a wired connection to maximal 100 slaves. 13 | * For use with wireless connections or lots of slaves the timeouts need 14 | * increasing. For fast systems running Xenomai and RT-net or alike the 15 | * timeouts need to be shorter. 16 | */ 17 | 18 | #ifndef _EC_TYPE_H 19 | #define _EC_TYPE_H 20 | 21 | #ifdef __cplusplus 22 | extern "C" 23 | { 24 | #endif 25 | 26 | /** Define Little or Big endian target */ 27 | #define EC_LITTLE_ENDIAN 28 | 29 | /** define EC_VER1 if version 1 default context and functions are needed 30 | * comment if application uses only ecx_ functions and own context */ 31 | #define EC_VER1 32 | 33 | #include "osal/osal.h" 34 | 35 | /** return value general error */ 36 | #define EC_ERROR -3 37 | /** return value no frame returned */ 38 | #define EC_NOFRAME -1 39 | /** return value unknown frame received */ 40 | #define EC_OTHERFRAME -2 41 | /** maximum EtherCAT frame length in bytes */ 42 | #define EC_MAXECATFRAME 1518 43 | /** maximum EtherCAT LRW frame length in bytes */ 44 | /* MTU - Ethernet header - length - datagram header - WCK - FCS */ 45 | #define EC_MAXLRWDATA (EC_MAXECATFRAME - 14 - 2 - 10 - 2 - 4) 46 | /** size of DC datagram used in first LRW frame */ 47 | #define EC_FIRSTDCDATAGRAM 20 48 | /** standard frame buffer size in bytes */ 49 | #define EC_BUFSIZE EC_MAXECATFRAME 50 | /** datagram type EtherCAT */ 51 | #define EC_ECATTYPE 0x1000 52 | /** number of frame buffers per channel (tx, rx1 rx2) */ 53 | #define EC_MAXBUF 2 // TODO shrinked for memory reduction (original = 16) 54 | /** timeout value in us for tx frame to return to rx */ 55 | #define EC_TIMEOUTRET 20000 // TODO For some reason, it takes too much time to receive data (oiginal = 2000) 56 | /** timeout value in us for safe data transfer, max. triple retry */ 57 | #define EC_TIMEOUTRET3 (EC_TIMEOUTRET * 3) 58 | /** timeout value in us for return "safe" variant (f.e. wireless) */ 59 | #define EC_TIMEOUTSAFE 20000 60 | /** timeout value in us for EEPROM access */ 61 | #define EC_TIMEOUTEEP 20000 62 | /** timeout value in us for tx mailbox cycle */ 63 | #define EC_TIMEOUTTXM 20000 64 | /** timeout value in us for rx mailbox cycle */ 65 | #define EC_TIMEOUTRXM 700000 66 | /** timeout value in us for check statechange */ 67 | #define EC_TIMEOUTSTATE 2000000 68 | /** size of EEPROM bitmap cache */ 69 | #define EC_MAXEEPBITMAP 128 70 | /** size of EEPROM cache buffer */ 71 | #define EC_MAXEEPBUF EC_MAXEEPBITMAP << 5 72 | /** default number of retries if wkc <= 0 */ 73 | #define EC_DEFAULTRETRIES 3 74 | /** default group size in 2^x */ 75 | #define EC_LOGGROUPOFFSET 16 76 | 77 | /** definition for frame buffers */ 78 | typedef uint8 ec_bufT[EC_BUFSIZE]; 79 | 80 | /** ethernet header definition */ 81 | PACKED_BEGIN 82 | typedef struct PACKED 83 | { 84 | /** destination MAC */ 85 | uint16 da0,da1,da2; 86 | /** source MAC */ 87 | uint16 sa0,sa1,sa2; 88 | /** ethernet type */ 89 | uint16 etype; 90 | } ec_etherheadert; 91 | PACKED_END 92 | 93 | /** ethernet header size */ 94 | #define ETH_HEADERSIZE sizeof(ec_etherheadert) 95 | 96 | /** EtherCAT datagram header definition */ 97 | PACKED_BEGIN 98 | typedef struct PACKED 99 | { 100 | /** length of EtherCAT datagram */ 101 | uint16 elength; 102 | /** EtherCAT command, see ec_cmdtype */ 103 | uint8 command; 104 | /** index, used in SOEM for Tx to Rx recombination */ 105 | uint8 index; 106 | /** ADP */ 107 | uint16 ADP; 108 | /** ADO */ 109 | uint16 ADO; 110 | /** length of data portion in datagram */ 111 | uint16 dlength; 112 | /** interrupt, currently unused */ 113 | uint16 irpt; 114 | } ec_comt; 115 | PACKED_END 116 | 117 | /** EtherCAT header size */ 118 | #define EC_HEADERSIZE sizeof(ec_comt) 119 | /** size of ec_comt.elength item in EtherCAT header */ 120 | #define EC_ELENGTHSIZE sizeof(uint16) 121 | /** offset position of command in EtherCAT header */ 122 | #define EC_CMDOFFSET EC_ELENGTHSIZE 123 | /** size of workcounter item in EtherCAT datagram */ 124 | #define EC_WKCSIZE sizeof(uint16) 125 | /** definition of datagram follows bit in ec_comt.dlength */ 126 | #define EC_DATAGRAMFOLLOWS (1 << 15) 127 | 128 | /** Possible error codes returned. */ 129 | typedef enum 130 | { 131 | /** No error */ 132 | EC_ERR_OK = 0, 133 | /** Library already initialized. */ 134 | EC_ERR_ALREADY_INITIALIZED, 135 | /** Library not initialized. */ 136 | EC_ERR_NOT_INITIALIZED, 137 | /** Timeout occurred during execution of the function. */ 138 | EC_ERR_TIMEOUT, 139 | /** No slaves were found. */ 140 | EC_ERR_NO_SLAVES, 141 | /** Function failed. */ 142 | EC_ERR_NOK 143 | } ec_err; 144 | 145 | /** Possible EtherCAT slave states */ 146 | typedef enum 147 | { 148 | /** No valid state. */ 149 | EC_STATE_NONE = 0x00, 150 | /** Init state*/ 151 | EC_STATE_INIT = 0x01, 152 | /** Pre-operational. */ 153 | EC_STATE_PRE_OP = 0x02, 154 | /** Boot state*/ 155 | EC_STATE_BOOT = 0x03, 156 | /** Safe-operational. */ 157 | EC_STATE_SAFE_OP = 0x04, 158 | /** Operational */ 159 | EC_STATE_OPERATIONAL = 0x08, 160 | /** Error or ACK error */ 161 | EC_STATE_ACK = 0x10, 162 | EC_STATE_ERROR = 0x10 163 | } ec_state; 164 | 165 | /** Possible buffer states */ 166 | typedef enum 167 | { 168 | /** Empty */ 169 | EC_BUF_EMPTY = 0x00, 170 | /** Allocated, but not filled */ 171 | EC_BUF_ALLOC = 0x01, 172 | /** Transmitted */ 173 | EC_BUF_TX = 0x02, 174 | /** Received, but not consumed */ 175 | EC_BUF_RCVD = 0x03, 176 | /** Cycle completed */ 177 | EC_BUF_COMPLETE = 0x04 178 | } ec_bufstate; 179 | 180 | /** Ethercat data types */ 181 | typedef enum 182 | { 183 | ECT_BOOLEAN = 0x0001, 184 | ECT_INTEGER8 = 0x0002, 185 | ECT_INTEGER16 = 0x0003, 186 | ECT_INTEGER32 = 0x0004, 187 | ECT_UNSIGNED8 = 0x0005, 188 | ECT_UNSIGNED16 = 0x0006, 189 | ECT_UNSIGNED32 = 0x0007, 190 | ECT_REAL32 = 0x0008, 191 | ECT_VISIBLE_STRING = 0x0009, 192 | ECT_OCTET_STRING = 0x000A, 193 | ECT_UNICODE_STRING = 0x000B, 194 | ECT_TIME_OF_DAY = 0x000C, 195 | ECT_TIME_DIFFERENCE = 0x000D, 196 | ECT_DOMAIN = 0x000F, 197 | ECT_INTEGER24 = 0x0010, 198 | ECT_REAL64 = 0x0011, 199 | ECT_INTEGER64 = 0x0015, 200 | ECT_UNSIGNED24 = 0x0016, 201 | ECT_UNSIGNED64 = 0x001B, 202 | ECT_BIT1 = 0x0030, 203 | ECT_BIT2 = 0x0031, 204 | ECT_BIT3 = 0x0032, 205 | ECT_BIT4 = 0x0033, 206 | ECT_BIT5 = 0x0034, 207 | ECT_BIT6 = 0x0035, 208 | ECT_BIT7 = 0x0036, 209 | ECT_BIT8 = 0x0037 210 | } ec_datatype; 211 | 212 | /** Ethercat command types */ 213 | typedef enum 214 | { 215 | /** No operation */ 216 | EC_CMD_NOP = 0x00, 217 | /** Auto Increment Read */ 218 | EC_CMD_APRD, 219 | /** Auto Increment Write */ 220 | EC_CMD_APWR, 221 | /** Auto Increment Read Write */ 222 | EC_CMD_APRW, 223 | /** Configured Address Read */ 224 | EC_CMD_FPRD, 225 | /** Configured Address Write */ 226 | EC_CMD_FPWR, 227 | /** Configured Address Read Write */ 228 | EC_CMD_FPRW, 229 | /** Broadcast Read */ 230 | EC_CMD_BRD, 231 | /** Broadcast Write */ 232 | EC_CMD_BWR, 233 | /** Broadcast Read Write */ 234 | EC_CMD_BRW, 235 | /** Logical Memory Read */ 236 | EC_CMD_LRD, 237 | /** Logical Memory Write */ 238 | EC_CMD_LWR, 239 | /** Logical Memory Read Write */ 240 | EC_CMD_LRW, 241 | /** Auto Increment Read Multiple Write */ 242 | EC_CMD_ARMW, 243 | /** Configured Read Multiple Write */ 244 | EC_CMD_FRMW 245 | /** Reserved */ 246 | } ec_cmdtype; 247 | 248 | /** Ethercat EEprom command types */ 249 | typedef enum 250 | { 251 | /** No operation */ 252 | EC_ECMD_NOP = 0x0000, 253 | /** Read */ 254 | EC_ECMD_READ = 0x0100, 255 | /** Write */ 256 | EC_ECMD_WRITE = 0x0201, 257 | /** Reload */ 258 | EC_ECMD_RELOAD = 0x0300 259 | } ec_ecmdtype; 260 | 261 | /** EEprom state machine read size */ 262 | #define EC_ESTAT_R64 0x0040 263 | /** EEprom state machine busy flag */ 264 | #define EC_ESTAT_BUSY 0x8000 265 | /** EEprom state machine error flag mask */ 266 | #define EC_ESTAT_EMASK 0x7800 267 | /** EEprom state machine error acknowledge */ 268 | #define EC_ESTAT_NACK 0x2000 269 | 270 | /* Ethercat SSI (Slave Information Interface) */ 271 | 272 | /** Start address SII sections in Eeprom */ 273 | #define ECT_SII_START 0x0040 274 | 275 | enum 276 | { 277 | /** SII category strings */ 278 | ECT_SII_STRING = 10, 279 | /** SII category general */ 280 | ECT_SII_GENERAL = 30, 281 | /** SII category FMMU */ 282 | ECT_SII_FMMU = 40, 283 | /** SII category SM */ 284 | ECT_SII_SM = 41, 285 | /** SII category PDO */ 286 | ECT_SII_PDO = 50 287 | }; 288 | 289 | /** Item offsets in SII general section */ 290 | enum 291 | { 292 | ECT_SII_MANUF = 0x0008, 293 | ECT_SII_ID = 0x000a, 294 | ECT_SII_REV = 0x000c, 295 | ECT_SII_BOOTRXMBX = 0x0014, 296 | ECT_SII_BOOTTXMBX = 0x0016, 297 | ECT_SII_MBXSIZE = 0x0019, 298 | ECT_SII_TXMBXADR = 0x001a, 299 | ECT_SII_RXMBXADR = 0x0018, 300 | ECT_SII_MBXPROTO = 0x001c 301 | }; 302 | 303 | /** Mailbox types definitions */ 304 | enum 305 | { 306 | /** Error mailbox type */ 307 | ECT_MBXT_ERR = 0x00, 308 | /** ADS over EtherCAT mailbox type */ 309 | ECT_MBXT_AOE, 310 | /** Ethernet over EtherCAT mailbox type */ 311 | ECT_MBXT_EOE, 312 | /** CANopen over EtherCAT mailbox type */ 313 | ECT_MBXT_COE, 314 | /** File over EtherCAT mailbox type */ 315 | ECT_MBXT_FOE, 316 | /** Servo over EtherCAT mailbox type */ 317 | ECT_MBXT_SOE, 318 | /** Vendor over EtherCAT mailbox type */ 319 | ECT_MBXT_VOE = 0x0f 320 | }; 321 | 322 | /** CoE mailbox types */ 323 | enum 324 | { 325 | ECT_COES_EMERGENCY = 0x01, 326 | ECT_COES_SDOREQ, 327 | ECT_COES_SDORES, 328 | ECT_COES_TXPDO, 329 | ECT_COES_RXPDO, 330 | ECT_COES_TXPDO_RR, 331 | ECT_COES_RXPDO_RR, 332 | ECT_COES_SDOINFO 333 | }; 334 | 335 | /** CoE SDO commands */ 336 | enum 337 | { 338 | ECT_SDO_DOWN_INIT = 0x21, 339 | ECT_SDO_DOWN_EXP = 0x23, 340 | ECT_SDO_DOWN_INIT_CA = 0x31, 341 | ECT_SDO_UP_REQ = 0x40, 342 | ECT_SDO_UP_REQ_CA = 0x50, 343 | ECT_SDO_SEG_UP_REQ = 0x60, 344 | ECT_SDO_ABORT = 0x80 345 | }; 346 | 347 | /** CoE Object Description commands */ 348 | enum 349 | { 350 | ECT_GET_ODLIST_REQ = 0x01, 351 | ECT_GET_ODLIST_RES = 0x02, 352 | ECT_GET_OD_REQ = 0x03, 353 | ECT_GET_OD_RES = 0x04, 354 | ECT_GET_OE_REQ = 0x05, 355 | ECT_GET_OE_RES = 0x06, 356 | ECT_SDOINFO_ERROR = 0x07 357 | }; 358 | 359 | /** FoE opcodes */ 360 | enum 361 | { 362 | ECT_FOE_READ = 0x01, 363 | ECT_FOE_WRITE, 364 | ECT_FOE_DATA, 365 | ECT_FOE_ACK, 366 | ECT_FOE_ERROR, 367 | ECT_FOE_BUSY 368 | }; 369 | 370 | /** SoE opcodes */ 371 | enum 372 | { 373 | ECT_SOE_READREQ = 0x01, 374 | ECT_SOE_READRES, 375 | ECT_SOE_WRITEREQ, 376 | ECT_SOE_WRITERES, 377 | ECT_SOE_NOTIFICATION, 378 | ECT_SOE_EMERGENCY 379 | }; 380 | 381 | /** Ethercat registers */ 382 | enum 383 | { 384 | ECT_REG_TYPE = 0x0000, 385 | ECT_REG_PORTDES = 0x0007, 386 | ECT_REG_ESCSUP = 0x0008, 387 | ECT_REG_STADR = 0x0010, 388 | ECT_REG_ALIAS = 0x0012, 389 | ECT_REG_DLCTL = 0x0100, 390 | ECT_REG_DLPORT = 0x0101, 391 | ECT_REG_DLALIAS = 0x0103, 392 | ECT_REG_DLSTAT = 0x0110, 393 | ECT_REG_ALCTL = 0x0120, 394 | ECT_REG_ALSTAT = 0x0130, 395 | ECT_REG_ALSTATCODE = 0x0134, 396 | ECT_REG_PDICTL = 0x0140, 397 | ECT_REG_IRQMASK = 0x0200, 398 | ECT_REG_RXERR = 0x0300, 399 | ECT_REG_FRXERR = 0x0308, 400 | ECT_REG_EPUECNT = 0x030C, 401 | ECT_REG_PECNT = 0x030D, 402 | ECT_REG_PECODE = 0x030E, 403 | ECT_REG_LLCNT = 0x0310, 404 | ECT_REG_WDCNT = 0x0442, 405 | ECT_REG_EEPCFG = 0x0500, 406 | ECT_REG_EEPCTL = 0x0502, 407 | ECT_REG_EEPSTAT = 0x0502, 408 | ECT_REG_EEPADR = 0x0504, 409 | ECT_REG_EEPDAT = 0x0508, 410 | ECT_REG_FMMU0 = 0x0600, 411 | ECT_REG_FMMU1 = ECT_REG_FMMU0 + 0x10, 412 | ECT_REG_FMMU2 = ECT_REG_FMMU1 + 0x10, 413 | ECT_REG_FMMU3 = ECT_REG_FMMU2 + 0x10, 414 | ECT_REG_SM0 = 0x0800, 415 | ECT_REG_SM1 = ECT_REG_SM0 + 0x08, 416 | ECT_REG_SM2 = ECT_REG_SM1 + 0x08, 417 | ECT_REG_SM3 = ECT_REG_SM2 + 0x08, 418 | ECT_REG_SM0STAT = ECT_REG_SM0 + 0x05, 419 | ECT_REG_SM1STAT = ECT_REG_SM1 + 0x05, 420 | ECT_REG_SM1ACT = ECT_REG_SM1 + 0x06, 421 | ECT_REG_SM1CONTR = ECT_REG_SM1 + 0x07, 422 | ECT_REG_DCTIME0 = 0x0900, 423 | ECT_REG_DCTIME1 = 0x0904, 424 | ECT_REG_DCTIME2 = 0x0908, 425 | ECT_REG_DCTIME3 = 0x090C, 426 | ECT_REG_DCSYSTIME = 0x0910, 427 | ECT_REG_DCSOF = 0x0918, 428 | ECT_REG_DCSYSOFFSET = 0x0920, 429 | ECT_REG_DCSYSDELAY = 0x0928, 430 | ECT_REG_DCSYSDIFF = 0x092C, 431 | ECT_REG_DCSPEEDCNT = 0x0930, 432 | ECT_REG_DCTIMEFILT = 0x0934, 433 | ECT_REG_DCCUC = 0x0980, 434 | ECT_REG_DCSYNCACT = 0x0981, 435 | ECT_REG_DCSTART0 = 0x0990, 436 | ECT_REG_DCCYCLE0 = 0x09A0, 437 | ECT_REG_DCCYCLE1 = 0x09A4 438 | }; 439 | 440 | /** standard SDO Sync Manager Communication Type */ 441 | #define ECT_SDO_SMCOMMTYPE 0x1c00 442 | /** standard SDO PDO assignment */ 443 | #define ECT_SDO_PDOASSIGN 0x1c10 444 | /** standard SDO RxPDO assignment */ 445 | #define ECT_SDO_RXPDOASSIGN 0x1c12 446 | /** standard SDO TxPDO assignment */ 447 | #define ECT_SDO_TXPDOASSIGN 0x1c13 448 | 449 | /** Ethercat packet type */ 450 | #define ETH_P_ECAT 0x88A4 451 | 452 | /** Error types */ 453 | typedef enum 454 | { 455 | EC_ERR_TYPE_SDO_ERROR = 0, 456 | EC_ERR_TYPE_EMERGENCY = 1, 457 | EC_ERR_TYPE_PACKET_ERROR = 3, 458 | EC_ERR_TYPE_SDOINFO_ERROR = 4, 459 | EC_ERR_TYPE_FOE_ERROR = 5, 460 | EC_ERR_TYPE_FOE_BUF2SMALL = 6, 461 | EC_ERR_TYPE_FOE_PACKETNUMBER = 7, 462 | EC_ERR_TYPE_SOE_ERROR = 8, 463 | EC_ERR_TYPE_MBX_ERROR = 9, 464 | EC_ERR_TYPE_FOE_FILE_NOTFOUND = 10, 465 | EC_ERR_TYPE_EOE_INVALID_RX_DATA = 11 466 | } ec_err_type; 467 | 468 | /** Struct to retrieve errors. */ 469 | typedef struct 470 | { 471 | /** Time at which the error was generated. */ 472 | ec_timet Time; 473 | /** Signal bit, error set but not read */ 474 | boolean Signal; 475 | /** Slave number that generated the error */ 476 | uint16 Slave; 477 | /** CoE SDO index that generated the error */ 478 | uint16 Index; 479 | /** CoE SDO subindex that generated the error */ 480 | uint8 SubIdx; 481 | /** Type of error */ 482 | ec_err_type Etype; 483 | union 484 | { 485 | /** General abortcode */ 486 | int32 AbortCode; 487 | /** Specific error for Emergency mailbox */ 488 | struct 489 | { 490 | uint16 ErrorCode; 491 | uint8 ErrorReg; 492 | uint8 b1; 493 | uint16 w1; 494 | uint16 w2; 495 | }; 496 | }; 497 | } ec_errort; 498 | 499 | /** Helper macros */ 500 | /** Macro to make a word from 2 bytes */ 501 | #define MK_WORD(msb, lsb) ((((uint16)(msb))<<8) | (lsb)) 502 | /** Macro to get hi byte of a word */ 503 | #define HI_BYTE(w) ((w) >> 8) 504 | /** Macro to get low byte of a word */ 505 | #define LO_BYTE(w) ((w) & 0x00ff) 506 | /** Macro to swap hi and low byte of a word */ 507 | #define SWAP(w) ((((w)& 0xff00) >> 8) | (((w) & 0x00ff) << 8)) 508 | /** Macro to get hi word of a dword */ 509 | #define LO_WORD(l) ((l) & 0xffff) 510 | /** Macro to get hi word of a dword */ 511 | #define HI_WORD(l) ((l) >> 16) 512 | 513 | #define get_unaligned(ptr) \ 514 | ({ __typeof__(*(ptr)) __tmp; memcpy(&__tmp, (ptr), sizeof(*(ptr))); __tmp; }) 515 | 516 | #define put_unaligned32(val, ptr) \ 517 | (memcpy((ptr), &(val), 4)) 518 | 519 | #define put_unaligned64(val, ptr) \ 520 | (memcpy((ptr), &(val), 8)) 521 | 522 | #if !defined(EC_BIG_ENDIAN) && defined(EC_LITTLE_ENDIAN) 523 | 524 | #define htoes(A) (A) 525 | #define htoel(A) (A) 526 | #define htoell(A) (A) 527 | #define etohs(A) (A) 528 | #define etohl(A) (A) 529 | #define etohll(A) (A) 530 | 531 | #elif !defined(EC_LITTLE_ENDIAN) && defined(EC_BIG_ENDIAN) 532 | 533 | #define htoes(A) ((((uint16)(A) & 0xff00) >> 8) | \ 534 | (((uint16)(A) & 0x00ff) << 8)) 535 | #define htoel(A) ((((uint32)(A) & 0xff000000) >> 24) | \ 536 | (((uint32)(A) & 0x00ff0000) >> 8) | \ 537 | (((uint32)(A) & 0x0000ff00) << 8) | \ 538 | (((uint32)(A) & 0x000000ff) << 24)) 539 | #define htoell(A) ((((uint64)(A) & (uint64)0xff00000000000000ULL) >> 56) | \ 540 | (((uint64)(A) & (uint64)0x00ff000000000000ULL) >> 40) | \ 541 | (((uint64)(A) & (uint64)0x0000ff0000000000ULL) >> 24) | \ 542 | (((uint64)(A) & (uint64)0x000000ff00000000ULL) >> 8) | \ 543 | (((uint64)(A) & (uint64)0x00000000ff000000ULL) << 8) | \ 544 | (((uint64)(A) & (uint64)0x0000000000ff0000ULL) << 24) | \ 545 | (((uint64)(A) & (uint64)0x000000000000ff00ULL) << 40) | \ 546 | (((uint64)(A) & (uint64)0x00000000000000ffULL) << 56)) 547 | 548 | #define etohs htoes 549 | #define etohl htoel 550 | #define etohll htoell 551 | 552 | #else 553 | 554 | #error "Must define one of EC_BIG_ENDIAN or EC_LITTLE_ENDIAN" 555 | 556 | #endif 557 | 558 | #ifdef __cplusplus 559 | } 560 | #endif 561 | 562 | #endif /* _EC_TYPE_H */ 563 | -------------------------------------------------------------------------------- /src/w5500/w5500.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 by WIZnet 3 | * 4 | * This file is free software; you can redistribute it and/or modify 5 | * it under the terms of either the GNU General Public License version 2 6 | * or the GNU Lesser General Public License version 2.1, both as 7 | * published by the Free Software Foundation. 8 | * 9 | * - 10 Apr. 2015 10 | * Added support for Arduino Ethernet Shield 2 11 | * by Arduino.org team 12 | */ 13 | 14 | #include 15 | #include 16 | #include "Arduino.h" 17 | 18 | #include "w5500/w5500.h" 19 | //#if defined(W5500_ETHERNET_SHIELD) 20 | 21 | // W5500 controller instance 22 | W5500Class w5500; 23 | 24 | // SPI details 25 | SPISettings wiznet_SPI_settings(8000000, MSBFIRST, SPI_MODE0); 26 | uint8_t SPI_CS; 27 | 28 | void W5500Class::init(uint8_t ss_pin) 29 | { 30 | SPI_CS = ss_pin; 31 | 32 | delay(1000); 33 | initSS(); 34 | SPI.begin(); 35 | w5500.swReset(); 36 | for (int i=0; i> 8); 126 | SPI.transfer(_addr & 0xFF); 127 | SPI.transfer(_cb); 128 | SPI.transfer(_data); 129 | resetSS(); 130 | SPI.endTransaction(); 131 | 132 | return 1; 133 | } 134 | 135 | uint16_t W5500Class::write(uint16_t _addr, uint8_t _cb, const uint8_t *_buf, uint16_t _len) 136 | { 137 | SPI.beginTransaction(wiznet_SPI_settings); 138 | setSS(); 139 | SPI.transfer(_addr >> 8); 140 | SPI.transfer(_addr & 0xFF); 141 | SPI.transfer(_cb); 142 | for (uint16_t i=0; i<_len; i++){ 143 | SPI.transfer(_buf[i]); 144 | } 145 | resetSS(); 146 | SPI.endTransaction(); 147 | 148 | return _len; 149 | } 150 | 151 | uint8_t W5500Class::read(uint16_t _addr, uint8_t _cb) 152 | { 153 | SPI.beginTransaction(wiznet_SPI_settings); 154 | setSS(); 155 | SPI.transfer(_addr >> 8); 156 | SPI.transfer(_addr & 0xFF); 157 | SPI.transfer(_cb); 158 | uint8_t _data = SPI.transfer(0); 159 | resetSS(); 160 | SPI.endTransaction(); 161 | 162 | return _data; 163 | } 164 | 165 | uint16_t W5500Class::read(uint16_t _addr, uint8_t _cb, uint8_t *_buf, uint16_t _len) 166 | { 167 | SPI.beginTransaction(wiznet_SPI_settings); 168 | setSS(); 169 | SPI.transfer(_addr >> 8); 170 | SPI.transfer(_addr & 0xFF); 171 | SPI.transfer(_cb); 172 | for (uint16_t i=0; i<_len; i++){ 173 | _buf[i] = SPI.transfer(0); 174 | } 175 | resetSS(); 176 | SPI.endTransaction(); 177 | 178 | return _len; 179 | } 180 | 181 | void W5500Class::execCmdSn(SOCKET s, SockCMD _cmd) { 182 | // Send command to socket 183 | writeSnCR(s, _cmd); 184 | // Wait for command to complete 185 | while (readSnCR(s)) 186 | ; 187 | } 188 | 189 | 190 | uint8_t W5500Class::readVersion(void) 191 | { 192 | SPI.beginTransaction(wiznet_SPI_settings); 193 | setSS(); 194 | SPI.transfer( 0x00 ); 195 | SPI.transfer( 0x39 ); 196 | SPI.transfer( 0x01); 197 | uint8_t _data = SPI.transfer(0); 198 | resetSS(); 199 | SPI.endTransaction(); 200 | 201 | return _data; 202 | } 203 | 204 | 205 | //#endif 206 | -------------------------------------------------------------------------------- /src/w5500/w5500.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 by WIZnet 3 | * 4 | * This file is free software; you can redistribute it and/or modify 5 | * it under the terms of either the GNU General Public License version 2 6 | * or the GNU Lesser General Public License version 2.1, both as 7 | * published by the Free Software Foundation. 8 | * 9 | * - 10 Apr. 2015 10 | * Added support for Arduino Ethernet Shield 2 11 | * by Arduino.org team 12 | */ 13 | 14 | #ifndef W5500_H_INCLUDED 15 | #define W5500_H_INCLUDED 16 | 17 | #define MAX_SOCK_NUM 8 18 | #include 19 | #include 20 | 21 | extern uint8_t SPI_CS; 22 | 23 | 24 | typedef uint8_t SOCKET; 25 | /* 26 | class MR { 27 | public: 28 | static const uint8_t RST = 0x80; 29 | static const uint8_t PB = 0x10; 30 | static const uint8_t PPPOE = 0x08; 31 | static const uint8_t LB = 0x04; 32 | static const uint8_t AI = 0x02; 33 | static const uint8_t IND = 0x01; 34 | }; 35 | */ 36 | /* 37 | class IR { 38 | public: 39 | static const uint8_t CONFLICT = 0x80; 40 | static const uint8_t UNREACH = 0x40; 41 | static const uint8_t PPPoE = 0x20; 42 | static const uint8_t SOCK0 = 0x01; 43 | static const uint8_t SOCK1 = 0x02; 44 | static const uint8_t SOCK2 = 0x04; 45 | static const uint8_t SOCK3 = 0x08; 46 | static inline uint8_t SOCK(SOCKET ch) { return (0x01 << ch); }; 47 | }; 48 | */ 49 | 50 | class SnMR { 51 | public: 52 | static const uint8_t CLOSE = 0x00; 53 | static const uint8_t TCP = 0x01; 54 | static const uint8_t UDP = 0x02; 55 | static const uint8_t IPRAW = 0x03; 56 | static const uint8_t MACRAW = 0x04; 57 | static const uint8_t PPPOE = 0x05; 58 | static const uint8_t ND = 0x20; 59 | static const uint8_t MULTI = 0x80; 60 | }; 61 | 62 | enum SockCMD { 63 | Sock_OPEN = 0x01, 64 | Sock_LISTEN = 0x02, 65 | Sock_CONNECT = 0x04, 66 | Sock_DISCON = 0x08, 67 | Sock_CLOSE = 0x10, 68 | Sock_SEND = 0x20, 69 | Sock_SEND_MAC = 0x21, 70 | Sock_SEND_KEEP = 0x22, 71 | Sock_RECV = 0x40 72 | }; 73 | 74 | /*class SnCmd { 75 | public: 76 | static const uint8_t OPEN = 0x01; 77 | static const uint8_t LISTEN = 0x02; 78 | static const uint8_t CONNECT = 0x04; 79 | static const uint8_t DISCON = 0x08; 80 | static const uint8_t CLOSE = 0x10; 81 | static const uint8_t SEND = 0x20; 82 | static const uint8_t SEND_MAC = 0x21; 83 | static const uint8_t SEND_KEEP = 0x22; 84 | static const uint8_t RECV = 0x40; 85 | }; 86 | */ 87 | 88 | class SnIR { 89 | public: 90 | static const uint8_t SEND_OK = 0x10; 91 | static const uint8_t TIMEOUT = 0x08; 92 | static const uint8_t RECV = 0x04; 93 | static const uint8_t DISCON = 0x02; 94 | static const uint8_t CON = 0x01; 95 | }; 96 | 97 | class SnSR { 98 | public: 99 | static const uint8_t CLOSED = 0x00; 100 | static const uint8_t INIT = 0x13; 101 | static const uint8_t LISTEN = 0x14; 102 | static const uint8_t SYNSENT = 0x15; 103 | static const uint8_t SYNRECV = 0x16; 104 | static const uint8_t ESTABLISHED = 0x17; 105 | static const uint8_t FIN_WAIT = 0x18; 106 | static const uint8_t CLOSING = 0x1A; 107 | static const uint8_t TIME_WAIT = 0x1B; 108 | static const uint8_t CLOSE_WAIT = 0x1C; 109 | static const uint8_t LAST_ACK = 0x1D; 110 | static const uint8_t UDP = 0x22; 111 | static const uint8_t IPRAW = 0x32; 112 | static const uint8_t MACRAW = 0x42; 113 | static const uint8_t PPPOE = 0x5F; 114 | }; 115 | 116 | class IPPROTO { 117 | public: 118 | static const uint8_t IP = 0; 119 | static const uint8_t ICMP = 1; 120 | static const uint8_t IGMP = 2; 121 | static const uint8_t GGP = 3; 122 | static const uint8_t TCP = 6; 123 | static const uint8_t PUP = 12; 124 | static const uint8_t UDP = 17; 125 | static const uint8_t IDP = 22; 126 | static const uint8_t ND = 77; 127 | static const uint8_t RAW = 255; 128 | }; 129 | 130 | class W5500Class { 131 | 132 | public: 133 | void init(uint8_t ss_pin = 10); 134 | void init(uint8_t sck_pin, uint8_t miso_pin, uint8_t mosi_pin, uint8_t ss_pin); // extension for M5Stack ATOM 135 | uint8_t readVersion(void); 136 | 137 | /** 138 | * @brief This function is being used for copy the data form Receive buffer of the chip to application buffer. 139 | * 140 | * It calculate the actual physical address where one has to read 141 | * the data from Receive buffer. Here also take care of the condition while it exceed 142 | * the Rx memory uper-bound of socket. 143 | */ 144 | void read_data(SOCKET s, volatile uint16_t src, volatile uint8_t * dst, uint16_t len); 145 | 146 | /** 147 | * @brief This function is being called by send() and sendto() function also. 148 | * 149 | * This function read the Tx write pointer register and after copy the data in buffer update the Tx write pointer 150 | * register. User should read upper byte first and lower byte later to get proper value. 151 | */ 152 | void send_data_processing(SOCKET s, const uint8_t *data, uint16_t len); 153 | /** 154 | * @brief A copy of send_data_processing that uses the provided ptr for the 155 | * write offset. Only needed for the "streaming" UDP API, where 156 | * a single UDP packet is built up over a number of calls to 157 | * send_data_processing_ptr, because TX_WR doesn't seem to get updated 158 | * correctly in those scenarios 159 | * @param ptr value to use in place of TX_WR. If 0, then the value is read 160 | * in from TX_WR 161 | * @return New value for ptr, to be used in the next call 162 | */ 163 | // FIXME Update documentation 164 | void send_data_processing_offset(SOCKET s, uint16_t data_offset, const uint8_t *data, uint16_t len); 165 | 166 | /** 167 | * @brief This function is being called by recv() also. 168 | * 169 | * This function read the Rx read pointer register 170 | * and after copy the data from receive buffer update the Rx write pointer register. 171 | * User should read upper byte first and lower byte later to get proper value. 172 | */ 173 | void recv_data_processing(SOCKET s, uint8_t *data, uint16_t len, uint8_t peek = 0); 174 | 175 | inline void setGatewayIp(uint8_t *_addr); 176 | inline void getGatewayIp(uint8_t *_addr); 177 | 178 | inline void setSubnetMask(uint8_t *_addr); 179 | inline void getSubnetMask(uint8_t *_addr); 180 | 181 | inline void setMACAddress(uint8_t * addr); 182 | inline void getMACAddress(uint8_t * addr); 183 | 184 | inline void setIPAddress(uint8_t * addr); 185 | inline void getIPAddress(uint8_t * addr); 186 | 187 | inline void setRetransmissionTime(uint16_t timeout); 188 | inline void setRetransmissionCount(uint8_t _retry); 189 | 190 | inline void swReset(); 191 | 192 | inline void setPHYCFGR(uint8_t _val); 193 | inline uint8_t getPHYCFGR(); 194 | 195 | void execCmdSn(SOCKET s, SockCMD _cmd); 196 | 197 | uint16_t getTXFreeSize(SOCKET s); 198 | uint16_t getRXReceivedSize(SOCKET s); 199 | 200 | 201 | // W5500 Registers 202 | // --------------- 203 | private: 204 | static uint8_t write(uint16_t _addr, uint8_t _cb, uint8_t _data); 205 | static uint16_t write(uint16_t _addr, uint8_t _cb, const uint8_t *buf, uint16_t len); 206 | static uint8_t read(uint16_t _addr, uint8_t _cb ); 207 | static uint16_t read(uint16_t _addr, uint8_t _cb, uint8_t *buf, uint16_t len); 208 | 209 | #define __GP_REGISTER8(name, address) \ 210 | static inline void write##name(uint8_t _data) { \ 211 | write(address, 0x04, _data); \ 212 | } \ 213 | static inline uint8_t read##name() { \ 214 | return read(address, 0x00); \ 215 | } 216 | #define __GP_REGISTER16(name, address) \ 217 | static void write##name(uint16_t _data) { \ 218 | write(address, 0x04, _data >> 8); \ 219 | write(address+1, 0x04, _data & 0xFF); \ 220 | } \ 221 | static uint16_t read##name() { \ 222 | uint16_t res = read(address, 0x00); \ 223 | res = (res << 8) + read(address + 1, 0x00); \ 224 | return res; \ 225 | } 226 | #define __GP_REGISTER_N(name, address, size) \ 227 | static uint16_t write##name(uint8_t *_buff) { \ 228 | return write(address, 0x04, _buff, size); \ 229 | } \ 230 | static uint16_t read##name(uint8_t *_buff) { \ 231 | return read(address, 0x00, _buff, size); \ 232 | } 233 | 234 | public: 235 | __GP_REGISTER8 (MR, 0x0000); // Mode 236 | __GP_REGISTER_N(GAR, 0x0001, 4); // Gateway IP address 237 | __GP_REGISTER_N(SUBR, 0x0005, 4); // Subnet mask address 238 | __GP_REGISTER_N(SHAR, 0x0009, 6); // Source MAC address 239 | __GP_REGISTER_N(SIPR, 0x000F, 4); // Source IP address 240 | __GP_REGISTER8 (IR, 0x0015); // Interrupt 241 | __GP_REGISTER8 (IMR, 0x0016); // Interrupt Mask 242 | __GP_REGISTER16(RTR, 0x0019); // Timeout address 243 | __GP_REGISTER8 (RCR, 0x001B); // Retry count 244 | __GP_REGISTER_N(UIPR, 0x0028, 4); // Unreachable IP address in UDP mode 245 | __GP_REGISTER16(UPORT, 0x002C); // Unreachable Port address in UDP mode 246 | __GP_REGISTER8 (PHYCFGR, 0x002E); // PHY Configuration register, default value: 0b 1011 1xxx 247 | 248 | 249 | #undef __GP_REGISTER8 250 | #undef __GP_REGISTER16 251 | #undef __GP_REGISTER_N 252 | 253 | // W5500 Socket registers 254 | // ---------------------- 255 | private: 256 | static inline uint8_t readSn(SOCKET _s, uint16_t _addr); 257 | static inline uint8_t writeSn(SOCKET _s, uint16_t _addr, uint8_t _data); 258 | static inline uint16_t readSn(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t len); 259 | static inline uint16_t writeSn(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t len); 260 | 261 | //static const uint16_t CH_BASE = 0x0000; 262 | //static const uint16_t CH_SIZE = 0x0000; 263 | 264 | #define __SOCKET_REGISTER8(name, address) \ 265 | static inline void write##name(SOCKET _s, uint8_t _data) { \ 266 | writeSn(_s, address, _data); \ 267 | } \ 268 | static inline uint8_t read##name(SOCKET _s) { \ 269 | return readSn(_s, address); \ 270 | } 271 | #if defined(REL_GR_KURUMI) || defined(REL_GR_KURUMI_PROTOTYPE) 272 | #define __SOCKET_REGISTER16(name, address) \ 273 | static void write##name(SOCKET _s, uint16_t _data) { \ 274 | writeSn(_s, address, _data >> 8); \ 275 | writeSn(_s, address+1, _data & 0xFF); \ 276 | } \ 277 | static uint16_t read##name(SOCKET _s) { \ 278 | uint16_t res = readSn(_s, address); \ 279 | uint16_t res2 = readSn(_s,address + 1); \ 280 | res = res << 8; \ 281 | res2 = res2 & 0xFF; \ 282 | res = res | res2; \ 283 | return res; \ 284 | } 285 | #else 286 | #define __SOCKET_REGISTER16(name, address) \ 287 | static void write##name(SOCKET _s, uint16_t _data) { \ 288 | writeSn(_s, address, _data >> 8); \ 289 | writeSn(_s, address+1, _data & 0xFF); \ 290 | } \ 291 | static uint16_t read##name(SOCKET _s) { \ 292 | uint16_t res = readSn(_s, address); \ 293 | res = (res << 8) + readSn(_s, address + 1); \ 294 | return res; \ 295 | } 296 | #endif 297 | #define __SOCKET_REGISTER_N(name, address, size) \ 298 | static uint16_t write##name(SOCKET _s, uint8_t *_buff) { \ 299 | return writeSn(_s, address, _buff, size); \ 300 | } \ 301 | static uint16_t read##name(SOCKET _s, uint8_t *_buff) { \ 302 | return readSn(_s, address, _buff, size); \ 303 | } 304 | 305 | public: 306 | __SOCKET_REGISTER8(SnMR, 0x0000) // Mode 307 | __SOCKET_REGISTER8(SnCR, 0x0001) // Command 308 | __SOCKET_REGISTER8(SnIR, 0x0002) // Interrupt 309 | __SOCKET_REGISTER8(SnSR, 0x0003) // Status 310 | __SOCKET_REGISTER16(SnPORT, 0x0004) // Source Port 311 | __SOCKET_REGISTER_N(SnDHAR, 0x0006, 6) // Destination Hardw Addr 312 | __SOCKET_REGISTER_N(SnDIPR, 0x000C, 4) // Destination IP Addr 313 | __SOCKET_REGISTER16(SnDPORT, 0x0010) // Destination Port 314 | __SOCKET_REGISTER16(SnMSSR, 0x0012) // Max Segment Size 315 | __SOCKET_REGISTER8(SnPROTO, 0x0014) // Protocol in IP RAW Mode 316 | __SOCKET_REGISTER8(SnTOS, 0x0015) // IP TOS 317 | __SOCKET_REGISTER8(SnTTL, 0x0016) // IP TTL 318 | __SOCKET_REGISTER16(SnTX_FSR, 0x0020) // TX Free Size 319 | __SOCKET_REGISTER16(SnTX_RD, 0x0022) // TX Read Pointer 320 | __SOCKET_REGISTER16(SnTX_WR, 0x0024) // TX Write Pointer 321 | __SOCKET_REGISTER16(SnRX_RSR, 0x0026) // RX Free Size 322 | __SOCKET_REGISTER16(SnRX_RD, 0x0028) // RX Read Pointer 323 | __SOCKET_REGISTER16(SnRX_WR, 0x002A) // RX Write Pointer (supported?) 324 | 325 | #undef __SOCKET_REGISTER8 326 | #undef __SOCKET_REGISTER16 327 | #undef __SOCKET_REGISTER_N 328 | 329 | 330 | private: 331 | static const uint8_t RST = 7; // Reset BIT 332 | static const int SOCKETS = 8; 333 | 334 | public: 335 | static const uint16_t SSIZE = 2048; // Max Tx buffer size 336 | private: 337 | static const uint16_t RSIZE = 2048; // Max Rx buffer size 338 | 339 | private: 340 | // could do inline optimizations 341 | static inline void initSS() { pinMode(SPI_CS, OUTPUT); } 342 | static inline void setSS() { digitalWrite(SPI_CS, LOW); } 343 | static inline void resetSS() { digitalWrite(SPI_CS, HIGH); } 344 | }; 345 | 346 | extern W5500Class w5500; 347 | 348 | uint8_t W5500Class::readSn(SOCKET _s, uint16_t _addr) { 349 | uint8_t cntl_byte = (_s<<5)+0x08; 350 | return read(_addr, cntl_byte); 351 | } 352 | 353 | uint8_t W5500Class::writeSn(SOCKET _s, uint16_t _addr, uint8_t _data) { 354 | uint8_t cntl_byte = (_s<<5)+0x0C; 355 | return write(_addr, cntl_byte, _data); 356 | } 357 | 358 | uint16_t W5500Class::readSn(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t _len) { 359 | uint8_t cntl_byte = (_s<<5)+0x08; 360 | return read(_addr, cntl_byte, _buf, _len ); 361 | } 362 | 363 | uint16_t W5500Class::writeSn(SOCKET _s, uint16_t _addr, uint8_t *_buf, uint16_t _len) { 364 | uint8_t cntl_byte = (_s<<5)+0x0C; 365 | return write(_addr, cntl_byte, _buf, _len); 366 | } 367 | 368 | void W5500Class::getGatewayIp(uint8_t *_addr) { 369 | readGAR(_addr); 370 | } 371 | 372 | void W5500Class::setGatewayIp(uint8_t *_addr) { 373 | writeGAR(_addr); 374 | } 375 | 376 | void W5500Class::getSubnetMask(uint8_t *_addr) { 377 | readSUBR(_addr); 378 | } 379 | 380 | void W5500Class::setSubnetMask(uint8_t *_addr) { 381 | writeSUBR(_addr); 382 | } 383 | 384 | void W5500Class::getMACAddress(uint8_t *_addr) { 385 | readSHAR(_addr); 386 | } 387 | 388 | void W5500Class::setMACAddress(uint8_t *_addr) { 389 | writeSHAR(_addr); 390 | } 391 | 392 | void W5500Class::getIPAddress(uint8_t *_addr) { 393 | readSIPR(_addr); 394 | } 395 | 396 | void W5500Class::setIPAddress(uint8_t *_addr) { 397 | writeSIPR(_addr); 398 | } 399 | 400 | void W5500Class::setRetransmissionTime(uint16_t _timeout) { 401 | writeRTR(_timeout); 402 | } 403 | 404 | void W5500Class::setRetransmissionCount(uint8_t _retry) { 405 | writeRCR(_retry); 406 | } 407 | 408 | void W5500Class::setPHYCFGR(uint8_t _val) { 409 | writePHYCFGR(_val); 410 | } 411 | 412 | uint8_t W5500Class::getPHYCFGR() { 413 | // readPHYCFGR(); 414 | return read(0x002E, 0x00); 415 | } 416 | 417 | void W5500Class::swReset() { 418 | writeMR( (readMR() | 0x80) ); 419 | } 420 | 421 | #endif 422 | --------------------------------------------------------------------------------