SCL - Load digital input image into an array

nettogrisen

Lifetime Supporting Member
Join Date
Mar 2007
Location
Here
Posts
383
Hi

First time with SCL, so bear with me.
I'm trying to load the digital input image (using piw) into an array.

This is what I have so far:


FUNCTION FC100 : void
// Load DI image into alarm array
VAR_TEMP
almNo : INT; // Alarm number

END_VAR
FOR almNo := 0 TO 15 BY 1 DO

DB100.AlmArray[almNo].almHigh := (PIW0 & (2**almNo));
END_FOR;

END_FUNCTION



but it won't compile.
It says "Invallid types of addresses" on this line:

DB100.AlmArray[almNo].almHigh := (PIW0 & (2**almNo));

Can i cast it somehow?

This is what I would have done in c++:


#include <iostream>
#include <cmath>
using namespace std;

int main() {

bool almArray[16];
int almNo;
int input = 13; // 1101

for(almNo = 0; almNo < 15; almNo++) {
almArray[almNo] = ((int)pow(2, (double)almNo) & input);
cout << almArray[almNo] << \"\\n\";
}


return 0;

}




and that works fine.
 
example

maybe a little example out of my projects can help:

Code:
HW_ADDR: INT;
BEAM_BYTES: ARRAY [1..12] OF BYTE; 
 
FOR i := 1 TO 12 DO
    BEAM_BYTES[i]:= PIB[HW_ADDR + 2 + i];      
END_FOR;


Hi

First time with SCL, so bear with me.
I'm trying to load the digital input image (using piw) into an array.

This is what I have so far:


FUNCTION FC100 : void
// Load DI image into alarm array
VAR_TEMP
almNo : INT; // Alarm number

END_VAR
FOR almNo := 0 TO 15 BY 1 DO

DB100.AlmArray[almNo].almHigh := (PIW0 & (2**almNo));
END_FOR;

END_FUNCTION



but it won't compile.
It says "Invallid types of addresses" on this line:

DB100.AlmArray[almNo].almHigh := (PIW0 & (2**almNo));

Can i cast it somehow?

This is what I would have done in c++:


#include <iostream>
#include <cmath>
using namespace std;

int main() {

bool almArray[16];
int almNo;
int input = 13; // 1101

for(almNo = 0; almNo < 15; almNo++) {
almArray[almNo] = ((int)pow(2, (double)almNo) & input);
cout << almArray[almNo] << \"\\n\";
}


return 0;

}




and that works fine.
 
Thanks combo :), but that still leaves me with a byte.
I realize that I wasn't clear enough

DB100.AlmArray[almNo].almHigh is of bool.

I've been trying with word_to_int, real_to_int ect. but I can't seem to get it running.
 
Hi,
there are different ways to do this dependig of your data-structure in your DB.
If you have only an array of bool with no other data, you could copy your inputs simply with a BlockMove (SFC20).
In SCL it's a littly bit fuzzy to build an ANY-Pointer, in AWL this would be easier.
Code:
// Version when bools are aligned
TYPE UDT112
    STRUCT
        SyntaxID: BYTE;  //A Syntax-ID
        DataType: BYTE;  //Code for type
        DataCount: INT;  //repeats
        DB_Number: WORD;  //db number
        BytePointer: DWORD;  // pointer to Byte- and Bitaddress
    END_STRUCT
END_TYPE

DATA_BLOCK DB112
    STRUCT
        AlmArray : ARRAY [0 .. 128] OF BOOL; // will be 16 Bytes
    END_STRUCT
BEGIN
END_DATA_BLOCK

// read 16 bytes of input
FUNCTION FC112 : VOID

VAR
    retVal : INT;
    pSrc : ANY;    
    pDest : ANY;
    atSrc AT pSrc : UDT112;
    atDest AT pDest : UDT112;
