Skip to content

Commit ea1a47b

Browse files
Merge pull request #92 from nasa-jpl/feat-el1008-driver
Feat el1008 driver
2 parents d4a0cd0 + a3c03a8 commit ea1a47b

File tree

10 files changed

+285
-1
lines changed

10 files changed

+285
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ most useful devices. The following table represents the latest state of the JSD
6161
| EL3356 | 2 | 1.1.0 |
6262
| JED (JPL EtherCat Device) | 2 | 1.2.0 |
6363
| ATI Force-Torque Sensor | 2 | 1.4.0 |
64-
| EL1008 | 2 | TBD |
64+
| EL1008 | 2 | 2.3.4 |
6565
| EL3202-0010 | 2 | 1.5.0 |
6666
| EL3255 | 2 | TBD |
6767
| EL3318 | 2 | 1.5.0 |

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ add_library(jsd-lib STATIC
1616
jsd_timer.c
1717
jsd_ati_fts.c
1818
jsd_el3104.c
19+
jsd_el1008.c
1920
jsd_el3202.c
2021
jsd_el3318.c
2122
jsd_el3162.c

src/jsd.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "jsd/jsd_ati_fts.h"
1010
#include "jsd/jsd_egd.h"
11+
#include "jsd/jsd_el1008.h"
1112
#include "jsd/jsd_el2124.h"
1213
#include "jsd/jsd_el3104.h"
1314
#include "jsd/jsd_el3162.h"
@@ -439,6 +440,10 @@ bool jsd_init_single_device(jsd_t* self, uint16_t slave_id) {
439440
return jsd_egd_init(self, slave_id);
440441
break;
441442
}
443+
case JSD_EL1008_PRODUCT_CODE: {
444+
return jsd_el1008_init(self, slave_id);
445+
break;
446+
}
442447
case JSD_EL2124_PRODUCT_CODE: {
443448
return jsd_el2124_init(self, slave_id);
444449
break;

src/jsd_el1008.c

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#include "jsd/jsd_el1008.h"
2+
3+
#include <assert.h>
4+
#include <string.h>
5+
6+
#include "jsd/jsd_sdo.h"
7+
8+
/****************************************************
9+
* Public functions
10+
****************************************************/
11+
12+
const jsd_el1008_state_t* jsd_el1008_get_state(jsd_t* self, uint16_t slave_id) {
13+
assert(self);
14+
assert(self->ecx_context.slavelist[slave_id].eep_id ==
15+
JSD_EL1008_PRODUCT_CODE);
16+
17+
jsd_el1008_state_t* state = &self->slave_states[slave_id].el1008;
18+
return state;
19+
}
20+
21+
void jsd_el1008_read(jsd_t* self, uint16_t slave_id) {
22+
assert(self);
23+
assert(self->ecx_context.slavelist[slave_id].eep_id ==
24+
JSD_EL1008_PRODUCT_CODE);
25+
26+
jsd_el1008_state_t* state = &self->slave_states[slave_id].el1008;
27+
28+
const jsd_el1008_txpdo_t* txpdo =
29+
(jsd_el1008_txpdo_t*)self->ecx_context.slavelist[slave_id].inputs;
30+
31+
for (int ch = 0; ch < JSD_EL1008_NUM_CHANNELS; ++ch) {
32+
state->values[ch] = (1 << ch) & txpdo->channels; // Bit shift the channel to get value
33+
}
34+
}
35+
36+
/****************************************************
37+
* Private functions
38+
****************************************************/
39+
40+
bool jsd_el1008_init(jsd_t* self, uint16_t slave_id) {
41+
assert(self);
42+
assert(self->ecx_context.slavelist[slave_id].eep_id ==
43+
JSD_EL1008_PRODUCT_CODE);
44+
assert(self->ecx_context.slavelist[slave_id].eep_man ==
45+
JSD_BECKHOFF_VENDOR_ID);
46+
47+
ec_slavet* slaves = self->ecx_context.slavelist;
48+
ec_slavet* slave = &slaves[slave_id];
49+
50+
slave->PO2SOconfigx = jsd_el1008_PO2SO_config;
51+
52+
return true;
53+
}
54+
55+
int jsd_el1008_PO2SO_config(ecx_contextt* ecx_context, uint16_t slave_id) {
56+
assert(ecx_context);
57+
assert(ecx_context->slavelist[slave_id].eep_id == JSD_EL1008_PRODUCT_CODE);
58+
59+
// Since this function prototype is forced by SOEM, we have embedded a
60+
// reference to jsd.slave_configs within the ecx_context and extract it here.
61+
jsd_slave_config_t* slave_configs =
62+
(jsd_slave_config_t*)ecx_context->userdata;
63+
64+
jsd_slave_config_t* config = &slave_configs[slave_id];
65+
66+
MSG("Configuring slave no: %u, SII inferred name: %s", slave_id,
67+
ecx_context->slavelist[slave_id].name);
68+
MSG("\t Configured name: %s", config->name);
69+
70+
config->PO2SO_success = true;
71+
return 1;
72+
}

src/jsd_el1008.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#ifndef JSD_EL1008_H
2+
#define JSD_EL1008_H
3+
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif
7+
8+
#include "jsd/jsd.h"
9+
10+
/**
11+
* @brief TxPDO struct used to read device data in SOEM IOmap
12+
*
13+
* Note: Struct order matters and must be packed.
14+
*/
15+
typedef struct __attribute__((__packed__)) {
16+
uint8_t channels; // Each bit corresponds to a channel.
17+
} jsd_el1008_txpdo_t;
18+
19+
/** @brief Initializes el1008 and registers the PO2SO function
20+
*
21+
* @param self pointer JSD context
22+
* @param slave_id index of device on EtherCAT bus
23+
* @return true on success, false on failure
24+
*/
25+
bool jsd_el1008_init(jsd_t* self, uint16_t slave_id);
26+
27+
/**
28+
* @brief Configuration function called by SOEM upon a PreOp to SafeOp state
29+
* transition that (re)configures EL1008 device settings
30+
*
31+
* @param ecx_context SOEM context pointer
32+
* @param slave_id index of device on EtherCAT bus
33+
* @return 1 on success, 0 on failure
34+
*/
35+
int jsd_el1008_PO2SO_config(ecx_contextt* ecx_context, uint16_t slave_id);
36+
37+
#ifdef __cplusplus
38+
}
39+
#endif
40+
41+
#endif

src/jsd_el1008_pub.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#ifndef JSD_EL1008_PUB_H
2+
#define JSD_EL1008_PUB_H
3+
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif
7+
8+
#include "jsd/jsd_pub.h"
9+
10+
/**
11+
* @brief Read the EL1008 device state
12+
*
13+
* @param self Pointer to JSD context
14+
* @param slave_id Slave ID of EL1008 device
15+
* @return Pointer to EL1008 device state
16+
*/
17+
const jsd_el1008_state_t* jsd_el1008_get_state(jsd_t* self, uint16_t slave_id);
18+
19+
/**
20+
* @brief Converts raw PDO data to state data
21+
*
22+
* @param self pointer to JSD context
23+
* @param slave_id ID of EL1008 device
24+
*/
25+
void jsd_el1008_read(jsd_t* self, uint16_t slave_id);
26+
27+
#ifdef __cplusplus
28+
}
29+
#endif
30+
31+
#endif

