The Standard and Optimized DB Access in Programming

Apr 12, 2025 min read

Introduction

When we program functions / function blocks in TIA Portal, it is a common thing that we want to access some global / udt derived data blocks. We want to make our program flexible and can work with different DBs. In the Step 7 era, there are solutions but not ideal due to its limitations. It is generally regarded as the standard DB access. Now with TIA Portal, the same task can be achieved not only easier, but also makes the program easy to read and understand. These methods are regarded as Optimized DB access.

This article will showcase the standard DB access in STL and the optimized DB access in SCL which can help make your program smooth and elegant.

The Standard DB Access

The standard DB access is the traditional way of accessing a DB (global or derived from an UDT). It mainly relys on loading the DB to the CPU’s register then access its contents using address offsets.

For example, say a DB is declared as below.

DATA_BLOCK "db_to_Access"
{ S7_Optimized_Access := 'FALSE' }
VERSION : 0.1
NON_RETAIN
   STRUCT 
      a_bool : Bool;
      an_int : Int;
   END_STRUCT;

BEGIN

END_DATA_BLOCK

To access the DB and make the interface generic, a function block can be programmed like below using STL.

FUNCTION_BLOCK "IndirectAccess_Standard"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      read_DB : DB_ANY;
   END_VAR

   VAR 
      the_Bool : Bool;
      the_Int : Int;
   END_VAR

BEGIN
NETWORK
TITLE = 
      OPN #read_DB;

      A %DBX0.0;
      = #the_Bool;

      L %DBW2;
      T #the_Int;

END_FUNCTION_BLOCK

In this function block, command OPN is used to load the DB input to the FB with a DB_ANY pointer. The DB’s content is loaded into the PLC’s memory register. Then the program copys the input DB’s values to the FB’s static memory using memory address offset.

Since the data access after OPN relies on the memory offset, this approach can access different DBs as long as for each access the memory offset is correct. This approach is easy, though, relies on memory offsets instead of syntax which makes the program hard to read and understand. Also, any memory offset mishandle will lead to unpredictable error and this kind of mistake can be very hard to troubleshoot.

To tackle the issues above in the TIA Portal era, new ways are introduced to achieve the objectives with much better reliability and clarity.

The Optimized DB Access

TIA Portal is built on syntax and it is recommended to use optimized DB access when possible. Therefore, Variant as a new way to access DB and UDT was introduced. In essence, Variant is a pointer like DB_Any but it works with syntex better.

Below are two scenarios that showcases the optimized DB access. The first one shows how to use Variant to access different UDTs indirectly. The second one shows how to use array[*] of “udt” to access random length arrays.

Access UDT Derived DB or UDT Tag in a Global DB

To let our FB response to different UDT derived DBs or UDT tags, we can use a command Typeof(Variant) and use case of to make it flexible.

For example, we have 3 UDTS delared below.

  • udt_Type1
TYPE "udt_Type1"
VERSION : 0.1
   STRUCT
      a : Bool;
      b : Bool;
      c : Bool;
   END_STRUCT;

END_TYPE
  • udt_Type2
TYPE "udt_Type2"
VERSION : 0.1
   STRUCT
      x : Int;
      y : Int;
      z : Int;
   END_STRUCT;

END_TYPE
  • udt_Type3
TYPE "udt_Type3"
VERSION : 0.1
   STRUCT
      u : String;
      v : String;
      w : String;
   END_STRUCT;

END_TYPE

Each UDT can derive DBs. To make our FB response differently based on the UDTs, the following program can do the trick.

FUNCTION_BLOCK "IndirectAccess_Optimized"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      read_DB : Variant;
   END_VAR

   VAR 
      udt_Type1 { S7_SetPoint := 'False'} : "udt_Type1";
      udt_Type2 { S7_SetPoint := 'False'} : "udt_Type2";
      udt_Type3 { S7_SetPoint := 'False'} : "udt_Type3";
   END_VAR

BEGIN
	REGION Copy DB Values
	    CASE TypeOf(#read_DB) OF
	        "udt_Type1":
	            VariantGet(SRC:=#read_DB,
	                       DST=>#udt_Type1);
	        "udt_Type2":
	            VariantGet(SRC:=#read_DB,
	                       DST=>#udt_Type2);
	        "udt_Type3":
	            VariantGet(SRC:=#read_DB,
	                       DST=>#udt_Type3);
	            
	    END_CASE;
	    
	END_REGION

END_FUNCTION_BLOCK

The FB will use Typeof(Variant) command to first get the type of UDT DB or Tag then get the entire UDT DB or Tag values to its static memory. Since this FB can execute in every PLC scan, we can use temp memory instead of the static memory to save the PLC work memory.

By doing the above, the same FB can work with as many different UDT DBs and / or tags as we want which can make our program very flexible. Also, our program uses syntax instead of memory offsets so it is much easier to read. Lastly, any UDT declaration change won’t affect the program as our program will adopt the new UDT automatically.

Access UDT Derived Array of DB in Different Length

To make the program even more flexible, let’s see if we can process random length of UDT derived DBs with a FB.

Here we use udt_Type1 for the example, its declaration is as below.

TYPE "udt_Type1"
VERSION : 0.1
   STRUCT
      a : Bool;
      b : Bool;
      c : Bool;
   END_STRUCT;

END_TYPE

Now we have 2 array DBs both derived from udt_Type1 but have different length, let’s say one’s length is 10 and the other is 20.

Now we can access the DB in a generic manner with array[*] of “udt_Type1” declared as an InOut interface. This is because InOut is a pointer so it allows variable interface length. Then we can use UPPER_BOUND and LOWER_BOUND to get the start and end of the array index. With these information available, we can easily access each array DB element with an index changing from the lower boundary to the upper boundary.

FUNCTION_BLOCK "IndirectAccess_Optimized"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_IN_OUT 
      "array_DB*" : Array[*] of "udt_Type1";
   END_VAR

   VAR 
      upperBoundary : DInt;
      lowerBoundary : DInt;
      index : DInt;
   END_VAR

BEGIN
	REGION Get Array DB Info
	    #upperBoundary := UPPER_BOUND(ARR := #"array_DB*", DIM := 1);
	    #lowerBoundary := LOWER_BOUND(ARR := #"array_DB*", DIM := 1);
	    
	    FOR #index := #lowerBoundary TO #upperBoundary DO
	        ; //Data processing program
	    END_FOR;
	    
	END_REGION
END_FUNCTION_BLOCK

We can also use similar approach to process random length of udt arrays in a global DB.

Conclusion

The standard DB access allows us to get DB elements flexibly but will impose challenges reading, understanding and maintaining the program. With the Optimized DB access methods, the DB access can be more flexible without rendering the program’s readability and reliability through its maintainance cycle.