END_VAR    
    atSrc.SyntaxID:= 16#10;     // 0x10 for S7
    atSrc.DataType:= 16#02;     // 0x02 = Byte
    atSrc.DataCount:= 16#0F;    // 16 Bytes
    atSrc.DB_Number:= 16#00;    //
    atSrc.BytePointer:= DW#16#81000000;  //0x81 = Input
    
    atDest.SyntaxID:= 16#10;    // 0x10 for S7
    atDest.DataType:= 16#02;    // 0x02 = Byte
    atDest.DataCount:= 16#0F;   // 16 Bytes
    atDest.DB_Number:= 16#70;   // DB112 dec
    atDest.BytePointer:= DW#16#84000000;  //0x84 = DB
   
    retVal := SFC20(SRCBLK := pSrc, DSTBLK := pDest);     
END_FUNCTION
If you have other data in your DB too (foo in my example), then you could do it like this:
Code:
// Version when bools are not aligned
DATA_BLOCK DB111
    STRUCT
        AlmArray : ARRAY [0 .. 128] OF STRUCT
                almHigh : BOOL;
                foo     : BOOL;
            END_STRUCT;
    END_STRUCT
BEGIN
END_DATA_BLOCK

// read 16 bytes of input
FUNCTION FC111 : VOID

VAR_TEMP
    b : BYTE;
    bArr AT b : ARRAY [0..7] OF BOOL;
    i : INT;
    j : INT;
END_VAR

    FOR i := 0 TO 15 DO
        b := EB[i];
        FOR j := 0 TO 7 DO
            DB111.AlmArray[(i * 8) + j].almHigh := bArr[j];
        END_FOR;
    END_FOR;
    
END_FUNCTION
Thomas
 
Hej Jesper.

Tell us what you want to achieve in the end.
Your C++ code is gibberish to us, because we cannot see what you want to achieve.

I think this has something to do with that your digital inputs follow a certain pattern, and some of them are alarms that you want to assign to an alarm DB that is formatted to another pattern.
Tell us all the relevant details, and also what the whole idea is.
 
Hej Jesper

I'm designing an alarmpanel for fishing vessels (ofcourse I am, I'm from Skagen ;) ).

We don't install the panels, so the idea is to be able to type in the alarmtext, alarm delay and weather it's NO/NC wired for each alarm.
As it is right now we use ML1100/1400s for this but it's very messy with 5 lines of code pr. alarm with about 100 alarms, and I still have to type in all the alarm texts.
Actually I have to type in all the alarm texts on each HMI, as there are sometimes up to 5 Magelis HMI's of different sizes, that doesn't let me change alarm-texts in runtime.
This is why I want the texts saved as strings inside the PLC and roll my own alarmlog.

The panels also include tanklevel measurement, main engine cylinder temperatures, navigationlights, decklights and more

What I've done so far is this:

Made an UDT (AlmDT) with:
AlmHigh : BOOL
AlmDelay : INT
AlmText : STRING
AlmWiring : BOOL

Made an array of the UDT
AlmArray[0..100] of AlmDT

What I'm trying to do is to load the first 50 DI's into
AlmArray[x].AlmHigh where x is the alarmnumber.

So if I0.0 goes high, so does Almarray[0].AlmHigh

Then when an alarm has AlmHigh I copy it's string in an alarmlog array, shifting the array (FILO).

To display in on the HMI I'll need another array, but I'll deal with that when I get there :)

I hope that this makes sense

Thomas, that looks a bit complicated for what I'm trying to do, but you might be right

BTW. Will the PLC be able to handle this relatively fast or will the scantimes be very long?
 
What I've done so far is this:

Made an UDT (AlmDT) with:
AlmHigh : BOOL
AlmDelay : INT
AlmText : STRING
AlmWiring : BOOL

Made an array of the UDT
AlmArray[0..100] of AlmDT

I don't think that there is a solution without a loop, due to the ordering of the AlmHigh bits in your DB.

If you simplify my example above it would be:
Code:
FUNCTION FC113 : VOID

VAR_TEMP
    i : INT;
    j : INT;
