Skip to content

File syn_ethercat.h

File List > proto > syn_ethercat.h

Go to the documentation of this file

#ifndef SYN_ETHERCAT_H
#define SYN_ETHERCAT_H

#include "../common/syn_defs.h"
#include "../pt/syn_pt.h"
#include "syn_canopen.h"

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

#define SYN_ETHERCAT_ETHERTYPE 0x88A4

#define SYN_ETHERCAT_TYPE_DATAGRAM 1

/* ── EtherCAT Commands ─────────────────────────────────────────────────── */

typedef enum {
    SYN_ECAT_CMD_NOP = 0,   
    SYN_ECAT_CMD_APRD = 1,  
    SYN_ECAT_CMD_APWR = 2,  
    SYN_ECAT_CMD_APRW = 3,  
    SYN_ECAT_CMD_FPRD = 4,  
    SYN_ECAT_CMD_FPWR = 5,  
    SYN_ECAT_CMD_FPRW = 6,  
    SYN_ECAT_CMD_BRD = 7,   
    SYN_ECAT_CMD_BWR = 8,   
    SYN_ECAT_CMD_BRW = 9,   
    SYN_ECAT_CMD_LRD = 10,  
    SYN_ECAT_CMD_LWR = 11,  
    SYN_ECAT_CMD_LRW = 12,  
    SYN_ECAT_CMD_ARMW = 13, 
    SYN_ECAT_CMD_FRMW = 14  
} SYN_EcatCmd;

/* ── EtherCAT State Machine (ESM) States ────────────────────────────────── */

typedef enum {
    SYN_ECAT_STATE_NONE = 0x00,   
    SYN_ECAT_STATE_INIT = 0x01,   
    SYN_ECAT_STATE_PREOP = 0x02,  
    SYN_ECAT_STATE_BOOT = 0x03,   
    SYN_ECAT_STATE_SAFEOP = 0x04, 
    SYN_ECAT_STATE_OP = 0x08      
} SYN_EcatState;

/* ── CoE Mailbox Service Types ──────────────────────────────────────────── */

typedef enum {
    SYN_ECAT_COE_TYPE_EMERGENCY = 1, 
    SYN_ECAT_COE_TYPE_SDO_REQ = 2,   
    SYN_ECAT_COE_TYPE_SDO_RESP = 3,  
    SYN_ECAT_COE_TYPE_RXPDO = 4,     
    SYN_ECAT_COE_TYPE_TXPDO = 5,     
    SYN_ECAT_COE_TYPE_SDO_INFO = 6   
} SYN_EcatCoEType;

/* ── Struct Definitions ─────────────────────────────────────────────────── */

typedef struct {
    uint16_t length : 11;  
    uint16_t reserved : 1; 
    uint16_t type : 4;     
} SYN_EcatHeader;

typedef struct {
    uint8_t cmd;       
    uint8_t idx;       
    uint32_t addr;     
    uint16_t len : 11; 
    uint16_t res : 3;  
    uint16_t circ : 1; 
    uint16_t m : 1;    
    uint16_t irq;      
    uint16_t wkc;      
} SYN_EcatDatagram;

typedef struct {
    uint16_t number : 9;       
    uint16_t res : 3;          
    uint16_t service_type : 4; 
} SYN_EcatCoEHeader;

typedef struct {
    SYN_EcatState state;        
    SYN_EcatState target_state; 
    uint16_t station_addr;      
    uint16_t al_status;         
    uint16_t wkc_expected;      
    uint16_t wkc_last;          
    uint32_t rx_pdos;           
    uint32_t tx_pdos;           
    SYN_CANOpenNode *od;        
} SYN_EcatNode;

/* ── API Functions ──────────────────────────────────────────────────────── */

void syn_ecat_init(SYN_EcatNode *node, uint16_t station_addr, SYN_CANOpenNode *od);

size_t syn_ecat_build_datagram_frame(uint8_t *buf, size_t buf_len, const SYN_EcatDatagram *datagram,
                                     const uint8_t *data, uint16_t data_len);

SYN_Status syn_ecat_parse_frame(SYN_EcatNode *node, const uint8_t *frame, size_t len,
                                uint16_t *wkc);

SYN_Status syn_ecat_set_state(SYN_EcatNode *node, SYN_EcatState new_state);

void syn_ecat_update(SYN_EcatNode *node);

/* ── EtherCAT Master & Configuration Structs ────────────────────────────── */

#ifndef SYN_ECAT_MAX_SLAVES
#define SYN_ECAT_MAX_SLAVES 16U 
#endif

typedef struct {
    uint16_t start_addr; 
    uint16_t length;     
    uint8_t control;     
    uint8_t enable;      
} SYN_EcatSMConfig;

