新增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风格注释文档, 提高代码可读性和维护性。
141 lines
6.7 KiB
C++
141 lines
6.7 KiB
C++
/**
|
||
* @file esc_exmc_bus.cpp
|
||
* @brief LAN9252 ESC 的 EXMC 16-bit 并行总线驱动实现。
|
||
*
|
||
* 使用 GigaDevice gd32e50x_exmc 标准库。
|
||
* 注意:DFP 只把 FMC 注册为 CMSIS 组件,EXMC 未进组件列表;
|
||
* 源文件放在 RTE/Device/... 并由 cproject group device_exmc 本地编译。
|
||
*/
|
||
#include "comm/esc_exmc_bus.h"
|
||
#include "sys/config.h"
|
||
#include <stdio.h>
|
||
|
||
extern "C" {
|
||
#include "gd32e50x_rcu.h"
|
||
#include "gd32e50x_gpio.h"
|
||
#include "gd32e50x_exmc.h" /* RTE/Device/GD32E508VE 或 -I 同目录 */
|
||
}
|
||
|
||
/* 内存映射访问宏:基址 + 偏移(按 16-bit 半字对齐,LAN9252 HBI 地址左移 1 位)。 */
|
||
#define ESC_EXMC_REG16(offset) (*(volatile uint16_t*)(CONFIG_ESC_EXMC_BASE_ADDR + (offset)))
|
||
|
||
bool EscExmcBus::s_ok = false;
|
||
|
||
bool EscExmcBus::init()
|
||
{
|
||
s_ok = false;
|
||
|
||
printf("[ECAT][EXMC] init: enable RCU clocks\r\n");
|
||
rcu_periph_clock_enable(RCU_GPIOD);
|
||
rcu_periph_clock_enable(RCU_GPIOE);
|
||
rcu_periph_clock_enable(RCU_AF);
|
||
rcu_periph_clock_enable(RCU_EXMC);
|
||
|
||
/* 配置 16-bit 数据线 D0-D15 为 AF 推挽 */
|
||
gpio_init(CONFIG_EXMC_D0_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D0_PIN);
|
||
gpio_init(CONFIG_EXMC_D1_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D1_PIN);
|
||
gpio_init(CONFIG_EXMC_D2_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D2_PIN);
|
||
gpio_init(CONFIG_EXMC_D3_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D3_PIN);
|
||
gpio_init(CONFIG_EXMC_D4_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D4_PIN);
|
||
gpio_init(CONFIG_EXMC_D5_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D5_PIN);
|
||
gpio_init(CONFIG_EXMC_D6_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D6_PIN);
|
||
gpio_init(CONFIG_EXMC_D7_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D7_PIN);
|
||
gpio_init(CONFIG_EXMC_D8_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D8_PIN);
|
||
gpio_init(CONFIG_EXMC_D9_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D9_PIN);
|
||
gpio_init(CONFIG_EXMC_D10_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D10_PIN);
|
||
gpio_init(CONFIG_EXMC_D11_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D11_PIN);
|
||
gpio_init(CONFIG_EXMC_D12_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D12_PIN);
|
||
gpio_init(CONFIG_EXMC_D13_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D13_PIN);
|
||
gpio_init(CONFIG_EXMC_D14_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D14_PIN);
|
||
gpio_init(CONFIG_EXMC_D15_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D15_PIN);
|
||
|
||
/* 控制线:A16 / A17 / NOE / NWE / NE1
|
||
* A17(PD12) 用于 HBI Indexed Mode 访问 HBI_DATA_0,已将 LAN9252 A2 (Pin 29) 接到 PD12。 */
|
||
gpio_init(CONFIG_EXMC_A16_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_A16_PIN);
|
||
gpio_init(CONFIG_EXMC_A17_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_A17_PIN);
|
||
gpio_init(CONFIG_EXMC_NOE_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_NOE_PIN);
|
||
gpio_init(CONFIG_EXMC_NWE_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_NWE_PIN);
|
||
gpio_init(CONFIG_EXMC_NE1_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_NE1_PIN);
|
||
printf("[ECAT][EXMC] init: GPIO configured (D0-D15/A16/A17/NOE/NWE/NE1)\r\n");
|
||
|
||
/*
|
||
* EXMC NOR/SRAM Region1:16-bit、非复用、异步写。
|
||
* 时序(AHB=180MHz,1 HCLK ≈ 5.56ns):
|
||
* LAN9252 HBI 最小读/写周期约 100ns → data_setup ≥ 18 HCLK。
|
||
* asyn_* 字段单位为 HCLK 计数(库文档范围 address_setup 1..16,data_setup 2..256)。
|
||
*/
|
||
exmc_norsram_parameter_struct norsram_init;
|
||
exmc_norsram_timing_parameter_struct rw_timing;
|
||
exmc_norsram_timing_parameter_struct wr_timing;
|
||
|
||
norsram_init.read_write_timing = &rw_timing;
|
||
norsram_init.write_timing = &wr_timing;
|
||
exmc_norsram_struct_para_init(&norsram_init);
|
||
|
||
norsram_init.norsram_region = CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION1;
|
||
norsram_init.write_mode = EXMC_ASYN_WRITE;
|
||
norsram_init.extended_mode = DISABLE;
|
||
norsram_init.asyn_wait = DISABLE;
|
||
norsram_init.nwait_signal = DISABLE;
|
||
norsram_init.memory_write = ENABLE;
|
||
norsram_init.nwait_config = EXMC_NWAIT_CONFIG_BEFORE;
|
||
norsram_init.wrap_burst_mode = DISABLE;
|
||
norsram_init.nwait_polarity = EXMC_NWAIT_POLARITY_LOW;
|
||
norsram_init.burst_mode = DISABLE;
|
||
norsram_init.databus_width = EXMC_NOR_DATABUS_WIDTH_16B;
|
||
norsram_init.memory_type = EXMC_MEMORY_TYPE_SRAM;
|
||
norsram_init.address_data_mux = DISABLE;
|
||
|
||
/* 读/写共用时序(非 extended mode) */
|
||
rw_timing.asyn_access_mode = EXMC_ACCESS_MODE_A;
|
||
rw_timing.syn_data_latency = EXMC_DATALAT_2_CLK;
|
||
rw_timing.syn_clk_division = EXMC_SYN_CLOCK_RATIO_DISABLE;
|
||
rw_timing.bus_latency = 1U;
|
||
rw_timing.asyn_address_setuptime = 2U; /* ~11ns */
|
||
rw_timing.asyn_address_holdtime = 2U; /* min 2 */
|
||
rw_timing.asyn_data_setuptime = 20U; /* ~111ns ≥ 100ns HBI */
|
||
|
||
wr_timing = rw_timing;
|
||
|
||
printf("[ECAT][EXMC] init: applying NOR/SRAM Region1 params (16b, async, t_data=%u HCLK)\r\n",
|
||
(unsigned)rw_timing.asyn_data_setuptime);
|
||
|
||
exmc_norsram_init(&norsram_init);
|
||
exmc_norsram_enable(CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION1);
|
||
|
||
/* 总线探活:Region1 使能后立即读取 LAN9252 芯片 ID 低半字(期望 0x4321)。
|
||
* 0x0000/0xFFFF 通常意味着 EXMC 时序不满足、地址线接错或 ESC 未上电;
|
||
* 此处仅做总线可见性探活,芯片 ID 完整性校验留给 EscLan9252::init()。 */
|
||
const uint16_t probe_low = read16((uint32_t)0x0000U << 1U);
|
||
const uint16_t probe_high = read16((uint32_t)0x0002U << 1U);
|
||
const uint32_t probe_id = ((uint32_t)probe_high << 16) | probe_low;
|
||
printf("[ECAT][EXMC] init: Region1 enabled, probe id_lo=0x%04X id_hi=0x%04X (id=0x%08lX)\r\n",
|
||
(unsigned)probe_low, (unsigned)probe_high, (unsigned long)probe_id);
|
||
|
||
/* 总线挂死(全 0/全 1)视为 EXMC 未就绪;chip ID 完整校验在 EscLan9252::init()。 */
|
||
if (probe_low == 0x0000U && probe_high == 0x0000U) {
|
||
printf("[ECAT][EXMC] init: FAIL, bus reads all-zero (check wiring/NE1/A16/timing)\r\n");
|
||
return false;
|
||
}
|
||
if (probe_low == 0xFFFFU && probe_high == 0xFFFFU) {
|
||
printf("[ECAT][EXMC] init: FAIL, bus reads all-ones (check ESC power/NOE/NWE)\r\n");
|
||
return false;
|
||
}
|
||
|
||
s_ok = true;
|
||
printf("[ECAT][EXMC] init: OK, bus visible\r\n");
|
||
return true;
|
||
}
|
||
|
||
__attribute__((section(".ramfunc"), noinline))
|
||
uint16_t EscExmcBus::read16(uint32_t offset)
|
||
{
|
||
return ESC_EXMC_REG16(offset);
|
||
}
|
||
|
||
__attribute__((section(".ramfunc"), noinline))
|
||
void EscExmcBus::write16(uint32_t offset, uint16_t data)
|
||
{
|
||
ESC_EXMC_REG16(offset) = data;
|
||
}
|