END_VAR
    FOR i := 0 TO 15 DO
        FOR j := 0 TO 7 DO
            DB113.AlmArray[(i * 8) + j].AlmHigh := I[i, j];
        END_FOR;
    END_FOR;
    
END_FUNCTION
to read I0.0 to I15.7.

You can calculate the execution time of the generated IL.
Maybe it's faster to read a complete byte/word/dword to local data and then split it into bits.

If you want best performance -> write it yourself in IL.
 
I have gotta go now as I am off to Skjern for a commisioning, too late already, 4 hous drive is awaiting.
But Thomas code looks OK to me.
Only thing is that your data structure looks inefficient.
And if the Magelis HMI has to scan all the alarm texts, wouldnt it be better to put them in one contigous block ?
As to if it will run efficiently or not, then I think there is something to gain from rearranging the data into contigous blocks.
And yes, some clever usage of AT and TEMPs can also speed up the execution.
Maybe it is not sooo important, todays CPUs are so much faster than yesterdays. Be careful to order the latest generation CPU (---14 in the type) if you are using a 312, 314 or 315-2DP. They are 2-5 times faster than the previous generation.
 
If so then can't one use the 'AT' feature to put the alarm bits over the alarm word?

I'll look into "AT", but what's that about a book?

Thanks Peter

I don't think that there is a solution without a loop, due to the ordering of the AlmHigh bits in your DB.

If you simplify my example above it would be:
Code:
FUNCTION FC113 : VOID
 
VAR_TEMP
    i : INT;
    j : INT;
END_VAR
    FOR i := 0 TO 15 DO
        FOR j := 0 TO 7 DO
            DB113.AlmArray[(i * 8) + j].AlmHigh := I[i, j];
        END_FOR;
    END_FOR;
 
END_FUNCTION
to read I0.0 to I15.7.

You can calculate the execution time of the generated IL.
Maybe it's faster to read a complete byte/word/dword to local data and then split it up into bits.

If you want best performance -> write it yourself in IL.

Aha!

I[i, j];

I was fiddeling with that but I couldn't quite get it right, that's why I was trying with PIW and split it into bits.

Thanks Thomas

I have gotta go now as I am off to Skjern for a commisioning, too late already, 4 hous drive is awaiting.
But Thomas code looks OK to me.
Only thing is that your data structure looks inefficient.
And if the Magelis HMI has to scan all the alarm texts, wouldnt it be better to put them in one contigous block ?
As to if it will run efficiently or not, then I think there is something to gain from rearranging the data into contigous blocks.
And yes, some clever usage of AT and TEMPs can also speed up the execution.
Maybe it is not sooo important, todays CPUs are so much faster than yesterdays. Be careful to order the latest generation CPU (---14 in the type) if you are using a 312, 314 or 315-2DP. They are 2-5 times faster than the previous generation.

I'll try to structure it differently, and will look into AT and Temps but what is a contigous block?
I hope that you have a nice trip. Isn't it kind of late for a commisioning?

Thanks Jesper
 
Back to Siemens

I'll look into "AT", but what's that about a book?
I don't have a book. I am reading scl_e.pdf from the Siemens website. In chapter 8.4 it uses the 'AT' keyword to do the same thing as a union in 'C'. The scl_e.pdf calls this a view.
Code:
VAR_INPUT
  BUFFER: ARRAY[0..15] OF WORD;
  ALARMS AT BUFFER : ARRAY[0..239] OF BOOL;
END_VAR

The idea is that you pass all your alarms in as an array of WORDs but can access them within the function as either words or booleans. This would save a lot of CPU time copy 240 bools bit by bit.

Note, I haven't tried this in SCL. I use this technique when programming in C and I see it is available in SCL.
 
I would do the opposite of Peters example.
I would set up the Alarms as an array of bits. This because you probably want to control them as such everywhere else in the program.
And then I would scan them as words, this because it is the most efficient.

