feat(canfd): 添加收发器静默模式支持和SPI命令接口
添加了CAN FD收发器静默模式功能,允许通过硬件引脚控制发送使能: - 新增setTxSilent/isTxSilent接口控制TCAN1057收发器Silent模式 - 静默时驱动器关断但接收机继续工作,支持只听不发场景 - 添加SPI命令0xC8用于ESP32远程控制静默模式切换 - 在总线状态位图中增加bit5表示静默状态 - 新增canBusyWaitUs函数用于ISR安全的微秒延时 fix(esc_lan9252): 改进warm jump后LAN9252探测逻辑 修复warm jump导致的LAN9252初始化问题: - 识别warm jump后芯片非POR状态(idx0!=0x1234)的情况 - 自动补发硬件复位对齐到POR状态后再进行正常探测 - 改进调试信息输出,区分POR状态和warm jump残值情况 - 修正失败路径的复位标志初始化逻辑 refactor(spi): 整理SPI命令文件结构 调整SPI命令相关文件的组织结构: - 移动诊断相关文件到更合适的位置 - 重新整理SPI命令处理文件的分组
This commit is contained in:
@@ -19,6 +19,27 @@ static inline void spinDelay(uint32_t loops) {
|
||||
for (volatile uint32_t d = 0; d < loops; ++d) { }
|
||||
}
|
||||
|
||||
/** @brief DWT CYCCNT 忙等微秒(ISR 安全)。
|
||||
* @note setTxSilent 退 Silent 时要等收发器 tMODE=20µs;本函数可能在 SPI CS
|
||||
* EXTI ISR 内被 0xC8 handler 调用,而 SysTickTimer::delayMs 依赖 SysTick
|
||||
* 中断计数,在该上下文会被冻死(同 esc_lan9252.cpp 的 pmBusyWaitUs)。
|
||||
* **不得复位 CYCCNT**:cpu_load / ethercat_diff_stats / SSC 软件定时器都拿它
|
||||
* 当时间基,清零会让它们的窗口计算瞬时报错。只在未使能时补使能位。 */
|
||||
static inline void canBusyWaitUs(uint32_t us)
|
||||
{
|
||||
if ((DWT->CTRL & DWT_CTRL_CYCCNTENA_Msk) == 0U) {
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||
}
|
||||
const uint32_t start = DWT->CYCCNT;
|
||||
const uint32_t cycles = us * (SystemCoreClock / 1000000U);
|
||||
/* 有界自旋:DWT 因故没在跑时不能变成无限等(最多多转 ~1ms 的圈数) */
|
||||
const uint32_t spin_cap = cycles + (SystemCoreClock / 1000U);
|
||||
for (uint32_t i = 0U; i < spin_cap; ++i) {
|
||||
if ((DWT->CYCCNT - start) >= cycles) break;
|
||||
}
|
||||
}
|
||||
|
||||
/* DLENC>8 的长度映射,与厂商库 g_can_fdlength_table 一致 */
|
||||
constexpr uint8_t kFdLenTable[7] = { 12U, 16U, 20U, 24U, 32U, 48U, 64U };
|
||||
|
||||
@@ -308,6 +329,7 @@ uint32_t CanFd::s_feedbackSeq = 0U;
|
||||
CanFd::IdSnapshotTable CanFd::s_idSnapshot[3]{};
|
||||
bool CanFd::s_channelHwOk[3]{};
|
||||
bool CanFd::s_brsSuppress[3]{};
|
||||
volatile uint8_t CanFd::s_txSilent[3]{};
|
||||
|
||||
/* TX 错误事件标志(ARD=1 单发丢帧通知):TX 完成 ISR 置位,主循环
|
||||
* consumeTxError() 读清;单写单读,无锁。 */
|
||||
@@ -492,7 +514,8 @@ void CanFd::initChannelHardware(uint8_t i)
|
||||
}
|
||||
s_channelHwOk[i] = false;
|
||||
rcu_periph_clock_enable(ch.canRcu);
|
||||
gpio_bit_reset(ch.slpPort, ch.slpPin); /* STB=0 -> Normal */
|
||||
gpio_bit_reset(ch.slpPort, ch.slpPin); /* S=0 → Normal(静默不跨重启,上电一律放开发送) */
|
||||
s_txSilent[i] = 0U;
|
||||
const uint8_t rxLevelBefore = (gpio_input_bit_get(ch.rxPort, ch.rxPin) != RESET) ? 1U : 0U;
|
||||
const uint8_t stbLevel = (gpio_output_bit_get(ch.slpPort, ch.slpPin) != RESET) ? 1U : 0U;
|
||||
|
||||
@@ -1096,6 +1119,13 @@ CanFd::CanTxResult CanFd::transmitFrame(uint8_t channelIndex, uint32_t ext_id,
|
||||
if (dlc > 0U && data == nullptr) {
|
||||
return CanTxResult::kErrInvalid;
|
||||
}
|
||||
/* 收发器 Silent(只听不发):本端主动配置的发送禁能,不是总线状态。
|
||||
* 与下面「BUS_OFF/TEC 不做软件拦截」的约定不冲突——那两条是粘性总线侧标志会误判,
|
||||
* 这条是 s_txSilent 单一真源,且驱动器已关断:硬发只会让控制器白涨 TEC。
|
||||
* 拒绝路径不碰任何计数/诊断,静默期间的拒发不是错误。 */
|
||||
if (s_txSilent[channelIndex] != 0U) {
|
||||
return CanTxResult::kErrSilent;
|
||||
}
|
||||
|
||||
CanTxConcurrencyGuard guard(channelIndex);
|
||||
|
||||
@@ -1211,6 +1241,12 @@ CanFd::CanTxResult CanFd::enqueueTx(uint8_t channelIndex, uint32_t ext_id,
|
||||
if (dlc > 0U && data == nullptr) {
|
||||
return CanTxResult::kErrInvalid;
|
||||
}
|
||||
/* 静默通道在入队口就拒掉:否则帧会进软件队列,等 TX 完成中断 drainTxQueueIsr
|
||||
* 调 transmitFrame 时才被拒,那一轮会把 head/count 推进并静默丢帧,
|
||||
* 表面上"已受理"实则必丢。 */
|
||||
if (s_txSilent[channelIndex] != 0U) {
|
||||
return CanTxResult::kErrSilent;
|
||||
}
|
||||
|
||||
TxQueue &q = s_txQueues[channelIndex];
|
||||
|
||||
@@ -1351,6 +1387,49 @@ void CanFd::setChannelBrsSuppress(uint8_t channelIndex, bool suppress)
|
||||
CAN_LOGI("[CAN] CH%u set brs_suppress=%u\r\n", (unsigned)channelIndex, (unsigned)suppress);
|
||||
}
|
||||
|
||||
bool CanFd::setTxSilent(uint8_t channelIndex, bool silent)
|
||||
{
|
||||
if (channelIndex >= kChannelCount || !kChannelEnabled[channelIndex]) {
|
||||
return false;
|
||||
}
|
||||
const auto &ch = kChannels[channelIndex];
|
||||
|
||||
if (silent) {
|
||||
/* 先立旗再动脚:并发路径(主循环/其它 ISR)调 transmitFrame 立刻被拒,
|
||||
* 不会在关驱动器的当口又塞一帧进邮箱。 */
|
||||
s_txSilent[channelIndex] = 1U;
|
||||
/* 驱动器即将关断:残留 PENDING 邮箱发不出也等不到 ACK,只会白涨 TEC,
|
||||
* 进静默前先把它们停掉。 */
|
||||
abortPendingTx(channelIndex);
|
||||
gpio_bit_set(ch.slpPort, ch.slpPin); /* S=1 → TCAN1057 Silent(驱动关断,接收照常) */
|
||||
} else {
|
||||
gpio_bit_reset(ch.slpPort, ch.slpPin); /* S=0 → Normal */
|
||||
/* 收发器模式建立时间 tMODE=20µs(手册 6.9):等够再放开发送门,
|
||||
* 否则紧随其后的第一帧会被驱动器忽略。 */
|
||||
canBusyWaitUs(20U);
|
||||
s_txSilent[channelIndex] = 0U;
|
||||
}
|
||||
|
||||
const uint8_t pinHigh = (gpio_output_bit_get(ch.slpPort, ch.slpPin) != RESET) ? 1U : 0U;
|
||||
CAN_LOGI("[CAN] CH%u tx_silent=%u (S pin=%u)\r\n", (unsigned)channelIndex,
|
||||
(unsigned)silent, (unsigned)pinHigh);
|
||||
/* 回读确认脚电平与意图一致;不一致就把标志回滚成原状态再报错,
|
||||
* 否则 isTxSilent()(发送门控 + 0xC1 bit5)会与物理脚电平各说一套。 */
|
||||
if (pinHigh != (silent ? 1U : 0U)) {
|
||||
s_txSilent[channelIndex] = silent ? 0U : 1U;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CanFd::isTxSilent(uint8_t channelIndex)
|
||||
{
|
||||
if (channelIndex >= kChannelCount) {
|
||||
return false;
|
||||
}
|
||||
return s_txSilent[channelIndex] != 0U;
|
||||
}
|
||||
|
||||
uint32_t CanFd::readChannelErrorReg(uint8_t channelIndex)
|
||||
{
|
||||
if (channelIndex >= kChannelCount || !kChannelEnabled[channelIndex]) {
|
||||
@@ -1826,14 +1905,17 @@ void CanFd::refreshFeedbackSnapshot()
|
||||
/* 写入硬件层错误状态供 SPI 反馈使用 */
|
||||
s_feedback[i].stats.tec = static_cast<uint8_t>(hw_tec[i]);
|
||||
s_feedback[i].stats.rec = static_cast<uint8_t>(hw_rec[i]);
|
||||
/* 总线状态位图:bit0=init, bit1=sleep, bit2=error_passive, bit3=bus_off, bit4=error_warning
|
||||
* init/sleep 来自 CAN_STAT,error_passive/bus_off/error_warning 来自 CAN_ERR */
|
||||
/* 总线状态位图:bit0=init, bit1=控制器sleep, bit2=error_passive, bit3=bus_off,
|
||||
* bit4=error_warning, bit5=本端收发器 Silent(只听不发)
|
||||
* bit0~1 来自 CAN_STAT,bit2~4 来自 CAN_ERR;bit5 来自 s_txSilent(唯一真源,
|
||||
* 与发送门控同一个变量),别拿 bit1 当收发器状态——两者不是一回事。 */
|
||||
uint8_t busState = 0U;
|
||||
if (hw_stat[i] & CAN_STAT_IWS) busState |= (1U << 0); /* 初始化模式 */
|
||||
if (hw_stat[i] & CAN_STAT_SLPWS) busState |= (1U << 1); /* 睡眠模式 */
|
||||
if (hw_stat[i] & CAN_STAT_SLPWS) busState |= (1U << 1); /* 控制器睡眠模式 */
|
||||
if (hw_err[i] & CAN_ERR_PERR) busState |= (1U << 2); /* 错误被动 */
|
||||
if (hw_err[i] & CAN_ERR_BOERR) busState |= (1U << 3); /* 总线关闭 */
|
||||
if (hw_err[i] & CAN_ERR_WERR) busState |= (1U << 4); /* 错误警告 */
|
||||
if (s_txSilent[i] != 0U) busState |= (1U << 5); /* 收发器 Silent */
|
||||
s_feedback[i].stats.bus_state = busState;
|
||||
s_idSnapshot[i].seq = s_feedbackSeq;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user