新增SPI命令类型0xC4用于ADC调试反馈,实现在GD32上采集 温度/VDDA/VIN等模拟量数据的反馈功能,并支持主动推送和被动查询模式。 feat(spi): 扩展PING命令响应格式 修改PING命令响应格式,增加APP身份信息16字节载荷,包含版本号、 启动时间、功能开关位图等信息,用于主机获取从设备详细状态。 refactor(can_feedback): 优化CAN反馈推送逻辑 改进CAN反馈推送机制,跳过编译期关闭的通道,只遍历启用的通道, 确保推送效率并避免无效操作。 feat(comm): 添加EXMC总线初始化和诊断功能 添加了EXMC总线的初始化功能,包括GPIO配置、NOR/SRAM区域参数设置, 以及总线探活机制。实现了总线可见性检测,当检测到全0或全1的挂死状态时 会返回false并输出错误信息。同时添加了printf日志输出用于调试。 refactor(esc_lan9252): 重构ESC初始化返回值 将EscLan9252::init()方法的返回类型从void改为bool, 使其能够返回EXMC总线准备状态,便于上层进行状态检查。 docs(app_spi_cmd_ping): 添加代码注释文档 为SPI命令处理相关类和方法添加详细的Doxygen风格注释文档, 提高代码可读性和维护性。
128 lines
5.1 KiB
C++
128 lines
5.1 KiB
C++
/**
|
|
* @file esc_lan9252.cpp
|
|
* @brief LAN9252 EtherCAT ESC 驱动实现。
|
|
*/
|
|
#include "comm/esc_lan9252.h"
|
|
#include "comm/esc_exmc_bus.h"
|
|
#include "sys/config.h"
|
|
#include <stdio.h>
|
|
#include "RTE_Components.h"
|
|
#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。 */
|
|
|
|
/* PDI 内存基址偏移(实际取决于 LAN9252 PDI 配置,这里为示意值) */
|
|
constexpr uint16_t LAN9252_PDI_BASE_OFFSET = 0x1000U;
|
|
}
|
|
|
|
bool EscLan9252::s_busReady = false;
|
|
|
|
bool EscLan9252::init()
|
|
{
|
|
/* 使能 DWT 周期计数器,供 periodicIsr 执行时间测量 */
|
|
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
|
DWT->CYCCNT = 0U;
|
|
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
|
|
|
s_busReady = EscExmcBus::init();
|
|
if (!s_busReady) {
|
|
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。
|
|
* ============================================================ */
|
|
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;
|
|
}
|
|
|
|
uint16_t EscLan9252::readReg(uint16_t addr)
|
|
{
|
|
/* ESC 寄存器地址按字节寻址,EXMC 16-bit 访问需要地址左移 1 位 */
|
|
return EscExmcBus::read16((uint32_t)addr << 1U);
|
|
}
|
|
|
|
__attribute__((section(".ramfunc"), noinline))
|
|
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);
|
|
}
|
|
/* 处理奇数字节 */
|
|
if ((len & 1U) != 0U) {
|
|
uint16_t w = EscExmcBus::read16(exmc_offset + (uint32_t)(half_count * 2U));
|
|
buf[half_count * 2U] = (uint8_t)(w & 0xFFU);
|
|
}
|
|
}
|
|
|
|
__attribute__((section(".ramfunc"), noinline))
|
|
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);
|
|
}
|
|
if ((len & 1U) != 0U) {
|
|
EscExmcBus::write16(exmc_offset + (uint32_t)(half_count * 2U), (uint16_t)buf[half_count * 2U]);
|
|
}
|
|
}
|
|
|
|
/* ---- initOnly* 接口:INIT-ONLY,仅允许在 EthercatSync::init() 使能 EXTI NVIC 之前调用 ----
|
|
* 运行期禁止调用;需 ESC 寄存器访问须通过 SYNC0 ISR 代访队列(本 spec 不涵盖)。 */
|
|
uint16_t EscLan9252::initOnlyReadReg(uint16_t addr)
|
|
{
|
|
return readReg(addr);
|
|
}
|