- 新增ESC硬件复位序列(1.5.25):PB10驱动LAN9252 RST#,拉低30ms+释放后300ms轮询HBI就绪, 满足tpurstd/trstia时序;此前RST#仅靠R44上拉自然释放 - 修正EXMC 16-bit地址左移(1.5.27):HBI DATA偏移0x20000/0x30000→0x40000/0x60000; ADDSET 2→4 HCLK;片选更名NE1→NE0(LQFP100仅Region0) - 按LAN9252手册/AN1907修正EEPROM镜像(1.5.28):word1高字节0x00→0xCC(SYNC0/1 Map+Output, 否则SYNC0留LATCH态DC过程数据死)、word2=5000(脉宽50µs)、尾部补真实0xFF; gen_eeprom.py按ETG.1000.6重写;DCU_ESI.xml PDI 0x05→0x8D、RxPdo/TxPdo互换改4×64bit对齐 - 删除GD32侧EEPROM烧录路径(1.5.29):移除EscEeprom/esc_eeprom、eeprom_image.h及主循环 烧录块,ESI镜像改由主站ecat_flash_eeprom.py经ESC 0x0500刷写(162字全校验通过); HBI互斥补屏蔽EXTI2(SYNC1),防Sync1_Isr两步事务打断主循环HBI访问致错址 - DC SYNC0通路可观测(1.5.30/31):EthercatSync新增sync0/sync1/escirq三个volatile计数器, task_runner 5s按增量打印速率;SSC OD新增只读0x2000 SYNC Diag,主站SDO轮询网页显示 - AL通路诊断(1.5.32):ECAT_Main/SetALStatus打印主站AL请求与写回状态+错误码, task_runner新增5s [ECAT][HB]心跳(仅同步ISR激活时);dc_app.c导出EcatDbg_ReadAlDiag - 删除死代码EthercatSlave::periodicIsr三缓冲PDO桥,PDO交换由SSC完成(4×64bit=32B); EthercatSlave::init()恢复调用并以isBusReady()(BYTE_TEST)门控 - CANFD修复同通道突发只发3帧:transmitFrame新增allow_abort区分突发/软件队列路径, 邮箱满落入软件TX队列(深度13)由TX完成中断drainTxQueueIsr回灌;BRS重新启用(0→1) - motor_state新增CONFIG帧缓存(0x00000101, DLC 24)记录目标值回报,条目44B→76B、每页5→3 - SPI 0xA2诊断扩展action 20~24(ECAT_NRST拉低/拉高/浮空/回读);新增ESP32 EN自检打印 - 版本号更新至1.5.32
224 lines
9.2 KiB
C++
224 lines
9.2 KiB
C++
/**
|
|
* @file esc_lan9252.cpp
|
|
* @brief LAN9252 EtherCAT ESC 驱动实现。
|
|
*
|
|
* 硬件使用 16-bit HBI Indexed Address Mode:
|
|
* - A2=0,A1=0 (偏移 0x00000) -> HBI_IDX_0
|
|
* - A2=1,A1=0 (偏移 0x40000) -> HBI_DATA_0 低 16 位
|
|
* - A2=1,A1=1 (偏移 0x60000) -> HBI_DATA_0 高 16 位
|
|
*
|
|
* 地址线换算(1.5.27 修正,此前 0x20000/0x30000 是按 8-bit 规则算的,DATA 访问全部
|
|
* 落在 HBI_IDX_0 高半字,IDX_0=0x1234 探针假通过而 BYTE_TEST 永远失败):
|
|
* 板线:LAN9252 A1(Pin25)→PD11=EXMC_A16,A2(Pin29)→PD12=EXMC_A17。
|
|
* GD32E50x 用户手册 p869:16-bit 数据宽度时 EXMC 内部将 HADDR[25:1] 接到
|
|
* EXMC_A[24:0],即 EXMC_A16=CPU 偏移 bit17(0x20000)、EXMC_A17=bit18(0x40000)。
|
|
* LAN9252 手册 Table 9-3:A2=1 选 HBI_DATA_0,A1 选低/高半字。
|
|
*
|
|
* 所有 ESC 寄存器/PDI 内存访问均通过"写 IDX -> 读写 DATA"完成。
|
|
*/
|
|
#include "comm/esc_lan9252.h"
|
|
#include "comm/esc_exmc_bus.h"
|
|
#include "sys/config.h"
|
|
#include "sys/systick.h"
|
|
#include <stdio.h>
|
|
#include "RTE_Components.h"
|
|
#include CMSIS_device_header
|
|
#include "gd32e50x_gpio.h"
|
|
#include "gd32e50x_rcu.h"
|
|
|
|
namespace {
|
|
/* HBI Indexed Mode 下 EXMC 偏移(A2=LAN9252 Pin29←PD12=EXMC_A17, A1=Pin25←PD11=EXMC_A16)
|
|
* EXMC 16-bit 模式地址左移 1 位:A16 引脚=CPU 偏移 0x20000,A17 引脚=CPU 偏移 0x40000。 */
|
|
constexpr uint32_t HBI_IDX0_OFFSET = 0x00000U; /* A2=0, A1=0 */
|
|
constexpr uint32_t HBI_DATA0_LOW_OFF = 0x40000U; /* A2=1(EXMC_A17), A1=0(EXMC_A16) */
|
|
constexpr uint32_t HBI_DATA0_HIGH_OFF = 0x60000U; /* A2=1(EXMC_A17), A1=1(EXMC_A16) */
|
|
|
|
/* 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 默认值(小端序)。 */
|
|
|
|
__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))
|
|
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);
|
|
}
|
|
} /* anonymous namespace */
|
|
|
|
bool EscLan9252::s_busReady = false;
|
|
bool EscLan9252::s_resetDone = false;
|
|
|
|
void EscLan9252::hardwareReset()
|
|
{
|
|
/* 受控复位 ECAT_NRST(PB10 → LAN9252 Pin11 RST#,SCH 第1页 Pin47)。
|
|
* 2026-08-22(1.5.25) 前固件从未初始化 PB10,RST# 仅靠 R44 10k 上拉自然释放,
|
|
* 不满足手册 18.6.3:tpurstd(电源稳定→RST#释放)≥25ms、trstia(低脉宽)≥200µs,
|
|
* 且要求释放前时钟输入已稳定。固件执行到此点时上电早已完成、晶振已稳定,
|
|
* 统一 30ms 低脉宽同时覆盖两个下限。RST# 释放瞬间重锁 CHIP_MODE0/1 strap
|
|
* (LED_LINK1/2 经 R35/R36 10k 下拉 → 00b = 2 端口 HBI 模式)。
|
|
* 释放后保持推挽输出高:空闲电平确定,并保留运行期软件复位手段;
|
|
* GD32 自身复位期间引脚回浮空,由 R44 上拉维持高,不会误复位 ESC。 */
|
|
rcu_periph_clock_enable(RCU_GPIOB);
|
|
gpio_init(CONFIG_ESC_NRST_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,
|
|
CONFIG_ESC_NRST_PIN);
|
|
gpio_bit_reset(CONFIG_ESC_NRST_PORT, CONFIG_ESC_NRST_PIN); /* RST# 拉低 */
|
|
SysTickTimer::delayMs(CONFIG_ESC_NRST_ASSERT_MS);
|
|
gpio_bit_set(CONFIG_ESC_NRST_PORT, CONFIG_ESC_NRST_PIN); /* 释放复位 */
|
|
SysTickTimer::delayMs(CONFIG_ESC_HBI_POLL_STEP_MS);
|
|
/* 回读实际电平:GD32 输入通路常连引脚,输出态也能读到真实电压。
|
|
* 驱动高却读回低 => ECAT_NRST 网络被外部短路到地(或引脚损伤),
|
|
* LAN9252 被按死在复位态 —— 直接解释 HBI 全浮空 + 晶振照振。 */
|
|
const bool pinHigh = (gpio_input_bit_get(CONFIG_ESC_NRST_PORT,
|
|
CONFIG_ESC_NRST_PIN) == SET);
|
|
printf("[ECAT][ESC] hardwareReset: RST# low %ums -> released, pin reads %s\r\n",
|
|
(unsigned)CONFIG_ESC_NRST_ASSERT_MS,
|
|
pinHigh ? "HIGH" : "*** LOW (net shorted to GND / pin damaged) ***");
|
|
}
|
|
|
|
uint8_t EscLan9252::nrstDrive(uint8_t action)
|
|
{
|
|
/* SPI 0xA2 action 20+ 的 NRST 手动诊断入口。
|
|
* 返回值 = 实际引脚电平(0/1),非 OK 码;0xEE = 无效 action。 */
|
|
switch (action) {
|
|
case 0: /* 恢复自动态:推挽输出高(与 hardwareReset 释放后一致) */
|
|
case 2: /* 持续拉高 */
|
|
gpio_init(CONFIG_ESC_NRST_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,
|
|
CONFIG_ESC_NRST_PIN);
|
|
gpio_bit_set(CONFIG_ESC_NRST_PORT, CONFIG_ESC_NRST_PIN);
|
|
break;
|
|
case 1: /* 持续拉低(手动保持复位) */
|
|
gpio_init(CONFIG_ESC_NRST_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,
|
|
CONFIG_ESC_NRST_PIN);
|
|
gpio_bit_reset(CONFIG_ESC_NRST_PORT, CONFIG_ESC_NRST_PIN);
|
|
break;
|
|
case 3: /* 释放:浮空输入,网络交给 R44 10k 上拉 */
|
|
gpio_init(CONFIG_ESC_NRST_PORT, GPIO_MODE_IN_FLOATING, 0,
|
|
CONFIG_ESC_NRST_PIN);
|
|
break;
|
|
case 4: /* 只读回,不改驱动 */
|
|
break;
|
|
default:
|
|
return 0xEE;
|
|
}
|
|
const bool level = (gpio_input_bit_get(CONFIG_ESC_NRST_PORT,
|
|
CONFIG_ESC_NRST_PIN) == SET);
|
|
static const char *const kName[5] = {
|
|
"auto(high)", "drive LOW", "drive HIGH", "float", "read-only"};
|
|
printf("[ECAT][NRST] %s -> pin reads %s\r\n", kName[action],
|
|
level ? "HIGH" : "LOW");
|
|
return level ? 1U : 0U;
|
|
}
|
|
|
|
bool EscLan9252::init()
|
|
{
|
|
s_busReady = false; /* 1.5.27:s_busReady 语义 = init 完整成功(EXMC+探针+BYTE_TEST) */
|
|
|
|
/* 使能 DWT 周期计数器,供 hw_gd32exmc/SSC 执行时间测量 */
|
|
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
|
DWT->CYCCNT = 0U;
|
|
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
|
|
|
/* 受控 RST# 复位(仅首次;重复调用 init 不得二次复位,否则清掉 SSC 配置) */
|
|
if (!s_resetDone) {
|
|
hardwareReset();
|
|
s_resetDone = true;
|
|
}
|
|
|
|
if (!EscExmcBus::init()) {
|
|
printf("[ECAT][ESC] init: FAIL, EXMC bus not ready (see [ECAT][EXMC] log)\r\n");
|
|
return false;
|
|
}
|
|
|
|
/* HBI 存活探针(数据手册 9.5.4.2):先直接读 HBI_IDX_0(offset 0,不写任何东西),
|
|
* 其复位默认值 = 0x1234。RST# 释放后 ESC 需完成内部启动与 EEPROM 加载,
|
|
* 故按手册建议轮询(此前为单次读,复位后立即读可能仍在启动中)。
|
|
* 若超时仍 != 0x1234(通常是 0x0000/0xFFFF 或上次写入的回显),
|
|
* 说明 LAN9252 完全没有驱动总线——CS 未拉低、CHIP_MODE strap 非 00(三端口=SPI only)
|
|
* 或芯片未出复位/未上电,而不是 EEPROM 问题(HBI 在 EEPROM 空白时也工作)。 */
|
|
uint16_t idx0 = EscExmcBus::read16(HBI_IDX0_OFFSET);
|
|
for (uint32_t waited = 0U;
|
|
idx0 != 0x1234U && waited < CONFIG_ESC_HBI_POLL_TIMEOUT_MS;
|
|
waited += CONFIG_ESC_HBI_POLL_STEP_MS) {
|
|
SysTickTimer::delayMs(CONFIG_ESC_HBI_POLL_STEP_MS);
|
|
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(小端)。
|
|
* 该值只有 HBI_DATA_0 偏移正确(EXMC 16-bit 地址左移 1 位)才可能读到。 */
|
|
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);
|
|
printf("[ECAT][ESC] hint: IDX probe passed but DATA access failed -> check HBI_DATA_0 offsets (0x40000/0x60000)\r\n");
|
|
return false;
|
|
}
|
|
printf("[ECAT][ESC] init: OK, HBI alive (IDX=0x1234, BYTE_TEST=0x87654321)\r\n");
|
|
s_busReady = true;
|
|
return true;
|
|
}
|
|
|
|
uint32_t EscLan9252::readChipId()
|
|
{
|
|
/* INIT-ONLY:init 阶段中断未使能,直接读取 BYTE_TEST(32-bit) */
|
|
return readReg32(LAN9252_REG_BYTE_TEST);
|
|
}
|
|
|
|
uint16_t EscLan9252::initOnlyReadReg(uint16_t addr)
|
|
{
|
|
return readReg16(addr);
|
|
}
|
|
|
|
void EscLan9252::initOnlyWriteReg(uint16_t addr, uint16_t v)
|
|
{
|
|
writeReg16(addr, v);
|
|
}
|