# TwinCAT

# Boolean logic

# Python

# %%

# notebook for generating logic tables
# https://en.wikipedia.org/wiki/Boolean_algebra

# Boolean AND

A = [False, True]
B = [False, True]

print("Boolean AND")

for a in A:
    for b in B:
        c = a and b
        print("{} AND {} = {}".format(a, b, c))

print()

A = [0b0, 0b1]
B = [0b0, 0b1]

print("Bitwise AND")

for a in A:
    for b in B:
        c = a & b
        print("{} & {} = {}".format(a, b, c))

print()

A = 0b1100
B = 0b1001

C = A & B

print("Bitwise example:")
print("  {:08b}\n& {:08b}\n  {:08b}".format(A, B, C))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Boolean AND

False AND False = False
False AND True = False
True AND False = False
True AND True = True

Bitwise AND

0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

Bitwise example:

  00001100
& 00001001
  00001000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# %%

# Boolean OR

A = [False, True]
B = [False, True]

print("Boolean OR")

for a in A:
    for b in B:
        c = a or b
        print("{} OR {} = {}".format(a, b, c))

print()

A = [0b0, 0b1]
B = [0b0, 0b1]

print("Bitwise OR")

for a in A:
    for b in B:
        c = a | b
        print("{} | {} = {}".format(a, b, c))

print()

A = 0b1100
B = 0b1001

C = A | B

print("Bitwise example:")
print("  {:08b}\n| {:08b}\n  {:08b}".format(A, B, C))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Boolean OR

False OR False = False
False OR True = True
True OR False = True
True OR True = True

Bitwise OR

0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1

Bitwise example:

  00001100
| 00001001
  00001101
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#%%

# Boolean XOR

A = [False, True]
B = [False, True]

print("Boolean XOR")

for a in A:
    for b in B:
        c = (a != b)
        print("{} XOR {} = {}".format(a, b, c))

print()

A = [0b0, 0b1]
B = [0b0, 0b1]

print("Bitwise XOR")

for a in A:
    for b in B:
        c = a ^ b
        print("{} ^ {} = {}".format(a, b, c))

print()

A = 0b1100
B = 0b1001

C = A ^ B

print("Bitwise example:")
print("  {:08b}\n^ {:08b}\n  {:08b}".format(A, B, C))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Boolean XOR

False XOR False = False
False XOR True = True
True XOR False = True
True XOR True = False

Bitwise XOR

0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0

Bitwise example:

  00001100
^ 00001001
  00000101

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# %%

# Boolean NOT

A = [False, True]

print("Boolean NOT")
for a in A:
    b = not a
    print("NOT {} = {}".format(a, b))

print()

A = [0b0, 0b1]

print("Bitwise NOT via XOR")
for a in A:
    b = a ^ 0b1
    print("{} ^ 0b1 = {}".format(a, b))

print()

A = 0b1100

B = A ^ 0b11111111

print("Bitwise example:")
print("~{:08b}\n {:08b}".format(A, B))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Boolean NOT

NOT False = True
NOT True = False
Bitwise NOT via XOR

0 ^ 0b1 = 1
1 ^ 0b1 = 0

Bitwise example:

~00001100
 11110011
1
2
3
4
5
6
7
8
9
10
11
12
13
# %%

# Bitwise complement

A = [0b0, 0b1]

print("Bitwise NOT via ~")
for a in A:
    b = ~a
    print("~ {} = {}".format(a, b))

print()

A = 0b1100

B = ~A

print("Bitwise example:")
print("~{:08b}\n {:08b}".format(A, B))

# ~ inverts the number's bits.
# The result looks like the 2's complement of a number.
# 2's complement is used to represent a negative number. 
# Python shows the result as the negative number.

# https://en.wikipedia.org/wiki/Ones%27_complement
# https://en.wikipedia.org/wiki/Two%27s_complement
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
itwise NOT via ~

~ 0 = -1
~ 1 = -2

Bitwise example:

~00001100
 -0001101

