fix(comm): 回退LAN9252批量读写优化并修复多个CAN-EtherCAT桥竞态问题
回退1.6.0引入的LAN9252 PRAM批量读写优化,因其AVAIL_CNT计数器对短读存在语义问题, 导致邮箱短读和探针传输时出现FIFO陈旧数据损坏问题。 同时修复EtherCAT-CAN桥中的多个竞态条件: - 修复消费者在fillRxBatch中直接访问共享ring buffer导致的数据混淆问题 - 修复生产者-消费者间tail指针发布时机不当造成的槽位覆盖竞态 - 修复WIN窗口基址更新位置错误导致电机状态窗口被锁定的问题 - 修复buildInput中断上下文与主循环间的数据竞争和内存撕裂问题 - 添加STATUS帧过滤机制减少批量区压力并解决队列饱和问题 此外还增加了多个诊断计数器用于监控撕裂、溢出和拼接错误,并调整了 输入PDO段布局和大小参数以支持新的48字节STATUS格式。 这些修改解决了上位机观察到的帧标签错误、电机状态丢失和数据拼接问题。
This commit is contained in:
@@ -2,8 +2,10 @@
|
||||
* @file motor_state.cpp
|
||||
* @brief 多电机状态表实现 — CAN STATUS 帧识别、内存缓存、SPI 推送序列化。
|
||||
*
|
||||
* 所有函数仅在主循环上下文调用(taskCanRxParse / taskHeartbeat / pushStep),
|
||||
* 无 ISR 竞态,不需要临界区保护。
|
||||
* 本模块的函数只在主循环上下文调用(taskCanRxParse / taskHeartbeat / pushStep),
|
||||
* 但消费者不止主循环:EtherCAT 的 buildInput 跑在 PDI_Isr,会抢占 STATUS 写侧,
|
||||
* 故 STATUS 的 seq/data 由 MotorSlot.wr_seq(seqlock)保护,见 onCanFrame。
|
||||
* SPI 侧的 buildPayload 与写侧同上下文,天然无此问题。
|
||||
*/
|
||||
|
||||
#include "device/motor_state.h"
|
||||
@@ -36,7 +38,7 @@ void onCanFrame(uint8_t channel, uint32_t id, const uint8_t *data, uint8_t len)
|
||||
|
||||
const uint32_t masked = id & kMotorStatusIdMask;
|
||||
|
||||
/* STATUS 帧 0x000N0100 (DLC 32, 实时值) */
|
||||
/* STATUS 帧 0x000N0100 (DLC 48, 实时值,字节 32~35 = InitStatus) */
|
||||
if (masked == kMotorStatusIdMatch) {
|
||||
const uint8_t node = static_cast<uint8_t>((id >> 16) & 0x0FU);
|
||||
if (node < 1U || node > kMotorMaxNodes || channel >= kMotorChannels) {
|
||||
@@ -45,6 +47,14 @@ void onCanFrame(uint8_t channel, uint32_t id, const uint8_t *data, uint8_t len)
|
||||
|
||||
MotorSlot &slot = s_motorTable.slots[(node - 1U) * kMotorChannels + channel];
|
||||
|
||||
/* seqlock 写侧(1.6.5):奇数=本槽 seq/data 正在改。EtherCAT 的 buildInput
|
||||
* 跑在 PDI_Isr,会抢占这里;抢占落在下面那次 memcpy 中途就会发布
|
||||
* "新 seq + 半新半旧 48B"——两帧字节拼成一个 STATUS,边界若落在 float 的
|
||||
* 符号/指数字节上就是网页上的离谱值。缺的不是顺序而是让消费者能认出
|
||||
* "这一段没写完"的标记。 */
|
||||
++slot.wr_seq; /* → 奇数 */
|
||||
__DMB(); /* 普通写不得重排到奇/偶两次自增之外 */
|
||||
|
||||
slot.node_id = node;
|
||||
slot.channel = channel;
|
||||
slot.online = 1U;
|
||||
@@ -57,6 +67,9 @@ void onCanFrame(uint8_t channel, uint32_t id, const uint8_t *data, uint8_t len)
|
||||
if (slot.dlc < kMotorStatusDlc) {
|
||||
std::memset(&slot.data[slot.dlc], 0, kMotorStatusDlc - slot.dlc);
|
||||
}
|
||||
|
||||
__DMB();
|
||||
++slot.wr_seq; /* → 偶数:本槽整体一致 */
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -159,9 +172,10 @@ uint16_t buildPayload(uint8_t page, uint8_t *buf, uint16_t maxLen)
|
||||
writeU32Le(&buf[offset + 4U], slot.rx_count);
|
||||
writeU32Le(&buf[offset + 8U], slot.seq);
|
||||
std::memcpy(&buf[offset + 12U], slot.data, kMotorStatusDlc);
|
||||
writeU32Le(&buf[offset + 44U], slot.config_rx_count);
|
||||
writeU32Le(&buf[offset + 48U], slot.config_seq);
|
||||
std::memcpy(&buf[offset + 52U], slot.config_data, kMotorConfigDlc);
|
||||
/* STATUS DLC 48 后,CONFIG 字段整体后移 16B */
|
||||
writeU32Le(&buf[offset + 60U], slot.config_rx_count);
|
||||
writeU32Le(&buf[offset + 64U], slot.config_seq);
|
||||
std::memcpy(&buf[offset + 68U], slot.config_data, kMotorConfigDlc);
|
||||
|
||||
offset += kMotorEntrySize;
|
||||
count++;
|
||||
|
||||
Reference in New Issue
Block a user