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 同步仅用于统一各节点本地时间,不直接参与心跳调度逻辑。

相关推荐
weixin_399733622 天前
C语言教程
c语言·嵌入式开发·编程教程·谭浩强·c程序设计
Tronlong创龙4 天前
RK3576 单板机嵌入式 Qt 界面与多屏异显开发手册(一)
开发板·嵌入式开发·硬件开发·工业控制
武汉唯众智创4 天前
唯众无人驾驶智能小车实训平台:从ROS开发到SLAM建图,一站式搞定无人驾驶实训
嵌入式开发·ai 实训平台·无人驾驶智能小车·高校实训装备·智能小车实训
Tronlong创龙5 天前
RK3576 单板机嵌入式 Qt 界面与多屏异显开发手册(二)
开发板·嵌入式开发·硬件开发·工业控制
hipolymers6 天前
C语言是什么
c语言·嵌入式开发·编程范式·高效性·系统级编程
合众恒跃6 天前
边缘计算终端,前路何方?小型化,或是破局之选。
边缘计算·开发板·嵌入式开发·工业物联网·瑞芯微
虹科Pico汽车示波器8 天前
汽车免拆诊断案例 | 2015 款奔驰 CLS320 车发动机偶尔无法起动
汽车·汽车示波器·can总线·发动机故障·偶发故障·通信故障·奔驰cls320
静听夜半雨9 天前
万字长文——基于CANoe/CAPL的UDS Bootloader上位机实现(附完整可运行代码及工程文件)
网络·上位机·canoe·can总线·ecu刷写·uds升级·capl编程
玄奕子15 天前
VS Code 上传 GitHub 全流程(Windows 环境):HTTP 与 SSH 两种方案(含常见报错排查)
git·http·ssh·github·嵌入式开发
菠萝地亚狂想曲17 天前
FreeRTOS heap4
c语言·stm32·嵌入式开发