1
2
3
4
5
6
7
8
9
10
# %%

# Boolean NAND

A = [False, True]
B = [False, True]

print("Boolean NAND")

for a in A:
    for b in B:
        c = not (a and b)
        print("{} NAND {} = {}".format(a, b, c))

print()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Boolean NAND

False NAND False = True
False NAND True = True
True NAND False = True
True NAND True = False
1
2
3
4
5
6
# %%

# Boolean NOR

A = [False, True]
B = [False, True]

print("Boolean NOR")

for a in A:
    for b in B:
        c = not (a | b)
        print("{} NOR {} = {}".format(a, b, c))

print()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Boolean NOR

False NOR False = True
False NOR True = False
True NOR False = False
True NOR True = False
1
2
3
4
5
6

# C++

WARNING

Write C++ code Here later

# Setting Up TwinCAT

WARNING

go to C:\TwinCAT\3.1\System and on an administrator terminal execute the file:

'win8settick'

Then restart the computer!!

# Real-Time - CPU cores

# PLC

# Creating PLC new Item

# Some definition inside the project:

ID Definition Explanation
DUTs User defined data types Structures created
GVLs Global Variables Global variables like in C++, accessible to all files in the project
POUs Program Organized Units Where the MAIN program is located

# New POU

We can choose between various languages:

We now have to include in the MAIN program 'POU();' and this will run POU in the MAIN while loop as fast as the computer can based on the cicle time selected in the "Real-time", in this case 1ms.

MAIN:

PROGRAM MAIN
VAR
END_VAR
---------------------------
POU();
1
2
3
4
5

POU:

PROGRAM MAIN
VAR
END_VAR
---------------------------


1
2
3
4
5
6

# PlcTask

Looking at Cycle ticks we can see that the program will run once every 10 ms.

The Cycle ticks are based on the Base Time defined at the "Real-Time" window, in this case 1 ms

Looking back at the Cycle ticks we can sse that it's going to wake up every 10 Base times and 10*1ms = 10ms.

# Connecting the computer to a hardware

PLCs have inputs and Outputs, so we can define variables in the program as inputs, and we can map those inputs with the hardware pins. In order to describe an input we have to describe a variable name.

Variable 'A' which is an input variable '%I*' of the type Boolean.

PROGRAM POU
VAR
	A AT %I* : BOOL;
	B AT %I* : BOOL;
	A AT %Q* : BOOL := TRUE;
	
END_VAR
1
2
3
4
5
6
7

After building and run we can see the inputs and outputs:

# First Program

PROGRAM POU
VAR
	
	i : INT := 0;
	f : REAL := 0.0;
	elapsed : TIME := TIME#0M0S0MS;
	today : DATE := DATE#2020-06-12;
	message : STRING := '';
	count : BYTE := 0;
	
	A : BOOL := FALSE;
	B : BOOL := FALSE;
	C : BOOL := FALSE;
	D : BOOL := FALSE;
	
	op1 : INT := 1;
	op2 : INT := 2;
	res : REAL := 0.0;
	test : BOOL := FALSE;
	
	TON_0 : TON;
	
END_VAR
---------------------------

// Single line comment

(*
	Multiple line comment
*)

i := 0;
f := 0.0;
elapsed := T#1S;
today := D#2020-06-12;
message := 'Hello World';

// Operators

test := A AND ( A AND B );

res := MAX(op1, op2);

res := EXPT(op1, op2);

res := -op1;
B := NOT A;

res := op1 * op2;
res := op1 / op2;
res := op1 MOD op2;

test := op1 < op2;
test := op1 > op2;
test := op1 <= op2;
test := op1 >= op2;

C := A AND B;

C := A XOR B;

C := A OR B;

// IF statements

IF A = B THEN
	D := TRUE;
END_IF


IF A = B THEN
	D := TRUE;
ELSE
	D := FALSE;
END_IF


IF A = B THEN
	D := TRUE;
ELSIF A = C THEN
	D := FALSE;
