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:
@@ -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>
|
||||
@@ -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];
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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" */
|
||||
Reference in New Issue
Block a user