Create a Target Communication Channel for Processor-in-the-Loop (PIL) Simulation
Implement a communication channel for processor-in-the-loop (PIL) simulation.
The communication channel enables exchange of data between different processes. The communication channel supports capabilities that require the exchange of data between the Simulink® software environment that runs on your development computer (host) and deployed code that runs on target hardware. For example, a PIL simulation.
You learn about the rtiostream interface and how it provides a generic communication channel that you can implement in the form of target connectivity drivers for different connection types. This example describes how to use the default TCP/IP implementation.
Two entities, Station A and Station B, use the rtiostream interface to set up a communication channel and exchange data. For this example, Station A and Station B are configured within the same process on your desktop computer.
The target connectivity drivers support an on-target PIL simulation. In the simulation, Station A and Station B represent the target and host computers that exchange data via the communication channel. On the host side, the target connectivity driver is implemented as a shared library that is loaded and called from within the MATLAB® product. On the target side, the driver is source code or a library that is linked to the application that runs on the target.
Additionally, you can:
Configure your own target-side driver for TCP/IP to operate with the default host-side TCP/IP driver.
Configure the supplied host-side driver for serial communications.
Implement custom target connectivity drivers, for example, by using CAN or USB for host and target sides of the communication channel.
See also Test Generated Code with SIL and PIL Simulations and Configure Processor-In-The-Loop (PIL) for a Custom Target.
View Source Code for the Default TCP/IP Implementation
The file rtiostream_tcpip.c implements client-side and server-side TCP/IP communication. A startup parameter configures the driver to operate in either client or server mode. You can use this source file as a starting point for a custom implementation. Each side of the communication channel requires only one or the other of the server or client implementations. If the client and server drivers run on different architectures, consider placing the driver code for each architecture in a separate source file.
The header file rtiostream.h contains prototypes for the functions rtIOStreamOpen/Send/Recv/Close. Include it (using #include) for custom implementations.
Extract the location of TCP/IP driver source code.
rtiostream_src_dir=fullfile(matlabroot,'toolbox','coder','rtiostream','src'); tcpip_dir=fullfile(rtiostream_src_dir,'rtiostreamtcpip');
View rtiostream_tcpip.c.
edit(fullfile(tcpip_dir,'rtiostream_tcpip.c'));
View rtiostream.h.
edit(fullfile(rtiostream_src_dir,'rtiostream.h'));
Location of Shared Library Files
To access the target connectivity drivers from the MATLAB product, you must compile them to a shared library. The shared library must be located on your system path. A shared library for the default TCP/IP drivers is located in matlabroot/bin/$ARCH ($ARCH is your system architecture, for example, win64).
The shared library filename extension and location depends on your operating system.
[~, ~, sharedLibExt] = coder.BuildConfig.getStdLibInfo;
Identify the shared library for Station A and Station B.
libTcpip = ['libmwrtiostreamtcpip' sharedLibExt];
disp(libTcpip)
libmwrtiostreamtcpip.dll
Test the Target Connectivity Drivers
If you are implementing a custom target connectivity driver, it is helpful to test it from within the MATLAB product. The following example shows how to load the default TCP/IP target connectivity drivers and use them for data exchange between Station A and Station B.
To access the drivers, you can use the MEX-file rtiostream_wrapper. With this MEX-file, you can load the shared library and access the rtiostream functions to open and close an rtiostream channel, and send and receive data.
Station A and Station B run on the host computer. Station A is configured as a TCP/IP server and Station B as a TCP/IP client. For host to target communication, you typically configure the host as a TCP/IP client and the target as a TCP/IP server.
Choose a port number for 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
Open the Station A rtiostream as a TCP/IP server.
stationA = rtiostream_wrapper(libTcpip,'open',... '-client', '0',... '-blocking', '0',... '-port',port);
If the communication channel opens, the return value is a handle to the connection. A return value of -1 indicates an error.
Check the return value.
assert(stationA~=(-1))
Open the Station B rtiostream as a TCP/IP client.
stationB = rtiostream_wrapper(libTcpip,'open',... '-client','1',... '-blocking', '0',... '-port',port,... '-hostname','localhost');
If the communication channel opens, the return value is a handle to the connection. A return value of -1 indicates an error.
Check the return value.
assert(stationB~=(-1))
Send Data from Station B to Station A
The target connectivity drivers send a stream of data in 8-bit bytes. For processors that are not byte-addressable, the data is sent in the smallest addressable word size.
Send message data from Station B to Station A.
msgOut = uint8('Station A, this is Station B. Are you there? OVER'); [retVal, sizeSent] = rtiostream_wrapper(libTcpip,... 'send',... stationB,... msgOut,... length(msgOut));
A return value of zero indicates success.
assert(retVal==0);
Make sure that bytes in the message were sent.
assert(sizeSent==length(msgOut));
Allow time for data transmission to be completed.
pause(0.2)
Receive data in Station A.
[retVal, msgRecvd, sizeRecvd] = rtiostream_wrapper(libTcpip,... 'recv',... stationA,... 100);
A return value of zero indicates success.
assert(retVal==0);
Make sure that bytes in the message were received.
assert(sizeRecvd==sizeSent);
Display the received data.
disp(char(msgRecvd))
Station A, this is Station B. Are you there? OVER
Send a Response from Station A to Station B
Send response data from Station A to Station B.
msgOut = uint8('Station B, this is Station A. Yes, I''m here! OVER.'); [~, sizeSent] = rtiostream_wrapper(libTcpip,... %#ok 'send',... stationA,... msgOut,... length(msgOut));
Allow time for data transmission to be completed.
pause(0.2)
Receive data in Station B.
[~, msgRecvd, sizeRecvd] = rtiostream_wrapper(libTcpip,... %#ok 'recv',... stationB,... 100);
Display the received data.
disp(char(msgRecvd))
Station B, this is Station A. Yes, I'm here! OVER.
Close Connection and Unload Shared Libraries
Close rtiostream on Station B.
retVal = rtiostream_wrapper(libTcpip,'close',stationB);
A return value of zero indicates success.
assert(retVal==0);
Close rtiostream on Station A.
retVal = rtiostream_wrapper(libTcpip,'close',stationA);
A return value of zero indicates success.
assert(retVal==0)
Unload the shared library.
rtiostream_wrapper(libTcpip, 'unloadlibrary');
Host-Side Driver for Serial Communications
You can use the supplied host-side driver for serial communications as an alternative to the drivers for TCP/IP. To configure the serial driver, see rtiostream_wrapper
in the Embedded Coder® reference documentation.
Configure Your Own Target-Side Driver
If your target has an Ethernet connection and you have a TCP/IP stack available, follow these steps:
Write a wrapper for your TCP/IP stack that makes it available via the rtiostream interface defined in rtiostream.h.
Write a test application for your target that sends and receives some data.
Use the rtiostream_wrapper MEX-file and host-side TCP/IP driver to test your driver software that is running on the target.
When you have a working target-side driver, include the driver source files in the build for your automatically generated code.
You can configure your target-side driver to operate only as a TCP/IP server because the default host-side driver for PIL is configured as a TCP/IP client.
If you need to use a communications channel that is not already supported on the host-side, write drivers for host and target. In this case, you can still use the rtiostream_wrapper MEX-file for testing your rtiostream drivers.
Configure Your Own Host-Side Driver
You can implement the target connectivity drivers by using different communication channels. For example, you can implement host-target communications via a special serial connection, which requires that you provide drivers for the host and target.
On the host side, you can test the drivers by using the rtiostream_wrapper MEX-file. If your driver includes diagnostic output from printf statements and rtiostream_wrapper loads the shared library, you must replace the printf statements with mexPrintf statements.
When you have a working host-side device driver, you must make it available within the Simulink software environment. For PIL simulation, register the shared host-side shared library via sl_customization.