feat(project): 更新项目配置并增加ESC EEPROM功能

- 添加新的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初始化逻辑,添加重试机制和诊断功能
This commit is contained in:
cbb
2026-08-09 03:27:21 +08:00
parent 8e5da18100
commit a2d0b28b3d
49 changed files with 13258 additions and 554 deletions
@@ -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.
+108 -42
View File
@@ -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)
+433 -101
View File
@@ -8,133 +8,465 @@
*/
#include "comm/esc_exmc_bus.h"
#include "sys/config.h"
#include "device/watchdog.h"
#include <stdio.h>
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<<kPinCs) | (1U<<kPinRd) | (1U<<kPinWr) |
(1U<<kPinA1) | (1U<<kPinA2));
gpio_init(GPIOE, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, kDataMaskE);
/* 控制线空闲态: CS/RD/WR=高, A1=A2=低 */
GPIO_BOP(GPIOD) = (1U<<kPinCs) | (1U<<kPinRd) | (1U<<kPinWr);
GPIO_BC(GPIOD) = (1U<<kPinA1) | (1U<<kPinA2);
s_continuityActive = true;
printf("[CONT] test mode entered (GPIO -> 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<<kPinCs) | (1U<<kPinRd) | (1U<<kPinWr);
GPIO_BC(GPIOD) = (1U<<kPinA1) | (1U<<kPinA2);
printf("[CONT] pattern 0x%04X driven\r\n", (unsigned)pat);
return 0;
}
case 5: /* CS low */
case 6: /* RD low */
case 7: /* WR low */
case 8: /* A1 high */
case 9: { /* A2 high */
continuityEnterTestMode();
/* 数据线恢复空闲(避免上次数据 pattern 残留) */
bbDriveData(0x0000);
uint32_t pin;
bool drive_high;
switch (action) {
case 5: pin = kPinCs; drive_high = false; break; /* CS low */
case 6: pin = kPinRd; drive_high = false; break; /* RD low */
case 7: pin = kPinWr; drive_high = false; break; /* WR low */
case 8: pin = kPinA1; drive_high = true; break; /* A1 high */
case 9: pin = kPinA2; drive_high = true; break; /* A2 high */
default: return 0xEE;
}
/* 先把所有控制线恢复空闲,再驱动目标线 */
GPIO_BOP(GPIOD) = (1U<<kPinCs) | (1U<<kPinRd) | (1U<<kPinWr);
GPIO_BC(GPIOD) = (1U<<kPinA1) | (1U<<kPinA2);
if (drive_high) {
GPIO_BOP(GPIOD) = (1U << pin);
} else {
GPIO_BC(GPIOD) = (1U << pin);
}
printf("[CONT] control line PD%lu driven %s\r\n",
(unsigned long)pin, drive_high ? "HIGH" : "LOW");
return 0;
}
s_ok = true;
ECAT_LOGI("[ECAT][EXMC] init: OK, bus visible\r\n");
return true;
case 10: /* MODE0 high (LAN9252 Pin48) via PB8 */
case 11: /* MODE1 high (LAN9252 Pin46) via PB9 */
case 12: /* MODE0 low (LAN9252 Pin48) via PB8 */
case 13: { /* MODE1 low (LAN9252 Pin46) via PB9 */
continuityEnterTestMode();
/* 先把 MODE0/MODE1 恢复空闲(低),再驱动目标线 */
GPIO_BOP(GPIOB) = 0U;
GPIO_BC(GPIOB) = (1U<<kPinMode0) | (1U<<kPinMode1);
/* 确保 PB8/PB9 为输出 */
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,
(1U<<kPinMode0) | (1U<<kPinMode1));
uint32_t pin = (action==10 || action==12) ? kPinMode0 : kPinMode1;
bool high = (action==10 || action==11);
if (high) {
GPIO_BOP(GPIOB) = (1U << pin);
} else {
GPIO_BC(GPIOB) = (1U << pin);
}
printf("[CONT] MODE%d line PB%lu driven %s (for strap check)\r\n",
(action==10||action==12)?0:1, (unsigned long)pin,
high ? "HIGH" : "LOW");
return 0;
}
default:
return 0xEE;
}
}
__attribute__((section(".ramfunc"), noinline))
uint16_t EscExmcBus::read16(uint32_t offset)
{
return ESC_EXMC_REG16(offset);
__attribute__((section(".ramfunc"), noinline)) uint16_t
EscExmcBus::read16(uint32_t offset) {
return ESC_EXMC_REG16(offset);
}
__attribute__((section(".ramfunc"), noinline))
void EscExmcBus::write16(uint32_t offset, uint16_t data)
{
ESC_EXMC_REG16(offset) = data;
}
__attribute__((section(".ramfunc"), noinline)) void
EscExmcBus::write16(uint32_t offset, uint16_t data) {
ESC_EXMC_REG16(offset) = data;
}
+17 -6
View File
@@ -1,8 +1,9 @@
/**
* @file esc_exmc_bus.h
* @brief LAN9252 ESC 的 EXMC 16-bit 并行总线抽象接口。
* @note 通过 EXMC NOR/SRAM Region1(NE1,基址 0x64000000)以内存映射方式访问 LAN9252。
* 16-bit 数据总线 + A16 地址线 + NOE/NWE/NE1 控制线。
* @note 通过 EXMC NOR/SRAM Region0(NE0,基址 0x60000000)以内存映射方式访问 LAN9252。
* 16-bit 数据总线 + A16/A17 地址线 + NOE/NWE/NE0 控制线。
* (LQFP100 只有 NE0=PD7 一个片选引脚,故用 Region0/SNCTL0)
*/
#ifndef COMM_ESC_EXMC_BUS_H
#define COMM_ESC_EXMC_BUS_H
@@ -16,8 +17,8 @@
*/
class EscExmcBus {
public:
/** @brief 初始化 EXMC NOR/SRAM Region1 与全部 GPIO(AF 复用)。
* @return bool true 表示 EXMC Region1 已成功使能且首次回读探活通过。 */
/** @brief 初始化 EXMC NOR/SRAM Region0 与全部 GPIO(AF 复用)。
* @return bool true 表示 EXMC Region0 已成功使能且首次回读探活通过。 */
static bool init();
/** @brief ISR-only:仅供 preempt=0 调用,无锁。 */
@@ -26,11 +27,21 @@ public:
/** @brief ISR-only:仅供 preempt=0 调用,无锁。 */
static void write16(uint32_t offset, uint16_t data);
/** @brief init 后查询:EXMC Region1 是否使能且探活通过(供上层诊断汇总)。 */
/** @brief init 后查询:EXMC Region0 是否使能且探活通过(供上层诊断汇总)。 */
static bool isOk() { return s_ok; }
/** @brief 硬件诊断:bit-bang 探活 + 位级回显分析(精简版)。 */
static void diagDump();
/** @brief 通断测试:逐个 pattern 驱动(不延时,保持到下次调用)。
* @param action 0=stop(恢复AF_PP), 1=0xFFFF, 2=0x0000, 3=0xAAAA, 4=0x5555,
* 5=CS低, 6=RD低, 7=WR低, 8=A1高, 9=A2高,
* 10=MODE0高(Pin48), 11=MODE1高(Pin46), 12=MODE0低(Pin48), 13=MODE1低(Pin46)
* @return 0=OK, 0xEE=无效 action */
static uint8_t continuityDrive(uint8_t action);
private:
static bool s_ok; /**< EXMC Region1 使能 + 探活结果(init 后置位)。 */
static bool s_ok; /**< EXMC Region0 使能 + 探活结果(init 后置位)。 */
};
#endif /* COMM_ESC_EXMC_BUS_H */
+182 -63
View File
@@ -1,6 +1,13 @@
/**
* @file esc_lan9252.cpp
* @brief LAN9252 EtherCAT ESC 驱动实现。
*
* 硬件使用 16-bit HBI Indexed Address Mode:
* - A2=0,A1=0 (偏移 0x00000) -> 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);
}
+18 -4
View File
@@ -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; }
+2 -2
View File
@@ -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");
+17 -12
View File
@@ -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 <cstdio>
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
@@ -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
<br>Changes to version - :<br>
V5.10.1 : Start file change log
*/
/*-----------------------------------------------------------------------------------------
------
------ Includes
------
-----------------------------------------------------------------------------------------*/
#ifndef _APPL_INTERFACE_H_
#define _APPL_INTERFACE_H_
#include "ecat_def.h"
#include <stdlib.h>
#include <string.h>
#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
+68
View File
@@ -0,0 +1,68 @@
/**
* \addtogroup CoE CAN Application Profile over EtherCAT
* @{
*/
/**
\file coeappl.h
\author EthercatSSC@beckhoff.com
\version 5.11
<br>Changes to version V5.01:<br>
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
<br>Changes to version - :<br>
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
/** @}*/
+124
View File
@@ -0,0 +1,124 @@
/**
\addtogroup EcatAppl EtherCAT application
* @{
*/
/**
\file ecatappl.h
\author EthercatSSC@beckhoff.com
\version 5.11
<br>Changes to version V5.10:<br>
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
<br>Changes to version V5.01:<br>
V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)<br>
Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings<br>
Update 0x1C3x entries<br>
V5.10 ECAT4: Update alignment marco for 8 to 15 bit alignments (16 and 32 Bit controllers)<br>
Bugfix calculate LED blink frequency<br>
<br>Changes to version - :<br>
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
/** @}*/
+113
View File
@@ -0,0 +1,113 @@
/**
* \addtogroup CoE CAN Application Profile over EtherCAT
* @{
*/
/**
\file ecatcoe.h
\author EthercatSSC@beckhoff.com
\version 5.11
<br>Changes to version V5.01:<br>
V5.11 COE4: "change prototype of ""COE_ContinueInd()"" return <> 0 if a failure occurred"<br>
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
<br>Changes to version - :<br>
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
/** @}*/
+455
View File
@@ -0,0 +1,455 @@
/**
* \addtogroup ESM EtherCAT State Machine
* @{
*/
/**
\file ecatslv.h
\author EthercatSSC@beckhoff.com
\version 5.11
<br>Changes to version V5.10:<br>
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
V5.11 ECAT4: enhance SM/Sync monitoring for input/output only slaves<br>
V5.11 ECAT9: add new AL Status Codes (ALSTATUSCODE_FW_SII_NOT_MATCH/0x0006, ALSTATUSCODE_FW_UPDATE_FAILED / 0x0007)<br>
V5.11 ESM7: "add Sync define for 0x22 (""SYNCTYPE_SM2_SYNCHRON""), support value 0x22 for 0x1C33.1 (SM2 sync)"<br>
V5.11 HW1: "move hardware independent functions ""HW_DisableSyncManChannel()"", ""HW_EnableSyncManChannel()"", ""HW_GetSyncMan()"", ""HW_ResetALEventMask()"", ""HW_SetALEventMask()"" to ecatalv.c"<br>
<br>Changes to version V5.01:<br>
V5.10 COE1: Define one entry description for all 0x1C3x objects and change data type of SI11,12,13 to UINT16 (according ETG.1020)<br>
V5.10 ECAT12: Add new AL Status Codes (0x2E,0x52,0x70)<br>
V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)<br>
Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings<br>
Update 0x1C3x entries<br>
<br>Changes to version - :<br>
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
/** @}*/
@@ -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_ */
+150
View File
@@ -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
+175
View File
@@ -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
<br>Changes to version V5.01:<br>
V5.10 ESC1: Update address register offset for 32Bit ESC access<br>
V5.10 ESC2: Check if defined SM settings do not exceed the available DPRAM range (in error case AL Status 0x14 is returned)<br>
V5.10 ESC3: Handle DC cControl register values in case of 32Bit ESC access (a Sync activation mask need to defined/used)<br>
<br>Changes to version - :<br>
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_
/** @}*/
+196
View File
@@ -0,0 +1,196 @@
/**
* \addtogroup Mailbox Mailbox Functions
* @{
*/
/**
\file mailbox.h
\author EthercatSSC@beckhoff.com
\version 5.11
<br>Changes to version V5.01:<br>
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
V5.11 MBX1: "return value of ""MBX_StartMailboxHandler()"" changed to UINT16"<br>
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)<br>
<br>Changes to version - :<br>
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
/** @}*/
+291
View File
@@ -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
<br>Changes to version V5.10:<br>
V5.11 COE2: update synctype supported flag for freerun<br>
V5.11 COE3: change 0x10F3.2 (Sync Error limit) from UINT32 to UINT16 (according to the ETG.1020)<br>
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
<br>Changes to version V5.01:<br>
V5.10 COE1: Define one entry description for all 0x1C3x objects and change data type of SI11,12,13 to UINT16 (according ETG.1020)<br>
V5.10 COE2: Change return value of Get ObjectSize to UINT32<br>
Change object size to UINT32<br>
V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)<br>
Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings<br>
Update 0x1C3x entries<br>
V5.10 ECAT8: Update Datatype defines according ETG.1020<br>
<br>Changes to version - :<br>
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
/** @}*/
+641
View File
@@ -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
<br>Changes to version V5.10:<br>
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
V5.11 SDO10: add new SDO abort code 0x6010004 (complete access not supported)<br>
<br>Changes to version V5.01:<br>
V5.10 SDO3: Add new SDO Abort Code (0x06090033)<br>
<br>Changes to version - :<br>
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<br>1: Expedited<br>0: Normal*/
#define SDOHEADER_DATASETSIZE ((UINT8) 0x0C) /**< \brief Data Set Size (used for Expedited transfer)*/
#define SDOHEADER_COMPLETEACCESS ((UINT8) 0x10) /**< \brief Complete Access indication<br>0: Only specified entry will be transferred<br>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
<br>Bit 1: Read Access in Safe-Op
<br>Bit 2: Read Access in Op
<br>Bit 3: Write Access in Pre-Op
<br>Bit 4: Write Access in Safe-Op
<br>Bit 5: Write Access in Op
<br>Bit 6: mappable in RxPDO
<br>Bit 7: mappable in TxPDO
<br>Bit 8: entry will be included in backup
<br>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
/** @}*/
@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<EtherCATInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="EtherCATInfo.xsd" Version="1.6">
<Vendor>
<Id>#x00000000</Id>
<Name>DCU Project</Name>
</Vendor>
<Descriptions>
<Groups>
<Group>
<Type>DCU</Type>
<Name LcId="1033">DCU Controller</Name>
</Group>
</Groups>
<Devices>
<Device Physics="YY">
<Type ProductCode="#x00009252" RevisionNo="#x00010000">GD32E508 DCU</Type>
<Name LcId="1033">GD32E508 DCU EtherCAT Slave</Name>
<GroupType>DCU</GroupType>
<Fmmu>Outputs</Fmmu>
<Fmmu>Inputs</Fmmu>
<!-- SM0: Mailbox Receive (Master -> Slave) -->
<Sm DefaultSize="256" StartAddress="#x1000" ControlByte="#x26" Enable="1">MBoxOut</Sm>
<!-- SM1: Mailbox Send (Slave -> Master) -->
<Sm DefaultSize="256" StartAddress="#x1200" ControlByte="#x22" Enable="1">MBoxIn</Sm>
<!-- SM2: Process Data Output (RxPDO, Master -> Slave) -->
<Sm StartAddress="#x1400" ControlByte="#x64" Enable="1">Outputs</Sm>
<!-- SM3: Process Data Input (TxPDO, Slave -> Master) -->
<Sm StartAddress="#x1480" ControlByte="#x20" Enable="1">Inputs</Sm>
<!-- RxPDO (Output) -->
<TxPdo Fixed="1" Mandatory="1" Sm="2">
<Index>#x1600</Index>
<Name>Output</Name>
<Entry>
<Index>#x7000</Index>
<SubIndex>1</SubIndex>
<BitLen>256</BitLen>
<Name>Control Data</Name>
<DataType>ARRAY [0..31] OF BYTE</DataType>
</Entry>
</TxPdo>
<!-- TxPDO (Input) -->
<RxPdo Fixed="1" Mandatory="1" Sm="3">
<Index>#x1A00</Index>
<Name>Input</Name>
<Entry>
<Index>#x6000</Index>
<SubIndex>1</SubIndex>
<BitLen>256</BitLen>
<Name>Status Data</Name>
<DataType>ARRAY [0..31] OF BYTE</DataType>
</Entry>
</RxPdo>
<Mailbox DataLinkLayer="true">
<CoE SdoInfo="true" CompleteAccess="true" SegmentedSdo="true"/>
</Mailbox>
<Dc>
<OpMode>
<Name>DC SYNC0</Name>
<Desc>DC for synchronization</Desc>
<AssignActivate>#x0300</AssignActivate>
<CycleTimeSync0 Factor="1">0</CycleTimeSync0>
<ShiftTimeSync0>0</ShiftTimeSync0>
</OpMode>
<OpMode>
<Name>Free Run</Name>
<Desc>No synchronization</Desc>
<AssignActivate>#x0000</AssignActivate>
</OpMode>
</Dc>
<Eeprom>
<ByteSize>4096</ByteSize>
<ConfigData>050C000000000000</ConfigData>
</Eeprom>
</Device>
</Devices>
</Descriptions>
</EtherCATInfo>
+229
View File
@@ -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 <string.h>
/*---------------------------------------------------------------------------
* 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];
}
+39
View File
@@ -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 */
+734
View File
@@ -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 <stdlib.h>
#include <string.h>
/*-----------------------------------------------------------------------------------------
------
------ 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_ */
@@ -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 <stdint.h>
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 */
@@ -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('<H', eeprom, offset, value & 0xFFFF)
def w32(offset, value):
"""Write 32-bit little-endian value at byte offset."""
struct.pack_into('<I', eeprom, offset, value & 0xFFFFFFFF)
# --- Header (bytes 0-63) ---
# Word 0: PDI Control + PDI Config
eeprom[0] = PDI_CONTROL
eeprom[1] = PDI_CONFIG
# Word 1: ESC Configuration (Sync Watchdog divider etc.) - 0x0000
w16(2, 0x0000)
# Word 2: PDI Config 2 + Sync Impulse Length - 0x0000
w16(4, 0x0000)
# Word 3: Alias + Reserved
w16(6, 0x0000)
# Words 4-5: Vendor ID
w32(8, VENDOR_ID)
# Words 6-7: Product Code
w32(12, PRODUCT_CODE)
# Words 8-9: Revision Number
w32(16, REVISION_NO)
# Words 10-11: Serial Number
w32(20, SERIAL_NO)
# Words 12-15: Reserved (0x0000)
for i in range(24, 32, 2):
w16(i, 0x0000)
# Word 16: Bootstrap RX mailbox offset
w16(32, MBX_RX_OFFSET)
# Word 17: Bootstrap RX mailbox size
w16(34, MBX_RX_SIZE)
# Word 18: Bootstrap TX mailbox offset
w16(36, MBX_TX_OFFSET)
# Word 19: Bootstrap TX mailbox size
w16(38, MBX_TX_SIZE)
# Word 20: Standard RX mailbox offset
w16(40, MBX_RX_OFFSET)
# Word 21: Standard RX mailbox size
w16(42, MBX_RX_SIZE)
# Word 22: Standard TX mailbox offset
w16(44, MBX_TX_OFFSET)
# Word 23: Standard TX mailbox size
w16(46, MBX_TX_SIZE)
# Word 24: Mailbox protocol (CoE = bit 0)
w16(48, MBX_PROTOCOL)
# Words 25-31: Reserved
for i in range(50, 64, 2):
w16(i, 0x0000)
# --- SM Configuration (starts at byte 64, word 32) ---
# Each SM entry: 8 bytes
# [0:2] Start address (16-bit LE)
# [2:4] Length (16-bit LE)
# [4] Control byte
# [5] Status (0)
# [6] Activate (1=enabled)
# [7] PDI control (0)
sm_base = 64
def write_sm(idx, start, length, control, activate=1):
off = sm_base + idx * 8
w16(off, start)
w16(off + 2, length)
eeprom[off + 4] = control
eeprom[off + 5] = 0 # status
eeprom[off + 6] = activate
eeprom[off + 7] = 0 # PDI control
# SM0: Mailbox Receive (Master -> 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('<H', 0x1600)) # Index
pdo_cat.append(2) # SyncManager = SM2
pdo_cat.append(0) # DC Sync
pdo_cat.append(0) # Name string index (0 = use index as name)
pdo_cat.append(0x00) # Flags (fixed, mandatory)
pdo_cat.append(1) # Entry count
pdo_cat.append(0) # Reserved
# Entry 1: 0x7000:01, 256 bits
pdo_cat.extend(struct.pack('<H', 0x7000)) # Index
pdo_cat.append(1) # SubIndex
pdo_cat.append(0) # Name string index
pdo_cat.append(0x0F) # DataType: ARRAY OF BYTE (0x0F = BYTE in CoE)
pdo_cat.extend(struct.pack('<H', 256)) # BitLen
pdo_cat.extend(struct.pack('<H', 0)) # Flags
# RxPDO 0x1A00 (assigned to SM3, inputs)
pdo_cat.extend(struct.pack('<H', 0x1A00)) # Index
pdo_cat.append(3) # SyncManager = SM3
pdo_cat.append(0) # DC Sync
pdo_cat.append(0) # Name string index
pdo_cat.append(0x00) # Flags
pdo_cat.append(1) # Entry count
pdo_cat.append(0) # Reserved
# Entry 1: 0x6000:01, 256 bits
pdo_cat.extend(struct.pack('<H', 0x6000)) # Index
pdo_cat.append(1) # SubIndex
pdo_cat.append(0) # Name string index
pdo_cat.append(0x0F) # DataType: BYTE
pdo_cat.extend(struct.pack('<H', 256)) # BitLen
pdo_cat.extend(struct.pack('<H', 0)) # Flags
if len(pdo_cat) % 2 != 0:
pdo_cat.append(0)
pdo_words = len(pdo_cat) // 2
eeprom[cat_offset] = 50 # Type: PDO
eeprom[cat_offset + 1] = pdo_words
eeprom[cat_offset + 2: cat_offset + 2 + len(pdo_cat)] = pdo_cat
cat_offset += 2 + len(pdo_cat)
# --- DC Category (Type 60) ---
# DC settings: [AssignActivate(2)] [Sync0 Cycle(4)] [Sync0 Shift(4)] [Sync1 Cycle(4)] [Sync1 Shift(4)]
dc_cat = bytearray()
dc_cat.extend(struct.pack('<H', 0x0300)) # AssignActivate: SYNC0 enabled
dc_cat.extend(struct.pack('<I', 0)) # SYNC0 cycle time (0 = set by master)
dc_cat.extend(struct.pack('<I', 0)) # SYNC0 shift time
dc_cat.extend(struct.pack('<I', 0)) # SYNC1 cycle time (unused)
dc_cat.extend(struct.pack('<I', 0)) # SYNC1 shift time
dc_words = len(dc_cat) // 2
eeprom[cat_offset] = 60 # Type: DC
eeprom[cat_offset + 1] = dc_words
eeprom[cat_offset + 2: cat_offset + 2 + len(dc_cat)] = dc_cat
cat_offset += 2 + len(dc_cat)
# --- End of categories ---
eeprom[cat_offset] = 0 # Type 0 = end
eeprom[cat_offset + 1] = 0
# ============================================================
# Compute checksum (word 0x0007, byte offset 14)
# Checksum = XOR of all words from offset 0 to (checksum_word - 1)
# Then stored so that XOR of all words 0..7 == 0x0000
# ============================================================
# Actually per ETG.1000.6: checksum is at word 7 (byte 14-15)
# It's computed as: XOR of words 0..6, then checksum = ~xor (bitwise NOT)
# such that XOR of words 0..7 == 0xFFFF? No...
# Per spec: "The checksum is calculated by XORing all words from 0x00 to 0x06.
# The result is stored in word 0x07."
# Verification: XOR of words 0x00..0x07 should equal 0x0000.
xor_val = 0
for i in range(0, 14, 2):
xor_val ^= struct.unpack_from('<H', eeprom, i)[0]
w16(14, xor_val)
# ============================================================
# Output as C header
# ============================================================
print("/**")
print(" * @file eeprom_image.h")
print(" * @brief LAN9252 EEPROM (ESI) binary image - auto-generated by gen_eeprom.py")
print(" *")
print(f" * Vendor ID: 0x{VENDOR_ID:08X}")
print(f" * Product Code: 0x{PRODUCT_CODE:08X}")
print(f" * Revision: 0x{REVISION_NO:08X}")
print(f" * Serial: 0x{SERIAL_NO:08X}")
print(f" * Size: {EEPROM_SIZE} bytes (AT24C32)")
print(" *")
print(" * DO NOT EDIT - regenerate with: python gen_eeprom.py > eeprom_image.h")
print(" */")
print("#ifndef COMM_SSC_PORT_EEPROM_IMAGE_H")
print("#define COMM_SSC_PORT_EEPROM_IMAGE_H")
print("")
print("#include <stdint.h>")
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 */")
@@ -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 <cstdio>
#include <cstring>
/*---------------------------------------------------------------------------
* 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" */
+780
View File
@@ -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
<br>Changes to version V5.10:<br>
V5.11 COE1: update invalid end entry in the object dictionaries (error with some compilers)<br>
V5.11 COE3: change 0x10F3.2 (Sync Error limit) from UINT32 to UINT16 (according to the ETG.1020)<br>
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
V5.11 ECAT4: enhance SM/Sync monitoring for input/output only slaves<br>
<br>Changes to version V5.01:<br>
V5.10 COE1: Define one entry description for all 0x1C3x objects and change data type of SI11,12,13 to UINT16 (according ETG.1020)<br>
V5.10 COE2: Change return value of Get ObjectSize to UINT32<br>
Change object size to UINT32<br>
V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)<br>
Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings<br>
Update 0x1C3x entries<br>
<br>Changes to version V5.0:<br>
V5.01 APPL3: Include library demo application<br>
V5.01 COE1: Remove alignment entry (SI33) from objects 0x1C32/0x1C33<br>
V5.01 EL9800 1: Read/Write functions for setting object 0x8020 moved to el9800appl.c<br>
V5.01 EL9800 2: Add TxPdo Parameter object 0x1802<br>
V5.01 SDO6: Update SDO response interface handling. (used if the object access function returns "ABORTIDX_WORKING" and SDO_RES_INTERFACE is active)<br>
<br>Changes to version V4.40:<br>
V5.0 TEST1: Add test application. See Application Note ET9300 for more details.<br>
V5.0 COE1: Add reserved Subindex7 to object 0x1C32 and 0x1C33.<br>
V5.0 COE4: Create object dictionary on device startup.<br>
V5.0 COE6: Update entry descriptions for object with the code ARRAY.<br>
V5.0 ECAT2: Reference to application specific object dictionary is set to "ApplicationObjDic".<br>
<br>Changes to version V4.30:<br>
V4.40 COE 6: change dynamic object dictionary handling<br>
V4.40 CiA402 2: enable freerun Sync Mode for CiA402 Device (The motion controller application will only be triggered if DC Synchronisation is disabled)<br>
V4.40 COE5: move identification and synchronisation object values to ecat_def.h<br>
V4.40 DIAG1: Add diagnosis object 0x10F3<br>
V4.40 OBJ3: Update the global object dictionary pointer if a new list head is added<br>
V4.40 COE4: Prevent access NULL pointer<br>
V4.40 SYNC1: Initialize 0x1C3x objects<br>
V4.40 HW1: Add (adapt) objects for FC1100 support<br>
<br>Changes to version V4.20:<br>
V4.30 OBJ 3: Handle object dictionary in double link list<br>
V4.30 OBJ 2: General EtherCAT device objects are moved from chnappl.h to coeappl.c<br>
V4.30 SYNC: set 0x1C32:05; 0x1C33:05 (minCyleTime) to MIN_PD_CYCLE_TIME
(should be set dynamic if required)<br>
V4.30 PDO: rename PDO specific function calls
(PDO specific functions are moved to ecatappl.c)<br>
V4.20 PIC24: Add EL9800_4 (PIC24) required source code<br>
V4.11 ECAT 1: C166: MinDelayTime renamed in CalcAndCopyTime<br>
<br>Changes to version V4.07:<br>
V4.10 ECAT 1: To save and restore arrays in the object directory.<br>
The code to get the size of an array entry was changed.<br>
V4.10 COE 1: The SM-Parameter were extended<br>
<br>Changes to version V4.06:<br>
V4.07 COEAPPL 1: The PDO numbers were adapted according top the modular device profile,<br>
the PDO numbers increment now with every module (in chnappl.h)<br>
V4.07 COEAPPL 2: The example is working for the NIOS with the evaluation board DBC2C20 V1.2<br>
which is available by Altera<br>
V4.07 ECAT 1: The sources for SPI and MCI were merged (in ecat_def.h<br>
set the switch MCI_HW to 1 when using the MCI,<br>
set the switch SPI_HW to 1 when using the SPI<br>
<br>Changes to version V4.03:<br>
V4.04 SDO 1: The SDO interface was changed in that way that a SDO response<br>
could be sent by the application to a later time. In that case<br>
the functions OBJ_Read and OBJ_Write shall return the value<br>
ABORTIDX_WORKING. To send the SDO response the new function SDOS_SdoRes<br>
has to be called by the application. While waiting for the call<br>
of SDOS_SdoRes the SDO interface will answer to another SDO request<br>
with the error MBXERR_SERVICEINWORK in the mailbox protocol.<br>
In this example the reading and writing of object 0x8020 is delayed,<br>
the function SDOS_SdoRes will be called from COE_Main.<br>
<br>Changes to version V4.00:<br>
V4.01 COEAPPL 1: The object dictionary and the mapping functions are now defined<br>
in chnappl.h (chnappl.inc made problems with some developing tools)<br>
<br>Changes to version V3.20:<br>
V4.00 COEAPPL 1: The object dictionary and the mapping functions are now defined<br>
in chnappl.c and were adapted to the modular device profile<br>
V4.00 COEAPPL 2: The handling of backup parameters was included according to<br>
the EtherCAT Guidelines and Protocol Enhancements Specification<br>
V4.00 COEAPPL 3: The handling of the TxPDO Toggle was included according to<br>
the EtherCAT Guidelines and Protocol Enhancements Specification<br>
V4.00 COEAPPL 4: The handling of the TxPDO State was included according to<br>
the EtherCAT Guidelines and Protocol Enhancements Specification<br>
V4.00 ECAT 1: The handling of the Sync Manager Parameter was included according to<br>
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;
}
}
}
/** @} */
+630
View File
@@ -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
<br>Changes to version V5.10.1:<br>
V5.11 COE3: change 0x10F3.2 (Sync Error limit) from UINT32 to UINT16 (according to the ETG.1020)<br>
V5.11 ECAT1: update EEPROM access reset operation<br>
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
V5.11 ECAT11: create application interface function pointer, add eeprom emulation interface functions<br>
V5.11 ECAT2: update EEPROM access retry cycle (add 10ms delay between two retry cycles)<br>
V5.11 ECAT3: handle bus cycle calculation for input/output only devices and create warning diag message only if calculation failed<br>
V5.11 ECAT4: enhance SM/Sync monitoring for input/output only slaves<br>
V5.11 ECAT6: add function to calculate bus cycle time<br>
V5.11 ECAT8: call PDO_InputMapping only once if DC is enabled and COE is not supported<br>
V5.11 EEPROM1: fix compiler error during pEEPROM pointer initialization<br>
V5.11 EEPROM2: write Station alias value to EEPROM data register on EEPROM reload command<br>
V5.11 EEPROM3: clear EEPROM error bits<br>
V5.11 EEPROM4: prevent the variable in the EEPROM busy loop to be removed by the compiler<br>
V5.11 ESM7: "add Sync define for 0x22 (""SYNCTYPE_SM2_SYNCHRON""), support value 0x22 for 0x1C33.1 (SM2 sync)"<br>
<br>Changes to version V5.01:<br>
V5.10 COE1: Define one entry description for all 0x1C3x objects and change data type of SI11,12,13 to UINT16 (according ETG.1020)<br>
V5.10 ECAT1: Correct calculation of blinking and flashing sequence<br>
V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)<br>
Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings<br>
Update 0x1C3x entries<br>
V5.10 ECAT2: Prevent EEPROM data null pointer access (if the pointer is null an command error is set)<br>
EEPROM emulation return command error if unknown command was received<br>
V5.10 ECAT4: Update alignment marco for 8 to 15 bit alignments (16 and 32 Bit controllers)<br>
Bugfix calculate LED blink frequency<br>
V5.10 ECAT7: Add "bInitFinished" to indicate if the initialization is complete<br>
V5.10 HW2: Change HW_GetTimer return value to UINT32<br>
<br>Changes to version V5.0:<br>
V5.01 APPL3: Include library demo application<br>
V5.01 ESC1: Change ESC access function (if EEPROM Emulation is active)<br>
V5.01 ESC2: Add missed value swapping<br>
<br>Changes to version V4.40:<br>
V5.0 TEST1: Add test application. See Application Note ET9300 for more details.<br>
V5.0 ECAT2: Application specific functions are moved to application files.<br>
V5.0 ECAT3: Global dummy variables used for dummy ESC operations.<br>
V5.0 ESC1: ESC 32Bit Access added.<br>
V5.0 ESC3: Add EEPROM emulation support.<br>
V5.0 ESM3: Handling pending ESM transitions.<br>
V5.0 ESC5: Enhance EEPROM access handling.<br>
V5.0 PDO1: AL Event flags are not rechecked in PDO_OutputMappping(). (Already checked before call function)<br>
V5.0 SYNC1: Add missed SM event indication (0x1C32/0x1C33 SI11).<br>
<br>Changes to version V4.30:<br>
V4.40 DIAG1: add diagnosis message support<br>
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.<br>
V4.40 PDO2: Generic process data length calculation<br>
V4.40 ECAT2: call cyclic CheckIfLocalError() to check the local flags<br>
V4.40 HW0: Generic hardware access functions. Add Function (PDI_Isr()), content merged from spihw.c and mcihw.c.<br>
V4.40 WD1: define (ESC_SM_WD_SUPPORTED) to choose ESC SyncManager watchdog or local watchdog<br>
V4.40 ESM2: Change state transition behaviour from SafeOP to OP<br>
V4.40 TIMER1: Change bus cycle time calculation and trigger of ECAT_CheckTimer() if ECAT_TIMER_INT is reset<br>
V4.40 HW1: Add support for fc1100 hardware<br>
<br>Changes to version V4.20:<br>
V4.30 EL9800: EL9800_x cyclic application is moved to el9800.c<br>
V4.30 OBJ 3: add object dictionary initialization<br>
V4.30 SYNC: add CalcSMCycleTime() (calculation of bus cycle time); change synchronisation control functions<br>
V4.30 PDO: include PDO specific functions (moved from coeappl.c).<br>
xxx_InputMapping(); xxx_OutputMapping(); xxx_ReadInputs(); xxx_ResetOutputs(); xxx_Application()<br>
V4.30 CiA402: Add CiA402_StateMachine() and CiA402_Application() call<br>
V4.20 DC 1: Add DC pending Statemachine handling<br>
V4.20 PIC24: Add EL9800_4 (PIC24) required source code<br>
V4.20 LED 1: Modified LED Handling<br>
V4.11 APPL 1: The checkWatchdog() function should not called in checkTimer() if this function is triggered by an Interrupt<br>
<br>Changes to version V4.08:<br>
V4.10 LED 1: The handling of the EtherCAT-Error-LED was added<br>
V4.10 AOE 3: The AoE fragment size has to be initialized during the state transition<br>
from INIT to PREOP<br>
<br>Changes to version V4.07:<br>
V4.08 LED 1: The handling of the EtherCAT-LED can be (de-)selected by the switch LEDS_SUPPORTED<br>
because the ET1100 and ET1200 have an output port which could be connected directly.<br>
<br>Changes to version V4.01:<br>
V4.02 ECAT 1: The watchdog timer variables shall be initialized.<br>
<br>Changes to version V4.00:<br>
V4.01 APPL 1: If the application is running in synchron mode and no SM event<br>
is received, the application should be called from the main loop<br>
V4.01 APPL 2: In FreeRun mode the output should only be copied if the slave is in OP<br>
<br>Changes to version V3.20:<br>
V4.00 APPL 1: The watchdog checking should be done by a microcontroller<br>
timer because the watchdog trigger of the ESC will be reset too<br>
if only a part of the sync manager data is written<br>
V4.00 APPL 2: The setting of EtherCAT state LEDs were included<br>
V4.00 APPL 3: The outputs should be reset to a safe state,<br>
when the state OP is left<br>
V4.00 APPL 4: An example for the EEPROM access through the ESC is shown in<br>
the function APPL_StartMailboxHandler<br>
V4.00 APPL 5: The inputs should be read once when the state transition<br>
from PREOP to SAFEOP is made<br>
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 */
}
/** @} */
+180
View File
@@ -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
<br>Changes to version V5.0:<br>
V5.11 COE4: "change prototype of ""COE_ContinueInd()"" return <> 0 if a failure occurred"<br>
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
V5.11 TEST6: add test object to trigger Slave-to-Slave communication<br>
V5.11 TEST7: add test behaviour to send an emergency on every SDO request (in SafeOP)<br>
<br>Changes to version V4.40:<br>
V5.0 SDO7: "SDOS_SdoInfoInd()" never return pending SDO Info service. Delete "NOERROR_INWORK" handling.<br>
<br>Changes to version V4.08:<br>
V4.40 SDO1: add initial value for "nSdoInfoFragmentsLeft"<br>
V4.40 MBX6: change return value if no mailbox buffer is available<br>
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;
}
/** @} */
File diff suppressed because it is too large Load Diff
+929
View File
@@ -0,0 +1,929 @@
/**
\addtogroup Mailbox Mailbox Functions
@{
*/
/**
\file mailbox.c
\author EthercatSSC@beckhoff.com
\brief Implementation
<br>Description of the mailbox buffer handling (MAILBOX_QUEUE = 0):<br>
There are two mailbox buffer for sending and receiving mailbox services.<br>
Normal operation:<br>
When starting the mailbox handler psWriteMbx contains mailbox buffer 1,<br>
psReadMbx, psRepeatMbx and psStoreMbx are 0.<br>
In this state a repeat request would be ignored because there was no service sent yet.<br>
When the first mailbox service is sent (in MBX_CopyToSendMailbox), psWriteMbx gets mailbox buffer 2<br>
and psReadMbx gets the sent mailbox buffer 1, psRepeatMbx and psStoreMbx are still 0.<br>
When the first mailbox service was read from the master, the sent mailbox buffer 1 is stored<br>
in psRepeatMbx (in MBX_MailboxReadInd).<br>
After that psReadMbx gets always the actual sent mailbox buffer, psWriteMbx is set to 0 (another<br>
received mailbox service from the master will not be handled until the sent mailbox service was read<br>
and MBX_MailboxReadInd was called).<br>
When the mailbox service is read, psWriteMbx gets the Buffer of psRepeatMbx and psRepeatMbx gets the <br>
buffer of psReadMbx.<br>
Repeat Request from the master:<br>
When a Repeat from the master is requested (MBX_MailboxRepeatReq), there are three different possibilities:<br>
1. no mailbox service was sent since the mailbox handler was started (psRepeatMbx = 0): nothing to do<br>
2. the acknowledge of the last sent mailbox service was received (in MBX_MailboxReadInd) (bSendMbxIsFull = 0):<br>
the last sent mailbox service (psRepeatMbx) will be sent again (in MBX_CopyToSendMailbox) and stored in psReadMbx,<br>
psRepeatMbx will be set to 0<br>
3. the acknowledge of the last sent mailbox service was not received (psReadMbx and psRepeatMbx contain different buffers,<br>
psReadMbx is still in the mailbox (because MBX_MailboxReadInd is not called yet, bSendMbxIsFull = 1): <br>
psReadMbx will be deleted in the mailbox (call of DisableSyncManChannel and EnableSyncManChannel) and<br>
stored in psStoreMbx, psRepeatMbx will be sent again (in MBX_CopyToSendMailbox) and stored in psReadMbx,<br>
psRepeatMbx will be set to 0.<br>
When the repeated mailbox service was sent (call of MBX_MailboxReadInd), psReadMbx will be stored in psRepeatMbx<br>
and psStoreMbx will be sent (in MBX_CopyToSendMailbox) and stored in psReadMbx, psStoreMbx will be set to 0.<br>
<br>Description of the mailbox buffer handling (MAILBOX_QUEUE = 1):<br>
There are two mailbox buffer for sending and receiving mailbox services.<br>
Normal operation (psWriteMbx is only used for local storage):<br>
When starting the mailbox handler psReadMbx, psRepeatMbx and psStoreMbx are 0.<br>
In this state a repeat request would be ignored because there was no service sent yet.<br>
When a mailbox service is received from the master (in MBX_CheckAndCopyMailbox) a mailbox buffer<br>
will be get with APPL_AllocMailboxBuffer and the corresponding protocol service function will<br>
be called (in MBX_WriteMailboxInd). This buffer shall be used for the protocol service response.<br>
When the first mailbox service is sent (in MBX_CopyToSendMailbox), psReadMbx gets the sent mailbox buffer,<br>
psRepeatMbx and psStoreMbx are still 0.<br>
When the first mailbox service was read from the master, the sent mailbox buffer (psReadMbx) is stored<br>
in psRepeatMbx (in MBX_MailboxReadInd).<br>
After that psReadMbx gets always the actual sent mailbox buffer (in MBX_CopyToSendMailbox)<br>
When the mailbox service is read, psRepeatMbx is returned (with APPL_FreeMailboxBuffer) and psRepeatMbx gets the <br>
buffer of psReadMbx.<br>
Repeat Request from the master:<br>
When a Repeat from the master is requested (MBX_MailboxRepeatReq), there are three different possibilities:<br>
1. no mailbox service was sent since the mailbox handler was started (psRepeatMbx = 0): nothing to do<br>
2. the acknowledge of the last sent mailbox service was received (in MBX_MailboxReadInd) (bSendMbxIsFull = 0):<br>
the last sent mailbox service (psRepeatMbx) will be sent again (in MBX_CopyToSendMailbox) and stored in psReadMbx,<br>
psRepeatMbx will be set to 0<br>
3. the acknowledge of the last sent mailbox service was not received (psReadMbx and psRepeatMbx contain different buffers,<br>
psReadMbx is still in the mailbox (because MBX_MailboxReadInd is not called yet, bSendMbxIsFull = 1): <br>
psReadMbx will be deleted in the mailbox (call of DisableSyncManChannel and EnableSyncManChannel) and<br>
stored in psStoreMbx, psRepeatMbx will be sent again (in MBX_CopyToSendMailbox) and stored in psReadMbx,<br>
psRepeatMbx will be set to 0.<br>
When the repeated mailbox service was sent (call of MBX_MailboxReadInd), psReadMbx will be stored in psRepeatMbx<br>
and psStoreMbx will be sent (in MBX_CopyToSendMailbox) and stored in psReadMbx, psStoreMbx will be set to 0.
\version 5.11
<br>Changes to version V5.10:<br>
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
V5.11 ECAT7: add missing big endian swapping<br>
V5.11 HW1: "move hardware independent functions ""HW_DisableSyncManChannel()"", ""HW_EnableSyncManChannel()"", ""HW_GetSyncMan()"", ""HW_ResetALEventMask()"", ""HW_SetALEventMask()"" to ecatalv.c"<br>
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"<br>
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)<br>
V5.11 TEST4: add new mailbox test behaviour (the master mailbox cnt shall be incremented by 1 and the slave mailbox cnt is alternating)<br>
<br>Changes to version V5.01:<br>
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)<br>
V5.10 ESC6: Update SyncManager register size (only for 8Bit ESC access)<br>
V5.10 MBX2: Allocate only memory for current configured mailbox size (max supported size was allocated before)<br>
<br>Changes to version V5.0:<br>
V5.01 ESC2: Add missed value swapping<br>
V5.01 MBX2: Return an error if length specified in mailbnox header is too large.<br>
<br>Changes to version V4.40:<br>
V5.0 ECAT3: Global dummy variables used for dummy ESC operations.<br>
V5.0 ESC1: ESC 32Bit Access added.<br>
V5.0 MBX2: Prevent double swapping (MBX repeat service) of MBX length field.<br>
V5.0 MBX4: Unlock SM buffer based on SM state.<br>
<br>Changes to version V4.11:<br>
V4.40 MBX8: Handle pending mailbox data even if mailbox queue is enabled<br>
V4.40 MBX7: Reset SyncManger 1 Repeat Acknowledge bit if local variable is reset<br>
V4.40 EMCY2: Send queued emergency messages<br>
V4.40 BOOT2: Support only FoE in Bootstrap state<br>
V4.40 MBX5: Prevent multiple free buffer<br>
V4.40 MBX6: change return value if no mailbox buffer is available<br>
<br>Changes to version V4.08:<br>
V4.11 MBX 1: free some dynamic memory to avoid memory leaks in the mailbox queue<br>
<br>Changes to version V4.07:<br>
V4.10 MBX 1: check Mailbox message buffer and print emergency messages<br>
during state change Init -> PreOP<br>
<br>Changes to version V4.07:<br>
V4.08 MBX 1: The mailbox queue was added (switch MAILBOX_QUEUE), if the<br>
mailbox should be handled in the ESC-Interrupt Service Routine<br>
the defines ENTER_MBX_CRITICAL and LEAVE_MBX_CRITICAL could be used<br>
to disable the interrupts or change the priority<br>
V4.08 AOE 1: AoE was added<br>
<br>Changes to version V4.06:<br>
V4.07 ECAT 1: The sources for SPI and MCI were merged (in ecat_def.h<br>
set the switch MCI_HW to 1 when using the MCI,<br>
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();
}
}
/** @} */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2 -3
View File
@@ -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<uint16_t>(ms * kMotorTicksPerMs);
return ms;
}
@@ -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*/) {}
@@ -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<uint16_t>(
(static_cast<uint16_t>(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<uint16_t>(1U + dlc);
const uint16_t need = static_cast<uint16_t>(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<uint8_t>((chnode >> 4) & 0x0FU);
const uint8_t node = static_cast<uint8_t>(chnode & 0x0FU);
const uint32_t id = (static_cast<uint32_t>(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<uint8_t>(r);
}
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
SpiDefs::kRspAck, resp, static_cast<uint16_t>(1U + count));
}
};
static SpiCanBurstHandler s_handler;
} // namespace
void registerSpiCanBurstCommand(AppSpiCommand &bus)
{
bus.registerHandler(&s_handler);
}
@@ -44,8 +44,8 @@
#include <cstdio>
#include <cstring>
/* ---- 文件内共享常量与辅助函数(供匿名命名空间内的 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 反馈与模式切换处理器。
@@ -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);
}
@@ -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
@@ -44,7 +44,7 @@ void motorVersionPushStep()
return;
}
/* 电机 OTA relay 活跃期间停止版本推送,避免按铃干扰 0xD1 数据流
* (详见 canFeedbackPushStep 同款注释)。dirty 保留不丢,OTA 结束后继续推。 */
* (0xC5/0xC4 已改请求驱动,0xC6 保留本门控)。dirty 保留不丢,OTA 结束后继续推。 */
if (MotorOtaRelay::isActive()) {
return;
}
@@ -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) {
+5 -34
View File
@@ -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();
@@ -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;
-6
View File
@@ -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;
};
+10 -7
View File
@@ -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)。 */
+20 -5
View File
@@ -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();
-9
View File
@@ -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;
+52 -38
View File
@@ -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();
}
-2
View File
@@ -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 映射)。 */