typedef struct {
    uint32_t logical_start;    
    uint16_t length;           
    uint8_t logical_start_bit; 
    uint8_t logical_end_bit;   
    uint16_t phys_start_addr;  
    uint8_t phys_start_bit;    
    uint8_t type;              
    bool enable;               
} SYN_EcatFMMUConfig;

typedef struct {
    uint16_t station_addr;   
    uint16_t auto_inc_addr;  
    uint32_t vendor_id;      
    uint32_t product_code;   
    uint32_t revision;       
    SYN_EcatState state;     
    uint16_t al_status_code; 
    uint8_t sm_count;        
    uint8_t fmmu_count;      
} SYN_EcatSlaveInfo;

typedef struct {
    SYN_EcatSlaveInfo slaves[SYN_ECAT_MAX_SLAVES]; 
    uint8_t slave_count;                           
    uint8_t *output_image; 
    uint8_t *input_image;  
    uint16_t output_size;  
    uint16_t input_size;   
    uint32_t logical_addr; 
    uint16_t wkc_expected; 
    uint16_t wkc_last;     
    uint32_t cycle_count;  
    uint32_t wkc_errors;   
    uint8_t *tx_buf;      
    uint8_t *rx_buf;      
    uint16_t tx_buf_size; 
    uint16_t rx_buf_size; 
    /* Transport & Protothread Task State */
    SYN_EcatState master_state; 
    uint8_t current_slave_idx;  
    uint16_t tx_frame_len;      
    uint16_t rx_frame_len;      
    bool frame_tx_pending;      
    bool frame_rx_ready;        
    /* Scratch state for non-blocking task drivers & hardware polling */
    SYN_PT sub_pt;          
    uint8_t pdo_count;      
    uint8_t pdo_entry_idx;  
    uint16_t pdo_map_idx;   
    uint32_t pdo_sdo_val;   
    size_t pdo_read_len;    
    uint32_t total_rx_bits; 
    uint32_t total_tx_bits; 
    uint16_t poll_retries;  
} SYN_EcatMaster;

/* ── EtherCAT Master Transport & Protothread Tasks ─────────────────────── */

#include "../sched/syn_task.h"

SYN_Status syn_ecat_master_set_rx_frame(SYN_EcatMaster *m, const uint8_t *frame, size_t len);

bool syn_ecat_master_pop_tx_frame(SYN_EcatMaster *m, const uint8_t **out_frame, size_t *out_len);

SYN_PT_Status syn_ecat_master_scan_task(SYN_PT *pt, SYN_EcatMaster *m);

SYN_PT_Status syn_ecat_master_transition_task(SYN_PT *pt, SYN_EcatMaster *m,
                                              SYN_EcatState target_state);

SYN_PT_Status syn_ecat_master_cyclic_task(SYN_PT *pt, SYN_EcatMaster *m);

size_t syn_ecat_encode_read_reg(SYN_EcatMaster *m, uint16_t station_addr, uint16_t reg_addr,
                                uint16_t len);

size_t syn_ecat_encode_write_reg(SYN_EcatMaster *m, uint16_t station_addr, uint16_t reg_addr,
                                 const void *data, uint16_t len);

SYN_Status syn_ecat_decode_read_reg(SYN_EcatMaster *m, size_t rx_len, void *out_data, uint16_t len);

SYN_PT_Status syn_ecat_master_read_sii_task(SYN_PT *pt, SYN_EcatMaster *m, uint16_t station_addr,
                                            uint16_t word_offset, uint32_t *out_data);

SYN_PT_Status syn_ecat_master_sdo_read_task(SYN_PT *pt, SYN_EcatMaster *m, uint16_t station_addr,
                                            uint16_t index, uint8_t subindex, void *out_data,
                                            size_t max_len, size_t *out_len);

SYN_PT_Status syn_ecat_master_discover_pdo_mapping_task(SYN_PT *pt, SYN_EcatMaster *m,
                                                        uint16_t station_addr,
                                                        uint16_t *out_rx_bytes,
                                                        uint16_t *out_tx_bytes);

/* ── EtherCAT Master API ────────────────────────────────────────────────── */

SYN_Status syn_ecat_master_init(SYN_EcatMaster *m, uint8_t *tx_buf, uint16_t tx_buf_size,
                                uint8_t *rx_buf, uint16_t rx_buf_size, uint8_t *output_image,
                                uint16_t output_size, uint8_t *input_image, uint16_t input_size);

size_t syn_ecat_encode_scan_bus(SYN_EcatMaster *m);

uint8_t syn_ecat_decode_scan_bus(SYN_EcatMaster *m, size_t rx_len);

size_t syn_ecat_encode_assign_addr(SYN_EcatMaster *m, uint16_t position, uint16_t station_addr);

