主要内容

使用通道回调函数将 CAN 报文记录到 BLF 文件

此示例说明如何使用回调函数将在 CAN 通道上接收的报文记录到 BLF 文件中。

该示例在环回配置中使用 MathWorks® 虚拟 CAN 通道对报文记录进行仿真,而无需物理硬件。然而,相同的工作流也扩展到来自真实 CAN 或 CAN FD 网络的 BLF 记录应用。

在此示例中,您将学习如何:

  • 在 MATLAB® 中配置 CAN 通道。

  • 实现回调函数以批量记录报文。

  • 生成用于测试目的的合成 CAN 流量。

  • 将数据保存到带时间戳的 BLF 文件中以进行离线分析。

此工作流对于构建自动化记录系统、测试 CAN 应用程序或在受控环境中仿真真实流量非常有用。

创建传输和接收通道

使用 canChannel 函数创建两个 MathWorks 虚拟 CAN 通道:一个用于传输,一个用于接收。这些通道以环回配置形式内部连接。

txCh = canChannel("MathWorks", "Virtual 1", 1)
txCh = 
  Channel with properties:

   Device Information
            DeviceVendor: 'MathWorks'
                  Device: 'Virtual 1'
      DeviceChannelIndex: 1
      DeviceSerialNumber: 0
            ProtocolMode: 'CAN'

   Status Information
                 Running: 0
       MessagesAvailable: 0
        MessagesReceived: 0
     MessagesTransmitted: 0
    InitializationAccess: 1
        InitialTimestamp: [0×0 datetime]
           FilterHistory: 'Standard ID Filter: Allow All | Extended ID Filter: Allow All'

   Channel Information
               BusStatus: 'N/A'
              SilentMode: 0
         TransceiverName: 'N/A'
        TransceiverState: 'N/A'
       ReceiveErrorCount: 0
      TransmitErrorCount: 0
                BusSpeed: 500000
                     SJW: []
                   TSEG1: []
                   TSEG2: []
            NumOfSamples: []

   Other Information
                Database: []
                UserData: []

rxCh = canChannel("MathWorks", "Virtual 1", 2)
rxCh = 
  Channel with properties:

   Device Information
            DeviceVendor: 'MathWorks'
                  Device: 'Virtual 1'
      DeviceChannelIndex: 2
      DeviceSerialNumber: 0
            ProtocolMode: 'CAN'

   Status Information
                 Running: 0
       MessagesAvailable: 0
        MessagesReceived: 0
     MessagesTransmitted: 0
    InitializationAccess: 1
        InitialTimestamp: [0×0 datetime]
           FilterHistory: 'Standard ID Filter: Allow All | Extended ID Filter: Allow All'

   Channel Information
               BusStatus: 'N/A'
              SilentMode: 0
         TransceiverName: 'N/A'
        TransceiverState: 'N/A'
       ReceiveErrorCount: 0
      TransmitErrorCount: 0
                BusSpeed: 500000
                     SJW: []
                   TSEG1: []
                   TSEG2: []
            NumOfSamples: []

   Other Information
                Database: []
                UserData: []

配置回调函数

要自动处理传入的报文,请将回调函数 logToBLF 分配给接收通道。当有指定数量的报文可用时,将触发此函数。

rxCh.MessageReceivedFcn = @logToBLF;

设置回调执行的报文阈值

定义在回调函数执行之前应接收多少条报文。在此示例中,每次有 200 条报文可用时回调就会运行。这种批处理方法有助于高效管理内存和文件 I/O,尤其是在高吞吐量场景中。

rxCh.MessageReceivedFcnCount = 200;

实现回调函数

回调函数 logToBLF 从通道接收所有可用的报文,并将它们写入新 BLF 文件。每个文件使用时间戳来唯一命名以避免覆盖。

type logToBLF.m
function logToBLF(rxCh)
% Receive available CAN messages on the input CAN channel and log them to 
% a new BLF file. This callback function is triggered whenever the number
% of messages available on the channel reaches the configured threshold.

