Main Content

为处理器在环 (PIL) 仿真创建目标通信信道

实现处理器在环 (PIL) 仿真的通信信道。

通信信道可实现不同进程之间的数据交换。对于需要在开发计算机(主机)上运行的 Simulink® 软件环境与在目标硬件上运行的已部署代码之间交换数据的功能,可通过通信信道为其提供支持。例如,PIL 仿真。

您将通过本例了解 rtiostream 接口以及如何利用它提供的通用通信信道来针对不同的连接类型实现目标连接驱动程序。此示例说明如何使用默认的 TCP/IP 实现。

假设有 A 站和 B 站两个实体,它们使用 rtiostream 接口建立通信信道并交换数据。在此示例中,A 站和 B 站在台式计算机的同一进程中进行配置。

目标连接驱动程序支持在目标系统上运行 PIL 仿真。在仿真中,A 站和 B 站分别代表通过通信信道交换数据的目标和主机。在主机端,目标连接驱动程序以共享库的形式实现,该共享库是一个在 MATLAB® 产品内部加载和调用的库。在目标端,驱动程序是一段源代码或一个库,链接到在目标上运行的应用程序。

此外,您还可以:

  • 配置您自己的 TCP/IP 目标端驱动程序,以使用默认的主机端 TCP/IP 驱动程序来工作。

  • 配置提供的主机端驱动程序以进行串行通信。

  • 实现自定义目标连接驱动程序,例如通过对通信信道的主机端和目标端使用 CAN 或 USB 来实现。

另请参阅用 SIL 和 PIL 仿真测试生成的代码为自定义目标配置处理器在环 (PIL) 仿真

查看默认 TCP/IP 实现的源代码

文件 rtiostream_tcpip.c 用于实现客户端和服务器端的 TCP/IP 通信。启动参数会对驱动程序进行配置,使其以客户端模式或服务器模式运行。您可以使用此源文件作为自定义实现的起点。通信信道的两端或者需要服务器实现,或者需要客户端实现。如果客户端和服务器驱动程序在不同架构上运行,请考虑将适用于每个架构的驱动程序代码放在单独的源文件中。

