CAN多节点通信实战:基于系统RTC及系统时钟Tick的心跳风暴抑制方案

方案定义

本方案采用两级时基:

  • 系统RTC:用于计算统一周期起点 periodStart
  • 系统时钟Tick:用于保证发送窗口精度

本方案支持两种通信模式:

  • 主从通信:由 Master 周期发布系统 RTC
  • 对等通信:由 1号节点 周期发布系统 RTC

系统 RTC 发布节点只负责授时,不直接参与其他节点的心跳调度计算。


全局参数

  • Thb:节点心跳周期
  • N:系统容量
  • NodeId:节点号
  • rtcNow:当前节点本地 RTC 时间
  • localTickNow:当前节点本地系统 Tick

当前参数示例:

  • Thb = 30s
  • N = 200
  • 系统 RTC 发布周期 = 10分钟
  • 本地 Tick 精度 = 10ms

系统RTC来源

主从通信模式

系统 RTC 由 Master 周期发布。

Slave 接收后同步本地 RTC。

对等通信模式

系统 RTC 由 1号节点 周期发布, 其他节点接收后同步本地 RTC。

对等通信模式下,1号节点 发送,所有节点仍按统一规则计算心跳周期与发送窗口。


周期起点计算

当前心跳周期起点由节点本地 RTC 直接计算:

c 复制代码
periodStart = (rtcNow / Thb) * Thb;

其中:

  • rtcNow 为以秒为单位的系统时间戳
  • Thb 为以秒为单位的心跳周期

示例:

c 复制代码
rtcNow = 1719833725;
Thb = 30;
periodStart = (1719833725 / 30) * 30 = 1719833710;

所有节点只要 RTC 已同步到同一系统时间基准,计算得到的 periodStart 就一致。


发送窗口计算

每个节点固定映射到一个发送窗口:

c 复制代码
windowSize = Thb / N;
sendOffset = (NodeId % N) * windowSize;

参数代入:

c 复制代码
windowSize = 30000ms / 200 = 150ms;

即在每个 30 秒周期内,200 个节点均匀分散到 200 个发送窗口中。


本地Tick映射

在轮询中根据当前 RTC 计算当前周期起点:

c 复制代码
periodStartRtc = (rtcNowSec / T_hbSec) * T_hbSec;

若检测到进入新周期,则记录当前本地 Tick:

c 复制代码
periodStartTick = localTickNow;

该值表示当前周期起点在本地 Tick 时间轴上的映射时刻。


心跳发送规则

本节点发送偏移为:

c 复制代码
sendOffset = (NodeId % N) * windowSize;
sendTick = periodStartTick + sendOffset;

发送条件:

c 复制代码
if (!sentFlag && localTickNow >= sendTick) {
    send_heartbeat();
    sentFlag = 1;
}

每个周期只发送一次心跳。

进入新周期后清除 sentFlag


RTC同步规则

系统 RTC 发布节点周期发送 RTC 同步报文。

节点收到后执行本地 RTC 同步。

RTC 同步报文只用于:

  • 更新本地 RTC

RTC 同步报文不用于:

  • 不直接计算 periodStart
  • 不直接修改发送窗口
  • 不直接触发心跳发送

运行流程

1. 节点初始化

初始化上下文参数:

  • 心跳周期 Thb
  • 系统容量 N
  • 节点号 NodeId
  • 本周期发送标志 sentFlag

2. 系统RTC同步

系统 RTC 发布节点周期发送 RTC 同步报文, 其他节点接收后同步本地 RTC。

3. 周期轮询

节点周期调用轮询函数,输入:

  • 当前 RTC
  • 当前本地 Tick

4. 计算周期起点

根据当前 RTC 计算:

c 复制代码
periodStartRtc = (rtcNowSec / T_hbSec) * T_hbSec;

5. 新周期处理

periodStartRtc 发生变化:

  • 更新记录的周期起点
  • 保存新的 periodStartTick
  • 清除 sentFlag

6. 发送判定

根据节点号计算发送偏移与发送时刻,满足条件后发送一次心跳。


参考实现

