Seven segment displays are often thought of as a single display but still they consist of seven individual LEDs that have to be controlled separately. e.g. each LED should be turned on/off in order to form a decimal number from zero to nine. In this post I will present how this can be done in Process Simulate. To do so I will rely on SCL scripts for the implementation of the display controller.
To implement the controller we need to embed the display's truth table in the script. The truth table is a two-dimensional array mapping the input values (0-9) to a set of segments that should be turned on. Each segment is assigned a letter (a-g).
Our code should lookup the active segments in the truth table and turn them on based on the input. The rest should be turned off.
Let's define the table in the code. As it is always the case we need a static variable for the array:
VAR
truthTable : Array [0..9, 1..7] of BOOL; // truth table for the display panel
END_VAR
The first dimension is dedicated to the input decimal value. The second dimension is used for the state of the segments. In the case of decimal input '0' the table looks like this:
truthTable[0,1] := True; // segment 'a' is on
truthTable[0,2] := True; // segment 'b' is on
truthTable[0,3] := True; // segment 'c' is on
truthTable[0,4] := True; // segment 'd' is on
truthTable[0,5] := True; // segment 'e' is on
truthTable[0,6] := True; // segment 'f' is on
truthTable[0,7] := False; // segment 'g' is off
As for today (v2402) we don't have a way to initialize the array with a default value (not implemented yet) so this is the only way to do it. And this is what we do at time zero in our FB. We initialize the truth table like this:
IF SIM_TIME() = 0 THEN
// initialize truth table
...
END_IF;
Next we need a way to turn segments on/off. In PS this is based on kinematics. Kind of unexpected but each segment has a joint. The value of the joint makes the segment's geometry visible or not. The names of the joints are also defined in a table like this:
VAR
jointNames : Array [1..7] of STRING; // joint names
END_VAR
As you might expect joint names are also initialized at time zero.
#jointNames[1] := 'j_a'; // joint name for segment 'a'
#jointNames[2] := 'j_b'; // joint name for segment 'b'
#jointNames[3] := 'j_c'; // joint name for segment 'c'
#jointNames[4] := 'j_d'; // joint name for segment 'd'
#jointNames[5] := 'j_e'; // joint name for segment 'e'
#jointNames[6] := 'j_f'; // joint name for segment 'f'
#jointNames[7] := 'j_g'; // joint name for segment 'g'
Now that we have both tables defined and initialized it is time to implement the logic for the display controller. Believe it or not it is just a for loop iterating over the segments and then moving the joints based on the values in the truth table:
// turn segments ON/OFF based on the display's truth table
FOR #segment := 1 TO 7 DO
IF #truthTable[#in, #segment] THEN
JUMP_JOINT(NAME:=#jointNames[#segment], VALUE:=#OnValue);
ELSE
JUMP_JOINT(NAME:=#jointNames[#segment], VALUE:=#OffValue);
END_IF;
END_FOR;
ELSE
// clear the display
#clear(NAME:='HOME');
END_IF;
END_IF;
Here I am using an additional static variable (#last) to detect when there is a change in the input value (#in). This saves me unnecessary joint moves that ruin the performance. Also the on/off joint values are defined as constants:
VAR CONSTANT
OnValue : INT := 5; // joint ON value
OffValue : INT := 0; // joint OFF value
END_VAR
Lastly if the input value is out of range all segments are turned off by setting the pose to "HOME". This pose should be predefined on the display component. The MOVE_TO_POSE FB used here (static variable) allows us to change the pose.
In PS the display looks like this:
So far so good!
Now how do you multiply this?
Easy. Just copy/paste the display component and wire a different input signal to it.
And if you need to display a two digit number for example you'll also need some extra logic on top of the displays. I have put this extra logic on a separate resource. This is the brain decomposing the input decimal value into individual decimal values for each display.
Here is how this might be implemented:
FUNCTION_BLOCK "MAIN"
VERSION:1.0
VAR_INPUT
in : INT; // input decimal value (0-99)
END_VAR
VAR_OUTPUT
value1 : INT; // decimal value for the first display (0-9)
value2 : INT; // decimal value for the second display (0-9)
END_VAR
BEGIN
END_FUNCTION_BLOCK
Once you wire the output values to the correct display component the result looks like this:
You can expand this further to support three digit numbers or more.
I hope you find this article interesting. It is meant to showcase the power of the SCL script when more complex logic is needed.
Enjoy !
Comentários