使用通道回调函数将 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.mfunction 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.mfunction 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
⋮
这使您能够验证报文是否正确记录,并检查所记录数据的结构。