CODESYS - the IEC 61131-3 automation software

Welcome to the official CODESYS Forum
Deutsche Version English version russian version 
It is currently Tue May 30, 2017 11:57 am

All times are UTC+01:00




Post new topic  Reply to topic  [ 11 posts ] 
Author Message
PostPosted: Wed Apr 05, 2017 6:55 am 
Offline

Joined: Thu Mar 30, 2017 12:13 pm
Posts: 7
Dear readers,

I'm working with extension modules, and there are a lot of identical structures in a device. So I would like to have a single structure that I can reuse.
Declaring this structure globally would make it accessible through the entire controller.

//Making the structure with 16 booleans, resulting in a WORD width.
TYPE t16bitDUT:
STRUCT
BIT00 : BOOL; //Boolean Type
BIT01 : BOOL; //Boolean Type
BIT02 : BOOL; //Boolean Type
BIT03 : BOOL; //Boolean Type
BIT04 : BOOL; //Boolean Type
BIT05 : BOOL; //Boolean Type
BIT06 : BOOL; //Boolean Type
BIT07 : BOOL; //Boolean Type
BIT08 : BOOL; //Boolean Type
BIT09 : BOOL; //Boolean Type
BIT0A : BOOL; //Boolean Type
BIT0B : BOOL; //Boolean Type
BIT0C : BOOL; //Boolean Type
BIT0D : BOOL; //Boolean Type
BIT0E : BOOL; //Boolean Type
BIT0F : BOOL; //Boolean Type
END_STRUCT
END_TYPE

//And create globally AT the address of an extension module.
MY16bDUT AT %MW0 : t16bitDUT;

But this does also not work.. Results in a type mismatch.
So I tried :

//Create an union from the structure and the an overlapping WORD and place the word AT the address of the extension module.

UNION
bits: t16bitDUT;
all AT %MW0 : WORD;
END_UNION

But this doesn't work either..

//Or maybe define the union first
TYPE u16bitDUT
UNION
bits: t16bitDUT;
all : WORD;
END_UNION
END_TYPE

//And then create the variable globally
MY16bDUT AT %MW0 : u16bitDUT;

Also not working.

Does anybody know how this can be done?
A pointer type would also be acceptable.

best regards,

Tjarco


Top
   
PostPosted: Wed Apr 05, 2017 11:35 pm 
Offline
Frequent User
Frequent User

Joined: Fri Sep 02, 2011 8:02 pm
Posts: 218
A BOOL uses a byte of memory. It is not bit sized. Your hope of 16 BOOLs being a WORD is not correct.


Top
   
PostPosted: Mon Apr 10, 2017 1:34 pm 
Offline

Joined: Thu Mar 30, 2017 12:13 pm
Posts: 7
Hi,

Thanks for your reply. Your comment is not correct, when using as STRUCT they ARE bitwise members. See documentation:

Quote:
Bit Access in Structures
The data type BIT is a special data type which can only be defined in structures. It consumes memory space of 1 bit and allows you to address single bits of a structure by name.
TYPE <structurename>:
STRUCT
<bitname bit1> : BIT;
<bitname bit2> : BIT;
<bitname bit3> : BIT;
...
<bitname bitn> : BIT;
END_STRUCT
END_TYPE
You can gain access to the structure component BIT by using the following syntax:
<structurename>.<bitname>
NOTE: The usage of references and pointer on BIT variables is not possible. Furthermore, BIT variables are not allowed in


Top
   
PostPosted: Mon Apr 10, 2017 5:59 pm 
Offline
User avatar

Joined: Tue Aug 30, 2011 1:21 am
Posts: 89
@tjarcoboerkoel

Bit and BOOL are not the same. Your first post will work if you use BIT instead of BOOL. Also note BIT can only used in structures. When using BOOL codesys stores it as byte.


BR

_________________
The Original SoMachine Ninja


Top
   
PostPosted: Tue Apr 11, 2017 6:14 am 
Offline

Joined: Thu Mar 30, 2017 12:13 pm
Posts: 7
Are you serious?
There are two 'bit' types where the first is bitwise and the second is byte oriented?
Why on earth...?

Well, thanks for the clarification. I'll try again.

