feat(comm): 1.6.22 稀疏写 ESC + 1.6.23~24 pmDrive 电源管理; LAN9252 性能审计报告
This commit is contained in:
@@ -80,6 +80,28 @@ constexpr uint32_t PRAM_BUSY = 1UL << 31; /**< PRAM_*_CMD:写 1 启动,完成
|
||||
constexpr uint32_t PRAM_AVAIL = 1UL << 0; /**< PRAM_*_CMD:数据可用(读)/空间可用(写) */
|
||||
constexpr uint32_t POLL_MAX = 2000UL; /**< busy/avail 轮询上限(~ms 级,防芯片异常时死等) */
|
||||
|
||||
/* 电源管理(DS 6.3/表 5-1):芯片级睡眠 D1-D3。
|
||||
* READY=0 期间仅 HW_CFG/PMT_CTRL/BYTE_TEST/RESET_CTL 四个寄存器可访问(DS 6.2.3),
|
||||
* 恰好覆盖睡眠进入与唤醒流程所需;PMT_CTRL 可在睡眠态安全读(DS 6.3.5.1 注)。 */
|
||||
constexpr uint16_t LAN9252_REG_HW_CFG = 0x0074U; /**< 硬件配置:bit27=READY(与 PMT_CTRL bit0 同源,DS 6.3.5.1)。 */
|
||||
constexpr uint16_t LAN9252_REG_PMT_CTRL = 0x0084U; /**< 功耗管理控制:bit0=READY,bit28=PM_SLEEP_EN,bit31:29=PM_MODE。 */
|
||||
constexpr uint32_t PMT_CTRL_READY = 1UL << 0;
|
||||
constexpr uint32_t PMT_CTRL_PM_MODE_MSK = 7UL << 29; /**< 001=D1 010=D2 011=D3。 */
|
||||
constexpr uint32_t PMT_CTRL_PM_SLEEP_EN = 1UL << 28; /**< 置 1 进入 PM_MODE 指定的睡眠档(须与 PM_MODE 分两次写)。 */
|
||||
|
||||
/* ESC 核心 MII 管理(ETG.1000.4 ESC 寄存器 0x0510-0x0514,经 csrRead/csrWrite 间接访问;
|
||||
* 表 5-1 证实 PHY 寄存器不在 HBI 直接空间)。用于 D3 前置的 PHY General Power Down
|
||||
* (DS 6.3.4:D3 要求两 PHY 先 General Power Down)与唤醒后恢复。 */
|
||||
constexpr uint16_t ESC_REG_MII_CTRL = 0x0510U; /**< bit0=CMD(写 1 启动自清) bit1=WRITE bit8=BUSY。 */
|
||||
constexpr uint16_t ESC_REG_MII_ADDR = 0x0512U; /**< bit4:0=PHY_ADDR bit15:5=REG_ADDR。 */
|
||||
constexpr uint16_t ESC_REG_MII_DATA = 0x0514U; /**< bit15:0=MII 数据。 */
|
||||
constexpr uint16_t MII_CTRL_CMD = 1U << 0;
|
||||
constexpr uint16_t MII_CTRL_WRITE = 1U << 1;
|
||||
constexpr uint16_t MII_CTRL_BUSY = 1U << 8;
|
||||
constexpr uint8_t MII_REG_BMCR = 0U; /**< IEEE 802.3 Basic Control 寄存器 0。 */
|
||||
constexpr uint16_t MII_BMCR_POWER_DOWN = 1U << 11; /**< PHY_PWR_DWN(DS 11.2.8)。 */
|
||||
constexpr uint8_t MII_PHY_ADDR_B = 1U; /**< PHY B 地址恒 1(DS 11.1.1);A 模式相关 0/2,运行期判定。 */
|
||||
|
||||
__attribute__((section(".ramfunc"), noinline))
|
||||
void setHbiIndex(uint16_t addr)
|
||||
{
|
||||
@@ -126,11 +148,86 @@ void writeReg32(uint16_t addr, uint32_t v)
|
||||
writeDataLow((uint16_t)(v & 0xFFFFU));
|
||||
writeDataHigh((uint16_t)(v >> 16));
|
||||
}
|
||||
|
||||
/* ---- 电源管理 helper(仅 pmDrive 诊断路径使用) ---- */
|
||||
|
||||
/** @brief DWT CYCCNT 忙等微秒。
|
||||
* @note EXTI ISR 上下文(0xA2 在 AppSpiCommand::onCsRelease 内同步执行)禁止用
|
||||
* SysTickTimer::delayMs:其基于 SysTick 中断递增 s_ms,中断被 CS EXTI
|
||||
* 压住时计数冻结会死等。DWT CYCCNT 已由 init() 使能,纯寄存器读,ISR 安全。 */
|
||||
__attribute__((section(".ramfunc"), noinline))
|
||||
void pmBusyWaitUs(uint32_t us)
|
||||
{
|
||||
const uint32_t start = DWT->CYCCNT;
|
||||
const uint32_t cycles = us * (SystemCoreClock / 1000000U);
|
||||
while ((DWT->CYCCNT - start) < cycles) { }
|
||||
}
|
||||
|
||||
/** @brief ESC 核心 MII 管理事务(读/写一个 PHY 寄存器)。
|
||||
* @note 调用方必须已持 HBI 互斥(pmDrive 的 maskEnter 段内)。MII 事务典型 ~25µs
|
||||
* (2.5MHz MDIO × 32bit 帧结构),BUSY 轮询上限 POLL_MAX 防芯片异常死等。 */
|
||||
bool miiXfer(uint8_t phyAddr, uint8_t regAddr, bool write, uint16_t &data)
|
||||
{
|
||||
EscLan9252::csrWrite(ESC_REG_MII_ADDR, ((uint32_t)regAddr << 5) | phyAddr, 2U);
|
||||
if (write) {
|
||||
EscLan9252::csrWrite(ESC_REG_MII_DATA, data, 2U);
|
||||
}
|
||||
EscLan9252::csrWrite(ESC_REG_MII_CTRL,
|
||||
write ? (MII_CTRL_CMD | MII_CTRL_WRITE) : MII_CTRL_CMD, 2U);
|
||||
for (uint32_t i = 0U; i < POLL_MAX; i++) {
|
||||
if ((EscLan9252::csrRead(ESC_REG_MII_CTRL, 2U) & MII_CTRL_BUSY) == 0U) {
|
||||
if (!write) {
|
||||
data = (uint16_t)EscLan9252::csrRead(ESC_REG_MII_DATA, 2U);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @brief 置/清两 PHY 的 BMCR.PowerDown(D3 进入前置要求/唤醒后恢复)。
|
||||
* @return 全部事务成功 true。PHY A 地址模式相关(DS 11.1.1:0 或 2,随 chip_mode_strap),
|
||||
* 以 BMCR 读回有效性判定(0x0000/0xFFFF=无响应),结果缓存在静态变量。 */
|
||||
bool phySetPowerDown(bool down)
|
||||
{
|
||||
static uint8_t addrA = 0xFFU; /* 0xFF=未判定 */
|
||||
if (addrA == 0xFFU) {
|
||||
addrA = 0U;
|
||||
uint16_t probe = 0xFFFFU;
|
||||
if (!miiXfer(0U, MII_REG_BMCR, false, probe) ||
|
||||
probe == 0x0000U || probe == 0xFFFFU) {
|
||||
addrA = 2U;
|
||||
}
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
const uint8_t addrs[2] = { addrA, MII_PHY_ADDR_B };
|
||||
for (uint8_t i = 0U; i < 2U; i++) {
|
||||
uint16_t bmcr = 0U;
|
||||
ok = miiXfer(addrs[i], MII_REG_BMCR, false, bmcr) && ok;
|
||||
if (down) {
|
||||
bmcr |= MII_BMCR_POWER_DOWN;
|
||||
} else {
|
||||
bmcr &= (uint16_t)~MII_BMCR_POWER_DOWN;
|
||||
}
|
||||
ok = miiXfer(addrs[i], MII_REG_BMCR, true, bmcr) && ok;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
/** @brief 把 PMT_CTRL 值打包成单字节状态:bit0=READY bit1=PM_SLEEP_EN bit3:2=PM_MODE。 */
|
||||
uint8_t pmPackStatus(uint32_t pmt)
|
||||
{
|
||||
return (uint8_t)(((pmt & PMT_CTRL_READY) ? 0x01U : 0U) |
|
||||
((pmt & PMT_CTRL_PM_SLEEP_EN) ? 0x02U : 0U) |
|
||||
(((pmt >> 29) & 3U) << 2));
|
||||
}
|
||||
} /* anonymous namespace */
|
||||
|
||||
bool EscLan9252::s_busReady = false;
|
||||
bool EscLan9252::s_coreReady = false;
|
||||
bool EscLan9252::s_resetDone = false;
|
||||
bool EscLan9252::s_pmPhyDown = false;
|
||||
|
||||
void EscLan9252::hardwareReset()
|
||||
{
|
||||
@@ -193,6 +290,98 @@ uint8_t EscLan9252::nrstDrive(uint8_t action)
|
||||
return level ? 1U : 0U;
|
||||
}
|
||||
|
||||
uint8_t EscLan9252::pmDrive(uint8_t op)
|
||||
{
|
||||
if (op > 4U) {
|
||||
return 0xEEU;
|
||||
}
|
||||
if (!s_busReady) {
|
||||
printf("[ECAT][PM] op%u rejected: HBI bus not ready\r\n", op);
|
||||
return 0xE1U;
|
||||
}
|
||||
|
||||
/* 0xA2 在 CS EXTI ISR 上下文执行(AppSpiCommand::onCsRelease 同步调用)。
|
||||
* HBI Indexed 是两步事务(写 IDX→读写 DATA),而 SYNC0(EXTI0,preempt=0)是
|
||||
* 全系统最高优先级、BASEPRI 挡不住,与主循环 HW_* 路径相同必须关 NVIC:
|
||||
* maskEnter 屏蔽 EXTI0/EXTI1 并返回先前状态,ISR 里调用同样正确(纯 NVIC
|
||||
* 寄存器保存/恢复,挂起的中断在 maskExit 后补执行)。
|
||||
* 残余竞态窗口:CS EXTI 优先级更高,可抢占主循环持锁中的 HBI 事务半截——
|
||||
* 人工低频诊断可接受(continuityDrive 直接切走 EXMC 引脚破坏性更大)。 */
|
||||
const uint32_t mask = EscExmcBus::maskEnter();
|
||||
|
||||
uint8_t status;
|
||||
if (op == 0U) {
|
||||
/* 状态查询。注意:1.6.24 实测 HBI 读事务本身就会唤醒睡眠中的芯片,
|
||||
* 本操作的语义 = "读状态(若在睡眠则主动打破睡眠)",非无侵入观测。 */
|
||||
const uint32_t pmt = readReg32(LAN9252_REG_PMT_CTRL);
|
||||
status = pmPackStatus(pmt);
|
||||
printf("[ECAT][PM] status: PMT_CTRL=0x%08lX READY=%u SLEEP_EN=%u MODE=D%u\r\n",
|
||||
(unsigned long)pmt,
|
||||
(unsigned)((status >> 0) & 1U), (unsigned)((status >> 1) & 1U),
|
||||
(unsigned)((status >> 2) & 3U));
|
||||
} else if (op <= 3U) {
|
||||
/* 进入 D1/D2/D3。D3 最深(晶振/PLL 全关,仅手动唤醒),手册要求先把两个 PHY
|
||||
* 置 General Power Down,否则 PLL 在 D2 不真关/D3 直接不满足前置。 */
|
||||
if (op == 3U) {
|
||||
s_pmPhyDown = phySetPowerDown(true);
|
||||
printf("[ECAT][PM] D3 pre: both PHY power-down %s\r\n",
|
||||
s_pmPhyDown ? "OK" : "FAIL (entering anyway)");
|
||||
}
|
||||
uint32_t v = readReg32(LAN9252_REG_PMT_CTRL);
|
||||
v = (v & ~PMT_CTRL_PM_MODE_MSK) | ((uint32_t)op << 29);
|
||||
/* PM_MODE 与 PM_SLEEP_EN 必须分两次写(DS 6.3.5.1 注:同一写中改 MODE 置 EN 无效)。 */
|
||||
writeReg32(LAN9252_REG_PMT_CTRL, v);
|
||||
writeReg32(LAN9252_REG_PMT_CTRL, v | PMT_CTRL_PM_SLEEP_EN);
|
||||
/* 1.6.24 实测:进入睡眠后任何 HBI 访问(含读!)都会立即触发唤醒——
|
||||
* 手册 6.3.4.2 只说 HBI "write" 唤醒,但芯片实际按主机 CS 活动判定,
|
||||
* 写入后的读回验证会在 <50ms 内把芯片叫回 D0(板级复现:进 D1 后
|
||||
* 3s/6s 静默仍自动回 D0,唯读回验证为已知访问;去掉后待验证)。
|
||||
* 故此处置信构造返回值,不再读回;睡眠态观测交给"读状态"(读即唤醒,
|
||||
* 语义=主动打破睡眠),页面轮询须在睡眠态暂停。 */
|
||||
status = (uint8_t)(0x02U | ((uint32_t)op << 2)); /* READY=0 | SLEEP_EN=1 | MODE=op */
|
||||
static const char *const kMode[4] = {"D0", "D1", "D2", "D3"};
|
||||
printf("[ECAT][PM] enter %s: entered, no read-back (HBI read wakes chip)\r\n",
|
||||
kMode[op]);
|
||||
printf("[ECAT][PM] WARNING: EtherCAT comms halted, wake with 0xA2 action 34\r\n");
|
||||
} else {
|
||||
/* 手动唤醒(DS 6.3.4.2):对 BYTE_TEST 做一次 HBI 写触发,随后轮询
|
||||
* BYTE_TEST=0x87654321(接口恢复)→ PMT_CTRL.READY=1(回 D0 且 PLL 稳定)。
|
||||
* 手册标称 ≤2ms,轮询 50µs 步进、上限 10ms——EXTI ISR 内不做长阻塞。 */
|
||||
writeReg32(LAN9252_REG_BYTE_TEST, LAN9252_BYTE_TEST_VALUE);
|
||||
bool awake = false;
|
||||
for (uint32_t i = 0U; i < 200U; i++) {
|
||||
pmBusyWaitUs(50U);
|
||||
if (readReg32(LAN9252_REG_BYTE_TEST) == LAN9252_BYTE_TEST_VALUE) {
|
||||
awake = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (awake) {
|
||||
for (uint32_t i = 0U; i < 200U; i++) {
|
||||
pmBusyWaitUs(50U);
|
||||
if ((readReg32(LAN9252_REG_PMT_CTRL) & PMT_CTRL_READY) != 0U) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
awake = (readReg32(LAN9252_REG_PMT_CTRL) & PMT_CTRL_READY) != 0U;
|
||||
}
|
||||
if (awake && s_pmPhyDown) {
|
||||
/* D3 唤醒后恢复 PHY 上电,链路重新自协商(数秒后 LINK 恢复)。 */
|
||||
const bool restored = phySetPowerDown(false);
|
||||
s_pmPhyDown = !restored;
|
||||
printf("[ECAT][PM] wake: PHY power-up %s (autoneg re-negotiating)\r\n",
|
||||
restored ? "OK" : "FAIL (retry action 34)");
|
||||
}
|
||||
status = awake ? pmPackStatus(readReg32(LAN9252_REG_PMT_CTRL)) : 0U;
|
||||
printf("[ECAT][PM] wake: %s -> status=0x%02X (READY=%u SLEEP_EN=%u)\r\n",
|
||||
awake ? "OK" : "TIMEOUT (10ms, poll action 30 / retry 34)",
|
||||
status, (unsigned)(status & 1U), (unsigned)((status >> 1) & 1U));
|
||||
}
|
||||
|
||||
EscExmcBus::maskExit(mask);
|
||||
return status;
|
||||
}
|
||||
|
||||
bool EscLan9252::init()
|
||||
{
|
||||
s_busReady = false; /* 1.5.27:s_busReady 语义 = init 完整成功(EXMC+探针+BYTE_TEST) */
|
||||
|
||||
Reference in New Issue
Block a user