feat(ethercat): LAN9252 bring-up完成,EEPROM烧录改主站侧并增强DC/AL可观测性
- 新增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
This commit is contained in:
@@ -1,35 +1,20 @@
|
||||
/**
|
||||
* @file ethercat_slave.cpp
|
||||
* @brief EtherCAT 从站周期处理实现。
|
||||
* @brief EtherCAT 从站 init 探针实现。
|
||||
*
|
||||
* 运行期 PDO 交换由 SSC 协议栈完成(Sync0_Isr → ECAT_Application →
|
||||
* dc_app.c 的 APPL_InputMapping/APPL_OutputMapping),本文件只负责
|
||||
* init 阶段的 HBI 探活、芯片 ID 与 AL 状态诊断。
|
||||
*/
|
||||
#include "comm/ethercat_slave.h"
|
||||
#include "comm/esc_lan9252.h"
|
||||
#include "sys/config.h"
|
||||
#include "sys/task_manager.h"
|
||||
#include "main.h"
|
||||
#include <cstdio>
|
||||
|
||||
PdoBuffer EthercatSlave::s_inputs[3] = {};
|
||||
PdoBuffer EthercatSlave::s_outputs[3] = {};
|
||||
PdoBuffer EthercatSlave::s_appInputs = {};
|
||||
PdoBuffer EthercatSlave::s_appOutputs = {};
|
||||
volatile uint8_t EthercatSlave::s_inputWriteIdx = 0U;
|
||||
volatile uint8_t EthercatSlave::s_outputReadIdx = 0U;
|
||||
volatile uint16_t EthercatSlave::s_periodicSeq = 0U;
|
||||
uint16_t EthercatSlave::s_processedSeq = 0U;
|
||||
volatile uint32_t EthercatSlave::s_isrMaxCycles = 0U;
|
||||
volatile uint32_t EthercatSlave::s_isrAvgCycles = 0U;
|
||||
volatile uint32_t EthercatSlave::s_isrCount = 0U;
|
||||
bool EthercatSlave::s_escReady = false;
|
||||
uint32_t EthercatSlave::s_chipId = 0U;
|
||||
uint8_t EthercatSlave::s_alState = 0U;
|
||||
|
||||
/* ISR 执行时间评估:
|
||||
* - 若 DWT 实测 max > CONFIG_ETHERCAT_ISR_MAX_US_THRESHOLD(5µs),需评估:
|
||||
* a) 减小 CONFIG_ETHERCAT_PDO_SIZE(默认 32 字节)
|
||||
* b) 优化 EXMC 时序参数(esc_exmc_bus.cpp 中 read_latency/write_latency)
|
||||
* - 若 max ≤ 5µs,接受现状;preempt=0 阻塞窗口 ≤ 5µs,CAN/SPI 中断需容忍此窗口。
|
||||
* SPI0 从机在此窗口内不会 RX 溢出(假设 SPI 时钟 ≤ 10MHz,5µs ≥ 50 bit,远小于一帧)。 */
|
||||
|
||||
void EthercatSlave::init()
|
||||
{
|
||||
ECAT_LOGI("[ECAT][SLAVE] init: begin\r\n");
|
||||
@@ -40,10 +25,6 @@ void EthercatSlave::init()
|
||||
s_alState = 0U;
|
||||
s_escReady = false;
|
||||
LOG_ERR("[ECAT][SLAVE] init: FAIL, ESC bus not ready\r\n");
|
||||
s_inputWriteIdx = 0U;
|
||||
s_outputReadIdx = 0U;
|
||||
s_periodicSeq = 0U;
|
||||
s_processedSeq = 0U;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -93,98 +74,5 @@ void EthercatSlave::init()
|
||||
(unsigned long)s_chipId, (unsigned)s_alState, alStateName, (unsigned)alCode);
|
||||
}
|
||||
|
||||
s_inputWriteIdx = 0U;
|
||||
s_outputReadIdx = 0U;
|
||||
s_periodicSeq = 0U;
|
||||
s_processedSeq = 0U;
|
||||
LOG_ERR("[ECAT][SLAVE] init: %s\r\n", s_escReady ? "OK" : "FAIL(partial)");
|
||||
}
|
||||
|
||||
__attribute__((section(".ramfunc"), noinline))
|
||||
void EthercatSlave::periodicIsr()
|
||||
{
|
||||
const uint32_t cycStart = DWT->CYCCNT;
|
||||
|
||||
/* 1. 读取输入 PDO 到当前写入缓冲 */
|
||||
EscLan9252::readPdi(CONFIG_ESC_PDI_INPUT_OFFSET, s_inputs[s_inputWriteIdx].data, CONFIG_ETHERCAT_PDO_SIZE);
|
||||
s_inputs[s_inputWriteIdx].len = CONFIG_ETHERCAT_PDO_SIZE;
|
||||
s_inputWriteIdx = (s_inputWriteIdx + 1U) % 3U;
|
||||
|
||||
/* 2. 写入输出 PDO(从当前读取缓冲) */
|
||||
EscLan9252::writePdi(CONFIG_ESC_PDI_OUTPUT_OFFSET, s_outputs[s_outputReadIdx].data, s_outputs[s_outputReadIdx].len);
|
||||
s_outputReadIdx = (s_outputReadIdx + 1U) % 3U;
|
||||
|
||||
/* 3. 递增周期序列号通知主循环(替代 TaskManager::set,避免 BASEPRI 竞态) */
|
||||
s_periodicSeq++;
|
||||
|
||||
const uint32_t elapsed = DWT->CYCCNT - cycStart;
|
||||
s_isrCount++;
|
||||
if (elapsed > s_isrMaxCycles) {
|
||||
s_isrMaxCycles = elapsed;
|
||||
}
|
||||
/* 滑动平均:avg = avg + (elapsed - avg) / 16 (近似 EMA,避免除法精度问题) */
|
||||
s_isrAvgCycles = s_isrAvgCycles + ((int32_t)elapsed - (int32_t)s_isrAvgCycles) / 16;
|
||||
}
|
||||
|
||||
const PdoBuffer* EthercatSlave::getLatestInputs()
|
||||
{
|
||||
/* 返回最新完成的缓冲 = (writeIdx + 2) % 3,与 ISR 当前写入位置隔一个 */
|
||||
return &s_inputs[(s_inputWriteIdx + 2U) % 3U];
|
||||
}
|
||||
|
||||
void EthercatSlave::setOutputs(const PdoBuffer *outputs)
|
||||
{
|
||||
if (outputs == nullptr) {
|
||||
return;
|
||||
}
|
||||
/* 写入与 ISR 当前读取位置隔 2 个缓冲,提供 2 周期宽限。
|
||||
* 2 周期宽限下拷贝竞态窗口消除,前提是主循环处理时间 < 2 × 周期时间。 */
|
||||
const uint8_t write_idx = (s_outputReadIdx + 2U) % 3U;
|
||||
s_outputs[write_idx] = *outputs;
|
||||
}
|
||||
|
||||
void EthercatSlave::process()
|
||||
{
|
||||
/* 应用层 PDO 桥接(非实时,由主循环周期序列号或 ESC IRQ 触发)。
|
||||
* 运行期 PDO 交换仅通过 getLatestInputs()/setOutputs() 三缓冲进行;ESC 寄存器访问仅限 init 阶段。
|
||||
*
|
||||
* 框架级桥接:从三缓冲取最新输入到 s_appInputs,把 s_appOutputs 推回三缓冲。
|
||||
* TODO(spec): 应用对象字典映射(具体输入字节对应哪个应用变量,如字节 0-3=温度浮点)
|
||||
* 待后续应用层 spec 定义;
|
||||
* 应用层通过 getApplicationInputs()/setApplicationOutputs() 访问快照。 */
|
||||
const PdoBuffer *latest = getLatestInputs();
|
||||
if (latest != nullptr) {
|
||||
s_appInputs = *latest;
|
||||
}
|
||||
setOutputs(&s_appOutputs);
|
||||
}
|
||||
|
||||
const PdoBuffer* EthercatSlave::getApplicationInputs()
|
||||
{
|
||||
return &s_appInputs;
|
||||
}
|
||||
|
||||
void EthercatSlave::setApplicationOutputs(const PdoBuffer *outputs)
|
||||
{
|
||||
if (outputs == nullptr) {
|
||||
return;
|
||||
}
|
||||
s_appOutputs = *outputs;
|
||||
}
|
||||
|
||||
bool EthercatSlave::hasPendingPeriodic()
|
||||
{
|
||||
return s_periodicSeq != s_processedSeq;
|
||||
}
|
||||
|
||||
void EthercatSlave::markPeriodicProcessed()
|
||||
{
|
||||
s_processedSeq = s_periodicSeq;
|
||||
}
|
||||
|
||||
void EthercatSlave::getIsrStats(uint32_t *maxCycles, uint32_t *avgCycles, uint32_t *count)
|
||||
{
|
||||
if (maxCycles) *maxCycles = s_isrMaxCycles;
|
||||
if (avgCycles) *avgCycles = s_isrAvgCycles;
|
||||
if (count) *count = s_isrCount;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user