END_IF


// CASE

i := 0;

CASE i OF
	0 : A:= TRUE;
	1 : B:= TRUE;
	2 : C:= TRUE;
ELSE
	D := TRUE;
END_CASE


// FOR loops

f := 0.0;

FOR i := 0 TO 5 BY 1 DO
	f := f + 1.0;
END_FOR


f:= 0.0;

FOR i := 0 TO 5 BY 1 DO
	f := f + 1.0;
	
	IF (f > 5) THEN
		EXIT;
	END_IF

END_FOR


// WHILE loops

i := 0;
F := 0.0;

WHILE f < 50 DO
	i := i +1;
	f := f + i;
	
	IF f > 5 THEN
		EXIT;
	END_IF

END_WHILE


// REPEAT loop

i := 0;
f := 0.0;

REPEAT
	i := i + 1;
	f := f + i;
UNTIL f >= 5
END_REPEAT

i := 0;
f := 0.0;

REPEAT
	i := i + 1;
	f := f + i;
	
	IF f > 5 THEN
		EXIT;
	END_IF
UNTIL f > 50
END_REPEAT


// Timer

D := FALSE;
TON_0(IN := TRUE, PT := T#3S);

IF TON_0.ET > T#1S THEN
	D:= TRUE;
END_IF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

After building the solution go to Activate Configuration, to configure the hardware:



Then login to the PLC



and run:



At "Real-Time", Online we can check the CPU usage:



We can toggle break points to debug



Run Through the program and check the values



We can go to a value and right values and use the button Write values



To reset the values we can logout then login and do a reset cold



We can notice that the timer has elapsed 10ms as set on the "Real-Time"



# Functions

Go to POU and add an item



Write the function;



Add to POU and check the results:



# Sample Problem

  1. A conveyor and a mechanism are used to sort red and black objects into their corresponding bins. The conveyor has a sensor at its beginning that is used to detect an object's color. When an object is detected, the conveyor's motor turns on. When an object is detected, an actuator may move its attached linkage to sort the object into its proper bin. Objects take at least 1 second to be sorted after being detected. After an object has been sorted, the conveyor's motor turns off. After an object has been sorted, if the actuator moved its attached linkage, it moves back to its default location.

In Structure Text (ST), write a program that implements the described system's steps.

Check your solutions against those in solution's POU_1 program.

MAIN:

PROGRAM MAIN
VAR
END_VAR
---------------------------
POU_1();
1
2
3
4
5

POU_1:

PROGRAM POU_1
VAR
	
	objectDetected : BOOL := FALSE;

	blackObject : BOOL := FALSE;	
	redObject : BOOL := FALSE;
	
	motorOn : BOOL := FALSE;
	actuatorMove : BOOL := FALSE;
	
	objectSorted : BOOL := FALSE;
	
	startTimer : BOOL := FALSE;
	TON_0 : TON;	

END_VAR
---------------------------
TON_0(IN := startTimer, PT := T#5S);

IF objectDetected = TRUE THEN
	
	objectSorted := FALSE;
	
	IF redObject = TRUE THEN
		actuatorMove := TRUE;
	END_IF	

	startTimer := TRUE;
	motorOn := TRUE;

END_IF

IF TON_0.ET > T#1S THEN	
	
	motorOn := FALSE;
	startTimer := FALSE;
	actuatorMove := FALSE;
	
	objectSorted := TRUE;
	
END_IF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# Ladder

Create a new project, and add a PLC and create a POU:



Declaring the input and output variables

PROGRAM POU
VAR
	A AT %I* : BOOL := FALSE;
	B AT %I* : BOOL := FALSE;
	C AT %I* : BOOL := FALSE;
	D AT %Q* : BOOL := FALSE;	
END_VAR
1
2
3
4
5
6
7

We can add labels and then toggle the state to a comment:



We can insert contacts, labels, coils...



We can also negate an object or choose to set like the 3rd coil, when in set mode it will stay on after the contact is released, OR RESET.

# Implementing boolean logic



Save it, activate, run mode, login to the PLC, create the port



We can prepare and assign values by clicking on the contacts.



# FBD - Function Block Diagram

Create a new project, and add a PLC and create a POU, add the POU(); statement to the MAIN (PRG) and go to the POU to program it.

PROGRAM POU
VAR
	
	i : INT := 0;
	f : REAL := 0.0;
	elapsed : TIME := TIME#0M0S0MS;
	today : DATE := DATE#2020-06-12;
	message : STRING := '';
	count : BYTE := 0;
	
	A : BOOL := FALSE;
	B : BOOL := FALSE;
	C : BOOL := FALSE;
	D : BOOL := FALSE;
	
	op1 : INT := 1;
	op2 : INT := 2;
	res : REAL := 0.0;
	test : BOOL := FALSE;
	
	TON_0 : TON;
	
END_VAR
---------------------------

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

We can see that the options to include in the network are different from the Ladder program.



TIP

Use the arrow key to navigate through the network.



Insert a block and choose by navigating or by searching for the box name.



Insert the MAX block, then include the inputs op1 and op2 and assignment to the variable res.



Using all the logic blocks we have:

"Printed as PDF"







# Logic



# For loop

For the FOR loop we can add a jump and negate the jump so if i is less than 5, then TRUE, then TRUE gets negated, and we don't jump.


Then we increment i with an ADD block in another network.


Then we execute what we want inside the for loop, in this case f.


Then we want to branch back to the start of our function, so we are going to jump to Start and set Start equal to TRUE.



# Timers

We can insert a timer from the library



Now we are going to test if elapse is greater than 3 seconds and assign the output to D. We can use the values of Q and ET in other operators.



# Function

We have to add a new POU and to the POU we add a function that returns a REAL value.





Include a box and look for the function name.



Save all, build the project, Activate, Login to the PLC, create the port and run.

To debug go to the first executable line and add a break point.



Every time the program runs the timer is incremented 10 ms. After 5s the timer sets the output to TRUE



# Ladder + Function Block diagrams

Create a new project, and a PLC and create a POU, include the POU call at the main program.

to assign a value to a variable we create a contact and give the value, and then we creat a coil and give the variable name.









# Comparing the logic of Ladder and FBD



# For loop



# Timers



# Function

Create the function MULT with Structured text



MULT:

FUNCTION MULT : REAL
VAR_INPUT
	in1 : REAL;
	in2 : REAL;
END_VAR
VAR
END_VAR
----------------------
MULT := in1 * in2;
1
2
3
4
5
6
7
8
9


Now activate configuration, this will put it on run mode, then login into the PLC, go to the top of the program and right click on the first run and toggle a breakpoint, then start the program and step through.

Or you can just run without breakpoints to execute the program.



As before, the timer is being incremented by 10 ms



And if we disable the brakpoint and run the program, at the end we can see that the variable turns TRUE.



# Visualization

As before, create a new project, and add a PLC and create a POU, add the POU(); statement to the MAIN (PRG) and go to the POU to program it.

If (A and B) are TRUE, then enable C (C becomes TRUE), if C is TRUE start the timer, if the timer has elapsed enable D.

PROGRAM POU
VAR
	
	A : BOOL := FALSE;
	B : BOOL := FALSE;
	C : BOOL := FALSE;
	D : BOOL := FALSE;
	
	TON_0 :TON;
	
END_VAR
1
2
3
4
5
6
7
8
9
10
11


Then we can add a visualization:





Create a Rounded Rectangle, give some measures and double click in the middle to give it a sintax, in this case A button. Then we can connect the A button to the A variable.



At OnMouseDown add the Toggle a variable to toggle POU.A. So now the block is connected to the input A.



Copy and paste the button, then change the text and the variable that it toggles. And add a couple of elipses.



On color variables set the toggle colors. Also change the color of the button.



Build the project, activate, login to the PLC etc...

If we toggle A, after 5 s B is toggled.