size_t syn_ecat_encode_read_sii(SYN_EcatMaster *m, uint16_t station_addr, uint16_t word_offset);

SYN_Status syn_ecat_decode_read_sii(SYN_EcatMaster *m, size_t rx_len, uint32_t *out_val);

size_t syn_ecat_encode_write_sm(SYN_EcatMaster *m, uint16_t station_addr, uint8_t sm_index,
                                const SYN_EcatSMConfig *cfg);

size_t syn_ecat_encode_write_fmmu(SYN_EcatMaster *m, uint16_t station_addr, uint8_t fmmu_index,
                                  const SYN_EcatFMMUConfig *cfg);

size_t syn_ecat_encode_read_al_status(SYN_EcatMaster *m, uint16_t station_addr);

SYN_Status syn_ecat_decode_read_al_status(SYN_EcatMaster *m, size_t rx_len, SYN_EcatState *state,
                                          uint16_t *status_code);

/* ── Multi-Datagram Frame Builder & Parser ─────────────────────────────── */

#define SYN_ESC_REG_TYPE 0x0000U             
#define SYN_ESC_REG_STATION_ADDR 0x0010U     
#define SYN_ESC_REG_STATION_ALIAS 0x0012U    
#define SYN_ESC_REG_AL_CONTROL 0x0120U       
#define SYN_ESC_REG_AL_STATUS 0x0130U        
#define SYN_ESC_REG_AL_STATUS_CODE 0x0134U   
#define SYN_ESC_REG_PDI_CONTROL 0x0140U      
#define SYN_ESC_REG_SII_EEPROM_CTRL 0x0502U  
#define SYN_ESC_REG_FMMU0 0x0600U            
#define SYN_ESC_REG_SM0 0x0800U              
#define SYN_ESC_REG_DC_SYSTEM_TIME 0x0900U   
#define SYN_ESC_REG_DC_SYSTEM_OFFSET 0x0920U 
#define SYN_ESC_REG_DC_SYNC0_CYCLE 0x09A0U   
/* ── Multi-Datagram Frame Builder & Parser ─────────────────────────────── */

typedef struct {
    uint8_t cmd;         
    uint8_t idx;         
    uint32_t addr;       
    const uint8_t *data; 
    uint16_t data_len;   
    uint16_t wkc;        
} SYN_EcatDatagramResult;

void syn_ecat_frame_begin(uint8_t *buf, size_t buf_size);

size_t syn_ecat_frame_add_datagram(uint8_t *buf, size_t buf_size, SYN_EcatCmd cmd, uint8_t idx,
                                   uint32_t addr, const uint8_t *data, uint16_t data_len,
                                   bool circ);

size_t syn_ecat_frame_finalize(uint8_t *buf);

bool syn_ecat_frame_parse_next(const uint8_t *frame, size_t frame_len, size_t *offset,
                               SYN_EcatDatagramResult *out);

size_t syn_ecat_coe_encode_sdo_download(uint8_t *buf, size_t buf_len, uint16_t index,
                                        uint8_t subindex, const void *data, size_t data_len);

size_t syn_ecat_coe_encode_sdo_upload(uint8_t *buf, size_t buf_len, uint16_t index,
                                      uint8_t subindex);

/* ── Phase 3: Mailbox & CoE SDO API ─────────────────────────────────────── */

size_t syn_ecat_encode_coe_sdo_write(SYN_EcatMaster *m, uint16_t station_addr, uint16_t index,
                                     uint8_t subindex, const void *data, size_t data_len);

size_t syn_ecat_encode_coe_sdo_read(SYN_EcatMaster *m, uint16_t station_addr, uint16_t index,
                                    uint8_t subindex);

SYN_Status syn_ecat_decode_coe_sdo_response(SYN_EcatMaster *m, size_t rx_len, void *out_data,
                                            size_t max_len, size_t *out_len);

/* ── Phase 4: Cyclic Process Data Exchange API ──────────────────────────── */

size_t syn_ecat_encode_cyclic(SYN_EcatMaster *m);

SYN_Status syn_ecat_decode_cyclic(SYN_EcatMaster *m, size_t rx_len);

/* ── Phase 5: ESM State Transitions ─────────────────────────────────────── */

size_t syn_ecat_encode_set_al_control(SYN_EcatMaster *m, uint16_t station_addr,
                                      SYN_EcatState state);

/* ── Phase 6: Distributed Clocks (DC) API ───────────────────────────────── */

size_t syn_ecat_encode_dc_read_system_time(SYN_EcatMaster *m);

size_t syn_ecat_encode_dc_configure(SYN_EcatMaster *m, uint16_t station_addr,
                                    uint32_t sync0_cycle_ns, int32_t sync0_shift_ns);

#ifdef __cplusplus
}
#endif

#endif /* SYN_ETHERCAT_H */