头文件 rtiostream.h 包含函数 rtIOStreamOpen/Send/Recv/Close 的原型。请包括它(使用 #include)以进行自定义实现。

提取 TCP/IP 驱动程序源代码的位置。

rtiostream_src_dir=fullfile(matlabroot,'toolbox','coder','rtiostream','src');
tcpip_dir=fullfile(rtiostream_src_dir,'rtiostreamtcpip');

查看 rtiostream_tcpip.c。

edit(fullfile(tcpip_dir,'rtiostream_tcpip.c'));

查看 rtiostream.h。

edit(fullfile(rtiostream_src_dir,'rtiostream.h'));

共享库文件的位置

要从 MATLAB 产品访问目标连接驱动程序,您必须将这些驱动程序编译为一个共享库。该共享库必须位于系统路径上。默认 TCP/IP 驱动程序的共享库位于 matlabroot/bin/$ARCH 中($ARCH 是您的系统架构,例如 win64)。

共享库文件名扩展名和位置取决于您的操作系统。

[~, ~, sharedLibExt] = coder.BuildConfig.getStdLibInfo;

确定 A 站和 B 站的共享库。

libTcpip = ['libmwrtiostreamtcpip' sharedLibExt];
disp(libTcpip)
libmwrtiostreamtcpip.dll

测试目标连接驱动程序

如果您正在实现自定义目标连接驱动程序,则从 MATLAB 产品中进行测试是有帮助的。以下示例说明如何加载默认 TCP/IP 目标连接驱动程序,并将其用于 A 站和 B 站之间的数据交换。

要访问驱动程序,可以使用 MEX 文件 rtiostream_wrapper。使用此 MEX 文件,您可以加载共享库并访问 rtiostream 函数来打开和关闭 rtiostream 信道,以及发送和接收数据。

A 站和 B 站均运行在主机上。A 站配置为 TCP/IP 服务器,B 站配置为 TCP/IP 客户端。对于从主机到目标的通信,通常将主机配置为 TCP/IP 客户端,而将目标配置为 TCP/IP 服务器。

选择 TCP 的端口号。

if usejava('jvm')
    % Find a free port
    tempSocket = java.net.ServerSocket(0);
    port = num2str(tempSocket.getLocalPort);
    tempSocket.close;
else
    % Use a hard-coded port
    port = '14646';
end

打开 A 站 rtiostream 作为 TCP/IP 服务器。

stationA = rtiostream_wrapper(libTcpip,'open',...
                                 '-client', '0',...
                                 '-blocking', '0',...
                                 '-port',port);

如果通信信道打开,则返回值是连接的句柄。返回值为 -1 表示错误。

检查返回值。

assert(stationA~=(-1))

打开 B 站 rtiostream 作为 TCP/IP 客户端。

stationB = rtiostream_wrapper(libTcpip,'open',...
                                 '-client','1',...
                                 '-blocking', '0',...
                                 '-port',port,...
                                 '-hostname','localhost');

如果通信信道打开,则返回值是连接的句柄。返回值为 -1 表示错误。

检查返回值。

assert(stationB~=(-1))

从 B 站向 A 站发送数据

目标连接驱动程序以 8 位字节发送数据流。对于非字节寻址型处理器,数据以最小可寻址字长发送。

从 B 站向 A 站发送消息数据。

msgOut = uint8('Station A, this is Station B. Are you there? OVER');

[retVal, sizeSent] = rtiostream_wrapper(libTcpip,...
                                       'send',...
                                       stationB,...
                                       msgOut,...
                                       length(msgOut));

返回值为零表示成功。

assert(retVal==0);

确保消息中的字节已发送。

assert(sizeSent==length(msgOut));

留出足以完成数据传输的时间。

pause(0.2)

在 A 站接收数据。

[retVal, msgRecvd, sizeRecvd] = rtiostream_wrapper(libTcpip,...
                                                 'recv',...
                                                 stationA,...
                                                 100);

返回值为零表示成功。

assert(retVal==0);

确保收到消息中的字节。

assert(sizeRecvd==sizeSent);

显示收到的数据。

disp(char(msgRecvd))
Station A, this is Station B. Are you there? OVER                                                   

从 A 站向 B 站发回响应

从 A 站向 B 站发送响应数据。

msgOut = uint8('Station B, this is Station A. Yes, I''m here! OVER.');
[~, sizeSent] = rtiostream_wrapper(libTcpip,... %#ok
                                       'send',...
                                       stationA,...
                                       msgOut,...
                                       length(msgOut));

留出足以完成数据传输的时间。

pause(0.2)

在 B 站接收数据。

[~, msgRecvd, sizeRecvd] = rtiostream_wrapper(libTcpip,... %#ok
                                                 'recv',...
                                                 stationB,...
                                                 100);

显示收到的数据。

disp(char(msgRecvd))
Station B, this is Station A. Yes, I'm here! OVER.                                                  

关闭连接并卸载共享库

在 B 站上关闭 rtiostream。

retVal = rtiostream_wrapper(libTcpip,'close',stationB);

返回值为零表示成功。

assert(retVal==0);

在 A 站上关闭 rtiostream。

retVal = rtiostream_wrapper(libTcpip,'close',stationA);

返回值为零表示成功。

assert(retVal==0)

卸载共享库。

rtiostream_wrapper(libTcpip, 'unloadlibrary');

用于串行通信的主机端驱动程序

您可以使用提供的主机端驱动程序进行串行通信,作为适用于 TCP/IP 的驱动程序的替代方法。要配置串行驱动程序,请参阅 Embedded Coder® 参考文档中的 rtiostream_wrapper

配置您自己的目标端驱动程序

如果您的目标具有以太网连接,并且可以使用 TCP/IP 堆栈,请按照下列步骤操作:

  1. 为您的 TCP/IP 堆栈编写包装器,使其可供 rtiostream.h 中定义的 rtiostream 接口使用。

  2. 为您的目标编写一个可以发送和接收一些数据的测试应用程序。

  3. 使用 rtiostream_wrapper MEX 文件和主机端 TCP/IP 驱动程序来测试在目标上运行的驱动程序软件。

  4. 当您的目标端驱动程序可以工作后,请将驱动程序的源文件放入自动生成代码的编译版本中。

您可以将目标端驱动程序配置为仅作为 TCP/IP 服务器运行,因为 PIL 的默认主机端驱动程序配置为 TCP/IP 客户端。

如果您需要使用主机端尚不支持的通信信道,请为主机和目标编写驱动程序。在本例中,您仍可以使用 rtiostream_wrapper MEX 文件来测试您的 rtiostream 驱动程序。

配置您自己的主机端驱动程序

您可以使用不同的通信信道来实现目标连接驱动程序。例如,您可以通过特殊的串行连接来实现主机-目标通信,前提是您需要为主机和目标提供驱动程序。

在主机端,您可以使用 rtiostream_wrapper MEX 文件来测试驱动程序。如果您的驱动程序包含来自 printf 语句的诊断输出,并且 rtiostream_wrapper 加载了共享库,则必须使用 mexPrintf 语句替换 printf 语句。

当您有一个可以工作的主机端设备驱动程序后,您必须使其在 Simulink 软件环境中可用。对于 PIL 仿真,请通过 sl_customization 注册共享主机端共享库。

相关主题