Main Content

本页翻译不是最新的。点击此处可查看最新英文版本。

在 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 报文 EngineMsgTransmissionMsg

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