Indeed, the expansion module has also type BOOL, so that makes it confusing.
I've created the STRUCT in a DUT, within TYPE declarations and I created the variable in a GVL. That works fine.
Then I assigned the variable to the PARENT element of the digital interface and start compilation.

Quote:
------ Build started: Application: MyController.Sim.MyController.Application -------
|ERROR| .... The name used in the interface is not identical with the object's Name
Compile complete -- 1 errors, 0 warnings


I get an error, but I don't really understand it. The 'name' is not the same.. Why should the name be the same..?

Code:
/*DUT code*/
TYPE FLOOR1 :
   STRUCT
      FloorValve2A:          BIT:=FALSE;   (* Hal + Gang begandegrond*)
      FloorValve3A:         BIT:=FALSE;   (* Woonkamer, verdeeld in 3 zones *)
      FloorValve3B:         BIT:=FALSE;   (* Woonkamer, verdeeld in 3 zones *)
      FloorValve3C:         BIT:=FALSE;   (* Woonkamer, verdeeld in 3 zones *)
      FloorValve4A:         BIT:=FALSE;   (* Keuken, verdeeld in 3 zones *)
      FloorValve4B:         BIT:=FALSE;   (* Keuken, verdeeld in 3 zones *)
      FloorValve4C:         BIT:=FALSE;   (* Keuken, verdeeld in 3 zones *)
      FloorValve6A:         BIT:=FALSE;   (* Berging, verdeeld in 2 zones *)
      FloorValve6B:         BIT:=FALSE;   (* Berging, verdeeld in 2 zones *)
      FloorValve7A:         BIT:=FALSE;   (* Badkamer + Toilet *)
      FloorValve8A:         BIT:=FALSE;   (* Werk / slaapkamer, verdeeld in 2 zones *)
      FloorValve8B:         BIT:=FALSE;   (* Werk / slaapkamer, verdeeld in 2 zones *)
      
      FloorValve10A:          BIT:=FALSE;   (* Overloop 1ste verdieping*)
      FloorValve11A:          BIT:=FALSE;   (* Slaapkamer 1ste verdieping*)
      FloorValve12A:          BIT:=FALSE;   (* Slaapkamer 1ste verdieping*)
      FloorValve13A:          BIT:=FALSE;   (* Slaapkamer 1ste verdieping*)
   END_STRUCT
END_TYPE


Code:
/*GVL code*/
VAR_GLOBAL
      tester :FLOOR1;
      ...
END_VAR


/*IO MAPPING*/
Attachment:
DigitalOutput.jpg



Even if I create a simple variable of type WORD, and assign this to the PARENT (testword:WORD;) then I get the same error.

best regards


You do not have the required permissions to view the files attached to this post.


Top
   
PostPosted: Tue Apr 11, 2017 2:26 pm 
Offline

Joined: Thu Mar 30, 2017 12:13 pm
Posts: 7
Well,

I've changed the DUT (file) name to be identical to the TYPE name. And this makes the error about the name disappear and now I'm able to build without errors.

Thank you for the thoughts!


Top
   
PostPosted: Wed Apr 12, 2017 1:23 pm 
Offline
Frequent User
Frequent User

Joined: Fri Sep 02, 2011 8:02 pm
Posts: 218
CoDeSys is a hardware independent software and I've learned one should never count on a particular data size or byte order (Motorola vs. Intel). Using c language bit-banging tricks (which were necessary when RAM and CPU power was low) can get you in trouble fast. As you just found out with BIT vs BOOL. I never use UNIONS (since I started with CoDeSys in 2005) and avoid bit-level manipulation when possible. Even when I have to deal with bit-coded control words for drives, I define a control word "object" (function block) and immediate generate BOOL outputs or "Properties" so I don't build in bit-order dependence in my code. When I switch from brand X to Y and the Enable bit moves from bit 0 to bit 3, I only change one line of code.


Top
   
PostPosted: Wed Apr 12, 2017 3:23 pm 
Offline

Joined: Thu Mar 30, 2017 12:13 pm
Posts: 7
Hi Scott,

Thank you for your reply. I agree that it's not the most "sustainable" is for cross platform solutions, but on the other hand you always have to change something.
As you probably noticed, I've experience in C/C++ in bare-metal/embedded and linux environments. not much in this area (yet). I have some experience in Structured Text.

