├── 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 |
--------------------------------------------------------------------------------