c 复制代码
#include <stdint.h>

typedef struct {
    uint32_t lastPeriodStartRtc;  // 当前周期起点,单位:秒
    uint32_t periodStartTick;     // 当前周期起点对应的本地Tick,单位:ms
    uint32_t windowSizeMs;        // 窗口宽度,单位:ms
    uint32_t periodSec;           // 心跳周期,单位:秒
    uint16_t nodeId;
    uint16_t nodeCount;
    uint8_t sentFlag;
} HeartbeatCtx;

void heartbeat_init(HeartbeatCtx *ctx,
                    uint16_t nodeId,
                    uint16_t nodeCount,
                    uint32_t periodSec)
{
    ctx->lastPeriodStartRtc = 0;
    ctx->periodStartTick = 0;
    ctx->windowSizeMs = (periodSec * 1000) / nodeCount;
    ctx->periodSec = periodSec;
    ctx->nodeId = nodeId;
    ctx->nodeCount = nodeCount;
    ctx->sentFlag = 0;
}

void heartbeat_poll(HeartbeatCtx *ctx, uint32_t rtcNowSec, uint32_t localTickNow)
{
    uint32_t periodStartRtc = (rtcNowSec / ctx->periodSec) * ctx->periodSec;

    if (periodStartRtc != ctx->lastPeriodStartRtc) {
        ctx->lastPeriodStartRtc = periodStartRtc;
        ctx->periodStartTick = localTickNow;
        ctx->sentFlag = 0;
    }

    uint32_t sendOffset =(uint32_t)(ctx->nodeId % ctx->nodeCount) * ctx->windowSizeMs;
    uint32_t sendTick = ctx->periodStartTick + sendOffset;

    if (!ctx->sentFlag && localTickNow >= sendTick) {
        send_heartbeat();
        ctx->sentFlag = 1;
    }
}

参数示例

按当前项目参数:

  • nodeCount = 200
  • periodSec = 30
  • windowSizeMs = 150
  • RTC 发布周期 = 10分钟
  • local Tick = 10ms

小结

本方案统一以系统 RTC 计算 periodStart,以节点号分配固定发送窗口,以本地 Tick 实现窗口级精确发送。系统 RTC 可由 Master 发布,也可在对等通信模式下由 1 号机发布。RTC 同步仅用于统一各节点本地时间,不直接参与心跳调度逻辑。

相关推荐
天空'之城3 小时前
C 语言工业级通用组件手写 09:CRC32 数据校验
c语言·嵌入式开发·数据校验·crc32·工业级组件
天空'之城8 小时前
C 语言工业级通用组件手写 06:固定块内存池
c语言·嵌入式开发·内存池·底层优化
天空'之城3 天前
C 语言工业级通用组件手写 04:阻塞队列
c语言·嵌入式开发·阻塞队列·多线程开发
俊基科技3 天前
对话越嘈杂,听得越清晰:A-29P 让每一台通话设备都装上 “AI 听觉大脑”
嵌入式开发·硬件开发·ai降噪·回声消除·定向拾音·拾音降噪·双麦波束
天空'之城4 天前
C 语言工业级通用组件手写 03:双向链表
c语言·嵌入式开发·双向链表
天空'之城5 天前
VS Code Git 工作树:告别频繁切分支,实现多分支并行开发实战方案
git·vscode·版本控制·嵌入式开发·开发效率·worktree·多分支管理
qydz1110 天前
杰理开发基础知识(3)
开发语言·嵌入式开发·杰理科技
似是燕归来11 天前
嵌入式工程师进阶指南:适配器编程设计模式一
c语言·stm32·适配器模式·嵌入式开发
想你依然心痛11 天前
工业网关设计:多协议转换与边缘计算的数据流架构——Modbus/CAN/OPC UA全栈实战
边缘计算·modbus·规则引擎·can总线·工业网关·多协议转换·数据流架构
Tronlong创龙14 天前
TL3588 工控单板 ROS2 开发实战:交叉编译、机器人通信与功能包部署全流程
机器人·开发板·嵌入式开发·硬件开发·工业控制·工业开发板