Do you have a simple example or know where I can find a simple example to create this 'control word object'?
Do you mean creating a function with "input" variable WORD and call the function from the ?

Best regards,


Top
   
PostPosted: Thu Apr 13, 2017 2:09 pm 
Offline
Frequent User
Frequent User

Joined: Fri Sep 02, 2011 8:02 pm
Posts: 218
I first came from c language and embedded systems, too. I have learned to toss out many things I used to do that were all about saving RAM, stack size and overhead. I now think: "I have unlimited RAM - don't care about it. I can transfer lots of variables - don't care about it. I have fast execute times - don't care about it." On some platforms, I had to revisit the execute time, but so rarely I still ignore it at the beginning of the project. I've never had to deal with "out of memory" issues (how that makes life so much easier compared to the old on-chip days).

Regarding the example - I create a FB for the control word encoding (or anywhere I have to encode/decode bits). Only in this FB, will "evil" bit coding occur...

The "traditional" solution would use VAR_INPUTS and VAR_OUTPUTS and the FB would have to be called to calculate the CW:
Code:
FUNCTION_BLOCK CwEncode
VAR_INPUT
   Enable : BOOL;
   Run    : BOOL;
   EStop  : BOOL;
END_VAR
VAR_OUTPUT
   CtrlWd : INT;
END_VAR

CtrlWd.0 := Enable;
CtrlWd.1 := Run;
CtrlWd.2 := EStop;
END_FUNCTION_BLOCK

PROGRAM PLC_PRG
VAR
   Drive1CW       : CtrlWdEncode;
   Drive2CW       : CtrlWdEncode;
   MachineEStop   : BOOL;
   EnableDrives   : BOOL;
   RunDrive1      : BOOL;
   RunDrive2      : BOOL;
END_VAR

Drive1CW(Enable:=EnableDrives, EStop:=MachineEStop, Run:=RunDrive1);
Drive2CW(Enable:=EnableDrives, EStop:=MachineEStop, Run:=RunDrive2);
END_PROGRAM


This solution requires calling Drive1CW() and Drive2CW() to actually update the output variable CtrlWd. You run into trouble if you do: Drive1CW.Enable:=TRUE; because this never actually runs the FBs code - only sets the VAR_INPUT to TRUE... This is usually not an issue for simple systems. Example:
Code:
Drive1CW.Enable:=TRUE; //CtrlWd not changed
Drive1CW.MachineEstop:=TRUE; //CtrlWd not changed
Drive1CW(); //now CtrlWd is calculated


