From a2d0b28b3d1154c202afbb1d9715e293123dced7 Mon Sep 17 00:00:00 2001 From: cbb <2625274797@qq.com> Date: Sun, 9 Aug 2026 03:27:21 +0800 Subject: [PATCH] =?UTF-8?q?feat(project):=20=E6=9B=B4=E6=96=B0=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E9=85=8D=E7=BD=AE=E5=B9=B6=E5=A2=9E=E5=8A=A0ESC=20EEP?= =?UTF-8?q?ROM=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加新的SPI命令文件:app_spi_cmd_can_burst.cpp 和 app_spi_cmd_exmc_diag.cpp - 移除旧的SPI命令文件:app_spi_cmd_ota_wait.cpp, app_spi_cmd_ping.cpp, app_spi_cmd_can_bridge.cpp, app_spi_cmd_can_feedback.cpp, app_spi_cmd_id_snapshot.cpp, app_spi_cmd_adc_feedback.cpp, app_spi_cmd_motor_state.cpp, app_spi_cmd_motor_version.cpp, app_spi_cmd_motor_ota.cpp - 新增SSC EtherCAT从站协议栈相关文件组,包含头文件和源文件 - 添加ESC EEPROM操作相关的头文件和源文件 - 修正HBI寄存器EXMC偏移地址映射错误 - 实现8位、16位、32位ESC寄存器读写函数 - 改进EEPROM初始化逻辑,添加重试机制和诊断功能 --- .../GD32E508_DCU_APP.cproject.yml | 27 + GD32E508_DCU_APP/comm/esc_eeprom.cpp | 150 +- GD32E508_DCU_APP/comm/esc_exmc_bus.cpp | 534 +++- GD32E508_DCU_APP/comm/esc_exmc_bus.h | 23 +- GD32E508_DCU_APP/comm/esc_lan9252.cpp | 245 +- GD32E508_DCU_APP/comm/esc_lan9252.h | 22 +- GD32E508_DCU_APP/comm/ethercat_slave.cpp | 4 +- GD32E508_DCU_APP/comm/ethercat_sync.cpp | 29 +- GD32E508_DCU_APP/comm/ssc/inc/applInterface.h | 125 + GD32E508_DCU_APP/comm/ssc/inc/coeappl.h | 68 + GD32E508_DCU_APP/comm/ssc/inc/ecatappl.h | 124 + GD32E508_DCU_APP/comm/ssc/inc/ecatcoe.h | 113 + GD32E508_DCU_APP/comm/ssc/inc/ecatslv.h | 455 +++ GD32E508_DCU_APP/comm/ssc/inc/el9800appl.h | 33 + GD32E508_DCU_APP/comm/ssc/inc/el9800hw.h | 150 + GD32E508_DCU_APP/comm/ssc/inc/esc.h | 175 ++ GD32E508_DCU_APP/comm/ssc/inc/mailbox.h | 196 ++ GD32E508_DCU_APP/comm/ssc/inc/objdef.h | 291 ++ GD32E508_DCU_APP/comm/ssc/inc/sdoserv.h | 641 +++++ GD32E508_DCU_APP/comm/ssc/port/DCU_ESI.xml | 77 + GD32E508_DCU_APP/comm/ssc/port/dc_app.c | 229 ++ GD32E508_DCU_APP/comm/ssc/port/dc_app.h | 39 + GD32E508_DCU_APP/comm/ssc/port/ecat_def.h | 734 +++++ GD32E508_DCU_APP/comm/ssc/port/eeprom_image.h | 53 + GD32E508_DCU_APP/comm/ssc/port/gen_eeprom.py | 357 +++ .../comm/ssc/port/hw_gd32exmc.cpp | 345 +++ GD32E508_DCU_APP/comm/ssc/src/coeappl.c | 780 +++++ GD32E508_DCU_APP/comm/ssc/src/ecatappl.c | 630 +++++ GD32E508_DCU_APP/comm/ssc/src/ecatcoe.c | 180 ++ GD32E508_DCU_APP/comm/ssc/src/ecatslv.c | 2504 +++++++++++++++++ GD32E508_DCU_APP/comm/ssc/src/mailbox.c | 929 ++++++ GD32E508_DCU_APP/comm/ssc/src/objdef.c | 1699 +++++++++++ GD32E508_DCU_APP/comm/ssc/src/sdoserv.c | 1261 +++++++++ GD32E508_DCU_APP/device/motor_state.cpp | 5 +- .../device/spi/app_spi_cmd_adc_feedback.cpp | 65 +- .../device/spi/app_spi_cmd_can_burst.cpp | 99 + .../device/spi/app_spi_cmd_can_feedback.cpp | 104 +- .../device/spi/app_spi_cmd_exmc_diag.cpp | 42 + .../device/spi/app_spi_cmd_motor_state.cpp | 67 +- .../device/spi/app_spi_cmd_motor_version.cpp | 2 +- .../device/spi/app_spi_command.cpp | 7 +- GD32E508_DCU_APP/device/spi/app_spi_command.h | 39 +- .../device/spi/app_spi_command_defs.h | 11 + GD32E508_DCU_APP/main.h | 6 - GD32E508_DCU_APP/sys/config.h | 17 +- GD32E508_DCU_APP/sys/initial.cpp | 25 +- GD32E508_DCU_APP/sys/task_manager.cpp | 9 - GD32E508_DCU_APP/sys/task_runner.cpp | 90 +- GD32E508_DCU_APP/sys/task_runner.h | 2 - 49 files changed, 13258 insertions(+), 554 deletions(-) create mode 100644 GD32E508_DCU_APP/comm/ssc/inc/applInterface.h create mode 100644 GD32E508_DCU_APP/comm/ssc/inc/coeappl.h create mode 100644 GD32E508_DCU_APP/comm/ssc/inc/ecatappl.h create mode 100644 GD32E508_DCU_APP/comm/ssc/inc/ecatcoe.h create mode 100644 GD32E508_DCU_APP/comm/ssc/inc/ecatslv.h create mode 100644 GD32E508_DCU_APP/comm/ssc/inc/el9800appl.h create mode 100644 GD32E508_DCU_APP/comm/ssc/inc/el9800hw.h create mode 100644 GD32E508_DCU_APP/comm/ssc/inc/esc.h create mode 100644 GD32E508_DCU_APP/comm/ssc/inc/mailbox.h create mode 100644 GD32E508_DCU_APP/comm/ssc/inc/objdef.h create mode 100644 GD32E508_DCU_APP/comm/ssc/inc/sdoserv.h create mode 100644 GD32E508_DCU_APP/comm/ssc/port/DCU_ESI.xml create mode 100644 GD32E508_DCU_APP/comm/ssc/port/dc_app.c create mode 100644 GD32E508_DCU_APP/comm/ssc/port/dc_app.h create mode 100644 GD32E508_DCU_APP/comm/ssc/port/ecat_def.h create mode 100644 GD32E508_DCU_APP/comm/ssc/port/eeprom_image.h create mode 100644 GD32E508_DCU_APP/comm/ssc/port/gen_eeprom.py create mode 100644 GD32E508_DCU_APP/comm/ssc/port/hw_gd32exmc.cpp create mode 100644 GD32E508_DCU_APP/comm/ssc/src/coeappl.c create mode 100644 GD32E508_DCU_APP/comm/ssc/src/ecatappl.c create mode 100644 GD32E508_DCU_APP/comm/ssc/src/ecatcoe.c create mode 100644 GD32E508_DCU_APP/comm/ssc/src/ecatslv.c create mode 100644 GD32E508_DCU_APP/comm/ssc/src/mailbox.c create mode 100644 GD32E508_DCU_APP/comm/ssc/src/objdef.c create mode 100644 GD32E508_DCU_APP/comm/ssc/src/sdoserv.c create mode 100644 GD32E508_DCU_APP/device/spi/app_spi_cmd_can_burst.cpp create mode 100644 GD32E508_DCU_APP/device/spi/app_spi_cmd_exmc_diag.cpp diff --git a/GD32E508_DCU_APP/GD32E508_DCU_APP.cproject.yml b/GD32E508_DCU_APP/GD32E508_DCU_APP.cproject.yml index b9b8337..3b21c3b 100644 --- a/GD32E508_DCU_APP/GD32E508_DCU_APP.cproject.yml +++ b/GD32E508_DCU_APP/GD32E508_DCU_APP.cproject.yml @@ -57,12 +57,14 @@ project: - file: ./device/spi/app_spi_cmd_ota_wait.cpp - file: ./device/spi/app_spi_cmd_ping.cpp - file: ./device/spi/app_spi_cmd_can_bridge.cpp + - file: ./device/spi/app_spi_cmd_can_burst.cpp - file: ./device/spi/app_spi_cmd_can_feedback.cpp - file: ./device/spi/app_spi_cmd_id_snapshot.cpp - file: ./device/spi/app_spi_cmd_adc_feedback.cpp - file: ./device/spi/app_spi_cmd_motor_state.cpp - file: ./device/spi/app_spi_cmd_motor_version.cpp - file: ./device/spi/app_spi_cmd_motor_ota.cpp + - file: ./device/spi/app_spi_cmd_exmc_diag.cpp - group: spi_link files: - file: ../../spi_link/spi_link_v2.cpp @@ -72,10 +74,35 @@ project: - file: ./comm/esc_exmc_bus.cpp - file: ./comm/esc_lan9252.h - file: ./comm/esc_lan9252.cpp + - file: ./comm/esc_eeprom.h + - file: ./comm/esc_eeprom.cpp - file: ./comm/ethercat_slave.h - file: ./comm/ethercat_slave.cpp - file: ./comm/ethercat_sync.h - file: ./comm/ethercat_sync.cpp + - group: comm/ssc + files: + - file: ./comm/ssc/inc/ecatslv.h + - file: ./comm/ssc/inc/ecatappl.h + - file: ./comm/ssc/inc/mailbox.h + - file: ./comm/ssc/inc/ecatcoe.h + - file: ./comm/ssc/inc/sdoserv.h + - file: ./comm/ssc/inc/objdef.h + - file: ./comm/ssc/inc/coeappl.h + - file: ./comm/ssc/inc/esc.h + - file: ./comm/ssc/inc/applInterface.h + - file: ./comm/ssc/inc/el9800hw.h + - file: ./comm/ssc/src/ecatslv.c + - file: ./comm/ssc/src/ecatappl.c + - file: ./comm/ssc/src/mailbox.c + - file: ./comm/ssc/src/ecatcoe.c + - file: ./comm/ssc/src/sdoserv.c + - file: ./comm/ssc/src/objdef.c + - file: ./comm/ssc/src/coeappl.c + - file: ./comm/ssc/port/hw_gd32exmc.cpp + - file: ./comm/ssc/port/dc_app.c + - file: ./comm/ssc/port/dc_app.h + - file: ./comm/ssc/port/ecat_def.h # List components to use for your application. # A software component is a re-usable unit that may be configurable. diff --git a/GD32E508_DCU_APP/comm/esc_eeprom.cpp b/GD32E508_DCU_APP/comm/esc_eeprom.cpp index 9a1ba66..1cee4cf 100644 --- a/GD32E508_DCU_APP/comm/esc_eeprom.cpp +++ b/GD32E508_DCU_APP/comm/esc_eeprom.cpp @@ -8,8 +8,8 @@ * * HBI 寄存器 EXMC 偏移(A1=EXMC_A16=PD11, A2=EXMC_A17=PD12): * HBI_IDX_0 低 word: A2=0, A1=0 → EXMC 偏移 0x00000 - * HBI_DATA_0 低 word: A2=1, A1=0 → EXMC 偏移 0x40000 - * HBI_DATA_0 高 word: A2=1, A1=1 → EXMC 偏移 0x60000 + * HBI_DATA_0 低 word: A2=1, A1=0 → EXMC 偏移 0x20000 + * HBI_DATA_0 高 word: A2=1, A1=1 → EXMC 偏移 0x30000 * * 注意:32-bit 寄存器访问时,写一次 IDX 后连续写/读两次 DATA_0(低 word 在前), * HBI 内部 BYTE/WORD 计数器会自动组装 DWORD,无需改 IDX 地址。 @@ -24,13 +24,13 @@ namespace { /* HBI Indexed Mode 寄存器 EXMC 偏移 * 地址解码(A2 是 IDX/DATA 选择位,必须接到 EXMC_A17=PD12): * A2=0, A1=0 → word 0 → 偏移 0x00000 → HBI_IDX_0 低 word - * A2=0, A1=1 → word 1 → 偏移 0x20000 → HBI_IDX_0 高 word(A1=EXMC_A16=PD11) - * A2=1, A1=0 → word 2 → 偏移 0x40000 → HBI_DATA_0 低 word(A2=EXMC_A17=PD12) - * A2=1, A1=1 → word 3 → 偏移 0x60000 → HBI_DATA_0 高 word - * 注:EXMC A16 对应 byte 偏移 0x20000,A17 对应 0x40000。 */ + * A2=0, A1=1 → word 1 → 偏移 0x10000 → HBI_IDX_0 高 word(A1=EXMC_A16=PD11) + * A2=1, A1=0 → word 2 → 偏移 0x20000 → HBI_DATA_0 低 word(A2=EXMC_A17=PD12) + * A2=1, A1=1 → word 3 → 偏移 0x30000 → HBI_DATA_0 高 word + * 注:EXMC A16 对应 byte 偏移 0x10000,A17 对应 0x20000。 */ constexpr uint32_t HBI_IDX0_OFFSET = 0x00000U; /* A2=0, A1=0 */ -constexpr uint32_t HBI_DATA0_LOW_OFF = 0x40000U; /* A2=1, A1=0 */ -constexpr uint32_t HBI_DATA0_HIGH_OFF = 0x60000U; /* A2=1, A1=1 */ +constexpr uint32_t HBI_DATA0_LOW_OFF = 0x20000U; /* A2=1, A1=0 */ +constexpr uint32_t HBI_DATA0_HIGH_OFF = 0x30000U; /* A2=1, A1=1 */ /* ESC EEPROM 寄存器字节地址 */ constexpr uint16_t ESC_EEP_CFG = 0x0500U; /* 8-bit */ @@ -76,14 +76,36 @@ bool waitIdle(uint32_t timeout_loops = 500000U) uint8_t readEsc8(uint16_t esc_addr) { - EscExmcBus::write16(HBI_IDX0_OFFSET, esc_addr); - return (uint8_t)(EscExmcBus::read16(HBI_DATA0_LOW_OFF) & 0x00FFU); + const uint16_t idx = esc_addr & 0xFFFCU; + EscExmcBus::write16(HBI_IDX0_OFFSET, idx); + const uint8_t byte_offset = esc_addr & 0x3U; + if (byte_offset < 2U) { + uint16_t w = EscExmcBus::read16(HBI_DATA0_LOW_OFF); + return (byte_offset == 0U) ? (uint8_t)(w & 0xFFU) : (uint8_t)((w >> 8) & 0xFFU); + } else { + uint16_t w = EscExmcBus::read16(HBI_DATA0_HIGH_OFF); + return (byte_offset == 2U) ? (uint8_t)(w & 0xFFU) : (uint8_t)((w >> 8) & 0xFFU); + } } void writeEsc8(uint16_t esc_addr, uint8_t val) { - EscExmcBus::write16(HBI_IDX0_OFFSET, esc_addr); - EscExmcBus::write16(HBI_DATA0_LOW_OFF, (uint16_t)val); + const uint16_t idx = esc_addr & 0xFFFCU; + EscExmcBus::write16(HBI_IDX0_OFFSET, idx); + const uint8_t byte_offset = esc_addr & 0x3U; + if (byte_offset < 2U) { + uint16_t w = EscExmcBus::read16(HBI_DATA0_LOW_OFF); + w = (byte_offset == 0U) ? ((w & 0xFF00U) | (uint16_t)val) + : ((w & 0x00FFU) | ((uint16_t)val << 8)); + EscExmcBus::write16(HBI_IDX0_OFFSET, idx); + EscExmcBus::write16(HBI_DATA0_LOW_OFF, w); + } else { + uint16_t w = EscExmcBus::read16(HBI_DATA0_HIGH_OFF); + w = (byte_offset == 2U) ? ((w & 0xFF00U) | (uint16_t)val) + : ((w & 0x00FFU) | ((uint16_t)val << 8)); + EscExmcBus::write16(HBI_IDX0_OFFSET, idx); + EscExmcBus::write16(HBI_DATA0_HIGH_OFF, w); + } } uint16_t readEsc16(uint16_t esc_addr) @@ -102,21 +124,30 @@ uint32_t readEsc32(uint16_t esc_addr) { EscExmcBus::write16(HBI_IDX0_OFFSET, esc_addr); uint16_t lo = EscExmcBus::read16(HBI_DATA0_LOW_OFF); - uint16_t hi = EscExmcBus::read16(HBI_DATA0_LOW_OFF); /* 计数器自增,读高 word */ + uint16_t hi = EscExmcBus::read16(HBI_DATA0_HIGH_OFF); /* HBI_DATA_0 高 16 位 */ return ((uint32_t)hi << 16) | lo; } void writeEsc32(uint16_t esc_addr, uint32_t val) { EscExmcBus::write16(HBI_IDX0_OFFSET, esc_addr); - EscExmcBus::write16(HBI_DATA0_LOW_OFF, (uint16_t)(val & 0xFFFFU)); /* 低 word */ - EscExmcBus::write16(HBI_DATA0_LOW_OFF, (uint16_t)((val >> 16) & 0xFFFFU));/* 高 word */ + EscExmcBus::write16(HBI_DATA0_LOW_OFF, (uint16_t)(val & 0xFFFFU)); /* 低 word */ + EscExmcBus::write16(HBI_DATA0_HIGH_OFF, (uint16_t)((val >> 16) & 0xFFFFU));/* 高 word */ } } /* anonymous namespace */ bool EscEeprom::s_inited = false; +namespace { +/* 简单延时(~0.5ms @180MHz,volatile 防优化) */ +void delayApprox(void) +{ + volatile uint32_t n = 22000U; + while (n-- > 0U) { __NOP(); } +} +} /* anonymous namespace */ + bool EscEeprom::init() { s_inited = false; @@ -126,37 +157,72 @@ bool EscEeprom::init() return false; } - /* 步骤 1:置 PDI EEPROM Control = 1,把 EEPROM 接口交给 PDI(GD32) */ - writeEsc8(ESC_EEP_CFG, EEP_CFG_PDI_CTRL); + /* LAN9252 上电后 EEPROM 控制器先做初始加载(读 AT24C32), + * 即使 EEPROM 空白也需要几毫秒完成/超时。此期间 PDI 写 0x0500 会被忽略。 + * 重试最多 50 次(~100ms),等待控制器释放后 PDI 才能接管。 */ + bool acquired = false; + for (uint32_t attempt = 0U; attempt < 50U; ++attempt) { + /* 首次重试前打印诊断:raw 读 + 直接 word 写(绕过 writeEsc8 字节 RMW) */ + if (attempt == 0U) { + EscExmcBus::write16(HBI_IDX0_OFFSET, ESC_EEP_CFG); + uint16_t raw_cfg = EscExmcBus::read16(HBI_DATA0_LOW_OFF); + EscExmcBus::write16(HBI_IDX0_OFFSET, ESC_EEP_CTRL); + uint16_t raw_ctrl = EscExmcBus::read16(HBI_DATA0_LOW_OFF); + printf("[EEPROM] diag: raw cfg(0x0500)=0x%04X ctrl(0x0502)=0x%04X\r\n", + (unsigned)raw_cfg, (unsigned)raw_ctrl); + if (raw_cfg == ESC_EEP_CFG && raw_ctrl == ESC_EEP_CTRL) { + /* 读回值 == 刚写入的 IDX 值 => 数据总线回显,LAN9252 完全没有驱动总线。 + * 注意:LAN9252 的 HBI 在 EEPROM 空白时也工作(手册 9.5.4.2), + * 所以这不是 EEPROM 问题,而是:CS(PD7/NE0)未拉低、 + * CHIP_MODE0/1 strap 非 00(三端口模式=SPI only)、 + * 或芯片未复位/未上电。先看 [ECAT][ESC] HBI_IDX_0 探针。 */ + printf("[EEPROM] diag: *** LAN9252 NOT RESPONDING (bus echo of IDX) ***\r\n"); + printf("[EEPROM] diag: *** check CS(PD7) pulse / CHIP_MODE straps=00 / RST# ***\r\n"); + } + /* 尝试直接 16-bit word 写 0x0001 到 0x0500 */ + EscExmcBus::write16(HBI_IDX0_OFFSET, ESC_EEP_CFG); + EscExmcBus::write16(HBI_DATA0_LOW_OFF, 0x0001U); + EscExmcBus::write16(HBI_IDX0_OFFSET, ESC_EEP_CFG); + uint16_t raw_after = EscExmcBus::read16(HBI_DATA0_LOW_OFF); + printf("[EEPROM] diag: after direct word write, cfg=0x%04X\r\n", (unsigned)raw_after); + } - /* 步骤 2:置 Access to EEPROM = 1,获取访问权 */ - writeEsc8(ESC_EEP_PDI_ACC, EEP_PDI_ACC_ACCESS); + /* 步骤 1:置 PDI EEPROM Control = 1,把 EEPROM 接口交给 PDI(GD32) */ + writeEsc8(ESC_EEP_CFG, EEP_CFG_PDI_CTRL); - /* 步骤 3:等待接口空闲,读回状态确认 */ - if (!waitIdle()) { - printf("[EEPROM] init: FAIL, EEPROM interface busy timeout\r\n"); - return false; + /* 步骤 2:置 Access to EEPROM = 1,获取访问权 */ + writeEsc8(ESC_EEP_PDI_ACC, EEP_PDI_ACC_ACCESS); + + /* 步骤 3:等待接口空闲 */ + if (!waitIdle()) { + delayApprox(); + continue; + } + + /* 读回 Configuration 寄存器确认 PDI 控制已生效 */ + uint8_t cfg = readEsc8(ESC_EEP_CFG); + if ((cfg & EEP_CFG_PDI_CTRL) != 0U) { + acquired = true; + + /* 确认 EEPROM 算法:AT24C32 = 32Kbit,需要 2 地址字节模式 */ + uint16_t ctrl = readEsc16(ESC_EEP_CTRL); + if ((ctrl & EEP_CTRL_2ADDR) == 0U) { + printf("[EEPROM] init: WARN, 2-address-byte mode not set (ctrl=0x%04X)\r\n", + (unsigned)ctrl); + } + + s_inited = true; + printf("[EEPROM] init: OK (attempt %u, cfg=0x%02X, ctrl=0x%04X)\r\n", + (unsigned)(attempt + 1U), (unsigned)cfg, (unsigned)ctrl); + return true; + } + + /* PDI 控制位未生效,等待后重试 */ + delayApprox(); } - /* 读回 Configuration 寄存器确认 PDI 控制已生效 */ - uint8_t cfg = readEsc8(ESC_EEP_CFG); - if ((cfg & EEP_CFG_PDI_CTRL) == 0U) { - printf("[EEPROM] init: FAIL, PDI EEPROM Control not set (cfg=0x%02X)\r\n", (unsigned)cfg); - return false; - } - - /* 确认 EEPROM 算法:AT24C32 = 32Kbit,需要 2 地址字节模式 */ - uint16_t ctrl = readEsc16(ESC_EEP_CTRL); - if ((ctrl & EEP_CTRL_2ADDR) == 0U) { - printf("[EEPROM] init: WARN, 2-address-byte mode not set (ctrl=0x%04X), AT24C32 requires it\r\n", - (unsigned)ctrl); - /* 不直接 fail,允许继续尝试;可能由 strap 自动配置 */ - } - - s_inited = true; - printf("[EEPROM] init: OK, PDI has EEPROM control (cfg=0x%02X, ctrl=0x%04X)\r\n", - (unsigned)cfg, (unsigned)ctrl); - return true; + printf("[EEPROM] init: FAIL, PDI control not acquired after 50 retries\r\n"); + return false; } bool EscEeprom::readWord(uint16_t word_addr, uint16_t &out) diff --git a/GD32E508_DCU_APP/comm/esc_exmc_bus.cpp b/GD32E508_DCU_APP/comm/esc_exmc_bus.cpp index a315515..3f2ced4 100644 --- a/GD32E508_DCU_APP/comm/esc_exmc_bus.cpp +++ b/GD32E508_DCU_APP/comm/esc_exmc_bus.cpp @@ -8,133 +8,465 @@ */ #include "comm/esc_exmc_bus.h" #include "sys/config.h" +#include "device/watchdog.h" #include extern "C" { -#include "gd32e50x_rcu.h" +#include "gd32e50x_exmc.h" /* RTE/Device/GD32E508VE 或 -I 同目录 */ #include "gd32e50x_gpio.h" -#include "gd32e50x_exmc.h" /* RTE/Device/GD32E508VE 或 -I 同目录 */ +#include "gd32e50x_rcu.h" } /* 内存映射访问宏:基址 + 偏移(按 16-bit 半字对齐,LAN9252 HBI 地址左移 1 位)。 */ -#define ESC_EXMC_REG16(offset) (*(volatile uint16_t*)(CONFIG_ESC_EXMC_BASE_ADDR + (offset))) +#define ESC_EXMC_REG16(offset) \ + (*(volatile uint16_t *)(CONFIG_ESC_EXMC_BASE_ADDR + (offset))) + +/* ======================================================================== + * GPIO 位拍(bit-bang)辅助函数 —— 完全绕过 EXMC 外设, + * 用普通 GPIO 手动产生 LAN9252 HBI 时序,用于判定"芯片是否活着"。 + * 数据线物理映射:D0=PD14 D1=PD15 D2=PD0 D3=PD1 D4..D12=PE7..PE15 + * D13=PD8 D14=PD9 D15=PD10 + * 控制线:CS#=PD7(NE0) RD#=PD4(NOE) WR#=PD5(NWE) A1=PD11 A2=PD12 + * ======================================================================== */ +namespace { + +/* 通断测试模式状态(提前声明供 diagDump() 检查) */ +static bool s_continuityActive = false; + +/* GPIOD/GPIOE 上属于数据总线的引脚掩码 */ +constexpr uint32_t kDataMaskD = + (1U << 0) | (1U << 1) | (1U << 8) | (1U << 9) | (1U << 10) | (1U << 14) | + (1U << 15); +constexpr uint32_t kDataMaskE = 0xFF80U; /* PE7..PE15 */ + +constexpr uint32_t kPinCs = 7U; /* PD7 */ +constexpr uint32_t kPinRd = 4U; /* PD4 */ +constexpr uint32_t kPinWr = 5U; /* PD5 */ +constexpr uint32_t kPinA1 = 11U; /* PD11 */ +constexpr uint32_t kPinA2 = 12U; /* PD12 */ +/* LAN9252 MODE0/1 strap 引脚:临时用 PB8/PB9 模拟拉高/拉低测试。 + * 实际硬件 MODE0(Pin48)=PB8, MODE1(Pin46)=PB9(若未接 PB8/PB9 则此测试无效)。 */ +constexpr uint32_t kPinMode0 = 8U; /* PB8 -> LAN9252 Pin48 MODE0 */ +constexpr uint32_t kPinMode1 = 9U; /* PB9 -> LAN9252 Pin46 MODE1 */ + +void bbDelay() { + for (volatile uint32_t i = 0U; i < 3000U; i++) { + } /* ~40-80us @180MHz,远大于 HBI 时序要求 */ +} + +uint16_t bbReadData() { + uint32_t d = GPIO_ISTAT(GPIOD); + uint32_t e = GPIO_ISTAT(GPIOE); + uint16_t v = 0U; + if (d & (1U << 14)) v |= (uint16_t)(1U << 0); /* D0 */ + if (d & (1U << 15)) v |= (uint16_t)(1U << 1); /* D1 */ + if (d & (1U << 0)) v |= (uint16_t)(1U << 2); /* D2 */ + if (d & (1U << 1)) v |= (uint16_t)(1U << 3); /* D3 */ + if (e & (1U << 7)) v |= (uint16_t)(1U << 4); /* D4 */ + if (e & (1U << 8)) v |= (uint16_t)(1U << 5); /* D5 */ + if (e & (1U << 9)) v |= (uint16_t)(1U << 6); /* D6 */ + if (e & (1U << 10)) v |= (uint16_t)(1U << 7); /* D7 */ + if (e & (1U << 11)) v |= (uint16_t)(1U << 8); /* D8 */ + if (e & (1U << 12)) v |= (uint16_t)(1U << 9); /* D9 */ + if (e & (1U << 13)) v |= (uint16_t)(1U << 10); /* D10 */ + if (e & (1U << 14)) v |= (uint16_t)(1U << 11); /* D11 */ + if (e & (1U << 15)) v |= (uint16_t)(1U << 12); /* D12 */ + if (d & (1U << 8)) v |= (uint16_t)(1U << 13); /* D13 */ + if (d & (1U << 9)) v |= (uint16_t)(1U << 14); /* D14 */ + if (d & (1U << 10)) v |= (uint16_t)(1U << 15); /* D15 */ + return v; +} + +void bbDriveData(uint16_t v) { + uint32_t dset = 0U, dclr = 0U, eset = 0U, eclr = 0U; + /* GPIOD 数据位 */ + const uint32_t dpins[7] = {14U, 15U, 0U, 1U, 8U, 9U, 10U}; + const int dbits[7] = {0, 1, 2, 3, 13, 14, 15}; + for (int i = 0; i < 7; ++i) { + if (v & (1U << dbits[i])) dset |= (1U << dpins[i]); + else dclr |= (1U << dpins[i]); + } + /* GPIOE 数据位:D4..D12 = PE7..PE15 = 数据位 4..12 */ + for (int bit = 4; bit <= 12; ++bit) { + uint32_t pin = (uint32_t)(bit + 3); /* bit4->PE7 ... bit12->PE15 */ + if (v & (1U << bit)) eset |= (1U << pin); + else eclr |= (1U << pin); + } + if (dset) GPIO_BOP(GPIOD) = dset; + if (dclr) GPIO_BC(GPIOD) = dclr; + if (eset) GPIO_BOP(GPIOE) = eset; + if (eclr) GPIO_BC(GPIOE) = eclr; +} + +} /* anonymous namespace */ bool EscExmcBus::s_ok = false; +bool EscExmcBus::init() { + s_ok = false; + ECAT_LOGI("[ECAT][EXMC] init: enable RCU clocks\r\n"); + rcu_periph_clock_enable(RCU_GPIOD); + rcu_periph_clock_enable(RCU_GPIOE); + rcu_periph_clock_enable(RCU_AF); + rcu_periph_clock_enable(RCU_EXMC); + /* 配置 16-bit 数据线 D0-D15 为 AF 推挽 */ + gpio_init(CONFIG_EXMC_D0_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_D0_PIN); + gpio_init(CONFIG_EXMC_D1_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_D1_PIN); + gpio_init(CONFIG_EXMC_D2_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_D2_PIN); + gpio_init(CONFIG_EXMC_D3_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_D3_PIN); + gpio_init(CONFIG_EXMC_D4_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_D4_PIN); + gpio_init(CONFIG_EXMC_D5_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_D5_PIN); + gpio_init(CONFIG_EXMC_D6_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_D6_PIN); + gpio_init(CONFIG_EXMC_D7_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_D7_PIN); + gpio_init(CONFIG_EXMC_D8_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_D8_PIN); + gpio_init(CONFIG_EXMC_D9_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_D9_PIN); + gpio_init(CONFIG_EXMC_D10_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_D10_PIN); + gpio_init(CONFIG_EXMC_D11_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_D11_PIN); + gpio_init(CONFIG_EXMC_D12_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_D12_PIN); + gpio_init(CONFIG_EXMC_D13_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_D13_PIN); + gpio_init(CONFIG_EXMC_D14_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_D14_PIN); + gpio_init(CONFIG_EXMC_D15_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_D15_PIN); -bool EscExmcBus::init() -{ - s_ok = false; + /* 控制线:A16 / A17 / NOE / NWE / NE1 + * A17(PD12) 用于 HBI Indexed Mode 访问 HBI_DATA_0,已将 LAN9252 A2 (Pin 29) + * 接到 PD12。 */ + gpio_init(CONFIG_EXMC_A16_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_A16_PIN); + gpio_init(CONFIG_EXMC_A17_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_A17_PIN); + gpio_init(CONFIG_EXMC_NOE_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_NOE_PIN); + gpio_init(CONFIG_EXMC_NWE_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_NWE_PIN); + gpio_init(CONFIG_EXMC_NE1_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, + CONFIG_EXMC_NE1_PIN); + ECAT_LOGI( + "[ECAT][EXMC] init: GPIO configured (D0-D15/A16/A17/NOE/NWE/NE1)\r\n"); - ECAT_LOGI("[ECAT][EXMC] init: enable RCU clocks\r\n"); - rcu_periph_clock_enable(RCU_GPIOD); - rcu_periph_clock_enable(RCU_GPIOE); - rcu_periph_clock_enable(RCU_AF); - rcu_periph_clock_enable(RCU_EXMC); + /* + * EXMC NOR/SRAM Region0:16-bit、非复用、异步写。 + * 注意:GD32E508Vx (LQFP100) 只有 NE0 (PD7) 一个 NOR/SRAM 片选引脚, + * NE0 归属 Region0 (0x60000000, SNCTL0),必须用 Region0。 + * 时序(AHB=180MHz,1 HCLK ≈ 5.56ns): + * LAN9252 HBI 最小读/写周期约 100ns → data_setup ≥ 18 HCLK。 + * asyn_* 字段单位为 HCLK 计数(库文档范围 + * address_setup 1..16,data_setup 2..256)。 + */ + exmc_norsram_parameter_struct norsram_init; + exmc_norsram_timing_parameter_struct rw_timing; + exmc_norsram_timing_parameter_struct wr_timing; - /* 配置 16-bit 数据线 D0-D15 为 AF 推挽 */ - gpio_init(CONFIG_EXMC_D0_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D0_PIN); - gpio_init(CONFIG_EXMC_D1_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D1_PIN); - gpio_init(CONFIG_EXMC_D2_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D2_PIN); - gpio_init(CONFIG_EXMC_D3_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D3_PIN); - gpio_init(CONFIG_EXMC_D4_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D4_PIN); - gpio_init(CONFIG_EXMC_D5_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D5_PIN); - gpio_init(CONFIG_EXMC_D6_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D6_PIN); - gpio_init(CONFIG_EXMC_D7_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D7_PIN); - gpio_init(CONFIG_EXMC_D8_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D8_PIN); - gpio_init(CONFIG_EXMC_D9_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D9_PIN); - gpio_init(CONFIG_EXMC_D10_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D10_PIN); - gpio_init(CONFIG_EXMC_D11_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D11_PIN); - gpio_init(CONFIG_EXMC_D12_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D12_PIN); - gpio_init(CONFIG_EXMC_D13_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D13_PIN); - gpio_init(CONFIG_EXMC_D14_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D14_PIN); - gpio_init(CONFIG_EXMC_D15_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D15_PIN); + norsram_init.read_write_timing = &rw_timing; + norsram_init.write_timing = &wr_timing; + exmc_norsram_struct_para_init(&norsram_init); - /* 控制线:A16 / A17 / NOE / NWE / NE1 - * A17(PD12) 用于 HBI Indexed Mode 访问 HBI_DATA_0,已将 LAN9252 A2 (Pin 29) 接到 PD12。 */ - gpio_init(CONFIG_EXMC_A16_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_A16_PIN); - gpio_init(CONFIG_EXMC_A17_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_A17_PIN); - gpio_init(CONFIG_EXMC_NOE_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_NOE_PIN); - gpio_init(CONFIG_EXMC_NWE_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_NWE_PIN); - gpio_init(CONFIG_EXMC_NE1_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_NE1_PIN); - ECAT_LOGI("[ECAT][EXMC] init: GPIO configured (D0-D15/A16/A17/NOE/NWE/NE1)\r\n"); + norsram_init.norsram_region = CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION0; + norsram_init.write_mode = EXMC_ASYN_WRITE; + norsram_init.extended_mode = DISABLE; + norsram_init.asyn_wait = DISABLE; + norsram_init.nwait_signal = DISABLE; + norsram_init.memory_write = ENABLE; + norsram_init.nwait_config = EXMC_NWAIT_CONFIG_BEFORE; + norsram_init.wrap_burst_mode = DISABLE; + norsram_init.nwait_polarity = EXMC_NWAIT_POLARITY_LOW; + norsram_init.burst_mode = DISABLE; + norsram_init.databus_width = EXMC_NOR_DATABUS_WIDTH_16B; + norsram_init.memory_type = EXMC_MEMORY_TYPE_SRAM; + norsram_init.address_data_mux = DISABLE; - /* - * EXMC NOR/SRAM Region1:16-bit、非复用、异步写。 - * 时序(AHB=180MHz,1 HCLK ≈ 5.56ns): - * LAN9252 HBI 最小读/写周期约 100ns → data_setup ≥ 18 HCLK。 - * asyn_* 字段单位为 HCLK 计数(库文档范围 address_setup 1..16,data_setup 2..256)。 - */ - exmc_norsram_parameter_struct norsram_init; - exmc_norsram_timing_parameter_struct rw_timing; - exmc_norsram_timing_parameter_struct wr_timing; + /* 读/写共用时序(非 extended mode) */ + rw_timing.asyn_access_mode = EXMC_ACCESS_MODE_A; + rw_timing.syn_data_latency = EXMC_DATALAT_2_CLK; + rw_timing.syn_clk_division = EXMC_SYN_CLOCK_RATIO_DISABLE; + rw_timing.bus_latency = 1U; + rw_timing.asyn_address_setuptime = 2U; /* ~11ns */ + rw_timing.asyn_address_holdtime = 2U; /* min 2 */ + rw_timing.asyn_data_setuptime = 20U; /* ~111ns ≥ 100ns HBI */ - norsram_init.read_write_timing = &rw_timing; - norsram_init.write_timing = &wr_timing; - exmc_norsram_struct_para_init(&norsram_init); + wr_timing = rw_timing; - norsram_init.norsram_region = CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION1; - norsram_init.write_mode = EXMC_ASYN_WRITE; - norsram_init.extended_mode = DISABLE; - norsram_init.asyn_wait = DISABLE; - norsram_init.nwait_signal = DISABLE; - norsram_init.memory_write = ENABLE; - norsram_init.nwait_config = EXMC_NWAIT_CONFIG_BEFORE; - norsram_init.wrap_burst_mode = DISABLE; - norsram_init.nwait_polarity = EXMC_NWAIT_POLARITY_LOW; - norsram_init.burst_mode = DISABLE; - norsram_init.databus_width = EXMC_NOR_DATABUS_WIDTH_16B; - norsram_init.memory_type = EXMC_MEMORY_TYPE_SRAM; - norsram_init.address_data_mux = DISABLE; + ECAT_LOGI("[ECAT][EXMC] init: applying NOR/SRAM Region0 params (16b, async, " + "t_data=%u HCLK)\r\n", + (unsigned)rw_timing.asyn_data_setuptime); - /* 读/写共用时序(非 extended mode) */ - rw_timing.asyn_access_mode = EXMC_ACCESS_MODE_A; - rw_timing.syn_data_latency = EXMC_DATALAT_2_CLK; - rw_timing.syn_clk_division = EXMC_SYN_CLOCK_RATIO_DISABLE; - rw_timing.bus_latency = 1U; - rw_timing.asyn_address_setuptime = 2U; /* ~11ns */ - rw_timing.asyn_address_holdtime = 2U; /* min 2 */ - rw_timing.asyn_data_setuptime = 20U; /* ~111ns ≥ 100ns HBI */ + exmc_norsram_init(&norsram_init); + exmc_norsram_enable(CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION0); - wr_timing = rw_timing; + /* === 清除 reserved bit 7 === + * 现象:exmc_norsram_init 后 EXMC_SNCTL0=0x00001091,bit 7(reserved)=1。 + * DFP 复位值 0x000030DB 的 bit 7=1,可能被硬件保留或导致未定义行为。 + * NRW(bits 4-5)=01 已经是 16-bit,无需修改。 + * 修复:清除 bit 7,保持其他位不变。 */ + uint32_t snctl_before = EXMC_SNCTL0; + EXMC_SNCTL0 = snctl_before & ~BIT(7); + uint32_t snctl_after = EXMC_SNCTL0; + ECAT_LOGI("[ECAT][EXMC] init: SNCTL0 fix: before=0x%08lX after=0x%08lX (bit7 reserved cleared)\r\n", + (unsigned long)snctl_before, (unsigned long)snctl_after); - ECAT_LOGI("[ECAT][EXMC] init: applying NOR/SRAM Region1 params (16b, async, t_data=%u HCLK)\r\n", - (unsigned)rw_timing.asyn_data_setuptime); + /* 总线探活:LAN9252 处于 16-bit HBI Indexed Mode,无法通过固定偏移直接读取。 + * 真实的芯片 ID / AL 状态校验由 EscLan9252::init() 通过 HBI_IDX_0/HBI_DATA_0 + * 间接访问完成。 注意:LAN9252 上电后第一次 Host Bus + * 操作必须是读周期,否则后续写操作会被忽略; EscLan9252 在 init 中首先读取 + * CHIP_ID,满足该约束。 */ + ECAT_LOGI("[ECAT][EXMC] init: Region0 enabled, chip ID will be verified by " + "ESC driver\r\n"); - exmc_norsram_init(&norsram_init); - exmc_norsram_enable(CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION1); + /* ---- 硬件诊断: 转储关键寄存器 + GPIO 手动输出测试 ---- */ + diagDump(); - /* 总线探活:Region1 使能后立即读取 LAN9252 芯片 ID 低半字(期望 0x4321)。 - * 0x0000/0xFFFF 通常意味着 EXMC 时序不满足、地址线接错或 ESC 未上电; - * 此处仅做总线可见性探活,芯片 ID 完整性校验留给 EscLan9252::init()。 */ - const uint16_t probe_low = read16((uint32_t)0x0000U << 1U); - const uint16_t probe_high = read16((uint32_t)0x0002U << 1U); - const uint32_t probe_id = ((uint32_t)probe_high << 16) | probe_low; - ECAT_LOGI("[ECAT][EXMC] init: Region1 enabled, probe id_lo=0x%04X id_hi=0x%04X (id=0x%08lX)\r\n", - (unsigned)probe_low, (unsigned)probe_high, (unsigned long)probe_id); + s_ok = true; + ECAT_LOGI("[ECAT][EXMC] init: OK, bus enabled\r\n"); + return true; +} - /* 总线挂死(全 0/全 1)视为 EXMC 未就绪;chip ID 完整校验在 EscLan9252::init()。 */ - if (probe_low == 0x0000U && probe_high == 0x0000U) { - LOG_ERR("[ECAT][EXMC] init: FAIL, bus reads all-zero (check wiring/NE1/A16/timing)\r\n"); - return false; +/* ======================================================================== + * 硬件诊断: 精简版 —— 只保留核心探活 + bit-bang + 位级回显 + * ======================================================================== */ +void EscExmcBus::diagDump() { + /* 通断测试模式活跃时跳过诊断,避免 20s 周期 probe 覆盖用户设置的电平 */ + if (s_continuityActive) { + printf("[EXMC-DIAG] skipped: continuity test mode active\r\n"); + return; + } + printf("[EXMC-DIAG] --- bit-bang probe (bypass EXMC) ---\r\n"); + + /* 保存原 GPIO 配置 */ + uint32_t save_ctl0_d = GPIO_CTL0(GPIOD); + uint32_t save_ctl1_d = GPIO_CTL1(GPIOD); + uint32_t save_ctl0_e = GPIO_CTL0(GPIOE); + uint32_t save_ctl1_e = GPIO_CTL1(GPIOE); + + /* 控制线 OUT_PP, 数据线 IN */ + gpio_init(GPIOD, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, + GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_7|GPIO_PIN_11|GPIO_PIN_12); + gpio_init(GPIOD, GPIO_MODE_IN_FLOATING, 0, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_8| + GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_14|GPIO_PIN_15); + gpio_init(GPIOE, GPIO_MODE_IN_FLOATING, 0, GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9| + GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15); + + /* 空闲:CS/RD/WR 高, A1=A2=0 */ + GPIO_BOP(GPIOD) = (1U<<7)|(1U<<4)|(1U<<5); + GPIO_BC(GPIOD) = (1U<<11)|(1U<<12); + bbDelay(); + + /* 读 IDX_0 (A2=0) —— 复位默认值 0x1234 */ + GPIO_BC(GPIOD) = (1U<<7); /* CS=低 */ + bbDelay(); + GPIO_BC(GPIOD) = (1U<<4); /* RD=低 */ + bbDelay(); + uint16_t bb_idx0 = bbReadData(); + GPIO_BOP(GPIOD) = (1U<<4); /* RD=高 */ + bbDelay(); + GPIO_BOP(GPIOD) = (1U<<7); /* CS=高 */ + bbDelay(); + + printf("[EXMC-DIAG] bit-bang IDX_0 = 0x%04X (expect 0x1234)\r\n", + (unsigned)bb_idx0); + + /* 判读 */ + if (bb_idx0 == 0x1234U) { + printf("[EXMC-DIAG] *** LAN9252 ALIVE ***\r\n"); + } else if (bb_idx0 == 0x0000U || bb_idx0 == 0xFFFFU) { + printf("[EXMC-DIAG] FAIL: bus floating (0x%04X) -> data line broken or chip not driving\r\n", + (unsigned)bb_idx0); + } else { + printf("[EXMC-DIAG] UNEXPECTED: 0x%04X -> partial data line issue?\r\n", + (unsigned)bb_idx0); + } + + /* 恢复 GPIO */ + GPIO_CTL0(GPIOD) = save_ctl0_d; + GPIO_CTL1(GPIOD) = save_ctl1_d; + GPIO_CTL0(GPIOE) = save_ctl0_e; + GPIO_CTL1(GPIOE) = save_ctl1_e; + + /* EXMC 寄存器回读(一行) */ + printf("[EXMC-DIAG] SNCTL0=0x%08lX SNTCFG0=0x%08lX\r\n", + (unsigned long)EXMC_SNCTL0, (unsigned long)EXMC_SNTCFG0); + + /* EXMC raw 读 + 位级回显(仅在未响应时) */ + uint16_t idx0_raw = ESC_EXMC_REG16(0x00000); + printf("[EXMC-DIAG] EXMC raw IDX_0 = 0x%04X\r\n", (unsigned)idx0_raw); + + if (idx0_raw != 0x1234U) { + /* 位级回显: 写 4 个 pattern 看哪些位回显(浮空)哪些位被驱动 */ + const uint16_t patterns[4] = {0x0000U, 0xFFFFU, 0xAAAAU, 0x5555U}; + uint16_t readbacks[4] = {0}; + for (int i = 0; i < 4; ++i) { + ESC_EXMC_REG16(0x00000) = patterns[i]; + for (volatile int d = 0; d < 50; d++) {} + readbacks[i] = ESC_EXMC_REG16(0x00000); } - if (probe_low == 0xFFFFU && probe_high == 0xFFFFU) { - LOG_ERR("[ECAT][EXMC] init: FAIL, bus reads all-ones (check ESC power/NOE/NWE)\r\n"); - return false; + uint16_t echo_bits = 0U, driven_bits = 0U; + for (int b = 0; b < 16; ++b) { + uint16_t mask = (uint16_t)(1U << b); + bool all_echo = true; + for (int i = 0; i < 4; ++i) { + if ((readbacks[i] & mask) != (patterns[i] & mask)) { all_echo = false; break; } + } + if (all_echo) echo_bits |= mask; else driven_bits |= mask; + } + printf("[EXMC-DIAG] echo bits(disconnected): 0x%04X driven bits(connected): 0x%04X\r\n", + (unsigned)echo_bits, (unsigned)driven_bits); + + if (echo_bits == 0xFFFFU) { + printf("[EXMC-DIAG] ALL 16 bits floating -> CS not reaching LAN9252 or chip dead\r\n"); + printf("[EXMC-DIAG] -> run continuityTest() to check data lines with multimeter\r\n"); + } else if (driven_bits == 0xFFFFU) { + printf("[EXMC-DIAG] ALL 16 bits driven -> HBI alive but IDX_0!=0x1234 (unexpected)\r\n"); + } else { + printf("[EXMC-DIAG] PARTIAL echo -> broken data lines: "); + for (int b = 0; b < 16; ++b) if (echo_bits & (1U << b)) printf("D%d ", b); + printf("\r\n"); + } + } else { + printf("[EXMC-DIAG] === HBI ALIVE ===\r\n"); + } +} + +/* ======================================================================== + * 通断测试: 网页逐个 pattern 触发,GD32 驱动数据线/控制线输出已知电平, + * 用户用万用表在 LAN9252 端测量对应引脚电压。 + * 设计: + * - continuityDrive(action) 不延时,驱动完立即返回,电平保持到下次调用 + * - action=0(stop) 恢复 GPIO 为 EXMC AF_PP,回到正常工作模式 + * - 不点按钮时引脚是高阻输入态(安全),手碰到也不短路 + * 判据: + * - GD32 输出高(3.3V)但 LAN9252 端测到 0V/浮空 → 断线 + * - GD32 输出低(0V)但 LAN9252 端测到 3.3V → 断线/短路 + * - 电压跟随 → 连线正常 + * ======================================================================== */ + +static uint32_t s_save_ctl0_d = 0, s_save_ctl1_d = 0; +static uint32_t s_save_ctl0_e = 0, s_save_ctl1_e = 0; + +static void continuityEnterTestMode() { + if (s_continuityActive) return; + s_save_ctl0_d = GPIO_CTL0(GPIOD); + s_save_ctl1_d = GPIO_CTL1(GPIOD); + s_save_ctl0_e = GPIO_CTL0(GPIOE); + s_save_ctl1_e = GPIO_CTL1(GPIOE); + /* 全部数据线 + 控制线设为 OUT_PP */ + gpio_init(GPIOD, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, + kDataMaskD | (1U< OUT_PP)\r\n"); +} + +static void continuityExitTestMode() { + if (!s_continuityActive) return; + GPIO_CTL0(GPIOD) = s_save_ctl0_d; + GPIO_CTL1(GPIOD) = s_save_ctl1_d; + GPIO_CTL0(GPIOE) = s_save_ctl0_e; + GPIO_CTL1(GPIOE) = s_save_ctl1_e; + s_continuityActive = false; + printf("[CONT] test mode exited (GPIO -> AF_PP restored)\r\n"); +} + +uint8_t EscExmcBus::continuityDrive(uint8_t action) { + switch (action) { + case 0: /* stop */ + continuityExitTestMode(); + return 0; + case 1: /* 0xFFFF ALL HIGH */ + case 2: /* 0x0000 ALL LOW */ + case 3: /* 0xAAAA ODD HIGH */ + case 4: { /* 0x5555 EVEN HIGH */ + continuityEnterTestMode(); + static const uint16_t pats[4] = {0xFFFF, 0x0000, 0xAAAA, 0x5555}; + uint16_t pat = pats[action - 1]; + bbDriveData(pat); + /* 控制线恢复空闲态(避免上次控制线测试残留) */ + GPIO_BOP(GPIOD) = (1U< HBI_IDX_0 低 16 位 + * - A2=1,A1=0 (偏移 0x20000) -> HBI_DATA_0 低 16 位 + * - A2=1,A1=1 (偏移 0x30000) -> HBI_DATA_0 高 16 位 + * + * 所有 ESC 寄存器/PDI 内存访问均通过"写 IDX -> 读写 DATA"完成。 */ #include "comm/esc_lan9252.h" #include "comm/esc_exmc_bus.h" @@ -10,15 +17,86 @@ #include CMSIS_device_header namespace { -/* LAN9252 ESC 关键寄存器地址(字节偏移,EXMC 访问) */ -constexpr uint16_t LAN9252_REG_CHIP_ID_L = 0x0000U; /**< 芯片 ID 低 16 位(=0x4321)。 */ -constexpr uint16_t LAN9252_REG_CHIP_ID_H = 0x0002U; /**< 芯片 ID 高 16 位(=0x8765)。 */ -constexpr uint32_t LAN9252_CHIP_ID_VALUE = 0x87654321U; /**< 期望的 32-bit 芯片 ID。 */ +/* HBI Indexed Mode 下 EXMC 偏移(A2=EXMC_A17=PD12, A1=EXMC_A16=PD11) + * 注意: A16=bit16=0x10000, A17=bit17=0x20000 (不是 0x40000!) */ +constexpr uint32_t HBI_IDX0_OFFSET = 0x00000U; /* A2=0, A1=0 */ +constexpr uint32_t HBI_DATA0_LOW_OFF = 0x20000U; /* A2=1(A17), A1=0(A16) */ +constexpr uint32_t HBI_DATA0_HIGH_OFF = 0x30000U; /* A2=1(A17), A1=1(A16) */ -/* PDI 内存基址偏移(实际取决于 LAN9252 PDI 配置,这里为示意值) */ -constexpr uint16_t LAN9252_PDI_BASE_OFFSET = 0x1000U; +/* ESC 关键寄存器字节地址。 + * 注: LAN9252 的 HBI 在 EEPROM 空白/无效时依然工作(数据手册 9.5.4.2: + * 先轮询 HBI_IDX_0 默认值 0x1234,再轮询 BYTE_TEST(0x064)=0x87654321)。 + * 芯片身份探针用 BYTE_TEST 寄存器(0x064),不是 ESC 0x0000(那是类型寄存器=0xC0)。 */ +constexpr uint16_t LAN9252_REG_BYTE_TEST = 0x0064U; /**< 字节顺序测试寄存器(默认 0x87654321,LAN9252 手册 16.1.2)。 */ +constexpr uint32_t LAN9252_BYTE_TEST_VALUE = 0x87654321U; /**< BYTE_TEST 默认值(小端序)。 */ + +/* PDI 内存基址偏移。 + * CONFIG_ESC_PDI_INPUT_OFFSET / CONFIG_ESC_PDI_OUTPUT_OFFSET 已包含 SyncManager + * 在 ESC 内存空间中的实际地址,因此这里基址为 0。 */ +constexpr uint16_t LAN9252_PDI_BASE_OFFSET = 0x0000U; + +__attribute__((section(".ramfunc"), noinline)) +void setHbiIndex(uint16_t addr) +{ + EscExmcBus::write16(HBI_IDX0_OFFSET, addr); } +__attribute__((section(".ramfunc"), noinline)) +uint16_t readDataLow() +{ + return EscExmcBus::read16(HBI_DATA0_LOW_OFF); +} + +__attribute__((section(".ramfunc"), noinline)) +uint16_t readDataHigh() +{ + return EscExmcBus::read16(HBI_DATA0_HIGH_OFF); +} + +__attribute__((section(".ramfunc"), noinline)) +void writeDataLow(uint16_t v) +{ + EscExmcBus::write16(HBI_DATA0_LOW_OFF, v); +} + +__attribute__((section(".ramfunc"), noinline)) +void writeDataHigh(uint16_t v) +{ + EscExmcBus::write16(HBI_DATA0_HIGH_OFF, v); +} + +__attribute__((section(".ramfunc"), noinline)) +uint16_t readReg16(uint16_t addr) +{ + setHbiIndex(addr); + return readDataLow(); +} + +__attribute__((section(".ramfunc"), noinline)) +uint32_t readReg32(uint16_t addr) +{ + setHbiIndex(addr); + uint16_t lo = readDataLow(); + uint16_t hi = readDataHigh(); + return ((uint32_t)hi << 16) | lo; +} + +__attribute__((section(".ramfunc"), noinline)) +void writeReg16(uint16_t addr, uint16_t v) +{ + setHbiIndex(addr); + writeDataLow(v); +} + +__attribute__((section(".ramfunc"), noinline)) +void writeReg32(uint16_t addr, uint32_t v) +{ + setHbiIndex(addr); + writeDataLow((uint16_t)(v & 0xFFFFU)); + writeDataHigh((uint16_t)((v >> 16) & 0xFFFFU)); +} +} /* anonymous namespace */ + bool EscLan9252::s_busReady = false; bool EscLan9252::init() @@ -33,52 +111,44 @@ bool EscLan9252::init() printf("[ECAT][ESC] init: FAIL, EXMC bus not ready (see [ECAT][EXMC] log)\r\n"); return false; } - printf("[ECAT][ESC] init: EXMC bus ready\r\n"); - /* ============================================================ - * TODO(spec): 完整 ESC 寄存器配置(需 SSC 或等价协议栈代码,本 spec 不实现) - * ============================================================ - * 当前仅完成 EXMC 总线初始化与芯片 ID 读取。要使 SYNC0 实际触发,还需配置: - * - * 1. SyncManager(SM)初始化: - * - SM0/SM1:邮箱输入/输出(IN/OUT),地址 0x0520-0x0530 - * - SM2:TxPDO(输入),地址 0x0530,配置为 3-buffer 模式 - * - SM3:RxPDO(输出),地址 0x0540,配置为 3-buffer 模式 - * - 各 SM 的 StartAddr/Length/Control/Status/Activate 寄存器 - * - * 2. FMMU(Fieldbus Memory Mapping Unit)配置: - * - FMMU0:TxPDO 映射(逻辑地址 → 物理地址,读) - * - FMMU1:RxPDO 映射(逻辑地址 → 物理地址,写) - * - 地址 0x0580-0x0590 - * - * 3. AL Control(0x0120):写入 0x08 请求 OP 状态(需主站配合完成状态迁移) - * - * 4. SYNC0 生成寄存器: - * - 0x0981-0x0982:SYNC0 周期(cycle time,单位 ns) - * - 0x0983:SYNC0 脉冲长度 - * - 0x0984:SYNC0 控制(使能/触发模式) - * - 需主站 DC 同步后由 ESC 自动生成 SYNC0 脉冲 - * - * 这些步骤通常由 EtherCAT SSC(Slave Stack Code)或等价协议栈代码完成, - * 涉及 AL 状态机管理、邮箱通信、PDO 映射协商等复杂逻辑。 - * 本 spec 仅通过 EthercatSlave::isEscReady() 的 AL 状态守卫使 - * "SYNC0 不会触发"在运行期可观测,完整配置留待 SSC 集成 spec。 - * ============================================================ */ + /* HBI 存活探针(数据手册 9.5.4.2):先直接读 HBI_IDX_0(offset 0,不写任何东西), + * 其复位默认值 = 0x1234。若读回 != 0x1234(通常是 0x0000/0xFFFF 或上次写入的回显), + * 说明 LAN9252 完全没有驱动总线——CS 未拉低、CHIP_MODE strap 非 00(三端口=SPI only) + * 或芯片未复位/未上电,而不是 EEPROM 问题(HBI 在 EEPROM 空白时也工作)。 */ + const uint16_t idx0 = EscExmcBus::read16(HBI_IDX0_OFFSET); + if (idx0 != 0x1234U) { + printf("[ECAT][ESC] init: FAIL, HBI_IDX_0=0x%04X (expect 0x1234) -> LAN not driving bus\r\n", + (unsigned)idx0); + printf("[ECAT][ESC] hint: check CS(PD7) pulse, CHIP_MODE0/1 straps=00, RST#/power\r\n"); + return false; + } + + /* 再读 BYTE_TEST(0x064)确认接口完全就绪,默认 0x87654321(小端)。 */ + const uint32_t byteTest = readReg32(LAN9252_REG_BYTE_TEST); + if (byteTest != LAN9252_BYTE_TEST_VALUE) { + printf("[ECAT][ESC] init: FAIL, BYTE_TEST=0x%08lX (expect 0x87654321)\r\n", + (unsigned long)byteTest); + return false; + } + printf("[ECAT][ESC] init: OK, HBI alive (IDX=0x1234, BYTE_TEST=0x87654321)\r\n"); return true; } uint32_t EscLan9252::readChipId() { - /* INIT-ONLY:init 阶段中断未使能,但仍走 initOnly 路径保持一致性 */ - const uint16_t id_low = initOnlyReadReg(LAN9252_REG_CHIP_ID_L); - const uint16_t id_high = initOnlyReadReg(LAN9252_REG_CHIP_ID_H); - return ((uint32_t)id_high << 16) | id_low; + /* INIT-ONLY:init 阶段中断未使能,直接读取 BYTE_TEST(32-bit) */ + return readReg32(LAN9252_REG_BYTE_TEST); } uint16_t EscLan9252::readReg(uint16_t addr) { - /* ESC 寄存器地址按字节寻址,EXMC 16-bit 访问需要地址左移 1 位 */ - return EscExmcBus::read16((uint32_t)addr << 1U); + return readReg16(addr); +} + +void EscLan9252::writeReg(uint16_t addr, uint16_t v) +{ + writeReg16(addr, v); } __attribute__((section(".ramfunc"), noinline)) @@ -87,18 +157,34 @@ void EscLan9252::readPdi(uint16_t offset, uint8_t *buf, size_t len) if (buf == nullptr || len == 0U) { return; } - /* PDI 按 16-bit 读取,非对齐字节需特殊处理(此处简化为按半字读取) */ - const uint32_t exmc_offset = (uint32_t)(LAN9252_PDI_BASE_OFFSET + offset) << 1U; - const size_t half_count = len / 2U; - for (size_t i = 0U; i < half_count; ++i) { - uint16_t w = EscExmcBus::read16(exmc_offset + (uint32_t)(i * 2U)); - buf[i * 2U] = (uint8_t)(w & 0xFFU); - buf[i * 2U + 1] = (uint8_t)((w >> 8) & 0xFFU); + + uint16_t addr = LAN9252_PDI_BASE_OFFSET + offset; + size_t i = 0U; + + /* 按 32-bit 字读取 */ + while ((i + 4U) <= len) { + const uint32_t w = readReg32(addr); + buf[i + 0U] = (uint8_t)(w & 0xFFU); + buf[i + 1U] = (uint8_t)((w >> 8) & 0xFFU); + buf[i + 2U] = (uint8_t)((w >> 16) & 0xFFU); + buf[i + 3U] = (uint8_t)((w >> 24) & 0xFFU); + addr += 4U; + i += 4U; } - /* 处理奇数字节 */ - if ((len & 1U) != 0U) { - uint16_t w = EscExmcBus::read16(exmc_offset + (uint32_t)(half_count * 2U)); - buf[half_count * 2U] = (uint8_t)(w & 0xFFU); + + /* 剩余 2 字节 */ + if ((i + 2U) <= len) { + const uint16_t w = readReg16(addr); + buf[i + 0U] = (uint8_t)(w & 0xFFU); + buf[i + 1U] = (uint8_t)((w >> 8) & 0xFFU); + addr += 2U; + i += 2U; + } + + /* 剩余 1 字节 */ + if (i < len) { + const uint16_t w = readReg16(addr); + buf[i] = (uint8_t)(w & 0xFFU); } } @@ -108,20 +194,53 @@ void EscLan9252::writePdi(uint16_t offset, const uint8_t *buf, size_t len) if (buf == nullptr || len == 0U) { return; } - const uint32_t exmc_offset = (uint32_t)(LAN9252_PDI_BASE_OFFSET + offset) << 1U; - const size_t half_count = len / 2U; - for (size_t i = 0U; i < half_count; ++i) { - uint16_t w = (uint16_t)buf[i * 2U] | ((uint16_t)buf[i * 2U + 1] << 8); - EscExmcBus::write16(exmc_offset + (uint32_t)(i * 2U), w); + + uint16_t addr = LAN9252_PDI_BASE_OFFSET + offset; + size_t i = 0U; + + /* 按 32-bit 字写入 */ + while ((i + 4U) <= len) { + const uint32_t w = ((uint32_t)buf[i + 3U] << 24) | + ((uint32_t)buf[i + 2U] << 16) | + ((uint32_t)buf[i + 1U] << 8) | + (uint32_t)buf[i + 0U]; + writeReg32(addr, w); + addr += 4U; + i += 4U; } - if ((len & 1U) != 0U) { - EscExmcBus::write16(exmc_offset + (uint32_t)(half_count * 2U), (uint16_t)buf[half_count * 2U]); + + /* 剩余 2 字节 */ + if ((i + 2U) <= len) { + const uint16_t w = ((uint16_t)buf[i + 1U] << 8) | + (uint16_t)buf[i + 0U]; + writeReg16(addr, w); + addr += 2U; + i += 2U; + } + + /* 剩余 1 字节 */ + if (i < len) { + const uint16_t w = (uint16_t)buf[i]; + writeReg16(addr, w); } } -/* ---- initOnly* 接口:INIT-ONLY,仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用 ---- - * 运行期禁止调用;需 ESC 寄存器访问须通过 SYNC0 ISR 代访队列(本 spec 不涵盖)。 */ uint16_t EscLan9252::initOnlyReadReg(uint16_t addr) { - return readReg(addr); + return readReg16(addr); +} + +void EscLan9252::initOnlyWriteReg(uint16_t addr, uint16_t v) +{ + writeReg16(addr, v); +} + +void EscLan9252::initOnlyReadPdi(uint16_t offset, uint8_t *buf, size_t len) +{ + readPdi(offset, buf, len); +} + +void EscLan9252::initOnlyWritePdi(uint16_t offset, const uint8_t *buf, size_t len) +{ + writePdi(offset, buf, len); } diff --git a/GD32E508_DCU_APP/comm/esc_lan9252.h b/GD32E508_DCU_APP/comm/esc_lan9252.h index ae78eb6..1f82edc 100644 --- a/GD32E508_DCU_APP/comm/esc_lan9252.h +++ b/GD32E508_DCU_APP/comm/esc_lan9252.h @@ -1,11 +1,13 @@ /** * @file esc_lan9252.h * @brief LAN9252 EtherCAT 从站控制器(ESC)驱动接口。 - * @note 通过 EscExmcBus 16-bit 并行总线访问 LAN9252 寄存器与 PDI 内存。 + * @note 通过 EscExmcBus 16-bit 并行总线,以 HBI Indexed Address Mode 访问 + * LAN9252 寄存器与 PDI 内存:先写 HBI_IDX_0,再读写 HBI_DATA_0。 * * 接口调用约束(避免 EXMC 总线争用): - * - readReg/readPdi/writePdi:ISR-only,仅供 preempt=0 的 periodicIsr() 调用,无锁。 - * - initOnlyReadReg:INIT-ONLY,仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用;运行期禁止调用,需 ESC 寄存器访问须通过 SYNC0 ISR 代访队列(本 spec 不涵盖)。 + * - readReg/writeReg/readPdi/writePdi:ISR-only,仅供 preempt=0 的 periodicIsr() 调用,无锁。 + * - initOnly* 系列:INIT-ONLY,仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用; + * 运行期禁止调用,需要访问 ESC 寄存器须通过 SYNC0 ISR 路径(本 spec 不涵盖)。 * * PDI offset 语义:offset 参数为 SyncManager 内存区相对偏移,输入 PDO(TxPDO)与输出 PDO(RxPDO)必须使用不同 offset,参见 CONFIG_ESC_PDI_INPUT_OFFSET / CONFIG_ESC_PDI_OUTPUT_OFFSET。 */ @@ -34,15 +36,27 @@ public: /** @brief ISR-only:仅供 preempt=0 的 periodicIsr() 调用,无锁。主循环必须用 initOnly 版本。 */ static uint16_t readReg(uint16_t addr); + /** @brief ISR-only:仅供 preempt=0 的 periodicIsr() 调用,无锁。主循环必须用 initOnly 版本。 */ + static void writeReg(uint16_t addr, uint16_t v); + /** @brief ISR-only:仅供 preempt=0 的 periodicIsr() 调用,无锁。主循环必须用 initOnly 版本。 */ static void readPdi(uint16_t offset, uint8_t *buf, size_t len); /** @brief ISR-only:仅供 preempt=0 的 periodicIsr() 调用,无锁。主循环必须用 initOnly 版本。 */ static void writePdi(uint16_t offset, const uint8_t *buf, size_t len); - /** @brief INIT-ONLY:仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用;运行期禁止调用,需 ESC 寄存器访问须通过 SYNC0 ISR 代访队列(本 spec 不涵盖)。 */ + /** @brief INIT-ONLY:仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用;运行期禁止调用,需 ESC 寄存器访问须通过 SYNC0 ISR 路径。 */ static uint16_t initOnlyReadReg(uint16_t addr); + /** @brief INIT-ONLY:仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用;运行期禁止调用。 */ + static void initOnlyWriteReg(uint16_t addr, uint16_t v); + + /** @brief INIT-ONLY:仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用;运行期禁止调用。 */ + static void initOnlyReadPdi(uint16_t offset, uint8_t *buf, size_t len); + + /** @brief INIT-ONLY:仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用;运行期禁止调用。 */ + static void initOnlyWritePdi(uint16_t offset, const uint8_t *buf, size_t len); + /** @brief EXMC 总线是否就绪(init 后可查询)。 */ static bool isBusReady() { return s_busReady; } diff --git a/GD32E508_DCU_APP/comm/ethercat_slave.cpp b/GD32E508_DCU_APP/comm/ethercat_slave.cpp index 2e7f782..83bf3eb 100644 --- a/GD32E508_DCU_APP/comm/ethercat_slave.cpp +++ b/GD32E508_DCU_APP/comm/ethercat_slave.cpp @@ -47,10 +47,10 @@ void EthercatSlave::init() return; } - /* 第 1 层调试可观测性:读取芯片 ID 并打印 selfTest 结果 */ + /* 第 1 层调试可观测性:读取 BYTE_TEST 寄存器(0x064,默认 0x87654321)并打印 selfTest 结果 */ s_chipId = EscLan9252::readChipId(); const bool chipIdOk = (s_chipId == 0x87654321U); - ECAT_LOGI("[ECAT][SLAVE] chip id=0x%08lX expected=0x%08lX selfTest=%s\r\n", + ECAT_LOGI("[ECAT][SLAVE] byte_test=0x%08lX expected=0x%08lX selfTest=%s\r\n", (unsigned long)s_chipId, (unsigned long)0x87654321UL, chipIdOk ? "PASS" : "FAIL"); diff --git a/GD32E508_DCU_APP/comm/ethercat_sync.cpp b/GD32E508_DCU_APP/comm/ethercat_sync.cpp index 3a60b0a..f484cf3 100644 --- a/GD32E508_DCU_APP/comm/ethercat_sync.cpp +++ b/GD32E508_DCU_APP/comm/ethercat_sync.cpp @@ -1,12 +1,14 @@ /** * @file ethercat_sync.cpp - * @brief EtherCAT SYNC0 周期同步实现。 + * @brief EtherCAT SYNC0/IRQ/SYNC1 中断入口 — 转发到 SSC 协议栈。 + * + * SYNC0 (EXTI0, preempt=0) → Sync0_Isr() : DC 周期同步, PDO 交换 + * ESC IRQ (EXTI1, preempt=1) → PDI_Isr() : AL Event (邮箱/SM 事件) + * SYNC1 (EXTI2, preempt=1) → Sync1_Isr() : 预留 */ #include "comm/ethercat_sync.h" -#include "comm/ethercat_slave.h" #include "sys/config.h" -#include "sys/task_manager.h" -#include "main.h" +#include extern "C" { #include "gd32e50x_rcu.h" @@ -16,6 +18,11 @@ extern "C" { #if (CONFIG_ETHERCAT_SYNC_MODE == 1) #include "gd32e50x_timer.h" #endif + +/* SSC 协议栈 ISR 入口 (ecatappl.c) */ +void PDI_Isr(void); +void Sync0_Isr(void); +void Sync1_Isr(void); } void EthercatSync::init() @@ -63,7 +70,7 @@ void EthercatSync::init() timer_init(TIMER0, &timer_initpara); timer_interrupt_flag_clear(TIMER0, TIMER_INT_FLAG_UP); timer_interrupt_enable(TIMER0, TIMER_INT_UP); - nvic_irq_enable(TIMER0_IRQn, CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC, 0U); + nvic_irq_enable(TIMER0_UP_IRQn, CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC, 0U); timer_enable(TIMER0); printf("[ECAT][SYNC] init: TIMER0 enabled (100us, preempt=%u)\r\n", (unsigned)CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC); @@ -97,7 +104,7 @@ void EthercatSync::onSync0Exti() /* 直接寄存器访问,绕过 GD32 库函数以消除可变抖动 */ if ((EXTI_PD & (uint32_t)CONFIG_ESC_SYNC0_EXTI) != RESET) { EXTI_PD = (uint32_t)CONFIG_ESC_SYNC0_EXTI; - EthercatSlave::periodicIsr(); + Sync0_Isr(); } } @@ -105,8 +112,7 @@ void EthercatSync::onEscIrqExti() { if (exti_interrupt_flag_get(CONFIG_ESC_IRQ_EXTI) != RESET) { exti_interrupt_flag_clear(CONFIG_ESC_IRQ_EXTI); - /* 极简:仅置位任务,事件处理移主循环 */ - TaskManager::set(1UL << TASK_ETHERCAT_PROCESS); + PDI_Isr(); } } @@ -114,8 +120,7 @@ void EthercatSync::onSync1Exti() { if (exti_interrupt_flag_get(CONFIG_ESC_SYNC1_EXTI) != RESET) { exti_interrupt_flag_clear(CONFIG_ESC_SYNC1_EXTI); - /* 预留:SYNC1 通道当前未承载业务,仅清 EXTI 标志防止中断挂起; - * 启用 SYNC1 业务时在此处添加处理逻辑。 */ + Sync1_Isr(); } } @@ -124,7 +129,7 @@ void EthercatSync::onTimerIrq() #if (CONFIG_ETHERCAT_SYNC_MODE == 1) if (timer_interrupt_flag_get(TIMER0, TIMER_INT_FLAG_UP) != RESET) { timer_interrupt_flag_clear(TIMER0, TIMER_INT_FLAG_UP); - EthercatSlave::periodicIsr(); + Sync0_Isr(); } #else /* EXTI 模式下 TIMER0 不使用,本函数为占位;CONFIG_ETHERCAT_SYNC_MODE=1 时激活。 */ @@ -140,7 +145,7 @@ void EXTI0_IRQHandler(void) { EthercatSync::onSync0Exti(); } void EXTI1_IRQHandler(void) { EthercatSync::onEscIrqExti(); } void EXTI2_IRQHandler(void) { EthercatSync::onSync1Exti(); } #else -void TIMER0_IRQHandler(void) { EthercatSync::onTimerIrq(); } +void TIMER0_UP_TIMER9_IRQHandler(void) { EthercatSync::onTimerIrq(); } void EXTI1_IRQHandler(void) { EthercatSync::onEscIrqExti(); } void EXTI2_IRQHandler(void) { EthercatSync::onSync1Exti(); } #endif diff --git a/GD32E508_DCU_APP/comm/ssc/inc/applInterface.h b/GD32E508_DCU_APP/comm/ssc/inc/applInterface.h new file mode 100644 index 0000000..01c9a5c --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/inc/applInterface.h @@ -0,0 +1,125 @@ +/** +\addtogroup EcatAppl EtherCAT application +@{ +*/ + +/** +\file applInterface.c +\author EthercatSSC@beckhoff.com +\brief Definition of the application interface functions + +\version 5.11 + +
Changes to version - :
+V5.10.1 : Start file change log +*/ + +/*----------------------------------------------------------------------------------------- +------ +------ Includes +------ +-----------------------------------------------------------------------------------------*/ + + +#ifndef _APPL_INTERFACE_H_ +#define _APPL_INTERFACE_H_ + +#include "ecat_def.h" +#include +#include +#include "esc.h" +#include "ecatslv.h" +#include "objdef.h" +#include "ecatappl.h" + + + +#endif /*#ifndef _APPL_INTERFACE_H_*/ + +#if defined(_APPL_INTERFACE_) && (_APPL_INTERFACE_ == 1) + #define PROTO +#else + #define PROTO extern +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ Function pointer which are used to call optional functions from the application +------ +-----------------------------------------------------------------------------------------*/ + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \return 0 if successful or greater 0 in case of an error + + \param wordaddr Word address of the EEPROM data to be read + + \brief This function shall copy EEPROM data to the ESC EEPROM data register (0x508:0x50F/0x50B). + \brief The EEPROM data starting at the specified word address and the length specified with "EEPROM_READ_SIZE". + \brief The data shall be copied to the ESC EEPROM buffer (ESC offset 0x508) +*//////////////////////////////////////////////////////////////////////////////////////// +PROTO UINT16 (* pAPPL_EEPROM_Read)(UINT32 wordaddr); + +///////////////////////////////////////////////////////////////////////////////////////// +/** +\return 0 if successful or greater 0 in case of an error + +\param wordaddr Word address of the EEPROM data to be written + +\brief This function shall copy data from the ESC EEPROM data register (0x508:0x50F/0x50B) to the EEPROM memory. +\brief The EEPROM data starting at the specified word address and the length specified with "EEPROM_WRITE_SIZE". +*//////////////////////////////////////////////////////////////////////////////////////// +PROTO UINT16(*pAPPL_EEPROM_Write)(UINT32 wordaddr); + +///////////////////////////////////////////////////////////////////////////////////////// +/** +\return 0 if successful or greater 0 in case of an error + +\brief This function shall copy the EEPROM reload information to the ESC EEPROM data register (0x508:0x50F/0x50B). +\brief Read the ESC data sheet for the reload information (e.g. Beckhoff IPCore ESC Datasheet section II, clause 3.45.1) +*//////////////////////////////////////////////////////////////////////////////////////// +PROTO UINT16(*pAPPL_EEPROM_Reload)(void); + + + +/*----------------------------------------------------------------------------------------- +------ +------ Functions provided by the SSC to be called from the application +------ +-----------------------------------------------------------------------------------------*/ + + +///////////////////////////////////////////////////////////////////////////////////////// +/** +\return 0 if successful or greater 0 in case of an error + +\brief Initialize the generic slave stack. +\brief This function should be called after the platform including operating system and +\brief ESC is ready to use. +*//////////////////////////////////////////////////////////////////////////////////////// +PROTO UINT16 MainInit(void); + + +///////////////////////////////////////////////////////////////////////////////////////// +/** +\brief This function handles the low priority function like EtherCAT state machine +\brief handling, mailbox protocols and if no synchronization is enabled also the +\brief application. +\brief This function shall be called cyclically from the application. +*//////////////////////////////////////////////////////////////////////////////////////// +PROTO void MainLoop(void); + + +///////////////////////////////////////////////////////////////////////////////////////// +/** +\param alStatus requested AL Status +\param alStatusCode requested AL Status Code + +\brief This function shall be called by the application to trigger state transition in case +\brief of an application error or to complete a pending transition. +\brief If the function was called due to an error it shall be again if the error is gone. +\brief NOTE: state requests to a higher state than the current state are not allowed. +*//////////////////////////////////////////////////////////////////////////////////////// +PROTO void ECAT_StateChange(UINT8 alStatus, UINT16 alStatusCode); + +#undef PROTO diff --git a/GD32E508_DCU_APP/comm/ssc/inc/coeappl.h b/GD32E508_DCU_APP/comm/ssc/inc/coeappl.h new file mode 100644 index 0000000..2c117e6 --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/inc/coeappl.h @@ -0,0 +1,68 @@ +/** + * \addtogroup CoE CAN Application Profile over EtherCAT + * @{ + */ + +/** +\file coeappl.h +\author EthercatSSC@beckhoff.com + +\version 5.11 + +
Changes to version V5.01:
+V5.11 ECAT10: change PROTO handling to prevent compiler errors
+
Changes to version - :
+V5.01 : Start file change log + */ +#ifndef _COEAPPL_H_ +#define _COEAPPL_H_ + +/*----------------------------------------------------------------------------------------- +------ +------ Includes +------ +-----------------------------------------------------------------------------------------*/ +#include "objdef.h" + + +/*----------------------------------------------------------------------------------------- +------ +------ Defines and Types +------ +-----------------------------------------------------------------------------------------*/ + + + +#endif //_COEAPPL_H_ + +/* ECATCHANGE_START(V5.11) ECAT10*/ +#if defined(_COEAPPL_) && (_COEAPPL_ == 1) +/* ECATCHANGE_END(V5.11) ECAT10*/ + #define PROTO +#else + #define PROTO extern +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ Global variables +------ +-----------------------------------------------------------------------------------------*/ + + +/*----------------------------------------------------------------------------------------- +------ +------ Global functions +------ +-----------------------------------------------------------------------------------------*/ +PROTO void COE_ObjInit(void); +PROTO void COE_Main(void); +PROTO UINT16 COE_ObjDictionaryInit(void); +PROTO UINT16 COE_AddObjectToDic(TOBJECT OBJMEM * pNewObjEntry); +PROTO void COE_RemoveDicEntry(UINT16 index); +PROTO void COE_ClearObjDictionary(void); +PROTO OBJCONST TOBJECT OBJMEM * COE_GetObjectDictionary(void); + + +#undef PROTO +/** @}*/ diff --git a/GD32E508_DCU_APP/comm/ssc/inc/ecatappl.h b/GD32E508_DCU_APP/comm/ssc/inc/ecatappl.h new file mode 100644 index 0000000..03ad73f --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/inc/ecatappl.h @@ -0,0 +1,124 @@ +/** + \addtogroup EcatAppl EtherCAT application + * @{ + */ + +/** +\file ecatappl.h +\author EthercatSSC@beckhoff.com + +\version 5.11 + +
Changes to version V5.10:
+V5.11 ECAT10: change PROTO handling to prevent compiler errors
+
Changes to version V5.01:
+V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)
+ Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings
+ Update 0x1C3x entries
+V5.10 ECAT4: Update alignment marco for 8 to 15 bit alignments (16 and 32 Bit controllers)
+ Bugfix calculate LED blink frequency
+
Changes to version - :
+V5.01 : Start file change log + */ +/*----------------------------------------------------------------------------------------- +------ +------ Description +------ +------ ecatappl.h +------ +------ EtherCAT Slave Application +------ ------ +-----------------------------------------------------------------------------------------*/ + +#ifndef _ECATAPPL_H_ +#define _ECATAPPL_H_ + +/*----------------------------------------------------------------------------------------- +------ +------ Includes +------ +-----------------------------------------------------------------------------------------*/ + +#include "ecat_def.h" + +/*----------------------------------------------------------------------------------------- +------ +------ Defines and Types +------ +-----------------------------------------------------------------------------------------*/ +/*Set to unsigned short to handle bit entries correct*/ +#define BOOLEAN(x) unsigned short(x):1 /**< \brief Marco to define BOOLEAN object entry*/ +#define BIT1(x) unsigned short(x):1 /**< \brief Marco to define BIT1 object entry*/ +#define BIT2(x) unsigned short(x):2 /**< \brief Marco to define BIT2 object entry*/ +#define BIT3(x) unsigned short(x):3 /**< \brief Marco to define BIT3 object entry*/ +#define BIT4(x) unsigned short(x):4 /**< \brief Marco to define BIT4 object entry*/ +#define BIT5(x) unsigned short(x):5 /**< \brief Marco to define BIT5 object entry*/ +#define BIT6(x) unsigned short(x):6 /**< \brief Marco to define BIT5 object entry*/ +#define BIT7(x) unsigned short(x):7 /**< \brief Marco to define BIT6 object entry*/ +#define BIT8(x) unsigned short(x):8 /**< \brief Marco to define BIT7 object entry*/ +#define ALIGN0(x) +#define ALIGN1(x) unsigned short(x):1; /**< \brief Marco to define ALIGN1 object entry*/ +#define ALIGN2(x) unsigned short(x):2; /**< \brief Marco to define ALIGN2 object entry*/ +#define ALIGN3(x) unsigned short(x):3; /**< \brief Marco to define ALIGN3 object entry*/ +#define ALIGN4(x) unsigned short(x):4; /**< \brief Marco to define ALIGN4 object entry*/ +#define ALIGN5(x) unsigned short(x):5; /**< \brief Marco to define ALIGN5 object entry*/ +#define ALIGN6(x) unsigned short(x):6; /**< \brief Marco to define ALIGN6 object entry*/ +#define ALIGN7(x) unsigned short(x):7; /**< \brief Marco to define ALIGN7 object entry*/ +#define ALIGN8(x) unsigned short(x):8; /**< \brief Marco to define ALIGN8 object entry*/ +#define ALIGN9(x) unsigned short(x):9; /**< \brief Marco to define ALIGN9 object entry*/ +#define ALIGN10(x) unsigned short(x):10; /**< \brief Marco to define ALIGN10 object entry*/ +#define ALIGN11(x) unsigned short(x):11; /**< \brief Marco to define ALIGN11 object entry*/ +#define ALIGN12(x) unsigned short(x):12; /**< \brief Marco to define ALIGN12 object entry*/ +#define ALIGN13(x) unsigned short(x):13; /**< \brief Marco to define ALIGN13 object entry*/ +#define ALIGN14(x) unsigned short(x):14; /**< \brief Marco to define ALIGN14 object entry*/ +#define ALIGN15(x) unsigned short(x):15; /**< \brief Marco to define ALIGN15 object entry*/ +#endif //_ECATAPPL_H_ + +/* ECATCHANGE_START(V5.11) ECAT10*/ +#if defined(_ECATAPPL_) && (_ECATAPPL_ == 1) +/* ECATCHANGE_END(V5.11) ECAT10*/ + #define PROTO +#else + #define PROTO extern +#endif + + +/*----------------------------------------------------------------------------------------- +------ +------ Global Variables +------ +-----------------------------------------------------------------------------------------*/ + + + +PROTO BOOL bEcatWaitForInputUpdate; /**< \brief Wait for input update*/ +PROTO BOOL bEtherCATRunLed; /**< \brief Current run LED value*/ +PROTO BOOL bEtherCATErrorLed; /**< \brief Current error LED value*/ +PROTO BOOL bRunApplication; /**< \brief Indicates if the stack shall be running (if false the Hardware will be released)*/ + + +/*----------------------------------------------------------------------------------------- +------ +------ Global Functions +------ +-----------------------------------------------------------------------------------------*/ + + + + +PROTO void ECAT_CheckTimer(void); +PROTO void PDI_Isr(void); +PROTO void Sync0_Isr(void); +PROTO void Sync1_Isr(void); +PROTO void ECAT_Application(void); +PROTO void PDO_ResetOutputs(void); +PROTO void PDO_ReadInputs(void); +PROTO void PDO_InputMapping(void); + +PROTO void CalcSMCycleTime(void); + + + + +#undef PROTO +/** @}*/ diff --git a/GD32E508_DCU_APP/comm/ssc/inc/ecatcoe.h b/GD32E508_DCU_APP/comm/ssc/inc/ecatcoe.h new file mode 100644 index 0000000..49c418a --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/inc/ecatcoe.h @@ -0,0 +1,113 @@ +/** + * \addtogroup CoE CAN Application Profile over EtherCAT + * @{ + */ + +/** +\file ecatcoe.h +\author EthercatSSC@beckhoff.com + +\version 5.11 + +
Changes to version V5.01:
+V5.11 COE4: "change prototype of ""COE_ContinueInd()"" return <> 0 if a failure occurred"
+V5.11 ECAT10: change PROTO handling to prevent compiler errors
+
Changes to version - :
+V5.01 : Start file change log + */ +#ifndef _ECATCOE_H_ + +#define _ECATCOE_H_ + +/*----------------------------------------------------------------------------------------- +------ +------ Includes +------ +-----------------------------------------------------------------------------------------*/ +#include "mailbox.h" + +/*----------------------------------------------------------------------------------------- +------ +------ Defines and Types +------ +-----------------------------------------------------------------------------------------*/ + +/*--------------------------------------------- +- Error codes +-----------------------------------------------*/ +#define ERROR_COEINVALIDSERVICE 0x01 /**< \brief Invalid SDO service*/ +#define ERROR_COENOTSUPPORTED 0x02 /**< \brief Not supported service*/ + + +/*--------------------------------------------- +- COE services +-----------------------------------------------*/ +#define COESERVICE_EMERGENCY 0x01 /**< \brief CoE Emergency*/ +#define COESERVICE_SDOREQUEST 0x02 /**< \brief CoE SDO request*/ +#define COESERVICE_SDORESPONSE 0x03 /**< \brief CoE SDO response*/ +#define COESERVICE_TXPDO 0x04 /**< \brief CoE TxPDO*/ +#define COESERVICE_RXPDO 0x05 /**< \brief CoE RxPDO*/ +#define COESERVICE_TXPDOREMREQ 0x06 /**< \brief CoE TxPDO map request*/ +#define COESERVICE_RXPDOREMREQ 0x07 /**< \brief CoE RxPDO map request*/ +#define COESERVICE_SDOINFO 0x08 /**< \brief CoE SDO Info*/ + + + +/*--------------------------------------------- +- COE Structures +-----------------------------------------------*/ +#define COEHEADER_COESERVICESHIFT 12 /**< \brief CoE service shift (within CoE header)*/ +#define COEHEADER_COESERVICEMASK 0xF000 /**< \brief CoE service mask (within CoE header)*/ + +#define COE_HEADER_SIZE 2 /**< \brief CoE header size*/ + +typedef UINT16 TCOEHEADER; /**< \brief CoE header*/ + + +/** + * \brief CoE Mailbox header + */ +typedef struct MBX_STRUCT_PACKED_START +{ + TMBXHEADER MbxHeader; /**< \brief mailbox header*/ + TCOEHEADER CoeHeader; /**< \brief CoE header*/ + UINT16 Data[((MAX_MBX_DATA_SIZE)-(COE_HEADER_SIZE)) >> 1]; /**< \brief CoE data*/ +}MBX_STRUCT_PACKED_END +TCOEMBX; + + +#endif //_ECATCOE_H_ + +/* ECATCHANGE_START(V5.11) ECAT10*/ +#if defined(_ECATCOE_) && (_ECATCOE_ == 1) +/* ECATCHANGE_END(V5.11) ECAT10*/ + #define PROTO +#else + #define PROTO extern +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ Global Variables +------ +-----------------------------------------------------------------------------------------*/ +PROTO TMBX MBXMEM * VARMEM pCoeSendStored; /** if the mailbox service could not be sent (or stored), + the CoE service will be stored in this variable + and will be sent automatically from the mailbox handler + (COE_ContinueInd) when the send mailbox will be read + the next time from the master */ + +/*----------------------------------------------------------------------------------------- +------ +------ Global Functions +------ +-----------------------------------------------------------------------------------------*/ + +PROTO void COE_Init(void); +PROTO UINT8 COE_ServiceInd(TCOEMBX MBXMEM *pCoeMbx); +/*ECATCHANGE_START(V5.11) COE4*/ +PROTO UINT8 COE_ContinueInd(TMBX MBXMEM * pMbx); +/*ECATCHANGE_END(V5.11) COE4*/ + +#undef PROTO +/** @}*/ diff --git a/GD32E508_DCU_APP/comm/ssc/inc/ecatslv.h b/GD32E508_DCU_APP/comm/ssc/inc/ecatslv.h new file mode 100644 index 0000000..cceca52 --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/inc/ecatslv.h @@ -0,0 +1,455 @@ +/** + * \addtogroup ESM EtherCAT State Machine + * @{ + */ + +/** +\file ecatslv.h +\author EthercatSSC@beckhoff.com + +\version 5.11 + +
Changes to version V5.10:
+V5.11 ECAT10: change PROTO handling to prevent compiler errors
+V5.11 ECAT4: enhance SM/Sync monitoring for input/output only slaves
+V5.11 ECAT9: add new AL Status Codes (ALSTATUSCODE_FW_SII_NOT_MATCH/0x0006, ALSTATUSCODE_FW_UPDATE_FAILED / 0x0007)
+V5.11 ESM7: "add Sync define for 0x22 (""SYNCTYPE_SM2_SYNCHRON""), support value 0x22 for 0x1C33.1 (SM2 sync)"
+V5.11 HW1: "move hardware independent functions ""HW_DisableSyncManChannel()"", ""HW_EnableSyncManChannel()"", ""HW_GetSyncMan()"", ""HW_ResetALEventMask()"", ""HW_SetALEventMask()"" to ecatalv.c"
+
Changes to version V5.01:
+V5.10 COE1: Define one entry description for all 0x1C3x objects and change data type of SI11,12,13 to UINT16 (according ETG.1020)
+V5.10 ECAT12: Add new AL Status Codes (0x2E,0x52,0x70)
+V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)
+ Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings
+ Update 0x1C3x entries
+
Changes to version - :
+V5.01 : Start file change log + */ + +#ifndef _ECATSLV_H_ +#define _ECATSLV_H_ + +/*----------------------------------------------------------------------------------------- +------ +------ Includes +------ +-----------------------------------------------------------------------------------------*/ + +#include "ecat_def.h" + + +#if MCI_HW +#include "mcihw.h" +#endif +#if EL9800_HW +#include "el9800hw.h" +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ Defines and Types +------ +-----------------------------------------------------------------------------------------*/ + +#ifndef OBJGETNEXTSTR + /* the old definition was not working with all compilers */ + /* old: #define OBJGETNEXTSTR(p) ( (OBJCONST CHAR OBJMEM * )( ((UINT32) p) + OBJSTRLEN( (OBJCONST CHAR OBJMEM *) p ) + 1 ) ) */ + #define OBJGETNEXTSTR(p) ( (OBJCONST CHAR OBJMEM * )( &((p)[OBJSTRLEN( (OBJCONST CHAR OBJMEM *) (p) ) + 1]) ) ) /**< \brief Macro to get next name within the objetc name string*/ +#endif + +#ifndef LO_BYTE + #define LO_BYTE 0 /**< \brief LowByte within an WORD*/ +#endif + +#ifndef HI_BYTE + #define HI_BYTE 1 /**< \brief HighByte within an WORD*/ +#endif + +#ifndef LOLO_BYTE + #define LOLO_BYTE 0 /**< \brief LowLowByte within an DWORD*/ +#endif + +#ifndef LOHI_BYTE + #define LOHI_BYTE 1 /**< \brief LowHighByte within an DWORD*/ +#endif + +#ifndef HILO_BYTE + #define HILO_BYTE 2 /**< \brief HighLowByte within an DWORD*/ +#endif + +#ifndef HIHI_BYTE + #define HIHI_BYTE 3 /**< \brief HighHighByte within an DWORD*/ +#endif + +#ifndef LO_WORD + #define LO_WORD 0 /**< \brief LowWord within an DWORD*/ +#endif + +#ifndef HI_WORD + #define HI_WORD 1 /**< \brief HighWord within an DWORD*/ +#endif + +#ifndef SWAPWORD + #define SWAPWORD(x) (x) /**< \brief Macro to swap a WORD*/ +#endif + +#ifndef SWAPDWORD + #define SWAPDWORD(x) (x) /**< \brief Macro to swap a DWORD*/ +#endif + +#ifndef LOBYTE + #define LOBYTE(x) ((x)&0xFF) /**< \brief LowByte mask*/ +#endif + +#ifndef HIBYTE + #define HIBYTE(x) (((x)&0xFF00)>>8)/**< \brief HighwByte mask*/ +#endif + +#ifndef LOLOBYTE + #define LOLOBYTE(x) ((x)&0xFF)/**< \brief LowLowByte mask*/ +#endif + +#ifndef LOHIBYTE + #define LOHIBYTE(x) (((x)&0xFF00)>>8)/**< \brief LowHighByte mask*/ +#endif + +#ifndef HILOBYTE + #define HILOBYTE(x) (((x)&0xFF0000)>>16)/**< \brief HighLowByte mask*/ +#endif + +#ifndef HIHIBYTE + #define HIHIBYTE(x) (((x)&0xFF000000)>>24)/**< \brief HighHighByte mask*/ +#endif + +#ifndef LOWORD + #define LOWORD(x) ((x)&0xFFFF)/**< \brief LowWord mask*/ +#endif + +#ifndef HIWORD + #define HIWORD(x) (((x)&0xFFFF0000)>>16)/**< \brief HighByte mask*/ +#endif + +#ifndef BIT2BYTE + #define BIT2BYTE(x) (((x)+7)>>3) /**< \brief Marco to convert Bit size to Byte size (round up)*/ +#endif + +#ifndef BYTE2BIT + #define BYTE2BIT(x) ((x)<<3) /**< \brief Marco to convert Byte size to Bit size*/ +#endif + +#ifndef BIT2WORD + #define BIT2WORD(x) (((x)+15)>>4) /**< \brief Marco to convert Bit size to Word size (round up)*/ +#endif + +#ifndef BYTE2WORD + #define BYTE2WORD(x) (((x)+1)>>1) /**< \brief Marco to convert Bytes size to Word size (round up)*/ +#endif + +#ifndef ROUNDUPBYTE2WORD + #define ROUNDUPBYTE2WORD(x) ((((x)+1)>>1)<<1) /**< \brief Round up Byte size to even number of Words*/ +#endif + + +/*--------------------------------------------- +- State defines +-----------------------------------------------*/ +#define STATE_INIT ((UINT8) 0x01) /**< \brief State INIT*/ +#define STATE_PREOP ((UINT8) 0x02) /**< \brief State PreOP*/ +#define STATE_BOOT ((UINT8) 0x03) /**< \brief State BOOT*/ +#define STATE_SAFEOP ((UINT8) 0x04) /**< \brief State SafeOP*/ +#define STATE_OP ((UINT8) 0x08) /**< \brief State OP*/ + +#define STATE_MASK ((UINT8) 0x0F) /**< \brief State mask*/ +#define STATE_CHANGE ((UINT8) 0x10) /**< \brief State change (Error indication/acknowledge) mask and value*/ +#define STATE_DEVID ((UINT8) 0x20) /**< \brief Request/response Explicit device ID*/ + +#define BOOT_2_INIT ((UINT8)((STATE_BOOT) << 4) | (STATE_INIT)) /**< \brief State transition BOOT to INIT*/ +#define BOOT_2_PREOP ((UINT8)((STATE_BOOT) << 4) | (STATE_PREOP)) /**< \brief State transition BOOT to INIT*/ +#define BOOT_2_SAFEOP ((UINT8)((STATE_BOOT) << 4) | (STATE_SAFEOP)) /**< \brief State transition BOOT to INIT*/ +#define BOOT_2_OP ((UINT8)((STATE_BOOT) << 4) | (STATE_OP)) /**< \brief State transition BOOT to INIT*/ + +#define INIT_2_BOOT ((UINT8)((STATE_INIT) << 4) | (STATE_BOOT)) /**< \brief State transition INIT to BOOT*/ +#define PREOP_2_BOOT ((UINT8)((STATE_PREOP) << 4) | (STATE_BOOT)) /**< \brief State transition PREOP to BOOT*/ +#define SAFEOP_2_BOOT ((UINT8)((STATE_SAFEOP) << 4) | (STATE_BOOT)) /**< \brief State transition SAFEOP to BOOT*/ +#define OP_2_BOOT ((UINT8)((STATE_OP) << 4) | (STATE_BOOT)) /**< \brief State transition OP to BOOT*/ + +#define INIT_2_INIT ((UINT8)((STATE_INIT) << 4) | (STATE_INIT)) /**< \brief State transition INIT to INIT*/ +#define INIT_2_PREOP ((UINT8)((STATE_INIT) << 4) | (STATE_PREOP)) /**< \brief State transition INIT to PREOP*/ +#define INIT_2_SAFEOP ((UINT8)((STATE_INIT) << 4) | (STATE_SAFEOP)) /**< \brief State transition INIT to SAFEOP*/ +#define INIT_2_OP ((UINT8)((STATE_INIT) << 4) | (STATE_OP)) /**< \brief State transition INIT to OP*/ + +#define PREOP_2_INIT ((UINT8)((STATE_PREOP) << 4) | (STATE_INIT)) /**< \brief State transition PREOP to INIT*/ +#define PREOP_2_PREOP ((UINT8)((STATE_PREOP) << 4) | (STATE_PREOP)) /**< \brief State transition PREOP to PREOP*/ +#define PREOP_2_SAFEOP ((UINT8)((STATE_PREOP) << 4) | (STATE_SAFEOP)) /**< \brief State transition PREOP to SAFEOP*/ +#define PREOP_2_OP ((UINT8)((STATE_PREOP) << 4) | (STATE_OP)) /**< \brief State transition PREOP to OP*/ + +#define SAFEOP_2_INIT ((UINT8)((STATE_SAFEOP) << 4) | (STATE_INIT)) /**< \brief State transition SAFEOP to INIT*/ +#define SAFEOP_2_PREOP ((UINT8)((STATE_SAFEOP) << 4) | (STATE_PREOP)) /**< \brief State transition SAFEOP to PREOP*/ +#define SAFEOP_2_SAFEOP ((UINT8)((STATE_SAFEOP) << 4) | (STATE_SAFEOP)) /**< \brief State transition SAFEOP to SAFEOP*/ +#define SAFEOP_2_OP ((UINT8)((STATE_SAFEOP) << 4) |( STATE_OP)) /**< \brief State transition SAFEOP to OP*/ + +#define OP_2_INIT ((UINT8)((STATE_OP) << 4) | (STATE_INIT)) /**< \brief State transition OP to INIT*/ +#define OP_2_PREOP ((UINT8)((STATE_OP) << 4) | (STATE_PREOP)) /**< \brief State transition OP to PREOP*/ +#define OP_2_SAFEOP ((UINT8)((STATE_OP) << 4) | (STATE_SAFEOP)) /**< \brief State transition OP to SAFEOP*/ +#define OP_2_OP ((UINT8)((STATE_OP) << 4) | (STATE_OP)) /**< \brief State transition OP to OP*/ + + +/*------------------------------------------------------------------------------------------------------------------------- + + ESM transition error codes + +*/ + +#define SYNCMANCHODDADDRESS 0x00 /**< \brief Emergency and Diagnosis code for an odd SyncManager address*/ +#define SYNCMANCHADDRESS 0x01 /**< \brief Emergency and Diagnosis code for an invalid SyncManager address*/ +#define SYNCMANCHSIZE 0x02 /**< \brief Emergency and Diagnosis code for an invalid SyncManager size*/ +#define SYNCMANCHSETTINGS 0x03 /**< \brief Emergency and Diagnosis code for an invalid SyncManager settings*/ +#define ERROR_SYNCMANCH(code, channel) ((code)+((channel)<<2)) /**< \brief Marco to add SyncManager channel*/ +#define ERROR_SYNCMANCHODDADDRESS(channel) ((SYNCMANCHODDADDRESS)+((channel)<<2)) /**< \brief Marco to add SyncManager channel*/ +#define ERROR_SYNCMANCHADDRESS(channel) ((SYNCMANCHADDRESS)+((channel)<<2)) /**< \brief Marco to add SyncManager channel*/ +#define ERROR_SYNCMANCHSIZE(channel) ((SYNCMANCHSIZE)+((channel)<<2)) /**< \brief Marco to add SyncManager channel*/ +#define ERROR_SYNCMANCHSETTINGS(channel) ((SYNCMANCHSETTINGS)+((channel)<<2)) /**< \brief Marco to add SyncManager channel*/ +#define ERROR_SYNCTYPES 0x80 /**< \brief Emergency and Diagnosis code for an invalid Sync types*/ +#define ERROR_DCSYNCCONTROL 0x81 /**< \brief Emergency and Diagnosis code for an invalid DC Sync control*/ +#define ERROR_DCSYNC0CYCLETIME 0x82 /**< \brief Emergency and Diagnosis code for an invalid Sync0 cycle time*/ +#define ERROR_DCSYNC1CYCLETIME 0x83 /**< \brief Emergency and Diagnosis code for an invalid Sync1 cycle time*/ +#define ERROR_DCCYCLEPARAMETER 0x84 /**< \brief Emergency and Diagnosis code for an invalid Cycle parameter*/ +#define ERROR_DCLATCHCONTROL 0x85 /**< \brief Emergency and Diagnosis code for an invalid Latch control*/ + +#define ERROR_INVALIDSTATE 0xF0 /**< \brief Emergency and Diagnosis code for an invalid State*/ +#define ERROR_NOMEMORY 0xF1 /**< \brief Emergency and Diagnosis code for no memory*/ +#define ERROR_OBJECTDICTIONARY 0xF2 /**< \brief Emergency and Diagnosis code for generic object dictionary error*/ +#define ERROR_NOSYNCMANACCESS 0xF3 /**< \brief Emergency and Diagnosis code for no SyncManager access*/ +#define ERROR_NOOFRXPDOS 0xF4 /**< \brief Emergency and Diagnosis code for no RxPDOs*/ +#define ERROR_NOOFTXPDOS 0xF5 /**< \brief Emergency and Diagnosis code for no TxPDOs*/ +#define ERROR_STATECHANGE 0xF6 /**< \brief Emergency and Diagnosis code state change error*/ + +#define NOERROR_NOSTATECHANGE 0xFE /**< \brief Indicates no state change*/ +#define NOERROR_INWORK 0xFF /**< \brief Indication for no error but operation is pending*/ + +#define EMCY_SM_ERRORCODE 0xA000 /**< \brief Emergency and Diagnosis code for an SyncManager error*/ +#define EMCY_SM_DEVICESPECIFIC 0xFF00 /**< \brief Emergency and Diagnosis code for a device specific error*/ + + +/*--------------------------------------------- +- AL Status Codes +-----------------------------------------------*/ +#define ALSTATUSCODE_NOERROR 0x0000 /**< \brief No error*/ +#define ALSTATUSCODE_UNSPECIFIEDERROR 0x0001 /**< \brief Unspecified error*/ +#define ALSTATUSCODE_NOMEMORY 0x0002 /**< \brief No Memory*/ +/* ECATCHANGE_START(V5.11) ECAT9*/ +#define ALSTATUSCODE_FW_SII_NOT_MATCH 0x0006 /**< \brief Firmware and EEPROM do not match. Slave needs BOOT-INIT transition*/ +#define ALSTATUSCODE_FW_UPDATE_FAILED 0x0007 /**< \brief Firmware update not successful. Old firmware still running*/ +/* ECATCHANGE_END(V5.11) ECAT9*/ +#define ALSTATUSCODE_INVALIDALCONTROL 0x0011 /**< \brief Invalid requested state change*/ +#define ALSTATUSCODE_UNKNOWNALCONTROL 0x0012 /**< \brief Unknown requested state*/ +#define ALSTATUSCODE_BOOTNOTSUPP 0x0013 /**< \brief Bootstrap not supported*/ +#define ALSTATUSCODE_NOVALIDFIRMWARE 0x0014 /**< \brief No valid firmware*/ +#define ALSTATUSCODE_INVALIDMBXCFGINBOOT 0x0015 /**< \brief Invalid mailbox configuration (BOOT state)*/ +#define ALSTATUSCODE_INVALIDMBXCFGINPREOP 0x0016 /**< \brief Invalid mailbox configuration (PreOP state)*/ +#define ALSTATUSCODE_INVALIDSMCFG 0x0017 /**< \brief Invalid sync manager configuration*/ +#define ALSTATUSCODE_NOVALIDINPUTS 0x0018 /**< \brief No valid inputs available*/ +#define ALSTATUSCODE_NOVALIDOUTPUTS 0x0019 /**< \brief No valid outputs*/ +#define ALSTATUSCODE_SYNCERROR 0x001A /**< \brief Synchronization error*/ +#define ALSTATUSCODE_SMWATCHDOG 0x001B /**< \brief Sync manager watchdog*/ +#define ALSTATUSCODE_SYNCTYPESNOTCOMPATIBLE 0x001C /**< \brief Invalid Sync Manager Types*/ +#define ALSTATUSCODE_INVALIDSMOUTCFG 0x001D /**< \brief Invalid Output Configuration*/ +#define ALSTATUSCODE_INVALIDSMINCFG 0x001E /**< \brief Invalid Input Configuration*/ +#define ALSTATUSCODE_INVALIDWDCFG 0x001F /**< \brief Invalid Watchdog Configuration*/ +#define ALSTATUSCODE_WAITFORCOLDSTART 0x0020 /**< \brief Slave needs cold start*/ +#define ALSTATUSCODE_WAITFORINIT 0x0021 /**< \brief Slave needs INIT*/ +#define ALSTATUSCODE_WAITFORPREOP 0x0022 /**< \brief Slave needs PREOP*/ +#define ALSTATUSCODE_WAITFORSAFEOP 0x0023 /**< \brief Slave needs SAFEOP*/ +#define ALSTATUSCODE_INVALIDINPUTMAPPING 0x0024 /**< \brief Invalid Input Mapping*/ +#define ALSTATUSCODE_INVALIDOUTPUTMAPPING 0x0025 /**< \brief Invalid Output Mapping*/ +#define ALSTATUSCODE_INCONSISTENTSETTINGS 0x0026 /**< \brief Inconsistent Settings*/ +#define ALSTATUSCODE_FREERUNNOTSUPPORTED 0x0027 /**< \brief FreeRun not supported*/ +#define ALSTATUSCODE_SYNCHRONNOTSUPPORTED 0x0028 /**< \brief SyncMode not supported*/ +#define ALSTATUSCODE_FREERUNNEEDS3BUFFERMODE 0x0029 /**< \brief FreeRun needs 3Buffer Mode*/ +#define ALSTATUSCODE_BACKGROUNDWATCHDOG 0x002A /**< \brief Background Watchdog*/ +#define ALSTATUSCODE_NOVALIDINPUTSANDOUTPUTS 0x002B /**< \brief No Valid Inputs and Outputs*/ +#define ALSTATUSCODE_FATALSYNCERROR 0x002C /**< \brief Fatal Sync Error*/ +#define ALSTATUSCODE_NOSYNCERROR 0x002D /**< \brief No Sync Error*/ +#define ALSTATUSCODE_CYCLETIMETOOSMALL 0x002E /**< \brief EtherCAT cycle time smaller Minimum Cycle Time supported by slave*/ +#define ALSTATUSCODE_DCINVALIDSYNCCFG 0x0030 /**< \brief Invalid DC SYNCH Configuration*/ +#define ALSTATUSCODE_DCINVALIDLATCHCFG 0x0031 /**< \brief Invalid DC Latch Configuration*/ +#define ALSTATUSCODE_DCPLLSYNCERROR 0x0032 /**< \brief PLL Error*/ +#define ALSTATUSCODE_DCSYNCIOERROR 0x0033 /**< \brief DC Sync IO Error*/ +#define ALSTATUSCODE_DCSYNCMISSEDERROR 0x0034 /**< \brief DC Sync Timeout Error*/ +#define ALSTATUSCODE_DCINVALIDSYNCCYCLETIME 0x0035 /**< \brief DC Invalid Sync Cycle Time*/ +#define ALSTATUSCODE_DCSYNC0CYCLETIME 0x0036 /**< \brief DC Sync0 Cycle Time*/ +#define ALSTATUSCODE_DCSYNC1CYCLETIME 0x0037 /**< \brief DC Sync1 Cycle Time*/ +#define ALSTATUSCODE_MBX_AOE 0x0041 /**< \brief MBX_AOE*/ +#define ALSTATUSCODE_MBX_EOE 0x0042 /**< \brief MBX_EOE*/ +#define ALSTATUSCODE_MBX_COE 0x0043 /**< \brief MBX_COE*/ +#define ALSTATUSCODE_MBX_FOE 0x0044 /**< \brief MBX_FOE*/ +#define ALSTATUSCODE_MBX_SOE 0x0045 /**< \brief MBX_SOE*/ +#define ALSTATUSCODE_MBX_VOE 0x004F /**< \brief MBX_VOE*/ +#define ALSTATUSCODE_EE_NOACCESS 0x0050 /**< \brief EEPROM no access*/ +#define ALSTATUSCODE_EE_ERROR 0x0051 /**< \brief EEPROM Error*/ +#define ALSTATUSCODE_EXT_HARDWARE_NOT_READY 0x0052 /**< \brief External hardware not ready. This AL Status Code should be used if the EtherCAT-Slave refused the state transition due to an external connection to another device or signal is missing*/ +#define ALSTATUSCODE_MODULE_ID_LIST_NOT_MATCH 0x0070 /**< \brief Detected Module Ident List (0xF030) and Configured Module Ident List (0xF050) does not match*/ + + + +/*--------------------------------------------- +- Configured Sync Type (0x1C32.1 / 0x1C33.1) +-----------------------------------------------*/ +#define SYNCTYPE_FREERUN 0x0000 /**< \brief Sync type FreeRun*/ +/*ECATCHANGE_START(V5.11) ESM7*/ +#define SYNCTYPE_SM_SYNCHRON 0x0001 /**< \brief SyncManager synchron (synchron to the corresponding SM, 0x1C32.1 -> SM2 ; 0x1C33.1 -> SM3) */ +#define SYNCTYPE_SM2_SYNCHRON 0x0022 /**< \brief SyncManager2 synchron (only used for 0x1C33.1)*/ +/*ECATCHANGE_END(V5.11) ESM7*/ +#define SYNCTYPE_DCSYNC0 0x0002 /**< \brief Sync type Sync0 synchron*/ +#define SYNCTYPE_DCSYNC1 0x0003 /**< \brief Sync type Sync1 synchron*/ + + +/*--------------------------------------------- +- AL Event masks +-----------------------------------------------*/ +#define AL_CONTROL_EVENT ((UINT16) 0x01) /**< \brief AL Control event*/ +#define SYNC0_EVENT ((UINT16) 0x04) /**< \brief Sync0 event*/ +#define SYNC1_EVENT ((UINT16) 0x08) /**< \brief Sync1 event*/ +#define SM_CHANGE_EVENT ((UINT16) 0x10) /**< \brief SyncManager changed event*/ +#define EEPROM_CMD_PENDING ((UINT16) 0x20) /**< \brief EEPROM command pending*/ +#define MAILBOX_WRITE_EVENT ((UINT16) 0x0100) /**< \brief MBoxOut write event*/ +#define MAILBOX_READ_EVENT ((UINT16) 0x0200) /**< \brief MBoxIn read event*/ +#define PROCESS_OUTPUT_EVENT ((UINT16) 0x0400) /**< \brief Output process data write event*/ +#define PROCESS_INPUT_EVENT ((UINT16) 0x0800) /**< \brief Input process data read event*/ + + +#ifndef MAX_PD_SYNC_MAN_CHANNELS + #define MAX_PD_SYNC_MAN_CHANNELS 2 /**< \brief maximum number of process data SyncManager channels*/ +#endif +#define MAX_NUMBER_OF_SYNCMAN ((MAX_PD_SYNC_MAN_CHANNELS)+2) /**< \brief Maximum number of SyncManagers (max number of Pd SM + 2 mailbox SyncManager)*/ + +#define MAILBOX_WRITE 0 /**< \brief SyncManager ID for MBoxOut (master to slave)*/ +#define MAILBOX_READ 1 /**< \brief SyncManager ID for MBoxIn (slave to master)*/ +#define PROCESS_DATA_OUT 2 /**< \brief SyncManager ID for output process data (master to slave)*/ +#define PROCESS_DATA_IN 3 /**< \brief SyncManager ID for input process data (slave to master)*/ + + + + + + + +#define MEMORY_START_ADDRESS 0x1000 /**< \brief ESC DPRAM start address*/ + + +#ifndef DC_SYNC_ACTIVE + #define DC_SYNC_ACTIVE ESC_DC_SYNC0_ACTIVE_MASK /**< \brief Expected activated Sync signals by the master*/ +#endif +#ifndef DC_EVENT_MASK + #define DC_EVENT_MASK PROCESS_OUTPUT_EVENT /**< \brief AL Event mask (value of register 0x204) in case of DC synchronisation*/ +#endif + + +#endif //_ECATSLV_H_ + +/* ECATCHANGE_START(V5.11) ECAT10*/ +#if defined(_ECATSLV_) && (_ECATSLV_ == 1) +/* ECATCHANGE_END(V5.11) ECAT10*/ + #define PROTO +#else + #define PROTO extern +#endif +/*----------------------------------------------------------------------------------------- +------ +------ Global Variables +------ +-----------------------------------------------------------------------------------------*/ +PROTO BOOL bEcatOutputUpdateRunning; /**< \brief Indicates the OP state, will be set in StartOutputHandler + and reset in StopOutputHandler*/ + +PROTO BOOL bEcatInputUpdateRunning; /**< \brief Indicates the SAFEOP or OP state, will be set in StartInputHandler + and reset in Stop InputHandler*/ + +PROTO BOOL bEcatFirstOutputsReceived; /**< \brief Indicates if outputs were received (SM2-event) + or inputs were read (SM3-event, if the output size is 0), + has to be set by the application and reset in StopOutputHandler*/ + +PROTO BOOL bWdTrigger; /**< \brief Indicates that the SM2 WD trigger bit (0x814 bit6) is set.*/ + +PROTO BOOL bDcSyncActive; /**< \brief Indicates that the Distributed Clocks synchronization is active,*/ + +PROTO INT16 EsmTimeoutCounter; /**< \brief Counter used to detect an ESM timeout. + -1 indicates a deactivated counter and 0 is expired*/ + +#define MAX_SM_EVENT_MISSED 4 /**< \brief threshold of max missed counter value (0x1C32.11 / 0x1C33.11)*/ + +PROTO BOOL bDcRunning; /**< \brief Indicates if Sync0 events are received*/ + +PROTO UINT16 u16SmSync0Counter; /**< /brief Incremented by one on every Sync0 event and reset to 0 on every SM event. It is used to check the SM/Sync0 sequence */ +PROTO UINT16 u16SmSync0Value; /**< /brief Allowed Sync0 events within one SM cycle. If 0 the Sequence check is disabled */ + +PROTO BOOL bSmSyncSequenceValid; /**< \brief Set to true if SM/Sync0 sequence is valid*/ + +PROTO INT16 i16WaitForPllRunningTimeout; /**< \brief The time bPllRunnig shall be true while state change from SafeOp to Op*/ + +PROTO INT16 i16WaitForPllRunningCnt; /**< \brief Incremented with every valid Sync-SyncManger cycle */ + +PROTO UINT16 Sync0WdCounter; /**< \brief Sync0 watchdog counter*/ +PROTO UINT16 Sync0WdValue; /**< \brief Sync0 watchdog value*/ + +PROTO UINT16 Sync1WdCounter; /**< \brief Sync1 watchdog counter*/ +PROTO UINT16 Sync1WdValue; /**< \brief Sync1 watchdog value*/ + +PROTO UINT16 LatchInputSync0Value; /**< \brief Sync0 event on which the inputs shall be latched and copied to the ESC buffer. If the inputs shall be latched base don Sync1 the value is set to 0.*/ +PROTO UINT16 LatchInputSync0Counter; /**< \brief Sync0 counter used to get the Sync0 event to latch the Inputs. */ + + + + +PROTO BOOL bEscIntEnabled; /**< \brief Indicates that the ESC interrupt is enabled (SM2/3 or SYNC0/1-event), + will be set in StartInputHandler and reset in StopInputHandler*/ + +PROTO BOOL b3BufferMode; /**< \brief Indicates that inputs and outputs are running in 3-Buffer-Mode*/ + +PROTO BOOL bLocalErrorFlag; /**< \brief Contains the information if the application has a local error*/ +PROTO UINT16 u16LocalErrorCode; /**< \brief Reason for local error*/ +PROTO BOOL bApplEsmPending; /**< \brief Indicates if the local application ESM function need to be called from Al_ConntrolRes (is true if NOERR_INWORK is returned by generic ESM function)*/ +PROTO BOOL bEcatWaitForAlControlRes; /**< \brief Contains the information that the state machine waits for an acknowledge + for the last AL_ControlInd from the application/generic stack*/ + +PROTO UINT16 nEcatStateTrans; /**< \brief Current state transition*/ + +/*ECATCHANGE_START(V5.11) ECAT4*/ + +PROTO UINT16 nPdInputSize; /**< \brief Contains the input size (SM3 size)/SM2 if no outputs are supported, has to be written by the application*/ + +PROTO UINT16 nPdOutputSize; /**< \brief Contains the output size (SM2 size), has to be written by the application*/ +/*ECATCHANGE_END(V5.11) ECAT4*/ + +PROTO UINT8 nMaxSyncMan; /**< \brief Contains the maximum number of Sync Manager channels, will be initialized in ECAT_Main*/ + +PROTO UINT16 nMaxEscAddress; /**< \brief Max supported address by the ESC (register(0x1000) + size of the supported DPRAM in Byte)*/ + +PROTO UINT8 nAlStatus; /**< \brief Contains the actual AL Status, will be written in AL_ControlInd*/ + + +PROTO UINT16 EcatWdValue; /**< \brief Contains the value of the watchdog in 100us, will be written in StartInputHandler. + In case that the ESC watchdog feature is used this variable just indicates if the watchdog is enabled or disabled*/ +PROTO UINT16 nEscAddrOutputData; /**< \brief Contains the SM address for the output process data*/ +PROTO UINT16 nEscAddrInputData; /**< \brief Contains the SM address for the input process data*/ + + +/*----------------------------------------------------------------------------------------- +------ +------ Global Functions +------ +-----------------------------------------------------------------------------------------*/ +/*ECATCHANGE_START(V5.11) HW1*/ +PROTO void EnableSyncManChannel(UINT8 channel); +PROTO void DisableSyncManChannel(UINT8 channel); +PROTO TSYNCMAN ESCMEM *GetSyncMan(UINT8 channel); +/*ECATCHANGE_END(V5.11) HW1*/ +PROTO void SetALStatus(UINT8 alStatus, UINT16 alStatusCode); +PROTO void AL_ControlInd(UINT8 alControl, UINT16 alStatusCode); +PROTO void DC_CheckWatchdog(void); +PROTO void CheckIfEcatError(void); +PROTO void ECAT_Init(void); + +PROTO void ECAT_Main(void); + +#undef PROTO +/** @}*/ diff --git a/GD32E508_DCU_APP/comm/ssc/inc/el9800appl.h b/GD32E508_DCU_APP/comm/ssc/inc/el9800appl.h new file mode 100644 index 0000000..9a6605e --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/inc/el9800appl.h @@ -0,0 +1,33 @@ +/** + * @file el9800appl.h + * @brief EL9800 应用层桩头文件 — 本项目不使用 EL9800 示例应用 + * + * ecatappl.c / ecatslv.c 无条件 include 此文件。 + * 本项目的应用逻辑在 dc_app.c/h 中实现。 + */ +#ifndef _EL9800APPL_H_ +#define _EL9800APPL_H_ + +#include "ecatappl.h" +#include "objdef.h" +#include "dc_app.h" + +/*----------------------------------------------------------------------------------------- +------ +------ Application Object Dictionary +------ +------ Phase 1: 空(无应用层专用 OD 条目)。 +------ Phase 3: 在此添加 DCU 专用对象(如 0x2000 传感器数据)。 +------ coeappl.c 中 COE_ObjInit() 会检查此指针,NULL 则跳过。 +-----------------------------------------------------------------------------------------*/ +#if defined(_EL9800APPL_) && (_EL9800APPL_ == 1) + #define PROTO +#else + #define PROTO extern +#endif + +PROTO TOBJECT OBJMEM * VARMEM ApplicationObjDic; + +#undef PROTO + +#endif /* _EL9800APPL_H_ */ diff --git a/GD32E508_DCU_APP/comm/ssc/inc/el9800hw.h b/GD32E508_DCU_APP/comm/ssc/inc/el9800hw.h new file mode 100644 index 0000000..e395130 --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/inc/el9800hw.h @@ -0,0 +1,150 @@ +/** + * @file el9800hw.h + * @brief SSC 硬件抽象层 — GD32E508 + LAN9252 (EXMC 16-bit HBI Indexed Mode) + * + * 本文件替换 Beckhoff EL9800 SPI 版本,提供 SSC 所需的 HW_EscRead/Write 宏 + * 和中断/定时器控制宏。实际实现在 hw_gd32exmc.cpp。 + * + * HBI Indexed 寻址(A2=PD12=A17, A1=PD11=A16): + * IDX_0 = EXMC offset 0x00000 (A2=0, A1=0) + * DATA_0 = EXMC offset 0x20000 (A2=1, A1=0) 低 16 位 + * = EXMC offset 0x30000 (A2=1, A1=1) 高 16 位 + */ +#ifndef _EL9800HW_H_ +#define _EL9800HW_H_ + +/*----------------------------------------------------------------------------------------- +------ +------ Includes +------ +-----------------------------------------------------------------------------------------*/ +#include "esc.h" + +/* GD32E508 CMSIS (通过 RTE 组件获取) */ +#include "RTE_Components.h" +#include CMSIS_device_header + +/*----------------------------------------------------------------------------------------- +------ +------ Hardware Timer Settings +------ +-----------------------------------------------------------------------------------------*/ + +/** SSC 看门狗定时器分辨率:ticks per ms。 + * 本项目使用 SysTick (180MHz / 1000 = 180000 ticks/ms)。 + * 实际由 hw_gd32exmc.cpp 中 HW_GetTimer() 实现决定。 */ +#define ECAT_TIMER_INC_P_MS 180000 + +/*----------------------------------------------------------------------------------------- +------ +------ Interrupt Control (ESC IRQ = EXTI1, PB1) +------ +-----------------------------------------------------------------------------------------*/ + +#ifndef DISABLE_ESC_INT +#define DISABLE_ESC_INT() NVIC_DisableIRQ(EXTI1_IRQn) +#endif + +#ifndef ENABLE_ESC_INT +#define ENABLE_ESC_INT() NVIC_EnableIRQ(EXTI1_IRQn) +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ AL Event Interrupt Mask (主循环保护) +------ +------ SSC 在主循环中访问 ESC 时需要 mask SYNC0 ISR (EXTI0, preempt=0) +------ 防止 HBI IDX 寄存器被 ISR 抢占导致地址错乱。 +-----------------------------------------------------------------------------------------*/ + +#ifndef DISABLE_AL_EVENT_INT +#define DISABLE_AL_EVENT_INT() do { NVIC_DisableIRQ(EXTI0_IRQn); NVIC_DisableIRQ(EXTI1_IRQn); } while(0) +#endif + +#ifndef ENABLE_AL_EVENT_INT +#define ENABLE_AL_EVENT_INT() do { NVIC_EnableIRQ(EXTI1_IRQn); NVIC_EnableIRQ(EXTI0_IRQn); } while(0) +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ Timer Access +------ +-----------------------------------------------------------------------------------------*/ + +#ifndef HW_GetTimer +#define HW_GetTimer() HW_GetTimerValue() +#endif + +#ifndef HW_ClearTimer +#define HW_ClearTimer() HW_ClearTimerValue() +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ ESC Access Macros (SSC 核心接口) +------ +------ MEM_ADDR = UINT16 (CONTROLLER_16BIT=1) +------ 所有宏最终调用 HW_EscRead / HW_EscWrite (hw_gd32exmc.cpp) +-----------------------------------------------------------------------------------------*/ + +#define HW_EscReadByte(ByteValue, Address) HW_EscRead(((MEM_ADDR *)&(ByteValue)), ((UINT16)(Address)), 1) +#define HW_EscReadWord(WordValue, Address) HW_EscRead(((MEM_ADDR *)&(WordValue)), ((UINT16)(Address)), 2) +#define HW_EscReadDWord(DWordValue, Address) HW_EscRead(((MEM_ADDR *)&(DWordValue)), ((UINT16)(Address)), 4) +#define HW_EscReadMbxMem(pData, Address, Len) HW_EscRead(((MEM_ADDR *)(pData)), ((UINT16)(Address)), (Len)) + +#define HW_EscReadByteIsr(ByteValue, Address) HW_EscReadIsr(((MEM_ADDR *)&(ByteValue)), ((UINT16)(Address)), 1) +#define HW_EscReadWordIsr(WordValue, Address) HW_EscReadIsr(((MEM_ADDR *)&(WordValue)), ((UINT16)(Address)), 2) +#define HW_EscReadDWordIsr(DWordValue, Address) HW_EscReadIsr(((MEM_ADDR *)&(DWordValue)), ((UINT16)(Address)), 4) + +#define HW_EscWriteByte(ByteValue, Address) HW_EscWrite(((MEM_ADDR *)&(ByteValue)), ((UINT16)(Address)), 1) +#define HW_EscWriteWord(WordValue, Address) HW_EscWrite(((MEM_ADDR *)&(WordValue)), ((UINT16)(Address)), 2) +#define HW_EscWriteDWord(DWordValue, Address) HW_EscWrite(((MEM_ADDR *)&(DWordValue)), ((UINT16)(Address)), 4) +#define HW_EscWriteMbxMem(pData, Address, Len) HW_EscWrite(((MEM_ADDR *)(pData)), ((UINT16)(Address)), (Len)) + +#define HW_EscWriteByteIsr(ByteValue, Address) HW_EscWriteIsr(((MEM_ADDR *)&(ByteValue)), ((UINT16)(Address)), 1) +#define HW_EscWriteWordIsr(WordValue, Address) HW_EscWriteIsr(((MEM_ADDR *)&(WordValue)), ((UINT16)(Address)), 2) +#define HW_EscWriteDWordIsr(DWordValue, Address) HW_EscWriteIsr(((MEM_ADDR *)&(DWordValue)), ((UINT16)(Address)), 4) + +#endif /* _EL9800HW_H_ */ + +/*----------------------------------------------------------------------------------------- +------ +------ Function Prototypes +------ +-----------------------------------------------------------------------------------------*/ +#if _EL9800HW_ + #define PROTO +#else + #define PROTO extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +PROTO UINT8 HW_Init(void); +PROTO void HW_Release(void); +PROTO UINT16 HW_GetALEventRegister(void); +PROTO UINT16 HW_GetALEventRegister_Isr(void); +PROTO void HW_ResetALEventMask(UINT16 intMask); +PROTO void HW_SetALEventMask(UINT16 intMask); +PROTO void HW_SetLed(UINT8 RunLed, UINT8 ErrLed); + +PROTO void HW_EscRead(MEM_ADDR *pData, UINT16 Address, UINT16 Len); +PROTO void HW_EscReadIsr(MEM_ADDR *pData, UINT16 Address, UINT16 Len); +PROTO void HW_EscWrite(MEM_ADDR *pData, UINT16 Address, UINT16 Len); +PROTO void HW_EscWriteIsr(MEM_ADDR *pData, UINT16 Address, UINT16 Len); + +PROTO void HW_DisableSyncManChannel(UINT8 channel); +PROTO void HW_EnableSyncManChannel(UINT8 channel); +PROTO TSYNCMAN ESCMEM *HW_GetSyncMan(UINT8 channel); + +/* Timer helpers (implemented in hw_gd32exmc.cpp) */ +PROTO UINT32 HW_GetTimerValue(void); +PROTO void HW_ClearTimerValue(void); + +#ifdef __cplusplus +} +#endif + +#undef PROTO diff --git a/GD32E508_DCU_APP/comm/ssc/inc/esc.h b/GD32E508_DCU_APP/comm/ssc/inc/esc.h new file mode 100644 index 0000000..7687c57 --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/inc/esc.h @@ -0,0 +1,175 @@ +/** + * \addtogroup ESCRegister ESC Register + * @{ + */ + +/** +\file esc.h +\author EthercatSSC@beckhoff.com +\brief EtherCAT Slave Controller related defines and Types + +\version 5.10 + +
Changes to version V5.01:
+V5.10 ESC1: Update address register offset for 32Bit ESC access
+V5.10 ESC2: Check if defined SM settings do not exceed the available DPRAM range (in error case AL Status 0x14 is returned)
+V5.10 ESC3: Handle DC cControl register values in case of 32Bit ESC access (a Sync activation mask need to defined/used)
+
Changes to version - :
+V5.01 : Start file change log + */ +#ifndef _ESC_H_ +#define _ESC_H_ + +/*----------------------------------------------------------------------------------------- +------ +------ Includes +------ +-----------------------------------------------------------------------------------------*/ + +#include "ecat_def.h" + + +/*----------------------------------------------------------------------------------------- +------ +------ Defines and Types +------ +-----------------------------------------------------------------------------------------*/ + +#define SIZEOF_SM_REGISTER 8 /**< \brief Each SyncManger has 8Byte Configuration/Status Registers*/ +#define MAX_NO_OF_SYNC_MAN 16/**< \brief Maximum Number of SyncManager channels supported by an ESC*/ + +#define BL_PAGE_SIZE 512 + +/*--------------------------------------------- +- ESC Offsets (Detailed Information about the registers are located in the ESC Datasheets e.g. ET1100 Datasheet www.beckhoff.com/english.asp?download/ethercat_development_products.htm?id=71003127100387) +-----------------------------------------------*/ +#define ESC_INFO_OFFSET 0x0000 /**< \brief ESC information registers startoffset*/ + +#define ESC_COMM_INFO_OFFSET 0x0004 /**< \brief Communication information registers startoffset*/ + +#define ESC_DPRAM_SIZE_OFFSET 0x0006 /**< \brief Register Description: Size of the supported DPRAM in KB*/ +#define ESC_SM_CHANNELS_MASK 0xFF00 /**< \brief Mask based on ESC offset "ESC_COMM_INFO_OFFSET"*/ +#define ESC_SM_CHANNELS_SHIFT 8 /**< \brief Bit shift based on ESC offset "ESC_COMM_INFO_OFFSET"*/ +#define ESC_DPRAM_SIZE_MASK 0x00FF + + +#define ESC_SLAVE_ADDRESS_OFFSET 0x0010 /**< \brief Register Description: Address used for node addressing (FPxx commands)*/ + + + +#define ESC_AL_CONTROL_OFFSET 0x0120 /**< \brief Register Description: Initiate State Transition of the Device State Machine*/ +#define ESC_AL_STATUS_OFFSET 0x0130 /**< \brief Register Description: Actual State of the Device State Machine*/ +#define ESC_AL_STATUS_CODE_OFFSET 0x0134 /**< \brief Register Description: AL Status Code*/ + +#define ESC_RUN_LED_OVERRIDE 0x0138 /**< \brief Register Description: Set Ecat Run indication via ESC. (not all ESC types support this feature)*/ +#define ESC_ERROR_LED_OVERRIDE 0x0139 /**< \brief Register Description: Set Ecat Error indication via ESC. (not all ESC types support this feature)*/ + +#define ESC_PDI_CONTROL_OFFSET 0x0140 /**< \brief Register Description: Specifies the process data interface*/ + +#define ESC_AL_EVENTMASK_OFFSET 0x0204 /**< \brief Register Description: AL Event masking of the AL Event Request register Events for mapping to PDI IRQ signal*/ +#define ESC_AL_EVENT_OFFSET 0x0220 /**< \brief Register Description: "Mirror" register for ESC events*/ + +#define ESC_WD_DIVIDER_OFFSET 0x0400 /**< \brief Register Description: Number of 25 MHz tics (minus 2) that represents the basic watchdog increment. (Default value is 100us = 2498)*/ + +#define ESC_PD_WD_TIME 0x0420 /**< \brief Register Description: Number of basic watchdog increments (Default value with Watchdog divider 100us means 100ms Watchdog)*/ +#define ESC_PD_WD_STATE 0x0440 /**< \brief Register Description: Watchdog Status of Process Data (triggered by SyncManagers)*/ +#define ESC_PD_WD_TRIGGER_MASK 0x0001 /**< \brief Trigger state of the process data watchdog*/ + +#define ESC_EEPROM_CONFIG_OFFSET 0x0500 /**< \brief Register Description: EEPROM Access Configuration*/ +/*EEPROM config and access state bit mask (based on "ESC_EEPROM_CONFIG_OFFSET") - START*/ +#define ESC_EEPROM_ASSIGN_TO_PDI_MASK 0x0001 /**< \brief Description (0x500.0): PDI has EEPROM control*/ +#define ESC_EEPROM_LOCKED_BY_PDI_MASK 0x0100 /**< \brief Description (0x500.8): PDI locked EEPROM access*/ +/*EEPROM config and access state bit mask (based on "ESC_EEPROM_CONFIG_OFFSET") - END*/ + +#define ESC_EEPROM_CONTROL_OFFSET 0x0502 +/* EEPROM command and status bit masks (based on "ESC_EEPROM_CONTROL_OFFSET") - START*/ +#define ESC_EEPROM_SUPPORTED_READBYTES_MASK 0x0040 /**< \brief Description (0x502.6): Supported number of EEPROM read bytes: 0-> 4 Bytes; 1 -> 8 Bytes*/ +#define ESC_EEPROM_CMD_MASK 0x0700 /**< \brief Description (0x502.8:10): Command bit mask*/ +#define ESC_EEPROM_CMD_READ_MASK 0x0100 /**< \brief Description (0x502.8): Currently executed read command*/ +#define ESC_EEPROM_CMD_WRITE_MASK 0x0200 /**< \brief Description (0x502.9): Initialize Write Command*/ +#define ESC_EEPROM_CMD_RELOAD_MASK 0x0400 /**< \brief Description (0x502.10): Trigger EEPROM reload*/ +#define ESC_EEPROM_ERROR_MASK 0x7800 /**< \brief Description : Mask all EEPROM error bits; Checksum error (0x0502.11); EEPROM not loaded (0x0502.12); Missing EEPROM Acknowledge (0x0502.13); Write Error (0x0502.14)*/ +#define ESC_EEPROM_ERROR_CRC 0x0800 /**< \brief Description (0x502.11): EEPROM CRC Error*/ +#define ESC_EEPROM_ERROR_LOAD 0x1000 /**< \brief Description (0x502.11): EEPROM loading status (0 = OK)*/ +#define ESC_EEPROM_ERROR_CMD_ACK 0x2000 /**< \brief Description (0x502.13): EEPROM Acknowledge/Command*/ +#define ESC_EEPROM_BUSY_MASK 0x8000 /**< \brief Description (0x502.15): EEPROM Busy*/ +/* EEPROM command and status bit masks (based on "ESC_EEPROM_CONTROL_OFFSET") - END*/ +#define ESC_EEPROM_ADDRESS_OFFSET 0x0504 +#define ESC_EEPROM_DATA_OFFSET 0x0508 + +#define ESC_SYNCMAN_REG_OFFSET 0x0800 /**< \brief Register Description: Start address of the SyncManager Configuration/Staus registers*/ +#define ESC_SYNCMAN_CONTROL_OFFSET 0x0804 /**< \brief Register Description: SyncManager Setting Register*/ + + +#define ESC_SYNCMAN_ACTIVE_OFFSET 0x0806 /**< \brief Register Description: SyncManager Activation Register*/ + + +#define ESC_SYSTEMTIME_OFFSET 0x0910 /**< \brief Register Description: Local copy of the System Time*/ + +#define ESC_DC_UNIT_CONTROL_OFFSET 0x0980 /**< \brief Register Description: Control registers for Cycle and Sync Unit (can be predefined with the "AssignActivate" Element in the device description, for further information see ETG.2000)*/ +#define ESC_DC_SYNC_UNIT_ACTIVE_MASK 0x0100 /**< \brief Description (0x980.8): Sync Out Unit is activated*/ +#define ESC_DC_SYNC0_ACTIVE_MASK 0x0200 /**< \brief Description (0x980.9): Sync0 generation is activated*/ +#define ESC_DC_SYNC1_ACTIVE_MASK 0x0400 /**< \brief Description (0x980.10): Sync1 generation is activated*/ +#define ESC_DC_SYNC_UNIT_AUTO_ACTIVE_MASK 0x0800 /**< \brief Description (0x980.11): Sync Out Unit is activated automatic when System time was written*/ + +#define ESC_DC_SYNC0_CYCLETIME_OFFSET 0x09A0 /**< \brief Register Description: 32Bit Time between two consecutive SYNC0 pulses in ns*/ +#define ESC_DC_SYNC1_CYCLETIME_OFFSET 0x09A4 /**< \brief Register Description: 32Bit Time between two consecutive SYNC1 pulses in ns*/ + + + + +/*--------------------------------------------- +- Sync Manager +-----------------------------------------------*/ +/** \brief SyncManager Register struture*/ +typedef struct STRUCT_PACKED_START +{ + + UINT16 PhysicalStartAddress; /**< \brief SyncManager address*/ + UINT16 Length; /**< \brief SyncManager length*/ + + UINT16 Settings[2]; /**< \brief Register values 0x0804 : 0x0807*/ + +/*Defines to access : +* - SM Control register 0x0804 +* - SM Status register 0x0805 +* +* all defines are based on 0x0804 +*/ + +/*SyncManger control (0x0804) access*/ +#define SM_SETTING_CONTROL_OFFSET 0 /**< \brief Offset to value of register 0x0804*/ +#define SM_SETTING_MODE_MASK 0x0002 /**< \brief SyncManager mode mask*/ +#define SM_SETTING_MODE_THREE_BUFFER_VALUE 0x0000 /**< \brief SyncManager 3Buffer mode value*/ +#define SM_SETTING_MODE_ONE_BUFFER_VALUE 0x0002 /**< \brief SyncManager 1Buffer mode value*/ +#define SM_SETTING_DIRECTION_MASK 0x000C /**< \brief SyncManager direction mask*/ +#define SM_SETTING_DIRECTION_READ_VALUE 0x0000 /**< \brief SyncManager read direction*/ +#define SM_SETTING_DIRECTION_WRITE_VALUE 0x0004 /**< \brief SyncManager write direction*/ +#define SM_SETTING_WATCHDOG_VALUE 0x0040 /**< \brief SyncManager watchdog value*/ + +/*SyncManger status (0x0805) access*/ +#define SM_STATUS_MBX_BUFFER_FULL 0x0800 /**< \brief Indicates in one buffer mode if buffer was completely written*/ + +/*Defines to access : +* - SM Active register 0x0806 +* - SM PDI Control register 0x0807 +* +* all defines are based on 0x0806 +*/ + +/*SyncManger active (0x0806) access*/ +#define SM_SETTING_ACTIVATE_OFFSET 1 /**< \brief Offset to value of register 0x0806*/ +#define SM_SETTING_ENABLE_VALUE 0x0001 /**< \brief SyncManager enable*/ +#define SM_SETTING_REPAET_REQ_MASK 0x0002 /**< \brief SyncManager Repeat request*/ +#define SM_SETTING_REPEAT_REQ_SHIFT 0 /**< \brief SyncManager Repeat request shift*/ + +/*SyncManger PDI Control (0x0807) access*/ +#define SM_SETTING_PDI_DISABLE 0x0100 /**< \brief Bit0 of register 0x0807 (if 1 SM is disabled from PDI)*/ +#define SM_SETTING_REPEAT_ACK 0x0200 /**< \brief Bit1 of register 0x0807*/ +}STRUCT_PACKED_END +TSYNCMAN; + + + +#endif //_ESC_H_ +/** @}*/ diff --git a/GD32E508_DCU_APP/comm/ssc/inc/mailbox.h b/GD32E508_DCU_APP/comm/ssc/inc/mailbox.h new file mode 100644 index 0000000..92e6acf --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/inc/mailbox.h @@ -0,0 +1,196 @@ +/** + * \addtogroup Mailbox Mailbox Functions + * @{ + */ + +/** +\file mailbox.h +\author EthercatSSC@beckhoff.com + +\version 5.11 + +
Changes to version V5.01:
+V5.11 ECAT10: change PROTO handling to prevent compiler errors
+V5.11 MBX1: "return value of ""MBX_StartMailboxHandler()"" changed to UINT16"
+V5.11 MBX3: set application triggered emergency and EoE data to pending if no mailbox queue is supported and another mailbox request is currently handled, Handle only one mailbox request at a time (in case that MAILBPX_QUEUE is disabled)
+
Changes to version - :
+V5.01 : Start file change log + */ + +#ifndef _MAILBOX_H_ +#define _MAILBOX_H_ + +/*----------------------------------------------------------------------------------------- +------ +------ Includes +------ +-----------------------------------------------------------------------------------------*/ +#include "ecat_def.h" + + +/*----------------------------------------------------------------------------------------- +------ +------ Defines and Types +------ +-----------------------------------------------------------------------------------------*/ +#define MBX_TYPE_AOE 1 /**< \brief Mailbox type AoE*/ +#define MBX_TYPE_EOE 2 /**< \brief Mailbox type EoE*/ +#define MBX_TYPE_COE 3 /**< \brief Mailbox type CoE*/ +#define MBX_TYPE_FOE 4 /**< \brief Mailbox type FoE*/ +#define MBX_TYPE_SOE 5 /**< \brief Mailbox type SoE*/ +#define MBX_TYPE_VOE 15 /**< \brief Mailbox type VoE*/ + +#define EMCY_SERVICE ((UINT8) 0x0001) /**< \brief Emergency service*/ +#define COE_SERVICE ((UINT8) 0x0002) /**< \brief CoE service*/ +#define SOE_SERVICE ((UINT8) 0x0004) /**< \brief SoE service*/ +#define EOE_SERVICE ((UINT8) 0x0008) /**< \brief EoE service*/ +#define AOE_SERVICE ((UINT8) 0x0010) /**< \brief AoE service*/ +#define VOE_SERVICE ((UINT8) 0x0020) /**< \brief VoE service*/ +#define FOE_SERVICE ((UINT8) 0x0040) /**< \brief FoE service*/ +#define FRAGMENTS_FOLLOW ((UINT8) 0x0080) /**< \brief Fragments follow service*/ + +#ifndef DISABLE_MBX_INT + #define DISABLE_MBX_INT /**< \brief Disable mailbox interrupt*/ +#endif +#ifndef ENABLE_MBX_INT + #define ENABLE_MBX_INT /**< \brief Enable mailbox interrupt*/ +#endif + +#ifndef ENTER_MBX_CRITICAL + #define ENTER_MBX_CRITICAL /**< \brief Enter mailbox critical section*/ +#endif + +#ifndef LEAVE_MBX_CRITICAL + #define LEAVE_MBX_CRITICAL /**< \brief Leave mailbox critical section*/ +#endif + +#ifndef MAX_MBX_QUEUE_SIZE + #define MAX_MBX_QUEUE_SIZE 10 /**< \brief Mailbox queue size*/ +#endif + + +/*--------------------------------------------- +- Command Codes for the mailbox type 0 +-----------------------------------------------*/ +#define MBXSERVICE_MBXERRORCMD 0x01 /**< \brief Mailbox error command*/ + + +/*--------------------------------------------- +- Error Codes for a mailbox error response +-----------------------------------------------*/ +#define MBXERR_SYNTAX 0x01 /**< \brief Mailbox error "syntax"*/ +#define MBXERR_UNSUPPORTEDPROTOCOL 0x02 /**< \brief Mailbox error "unsupported protocol"*/ +#define MBXERR_INVALIDCHANNEL 0x03 /**< \brief Mailbox error "invalid channel"*/ +#define MBXERR_SERVICENOTSUPPORTED 0x04 /**< \brief Mailbox error "service not supported"*/ +#define MBXERR_INVALIDHEADER 0x05 /**< \brief Mailbox error "invalid header"*/ +#define MBXERR_SIZETOOSHORT 0x06 /**< \brief Mailbox error "Size too short"*/ +#define MBXERR_NOMOREMEMORY 0x07 /**< \brief Mailbox error "No memory"*/ +#define MBXERR_INVALIDSIZE 0x08 /**< \brief Mailbox error "Invalid size"*/ +#define MBXERR_SERVICEINWORK 0x09 /**< \brief Mailbox error "Service in work"*/ + + +/** + * \brief Mailbox header + */ +typedef struct MBX_STRUCT_PACKED_START +{ + UINT16 Length; /**< \brief Length*/ + UINT16 Address; /**< \brief Address*/ + + UINT16 Flags[1]; /**< \brief Flags*/ + #define MBX_OFFS_TYPE 0 /**< \brief Protocol type offset*/ + #define MBX_MASK_TYPE 0x0F00 /**< \brief Protocol type mask*/ + #define MBX_SHIFT_TYPE 8 /**< \brief Protocol type shift*/ + #define MBX_OFFS_COUNTER 0 /**< \brief Protocol counter offset*/ + #define MBX_MASK_COUNTER 0xF000 /**< \brief Protocol counter mask*/ + #define MBX_SHIFT_COUNTER 12 /**< \brief Protocol counter shift*/ +}MBX_STRUCT_PACKED_END +TMBXHEADER; + + +#define MBX_HEADER_SIZE SIZEOF(TMBXHEADER) /**< \brief Mailbox header size*/ + + +#define MAX_MBX_DATA_SIZE (MAX_MBX_SIZE - MBX_HEADER_SIZE) /**< \brief Mailbox data size*/ + +/** + * \brief Mailbox datagram + */ +typedef struct MBX_STRUCT_PACKED_START +{ + TMBXHEADER MbxHeader; /**< \brief Mailbox header*/ + UINT16 Data[(MAX_MBX_DATA_SIZE >> 1)]; /**< \brief Mailbox data*/ +}MBX_STRUCT_PACKED_END +TMBX; + + +/** + * \brief Mailbox queue + */ +typedef struct +{ + UINT16 firstInQueue; /**< \brief First in Queue*/ + UINT16 lastInQueue; /**< \brief Last in Queue*/ + UINT16 maxQueueSize; /**< \brief Max queue size*/ + TMBX MBXMEM * queue[(MAX_MBX_QUEUE_SIZE)+1]; /**< \brief Queue buffer*/ +} TMBXQUEUE; + + + +#endif //_MAILBOX_H_ + + +/*----------------------------------------------------------------------------------------- +------ +------ Global Variables +------ +-----------------------------------------------------------------------------------------*/ +/* ECATCHANGE_START(V5.11) ECAT10*/ +#if defined(_MAILBOX_) && (_MAILBOX_ == 1) +/* ECATCHANGE_END(V5.11) ECAT10*/ + #define PROTO +#else + #define PROTO extern +#endif + +PROTO BOOL bReceiveMbxIsLocked; /**< \brief Receive mailbox is locked (MBoxOut, default SM0)*/ +PROTO BOOL bSendMbxIsFull; /**< \brief Send mailbox is full (MBoxIn, default SM1)*/ +PROTO BOOL bMbxRunning; /**< \brief Mailbox running (At least PreOP state)*/ +PROTO BOOL bMbxRepeatToggle; /**< \brief Mailbox repeat toggle*/ +PROTO UINT16 u16SendMbxSize; /**< \brief Send mailbox size (default SM1)*/ +PROTO UINT16 u16ReceiveMbxSize; /**< \brief Receive mailbox size (default SM0)*/ +PROTO UINT16 u16EscAddrReceiveMbx; /**< \brief Receive mailbox address (default SM0)*/ +PROTO UINT16 u16EscAddrSendMbx; /**< \brief Send mailbox address (default SM1)*/ +PROTO UINT8 u8MbxWriteCounter; /**< \brief Write mailbox counter*/ +PROTO UINT8 u8MbxReadCounter; /**< \brief Read mailbox counter*/ +PROTO TMBX MBXMEM asMbx[2]; /**< \brief Mailbox buffer (for repeat service)*/ +PROTO UINT8 u8MailboxSendReqStored; /**< \brief Mailbox send request stored*/ +PROTO TMBX MBXMEM * psWriteMbx; /**< \brief Pointer to write mailbox buffer*/ +PROTO TMBX MBXMEM * psReadMbx; /**< \brief Pointer to read mailbox buffer*/ +PROTO TMBX MBXMEM * psRepeatMbx; /**< \brief Pointer to repeat mailbox buffer*/ +PROTO TMBX MBXMEM * psStoreMbx; /**< \brief Pointer to store mailbox buffer*/ +PROTO TMBXQUEUE MBXMEM sMbxSendQueue; /**< \brief Send mailbox queue*/ +PROTO TMBXQUEUE MBXMEM sMbxReceiveQueue; /**< \brief Receive mailbox queue*/ + + +/*----------------------------------------------------------------------------------------- +------ +------ Global Functions +------ +-----------------------------------------------------------------------------------------*/ + +PROTO void MBX_Init(void); +/* ECATCHANGE_START(V5.11) MBX1*/ +PROTO UINT16 MBX_StartMailboxHandler(void); +/* ECATCHANGE_END(V5.11) MBX1*/ +PROTO void MBX_StopMailboxHandler(void); +PROTO void MBX_MailboxWriteInd(TMBX MBXMEM *pMbx); +PROTO void MBX_MailboxReadInd(void); +PROTO void MBX_MailboxRepeatReq(void); +PROTO UINT8 MBX_MailboxSendReq(TMBX MBXMEM * pMbx, UINT8 flags); +PROTO void MBX_CheckAndCopyMailbox(void); +PROTO UINT8 MBX_CopyToSendMailbox(TMBX MBXMEM *pMbx); +PROTO void MBX_Main(void); + +#undef PROTO +/** @}*/ diff --git a/GD32E508_DCU_APP/comm/ssc/inc/objdef.h b/GD32E508_DCU_APP/comm/ssc/inc/objdef.h new file mode 100644 index 0000000..ee7ce96 --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/inc/objdef.h @@ -0,0 +1,291 @@ +/** + * \addtogroup CoE CAN Application Profile over EtherCAT + * @{ + */ + +/** +\file objdef.h +\author EthercatSSC@beckhoff.com +\brief CAN application profile over EtherCAT object dictionary + +\version 5.11 + +
Changes to version V5.10:
+V5.11 COE2: update synctype supported flag for freerun
+V5.11 COE3: change 0x10F3.2 (Sync Error limit) from UINT32 to UINT16 (according to the ETG.1020)
+V5.11 ECAT10: change PROTO handling to prevent compiler errors
+
Changes to version V5.01:
+V5.10 COE1: Define one entry description for all 0x1C3x objects and change data type of SI11,12,13 to UINT16 (according ETG.1020)
+V5.10 COE2: Change return value of Get ObjectSize to UINT32
+ Change object size to UINT32
+V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)
+ Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings
+ Update 0x1C3x entries
+V5.10 ECAT8: Update Datatype defines according ETG.1020
+
Changes to version - :
+V5.01 : Start file change log + */ + +#ifndef _OBJDEF_H_ +#define _OBJDEF_H_ + +/*----------------------------------------------------------------------------------------- +------ +------ Includes +------ +-----------------------------------------------------------------------------------------*/ +#include "sdoserv.h" + + +/*----------------------------------------------------------------------------------------- +------ +------ Defines and Types +------ +-----------------------------------------------------------------------------------------*/ + +/*--------------------------------------------- +- DataTypes +-----------------------------------------------*/ +#define DEFTYPE_NULL 0x0000 /**< \brief Null*/ +#define DEFTYPE_BOOLEAN 0x0001 /**< \brief BOOLEAN*/ +#define DEFTYPE_INTEGER8 0x0002 /**< \brief INTEGER8*/ +#define DEFTYPE_INTEGER16 0x0003 /**< \brief INTEGER16*/ +#define DEFTYPE_INTEGER32 0x0004 /**< \brief INTEGER32*/ +#define DEFTYPE_UNSIGNED8 0x0005 /**< \brief UNSIGNED8*/ +#define DEFTYPE_UNSIGNED16 0x0006 /**< \brief UNSIGNED16*/ +#define DEFTYPE_UNSIGNED32 0x0007 /**< \brief UNSIGNED32*/ +#define DEFTYPE_REAL32 0x0008 /**< \brief REAL32*/ +#define DEFTYPE_VISIBLESTRING 0x0009 /**< \brief VISIBLE_STRING*/ +#define DEFTYPE_OCTETSTRING 0x000A /**< \brief OCTET_STRING*/ +#define DEFTYPE_UNICODE_STRING 0x000B /**< \brief UNICODE_STRING*/ +#define DEFTYPE_TIME_OF_DAY 0x000C /**< \brief TIME_OF_DAY*/ +#define DEFTYPE_TIME_DIFFERENCE 0x000D /**< \brief TIME_DIFFERENCE*/ +#define DEFTYPE_INTEGER24 0x0010 /**< \brief INTEGER24*/ +#define DEFTYPE_REAL64 0x0011 /**< \brief REAL64*/ +#define DEFTYPE_INTEGER40 0x0012 /**< \brief INTEGER40*/ +#define DEFTYPE_INTEGER48 0x0013 /**< \brief INTEGER48*/ +#define DEFTYPE_INTEGER56 0x0014 /**< \brief INTEGER56*/ +#define DEFTYPE_INTEGER64 0x0015 /**< \brief INTEGER64*/ +#define DEFTYPE_UNSIGNED24 0x0016 /**< \brief UNSIGNED24*/ +#define DEFTYPE_UNSIGNED40 0x0018 /**< \brief UNSIGNED40*/ +#define DEFTYPE_UNSIGNED48 0x0019 /**< \brief UNSIGNED48*/ +#define DEFTYPE_UNSIGNED56 0x001A /**< \brief UNSIGNED56*/ +#define DEFTYPE_UNSIGNED64 0x001B /**< \brief UNSIGNED64*/ +#define DEFTYPE_GUID 0x001D /**< \brief DEFTYPE_GUID*/ +#define DEFTYPE_BYTE 0x001E /**< \brief DEFTYPE_BYTE*/ +#define DEFTYPE_WORD 0x001F /**< \brief DEFTYPE_WORD*/ +#define DEFTYPE_DWORD 0x0020 /**< \brief DEFTYPE_DWORD*/ +#define DEFTYPE_PDOMAPPING 0x0021 /**< \brief PDO_MAPPING*/ +#define DEFTYPE_IDENTITY 0x0023 /**< \brief IDENTITY*/ +#define DEFTYPE_COMMAND 0x0025 /**< \brief COMMAND_PAR*/ +#define DEFTYPE_PDOCOMPAR 0x0027 /**< \brief PDO_PARAMETER*/ +#define DEFTYPE_ENUM 0x0028 /**< \brief DEFTYPE_ENUM */ +#define DEFTYPE_SMPAR 0x0029 /**< \brief SM_SYNCHRONISATION*/ +#define DEFTYPE_RECORD 0x002A /**< \brief DEFTYPE_RECORD */ +#define DEFTYPE_BACKUP 0x002B /**< \brief BACKUP_PARAMETER*/ +#define DEFTYPE_MDP 0x002C /**< \brief MODULAR_DEVICE_PROFILE*/ +#define DEFTYPE_BITARR8 0x002D /**< \brief BITARR8*/ +#define DEFTYPE_BITARR16 0x002E /**< \brief BITARR16*/ +#define DEFTYPE_BITARR32 0x002F /**< \brief BITARR32*/ +#define DEFTYPE_BIT1 0x0030 /**< \brief BIT1*/ +#define DEFTYPE_BIT2 0x0031 /**< \brief BIT2*/ +#define DEFTYPE_BIT3 0x0032 /**< \brief BIT3*/ +#define DEFTYPE_BIT4 0x0033 /**< \brief BIT4*/ +#define DEFTYPE_BIT5 0x0034 /**< \brief BIT5*/ +#define DEFTYPE_BIT6 0x0035 /**< \brief BIT6*/ +#define DEFTYPE_BIT7 0x0036 /**< \brief BIT7*/ +#define DEFTYPE_BIT8 0x0037 /**< \brief BIT8*/ +#define DEFTYPE_ARRAY_OF_INT 0x0260 /**< \brief DEFTYPE_ARRAY_OF_INT*/ +#define DEFTYPE_ARRAY_OF_SINT 0x0261 /**< \brief DEFTYPE_ARRAY_OF_SINT*/ +#define DEFTYPE_ARRAY_OF_DINT 0x0262 /**< \brief DEFTYPE_ARRAY_OF_DINT*/ +#define DEFTYPE_ARRAY_OF_UDINT 0x0263 /**< \brief DEFTYPE_ARRAY_OF_UDINT*/ +#define DEFTYPE_ERRORHANDLING 0x0281 /**< \brief DEFTYPE_ERRORHANDLING*/ +#define DEFTYPE_DIAGHISTORY 0x0282 /**< \brief DEFTYPE_DIAGHISTORY*/ +#define DEFTYPE_SYNCSTATUS 0x0283 /**< \brief DEFTYPE_SYNCSTATUS*/ +#define DEFTYPE_SYNCSETTINGS 0x0284 /**< \brief DEFTYPE_SYNCSETTINGS*/ +#define DEFTYPE_FSOEFRAME 0x0285 /**< \brief DEFTYPE_FSOEFRAME*/ +#define DEFTYPE_FSOECOMMPAR 0x0286 /**< \brief DEFTYPE_FSOECOMMPAR*/ + + +/*--------------------------------------------- +- Flags for supported Synctypes (0x1C32.4 / 0x1C33.4) +-----------------------------------------------*/ + +/*ECATCHANGE_START(V5.11) COE2*/ +#define SYNCTYPE_FREERUNSUPP 0x0001 /**< \brief FreeRun supported*/ +/*ECATCHANGE_END(V5.11) COE2*/ +#define SYNCTYPE_SYNCHRONSUPP 0x0002 /**< \brief SyncManager synchron supported*/ +#define SYNCTYPE_DCSYNC0SUPP 0x0004 /**< \brief Sync0 synchron supported*/ +#define SYNCTYPE_DCSYNC1SUPP 0x0008 /**< \brief Sync1 synchron supported*/ +#define SYNCTYPE_SUBCYCLESUPP 0x0010 /**< \brief Subordinated Application with fixed Sync0 supported*/ +#define SYNCTYPE_LOCALSHIFTSUPP 0x0020 /**< \brief Event shift with local timer supported. Output shift : 0x1C32.4 ; Input shift : 0x1C33.4 (not supported by the default SSC)*/ +#define SYNCTYPE_SHIFTBYSYNC1SUPP 0x0040 /**< \brief Event shift with Sync1 event supported. Output shift : 0x1C32.4 ; Input shift : 0x1C33.4*/ +#define SYNCTYPE_MEASURE_DELAYSUPP 0x0200 /**< \brief Delay times should be measured (not supported by the default SSC, only the cycle time can be measured (1ms resolution)*/ +#define SYNCTYPE_FIXED_DELAYSUPP 0x0400 /**< \brief Delay times are fixed*/ +#define SYNCTYPE_TIMESVARIABLE 0x4000 /**< \brief Dynamic Cycle Times supported*/ + + +#define IS_PDO_ASSIGN(x) ((x >= 0x1C10) && (x <= 0x1C2F)) /**< \brief Marco to check if object index is SyncManager assign object*/ +#define IS_RX_PDO(x) (((x) >= 0x1600) && ((x) <= 0x17FF)) /**< \brief Marco to check if object index RxPDO mapping object*/ +#define IS_TX_PDO(x) (((x) >= 0x1A00) && ((x) <= 0x1BFF)) /**< \brief Marco to check if object index TxPDO mapping object*/ + + +/** + * \brief Object dictionary entry structure + */ +typedef struct OBJ_ENTRY +{ + struct OBJ_ENTRY *pPrev; /**< \brief Previous entry(object) in the object dictionary list*/ + struct OBJ_ENTRY *pNext; /**< \brief Next entry(object) in the object dictionary list*/ + + UINT16 Index; /**< \brief Object index*/ + TSDOINFOOBJDESC ObjDesc; /**< \brief Object access, type and code*/ + OBJCONST TSDOINFOENTRYDESC OBJMEM *pEntryDesc; /**< \brief pointer to object entry descriptions*/ + OBJCONST UCHAR OBJMEM *pName; /**< \brief Pointer to object and entry names*/ + void MBXMEM *pVarPtr; /**< \brief Pointer to object buffer*/ + UINT8 (* Read)( UINT16 Index, UINT8 Subindex, UINT32 Size, UINT16 MBXMEM * pData, UINT8 bCompleteAccess ); /**< \brief Function pointer to read function (if NULL default read function will be used)*/ + UINT8 (* Write)( UINT16 Index, UINT8 Subindex, UINT32 Size, UINT16 MBXMEM * pData, UINT8 bCompleteAccess ); /**< \brief Function pointer to write function (if NULL default write function will be used)*/ + UINT16 NonVolatileOffset; /**< \brief Offset within the non volatile memory (need to be defined for backup objects)*/ +} +TOBJECT; + + +/** + * Object 0x1C3x (SyncManager Parameter) data structure + */ +typedef struct OBJ_STRUCT_PACKED_START +{ + UINT16 subindex0;/**< \brief SubIndex0*/ + UINT16 u16SyncType; /**< \brief SunbIndex 001: Sync type*/ + UINT32 u32CycleTime;/**< \brief SunbIndex 002: Cycle time*/ + UINT32 u32ShiftTime;/**< \brief SunbIndex 003: Shift time (not supported, variable is only used as a place holder)*/ + UINT16 u16SyncTypesSupported;/**< \brief SunbIndex 004: Supported Sync types*/ + UINT32 u32MinCycleTime;/**< \brief SunbIndex 005: Min cycle time*/ + UINT32 u32CalcAndCopyTime;/**< \brief SunbIndex 006: Calc and Copy time*/ + UINT32 u32Si7Reserved;/**< \brief SunbIndex 007: 32Bit Reserved*/ + UINT16 u16GetCycleTime;/**< \brief SunbIndex 008: Get cycle time*/ + UINT32 u32DelayTime; /**< \brief SunbIndex 009: Delay time*/ + UINT32 u32Sync0CycleTime; /**< \brief SunbIndex 010: Sync0 cycle time*/ + UINT16 u16SmEventMissedCounter; /**< \brief SunbIndex 011: SyncManager event missed counter*/ + UINT16 u16CycleExceededCounter; /**< \brief SunbIndex 012: Cycle exceed counter*/ + UINT16 u16Si13Reserved; /**< \brief SunbIndex 013: Shift too short (not supported, only padding)*/ + UINT16 u16Si14Reserved; /**< \brief SubIndex14 not supported*/ + UINT32 u32Si15Reserved; /**< \brief SubIndex15 not supported*/ + UINT32 u32Si16Reserved; /**< \brief SubIndex16 not supported*/ + UINT32 u32Si17Reserved; /**< \brief SubIndex17 not supported*/ + UINT32 u32Si18Reserved; /**< \brief SubIndex18 not supported*/ + UINT8 u8SyncError; /**< \brief Sync Error*/ +}OBJ_STRUCT_PACKED_END +TSYNCMANPAR; + +/** + * Cycle diagnosis + */ +typedef struct OBJ_STRUCT_PACKED_START +{ + UINT16 syncFailedCounter; /**< \brief Sync Failed Counter*/ +}OBJ_STRUCT_PACKED_END +TCYCLEDIAG; + + +/** + * Object 0x10F1 (Error Settings Object) data structure + */ +typedef struct OBJ_STRUCT_PACKED_START { + UINT16 u16SubIndex0; /**< \brief SubIndex0*/ + UINT32 u32LocalErrorReaction; /**< \brief Local error reaction*/ +/*ECATCHANGE_START(V5.11) COE3*/ + UINT16 u16SyncErrorCounterLimit; /**< \brief Sync error counter limit*/ +/*ECATCHANGE_END(V5.11) COE3*/ +} OBJ_STRUCT_PACKED_END +TOBJ10F1; + +#endif //_OBJDEF_H_ + + +/*----------------------------------------------------------------------------------------- +------ +------ Global variables +------ +-----------------------------------------------------------------------------------------*/ +/* ECATCHANGE_START(V5.11) ECAT10*/ +#if defined(_OBJDEF_) && (_OBJDEF_ == 1) +/* ECATCHANGE_END(V5.11) ECAT10*/ + #define PROTO +#else + #define PROTO extern +#endif + +/** + * Indicates if the synchronisation was written by the user on startup (by writing 0x1C3x.1). + * If it was not written the sync mode will be set based on the DC registers 0x980:0x981 + */ +PROTO BOOL bSyncSetByUser; + +/** + * \brief Cycle diagnosis variable + */ +PROTO TCYCLEDIAG sCycleDiag; + + +/** + * \brief Object 0x1C32 (SyncManager 2 Parameter) object variable + */ +PROTO TSYNCMANPAR MBXMEM sSyncManOutPar; + + +/** + * \brief Object 0x1C33 (SyncManager 3 Parameter) object variable + */ +PROTO TSYNCMANPAR MBXMEM sSyncManInPar; + + +/** + * \brief Object 0x10F1 (Error Settings) object variable + * + * SubIndex0 : 2 + * Local error reaction : 1 + * Sync error counter limit : MAX_SM_EVENT_MISSED + */ +PROTO TOBJ10F1 sErrorSettings +/* ECATCHANGE_START(V5.11) ECAT10*/ +#if defined(_OBJDEF_) && (_OBJDEF_ == 1) +/* ECATCHANGE_END(V5.11) ECAT10*/ += {2, 0x01, MAX_SM_EVENT_MISSED} +#endif +; + + +/** + * \brief Default entry name "SubIndex 000" + */ +PROTO CHAR OBJMEM aSubindexDesc[13] +/* ECATCHANGE_START(V5.11) ECAT10*/ +#if defined(_OBJDEF_) && (_OBJDEF_ == 1) +/* ECATCHANGE_END(V5.11) ECAT10*/ + = { "SubIndex 000" } +#endif +; + + +/*----------------------------------------------------------------------------------------- +------ +------ Global functions +------ +-----------------------------------------------------------------------------------------*/ + +PROTO OBJCONST TOBJECT OBJMEM * OBJ_GetObjectHandle( UINT16 index ); +PROTO UINT32 OBJ_GetObjectLength( UINT16 index, UINT8 subindex, OBJCONST TOBJECT OBJMEM * pObjEntry, UINT8 bCompleteAccess); +PROTO UINT16 OBJ_GetNoOfObjects(UINT8 listType); +PROTO UINT16 OBJ_GetObjectList(UINT16 listType, UINT16 *pIndex, UINT16 size, UINT16 MBXMEM *pData,UINT8 *pAbort); +PROTO UINT16 OBJ_GetDesc( UINT16 index, UINT8 subindex, OBJCONST TOBJECT OBJMEM * pObjEntry, UINT16 MBXMEM * pData ); +PROTO OBJCONST TSDOINFOENTRYDESC OBJMEM * OBJ_GetEntryDesc(OBJCONST TOBJECT OBJMEM * pObjEntry, UINT8 Subindex); +PROTO OBJCONST TSDOINFOOBJDESC OBJMEM * OBJ_GetObjDesc(OBJCONST TOBJECT OBJMEM * pObjEntry); +PROTO UINT16 OBJ_GetEntryOffset(UINT8 subindex, OBJCONST TOBJECT OBJMEM * pObjEntry); +PROTO UINT8 CheckSyncTypeValue(UINT16 index, UINT16 NewSyncType); +PROTO UINT8 OBJ_Read(UINT16 index, UINT8 subindex, UINT32 objSize, OBJCONST TOBJECT OBJMEM * pObjEntry, UINT16 MBXMEM * pData, UINT8 bCompleteAccess); +PROTO UINT8 OBJ_Write(UINT16 index, UINT8 subindex, UINT32 dataSize, OBJCONST TOBJECT OBJMEM * pObjEntry, UINT16 MBXMEM * pData, UINT8 bCompleteAccess); +PROTO void COE_WriteBackupEntry(UINT8 subindex, OBJCONST TOBJECT OBJMEM * pObjEntry); + +#undef PROTO +/** @}*/ diff --git a/GD32E508_DCU_APP/comm/ssc/inc/sdoserv.h b/GD32E508_DCU_APP/comm/ssc/inc/sdoserv.h new file mode 100644 index 0000000..126c510 --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/inc/sdoserv.h @@ -0,0 +1,641 @@ +/** + * \addtogroup CoE CAN Application Profile over EtherCAT + * @{ + */ + + +/** +\file sdoserv.h +\author EthercatSSC@beckhoff.com +\brief SDO Service definitions + +\version 5.11 + +
Changes to version V5.10:
+V5.11 ECAT10: change PROTO handling to prevent compiler errors
+V5.11 SDO10: add new SDO abort code 0x6010004 (complete access not supported)
+
Changes to version V5.01:
+V5.10 SDO3: Add new SDO Abort Code (0x06090033)
+
Changes to version - :
+V5.01 : Start file change log + */ + +#ifndef _SDOSERV_H_ + +#define _SDOSERV_H_ + +/*----------------------------------------------------------------------------------------- +------ +------ Includes +------ +-----------------------------------------------------------------------------------------*/ +#include "ecatcoe.h" + + +/*----------------------------------------------------------------------------------------- +------ +------ Defines and Types +------ +-----------------------------------------------------------------------------------------*/ + +/** + * \addtogroup SdoErrorCodes SDO Error Codes + * @{ + */ +#define ERROR_SDOINVALIDCOMMAND 0x1101 /**< \brief Invalid SDO command*/ +#define ERROR_SDOINVALIDHEADER 0x1102 /**< \brief Invalid SDO header*/ +#define ERROR_SDONOTSUPPORTED 0x1103 /**< \brief SDO service not supported*/ +/** @}*/ + + +/** + * \addtogroup SdoHeaderData SDO Header and Data + * @{ + */ +#define SDOHEADER_SIZEINDICATOR ((UINT8) 0x01) /**< \brief Size Indicator size of data in Data Set Size specified*/ +#define SDOHEADER_TRANSFERTYPE ((UINT8) 0x02) /**< \brief Transfer Type
1: Expedited
0: Normal*/ +#define SDOHEADER_DATASETSIZE ((UINT8) 0x0C) /**< \brief Data Set Size (used for Expedited transfer)*/ +#define SDOHEADER_COMPLETEACCESS ((UINT8) 0x10) /**< \brief Complete Access indication
0: Only specified entry will be transferred
1: Whole object (with or without Subindex0) will be transferred)*/ +#define SDOHEADER_COMMAND ((UINT8) 0xE0) /**< \brief SDO Command*/ +#define SDOHEADERSHIFT_SIZEINDICATOR ((UINT8) 0) /**< \brief Shift for Size Indicator*/ +#define SDOHEADERSHIFT_TRANSFERTYPE ((UINT8) 1) /**< \brief Shift for Transfer Type*/ +#define SDOHEADERSHIFT_DATASETSIZE ((UINT8) 2) /**< \brief Shift for Data Set Size*/ +#define SDOHEADERSHIFT_INDEXACCESS ((UINT8) 4) /**< \brief Shift for Complete Access*/ +#define SDOHEADERSHIFT_COMMAND ((UINT8) 5) /**< \brief Shift for SDO Command*/ +/** @}*/ + + +/** + * \addtogroup SdoServices SDO Services + * @{ + */ +#define SDOSERVICE_INITIATEDOWNLOADREQ ((UINT8) (0x01 << (SDOHEADERSHIFT_COMMAND))) /**< \brief SDO download request*/ +#define SDOSERVICE_INITIATEDOWNLOADRES ((UINT8) (0x03 << (SDOHEADERSHIFT_COMMAND))) /**< \brief SDO download response*/ +#define SDOSERVICE_DOWNLOADSEGMENTREQ ((UINT8) (0x00 << (SDOHEADERSHIFT_COMMAND))) /**< \brief SDO segmented download request*/ +#define SDOSERVICE_DOWNLOADSEGMENTRES ((UINT8) (0x01 << (SDOHEADERSHIFT_COMMAND))) /**< \brief SDO segmented download response*/ +#define SDOSERVICE_INITIATEUPLOADREQ ((UINT8) (0x02 << (SDOHEADERSHIFT_COMMAND))) /**< \brief SDO upload request*/ +#define SDOSERVICE_INITIATEUPLOADRES ((UINT8) (0x02 << (SDOHEADERSHIFT_COMMAND))) /**< \brief SDO upload response*/ +#define SDOSERVICE_UPLOADSEGMENTREQ ((UINT8) (0x03 << (SDOHEADERSHIFT_COMMAND))) /**< \brief SDO segmented upload request*/ +#define SDOSERVICE_UPLOADSEGMENTRES ((UINT8) (0x00 << (SDOHEADERSHIFT_COMMAND))) /**< \brief SDO segmented upload response*/ +#define SDOSERVICE_ABORTTRANSFER ((UINT8) (((UINT8) 0x04) << (SDOHEADERSHIFT_COMMAND))) /**< \brief SDO abort*/ +/** @}*/ + + +/** + * \addtogroup SdoHeaderData SDO Header and Data + * @{ + */ +/** + * Structure to handle the basic SDO header + */ +typedef struct MBX_STRUCT_PACKED_START +{ + UINT16 Sdo[2]; /**< \brief 32Bit SDO header buffer*/ + #define SDOHEADER_COMMANDOFFSET 0 /**< \brief Memory offset for the command*/ + #define SDOHEADER_INDEXLOOFFSET 0 /**< \brief Memory offset for the low Byte of the object index*/ + #define SDOHEADER_INDEXHIOFFSET 1 /**< \brief Memory offset for the high Byte of the object index*/ + #define SDOHEADER_SUBINDEXOFFSET 1 /**< \brief Memory offset for subindex*/ + #define SDOHEADER_COMMANDMASK 0xFF /**< \brief Mask to get the command Byte*/ + #define SDOHEADER_INDEXLOSHIFT 8 /**< \brief Shift to get the low Byte of the object index*/ + #define SDOHEADER_INDEXHIMASK 0xFF /**< \brief Mask to get the high byte of the object index*/ + #define SDOHEADER_SUBINDEXSHIFT 8 /**< \brief Shift to get the subindex*/ +}MBX_STRUCT_PACKED_END +TINITSDOHEADER; + + +#define INITSDO_HEADER_SIZE SIZEOF(TINITSDOHEADER) /**< \brief Size of the SDO header*/ + + +/** + *Complete SDO datagram including mailbox header, CoE header and SDO header + */ +typedef struct MBX_STRUCT_PACKED_START +{ + TMBXHEADER MbxHeader; /**< \brief Mailbox header*/ + TCOEHEADER CoeHeader; /**< \brief CoE header*/ + TINITSDOHEADER SdoHeader; /**< \brief SDO header*/ +}MBX_STRUCT_PACKED_END +TINITSDOMBX; + + +#define MAX_EXPEDITED_DATA 4 /**< \brief Maximum number of Bytes handled via an expedited transfer*/ +#define MIN_SEGMENTED_DATA ((UINT16) 7) /**< \brief Minimum number of Bytes required for a segmented transfer*/ +#define EXPEDITED_FRAME_SIZE ( COE_HEADER_SIZE + INITSDO_HEADER_SIZE + (MAX_EXPEDITED_DATA) ) /**< \brief Size of an expedited mailbox datagram*/ +#define DOWNLOAD_NORM_REQ_SIZE ( COE_HEADER_SIZE + INITSDO_HEADER_SIZE + 4 ) /**< \brief Size of an normal download request*/ +/* HBu 06.02.06: names of defines changed */ +/* HBu 21.03.06: the SDO_Download-Response has to have always 8 bytes */ +#define DOWNLOAD_NORM_RES_SIZE ( COE_HEADER_SIZE + INITSDO_HEADER_SIZE + 4 ) /**< \brief Frame size of an normal download response*/ +#define UPLOAD_NORM_RES_SIZE ( COE_HEADER_SIZE + INITSDO_HEADER_SIZE + 4 ) /**< \brief Frame size of an normal upload response*/ +#define SEGMENT_NORM_HEADER_SIZE ( COE_HEADER_SIZE + 1 ) /**< \brief Minimum SDO header size*/ +#define SEGMENT_NORM_RES_SIZE ( (SEGMENT_NORM_HEADER_SIZE) + (MIN_SEGMENTED_DATA) ) /**< \brief Minimum SDO size*/ +#define SEGMENT_MBX_SIZE ( MBX_HEADER_SIZE + (SEGMENT_NORM_HEADER_SIZE) )/**< \brief Minimum mailbox datagram*/ + + +/** + * Segmented SDO header including data + */ +typedef struct MBX_STRUCT_PACKED_START +{ + UINT16 SegHeader; /**< \brief 8Bit header buffer and first 8Bit of the Data*/ +#define SEGHEADER_MASK ((UINT16) 0xFF) /**< \brief SDO header mask*/ +#define SEGHEADER_NOMOREFOLLOWS ((UINT16) 0x01) /**< \brief More segments follow flag*/ +#define SEGHEADER_SEGDATASIZE ((UINT16) 0x0E) /**< \brief Segmented data size*/ +#define SEGHEADER_TOGGLE ((UINT16) 0x10) /**< \brief Toogle flag*/ +#define SEGHEADER_COMMAND ((UINT16) 0xE0) /**< \brief Command identifier*/ +#define SEGHEADERSHIFT_SEGDATASIZE ((UINT16) 1) /**< \brief Shift for Segmented data size*/ +#define SEGHEADERSHIFT_TOGGLE ((UINT16) 4) /**< \brief Shift for toggle flag*/ +#define SEGHEADERSHIFT_COMMAND ((UINT16) 5) /**< \brief Shift for command identifier*/ +#define SEGHDATA_MASK ((UINT16) 0xFF00) /**< \brief Data mask*/ +#define SEGDATASHIFT ((UINT16) 8) /**< \brief Shift for first data Byte*/ + UINT16 Data[(((MAX_MBX_DATA_SIZE)-(SEGMENT_NORM_HEADER_SIZE) - 1 ) >> 1)]; /**< \brief Data buffer (second and following Bytes, the first byte is located in the header variable)*/ +}MBX_STRUCT_PACKED_END +TSDOSEGHEADERDATA; + + +/** + *Expedited download request + */ +typedef struct MBX_STRUCT_PACKED_START +{ + TMBXHEADER MbxHeader; /**< \brief Mailbox header*/ + TCOEHEADER CoeHeader; /**< \brief CoE header*/ + TINITSDOHEADER SdoHeader; /**< \brief SDO header*/ + UINT16 Data[(MAX_EXPEDITED_DATA) >> 1]; /**< \brief Data buffer*/ +}MBX_STRUCT_PACKED_END +TINITSDODOWNLOADEXPREQMBX; + +/** + *Normal download request + */ +typedef struct MBX_STRUCT_PACKED_START +{ + TMBXHEADER MbxHeader; /**< \brief Mailbox header*/ + TCOEHEADER CoeHeader; /**< \brief CoE header*/ + TINITSDOHEADER SdoHeader; /**< \brief SDO header*/ + UINT16 CompleteSize[2]; /**< \brief Complete object size*/ + UINT16 Data[(((MAX_MBX_DATA_SIZE)-(DOWNLOAD_NORM_REQ_SIZE)) >> 1)]; /**< \brief Data buffer*/ +}MBX_STRUCT_PACKED_END +TINITSDODOWNLOADNORMREQMBX; + + +/** + *Expedited and normal download response: + */ +typedef struct MBX_STRUCT_PACKED_START +{ + TMBXHEADER MbxHeader; /**< \brief Mailbox header*/ + TCOEHEADER CoeHeader; /**< \brief CoE header*/ + TINITSDOHEADER SdoHeader; /**< \brief SDO header*/ +}MBX_STRUCT_PACKED_END +TINITSDODOWNLOADRESMBX; + + +/** + *Segmented download request + */ +typedef struct MBX_STRUCT_PACKED_START +{ + TMBXHEADER MbxHeader; /**< \brief Mailbox header*/ + TCOEHEADER CoeHeader; /**< \brief CoE header*/ + TSDOSEGHEADERDATA SdoHeader; /**< \brief SDO header (data is included in header)*/ +}MBX_STRUCT_PACKED_END +TDOWNLOADSDOSEGREQMBX; + + +/** + *Segmented download response + */ +typedef struct MBX_STRUCT_PACKED_START +{ + TMBXHEADER MbxHeader; /**< \brief Mailbox header*/ + TCOEHEADER CoeHeader; /**< \brief CoE header*/ + UINT8 SegHeader; /**< \brief SDO header*/ +}MBX_STRUCT_PACKED_END +TDOWNLOADSDOSEGRESMBX; + + +/** + *Expedited and normal upload request + */ +typedef struct MBX_STRUCT_PACKED_START +{ + TMBXHEADER MbxHeader; /**< \brief Mailbox header*/ + TCOEHEADER CoeHeader; /**< \brief CoE header*/ + TINITSDOHEADER SdoHeader; /**< \brief SDO header*/ +}MBX_STRUCT_PACKED_END +TINITSDOUPLOADREQMBX; + + +/** + *Expedited upload response + */ +typedef struct MBX_STRUCT_PACKED_START +{ + TMBXHEADER MbxHeader; /**< \brief Mailbox header*/ + TCOEHEADER CoeHeader; /**< \brief CoE header*/ + TINITSDOHEADER SdoHeader; /**< \brief SDO header*/ + UINT16 Data[((MAX_EXPEDITED_DATA) >> 1)]; /**< \brief Data buffer*/ +}MBX_STRUCT_PACKED_END +TINITSDOUPLOADEXPRESMBX; + + +/** + *Normal upload response + */ +typedef struct MBX_STRUCT_PACKED_START +{ + TMBXHEADER MbxHeader; /**< \brief Mailbox header*/ + TCOEHEADER CoeHeader; /**< \brief CoE header*/ + TINITSDOHEADER SdoHeader; /**< \brief SDO header*/ + UINT16 CompleteSize[2]; /**< \brief Complete object size*/ + UINT16 Data[(((MAX_MBX_DATA_SIZE)-(UPLOAD_NORM_RES_SIZE)) >> 1)]; /**< \brief Data buffer*/ +}MBX_STRUCT_PACKED_END +TINITSDOUPLOADNORMRESMBX; + + +/** + *Segmented upload request + */ +typedef struct MBX_STRUCT_PACKED_START +{ + TMBXHEADER MbxHeader; /**< \brief Mailbox header*/ + TCOEHEADER CoeHeader; /**< \brief CoE header*/ + UINT8 SegHeader; /**< \brief SDO header*/ +}MBX_STRUCT_PACKED_END +TUPLOADSDOSEGREQMBX; + +/** + *Segmented upload response + */ +typedef struct MBX_STRUCT_PACKED_START +{ + TMBXHEADER MbxHeader; /**< \brief Mailbox header*/ + TCOEHEADER CoeHeader; /**< \brief CoE header*/ + TSDOSEGHEADERDATA SdoHeader; /**< \brief SDO header (data is included in header)*/ +}MBX_STRUCT_PACKED_END +TUPLOADSDOSEGRESMBX; + + +/** + *Abort request + */ +typedef struct MBX_STRUCT_PACKED_START +{ + TMBXHEADER MbxHeader; /**< \brief Mailbox header*/ + TCOEHEADER CoeHeader; /**< \brief CoE header*/ + TINITSDOHEADER SdoHeader; /**< \brief SDO header*/ + UINT32 AbortCode; /**< \brief SDO abort code*/ +}MBX_STRUCT_PACKED_END +TABORTSDOTRANSFERREQMBX; + + +#define ABORT_NORM_RES_SIZE (SIZEOF(TABORTSDOTRANSFERREQMBX) - SIZEOF(TMBXHEADER))/**< \brief Size of the SDO Abort datagram (without the mailbox header)*/ +/** @}*/ + + +/** + * \addtogroup SDOAbort SDO Abort Codes + * @{ + */ +#define ABORT_NOERROR 0x00000000 /**< \brief No SDO error*/ +#define ABORT_TOGGLE_BIT_NOT_CHANGED 0x05030000 /**< \brief Toggle bit not changed*/ +#define ABORT_SDO_PROTOCOL_TIMEOUT 0x05040000 /**< \brief SDO timeout*/ +#define ABORT_COMMAND_SPECIFIER_UNKNOWN 0x05040001 /**< \brief Command specifier unknown*/ +#define ABORT_OUT_OF_MEMORY 0x05040005 /**< \brief Out of memory*/ +#define ABORT_UNSUPPORTED_ACCESS 0x06010000 /**< \brief Unsupported Access*/ +#define ABORT_WRITE_ONLY_ENTRY 0x06010001 /**< \brief Write only entry*/ +#define ABORT_READ_ONLY_ENTRY 0x06010002 /**< \brief Read only entry*/ +#define ABORT_ENTRY_CANT_BE_WRITTEN_SI0_NOT_0 0x06010003 /**< \brief Entry can not be written because Subindex0 is not 0*/ +/* ECATCHANGE_START(V5.11) SDO10*/ +#define ABORT_COMPLETE_ACCESS_NOT_SUPPORTED 0x06010004 /**< \brief The object can not be accessed via complete access*/ +/* ECATCHANGE_END(V5.11) SDO10*/ +#define ABORT_OBJECT_NOT_EXISTING 0x06020000 /**< \brief Object not existing*/ +#define ABORT_OBJECT_CANT_BE_PDOMAPPED 0x06040041 /**< \brief Object can not be mapped to PDO*/ +#define ABORT_MAPPED_OBJECTS_EXCEED_PDO 0x06040042 /**< \brief Mapped Object exceeds PDO*/ +#define ABORT_PARAM_IS_INCOMPATIBLE 0x06040043 /**< \brief Parameter is incompatible*/ +#define ABORT_INTERNAL_DEVICE_INCOMPATIBILITY 0x06040047 /**< \brief Device incompatibility*/ +#define ABORT_HARDWARE_ERROR 0x06060000 /**< \brief Hardware error*/ +#define ABORT_PARAM_LENGTH_ERROR 0x06070010 /**< \brief Parameter length error*/ +#define ABORT_PARAM_LENGTH_TOO_LONG 0x06070012 /**< \brief Parameter is too long*/ +#define ABORT_PARAM_LENGTH_TOO_SHORT 0x06070013 /**< \brief Parameter is too short*/ +#define ABORT_SUBINDEX_NOT_EXISTING 0x06090011 /**< \brief Subindex (Entry) not exists*/ +#define ABORT_VALUE_EXCEEDED 0x06090030 /**< \brief Value exceeds*/ +#define ABORT_VALUE_TOO_GREAT 0x06090031 /**< \brief Value is too great*/ +#define ABORT_VALUE_TOO_SMALL 0x06090032 /**< \brief Value is too small*/ +#define ABORT_MODULE_ID_LIST_NOT_MATCH 0x06090033 /**< \brief Detected Module Ident List (0xF030) and Configured Module Ident list (0xF050) does not match*/ +#define ABORT_MAX_VALUE_IS_LESS_THAN_MIN_VALUE 0x06090036 /**< \brief Value is less than minimum value*/ +#define ABORT_GENERAL_ERROR 0x08000000 /**< \brief General error*/ +#define ABORT_DATA_CANNOT_BE_READ_OR_STORED 0x08000020 /**< \brief Data can not be read or written*/ +#define ABORT_DATA_CANNOT_BE_READ_OR_STORED_BECAUSE_OF_LOCAL_CONTROL 0x08000021 /**< \brief Data can not be accessed because of local control*/ +#define ABORT_DATA_CANNOT_BE_READ_OR_STORED_IN_THIS_STATE 0x08000022 /**< \brief Data can not be read or written in the current state*/ +#define ABORT_NO_OBJECT_DICTIONARY_IS_PRESENT 0x08000023 /**< \brief Object is not in the object dictionary*/ + + +/** + * \addtogroup SDOAbortIndex SDO Abort Codes Identifier + * @{ + * Internal the SDO abort codes are handled in an array. + */ +#define ABORTIDX_TOGGLE_BIT_NOT_CHANGED 0x01 /**< \brief Index of "Toggle bit not changed"*/ +#define ABORTIDX_SDO_PROTOCOL_TIMEOUT 0x02 /**< \brief Index of "SDO timeout"*/ +#define ABORTIDX_COMMAND_SPECIFIER_UNKNOWN 0x03 /**< \brief Index of "Command specifier unknown"*/ +#define ABORTIDX_OUT_OF_MEMORY 0x04 /**< \brief Index of "Out of memory"*/ +#define ABORTIDX_UNSUPPORTED_ACCESS 0x05 /**< \brief Index of "Unsupported Access"*/ +#define ABORTIDX_WRITE_ONLY_ENTRY 0x06 /**< \brief Index of "Write only entry"*/ +#define ABORTIDX_READ_ONLY_ENTRY 0x07 /**< \brief Index of "Read only entry"*/ +#define ABORTIDX_OBJECT_NOT_EXISTING 0x08 /**< \brief Index of "Object not existing"*/ +#define ABORTIDX_OBJECT_CANT_BE_PDOMAPPED 0x09 /**< \brief Index of "Object can not be mapped to PDO"*/ +#define ABORTIDX_MAPPED_OBJECTS_EXCEED_PDO 0x0A /**< \brief Index of "Mapped Object exceeds PDO"*/ +#define ABORTIDX_PARAM_IS_INCOMPATIBLE 0x0B /**< \brief Index of "Parameter is incompatible"*/ +#define ABORTIDX_INTERNAL_DEVICE_INCOMPATIBILITY 0x0C /**< \brief Index of "Device incompatibility"*/ +#define ABORTIDX_HARDWARE_ERROR 0x0D /**< \brief Index of "Hardware error"*/ +#define ABORTIDX_PARAM_LENGTH_ERROR 0x0E /**< \brief Index of "Parameter length error"*/ +#define ABORTIDX_PARAM_LENGTH_TOO_LONG 0x0F /**< \brief Index of "Parameter is too long"*/ +#define ABORTIDX_PARAM_LENGTH_TOO_SHORT 0x10 /**< \brief Index of "Parameter is too short"*/ +#define ABORTIDX_SUBINDEX_NOT_EXISTING 0x11 /**< \brief Index of "Subindex (Entry) not exists"*/ +#define ABORTIDX_VALUE_EXCEEDED 0x12 /**< \brief Index of "Value exceeds"*/ +#define ABORTIDX_VALUE_TOO_GREAT 0x13 /**< \brief Index of "Value is too great"*/ +#define ABORTIDX_VALUE_TOO_SMALL 0x14 /**< \brief Index of "Value is too small"*/ +#define ABORTIDX_MODULE_ID_LIST_NOT_MATCH 0x15 /**< \brief Index of "Unequal Module Id list"*/ +#define ABORTIDX_MAX_VALUE_IS_LESS_THAN_MIN_VALUE 0x16 /**< \brief Index of "Value is less than minimum value"*/ +#define ABORTIDX_GENERAL_ERROR 0x17 /**< \brief Index of "General error"*/ +#define ABORTIDX_DATA_CANNOT_BE_READ_OR_STORED 0x18 /**< \brief Index of "Data can not be read or written"*/ +#define ABORTIDX_DATA_CANNOT_BE_ACCESSED_BECAUSE_OF_LOCAL_CONTROL 0x19 /**< \brief Index of "Data can not be accessed because of local control"*/ +#define ABORTIDX_IN_THIS_STATE_DATA_CANNOT_BE_READ_OR_STORED 0x1A /**< \brief Index of "Data can not be read or written in the current state"*/ +#define ABORTIDX_NO_OBJECT_DICTIONARY_IS_PRESENT 0x1B /**< \brief Index of "Object is not in the object dictionary"*/ +#define ABORTIDX_ENTRY_CANT_BE_WRITTEN_SI0_NOT_0 0x1C /**< \brief Index of "Entry can not be written because Subindex0 is not 0"*/ +/* ECATCHANGE_START(V5.11) SDO10*/ +#define ABORTIDX_COMPLETE_ACCESS_NOT_SUPPORTED 0x1D /**< \brief The object can not be accessed via complete access*/ +/* ECATCHANGE_END(V5.11) SDO10*/ +#define ABORTIDX_WORKING 0xFF /**< \brief Index of application is handling the SDO request*/ +/** @}*/ +/** @}*/ + + +/** + *\addtogroup SdoServices SDO Services + * @{ + */ +#define SDOINFOSERVICE_OBJDICTIONARYLIST_Q 0x01 /**< \brief SDO Info Get object list request*/ +#define SDOINFOSERVICE_OBJDICTIONARYLIST_S 0x02 /**< \brief SDO Info Get object list response*/ +#define SDOINFOSERVICE_OBJDESCRIPTION_Q 0x03 /**< \brief SDO Info Get object description request*/ +#define SDOINFOSERVICE_OBJDESCRIPTION_S 0x04 /**< \brief SDO Info Get object description response*/ +#define SDOINFOSERVICE_ENTRYDESCRIPTION_Q 0x05 /**< \brief SDO Info Get entry description request*/ +#define SDOINFOSERVICE_ENTRYDESCRIPTION_S 0x06 /**< \brief SDO Info Get entry description request*/ +#define SDOINFOSERVICE_ERROR_Q 0x07 /**< \brief SDO Info Error*/ +#define SDOINFOSERVICE_INCOMPLETE 0x80 /**< \brief SDO Info Incomplete flag*/ +/** @}*/ + + +/** + *\addtogroup SdoHeaderData SDO Header and Data + * @{ + */ +/** + * SDO Info Object list + */ +typedef struct MBX_STRUCT_PACKED_START +{ + UINT16 ListType; /**< \brief List type variable*/ + #define INFO_LIST_TYPE_LENGTH 0 /**< \brief Length of all list types*/ + #define INFO_LIST_TYPE_ALL 1 /**< \brief All objects*/ + #define INFO_LIST_TYPE_RXPDO 2 /**< \brief Only RxPDO mappable objects*/ + #define INFO_LIST_TYPE_TXPDO 3 /**< \brief Only TxPDO mappable objects*/ + #define INFO_LIST_TYPE_BACKUP 4 /**< \brief Only backup objects*/ + #define INFO_LIST_TYPE_SET 5 /**< \brief Only setting objects*/ + #define INFO_LIST_TYPE_MAX 5 /**< \brief Max list identifier*/ +}MBX_STRUCT_PACKED_END +TSDOINFOLIST; + + +/** + * Object description + */ +typedef struct MBX_STRUCT_PACKED_START +{ + UINT16 DataType; /**< \brief Object data type*/ + UINT16 ObjFlags; /**< \brief Object flags (including object code and max subindex)*/ + + /*Object Code and MaxSubindex will be set manually in the object dictionary. The value is always created in little endian format*/ + #define OBJFLAGS_MAXSUBINDEXMASK 0x00FF /**< \brief Max subindex mask*/ + #define OBJFLAGS_MAXSUBINDEXSHIFT 0 /**< \brief Max subindex shift*/ + #define OBJFLAGS_OBJCODEMASK 0x0F00 /**< \brief Mask Object code mask*/ + #define OBJFLAGS_OBJCODESHIFT 8 /**< \brief Object code shift*/ + + #define OBJCODE_VAR 0x07 /**< \brief Object code VARIABLE*/ + #define OBJCODE_ARR 0x08 /**< \brief Object code ARRAY*/ + #define OBJCODE_REC 0x09 /**< \brief Object code RECORD*/ + +// char Name[]; // rest of mailbox data +}MBX_STRUCT_PACKED_END +TSDOINFOOBJDESC; + + +#define SDO_INFO_OBJ_DESC_SIZE SIZEOF(TSDOINFOOBJDESC) /**< \brief Object description size*/ + + +/** + * SDO Info object description service data + */ +typedef struct MBX_STRUCT_PACKED_START +{ + UINT16 Index; /**< \brief Object index*/ + TSDOINFOOBJDESC Res; /**< \brief Description data*/ +}MBX_STRUCT_PACKED_END +TSDOINFOOBJ; + + +#define SDO_INFO_OBJ_DESC_RES_SIZE SIZEOF(TSDOINFOOBJ)/**< \brief Size of the object description service data*/ + + +/** + * SDO Info entry description + */ +typedef struct MBX_STRUCT_PACKED_START +{ + UINT16 DataType; /**< \brief Entry data type*/ + UINT16 BitLength; /**< \brief Entry bit size*/ + UINT16 ObjAccess; /**< \brief Entry access rights*/ + /**< + Bit 0: Read Access in Pre-Op +
Bit 1: Read Access in Safe-Op +
Bit 2: Read Access in Op +
Bit 3: Write Access in Pre-Op +
Bit 4: Write Access in Safe-Op +
Bit 5: Write Access in Op +
Bit 6: mappable in RxPDO +
Bit 7: mappable in TxPDO +
Bit 8: entry will be included in backup +
Bit 9: entry will be included in settings*/ + + #define ACCESS_READWRITE 0x003F /**< \brief Read/write in all states*/ + #define ACCESS_READ 0x0007 /**< \brief Read only in all states*/ + #define ACCESS_READ_PREOP 0x0001 /**< \brief Read only in PreOP*/ + #define ACCESS_READ_SAFEOP 0x0002 /**< \brief Read only in SafeOP*/ + #define ACCESS_READ_OP 0x0004 /**< \brief Read only in OP*/ + #define ACCESS_WRITE 0x0038 /**< \brief Write only in all states*/ + #define ACCESS_WRITE_PREOP 0x0008 /**< \brief Write only in PreOP*/ + #define ACCESS_WRITE_SAFEOP 0x0010 /**< \brief Write only in SafeOP*/ + #define ACCESS_WRITE_OP 0x0020 /**< \brief Write only in OP*/ + #define OBJACCESS_NOPDOMAPPING 0x0000 /**< \brief Not PDO mappable*/ + #define OBJACCESS_RXPDOMAPPING 0x0040 /**< \brief Mappable in RxPDOs*/ + #define OBJACCESS_TXPDOMAPPING 0x0080 /**< \brief Mappable in TxPDOs*/ + #define OBJACCESS_BACKUP 0x0100 /**< \brief Backup entry*/ + #define OBJACCESS_SETTINGS 0x0200 /**< \brief Setting Entry*/ + #define OBJACCESS_SAFEINPUTS 0x0400 /**< \brief Safe input*/ + #define OBJACCESS_SAFEOUTPUTS 0x0800 /**< \brief Safe output*/ + #define OBJACCESS_SAFEPARAMETER 0x1000 /**< \brief Safe parameter*/ +}MBX_STRUCT_PACKED_END +TSDOINFOENTRYDESC; + + +/** + * SDO Info entry description service data + */ +typedef struct MBX_STRUCT_PACKED_START +{ + UINT16 Index; /**< \brief Index of the parent object*/ + UINT16 Info; /**< \brief Info data including the subindex and identifier for the payload data*/ + #define ENTRY_MASK_SUBINDEX 0x00FF /**< \brief Subindex mask*/ + #define ENTRY_SUBINDEX_SHIFT 0 /**< \brief Subindex shift*/ + #define ENTRY_MASK_VALUEINFO 0xFF00 /**< \brief Value info mask*/ + #define ENTRY_VALUEINFO_SHIFT 8 /**< \brief Value info shift*/ + + TSDOINFOENTRYDESC Res; /**< \brief Entry description data*/ +}MBX_STRUCT_PACKED_END +TSDOINFOENTRY; + + +/** + * SDO info Error + */ +typedef struct MBX_STRUCT_PACKED_START +{ + UINT32 ErrorCode; /**< \brief SDO Info error code*/ +}MBX_STRUCT_PACKED_END +TSDOINFOERROR; + + +#define SDO_INFO_ERROR_SIZE SIZEOF(TSDOINFOERROR)/**< \brief SDO Info error size*/ + + +/** + *SDO Info header + */ +typedef struct MBX_STRUCT_PACKED_START +{ + UINT16 InfoHead; /**< \brief Opcode, Incomplete flag and 8 reserved bits*/ + #define INFOHEAD_OPCODE_MASK 0x007F /**< \brief Opcode mask*/ + #define INFOHEAD_OPCODE_SHIFT 0 /**< \brief Opcode shift*/ + #define INFOHEADER_INCOMPLETE_MASK 0x0080 /**< \brief Incomplete mask*/ + #define INFOHEADER_INCOMPLETE_SHIFT 0 /**< \brief Incomplete shift, the incomplete value "SDOINFOSERVICE_INCOMPLETE" is defined as a UINT8 => no shift required*/ + + UINT16 FragmentsLeft; /**< \brief Number of fragments which will follow*/ + + union MBX_STRUCT_PACKED_START + { + TSDOINFOLIST List;/**< \brief List data*/ + TSDOINFOOBJ Obj;/**< \brief Object Description data*/ + TSDOINFOENTRY Entry;/**< \brief Entry description data*/ + TSDOINFOERROR Error;/**< \brief Error data*/ + UINT16 Data[1];/**< \brief Unspecified data*/ + }MBX_STRUCT_PACKED_END + Data;/**< \brief Service data*/ + +}MBX_STRUCT_PACKED_END +TSDOINFOHEADER; + + +/** + * SDO Info complete datagram + */ +typedef struct MBX_STRUCT_PACKED_START +{ + TMBXHEADER MbxHeader; /**< \brief Mailbox header*/ + TCOEHEADER CoeHeader; /**< \brief CoE header*/ + TSDOINFOHEADER SdoHeader; /**< \brief SDO Info header and data*/ +}MBX_STRUCT_PACKED_END +TSDOINFORMATION; + + +#define SIZEOF_SDOINFOHEAD 4 /**< \brief SDO Info header size*/ +#define SIZEOF_SDOINFO ( (COE_HEADER_SIZE) + (SIZEOF_SDOINFOHEAD )) /**< \brief SDO Info and CoE Size*/ +#define SIZEOF_SDOINFOSTRUCT ( (MBX_HEADER_SIZE) + (COE_HEADER_SIZE) + (SIZEOF_SDOINFOHEAD) ) /**< \brief Complete SDO Info datagram size*/ +#define SIZEOF_SDOINFOLISTHEAD 2 /**< \brief SDO Info list header size*/ +#define SIZEOF_SDOINFOLISTSTRUCT (( COE_HEADER_SIZE) + (SIZEOF_SDOINFOHEAD) + (SIZEOF_SDOINFOLISTHEAD) ) /**< \brief Complete SDO Info list size*/ +#define SIZEOF_SDOINFOOBJSTRUCT ( (COE_HEADER_SIZE) + (SIZEOF_SDOINFOHEAD) + (SDO_INFO_OBJ_DESC_RES_SIZE) ) /**< \brief Complete SDO Info object description response size*/ +#define SIZEOF_SDOINFOENTRYREQHEAD 4 /**< \brief SDO Info entry description request size*/ +#define SIZEOF_SDOINFOENTRYREQSTRUCT ( (COE_HEADER_SIZE) + (SIZEOF_SDOINFOHEAD) + (SIZEOF_SDOINFOENTRYREQHEAD) ) /**< \brief SDO Info entry description request size*/ +#define SIZEOF_SDOINFOERRORSTRUCT ( (COE_HEADER_SIZE) + (SIZEOF_SDOINFOHEAD) + (SDO_INFO_ERROR_SIZE) ) /**< \brief SDO Info error size*/ + + +#define SDO_INFO_HEADER_BYTE_SIZE ((SIZEOF_SDOINFOSTRUCT)+(SIZEOF_SDOINFOLISTHEAD)) /**< \brief SDO Info size*/ +/** @}*/ + + +/** + * \addtogroup SdoPendingHandler SDO Pending Response + * @{ + * If this feature is enabled (SDO_RES_INTERFACE 1) a SDO request may set to pending and is completed afterwards. + * This can be used to forward the read/write request an additional thread, uController. + */ +#define SDO_PENDING_WRITE 0x1 /**< \brief SDO write request is pending*/ +#define SDO_PENDING_SEG_WRITE 0x2 /**< \brief SDO segmented write request is pending*/ +#define SDO_PENDING_READ 0x3 /**< \brief SDO read request is pending*/ +#define SDO_PENDING_SEG_READ 0x4 /**< \brief SDO segmented read request is pending*/ +/** @}*/ + + +#endif //_SDOSRV_H_ + +/*----------------------------------------------------------------------------------------- +------ +------ Global Variables +------ +-----------------------------------------------------------------------------------------*/ +/* ECATCHANGE_START(V5.11) ECAT10*/ +#if defined(_SDOSERV_) && (_SDOSERV_ == 1) +/* ECATCHANGE_END(V5.11) ECAT10*/ + #define PROTO +#else + #define PROTO extern +#endif + +/** + * \addtogroup SdoPendingHandler SDO Pending Response + *@{ + */ +PROTO UINT8 u8PendingSdo; /**< \brief Identifier for the pending SDO service (see SDO_PENDING_XXX defines)*/ +PROTO BOOL bStoreCompleteAccess; /**< \brief Indicates if the pending SDO service is an complete access*/ +PROTO UINT8 u8StoreSubindex; /**< \brief Contains the subindex for the pending SDO request*/ +PROTO UINT16 u16StoreIndex; /**< \brief Contains the Index for the pending SDO request*/ +PROTO UINT32 u32StoreDataSize; /**< \brief Complete data size to be handled*/ +PROTO UINT16 MBXMEM *pStoreData; /**< \brief Pointer to the buffer to be handled*/ +PROTO UINT8 (* pSdoPendFunc)( UINT16 Index, UINT8 Subindex, UINT32 Size, UINT16 MBXMEM * pData, UINT8 bCompleteAccess ); /**< \brief Function pointer for the SDO pending request handler*/ +/** @}*/ + + +PROTO MEM_ADDR VARMEM aSdoInfoHeader[GET_MEM_SIZE(SDO_INFO_HEADER_BYTE_SIZE)]; /**< \brief SDO Info buffer*/ + + +/** + * \addtogroup SegmentedSdo Segmented SDO + * @{ + */ +PROTO UINT16 VARMEM * VARMEM pSdoSegData; /**< \brief Pointer to the buffer handling to full object/entry data*/ +PROTO UINT16 VARMEM nSdoInfoFragmentsLeft; /**< \brief Number of fragments which need to be transmitted*/ +/** @}*/ + + +/*----------------------------------------------------------------------------------------- +------ +------ Global Functions +------ +-----------------------------------------------------------------------------------------*/ +PROTO UINT8 SDOS_SdoInfoInd(TSDOINFORMATION MBXMEM *pSdoInfoInd); +PROTO UINT8 SDOS_SdoInd(TINITSDOMBX MBXMEM *pSdoInd); + +PROTO void SDOS_SdoRes(UINT8 abort, UINT32 objLength, UINT16 MBXMEM *pData); + +#undef PROTO +/** @}*/ diff --git a/GD32E508_DCU_APP/comm/ssc/port/DCU_ESI.xml b/GD32E508_DCU_APP/comm/ssc/port/DCU_ESI.xml new file mode 100644 index 0000000..fef567c --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/port/DCU_ESI.xml @@ -0,0 +1,77 @@ + + + + #x00000000 + DCU Project + + + + + DCU + DCU Controller + + + + + GD32E508 DCU + GD32E508 DCU EtherCAT Slave + DCU + Outputs + Inputs + + MBoxOut + + MBoxIn + + Outputs + + Inputs + + + #x1600 + Output + + #x7000 + 1 + 256 + Control Data + ARRAY [0..31] OF BYTE + + + + + #x1A00 + Input + + #x6000 + 1 + 256 + Status Data + ARRAY [0..31] OF BYTE + + + + + + + + DC SYNC0 + DC for synchronization + #x0300 + 0 + 0 + + + Free Run + No synchronization + #x0000 + + + + 4096 + 050C000000000000 + + + + + diff --git a/GD32E508_DCU_APP/comm/ssc/port/dc_app.c b/GD32E508_DCU_APP/comm/ssc/port/dc_app.c new file mode 100644 index 0000000..381f25a --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/port/dc_app.c @@ -0,0 +1,229 @@ +/** + * @file dc_app.c + * @brief DCU EtherCAT 应用层 — SSC APPL_ 回调 + 应用对象字典 + * + * 对象字典结构 (匹配 DCU_ESI.xml / eeprom_image.h): + * 0x1C12 RxPDO Assign → 0x1600 + * 0x1600 RxPDO Mapping → 0x7000:01..04 (4×64bit = 32 bytes) + * 0x7000 Output Data (主站→从站, 32 bytes) + * 0x1C13 TxPDO Assign → 0x1A00 + * 0x1A00 TxPDO Mapping → 0x6000:01..04 (4×64bit = 32 bytes) + * 0x6000 Input Data (从站→主站, 32 bytes) + */ + +#include "ecat_def.h" +#include "esc.h" +#include "ecatslv.h" +#include "ecatappl.h" +#include "objdef.h" +#include "dc_app.h" + +#include + +/*--------------------------------------------------------------------------- + * el9800appl.h 桩 include (SSC 内部需要) + *---------------------------------------------------------------------------*/ +#define _EL9800APPL_ 1 +#include "el9800appl.h" +#undef _EL9800APPL_ + +/*--------------------------------------------------------------------------- + * PDO 数据缓冲 + *---------------------------------------------------------------------------*/ + +/** 输出数据对象 0x7000 (主站→从站, RxPDO) */ +typedef struct OBJ_STRUCT_PACKED_START { + UINT16 u16SubIndex0; + UINT64 u64Data[4]; /* 4×8 = 32 bytes */ +} OBJ_STRUCT_PACKED_END +TOBJ7000; + +/** 输入数据对象 0x6000 (从站→主站, TxPDO) */ +typedef struct OBJ_STRUCT_PACKED_START { + UINT16 u16SubIndex0; + UINT64 u64Data[4]; /* 4×8 = 32 bytes */ +} OBJ_STRUCT_PACKED_END +TOBJ6000; + +static TOBJ7000 s_outputObj = {4, {0, 0, 0, 0}}; +static TOBJ6000 s_inputObj = {4, {0, 0, 0, 0}}; + +/*--------------------------------------------------------------------------- + * PDO Assign 对象 (0x1C12 / 0x1C13) + *---------------------------------------------------------------------------*/ + +typedef struct OBJ_STRUCT_PACKED_START { + UINT16 u16SubIndex0; + UINT16 aEntries[1]; +} OBJ_STRUCT_PACKED_END +TOBJ_PDO_ASSIGN; + +static TOBJ_PDO_ASSIGN sRxPDOassign = {1, {0x1600}}; +static TOBJ_PDO_ASSIGN sTxPDOassign = {1, {0x1A00}}; + +/*--------------------------------------------------------------------------- + * PDO Mapping 对象 (0x1600 / 0x1A00) + *---------------------------------------------------------------------------*/ + +typedef struct OBJ_STRUCT_PACKED_START { + UINT16 u16SubIndex0; + UINT32 aEntries[4]; +} OBJ_STRUCT_PACKED_END +TOBJ_PDO_MAPPING; + +/* 0x1600: 映射 0x7000:01..04, 每个 64 bits */ +static TOBJ_PDO_MAPPING sRxPDOMap = {4, { + 0x70000140, /* Index=0x7000, SubIndex=0x01, BitLen=64 */ + 0x70000240, /* Index=0x7000, SubIndex=0x02, BitLen=64 */ + 0x70000340, /* Index=0x7000, SubIndex=0x03, BitLen=64 */ + 0x70000440 /* Index=0x7000, SubIndex=0x04, BitLen=64 */ +}}; + +/* 0x1A00: 映射 0x6000:01..04, 每个 64 bits */ +static TOBJ_PDO_MAPPING sTxPDOMap = {4, { + 0x60000140, /* Index=0x6000, SubIndex=0x01, BitLen=64 */ + 0x60000240, /* Index=0x6000, SubIndex=0x02, BitLen=64 */ + 0x60000340, /* Index=0x6000, SubIndex=0x03, BitLen=64 */ + 0x60000440 /* Index=0x6000, SubIndex=0x04, BitLen=64 */ +}}; + +/*--------------------------------------------------------------------------- + * Entry Descriptions + *---------------------------------------------------------------------------*/ + +/* PDO Assign (0x1C12/0x1C13): Sub0=UINT8 count, Sub1..n=UINT16 index */ +OBJCONST TSDOINFOENTRYDESC OBJMEM asPDOAssignEntryDesc[] = { + {DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ}, + {DEFTYPE_UNSIGNED16, 0x10, ACCESS_READ} +}; + +/* PDO Mapping (0x1600/0x1A00): Sub0=UINT8 count, Sub1..n=UINT32 mapping */ +OBJCONST TSDOINFOENTRYDESC OBJMEM asPDOMappingEntryDesc[] = { + {DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ}, + {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ} +}; + +/* Data object 0x7000/0x6000: Sub0=UINT8 count, Sub1..4=UINT64 */ +OBJCONST TSDOINFOENTRYDESC OBJMEM asDataEntryDesc[] = { + {DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ}, + {DEFTYPE_UNSIGNED64, 0x40, ACCESS_READWRITE} +}; + +/*--------------------------------------------------------------------------- + * Object Names + *---------------------------------------------------------------------------*/ + +OBJCONST UCHAR OBJMEM aName0x1C12[] = "RxPDO assign"; +OBJCONST UCHAR OBJMEM aName0x1C13[] = "TxPDO assign"; +OBJCONST UCHAR OBJMEM aName0x1600[] = "RxPDO mapping"; +OBJCONST UCHAR OBJMEM aName0x1A00[] = "TxPDO mapping"; +OBJCONST UCHAR OBJMEM aName0x7000[] = "Output Data"; +OBJCONST UCHAR OBJMEM aName0x6000[] = "Input Data"; + +/*--------------------------------------------------------------------------- + * Application Object Dictionary + *---------------------------------------------------------------------------*/ + +TOBJECT OBJMEM ApplicationObjDicList[] = { + /* 0x1C12: RxPDO Assign */ + {NULL, NULL, 0x1C12, {DEFTYPE_UNSIGNED16, 1 | (OBJCODE_ARR << 8)}, + asPDOAssignEntryDesc, aName0x1C12, &sRxPDOassign, NULL, NULL, 0x0000}, + + /* 0x1600: RxPDO Mapping */ + {NULL, NULL, 0x1600, {DEFTYPE_PDOMAPPING, 4 | (OBJCODE_REC << 8)}, + asPDOMappingEntryDesc, aName0x1600, &sRxPDOMap, NULL, NULL, 0x0000}, + + /* 0x7000: Output Data (主站→从站) */ + {NULL, NULL, 0x7000, {DEFTYPE_RECORD, 4 | (OBJCODE_REC << 8)}, + asDataEntryDesc, aName0x7000, &s_outputObj, NULL, NULL, 0x0000}, + + /* 0x1C13: TxPDO Assign */ + {NULL, NULL, 0x1C13, {DEFTYPE_UNSIGNED16, 1 | (OBJCODE_ARR << 8)}, + asPDOAssignEntryDesc, aName0x1C13, &sTxPDOassign, NULL, NULL, 0x0000}, + + /* 0x1A00: TxPDO Mapping */ + {NULL, NULL, 0x1A00, {DEFTYPE_PDOMAPPING, 4 | (OBJCODE_REC << 8)}, + asPDOMappingEntryDesc, aName0x1A00, &sTxPDOMap, NULL, NULL, 0x0000}, + + /* 0x6000: Input Data (从站→主站) */ + {NULL, NULL, 0x6000, {DEFTYPE_RECORD, 4 | (OBJCODE_REC << 8)}, + asDataEntryDesc, aName0x6000, &s_inputObj, NULL, NULL, 0x0000}, + + /* 终止条目 */ + {NULL, NULL, 0xFFFF, {0, 0}, NULL, NULL, NULL, NULL, NULL, 0x000} +}; + +TOBJECT OBJMEM * VARMEM ApplicationObjDic = ApplicationObjDicList; + +/*--------------------------------------------------------------------------- + * APPL_GenerateMapping + *---------------------------------------------------------------------------*/ +UINT16 APPL_GenerateMapping(UINT16 *pInputSize, UINT16 *pOutputSize) +{ + *pInputSize = 32; /* 4×UINT64 = 32 bytes */ + *pOutputSize = 32; + return 0; +} + +/*--------------------------------------------------------------------------- + * Mailbox Handler + *---------------------------------------------------------------------------*/ +UINT16 APPL_StartMailboxHandler(void) { return 0; } +UINT16 APPL_StopMailboxHandler(void) { return 0; } + +/*--------------------------------------------------------------------------- + * Input Handler (SAFEOP 进入) + *---------------------------------------------------------------------------*/ +UINT16 APPL_StartInputHandler(UINT16 *pIntMask) +{ + *pIntMask = 0x0800U; /* SM3 AL Event */ + return 0; +} + +UINT16 APPL_StopInputHandler(void) { return 0; } + +/*--------------------------------------------------------------------------- + * Output Handler (SAFEOP→OP) + *---------------------------------------------------------------------------*/ +UINT16 APPL_StartOutputHandler(void) { return 0; } +UINT16 APPL_StopOutputHandler(void) { return 0; } + +/*--------------------------------------------------------------------------- + * APPL_AckErrorInd + *---------------------------------------------------------------------------*/ +void APPL_AckErrorInd(UINT16 stateTrans) +{ + (void)stateTrans; +} + +/*--------------------------------------------------------------------------- + * APPL_InputMapping — 每周期从应用层填充 ESC 输入 PDO + *---------------------------------------------------------------------------*/ +void APPL_InputMapping(UINT16 *pInputData) +{ + memcpy(pInputData, &s_inputObj.u64Data[0], 32); +} + +/*--------------------------------------------------------------------------- + * APPL_OutputMapping — 每周期从 ESC 输出 PDO 读取到应用层 + *---------------------------------------------------------------------------*/ +void APPL_OutputMapping(UINT16 *pOutputData) +{ + memcpy(&s_outputObj.u64Data[0], pOutputData, 32); +} + +/*--------------------------------------------------------------------------- + * APPL_Application — 主循环周期调用 + *---------------------------------------------------------------------------*/ +void APPL_Application(void) +{ + /* Phase 2: 回环测试 — 将输出数据原样回填到输入数据。 + * TwinCAT 主站写 0x7000 → ESC → APPL_OutputMapping → s_outputObj + * APPL_Application: s_inputObj = s_outputObj (回环) + * APPL_InputMapping → ESC → 主站读 0x6000 + * 主站可验证:写入的值 == 读回的值。 */ + s_inputObj.u64Data[0] = s_outputObj.u64Data[0]; + s_inputObj.u64Data[1] = s_outputObj.u64Data[1]; + s_inputObj.u64Data[2] = s_outputObj.u64Data[2]; + s_inputObj.u64Data[3] = s_outputObj.u64Data[3]; +} diff --git a/GD32E508_DCU_APP/comm/ssc/port/dc_app.h b/GD32E508_DCU_APP/comm/ssc/port/dc_app.h new file mode 100644 index 0000000..f684e08 --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/port/dc_app.h @@ -0,0 +1,39 @@ +/** + * @file dc_app.h + * @brief DCU EtherCAT 应用层接口声明 + * + * 提供 SSC 所需的 APPL_ 回调函数声明。 + * Phase 1: 桩实现,仅保证编译通过。 + * Phase 3: 接入实际 PDO 映射(DCU 传感器输入 / 控制输出)。 + */ +#ifndef COMM_SSC_PORT_DC_APP_H +#define COMM_SSC_PORT_DC_APP_H + +#include "ecat_def.h" +#include "esc.h" +#include "ecatslv.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* SSC 状态机回调 (ecatslv.c 调用) */ +UINT16 APPL_GenerateMapping(UINT16 *pInputSize, UINT16 *pOutputSize); +UINT16 APPL_StartMailboxHandler(void); +UINT16 APPL_StopMailboxHandler(void); +UINT16 APPL_StartInputHandler(UINT16 *pIntMask); +UINT16 APPL_StopInputHandler(void); +UINT16 APPL_StartOutputHandler(void); +UINT16 APPL_StopOutputHandler(void); +void APPL_AckErrorInd(UINT16 stateTrans); + +/* 周期数据映射回调 (ecatappl.c 调用) */ +void APPL_InputMapping(UINT16 *pInputData); +void APPL_OutputMapping(UINT16 *pOutputData); +void APPL_Application(void); + +#ifdef __cplusplus +} +#endif + +#endif /* COMM_SSC_PORT_DC_APP_H */ diff --git a/GD32E508_DCU_APP/comm/ssc/port/ecat_def.h b/GD32E508_DCU_APP/comm/ssc/port/ecat_def.h new file mode 100644 index 0000000..008c872 --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/port/ecat_def.h @@ -0,0 +1,734 @@ +/** + * @file ecat_def.h + * @brief SSC V5.11 项目配置 — GD32E508 + LAN9252 (EXMC 16-bit HBI Indexed Mode) + * + * 基于 Beckhoff SSC V5.11 例程配置裁剪,适配本项目硬件: + * - MCU: GD32E508VE (Cortex-M33, 180MHz, little-endian) + * - ESC: LAN9252 via EXMC NOR/SRAM Region0, 16-bit HBI Indexed Address Mode + * - 协议: CoE (SDO + PDO), DC SYNC0, 无 FoE/EoE/AoE/SoE + */ +#ifndef _ECATDEF_H_ +#define _ECATDEF_H_ + +#include +#include + +/*----------------------------------------------------------------------------------------- +------ +------ Hardware Platform Selection +------ +-----------------------------------------------------------------------------------------*/ + +/** EL9800_HW: 保留=1 以启用 el9800hw.h 接口(本项目重写该文件为 GD32 EXMC 版本)。 */ +#ifndef EL9800_HW +#define EL9800_HW 1 +#endif + +#ifndef MCI_HW +#define MCI_HW 0 +#endif + +#ifndef FC1100_HW +#define FC1100_HW 0 +#endif + +#ifndef TIESC_HW +#define TIESC_HW 0 +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ Controller / ESC Access Width +------ +-----------------------------------------------------------------------------------------*/ + +/** GD32E508 是 32-bit MCU,但 EXMC 数据总线为 16-bit。 + * SSC 用 CONTROLLER_16BIT 决定 MEM_ADDR 类型和指针算术。 + * 与例程(STM32F407 SPI 16-bit)保持一致。 */ +#ifndef CONTROLLER_16BIT +#define CONTROLLER_16BIT 1 +#endif + +#ifndef CONTROLLER_32BIT +#define CONTROLLER_32BIT 0 +#endif + +/** ESC 16-bit 访问(HBI DATA 寄存器为 16-bit word)。 */ +#ifndef ESC_16BIT_ACCESS +#define ESC_16BIT_ACCESS 1 +#endif + +#ifndef ESC_32BIT_ACCESS +#define ESC_32BIT_ACCESS 0 +#endif + +/** 邮箱本地内存 16-bit 访问。 */ +#ifndef MBX_16BIT_ACCESS +#define MBX_16BIT_ACCESS 1 +#endif + +/** GD32E508 为 little-endian。 */ +#ifndef BIG_ENDIAN_16BIT +#define BIG_ENDIAN_16BIT 0 +#endif + +#ifndef BIG_ENDIAN_FORMAT +#define BIG_ENDIAN_FORMAT 0 +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ Platform-specific (STM32 IO defines disabled) +------ +-----------------------------------------------------------------------------------------*/ + +#ifndef _STM32_IO4 +#define _STM32_IO4 0 +#endif + +#ifndef _STM32_IO8 +#define _STM32_IO8 0 +#endif + +#ifndef EXT_DEBUGER_INTERFACE +#define EXT_DEBUGER_INTERFACE 0 +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ LED / EEPROM +------ +-----------------------------------------------------------------------------------------*/ + +/** LAN9252 支持 ESC 硬件驱动 Run/Error LED。 */ +#ifndef UC_SET_ECAT_LED +#define UC_SET_ECAT_LED 0 +#endif + +#ifndef ESC_SUPPORT_ECAT_LED +#define ESC_SUPPORT_ECAT_LED 1 +#endif + +/** 不使用 EEPROM 仿真(本项目有真实 AT24C32)。 */ +#ifndef ESC_EEPROM_EMULATION +#define ESC_EEPROM_EMULATION 0 +#endif + +/** AT24C32 = 4KB。 */ +#ifndef ESC_EEPROM_SIZE +#define ESC_EEPROM_SIZE 0x1000 +#endif + +#ifndef EEPROM_READ_SIZE +#define EEPROM_READ_SIZE 0x08 +#endif + +#ifndef EEPROM_WRITE_SIZE +#define EEPROM_WRITE_SIZE 0x2 +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ Interrupt / Timer / DC +------ +-----------------------------------------------------------------------------------------*/ + +/** AL Event 中断使能(ESC IRQ -> EXTI1)。 */ +#ifndef AL_EVENT_ENABLED +#define AL_EVENT_ENABLED 1 +#endif + +/** Distributed Clocks 支持(SYNC0 -> EXTI0)。 */ +#ifndef DC_SUPPORTED +#define DC_SUPPORTED 1 +#endif + +/** 看门狗由定时器中断检查。 */ +#ifndef ECAT_TIMER_INT +#define ECAT_TIMER_INT 1 +#endif + +#ifndef INTERRUPTS_SUPPORTED +#define INTERRUPTS_SUPPORTED 1 +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ Application Selection +------ +-----------------------------------------------------------------------------------------*/ + +/** 不使用 SSC 自带测试/示例应用,本项目自写 dc_app.c。 */ +#ifndef TEST_APPLICATION +#define TEST_APPLICATION 0 +#endif + +#ifndef EL9800_APPLICATION +#define EL9800_APPLICATION 0 +#endif + +#ifndef CiA402_DEVICE +#define CiA402_DEVICE 0 +#endif + +#ifndef SAMPLE_APPLICATION +#define SAMPLE_APPLICATION 0 +#endif + +#ifndef SAMPLE_APPLICATION_INTERFACE +#define SAMPLE_APPLICATION_INTERFACE 0 +#endif + +/** 不使用 SSC 默认 main(),本项目有自己的 task 系统。 */ +#ifndef USE_DEFAULT_MAIN +#define USE_DEFAULT_MAIN 0 +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ Mailbox / Protocol Selection +------ +-----------------------------------------------------------------------------------------*/ + +#ifndef MAILBOX_QUEUE +#define MAILBOX_QUEUE 1 +#endif + +/** CoE (CANopen over EtherCAT) — SDO + PDO mapping。 */ +#ifndef COE_SUPPORTED +#define COE_SUPPORTED 1 +#endif + +#ifndef COMPLETE_ACCESS_SUPPORTED +#define COMPLETE_ACCESS_SUPPORTED 1 +#endif + +#ifndef SEGMENTED_SDO_SUPPORTED +#define SEGMENTED_SDO_SUPPORTED 1 +#endif + +#ifndef SDO_RES_INTERFACE +#define SDO_RES_INTERFACE 1 +#endif + +#ifndef USE_SINGLE_PDO_MAPPING_ENTRY_DESCR +#define USE_SINGLE_PDO_MAPPING_ENTRY_DESCR 0 +#endif + +#ifndef BACKUP_PARAMETER_SUPPORTED +#define BACKUP_PARAMETER_SUPPORTED 0 +#endif + +#ifndef STORE_BACKUP_PARAMETER_IMMEDIATELY +#define STORE_BACKUP_PARAMETER_IMMEDIATELY 0 +#endif + +/** 以下协议全部关闭。 */ +#ifndef AOE_SUPPORTED +#define AOE_SUPPORTED 0 +#endif + +#ifndef DIAGNOSIS_SUPPORTED +#define DIAGNOSIS_SUPPORTED 0 +#endif + +#ifndef EMERGENCY_SUPPORTED +#define EMERGENCY_SUPPORTED 0 +#endif + +#ifndef VOE_SUPPORTED +#define VOE_SUPPORTED 0 +#endif + +#ifndef SOE_SUPPORTED +#define SOE_SUPPORTED 0 +#endif + +#ifndef EOE_SUPPORTED +#define EOE_SUPPORTED 0 +#endif + +#ifndef STATIC_ETHERNET_BUFFER +#define STATIC_ETHERNET_BUFFER 0 +#endif + +#ifndef FOE_SUPPORTED +#define FOE_SUPPORTED 0 +#endif + +#ifndef FOE_SAVE_FILES +#define FOE_SAVE_FILES 0 +#endif + +/** MAILBOX_SUPPORTED: CoE 开启则自动需要邮箱。 */ +#ifndef MAILBOX_SUPPORTED +#define MAILBOX_SUPPORTED 1 +#endif + +#ifndef BOOTSTRAPMODE_SUPPORTED +#define BOOTSTRAPMODE_SUPPORTED 0 +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ State Machine / Watchdog +------ +-----------------------------------------------------------------------------------------*/ + +#ifndef OP_PD_REQUIRED +#define OP_PD_REQUIRED 1 +#endif + +/** PreOP 超时 2000ms。 */ +#ifndef PREOPTIMEOUT +#define PREOPTIMEOUT 0x7D0 +#endif + +/** SafeOP->OP 超时 9000ms。 */ +#ifndef SAFEOP2OPTIMEOUT +#define SAFEOP2OPTIMEOUT 0x2328 +#endif + +#ifndef EXPLICIT_DEVICE_ID +#define EXPLICIT_DEVICE_ID 0 +#endif + +/** LAN9252 支持 SM 看门狗。 */ +#ifndef ESC_SM_WD_SUPPORTED +#define ESC_SM_WD_SUPPORTED 1 +#endif + +#ifndef STATIC_OBJECT_DIC +#define STATIC_OBJECT_DIC 0 +#endif + +#ifndef ESC_EEPROM_ACCESS_SUPPORT +#define ESC_EEPROM_ACCESS_SUPPORT 0 +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ Compiler / Type Defines (ARM Cortex-M33, GCC/AC6) +------ +-----------------------------------------------------------------------------------------*/ + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef BOOL +#define BOOL unsigned char +#endif + +#ifndef UINT8 +#define UINT8 unsigned char +#endif + +#ifndef UINT16 +#define UINT16 unsigned short +#endif + +#ifndef UINT32 +#define UINT32 unsigned long +#endif + +#ifndef UINT64 +#define UINT64 unsigned long long +#endif + +#ifndef USHORT +#define USHORT unsigned short +#endif + +#ifndef INT8 +#define INT8 char +#endif + +#ifndef INT16 +#define INT16 short +#endif + +#ifndef INT32 +#define INT32 long +#endif + +#ifndef CHAR +#define CHAR char +#endif + +#ifndef UCHAR +#define UCHAR unsigned char +#endif + +#ifndef SIZEOF +#define SIZEOF(x) sizeof(x) +#endif + +/** MEM_ADDR: SSC 用于 ESC 内存访问的指针类型。 + * CONTROLLER_16BIT=1 时为 UINT16。 */ +#ifndef MEM_ADDR +#define MEM_ADDR UINT16 +#endif + +/** VARMEM: 动态变量内存类型修饰符(flat memory 留空)。 */ +#ifndef VARMEM +#define VARMEM +#endif + +#ifndef VARMEMSET +#define VARMEMSET memset +#endif + +#ifndef VARMEMCPY +#define VARMEMCPY memcpy +#endif + +/** GET_MEM_SIZE: 将字节数向上对齐到 MEM_ADDR 边界。 */ +#ifndef GET_MEM_SIZE +#define GET_MEM_SIZE(ByteSize) (((ByteSize)+1) >> 1) +#endif + +/** GET_MEM_BYTES: 将 MEM_ADDR 单位转回字节数。 */ +#ifndef GET_MEM_BYTES +#define GET_MEM_BYTES(MemSize) ((MemSize) << 1) +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ Memory Model (flat memory, no near/far/huge) +------ +-----------------------------------------------------------------------------------------*/ + +#ifndef HUGE +#define HUGE +#endif + +#ifndef HMEMSET +#define HMEMSET memset +#endif + +#ifndef HMEMCPY +#define HMEMCPY memcpy +#endif + +#ifndef HMEMCMP +#define HMEMCMP memcmp +#endif + +#ifndef ESCMEM +#define ESCMEM +#endif + +#ifndef ESCMEMCPY +#define ESCMEMCPY memcpy +#endif + +#ifndef ESCMEMSET +#define ESCMEMSET memset +#endif + +#ifndef ESCMBXMEMCPY +#define ESCMBXMEMCPY memcpy +#endif + +#ifndef MBXMEM +#define MBXMEM +#endif + +#ifndef MBXMEMCPY +#define MBXMEMCPY memcpy +#endif + +#ifndef MBXMEMCMP +#define MBXMEMCMP memcmp +#endif + +#ifndef MBXMEMSET +#define MBXMEMSET memset +#endif + +#ifndef MBXSTRLEN +#define MBXSTRLEN strlen +#endif + +#ifndef MBXSTRCPY +#define MBXSTRCPY memcpy +#endif + +#ifndef OBJCONST +#define OBJCONST const +#endif + +#ifndef VARCONST +#define VARCONST +#endif + +#ifndef VARVOLATILE +#define VARVOLATILE volatile +#endif + +#ifndef OBJMEM +#define OBJMEM +#endif + +#ifndef OBJTOMBXMEMCPY +#define OBJTOMBXMEMCPY memcpy +#endif + +#ifndef OBJTOMBXSTRCPY +#define OBJTOMBXSTRCPY memcpy +#endif + +#ifndef OBJMEMCPY +#define OBJMEMCPY memcpy +#endif + +#ifndef OBJSTRLEN +#define OBJSTRLEN strlen +#endif + +#ifndef OBJSTRCPY +#define OBJSTRCPY memcpy +#endif + +#ifndef MAKE_HUGE_PTR +#define MAKE_HUGE_PTR +#endif + +#ifndef MAKE_PTR_TO_ESC +#define MAKE_PTR_TO_ESC +#endif + +#ifndef EMCYMEMCPY +#define EMCYMEMCPY memcpy +#endif + +#ifndef EMCYMEMSET +#define EMCYMEMSET memset +#endif + +#ifndef EMCYMEM +#define EMCYMEM +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ Struct Packing (ARM AC6: __attribute__((packed)) 不需要前后宏,留空即可) +------ +-----------------------------------------------------------------------------------------*/ + +#ifndef STRUCT_PACKED_START +#define STRUCT_PACKED_START +#endif + +#ifndef STRUCT_PACKED_END +#define STRUCT_PACKED_END +#endif + +#ifndef MBX_STRUCT_PACKED_START +#define MBX_STRUCT_PACKED_START +#endif + +#ifndef MBX_STRUCT_PACKED_END +#define MBX_STRUCT_PACKED_END +#endif + +#ifndef OBJ_STRUCT_PACKED_START +#define OBJ_STRUCT_PACKED_START +#endif + +#ifndef OBJ_STRUCT_PACKED_END +#define OBJ_STRUCT_PACKED_END +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ Mailbox Size +------ +-----------------------------------------------------------------------------------------*/ + +/** 最小邮箱字节数(主站可设置的下限)。 */ +#ifndef MIN_MBX_SIZE +#define MIN_MBX_SIZE 0x0022 +#endif + +/** 最大邮箱字节数(主站可设置的上限)。 */ +#ifndef MAX_MBX_SIZE +#define MAX_MBX_SIZE 0x0100 +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ PDO Size (match CONFIG_ETHERCAT_PDO_SIZE in sys/config.h) +------ +-----------------------------------------------------------------------------------------*/ + +/** 最大输出 PDO 字节数(RxPDO,主站→从站)。 */ +#ifndef MAX_PD_OUTPUT_SIZE +#define MAX_PD_OUTPUT_SIZE 0x20 +#endif + +/** 最大输入 PDO 字节数(TxPDO,从站→主站)。 */ +#ifndef MAX_PD_INPUT_SIZE +#define MAX_PD_INPUT_SIZE 0x20 +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ Device Identity (CoE Object 0x1018) +------ +-----------------------------------------------------------------------------------------*/ + +#ifndef VENDOR_ID +#define VENDOR_ID 0x00000000 +#endif + +#ifndef PRODUCT_CODE +#define PRODUCT_CODE 0x00009252 +#endif + +#ifndef REVISION_NUMBER +#define REVISION_NUMBER 0x00010000 +#endif + +#ifndef SERIAL_NUMBER +#define SERIAL_NUMBER 0x00000001 +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ Device Information (CoE Objects 0x1000/0x1008/0x1009/0x100A) +------ +-----------------------------------------------------------------------------------------*/ + +#ifndef DEVICE_PROFILE_TYPE +#define DEVICE_PROFILE_TYPE 0x00000000 +#endif + +#ifndef DEVICE_NAME +#define DEVICE_NAME "GD32E508 DCU" +#endif + +#ifndef DEVICE_NAME_LEN +#define DEVICE_NAME_LEN 0x0D +#endif + +#ifndef DEVICE_HW_VERSION +#define DEVICE_HW_VERSION "1.0" +#endif + +#ifndef DEVICE_HW_VERSION_LEN +#define DEVICE_HW_VERSION_LEN 0x3 +#endif + +#ifndef DEVICE_SW_VERSION +#define DEVICE_SW_VERSION "1.0" +#endif + +#ifndef DEVICE_SW_VERSION_LEN +#define DEVICE_SW_VERSION_LEN 0x3 +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ ESC Memory Address Ranges (SM buffer allocation boundaries) +------ +-----------------------------------------------------------------------------------------*/ + +#ifndef MIN_PD_WRITE_ADDRESS +#define MIN_PD_WRITE_ADDRESS 0x1000 +#endif + +#ifndef MAX_PD_WRITE_ADDRESS +#define MAX_PD_WRITE_ADDRESS 0x1FFF +#endif + +#ifndef MIN_PD_READ_ADDRESS +#define MIN_PD_READ_ADDRESS 0x1000 +#endif + +#ifndef MAX_PD_READ_ADDRESS +#define MAX_PD_READ_ADDRESS 0x1FFF +#endif + +#ifndef MIN_MBX_WRITE_ADDRESS +#define MIN_MBX_WRITE_ADDRESS 0x1000 +#endif + +#ifndef MAX_MBX_WRITE_ADDRESS +#define MAX_MBX_WRITE_ADDRESS 0x1FFF +#endif + +#ifndef MIN_MBX_READ_ADDRESS +#define MIN_MBX_READ_ADDRESS 0x1000 +#endif + +#ifndef MAX_MBX_READ_ADDRESS +#define MAX_MBX_READ_ADDRESS 0x1FFF +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ PDO Timing (DC sync parameters, unit: ns) +------ +-----------------------------------------------------------------------------------------*/ + +#ifndef MIN_PD_CYCLE_TIME +#define MIN_PD_CYCLE_TIME 0x186A0 +#endif + +#ifndef MAX_PD_CYCLE_TIME +#define MAX_PD_CYCLE_TIME 0xC3500000 +#endif + +#ifndef PD_OUTPUT_CALC_AND_COPY_TIME +#define PD_OUTPUT_CALC_AND_COPY_TIME 0x0 +#endif + +#ifndef PD_OUTPUT_DELAY_TIME +#define PD_OUTPUT_DELAY_TIME 0x0 +#endif + +#ifndef PD_INPUT_CALC_AND_COPY_TIME +#define PD_INPUT_CALC_AND_COPY_TIME 0x0 +#endif + +#ifndef PD_INPUT_DELAY_TIME +#define PD_INPUT_DELAY_TIME 0x0 +#endif + +/*----------------------------------------------------------------------------------------- +------ +------ Dynamic Memory +------ +-----------------------------------------------------------------------------------------*/ + +#ifndef FREEMEM +#define FREEMEM(pointer) free((pointer)) +#endif + +#ifndef GETMEM +#define GETMEM(size) malloc((size)) +#endif + +#ifndef APPL_AllocMailboxBuffer +#define APPL_AllocMailboxBuffer(size) malloc((size)) +#endif + +#ifndef APPL_FreeMailboxBuffer +#define APPL_FreeMailboxBuffer(pointer) free((pointer)) +#endif + +#ifndef MEMCPY +#define MEMCPY memcpy +#endif + +#ifndef ALLOCMEM +#define ALLOCMEM(size) malloc((size)) +#endif + +#endif /* _ECATDEF_H_ */ diff --git a/GD32E508_DCU_APP/comm/ssc/port/eeprom_image.h b/GD32E508_DCU_APP/comm/ssc/port/eeprom_image.h new file mode 100644 index 0000000..adb3a3b --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/port/eeprom_image.h @@ -0,0 +1,53 @@ +/** + * @file eeprom_image.h + * @brief LAN9252 EEPROM (ESI) binary image - auto-generated by gen_eeprom.py + * + * Vendor ID: 0x00000000 + * Product Code: 0x00009252 + * Revision: 0x00010000 + * Serial: 0x00000001 + * Size: 4096 bytes (AT24C32) + * + * DO NOT EDIT - regenerate with: python gen_eeprom.py > eeprom_image.h + */ +#ifndef COMM_SSC_PORT_EEPROM_IMAGE_H +#define COMM_SSC_PORT_EEPROM_IMAGE_H + +#include + +static const uint8_t ESI_EEPROM_IMAGE[4096] = { + /* 0x0000 */ 0x8D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x92, 0xDF, 0x9E, + /* 0x0010 */ 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0020 */ 0x00, 0x10, 0x00, 0x01, 0x00, 0x12, 0x00, 0x01, 0x00, 0x10, 0x00, 0x01, 0x00, 0x12, 0x00, 0x01, + /* 0x0030 */ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0040 */ 0x00, 0x10, 0x00, 0x01, 0x26, 0x00, 0x01, 0x00, 0x00, 0x12, 0x00, 0x01, 0x22, 0x00, 0x01, 0x00, + /* 0x0050 */ 0x00, 0x14, 0x20, 0x00, 0x64, 0x00, 0x01, 0x00, 0x80, 0x14, 0x20, 0x00, 0x20, 0x00, 0x01, 0x00, + /* 0x0060 */ 0x01, 0x02, 0x0A, 0x0D, 0x02, 0x0B, 0x44, 0x43, 0x55, 0x20, 0x50, 0x72, 0x6F, 0x6A, 0x65, 0x63, + /* 0x0070 */ 0x74, 0x0C, 0x47, 0x44, 0x33, 0x32, 0x45, 0x35, 0x30, 0x38, 0x20, 0x44, 0x43, 0x55, 0x14, 0x08, + /* 0x0080 */ 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0090 */ 0x1E, 0x01, 0x01, 0x02, 0x28, 0x10, 0x00, 0x10, 0x00, 0x01, 0x26, 0x00, 0x01, 0x00, 0x00, 0x12, + /* 0x00A0 */ 0x00, 0x01, 0x22, 0x00, 0x01, 0x00, 0x00, 0x14, 0x20, 0x00, 0x64, 0x00, 0x01, 0x00, 0x80, 0x14, + /* 0x00B0 */ 0x20, 0x00, 0x20, 0x00, 0x01, 0x00, 0x32, 0x11, 0x00, 0x16, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, + /* 0x00C0 */ 0x00, 0x70, 0x01, 0x00, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1A, 0x03, 0x00, 0x00, 0x00, 0x01, + /* 0x00D0 */ 0x00, 0x00, 0x60, 0x01, 0x00, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x3C, 0x09, 0x00, 0x03, 0x00, 0x00, + /* 0x00E0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0100: (0xFF padding) */ + /* 0x0200: (0xFF padding) */ + /* 0x0300: (0xFF padding) */ + /* 0x0400: (0xFF padding) */ + /* 0x0500: (0xFF padding) */ + /* 0x0600: (0xFF padding) */ + /* 0x0700: (0xFF padding) */ + /* 0x0800: (0xFF padding) */ + /* 0x0900: (0xFF padding) */ + /* 0x0A00: (0xFF padding) */ + /* 0x0B00: (0xFF padding) */ + /* 0x0C00: (0xFF padding) */ + /* 0x0D00: (0xFF padding) */ + /* 0x0E00: (0xFF padding) */ + /* 0x0F00: (0xFF padding) */ +}; + +static const uint32_t ESI_EEPROM_SIZE = 4096U; + +#endif /* COMM_SSC_PORT_EEPROM_IMAGE_H */ diff --git a/GD32E508_DCU_APP/comm/ssc/port/gen_eeprom.py b/GD32E508_DCU_APP/comm/ssc/port/gen_eeprom.py new file mode 100644 index 0000000..6150275 --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/port/gen_eeprom.py @@ -0,0 +1,357 @@ +#!/usr/bin/env python3 +""" +Generate LAN9252 EEPROM binary image (ESI) for GD32E508 DCU. +Outputs a C header file with the EEPROM content as a byte array. + +EEPROM format per ETG.1000.6 (EtherCAT Slave Information): +- All multi-byte values are little-endian +- Addressing is in bytes (the ESC EEPROM interface uses word addresses internally, + but the content is byte-addressed) +- Total size: 4096 bytes (AT24C32) + +Usage: python gen_eeprom.py > eeprom_image.h +""" +import struct +import sys + +# ============================================================ +# Configuration (must match ecat_def.h and DCU_ESI.xml) +# ============================================================ +VENDOR_ID = 0x00000000 +PRODUCT_CODE = 0x00009252 +REVISION_NO = 0x00010000 +SERIAL_NO = 0x00000001 + +# Mailbox configuration +MBX_RX_OFFSET = 0x1000 # SM0: Master -> Slave (receive mailbox) +MBX_RX_SIZE = 256 +MBX_TX_OFFSET = 0x1200 # SM1: Slave -> Master (send mailbox) +MBX_TX_SIZE = 256 +MBX_PROTOCOL = 0x0001 # CoE + +# Process Data configuration +PD_OUT_OFFSET = 0x1400 # SM2: RxPDO (Master -> Slave outputs) +PD_OUT_SIZE = 32 +PD_IN_OFFSET = 0x1480 # SM3: TxPDO (Slave -> Master inputs) +PD_IN_SIZE = 32 + +# PDI type: LAN9252 HBI 16-bit indexed mode +# 数据手册 LAN9252_en.pdf 表 14-2 (PDI 模式选择): +# 0x04=DIG I/O, 0x80=SPI, 0x88~0x8B=HBI 复用, 0x8C=HBI 变址8位, 0x8D=HBI 变址16位 +# 本设计用 16-bit HBI Indexed Mode(A1/A2 选 IDX/DAT),必须写 0x8D。 +# 注意:0x05 是保留值!若 EEPROM 写入 0x05 或 EEPROM 空白(默认 0x00 保留), +# LAN9252 的 PDI 无效,主机总线(HBI)完全不响应——表现为所有总线读回都是上次写入的回显。 +PDI_CONTROL = 0x8D +PDI_CONFIG = 0x0C + +EEPROM_SIZE = 4096 + +# ============================================================ +# Build EEPROM image +# ============================================================ +eeprom = bytearray(EEPROM_SIZE) +# Fill with 0xFF (erased state) +eeprom[:] = b'\xFF' * EEPROM_SIZE + +def w16(offset, value): + """Write 16-bit little-endian value at byte offset.""" + struct.pack_into(' Slave), 3-buffer mode +write_sm(0, MBX_RX_OFFSET, MBX_RX_SIZE, 0x26) +# SM1: Mailbox Send (Slave -> Master), 3-buffer mode +write_sm(1, MBX_TX_OFFSET, MBX_TX_SIZE, 0x22) +# SM2: Process Data Output (RxPDO), 3-buffer, watchdog trigger +write_sm(2, PD_OUT_OFFSET, PD_OUT_SIZE, 0x64) +# SM3: Process Data Input (TxPDO), 3-buffer +write_sm(3, PD_IN_OFFSET, PD_IN_SIZE, 0x20) + +# --- FMMU Configuration (after SM, byte 64 + 4*8 = 96) --- +# Each FMMU entry: 1 byte (type: 0x01=used for outputs, 0x02=used for inputs) +fmmu_base = sm_base + 4 * 8 # = 96 +eeprom[fmmu_base] = 0x01 # FMMU0: outputs +eeprom[fmmu_base + 1] = 0x02 # FMMU1: inputs + +# --- Categories (after FMMU) --- +# Category format: [Type (1 byte)] [Size in words (1 byte)] [Data...] +# Type 0 = end of categories +# Type 10 = Strings +# Type 20 = General +# Type 30 = FMMU +# Type 40 = SM +# Type 50 = PDO (TxPDO/RxPDO) +# Type 60 = DC + +cat_offset = fmmu_base + 2 # = 98 + +# --- String Category (Type 10) --- +# First byte = number of strings, then for each: [len(1)] [chars...] +strings = [b"DCU Project", b"GD32E508 DCU"] +str_data = bytearray() +str_data.append(len(strings)) +for s in strings: + str_data.append(len(s)) + str_data.extend(s) +# Pad to even length +if len(str_data) % 2 != 0: + str_data.append(0) +str_words = len(str_data) // 2 + +eeprom[cat_offset] = 10 # Type: Strings +eeprom[cat_offset + 1] = str_words +eeprom[cat_offset + 2: cat_offset + 2 + len(str_data)] = str_data +cat_offset += 2 + len(str_data) + +# --- General Category (Type 20) --- +# Format: [GroupInfo(1)] [ImageName(1)] [OrderIdx(1)] [CoE details... ] +# Simplified: just enough for TwinCAT to display +gen_data = bytearray(16) +gen_data[0] = 1 # Group info index (1-based into strings? or group ID) +gen_data[1] = 2 # Image name string index +gen_data[2] = 0 # Order index +gen_data[3] = 0 # CoE details: SDO info supported +gen_data[4] = 0x01 # CoE: SDO info +gen_data[5] = 0x00 +gen_data[6] = 0 # FoE/EoE/etc flags +gen_data[7] = 0 +# Physics: YY ( EtherCAT on both ports) +gen_data[8] = 0x03 # Physics byte: port0=YY, port1=YY +gen_data[9] = 0 +# Watchdog divider +gen_data[10] = 0 +gen_data[11] = 0 +# SM watchdog +gen_data[12] = 0 +gen_data[13] = 0 +# SM watchdog divider +gen_data[14] = 0 +gen_data[15] = 0 +gen_words = len(gen_data) // 2 + +eeprom[cat_offset] = 20 # Type: General +eeprom[cat_offset + 1] = gen_words +eeprom[cat_offset + 2: cat_offset + 2 + len(gen_data)] = gen_data +cat_offset += 2 + len(gen_data) + +# --- FMMU Category (Type 30) --- +# List of FMMU usages (1 byte each): 0x01=Outputs, 0x02=Inputs +fmmu_cat = bytearray([0x01, 0x02]) +if len(fmmu_cat) % 2 != 0: + fmmu_cat.append(0) +fmmu_words = len(fmmu_cat) // 2 + +eeprom[cat_offset] = 30 # Type: FMMU +eeprom[cat_offset + 1] = fmmu_words +eeprom[cat_offset + 2: cat_offset + 2 + len(fmmu_cat)] = fmmu_cat +cat_offset += 2 + len(fmmu_cat) + +# --- SM Category (Type 40) --- +# Each SM: [StartAddr(2)] [Length(2)] [Control(1)] [Status(1)] [Activate(1)] [PDI(1)] +sm_cat = bytearray() +for idx in range(4): + off = sm_base + idx * 8 + sm_cat.extend(eeprom[off:off+8]) +sm_words = len(sm_cat) // 2 + +eeprom[cat_offset] = 40 # Type: SM +eeprom[cat_offset + 1] = sm_words +eeprom[cat_offset + 2: cat_offset + 2 + len(sm_cat)] = sm_cat +cat_offset += 2 + len(sm_cat) + +# --- PDO Category (Type 50) --- +# TxPDO (RxPDO from master perspective, SM2): +# [Index(2)] [SyncManager(1)] [DC Sync(1)] [NameIdx(1)] [Flags(1)] [EntryCount(1)] [Reserved(1)] +# Then entries: [Index(2)] [SubIndex(1)] [NameIdx(1)] [DataType(1)] [BitLen(2)] [Flags(2)] +pdo_cat = bytearray() + +# TxPDO 0x1600 (assigned to SM2, outputs) +pdo_cat.extend(struct.pack(' eeprom_image.h") +print(" */") +print("#ifndef COMM_SSC_PORT_EEPROM_IMAGE_H") +print("#define COMM_SSC_PORT_EEPROM_IMAGE_H") +print("") +print("#include ") +print("") +print(f"static const uint8_t ESI_EEPROM_IMAGE[{EEPROM_SIZE}] = {{") + +for row in range(0, EEPROM_SIZE, 16): + chunk = eeprom[row:row+16] + hex_str = ", ".join(f"0x{b:02X}" for b in chunk) + # Only print non-0xFF rows with annotation, skip bulk 0xFF + if all(b == 0xFF for b in chunk): + if row == 64 or row % 256 == 0: + print(f" /* 0x{row:04X}: (0xFF padding) */") + continue + print(f" /* 0x{row:04X} */ {hex_str},") + +print("};") +print("") +print(f"static const uint32_t ESI_EEPROM_SIZE = {EEPROM_SIZE}U;") +print("") +print("#endif /* COMM_SSC_PORT_EEPROM_IMAGE_H */") diff --git a/GD32E508_DCU_APP/comm/ssc/port/hw_gd32exmc.cpp b/GD32E508_DCU_APP/comm/ssc/port/hw_gd32exmc.cpp new file mode 100644 index 0000000..b966488 --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/port/hw_gd32exmc.cpp @@ -0,0 +1,345 @@ +/** + * @file hw_gd32exmc.cpp + * @brief SSC V5.11 硬件 port 层 — GD32E508 + LAN9252 (EXMC 16-bit HBI Indexed Mode) + * + * 实现 el9800hw.h 中声明的全部 HW_* 函数,将 SSC 的 ESC 访问请求 + * 转发到 EscExmcBus (EXMC NOR/SRAM Region0, 16-bit 内存映射)。 + * + * HBI Indexed 寻址协议: + * 1. 写 HBI_IDX_0 (EXMC offset 0x00000) = ESC 寄存器字节地址 + * 2. 读/写 HBI_DATA_0 低 word (EXMC offset 0x20000, A17=1) + * 3. 读/写 HBI_DATA_0 高 word (EXMC offset 0x30000, A17=1, A16=1) + * + * 并发保护: + * - HW_EscRead/HW_EscWrite (主循环):调用前 SSC 已执行 DISABLE_AL_EVENT_INT + * (mask EXTI0+EXTI1),保证 HBI IDX 不被 ISR 抢占。 + * - HW_EscReadIsr/HW_EscWriteIsr (ISR):在 preempt=0 的 SYNC0 ISR 内调用, + * 天然不会被抢占。 + */ + +/* SSC 配置与类型 */ +#define _EL9800HW_ 1 +#include "ecat_def.h" +#include "esc.h" +#include "el9800hw.h" +#include "ecatslv.h" + +/* 项目硬件驱动 */ +#include "comm/esc_exmc_bus.h" +#include "sys/config.h" + +#include +#include + +/*--------------------------------------------------------------------------- + * HBI Indexed Mode EXMC 偏移 (与 esc_lan9252.cpp 一致) + *---------------------------------------------------------------------------*/ +namespace { + +constexpr uint32_t HBI_IDX0_OFFSET = 0x00000U; /* A2=0, A1=0 */ +constexpr uint32_t HBI_DATA0_LOW_OFF = 0x20000U; /* A2=1(A17), A1=0(A16) */ +constexpr uint32_t HBI_DATA0_HIGH_OFF = 0x30000U; /* A2=1(A17), A1=1(A16) */ + +/* AL Event Mask 寄存器影子 (ESC 0x0204) */ +volatile uint16_t s_alEventMask = 0xFFFFU; + +/* SSC 看门狗软件定时器 (DWT CYCCNT 基准) */ +volatile uint32_t s_timerBase = 0U; + +/*--------------------------------------------------------------------------- + * 内部 HBI 访问原语 (无锁,调用者负责并发保护) + *---------------------------------------------------------------------------*/ + +inline void hbiSetIndex(uint16_t addr) +{ + EscExmcBus::write16(HBI_IDX0_OFFSET, addr); +} + +inline uint16_t hbiReadLow(void) +{ + return EscExmcBus::read16(HBI_DATA0_LOW_OFF); +} + +inline uint16_t hbiReadHigh(void) +{ + return EscExmcBus::read16(HBI_DATA0_HIGH_OFF); +} + +inline void hbiWriteLow(uint16_t v) +{ + EscExmcBus::write16(HBI_DATA0_LOW_OFF, v); +} + +inline void hbiWriteHigh(uint16_t v) +{ + EscExmcBus::write16(HBI_DATA0_HIGH_OFF, v); +} + +/** 从 ESC 地址读取 Len 字节到 pData (按 16-bit word 对齐处理) */ +void hbiRead(void *pData, uint16_t address, uint16_t len) +{ + uint8_t *dst = (uint8_t *)pData; + uint16_t addr = address; + uint16_t remaining = len; + + /* 奇数起始地址:读低 byte */ + if ((addr & 1U) != 0U && remaining > 0U) { + hbiSetIndex(addr & 0xFFFEU); + uint16_t w = hbiReadLow(); + *dst++ = (uint8_t)((w >> 8) & 0xFFU); + addr++; + remaining--; + } + + /* 对齐的 16-bit word 读取 */ + while (remaining >= 2U) { + hbiSetIndex(addr); + uint16_t w = hbiReadLow(); + dst[0] = (uint8_t)(w & 0xFFU); + dst[1] = (uint8_t)((w >> 8) & 0xFFU); + dst += 2; + addr += 2; + remaining -= 2; + } + + /* 尾部奇数字节 */ + if (remaining > 0U) { + hbiSetIndex(addr); + uint16_t w = hbiReadLow(); + *dst = (uint8_t)(w & 0xFFU); + } +} + +/** 向 ESC 地址写入 Len 字节 (按 16-bit word 对齐处理) */ +void hbiWrite(const void *pData, uint16_t address, uint16_t len) +{ + const uint8_t *src = (const uint8_t *)pData; + uint16_t addr = address; + uint16_t remaining = len; + + /* 奇数起始地址:RMW 高 byte */ + if ((addr & 1U) != 0U && remaining > 0U) { + uint16_t aligned = addr & 0xFFFEU; + hbiSetIndex(aligned); + uint16_t w = hbiReadLow(); + w = (w & 0x00FFU) | ((uint16_t)(*src++) << 8); + hbiSetIndex(aligned); + hbiWriteLow(w); + addr++; + remaining--; + } + + /* 对齐的 16-bit word 写入 */ + while (remaining >= 2U) { + uint16_t w = (uint16_t)src[0] | ((uint16_t)src[1] << 8); + hbiSetIndex(addr); + hbiWriteLow(w); + src += 2; + addr += 2; + remaining -= 2; + } + + /* 尾部奇数字节:RMW 低 byte */ + if (remaining > 0U) { + hbiSetIndex(addr); + uint16_t w = hbiReadLow(); + w = (w & 0xFF00U) | (uint16_t)(*src); + hbiSetIndex(addr); + hbiWriteLow(w); + } +} + +} /* anonymous namespace */ + +/*--------------------------------------------------------------------------- + * SSC Port API 实现 (extern "C" linkage) + *---------------------------------------------------------------------------*/ +extern "C" { + +/** + * @brief 硬件初始化。 + * @note EXMC 总线与 ESC 基本探活已由 EthercatSlave::init() 完成。 + * 本函数在 SSC MainInit() 之前被调用,做 SSC 特有的初始化: + * - 使能 DWT 周期计数器 (若尚未使能) + * - 读取 AL Event Mask 寄存器影子 + */ +UINT8 HW_Init(void) +{ + /* DWT 周期计数器 (可能已被 EscLan9252::init 使能,幂等操作) */ + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; + DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; + s_timerBase = DWT->CYCCNT; + + /* 读取当前 AL Event Mask */ + hbiSetIndex(ESC_AL_EVENTMASK_OFFSET); + s_alEventMask = hbiReadLow(); + + return 0; /* 0 = success */ +} + +void HW_Release(void) +{ + /* 无需释放资源 */ +} + +/** + * @brief 读取 AL Event 寄存器 (0x0220),主循环上下文。 + * @note 调用者已执行 DISABLE_AL_EVENT_INT。 + */ +UINT16 HW_GetALEventRegister(void) +{ + hbiSetIndex(ESC_AL_EVENT_OFFSET); + return hbiReadLow(); +} + +/** + * @brief 读取 AL Event 寄存器 (0x0220),ISR 上下文。 + */ +UINT16 HW_GetALEventRegister_Isr(void) +{ + hbiSetIndex(ESC_AL_EVENT_OFFSET); + return hbiReadLow(); +} + +/** + * @brief 清除 AL Event Mask 中的指定位 (写 ESC 0x0204)。 + */ +void HW_ResetALEventMask(UINT16 intMask) +{ + s_alEventMask &= ~intMask; + hbiSetIndex(ESC_AL_EVENTMASK_OFFSET); + hbiWriteLow(s_alEventMask); +} + +/** + * @brief 置位 AL Event Mask 中的指定位 (写 ESC 0x0204)。 + */ +void HW_SetALEventMask(UINT16 intMask) +{ + s_alEventMask |= intMask; + hbiSetIndex(ESC_AL_EVENTMASK_OFFSET); + hbiWriteLow(s_alEventMask); +} + +/** + * @brief 设置 EtherCAT Run/Error LED。 + * @note LAN9252 支持 ESC 硬件驱动 LED (ESC_SUPPORT_ECAT_LED=1), + * 通过寄存器 0x0138/0x0139 控制。若硬件未接 LED 则忽略。 + */ +void HW_SetLed(UINT8 RunLed, UINT8 ErrLed) +{ + /* Run LED override (0x0138): bit0 = LED on */ + hbiSetIndex(ESC_RUN_LED_OVERRIDE); + hbiWriteLow(RunLed ? 0x0001U : 0x0000U); + + /* Error LED override (0x0139): bit0 = LED on */ + hbiSetIndex(ESC_ERROR_LED_OVERRIDE); + hbiWriteLow(ErrLed ? 0x0001U : 0x0000U); +} + +/*--------------------------------------------------------------------------- + * ESC 内存读写 — 主循环版本 (SSC 已 mask 中断) + *---------------------------------------------------------------------------*/ + +void HW_EscRead(MEM_ADDR *pData, UINT16 Address, UINT16 Len) +{ + hbiRead(pData, Address, Len); +} + +void HW_EscWrite(MEM_ADDR *pData, UINT16 Address, UINT16 Len) +{ + hbiWrite(pData, Address, Len); +} + +/*--------------------------------------------------------------------------- + * ESC 内存读写 — ISR 版本 (preempt=0 内调用,无需额外保护) + *---------------------------------------------------------------------------*/ + +void HW_EscReadIsr(MEM_ADDR *pData, UINT16 Address, UINT16 Len) +{ + hbiRead(pData, Address, Len); +} + +void HW_EscWriteIsr(MEM_ADDR *pData, UINT16 Address, UINT16 Len) +{ + hbiWrite(pData, Address, Len); +} + +/*--------------------------------------------------------------------------- + * SyncManager 通道控制 + *---------------------------------------------------------------------------*/ + +/** + * @brief 禁用指定 SM 通道 (清 SM Activate 寄存器 bit0)。 + * @param channel SM 通道号 (0-15) + */ +void HW_DisableSyncManChannel(UINT8 channel) +{ + /* SM 寄存器基址 0x0800,每个 SM 占 8 字节,Activate 在 offset +4 (bit0) */ + uint16_t smBase = 0x0800U + (uint16_t)channel * 8U; + uint16_t activateAddr = smBase + 4U; + + hbiSetIndex(activateAddr); + uint16_t val = hbiReadLow(); + val &= ~0x0001U; /* 清 bit0 = deactivate */ + hbiSetIndex(activateAddr); + hbiWriteLow(val); +} + +/** + * @brief 使能指定 SM 通道 (置 SM Activate 寄存器 bit0)。 + */ +void HW_EnableSyncManChannel(UINT8 channel) +{ + uint16_t smBase = 0x0800U + (uint16_t)channel * 8U; + uint16_t activateAddr = smBase + 4U; + + hbiSetIndex(activateAddr); + uint16_t val = hbiReadLow(); + val |= 0x0001U; + hbiSetIndex(activateAddr); + hbiWriteLow(val); +} + +/** + * @brief 获取指定 SM 通道的寄存器块指针。 + * @note SSC 用此函数读取 SM 配置 (StartAddr, Length, Control, Status, Activate)。 + * 由于 HBI 是间接访问,无法返回真实指针。SSC 在 EL9800_HW 模式下 + * 实际通过 HW_EscRead 读取 SM 寄存器,此函数返回 NULL 并由调用者 + * 使用 HW_EscRead 替代。 + * + * 检查 ecatslv.c 中 HW_GetSyncMan 的使用方式: + * 如果 SSC 直接解引用返回的指针,则需要提供一个本地缓冲。 + * 这里提供一个静态缓冲,每次调用时从 ESC 读入。 + */ +static TSYNCMAN s_smBuf; + +TSYNCMAN ESCMEM *HW_GetSyncMan(UINT8 channel) +{ + uint16_t smBase = 0x0800U + (uint16_t)channel * 8U; + hbiRead(&s_smBuf, smBase, SIZEOF_SM_REGISTER); + return &s_smBuf; +} + +/*--------------------------------------------------------------------------- + * 定时器 (SSC 看门狗用) + *---------------------------------------------------------------------------*/ + +/** + * @brief 获取定时器当前值 (ticks since HW_ClearTimer)。 + * @note 使用 DWT CYCCNT (180MHz),ECAT_TIMER_INC_P_MS=180000。 + */ +UINT32 HW_GetTimerValue(void) +{ + return DWT->CYCCNT - s_timerBase; +} + +/** + * @brief 清零定时器基准。 + */ +void HW_ClearTimerValue(void) +{ + s_timerBase = DWT->CYCCNT; +} + +} /* extern "C" */ diff --git a/GD32E508_DCU_APP/comm/ssc/src/coeappl.c b/GD32E508_DCU_APP/comm/ssc/src/coeappl.c new file mode 100644 index 0000000..5c6ea06 --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/src/coeappl.c @@ -0,0 +1,780 @@ +/** +\addtogroup CoE CAN Application Profile over EtherCAT +@{ +*/ + +/** +\file coeappl.c +\author EthercatSSC@beckhoff.com +\brief Implementation +This file contains an example for CoE services and the CoE object dictionary + +\version 5.11 + +
Changes to version V5.10:
+V5.11 COE1: update invalid end entry in the object dictionaries (error with some compilers)
+V5.11 COE3: change 0x10F3.2 (Sync Error limit) from UINT32 to UINT16 (according to the ETG.1020)
+V5.11 ECAT10: change PROTO handling to prevent compiler errors
+V5.11 ECAT4: enhance SM/Sync monitoring for input/output only slaves
+
Changes to version V5.01:
+V5.10 COE1: Define one entry description for all 0x1C3x objects and change data type of SI11,12,13 to UINT16 (according ETG.1020)
+V5.10 COE2: Change return value of Get ObjectSize to UINT32
+ Change object size to UINT32
+V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)
+ Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings
+ Update 0x1C3x entries
+
Changes to version V5.0:
+V5.01 APPL3: Include library demo application
+V5.01 COE1: Remove alignment entry (SI33) from objects 0x1C32/0x1C33
+V5.01 EL9800 1: Read/Write functions for setting object 0x8020 moved to el9800appl.c
+V5.01 EL9800 2: Add TxPdo Parameter object 0x1802
+V5.01 SDO6: Update SDO response interface handling. (used if the object access function returns "ABORTIDX_WORKING" and SDO_RES_INTERFACE is active)
+
Changes to version V4.40:
+V5.0 TEST1: Add test application. See Application Note ET9300 for more details.
+V5.0 COE1: Add reserved Subindex7 to object 0x1C32 and 0x1C33.
+V5.0 COE4: Create object dictionary on device startup.
+V5.0 COE6: Update entry descriptions for object with the code ARRAY.
+V5.0 ECAT2: Reference to application specific object dictionary is set to "ApplicationObjDic".
+
Changes to version V4.30:
+V4.40 COE 6: change dynamic object dictionary handling
+V4.40 CiA402 2: enable freerun Sync Mode for CiA402 Device (The motion controller application will only be triggered if DC Synchronisation is disabled)
+V4.40 COE5: move identification and synchronisation object values to ecat_def.h
+V4.40 DIAG1: Add diagnosis object 0x10F3
+V4.40 OBJ3: Update the global object dictionary pointer if a new list head is added
+V4.40 COE4: Prevent access NULL pointer
+V4.40 SYNC1: Initialize 0x1C3x objects
+V4.40 HW1: Add (adapt) objects for FC1100 support
+
Changes to version V4.20:
+V4.30 OBJ 3: Handle object dictionary in double link list
+V4.30 OBJ 2: General EtherCAT device objects are moved from chnappl.h to coeappl.c
+V4.30 SYNC: set 0x1C32:05; 0x1C33:05 (minCyleTime) to MIN_PD_CYCLE_TIME + (should be set dynamic if required)
+V4.30 PDO: rename PDO specific function calls + (PDO specific functions are moved to ecatappl.c)
+V4.20 PIC24: Add EL9800_4 (PIC24) required source code
+V4.11 ECAT 1: C166: MinDelayTime renamed in CalcAndCopyTime
+
Changes to version V4.07:
+V4.10 ECAT 1: To save and restore arrays in the object directory.
+ The code to get the size of an array entry was changed.
+V4.10 COE 1: The SM-Parameter were extended
+
Changes to version V4.06:
+V4.07 COEAPPL 1: The PDO numbers were adapted according top the modular device profile,
+ the PDO numbers increment now with every module (in chnappl.h)
+V4.07 COEAPPL 2: The example is working for the NIOS with the evaluation board DBC2C20 V1.2
+ which is available by Altera
+V4.07 ECAT 1: The sources for SPI and MCI were merged (in ecat_def.h
+ set the switch MCI_HW to 1 when using the MCI,
+ set the switch SPI_HW to 1 when using the SPI
+
Changes to version V4.03:
+V4.04 SDO 1: The SDO interface was changed in that way that a SDO response
+ could be sent by the application to a later time. In that case
+ the functions OBJ_Read and OBJ_Write shall return the value
+ ABORTIDX_WORKING. To send the SDO response the new function SDOS_SdoRes
+ has to be called by the application. While waiting for the call
+ of SDOS_SdoRes the SDO interface will answer to another SDO request
+ with the error MBXERR_SERVICEINWORK in the mailbox protocol.
+ In this example the reading and writing of object 0x8020 is delayed,
+ the function SDOS_SdoRes will be called from COE_Main.
+
Changes to version V4.00:
+V4.01 COEAPPL 1: The object dictionary and the mapping functions are now defined
+ in chnappl.h (chnappl.inc made problems with some developing tools)
+
Changes to version V3.20:
+V4.00 COEAPPL 1: The object dictionary and the mapping functions are now defined
+ in chnappl.c and were adapted to the modular device profile
+V4.00 COEAPPL 2: The handling of backup parameters was included according to
+ the EtherCAT Guidelines and Protocol Enhancements Specification
+V4.00 COEAPPL 3: The handling of the TxPDO Toggle was included according to
+ the EtherCAT Guidelines and Protocol Enhancements Specification
+V4.00 COEAPPL 4: The handling of the TxPDO State was included according to
+ the EtherCAT Guidelines and Protocol Enhancements Specification
+V4.00 ECAT 1: The handling of the Sync Manager Parameter was included according to
+ the EtherCAT Guidelines and Protocol Enhancements Specification +*/ + +/*----------------------------------------------------------------------------------------- +------ +------ Includes +------ +-----------------------------------------------------------------------------------------*/ + +#include "ecat_def.h" + + +#include "ecatslv.h" + +#define _COEAPPL_ 1 +#include "coeappl.h" +#undef _COEAPPL_ +/* ECATCHANGE_START(V5.11) ECAT10*/ +/*remove definition of _COEAPPL_ (#ifdef is used in coeappl.h)*/ +/* ECATCHANGE_END(V5.11) ECAT10*/ + +#define _OBJD_ + +/*Add Application specific Objects*/ +#include "el9800appl.h" + + +#undef _OBJD_ +/*----------------------------------------------------------------------------------------- +------ +------ Backup Parameter +------ +-----------------------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------------------- +------ +------ Generic EtherCAT device object +------ +-----------------------------------------------------------------------------------------*/ + +/*--------------------------------------------- +- 0x1000 +-----------------------------------------------*/ +/** + * \brief 0x1000 (Device type) variable to handle the object data + */ +UINT32 u32Devicetype = (DEVICE_PROFILE_TYPE); + +/** + * \brief 0x1000 (Device type) entry description + */ +OBJCONST TSDOINFOENTRYDESC OBJMEM sEntryDesc0x1000 = {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}; + +/** + * \brief 0x1000 (Device type) object name + */ +OBJCONST UCHAR OBJMEM aName0x1000[] = "Device type"; + + +/*--------------------------------------------- +- 0x1001 +-----------------------------------------------*/ +/** + * \brief 0x1001 (Error Register) variable to handle the object data + */ +UINT16 u16ErrorRegister = 0x0; + +/** + * \brief 0x1001 (Error Register) entry description + */ +OBJCONST TSDOINFOENTRYDESC OBJMEM sEntryDesc0x1001 = {DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ}; + +/** + * \brief 0x1001 (Error Register) object name + */ +OBJCONST UCHAR OBJMEM aName0x1001[] = "Error register"; + + +/*--------------------------------------------- +- 0x1008 +-----------------------------------------------*/ +/** + * \brief 0x1008 (Device name) variable to handle the object data + */ +CHAR acDevicename[] = DEVICE_NAME; + +/** + * \brief 0x1008 (Device name) entry description + */ +OBJCONST TSDOINFOENTRYDESC OBJMEM sEntryDesc0x1008 = {DEFTYPE_VISIBLESTRING, BYTE2BIT(DEVICE_NAME_LEN), ACCESS_READ}; + +/** + * \brief 0x1008 (Device name) object name + */ +OBJCONST UCHAR OBJMEM aName0x1008[] = "Device name"; + + +/*--------------------------------------------- +- 0x1009 +-----------------------------------------------*/ +/** + * \brief 0x1009 (Hardware version) variable to handle the object data + */ +CHAR acHardwareversion[] = DEVICE_HW_VERSION; + +/** + * \brief 0x1009 (Hardware version) entry description + */ +OBJCONST TSDOINFOENTRYDESC OBJMEM sEntryDesc0x1009 = {DEFTYPE_VISIBLESTRING,BYTE2BIT(DEVICE_HW_VERSION_LEN), ACCESS_READ}; + +/** + * \brief 0x1009 (Hardware version) object name + */ +OBJCONST UCHAR OBJMEM aName0x1009[] = "Hardware version"; + + +/*--------------------------------------------- +- 0x100A +-----------------------------------------------*/ +/** + * \brief 0x100A (Software version) variable to handle the object data + */ +CHAR acSoftwareversion[] = DEVICE_SW_VERSION; + +/** + * \brief 0x1009 (Hardware version) entry description + */ +OBJCONST TSDOINFOENTRYDESC OBJMEM sEntryDesc0x100A = {DEFTYPE_VISIBLESTRING,BYTE2BIT(DEVICE_SW_VERSION_LEN), ACCESS_READ}; + +/** + * \brief 0x1009 (Hardware version) object name + */ +OBJCONST UCHAR OBJMEM aName0x100A[] = "Software version"; + + + + +/*--------------------------------------------- +- 0x1018 +-----------------------------------------------*/ +/** + * \brief 0x1018 (Identity) object structure + */ +typedef struct OBJ_STRUCT_PACKED_START { + UINT16 u16SubIndex0; /**< \brief SubIndex 0*/ + UINT32 u32VendorID; /**< \brief EtherCAT vendor ID */ + UINT32 u32Productcode; /**< \brief Product code*/ + UINT32 u32Revision; /**< \brief Revision number*/ + UINT32 u32Serialnumber; /**< \brief Serial number*/ +} OBJ_STRUCT_PACKED_END +TOBJ1018; + +/** + * \brief 0x1018 (Identity) variable to handle the object data + */ +TOBJ1018 sIdentity = {4, (VENDOR_ID), (PRODUCT_CODE), (REVISION_NUMBER), (SERIAL_NUMBER)}; + +/** + * \brief 0x1018 (Identity) entry descriptions + * + * Subindex 000 + * SubIndex 001: Vendor ID + * SubIndex 002: Product code + * SubIndex 003: Revision + * SubIndex 004: Serial number + */ +OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x1018[] = { + {DEFTYPE_UNSIGNED8, 0x8, ACCESS_READ }, + {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, + {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, + {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, + {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}}; + +/** + * \brief 0x1018 (Identity) object and entry names + */ +OBJCONST UCHAR OBJMEM aName0x1018[] = "Identity\000Vendor ID\000Product code\000Revision\000Serial number\000\377"; + + + +/*--------------------------------------------- +- 0x10F1 +-----------------------------------------------*/ +/** + * \brief 0x10F1 (Error settings) entry description + * Subindex 000 + * SubIndex 001: Local Error Reaction + * SubIndex 002: Sync Error Counter Limit + */ +OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x10F1[] = { + {DEFTYPE_UNSIGNED8, 0x8, ACCESS_READ }, + {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READWRITE}, +/*ECATCHANGE_START(V5.11) COE3*/ + {DEFTYPE_UNSIGNED16, 0x10, ACCESS_READWRITE}}; +/*ECATCHANGE_END(V5.11) COE3*/ + +/** + * \brief 0x10F1 (Error settings) object and entry names + */ +OBJCONST UCHAR OBJMEM aName0x10F1[] = "Error Settings\000Local Error Reaction\000Sync Error Counter Limit\000\377"; + + +//object declaration and initialization in objdef.h + + +/*--------------------------------------------- +- 0x1C00 +-----------------------------------------------*/ +/** + * \brief 0x1C00 (Sync manager type) object structure + */ +typedef struct OBJ_STRUCT_PACKED_START { + UINT16 u16SubIndex0; /**< \brief SubIndex 0*/ + UINT8 aEntries[4]; /**< \brief Entry buffer (one for each SyncManager)*/ +} OBJ_STRUCT_PACKED_END +TOBJ1C00; + +/** + * \brief 0x1C00 (Sync manager type) variable to handle the object data + * + * SI 0 : 4 (4 SyncManagers available) + * SI1 (SM0): Mailbox Out (0x1) + * SI2 (SM1): Mailbox In (0x2) + * SI3 (SM2): Process data Out (0x3) + * SI4 (SM3): Process data In (0x4) + * + */ +TOBJ1C00 sSyncmanagertype = {0x04, {0x01, 0x02, 0x03, 0x04}}; + + +/** + * \brief 0x1C00 (Sync manager type) entry descriptions + * + * The object is an array so only the first entry is described (which is used for all entries). + */ +OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x1C00[2] = { + {DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ}, + {DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ}}; + +/** + * \brief 0x1C00 (Sync manager type) object name + */ +OBJCONST UCHAR OBJMEM aName0x1C00[] = "Sync manager type"; + + +/*--------------------------------------------- +- 0x1C3x +-----------------------------------------------*/ +/** + * \brief 0x1C3x (SyncManager parameter) entry description + * + * The entry descriptions are used for 0x1C32 and 0x1C33 + */ +OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x1C3x[] = { + {DEFTYPE_UNSIGNED8, 0x8, ACCESS_READ }, /* Subindex 000 */ + {DEFTYPE_UNSIGNED16, 0x10, (ACCESS_READ | ACCESS_WRITE_PREOP)}, /* SubIndex 001: Synchronization Type */ + {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* SubIndex 002: Cycle Time */ + {0x0000, 0x20, 0}, /* SubIndex 003: Shift Time (not supported)*/ + {DEFTYPE_UNSIGNED16, 0x10, ACCESS_READ}, /* SubIndex 004: Synchronization Types supported */ + {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* SubIndex 005: Minimum Cycle Time */ + {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* SubIndex 006: Calc and Copy Time */ + {0x0000, 0x20, 0}, /* Subindex 007 Minimum Delay Time (not supported)*/ + {DEFTYPE_UNSIGNED16, 0x10, ACCESS_READWRITE}, /* SubIndex 008: Get Cycle Time */ + {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* SubIndex 009: Delay Time */ + {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READWRITE}, /* SubIndex 010: Sync0 Cycle Time */ + {DEFTYPE_UNSIGNED16, 0x10, ACCESS_READ}, /* SubIndex 011: SM-Event Missed */ + {DEFTYPE_UNSIGNED16, 0x10, ACCESS_READ}, /* SubIndex 012: Cycle Time Too Small */ + {0x0000, 0x10, 0}, /* SubIndex 013: Shift Too Short Counter (not supported)*/ + {0x0000, 0x10, 0}, /* Subindex 014: RxPDO Toggle Failed (not supported)*/ + {0x0000, 0x20, 0}, /* Subindex 015: Minimum Cycle Distance (not supported)*/ + {0x0000, 0x20, 0}, /* Subindex 016: Maximum Cycle Distance (not supported)*/ + {0x0000, 0x20, 0}, /* Subindex 017: Minimum SM Sync Distance (not supported)*/ + {0x0000, 0x20, 0}, /* Subindex 018: Maximum SM Sync Distance (not supported)*/ + {0x0000, 0, 0}, /* Subindex 019 doesn't exist */ + {0x0000, 0, 0}, /* Subindex 020 doesn't exist */ + {0x0000, 0, 0}, /* Subindex 021 doesn't exist */ + {0x0000, 0, 0}, /* Subindex 022 doesn't exist */ + {0x0000, 0, 0}, /* Subindex 023 doesn't exist */ + {0x0000, 0, 0}, /* Subindex 024 doesn't exist */ + {0x0000, 0, 0}, /* Subindex 025 doesn't exist */ + {0x0000, 0, 0}, /* Subindex 026 doesn't exist */ + {0x0000, 0, 0}, /* Subindex 027 doesn't exist */ + {0x0000, 0, 0}, /* Subindex 028 doesn't exist */ + {0x0000, 0, 0}, /* Subindex 029 doesn't exist */ + {0x0000, 0, 0}, /* Subindex 030 doesn't exist */ + {0x0000, 0, 0}, /* Subindex 031 doesn't exist */ + {DEFTYPE_BOOLEAN, 0x01, ACCESS_READ}}; /* SubIndex 032: Sync error */ + +/*ECATCHANGE_START(V5.11) ECAT4*/ +/** + * \brief 0x1C32 (SyncManager 2 parameter) object and entry names + */ +OBJCONST UCHAR OBJMEM aName0x1C32[] = "SM output parameter\000Synchronization Type\000Cycle Time\000\000Synchronization Types supported\000Minimum Cycle Time\000Calc and Copy Time\000\000Get Cycle Time\000Delay Time\000Sync0 Cycle Time\000SM-Event Missed\000Cycle Time Too Small\000Shift Time Too Short\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000Sync Error\000\377"; +/*ECATCHANGE_END(V5.11) ECAT4*/ + +/*ECATCHANGE_START(V5.11) ECAT4*/ +/** + * \brief 0x1C33 (SyncManager 3 parameter) object and entry names + */ +OBJCONST UCHAR OBJMEM aName0x1C33[] = "SM input parameter\000Synchronization Type\000Cycle Time\000\000Synchronization Types supported\000Minimum Cycle Time\000Calc and Copy Time\000\000Get Cycle Time\000Delay Time\000Sync0 Cycle Time\000SM-Event Missed\000Cycle Time Too Small\000Shift Time Too Short\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000Sync Error\000\377"; + +/****************************************************************************** +** Object Dictionary +******************************************************************************/ +/** + * \brief Object dictionary pointer + */ +TOBJECT OBJMEM * ObjDicList = NULL; + +/** + * \brief List of generic application independent objects + */ +TOBJECT OBJMEM GenObjDic[] = { + /* Object 0x1000 */ + {NULL,NULL, 0x1000, {DEFTYPE_UNSIGNED32, 0 | (OBJCODE_VAR << 8)}, &sEntryDesc0x1000, aName0x1000, &u32Devicetype, NULL, NULL, 0x0000 }, + /* Object 0x1001 */ + {NULL,NULL, 0x1001, {DEFTYPE_UNSIGNED8, 0 | (OBJCODE_VAR << 8)}, &sEntryDesc0x1001, aName0x1001, &u16ErrorRegister, NULL, NULL, 0x0000 }, +/* Object 0x1008 */ + {NULL,NULL, 0x1008, {DEFTYPE_VISIBLESTRING, 0 | (OBJCODE_VAR << 8)}, &sEntryDesc0x1008, aName0x1008, acDevicename, NULL, NULL, 0x0000 }, + /* Object 0x1009 */ + {NULL,NULL, 0x1009, {DEFTYPE_VISIBLESTRING, 0 | (OBJCODE_VAR << 8)}, &sEntryDesc0x1009, aName0x1009, acHardwareversion, NULL, NULL, 0x0000 }, + /* Object 0x100A */ + {NULL,NULL, 0x100A, {DEFTYPE_VISIBLESTRING, 0 | (OBJCODE_VAR << 8)}, &sEntryDesc0x100A, aName0x100A, acSoftwareversion, NULL, NULL, 0x0000 }, + /* Object 0x1018 */ + {NULL,NULL, 0x1018, {DEFTYPE_IDENTITY, 4 | (OBJCODE_REC << 8)}, asEntryDesc0x1018, aName0x1018, &sIdentity, NULL, NULL, 0x0000 }, + /* Object 0x10F1 */ + {NULL,NULL, 0x10F1, {DEFTYPE_RECORD, 2 | (OBJCODE_REC << 8)}, asEntryDesc0x10F1, aName0x10F1, &sErrorSettings, NULL, NULL, 0x0000 }, + /* Object 0x1C00 */ + {NULL,NULL, 0x1C00, {DEFTYPE_UNSIGNED8, 4 | (OBJCODE_ARR << 8)}, asEntryDesc0x1C00, aName0x1C00, &sSyncmanagertype, NULL, NULL, 0x0000 }, + /* Object 0x1C32 */ + {NULL,NULL, 0x1C32, {DEFTYPE_SMPAR, 32 | (OBJCODE_REC << 8)}, asEntryDesc0x1C3x, aName0x1C32, &sSyncManOutPar, NULL, NULL, 0x0000 }, + /* Object 0x1C33 */ + {NULL,NULL, 0x1C33, {DEFTYPE_SMPAR, 32 | (OBJCODE_REC << 8)}, asEntryDesc0x1C3x, aName0x1C33, &sSyncManInPar, NULL, NULL, 0x0000 }, + + /*end of entries*/ +/*ECATCHANGE_START(V5.11) COE1*/ + {NULL,NULL, 0xFFFF, {0, 0}, NULL, NULL, NULL, NULL, NULL, 0x000}}; +/*ECATCHANGE_END(V5.11) COE1*/ + + +/*----------------------------------------------------------------------------------------- +------ +------ Functions +------ +-----------------------------------------------------------------------------------------*/ + +///////////////////////////////////////////////////////////////////////////////////////// +/** + + \brief returns the pointer to the object dictionary +*//////////////////////////////////////////////////////////////////////////////////////// +OBJCONST TOBJECT OBJMEM * COE_GetObjectDictionary(void) +{ + return (OBJCONST TOBJECT OBJMEM *) ObjDicList; +} + + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \brief This function initialize the several objects +*//////////////////////////////////////////////////////////////////////////////////////// + +void COE_ObjInit(void) +{ + + /* initialize the Sync Manager Output parameter object 0x1C32 */ + + sSyncManOutPar.subindex0 = 32; + /* + subindex 1 contains the actual synchronization mode, it could be written + from the master to switch between ECAT FreeRun and ECAT Synchron Mode + if the slave supports both modes, + in DC mode (selected by the DC registers) this value will be overwritten + with SYNCTYPE_DCSYNC0 or SYNCTYPE_DCSYNC1 */ + /*default mode is ECAT Synchron Mode */ + sSyncManOutPar.u16SyncType = SYNCTYPE_FREERUN; + + /* subindex 2 contains the cycle time of the application, + in ECAT FreeRun mode it could be used for a timer interrupt to run the application, + in ECAT Synchron mode it could be written from the master with its local cycle time + that the slave can check if this cycle time is supported, + in DC Mode this value will be overwritten with the DC cycle time register */ + sSyncManOutPar.u32CycleTime = 0; + /* only for DC Mode important: the subindex 3 contains the time shift between the + SYNC0 (SYNC1) signal and when the outputs are put to the hardware to allow the + master a very exactly calculation of delay times*/ + sSyncManOutPar.u32ShiftTime = 0; + + /* the subindex 4 contains the supported synchronization types */ + + sSyncManOutPar.u16SyncTypesSupported = SYNCTYPE_FREERUNSUPP /* ECAT FreeRun Mode is supported */ + | SYNCTYPE_TIMESVARIABLE /* the execution times depend on the connected modules */ + | SYNCTYPE_SYNCHRONSUPP /* ECAT Synchron Mode is supported */ + | SYNCTYPE_DCSYNC0SUPP /* DC Sync0 Mode is supported */ + | SYNCTYPE_DCSYNC1SUPP /* DC Sync1 Mode is supported */ + | SYNCTYPE_SUBCYCLESUPP /*Subordinated application cycles supported*/ + ; + + /* subindex 5 contains the minimum cycle time the slave is able to support, + will be calculated dynamically because it depends on the connected modules + (in this example we will make an online measurement in the ESC Interrupt Routine). + For the sample application this value is set to MIN_PD_CYCLE_TIME */ + sSyncManOutPar.u32MinCycleTime = MIN_PD_CYCLE_TIME; + /* only for DC Mode important: subindex 6 contains the minimum delay time the slave + needs after receiving the SM2-event before the SYNC0(SYNC1) can be received without delays + will be calculated dynamically because it depends on the connected modules + (in this example we will make an online measurement in the ESC Interrupt Routine) */ + sSyncManOutPar.u32CalcAndCopyTime = (PD_OUTPUT_CALC_AND_COPY_TIME); + + /*subindex 8: trigger cycle time measurement*/ + sSyncManOutPar.u16GetCycleTime = 0; + + /*subindex 9: time from start driving outputs until outputs are valid*/ + sSyncManOutPar.u32DelayTime = (PD_OUTPUT_DELAY_TIME); + + /*subindex 32: indicates if a synchronisation error has occurred*/ + sSyncManOutPar.u8SyncError = 0; + +/*ECATCHANGE_START(V5.11) ECAT4*/ + /* initialize the Sync Manager Input parameter object 0x1C33 */ + sSyncManInPar.subindex0 = 32; + /* default mode is ECAT Synchron Mode, if output size > 0 the inputs are updated with the SM2-event */ + sSyncManInPar.u16SyncType = SYNCTYPE_FREERUN; + + /* subindex 2: same as 0x1C32:02 */ + sSyncManInPar.u32CycleTime = sSyncManOutPar.u32CycleTime; + /* only for DC Mode important: subindex 3 contains the time shift between the + SYNC0 (SYNC1) signal and when the inputs are got to the hardware to allow the + master a very exactly calculation of delay times, + will be calculated dynamically because it depends on the connected modules + (in this example we will make an online measurement in the ESC Interrupt Routine) */ + sSyncManInPar.u32ShiftTime = 0; + /* subindex 4: same as 0x1C32:04 */ + sSyncManInPar.u16SyncTypesSupported = sSyncManOutPar.u16SyncTypesSupported; + + /* subindex 5: same as 0x1C32:05 */ + sSyncManInPar.u32MinCycleTime = MIN_PD_CYCLE_TIME; + /* subindex 6: delay read inputs, calculation and copy to SM buffer*/ + sSyncManInPar.u32CalcAndCopyTime = (PD_INPUT_CALC_AND_COPY_TIME); + /*subindex 8: trigger cycle time measurement*/ + sSyncManInPar.u16GetCycleTime = 0; + /*subindex 9: delay to prepare input latch*/ + sSyncManInPar.u32DelayTime = (PD_INPUT_DELAY_TIME); + + /*subindex 32: incremented if a synchronisation error has occurred*/ + sSyncManInPar.u8SyncError = 0; +/*ECATCHANGE_END(V5.11) ECAT4*/ + + /*Indicate no user specified Sync mode*/ + bSyncSetByUser = FALSE; + + { + UINT16 result = COE_ObjDictionaryInit(); + if(result != 0) + { + /*clear already linked objects*/ + COE_ClearObjDictionary(); + } + } + + u8PendingSdo = 0; + bStoreCompleteAccess = FALSE; + u16StoreIndex = 0; + u8StoreSubindex = 0; + u32StoreDataSize = 0; + pStoreData = NULL; + pSdoPendFunc = NULL; + + pSdoSegData = NULL; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \return 0 object successful added to object dictionary + ALSTATUSCODE_XX add object failed + + \brief This function adds an object to the object dictionary + *//////////////////////////////////////////////////////////////////////////////////////// +UINT16 COE_AddObjectToDic(TOBJECT OBJMEM * pNewObjEntry) +{ + if(pNewObjEntry != NULL) + { + if(ObjDicList == NULL) + { + /* Object dictionary is empty */ + ObjDicList = pNewObjEntry; + ObjDicList->pNext = NULL; + ObjDicList->pPrev = NULL; + return 0; + } + else if(ObjDicList->Index > pNewObjEntry->Index) + { + /*insert new object dictionary head*/ + pNewObjEntry->pPrev = NULL; + pNewObjEntry->pNext = ObjDicList; + ObjDicList->pPrev = pNewObjEntry; + ObjDicList = pNewObjEntry; + return 0; + } + else + { + TOBJECT OBJMEM * pDicEntry = ObjDicList; + while(pDicEntry != NULL) + { + if(pDicEntry->Index == pNewObjEntry->Index) + { + /*object already exists in object dictionary*/ + return ALSTATUSCODE_UNSPECIFIEDERROR; + } + else if(pDicEntry->Index > pNewObjEntry->Index) + { + pNewObjEntry->pPrev = pDicEntry->pPrev; + pNewObjEntry->pNext = pDicEntry; + + if(pDicEntry->pPrev != NULL) + pDicEntry->pPrev->pNext = pNewObjEntry; + + pDicEntry->pPrev = pNewObjEntry; + + return 0; + } + else if(pDicEntry->pNext == NULL) + { + /*Last entry reached => add object to list tail*/ + pDicEntry->pNext = pNewObjEntry; + pNewObjEntry->pPrev = pDicEntry; + pNewObjEntry->pNext = NULL; + return 0; + } + else + { + /*The new object index is smaller than the current index. Get next object handle.*/ + pDicEntry = pDicEntry->pNext; + } + } + } + } + return ALSTATUSCODE_UNSPECIFIEDERROR; +} +///////////////////////////////////////////////////////////////////////////////////////// +/** + + \brief This function removes an object to the object dictionary +*//////////////////////////////////////////////////////////////////////////////////////// +void COE_RemoveDicEntry(UINT16 index) +{ + TOBJECT OBJMEM * pDicEntry = ObjDicList; + + while(pDicEntry != NULL) + { + if(pDicEntry->Index == index) + { + TOBJECT OBJMEM *pPrevEntry = pDicEntry->pPrev; + TOBJECT OBJMEM *pNextEntry = pDicEntry->pNext; + + if(pPrevEntry != NULL) + pPrevEntry->pNext = pNextEntry; + + if(pNextEntry != NULL) + pNextEntry->pPrev = pPrevEntry; + + pDicEntry->pPrev = NULL; + pDicEntry->pNext = NULL; + /*Update Object dictionary pointer if list head was removed*/ + if(pDicEntry->Index == ObjDicList->Index) + { + ObjDicList = pNextEntry; + } + return; + } + + pDicEntry = pDicEntry->pNext; + } +} +///////////////////////////////////////////////////////////////////////////////////////// +/** + + \brief This function clear the object dictionary +*//////////////////////////////////////////////////////////////////////////////////////// +void COE_ClearObjDictionary(void) +{ + TOBJECT OBJMEM * pObjEntry = (TOBJECT OBJMEM *) ObjDicList; + UINT16 Index = 0; + + while(pObjEntry != NULL) + { + Index = pObjEntry->Index; + pObjEntry = pObjEntry->pNext; + + COE_RemoveDicEntry(Index); + } + ObjDicList = NULL; +} + + +UINT16 AddObjectsToObjDictionary(TOBJECT OBJMEM * pObjEntry) +{ + UINT16 result = 0; + TOBJECT OBJMEM * pEntry = (TOBJECT OBJMEM *)pObjEntry; + + while(pEntry->Index != 0xFFFF) + { + result = COE_AddObjectToDic(pEntry); + + if(result != 0) + return result; + + pEntry++; + } + + return result; + +} +///////////////////////////////////////////////////////////////////////////////////////// +/** + \return 0 object dictionary created successful + ALSTATUSCODE_XX create object dictionary failed + + \brief This function initialize the object dictionary +*//////////////////////////////////////////////////////////////////////////////////////// +UINT16 COE_ObjDictionaryInit(void) +{ + UINT16 result = 0; + + /*Reset object dictionary pointer*/ + ObjDicList = NULL; + + result = AddObjectsToObjDictionary((TOBJECT OBJMEM *) GenObjDic); + + if(result != 0) + return result; + if(ApplicationObjDic != NULL) + { + result = AddObjectsToObjDictionary((TOBJECT OBJMEM *) ApplicationObjDic); + } + + return result; +} + + +///////////////////////////////////////////////////////////////////////////////////////// +/** + + \brief is called for background calculations which should not influence the + ECAT_Application in synchronous modes +*//////////////////////////////////////////////////////////////////////////////////////// + +void COE_Main(void) +{ + UINT8 abort = 0; + if(pSdoPendFunc != NULL) + { + abort = pSdoPendFunc(u16StoreIndex,u8StoreSubindex,u32StoreDataSize,pStoreData,bStoreCompleteAccess); + + if(abort != ABORTIDX_WORKING) + { + switch(u8PendingSdo) + { + case SDO_PENDING_SEG_WRITE: + if(pSdoSegData) + { + /* the allocated buffer can be released */ + FREEMEM( (UINT16 VARMEM *) pSdoSegData ); + pSdoSegData = NULL; + } + case SDO_PENDING_WRITE: + /*send SDO Download Response*/ + SDOS_SdoRes(abort, 0, NULL); + break; + + case SDO_PENDING_SEG_READ: + case SDO_PENDING_READ: + /* send SDO upload response */ + SDOS_SdoRes(abort, u32StoreDataSize, pStoreData); + break; + + } + + u8PendingSdo = 0; + u16StoreIndex = 0; + u8StoreSubindex = 0; + u32StoreDataSize = 0; + pStoreData = NULL; + bStoreCompleteAccess = 0; + pSdoPendFunc = NULL; + } + } +} + +/** @} */ diff --git a/GD32E508_DCU_APP/comm/ssc/src/ecatappl.c b/GD32E508_DCU_APP/comm/ssc/src/ecatappl.c new file mode 100644 index 0000000..42c7525 --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/src/ecatappl.c @@ -0,0 +1,630 @@ +/** +\addtogroup EcatAppl EtherCAT application +@{ +*/ + + +/** +\file ecatappl.c +\author EthercatSSC@beckhoff.com +\brief Implementation +This file contains the Process Data interface + +\version 5.11 + +
Changes to version V5.10.1:
+V5.11 COE3: change 0x10F3.2 (Sync Error limit) from UINT32 to UINT16 (according to the ETG.1020)
+V5.11 ECAT1: update EEPROM access reset operation
+V5.11 ECAT10: change PROTO handling to prevent compiler errors
+V5.11 ECAT11: create application interface function pointer, add eeprom emulation interface functions
+V5.11 ECAT2: update EEPROM access retry cycle (add 10ms delay between two retry cycles)
+V5.11 ECAT3: handle bus cycle calculation for input/output only devices and create warning diag message only if calculation failed
+V5.11 ECAT4: enhance SM/Sync monitoring for input/output only slaves
+V5.11 ECAT6: add function to calculate bus cycle time
+V5.11 ECAT8: call PDO_InputMapping only once if DC is enabled and COE is not supported
+V5.11 EEPROM1: fix compiler error during pEEPROM pointer initialization
+V5.11 EEPROM2: write Station alias value to EEPROM data register on EEPROM reload command
+V5.11 EEPROM3: clear EEPROM error bits
+V5.11 EEPROM4: prevent the variable in the EEPROM busy loop to be removed by the compiler
+V5.11 ESM7: "add Sync define for 0x22 (""SYNCTYPE_SM2_SYNCHRON""), support value 0x22 for 0x1C33.1 (SM2 sync)"
+
Changes to version V5.01:
+V5.10 COE1: Define one entry description for all 0x1C3x objects and change data type of SI11,12,13 to UINT16 (according ETG.1020)
+V5.10 ECAT1: Correct calculation of blinking and flashing sequence
+V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)
+ Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings
+ Update 0x1C3x entries
+V5.10 ECAT2: Prevent EEPROM data null pointer access (if the pointer is null an command error is set)
+ EEPROM emulation return command error if unknown command was received
+V5.10 ECAT4: Update alignment marco for 8 to 15 bit alignments (16 and 32 Bit controllers)
+ Bugfix calculate LED blink frequency
+V5.10 ECAT7: Add "bInitFinished" to indicate if the initialization is complete
+V5.10 HW2: Change HW_GetTimer return value to UINT32
+
Changes to version V5.0:
+V5.01 APPL3: Include library demo application
+V5.01 ESC1: Change ESC access function (if EEPROM Emulation is active)
+V5.01 ESC2: Add missed value swapping
+
Changes to version V4.40:
+V5.0 TEST1: Add test application. See Application Note ET9300 for more details.
+V5.0 ECAT2: Application specific functions are moved to application files.
+V5.0 ECAT3: Global dummy variables used for dummy ESC operations.
+V5.0 ESC1: ESC 32Bit Access added.
+V5.0 ESC3: Add EEPROM emulation support.
+V5.0 ESM3: Handling pending ESM transitions.
+V5.0 ESC5: Enhance EEPROM access handling.
+V5.0 PDO1: AL Event flags are not rechecked in PDO_OutputMappping(). (Already checked before call function)
+V5.0 SYNC1: Add missed SM event indication (0x1C32/0x1C33 SI11).
+
Changes to version V4.30:
+V4.40 DIAG1: add diagnosis message support
+V4.40 PDO1: merge content of HW_InputMapping (spihw.c/mcihw.c) to PDO_InputMapping. merge content of HW_OutputMapping (spihw.c/mcihw.c) to PDO_OutputMapping.
+V4.40 PDO2: Generic process data length calculation
+V4.40 ECAT2: call cyclic CheckIfLocalError() to check the local flags
+V4.40 HW0: Generic hardware access functions. Add Function (PDI_Isr()), content merged from spihw.c and mcihw.c.
+V4.40 WD1: define (ESC_SM_WD_SUPPORTED) to choose ESC SyncManager watchdog or local watchdog
+V4.40 ESM2: Change state transition behaviour from SafeOP to OP
+V4.40 TIMER1: Change bus cycle time calculation and trigger of ECAT_CheckTimer() if ECAT_TIMER_INT is reset
+V4.40 HW1: Add support for fc1100 hardware
+
Changes to version V4.20:
+V4.30 EL9800: EL9800_x cyclic application is moved to el9800.c
+V4.30 OBJ 3: add object dictionary initialization
+V4.30 SYNC: add CalcSMCycleTime() (calculation of bus cycle time); change synchronisation control functions
+V4.30 PDO: include PDO specific functions (moved from coeappl.c).
+ xxx_InputMapping(); xxx_OutputMapping(); xxx_ReadInputs(); xxx_ResetOutputs(); xxx_Application()
+V4.30 CiA402: Add CiA402_StateMachine() and CiA402_Application() call
+V4.20 DC 1: Add DC pending Statemachine handling
+V4.20 PIC24: Add EL9800_4 (PIC24) required source code
+V4.20 LED 1: Modified LED Handling
+V4.11 APPL 1: The checkWatchdog() function should not called in checkTimer() if this function is triggered by an Interrupt
+
Changes to version V4.08:
+V4.10 LED 1: The handling of the EtherCAT-Error-LED was added
+V4.10 AOE 3: The AoE fragment size has to be initialized during the state transition
+ from INIT to PREOP
+
Changes to version V4.07:
+V4.08 LED 1: The handling of the EtherCAT-LED can be (de-)selected by the switch LEDS_SUPPORTED
+ because the ET1100 and ET1200 have an output port which could be connected directly.
+
Changes to version V4.01:
+V4.02 ECAT 1: The watchdog timer variables shall be initialized.
+
Changes to version V4.00:
+V4.01 APPL 1: If the application is running in synchron mode and no SM event
+ is received, the application should be called from the main loop
+V4.01 APPL 2: In FreeRun mode the output should only be copied if the slave is in OP
+
Changes to version V3.20:
+V4.00 APPL 1: The watchdog checking should be done by a microcontroller
+ timer because the watchdog trigger of the ESC will be reset too
+ if only a part of the sync manager data is written
+V4.00 APPL 2: The setting of EtherCAT state LEDs were included
+V4.00 APPL 3: The outputs should be reset to a safe state,
+ when the state OP is left
+V4.00 APPL 4: An example for the EEPROM access through the ESC is shown in
+ the function APPL_StartMailboxHandler
+V4.00 APPL 5: The inputs should be read once when the state transition
+ from PREOP to SAFEOP is made
+V4.00 APPL 6: The main function was split in MainInit and MainLoop +*/ + + +/*----------------------------------------------------------------------------------------- +------ +------ Includes +------ +-----------------------------------------------------------------------------------------*/ +#include "ecatslv.h" + +#define _ECATAPPL_ 1 +#include "ecatappl.h" +#undef _ECATAPPL_ +/* ECATCHANGE_START(V5.11) ECAT10*/ +/*remove definition of _ECATAPPL_ (#ifdef is used in ecatappl.h)*/ +/* ECATCHANGE_END(V5.11) ECAT10*/ + +#include "coeappl.h" + + +/* ECATCHANGE_START(V5.11) ECAT11*/ +#define _APPL_INTERFACE_ 1 +#include "applInterface.h" +#undef _APPL_INTERFACE_ +/* ECATCHANGE_END(V5.11) ECAT11*/ + +#include "el9800appl.h" + + + +/*-------------------------------------------------------------------------------------- +------ +------ local Types and Defines +------ +--------------------------------------------------------------------------------------*/ + + +#ifndef ECAT_TIMER_INC_P_MS +/** + * \todo Define the timer ticks per ms + */ +#warning "Define the timer ticks per ms" +#endif /* #ifndef ECAT_TIMER_INC_P_MS */ + + +/*----------------------------------------------------------------------------------------- +------ +------ local variables and constants +------ +-----------------------------------------------------------------------------------------*/ +/*variables only required to calculate values for SM Synchronisation objects (0x1C3x)*/ +UINT16 u16BusCycleCntMs; //used to calculate the bus cycle time in Ms +UINT32 StartTimerCnt; //variable to store the timer register value when get cycle time was triggered +BOOL bCycleTimeMeasurementStarted; // indicates if the bus cycle measurement is started + +UINT16 aPdOutputData[(MAX_PD_OUTPUT_SIZE>>1)]; +UINT16 aPdInputData[(MAX_PD_INPUT_SIZE>>1)]; + +/*variables are declared in ecatslv.c*/ + extern VARVOLATILE UINT16 u16dummy; +BOOL bInitFinished = FALSE; /** < \brief indicates if the initialization is finished*/ +/*----------------------------------------------------------------------------------------- +------ +------ local functions +------ +-----------------------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------------------- +------ +------ Functions +------ +-----------------------------------------------------------------------------------------*/ +///////////////////////////////////////////////////////////////////////////////////////// +/** +\brief This function will copies the inputs from the local memory to the ESC memory + to the hardware +*//////////////////////////////////////////////////////////////////////////////////////// +void PDO_InputMapping(void) +{ + APPL_InputMapping((UINT16*)aPdInputData); + HW_EscWriteIsr(((MEM_ADDR *) aPdInputData), nEscAddrInputData, nPdInputSize ); +} +///////////////////////////////////////////////////////////////////////////////////////// +/** +\brief This function will copies the outputs from the ESC memory to the local memory + to the hardware. This function is only called in case of an SM2 + (output process data) event. +*//////////////////////////////////////////////////////////////////////////////////////// +void PDO_OutputMapping(void) +{ + + HW_EscReadIsr(((MEM_ADDR *)aPdOutputData), nEscAddrOutputData, nPdOutputSize ); + + APPL_OutputMapping((UINT16*) aPdOutputData); +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \brief This function shall be called every 1ms. + \brief If the switch ECAT_TIMER_INT is 0, the watchdog control is implemented without using + \brief interrupts. In this case a local timer register is checked every ECAT_Main cycle + \brief and the function is triggered if 1 ms is elapsed + *//////////////////////////////////////////////////////////////////////////////////////// + +void ECAT_CheckTimer(void) +{ + if(sSyncManOutPar.u32CycleTime == 0) + { + u16BusCycleCntMs++; + } + + /*decrement the state transition timeout counter*/ + if(bEcatWaitForAlControlRes && (EsmTimeoutCounter > 0)) + { + EsmTimeoutCounter--; + } + + + + DC_CheckWatchdog(); +} + +/*ECATCHANGE_START(V5.11) ECAT6*/ +///////////////////////////////////////////////////////////////////////////////////////// +/** + \brief This function is called from the PDI_Isr and is used to calculate the bus cycle time + *//////////////////////////////////////////////////////////////////////////////////////// +void HandleBusCycleCalculation(void) +{ + /*calculate the cycle time if device is in SM Sync mode and Cycle time was not calculated yet*/ + if ( !bDcSyncActive && bEscIntEnabled) + { + BOOL bTiggerCalcCycleTime = FALSE; + + if(sSyncManOutPar.u16GetCycleTime == 1) + bTiggerCalcCycleTime = TRUE; + if(bTiggerCalcCycleTime) + { + /*get bus cycle time triggered */ + sSyncManOutPar.u32CycleTime = 0; + sSyncManOutPar.u16GetCycleTime = 0; + + sSyncManInPar.u32CycleTime = 0; + sSyncManInPar.u16GetCycleTime = 0; + + u16BusCycleCntMs = 0; + bCycleTimeMeasurementStarted = TRUE; + StartTimerCnt = (UINT32) HW_GetTimer(); + } + else + { + if(bCycleTimeMeasurementStarted == TRUE) + { + UINT32 CurTimerCnt = (UINT32)HW_GetTimer(); +/*ECATCHANGE_START(V5.11) ECAT3*/ + UINT32 CalcCycleTime = 0; + + +#if ECAT_TIMER_INC_P_MS + CalcCycleTime = (UINT32)u16BusCycleCntMs * 1000000 + (((INT32)(CurTimerCnt-StartTimerCnt))*1000000/ECAT_TIMER_INC_P_MS); //get elapsed cycle time in ns +#endif + +/*ECATCHANGE_START(V5.11) ECAT4*/ + sSyncManOutPar.u32CycleTime = CalcCycleTime; +/*ECATCHANGE_END(V5.11) ECAT4*/ + sSyncManInPar.u32CycleTime = CalcCycleTime; + u16BusCycleCntMs = 0; + StartTimerCnt = 0; + bCycleTimeMeasurementStarted = FALSE; + +/*ECATCHANGE_END(V5.11) ECAT3*/ + /* CiA402 Motion controller cycle time is only set if DC Synchronisation is active*/ + } + } + } +} +/*ECATCHANGE_END(V5.11) ECAT6*/ + +void PDI_Isr(void) +{ + if(bEscIntEnabled) + { + /* get the AL event register */ + UINT16 ALEvent = HW_GetALEventRegister_Isr(); + ALEvent = SWAPWORD(ALEvent); + + if ( ALEvent & PROCESS_OUTPUT_EVENT ) + { + if(bDcRunning && bDcSyncActive) + { + /* Reset SM/Sync0 counter. Will be incremented on every Sync0 event*/ + u16SmSync0Counter = 0; + } + if(sSyncManOutPar.u16SmEventMissedCounter > 0) + sSyncManOutPar.u16SmEventMissedCounter--; + + +/*ECATCHANGE_START(V5.11) ECAT6*/ + //calculate the bus cycle time if required + HandleBusCycleCalculation(); +/*ECATCHANGE_END(V5.11) ECAT6*/ + + /* Outputs were updated, set flag for watchdog monitoring */ + bEcatFirstOutputsReceived = TRUE; + + + /* + handle output process data event + */ + if ( bEcatOutputUpdateRunning ) + { + /* slave is in OP, update the outputs */ + PDO_OutputMapping(); + } + else + { + /* Just acknowledge the process data event in the INIT,PreOP and SafeOP state */ + HW_EscReadWordIsr(u16dummy,nEscAddrOutputData); + HW_EscReadWordIsr(u16dummy,(nEscAddrOutputData+nPdOutputSize-2)); + } + } + +/*ECATCHANGE_START(V5.11) ECAT4*/ + if (( ALEvent & PROCESS_INPUT_EVENT ) && (nPdOutputSize == 0)) + { + //calculate the bus cycle time if required + HandleBusCycleCalculation(); + } +/*ECATCHANGE_END(V5.11) ECAT4*/ + + /* + Call ECAT_Application() in SM Sync mode + */ + if (sSyncManOutPar.u16SyncType == SYNCTYPE_SM_SYNCHRON) + { + /* The Application is synchronized to process data Sync Manager event*/ + ECAT_Application(); + } + + if ( bEcatInputUpdateRunning +/*ECATCHANGE_START(V5.11) ESM7*/ + && ((sSyncManInPar.u16SyncType == SYNCTYPE_SM_SYNCHRON) || (sSyncManInPar.u16SyncType == SYNCTYPE_SM2_SYNCHRON)) +/*ECATCHANGE_END(V5.11) ESM7*/ + ) + { + /* EtherCAT slave is at least in SAFE-OPERATIONAL, update inputs */ + PDO_InputMapping(); + } + + /* + Check if cycle exceed + */ + /*if next SM event was triggered during runtime increment cycle exceed counter*/ + ALEvent = HW_GetALEventRegister_Isr(); + ALEvent = SWAPWORD(ALEvent); + + if ( ALEvent & PROCESS_OUTPUT_EVENT ) + { + sSyncManOutPar.u16CycleExceededCounter++; + sSyncManInPar.u16CycleExceededCounter = sSyncManOutPar.u16CycleExceededCounter; + + /* Acknowledge the process data event*/ + HW_EscReadWordIsr(u16dummy,nEscAddrOutputData); + HW_EscReadWordIsr(u16dummy,(nEscAddrOutputData+nPdOutputSize-2)); + } + } //if(bEscIntEnabled) +} + +void Sync0_Isr(void) +{ + + Sync0WdCounter = 0; + + if(bDcSyncActive) + { + + if ( bEcatInputUpdateRunning ) + { + LatchInputSync0Counter++; + } + +/*ECATCHANGE_START(V5.11) ECAT4*/ + if(u16SmSync0Value > 0) + { + /* Check if Sm-Sync sequence is invalid */ + if (u16SmSync0Counter > u16SmSync0Value) + { + /*ECATCHANGE_START(V5.11) COE3*/ + if ((nPdOutputSize > 0) && (sSyncManOutPar.u16SmEventMissedCounter <= sErrorSettings.u16SyncErrorCounterLimit)) + { + /*ECATCHANGE_END(V5.11) COE3*/ + sSyncManOutPar.u16SmEventMissedCounter = sSyncManOutPar.u16SmEventMissedCounter + 3; + } + +/*ECATCHANGE_START(V5.11) COE3*/ + if ((nPdInputSize > 0) && (nPdOutputSize == 0) && (sSyncManInPar.u16SmEventMissedCounter <= sErrorSettings.u16SyncErrorCounterLimit)) + { +/*ECATCHANGE_END(V5.11) COE3*/ + sSyncManInPar.u16SmEventMissedCounter = sSyncManInPar.u16SmEventMissedCounter + 3; + } + + } // if (u16SmSync0Counter > u16SmSync0Value) + + + if ((nPdOutputSize == 0) && (nPdInputSize > 0)) + { + /* Input only with DC, check if the last input data was read*/ + UINT16 ALEvent = HW_GetALEventRegister_Isr(); + ALEvent = SWAPWORD(ALEvent); + + if ((ALEvent & PROCESS_INPUT_EVENT) == 0) + { + /* no input data was read by the master, increment the sm missed counter*/ + u16SmSync0Counter++; + } + else + { + /* Reset SM/Sync0 counter*/ + u16SmSync0Counter = 0; + + sSyncManInPar.u16SmEventMissedCounter = 0; + + } + } + else + { + u16SmSync0Counter++; + } + }//SM -Sync monitoring enabled +/*ECATCHANGE_END(V5.11) ECAT4*/ + + + if(!bEscIntEnabled && bEcatOutputUpdateRunning) + { + /* Output mapping was not done by the PDI ISR */ + PDO_OutputMapping(); + } + + /* Application is synchronized to SYNC0 event*/ + ECAT_Application(); + + if ( bEcatInputUpdateRunning + && (LatchInputSync0Value > 0) && (LatchInputSync0Value == LatchInputSync0Counter) ) /* Inputs shall be latched on a specific Sync0 event */ + { + /* EtherCAT slave is at least in SAFE-OPERATIONAL, update inputs */ + PDO_InputMapping(); + + if(LatchInputSync0Value == 1) + { + /* if inputs are latched on every Sync0 event (otherwise the counter is reset on the next Sync1 event) */ + LatchInputSync0Counter = 0; + } + } + + } + +} + +void Sync1_Isr(void) +{ + Sync1WdCounter = 0; + + if ( bEcatInputUpdateRunning + && (sSyncManInPar.u16SyncType == SYNCTYPE_DCSYNC1) + && (LatchInputSync0Value == 0)) /* Inputs are latched on Sync1 (LatchInputSync0Value == 0), if LatchInputSync0Value > 0 inputs are latched with Sync0 */ + { + /* EtherCAT slave is at least in SAFE-OPERATIONAL, update inputs */ + PDO_InputMapping(); + } + + /* Reset Sync0 latch counter (to start next Sync0 latch cycle) */ + LatchInputSync0Counter = 0; +} +///////////////////////////////////////////////////////////////////////////////////////// +/** + + \brief This function shall called within a 1ms cycle. + Set Run and Error Led depending on the Led state + +*//////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param pObjectDictionary Pointer to application specific object dictionary. + NULL if no specific object are available. +\return 0 if initialization was successful + + \brief This function initialize the EtherCAT Sample Code + +*//////////////////////////////////////////////////////////////////////////////////////// + +UINT16 MainInit(void) +{ + UINT16 Error = 0; +/*Hardware init function need to be called from the application layer*/ + +/*ECATCHANGE_START(V5.11) EEPROM1*/ +#ifdef SET_EEPROM_PTR + SET_EEPROM_PTR +#endif +/*ECATCHANGE_END(V5.11) EEPROM1*/ + + /* initialize the EtherCAT Slave Interface */ + ECAT_Init(); + /* initialize the objects */ + COE_ObjInit(); + + + /*Timer initialization*/ + u16BusCycleCntMs = 0; + StartTimerCnt = 0; + bCycleTimeMeasurementStarted = FALSE; + + /*indicate that the slave stack initialization finished*/ + bInitFinished = TRUE; + +/*Application Init need to be called from the application layer*/ + return Error; +} + + +///////////////////////////////////////////////////////////////////////////////////////// +/** + + \brief This function shall be called cyclically from main + +*//////////////////////////////////////////////////////////////////////////////////////// + +void MainLoop(void) +{ + /*return if initialization not finished */ + if(bInitFinished == FALSE) + return; + + + + /* FreeRun-Mode: bEscIntEnabled = FALSE, bDcSyncActive = FALSE + Synchron-Mode: bEscIntEnabled = TRUE, bDcSyncActive = FALSE + DC-Mode: bEscIntEnabled = TRUE, bDcSyncActive = TRUE */ + if ( + (!bEscIntEnabled || !bEcatFirstOutputsReceived) /* SM-Synchronous, but not SM-event received */ + && !bDcSyncActive /* DC-Synchronous */ + ) + { + /* if the application is running in ECAT Synchron Mode the function ECAT_Application is called + from the ESC interrupt routine (in mcihw.c or spihw.c), + in ECAT Synchron Mode it should be additionally checked, if the SM-event is received + at least once (bEcatFirstOutputsReceived = 1), otherwise no interrupt is generated + and the function ECAT_Application has to be called here (with interrupts disabled, + because the SM-event could be generated while executing ECAT_Application) */ + if ( !bEscIntEnabled ) + { + /* application is running in ECAT FreeRun Mode, + first we have to check, if outputs were received */ + UINT16 ALEvent = HW_GetALEventRegister(); + ALEvent = SWAPWORD(ALEvent); + + if ( ALEvent & PROCESS_OUTPUT_EVENT ) + { + /* set the flag for the state machine behaviour */ + bEcatFirstOutputsReceived = TRUE; + if ( bEcatOutputUpdateRunning ) + { + /* update the outputs */ + PDO_OutputMapping(); + } + } + else if ( nPdOutputSize == 0 ) + { + /* if no outputs are transmitted, the watchdog must be reset, when the inputs were read */ + if ( ALEvent & PROCESS_INPUT_EVENT ) + { + /* Outputs were updated, set flag for watchdog monitoring */ + bEcatFirstOutputsReceived = TRUE; + } + } + } + + DISABLE_ESC_INT(); + ECAT_Application(); + + if ( bEcatInputUpdateRunning ) + { + /* EtherCAT slave is at least in SAFE-OPERATIONAL, update inputs */ + PDO_InputMapping(); + } + ENABLE_ESC_INT(); + } + #if !ECAT_TIMER_INT + /* there is no interrupt routine for the hardware timer so check the timer register if the desired cycle elapsed*/ + { + UINT32 CurTimer = (UINT32)HW_GetTimer(); + + if(CurTimer>= ECAT_TIMER_INC_P_MS) + { + ECAT_CheckTimer(); + + HW_ClearTimer(); + + } + } +#endif + /* call EtherCAT functions */ + ECAT_Main(); + + /* call lower prior application part */ + COE_Main(); + CheckIfEcatError(); + +} + +/*The main function was moved to the application files.*/ +///////////////////////////////////////////////////////////////////////////////////////// +/** + \brief ECAT_Application (prev. SSC versions "COE_Application") + this function calculates and the physical process signals and triggers the input mapping +*//////////////////////////////////////////////////////////////////////////////////////// +void ECAT_Application(void) +{ + + APPL_Application(); + +/* PDO Input mapping is called from the specific trigger ISR */ +} + + + + +/** @} */ + diff --git a/GD32E508_DCU_APP/comm/ssc/src/ecatcoe.c b/GD32E508_DCU_APP/comm/ssc/src/ecatcoe.c new file mode 100644 index 0000000..5708213 --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/src/ecatcoe.c @@ -0,0 +1,180 @@ +/** +\addtogroup CoE CAN Application Profile over EtherCAT +@{ +*/ + +/** +\file ecatcoe.c +\author EthercatSSC@beckhoff.com +\brief Implementation +This file contains the CoE mailbox interface + +\version 5.11 + +
Changes to version V5.0:
+V5.11 COE4: "change prototype of ""COE_ContinueInd()"" return <> 0 if a failure occurred"
+V5.11 ECAT10: change PROTO handling to prevent compiler errors
+V5.11 TEST6: add test object to trigger Slave-to-Slave communication
+V5.11 TEST7: add test behaviour to send an emergency on every SDO request (in SafeOP)
+
Changes to version V4.40:
+V5.0 SDO7: "SDOS_SdoInfoInd()" never return pending SDO Info service. Delete "NOERROR_INWORK" handling.
+
Changes to version V4.08:
+V4.40 SDO1: add initial value for "nSdoInfoFragmentsLeft"
+V4.40 MBX6: change return value if no mailbox buffer is available
+V4.08 MBX 1: If the switch MAILBOX_QUEUE was set, we have to put all SDO Info Responses in the Send Queue +*/ + +/*--------------------------------------------------------------------------------------- +------ +------ Includes +------ +---------------------------------------------------------------------------------------*/ + +#include "ecat_def.h" + + +#include "sdoserv.h" +#define _ECATCOE_ 1 +#include "ecatcoe.h" +#undef _ECATCOE_ +/* ECATCHANGE_START(V5.11) ECAT10*/ +/*remove definition of _ECATCOE_ (#ifdef is used in ecatcoe.h)*/ +/* ECATCHANGE_END(V5.11) ECAT10*/ + + + +/*--------------------------------------------------------------------------------------- +------ +------ internal Types and Defines +------ +---------------------------------------------------------------------------------------*/ + +#define ECATCOE 0x4300 +#define ECATCOEMAX 0x02 + +/*--------------------------------------------------------------------------------------- +------ +------ static variables +------ +---------------------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------------------- +------ +------ static functions +------ +---------------------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------------------- +------ +------ functions +------ +---------------------------------------------------------------------------------------*/ + +///////////////////////////////////////////////////////////////////////////////////////// +/** + + \brief This function intialize the CoE Interface. +*//////////////////////////////////////////////////////////////////////////////////////// + +void COE_Init(void) +{ + pCoeSendStored = 0; + nSdoInfoFragmentsLeft = 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param pCoeMbx Pointer to the received mailbox data from the master. + + \return result of the operation (0 (success) or mailbox error code (MBXERR_.... defined in + mailbox.h)) + + \brief This function is called when a CoE (CAN application layer over EtherCAT) service is received from + the master. +*//////////////////////////////////////////////////////////////////////////////////////// + +UINT8 COE_ServiceInd(TCOEMBX MBXMEM *pCoeMbx) +{ + UINT8 result = 0; + + switch ((pCoeMbx->CoeHeader & COEHEADER_COESERVICEMASK) >> COEHEADER_COESERVICESHIFT) + { + case COESERVICE_SDOREQUEST: + /* SDO-Request received, call SDOS_SdoInd to process the SDO-Request + if an existing SDO-Stack shall be used, the corresponding function + should be called */ + result = SDOS_SdoInd( (TINITSDOMBX MBXMEM *) pCoeMbx ); + break; + + case COESERVICE_SDOINFO: + /* SDO-Information Request received, call SDOS_SdoInfoInd to process the SDO-Request */ + result = SDOS_SdoInfoInd( (TSDOINFORMATION MBXMEM *) pCoeMbx ); + /*NOERROR_INWORK is never returned by SDOS_SdoInfoInd() => delete return code handling*/ + break; + + + case COESERVICE_SDORESPONSE: + case COESERVICE_EMERGENCY: + case COESERVICE_TXPDO: + case COESERVICE_RXPDO: + case COESERVICE_TXPDOREMREQ: + case COESERVICE_RXPDOREMREQ: + /* these CoE services are not supported yet */ + result = MBXERR_SERVICENOTSUPPORTED; + break; + + default: + result = MBXERR_INVALIDHEADER; + break; + } + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param pMbx Pointer to the free mailbox to sent. + + \return result of the operation (0 (success) + + \brief This function is called when a CoE service to be sent is stored and can + \brief be put in the send mailbox. +*//////////////////////////////////////////////////////////////////////////////////////// + +/*ECATCHANGE_START(V5.11) COE4*/ +UINT8 COE_ContinueInd(TMBX MBXMEM * pMbx) +/*ECATCHANGE_END(V5.11) COE4*/ +{ + if (pCoeSendStored) + { + /* send the stored CoE service which could not be sent before */ + MBX_MailboxSendReq(pCoeSendStored, 0); + pCoeSendStored = 0; + } + else + { + /* send the next fragment of the last CoE service (only for SDO-Information possible) */ + /* in mailbox queue mode pMbx is always 0, so a mailbox buffer shall be get */ + pMbx = (TMBX MBXMEM *) APPL_AllocMailboxBuffer(SIZEOF(TMBX)); + /* it shall be checked if a valid pointer was returned */ +/*ECATCHANGE_START(V5.11) COE4*/ + if (pMbx == NULL) + { + return MBXERR_NOMOREMEMORY; + } + else +/*ECATCHANGE_END(V5.11) COE4*/ + { + /* copy the stored SDO-Info-Header in the request */ + MBXMEMCPY(pMbx, aSdoInfoHeader, SDO_INFO_HEADER_BYTE_SIZE); + /* call SDOS_SdoInfoInd to generate and send the next fragment */ + SDOS_SdoInfoInd( (TSDOINFORMATION MBXMEM *) pMbx ); + } + } + + return 0; +} + +/** @} */ + + + diff --git a/GD32E508_DCU_APP/comm/ssc/src/ecatslv.c b/GD32E508_DCU_APP/comm/ssc/src/ecatslv.c new file mode 100644 index 0000000..b0df487 --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/src/ecatslv.c @@ -0,0 +1,2504 @@ +/** +\addtogroup ESM EtherCAT State Machine +@{ +*/ + +/** +\file ecatslv.c +\author EthercatSSC@beckhoff.com +\brief Implementation +This file contains the EtherCAT State Machine. + +\version 5.11 + +
Changes to version V5.10:
+V5.11 COE3: change 0x10F3.2 (Sync Error limit) from UINT32 to UINT16 (according to the ETG.1020)
+V5.11 DIAG4: change parameter handling in DIAG_CreateNewMessage()
+V5.11 ECAT10: change PROTO handling to prevent compiler errors
+V5.11 ECAT4: enhance SM/Sync monitoring for input/output only slaves
+V5.11 ECAT5: "Add missing ""bEscAlEventEnbaled"" initialization if ""AL_EVENT_ENBALED"" is 0"""
+V5.11 ECAT7: add missing big endian swapping
+V5.11 ESC1: update max address calculation
+V5.11 ESM1: update calculation of subordinated cycles
+V5.11 ESM2: DC_SUPPORTED, Sync0 is not supported and Sync0 is generated according register values the state transition to SafeOP shall be rejected
+V5.11 ESM3: update checking of the user configured sync type
+V5.11 ESM4: prevent to go from ErrSafeOP to OP without re enabling Sync0/1
+V5.11 ESM5: DPRAM range was double checked
+V5.11 ESM6: in the SO transition wait by default until the master has send process data
+V5.11 HW1: "move hardware independent functions ""HW_DisableSyncManChannel()"", ""HW_EnableSyncManChannel()"", ""HW_GetSyncMan()"", ""HW_ResetALEventMask()"", ""HW_SetALEventMask()"" to ecatalv.c"
+V5.11 HW2: check during ESM handling if the SM address and length is aligned according the ESC access
+V5.11 TEST9: "add behaviour 0x2020.7 (SDO requests on 0x3006.0 are set to pending until an FoE read request on ""UnlockSdoResp"" is received or in case that no mbx queue is supported when a new mbx request was received)"
+
Changes to version V5.01:
+V5.10 COE1: Define one entry description for all 0x1C3x objects and change data type of SI11,12,13 to UINT16 (according ETG.1020)
+V5.10 DIAG1: Define diagmessage textIDs
+V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)
+ Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings
+ Update 0x1C3x entries
+V5.10 ESC2: Check if defined SM settings do not exceed the available DPRAM range (in error case AL Status 0x14 is returned)
+V5.10 ESC3: Handle DC cControl register values in case of 32Bit ESC access (a Sync activation mask need to defined/used)
+V5.10 ESC4: Mask lower 4 Bit of AL status to get Run led value
+ Invalid RunLed code was calculated if ESC set ECAT Run Led
+V5.10 ESC5: Add missing swapping
+V5.10 ESM2: Update "bApplEsmPending" flag during a transition to a lower state
+V5.10 ESM3: Add "volatile" directive for ESM dummy variables
+V5.10 HW5: Block ESC interrupts during Timer ISR
+V5.10 TEST9: Add option to prevent SM3 unlock during PS
+
Changes to version V5.0:
+V5.01 APPL3: Include library demo application
+V5.01 ESC2: Add missed value swapping
+V5.01 ESM1: Don't overwrite the error reason in case of an failed PS transition
+V5.01 ESM2: Don't check the "appl trigger" flag in case on an regular transition to a lower state (OS, SP, PI).
+V5.01 ESM3: Call Error acknowledge indication only if error was acknowledged by the master
+V5.01 HW3: Update blink code of an SM watchdog error
+
Changes to version V4.42:
+V5.0 ECAT1: Support Explicit Device ID.
+V5.0 ECAT2: Application specific functions are moved to application files.
+V5.0 ECAT3: Global dummy variables used for dummy ESC operations.
+V5.0 ESC1: ESC 32Bit Access added.
+V5.0 ESC2: Support ESC EtherCAT LED Indication.
+V5.0 ESC3: Support EEPROM Emulation.
+V5.0 ESM1: Update "LocalErrorFlag" handling.
+V5.0 ESM2: Update Error Acknowledge by ALControl INIT (without error acknowledge)
+V5.0 ESM3: Handle pending ESM transition
+V5.0 ESM4: ECAT_StateChange() will only be called form application. In case of an communication error AL_ControlInd is called.
+V5.0 MBX1: Support configuration without mailbox protocol support.
+V5.0 TEST1: Add test application. See Application Note ET9300 for more details.
+
Changes to version V4.40:
+V4.42 ESM1: Reset local error flag if master set the acknowledge bit (0x120.4)
+
Changes to version V4.30:
+V4.40 ESM5: Enable output SyncManager if local error acknowledged
+V4.40 HW0: Use common hardware access functions
+V4.40 PDO3: Add support if only input process data is used
+V4.40 ECAT4: Add read SM activation register to acknowledge SM Change event
+V4.40 PDO2: Check if max process data size was exceed
+V4.40 DIAG1: add diagnosis message support
+V4.40 ESM4: Change Check WD setup; add define OP_PD_REQUIRED (defines if process data required in state change to OP)
+V4.40 WD1: change WD behaviour depending if process data required in OP state
+V4.40 MBX4: Change processing order of mailbox SyncManager flags
+V4.40 ECAT1: Merge content of HW_Main (spihw.c /mcihw.c) to ECAT_Main
+V4.40 ECAT2: Added CheckIfLocalError() to check local flags and set ALStatus /Al Status code if required. This function is called cyclic from MainLoop.
+V4.40 ESM2: Add AL_ControlRes() to complete pending state requests. Change SafeOP to OP state response
+V4.40 ESM1: Prevent double call of StopOutputHandler()
+V4.40 BOOT1: Enable Mailbox SyncManger on state change to BOOT state (to enable FoE)
+V4.40 ESM3: Change State machine behaviour according to ETG.1000 V1.0.2 (state change #26)
+V4.40 LED1: Set error blink code
+V4.40 TIMER1: Added DC_CheckWatchdog() triggered from ECAT_CheckTimer(). Change local Sync0 watchdog variables. Change bus cycle calculation
+V4.40 WD1: Change check process data watchdog settings
+
Changes to version V4.20:
+V4.30 OBJ 3: initialize the object dictionary in state change INIT->PREOP; clear object dictionary in state change PREOP->INIT
+V4.30 SYNC: add 0x1C32:10; 0x1C33:10 (Sync0 cycle), change synchronisation control functionality
+V4.30 CiA402: add CiA402_Init() call in state change from PREOP to SAFEOP if DC synchronisation is enabled,
+ else the Init function is called when bus cycle time is calculated [CalcSMCycleTime() ].
+ trigger error handling if the EtherCAT state machine gets a transition from OP to an "lower" state
+V4.20 ECAT 1: add LEGACY_MODE behaviour in ECAT_CheckWatchdog()
+V4.20 DC 1: Add DC pending state machine handling and Dc watchdog functionality
+V4.20 ESM 2: Add State transition from BOOT to INIT
+V4.20 ESM 1: Non LEGACY_MODE State change handling
+V4.11 Renamed the function parameter "code" of Function "SendSmFailedEmergency() to avoid
+problems with some compilers"
+V4.11 ECAT 1: Fixed a possible problem with state change Init -> SafeOP. The output syncmanager
+was enabled by the state change-flag and not by the actual state
+V4.11 LED 1: Clear the error LED during error acknowledgement
+V4.11 ESC 1: fixed size of MBXHEADER in the TFOEMBX struct
+
Changes to version V4.08:
+V4.10 ECAT 1: clear bEcatOutputsReceived in startMailboxhandler()
+V4.10 ECAT 2: clear bEcatOutputsReceived in stopMailboxhandler()
+V4.10 ECAT 3: when switching from INIT to BOOT the SM settings shall be checked
+V4.10 ECAT 4: APPL_StartInputHandler shall always be called and bEcatInputUpdateRunning shall always be set
+ in StartInputHandler independent of the input size
+V4.10 ECAT 5: AL_ControlInd: the error acknowledge behaviour was changed
+ according to the protocol enhancements and the conformance test
+V4.10 ECAT 6: AL_ControlInd: if a state transitions failed the corresponding stop function is
+ called to get a consistent set of variables
+V4.10 ECAT 7: the local application requested to leave the state OP so we have to disable the SM2
+ and make the state change from OP to SAFEOP by calling StopOutputHandler
+V4.10 ECAT 8: the AL Status Code has to be reset if the error was acknowledged by the master
+V4.10 ECAT 9: ECAT_StateChange: when waiting for a State Change response from the application the
+ AL Status shall only be written if the final state was reached
+
Changes to version V4.07:
+V4.08 ECAT 1: The watchdog value was not rounded up
+V4.08 ECAT 2: The value of u16WdValue was not set 0 if the register 0x420 is 0
+V4.08 ECAT 3: The AlStatusCode is changed as parameter of the function AL_ControlInd
+V4.08 ECAT 4: In a state transition OP2PREOP, SAFEOP2INIT or OP2INIT is requested,
+ this was not working correctly if one of the application functions
+ APPL_StopInputHandler or APPL_StopOutputHandler were returning NOERROR_INWORK
+ (because only the first state transition was made in that case)
+V4.08 AOE 1: AoE was added
+
Changes to version V4.06:
+V4.07 ECAT 1: The sources for SPI and MCI were merged (in ecat_def.h
+ set the switch MCI_HW to 1 when using the MCI,
+ set the switch SPI_HW to 1 when using the SPI
+
Changes to version V4.00:
+V4.01 ECAT 1: The Output sync Manager was not disabled when the state OP was left
+ by a local request (watchdog or io error)
+V4.01 ECAT 2: APPL_StopOutputHandler returns an UINT16
+V4.01 ECAT 3: TwinCAT compatibility mode: The state transition to OP is allowed when the
+ WD-Trigger-Bit of the SM2-Control-Byte (0x814.6) is FALSE, in that case the
+ watchdog will not be started before the outputs were received the first time
+V4.01 ECAT 4: "else" was too much
+
Changes to version V3.20:
+V4.00 ECAT 1: The handling of the Sync Manager Parameter was included according to
+ the EtherCAT Guidelines and Protocol Enhancements Specification
+V4.00 ECAT 2: The output sync manager is initialized during the state transition
+ from PREOP to SAFEOP that the master can check if the slave could update
+ inputs and outputs before switching the slave to OP
+ behaviour according to the EtherCAT Guidelines and Protocol Enhancements Specification
+V4.00 ECAT 3: The watchdog will be enabled in SAFE-OP that it can be checked if the last SM event
+ was received during the watchdog time before switching to OP
+V4.00 ECAT 4: The function CheckSmChannelParameters is included in the function
+ CheckSmSettings to get a better overview
+V4.00 ECAT 5: In synchronous mode the slave should support 1- and 3-buffer mode, 3-buffer mode
+ should be the standard setting, because the controlling if the process data was updated
+ should be done with the TxPDO Toggle, but the 1-buffer mode should be setable too,
+ that the master could easily check if all slaves are synchronous by checking the
+ the working counter (if the outputs were not read or the inputs were not written
+ the ESC of the slave would not increment the working counter with expected value
+ if the 1-buffer mode is running)
+V4.00 ECAT 6: The function ECAT_StateChange was added, which the application should call if a local error
+ is detected (with the parameters alStatus = STATE_SAFEOP, alStatusCode = error code (> 0x1000))
+ or gone (with the parameters alStatus = STATE_OP, alStatusCode = 0)
+ or if one of the functions APPL_StartMailboxHandler, APPL_StopMailboxHandler, APPL_StartInputHandler,
+ APPL_StopInputHandler, APPL_StartOutputHandler, APPL_StopOutputHandler has returned NOERROR_INWORK
+ to acknowledge the last state transition (with the parameters alStatus = new AL-Status, alStatusCode =
+ new AL-Status-Code)
+V4.00 ECAT 7: The return values for the AL-StatusCode were changed to UINT16 +*/ + +/*----------------------------------------------------------------------------------------- +------ +------ Includes +------ +-----------------------------------------------------------------------------------------*/ + +#define _ECATSLV_ 1 +#include "ecatslv.h" +#undef _ECATSLV_ +/* ECATCHANGE_START(V5.11) ECAT10*/ +/*remove definition of _ECATSLV_ (#ifdef is used in ecatslv.h)*/ +/* ECATCHANGE_END(V5.11) ECAT10*/ + +#include "ecatappl.h" + + + + +#include "mailbox.h" + +#include "ecatcoe.h" +#include "objdef.h" + + + +#include "el9800appl.h" + +/*-------------------------------------------------------------------------------------- +------ +------ local Types and Defines +------ +--------------------------------------------------------------------------------------*/ + + +/*----------------------------------------------------------------------------------------- +------ +------ local variables and constants +------ +-----------------------------------------------------------------------------------------*/ +UINT16 u16ALEventMask; // Value which will be written to the 0x204 register (AL event mask) during the state transition PreOP to SafeOP + +/*Dummy variable to trigger read or writes events in the ESC*/ + VARVOLATILE UINT16 u16dummy; + + + VARVOLATILE UINT16 SMActivate = 0; + +/*ECATCHANGE_START(V5.11) HW1*/ +TSYNCMAN SyncManInfo; + +//indicates if the EEPORM was loaded correct +BOOL EepromLoaded = FALSE; +/*----------------------------------------------------------------------------------------- +------ +------ local functions +------ +-----------------------------------------------------------------------------------------*/ +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param intMask interrupt mask (disabled interrupt shall be zero) + + \brief This function makes an logical and with the AL Event Mask register (0x204) +*//////////////////////////////////////////////////////////////////////////////////////// +void ResetALEventMask(UINT16 intMask) +{ + UINT16 mask; + HW_EscReadWord(mask, ESC_AL_EVENTMASK_OFFSET); + + mask &= intMask; + + DISABLE_ESC_INT(); + + + HW_EscWriteWord(mask, ESC_AL_EVENTMASK_OFFSET); + ENABLE_ESC_INT(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param intMask interrupt mask (enabled interrupt shall be one) + + \brief This function makes an logical or with the AL Event Mask register (0x204) +*//////////////////////////////////////////////////////////////////////////////////////// +void SetALEventMask(UINT16 intMask) +{ + UINT16 mask; + HW_EscReadWord(mask, ESC_AL_EVENTMASK_OFFSET); + + mask |= intMask; + + DISABLE_ESC_INT(); + + + HW_EscWriteWord(mask, ESC_AL_EVENTMASK_OFFSET); + ENABLE_ESC_INT(); +} + + +///////////////////////////////////////////////////////////////////////////////////////// +/** + +\brief This function reads the EEPROM loaded state +*//////////////////////////////////////////////////////////////////////////////////////// +void UpdateEEPROMLoadedState(void) +{ + { + UINT16 TmpVar = 0; + //read EEPROM loaded information + HW_EscReadWord(TmpVar, ESC_EEPROM_CONTROL_OFFSET); + TmpVar = SWAPWORD(TmpVar); + + if (((TmpVar & ESC_EEPROM_ERROR_CRC) > 0) + || ((TmpVar & ESC_EEPROM_ERROR_LOAD) > 0)) + { + EepromLoaded = FALSE; + } + else + { + EepromLoaded = TRUE; + } + } +} + + +/*----------------------------------------------------------------------------------------- +------ +------ functions +------ +-----------------------------------------------------------------------------------------*/ + + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param channel Sync Manager channel + + \return pPdSyncMan Pointer to the settings of requested SYNC Manager channel + + \brief This function is called to read the SYNC Manager channel descriptions of the + process data SYNC Managers. +*//////////////////////////////////////////////////////////////////////////////////////// + +TSYNCMAN ESCMEM * GetSyncMan( UINT8 channel ) +{ + HW_EscRead((MEM_ADDR *)&SyncManInfo, ESC_SYNCMAN_REG_OFFSET + (channel * SIZEOF_SM_REGISTER), SIZEOF_SM_REGISTER ); + + + return &SyncManInfo; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param channel Sync Manager channel + + \brief This function disables a Sync Manager channel +*//////////////////////////////////////////////////////////////////////////////////////// +void DisableSyncManChannel(UINT8 channel) +{ + UINT16 Offset; + //The register 0x806 is only readable from PDI => writing 0 is valid + VARVOLATILE UINT16 smStatus = SM_SETTING_PDI_DISABLE; + Offset = (ESC_SYNCMAN_ACTIVE_OFFSET + (SIZEOF_SM_REGISTER*channel)); + + + HW_EscWriteWord(smStatus,Offset); + + /*wait until SyncManager is disabled*/ + do + { + HW_EscReadWord(smStatus, Offset); + }while(!(smStatus & SM_SETTING_PDI_DISABLE)); +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param channel Sync Manager channel + + \brief This function enables a Sync Manager channel +*//////////////////////////////////////////////////////////////////////////////////////// +void EnableSyncManChannel(UINT8 channel) +{ + UINT16 Offset; + //The register 0x806 is only readable from PDI => writing 0 is valid + VARVOLATILE UINT16 smStatus = 0x0000; + Offset = (ESC_SYNCMAN_ACTIVE_OFFSET + (SIZEOF_SM_REGISTER*channel)); + + + HW_EscWriteWord(smStatus,Offset); + + /*wait until SyncManager is enabled*/ + do + { + HW_EscReadWord(smStatus,Offset); + + }while((smStatus & SM_SETTING_PDI_DISABLE)); +} +/*ECATCHANGE_END(V5.11) HW1*/ + + + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param maxChannel last SM channel which should be checked + + \return 0: okay else AL Status Code + + \brief This function checks all SM channels + +*//////////////////////////////////////////////////////////////////////////////////////// + +UINT8 CheckSmSettings(UINT8 maxChannel) +{ + UINT8 i; + UINT8 result = 0; + TSYNCMAN ESCMEM *pSyncMan; + UINT16 SMLength = 0; + UINT16 SMAddress = 0; + + //Check if max address defines are within the available ESC address range + if((nMaxEscAddress < MAX_PD_WRITE_ADDRESS) + ||(nMaxEscAddress < MAX_PD_READ_ADDRESS) +/*ECATCHANGE_START(V5.11) ESM5*/ + ||(nMaxEscAddress < MAX_MBX_WRITE_ADDRESS) +/*ECATCHANGE_END(V5.11) ESM5*/ + ||(nMaxEscAddress < MAX_MBX_READ_ADDRESS)) + { + /*The defines for maximum SM addresses are invalid for the used ESC (change the defines in the file ecat_def.h or the SSC Tool) + It may be also required to adapt the SM settings in the ESI file*/ + + return ALSTATUSCODE_NOVALIDFIRMWARE; + } + + /* check the Sync Manager Parameter for the Receive Mailbox (Sync Manager Channel 0) */ +/*ECATCHANGE_START(V5.11) HW1*/ + pSyncMan = GetSyncMan(MAILBOX_WRITE); +/*ECATCHANGE_END(V5.11) HW1*/ + + SMLength = pSyncMan->Length; + SMAddress = pSyncMan->PhysicalStartAddress; + +/* ECATCHANGE_START(V5.11) HW2*/ + //Check if the start address and length are even 16Bit addresses + if ((SMLength & 0x1) > 0) + return ALSTATUSCODE_INVALIDSMCFG; + + if ((SMAddress & 0x1) > 0) + return ALSTATUSCODE_INVALIDSMCFG; +/* ECATCHANGE_END(V5.11) HW2*/ + + if (!(pSyncMan->Settings[SM_SETTING_ACTIVATE_OFFSET] & SM_SETTING_ENABLE_VALUE)) + /* receive mailbox is not enabled */ + result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; + else if ( (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_DIRECTION_MASK) != SM_SETTING_DIRECTION_WRITE_VALUE) + /* receive mailbox is not writable by the master*/ + result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; + else if ( (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_MODE_MASK) != SM_SETTING_MODE_ONE_BUFFER_VALUE ) + /* receive mailbox is not in one buffer mode */ + result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; + else if ( SMLength < MIN_MBX_SIZE ) + /* receive mailbox size is too small */ + result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; + else if ( SMLength > MAX_MBX_SIZE ) + /* receive mailbox size is too great */ + result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; + else if ( SMAddress < MIN_MBX_WRITE_ADDRESS ) + /* receive mailbox address is too small */ + result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; + else if ( SMAddress > MAX_MBX_WRITE_ADDRESS) + /* receive mailbox address is too great */ + result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; + + + if ( result == 0 ) + { + /* check the Sync Manager Parameter for the Send Mailbox (Sync Manager Channel 1) */ +/*ECATCHANGE_START(V5.11) HW1*/ + pSyncMan = GetSyncMan(MAILBOX_READ); +/*ECATCHANGE_END(V5.11) HW1*/ + + SMLength = pSyncMan->Length; + SMAddress = pSyncMan->PhysicalStartAddress; + +/* ECATCHANGE_START(V5.11) HW2*/ + //Check if the start address and length are even 16Bit addresses + if ((SMLength & 0x1) > 0) + return ALSTATUSCODE_INVALIDSMCFG; + + if ((SMAddress & 0x1) > 0) + return ALSTATUSCODE_INVALIDSMCFG; +/* ECATCHANGE_END(V5.11) HW2*/ + + if (!(pSyncMan->Settings[SM_SETTING_ACTIVATE_OFFSET] & SM_SETTING_ENABLE_VALUE)) + /* send mailbox is not enabled */ + result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; + else if ( (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_DIRECTION_MASK) != SM_SETTING_DIRECTION_READ_VALUE) + /* receive mailbox is not readable by the master*/ + result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; + else if ( (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_MODE_MASK) != SM_SETTING_MODE_ONE_BUFFER_VALUE ) + /* receive mailbox is not in one buffer mode */ + result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; + else if ( SMLength < MIN_MBX_SIZE ) + /* send mailbox size is too small */ + result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; + else if ( SMLength > MAX_MBX_SIZE ) + /* send mailbox size is too great */ + result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; + else if ( SMAddress < MIN_MBX_READ_ADDRESS ) + /* send mailbox address is too small */ + result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; + else if ( SMAddress > MAX_MBX_READ_ADDRESS ) + /* send mailbox address is too great */ + result = ALSTATUSCODE_INVALIDMBXCFGINPREOP; + + } + + if ( result == 0 && maxChannel > PROCESS_DATA_IN ) + { + /* b3BufferMode is only set, if inputs and outputs are running in 3-Buffer-Mode when leaving this function */ + b3BufferMode = TRUE; + /* check the Sync Manager Parameter for the Inputs (Sync Manager Channel 2) */ +/*ECATCHANGE_START(V5.11) HW1*/ + pSyncMan = GetSyncMan(PROCESS_DATA_IN); +/*ECATCHANGE_END(V5.11) HW1*/ + + SMLength = pSyncMan->Length; + SMAddress = pSyncMan->PhysicalStartAddress; + +/* ECATCHANGE_START(V5.11) HW2*/ + //Check if the start address and length are even 16Bit addresses + if ((SMLength & 0x1) > 0) + return ALSTATUSCODE_INVALIDSMCFG; + + if ((SMAddress & 0x1) > 0) + return ALSTATUSCODE_INVALIDSMCFG; +/* ECATCHANGE_END(V5.11) HW2*/ + + if ((pSyncMan->Settings[SM_SETTING_ACTIVATE_OFFSET] & SM_SETTING_ENABLE_VALUE) != 0 && SMLength == 0 ) + /* the SM3 size is 0 and the SM3 is active */ + result = SYNCMANCHSETTINGS+1; + else if (pSyncMan->Settings[SM_SETTING_ACTIVATE_OFFSET] & SM_SETTING_ENABLE_VALUE) + { + /* Sync Manager Channel 3 is active, input size has to greater 0 */ + if ( SMLength != nPdInputSize || nPdInputSize == 0 || SMLength > MAX_PD_INPUT_SIZE) + /* sizes don't match */ + result = SYNCMANCHSIZE+1; + else + /* sizes matches */ + if ( (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_DIRECTION_MASK) == SM_SETTING_DIRECTION_READ_VALUE ) + { + /* settings match */ + if ( ( ( nAlStatus == STATE_PREOP )&&( SMAddress >= MIN_PD_READ_ADDRESS )&&( SMAddress <= MAX_PD_READ_ADDRESS ) ) + ||( ( nAlStatus != STATE_PREOP )&&( SMAddress == nEscAddrInputData ) ) + ) + { + /* addresses match */ + + if ( (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_MODE_MASK) == SM_SETTING_MODE_ONE_BUFFER_VALUE ) + /* inputs are running in 1-Buffer-Mode, reset flag b3BufferMode */ + b3BufferMode = FALSE; + } + else + /* input address is out of the allowed area or has changed in SAFEOP or OP */ + result = SYNCMANCHADDRESS+1; + } + else + /* input settings do not match */ + result = SYNCMANCHSETTINGS+1; + } + else if ( SMLength != 0 || nPdInputSize != 0 ) + /* input size is not zero although the SM3 channel is not enabled */ + result = SYNCMANCHSIZE+1; + + + + if ( result != 0 ) + { + result = ALSTATUSCODE_INVALIDSMINCFG; + } + } + + +// else + if ( result == 0 && maxChannel > PROCESS_DATA_OUT ) + { + /* check the Sync Manager Parameter for the Outputs (Sync Manager Channel 2) */ +/*ECATCHANGE_START(V5.11) HW1*/ + pSyncMan = GetSyncMan(PROCESS_DATA_OUT); +/*ECATCHANGE_END(V5.11) HW1*/ + + SMLength = pSyncMan->Length; + SMAddress = pSyncMan->PhysicalStartAddress; + +/* ECATCHANGE_START(V5.11) HW2*/ + //Check if the start address and length are even 16Bit addresses + if ((SMLength & 0x1) > 0) + return ALSTATUSCODE_INVALIDSMCFG; + + if ((SMAddress & 0x1) > 0) + return ALSTATUSCODE_INVALIDSMCFG; +/* ECATCHANGE_END(V5.11) HW2*/ + + if ( (pSyncMan->Settings[SM_SETTING_ACTIVATE_OFFSET] & SM_SETTING_ENABLE_VALUE) != 0 && SMLength == 0 ) + /* the SM2 size is 0 and the SM2 is active */ + result = SYNCMANCHSETTINGS+1; + else if (pSyncMan->Settings[SM_SETTING_ACTIVATE_OFFSET] & SM_SETTING_ENABLE_VALUE) + { + /* Sync Manager Channel 2 is active, output size has to greater 0 */ + if ( SMLength == nPdOutputSize && nPdOutputSize != 0 && SMLength <= ((UINT16)MAX_PD_OUTPUT_SIZE)) + + { + /* sizes match */ + if ( (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_DIRECTION_MASK) == SM_SETTING_DIRECTION_WRITE_VALUE ) + { + /* settings match */ + if ( ( ( nAlStatus == STATE_PREOP )&&( SMAddress >= MIN_PD_WRITE_ADDRESS )&&( SMAddress <= MAX_PD_WRITE_ADDRESS ) ) + ||( ( nAlStatus != STATE_PREOP )&&( SMAddress == nEscAddrOutputData ) ) + ) + { + /* addresses match */ + { + /* check, if watchdog trigger is enabled */ + if (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_WATCHDOG_VALUE) + { + bWdTrigger = TRUE; + } + else + { + bWdTrigger = FALSE; + } + + if ( (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_MODE_MASK) == SM_SETTING_MODE_ONE_BUFFER_VALUE ) + /* outputs are running in 1-Buffer-Mode, reset flag b3BufferMode */ + b3BufferMode = FALSE; + } + } + else + /* output address is out of the allowed area or has changed in SAFEOP or OP */ + result = SYNCMANCHADDRESS+1; + } + else + /* output settings do not match */ + result = SYNCMANCHSETTINGS+1; + } + else + /* output sizes don't match */ + result = SYNCMANCHSIZE+1; + } + else if ( SMLength != 0 || nPdOutputSize != 0 ) + /* output size is not zero although the SM2 channel is not enabled */ + result = SYNCMANCHSIZE+1; + + if ( result != 0 ) + { + result = ALSTATUSCODE_INVALIDSMOUTCFG; + } + } + + + if ( result == 0 ) + { + /* the Enable-Byte of the rest of the SM channels has to be read to acknowledge the SM-Change-Interrupt */ + for (i = maxChannel; i < nMaxSyncMan; i++) + { +/*ECATCHANGE_START(V5.11) HW1*/ + pSyncMan = GetSyncMan(i); +/*ECATCHANGE_END(V5.11) HW1*/ + SMActivate = pSyncMan->Settings[SM_SETTING_ACTIVATE_OFFSET]; + } + } + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \return AL Status Code (see ecatslv.h ALSTATUSCODE_....) + + \brief This function is called in case of the state transition from PREOP to SAFEOP. + |brief the areas of the Sync Managers will be checked for overlapping, + \brief the synchronization mode (Free Run, Synchron, Distributed Clocks) is selected, + \brief the requested cycle time will be checked, the watchdog is started + \brief and the AL Event Mask register will be set + +*//////////////////////////////////////////////////////////////////////////////////////// + +UINT16 StartInputHandler(void) +{ + TSYNCMAN ESCMEM * pSyncMan; + UINT16 dcControl; + UINT16 wdiv = 0; +/*ECATCHANGE_START(V5.11) ECAT4*/ + UINT16 wd = 0; +/*ECATCHANGE_END(V5.11) ECAT4*/ + UINT32 cycleTimeSync0 = 0; /* Sync0 cycle time */ + UINT32 cycleTimeSync1 = 0; /* Delay between the Sync0 and Sycn1 signal. A new Sync1 cycle starts on the next Sync0 signal after Sync1 signal.*/ + BOOL bSubordinatedCycles = FALSE; + + UINT16 nPdInputBuffer = 3; + UINT16 nPdOutputBuffer = 3; + + UINT16 SyncType0x1C32 = 0; /* Helper variable for sync type for SM2 (required if no CoE is supported or no output process data available)*/ + UINT16 SyncType0x1C33 = 0; /* Helper variable for sync type for SM3 (required if no CoE is supported or no input process data available)*/ + + UINT16 u16MinSuppSyncType = 0xFFFF; /* Minimum supported Sync Types */ + + u16MinSuppSyncType &= sSyncManOutPar.u16SyncTypesSupported; + u16MinSuppSyncType &= sSyncManInPar.u16SyncTypesSupported; + + u16ALEventMask = 0; + + /* + --- Check if SyncManager areas overlapping --- + */ + bEcatFirstOutputsReceived = FALSE; + + /* get a pointer to the Sync Manager Channel 2 (Outputs) */ +/*ECATCHANGE_START(V5.11) HW1*/ + pSyncMan = GetSyncMan(PROCESS_DATA_OUT); +/*ECATCHANGE_END(V5.11) HW1*/ + /* store the address of the Sync Manager Channel 2 (Outputs) */ + nEscAddrOutputData = pSyncMan->PhysicalStartAddress; + /* get the number of output buffer used for calculating the address areas */ + if (pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_MODE_ONE_BUFFER_VALUE) + { + nPdOutputBuffer = 1; + } + + + /* get a pointer to the Sync Manager Channel 3 (Inputs) */ +/*ECATCHANGE_START(V5.11) HW1*/ + pSyncMan = GetSyncMan(PROCESS_DATA_IN); +/*ECATCHANGE_END(V5.11) HW1*/ + /* store the address of the Sync Manager Channel 3 (Inputs)*/ + nEscAddrInputData = pSyncMan->PhysicalStartAddress; + + /* get the number of input buffer used for calculating the address areas */ + if ( pSyncMan->Settings[SM_SETTING_CONTROL_OFFSET] & SM_SETTING_MODE_ONE_BUFFER_VALUE ) + nPdInputBuffer = 1; + + /* it has be checked if the Sync Manager memory areas for Inputs and Outputs will not overlap + the Sync Manager memory areas for the Mailbox */ + + if (((nEscAddrInputData + nPdInputSize * nPdInputBuffer) > u16EscAddrSendMbx && (nEscAddrInputData < (u16EscAddrSendMbx + u16SendMbxSize))) + || ((nEscAddrInputData + nPdInputSize * nPdInputBuffer) > u16EscAddrReceiveMbx && (nEscAddrInputData < (u16EscAddrReceiveMbx + u16ReceiveMbxSize))) + ) + { + return ALSTATUSCODE_INVALIDSMINCFG; + } + + if ( + ((nEscAddrOutputData + nPdOutputSize * nPdOutputBuffer) > u16EscAddrSendMbx && (nEscAddrOutputData < (u16EscAddrSendMbx + u16SendMbxSize))) + ||((nEscAddrOutputData + nPdOutputSize * nPdOutputBuffer) > u16EscAddrReceiveMbx && (nEscAddrOutputData < (u16EscAddrReceiveMbx + u16ReceiveMbxSize))) + || + ((nEscAddrOutputData + nPdOutputSize * nPdOutputBuffer) > nEscAddrInputData && (nEscAddrOutputData < (nEscAddrInputData + nPdInputSize))) + ) + { + /* Sync Manager Channel 2 memory area (Outputs) overlaps the Sync Manager memory areas for the Mailbox + or the Sync Manager Channel 3 memory area (Inputs) */ + return ALSTATUSCODE_INVALIDSMOUTCFG; + } + + /* + --- Check configured synchronisation --- + */ + + /* Get the DC Control/Activation register value*/ + /*Read registers 0x980:0x981 (corresponding masks are adapted)*/ + HW_EscReadWord(dcControl, ESC_DC_UNIT_CONTROL_OFFSET); + dcControl = SWAPWORD(dcControl); + + // Cycle time for Sync0 + HW_EscReadDWord(cycleTimeSync0, ESC_DC_SYNC0_CYCLETIME_OFFSET); + cycleTimeSync0 = SWAPDWORD(cycleTimeSync0); + + // Cycle time for Sync1 + HW_EscReadDWord(cycleTimeSync1, ESC_DC_SYNC1_CYCLETIME_OFFSET); + cycleTimeSync1 = SWAPDWORD(cycleTimeSync1); + + + SyncType0x1C32 = sSyncManOutPar.u16SyncType; + SyncType0x1C33 = sSyncManInPar.u16SyncType; + + + + /* check general DC register plausibility and if configuration is supported + - 0x981 DC Active + - 0x9A0:0x9A3 Sync0 Cycle + - 0x9A4:0x9A7 Sync1 Cycle + */ + if((dcControl & (ESC_DC_SYNC_UNIT_ACTIVE_MASK | ESC_DC_SYNC_UNIT_AUTO_ACTIVE_MASK)) != 0) + { + /* DC unit is active at least one Sync signal shall be generated */ + if((dcControl & (ESC_DC_SYNC0_ACTIVE_MASK | ESC_DC_SYNC1_ACTIVE_MASK)) == 0) + { + return ALSTATUSCODE_DCINVALIDSYNCCFG; + } + + /* If Sync1 shall only be active if also Sync0 will be generated*/ + if(((dcControl & ESC_DC_SYNC0_ACTIVE_MASK) == 0) + && ((dcControl & ESC_DC_SYNC1_ACTIVE_MASK) != 0)) + { + return ALSTATUSCODE_DCINVALIDSYNCCFG; + } + + if(u16MinSuppSyncType != 0) + { +/*ECATCHANGE_START(V5.11) ESM2*/ + if((((u16MinSuppSyncType & SYNCTYPE_DCSYNC0SUPP) == 0) && ((dcControl & ESC_DC_SYNC0_ACTIVE_MASK) != 0)) + ||(((u16MinSuppSyncType & SYNCTYPE_DCSYNC1SUPP) == 0) && ((dcControl & ESC_DC_SYNC1_ACTIVE_MASK) != 0))) +/*ECATCHANGE_END(V5.11) ESM2*/ + { + /* Sync0 is not supported but will be generated*/ + return ALSTATUSCODE_DCINVALIDSYNCCFG; + } + } + + /*Check if Sync0 cycle time is supported*/ + if ( cycleTimeSync0 != 0 && (cycleTimeSync0 < MIN_PD_CYCLE_TIME || cycleTimeSync0 > MAX_PD_CYCLE_TIME) ) + return ALSTATUSCODE_DCSYNC0CYCLETIME; + + + /* Check if Subordinated cycles are configured */ + if(((dcControl & ESC_DC_SYNC0_ACTIVE_MASK) != 0) && ((dcControl & ESC_DC_SYNC1_ACTIVE_MASK) != 0)) + { + /* For Subordinated cycles both Sync signals shall be active and Sync0 is not configured in single shot (cycle time == 0)*/ +/*ECATCHANGE_START(V5.11) ESM1*/ + if((cycleTimeSync1 > 0) && (cycleTimeSync1 >= cycleTimeSync0)) +/*ECATCHANGE_END(V5.11) ESM1*/ + { + bSubordinatedCycles = TRUE; + } + } + + /* Dump an error if subordinated cycles are configured but not supported */ + if(bSubordinatedCycles && ((u16MinSuppSyncType & SYNCTYPE_SUBCYCLESUPP) == 0)) + { + return ALSTATUSCODE_DCINVALIDSYNCCFG; + } + } + + + /* + Check if the user configured Sync Type matches the DC register values (if the Sync Type is supported was already checked in the object write function) + */ + if(bSyncSetByUser) + { + if((dcControl & (ESC_DC_SYNC_UNIT_ACTIVE_MASK | ESC_DC_SYNC_UNIT_AUTO_ACTIVE_MASK)) == 0) + { + /* DC out unit not enabled => no DC mode shall be set */ + if((SyncType0x1C32 == SYNCTYPE_DCSYNC0) || (SyncType0x1C32 == SYNCTYPE_DCSYNC1) + ||(SyncType0x1C33 == SYNCTYPE_DCSYNC0) || (SyncType0x1C33 == SYNCTYPE_DCSYNC1)) + { + return ALSTATUSCODE_DCINVALIDSYNCCFG; + } + } //if((dcControl & (ESC_DC_SYNC_UNIT_ACTIVE_MASK | ESC_DC_SYNC_UNIT_AUTO_ACTIVE_MASK)) == 0) + else + { + if((dcControl & ESC_DC_SYNC1_ACTIVE_MASK) == 0) + { + /* No Sync 1 is generated => No Sync1 Sync Type shall configured*/ +/*ECATCHANGE_START(V5.11) ESM3*/ + if((SyncType0x1C32 == (UINT16)SYNCTYPE_DCSYNC1) + ||(SyncType0x1C33 == (UINT16)SYNCTYPE_DCSYNC1)) +/*ECATCHANGE_END(V5.11) ESM3*/ + { + return ALSTATUSCODE_DCINVALIDSYNCCFG; + } + } //if((dcControl & ESC_DC_SYNC1_ACTIVE_MASK) == 0) + + if((dcControl & ESC_DC_SYNC0_ACTIVE_MASK) == 0) + { + /* No Sync 0 is generated => No Sync0 Sync Type shall configured*/ +/*ECATCHANGE_START(V5.11) ESM3*/ + if((SyncType0x1C32 == (UINT16)SYNCTYPE_DCSYNC0) + ||(SyncType0x1C33 == (UINT16)SYNCTYPE_DCSYNC0)) +/*ECATCHANGE_END(V5.11) ESM3*/ + { + return ALSTATUSCODE_DCINVALIDSYNCCFG; + } + } //if((dcControl & ESC_DC_SYNC0_ACTIVE_MASK) == 0) + + } + } //if(bSyncSetByUser) + else + { + /* No Sync Type selected by user => Configure Sync Type based on DC register values*/ + if((dcControl & (ESC_DC_SYNC_UNIT_ACTIVE_MASK | ESC_DC_SYNC_UNIT_AUTO_ACTIVE_MASK)) == 0) + { + /* Activation or auto activation of the Sync Out Unit is disabled => Free Run or SM Sync is configured*/ + + /* AL Event enabled => Configure SM Sync*/ + if (nPdOutputSize > 0) + { + SyncType0x1C32 = SYNCTYPE_SM_SYNCHRON; + + if (nPdInputSize > 0) + SyncType0x1C33 = SYNCTYPE_SM2_SYNCHRON; + else + SyncType0x1C33 = SYNCTYPE_FREERUN; + } + else if (nPdInputSize > 0) + { + SyncType0x1C32 = SYNCTYPE_FREERUN; + SyncType0x1C33 = SYNCTYPE_SM_SYNCHRON; + } + else + { + SyncType0x1C32 = SYNCTYPE_FREERUN; + SyncType0x1C33 = SYNCTYPE_FREERUN; + } + sSyncManOutPar.u16GetCycleTime = 1; + + } + else + { + if (nPdOutputSize > 0) + { + /* Sync Signal generation is active*/ + if (bSubordinatedCycles) + { + SyncType0x1C32 = SYNCTYPE_DCSYNC1; + } + else + { + SyncType0x1C32 = SYNCTYPE_DCSYNC0; + } + } + else + { + SyncType0x1C32 = SYNCTYPE_FREERUN; + } + + + if (nPdInputSize > 0) + { + if ((dcControl & ESC_DC_SYNC1_ACTIVE_MASK) != 0) + { + /* If Sync1 is available the inputs will always be mapped with Sync1 */ + SyncType0x1C33 = SYNCTYPE_DCSYNC1; + } + else + { + /* Map Inputs based on Sync0*/ + SyncType0x1C33 = SYNCTYPE_DCSYNC0; + } + } + else + { + SyncType0x1C33 = SYNCTYPE_FREERUN; + } + } + } + + /* Update Cycle time entries if DC Sync Mode enabled */ + if(SyncType0x1C32 == SYNCTYPE_DCSYNC1) + { + sSyncManOutPar.u32Sync0CycleTime = (UINT32)cycleTimeSync0; + +/*ECATCHANGE_START(V5.11) ECAT4*/ + sSyncManInPar.u32Sync0CycleTime = (UINT32)cycleTimeSync0; +/*ECATCHANGE_END(V5.11) ECAT4*/ + } + else if(SyncType0x1C32 == SYNCTYPE_DCSYNC0) + { + sSyncManOutPar.u32Sync0CycleTime = (UINT32)cycleTimeSync0; + +/*ECATCHANGE_START(V5.11) ECAT4*/ + sSyncManInPar.u32Sync0CycleTime = (UINT16)cycleTimeSync0; +/*ECATCHANGE_END(V5.11) ECAT4*/ + } + + /* Set global flags based on Sync Type */ + if ( !b3BufferMode ) + { + /* 1-Buffer-Mode configured => For free run it shall be 3Buffer mode*/ + if (( SyncType0x1C32 == SYNCTYPE_FREERUN ) || ( SyncType0x1C33 == SYNCTYPE_FREERUN )) + { + return ALSTATUSCODE_FREERUNNEEDS3BUFFERMODE; + } + } + + /* If no free run is supported the EscInt is always enabled*/ + if (( SyncType0x1C32 != SYNCTYPE_FREERUN ) || ( SyncType0x1C33 != SYNCTYPE_FREERUN )) + { + /* ECAT Synchron Mode, the ESC interrupt is enabled */ + bEscIntEnabled = TRUE; + } + + /* Update value for AL Event Mask register (0x204) */ + if(bEscIntEnabled) + { + if(nPdOutputSize > 0) + { + u16ALEventMask = PROCESS_OUTPUT_EVENT; + } + else if(nPdInputSize > 0) + { + u16ALEventMask = PROCESS_INPUT_EVENT; + } + + } + + if ((SyncType0x1C32 == SYNCTYPE_DCSYNC0) || (SyncType0x1C32 == SYNCTYPE_DCSYNC1) + || (SyncType0x1C33 == SYNCTYPE_DCSYNC0) || (SyncType0x1C33 == SYNCTYPE_DCSYNC1))/* Sync to Sync0 or Sync1 is enabled*/ + { + /* slave is running in DC-mode */ + bDcSyncActive = TRUE; + +/*ECATCHANGE_START(V5.11) ECAT4*/ + /*In case of an Input only application with DC no PDI Isr handling is required*/ + if (nPdOutputSize == 0) + { + u16ALEventMask = 0; + } +/*ECATCHANGE_END(V5.11) ECAT4*/ + } + + sSyncManOutPar.u16SyncType = SyncType0x1C32; + sSyncManInPar.u16SyncType = SyncType0x1C33; + + /* Calculate number of Sync0 events within one SM cycle and the Sync0 events on which the inputs has to be latched*/ + LatchInputSync0Value = 0; + LatchInputSync0Counter = 0; + u16SmSync0Value = 0; + u16SmSync0Counter = 0; + + + if(bSubordinatedCycles == TRUE) + { + /* get the number of Sync0 event within on SM cycle */ + if(cycleTimeSync1 >= cycleTimeSync0) + { + u16SmSync0Value = (UINT16)(cycleTimeSync1 / cycleTimeSync0); + + if((cycleTimeSync1 % cycleTimeSync0) == 0) + { + /* if the Sync1cycletime/Sync0cycletime ratio is even one additional tick */ + u16SmSync0Value ++; + } + } + else + { + u16SmSync0Value = 1; + } + + /* Calculate the Sync0 tick on which the inputs shall be latched (last Sync0 before the next Sync1 event)*/ + LatchInputSync0Value = (UINT16) (cycleTimeSync1 / cycleTimeSync0); + + if((cycleTimeSync1 % cycleTimeSync0) > 0) + LatchInputSync0Value++; + + } + else + { + if(SyncType0x1C32 == SYNCTYPE_DCSYNC0) + { + /* if SyncType of 0x1C32 is 2 the Sync0 event is trigger once during a SM cycle */ + u16SmSync0Value = 1; + } + + if(SyncType0x1C33 != SYNCTYPE_DCSYNC1) + { + LatchInputSync0Value = 1; + } + } + + + + /* reset the error counter indicating synchronization problems */ + sCycleDiag.syncFailedCounter = 0; + + + /* + --- Check watchdog settings --- + */ + + /*get the watchdog time (register 0x420). if value is > 0 watchdog is active*/ +/*ECATCHANGE_START(V5.11) ECAT4*/ + HW_EscReadWord(wd, ESC_PD_WD_TIME); + wd = SWAPWORD(wd); +/*ECATCHANGE_END(V5.11) ECAT4*/ + + if (nPdOutputSize > 0 && wd != 0 ) + { + /*get watchdog divider (register 0x400)*/ + HW_EscReadWord(wdiv, ESC_WD_DIVIDER_OFFSET); + wdiv = SWAPWORD(wdiv); + if ( wdiv != 0 ) + { + /* the ESC subtracts 2 in register 0x400 so it has to be added here */ + UINT32 d = wdiv+2; + + d *= wd; + /* store watchdog in ms in variable u16WdValue */ + /* watchdog value has to be rounded up */ + d += 24999; + d /= 25000; + EcatWdValue = (UINT16) d; + } + else + { + wd = 0; + /* wd value has to be set to zero, if the wd is 0 */ + EcatWdValue = 0; + } + } + else + { + /* the watchdog is deactivated or slave has no output process data*/ + wdiv = 0; + EcatWdValue = 0; + } + + if((EcatWdValue == 0 && bWdTrigger) || (EcatWdValue != 0 && !bWdTrigger)) + { + /* if the WD-Trigger in the Sync Manager Channel 2 Control-Byte is set (Bit 6 of Register 0x814) + an error has to be returned */ + return ALSTATUSCODE_INVALIDWDCFG; + } + + if ( bEscIntEnabled && nPdOutputSize != 0 ) + { + /* ECAT synchron Mode is active, the Sync Manager Channel 2 event + has to activated in the AL-Event mask register */ + u16ALEventMask |= PROCESS_OUTPUT_EVENT; + } +/*The application ESM function is separated from this function to handle pending transitions*/ + + Sync0WdValue = 0; + Sync0WdCounter = 0; + Sync1WdCounter = 0; + Sync1WdValue = 0; + bDcRunning = FALSE; + bSmSyncSequenceValid = FALSE; + i16WaitForPllRunningTimeout = 0; +/*ECATCHANGE_START(V5.11) ECAT4*/ + sSyncManInPar.u8SyncError = 0; +/*ECATCHANGE_END(V5.11) ECAT4*/ + sSyncManOutPar.u8SyncError = 0; + sSyncManOutPar.u16SmEventMissedCounter = 0; + + /* calculate the Sync0/Sync1 watchdog timeouts */ + if ( (dcControl & ESC_DC_SYNC0_ACTIVE_MASK) != 0 ) + { + /*calculate the Sync0 Watchdog counter value the minimum value is 1 ms + if the sync0 cycle is greater 500us the Sync0 Wd value is 2*Sycn0 cycle */ + if(cycleTimeSync0 == 0) + { + Sync0WdValue = 0; + } + else + { + UINT32 Sync0Cycle = cycleTimeSync0/100000; + if(Sync0Cycle < 5) + { + /*Sync0 cycle less than 500us*/ + Sync0WdValue = 1; + } + else + { + Sync0WdValue = (UINT16)(Sync0Cycle*2)/10; + } + } + + /* Calculate also the watchdog time for Sync1*/ + if ( (dcControl & ESC_DC_SYNC1_ACTIVE_MASK) != 0 ) + { + if(cycleTimeSync1 < cycleTimeSync0) + { + /* Sync 1 has the same cycle time than Sync0 (maybe with a shift (cycleTimeSync1 > 0))*/ + Sync1WdValue = Sync0WdValue; + } + else + { + /* Sync1 cycle is larger than Sync0 (e.g. subordinated Sync0 cycles) */ + UINT32 Sync1Cycle = cycleTimeSync1/100000; + if(Sync1Cycle < 5) + { + /*Sync0 cycle less than 500us*/ + Sync1WdValue = 1; + } + else + { + Sync1WdValue = (UINT16)(Sync1Cycle*2)/10; + } + + /* add one Sync0 cycle because the Sync1 cycle starts on the next Sync0 after the Sync1 signal */ + Sync1WdValue += Sync0WdValue/2; + } + } + } + + + + + + if(nPdOutputSize > 0) + { +/*ECATCHANGE_START(V5.11) HW1*/ + EnableSyncManChannel(PROCESS_DATA_OUT); +/*ECATCHANGE_END(V5.11) HW1*/ + } + + if(nPdInputSize > 0) + { +/*ECATCHANGE_START(V5.11) HW1*/ + EnableSyncManChannel(PROCESS_DATA_IN); +/*ECATCHANGE_END(V5.11) HW1*/ + } + + /*write initial input data*/ + PDO_InputMapping(); + + return ALSTATUSCODE_NOERROR; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \return AL Status Code (see ecatslv.h ALSTATUSCODE_....) + + \brief This function is called in case of the state transition from SAFEOP to OP. + \brief It will be checked if outputs had to be received before switching to OP + \brief and the state transition would be refused if outputs are missing + +*//////////////////////////////////////////////////////////////////////////////////////// + +UINT16 StartOutputHandler(void) +{ +/*ECATCHANGE_START(V5.11) ESM6*/ + /* by default the SO transition should be completed in AlControlRes(). + required to support also masters which starts to send process data after the SO transition was triggered + (if the master don't send process data within "SAFEOP2OPTIMEOUT" the transition is rejected)*/ + UINT16 result = NOERROR_INWORK; +/*ECATCHANGE_END(V5.11) ESM6*/ + if(bLocalErrorFlag) + { + /*Local error still exists => skip state request to OP and response with "u16LocalErrorCode"*/ + return u16LocalErrorCode; + } +/*The application ESM function is separated from this function to handle pending transitions*/ + + + /*DC synchronisation is active wait until pll is valid*/ + if(bDcSyncActive) + { + i16WaitForPllRunningTimeout = 200; + i16WaitForPllRunningCnt = 0; + } + + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \brief This function is called in case of the state transition from OP to SAFEOP + \brief the outputs can be set to an application specific safe state, + \brief the state transition can be delayed by returning NOERROR_INWORK + +*//////////////////////////////////////////////////////////////////////////////////////// + +void StopOutputHandler(void) +{ + /* reset the flags that outputs were received and that the slave is in OP */ + bEcatFirstOutputsReceived = FALSE; + bEcatOutputUpdateRunning = FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \brief This function is called in case of the state transition from SAFEOP to PREOP + +*//////////////////////////////////////////////////////////////////////////////////////// + +void StopInputHandler(void) +{ + if(nPdOutputSize > 0) + { + /* disable the Sync Manager Channel 2 (outputs) */ +/*ECATCHANGE_END(V5.11) HW1*/ + DisableSyncManChannel(PROCESS_DATA_OUT); +/*ECATCHANGE_END(V5.11) HW1*/ + } + + if(nPdInputSize > 0) + { + /*disable Sync Manager 3 (inputs) if no outputs available*/ +/*ECATCHANGE_START(V5.11) HW1*/ + DisableSyncManChannel(PROCESS_DATA_IN); +/*ECATCHANGE_END(V5.11) HW1*/ + } + + /* reset the events in the AL Event mask register (0x204) */ + { + UINT16 ResetMask = SYNC0_EVENT | SYNC1_EVENT; + ResetMask |= PROCESS_OUTPUT_EVENT; + ResetMask |= PROCESS_INPUT_EVENT; + +/*ECATCHANGE_START(V5.11) HW1*/ + ResetALEventMask( ~(ResetMask) ); +/*ECATCHANGE_END(V5.11) HW1*/ + } + /* reset the flags */ + bEcatFirstOutputsReceived = FALSE; + bEscIntEnabled = FALSE; +/*The application ESM function is separated from this function to handle pending transitions*/ + + bDcSyncActive = FALSE; + bDcRunning = FALSE; + bSmSyncSequenceValid = FALSE; + u16SmSync0Value = 0; + u16SmSync0Counter = 0; + + Sync0WdValue = 0; + Sync0WdCounter = 0; + Sync1WdCounter = 0; + Sync1WdValue = 0; + LatchInputSync0Value = 0; + LatchInputSync0Counter = 0; +/*ECATCHANGE_START(V5.11) ECAT4*/ + sSyncManInPar.u8SyncError = 0; +/*ECATCHANGE_END(V5.11) ECAT4*/ + + i16WaitForPllRunningTimeout = 0; + + bWdTrigger = FALSE; + bEcatInputUpdateRunning = FALSE; + + /*Indicate no user specified Sync mode*/ + bSyncSetByUser = FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** +\brief This function is called when a X to Init transition is completed + +*//////////////////////////////////////////////////////////////////////////////////////// + +void BackToInitTransition(void) +{ + /* Reset indication that the user has written a sync mode*/ + bSyncSetByUser = FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param alStatus New AL Status (written to register 0x130) + \param alStatusCode New AL Status Code (written to register 0x134) + + \brief The function changes the state of the EtherCAT ASIC to the requested. +*//////////////////////////////////////////////////////////////////////////////////////// +void SetALStatus(UINT8 alStatus, UINT16 alStatusCode) +{ + UINT16 Value = alStatusCode; + + /*update global status variable if required*/ + if(nAlStatus != alStatus) + { + nAlStatus = alStatus; + } + + + if (alStatusCode != 0xFFFF) + { + Value = SWAPWORD(Value); + + HW_EscWriteWord(Value,ESC_AL_STATUS_CODE_OFFSET); + } + + Value = nAlStatus; + Value = SWAPWORD(Value); + HW_EscWriteWord(Value,ESC_AL_STATUS_OFFSET); + +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param alControl requested new state + \param alStatusCode requested status code + + \brief This function handles the EtherCAT State Machine. It is called + * in case of an AL Control event (Bit 0 of AL-Event (Reg 0x220), + when the Master has written the AL Control Register (from ECAT_Main), + alControl contains the content of the AL Control (Reg 0x120) + * in case of a SM-Change event (Bit 4 of AL-Event (Reg 0x220)), + when an Activate SYNCM y register is written by the master (from ECAT_Main), + alControl contains the actual state (Bit 0-3 of AL Status (Reg 0x130)) + * in case of a locally expired watchdog (from ECAT_Main), + alControl contains the requested new state (SAFE_OP) + * in case of an application specific event to change the EtherCAT state (from application), + alControl contains the requested new state (INIT, PRE_OP or SAFE_OP) + +*//////////////////////////////////////////////////////////////////////////////////////// + +void AL_ControlInd(UINT8 alControl, UINT16 alStatusCode) +{ + UINT16 result = 0; + UINT8 bErrAck = 0; + UINT8 stateTrans; + /*deactivate ESM timeout counter*/ + EsmTimeoutCounter = -1; + bApplEsmPending = TRUE; + + /* reset the Error Flag in case of acknowledge by the Master */ + if ( alControl & STATE_CHANGE ) + { + bErrAck = 1; + nAlStatus &= ~STATE_CHANGE; + /*enable SM2 is moved to state transition block. First check SM Settings.*/ + } + else if ( (nAlStatus & STATE_CHANGE) + // HBu 17.04.08: the error has to be acknowledged before when sending the same (or a higher) state + // (the error was acknowledged with the same state before independent of the acknowledge flag) + /*Error Acknowledge with 0xX1 is allowed*/ + && (alControl & STATE_MASK) != STATE_INIT ) + /* the error flag (Bit 4) is set in the AL-Status and the ErrAck bit (Bit 4) + is not set in the AL-Control, so the state cannot be set to a higher state + and the new state request will be ignored */ + return; + else + { + nAlStatus &= STATE_MASK; + } + + /* generate a variable for the state transition + (Bit 0-3: new state (AL Control), Bit 4-7: old state (AL Status) */ + alControl &= STATE_MASK; + stateTrans = nAlStatus; + stateTrans <<= 4; + stateTrans += alControl; + + + /* check the SYNCM settings depending on the state transition */ + switch ( stateTrans ) + { + case INIT_2_PREOP: + case OP_2_PREOP: + case SAFEOP_2_PREOP: + case PREOP_2_PREOP: + /* in PREOP only the SYNCM settings for SYNCM0 and SYNCM1 (mailbox) + are checked, if result is unequal 0, the slave will stay in or + switch to INIT and set the ErrorInd Bit (bit 4) of the AL-Status */ + result = CheckSmSettings(MAILBOX_READ+1); + break; + case PREOP_2_SAFEOP: + { + /* before checking the SYNCM settings for SYNCM2 and SYNCM3 (process data) + the expected length of input data (nPdInputSize) and output data (nPdOutputSize) + could be adapted (changed by PDO-Assign and/or PDO-Mapping) + if result is unequal 0, the slave will stay in PREOP and set + the ErrorInd Bit (bit 4) of the AL-Status */ + result = APPL_GenerateMapping(&nPdInputSize,&nPdOutputSize); + + if (result != 0) + break; + } + case SAFEOP_2_OP: + case OP_2_SAFEOP: + case SAFEOP_2_SAFEOP: + case OP_2_OP: + /* in SAFEOP or OP the SYNCM settings for all SYNCM are checked + if result is unequal 0, the slave will stay in or + switch to PREOP and set the ErrorInd Bit (bit 4) of the AL-Status */ + result = CheckSmSettings(nMaxSyncMan); + break; + } + + if ( result == 0 ) + { + /* execute the corresponding local management service(s) depending on the state transition */ + nEcatStateTrans = 0; + switch ( stateTrans ) + { + case INIT_2_BOOT : + result = ALSTATUSCODE_BOOTNOTSUPP; + break; + + case BOOT_2_INIT : + result = ALSTATUSCODE_BOOTNOTSUPP; + + BackToInitTransition(); + + break; + case INIT_2_PREOP : + + UpdateEEPROMLoadedState(); + + if (EepromLoaded == FALSE) + { + //return an error if the EEPROM was not loaded correct (device restart is required after the new EEPORM update) + result = ALSTATUSCODE_EE_ERROR; + } + if (result == 0) + { + /* MBX_StartMailboxHandler (in mailbox.c) checks if the areas of the mailbox + sync managers SYNCM0 and SYNCM1 overlap each other + if result is unequal 0, the slave will stay in INIT + and sets the ErrorInd Bit (bit 4) of the AL-Status */ + result = MBX_StartMailboxHandler(); + if (result == 0) + { + bApplEsmPending = FALSE; + /* additionally there could be an application specific check (in ecatappl.c) + if the state transition from INIT to PREOP should be done + if result is unequal 0, the slave will stay in INIT + and sets the ErrorInd Bit (bit 4) of the AL-Status */ + result = APPL_StartMailboxHandler(); + if ( result == 0 ) + { + bMbxRunning = TRUE; + } + } + + if(result != 0 && result != NOERROR_INWORK) + { + /*Stop APPL Mbx handler if APPL Start Mbx handler was called before*/ + if(!bApplEsmPending) + APPL_StopMailboxHandler(); + + MBX_StopMailboxHandler(); + } + + } + break; + + case PREOP_2_SAFEOP: + /* start the input handler (function is defined above) */ + result = StartInputHandler(); + if ( result == 0 ) + { + bApplEsmPending = FALSE; + result = APPL_StartInputHandler(&u16ALEventMask); + + if(result == 0) + { + /* initialize the AL Event Mask register (0x204) */ +/*ECATCHANGE_START(V5.11) HW1*/ + SetALEventMask( u16ALEventMask ); +/*ECATCHANGE_END(V5.11) HW1*/ + + bEcatInputUpdateRunning = TRUE; + } + } + + /*if one start input handler returned an error stop the input handler*/ + if(result != 0 && result != NOERROR_INWORK) + { + if(!bApplEsmPending) + { + /*Call only the APPL stop handler if the APPL start handler was called before*/ + /*The application can react to the state transition in the function APPL_StopInputHandler */ + APPL_StopInputHandler(); + } + + StopInputHandler(); + } + break; + + case SAFEOP_2_OP: + /* start the output handler (function is defined above) */ + result = StartOutputHandler(); + if(result == 0) + { + bApplEsmPending = FALSE; + result = APPL_StartOutputHandler(); + + if(result == 0) + { + /*Device is in OPERATINAL*/ + bEcatOutputUpdateRunning = TRUE; + } + + } + + if ( result != 0 && result != NOERROR_INWORK) + { + if(!bApplEsmPending) + APPL_StopOutputHandler(); + + StopOutputHandler(); + } + + break; + + case OP_2_SAFEOP: + /* stop the output handler (function is defined above) */ + APPL_StopOutputHandler(); + + StopOutputHandler(); + + bApplEsmPending = FALSE; + + break; + + case OP_2_PREOP: + /* stop the output handler (function is defined above) */ + result = APPL_StopOutputHandler(); + + StopOutputHandler(); + + bApplEsmPending = FALSE; + + if (result != 0) + break; + + stateTrans = SAFEOP_2_PREOP; + + case SAFEOP_2_PREOP: + /* stop the input handler (function is defined above) */ + APPL_StopInputHandler(); + + StopInputHandler(); + + bApplEsmPending = FALSE; + + break; + + case OP_2_INIT: + /* stop the output handler (function is defined above) */ + result = APPL_StopOutputHandler(); + + StopOutputHandler(); + + bApplEsmPending = FALSE; + + if (result != 0) + break; + + stateTrans = SAFEOP_2_INIT; + + case SAFEOP_2_INIT: + /* stop the input handler (function is defined above) */ + result = APPL_StopInputHandler(); + + StopInputHandler(); + + bApplEsmPending = FALSE; + + if (result != 0) + break; + stateTrans = PREOP_2_INIT; + + case PREOP_2_INIT: + MBX_StopMailboxHandler(); + result = APPL_StopMailboxHandler(); + + BackToInitTransition(); + break; + + case INIT_2_INIT: + BackToInitTransition(); + case PREOP_2_PREOP: + case SAFEOP_2_SAFEOP: + case OP_2_OP: + if(bErrAck) + APPL_AckErrorInd(stateTrans); + + if(!bLocalErrorFlag) + { + /*no local error flag is currently active, enable SM*/ + if ( nAlStatus & (STATE_SAFEOP | STATE_OP)) + { + if(nPdOutputSize > 0) + { +/*ECATCHANGE_START(V5.11) HW1*/ + EnableSyncManChannel(PROCESS_DATA_OUT); +/*ECATCHANGE_END(V5.11) HW1*/ + } + else + if(nPdInputSize > 0) + { +/*ECATCHANGE_START(V5.11) HW1*/ + EnableSyncManChannel(PROCESS_DATA_IN); +/*ECATCHANGE_END(V5.11) HW1*/ + } + } + } + result = NOERROR_NOSTATECHANGE; + break; + + case INIT_2_SAFEOP: + case INIT_2_OP: + case PREOP_2_OP: + case PREOP_2_BOOT: + case SAFEOP_2_BOOT: + case OP_2_BOOT: + case BOOT_2_PREOP: + case BOOT_2_SAFEOP: + case BOOT_2_OP: + result = ALSTATUSCODE_INVALIDALCONTROL; + break; + + default: + result = ALSTATUSCODE_UNKNOWNALCONTROL; + break; + } + } + else + { + /* the checking of the sync manager settings was not successful + switch back the state to PREOP or INIT */ + switch (nAlStatus) + { + case STATE_OP: + /* stop the output handler (function is defined above) */ + APPL_StopOutputHandler(); + StopOutputHandler(); + case STATE_SAFEOP: + /* stop the input handler (function is defined above) */ + APPL_StopInputHandler(); + + StopInputHandler(); + + case STATE_PREOP: + + if ( result == ALSTATUSCODE_INVALIDMBXCFGINPREOP ) + { + /* the mailbox sync manager settings were wrong, switch back to INIT */ + MBX_StopMailboxHandler(); + APPL_StopMailboxHandler(); + + nAlStatus = STATE_INIT; + } + else + nAlStatus = STATE_PREOP; + } + } + + if ( result == NOERROR_INWORK ) + { + /* state transition is still in work + ECAT_StateChange must be called from the application */ + bEcatWaitForAlControlRes = TRUE; + /* state transition has to be stored */ + nEcatStateTrans = stateTrans; + + /*Init ESM timeout counter (will be decremented with the local 1ms timer)*/ + switch(nEcatStateTrans) + { + case INIT_2_PREOP: + case INIT_2_BOOT: + EsmTimeoutCounter = PREOPTIMEOUT; + break; + case PREOP_2_SAFEOP: + case SAFEOP_2_OP: + EsmTimeoutCounter = SAFEOP2OPTIMEOUT; + break; + default: + EsmTimeoutCounter = 200; //Set default timeout value to 200ms + break; + } + EsmTimeoutCounter -= 50; //subtract 50ms from the timeout to react before the master runs into a timeout. + + } + else if ( alControl != (nAlStatus & STATE_MASK) ) + { + /* The slave state has changed */ + + if ( (result != 0 || alStatusCode != 0) && ((alControl | nAlStatus) & STATE_OP) ) + { + /* the local application requested to leave the state OP so we have to disable the SM2 + and make the state change from OP to SAFEOP by calling StopOutputHandler */ + + //only execute StopOutputHandler() if Output update is still running + if(bEcatOutputUpdateRunning) + { + APPL_StopOutputHandler(); + + StopOutputHandler(); + } + + if(nPdOutputSize > 0) + { + /* disable the Sync Manager Channel 2 (outputs) */ +/*ECATCHANGE_START(V5.11) HW1*/ + DisableSyncManChannel(PROCESS_DATA_OUT); +/*ECATCHANGE_END(V5.11) HW1*/ + } + else + if(nPdInputSize > 0) + { + /*disable Sync Manager 3 (inputs) if no outputs available*/ +/*ECATCHANGE_START(V5.11) HW1*/ + DisableSyncManChannel(PROCESS_DATA_IN); +/*ECATCHANGE_END(V5.11) HW1*/ + } + + } + if ( result != 0 ) + { + if ( nAlStatus == STATE_OP ) + nAlStatus = STATE_SAFEOP; + /* save the failed status to be able to decide, if the AL Status Code shall be + reset in case of a coming successful state transition */ + nAlStatus |= STATE_CHANGE; + } + else + { + /* state transition was successful */ + if ( alStatusCode != 0 ) + { + /* state change request from the user */ + result = alStatusCode; + alControl |= STATE_CHANGE; + } + /* acknowledge the new state */ + nAlStatus = alControl; + } + + bEcatWaitForAlControlRes = FALSE; + + /* write the AL Status register */ + SetALStatus(nAlStatus, result); + } + else + { + /* Error acknowledgement without a state transition */ + + bEcatWaitForAlControlRes = FALSE; + + /* AL-Status has to be updated and AL-Status-Code has to be reset + if the the error bit was acknowledged */ + SetALStatus(nAlStatus, 0); + } + +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + + \brief This function is called cyclic if a state transition is pending (bEcatWaitForAlControlRes == TRUE) + \brief If the ESM timeout is expired the state transition will be rejected. Otherwise the application specific state transition function is called. + \brief If the pending state transition is triggered by the application the transition need to be completed by the application (ECAT_StateChange()) + *//////////////////////////////////////////////////////////////////////////////////////// +void AL_ControlRes(void) +{ + if(bEcatWaitForAlControlRes) + { + UINT16 result = 0; + UINT8 Status = 0; + UINT16 StatusCode = 0; + + if(EsmTimeoutCounter == 0) + { + Status = (UINT8)(nEcatStateTrans >> 4); + + /* ESM timeout expired*/ + switch(nEcatStateTrans) + { + case INIT_2_PREOP: + case INIT_2_BOOT: + + if(!bApplEsmPending) + APPL_StopMailboxHandler(); + + MBX_StopMailboxHandler(); + if(bLocalErrorFlag) + { + /*Set application specified error*/ + StatusCode = u16LocalErrorCode; + } + else + { + /*Set unspecified error*/ + StatusCode = ALSTATUSCODE_UNSPECIFIEDERROR; + } + break; + case PREOP_2_SAFEOP: + if(!bApplEsmPending) + APPL_StopInputHandler(); + + StopInputHandler(); + + if(bLocalErrorFlag) + { + /*Set application specified error*/ + StatusCode = u16LocalErrorCode; + } + else + { + /*Set unspecified error*/ + StatusCode = ALSTATUSCODE_UNSPECIFIEDERROR; + } + break; + case SAFEOP_2_OP: + if(bDcSyncActive) + { + /*SafeOP to OP timeout expired check which AL status code need to be written*/ + if(!bDcRunning) // + { + /*no Sync0 signal received*/ + StatusCode = ALSTATUSCODE_NOSYNCERROR; + } +/*ECATCHANGE_START(V5.11) ECAT4*/ + else if(!bEcatFirstOutputsReceived && (nPdOutputSize > 0)) +/*ECATCHANGE_END(V5.11) ECAT4*/ + { + /*no process data received*/ + StatusCode = ALSTATUSCODE_SMWATCHDOG; + } + else + { + /*SM/Sync Sequence is not valid*/ + StatusCode = ALSTATUSCODE_SYNCERROR; + } + } + else + { +/*ECATCHANGE_START(V5.11) ECAT4*/ + if (nPdOutputSize > 0) + { + StatusCode = ALSTATUSCODE_SMWATCHDOG; + } + else +/*ECATCHANGE_END(V5.11) ECAT4*/ + { + /*Set valid state transition even if timeout expired*/ + Status = STATE_OP; + StatusCode = 0; + /* Slave is OPERATIONAL */ + bEcatOutputUpdateRunning = TRUE; + } + } + + /*Stop handler on failed transition*/ + if(StatusCode != 0) + { + if(!bApplEsmPending) + APPL_StopOutputHandler(); + + StopOutputHandler(); + } + break; + } + } //ESM timeout + else + { + /*Call application specific transition function and complete transition it the function returns 0*/ + switch(nEcatStateTrans) + { + case INIT_2_PREOP: + case INIT_2_BOOT: + if(bApplEsmPending) + { + bApplEsmPending = FALSE; + /*APPL_StartMailboxHandler() need to be called*/ + result = APPL_StartMailboxHandler(); + + if(result == 0) + { + /*The application specific transition was successful => set active mailbox handler indication*/ + bMbxRunning = TRUE; + Status = (UINT8)(nEcatStateTrans & STATE_MASK); + } + else + { + /*The application specific transition failed. + (In pending case the application need to complete the transition)*/ + + if(result != NOERROR_INWORK) + { + APPL_StopMailboxHandler(); + MBX_StopMailboxHandler(); + } + } + } + break; + case PREOP_2_SAFEOP: + if(bApplEsmPending) + { + bApplEsmPending = FALSE; + result = APPL_StartInputHandler(&u16ALEventMask); + + if(result == 0) + { + bEcatInputUpdateRunning = TRUE; + Status = STATE_SAFEOP; + } + else + { + /*The application specific transition failed. + (In pending case the application need to complete the transition)*/ + + if(result != NOERROR_INWORK) + { + APPL_StopInputHandler(); + StopInputHandler(); + } + } + } + break; + case SAFEOP_2_OP: + if(bApplEsmPending) + { + if(bDcSyncActive) + { + if(i16WaitForPllRunningTimeout > 0 && i16WaitForPllRunningTimeout <= i16WaitForPllRunningCnt) + { + /*Pll sequence valid for 200ms (set in APPL_StartOutputHandler() ) + acknowledge state transition to OP */ + + i16WaitForPllRunningTimeout = 0; + i16WaitForPllRunningCnt = 0; + + result = APPL_StartOutputHandler(); + + if(result == 0) + { + /* Slave is OPERATIONAL */ + bEcatOutputUpdateRunning = TRUE; + Status = STATE_OP; + } + else + { + if(result != NOERROR_INWORK) + { + APPL_StopOutputHandler(); + StopOutputHandler(); + } + } + } + } + else + { + if(nPdOutputSize == 0 || bEcatFirstOutputsReceived) + { + result = APPL_StartOutputHandler(); + + if(result == 0) + { + /* Slave is OPERATIONAL */ + bEcatOutputUpdateRunning = TRUE; + Status = STATE_OP; + } + else + { + if(result != NOERROR_INWORK) + { + APPL_StopOutputHandler(); + StopOutputHandler(); + } + } + } + } + } + break; + }//Switch - transition + } + + if(Status != 0) + { + /*Pending state transition finished => write AL Status and AL Status Code*/ + bEcatWaitForAlControlRes = FALSE; + + if (StatusCode != 0) + { + Status |= STATE_CHANGE; + } + + SetALStatus(Status,StatusCode); + } + }// Pending state transition (bEcatWaitForAlControlRes == true) +} + + +///////////////////////////////////////////////////////////////////////////////////////// +/** + + \brief This function checks the current Sync state and set the local flags + The analyse of the local flags is handled in "CheckIfEcatError" + +*//////////////////////////////////////////////////////////////////////////////////////// +void DC_CheckWatchdog(void) +{ + DISABLE_ESC_INT(); + if(bDcSyncActive) + { +/*ECATCHANGE_START(V5.11) ESM4*/ + /*If Sync0 watchdog is enabled and expired*/ + if((Sync0WdValue > 0) && (Sync0WdCounter >= Sync0WdValue)) + { + /*Sync0 watchdog expired*/ + bDcRunning = FALSE; + } + else + { + if(Sync0WdCounter < Sync0WdValue) + { + Sync0WdCounter ++; + } + + bDcRunning = TRUE; + + } + + + if(bDcRunning) + { + /*Check the Sync1 cycle if Sync1 Wd is enabled*/ + if(Sync1WdValue > 0) + { + if(Sync1WdCounter < Sync1WdValue) + { + Sync1WdCounter ++; + } + else + { + /*Sync1 watchdog expired*/ + bDcRunning = FALSE; + } + } + } +/*ECATCHANGE_END(V5.11) ESM4*/ + + if(bDcRunning) + { +/*ECATCHANGE_START(V5.11) COE3*/ + if(sSyncManOutPar.u16SmEventMissedCounter < sErrorSettings.u16SyncErrorCounterLimit) +/*ECATCHANGE_END(V5.11) COE3*/ + { + bSmSyncSequenceValid = TRUE; + + /*Wait for PLL is active increment the Pll valid counter*/ + if(i16WaitForPllRunningTimeout > 0) + { + i16WaitForPllRunningCnt++; + } + } + else if(bSmSyncSequenceValid) + { + bSmSyncSequenceValid = FALSE; + sSyncManOutPar.u8SyncError = 1; + + /*Wait for PLL is active reset the Pll valid counter*/ + if(i16WaitForPllRunningTimeout > 0) + { + i16WaitForPllRunningCnt = 0; + } + } + } + else if(bSmSyncSequenceValid) + { + bSmSyncSequenceValid = FALSE; + } + + } + ENABLE_ESC_INT(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + + \brief Checks communication and synchronisation variables and update AL status / AL status code if an error has occurred + +*//////////////////////////////////////////////////////////////////////////////////////// +void CheckIfEcatError(void) +{ + /*if the watchdog is enabled check the the process data watchdog in the ESC + and set the AL status code 0x1B if the watchdog expired*/ + if (EcatWdValue != 0) + { + /*watchdog time is set => watchdog is active*/ + UINT16 WdStatusOK = 0; + + HW_EscReadWord(WdStatusOK, ESC_PD_WD_STATE); + WdStatusOK = SWAPWORD(WdStatusOK); + + /*ECATCHANGE_START(V5.11) ECAT4*/ + if (!(WdStatusOK & ESC_PD_WD_TRIGGER_MASK) && (nPdOutputSize > 0)) + { + /*The device is in OP state*/ + + if (bEcatOutputUpdateRunning) + { + AL_ControlInd(STATE_SAFEOP, ALSTATUSCODE_SMWATCHDOG); + return; + } + + else + { + bEcatFirstOutputsReceived = FALSE; + } + } + /*ECATCHANGE_END(V5.11) ECAT4*/ + } + + if(bDcSyncActive) + { + if(bEcatOutputUpdateRunning) + { + /*Slave is in OP state*/ + if(!bDcRunning) + { + AL_ControlInd(STATE_SAFEOP, ALSTATUSCODE_FATALSYNCERROR); + return; + } + else if(!bSmSyncSequenceValid) + { + AL_ControlInd(STATE_SAFEOP, ALSTATUSCODE_SYNCERROR); + return; + } + + } + } +} +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param alStatus: requested state + \param alStatusCode: value for the AL-Status register + + \brief This function changes the state of the EtherCAT slave if the requested state + is lower than the actual state, otherwise the error condition will be reset. +*//////////////////////////////////////////////////////////////////////////////////////// + +void ECAT_StateChange(UINT8 alStatus, UINT16 alStatusCode) +{ + UINT8 Status = alStatus; + + if(bEcatWaitForAlControlRes) + { + /*State transition is pending*/ + + if(bApplEsmPending) + { + /*The generic stack has currently control of the state transition. + In case on an local error force ESM timeout*/ + if(alStatusCode != 0) + { + bLocalErrorFlag = TRUE; + u16LocalErrorCode = alStatusCode; + EsmTimeoutCounter = 0; + } + } + else + { + /*complete the state transition*/ + + if(alStatusCode != 0) + { + bLocalErrorFlag = TRUE; + u16LocalErrorCode = alStatusCode; + + /*State transition failed due to local application reasons*/ + switch(nEcatStateTrans) + { + case INIT_2_PREOP: + case INIT_2_BOOT: + APPL_StopMailboxHandler(); + MBX_StopMailboxHandler(); + break; + case PREOP_2_SAFEOP: + APPL_StopInputHandler(); + StopInputHandler(); + break; + case SAFEOP_2_OP: + APPL_StopOutputHandler(); + StopOutputHandler(); + break; + } + + /*In case of a failed state transition the */ + Status = (UINT8)(nEcatStateTrans >> 4); + } + else + { + /*State transition succeed*/ + + switch(nEcatStateTrans) + { + case INIT_2_PREOP: + case INIT_2_BOOT: + bMbxRunning = TRUE; + break; + case PREOP_2_SAFEOP: + bEcatInputUpdateRunning = TRUE; + break; + case SAFEOP_2_OP: + bEcatOutputUpdateRunning = TRUE; + break; + } + + /*In case of a failed state transition the */ + Status = (UINT8)(nEcatStateTrans & STATE_MASK); + } + /*Pending state transition finished => write AL Status and AL Status Code*/ + bEcatWaitForAlControlRes = FALSE; + + if(alStatusCode != 0) + Status |= STATE_CHANGE; + + SetALStatus(Status,alStatusCode); + + }// state transition need to be completed by the local application + }//State transition pending + else + { + if ( alStatusCode != 0 ) + { + /* Local error has happened, we change the state if necessary */ + bLocalErrorFlag = TRUE; + u16LocalErrorCode = alStatusCode; + + if ( (alStatus & STATE_MASK) < (nAlStatus & STATE_MASK) ) + { + AL_ControlInd(alStatus, alStatusCode); + } + } + else if (bLocalErrorFlag) + { + /*a local error is gone */ + if ( (nAlStatus & STATE_MASK) == (STATE_SAFEOP | STATE_OP) ) + { + if(nPdOutputSize > 0) + { + /* we have to enable the output process data SyncManger (default: SM2), + because it was disabled when switching back to SAFE-OP */ +/*ECATCHANGE_START(V5.11) HW1*/ + EnableSyncManChannel(PROCESS_DATA_OUT); +/*ECATCHANGE_END(V5.11) HW1*/ + } + else + if (nPdInputSize > 0) + { + /* we have to enable the input process data SyncManger (default: SM3), + because it was disabled when switching back to SAFE-OP */ +/*ECATCHANGE_START(V5.11) HW1*/ + EnableSyncManChannel(PROCESS_DATA_IN); +/*ECATCHANGE_END(V5.11) HW1*/ + } + } + bLocalErrorFlag = FALSE; + u16LocalErrorCode = 0x00; + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + + \brief This function initialize the EtherCAT Slave Interface. +*//////////////////////////////////////////////////////////////////////////////////////// + +void ECAT_Init(void) +{ + UINT8 i; + + /*Get Maximum Number of SyncManagers and supported DPRAM size*/ + { + UINT16 TmpVar = 0; + + HW_EscReadWord(TmpVar, ESC_COMM_INFO_OFFSET); + + TmpVar = SWAPWORD(TmpVar); + nMaxSyncMan = (UINT8) ((TmpVar & ESC_SM_CHANNELS_MASK)>> ESC_SM_CHANNELS_SHIFT); + + HW_EscReadWord(TmpVar, ESC_DPRAM_SIZE_OFFSET); + TmpVar = SWAPWORD(TmpVar); + + //get max address (register + DPRAM size in Byte (in the register it is stored in KB)) + /* ECATCHANGE_START(V5.11) ESC1*/ + nMaxEscAddress = (UINT16) ((TmpVar & ESC_DPRAM_SIZE_MASK) << 10) + 0xFFF; + /* ECATCHANGE_END(V5.11) ESC1*/ + } + + /* Get EEPROM loaded information */ + UpdateEEPROMLoadedState(); + + /* disable all Sync Manager channels */ + for (i = 0; i < nMaxSyncMan; i++) + { +/*ECATCHANGE_START(V5.11) HW1*/ + DisableSyncManChannel(i); +/*ECATCHANGE_END(V5.11) HW1*/ + } + + /* initialize the mailbox handler */ + MBX_Init(); + + /* initialize variables */ + bApplEsmPending = FALSE; + bEcatWaitForAlControlRes = FALSE; + bEcatFirstOutputsReceived = FALSE; + bEcatOutputUpdateRunning = FALSE; + bEcatInputUpdateRunning = FALSE; + bWdTrigger = FALSE; + EcatWdValue = 0; + Sync0WdCounter = 0; + Sync0WdValue = 0; + Sync1WdCounter = 0; + Sync1WdValue = 0; + bDcSyncActive = FALSE; + bLocalErrorFlag = FALSE; + u16LocalErrorCode = 0x00; + + u16ALEventMask = 0; + nPdOutputSize = 0; + nPdInputSize = 0; + + /* initialize the AL Status register */ + nAlStatus = STATE_INIT; + SetALStatus(nAlStatus, 0); + nEcatStateTrans = 0; + +/* ECATCHANGE_START(V5.11) ECAT5*/ + bEscIntEnabled = FALSE; +/* ECATCHANGE_END(V5.11) ECAT5*/ + + /* initialize the COE part */ + COE_Init(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \brief This function has to be called cyclically. +*//////////////////////////////////////////////////////////////////////////////////////// + +void ECAT_Main(void) +{ + UINT16 ALEventReg; + UINT16 EscAlControl = 0x0000; + UINT16 sm1Activate = SM_SETTING_ENABLE_VALUE; + + /* check if services are stored in the mailbox */ + MBX_Main(); + + + if ( bMbxRunning ) + { + /* Slave is at least in PREOP, Mailbox is running */ + /* get the Activate-Byte of SM 1 (Register 0x80E) to check if a mailbox repeat request was received */ + HW_EscReadWord(sm1Activate,(ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER)); + sm1Activate = SWAPWORD(sm1Activate); + } + + /* Read AL Event-Register from ESC */ + ALEventReg = HW_GetALEventRegister(); + ALEventReg = SWAPWORD(ALEventReg); + + + if ((ALEventReg & AL_CONTROL_EVENT) && !bEcatWaitForAlControlRes) + { + /* AL Control event is set, get the AL Control register sent by the Master to acknowledge the event + (that the corresponding bit in the AL Event register will be reset) */ + HW_EscReadWord( EscAlControl, ESC_AL_CONTROL_OFFSET); + EscAlControl = SWAPWORD(EscAlControl); + + + /* reset AL Control event and the SM Change event (because the Sync Manager settings will be checked + in AL_ControlInd, too)*/ + ALEventReg &= ~((AL_CONTROL_EVENT) | (SM_CHANGE_EVENT)); + + AL_ControlInd((UINT8)EscAlControl, 0); /* in AL_ControlInd the state transition will be checked and done */ + + /* SM-Change-Event was handled too */ + } + + if ( (ALEventReg & SM_CHANGE_EVENT) && !bEcatWaitForAlControlRes && (nAlStatus & STATE_CHANGE) == 0 && (nAlStatus & ~STATE_CHANGE) != STATE_INIT ) + { + /* the SM Change event is set (Bit 4 of Register 0x220), when the Byte 6 (Enable, Lo-Byte of Register 0x806, 0x80E, 0x816,...) + of a Sync Manager channel was written */ + ALEventReg &= ~(SM_CHANGE_EVENT); + + /* AL_ControlInd is called with the actual state, so that the correct SM settings will be checked */ + AL_ControlInd(nAlStatus & STATE_MASK, 0); + } + + if(bEcatWaitForAlControlRes) + { + AL_ControlRes(); + } + + /*The order of mailbox event processing was changed to prevent race condition errors. + The SM1 activate Byte (Register 0x80E) was read before reading AL Event register. + 1. Handle Mailbox Read event + 2. Handle repeat toggle request + 3. Handle Mailbox write event + */ + if ( bMbxRunning ) + { + /*SnycManger change event (0x220:4) could be acknowledged by reading the SM1 control register without notification to the local application + => check if the SyncManger 1 is still enabled*/ + if(!(sm1Activate & SM_SETTING_ENABLE_VALUE)) + AL_ControlInd(nAlStatus & STATE_MASK, 0); + + if ( ALEventReg & (MAILBOX_READ_EVENT) ) + { + /* SM 1 (Mailbox Read) event is set, when the mailbox was read from the master, + to acknowledge the event the first byte of the mailbox has to be written, + by writing the first byte the mailbox is locked, too */ + u16dummy = 0; + HW_EscWriteWord(u16dummy,u16EscAddrSendMbx); + + /* the Mailbox Read event in the variable ALEventReg shall be reset before calling + MBX_MailboxReadInd, where a new mailbox datagram (if available) could be stored in the send mailbox */ + ALEventReg &= ~(MAILBOX_READ_EVENT); + MBX_MailboxReadInd(); + } + + DISABLE_MBX_INT; + /* bMbxRepeatToggle holds the last state of the Repeat Bit (Bit 1) */ + + if ( ( (sm1Activate & SM_SETTING_REPAET_REQ_MASK) && !bMbxRepeatToggle ) + ||( !(sm1Activate & SM_SETTING_REPAET_REQ_MASK) && bMbxRepeatToggle )) + { + /* Repeat Bit (Bit 1) has toggled, there is a repeat request, in MBX_MailboxRepeatReq the correct + response will put in the send mailbox again */ + MBX_MailboxRepeatReq(); + /* acknowledge the repeat request after the send mailbox was updated by writing the Repeat Bit + in the Repeat Ack Bit (Bit 1) of the PDI Ctrl-Byte of SM 1 (Register 0x80F) */ + if(bMbxRepeatToggle) + sm1Activate |= SM_SETTING_REPEAT_ACK; //set repeat acknowledge bit (bit 9) + else + sm1Activate &= ~SM_SETTING_REPEAT_ACK; //clear repeat acknowledge bit (bit 9) + + sm1Activate = SWAPWORD(sm1Activate); + HW_EscWriteWord(sm1Activate,(ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER)); + } + ENABLE_MBX_INT; + + /* Reload the AlEvent because it may be changed due to a SM disable, enable in case of an repeat request */ + ALEventReg = HW_GetALEventRegister(); + ALEventReg = SWAPWORD(ALEventReg); + + if ( ALEventReg & (MAILBOX_WRITE_EVENT) ) + { + /* SM 0 (Mailbox Write) event is set, when the mailbox was written from the master, + to acknowledge the event the first byte of the mailbox has to be read, + which will be done in MBX_CheckAndCopyMailbox */ + /* the Mailbox Write event in the variable ALEventReg shall be reset before calling + MBX_CheckAndCopyMailbox, where the received mailbox datagram will be processed */ + ALEventReg &= ~(MAILBOX_WRITE_EVENT); + MBX_CheckAndCopyMailbox(); + + } + } +} + + +/** @} */ + diff --git a/GD32E508_DCU_APP/comm/ssc/src/mailbox.c b/GD32E508_DCU_APP/comm/ssc/src/mailbox.c new file mode 100644 index 0000000..d2aba90 --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/src/mailbox.c @@ -0,0 +1,929 @@ +/** +\addtogroup Mailbox Mailbox Functions +@{ +*/ + +/** +\file mailbox.c +\author EthercatSSC@beckhoff.com +\brief Implementation + +
Description of the mailbox buffer handling (MAILBOX_QUEUE = 0):
+There are two mailbox buffer for sending and receiving mailbox services.
+Normal operation:
+When starting the mailbox handler psWriteMbx contains mailbox buffer 1,
+psReadMbx, psRepeatMbx and psStoreMbx are 0.
+In this state a repeat request would be ignored because there was no service sent yet.
+When the first mailbox service is sent (in MBX_CopyToSendMailbox), psWriteMbx gets mailbox buffer 2
+and psReadMbx gets the sent mailbox buffer 1, psRepeatMbx and psStoreMbx are still 0.
+When the first mailbox service was read from the master, the sent mailbox buffer 1 is stored
+in psRepeatMbx (in MBX_MailboxReadInd).
+After that psReadMbx gets always the actual sent mailbox buffer, psWriteMbx is set to 0 (another
+received mailbox service from the master will not be handled until the sent mailbox service was read
+and MBX_MailboxReadInd was called).
+When the mailbox service is read, psWriteMbx gets the Buffer of psRepeatMbx and psRepeatMbx gets the
+buffer of psReadMbx.
+Repeat Request from the master:
+When a Repeat from the master is requested (MBX_MailboxRepeatReq), there are three different possibilities:
+1. no mailbox service was sent since the mailbox handler was started (psRepeatMbx = 0): nothing to do
+2. the acknowledge of the last sent mailbox service was received (in MBX_MailboxReadInd) (bSendMbxIsFull = 0):
+the last sent mailbox service (psRepeatMbx) will be sent again (in MBX_CopyToSendMailbox) and stored in psReadMbx,
+psRepeatMbx will be set to 0
+3. the acknowledge of the last sent mailbox service was not received (psReadMbx and psRepeatMbx contain different buffers,
+psReadMbx is still in the mailbox (because MBX_MailboxReadInd is not called yet, bSendMbxIsFull = 1):
+psReadMbx will be deleted in the mailbox (call of DisableSyncManChannel and EnableSyncManChannel) and
+stored in psStoreMbx, psRepeatMbx will be sent again (in MBX_CopyToSendMailbox) and stored in psReadMbx,
+psRepeatMbx will be set to 0.
+When the repeated mailbox service was sent (call of MBX_MailboxReadInd), psReadMbx will be stored in psRepeatMbx
+and psStoreMbx will be sent (in MBX_CopyToSendMailbox) and stored in psReadMbx, psStoreMbx will be set to 0.
+ +
Description of the mailbox buffer handling (MAILBOX_QUEUE = 1):
+There are two mailbox buffer for sending and receiving mailbox services.
+Normal operation (psWriteMbx is only used for local storage):
+When starting the mailbox handler psReadMbx, psRepeatMbx and psStoreMbx are 0.
+In this state a repeat request would be ignored because there was no service sent yet.
+When a mailbox service is received from the master (in MBX_CheckAndCopyMailbox) a mailbox buffer
+will be get with APPL_AllocMailboxBuffer and the corresponding protocol service function will
+be called (in MBX_WriteMailboxInd). This buffer shall be used for the protocol service response.
+When the first mailbox service is sent (in MBX_CopyToSendMailbox), psReadMbx gets the sent mailbox buffer,
+psRepeatMbx and psStoreMbx are still 0.
+When the first mailbox service was read from the master, the sent mailbox buffer (psReadMbx) is stored
+in psRepeatMbx (in MBX_MailboxReadInd).
+After that psReadMbx gets always the actual sent mailbox buffer (in MBX_CopyToSendMailbox)
+When the mailbox service is read, psRepeatMbx is returned (with APPL_FreeMailboxBuffer) and psRepeatMbx gets the
+buffer of psReadMbx.
+Repeat Request from the master:
+When a Repeat from the master is requested (MBX_MailboxRepeatReq), there are three different possibilities:
+1. no mailbox service was sent since the mailbox handler was started (psRepeatMbx = 0): nothing to do
+2. the acknowledge of the last sent mailbox service was received (in MBX_MailboxReadInd) (bSendMbxIsFull = 0):
+the last sent mailbox service (psRepeatMbx) will be sent again (in MBX_CopyToSendMailbox) and stored in psReadMbx,
+psRepeatMbx will be set to 0
+3. the acknowledge of the last sent mailbox service was not received (psReadMbx and psRepeatMbx contain different buffers,
+psReadMbx is still in the mailbox (because MBX_MailboxReadInd is not called yet, bSendMbxIsFull = 1):
+psReadMbx will be deleted in the mailbox (call of DisableSyncManChannel and EnableSyncManChannel) and
+stored in psStoreMbx, psRepeatMbx will be sent again (in MBX_CopyToSendMailbox) and stored in psReadMbx,
+psRepeatMbx will be set to 0.
+When the repeated mailbox service was sent (call of MBX_MailboxReadInd), psReadMbx will be stored in psRepeatMbx
+and psStoreMbx will be sent (in MBX_CopyToSendMailbox) and stored in psReadMbx, psStoreMbx will be set to 0. + +\version 5.11 + +
Changes to version V5.10:
+V5.11 ECAT10: change PROTO handling to prevent compiler errors
+V5.11 ECAT7: add missing big endian swapping
+V5.11 HW1: "move hardware independent functions ""HW_DisableSyncManChannel()"", ""HW_EnableSyncManChannel()"", ""HW_GetSyncMan()"", ""HW_ResetALEventMask()"", ""HW_SetALEventMask()"" to ecatalv.c"
+V5.11 MBX2: "check in IP if enough dynamic memory is available to handle mailbox communication, if it is not the case the mbx error ""no memory"" is returned on any mbx request"
+V5.11 MBX3: set application triggered emergency and EoE data to pending if no mailbox queue is supported and another mailbox request is currently handled, Handle only one mailbox request at a time (in case that MAILBPX_QUEUE is disabled)
+V5.11 TEST4: add new mailbox test behaviour (the master mailbox cnt shall be incremented by 1 and the slave mailbox cnt is alternating)
+
Changes to version V5.01:
+V5.10 EOE3: Change local send frame pending indication variable to a global variable (it need to be resetted if the mailbox is stopped and a frame is pending)
+V5.10 ESC6: Update SyncManager register size (only for 8Bit ESC access)
+V5.10 MBX2: Allocate only memory for current configured mailbox size (max supported size was allocated before)
+
Changes to version V5.0:
+V5.01 ESC2: Add missed value swapping
+V5.01 MBX2: Return an error if length specified in mailbnox header is too large.
+
Changes to version V4.40:
+V5.0 ECAT3: Global dummy variables used for dummy ESC operations.
+V5.0 ESC1: ESC 32Bit Access added.
+V5.0 MBX2: Prevent double swapping (MBX repeat service) of MBX length field.
+V5.0 MBX4: Unlock SM buffer based on SM state.
+
Changes to version V4.11:
+V4.40 MBX8: Handle pending mailbox data even if mailbox queue is enabled
+V4.40 MBX7: Reset SyncManger 1 Repeat Acknowledge bit if local variable is reset
+V4.40 EMCY2: Send queued emergency messages
+V4.40 BOOT2: Support only FoE in Bootstrap state
+V4.40 MBX5: Prevent multiple free buffer
+V4.40 MBX6: change return value if no mailbox buffer is available
+
Changes to version V4.08:
+V4.11 MBX 1: free some dynamic memory to avoid memory leaks in the mailbox queue
+
Changes to version V4.07:
+V4.10 MBX 1: check Mailbox message buffer and print emergency messages
+ during state change Init -> PreOP
+
Changes to version V4.07:
+V4.08 MBX 1: The mailbox queue was added (switch MAILBOX_QUEUE), if the
+ mailbox should be handled in the ESC-Interrupt Service Routine
+ the defines ENTER_MBX_CRITICAL and LEAVE_MBX_CRITICAL could be used
+ to disable the interrupts or change the priority
+V4.08 AOE 1: AoE was added
+
Changes to version V4.06:
+V4.07 ECAT 1: The sources for SPI and MCI were merged (in ecat_def.h
+ set the switch MCI_HW to 1 when using the MCI,
+ set the switch SPI_HW to 1 when using the SPI +*/ + + +/*--------------------------------------------------------------------------------------- +------ +------ Includes +------ +---------------------------------------------------------------------------------------*/ + +#include "ecat_def.h" + + +#include "ecatslv.h" + + +#define _MAILBOX_ 1 +#include "mailbox.h" +#undef _MAILBOX_ +/* ECATCHANGE_START(V5.11) ECAT10*/ +/*remove definition of _MAILBOX_ (#ifdef is used in mailbox.h)*/ +/* ECATCHANGE_END(V5.11) ECAT10*/ + +#include "ecatcoe.h" + +/*-------------------------------------------------------------------------------------- +------ +------ internal Types and Defines +------ +--------------------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------------------- +------ +------ internal Variables +------ +--------------------------------------------------------------------------------------*/ +/*variables are declared in ecatslv.c*/ + extern VARVOLATILE UINT16 u16dummy; + +/* ECATCHANGE_START(V5.11) MBX2*/ + BOOL bNoMbxMemoryAvailable; /**< \brief Indicates if enough dynamic memory is available to handle at least one mailbox datagram */ +/* ECATCHANGE_END(V5.11) MBX2*/ +/*-------------------------------------------------------------------------------------- +------ +------ internal functions +------ +--------------------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------------------- +------ +------ functions +------ +---------------------------------------------------------------------------------------*/ + + +/////////////////////////////////////////////////////////////////////////////////////////// +// +// PutInMbxQueue +// + +UINT8 PutInMbxQueue(TMBX MBXMEM * pMbx, TMBXQUEUE MBXMEM * pQueue) +{ + UINT16 lastInQueue; + ENTER_MBX_CRITICAL; + + + lastInQueue = pQueue->lastInQueue+1; + if (lastInQueue == pQueue->maxQueueSize) + { + // Umbruch der Queue + lastInQueue = 0; + } + + if (pQueue->firstInQueue == lastInQueue) + { + // Ueberlauf der Queue -> letztes Element wieder herausnehmen + LEAVE_MBX_CRITICAL; + return MBXERR_NOMOREMEMORY; + } + + pQueue->queue[pQueue->lastInQueue] = pMbx; + pQueue->lastInQueue = lastInQueue; + + LEAVE_MBX_CRITICAL; + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +// +// GetOutOfMbxQueue +// + +TMBX MBXMEM * GetOutOfMbxQueue(TMBXQUEUE MBXMEM * pQueue) +{ + TMBX MBXMEM * pMbx; + ENTER_MBX_CRITICAL; + + if (pQueue->firstInQueue != pQueue->lastInQueue) + { + // Queue ist nicht leer + UINT16 firstInQueue = pQueue->firstInQueue; + pMbx = pQueue->queue[firstInQueue]; + firstInQueue++; + pQueue->firstInQueue = firstInQueue; + if (pQueue->firstInQueue == pQueue->maxQueueSize) + { + // Umbruch der Queue + pQueue->firstInQueue = 0; + } + } + else + pMbx = 0; + + + LEAVE_MBX_CRITICAL; + + return pMbx; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \brief This function intialize the Mailbox Interface. +*//////////////////////////////////////////////////////////////////////////////////////// + +void MBX_Init(void) +{ + u16ReceiveMbxSize = MIN_MBX_SIZE; + u16SendMbxSize = MAX_MBX_SIZE; + u16EscAddrReceiveMbx = MIN_MBX_WRITE_ADDRESS; + u16EscAddrSendMbx = MIN_MBX_READ_ADDRESS; + + sMbxReceiveQueue.firstInQueue = 0; + sMbxReceiveQueue.lastInQueue = 0; + sMbxReceiveQueue.maxQueueSize = MAX_MBX_QUEUE_SIZE; + sMbxSendQueue.firstInQueue = 0; + sMbxSendQueue.lastInQueue = 0; + sMbxSendQueue.maxQueueSize = MAX_MBX_QUEUE_SIZE; + psWriteMbx = NULL; + + + psRepeatMbx = NULL; + psReadMbx = NULL; + psStoreMbx = NULL; + + bMbxRepeatToggle = FALSE; + /*Reset Repeat acknowledge bit of SyncManager1 (0x80F bit 2)*/ + { + UINT16 sm1Activate = 0; + HW_EscReadWord(sm1Activate,(ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER)); + sm1Activate &= SWAPWORD(~0x0200); + HW_EscWriteWord(sm1Activate,(ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER)); + } + bMbxRunning = FALSE; + bSendMbxIsFull = FALSE; + bReceiveMbxIsLocked = FALSE; + u8MailboxSendReqStored = 0; + u8MbxWriteCounter = 0; + u8MbxReadCounter = 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + + \brief This function includes the state transition from INIT to + \brief PRE-OPERATIONAL in the EtherCAT Slave corresponding to + \brief local management service Start Mailbox Handler + \brief it is checked if the mailbox areas overlaps each other + \brief and the Sync Manager channels 0 and 1 are enabled. + \brief This function shall only be called if mailbox is supported. +*//////////////////////////////////////////////////////////////////////////////////////// +UINT16 MBX_StartMailboxHandler(void) +{ + UINT16 result = 0; + /* get address of the receive mailbox sync manager (SM0) */ +/*ECATCHANGE_START(V5.11) HW1*/ + TSYNCMAN ESCMEM * pSyncMan = (TSYNCMAN ESCMEM *)GetSyncMan(MAILBOX_WRITE); +/*ECATCHANGE_END(V5.11) HW1*/ + /* store size of the receive mailbox */ + u16ReceiveMbxSize = pSyncMan->Length; + /* store the address of the receive mailbox */ + u16EscAddrReceiveMbx = pSyncMan->PhysicalStartAddress; + + /* get address of the send mailbox sync manager (SM1) */ +/*ECATCHANGE_START(V5.11) HW1*/ + pSyncMan =(TSYNCMAN ESCMEM *) GetSyncMan(MAILBOX_READ); +/*ECATCHANGE_END(V5.11) HW1*/ + + /* store the size of the send mailbox */ + u16SendMbxSize = pSyncMan->Length; + /* store the address of the send mailbox */ + u16EscAddrSendMbx = pSyncMan->PhysicalStartAddress; + + // HBu 02.05.06: it should be checked if there are overlaps in the sync manager areas + if ((u16EscAddrReceiveMbx + u16ReceiveMbxSize) > u16EscAddrSendMbx && (u16EscAddrReceiveMbx < (u16EscAddrSendMbx + u16SendMbxSize))) + { + return ALSTATUSCODE_INVALIDMBXCFGINPREOP; + } + + /* enable the receive mailbox sync manager channel */ +/*ECATCHANGE_START(V5.11) HW1*/ + EnableSyncManChannel(MAILBOX_WRITE); + /* enable the send mailbox sync manager channel */ + EnableSyncManChannel(MAILBOX_READ); +/*ECATCHANGE_END(V5.11) HW1*/ + +/* ECATCHANGE_START(V5.11) MBX2*/ + psWriteMbx = (TMBX MBXMEM *) APPL_AllocMailboxBuffer(u16ReceiveMbxSize); + if(psWriteMbx == NULL) + { + bNoMbxMemoryAvailable = TRUE; + + //check if at least enough memory for an mailbox error is available (other wise stop the state transition) + psWriteMbx = (TMBX MBXMEM *) APPL_AllocMailboxBuffer(10); /* a mailbox error datagram length*/ + if(psWriteMbx == NULL) + { + result = ALSTATUSCODE_NOMEMORY; + } + + APPL_FreeMailboxBuffer(psWriteMbx); + } + else + { + bNoMbxMemoryAvailable = FALSE; + APPL_FreeMailboxBuffer(psWriteMbx); + } +/* ECATCHANGE_END(V5.11) MBX2*/ + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + + \brief This function includes the state transition from + \brief PRE-OPERATIONAL to INIT in the EtherCAT Slave corresponding to + \brief local management service Stop Mailbox Handler + \brief the Sync Manager channels 0 and 1 are disabled +*//////////////////////////////////////////////////////////////////////////////////////// + +void MBX_StopMailboxHandler(void) +{ + TMBX MBXMEM * pMbx; + + /* mailbox handler is stopped */ + bMbxRunning = FALSE; + /* disable the receive mailbox sync manager channel */ +/*ECATCHANGE_START(V5.11) HW1*/ + DisableSyncManChannel(MAILBOX_WRITE); + /* disable the send mailbox sync manager channel */ + DisableSyncManChannel(MAILBOX_READ); +/*ECATCHANGE_END(V5.11) HW1*/ + /* initialize variables again */ + + + if (psRepeatMbx != NULL) + APPL_FreeMailboxBuffer(psRepeatMbx); + + if (psStoreMbx != NULL && psStoreMbx != psRepeatMbx) + APPL_FreeMailboxBuffer(psStoreMbx); + + if (psReadMbx != NULL && psReadMbx != psRepeatMbx && psReadMbx != psStoreMbx) + APPL_FreeMailboxBuffer(psReadMbx); + + + psRepeatMbx = NULL; + psReadMbx = NULL; + psStoreMbx = NULL; + + bMbxRepeatToggle = FALSE; + /*Reset Repeat acknowledge bit of SyncManager1 (0x080F bit 2)*/ + { + UINT16 sm1Activate = 0; + HW_EscReadWord(sm1Activate,(ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER)); + sm1Activate &= SWAPWORD(~0x0200); + HW_EscWriteWord(sm1Activate,(ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER)); + } + bSendMbxIsFull = FALSE; + bReceiveMbxIsLocked = FALSE; + u8MailboxSendReqStored = 0; + u8MbxWriteCounter = 0; + u8MbxReadCounter = 0; + + do + { + pMbx = GetOutOfMbxQueue(&sMbxReceiveQueue); + if (pMbx) + { + APPL_FreeMailboxBuffer(pMbx); + pMbx = NULL; + } + } while (pMbx != NULL); + do + { + pMbx = GetOutOfMbxQueue(&sMbxSendQueue); + if (pMbx) + { + APPL_FreeMailboxBuffer(pMbx); + pMbx = NULL; + } + } while (pMbx != NULL); + +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param pMbx Pointer to the received Mailbox command from Master. + + \brief The function checks the mailbox header for the requested service and calls the + \brief corresponding XXXX_ServiceInd-function +*//////////////////////////////////////////////////////////////////////////////////////// + +UINT8 MailboxServiceInd(TMBX MBXMEM *pMbx) +{ + UINT8 result; + + + switch ( (pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] & MBX_MASK_TYPE) >> MBX_SHIFT_TYPE ) + { + case MBX_TYPE_COE: + /* CoE datagram received */ + result = COE_ServiceInd((TCOEMBX MBXMEM *) pMbx); + break; + + default: + + result = MBXERR_UNSUPPORTEDPROTOCOL; + break; + } + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param pMbx Pointer to the received Mailbox command from Master. + + \brief This function is called when the Master has written the Receive-Mailbox. + \brief It will only be called if the send mailbox is empty, that a response for the + \brief mailbox service could be stored. + \brief The function checks the mailbox header for the requested service and calls the + \brief corresponding XXXX_ServiceInd-function +*//////////////////////////////////////////////////////////////////////////////////////// + +void MBX_MailboxWriteInd(TMBX MBXMEM *pMbx) +{ + UINT8 result = 0; + UINT8 mbxCounter = pMbx->MbxHeader.Flags[MBX_OFFS_COUNTER] >> MBX_SHIFT_COUNTER; + UINT16 MbxLen = SWAPWORD(pMbx->MbxHeader.Length); + + if(MbxLen > MAX_MBX_SIZE) + { + /* Mailbox error response: size specified in mailbox header too large*/ + pMbx->MbxHeader.Length = 4; + pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] &= ~MBX_MASK_TYPE; + pMbx->Data[0] = SWAPWORD(MBXSERVICE_MBXERRORCMD); + pMbx->Data[1] = SWAPWORD(MBXERR_INVALIDSIZE); + MBX_MailboxSendReq(pMbx, 0); + } + else + /* if the mailbox datagram counter (Bit 4-6 of Byte 5 of the mailbox header) is unequal zero, + the master supports the mailbox data link layer, + in that case a repeated mailbox write request will be detected, if the counter is unequal zero + and unchanged */ + if ( mbxCounter == 0 || mbxCounter != u8MbxWriteCounter ) + { + /* new mailbox service received */ + /* mbxCounter = 0: old EtherCAT master */ + /* new MBX service received, store the new mailbox counter */ + u8MbxWriteCounter = mbxCounter; + + { + /* check the protocol type and call the XXXX_ServiceInd-function */ + + result = PutInMbxQueue(pMbx, &sMbxReceiveQueue); + } + + if ( result != 0 ) + { + /* Mailbox error response: type 0 (mailbox service protocol) */ + pMbx->MbxHeader.Length = 4; + pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] &= ~MBX_MASK_TYPE; + pMbx->Data[0] = SWAPWORD(MBXSERVICE_MBXERRORCMD); + pMbx->Data[1] = SWAPWORD(result); + MBX_MailboxSendReq(pMbx, 0); + } + } + // the mailbox buffer has to be freed here + else + { + APPL_FreeMailboxBuffer(pMbx); + pMbx = NULL; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \brief This function is called when the Master has read the Send-Mailbox. +*//////////////////////////////////////////////////////////////////////////////////////// + +void MBX_MailboxReadInd(void) +{ + bSendMbxIsFull = FALSE; + // HBu 02.05.06: the pointer psRepeatMbx is only free if there is no stored + // mailbox service from the last repeat + if ( psRepeatMbx && psStoreMbx == NULL ) + { + /* the last sent service is not stored for repeat any longer */ + APPL_FreeMailboxBuffer(psRepeatMbx); + psRepeatMbx = NULL; + } + + /* the actual sent service has to be stored for repeat */ + psRepeatMbx = psReadMbx; + + if ( psStoreMbx ) + { + /* there was a buffer stored */ + MBX_CopyToSendMailbox(psStoreMbx); + /* no more buffer to be stored any more */ + psStoreMbx = NULL; + } + else + { + TMBX MBXMEM *pMbx = GetOutOfMbxQueue(&sMbxSendQueue); + if (pMbx) + { + MBX_CopyToSendMailbox(pMbx); + } + } + + if ( u8MailboxSendReqStored ) + { + /* there are mailbox services stored to be sent */ + if ( u8MailboxSendReqStored & COE_SERVICE ) + { + UINT8 result = 0; + /* reset the flag indicating that CoE service to be sent was stored */ + u8MailboxSendReqStored &= ~COE_SERVICE; + + /* call CoE function that will send the stored CoE service */ + result = COE_ContinueInd(psWriteMbx); + + if (result != 0) + { + /*Set the pending CoE indication is an error occurred during the continue indication*/ + u8MailboxSendReqStored |= COE_SERVICE; + } + } + else + { + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \brief This function is called if the Master has requested a resending of the last + \brief sent mailbox +*//////////////////////////////////////////////////////////////////////////////////////// + +void MBX_MailboxRepeatReq(void) +{ + if (psRepeatMbx) + { + TMBX MBXMEM *pMbx = psRepeatMbx; + /* send mailbox service stored for repeat */ + /* HBu 13.10.06: if a repeat request is received (again) before the previously repeated mailbox telegram + was read from the master (psStoreMbx != NULL) the next mailbox telegram to be sent is still in the + read mailbox so it has not to updated exchanged */ + ENTER_MBX_CRITICAL; + + if (bSendMbxIsFull && psStoreMbx == NULL) + { + /* mailbox is full, take the buffer off */ +/*ECATCHANGE_START(V5.11) HW1*/ + DisableSyncManChannel(MAILBOX_READ); + + /* store the buffer to be sent next */ + psStoreMbx = psReadMbx; + /* enable the mailbox again */ + EnableSyncManChannel(MAILBOX_READ); +/*ECATCHANGE_END(V5.11) HW1*/ + + /* HBu 15.02.06: flag has to be reset otherwise the mailbox service + will not be copied by MBX_CopyToSendMailbox */ + bSendMbxIsFull = FALSE; + } + + + MBX_CopyToSendMailbox(pMbx); + // HBu 17.06.06: psRepeatMbx has to be set to 0, when it was repeated, otherwise it would be returned twice + // to the empty queue (MAILBOX_QUEUE=1) or a buffer get lost, if the the next repeat request will happen before + // the repeated buffer was read + psRepeatMbx = NULL; + LEAVE_MBX_CRITICAL; + } + + // Repeat was finished, toggle the acknowledge bit + bMbxRepeatToggle = !bMbxRepeatToggle; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param pMbx Pointer to a Mailbox command to be sent (read by the Master) + \param flags Bit 0-6: mailbox protocol type: + 0x01 - emergency service + 0x02 - CoE service + 0x04 - SoE service + 0x80 - EoE service + 0x10 - AoE service + 0x20 - VoE service + Bit 7: 0 - no more fragments to be sent for the requested mailbox service + 1 - additional fragments to be sent for the mailbox service, the + corresponding XXXX_ContinueInd-function will be called to get + the next fragment + + \return 0: Success - mailbox command could be stored in the send mailbox + 1: Failed - mailbox command could not be stored in the send mailbox, the + XXXX_ContinueInd service will be called when the mailbox was + read from the master to + + \brief This function puts a new Mailbox service in the Send Mailbox +*//////////////////////////////////////////////////////////////////////////////////////// + +UINT8 MBX_MailboxSendReq( TMBX MBXMEM * pMbx, UINT8 flags ) +{ + UINT8 result = 0; + + /* HBu 06.02.06: in INIT-state a mailbox send request shall be refused */ + if ( (nAlStatus & STATE_MASK) == STATE_INIT ) + return ERROR_INVALIDSTATE; + + + ENTER_MBX_CRITICAL; + DISABLE_MBX_INT; + + /* the counter in the mailbox header has to be incremented with every new mailbox service to be sent + if the mailbox data link layer is supported (software switch MAILBOX_REPEAT_SUPPORTED set)*/ + pMbx->MbxHeader.Flags[MBX_OFFS_COUNTER] &= ~MBX_MASK_COUNTER; + /* HBu 13.02.06: Repeat-Counter was incremented too much if the mailbox service could not be sent */ + /* u8MbxCounter holds the actual counter for the mailbox header, only the values + 1-7 are allowed if the mailbox data link layer is supported */ + if ( (u8MbxReadCounter & 0x07) == 0 ) + u8MbxReadCounter = 1; + + pMbx->MbxHeader.Flags[MBX_OFFS_COUNTER] |= u8MbxReadCounter << MBX_SHIFT_COUNTER; + + /* try to copy the mailbox command in the ESC */ + if ( MBX_CopyToSendMailbox(pMbx) != 0 ) + { + /* no success, send mailbox was full, set flag */ + result = PutInMbxQueue(pMbx, &sMbxSendQueue); + if (result != 0) + flags |= FRAGMENTS_FOLLOW; + else + u8MbxReadCounter++; + } + /* HBu 13.02.06: Repeat-Counter was incremented too much if the mailbox service could not be sent */ + else + { + u8MbxReadCounter++; + } + + if ( flags & FRAGMENTS_FOLLOW ) + { + /* store the mailbox service that the corresponding XXX_ContinueInd function will + be called when the send mailbox will have been read by the master because there + are mailbox commands to be sent for this service */ + u8MailboxSendReqStored |= (flags & ((UINT8) ~FRAGMENTS_FOLLOW)); + } + + ENABLE_MBX_INT; + LEAVE_MBX_CRITICAL; + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \brief This function is used to check if the received mailbox command can be processed. + + Also the contents of the Receive Mailbox will be copied in the variable sMbx. +*//////////////////////////////////////////////////////////////////////////////////////// + +void MBX_CheckAndCopyMailbox( void ) +{ + UINT16 mbxLen; + + /* get the size of the received mailbox command and acknowledge the event*/ + HW_EscReadWord(mbxLen,u16EscAddrReceiveMbx); + + /* the size has to be swapped here, all other bytes of the mailbox service will be swapped later */ + mbxLen = SWAPWORD(mbxLen); + +/* ECATCHANGE_START(V5.11) MBX2*/ + if(bNoMbxMemoryAvailable == TRUE) + { + /* Return a no memory error in case of any mailbox request*/ + TMBX MBXMEM *pMbx = (TMBX MBXMEM *) APPL_AllocMailboxBuffer(10); /* a mailbox error datagram length*/ + + if(pMbx != NULL) + { + HMEMSET(pMbx,0x00,10); + + /* Mailbox error response: type 0 (mailbox service protocol) */ + pMbx->MbxHeader.Length = 4; + pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] &= ~(MBX_MASK_TYPE); + pMbx->Data[0] = SWAPWORD(MBXSERVICE_MBXERRORCMD); + pMbx->Data[1] = SWAPWORD(MBXERR_NOMOREMEMORY); + MBX_MailboxSendReq(pMbx, 0); + + APPL_FreeMailboxBuffer(pMbx); + } + } +/* ECATCHANGE_END(V5.11) MBX2*/ + /* the length of the mailbox data is in the first two bytes of the mailbox, + so the length of the mailbox header has to be added */ + mbxLen += MBX_HEADER_SIZE; + + /* in this example there are only two mailbox buffers available in the firmware (one for processing and + one to stored the last sent response for a possible repeat request), so a + received mailbox service can only be processed if a free buffer is available */ + if ( ( bSendMbxIsFull ) /* a received mailbox service will not be processed + as long as the send mailbox is still full + (waits to be read from the master) */ + ||( u8MailboxSendReqStored ) /* a mailbox service to be sent is still stored + so the received mailbox service will not be processed + until all stored mailbox services are sent */ + ) + { + /* set flag that the processing of the mailbox service will be checked in the + function MBX_Main (called from ECAT_Main) */ + bReceiveMbxIsLocked = TRUE; + } + else + { + /* received mailbox command can be processed, reset flag */ + bReceiveMbxIsLocked = FALSE; + + /* if the read mailbox size is too big for the buffer, set the copy size to the maximum buffer size, otherwise + memory could be overwritten, + the evaluation of the mailbox size will be done in the mailbox protocols called from MBX_WriteMailboxInd */ + + if (mbxLen > u16ReceiveMbxSize) + mbxLen = u16ReceiveMbxSize; + + psWriteMbx = (TMBX MBXMEM *) APPL_AllocMailboxBuffer(u16ReceiveMbxSize); + + /* if there is no more memory for mailbox buffer, the mailbox should not be read */ + if (psWriteMbx == NULL) + { + /* set flag that the processing of the mailbox service will be checked in the + function MBX_Main (called from ECAT_Main) */ + bReceiveMbxIsLocked = TRUE; + return; + } + /* copy the mailbox header and data*/ + HW_EscReadMbxMem((MEM_ADDR MBXMEM *) psWriteMbx,u16EscAddrReceiveMbx,mbxLen); + + + { + /*Read Control and Status of SyncManager 0 to check if the buffer is unlocked*/ + VARVOLATILE UINT16 smstate = 0x00; + HW_EscReadWord(smstate,ESC_SYNCMAN_CONTROL_OFFSET); +/*ECATCHANGE_START(V5.11) ECAT7*/ + smstate = SWAPWORD(smstate); +/*ECATCHANGE_END(V5.11) ECAT7*/ + + if(smstate & SM_STATUS_MBX_BUFFER_FULL) + { + /*Unlock the mailbox SyncManger buffer*/ + u16dummy = 0; + HW_EscReadWord(u16dummy,(u16EscAddrReceiveMbx + u16ReceiveMbxSize - 2)); + + } + + } + + /* in MBX_MailboxWriteInd the mailbox protocol will be processed */ + MBX_MailboxWriteInd( psWriteMbx ); + + } +} + + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param pMbx Pointer to the Mailbox command to be send to the master. + + \brief This function copies data to the Send Mailbox. +*//////////////////////////////////////////////////////////////////////////////////////// + +UINT8 MBX_CopyToSendMailbox( TMBX MBXMEM *pMbx ) +{ + if ( (nAlStatus & STATE_MASK) == STATE_INIT) + { + /* the mailbox is disabled if the slave is in the INIT state */ + return( ERROR_INVALIDSTATE ); + } + + + if ( !bMbxRunning ) + { + /* the mailbox is disabled if the slave is in the INIT state */ + return( ERROR_INVALIDSTATE ); + } + + if ( bSendMbxIsFull ) + { + /* mailbox service cannot be sent because the send mailbox is still full */ + return MBXERR_NOMOREMEMORY; + } + else + { + /* the variable mbxSize contains the size of the mailbox data to be sent */ + UINT16 mbxSize = pMbx->MbxHeader.Length; + HW_EscWriteMbxMem((MEM_ADDR *)pMbx, u16EscAddrSendMbx, (mbxSize + MBX_HEADER_SIZE)); +/* ECATCHANGE_HW(V5.10) HW1*/ + + { + /*Read Control and Status of SyncManager 1 to check if the buffer is still marked as empty*/ + VARVOLATILE UINT16 smstate = 0x00; + HW_EscReadWord(smstate,(ESC_SYNCMAN_CONTROL_OFFSET + SIZEOF_SM_REGISTER)); +/*ECATCHANGE_START(V5.11) ECAT7*/ + smstate = SWAPWORD(smstate); +/*ECATCHANGE_END(V5.11) ECAT7*/ + + if(!(smstate & SM_STATUS_MBX_BUFFER_FULL)) + { + UINT8 BytesLeft = u16SendMbxSize - (mbxSize + MBX_HEADER_SIZE); + + /*Write last Byte to trigger mailbox full flag*/ + /*Read last 2 Bytes and write them again (required if low Byte of the WORD were written before)*/ + u16dummy = 0; + if(BytesLeft < 2) + { + /*The last 2Bytes are overlapping the already written buffer*/ + + /*Get the valid 16Bit address*/ + UINT32 LastDataAddress = ((mbxSize + MBX_HEADER_SIZE)/2)*2; + + /*Copy the buffer to overwrite*/ + MEMCPY((UINT16 *)&u16dummy,(((UINT8 *)pMbx) + LastDataAddress),(2 - BytesLeft)); + } + + HW_EscWriteWord(u16dummy,(u16EscAddrSendMbx + u16SendMbxSize - 2)); + } + } + + /* store last send mailbox service for a possible repeat + one buffer includes the last send service (psRepeatMbx), + the other one the actual service to be sent (psReadMbx), + there is no buffer available for a mailbox receive service + until the last sent buffer was read from the master + the exception is after the INIT2PREOP transition, in that + case there is no last sent service (psReadMbx = 0) */ + if ( psReadMbx ) + psWriteMbx = NULL; + else + { + /* only the first time after the INIT2PREOP-transition */ + psWriteMbx = &asMbx[1]; + } + // HBu 17.06.06: psRepeatMbx was already updated in MBX_MailboxReadInd + // psRepeatMbx = psReadMbx; + psReadMbx = pMbx; + + /* set flag that send mailbox is full now */ + bSendMbxIsFull = TRUE; + + + return 0; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \brief This function is called cyclically to check if a received Mailbox service was + stored. +*//////////////////////////////////////////////////////////////////////////////////////// + +void MBX_Main(void) +{ + TMBX MBXMEM *pMbx = NULL; + + do + { + UINT8 result = 0; + + pMbx = GetOutOfMbxQueue(&sMbxReceiveQueue); + if ( pMbx ) + result = MailboxServiceInd(pMbx); + + if ( result != 0 ) + { + /* Mailbox error response: type 0 (mailbox service protocol) */ + pMbx->MbxHeader.Length = 4; + pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] &= ~(MBX_MASK_TYPE); + pMbx->Data[0] = SWAPWORD(MBXSERVICE_MBXERRORCMD); + pMbx->Data[1] = SWAPWORD(result); + MBX_MailboxSendReq(pMbx, 0); + } + } + while ( pMbx != NULL ); + + + + if ( bReceiveMbxIsLocked ) + { + /* the work on the receive mailbox is locked, check if it can be unlocked (if all + mailbox commands has been sent */ + MBX_CheckAndCopyMailbox(); + } +} + +/** @} */ + + diff --git a/GD32E508_DCU_APP/comm/ssc/src/objdef.c b/GD32E508_DCU_APP/comm/ssc/src/objdef.c new file mode 100644 index 0000000..55789eb --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/src/objdef.c @@ -0,0 +1,1699 @@ +/** +\addtogroup CoE CAN Application Profile over EtherCAT +@{ +*/ + +/** +\file objdef.c +\author EthercatSSC@beckhoff.com +\brief Implementation +This file contains the object dictionary access functions + +\version 5.11 + +
Changes to version V5.10.1:
+V5.11 DIAG4: change parameter handling in DIAG_CreateNewMessage()
+V5.11 ECAT:
+V5.11 ECAT10: change PROTO handling to prevent compiler errors
+V5.11 ESM7: "add Sync define for 0x22 (""SYNCTYPE_SM2_SYNCHRON""), support value 0x22 for 0x1C33.1 (SM2 sync)"
+V5.11 OBJ1: OBJ_GetEntryOffset: return offset 0 for si0 (always offset 16 was returned before)
+V5.11 SDO1: "Obj_Read/Obj_Write: Check if entries > 8bit has even word offset (otherwise ""unsupported access"" is returned). To support these object a read/write callback function need to be used (see Application Note ET9300)"
+V5.11 SDO2: Obj_Read/Obj_Write: Handle arrays (string, array of byte, ...) with an odd word length (buffer shift was wrong). Only an entry <=8Bit is allowed after such an entry.
+V5.11 SDO3: handle not word aligned enums
+V5.11 SDO5: add missing swapping on SI0 write access
+V5.11 SDO6: OBJ_GetDesc ensure that the default string is used if no string was found
+V5.11 SDO7: add missing value swapping if memory values are bitwise modified on object read/write access
+V5.11 SDO9: "handle SDO access for entries which are not accessible (in case of SDO complete access), SDO access handle ""DEFTYPE_ARRAY_OF_INT"" , ""DEFTYPE_ARRAY_OF_SINT"" , ""DEFTYPE_ARRAY_OF_DINT"" , ""DEFTYPE_ARRAY_OF_UDINT"", SDO access handle : ""BYTE"" , ""WORD"" and ""DWORD"""
+V5.11 TEST2: add test 0x300C object (entry bitlength > 65535)
+V5.11 TEST8: create diag message on SDO write access to 0x3002
+
Changes to version V5.01:
+V5.10 COE2: Change return value of Get ObjectSize to UINT32
+ Change object size to UINT32
+V5.10 ECAT11: Add define "USE_SINGLE_PDO_MAPPING_ENTRY_DESCR" (Only one entry description is required for all PDO mapping entries)
+V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)
+ Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings
+ Update 0x1C3x entries
+V5.10 ECAT3: Add new datatype handling (BITARRxx)
+ Use read/write marco to access STRING entries
+V5.10 SDO1: Handle access to enums on odd memory addresses (only for MBX_16BIT_ACCESS)
+V5.10 SDO2: Check for termination char in every substring
+V5.10 SDO5: Check also for PDO mapping object if the subindex 0 was set to 0 before the entries will be changed.
+V5.10 SDO9: SDO complete write access update buffer offset calculation in case of SI0 is readonly
+V5.10 TEST2: Add 0x3004.14 (Access to this entry will always rejected with the code "cannot be acceesd because of local control")
+V5.10 TEST4: Add CoE test (0x2020.5) to return always max object data on complete access (not limited to value of SI0)
+
Changes to version V5.0:
+V5.01 COE2: Calculate the bit offset also for SI0 in case if an SDO download access.
+V5.01 SDO1: Update alignment entry handling.
+V5.01 SDO5: Update offset calculation in case of an not accessible entry
+V5.01 SDO7: Update object length calculation in case of an BitArray
+V5.01 TEST5: Add slave configuration object handling (0x8000, if test application is active)
+V5.01 TEST6: Add test to create dummy object dictionary.
+
Changes to version V4.40:
+V5.0 TEST1: Add test application.
+V5.0 COE3: Update OCTED_STRING and VISIBLE_STRING read/write.
+V5.0 COE5: Add BigEndian support for enum SDO read, handle complete access also for objects with align or not existing entries.
+V5.0 COE7: Support SDO Info entry name handling for 16Bit access
+V5.0 DIAG2: SDO Info request for diagnosis messages 0x10F3 SI5 or greater return always description of SI6.
+V5.0 COE2: Add plausibility check for PDO assign objects.
+V5.0 OBJ2: Support DWORD/WORD aligned object structures.
+V5.0 SDO7: Update SDO read/write for object(entries) less or equal 8Bit.
+
Changes to version V4.30:
+V4.40 DIAG1: Add diagnosis access functions
+V4.40 OBJ2: change counter variable to prevent deadlock caused by overrun (required if subindex0 is 255)
+V4.40 COE2: Prevent accessing empty object dictionary entry
+V4.40 COE3: Read whole object dictionary
+V4.40 COE1: Prototype of OBJ_GetObjectList() changed, set abort code if object dictionary is empty
+V4.30 OBJ 3: Object dictionary list handling
+V4.30 OBJ 2: fix SDO complete access bug
+V4.11 OBJ 1-3: OBJ_GetDesc() Now, subindex 0 instead of 0xFF is used to return the description of the whole object
+Subindex 0xFF could be used like a normal subindex.
+
Changes to version V4.08:
+V4.10 OBJ 1: calculate size of PdoObjectEntry Index 1 and not Index 0
+V4.10 OBJ 2: the pointer of the object dictionary will be get by a function (COE_GetObjectDictionary)
+
Changes to version V4.07:
+V4.08 OBJ 1: if subindex 0 is writable, the maximum subindex should be checked in an object specific function,
+ because for the PDO mapping and PDO assign the object shall only be written if subindex 0 is 0.
+
Changes to version V4.04:
+V4.04 OBJ 1: The loop in OBJ_GetObjectList was one subindex too low
+
Changes to version V4.03:
+V4.04 SDO 1: The SDO interface was changed in that way that a SDO response
+ could be sent by the application to a later time. In that case
+ the functions OBJ_Read and OBJ_Write shall return the value
+ ABORTIDX_WORKING. To send the SDO response the new function SDOS_SdoRes
+ has to be called by the application. While waiting for the call
+ of SDOS_SdoRes the SDO interface will answer to another SDO request
+ with the error MBXERR_SERVICEINWORK in the mailbox protocol
+V4.04 OBJ 1: The loop in OBJ_GetNoOfObjects was one subindex too low
+
Changes to version V4.02:
+V4.03 OBJ 1: The object list was not returned correctly for the list types 2-5
+
Changes to version V3.20:
+V4.00 OBJ 1: The OBJ functions are modified for a more flexible definition of
+ the object dictionary and to support bit types too
+V4.00 SDO 2: The object lists will be generated in the functions OBJ_GetNoOfObjects
+ and OBJ_GetObjectList in objdef.c to decouple the SDO services from
+ the implementation of the object dictionary
+V4.00 COEAPPL 2: The handling of backup parameters was included according to
+ the EtherCAT Guidelines and Protocol Enhancements Specification + +*/ + + +#define OBJDEF 0x4000 +#define OBJDEFMAX 0x0A + +/*--------------------------------------------------------------------------------------- +------ +------ Includes +------ +---------------------------------------------------------------------------------------*/ + +#include "ecat_def.h" + + +#include "ecatslv.h" +#include "coeappl.h" + +#define _OBJDEF_ 1 +#include "objdef.h" +#undef _OBJDEF_ +/* ECATCHANGE_START(V5.11) ECAT10*/ +/*remove definition of _OBJDEF_ (#ifdef is used in objdef.h)*/ +/* ECATCHANGE_END(V5.11) ECAT10*/ + + + + +extern OBJCONST TOBJECT OBJMEM *pSdoInfoObjEntry; +extern OBJCONST TSDOINFOENTRYDESC OBJMEM * OBJ_GetEntryDesc(OBJCONST TOBJECT OBJMEM * pObjEntry, UINT8 Subindex); + +//--------------------------------------------------------------------------------------- + +/*--------------------------------------------------------------------------------------- +------ +------ Internal types and defines +------ +---------------------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------------------- +------ +------ module internal function declarations +------ +---------------------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------------------- +------ +------ Module internal variable definitions +------ +---------------------------------------------------------------------------------------*/ +const UINT16 cBitMask[16] = {0x0000,0x0001,0x0003,0x0007,0x000F,0x001F,0x003F,0x007F,0x00FF,0x01FF,0x03FF,0x07FF,0x0FFF,0x1FFF,0x3FFF,0x7FFF}; +/*--------------------------------------------------------------------------------------- +------ +------ Functions +------ +---------------------------------------------------------------------------------------*/ + + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param index Indicates the index of the dictionary object. + + \return A handle to an object of the requested index or NULL if not found. + + \brief The function looks in all objects of the dictionary after the indicated index + and returns a handle if found. + +*//////////////////////////////////////////////////////////////////////////////////////// + +OBJCONST TOBJECT OBJMEM * OBJ_GetObjectHandle( UINT16 index ) +{ + OBJCONST TOBJECT OBJMEM * pObjEntry = (OBJCONST TOBJECT OBJMEM *) COE_GetObjectDictionary(); + + while (pObjEntry!= NULL) + { + if (pObjEntry->Index == index) + return pObjEntry; + pObjEntry = (TOBJECT OBJMEM *) pObjEntry->pNext; + } + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param index Index of the requested object. + \param subindex Subindex of the requested object. + \param pObjEntry Handle to the dictionary object returned by + OBJ_GetObjectHandle which was called before + \param bCompleteAccess Indicates if a complete read of all subindices of the + object shall be done or not + + \return The size of the object entry in bytes (!). + + \brief This function returns the size of the requested entry. If bCompleteaccess is set the size of the complete object is returned (the returned size is byte packed, gaps based on the used platform/compiler are not taken into account) + +*//////////////////////////////////////////////////////////////////////////////////////// +UINT32 OBJ_GetObjectLength( UINT16 index, UINT8 subindex, OBJCONST TOBJECT OBJMEM * pObjEntry, UINT8 bCompleteAccess) +{ + /* get the information of ObjCode and MaxSubindex in local variables to support different types of microcontroller */ + UINT8 objCode = (pObjEntry->ObjDesc.ObjFlags & OBJFLAGS_OBJCODEMASK) >> OBJFLAGS_OBJCODESHIFT; + UINT8 maxSubindex = (pObjEntry->ObjDesc.ObjFlags & OBJFLAGS_MAXSUBINDEXMASK) >> OBJFLAGS_MAXSUBINDEXSHIFT; + UINT32 size = 0; + + if ( bCompleteAccess ) + { + if ( objCode == OBJCODE_VAR ) + return 0; + else if ((objCode == OBJCODE_ARR) + ) + { + + /* we have to get the maxSubindex from the actual value of subindex 0, + which is stored as UINT16 at the beginning of the object's variable */ + maxSubindex = (UINT8) ((UINT16 MBXMEM *) (pObjEntry->pVarPtr))[0]; + + + size = pObjEntry->pEntryDesc[1].BitLength *maxSubindex; + +/* no padding required: Bit entries within an array object may overlap byte borders*/ + + /* we have to convert the size in bytes */ + size = BIT2BYTE(size); + + if ( subindex == 0 ) + { + /* add size for subindex 0 (is transmitted as UINT16) */ + size += 2; + } + return size; + } + else + { + UINT8 i; + + /* add the sizes of all entries */ + for (i = 1; i <= maxSubindex; i++) + { + size += pObjEntry->pEntryDesc[i].BitLength; + } + /* we have to convert the size in bytes */ + size = BIT2BYTE(size); + + if ( subindex == 0 ) + { + /* add size for subindex 0 (is transmitted as UINT16) */ + size += 2; + } + return size; + } + } + else + { + if ( objCode == OBJCODE_VAR ) + { + return (BIT2BYTE(pObjEntry->pEntryDesc->BitLength)); + + } + else if ( subindex == 0 ) + { + /* for single access subindex 0 is transmitted as UINT8 */ + return 1; + } + else if((objCode == OBJCODE_ARR) + ) + { + return (BIT2BYTE(pObjEntry->pEntryDesc[1].BitLength)); + } + else + { + return (BIT2BYTE(pObjEntry->pEntryDesc[subindex].BitLength)); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param pStr Pointer to a string + \param Number Number to be included in the string + + \brief The function copies the Number in the string pStr, + which shall be initialized with 000 + +*//////////////////////////////////////////////////////////////////////////////////////// + +void OBJ_CopyNumberToString(UCHAR MBXMEM *pStr, UINT8 Number) +{ + UINT8 Modulo; + + pStr[2] = '0'; + pStr[0] += (Number / 100); + Modulo = Number % 100; + pStr[1] += (Modulo / 10); + pStr[2] += (Modulo % 10); +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param listType requested listType (0=all objects, 1=RxPDO mappable objects, + 2=TxPDO mappable objects, 3=backup objects, 4=setting objects) + + \return Number of objects of the requested list type + + \brief The function counts the number of objects of the requested list type + +*//////////////////////////////////////////////////////////////////////////////////////// + +UINT16 OBJ_GetNoOfObjects(UINT8 listType) +{ + /* the variable listFlags contains the mask used for the ObjAccess in the Entry-Desc + see the structure TSDOINFOENTRYDESC in sdoserv.h, listType = 0 indicates that + all objects has to be counted */ + + UINT16 listFlags = 0x0020 << listType; + + /* set pObjEntry to the beginning of the object dictionary */ + OBJCONST TOBJECT OBJMEM * pObjEntry = (OBJCONST TOBJECT OBJMEM *) COE_GetObjectDictionary(); + UINT16 n = 0; + + + while (pObjEntry != NULL) + { + /* count the objects of the requested list type */ + if ( pObjEntry->Index >= 0x1000 ) + { + UINT8 t = listType; + if ( t ) + { + UINT8 maxSubindex = (pObjEntry->ObjDesc.ObjFlags & OBJFLAGS_MAXSUBINDEXMASK) >> OBJFLAGS_MAXSUBINDEXSHIFT; + UINT16 i = 0; + + while ( t && i <= maxSubindex ) + { + if ( OBJ_GetEntryDesc(pObjEntry,(UINT8) i)->ObjAccess & listFlags ) + t = 0; + i++; + } + } + if ( !t ) + { + /* object from listType found */ + n++; + } + } + /* next object in object dictionary */ + pObjEntry = (TOBJECT OBJMEM *) pObjEntry->pNext; + } + + return n; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param listType Requested listType (0=all objects, 1=RxPDO mappable objects, + 2=TxPDO mappable objects, 3=backup objects, 4=setting objects) + \param pIndex Pointer to the next Index of the object list to copied in the mailbox buffer, + has to adapted at the end of the function + \param size Size of the available mailbox buffer + \param pData Pointer to the mailbox buffer where (the part of) the object list requested listType (0=all objects, 1=RxPDO mappable objects, + \param pAbort Pointer to abortCode + + \return Size of the available mailbox buffer which was not copied to + + \brief The function copies (the part of) the object list in the mailbox buffer + +*//////////////////////////////////////////////////////////////////////////////////////// +UINT16 OBJ_GetObjectList(UINT16 listType, UINT16 *pIndex, UINT16 size, UINT16 MBXMEM *pData,UINT8 *pAbort) +{ + /* the variable listFlags contains the mask used for the ObjAccess in the Entry-Desc + see the structure TSDOINFOENTRYDESC in sdoserv.h, listType = 0 indicates that + all objects has to be counted */ + UINT16 listFlags = 0x0020 << listType; + OBJCONST TOBJECT OBJMEM * pObjEntry; + + + if ( pIndex[0] == 0x1000 ) + { + /* beginning of object list, set pObjEntry to the beginning of the object dictionary */ + pObjEntry = (OBJCONST TOBJECT OBJMEM *) COE_GetObjectDictionary(); + // set abort code if no object dictionary is available + if((pObjEntry == NULL) && (pAbort != NULL)) + { + *pAbort = ABORTIDX_NO_OBJECT_DICTIONARY_IS_PRESENT; + } + } + else + { + /* next fragment, the next object to be handled was stored in pSdoInfoObjEntry */ + pObjEntry = pSdoInfoObjEntry; + } + + if ( pObjEntry != NULL ) + { + while (pObjEntry != NULL && size > 1 ) + { + /* get the next index of the requested object list if there is enough space in the mailbox buffer */ + if ( pObjEntry->Index >= 0x1000 ) + { + /* UINT8 was changed to UINT16 */ + UINT16 t = listType; + if ( t ) + { + UINT8 maxSubindex = (pObjEntry->ObjDesc.ObjFlags & OBJFLAGS_MAXSUBINDEXMASK) >> OBJFLAGS_MAXSUBINDEXSHIFT; + UINT8 i = 0; + + while ( t && i <= maxSubindex ) + { + if ( OBJ_GetEntryDesc(pObjEntry, i)->ObjAccess & listFlags ) + t = 0; + i++; + } + } + if ( !t ) + { + /* store the index in the mailbox buffer */ + *pData = SWAPWORD(pObjEntry->Index); + pData++; + size -= 2; + } + } + pObjEntry = (TOBJECT OBJMEM *) pObjEntry->pNext; + } + } + + /* return the next Index to be handled */ + if(pObjEntry != NULL) + { + pIndex[0] = pObjEntry->Index; + } + else + { + /*last entry reached*/ + pIndex[0] = 0xFFFF; + } + + /* store object description pointer and index for next fragment */ + pSdoInfoObjEntry = pObjEntry; + + /* return the size of the available mailbox buffer which was not copied to */ + return size; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param index Indicates the index of the dictionary object. + \param subindex Indicates the subindex of the dictionary object. + Subindex 0xff returns the description of the whole object ( the name of + the object ). + Subindex 0x00 returns the description of the subindex 0 and so on. + \param pObjEntry Is a handle to the dictionary object ( for faster access ) or NULL. + \param pData Is the memory field for the description string or NULL ( if the size of + string is unknown ): + + \return The size in bytes of the description string (without null termination byte ). + 0 will be returned if a description for the indicated entry was not found. + + \brief The function returns size and description string of the requested entry. + + Its possible to define all description strings of one entry ( including the name + of the object ) in one structure:
+ {
+ name_of_object with index,
+ description_of_subindex0,
+ .
+ description_of_subindexN,
+ 0xFF
+ } +*//////////////////////////////////////////////////////////////////////////////////////// + +UINT16 OBJ_GetDesc( UINT16 index, UINT8 subindex, OBJCONST TOBJECT OBJMEM * pObjEntry, UINT16 MBXMEM * pData ) +{ + UINT16 strSize = 0; + OBJCONST UCHAR OBJMEM * pDesc = (OBJCONST UCHAR OBJMEM *) pObjEntry->pName; +/* get the information of ObjCode and MaxSubindex in local variables to support different types of microcontroller */ + UINT8 objCode = (pObjEntry->ObjDesc.ObjFlags & OBJFLAGS_OBJCODEMASK) >> OBJFLAGS_OBJCODESHIFT; + + if ( (subindex == 0) || (objCode == OBJCODE_VAR) ) + { + // Get object description length + strSize = OBJSTRLEN( (OBJCONST CHAR OBJMEM *) pDesc ); + + // If there is a pointer given, copy data: + if ( pData ) + { + OBJTOMBXSTRCPY( pData, pDesc, strSize ); + } + } + else + { + if ( objCode == OBJCODE_REC ) + { + { + // get pointer to description of subindex 1 : + // 16bit variable to avoid overflow if subindex 0xFF is read + UINT16 i = 1; + UINT16 tmpSubindex = subindex; + + { + + OBJCONST UCHAR OBJMEM * pSubDesc = (OBJCONST UCHAR OBJMEM *) OBJGETNEXTSTR( pDesc ); + while (( i <= tmpSubindex ) + &&( pSubDesc[0] != 0xFF && pSubDesc[0] != 0xFE )) + { + if ( i == tmpSubindex ) + { + strSize = OBJSTRLEN( (OBJCONST CHAR OBJMEM *) pSubDesc ); + if ( pData && strSize ) + OBJTOMBXSTRCPY( pData, pSubDesc, strSize ); +/* ECATCHANGE_START(V5.11) SDO6*/ + else + strSize = 0; +/* ECATCHANGE_END(V5.11) SDO6*/ + + break; + } + else + { + i++; + pSubDesc = (OBJCONST UCHAR OBJMEM *) OBJGETNEXTSTR( pSubDesc ); + } + } + } + } + } + + if ( strSize == 0 ) + { + // no string found for subindex x -> name is Subindex x + strSize = 12; + + if ( pData ) + { + UCHAR OBJMEM TmpDescr[13]; + OBJSTRCPY(TmpDescr,aSubindexDesc,SIZEOF(TmpDescr)); + + OBJ_CopyNumberToString( &TmpDescr[9], subindex ); + MBXSTRCPY( pData, TmpDescr, SIZEOF(TmpDescr) ); + } + } + } + + return strSize; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param pObjEntry handle to the dictionary object returned by + OBJ_GetObjectHandle which was called before + \param Subindex subindex of the requested object. + + \return Pointer to the EntryDesc of the Subindex + + \brief The function returns the Entry-Desc of a subindex to allow the application + to define the object dictionary independent of the sdoserv-files + +*//////////////////////////////////////////////////////////////////////////////////////// + +OBJCONST TSDOINFOENTRYDESC OBJMEM * OBJ_GetEntryDesc(OBJCONST TOBJECT OBJMEM * pObjEntry, UINT8 Subindex) +{ + OBJCONST TSDOINFOENTRYDESC OBJMEM *pEntry; + UINT8 objCode = (pObjEntry->ObjDesc.ObjFlags & OBJFLAGS_OBJCODEMASK) >> OBJFLAGS_OBJCODESHIFT; + + if ((objCode == OBJCODE_ARR) + ) + { + /* object is an array */ + if ( Subindex == 0 ) + /* subindex 0 has a description */ + pEntry = &pObjEntry->pEntryDesc[0]; + else + /* and all other elements have the same description */ + pEntry = &pObjEntry->pEntryDesc[1]; + } + else + { + { + /* object is a variable or a record return the corresponding entry */ + pEntry = &pObjEntry->pEntryDesc[Subindex]; + } + } + + return pEntry; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param pObjEntry handle to the dictionary object returned by + OBJ_GetObjectHandle which was called before + + \return Pointer to the ObjDesc of the Object + + \brief The function returns the Obj-Desc of an object to allow the application + to define the object dictionary independent of the sdoserv-files + +*//////////////////////////////////////////////////////////////////////////////////////// + +OBJCONST TSDOINFOOBJDESC OBJMEM * OBJ_GetObjDesc(OBJCONST TOBJECT OBJMEM * pObjEntry) +{ + return &pObjEntry->ObjDesc; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param subindex subindex of the entry + \param pObjEntry handle to the dictionary object returned by + OBJ_GetObjectHandle which was called before + + \return bit offset of the entry in the variable + + \brief This function calculates the bit offset of the entry in the object's variable +*//////////////////////////////////////////////////////////////////////////////////////// + +UINT16 OBJ_GetEntryOffset(UINT8 subindex, OBJCONST TOBJECT OBJMEM * pObjEntry) +{ + UINT16 i; + /* bitOffset will be initialized with the bit offset of subindex 1 */ +/*ECATCHANGE_START(V5.11) OBJ1*/ + UINT16 bitOffset = 0; + UINT8 objCode = (pObjEntry->ObjDesc.ObjFlags & OBJFLAGS_OBJCODEMASK) >> OBJFLAGS_OBJCODESHIFT; + OBJCONST TSDOINFOENTRYDESC OBJMEM *pEntry; + + if(subindex > 0) + { + /*subindex 1 has an offset of 16Bit (even if Si0 is only an UINT8) */ + bitOffset +=16; + } +/*ECATCHANGE_END(V5.11) OBJ1*/ + + if (objCode == OBJCODE_VAR) + return 0; + + for (i = 1; i <= subindex; i++) + { + /* get the entry description */ + if ((objCode == OBJCODE_ARR) + ) + pEntry = &pObjEntry->pEntryDesc[1]; + else + { + pEntry = &pObjEntry->pEntryDesc[i]; + } + + switch (pEntry->DataType) + { + case DEFTYPE_INTEGER16: + case DEFTYPE_UNSIGNED16: + case DEFTYPE_BITARR16: +/*ECATCHANGE_START(V5.11) SDO9*/ + case DEFTYPE_WORD: + case DEFTYPE_UNICODE_STRING: + case DEFTYPE_ARRAY_OF_INT : +/*ECATCHANGE_END(V5.11) SDO9*/ + +#if OBJ_WORD_ALIGN || OBJ_DWORD_ALIGN + /* the 16-bit variables in the structure are word-aligned, + align the actual bitOffset to a word */ + bitOffset = (bitOffset+15) & 0xFFF0; +#endif + + +/*ECATCHANGE_START(V5.11) SDO9*/ + if (i < subindex) + { + if((pEntry->DataType == DEFTYPE_UNICODE_STRING) + ||(pEntry->DataType == DEFTYPE_ARRAY_OF_INT)) + { + bitOffset += pEntry->BitLength; + } + + bitOffset += 16; + } +/*ECATCHANGE_END(V5.11) SDO9*/ + break; + case DEFTYPE_UNSIGNED32: + case DEFTYPE_INTEGER32: + case DEFTYPE_REAL32: + case DEFTYPE_BITARR32: +/*ECATCHANGE_START(V5.11) SDO9*/ + case DEFTYPE_DWORD: + case DEFTYPE_ARRAY_OF_DINT : + case DEFTYPE_ARRAY_OF_UDINT: +/*ECATCHANGE_END(V5.11) SDO9*/ +#if OBJ_DWORD_ALIGN + /* the 32-bit variables in the structure are dword-aligned, + align the actual bitOffset to a dword */ + bitOffset = (bitOffset+31) & 0xFFE0; +#elif OBJ_WORD_ALIGN + /* the 32-bit variables in the structure are word-aligned, + align the actual bitOffset to a word */ + bitOffset = (bitOffset+15) & 0xFFF0; +#endif + +/*ECATCHANGE_START(V5.11) SDO9*/ + if (i < subindex) + { + if((pEntry->DataType == DEFTYPE_ARRAY_OF_DINT) + ||(pEntry->DataType == DEFTYPE_ARRAY_OF_UDINT)) + { + bitOffset += pEntry->BitLength; + } + else + bitOffset += 32; + } +/*ECATCHANGE_END(V5.11) SDO9*/ + break; + default: + /* align the actual bitOffset to a byte */ + if (i < subindex) + bitOffset += pEntry->BitLength; + break; + } + } + + return bitOffset; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param index index of the SyncManager Parameter object + \param NewSyncType New value for the Sync Type (SubIndex 1) + + \return result Result of the value validation + + \brief Checks if the new Sync type value is valid +*//////////////////////////////////////////////////////////////////////////////////////// +UINT8 CheckSyncTypeValue(UINT16 index, UINT16 NewSyncType) +{ + /*ECATCHANGE_START(V5.11) ESM7*/ + switch (NewSyncType) + { + case SYNCTYPE_FREERUN: + return 0; //free run sync mode is always accepted + // break; + + case SYNCTYPE_SM_SYNCHRON: + if ((index == 0x1C32) + && (nPdOutputSize > 0) + && ((sSyncManOutPar.u16SyncTypesSupported & SYNCTYPE_SYNCHRONSUPP) > 0)) + { + /*SyncManager sync mode is supported and output process data is configured*/ + return 0; + } + else + if ((index == 0x1C33) + && ((sSyncManInPar.u16SyncTypesSupported & SYNCTYPE_SYNCHRONSUPP) > 0) + && (nPdOutputSize == 0) + && (nPdInputSize > 0)) + { + /*SyncManager sync mode is supported and input only shall be configured*/ + return 0; + } + break; + + case SYNCTYPE_SM2_SYNCHRON: + if ((index == 0x1C33) + && ((sSyncManInPar.u16SyncTypesSupported & SYNCTYPE_SYNCHRONSUPP) > 0) + && (nPdOutputSize > 0)) + { + /*SyncManager sync mode is supported and outputs are defined*/ + return 0; + } + break; + + case SYNCTYPE_DCSYNC0: + if ((index == 0x1C32) && ((sSyncManOutPar.u16SyncTypesSupported & SYNCTYPE_DCSYNC0SUPP) > 0)) + { + return 0; + } + else + if ((index == 0x1C33) && ((sSyncManInPar.u16SyncTypesSupported & SYNCTYPE_DCSYNC0SUPP) > 0)) + { + return 0; + } + break; + + case SYNCTYPE_DCSYNC1: + if ((index == 0x1C32) && ((sSyncManOutPar.u16SyncTypesSupported & SYNCTYPE_DCSYNC1SUPP) > 0)) + { + return 0; + } + else + if ((index == 0x1C33) && ((sSyncManInPar.u16SyncTypesSupported & SYNCTYPE_DCSYNC1SUPP) > 0)) + { + return 0; + } + break; + } //switch +/*ECATCHANGE_END(V5.11) ESM7*/ + return ABORTIDX_VALUE_EXCEEDED; + +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param index index of the requested object. + \param subindex subindex of the requested object. + \param objSize Size of the object, returned by the function OBJ_GetObjectLength + which was called before + \param pObjEntry handle to the dictionary object returned by + OBJ_GetObjectHandle which was called before + \param pData Pointer to the buffer where the read data shall be copied to + \param bCompleteAccess Indicates if a complete read of all subindices of the + object shall be done or not + + \return result of the read operation (0 (success) or an abort code (ABORTIDX_.... defined in + sdosrv.h)) + + \brief This function reads the requested object +*//////////////////////////////////////////////////////////////////////////////////////// + +UINT8 OBJ_Read( UINT16 index, UINT8 subindex, UINT32 objSize, OBJCONST TOBJECT OBJMEM * pObjEntry, UINT16 MBXMEM * pData, UINT8 bCompleteAccess ) +{ + UINT16 i = subindex; + /* get the information of ObjCode and MaxSubindex in local variables to support different types of microcontroller */ + UINT8 objCode = (pObjEntry->ObjDesc.ObjFlags & OBJFLAGS_OBJCODEMASK) >> OBJFLAGS_OBJCODESHIFT; + UINT16 maxSubindex = (pObjEntry->ObjDesc.ObjFlags & OBJFLAGS_MAXSUBINDEXMASK) >> OBJFLAGS_MAXSUBINDEXSHIFT; + OBJCONST TSDOINFOENTRYDESC OBJMEM *pEntry; + /* lastSubindex is used for complete access to make loop over the requested entries + to be read, we initialize this variable with the requested subindex that only + one loop will be done for a single access */ + UINT16 lastSubindex = subindex; + + if ( objCode != OBJCODE_VAR && index >= 0x1000 ) + { + /* if the object is an array or record we have to get the maxSubindex from the + actual value of subindex 0, which is stored as UINT16 at the beginning of the + object's variable */ + maxSubindex = (*((UINT16 *) (pObjEntry->pVarPtr))) & 0x00FF; + + } + + if ( bCompleteAccess ) + { + if ( objCode == OBJCODE_VAR || index < 0x1000 ) + { + /* complete access is not supported with simple objects or ENUM descriptions */ + return ABORTIDX_UNSUPPORTED_ACCESS; + } + + /* we read until the maximum subindex */ + lastSubindex = maxSubindex; + } + else + if ( subindex > maxSubindex ) + { + /* the maximum subindex is reached */ + return ABORTIDX_SUBINDEX_NOT_EXISTING; + } + else + { + /* get the corresponding entry description */ + pEntry = OBJ_GetEntryDesc(pObjEntry, (UINT8)i); + + /*Check access only for non-align entries*/ + if(pEntry->ObjAccess != 0x0) + { + /* check if we have read access (bits 0-2 (PREOP, SAFEOP, OP) of ObjAccess) + by comparing with the actual state (bits 1-3 (PREOP, SAFEOP, OP) of AL Status) */ + if ( ((UINT8) ((pEntry->ObjAccess & ACCESS_READ)<<1)) < (nAlStatus & STATE_MASK) ) + { + /* we don't have read access */ + if ( (pEntry->ObjAccess & ACCESS_READ) == 0 ) + { + /* it is a write only entry */ + return ABORTIDX_WRITE_ONLY_ENTRY; + } + else + { + /* we don't have read access in this state */ + return ABORTIDX_IN_THIS_STATE_DATA_CANNOT_BE_READ_OR_STORED; + } + } + } + } + if ( pObjEntry->Read != NULL ) + { + /* Read function is defined, we call the object specific read function */ + return pObjEntry->Read(index, subindex, objSize, pData, bCompleteAccess); + } + else if ( index < 0x1000 && subindex != 0 ) + { + /* an ENUM description is read */ + UINT16 size; + UINT16 MBXMEM *pVarPtr = (UINT16 MBXMEM *) pObjEntry->pVarPtr; + CHAR **p; + + /* we get the corresponding entry description */ + pEntry = OBJ_GetEntryDesc(pObjEntry, subindex); + size = BIT2BYTE(pEntry->BitLength); + + p = (CHAR **) pVarPtr; + pVarPtr = (UINT16 MBXMEM *)p[subindex-1]; + + if((((UINT16)pVarPtr) & 0x1) == 0x1) + { + /*enum is stored at an odd address*/ + UINT16 cnt = 0; + + //get last even WORD address +/*ECATCHANGE_START(V5.11) SDO3*/ + pVarPtr = (UINT16 MBXMEM *)(((MEM_ADDR)pVarPtr)& ~(MEM_ADDR)0x1); +/*ECATCHANGE_END(V5.11) SDO3*/ + + for(cnt = 0; cnt < (size / 2);cnt++) + { + //clear destination buffer + pData[0] = (pVarPtr[0] & 0xFF00) >> 8; + pData[0] |= (pVarPtr[1] & 0xFF) << 8; + + pData++; + pVarPtr++; + } + + if((size % 2) == 1) + { + pData[0] &= 0xFF00; //Clear last Byte + pData[0] |= (pVarPtr[0] & 0xFF00) >> 8; //Write last byte + } + } + else + { + // Get enum value (first 32Bit) + pData[0] = pVarPtr[0]; + pData[1] = pVarPtr[1]; + pData += 2; + pVarPtr += 2; + + // Get enum description + OBJTOMBXSTRCPY(pData,pVarPtr,size-4); + } + } + else + { + UINT8 bRead = 0x0; + UINT8 result = 0; + + + /* a variable object is read */ + for (i = subindex; i <= lastSubindex; i++) + { + /* if only a single entry is requested, this loop will only be done once */ + UINT16 MBXMEM *pVarPtr = (UINT16 MBXMEM *) pObjEntry->pVarPtr; + UINT16 bitOffset = 0; + + if (i == 0) + { + /* subindex 0 is requested, the entry's data is at the beginning of the object's variable */ + } + else if ( index >= 0x1000 ) + { + /* subindex 1-n of an variable object is requested, we get the offset of the variable here */ + bitOffset = OBJ_GetEntryOffset((UINT8)i, pObjEntry); + } + + /* we increment the variable pointer to the corresponding word address */ + pVarPtr += (bitOffset >> 4); + + /* get the corresponding entry description */ + pEntry = OBJ_GetEntryDesc(pObjEntry, (UINT8)i); + + if ( ((UINT8) ((pEntry->ObjAccess & ACCESS_READ)<<1)) >= (nAlStatus & STATE_MASK) ) + { + if ( i == subindex /* requested entry */ + || (bCompleteAccess && i >= subindex) ) /* complete access and entry should be read */ + { + UINT16 bitMask; + + /* we have to copy the entry */ + if ( i == 0 && objCode != OBJCODE_VAR ) + { + /* we read subindex 0 of an array or record */ + { + pData[0] = SWAPWORD((UINT16)maxSubindex); + } + + /* we increment the destination pointer by 2 because the subindex 0 will be + transmitted as UINT16 for a complete access */ + pData++; + } + else + { + UINT16 dataType = pEntry->DataType; + if (pEntry->DataType >= 0x700) + { + /* the ENUM data types are defined from index 0x700 in this example + convert in standard data type for the read access */ + if ( pEntry->BitLength <= 8 ) + dataType = DEFTYPE_BIT1-1+pEntry->BitLength; + else if ( pEntry->BitLength == 16 ) + dataType = DEFTYPE_UNSIGNED16; + else if ( pEntry->BitLength == 32 ) + dataType = DEFTYPE_UNSIGNED32; + } + + switch (dataType) + { + case DEFTYPE_NULL: + if(bCompleteAccess) + { + /*Handle alignment entry*/ + if (((pEntry->BitLength & 0xF) > 0) + && (((bitOffset + pEntry->BitLength) & 0x0F) == 0 )) + { + /* we have reached the UINT16 border */ + pData++; + } + + /*increment WORD offset*/ + pData += ((pEntry->BitLength & 0xF0) >> 4); + } + else + { + return ABORTIDX_DATA_CANNOT_BE_READ_OR_STORED; + } + break; + case DEFTYPE_BOOLEAN: + case DEFTYPE_BIT1: + case DEFTYPE_BIT2: + case DEFTYPE_BIT3: + case DEFTYPE_BIT4: + case DEFTYPE_BIT5: + case DEFTYPE_BIT6: + case DEFTYPE_BIT7: + case DEFTYPE_BIT8: + case DEFTYPE_BITARR8: + /* in this example the objects are defined in that way, + that the bit types are always inside a 16-bit field, + and they shall not overlap a byte border*/ + case DEFTYPE_INTEGER8: + case DEFTYPE_UNSIGNED8: +/*ECATCHANGE_START(V5.11) SDO9*/ + case DEFTYPE_BYTE : +/*ECATCHANGE_END(V5.11) SDO9*/ + { + /* depending on the bitOffset we have to copy the Hi or the Lo-Byte */ + UINT16 TmpValue = 0x0000; + + bitMask = cBitMask[pEntry->BitLength] << (bitOffset & 0x0F); + + /*Swap object data (if required); all masks and offsets are defined for little endian format*/ + TmpValue = SWAPWORD(pVarPtr[0]); + + /*Clear pData if the first bits within the WORD memory will be copied*/ + if ((bitOffset & 0x0F) == 0) + { + pData[0] = 0; + } + +/* ECATCHANGE_START(V5.11) SDO7*/ + pData[0] = SWAPWORD(pData[0]); + + if (bCompleteAccess) + { + /*shifting is not required for Complete access because the bits are set to the correct offset by the master*/ + pData[0] |= TmpValue & bitMask; + } + else + { + /*Shift Bits to the beginning of the mailbox memory*/ + pData[0] |= ((TmpValue & bitMask) >> (bitOffset & 0x0F)); + } + + pData[0] = SWAPWORD(pData[0]); +/* ECATCHANGE_END(V5.11) SDO7*/ + if ( ((bitOffset + pEntry->BitLength) & 0x0F) == 0 ) + { + /* we have reached the UINT16 border */ + pData++; + } + + } + break; + case DEFTYPE_INTEGER16: + case DEFTYPE_UNSIGNED16: + case DEFTYPE_BITARR16: +/*ECATCHANGE_START(V5.11) SDO9*/ + case DEFTYPE_WORD: +/*ECATCHANGE_END(V5.11) SDO9*/ +/*ECATCHANGE_START(V5.11) SDO1*/ + if(bitOffset & 0xF) + { + /* return an error in case of an odd word offset (to support 16bit data type on odd word addresses an object specific access function need to be implemented and register in the object dictionary, see SSC Application Note)*/ + return ABORTIDX_UNSUPPORTED_ACCESS; + } +/*ECATCHANGE_END(V5.11) SDO1*/ + /* in this example the objects are defined in that way, + that the 16 bit type are always starting at an exact WORD offset */ + pData[0] = SWAPWORD(pVarPtr[0]); + pData++; + break; + case DEFTYPE_UNSIGNED32: + case DEFTYPE_INTEGER32: + case DEFTYPE_REAL32: + case DEFTYPE_BITARR32: +/*ECATCHANGE_START(V5.11) SDO9*/ + case DEFTYPE_DWORD: +/*ECATCHANGE_END(V5.11) SDO9*/ +/*ECATCHANGE_START(V5.11) SDO1*/ + if(bitOffset & 0xF) + { + /* return an error in case of an odd word offset (to support 16bit data type on odd word addresses an object specific access function need to be implemented and register in the object dictionary, see SSC Application Note)*/ + return ABORTIDX_UNSUPPORTED_ACCESS; + } +/*ECATCHANGE_END(V5.11) SDO1*/ + + /* in this example the objects are defined in that way, + that the 32 bit type are always starting at an exact WORD offset */ + pData[0] = pVarPtr[0]; + pData[1] = pVarPtr[1]; + pData += 2; + break; + case DEFTYPE_REAL64: + case DEFTYPE_INTEGER64: + case DEFTYPE_UNSIGNED64: +/*ECATCHANGE_START(V5.11) SDO1*/ + if(bitOffset & 0xF) + { + /* return an error in case of an odd word offset (to support 16bit data type on odd word addresses an object specific access function need to be implemented and register in the object dictionary, see SSC Application Note)*/ + return ABORTIDX_UNSUPPORTED_ACCESS; + } +/*ECATCHANGE_END(V5.11) SDO1*/ + + /* in this example the objects are defined in that way, + that the 64 bit type are always starting at an exact WORD offset */ + pData[0] = pVarPtr[0]; + pData[1] = pVarPtr[1]; + pData[2] = pVarPtr[2]; + pData[3] = pVarPtr[3]; + pData += 4; + break; + case DEFTYPE_OCTETSTRING: + case DEFTYPE_UNICODE_STRING: +/*ECATCHANGE_START(V5.11) SDO9*/ + case DEFTYPE_ARRAY_OF_INT : + case DEFTYPE_ARRAY_OF_SINT : + case DEFTYPE_ARRAY_OF_DINT : + case DEFTYPE_ARRAY_OF_UDINT: +/*ECATCHANGE_END(V5.11) SDO9*/ +/*ECATCHANGE_START(V5.11) SDO1*/ + if(bitOffset & 0xF) + { + /* return an error in case of an odd word offset (to support 16bit data type on odd word addresses an object specific access function need to be implemented and register in the object dictionary, see SSC Application Note)*/ + return ABORTIDX_UNSUPPORTED_ACCESS; + } +/*ECATCHANGE_END(V5.11) SDO1*/ + + OBJTOMBXMEMCPY(pData, pVarPtr, BIT2BYTE(pEntry->BitLength)); + +/*ECATCHANGE_START(V5.11) SDO2*/ + pData += BIT2WORD((pEntry->BitLength & ~0xF)); + + if((pEntry->BitLength & 0xF) != 0) + { + /*current entry has an odd word length => clear last byte of next word*/ + *pData &= 0xFF; + } +/*ECATCHANGE_END(V5.11) SDO2*/ + + break; + case DEFTYPE_VISIBLESTRING: +/*ECATCHANGE_START(V5.11) SDO1*/ + if(bitOffset & 0xF) + { + /* return an error in case of an odd word offset (to support 16bit data type on odd word addresses an object specific access function need to be implemented and register in the object dictionary, see SSC Application Note)*/ + return ABORTIDX_UNSUPPORTED_ACCESS; + } +/*ECATCHANGE_END(V5.11) SDO1*/ + + /* in this example the objects are defined in that way, + that these types are always starting at an even WORD offset */ + OBJTOMBXSTRCPY(pData, pVarPtr, BIT2BYTE(pEntry->BitLength)); +/*ECATCHANGE_START(V5.11) SDO2*/ + pData += BIT2WORD((pEntry->BitLength & ~0xF)); + + if((pEntry->BitLength & 0xF) != 0) + { + /*current entry has an odd word length => clear last byte of next word*/ + *pData &= 0xFF; + } +/*ECATCHANGE_END(V5.11) SDO2*/ + + break; + default: + /* other data types are not supported from this example */ + return ABORTIDX_DATA_CANNOT_BE_READ_OR_STORED; + } //switch (deftype) + + bRead = 1; + } + } + } + else + { + /*No access to current object entry => shift pData if required*/ + UINT8 cnt = 0; + + /*If this entry is the first in the 16Bit block clear the memory*/ + if ((bitOffset & 0x0F) == 0) + { + *pData = 0; + } + + /*Handle Bit/Byte Offset*/ + if (((pEntry->BitLength & 0xF) > 0) + && (((bitOffset + pEntry->BitLength) & 0x0F) == 0 )) + { + /* we have reached the UINT16 border */ + pData++; + + /*Clear new buffer*/ + *pData = 0; + } + + /*increment WORD offset*/ + for(cnt = 0; cnt <((pEntry->BitLength & 0xF0) >> 4); cnt++) + { + /*current 16Bit are skipped => clear current buffer */ + pData++; + + /*Clear new buffer*/ + *pData = 0; + } + + + + + /* we don't have read access */ + if ( (pEntry->ObjAccess & ACCESS_READ) == 0 ) + { + /* it is a write only entry */ + result = ABORTIDX_WRITE_ONLY_ENTRY; + } + else + { + /* we don't have read access in this state */ + result = ABORTIDX_IN_THIS_STATE_DATA_CANNOT_BE_READ_OR_STORED; + } + } + } + + if(bRead == 0) + return result; + } + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param index index of the requested object. + \param subindex subindex of the requested object. + \param dataSize received data size of the SDO Download + \param pObjEntry handle to the dictionary object returned by + OBJ_GetObjectHandle which was called before + \param pData Pointer to the buffer where the written data can be copied from + \param bCompleteAccess Indicates if a complete write of all subindices of the + object shall be done or not + + \return result of the write operation (0 (success) or an abort code (ABORTIDX_.... defined in + sdosrv.h)) + + \brief This function writes the requested object +*//////////////////////////////////////////////////////////////////////////////////////// + +UINT8 OBJ_Write( UINT16 index, UINT8 subindex, UINT32 dataSize, OBJCONST TOBJECT OBJMEM * pObjEntry, UINT16 MBXMEM * pData, UINT8 bCompleteAccess ) +{ + UINT16 i = subindex; + /* get the information of ObjCode and MaxSubindex in local variables to support different types of microcontroller */ + UINT8 objCode = (pObjEntry->ObjDesc.ObjFlags & OBJFLAGS_OBJCODEMASK) >> OBJFLAGS_OBJCODESHIFT; + UINT16 maxSubindex = 0; + UINT16 maxConfiguredSubindex = (pObjEntry->ObjDesc.ObjFlags & OBJFLAGS_MAXSUBINDEXMASK) >> OBJFLAGS_MAXSUBINDEXSHIFT; //required to check if value for Subindex0 is valid + OBJCONST TSDOINFOENTRYDESC OBJMEM *pEntry; + BOOL bClearSubindex0Required = FALSE; + /* lastSubindex is used for complete access to make loop over the requested entries + to be read, we initialize this variable with the requested subindex that only + one loop will be done for a single access */ + UINT16 lastSubindex = subindex; + + /* if subindex 0 is writable, the maximum subindex should be checked in an object specific function, + because for the PDO mapping and PDO assign the object shall only be written if subindex 0 is 0. */ + if ( objCode != OBJCODE_VAR ) + { + /* if the object is an array or record we have to get the maxSubindex from the + actual value of subindex 0, which is stored as UINT16 at the beginning of the + object's variable */ + maxSubindex = (UINT8) ((UINT16 MBXMEM *) (pObjEntry->pVarPtr))[0]; + + /*If the subindex0 of a PDO assign or PDO mapping object is 0 the maximum subindex is specified by the object description*/ + if(maxSubindex == 0 && (IS_PDO_ASSIGN(index) || IS_RX_PDO(index) || IS_TX_PDO(index))) + { + maxSubindex = maxConfiguredSubindex; + } + } + + + if ( bCompleteAccess ) + { + if ( objCode == OBJCODE_VAR ) + /* complete access is not supported with simple objects */ + return ABORTIDX_UNSUPPORTED_ACCESS; + + if ((subindex == 0) && (dataSize > 0)) + { + /* we change the subindex 0 */ + maxSubindex = (UINT8) SWAPWORD(pData[0]); + } + + /* we write until the maximum subindex */ + lastSubindex = maxSubindex; + } + else + if (subindex > maxSubindex) + { + /* the maximum subindex is reached */ + return ABORTIDX_SUBINDEX_NOT_EXISTING; + } + else + { + /* we check the write access for single accesses here, a complete write access + is allowed if at least one entry is writable (in this case the values for the + read only entries shall be ignored) */ + /* we get the corresponding entry description */ + pEntry = OBJ_GetEntryDesc(pObjEntry, subindex); + + /* check if we have write access (bits 3-5 (PREOP, SAFEOP, OP) of ObjAccess) + by comparing with the actual state (bits 1-3 (PREOP, SAFEOP, OP) of AL Status) */ + if ( ((UINT8) ((pEntry->ObjAccess & ACCESS_WRITE) >> 2)) < (nAlStatus & STATE_MASK) ) + { + /* we don't have write access */ + if ( (pEntry->ObjAccess & ACCESS_WRITE) == 0 ) + { + /* it is a read only entry */ + return ABORTIDX_READ_ONLY_ENTRY; + } + else + { + /* we don't have write access in this state */ + return ABORTIDX_IN_THIS_STATE_DATA_CANNOT_BE_READ_OR_STORED; + } + } + } + + /* Subindex 0 shall be set to zero if a single PDO / PDO assign entry is written + or a complete access without subindex0 is requested */ + if((subindex > 0) && + (IS_PDO_ASSIGN(index) || IS_RX_PDO(index)|| IS_TX_PDO(index)) + ) + { + /*Check if Subindex0 was cleared before*/ + UINT16 Subindex0 = (*(UINT16 *)pObjEntry->pVarPtr) & 0x00FF; + if(Subindex0 != 0x00) + bClearSubindex0Required = TRUE; + } + + if ( pObjEntry->Write != NULL ) + { + /* Write function is defined, we call the object specific write function */ + return pObjEntry->Write(index, subindex, dataSize, pData, bCompleteAccess); + } + else + { + UINT8 bWritten = 0; + UINT8 result = ABORTIDX_READ_ONLY_ENTRY; + +/*ECATCHANGE_START(V5.11) ECAT*/ + if (dataSize == 0) + { + return 0; //no error + } +/*ECATCHANGE_END(V5.11) ECAT*/ + + /* we use the standard write function */ + for (i = subindex; i <= lastSubindex; i++) + { + /* if only a single entry is requested, this loop will only be done once */ + UINT16 MBXMEM *pVarPtr = (UINT16 MBXMEM *) pObjEntry->pVarPtr; + UINT16 bitOffset = 0; + + /* we get the corresponding entry description */ + pEntry = OBJ_GetEntryDesc(pObjEntry,(UINT8) i); + + /*Get the bitOffset before check the access rights to calculate pData offset*/ + bitOffset = OBJ_GetEntryOffset((UINT8)i, pObjEntry); + + /* we check if we have write access (bits 3-5 (PREOP, SAFEOP, OP) of ObjAccess) + by comparing with the actual state (bits 1-3 (PREOP, SAFEOP, OP) of AL Status) */ + if ( ((UINT8)((pEntry->ObjAccess & ACCESS_WRITE) >> 2)) >= (nAlStatus & STATE_MASK) ) + { + /* we have write access for this entry */ + if (i != 0) + { + /* we increment the variable pointer to the corresponding word address */ + pVarPtr += (bitOffset >> 4); + } + + + if ( i == subindex /* requested entry */ + || (bCompleteAccess && i >= subindex) ) /* complete access and entry should be read */ + { + UINT16 bitMask; + + /* we have to copy the entry */ + if (i == 0 && objCode != OBJCODE_VAR) + { + /*check if the value for subindex0 is valid */ + UINT8 NewSubindex0 = (UINT8) SWAPWORD(pData[0]); + if(maxConfiguredSubindex < NewSubindex0) + { + return ABORTIDX_VALUE_TOO_GREAT; + } + + /* subindex 0 of an array or record shall be written */ +/* ECATCHANGE_START(V5.11) SDO5*/ + pVarPtr[0] = SWAPWORD(pData[0]); +/* ECATCHANGE_END(V5.11) SDO5*/ + /* we increment the destination pointer by 2 because the subindex 0 will be + transmitted as UINT16 for a complete access */ + pData++; + } + else + { + UINT16 dataType = pEntry->DataType; + if (pEntry->DataType >= 0x700) + { + /* the ENUM data types are defined from index 0x700 in this example + convert in standard data type for the write access */ + if ( pEntry->BitLength <= 8 ) + dataType = DEFTYPE_BIT1-1+pEntry->BitLength; + else if ( pEntry->BitLength == 16 ) + dataType = DEFTYPE_UNSIGNED16; + else if ( pEntry->BitLength == 32 ) + dataType = DEFTYPE_UNSIGNED32; + } + + switch (dataType) + { + case DEFTYPE_NULL: + if(bCompleteAccess) + { + /*Handle alignment entry*/ + if (((pEntry->BitLength & 0xF) > 0) + && (((bitOffset + pEntry->BitLength) & 0x0F) == 0 )) + { + /* we have reached the UINT16 border */ + pData++; + } + + /*increment WORD offset*/ + pData += ((pEntry->BitLength & 0xF0) >> 4); + } + else + { + return ABORTIDX_DATA_CANNOT_BE_READ_OR_STORED; + } + break; + case DEFTYPE_BOOLEAN: + case DEFTYPE_BIT1: + case DEFTYPE_BIT2: + case DEFTYPE_BIT3: + case DEFTYPE_BIT4: + case DEFTYPE_BIT5: + case DEFTYPE_BIT6: + case DEFTYPE_BIT7: + case DEFTYPE_BIT8: + case DEFTYPE_BITARR8: + /* in this example the objects are defined in that way, + that the bit types are always inside a 16-bit field, + and shall not overlap a byte border*/ + case DEFTYPE_INTEGER8: + case DEFTYPE_UNSIGNED8: +/*ECATCHANGE_START(V5.11) SDO9*/ + case DEFTYPE_BYTE : +/*ECATCHANGE_END(V5.11) SDO9*/ + { + /* depending on the bitOffset we have to copy the Hi or the Lo-Byte */ + UINT16 TmpValue = 0x0000; + + bitMask = cBitMask[pEntry->BitLength] << (bitOffset & 0x0F); + + /*Swap object data (if required); all masks and offsets are defined for little endian format*/ + TmpValue = SWAPWORD(pVarPtr[0]); + + /*Clear corresponding bits*/ + TmpValue &= ~bitMask; + +/* ECATCHANGE_START(V5.11) SDO7*/ + if (bCompleteAccess) + { + /*shifting is not required for Complete access because the bits are set to the correct offset by the master*/ + TmpValue |= (SWAPWORD(pData[0]) & bitMask); + } + else + { + if((SWAPWORD(pData[0]) & ~cBitMask[pEntry->BitLength])) + { + /*written value exceed entry range*/ + return ABORTIDX_VALUE_EXCEEDED; + } + else + { + /*Shift Bits to corresponding offset within the object memory*/ + TmpValue |= ((SWAPWORD(pData[0]) & cBitMask[pEntry->BitLength]) << (bitOffset & 0x0F)); + } + } +/* ECATCHANGE_END(V5.11) SDO7*/ + + /*Swap written data to big endian format (if required)*/ + pVarPtr[0] = SWAPWORD(TmpValue); + + if ( ((bitOffset+pEntry->BitLength) & 0x0F) == 0 ) + /* we have reached the UINT16 border */ + pData++; + } + break; + case DEFTYPE_INTEGER16: + case DEFTYPE_UNSIGNED16: + case DEFTYPE_BITARR16: +/*ECATCHANGE_START(V5.11) SDO9*/ + case DEFTYPE_WORD: +/*ECATCHANGE_END(V5.11) SDO9*/ +/*ECATCHANGE_START(V5.11) SDO1*/ + if(bitOffset & 0xF) + { + /* return an error in case of an odd word offset (to support 16bit data type on odd word addresses an object specific access function need to be implemented and register in the object dictionary, see SSC Application Note)*/ + return ABORTIDX_UNSUPPORTED_ACCESS; + } +/*ECATCHANGE_END(V5.11) SDO1*/ + + { + /* in this example the objects are defined in that way, + that the 16 bit type are always starting at an exact WORD offset */ + UINT16 u16NewData = SWAPWORD(pData[0]); + if(bClearSubindex0Required && (pVarPtr[0] != u16NewData)) + { + /* try to write new data when subindex0 shall be reset*/ + return ABORTIDX_ENTRY_CANT_BE_WRITTEN_SI0_NOT_0; + } + + /*check value if a new PDO assign entry should be written*/ + if(IS_PDO_ASSIGN(index)) //PDO assign + { + if (!IS_RX_PDO(u16NewData) && !IS_TX_PDO(u16NewData) && (u16NewData != 0)) //check if the new assign entry value is valid + { + return ABORTIDX_VALUE_EXCEEDED; + } + } + + if((index == 0x1C32 || index == 0x1C33) && (i == 1)) + { + /* The Synchronisation type (0x1C3x.1) was written by the user => the Sync type will not be calculated based on the register settings (if they don't match an error will be returned P_2_S)*/ + if(pVarPtr[0] != u16NewData) + { + result = CheckSyncTypeValue(index,u16NewData); + + if(result != 0) + return result; + } + + /* The user may force to current Sync Mode for that reason the flag has also to be set if the same value was written */ + bSyncSetByUser = TRUE; + } + + pVarPtr[0] = u16NewData; + pData++; + } + break; + case DEFTYPE_UNSIGNED32: + case DEFTYPE_INTEGER32: + case DEFTYPE_REAL32: + case DEFTYPE_BITARR32: +/*ECATCHANGE_START(V5.11) SDO9*/ + case DEFTYPE_DWORD: +/*ECATCHANGE_END(V5.11) SDO9*/ +/*ECATCHANGE_START(V5.11) SDO1*/ + if(bitOffset & 0xF) + { + /* return an error in case of an odd word offset (to support 16bit data type on odd word addresses an object specific access function need to be implemented and register in the object dictionary, see SSC Application Note)*/ + return ABORTIDX_UNSUPPORTED_ACCESS; + } +/*ECATCHANGE_END(V5.11) SDO1*/ + { + + /* in this example the objects are defined in that way, + that the 32 bit type are always starting at an exact WORD offset */ + if(bClearSubindex0Required && + ((pVarPtr[0] != pData[0]) + || (pVarPtr[1] != pData[1]))) + { + /* try to write new data when subindex0 shall be reset*/ + return ABORTIDX_ENTRY_CANT_BE_WRITTEN_SI0_NOT_0; + } + + pVarPtr[0] = pData[0]; + pVarPtr[1] = pData[1]; + pData += 2; + } + break; + case DEFTYPE_REAL64: + case DEFTYPE_INTEGER64: + case DEFTYPE_UNSIGNED64: +/*ECATCHANGE_START(V5.11) SDO1*/ + if(bitOffset & 0xF) + { + /* return an error in case of an odd word offset (to support 16bit data type on odd word addresses an object specific access function need to be implemented and register in the object dictionary, see SSC Application Note)*/ + return ABORTIDX_UNSUPPORTED_ACCESS; + } +/*ECATCHANGE_END(V5.11) SDO1*/ + /* in this example the objects are defined in that way, + that the 64 bit type are always starting at an exact WORD offset */ + pVarPtr[0] = pData[0]; + pVarPtr[1] = pData[1]; + pVarPtr[2] = pData[2]; + pVarPtr[3] = pData[3]; + pData += 4; + break; + case DEFTYPE_VISIBLESTRING: +/*ECATCHANGE_START(V5.11) SDO1*/ + if(bitOffset & 0xF) + { + /* return an error in case of an odd word offset (to support 16bit data type on odd word addresses an object specific access function need to be implemented and register in the object dictionary, see SSC Application Note)*/ + return ABORTIDX_UNSUPPORTED_ACCESS; + } +/*ECATCHANGE_END(V5.11) SDO1*/ + + OBJTOMBXSTRCPY(pVarPtr, pData, BIT2BYTE(pEntry->BitLength)); + +/*ECATCHANGE_START(V5.11) SDO2*/ + pData += BIT2WORD((pEntry->BitLength)& ~0xF); +/*ECATCHANGE_END(V5.11) SDO2*/ + break; + case DEFTYPE_OCTETSTRING: + case DEFTYPE_UNICODE_STRING: +/*ECATCHANGE_START(V5.11) SDO9*/ + case DEFTYPE_ARRAY_OF_INT : + case DEFTYPE_ARRAY_OF_SINT : + case DEFTYPE_ARRAY_OF_DINT : + case DEFTYPE_ARRAY_OF_UDINT: +/*ECATCHANGE_END(V5.11) SDO9*/ +/*ECATCHANGE_START(V5.11) SDO1*/ + if(bitOffset & 0xF) + { + /* return an error in case of an odd word offset (to support 16bit data type on odd word addresses an object specific access function need to be implemented and register in the object dictionary, see SSC Application Note)*/ + return ABORTIDX_UNSUPPORTED_ACCESS; + } +/*ECATCHANGE_END(V5.11) SDO1*/ + + /* in this example the objects are defined in that way, + that the other types are always starting at an even byte offset */ + OBJTOMBXMEMCPY(pVarPtr, pData, BIT2BYTE(pEntry->BitLength)); +/*ECATCHANGE_START(V5.11) SDO2*/ + pData += BIT2WORD((pEntry->BitLength) & ~0xF); +/*ECATCHANGE_END(V5.11) SDO2*/ + + break; + default: + /* other data types are not supported from this example */ + return ABORTIDX_DATA_CANNOT_BE_READ_OR_STORED; + } + } + + /* set flag */ + bWritten = 1; + + } + } + else + { + if(i == 0) + { + /* For SubIndex0 16Bit are reserved even if the BitLength is 8 */ + pData++; + } + else + { + /*Handle Bit/Byte Offset*/ + if (((pEntry->BitLength & 0xF) > 0) + && (((bitOffset + pEntry->BitLength) & 0x0F) == 0 )) + { + /* we have reached the UINT16 border */ + pData++; + } + + /*increment WORD offset*/ +/*ECATCHANGE_START(V5.11) SDO9*/ + pData += ((pEntry->BitLength & 0xFFF0) >> 4); +/*ECATCHANGE_END(V5.11) SDO9*/ + } + /*If no other entry was written this result will be returned*/ + result = ABORTIDX_DATA_CANNOT_BE_READ_OR_STORED; + } + } + + if (bWritten == 0) + /* we didn't write anything, so we have to return the stored error code */ + return result; + } + + return 0; +} +/** @} */ + + + diff --git a/GD32E508_DCU_APP/comm/ssc/src/sdoserv.c b/GD32E508_DCU_APP/comm/ssc/src/sdoserv.c new file mode 100644 index 0000000..d178a02 --- /dev/null +++ b/GD32E508_DCU_APP/comm/ssc/src/sdoserv.c @@ -0,0 +1,1261 @@ +/** +\addtogroup CoE CAN Application Profile over EtherCAT +@{ +*/ + +/** +\file sdoserv.c +\author EthercatSSC@beckhoff.com +\brief Implementation +The SDO server handles all sdo and sdo information services + +\version 5.11 + +
Changes to version V5.10:
+V5.11 ECAT10: change PROTO handling to prevent compiler errors
+V5.11 ECAT7: add missing big endian swapping
+V5.11 SDO10: add new SDO abort code 0x6010004 (complete access not supported)
+V5.11 SDO4: "if insufficient memory is available on complete download access return ""unsupported access"""
+
Changes to version V5.01:
+V5.10 ESC5: Add missing swapping
+V5.10 MBX1: Remove multiple swapping of length filed in mailbox header
+V5.10 SDO3: Add new SDO Abort Code (0x06090033)
+V5.10 SDO4: Block SDO Info services for indices less 0x1000
+V5.10 SDO7: Correct mailbox length calculation on segmented or normal SDO upload response
+V5.10 SDO8: Fix invalid fragment calculation on SdoInfo list response
+V5.10 TEST5: test 0x2020.1 change limit from 10 to 16 Byte
+ Add test object 0x3009/0x300A (huge array and record objects)
+
Changes to version V5.0:
+V5.01 MBX1: Allocate always complete 16Bit memory areas
+V5.01 SDO2: Prevent invalid memory access in case of 8Bit mailbox memory handling
+V5.01 SDO3: Update mailbox data length calculation
+V5.01 SDO4: Update length calculation in case of an segmented SDO list response
+V5.01 SDO6: Update SDO response interface handling. (used if the object access function returns "ABORTIDX_WORKING" and SDO_RES_INTERFACE is active)
+V5.01 TEST2: Change Test 0x2020.1 (force Segmented transfer) to 16Byte
+
Changes to version V4.40:
+V5.0 TEST1: Add test application. see Application Note ET9300 for more details.
+V5.0 MBX3: Calculate MBX datagram length independent of SM size.
+V5.0 SDO2: SDO toggle bit don not do be cleared for segmented communication.
+V5.0 SDO3: Set SDO OpCode in list response.
+V5.0 SDO4: Update abort code for "BIG_ENDIAN_16BIT" configuration.
+V5.0 SDO5: Object code was calculated wrong for SDO Info service.
+V5.0 SDO6: Handle SDO segmented transfer if only 16Bit MBX memory access is allowed.
+
Changes to version V4.20:
+V4.40 SDO1: change size calculation for SDO services
+V4.40 MBX1: Prevent accessing odd address
+V4.40 COE1: Abort code is set in OBJ_GetObjectList()
+
Changes to version V4.11:
+V4.20 PIC24: Add EL9800_4 (PIC24) required source code
+V4.20 SDO 2: SDO mask value bug
+V4.20 SDO 1: check zero size object length
+
Changes to version V4.10:
+V4.11 SDO 1: fixed calculation of frame fragments during a object dictionary array list request
+V4.11 SDO 2-3: fixed size of entry description
+V4.11 SDO 4-7: add STRUCT_PACKED defines
+
Changes to version V4.08:\a
+V4.10 SDO 1: fixed zero size SDO comparison
+V4.10 SDO 2: set SdoService_CommandOffset to 0
+V4.10 SDO 3: fixed zero size SDO comparison
+V4.10 SDO 4: fixed struct_packed definition
+
Changes to version V4.07:
+V4.08 SDO 1: For an upload segment response the toggle bit was overwritten
+V4.08 SDO 2: For a segmented response the command was wrong in the response
+
Changes to version V4.06:
+V4.07 SDO 1: In SdoRes the command specifier was not set correctly in case of an abort
+V4.07 ECAT 1: The sources for SPI and MCI were merged (in ecat_def.h
+ set the switch MCI_HW to 1 when using the MCI,
+ set the switch SPI_HW to 1 when using the SPI
+
Changes to version V4.05:
+V4.06 SDO 1: The variable dataSize was used wrong in function SdoRes
+
Changes to version V4.03:
+V4.04 SDO 1: The SDO interface was changed in that way that a SDO response
+ could be sent by the application to a later time. In that case
+ the functions OBJ_Read and OBJ_Write shall return the value
+ ABORTIDX_WORKING. To send the SDO response the new function SDOS_SdoRes
+ has to be called by the application. While waiting for the call
+ of SDOS_SdoRes the SDO interface will answer to another SDO request
+ with the error MBXERR_SERVICEINWORK in the mailbox protocol
+
Changes to version V3.20:
+V4.00 SDO 1: The size of the written data in case of a SDO Download will be
+ in the function OBJ_Write to be more flexible
+V4.00 SDO 2: The object lists will be generated in the functions OBJ_GetNoOfObjects
+ and OBJ_GetObjectList in objdef.c to decouple the SDO services from
+ the implementation of the object dictionary
+V4.00 SDO 3: The name of an object or entry description will only be transmitted
+ if it fits in the mailbox because the fragmentation is not supported in the sample code.
+V4.00 SDO 4: SDOs with size greater than 65535 were not handled correctly, that is fixed now +*/ + +/*--------------------------------------------------------------------------------------- +------ +------ Includes +------ +---------------------------------------------------------------------------------------*/ + +#include "ecat_def.h" + +#include "ecatslv.h" + +#define _SDOSERV_ 1 +#include "objdef.h" +#undef _SDOSERV_ +/* ECATCHANGE_START(V5.11) ECAT10*/ +/*remove definition of _SDOSERV_ (#ifdef is used in objdef.h)*/ +/* ECATCHANGE_END(V5.11) ECAT10*/ + + + + +extern OBJCONST TOBJECT OBJMEM asObjDef[]; +extern UINT16 OBJ_GetDesc( UINT16 index, UINT8 subindex, OBJCONST TOBJECT OBJMEM * pObjEntry, UINT16 MBXMEM * pData ); +extern OBJCONST TSDOINFOENTRYDESC OBJMEM * OBJ_GetEntryDesc(OBJCONST TOBJECT OBJMEM * pObjEntry, UINT8 Subindex); +extern OBJCONST TSDOINFOOBJDESC OBJMEM * OBJ_GetObjDesc(OBJCONST TOBJECT OBJMEM * pObjEntry); + +/*--------------------------------------------------------------------------------------- +------ +------ Modulintern variable definitions +------ +---------------------------------------------------------------------------------------*/ + +const UINT32 MBXMEM cAbortCode[] = +{ + ABORT_NOERROR, + ABORT_TOGGLE_BIT_NOT_CHANGED, + ABORT_SDO_PROTOCOL_TIMEOUT, + ABORT_COMMAND_SPECIFIER_UNKNOWN, + ABORT_OUT_OF_MEMORY, + ABORT_UNSUPPORTED_ACCESS, + ABORT_WRITE_ONLY_ENTRY, + ABORT_READ_ONLY_ENTRY, + ABORT_OBJECT_NOT_EXISTING, + ABORT_OBJECT_CANT_BE_PDOMAPPED, + ABORT_MAPPED_OBJECTS_EXCEED_PDO, + ABORT_PARAM_IS_INCOMPATIBLE, + ABORT_INTERNAL_DEVICE_INCOMPATIBILITY, + ABORT_HARDWARE_ERROR, + ABORT_PARAM_LENGTH_ERROR, + ABORT_PARAM_LENGTH_TOO_LONG, + ABORT_PARAM_LENGTH_TOO_SHORT, + ABORT_SUBINDEX_NOT_EXISTING, + ABORT_VALUE_EXCEEDED, + ABORT_VALUE_TOO_GREAT, + ABORT_VALUE_TOO_SMALL, + ABORT_MODULE_ID_LIST_NOT_MATCH, + ABORT_MAX_VALUE_IS_LESS_THAN_MIN_VALUE, + ABORT_GENERAL_ERROR, + ABORT_DATA_CANNOT_BE_READ_OR_STORED, + ABORT_DATA_CANNOT_BE_READ_OR_STORED_BECAUSE_OF_LOCAL_CONTROL, + ABORT_DATA_CANNOT_BE_READ_OR_STORED_IN_THIS_STATE, + ABORT_NO_OBJECT_DICTIONARY_IS_PRESENT, + ABORT_ENTRY_CANT_BE_WRITTEN_SI0_NOT_0, +/* ECATCHANGE_START(V5.11) SDO10*/ + ABORT_COMPLETE_ACCESS_NOT_SUPPORTED +/* ECATCHANGE_END(V5.11) SDO10*/ +}; + +UINT16 VARMEM nSdoInfoIndex; +OBJCONST TOBJECT OBJMEM * VARMEM pSdoInfoObjEntry; + +TINITSDOMBX MBXMEM * VARMEM pSdoResStored; +BOOL VARMEM bSdoInWork = FALSE; + +UINT8 VARMEM nSdoSegService; +UINT8 VARMEM bSdoSegFollows; +UINT8 VARMEM bSdoSegAccess; +UINT16 VARMEM nSdoSegIndex; +UINT8 VARMEM nSdoSegSubindex; +UINT32 VARMEM nSdoSegBytesToHandle; +UINT8 VARMEM bSdoSegLastToggle; +UINT32 VARMEM nSdoSegCompleteSize; +OBJCONST TOBJECT OBJMEM * VARMEM pSdoSegObjEntry; + +/*--------------------------------------------------------------------------------------- +------ +------ module internal function declarations +------ +---------------------------------------------------------------------------------------*/ +static UINT8 SdoDownloadSegmentInd(TDOWNLOADSDOSEGREQMBX MBXMEM * pSdoInd); +static UINT8 SdoUploadSegmentInd(TUPLOADSDOSEGREQMBX MBXMEM * pSdoInd); +/*--------------------------------------------------------------------------------------- +------ +------ Functions +------ +---------------------------------------------------------------------------------------*/ + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param pSdoInd Pointer to the received mailbox data from the master. + + \return Indicates if an error occurred while the operation ( good = 0 ). + + \brief This function is called when a Download SDO Segment request + service is received from the master. If its the last segment + the data will be written to the object dictionary. The + function sends a response by itself. +*//////////////////////////////////////////////////////////////////////////////////////// + +static UINT8 SdoDownloadSegmentInd( TDOWNLOADSDOSEGREQMBX MBXMEM * pSdoInd ) +{ + UINT8 abort = 0; + UINT32 bytesToSave = 0; + + if ( (pSdoInd->SdoHeader.SegHeader & SEGHEADER_TOGGLE) == bSdoSegLastToggle ) + { + /* toggle bit has not toggled... */ + abort = ABORTIDX_TOGGLE_BIT_NOT_CHANGED; + } + else + { + /* maxData contains the maximum data to be received with a SDO-DownloadSegment */ + UINT16 maxData = u16ReceiveMbxSize - MBX_HEADER_SIZE - SEGMENT_NORM_HEADER_SIZE; + /* the new toggle bit is stored in bSdoSegLastToggle */ + bSdoSegLastToggle = pSdoInd->SdoHeader.SegHeader & SEGHEADER_TOGGLE; + + /* a SDO-Download Segment is only allowed if a SDO-Download Request was received before, + in that case a buffer for the received data was allocated in SDOS_SdoInd before */ + if ( pSdoSegData ) + { + /* bytesToSave contains the remaining data with this and maybe the following + SDO-Download Segment services */ + bytesToSave = nSdoSegCompleteSize - nSdoSegBytesToHandle; + + if ( pSdoInd->SdoHeader.SegHeader & SEGHEADER_NOMOREFOLLOWS ) + { + /* the last segment is received, check if the length of the remaining data is the + same as the length of the received data */ + if ( bytesToSave <= maxData ) + { +/* ECATCHANGE_START(V5.11) ECAT7*/ + UINT16 mbxSize = SWAPWORD(pSdoInd->MbxHeader.Length); + + /* for the check it is distinguished if the remaining bytes are less than 8 (in that + case 7 data bytes were sent and the SDO-Download Segment header contains the information + how much bytes are valid (CAN-compatibility)), otherwise the length has to match exactly + and the SDO-Download Segment-Headerbyte is ignored */ + if (((bytesToSave <= (UINT32)(mbxSize - SEGMENT_NORM_HEADER_SIZE)) + &&( bytesToSave == ((UINT16) (MIN_SEGMENTED_DATA - ((pSdoInd->SdoHeader.SegHeader & SEGHEADER_SEGDATASIZE) >> SEGHEADERSHIFT_SEGDATASIZE))) ) + ) + ||( ( bytesToSave > MIN_SEGMENTED_DATA ) + && (bytesToSave == (mbxSize - SEGMENT_NORM_HEADER_SIZE)) + ) ) +/* ECATCHANGE_END(V5.11) ECAT7*/ + { + /* length is correct */ + bSdoSegFollows = FALSE; + } + else + abort = ABORTIDX_PARAM_LENGTH_ERROR; + } + else + abort = ABORTIDX_PARAM_LENGTH_ERROR; + } + else + { + /* its not the last segment */ + bSdoSegFollows = TRUE; + /* we have to check if we expect less bytes than the maximum size which can be send with a single + SDO Download Segment */ + if ( bytesToSave <= maxData ) + abort = ABORTIDX_PARAM_LENGTH_ERROR; + else + /* length is okay, bytesToSave contains the data size to be copied */ + bytesToSave = maxData; + } + + if ( abort == 0 ) + { + /* the received data is copied in the buffer */ + UINT16 i = 0; + + if ((nSdoSegBytesToHandle & 0x1) == 0x01) + { + // Data0 will be copied to an odd address of pSdoSegData, Data1 to an even address (Segment 2, 4, ...) + // Read Data0 + pSdoSegData[(nSdoSegBytesToHandle >> 1)] &= ~SEGHDATA_MASK; + pSdoSegData[(nSdoSegBytesToHandle >> 1)] |= (pSdoInd->SdoHeader.SegHeader & SEGHDATA_MASK); + // Copy Data1 - DataN + MBXMEMCPY(&pSdoSegData[(nSdoSegBytesToHandle >> 1) + 1],pSdoInd->SdoHeader.Data, bytesToSave - 1 ); + } + else + { // Data0 will be copied to an even address of pSdoSegData, Data1 to an odd address (Segment 1, 3,...) + // Read Data0 and Data1 + pSdoSegData[( nSdoSegBytesToHandle >> 1)] = ((pSdoInd->SdoHeader.SegHeader >> SEGDATASHIFT) &~SEGHDATA_MASK) | ((pSdoInd->SdoHeader.Data[0] << 8) & SEGHDATA_MASK); + + // Copy Data2 - DataN + for (i = 1; i <((bytesToSave + 1) >> 1); i++) + { + pSdoSegData[(nSdoSegBytesToHandle >> 1) + i] = ((pSdoInd->SdoHeader.Data[i - 1] >> SEGDATASHIFT) & ~SEGHDATA_MASK) | ((pSdoInd->SdoHeader.Data[i] << 8) & SEGHDATA_MASK); + // (If bytesToSave is odd, one byte too much is copied. But, that is not a problem.) + } + } + + if ( bSdoSegFollows == FALSE ) + { + /* it was the last segment, OBJ_Write will called to make the Write-operation */ + abort = OBJ_Write( nSdoSegIndex, nSdoSegSubindex, nSdoSegCompleteSize, pSdoSegObjEntry, (UINT16 MBXMEM *) pSdoSegData, bSdoSegAccess ); + if ( abort == ABORTIDX_WORKING ) + { + /* the application generates the SDO-Response later on by calling SDOS_SdoRes (only possible if object access function pointer is defined) */ + u8PendingSdo = SDO_PENDING_SEG_WRITE; + bStoreCompleteAccess = bSdoSegAccess; + u8StoreSubindex = nSdoSegSubindex; + u16StoreIndex = nSdoSegIndex; + u32StoreDataSize = nSdoSegCompleteSize; + pStoreData = pSdoSegData; + + pSdoPendFunc = pSdoSegObjEntry->Write; + + bSdoInWork = TRUE; + pSdoResStored = (TINITSDOMBX MBXMEM *) pSdoInd; + + bSdoSegFollows = FALSE; + nSdoSegService = 0; + nSdoSegBytesToHandle = 0; + + return ABORTIDX_WORKING; + } + else + { + /* the allocated buffer can be released */ + FREEMEM( (UINT16 VARMEM *) pSdoSegData ); + pSdoSegData = NULL; + } + } + } + } + else + { + abort = ABORTIDX_COMMAND_SPECIFIER_UNKNOWN; + } + } + + if ( abort == 0) + { + /* send the SDO Download Segment response */ + pSdoInd->MbxHeader.Length = SEGMENT_NORM_RES_SIZE; + pSdoInd->CoeHeader &= ~COEHEADER_COESERVICEMASK; + pSdoInd->CoeHeader |= ((UINT16)COESERVICE_SDORESPONSE) << COEHEADER_COESERVICESHIFT; + /* the SDO Download Segment header depends if it was the last segment or not */ + if ( bSdoSegLastToggle ) + { + pSdoInd->SdoHeader.SegHeader = SDOSERVICE_DOWNLOADSEGMENTRES|SEGHEADER_TOGGLE; + } + else + pSdoInd->SdoHeader.SegHeader = SDOSERVICE_DOWNLOADSEGMENTRES; + + if ( bSdoSegFollows == TRUE ) + { + /* segments are still expected, nSdoSegBytesToHandle contains the number of received data bytes */ + nSdoSegBytesToHandle += bytesToSave; + } + else + { + /* the last segment was received, the variables are reset */ + nSdoSegBytesToHandle = 0; + nSdoSegService = 0; + } + } + else + { + /* the Abort-Response will be sent in SDOS_SdoInd*/ + bSdoSegFollows = FALSE; + nSdoSegService = 0; + if (pSdoSegData) + { + /* the memory has to be released if it is not released before. + In case of AbortIdx_Working the buffer will be freed in SDOS_SdoRes*/ + FREEMEM( (UINT16 VARMEM *) pSdoSegData ); + pSdoSegData = NULL; + } + + nSdoSegBytesToHandle = 0; + } + + return abort; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param pSdoInd Pointer to the received mailbox data from the master. + + \return Indicates if an error occurred while the operation ( good = 0 ). + + \brief This function is called when a Upload SDO Segment request service + is received from the master. It prepares and operates the + response and sends it by itself. +*//////////////////////////////////////////////////////////////////////////////////////// + +static UINT8 SdoUploadSegmentInd( TUPLOADSDOSEGREQMBX MBXMEM * pSdoInd ) +{ + UINT8 abort = 0; + TUPLOADSDOSEGRESMBX MBXMEM * pSdoSegRes = (TUPLOADSDOSEGRESMBX MBXMEM *)pSdoInd; + + if ( (pSdoInd->SegHeader & SEGHEADER_TOGGLE) == bSdoSegLastToggle ) + { + /* toggle bit has not toggled... */ + abort = ABORTIDX_TOGGLE_BIT_NOT_CHANGED; + } + else + { + /* maxData contains the maximum data to be sent with a SDO-Upload Segment response */ + + UINT32 size = 0; + UINT16 maxData; + { + maxData = u16SendMbxSize - MBX_HEADER_SIZE - SEGMENT_NORM_HEADER_SIZE; + } + + /* the new toggle bit is stored in bSdoSegLastToggle */ + bSdoSegLastToggle = pSdoInd->SegHeader & SEGHEADER_TOGGLE; + + if ( nSdoSegCompleteSize < (nSdoSegBytesToHandle + maxData) ) + { + /* the remaining data can be send with one SDO Upload Segment response, + size contains the data to be copied */ + size = nSdoSegCompleteSize - nSdoSegBytesToHandle; + bSdoSegFollows = FALSE; + } + else + { + /* more data will follow, size contains the data to be copied */ + size = maxData; + bSdoSegFollows = TRUE; + } + + /* copy the object data in the SDO Upload segment response */ + // Clear Data0 + pSdoSegRes->SdoHeader.SegHeader &= ~SEGHDATA_MASK; + if ((nSdoSegBytesToHandle & 0x1) == 0x01) + { // Data starts at odd byte number (Segment 2, 4,...): Data0 is at high byte, Data1 lies at an even address + // Write Data0 + pSdoSegRes->SdoHeader.SegHeader |= (pSdoSegData[(nSdoSegBytesToHandle >> 1)] & SEGHDATA_MASK); + // Copy Data1 - DataN + MBXMEMCPY( pSdoSegRes->SdoHeader.Data, &pSdoSegData[(nSdoSegBytesToHandle >> 1) + 1], size - 1); + } + else + { + UINT16 i = 0; + UINT32 nIndexOffset = nSdoSegBytesToHandle >> 1; + // Data starts at even byte number (Segment 1,3, ...): Data0 is at low byte, Data1 lies at an odd address + // Write Data0 + pSdoSegRes->SdoHeader.SegHeader |= ((pSdoSegData[(nSdoSegBytesToHandle >> 1)] << SEGDATASHIFT) & SEGHDATA_MASK); + // Copy Data1 - DataN + + for (i = 0; i < (size >> 1);i++) + { + pSdoSegRes->SdoHeader.Data[i] = ((pSdoSegData[i + nIndexOffset] & SEGHDATA_MASK) >> 8) | ((pSdoSegData[i + nIndexOffset + 1] & ~SEGHDATA_MASK) << 8); + // (If size is even, one byte too much is copied. But, that is not a problem.) + } + } + + /* the SDO Upload Segment header depends if there is still data to be sent */ + pSdoSegRes->CoeHeader &= ~COEHEADER_COESERVICEMASK; + pSdoSegRes->CoeHeader |= ((UINT16)COESERVICE_SDORESPONSE) << COEHEADER_COESERVICESHIFT; + + /*Clear SDO header*/ + pSdoSegRes->SdoHeader.SegHeader &= ~SEGHEADER_MASK; + if (bSdoSegFollows) + pSdoSegRes->SdoHeader.SegHeader |= SDOSERVICE_UPLOADSEGMENTRES | bSdoSegLastToggle; + else + pSdoSegRes->SdoHeader.SegHeader |= SDOSERVICE_UPLOADSEGMENTRES | bSdoSegLastToggle | SEGHEADER_NOMOREFOLLOWS; + + // operate CAN specific flag segDataSize: + /* HBu 06.02.06: the sizes were wrong */ + if ( size < MIN_SEGMENTED_DATA ) + { + // at least MIN_SEGMENTED_DATA bytes have to be send: + pSdoSegRes->MbxHeader.Length = SEGMENT_NORM_RES_SIZE; + pSdoSegRes->SdoHeader.SegHeader |= (MIN_SEGMENTED_DATA - size) << SEGHEADERSHIFT_SEGDATASIZE; + } + else + { + pSdoSegRes->MbxHeader.Length = ((UINT16) size) + SEGMENT_NORM_HEADER_SIZE; + } + + if ( bSdoSegFollows == TRUE ) + // updating the value of send bytes: + nSdoSegBytesToHandle += size; + else + { + FREEMEM( (UINT16 VARMEM *) pSdoSegData ); + pSdoSegData = NULL; + nSdoSegBytesToHandle = 0; + nSdoSegService = 0; + } + } + + return abort; +} + + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param abort Result of the SDO access + \param command SDO command index + \param completeAccess Indicates if complete access was requested + \param dataSize Available data buffer in the response + \param objLength Complete size of the object + \param pSdoRes Pointer to the mailbox buffer + + \brief This function is called when a SDO response shall be sent +*//////////////////////////////////////////////////////////////////////////////////////// + +void SdoRes(UINT8 abort, UINT8 command, UINT8 completeAccess, UINT16 dataSize, UINT32 objLength, TINITSDOMBX MBXMEM *pSdoRes) +{ + /* for an upload segment response the toggle bit was overwritten */ + if ((command != SDOSERVICE_UPLOADSEGMENTREQ) && (command != SDOSERVICE_DOWNLOADSEGMENTREQ)) + { + pSdoRes->SdoHeader.Sdo[SDOHEADER_COMMANDOFFSET] &= 0xFF00; + } + if ( abort == 0 ) + { + /* SDO-Download or SDO-Upload was successful, generate the SDO- and CoE-Header */ + pSdoRes->CoeHeader &= ~COEHEADER_COESERVICEMASK; + pSdoRes->CoeHeader |= ((UINT16)COESERVICE_SDORESPONSE) << COEHEADER_COESERVICESHIFT; + if ( command == SDOSERVICE_INITIATEUPLOADREQ ) + { + // HBu 06.02.06: Complete Access Bit in the SDO-Upload-Response too */ + if ( (objLength <= 4) && (objLength > 0) ) + { + /* Expedited Upload Response */ + pSdoRes->MbxHeader.Length = EXPEDITED_FRAME_SIZE; + pSdoRes->SdoHeader.Sdo[SDOHEADER_COMMANDOFFSET] |= SDOHEADER_SIZEINDICATOR | + SDOHEADER_TRANSFERTYPE | + completeAccess | + ((MAX_EXPEDITED_DATA - ((UINT8)objLength)) << SDOHEADERSHIFT_DATASETSIZE) | + SDOSERVICE_INITIATEUPLOADRES; + } + else + { + /* Normal or Segmented Upload Response */ + if (dataSize < objLength) + { + pSdoRes->MbxHeader.Length = UPLOAD_NORM_RES_SIZE+dataSize; + } + else + { + pSdoRes->MbxHeader.Length = UPLOAD_NORM_RES_SIZE+((UINT16)objLength); + } + ((TINITSDOUPLOADNORMRESMBX MBXMEM *) pSdoRes)->CompleteSize[0] = SWAPWORD((UINT16)objLength); + ((TINITSDOUPLOADNORMRESMBX MBXMEM *) pSdoRes)->CompleteSize[1] = SWAPWORD((UINT16)(objLength>>16)); + pSdoRes->SdoHeader.Sdo[SDOHEADER_COMMANDOFFSET] |= SDOHEADER_SIZEINDICATOR | + completeAccess | + SDOSERVICE_INITIATEUPLOADRES; + } + } + /* for a segmented response the command was wrong in the response */ + else if ( command == SDOSERVICE_DOWNLOADSEGMENTREQ ) + { + /* Download segmented response */ + pSdoRes->MbxHeader.Length = DOWNLOAD_NORM_RES_SIZE; + pSdoRes->SdoHeader.Sdo[SDOHEADER_COMMANDOFFSET] |= SDOSERVICE_DOWNLOADSEGMENTRES; + } + else if ( command != SDOSERVICE_UPLOADSEGMENTREQ ) + { + /* Download response */ + pSdoRes->MbxHeader.Length = DOWNLOAD_NORM_RES_SIZE; + pSdoRes->SdoHeader.Sdo[SDOHEADER_COMMANDOFFSET] |= SDOSERVICE_INITIATEDOWNLOADRES; + } + } + else + { + /* generate a SDO-Abort-Request */ + pSdoRes->MbxHeader.Length = ABORT_NORM_RES_SIZE; + pSdoRes->CoeHeader &= ~COEHEADER_COESERVICEMASK; + pSdoRes->CoeHeader |= ((UINT16)COESERVICE_SDOREQUEST) << COEHEADER_COESERVICESHIFT; + pSdoRes->SdoHeader.Sdo[SDOHEADER_COMMANDOFFSET] = SDOSERVICE_ABORTTRANSFER; + ((TABORTSDOTRANSFERREQMBX MBXMEM *) pSdoRes)->AbortCode = SWAPDWORD(cAbortCode[abort]); + } + + // HBu 02.05.06: if the CoE-response could not be sent because the + // send mailbox is full it should be stored + if (MBX_MailboxSendReq((TMBX MBXMEM *) pSdoRes, COE_SERVICE) != 0) + { + /* we store the CoE mailbox service to send it later (in COE_ContinueInd) when the mailbox is read */ + pCoeSendStored = (TMBX MBXMEM *) pSdoRes; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param pSdoInd Pointer to the received mailbox data from the master. + + \return Indicates if an error occurred while the operation ( good = 0 ). + + \brief This function is called when a SDO request service + is received from the master and calls depending from + the command the concerning function. +*//////////////////////////////////////////////////////////////////////////////////////// + +UINT8 SDOS_SdoInd(TINITSDOMBX MBXMEM *pSdoInd) +{ + UINT8 abort = 0; + UINT8 sdoHeader = pSdoInd->SdoHeader.Sdo[SDOHEADER_COMMANDOFFSET] & SDOHEADER_COMMANDMASK; + /* the SDO-command is in bit 5-7 of the first SDO-Byte */ + UINT8 command = (sdoHeader & SDOHEADER_COMMAND); + /* mbxSize contains the size of the mailbox (CoE-Header (2 Bytes) + SDO-Header (8 Bytes) + SDO-Data (if the data length is greater than 4)) */ + UINT16 mbxSize = SWAPWORD(pSdoInd->MbxHeader.Length); + UINT16 index; + UINT8 subindex; + OBJCONST TOBJECT OBJMEM * pObjEntry; + /* this variable contains the information, if all entries of an object will be read (bCompleteAccess > 0) or a single entry */ + UINT8 bCompleteAccess = 0; + UINT32 objLength = 0; + UINT32 dataSize = 0; + + if ( bSdoInWork ) + { + /* the last SDO is still in work */ + return MBXERR_SERVICEINWORK; + } + + + switch (command) + { + case SDOSERVICE_INITIATEDOWNLOADREQ: + case SDOSERVICE_INITIATEUPLOADREQ: + /* the variable index contains the requested index of the SDO service */ + index = pSdoInd->SdoHeader.Sdo[SDOHEADER_INDEXHIOFFSET] & SDOHEADER_INDEXHIMASK; + index <<= 8; + index += pSdoInd->SdoHeader.Sdo[SDOHEADER_INDEXLOOFFSET] >> SDOHEADER_INDEXLOSHIFT; + /* the variable subindex contains the requested subindex of the SDO service */ + subindex = pSdoInd->SdoHeader.Sdo[SDOHEADER_SUBINDEXOFFSET] >> SDOHEADER_SUBINDEXSHIFT; + + /* OBJ_GetObjectHandle checks if the requested index is defined in the object dictionary */ + pObjEntry = OBJ_GetObjectHandle( index ); + + if ( pObjEntry ) + { + /* transferType contains the information if the SDO Download Request or the SDO Upload Response + can be an expedited service (SDO data length <= 4, that means the data is stored in the + SDO-Header completely */ + UINT8 bTransferType = 0; + /* pData is the pointer to the received (SDO-Download) or sent (SDO-Upload) SDO data in the mailbox */ + UINT16 MBXMEM * pData = NULL; + UINT8 segTransfer = 0; + + { + dataSize = objLength = OBJ_GetObjectLength( index, subindex, pObjEntry, (UINT8) (sdoHeader & SDOHEADER_COMPLETEACCESS) ); + + if ( command == SDOSERVICE_INITIATEUPLOADREQ ) + { + /* SDO Upload */ + if ( mbxSize != EXPEDITED_FRAME_SIZE ) + /* a SDO Upload request has always a fixed size (2 Byte CoE-Header plus 8 Byte SDO-Header) */ + return MBXERR_INVALIDSIZE; + /* distinguish between expedited and normal upload response within the length of the response data */ + if ( (objLength <= MAX_EXPEDITED_DATA) && objLength != 0 ) + { + /* Expedited Upload */ + bTransferType = 1; + /* pData is the pointer where the object data has to be copied for the response */ + pData = ((TINITSDOUPLOADEXPRESMBX MBXMEM *) pSdoInd)->Data; + /* initialize the 4 data bytes of the SDO upload response because the requested object data + could be less than 4 */ + pData[0] = 0; + pData[1] = 0; + } + else + { + /* HBu 06.02.06: the variable dataSize has to be set to the available size in one mailbox */ + dataSize = u16SendMbxSize - MBX_HEADER_SIZE - UPLOAD_NORM_RES_SIZE; + if ( dataSize < objLength ) + /* Segmented Upload */ + segTransfer = 1; + else + /* Normal Upload */ + pData = ((TINITSDOUPLOADNORMRESMBX MBXMEM *) pSdoInd)->Data; + } + } + else + { + /* SDO-Download: store if the request is a expedited or normal request */ + bTransferType = sdoHeader & SDOHEADER_TRANSFERTYPE; + } + } + + if ( command == SDOSERVICE_INITIATEDOWNLOADREQ ) + { + /* SDO Download */ + if ( bTransferType ) + { + /* Expedited Download */ + if ( mbxSize != EXPEDITED_FRAME_SIZE ) + /* an Expedited SDO Download request has always a fixed size (2 Byte CoE-Header plus 8 Byte SDO-Header) */ + return MBXERR_INVALIDSIZE; + /* dataSize gets the real size of the downloaded object data (1,2,3 or 4) */ + dataSize = MAX_EXPEDITED_DATA - ((sdoHeader & SDOHEADER_DATASETSIZE) >> SDOHEADERSHIFT_DATASETSIZE); + /* pData is the pointer to the downloaded object data */ + pData = (UINT16 MBXMEM *) &pSdoInd[1]; + } + else + { + /* Normal Download */ + /* downloadSize gets the real size of the downloaded data */ + /* '&' operator was too much */ + + UINT32 downloadSize = ((UINT32)(SWAPWORD(((TINITSDODOWNLOADNORMREQMBX MBXMEM *) pSdoInd)->CompleteSize[1]))<<16) + (SWAPWORD(((TINITSDODOWNLOADNORMREQMBX MBXMEM *) pSdoInd)->CompleteSize[0])); + + /* HBu 29.03.06: if it is a segmented download the mbxSize has to be the complete mailbox size */ + if ( (MBX_HEADER_SIZE+EXPEDITED_FRAME_SIZE+downloadSize) > u16ReceiveMbxSize ) + { + if ( mbxSize != (u16ReceiveMbxSize-MBX_HEADER_SIZE) ) + return MBXERR_INVALIDSIZE; + } + else + { + if ( mbxSize != (EXPEDITED_FRAME_SIZE+downloadSize) ) + /* the mbxSize and the downloadSize are not consistent (mbxSize = downloadSize + 2 byte CoE-Header + 8 byte SDO Header */ + return MBXERR_INVALIDSIZE; + } + + /* pData is the pointer to the downloaded object data */ + pData = (UINT16 MBXMEM *) ((TINITSDODOWNLOADNORMREQMBX MBXMEM *) pSdoInd)->Data; + /* the received dataSize will be checked in the object specific functions called from + OBJ_Write (in objdef.c) */ + dataSize = downloadSize; + if ( dataSize > (UINT32)(mbxSize - DOWNLOAD_NORM_REQ_SIZE) ) + /* Segmented Download */ + segTransfer = 1; + } + } + + if ( sdoHeader & SDOHEADER_COMPLETEACCESS ) + { + bCompleteAccess = 1; + // HBu 02.05.06: Complete Access is only supported with subindex 0 and 1 + if (subindex > 1) + abort = ABORTIDX_UNSUPPORTED_ACCESS; + } + + if ( abort == 0 ) + { + if ( segTransfer ) + { + bSdoSegFollows = TRUE; + bSdoSegLastToggle = 1; + bSdoSegAccess = bCompleteAccess; + nSdoSegIndex = index; + nSdoSegSubindex = subindex; + pSdoSegObjEntry = pObjEntry; + if ( command == SDOSERVICE_INITIATEUPLOADREQ ) + nSdoSegCompleteSize = objLength; + else + nSdoSegCompleteSize = dataSize; + + if (pSdoSegData != NULL) + { + FREEMEM( (UINT16 VARMEM *) pSdoSegData); + pSdoSegData = NULL; + } + pSdoSegData = (UINT16 VARMEM *) ALLOCMEM( ROUNDUPBYTE2WORD(nSdoSegCompleteSize) ); + + if ( pSdoSegData == NULL ) + { +/*ECATCHANGE_START(V5.11) SDO4*/ + if(bCompleteAccess) + abort = ABORTIDX_UNSUPPORTED_ACCESS; + else +/*ECATCHANGE_END(V5.11) SDO4*/ + abort = ABORTIDX_OUT_OF_MEMORY; + } + else + { + if ( command == SDOSERVICE_INITIATEUPLOADREQ ) + { + /* Segmented Upload */ + abort = OBJ_Read( index, subindex, objLength, pObjEntry, (UINT16 MBXMEM *) pSdoSegData, bCompleteAccess ); + if ( abort == 0 ) + { + MBXMEMCPY( ((TINITSDOUPLOADNORMRESMBX MBXMEM *) pSdoInd)->Data, pSdoSegData, dataSize ); + nSdoSegService = SDOSERVICE_UPLOADSEGMENTREQ; + } + else if ( abort == ABORTIDX_WORKING ) + { + /* the application generates the SDO-Response later on by calling SDOS_SdoRes (only possible if object access function pointer is defined) */ + u8PendingSdo = SDO_PENDING_SEG_READ; + bStoreCompleteAccess = bCompleteAccess; + u8StoreSubindex = subindex; + u16StoreIndex = index; + u32StoreDataSize = objLength; + pStoreData = pSdoSegData; + pSdoPendFunc = pObjEntry->Read; + + bSdoInWork = TRUE; + /* we have to store the buffer and the response header */ + pSdoResStored = pSdoInd; + + /*update command field*/ + pSdoResStored->SdoHeader.Sdo[SDOHEADER_COMMANDOFFSET] &= ~SDOHEADER_COMMANDMASK; + pSdoResStored->SdoHeader.Sdo[SDOHEADER_COMMANDOFFSET] |= (sdoHeader & (SDOHEADER_COMPLETEACCESS | SDOHEADER_COMMAND)); + nSdoSegService = SDOSERVICE_UPLOADSEGMENTREQ; + return 0; + } + } + else + { + /* Segmented Download */ + MBXMEMCPY( pSdoSegData, ((TINITSDODOWNLOADNORMREQMBX MBXMEM *) pSdoInd)->Data, mbxSize-DOWNLOAD_NORM_REQ_SIZE ); + nSdoSegService = SDOSERVICE_DOWNLOADSEGMENTREQ; + dataSize = (mbxSize-DOWNLOAD_NORM_REQ_SIZE); + } + + nSdoSegBytesToHandle = dataSize; + } + } + else + { + if ( objLength == 0 ) + { + /* the objLength is not known, therefore the variables for a possible segmented transfer + should be initialized */ + nSdoSegIndex = index; + nSdoSegSubindex = subindex; + pSdoSegObjEntry = pObjEntry; + } + if ( command == SDOSERVICE_INITIATEUPLOADREQ ) + { + /* Expedited or Normal Upload */ + abort = OBJ_Read( index, subindex, objLength, pObjEntry, pData, bCompleteAccess ); + if ( abort == ABORTIDX_WORKING ) + { + /* the application generates the SDO-Response later on by calling SDOS_SdoRes (only possible if object access function pointer is defined) */ + u8PendingSdo = SDO_PENDING_READ; + bStoreCompleteAccess = bCompleteAccess; + u8StoreSubindex = subindex; + u16StoreIndex = index; + u32StoreDataSize = objLength; + pStoreData = pData; + pSdoPendFunc = pObjEntry->Read; + + bSdoInWork = TRUE; + /* we have to store the buffer and the response header */ + pSdoResStored = pSdoInd; + + /*update command field*/ + pSdoResStored->SdoHeader.Sdo[SDOHEADER_COMMANDOFFSET] &= ~SDOHEADER_COMMANDMASK; + pSdoResStored->SdoHeader.Sdo[SDOHEADER_COMMANDOFFSET] |= (sdoHeader & (SDOHEADER_COMPLETEACCESS | SDOHEADER_COMMAND)); + return 0; + } + } + else + { + /* Expedited or Normal Download */ + abort = OBJ_Write( index, subindex, dataSize, pObjEntry, pData, bCompleteAccess ); + if ( abort == ABORTIDX_WORKING ) + { + /* the application generates the SDO-Response later on by calling SDOS_SdoRes (only possible if object access function pointer is defined) */ + u8PendingSdo = SDO_PENDING_WRITE; + bStoreCompleteAccess = bCompleteAccess; + u8StoreSubindex = subindex; + u16StoreIndex = index; + u32StoreDataSize = dataSize; + pStoreData = pData; + pSdoPendFunc = pObjEntry->Write; + + bSdoInWork = TRUE; + /* we have to store the buffer and the response header */ + pSdoResStored = pSdoInd; + + /*update command field*/ + pSdoResStored->SdoHeader.Sdo[SDOHEADER_COMMANDOFFSET] &= ~SDOHEADER_COMMANDMASK; + pSdoResStored->SdoHeader.Sdo[SDOHEADER_COMMANDOFFSET] |= (sdoHeader & (SDOHEADER_COMPLETEACCESS | SDOHEADER_COMMAND)); + return 0; + } + } + } /* else if ( objLength == 0 ) */ + } /* if ( abort == 0 ) */ + + } //if(pEntry) (Object handle found) + else + { + abort = ABORTIDX_OBJECT_NOT_EXISTING; + } + break; + + case SDOSERVICE_DOWNLOADSEGMENTREQ: + case SDOSERVICE_UPLOADSEGMENTREQ: + if ( command == nSdoSegService ) + { + if ( command == SDOSERVICE_DOWNLOADSEGMENTREQ ) + abort = SdoDownloadSegmentInd( (TDOWNLOADSDOSEGREQMBX MBXMEM *) pSdoInd ); + else + abort = SdoUploadSegmentInd( (TUPLOADSDOSEGREQMBX MBXMEM *) pSdoInd ); + } + else + abort = ABORTIDX_COMMAND_SPECIFIER_UNKNOWN; + break; + + default: + abort = ABORTIDX_COMMAND_SPECIFIER_UNKNOWN; + break; + } + + if(abort != ABORTIDX_WORKING) + { + /* type cast was added because of warning */ + SdoRes(abort, command, (UINT8) (sdoHeader & SDOHEADER_COMPLETEACCESS), (UINT16) dataSize, objLength, pSdoInd); + } + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param abort Result of the SDO access + \param objLength Complete size of the object + \param pData Pointer to the mailbox buffer + + \brief This function is called when a SDO response shall be sent +*//////////////////////////////////////////////////////////////////////////////////////// + +void SDOS_SdoRes(UINT8 abort, UINT32 objLength, UINT16 MBXMEM *pData) +{ + UINT16 dataSize = 0; + + if (bSdoInWork) + { + /* SDO-Response is expected */ + UINT8 command = pSdoResStored->SdoHeader.Sdo[SDOHEADER_COMMANDOFFSET] & SDOHEADER_COMMAND; + UINT8 completeAccess = pSdoResStored->SdoHeader.Sdo[SDOHEADER_COMMANDOFFSET] & SDOHEADER_COMPLETEACCESS; + + if ( command == SDOSERVICE_INITIATEUPLOADREQ ) + { + /* dataSize contains the available size in one mailbox */ + dataSize = u16SendMbxSize - MBX_HEADER_SIZE - UPLOAD_NORM_RES_SIZE; + if ( dataSize < objLength ) + { + /* Segmented Upload, the variables for the segmented transfer should be initialized */ + bSdoSegFollows = TRUE; + bSdoSegLastToggle = 1; + bSdoSegAccess = completeAccess; + nSdoSegCompleteSize = objLength; + nSdoSegService = SDOSERVICE_UPLOADSEGMENTREQ; + pSdoSegData = (UINT16 VARMEM *) pData; + /* the first segment shall be copied */ + MBXMEMCPY(((TINITSDOUPLOADNORMRESMBX MBXMEM *) pSdoResStored)->Data, pData, dataSize); + nSdoSegBytesToHandle = dataSize; + } + else + if ( (objLength <= 4) && (objLength > 0) ) + { + /* Expedited response */ + if ( pData != ((TINITSDOUPLOADEXPRESMBX MBXMEM *) pSdoResStored)->Data ) + { + /* the data is not in the response buffer yet, it shall be copied */ + MBXMEMCPY(((TINITSDOUPLOADEXPRESMBX MBXMEM *) pSdoResStored)->Data, pData, objLength); + } + } + else + { + /* Normal response */ + if ( pData != ((TINITSDOUPLOADNORMRESMBX MBXMEM *) pSdoResStored)->Data ) + { + /* the data is not in the response buffer yet, it shall be copied */ + MBXMEMCPY(((TINITSDOUPLOADNORMRESMBX MBXMEM *) pSdoResStored)->Data, pData, objLength); + } + } + } + + /* SDO access is finished, send the response */ + bSdoInWork = FALSE; + SdoRes(abort, command, completeAccess, dataSize, objLength, pSdoResStored); + } +} + + +///////////////////////////////////////////////////////////////////////////////////////// +/** + \param pSdoInfoInd Pointer to the received mailbox data from the master. + + \return Indicates if an error occurred while the operation ( good = 0 ). + + \brief This function is called when a SDO-Info request service + is received from the master and calls depending from + the opcode the concerning function. +*//////////////////////////////////////////////////////////////////////////////////////// + +UINT8 SDOS_SdoInfoInd( TSDOINFORMATION MBXMEM *pSdoInfoInd ) +{ + UINT8 abort = 0; + /* the variable opCode contains the requested SDO Information type */ + UINT8 opCode = (UINT8) ((pSdoInfoInd->SdoHeader.InfoHead & INFOHEAD_OPCODE_MASK) >> INFOHEAD_OPCODE_SHIFT); + OBJCONST TOBJECT OBJMEM * pObjEntry; + UINT16 index; + UINT8 flags = COE_SERVICE; + + /* it has to be checked if the mailbox protocol is correct, the sent mailbox data length has to + great enough for the service header of the requested SDO Information type */ + if ( opCode == SDOINFOSERVICE_ENTRYDESCRIPTION_Q ) + { + if ( pSdoInfoInd->MbxHeader.Length < SIZEOF_SDOINFOENTRYREQSTRUCT ) + return MBXERR_SIZETOOSHORT; + } + else + { + if ( pSdoInfoInd->MbxHeader.Length < SIZEOF_SDOINFOLISTSTRUCT ) + return MBXERR_SIZETOOSHORT; + } + + switch ( opCode ) + { + case SDOINFOSERVICE_OBJDICTIONARYLIST_Q: + /* an object list is requested, check if the list type is supported */ + if ( SWAPWORD(pSdoInfoInd->SdoHeader.Data.List.ListType) <= INFO_LIST_TYPE_MAX ) + { + UINT16 size = 0; + /* the variable listType contains the requested listType */ + UINT8 listType = (UINT8) SWAPWORD(pSdoInfoInd->SdoHeader.Data.List.ListType); + + /* the SDO Information Header has to be stored because this function will be + called again if the response could not be sent with one mailbox service, the + variable nSdoInfoFragmentsLeft is 0 zero for the first call and unequal 0 + for the following calls */ + MBXMEMCPY(aSdoInfoHeader, pSdoInfoInd, SDO_INFO_HEADER_BYTE_SIZE); + if ( listType-- == 0 ) + { + /* List-Type 0: length of the lists */ + UINT8 i; + + /* the needed mailbox size for List-Type 0 response is just 24 bytes, the mailbox has always + to be at least 24 bytes to support the SDO Information service */ + nSdoInfoFragmentsLeft = 0; + for (i = 0; i < INFO_LIST_TYPE_MAX; i++) + { + UINT16 n = OBJ_GetNoOfObjects(i); + + /* copy the number of objects of the list type in the SDO Information response */ + ((UINT16 MBXMEM *) &pSdoInfoInd->CoeHeader)[(SIZEOF_SDOINFOLISTSTRUCT>>1)+i] = SWAPWORD(n); + } + + /* size of the mailbox service response */ + size = (INFO_LIST_TYPE_MAX << 1) + SIZEOF_SDOINFOLISTSTRUCT; + } + else + { + /* object list with indexes is requested */ + UINT16 MBXMEM * pData; + UINT16 n = 0; + + if ( nSdoInfoFragmentsLeft ) + { + /* the next fragment of the SDO Information response shall be sent */ + /* initialize size with the maximum size fits into one mailbox service */ + { + size = u16SendMbxSize - SIZEOF_SDOINFO - MBX_HEADER_SIZE; + } + /* initialize pData with the pointer where the fragment has to be copied */ + pData = &((UINT16 MBXMEM *) &pSdoInfoInd->CoeHeader)[SIZEOF_SDOINFO>>1]; + /* initialize index with the next index to be sent */ + index = nSdoInfoIndex; + /* decrement the number of fragments to be sent */ + nSdoInfoFragmentsLeft--; + } + else + { + /* the first fragment of the SDO Information response has to be sent */ + /* get the number of objects of the requested object list */ + n = OBJ_GetNoOfObjects(listType); + /* we start with index 0x1000 */ + index = 0x1000; + /* initialize size with the maximum size fits into one mailbox service */ + { + size = u16SendMbxSize - SIZEOF_SDOINFOLISTSTRUCT - MBX_HEADER_SIZE; + } + /* initialize pData with the pointer where the fragment has to be copied */ + pData = &((UINT16 MBXMEM *) &pSdoInfoInd->CoeHeader)[SIZEOF_SDOINFOLISTSTRUCT>>1]; + /*Check if List need to be send in fragments*/ + if( (n<<1) > size) + { + /*number of Bytes to transmit don't fit into one mailbox datagram*/ + + /*calculate number of fragments which need to be send + total number of bytes - bytes which will be transmitted with the current response plus the fragment size - 1 (to round up) divided by the size of the following fragments + */ + UINT16 Fragsize = size + 2; + nSdoInfoFragmentsLeft = (((n<<1)-size + (Fragsize-1)) /Fragsize); + } + else + { + nSdoInfoFragmentsLeft = 0; + } + } + + /* get the next part of the requested object list */ + size = OBJ_GetObjectList(listType, &index, size, pData,&abort); + + /* store index for next fragment */ + nSdoInfoIndex = index; + /* size contains before the instruction the size still available in the mailbox buffer + and shall contain the size of the mailbox response data after the next instruction */ + { + size = u16SendMbxSize - size - MBX_HEADER_SIZE; + } + } + + /* size of the mailbox response data */ + pSdoInfoInd->MbxHeader.Length = size; + + if(abort == 0) + { + pSdoInfoInd->SdoHeader.InfoHead &= ~INFOHEAD_OPCODE_MASK; + pSdoInfoInd->SdoHeader.InfoHead |= (UINT16) (SDOINFOSERVICE_OBJDICTIONARYLIST_S << INFOHEAD_OPCODE_SHIFT); + /* number of fragments still has to be sent */ + pSdoInfoInd->SdoHeader.FragmentsLeft = SWAPWORD(nSdoInfoFragmentsLeft); + + if (nSdoInfoFragmentsLeft) + { + /* there still are fragments to be sent, + the InComplete flag in the SDO Information response has to be sent */ + pSdoInfoInd->SdoHeader.InfoHead &= ~ INFOHEADER_INCOMPLETE_MASK; + pSdoInfoInd->SdoHeader.InfoHead |= (UINT16) (SDOINFOSERVICE_INCOMPLETE << INFOHEAD_OPCODE_SHIFT); + /* the FRAGMENTS_FOLLOW flag has to be set for the function MBX_MailboxSendReq to + indicate the mailbox handler that still fragments has to be sent so that this + function shall be called again from COE_ContinueInd when the actual mailbox buffer + was sent */ + flags = FRAGMENTS_FOLLOW | COE_SERVICE; + } + } + } + break; + + case SDOINFOSERVICE_OBJDESCRIPTION_Q: + case SDOINFOSERVICE_ENTRYDESCRIPTION_Q: + /* get the requested index */ + index = SWAPWORD(pSdoInfoInd->SdoHeader.Data.Obj.Index); + if(index < 0x1000) + { + /*SDO Info access is only allowed for objects >= 0x1000*/ + abort = ABORTIDX_UNSUPPORTED_ACCESS; + } + else + { + /* get the object handle of the requested index */ + pObjEntry = OBJ_GetObjectHandle( index ); + + if ( pObjEntry ) + { + /* object exists */ + UINT16 size = 0; + if ( opCode == SDOINFOSERVICE_OBJDESCRIPTION_Q ) + { + /* object description is requested */ + OBJTOMBXMEMCPY(&pSdoInfoInd->SdoHeader.Data.Obj.Res, OBJ_GetObjDesc(pObjEntry), SDO_INFO_OBJ_DESC_SIZE); + + /* the mailbox should be big enough that the maximum object description + fits in the mailbox (the fragmentation is not done in the sample code), + so it will be checked only if the object description fits */ + size = OBJ_GetDesc(index, 0, pObjEntry, NULL) + SIZEOF_SDOINFOOBJSTRUCT; + + if ( size > (u16SendMbxSize - MBX_HEADER_SIZE) ) + { + /* size of the object description does not fit in the mailbox, + the object description will be sent without the name */ + size = SIZEOF_SDOINFOOBJSTRUCT; + } + else + { + /* object description fits in the mailbox, get the name of the object */ + size = OBJ_GetDesc(index, 0, pObjEntry, ((UINT16 MBXMEM *) &(&pSdoInfoInd->SdoHeader.Data.Obj.Res)[1])) + SIZEOF_SDOINFOOBJSTRUCT; + } + } + else + { + /* entry description is requested, + get the requested subindex */ + UINT8 subindex = (UINT8) ((pSdoInfoInd->SdoHeader.Data.Entry.Info & ENTRY_MASK_SUBINDEX) >> ENTRY_SUBINDEX_SHIFT); + + /* get the maximum subindex */ + UINT8 maxSubindex = (OBJ_GetObjDesc(pObjEntry)->ObjFlags & OBJFLAGS_MAXSUBINDEXMASK) >> OBJFLAGS_MAXSUBINDEXSHIFT; + + if ( subindex <= maxSubindex ) + { + UINT16 ObjectFlags; + /* requested subindex is not too great */ + /* get the entry description of the requested entry */ + OBJTOMBXMEMCPY(&pSdoInfoInd->SdoHeader.Data.Entry.Res, OBJ_GetEntryDesc(pObjEntry, subindex), SIZEOF(TSDOINFOENTRYDESC)); + + /* the transmission of the value info is not supported yet of the sample code */ + pSdoInfoInd->SdoHeader.Data.Entry.Info &= ~ENTRY_MASK_VALUEINFO; + ObjectFlags = OBJ_GetObjDesc(pObjEntry)->ObjFlags; + ObjectFlags = (ObjectFlags & OBJFLAGS_OBJCODEMASK) >> OBJFLAGS_OBJCODESHIFT; + + if(((ObjectFlags == OBJCODE_ARR) || (ObjectFlags == OBJCODE_REC)) && (subindex == 0) ) + { + OBJTOMBXSTRCPY( ((UINT16 MBXMEM *) &(&pSdoInfoInd->SdoHeader.Data.Entry.Res)[1]), aSubindexDesc, SIZEOF(aSubindexDesc) ); + size = 12 + SIZEOF_SDOINFO + SIZEOF(TSDOINFOENTRY); // 12: Length of "SubIndex 000" + } + else + { + /* the mailbox should be big enough that the maximum entry description + fits in the mailbox (the fragmentation is not done in the sample code), + so it will be checked only if the entry description fits */ + size = OBJ_GetDesc(index, subindex, pObjEntry, NULL) + SIZEOF_SDOINFO + SIZEOF(TSDOINFOENTRY); + if ( size > (u16SendMbxSize - MBX_HEADER_SIZE) ) + { + /* size of the object description does not fit in the mailbox, + the object description will be sent without the name */ + size = SIZEOF_SDOINFO + SIZEOF(TSDOINFOENTRY); + } + else + { + /* object description fits in the mailbox, get the name of the entry */ + size = OBJ_GetDesc(index, subindex, pObjEntry, ((UINT16 MBXMEM *) &(&pSdoInfoInd->SdoHeader.Data.Entry.Res)[1])) + SIZEOF_SDOINFO + SIZEOF(TSDOINFOENTRY); + } + } + } + else + abort = ABORTIDX_SUBINDEX_NOT_EXISTING; + } + + if ( abort == 0 ) + { + { + /* for the object and entry description the sample code does not support the fragmentation, + the mailbox has to be big enough */ + pSdoInfoInd->SdoHeader.FragmentsLeft = 0; + /* store the size of the mailbox data in the mailbox buffer */ + pSdoInfoInd->MbxHeader.Length = size; + /* set the opCode of the SDO Information response */ + pSdoInfoInd->SdoHeader.InfoHead &= ~INFOHEAD_OPCODE_MASK; + pSdoInfoInd->SdoHeader.InfoHead |= (UINT16)((opCode + 1) << INFOHEAD_OPCODE_SHIFT); + } + } + } + else + abort = ABORTIDX_OBJECT_NOT_EXISTING; + } + break; + default: + abort = ABORTIDX_COMMAND_SPECIFIER_UNKNOWN; + } + + if ( abort ) + { + /* send a SDO Information Error response */ + pSdoInfoInd->MbxHeader.Length = SIZEOF_SDOINFOERRORSTRUCT; + + pSdoInfoInd->SdoHeader.InfoHead &= ~INFOHEAD_OPCODE_MASK; + pSdoInfoInd->SdoHeader.InfoHead |= (UINT16) ((SDOINFOSERVICE_ERROR_Q) << INFOHEAD_OPCODE_SHIFT); + + pSdoInfoInd->SdoHeader.FragmentsLeft = 0; + pSdoInfoInd->SdoHeader.Data.Error.ErrorCode = SWAPDWORD(cAbortCode[abort]); + + nSdoInfoFragmentsLeft = 0; + } + + if (MBX_MailboxSendReq((TMBX MBXMEM *) pSdoInfoInd, flags) != 0) + { + /* if the mailbox response could not be sent (or stored), the response will be + stored in the variable pCoeSendStored and will be sent automatically + from the mailbox handler (COE_ContinueInd) when the send mailbox will be read + the next time from the master */ + pCoeSendStored = (TMBX MBXMEM *) pSdoInfoInd; + } + + return 0; +} + +/** @} */ + + diff --git a/GD32E508_DCU_APP/device/motor_state.cpp b/GD32E508_DCU_APP/device/motor_state.cpp index 5392e80..7c58e40 100644 --- a/GD32E508_DCU_APP/device/motor_state.cpp +++ b/GD32E508_DCU_APP/device/motor_state.cpp @@ -148,11 +148,10 @@ uint16_t buildPayload(uint8_t page, uint8_t *buf, uint16_t maxLen) uint16_t setPushPeriodMs(uint16_t ms) { + /* 2026-08 请求驱动改造:0xC5 主动推送已移除,周期设置不再生效。 + * 仅钳制后回显,保留 0xFE 子命令协议兼容(ESP32 当前不发送)。 */ if (ms < kMotorPeriodMinMs) { ms = kMotorPeriodMinMs; } if (ms > kMotorPeriodMaxMs) { ms = kMotorPeriodMaxMs; } - /* ms→tick:100us/tick,每 ms 10 tick。写 volatile uint16_t 在 Cortex-M 上为原子, - * tick100us(ISR) 读该值无擕裂风险。 */ - g_scheduler.motor_state_period_ticks = static_cast(ms * kMotorTicksPerMs); return ms; } diff --git a/GD32E508_DCU_APP/device/spi/app_spi_cmd_adc_feedback.cpp b/GD32E508_DCU_APP/device/spi/app_spi_cmd_adc_feedback.cpp index 54794fd..99859a5 100644 --- a/GD32E508_DCU_APP/device/spi/app_spi_cmd_adc_feedback.cpp +++ b/GD32E508_DCU_APP/device/spi/app_spi_cmd_adc_feedback.cpp @@ -1,15 +1,15 @@ /** * @file app_spi_cmd_adc_feedback.cpp - * @brief GD32 ADC 调试反馈(温度 / VDDA / VIN)SPI 命令与 1Hz 主动推送。 + * @brief GD32 ADC 调试反馈(温度 / VDDA / VIN)SPI 被动查询处理器。 * * TYPE = SpiDefs::kTypeAdcFeedback (0xC4) * * 总开关:sys/config.h 中 CONFIG_SPI_ADC_FEEDBACK_ENABLE - * - 0:不注册 handler、不推送(本文件仍参与链接,函数空实现) - * - 1:1Hz 主动 requestEsp32Read(0xC4) + 支持 ESP32 被动查询 + * - 0:不注册 handler(本文件仍参与链接,函数空实现) + * - 1:ESP32 周期查询(1s),本 handler 被动回当前 ADC 快照 * - * 与 0xC1 CAN 反馈同款模型:主循环 pushStep 非阻塞;ring 未读走则跳过。 - * 优先级低于 CAN 反馈(runPending 里先 canFeedbackPushStep 再本函数)。 + * 2026-08 请求驱动改造:0xC4 不再主动按铃推送(原 1Hz pushStep 移除), + * 查询响应 seq 每次递增,供前端判断有新快照。 * * payload v1 固定 20 字节(小端): * [0] ver=1 @@ -19,7 +19,7 @@ * [6..7] vin_adc_mV uint16 引脚分压后电压 mV * [8..9] temp_raw uint16 * [10..11] vref_raw uint16 - * [12..15] seq uint32 推送序号 + * [12..15] seq uint32 快照序号(每次查询 +1) * [16..19] uptime_sec uint32 */ @@ -42,7 +42,6 @@ static constexpr uint8_t kPayloadVer = 1U; static constexpr uint16_t kPayloadLen = 20U; static uint32_t s_pushSeq = 0U; -static volatile bool s_needPush = false; /** * @brief 构建 0xC4 ADC 反馈 payload(20B)。 @@ -100,6 +99,7 @@ public: void handle(AppSpiCommand &bus, const SpiCommandContext &ctx) override { uint8_t payload[kPayloadLen] = {}; + ++s_pushSeq; /* 每次查询回一帧新序号,前端可据此判断有新快照 */ const uint16_t n = buildAdcFeedbackPayload(payload, sizeof(payload)); bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len, SpiDefs::kTypeAdcFeedback, payload, n); @@ -110,51 +110,8 @@ static SpiAdcFeedbackHandler s_handler; } // namespace -/** - * @brief 标记需要推送一帧 0xC4(由 1Hz taskHeartbeat 调用)。 - */ -void adcFeedbackStartRound() -{ - s_needPush = true; -} - -/** - * @brief 非阻塞推送一步:ring 空闲则 requestEsp32Read(0xC4)。 - * @note 主循环调用;与 CAN 反馈共用 ring,忙则跳过。 - */ -void adcFeedbackPushStep() -{ - if (!s_needPush) { - return; - } - if (OtaBootRequest::isRebootPending()) { - s_needPush = false; - return; - } - /* 电机 OTA relay 活跃期间停止 ADC 反馈推送,避免按铃干扰 0xD1 数据流 - * (详见 canFeedbackPushStep 同款注释)。 */ - if (MotorOtaRelay::isActive()) { - return; - } - /* 与 CAN 反馈共用 ring:未读走则等下一主循环,不抢占 */ - if (AppSpiCommandBus.isRingPending()) { - return; - } - - uint8_t payload[kPayloadLen] = {}; - const uint16_t n = buildAdcFeedbackPayload(payload, sizeof(payload)); - if (n == 0U) { - s_needPush = false; - return; - } - - ++s_pushSeq; - writeU32Le(&payload[12], s_pushSeq); - - if (AppSpiCommandBus.requestEsp32Read(SpiDefs::kTypeAdcFeedback, payload, n)) { - s_needPush = false; - } -} +/* 2026-08 请求驱动改造:0xC4 已改 ESP32 周期查询(1s,经 handler 被动响应), + * 主动按铃推送状态机整体移除(s_needPush/s_pushSeq 一并删除)。 */ /** @brief 注册 0xC4 处理器。 */ void registerSpiAdcFeedbackCommand(AppSpiCommand &bus) @@ -164,10 +121,6 @@ void registerSpiAdcFeedbackCommand(AppSpiCommand &bus) #else /* CONFIG_SPI_ADC_FEEDBACK_ENABLE == 0 */ -/** @brief 开关关闭时的空实现。 */ -void adcFeedbackStartRound() {} -/** @brief 开关关闭时的空实现。 */ -void adcFeedbackPushStep() {} /** @brief 开关关闭时不注册 handler。 */ void registerSpiAdcFeedbackCommand(AppSpiCommand & /*bus*/) {} diff --git a/GD32E508_DCU_APP/device/spi/app_spi_cmd_can_burst.cpp b/GD32E508_DCU_APP/device/spi/app_spi_cmd_can_burst.cpp new file mode 100644 index 0000000..05d3782 --- /dev/null +++ b/GD32E508_DCU_APP/device/spi/app_spi_cmd_can_burst.cpp @@ -0,0 +1,99 @@ +#include "device/spi/spi_command_handler.h" +#include "device/spi/app_spi_command.h" +#include "device/spi/app_spi_command_defs.h" +#include "device/canfd.h" +#include "sys/config.h" + +namespace { + +/* 单次突发最多帧数。SPI payload 上限 236B,DLC=32 时实际 ~7 帧;这里放宽到 16 作硬上限, + * 约束 EXTI ISR 内背靠背 transmitFrame 的总耗时。 */ +constexpr uint8_t kBurstMaxFrames = 16U; + +/** + * @brief CAN 跨通道突发发送处理器(TYPE 0xC7)。 + * + * 一次 SPI 事务携带多帧,GD32 在 CS 释放中断内对每一帧调用非阻塞的 + * CanFd::transmitFrame()(仅装载对应通道 CAN 邮箱、立即返回),各通道邮箱 + * 背靠背装载完成后几乎同时上线,实现跨通道「近同时」发送(组内偏差 <1ms)。 + * + * payload: + * [0] count 条目数(1..kBurstMaxFrames) + * [1] dlc 所有条目统一的数据长度(CAN FD 0..64,CMD 为 32) + * [2..3] suffix CAN ID 低 16 位,大端(CMD=0x0200) + * [4..] count 个条目,每条目 = chnode(1) + data[dlc] + * chnode = (ch<<4) | node,id = (node<<16) | suffix + * + * 响应 ACK:count(1) + 每条目 1 字节状态(CanTxResult 码,0x00=成功)。 + */ +class SpiCanBurstHandler final : public ISpiCommandHandler { +public: + bool canHandle(uint8_t frame_type) const override + { + return frame_type == SpiDefs::kTypeCanBurstSend; + } + + void handle(AppSpiCommand &bus, const SpiCommandContext &ctx) override + { + /* 头部至少 4 字节:count + dlc + suffix(2) */ + if (ctx.payload_len < 4U) { + uint8_t err = 0xE0U; + bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len, + SpiDefs::kRspError, &err, 1U); + return; + } + + const uint8_t count = ctx.payload[0]; + const uint8_t dlc = ctx.payload[1]; + const uint16_t suffix = static_cast( + (static_cast(ctx.payload[2]) << 8) | ctx.payload[3]); + + if (count == 0U || count > kBurstMaxFrames || dlc > 64U) { + uint8_t err = 0xE5U; + bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len, + SpiDefs::kRspError, &err, 1U); + return; + } + + /* payload 必须与 count*(1+dlc) 精确匹配,防截断/越界 */ + const uint16_t entry_len = static_cast(1U + dlc); + const uint16_t need = static_cast(4U + count * entry_len); + if (ctx.payload_len != need) { + uint8_t err = 0xE0U; + bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len, + SpiDefs::kRspError, &err, 1U); + return; + } + + uint8_t resp[1U + kBurstMaxFrames]; + resp[0] = count; + + const uint8_t *p = &ctx.payload[4]; + for (uint8_t i = 0U; i < count; ++i, p += entry_len) { + const uint8_t chnode = p[0]; + const uint8_t ch = static_cast((chnode >> 4) & 0x0FU); + const uint8_t node = static_cast(chnode & 0x0FU); + const uint32_t id = (static_cast(node) << 16) | suffix; + + if (ch >= 3U) { + resp[1U + i] = 0xE1U; /* 通道非法:跳过该帧,其余照常发送 */ + continue; + } + const CanFd::CanTxResult r = + CanFd::transmitFrame(ch, id, (dlc > 0U) ? &p[1] : nullptr, dlc); + resp[1U + i] = static_cast(r); + } + + bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len, + SpiDefs::kRspAck, resp, static_cast(1U + count)); + } +}; + +static SpiCanBurstHandler s_handler; + +} // namespace + +void registerSpiCanBurstCommand(AppSpiCommand &bus) +{ + bus.registerHandler(&s_handler); +} diff --git a/GD32E508_DCU_APP/device/spi/app_spi_cmd_can_feedback.cpp b/GD32E508_DCU_APP/device/spi/app_spi_cmd_can_feedback.cpp index d9d14aa..d539571 100644 --- a/GD32E508_DCU_APP/device/spi/app_spi_cmd_can_feedback.cpp +++ b/GD32E508_DCU_APP/device/spi/app_spi_cmd_can_feedback.cpp @@ -44,8 +44,8 @@ #include #include -/* ---- 文件内共享常量与辅助函数(供匿名命名空间内的 handler 与 - * 全局作用域的 canFeedbackPushStep() 共同复用) ---- */ +/* ---- 文件内共享常量与辅助函数(仅供本文件 handler 使用;原全局推送函数已随 + * 2026-08 请求驱动改造移除) ---- */ static constexpr uint8_t kFeedbackChannelCount = 3U; /* 业务 payload 上限:APP TX 缓冲 256 - v2 帧开销 20 = 236(与 ESP32 kFeedbackPayloadLen 一致) */ @@ -243,104 +243,8 @@ static SpiCanFeedbackHandler s_handler; } // namespace -/** - * @brief CAN 反馈主动推送一步(非阻塞)。 - * - * 由主循环每次迭代调用。维护内部 (channel, page) 游标,循环遍历 3 通道 × 2 页。 - * 若上一帧按铃尚未被 ESP32 读走(m_ringPending)则立即返回等待; - * 否则构建当前页反馈 payload 并通过 requestEsp32Read(0xC1) 主动推送到 ESP32, - * 然后推进游标。ESP32 检测到 SPIready 下降沿后发起读事务取走该帧, - * CS 释放后 GD32 中断唤醒主循环继续推送下一帧。 - * - * 1Hz 节流由任务调度器驱动:taskHeartbeat() 每秒调用 canFeedbackStartRound() - * 激活新轮次,canFeedbackPushStep() 推完一轮(回到 ch=0,page=0)后清除标志。 - * 与 CanFd::refreshFeedbackSnapshot() 的 1Hz 刷新对齐,避免重复推同一个 seq。 - */ -static volatile bool s_roundActive = true; /* 首次允许启动,无需等 1s */ - -/** - * @brief 激活新一轮 1Hz CAN 反馈推送(taskHeartbeat 调用)。 - */ -void canFeedbackStartRound() -{ - s_roundActive = true; -} - -/** - * @brief 非阻塞推送一步 0xC1(单页);跳过未使能通道。 - * @note 主循环调用;ring 忙或本轮未激活则立即返回。 - */ -void canFeedbackPushStep() -{ - static uint8_t s_channel = 0U; - static uint8_t s_page = 0U; - - /* 已收到 0xB0 进入 Bootloader 请求,停止主动按铃避免干扰 OTA 流程。 - * 复位前主循环仍在运行,但不再下拉 SPIready,让 ESP32 的 - * waitForBootloaderReady 信号检测不受干扰。 */ - if (OtaBootRequest::isRebootPending()) { - return; - } - - /* 电机 OTA relay 活跃期间停止 CAN 反馈主动推送: - * OTA 数据流(0xD1)在 SPI 上是紧循环 stop-and-wait,本推送会拉低 PB6 按 - * 铃、占用 m_txBuf,与 0xD1 的 ACK 响应争用单缓冲;ESP32 的 send/poll - * 两步法会把 0xC1 EVENT 帧当作 0xD1 响应误读,导致 feedTask 误判失败。 - * OTA 结束后 s_roundActive 由 taskHeartbeat 重新激活。 */ - if (MotorOtaRelay::isActive()) { - return; - } - - /* 1Hz 节流:当前轮次未激活则等待 taskHeartbeat() 触发 */ - if (!s_roundActive) { - return; - } - - /* 上一帧尚未被 ESP32 读走,等待(不占用主线带宽) */ - if (AppSpiCommandBus.isRingPending()) { - return; - } - - /* 跳过编译期关闭的通道(与 APP PING V2_FEAT_CAN_CHx / CONFIG_CAN_CHx_ENABLE 一致) */ - if (!CanFd::isChannelEnabled(s_channel)) { - s_page = 0U; - for (;;) { - ++s_channel; - if (s_channel >= kFeedbackChannelCount) { - s_channel = 0U; - s_roundActive = false; - return; - } - if (CanFd::isChannelEnabled(s_channel)) { - break; - } - } - } - - uint8_t payload[kFeedbackPayloadLen] = {}; - uint16_t payload_len = buildFeedbackPayload(s_channel, s_page, - payload, sizeof(payload)); - if (payload_len > 0U) { - (void)AppSpiCommandBus.requestEsp32Read(SpiDefs::kTypeCanFeedback, - payload, payload_len); - } - - /* 推进:每通道 2 页,仅遍历使能通道;到末通道后结束本秒轮次 */ - if (++s_page >= 2U) { - s_page = 0U; - for (;;) { - ++s_channel; - if (s_channel >= kFeedbackChannelCount) { - s_channel = 0U; - s_roundActive = false; - break; - } - if (CanFd::isChannelEnabled(s_channel)) { - break; - } - } - } -} +/* 2026-08 请求驱动改造:0xC1 CAN 反馈已改 ESP32 请求驱动(handleFeedback 被动 + * 响应),主动推送早已禁用;本次随 0xC5/0xC4 一并移除全部推送残留。 */ /** * @brief 注册 0xC1/0xC2 CAN 反馈与模式切换处理器。 diff --git a/GD32E508_DCU_APP/device/spi/app_spi_cmd_exmc_diag.cpp b/GD32E508_DCU_APP/device/spi/app_spi_cmd_exmc_diag.cpp new file mode 100644 index 0000000..5bf247a --- /dev/null +++ b/GD32E508_DCU_APP/device/spi/app_spi_cmd_exmc_diag.cpp @@ -0,0 +1,42 @@ +/** + * @file app_spi_cmd_exmc_diag.cpp + * @brief SPI 0xA2 EXMC/HBI 诊断命令处理器:驱动 LAN9252 通断测试 pattern。 + * + * 网页按钮 → ESP32 /spi/send → SPI 0xA2 → 本 handler → EscExmcBus::continuityDrive() + * 立即返回 ACK,不阻塞 SPI 总线。电平保持到下次调用或 action=0(stop)。 + */ +#include "device/spi/spi_command_handler.h" +#include "device/spi/app_spi_command.h" +#include "device/spi/app_spi_command_defs.h" +#include "comm/esc_exmc_bus.h" + +namespace { + +/** @brief SPI 0xA2 EXMC 诊断处理器。 */ +class SpiExmcDiagHandler final : public ISpiCommandHandler { +public: + bool canHandle(uint8_t frame_type) const override + { + return frame_type == SpiDefs::kTypeExmcDiag; + } + + void handle(AppSpiCommand &bus, const SpiCommandContext &ctx) override + { + uint8_t status = 0xEE; + if (ctx.payload_len >= 1) { + status = EscExmcBus::continuityDrive(ctx.payload[0]); + } + bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len, + SpiDefs::kRspAck, &status, 1); + } +}; + +static SpiExmcDiagHandler s_handler; + +} // namespace + +/** @brief 注册 0xA2 EXMC 诊断命令处理器。 */ +void registerSpiExmcDiagCommand(AppSpiCommand &bus) +{ + bus.registerHandler(&s_handler); +} diff --git a/GD32E508_DCU_APP/device/spi/app_spi_cmd_motor_state.cpp b/GD32E508_DCU_APP/device/spi/app_spi_cmd_motor_state.cpp index 193f8d7..0947ff2 100644 --- a/GD32E508_DCU_APP/device/spi/app_spi_cmd_motor_state.cpp +++ b/GD32E508_DCU_APP/device/spi/app_spi_cmd_motor_state.cpp @@ -1,11 +1,9 @@ /** * @file app_spi_cmd_motor_state.cpp - * @brief 多电机状态 250ms 主动推送 (TYPE 0xC5) 与被动查询处理器。 + * @brief 多电机状态查询处理器 (TYPE 0xC5)。 * - * 与 0xC1 CAN 反馈 / 0xC4 ADC 反馈同款模型: - * - 主循环 pushStep 非阻塞;ring 未读走则跳过。 - * - 优先级:CAN feedback > Motor state > ADC(runPending 调用顺序决定)。 - * - 启动节拍:taskMotorStatePush()(250ms 独立任务位,与 1Hz heartbeat 解耦)。 + * 2026-08 请求驱动改造:0xC5 由 ESP32 周期查询(250ms,ESP32 can_fb 任务), + * 本 handler 被动响应(请求 payload[0]=page,回该页快照);不再主动按铃推送。 * - 总开关:sys/config.h 中 CONFIG_MOTOR_STATE_ENABLE * * payload 格式 (每页): @@ -81,61 +79,8 @@ static SpiMotorStateHandler s_handler; } // namespace -/* ---- 主动推送状态机 ---- */ - -static volatile bool s_roundActive = true; /* 首次允许启动,无需等 1s */ -static uint8_t s_page = 0U; - -/** - * @brief 激活新一轮 0xC5 推送。 - * @note 若上一轮仍在进行中(s_roundActive=true),不重置 s_page, - * 避免在 250ms 节拍 < 单轮 P0+P1 排空时间时反复打断、 - * 导致 P1 永远没机会推(前端只看到 P0 的 5 个节点)。 - * 新一轮请求会被合并到当前轮次,推完 P1 后自然进入下一轮。 - * 这与 0xC1 canFeedbackStartRound() 的语义不同:CAN 反馈 1Hz - * 远大于排空时间,不会发生自打断;0xC5 在 250ms 节拍下需要这层保护。 - */ -void motorStateStartRound() -{ - s_roundActive = true; - /* 仅在轮次已结束时重置页号;进行中保持 s_page 不动 */ - if (s_page >= kMotorTotalPages) { - s_page = 0U; - } -} - -void motorStatePushStep() -{ - if (!s_roundActive) { - return; - } - if (OtaBootRequest::isRebootPending()) { - s_roundActive = false; - return; - } - /* 电机 OTA relay 活跃期间停止 motor state 推送,避免按铃干扰 0xD1 数据流 - * (详见 canFeedbackPushStep 同款注释)。 */ - if (MotorOtaRelay::isActive()) { - return; - } - if (AppSpiCommandBus.isRingPending()) { - return; - } - - uint8_t payload[kMotorPayloadMax] = {}; - const uint16_t n = MotorState::buildPayload(s_page, payload, sizeof(payload)); - - if (n > 0U) { - (void)AppSpiCommandBus.requestEsp32Read(SpiDefs::kTypeMotorState, payload, n); - } - - /* 推进页号;到末页后结束本轮 */ - s_page++; - if (s_page >= kMotorTotalPages) { - s_page = 0U; - s_roundActive = false; - } -} +/* 2026-08 请求驱动改造:0xC5 已改 ESP32 周期查询(经 handler 被动响应), + * 主动按铃推送状态机整体移除。 */ void registerSpiMotorStateCommand(AppSpiCommand &bus) { @@ -144,8 +89,6 @@ void registerSpiMotorStateCommand(AppSpiCommand &bus) #else /* CONFIG_MOTOR_STATE_ENABLE == 0 */ -void motorStateStartRound() {} -void motorStatePushStep() {} void registerSpiMotorStateCommand(AppSpiCommand & /*bus*/) {} #endif diff --git a/GD32E508_DCU_APP/device/spi/app_spi_cmd_motor_version.cpp b/GD32E508_DCU_APP/device/spi/app_spi_cmd_motor_version.cpp index c54a159..ff47d77 100644 --- a/GD32E508_DCU_APP/device/spi/app_spi_cmd_motor_version.cpp +++ b/GD32E508_DCU_APP/device/spi/app_spi_cmd_motor_version.cpp @@ -44,7 +44,7 @@ void motorVersionPushStep() return; } /* 电机 OTA relay 活跃期间停止版本推送,避免按铃干扰 0xD1 数据流 - * (详见 canFeedbackPushStep 同款注释)。dirty 保留不丢,OTA 结束后继续推。 */ + * (0xC5/0xC4 已改请求驱动,0xC6 保留本门控)。dirty 保留不丢,OTA 结束后继续推。 */ if (MotorOtaRelay::isActive()) { return; } diff --git a/GD32E508_DCU_APP/device/spi/app_spi_command.cpp b/GD32E508_DCU_APP/device/spi/app_spi_command.cpp index 597c949..7cbc382 100644 --- a/GD32E508_DCU_APP/device/spi/app_spi_command.cpp +++ b/GD32E508_DCU_APP/device/spi/app_spi_command.cpp @@ -383,11 +383,13 @@ extern void registerSpiBootRequestCommand(AppSpiCommand &bus); extern void registerSpiOtaWaitCommand(AppSpiCommand &bus); extern void registerSpiAppPingCommand(AppSpiCommand &bus); extern void registerSpiCanBridgeCommand(AppSpiCommand &bus); +extern void registerSpiCanBurstCommand(AppSpiCommand &bus); extern void registerSpiCanFeedbackCommand(AppSpiCommand &bus); extern void registerSpiIdSnapshotCommand(AppSpiCommand &bus); extern void registerSpiAdcFeedbackCommand(AppSpiCommand &bus); extern void registerSpiMotorStateCommand(AppSpiCommand &bus); extern void registerSpiMotorOtaCommand(AppSpiCommand &bus); +extern void registerSpiExmcDiagCommand(AppSpiCommand &bus); AppSpiCommand AppSpiCommandBus; void AppSpiCommand::init() { @@ -406,11 +408,13 @@ void AppSpiCommand::init() registerSpiOtaWaitCommand(AppSpiCommandBus); registerSpiAppPingCommand(AppSpiCommandBus); registerSpiCanBridgeCommand(AppSpiCommandBus); + registerSpiCanBurstCommand(AppSpiCommandBus); registerSpiCanFeedbackCommand(AppSpiCommandBus); registerSpiIdSnapshotCommand(AppSpiCommandBus); registerSpiAdcFeedbackCommand(AppSpiCommandBus); /* CONFIG 关时为空注册 */ registerSpiMotorStateCommand(AppSpiCommandBus); /* CONFIG 关时为空注册 */ registerSpiMotorOtaCommand(AppSpiCommandBus); + registerSpiExmcDiagCommand(AppSpiCommandBus); configureGpio(); configureDma(); @@ -486,7 +490,8 @@ void AppSpiCommand::registerHandler(ISpiCommandHandler *handler) void AppSpiCommand::finishResponseAndReady() { - /* 有响应待发时 arm 按铃,阻止 canFeedbackPushStep 覆盖 m_txBuf; + /* 有响应待发时 arm 按铃(ESP32 主动请求的响应即靠此通知; + * 推送源已随 2026-08 请求驱动改造移除,无其他写者争用 m_txBuf)。 * updateDataReady 按 m_txLen/m_ringPending 驱动 PB6。 * 延迟日志由 poll() 无条件消费 m_logPending,无需再置任务位。 */ if (m_txLen > 0U) { diff --git a/GD32E508_DCU_APP/device/spi/app_spi_command.h b/GD32E508_DCU_APP/device/spi/app_spi_command.h index c09e6b4..3d29590 100644 --- a/GD32E508_DCU_APP/device/spi/app_spi_command.h +++ b/GD32E508_DCU_APP/device/spi/app_spi_command.h @@ -148,43 +148,14 @@ private: void updateDataReady(); }; -/** - * @brief CAN 反馈主动推送一步(非阻塞,由主循环每次迭代调用)。 - * - * GD32 APP 主动循环推送 3 通道 × 2 页 CAN 反馈帧到 ESP32,替代 ESP32 - * 主动轮询。每次调用推进一帧:若上一帧尚未被 ESP32 读走则立即返回, - * 否则构建当前 (channel, page) 的反馈 payload 并 requestEsp32Read() 推送, - * 然后推进到下一页/下一通道。 - * - * 1Hz 节流由 taskHeartbeat() 调用 canFeedbackStartRound() 激活每秒一轮, - * 推完一轮后内部清除激活标志,等待下一次 taskHeartbeat() 触发。 - */ -void canFeedbackPushStep(); - -/** @brief 激活新一轮 CAN 反馈推送(由 1Hz taskHeartbeat 调用)。 */ -void canFeedbackStartRound(); - -/** - * @brief ADC 调试反馈(0xC4)主动推送一步。 - * @note CONFIG_SPI_ADC_FEEDBACK_ENABLE=0 时为空操作。 - */ -void adcFeedbackPushStep(); - -/** @brief 激活一次 ADC 调试反馈推送(1Hz taskHeartbeat)。 */ -void adcFeedbackStartRound(); - -/** - * @brief 多电机状态(0xC5)主动推送一步。 - * @note CONFIG_MOTOR_STATE_ENABLE=0 时为空操作。 - */ -void motorStatePushStep(); - -/** @brief 激活新一轮多电机状态推送(1Hz taskHeartbeat)。 */ -void motorStateStartRound(); +/* 2026-08 请求驱动改造:0xC1 CAN 反馈 / 0xC5 电机状态 / 0xC4 ADC 已改 + * ESP32 周期查询(本端仅被动响应 handler),主动按铃推送函数整体移除 + * (canFeedbackPushStep/canFeedbackStartRound/adcFeedbackPushStep/ + * adcFeedbackStartRound/motorStatePushStep/motorStateStartRound)。 */ /** * @brief 多电机版本(0xC6)事件驱动推送步进:有 dirty 版本且 ring 空闲时按铃推一帧。 - * @note CONFIG_MOTOR_VERSION_ENABLE=0 时为空操作。优先级低于 0xC1/0xC5/0xC4。 + * @note CONFIG_MOTOR_VERSION_ENABLE=0 时为空操作。低频、事件驱动,保留推送。 */ void motorVersionPushStep(); diff --git a/GD32E508_DCU_APP/device/spi/app_spi_command_defs.h b/GD32E508_DCU_APP/device/spi/app_spi_command_defs.h index 4366769..2142a67 100644 --- a/GD32E508_DCU_APP/device/spi/app_spi_command_defs.h +++ b/GD32E508_DCU_APP/device/spi/app_spi_command_defs.h @@ -19,6 +19,10 @@ static constexpr uint8_t kTypeOtaReq = 0x01U; static constexpr uint8_t kTypeOtaData = 0x02U; static constexpr uint8_t kTypeOtaCancel = 0x03U; static constexpr uint8_t kTypeAppPing = 0xA1U; +/** @brief EXMC/HBI 诊断命令(ESP32→GD32):payload[0]=action。 + * 0=stop(恢复AF_PP), 1-4=数据pattern(0xFFFF/0x0000/0xAAAA/0x5555), + * 5=CS低, 6=RD低, 7=WR低, 8=A1高, 9=A2高。用于 LAN9252 通断测试。 */ +static constexpr uint8_t kTypeExmcDiag = 0xA2U; static constexpr uint8_t kTypeBootReq = 0xB0U; static constexpr uint8_t kTypeCanSend = 0xC0U; static constexpr uint8_t kTypeCanFeedback = 0xC1U; /**< ESP32 -> GD32 轮询 CAN 反馈快照 */ @@ -35,6 +39,13 @@ static constexpr uint8_t kTypeMotorState = 0xC5U; * 由 CONFIG_MOTOR_VERSION_ENABLE 门控。 */ static constexpr uint8_t kTypeMotorVersion = 0xC6U; +/** @brief CAN 跨通道突发发送(ESP32→GD32):一次 SPI 事务背靠背入多个 CAN 邮箱, + * 实现跨通道近同时发送(组内 <1ms)。 + * payload: count(1) dlc(1) suffix_hi(1) suffix_lo(1) 然后 count 个条目, + * 每条目 = chnode(1) + data[dlc];chnode=(ch<<4)|node,id=(node<<16)|suffix。 + * 响应 ACK: count(1) + 每条目 1 字节状态(CanTxResult 码)。 */ +static constexpr uint8_t kTypeCanBurstSend = 0xC7U; + /** @brief 电机 OTA relay 命令(ESP32→GD32):start / cancel / query。 * payload[0]=cmd(0=start,1=cancel,2=query), start 时 payload[1]=ch, payload[2..5]=fw_size LE */ static constexpr uint8_t kTypeMotorOtaCmd = 0xD0U; diff --git a/GD32E508_DCU_APP/main.h b/GD32E508_DCU_APP/main.h index 4b8b4f7..364250e 100644 --- a/GD32E508_DCU_APP/main.h +++ b/GD32E508_DCU_APP/main.h @@ -18,7 +18,6 @@ enum system_task_t { TASK_CAN_RX_PARSE, /**< 解析并消费已接收 CANFD 帧任务。 */ TASK_CAN_TX_SEND, /**< CANFD 周期发送任务,每 10ms 每通道发送一帧。 */ TASK_HEARTBEAT, /**< 系统心跳维护任务。 */ - TASK_MOTOR_STATE_PUSH, /**< 多电机状态 0xC5 主动推送启动任务(250ms)。 */ TASK_DIAGNOSTIC_PRINT, /**< 诊断信息打印任务。 */ TASK_ETHERCAT_PROCESS, /**< EtherCAT 周期/事件处理任务(非实时应用层 PDO 映射)。 */ TASK_COUNT /**< 任务总数,用于边界或统计场景。 */ @@ -46,11 +45,6 @@ struct task_scheduler_t { volatile uint16_t div_can_tx; /** @brief 调度分频计数:心跳(1s)。 */ volatile uint16_t div_heartbeat; - /** @brief 调度分频计数:多电机状态推送(默认 250ms)。 */ - volatile uint16_t div_motor_state; - /** @brief 0xC5 推送周期(100us tick 数,运行时可由 ESP32 SPI 命令调整)。 - * 默认 CONFIG_TASK_PERIOD_MOTOR_STATE_TICKS,范围 500(50ms)~50000(5000ms)。 */ - volatile uint16_t motor_state_period_ticks; /** @brief 调度分频计数:APP 状态/诊断打印(5s)。 */ volatile uint16_t div_diag_print; }; diff --git a/GD32E508_DCU_APP/sys/config.h b/GD32E508_DCU_APP/sys/config.h index faa799f..4cd5163 100644 --- a/GD32E508_DCU_APP/sys/config.h +++ b/GD32E508_DCU_APP/sys/config.h @@ -115,7 +115,7 @@ constexpr bool CONFIG_CAN_CH2_ENABLE = true; * ============================================================ */ constexpr uint32_t APP_FW_VERSION_MAJOR = 1U; constexpr uint32_t APP_FW_VERSION_MINOR = 5U; -constexpr uint32_t APP_FW_VERSION_PATCH = 5U; /* 1.5.5:电机 OTA relay SPI 断流超时(0x26) + 0x103 心跳不再为无包在途的 kTransfer 续命 */ +constexpr uint32_t APP_FW_VERSION_PATCH = 15U; /* 1.5.15:EXMC 通断测试新增 MODE0/MODE1 strap 驱动测试(action 0x0A-0x0D, PB8/PB9) */ constexpr uint32_t APP_FW_VERSION_BUILD = 0U; @@ -303,13 +303,16 @@ constexpr uint32_t CONFIG_ESC_PDI_OUTPUT_OFFSET = 0x1200U; constexpr size_t CONFIG_ETHERCAT_PDO_SIZE = 32U; /* ---------- EXMC 引脚配置(GD32E508 EXMC 标准映射) ---------- */ -constexpr uint32_t CONFIG_ESC_EXMC_BASE_ADDR = 0x64000000U; -/* Region1 = NE1 @ 0x64000000(GD32E50x 用户手册 Rev2.2 图25-3: Bank0 四 Region 各 64MB, - * HADDR[27:26]=01 选中 NE1)。LAN9252 CS 接 PD7=EXMC_NE1,故基址为 0x64000000。 */ -#ifndef EXMC_BANK0_NORSRAM_REGION1 -#define EXMC_BANK0_NORSRAM_REGION1 ((uint32_t)0x00000001U) +constexpr uint32_t CONFIG_ESC_EXMC_BASE_ADDR = 0x60000000U; +/* Region0 = NE0 @ 0x60000000(GD32E50x 用户手册 Rev2.2 图25-3: Bank0 四 Region 各 64MB, + * HADDR[27:26]=00 选中 NE0)。LAN9252 CS 接 PD7=EXMC_NE0(数据手册 GD32E508xx + * Datasheet Rev1.8 LQFP100 引脚表: PD7 Alternate2 = EXMC_NE0/EXMC_NCE1)。 + * 注意: GD32E508Vx (LQFP100) 封装只引出 NE0 一个 NOR/SRAM 片选引脚, + * NE1~NE3 仅在 LQFP144 (Zx) 上存在;因此必须使用 Region0(SNCTL0),不能用 Region1。 */ +#ifndef EXMC_BANK0_NORSRAM_REGION0 +#define EXMC_BANK0_NORSRAM_REGION0 ((uint32_t)0x00000000U) #endif -#define CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION1 EXMC_BANK0_NORSRAM_REGION1 +#define CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION0 EXMC_BANK0_NORSRAM_REGION0 /* ---------- ESC 中断引脚(PB0/PB1/PB2,三个独立 EXTI 向量) ---------- */ /** @note 保留 PA0 给未来 STANDBY 模式唤醒(WKUP_PIN0)。 */ diff --git a/GD32E508_DCU_APP/sys/initial.cpp b/GD32E508_DCU_APP/sys/initial.cpp index 660e495..5a01204 100644 --- a/GD32E508_DCU_APP/sys/initial.cpp +++ b/GD32E508_DCU_APP/sys/initial.cpp @@ -20,9 +20,15 @@ #include "sys/ota_boot_request.h" #include "sys/task_manager.h" #include "sys/config.h" +#include "comm/esc_lan9252.h" #include "comm/ethercat_slave.h" #include "comm/ethercat_sync.h" +extern "C" { +uint16_t MainInit(void); +void MainLoop(void); +} + uint32_t AppInitializer::s_mask = 0U; namespace { @@ -137,11 +143,20 @@ void AppInitializer::init() SchedulerTimer::init(); mark(kScheduler); - EthercatSlave::init(); - if (EthercatSlave::isEscReady()) { - mark(kEthercat); - EthercatSync::init(); - mark(kEthercatSync); + /* ---- EtherCAT: EXMC 总线探活 → SSC 协议栈初始化 → SYNC0/IRQ 中断使能 ---- + * EEPROM 校验/烧录延迟到主循环执行,避免阻塞 init(烧录需 ~20s)。 */ + if (EscLan9252::init()) { + const uint16_t sscResult = MainInit(); + if (sscResult == 0U) { + mark(kEthercat); + EthercatSync::init(); + mark(kEthercatSync); + printf("[ECAT] SSC MainInit OK, sync ISR enabled\r\n"); + } else { + printf("[ECAT] SSC MainInit FAIL (code=0x%04X)\r\n", (unsigned)sscResult); + } + } else { + printf("[ECAT] EXMC bus not ready, SSC skipped\r\n"); } Watchdog::init(); diff --git a/GD32E508_DCU_APP/sys/task_manager.cpp b/GD32E508_DCU_APP/sys/task_manager.cpp index 5231017..e5dc3f7 100644 --- a/GD32E508_DCU_APP/sys/task_manager.cpp +++ b/GD32E508_DCU_APP/sys/task_manager.cpp @@ -32,8 +32,6 @@ void TaskManager::init() g_scheduler.div_can_rx = 0U; g_scheduler.div_can_tx = 0U; g_scheduler.div_heartbeat = 0U; - g_scheduler.div_motor_state = 0U; - g_scheduler.motor_state_period_ticks = CONFIG_TASK_PERIOD_MOTOR_STATE_TICKS; g_scheduler.div_diag_print = 0U; } @@ -107,13 +105,6 @@ void TaskManager::tick100us() set(1UL << TASK_HEARTBEAT); } - /* 多电机状态 0xC5 推送:250ms 独立节拍。 - * 不挂在 1Hz heartbeat 上,避免连带加速 OTA Boot 轮询/CAN快照/ADC推送/checkOffline。 */ - if(++g_scheduler.div_motor_state >= g_scheduler.motor_state_period_ticks) { - g_scheduler.div_motor_state = 0U; - set(1UL << TASK_MOTOR_STATE_PUSH); - } - /* 始终 5s 调度诊断任务:默认静默检查错误增量,PERIODIC_PRINT=1 时打全量摘要 */ if(++g_scheduler.div_diag_print >= CONFIG_TASK_PERIOD_DIAG_PRINT_TICKS) { g_scheduler.div_diag_print = 0U; diff --git a/GD32E508_DCU_APP/sys/task_runner.cpp b/GD32E508_DCU_APP/sys/task_runner.cpp index aee20b9..f89ed37 100644 --- a/GD32E508_DCU_APP/sys/task_runner.cpp +++ b/GD32E508_DCU_APP/sys/task_runner.cpp @@ -16,6 +16,13 @@ #include "sys/task_manager.h" #include "sys/task_runner.h" #include "comm/ethercat_slave.h" +#include "comm/esc_eeprom.h" +#include "comm/esc_exmc_bus.h" +#include "comm/ssc/port/eeprom_image.h" + +extern "C" { +void MainLoop(void); +} namespace { constexpr uint32_t kAdcFilterWindow = CONFIG_ADC_FILTER_SIZE + 1U; @@ -240,33 +247,17 @@ void TaskRunner::taskCanRxParse() * @param[in] 无。 * @param[out] 无。 * @retval 无返回值。 - * @note 推进 OTA Boot 请求、刷新 CAN 反馈快照、启动 CAN/ADC 1Hz 推送轮次。 + * @note 推进 OTA Boot 请求、刷新 CAN 反馈快照、启动 ADC 1Hz 推送轮次。 */ void TaskRunner::taskHeartbeat() { OtaBootRequest::process(); CanFd::refreshFeedbackSnapshot(); - canFeedbackStartRound(); - /* ADC 调试反馈 1Hz:CONFIG_SPI_ADC_FEEDBACK_ENABLE=0 时为空操作 */ - adcFeedbackStartRound(); #if (CONFIG_MOTOR_STATE_ENABLE != 0) MotorState::checkOffline(); #endif } -/** - * @brief 启动多电机状态 0xC5 推送轮次(250ms 独立节拍)。 - * @note 与 taskHeartbeat 解耦:不连带加速 OTA Boot 轮询 / CAN 快照 / ADC 推送 / - * checkOffline 离线判定(3s 阈值)。仅触发本轮 P0/P1 分页推送, - * 实际 SPI 传输由 motorStatePushStep() 在主循环按铃协议推进。 - */ -void TaskRunner::taskMotorStatePush() -{ -#if (CONFIG_MOTOR_STATE_ENABLE != 0) - motorStateStartRound(); -#endif -} - /** * @brief 拷贝当前 ADC 工程量快照(供 SPI 0xC4 等只读路径)。 * @param[out] out 非空时写入最新 Sample @@ -477,8 +468,44 @@ void TaskRunner::runPending() { Watchdog::feed(); + /* === HBI 周期诊断:每 20 秒重新跑一次 EXMC/HBI 探活,方便热插拔/焊接后观察 === */ + static uint32_t s_lastDiagSec = 0xFFFFFFFFU; + if (g_app_uptime_sec >= s_lastDiagSec + 20U || s_lastDiagSec == 0xFFFFFFFFU) { + s_lastDiagSec = g_app_uptime_sec; + printf("[ECAT][DIAG] === 20s periodic HBI probe (uptime=%lus) ===\r\n", + (unsigned long)g_app_uptime_sec); + EscExmcBus::diagDump(); + } + + /* 延迟 EEPROM 烧录:init 不阻塞,主循环第一轮执行一次。 + * 校验编译期镜像与 AT24C32 内容,不匹配则全量烧录(~20s,期间看门狗正常喂)。 */ + static bool s_eepromDone = false; + if (!s_eepromDone) { + s_eepromDone = true; + if (EscEeprom::init()) { + if (EscEeprom::verifyEsi(ESI_EEPROM_IMAGE, ESI_EEPROM_SIZE)) { + printf("[ECAT][EEPROM] content OK, skip burn\r\n"); + } else { + printf("[ECAT][EEPROM] content mismatch, burning ESI (%u bytes)...\r\n", + (unsigned)ESI_EEPROM_SIZE); + if (EscEeprom::writeEsi(ESI_EEPROM_IMAGE, ESI_EEPROM_SIZE)) { + if (EscEeprom::verifyEsi(ESI_EEPROM_IMAGE, ESI_EEPROM_SIZE)) { + EscEeprom::reload(); + printf("[ECAT][EEPROM] burn+verify OK, reload issued\r\n"); + } else { + printf("[ECAT][EEPROM] post-burn verify FAIL\r\n"); + } + } else { + printf("[ECAT][EEPROM] burn FAIL\r\n"); + } + } + } else { + printf("[ECAT][EEPROM] init FAIL (PDI control not acquired)\r\n"); + } + } + /* poll() 含按铃超时看门狗(cancelRing),必须每轮无条件执行。 - * 放在 canFeedbackPushStep() 前确保卡死的按铃先被清掉再推送下一帧。 */ + * 主动推送已随 2026-08 请求驱动改造移除,此处仅需消费请求响应的按铃。 */ AppSpiCommandBus.poll(); /* 0xB0 进 BOOT:必须每圈检查,不能只靠 1Hz TASK_HEARTBEAT。 @@ -494,26 +521,15 @@ void TaskRunner::runPending() * (替代原靠 tick100us 计数 + TASK_MOTOR_OTA_TX 的方式,消除计数器竞争)。 */ MotorOtaRelay::onTxTick(); - /* CAN 反馈主动推送:GD32 APP 循环推送 3 通道 × 2 页反馈帧到 ESP32, - * 替代 ESP32 主动轮询。非阻塞:上一帧未读走时立即返回。 - * 每次主循环迭代调用一次,ESP32 读走一帧后 CS 释放中断唤醒主循环继续推送。 */ - canFeedbackPushStep(); - /* 多电机状态(0xC5):优先级介于 CAN 反馈与 ADC 之间;ring 忙则跳过。 */ - motorStatePushStep(); - /* ADC 调试反馈(0xC4):优先级低于 CAN;ring 忙则跳过。可 config 关闭。 */ - adcFeedbackPushStep(); - /* 多电机版本(0xC6):事件驱动、低频,优先级最低;无 dirty 版本时立即返回。 */ + /* 2026-08 请求驱动改造:0xC1 CAN 反馈 / 0xC5 电机状态 / 0xC4 ADC 已改为 + * ESP32 周期查询(主动按铃推送全部移除,避免 OTA 期间悬空按铃刷屏)。 + * 仅保留事件驱动、低频的 0xC6 电机版本推送。 */ motorVersionPushStep(); - /* 周期路径( SYNC0 ISR preempt=0 )用 s_periodicSeq 序列号通知主循环, - * 不再走 task_flags,避免 BASEPRI 临界区与 preempt=0 的 RMW 竞态。 - * 优先于 takePending() 处理,确保周期数据及时消费。 - * ESC 未焊接时 selfTest 失败,isEscReady()=false,中断未使能, - * s_periodicSeq 不会递增,此分支自然不触发——双重守卫仅为防御性编程。 */ - if (EthercatSlave::isEscReady() && EthercatSlave::hasPendingPeriodic()) { - EthercatSlave::markPeriodicProcessed(); - taskEthercatProcess(); - } + /* SSC 协议栈主循环:AL 状态机 + 邮箱处理 + FreeRun PDO。 + * 每轮无条件调用,SSC 内部自行判断是否有待处理事件。 + * DC 模式下 PDO 交换由 SYNC0 ISR (Sync0_Isr) 完成,此处仅处理低优先级逻辑。 */ + MainLoop(); const uint32_t pending = TaskManager::takePending(); if(pending == 0U) return; @@ -524,7 +540,5 @@ void TaskRunner::runPending() if(pending & (1UL << TASK_CAN_RX_PARSE)) taskCanRxParse(); if(pending & (1UL << TASK_CAN_TX_SEND)) taskCanTxSend(); if(pending & (1UL << TASK_HEARTBEAT)) taskHeartbeat(); - if(pending & (1UL << TASK_MOTOR_STATE_PUSH)) taskMotorStatePush(); if(pending & (1UL << TASK_DIAGNOSTIC_PRINT)) taskDiagnosticPrint(); - if(pending & (1UL << TASK_ETHERCAT_PROCESS)) taskEthercatProcess(); } diff --git a/GD32E508_DCU_APP/sys/task_runner.h b/GD32E508_DCU_APP/sys/task_runner.h index 2ac3d59..fe17266 100644 --- a/GD32E508_DCU_APP/sys/task_runner.h +++ b/GD32E508_DCU_APP/sys/task_runner.h @@ -50,8 +50,6 @@ private: static void taskCanTxSend(); /** @brief 心跳维护任务。 */ static void taskHeartbeat(); - /** @brief 多电机状态 0xC5 主动推送启动(250ms 独立节拍)。 */ - static void taskMotorStatePush(); /** @brief 诊断打印任务。 */ static void taskDiagnosticPrint(); /** @brief EtherCAT 周期/事件处理(非实时应用层 PDO 映射)。 */