src/jsd_el1008_types.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#ifndef JSD_EL1008_TYPES_H
2+
#define JSD_EL1008_TYPES_H
3+
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif
7+
8+
#include "jsd/jsd_common_device_types.h"
9+
10+
#define JSD_EL1008_PRODUCT_CODE (uint32_t)0x03f03052
11+
#define JSD_EL1008_NUM_CHANNELS 8 // Each of the 8 values is 1 bit long.
12+
13+
/**
14+
* @brief configuration struct for EL1008 device initialization
15+
*/
16+
typedef struct {
17+
} jsd_el1008_config_t;
18+
19+
/**
20+
* @brief Read struct for el1008 module
21+
*/
22+
typedef struct {
23+
uint8_t values[JSD_EL1008_NUM_CHANNELS];
24+
} jsd_el1008_state_t;
25+
26+
#ifdef __cplusplus
27+
}
28+
#endif
29+
30+
#endif

src/jsd_types.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ extern "C" {
1212
#include "jsd/jsd_ati_fts_types.h"
1313
#include "jsd/jsd_egd_types.h"
1414
#include "jsd/jsd_el2124_types.h"
15+
#include "jsd/jsd_el1008_types.h"
1516
#include "jsd/jsd_el3104_types.h"
1617
#include "jsd/jsd_el3162_types.h"
1718
#include "jsd/jsd_el3202_types.h"
@@ -32,6 +33,7 @@ typedef struct {
3233
uint32_t product_code;
3334
char name[JSD_NAME_LEN];
3435
union {
36+
jsd_el1008_config_t el1008;
3537
jsd_el3602_config_t el3602;
3638
jsd_el3208_config_t el3208;
3739
jsd_el2124_config_t el2124;
@@ -54,6 +56,7 @@ typedef struct {
5456

5557
typedef struct {
5658
union {
59+
jsd_el1008_state_t el1008;
5760
jsd_el3602_state_t el3602;
5861
jsd_el3208_state_t el3208;
5962
jsd_el2124_state_t el2124;

test/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ if(BUILD_JSD_TESTS)
3636
jsd_test_utils.c)
3737
target_link_libraries(jsd_device_test_template ${jsd_test_libs})
3838

39+
add_executable(jsd_el1008_test
40+
device/jsd_el1008_test.c
41+
jsd_test_utils.c)
42+
target_link_libraries(jsd_el1008_test ${jsd_test_libs})
43+
3944
add_executable(jsd_el2124_test
4045
device/jsd_el2124_test.c
4146
jsd_test_utils.c)

test/device/jsd_el1008_test.c

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#include <assert.h>
2+
#include <string.h>
3+
4+
#include "jsd/jsd_el1008_pub.h"
5+
#include "jsd/jsd_el1008_types.h"
6+
#include "jsd_test_utils.h"
7+
8+
extern bool quit;
9+
extern FILE* file;
10+
uint8_t slave_id;
11+
12+
void telemetry_header() {
13+
if (!file) {
14+
return;
15+
}
16+
for (int i = 0; i < JSD_EL1008_NUM_CHANNELS; ++i) {
17+
fprintf(file, "EL1008_ch%d_level, ", i);
18+
}
19+
fprintf(file, "\n");
20+
}
21+
22+
void telemetry_data(void* self) {
23+
assert(self);
24+
25+
if (!file) {
26+
return;
27+
}
28+
29+
single_device_server_t* sds = (single_device_server_t*)self;
30+
const jsd_el1008_state_t* state = jsd_el1008_get_state(sds->jsd, slave_id);
31+
32+
for (int i = 0; i < JSD_EL1008_NUM_CHANNELS; ++i) {
33+
fprintf(file, "%d,", state->values[i]);
34+
}
35+
fprintf(file, "\n");
36+
fflush(file);
37+
}
38+
39+
void print_info(void* self) {
40+
assert(self);
41+
42+
single_device_server_t* sds = (single_device_server_t*)self;
43+
const jsd_el1008_state_t* state = jsd_el1008_get_state(sds->jsd, slave_id);
44+
MSG("Ch0: %d, Ch1: %d, Ch2: %d, Ch3: %d, Ch4: %d, Ch5: %d, Ch6: %d, Ch7: %d, ",
45+
state->values[0], state->values[1], state->values[2], state->values[3],
46+
state->values[4], state->values[5], state->values[6], state->values[7]);
47+
}
48+
49+
void extract_data(void* self) {
50+
assert(self);
51+
52+
single_device_server_t* sds = (single_device_server_t*)self;
53+
jsd_el1008_read(sds->jsd, slave_id);
54+
}
55+
56+
void command(void* self) { (void)self; };
57+
58+
int main(int argc, char* argv[]) {
59+
if (argc != 4) {
60+
ERROR("Expecting exactly 3 arguments");
61+
MSG("Usage: jsd_el1008_test <ifname> <el1008_slave_index> <loop_freq_hz>");
62+
MSG("Example: $ jsd_el1008_test eth0 2 1000");
63+
return 0;
64+
}
65+
66+
char* ifname = strdup(argv[1]);
67+
slave_id = atoi(argv[2]);
68+
uint32_t loop_freq_hz = atoi(argv[3]);
69+
MSG("Configuring device %s, using slave %d", ifname, slave_id);
70+
MSG("Using frequency of %i hz", loop_freq_hz);
71+
72+
single_device_server_t sds;
73+
74+
sds_set_telemetry_header_callback(&sds, telemetry_header);
75+
sds_set_telemetry_data_callback(&sds, telemetry_data);
76+
sds_set_print_info_callback(&sds, print_info);
77+
sds_set_extract_data_callback(&sds, extract_data);
78+
sds_set_command_callback(&sds, command);
79+
80+
sds_setup(&sds, loop_freq_hz);
81+
82+
// Set device configuration here.
83+
jsd_slave_config_t my_config = {0};
84+
85+
snprintf(my_config.name, JSD_NAME_LEN, "unicorn");
86+
my_config.configuration_active = true;
87+
my_config.product_code = JSD_EL1008_PRODUCT_CODE;
88+
89+
jsd_set_slave_config(sds.jsd, slave_id, my_config);
90+
91+
sds_run(&sds, ifname, "/tmp/jsd_el1008.csv");
92+
93+
free(ifname);
94+
95+
return 0;
96+
}

0 commit comments

Comments
 (0)