As an OOP alternative, I have started to use Properties, which can trigger code (so I don't need to call the FB explicitly when I set the property to TRUE or FALSE).

OOP "style":
Code:
FUNCTION_BLOCK CtrlWdEncode
VAR_OUTPUT
   CtrlWd : INT;
END_VAR

PROPERTY Enable : BOOL
SET
CtrlWd.0 := Enable;

PROPERTY Run : BOOL
SET
CtrlWd.1 := Run;

PROPERTY EStop : BOOL
SET
CtrlWd.2 := EStop;

END_FUNCTION_BLOCK

PROGRAM PLC_PRG
VAR
   Drive1CW       : CtrlWdEncode;
   Drive2CW       : CtrlWdEncode;
   MachineEStop   : BOOL;
   EnableDrives   : BOOL;
   RunDrive1      : BOOL;
   RunDrive2      : BOOL;
END_VAR

Drive1CW.Enable := EnableDrives; //CtrlWd is updated
Drive1CW.Run := RunDrive1; //CtrlWd is updated
Drive1CW.EStop := MachineEStop; //CtrlWd is updated
Drive2CW.Enable := EnableDrives; //CtrlWd is updated
Drive2CW.Run := RunDrive2; //CtrlWd is updated
Drive2CW.EStop := MachineEStop; //CtrlWd is updated
END_PROGRAM


I delete the GET part of the properties - I never want to read the CW bits... I only set them. In this trivial example, the code looks larger and probably more complicated for the OOP solution. However, for me, the advantage appears in real-life programs where you have some chunk of code deciding if drives 1 and 2 need to "run"; and a machine safety chunk of code deciding if estop mode should be triggered. I can just write Drive1CW.Estop := TRUE and I know the CW is already updated. Yes, Drive1CW(Estop:=TRUE) would also work, BUT(!) lets say for some reason that for the ESTOP to work correctly on the device, that several other bits need to change state, then in my Property version, I simply have a more advanced code block:
Code:
PROPERTY EStop : BOOL
SET
CtrlWd.2 := EStop;
CtrlWd.1 := FALSE; //no run bit
CtrlWd.0 := TRUE; //must be enabled!!!

Again, the traditional way works again but I need a more complicated call - Drive1CW(Estop:=TRUE, Run:=FALSE, Enable:=TRUE). That is OK until I change devices and the new one requires the RUN bit to stay TRUE. Now I have to look in my code for everywhere I set the estop and change Drive1CW(Estop:=TRUE, Run:=FALSE, Enable:=TRUE) to Drive1CW(Estop:=TRUE, Run:=TRUE, Enable:=TRUE)! Also know that properties can also call methods, so if you need to do something every time a bit is set, you can put the "something" in the method and call the method in the property.

Traditional vs OOP is a 47 day discussion and is ultimately a preference anyway. But in both of these solutions, if the bit coding needs to be altered, I only have to touch one FB.


Top
   
PostPosted: Wed Apr 19, 2017 6:37 am 
Offline

Joined: Thu Mar 30, 2017 12:13 pm
Posts: 7
Hi Scott,

Thank you for your example. I'll try it out this weekend, a lot of thing going on at the moment.
Yes, because of the embedded systems my first responds is to keep RAM resource usage as low as possible. I usually have to squeeze every ns from the (realtime) systems for control loops with 10us interrupts. It gives me confidence that you never ran into these limits.

I have A Schneider Modicon M251 system with:
- Analogue (temperature) and digital expansion modules
- two Modbus TCP Remote PWM output modules for valve control (Moxa e1211)
- A DucoBox focus mechanical ventilation with Modbus TCP,
- Some energy meters (E&W) on modbus RTU,
- Water-Water heatpump on modbus TCP.

With this I need to create a Building management system, with web visualization (see attachment). So things need to be retained in memory for presentation.
For temperature control I see the advantage of OOP. Write in the variable and trigger the PI-control loop at the same time. OOP seems to be perfect for this.

Thing is, I'm not sure what to do with the Modbus registers. It's a bulk of data, without any meaning. For example, the DucoBox has 100 subnodes, and each subnode has 20 read and 20 write registers of the type "device" (which can be CO2 sensor, actuator, humidity sensor, main unit, manual switch, etc). So I started to:
- Create Data Unit Types with structures for each device
- Created a new union to have a unified "device structure overlay"
- made an array of 100 of the type "device structure overlay"
So, I now have memory blocks with a meaning. And now I'm stuck.

The modbus registers are word addresses, and I don't seem to have an option to write the entire modbus data (pointer) to this array of devices (and back).
Do you have a suggestion for this?

Best regards,
Attachment:
Schakelschema.pdf


You do not have the required permissions to view the files attached to this post.


Top
   
PostPosted: Thu Apr 20, 2017 1:20 pm 
Offline
Frequent User
Frequent User

Joined: Fri Sep 02, 2011 8:02 pm
Posts: 218
I've solved small Modbus needs by having an array of words and then copying each needed data from that array to my local speaking variables, including all of the conversions I need. This occurs every PLC scan. The classic "brute force method".

You could possibly set up your structures to be REFERENCE TO WORD and set them to point to the needed Modbus WORD. (I don't think you can have the Modbus be REFERENCE TO you variables...). But you will still need to convert the raw data to something.

It could be interesting to set up small FBs for each type of sensor, etc. So for the thermocouple, you have a FB_Thermocouple, which has a local var REFERENCE TO WORD, which you link to your Modbus register. Then you have an output var on the FB which is a REAL for the actual temp, which handles the actual conversion. Once you setup your FBs (objects), create a structure of FBs to match your repetitive IO blocks. I've never done that, so maybe it works, maybe it doesn't...


Top
   
Display posts from previous:  Sort by  
Post new topic  Reply to topic  [ 11 posts ] 

All times are UTC+01:00


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Limited