在 MATLAB 中进行周期性 CAN 通信
此示例说明如何配置 CAN 通道和报文,以便周期性传输报文。它使用以环回配置形式连接的 MathWorks® 虚拟 CAN 通道。
由于此示例是在虚拟网络上发送和接收 CAN 报文,因此结合运行 CAN 通信管理器可以更全面地了解代码所执行的操作。要运行 CAN 通信管理器,请打开并配置它使用与示例的接收通道相同的接口。确保在开始运行示例之前启动 CAN 通信管理器,以便查看发生的所有报文。
此示例说明 CAN 网络的工作流,但所展示的概念也适用于 CAN FD 网络。
创建 CAN 通道
创建两个分别用于报文传输和接收的 CAN 通道。
txCh = canChannel("MathWorks", "Virtual 1", 1); rxCh = canChannel("MathWorks", "Virtual 1", 2);
打开包含报文和信号定义的 DBC 文件,并将其连接到这两个 CAN 通道。
db = canDatabase("CANDatabasePeriodic.dbc");
txCh.Database = db;
rxCh.Database = db;
创建 CAN 报文
使用数据库信息创建 CAN 报文 EngineMsg
和 TransmissionMsg
。
msgFast = canMessage(db, "EngineMsg")
msgFast = Message with properties: Message Identification ProtocolMode: 'CAN' ID: 100 Extended: 0 Name: 'EngineMsg' Data Details Timestamp: 0 Data: [0 0 0 0 0 0 0 0] Signals: [1×1 struct] Length: 8 Protocol Flags Error: 0 Remote: 0 Other Information Database: [1×1 can.Database] UserData: []
msgSlow = canMessage(db, "TransmissionMsg")
msgSlow = Message with properties: Message Identification ProtocolMode: 'CAN' ID: 200 Extended: 0 Name: 'TransmissionMsg' Data Details Timestamp: 0 Data: [0 0 0 0 0 0 0 0] Signals: [1×1 struct] Length: 8 Protocol Flags Error: 0 Remote: 0 Other Information Database: [1×1 can.Database] UserData: []
将报文配置为周期性传输
要使报文能够周期性传输,请使用 transmitPeriodic
命令指定传输通道、要在通道上注册的报文、状态值和周期性速率。
transmitPeriodic(txCh, msgFast, "On", 0.100); transmitPeriodic(txCh, msgSlow, "On", 0.500);
开始周期性传输
启动接收通道。
start(rxCh);
使用在上一步配置的周期性传输启动传输通道。周期传输立即开始。让通道运行两秒。
start(txCh); pause(2);
更新传输的数据
要更新传输到 CAN 总线上的实时报文或信号数据,请将新值直接写入报文 EngineMsg
中的 VehicleSpeed
信号。
msgFast.Signals.VehicleSpeed = 60; pause(1); msgFast.Signals.VehicleSpeed = 65; pause(1); msgFast.Signals.VehicleSpeed = 70; pause(1);
您也可以向创建的报文的 Data
属性写入新值。
接收报文
停止 CAN 通道并接收所有周期性传输的报文以进行分析。
stop(txCh); stop(rxCh); msgRx = receive(rxCh, Inf, "OutputFormat", "timetable");
使用 head
函数查看收到的报文的前几行。
head(msgRx)
Time ID Extended Name Data Length Signals Error Remote ____________ ___ ________ ___________________ ___________________ ______ ____________ _____ ______ 0.031922 sec 100 false {'EngineMsg' } {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 0.031924 sec 200 false {'TransmissionMsg'} {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 0.13752 sec 100 false {'EngineMsg' } {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 0.24011 sec 100 false {'EngineMsg' } {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 0.35028 sec 100 false {'EngineMsg' } {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 0.45648 sec 100 false {'EngineMsg' } {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 0.53209 sec 200 false {'TransmissionMsg'} {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 0.56215 sec 100 false {'EngineMsg' } {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false
分析周期性传输的行为
通过绘制每条收到的报文的标识符对其时间戳的图来分析报文的分布。请注意,由于这两条报文配置的周期性速率不同,它们的出现频率也不同。
plot(msgRx.Time, msgRx.ID, "x") ylim([0 400]) title("Message Distribution", "FontWeight", "bold") xlabel("Timestamp") ylabel("CAN Identifier")
要进一步分析,请将两条报文分离到单独的不同时间表中。
msgRxFast = msgRx(strcmpi("EngineMsg", msgRx.Name), :);
head(msgRxFast)
Time ID Extended Name Data Length Signals Error Remote ____________ ___ ________ _____________ ___________________ ______ ____________ _____ ______ 0.031922 sec 100 false {'EngineMsg'} {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 0.13752 sec 100 false {'EngineMsg'} {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 0.24011 sec 100 false {'EngineMsg'} {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 0.35028 sec 100 false {'EngineMsg'} {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 0.45648 sec 100 false {'EngineMsg'} {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 0.56215 sec 100 false {'EngineMsg'} {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 0.66495 sec 100 false {'EngineMsg'} {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 0.77479 sec 100 false {'EngineMsg'} {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false
msgRxSlow = msgRx(strcmpi("TransmissionMsg", msgRx.Name), :);
head(msgRxSlow)
Time ID Extended Name Data Length Signals Error Remote ____________ ___ ________ ___________________ ___________________ ______ ____________ _____ ______ 0.031924 sec 200 false {'TransmissionMsg'} {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 0.53209 sec 200 false {'TransmissionMsg'} {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 1.0317 sec 200 false {'TransmissionMsg'} {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 1.5323 sec 200 false {'TransmissionMsg'} {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 2.0437 sec 200 false {'TransmissionMsg'} {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 2.5418 sec 200 false {'TransmissionMsg'} {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 3.0423 sec 200 false {'TransmissionMsg'} {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false 3.5422 sec 200 false {'TransmissionMsg'} {[0 0 0 0 0 0 0 0]} 8 {1×1 struct} false false
分析每组报文的时间戳,以查看时间差的均值与配置的周期性速率的接近程度。
avgPeriodFast = mean(diff(msgRxFast.Time))
avgPeriodFast = duration
0.10604 sec
avgPeriodSlow = mean(diff(msgRxSlow.Time))
avgPeriodSlow = duration
0.50141 sec
使用 canSignalTimetable
将报文 EngineMsg
中的信号数据重新打包为一个信号时间表。
signalTimetable = canSignalTimetable(msgRx, "EngineMsg");
head(signalTimetable)
Time VehicleSpeed EngineRPM ____________ ____________ _________ 0.031922 sec 0 250 0.13752 sec 0 250 0.24011 sec 0 250 0.35028 sec 0 250 0.45648 sec 0 250 0.56215 sec 0 250 0.66495 sec 0 250 0.77479 sec 0 250
绘制接收到的信号 VehicleSpeed
的值随时间变化的图,并注意它如何反映报文数据中的三个更新。
plot(signalTimetable.Time, signalTimetable.VehicleSpeed) title("Vehicle Speed from EngineMsg", "FontWeight", "bold") xlabel("Timestamp") ylabel("Vehicle Speed") ylim([-5 75])
查看配置为周期性传输的报文
要查看为自动传输而在传输通道上配置的报文,请使用 transmitConfiguration
命令。
transmitConfiguration(txCh)
Periodic Messages ID Extended Name Data Rate (seconds) --- -------- --------------- ----------------- -------------- 100 false EngineMsg 0 0 0 0 70 0 0 0 0.100000 200 false TransmissionMsg 0 0 0 0 0 0 0 0 0.500000 Event Messages None
关闭通道和 DBC 文件
通过从工作区中清除 DBC 文件的变量,关闭对通道和 DBC 文件的访问。
clear rxCh txCh clear db