%   Copyright 2025 The MathWorks, Inc.

    % Receive all available CAN messages in a timetable.
    rxMsg = receive(rxCh, Inf, OutputFormat="timetable");

    % Get the current date and time.
    currentDateTime = datetime("now");
    
    % Format the datetime as a string suitable for a filename.
    formattedDateTime = string(currentDateTime, "yyyyMMdd_HHmmss_SSS");

    % Use the formatted string as part of the BLF file name.
    filename = strcat("can_log_", formattedDateTime, ".blf");

    % Write the CAN messages to a new BLF file on channel 1.
    blfwrite(filename, rxMsg, 1, "CAN");
end

启动通道

使用 start 命令将两个通道都设置为在线状态。在传输通道之前启动接收通道以避免丢失报文。

start(rxCh);
start(txCh);

在传输通道上生成 CAN 流

函数 generateCANTrafficSweepID 对 CAN ID 的扫描进行仿真。它创建 ID 范围为 1 到 1000 的 CAN 报文,并使用计时器对象以 0.01 秒的固定间隔传输它们。

type generateCANTrafficSweepID.m
function generateCANTrafficSweepID(txCh)
% Generate and transmit CAN messages with IDs from 1 to 1000 using a timer 
% executed at fixed interval of 0.01 seconds. This function assumes the 
% transmitting CAN channel is already created and started.

%   Copyright 2025 The MathWorks, Inc.

    % Create a structure to hold the user data for timer, including the
    % transmitting channel and the message ID.
    userData.TxCh = txCh;
    userData.MsgID = 1;

    % Create the timer object.
    txTimer = timer(ExecutionMode="fixedRate", Period=0.01, TasksToExecute=1000, UserData=userData, TimerFcn=@transmitMsg);

    % Start and wait for the timer to finish.
    start(txTimer);
    wait(txTimer);

    % Clean up the timer after use.
    delete(txTimer);
end

function transmitMsg(txTimer, ~)
% Local function used as the timer callback to transmit the next message.

    % Get user data.
    userData = txTimer.UserData;

    % Create a CAN message with the current ID and 8 bytes of random data.
    msg = canMessage(userData.MsgID, false, 8);
    msg.Data = uint8(randi([0 255], 1, 8));

    % Transmit the CAN message.
    transmit(userData.TxCh, msg);

    % Increment the message ID and update the timer's user data.
    userData.MsgID = userData.MsgID + 1;
    txTimer.UserData = userData;
end

在传输报文时,每次达到属性 MessageReceivedFcnCount 指定的阈值时,就会对接收通道执行回调函数。

generateCANTrafficSweepID(txCh);

手动记录剩余报文

传输完成后,接收缓冲区中可能仍有未达到回调阈值的报文。您可以手动调用一次回调,以确保任何剩余报文都已记录。

if rxCh.MessagesAvailable ~= 0
    rxCh.MessagesAvailable
    logToBLF(rxCh);
end
ans = 
197

停止并清理通道

在记录完成后,对通道执行 stop 并将其从工作区中清空。

stop(txCh);
stop(rxCh);
clear txCh rxCh

检查记录的 BLF 文件

列出生成的 BLF 文件,并检查最早和最新文件中的报文。

blfFiles = dir("*.blf")
blfFiles=5×1 struct array with fields:
    name
    folder
    date
    bytes
    isdir
    datenum

