Data exchange via synchronized buffers
This synchronization option is a user-specific implementation. As such, no special function blocks of a library are used. The complexity of the program code is greater and also depends on the chosen implementation.
A basic condition for the applicability of this method is that only one task should write to the same data.
Write and read access to the data is synchronized by means of additional temporary data buffers. To synchronize these data buffers, a MUTEX procedure is required. However, compared to the exclusive direct use of TestAndSet(), this method has the advantage that access to one of these additional data buffers is always granted and no alternative handling is necessary.
A second condition for the applicability of this method is that the data must not be function block instances or pointers of any kind (POINTER TO, interface pointers/interface variables, references, ...). It is therefore not possible to call methods from one and the same function block instance from two different task contexts.
You may only define data for data exchange that is required in the other task context, in order to avoid the data volume becoming unnecessarily large. The implementation performs copy actions to exchange data between the buffers. Because copying actions with large data blocks require a lot of computing time (calls to the MEMCPY function), this usually limits the possible amount of data to significantly less than 1 MB.
The methods required for this synchronization option are implemented by the user. This specific implementation can be executed in different ways. Some terms such as 3-way buffer or 3-buffer principle are also used for the type of implementation.
Sample program: Synchronization by exchanging data via synchronized buffers
The example shows the data exchange between a slow task and a fast task within a PLC instance.
As an example for any data blocks (no function block instances or pointers), two structures ST_DataA and ST_DataB are transferred to the other task. “DataA” is to be read from task context 1 and written from task context 2. In addition, “DataB” is to be read and written from task context 2 and accessed from task context 1. “DataA” and “DataB” are defined as structure instances for task context 1 and task context 2.
The example contains the function block FB_DataSync with the respective extensions FB_MyDataASync and FB_MyDataBSync for synchronizing these data buffers. Instances of these function blocks are declared in a global variable list and used in the program sequence. Furthermore, only one task context may write to these function block instances – method Write() – and another one may read - method Read(). For security reasons, the methods fail if ignored (return value FALSE).
As mentioned at the beginning, the implementation is more complex than with other PLC examples. In this example, the actual conversion of the data exchange is in function block FB_DataSync, which manages access to several buffers. If you want to test the functionality with your own new data structure, you can leave FB_DataSync unchanged. As with FB_MyDataASync, define a new function block derived from FB_DataSync. In addition, you instantiate your own data structure twice and append the methods Read()/Write(), which each require the assignment of a reference to your new data structure and internally call the methods ReadData()/WriteData() of the base class.
To convert FB_DataSync in this example, two internal buffers are used for temporary data storage. The writing task context copies its data into these buffers and the reading task context copies its data from them. The function TestAndSet() ensures correct access to one of the two internal buffers. This means that the same data is not accessed simultaneously, although data can be written and read simultaneously externally in the application code.