Code:
// this is an example on how to work with the same data in two different
// ways by means of AT.
//---------------------------------------------------
 
TYPE UDT_Alarms_as_bits 
    STRUCT
        bi_no : ARRAY[0..511] OF BOOL ;
    END_STRUCT
END_TYPE
 
//---------------------------------------------------
 
TYPE UDT_Alarms_as_words
    STRUCT
        word_no : ARRAY[0..31] OF WORD ;
    END_STRUCT
END_TYPE
 
//---------------------------------------------------

// The outputs outp_bi_alarm_on_1 and outp_bi_alarm_on_2 do the same thing, 
// but are calculated in two different ways
FUNCTION_BLOCK FB_Alarm_scan
 
VAR_TEMP
   i : INT ;
END_VAR
 
VAR
    // same data, two different "views"
    biAL : UDT_Alarms_as_bits ; 
    wordAL AT biAL : UDT_Alarms_as_words ;
END_VAR
 
VAR_OUTPUT
    outp_bi_alarm_on_1 : BOOL ;
    outp_bi_alarm_on_2 : BOOL ;
END_VAR
 
outp_bi_alarm_on_1:=FALSE ;
outp_bi_alarm_on_2:=FALSE ;
 
// scan through as bits    
FOR i:=0 TO 511 BY 1 DO
    IF NOT biAL.bi_no[i]=FALSE THEN 
        outp_bi_alarm_on_1:=TRUE ;
    END_IF ;
END_FOR ;
 
// scan through as words    
FOR i:=0 TO 31 BY 1 DO
    IF NOT wordAL.word_no[i]=W#16#0000 THEN 
        outp_bi_alarm_on_2:=TRUE ;
    END_IF ;
END_FOR ;
 
END_FUNCTION_BLOCK
//---------------------------------------------------
ORGANIZATION_BLOCK OB1
 
// set some of the alarms
"Alarms".biAL.bi_no[0]:=M10.2 ;
"Alarms".biAL.bi_no[111]:=M10.3 ;
 
//scan through all the alarms
"FB_Alarm_scan"."Alarms"(); 
 
//what was the result ?
M10.0 := "Alarms".outp_bi_alarm_on_1; 
M10.1 := "Alarms".outp_bi_alarm_on_2; 
 
END_ORGANIZATION_BLOCK
 
I was just trying to point out the technique

Since the accumulator registers are 'full' with either a word or dword then why not check 64 bits at a time using DWORDs?

Code:
FOR I:=0 TO 7 BY 2
   IF (DWORDARRAY[I] OR DWORDARRAY[I+1]) <> 0 THEN
   END_IF
END_FOR

The same can be done in STL and it would be very efficient because one can easily index AR1 by 64 and the offset in the OD DD[AR1,p#32.0] ( I probably screwed up the syntax but you get what I mean ) can be used to access DWORDARRAY[I+1]

The AT keyword is very handy when doing communications. You may get different packets of data and the first word can be used as a command to select the right case in a case statement. Each case has a UDT defined for the way the data appears in the buffer. Then each case inside the case statement one uses a UDT to get the proper 'view' or 'union' to access the data on the buffer based on the command.
 

Similar Topics

HI i would like to know how to get a variable that will store the amount of times a program has been executed. The issue is I have 3 DBs for 1 FB...
Replies
2
Views
71
Hi, I have an intermediate-advance knowledge of programming with TIA Porta in Ladder, would you recommend me to start learning SCL for it to...
Replies
11
Views
558
Hello nice to meet you, im new in here, I'm currently trying to convert code written in STL for a S7-400 to SCL for an S7-1500, because when i run...
Replies
5
Views
316
Hi everyone, I am new to this amazing world of PLC, well... I mean, in practice, since I already knew electronics, programming languages, IT, and...
Replies
7
Views
652
Hi all, This is my first post; I am new to PLC Controls stuff and English is not my native language, so I apologize in advance if something is...
Replies
4
Views
512
Back
Top Bottom