blfDataFirst = blfread(blfFiles(1).name);
blfDataFirst{1}
ans=202×8 timetable
        Time        ID    Extended       Name                     Data                   Length      Signals       Error    Remote
    ____________    __    ________    __________    _________________________________    ______    ____________    _____    ______

    0.090284 sec     1     false      {0×0 char}    {[ 208 231 32 233 161 24 71 140]}      8       {0×0 struct}    false    false 
    0.10638 sec      2     false      {0×0 char}    {[245 247 40 248 245 124 204 36]}      8       {0×0 struct}    false    false 
    0.11209 sec      3     false      {0×0 char}    {[107 234 202 245 167 9 217 239]}      8       {0×0 struct}    false    false 
    0.12054 sec      4     false      {0×0 char}    {[ 173 193 190 100 167 43 180 8]}      8       {0×0 struct}    false    false 
    0.1362 sec       5     false      {0×0 char}    {[    70 11 24 210 177 81 243 8]}      8       {0×0 struct}    false    false 
    0.13917 sec      6     false      {0×0 char}    {[112 97 195 203 47 125 114 165]}      8       {0×0 struct}    false    false 
    0.1502 sec       7     false      {0×0 char}    {[ 181 193 70 174 167 41 30 127]}      8       {0×0 struct}    false    false 
    0.15958 sec      8     false      {0×0 char}    {[ 245 87 149 57 192 65 129 178]}      8       {0×0 struct}    false    false 
    0.16989 sec      9     false      {0×0 char}    {[  228 245 140 35 38 65 215 65]}      8       {0×0 struct}    false    false 
    0.18071 sec     10     false      {0×0 char}    {[  208 62 237 89 50 64 157 121]}      8       {0×0 struct}    false    false 
    0.19051 sec     11     false      {0×0 char}    {[90 212 149 140 234 73 193 192]}      8       {0×0 struct}    false    false 
    0.20107 sec     12     false      {0×0 char}    {[  97 145 19 13 135 199 239 33]}      8       {0×0 struct}    false    false 
    0.21087 sec     13     false      {0×0 char}    {[   145 120 3 86 41 203 79 135]}      8       {0×0 struct}    false    false 
    0.22116 sec     14     false      {0×0 char}    {[ 42 154 67 167 176 191 115 21]}      8       {0×0 struct}    false    false 
    0.23125 sec     15     false      {0×0 char}    {[ 58 233 39 211 137 255 20 113]}      8       {0×0 struct}    false    false 
    0.24396 sec     16     false      {0×0 char}    {[  27 246 1 198 209 222 21 102]}      8       {0×0 struct}    false    false 
      ⋮

blfDataLast = blfread(blfFiles(end).name);
blfDataLast{1}
ans=197×8 timetable
       Time       ID     Extended       Name                      Data                   Length      Signals       Error    Remote
    __________    ___    ________    __________    __________________________________    ______    ____________    _____    ______

    8.1997 sec    804     false      {0×0 char}    {[253 238 118 231 98 154 143 216]}      8       {0×0 struct}    false    false 
    8.2122 sec    805     false      {0×0 char}    {[    72 169 154 168 79 84 48 25]}      8       {0×0 struct}    false    false 
    8.2193 sec    806     false      {0×0 char}    {[   73 90 137 253 7 181 231 221]}      8       {0×0 struct}    false    false 
    8.235 sec     807     false      {0×0 char}    {[ 30 244 112 224 221 90 161 221]}      8       {0×0 struct}    false    false 
    8.244 sec     808     false      {0×0 char}    {[       5 19 96 38 8 200 83 209]}      8       {0×0 struct}    false    false 
    8.2491 sec    809     false      {0×0 char}    {[    44 173 224 193 58 91 92 68]}      8       {0×0 struct}    false    false 
    8.2591 sec    810     false      {0×0 char}    {[     86 22 115 116 7 163 15 43]}      8       {0×0 struct}    false    false 
    8.2691 sec    811     false      {0×0 char}    {[     175 141 1 73 96 37 19 117]}      8       {0×0 struct}    false    false 
    8.2861 sec    812     false      {0×0 char}    {[     94 211 137 209 117 3 1 42]}      8       {0×0 struct}    false    false 
    8.2892 sec    813     false      {0×0 char}    {[     93 183 40 71 165 73 82 39]}      8       {0×0 struct}    false    false 
    8.3002 sec    814     false      {0×0 char}    {[ 99 229 227 100 172 64 243 159]}      8       {0×0 struct}    false    false 
    8.3093 sec    815     false      {0×0 char}    {[     52 28 144 70 18 40 12 174]}      8       {0×0 struct}    false    false 
    8.3195 sec    816     false      {0×0 char}    {[   200 206 67 229 155 3 88 136]}      8       {0×0 struct}    false    false 
    8.3295 sec    817     false      {0×0 char}    {[ 160 114 207 37 249 213 87 157]}      8       {0×0 struct}    false    false 
    8.3397 sec    818     false      {0×0 char}    {[   77 22 133 211 195 242 85 99]}      8       {0×0 struct}    false    false 
    8.35 sec      819     false      {0×0 char}    {[   38 85 141 140 41 29 102 212]}      8       {0×0 struct}    false    false 
      ⋮

这使您能够验证报文是否正确记录,并检查所记录数据的结构。