CODESYS problems with edge detection (bounce) - edge-detection

I have a problem with my CODESYS program. I have three buttons, which are defined as input. For each button a number is stored. For example the number 1.
I have now created a program, which recognizes an edge on the button and stores the numerical value (2) of the button in an array. If you now press another button with the value (3), the value is also stored in a variable again. The two variables should be added together. 2 + 3 = 23. In my program I have the problem that if I press the button tester with the value 2, I get 22. This is wrong. I think the problem is due to the bruise of the push button. Several edges are detected. So I wanted to solve this software technically with a delay. Do you have any idea how I could program that?
CODE:
IF (PLC_PRG.calls[5].gpio = TRUE) THEN // edge detection on gpio
IF (counter = 0) THEN // counter for the first value
floorstorage2[0] := PLC_PRG.calls[5].message.floorstore[5]; // save button value in the array to calculate the total
counter := 1;
ELSE
floorstorage2[1] := PLC_PRG.calls[5].message.floorstore[5]; // save button value in the array to calculate the total
counter := 0;
END_IF
END_IF
IF (PLC_PRG.calls[6].gpio = TRUE) THEN // edge detection on gpio
IF (counter = 0) THEN // counter for the first value
floorstorage2[0] := PLC_PRG.calls[6].message.floorstore[6]; // save button value in the array to calculate the total
counter := 1;
ELSE
floorstorage2[1] := PLC_PRG.calls[6].message.floorstore[6]; // save button value in the array to calculate the total
counter := 0;
END_IF
END_IF
IF (PLC_PRG.calls[7].gpio = TRUE) THEN // edge detection on gpio
IF (counter = 0) THEN // counter for the first value
floorstorage2[0] := PLC_PRG.calls[7].message.floorstore[7]; // save button value in the array to calculate the total
counter := 1;
ELSE
floorstorage2[1] := PLC_PRG.calls[7].message.floorstore[7]; // save button value in the array to calculate the total
counter := 0;
END_IF
END_IF
GlobalVar.floorstorage := concat(floorstorage2[0],floorstorage2[1]); // Total of value 1 and value 2 (1 + 2 = 12)

You need to implement edge detection. Here is a code template you can use:
// Generate Oneshot Signal
VAR_INPUT
SIGNAL : BOOL; // Input
END_VAR
VAR
LATCH_SIGNAL : BOOL; // Latch
END_VAR
VAR_TEMP
OS_P_SIGNAL : BOOL; // Oneshot - Rising edge detection
OS_N_SIGNAL : BOOL; // Oneshot - Falling edge detection
END_VAR
//Code - Rising edge detection
OS_P_SIGNAL := SIGNAL AND NOT LATCH_SIGNAL;
LATCH_SIGNAL := SIGNAL;
//Code - Falling edge detection
OS_N_SIGNAL := NOT SIGNAL AND NOT LATCH_SIGNAL;
LATCH_SIGNAL := NOT SIGNAL;

I do not see any edge detection here
IF (PLC_PRG.calls[5].gpio = TRUE) THEN // edge detection on gpio
IF (counter = 0) THEN // counter for the first value
floorstorage2[0] := PLC_PRG.calls[5].message.floorstore[5]; // save button value in the array to calculate the total
counter := 1;
ELSE
floorstorage2[1] := PLC_PRG.calls[5].message.floorstore[5]; // save button value in the array to calculate the total
counter := 0;
END_IF
END_IF
You check if the button is pressed. It means while you are holding this button this condition is true. It means that counter change it's state on every PLC cycle while you are holding the button. It means that if when you release button PLC made even number of cycles nothing will change and if odd number f cycles it will change. Here is how you detect a rising edge.
VAR
xSignal, xSignalM: BOOL;
END_VAR
IF xSignal AND NOT xSignalM THEN
// Raising edge is here
END_IF
xSignalM := xSignal;
This way condition will work only one PLC cycle and everything will be ok. So your code would look like this.
VAR
M1, M1, M3: BOOL;
END_VAR
IF (PLC_PRG.calls[5].gpio AND NOT M1) THEN // edge detection on gpio
IF (counter = 0) THEN // counter for the first value
floorstorage2[0] := PLC_PRG.calls[5].message.floorstore[5]; // save button value in the array to calculate the total
counter := 1;
ELSE
floorstorage2[1] := PLC_PRG.calls[5].message.floorstore[5]; // save button value in the array to calculate the total
counter := 0;
END_IF
END_IF
M1 = PLC_PRG.calls[5].gpio;
IF (PLC_PRG.calls[6].gpio AND NOT M2) THEN // edge detection on gpio
IF (counter = 0) THEN // counter for the first value
floorstorage2[0] := PLC_PRG.calls[6].message.floorstore[6]; // save button value in the array to calculate the total
counter := 1;
ELSE
floorstorage2[1] := PLC_PRG.calls[6].message.floorstore[6]; // save button value in the array to calculate the total
counter := 0;
END_IF
END_IF
M2 = PLC_PRG.calls[6].gpio;
IF (PLC_PRG.calls[7].gpio AND NOT M3) THEN // edge detection on gpio
IF (counter = 0) THEN // counter for the first value
floorstorage2[0] := PLC_PRG.calls[7].message.floorstore[7]; // save button value in the array to calculate the total
counter := 1;
ELSE
floorstorage2[1] := PLC_PRG.calls[7].message.floorstore[7]; // save button value in the array to calculate the total
counter := 0;
END_IF
END_IF
M3 = PLC_PRG.calls[7].gpio;
Or you can use R_TRIG
VAR
RT1:R_TRIG;
END_VAR
R1(CLK := PLC_PRG.calls[5].gpio);
IF (R1.Q) THEN
IF (counter = 0) THEN
floorstorage2[0] := PLC_PRG.calls[5].message.floorstore[5];
counter := 1;
ELSE
floorstorage2[1] := PLC_PRG.calls[5].message.floorstore[5];
counter := 0;
END_IF
END_IF

I would implement the whole logic in a more object oriented way.
First, we define a Button.
Declaration part:
FUNCTION_BLOCK FB_Button
VAR
bSignal AT%I* : BOOL;
IButtonHandler : I_ButtonHandler;
fbPushTimer : TON;
fbTrig : R_TRIG;
sValue : STRING;
END_VAR
Implementation part:
IF IButtonHandler = 0
THEN
RETURN;
END_IF
fbPushTimer(IN:= bSignal, PT:=T#50MS);
fbTrig(CLK:=fbPushTimer.Q);
IF fbTrig.Q
THEN
IButtonHandler.onPush(sValue);
END_IF
The button has four properties:
1st Property:
Declaration part:
PROPERTY getValue : String
Implementation part:
getValue := sValue;
2nd Property:
Declaration part:
PROPERTY isPushed : BOOL
Implementation part:
isPushed := bSignal;
3rd Property:
Declaration part:
PROPERTY setPushHandler : I_ButtonHandler
Implementation part:
IButtonHandler := setPushHandler;
4th Property:
Declaration part:
PROPERTY setValue : String
Implementation part:
sValue := setValue;
Then we define the interface.
INTERFACE I_ButtonHandler
and add the interface method:
METHOD onPush
VAR_INPUT
sValue : STRING;
END_VAR
At last we define the handler.
Declaration part:
FUNCTION_BLOCK FB_ButtonHandler IMPLEMENTS I_ButtonHandler
VAR_OUTPUT
floorstorage : STRING;
END_VAR
The handler has two methods:
1st Method:
Declaration part:
METHOD onPush
VAR_INPUT
sValue : STRING;
END_VAR
Implementaion part:
floorstorage := concat(floorstorage,sValue);
2nd Method:
Declaration part:
METHOD reset
Implementation part:
floorstorage := '';
Now we need to init the buttons and call them in main.
Main declaration part:
PROGRAM MAIN
VAR
aButtons : ARRAY[1..10] OF FB_Button;
fbButtonHandler : FB_ButtonHandler;
i : UINT;
bInit : BOOL;
END_VAR
Implementation part:
IF NOT bInit
THEN
FOR i := 1 TO 10 DO
aButtons[i].setPushHandler := fbButtonHandler;
aButtons[i].setValue := UINT_TO_STRING(i);
END_FOR
bInit := TRUE;
END_IF
FOR i := 1 TO 10 DO
aButtons[i]();
END_FOR
You can choose your own button value depending on the button.
For simplicity, I assigned the loop index as the value of the button.
Each time the button is pushed, the method onPush gets called only once after 50ms.
When you want to access the value of floorstorage you simply call fbButtonHandler.floorstorage to assign it to another var.
By doing so you reach a stronger encapsulation and data protection for your variable than declaring it as global.

Related

How to determine the maximum nth occurrence of an event at a given duration?

How can it be determined that the time interval of the nth event of an event is not more than a certain period of time?
For example, an event can occur up to ‍5 ti‍mes every 10 minutes.
In STL we can use this
VAR
counter:CTU;
timer:TON;
Event:BOOL;
bMaxEventHappend:BOOL;
tElapsedTime:TIME;
END_VAR
counter(CU:=Event);
IF counter.CV=1 THEN
timer(IN:=TRUE);
END_IF
IF counter.CV=5 THEN
bMaxEventHappend:=TRUE;
counter(Reset:=TRUE);
END_IF
//resetProcess
IF counter.CV=1 AND timer.et>=T#10M THEN
timer(IN:=FALSE);
counter(Reset:=TRUE);
ELSIF counter.CV=2 THEN
tElapsedTime:=timer.et;
IF timer.ET-tElapsedTime >=T#10M THEN
timer(IN:=FALSE);
counter(Reset:=TRUE);
END_IF
ELSIF counter.CV=3 THEN
tElapsedTime:=tElapsedTime+timer.et;
IF timer.ET-tElapsedTime >=T#10M THEN
timer(IN:=FALSE);
counter(Reset:=TRUE);
END_IF
ELSIF counter.CV=4 THEN
tElapsedTime:=tElapsedTime+timer.et;
IF timer.ET-tElapsedTime >=T#10M THEN
timer(IN:=FALSE);
counter(Reset:=TRUE);
END_IF
ELSIF counter.CV=5 THEN
tElapsedTime:=tElapsedTime+timer.et;
IF timer.ET-tElapsedTime >=T#10M THEN
timer(IN:=FALSE);
counter(Reset:=TRUE);
END_IF
END_IF
This method does not seem to be optimal for achieving the desired.
Is there another optimal method?
Any help would be appreciated.
Unfortunately, I was not able to understand what you are trying to accomplish exactly. If my answer does not give your ideas, just update your question and explain what you want to do.
TYPE MY_EVENT : STRUCT
TimeStart : TIME; (* Time when event was started *)
TimeEnd : TIME; (* Time when event was ended *)
TimeWorked: TIME; (* Time that event was working *)
Index: USINT; (* Index in array *)
END_STRUCT
END_TYPE
PROGRAM PLC_PRG
VAR
arEvents: ARARY[1..5] OF MY_EVENTS; (* Last 5 events *)
xEventStart: BOOL; (* Event started *)
xEventStartM: BOOL; (* Memmory of event started for edge detection *)
stEvent: MY_EVENT; (* Current event *)
usiCount: USINT := 1; (* Current index *)
usiFor: USINT := 1; (* FOR iterator *)
stSearchEvent: MY_EVENT; (* Searched event *)
END_VAR
(* Raising edge of event. Event starts *)
IF xEventStart AND NOT xEventStartM THEN
stEvent.TimeStart := TIME();
END_IF;
(* Falling edge of event. Event ends *)
IF NOT xEventStart AND xEventStartM THEN
(* Finalize event *)
stEvent.TimeEnd := TIME();
stEvent.Index := usiCount;
stEvent.TimeWorked := (stEvent.TimeEnd - stEvent.TimeStart);
(* Save event in array *)
arEvents[usiCount] := stEvent;
IF usiCount = 5 THEN
usiCount := 1;
ELSE
usiCount := usiCount + 1;
END_IF;
stEvent.TimeEnd := T#0S;
stEvent.TimeStart := T#0S;
stEvent.TimeWorked := T#0S;
END_IF;
xEventStartM := xEventStart;
(* Current event is longer than 1 minute *)
IF (TIME() - stEvent.TimeStart) > T#1M THEN
// Do something
END_IF;
(* Get longest event out of last 5 events *)
stSearchEvent.TimeWorked := T#0S;
FOR usiFor TO 5 DO
IF (stSearchEvent.TimeWorked < arEvents[usiFor].TimeWorked) THEN
stSearchEvent := arEvents[usiFor];
END_IF;
END_FOR;
END_PROGRAM
This is an example how to store last 5 events in array and then how to know that current event is too long and how to find longest event in array.
Finally, I found this method for this problem:
PROGRAM MAIN
VAR
trigger:r_Trig();
event: BOOL;
tDuration:TIME:=T#10M;
tTimeInit: TIME:=TIME()-tDuration;
aTime:ARRAY[1..5] OF TIME:=[tTimeInit,tTimeInit,tTimeInit,tTimeInit,tTimeInit];
alarm:BOOL;
i:INT;
END_VAR
trigger(clk:=event);
IF trigger.Q THEN
IF (TIME()-aTime[i+1]<tDuration) THEN
alarm:=TRUE;
END_IF
aTime[i+1]:=TIME();
i:=(i+1) MOD 5;
END_IF
and seems it works(have been tested for tDuration:TIME:=T#10s;)

How to find the second largest value with Pascal

I have a Problem to Show the second largest value.
Here is the Code
program testeFeldZweitMax (input, output);
{ testet die Funktion FeldZweitMax }
const
FELDGROESSE = 10;
type
tIndex = 1..FELDGROESSE;
tFeld = array [tIndex] of integer;
var
Feld : tFeld;
i : integer;
function FeldZweitMax (var inFeld : tFeld) : integer;
var
Maximum: integer;
j : tIndex;
begin
Maximum := inFeld[1];
for j := 2 to FELDGROESSE do
if inFeld[j] > Maximum then
Maximum := inFeld[j];
FeldZweitMax := Maximum
end;
begin { Testprogramm }
writeln('Bitte geben Sie ', FELDGROESSE, ' Zahlen ein:');
for i := 1 to FELDGROESSE do
read (Feld [i]);
writeln('Die zweitgroesste Zahl ist ', FeldZweitMax (Feld), '.');
end. { testeFeldZweitMax }
As you can see the Code Show me only the largest value. I Need some help with showing the second largest value.
var
Maximum, ZweitMax: integer;
j : tIndex;
begin
Maximum := inFeld[1];
ZweitMax := inFeld[2];
for j := 1 to FELDGROESSE do
begin
if inFeld[j] < Maximum then
inFeld[j] := Maximum;
Maximum := ZweitMax;
ZweitMax := inFeld[j];
FeldZweitMax := ZweitMax
end
end;
It doesn't work perfectly. Some suggestions for me?
Consider that you have (at some point) the values Maximum > ZweitMax (f.ex. 5 and 2 respectively).
The next value (x) to evaluate might be
a) x > Maximum
b) x > ZweitMax (but less than Maximum)
c) x < ZweitMax
In case a) Maximum should become x and ZweitMax should become previous Maximum
In case b) Maximum should remain and ZweitMax should become x
In case c) no change to Maximum and ZweitMax (IOW, no action required)
A couple of hints:
Initialize both Maximum and ZweitMax to the smallest possible value (according to the type) before you start to evaluate the actual inputted values.
In case a) set ZweitMax to previous Maximum before assigning the new value to Maximum.

Slow pascal graph unit

It's actually my first time in here, so..
What's the problem: standard graph module is too damn slow in drawing.
I have an institute task to make a big program with modules on pascal. Program has several parts but i'm interested in graphical one. I have something like counter (number) in the left corner of the screen and i need it to update fast. But I see every pixel it fills with color, lol.
I'm using Free Pascal 2.6.4.
Asking for some ideas or other ways to draw in a command window fast.
program graphical;
uses
wincrt, graph, querystring, kbd, Timer, sysutils;
const
speedX1 = 0;
speedY1 = 0;
var
//draw part
gd, gm, error, tw, th, i: integer;
speedX2: integer = 10;
speedY2: integer = 10;
speedSize: word;
speedImage: pointer;
size: word;
//qstring part
qrec: qstr;
qtext: AnsiString;
current, ch: char;
//keyboard part
kbrd: kb;
//speedometer part
counter: word = 0;
time: word;
speed: word;
//debug part
c: string;
t: comp;
procedure draw; //screens text
begin
qtext := copy(qrec.text, qrec.qpointer, Length(qrec.text) - qrec.qpointer + 1);
outTextXY(getMaxX div 2, getMaxY div 8, qtext);
end;
begin
//graphic initialization
//gd := detect;
gd := VGA;
gm := VGAHi;
initgraph(gd, gm, '..\bgi');
//checking for errors
error := graphResult;
if (error <> grOk) then
begin
writeln('800x600x256 is not supported');
halt(1);
end;
//querystring initialization
qInit(qrec);
//keyboard initialization
initKeyboard(kbrd);
//timer initialization
TimerOn;
time := 0;
//drawing
setTextStyle(defaultFont, horizDir, 8);
draw;
drawKeyboard(kbrd);
current := getCurrent(qrec);
randomize;
speedX2 := 200;
speedY2 := 100;
repeat
//on timer events
if (isTimer) then
begin
size := ImageSize(speedX1, speedY1, speedX2, speedY2);
GetMem(speedImage, size);
GetImage(speedX1, speedY1, speedX2, speedY2, speedImage^);
PutImage(speedX1, speedY1, speedImage^, 1); FreeMem(speedImage, size);
inc(time);
speed := round(counter/time/25*60);
speed := time;
outTextXY(0, 0, IntToStr(speed));
end;
if KeyPressed then
begin
ch := readkey;
if (ch = #0) then
ch := readkey;
end;
if (UpCase(ch) = UpCase(current)) then
begin
drawKeyboard(kbrd);
draw;
current := getCurrent(qrec);
inc(counter);
end
else
if (counter > 0) then
dec(counter);
until (ch = #27) or (getLength(qrec) < 0);
closegraph;
end.
What I see from the code:
drawKeyboard is called on each iteration, which might not be necessary
for each timer event, memory is allocated and released again, which normally is a rather costly operation. The memory needed seems to be of constant size, so try to move allocation and deallocation out of the loop.
Without fixing those, you'd probably have the same problems with other graph libraries too, so give it a try.

How to Correctly show component sizes in inno component page

I am not getting the correct memory sizes of all the component selected in the component page.
Please give me a solution how the total memory selected for all the components should be correct?
The memory size is displayed on the label at the bottom of the page.
If in the [files] section check flags are used, not all flags can be processed by Inno-setup. Especially if you created check flags which rely on the [code] section.
In my setup a created an array of all files.
In this record I have a selected flag, and a filesizeflag.
An example looks like:
files[index].selected := // true or false depending on your wizard flow
files[index].filesize := {#filesize(..)} // on the .. the source file, including path
Before calling the dir page wizard page you go through a procedure which counts the file sizes and adds them to the file size already counted.
Yhe file size already counted is for every setup different, depending how much code you have in the code section is my experience.
My example code for counting the space is a good start (I hope)
Procedure GetSetUsedDiskSpace();
// This procedure counts an displays the used disk space
{}
var
TempS, SearchString, NumberString, diskspace : String;
Position, p2 : Integer;
Tot_disk, TempSpace : Longint;
{}
begin
TempS := InitDiskSpace; // wizardform.DiskSpaceLabel.Caption;
SearchString := 'MB';
Position := pos(SearchString, TempS);
NumberString := copy(TempS, 1, Position-2); // exclusive the space before the MB
p2 := 0;
repeat // find the space before the number
p2 := pos(' ', NumberString);
NumberString := copy(NumberString, p2 + 1, length(NumberString) - p2);
until p2 = 0;
p2 := pos(',', NumberString);
if (p2 = 0) then
begin // Some languages use the period as a decimal separator
p2 := pos('.', NumberString);
end;
if (p2 > 0) then
begin
NumberString := copy(Numberstring, 1, p2-1) + copy(NumberString, p2+1, 1);
// If there is a need to more shifting we add some code
end;
TempSpace := StrToInt(NumberString);
TempSpace := TempSpace * 1024 * 1024; // Conversion to bytes
TempSpace := TempSpace / 10; // We replaced the decimal separator once
CountSpace; // Count the space for our selection
Tot_disk := UsedDiskSpace + TempSpace; // The total in bytes
UsedDiskSpace := Tot_disk; // We need this for the control panel
Tot_disk := Tot_disk / 1024; // The total in kilobytes
Tot_disk := Tot_disk / 1024; // The total in MB
diskspace := IntToStr(Tot_disk);
TempS := SetupMessage(msgDiskSpaceMBLabel);
StringChangeEx(TempS, '[mb]', diskspace, True);
WizardForm.DiskSpaceLabel.Caption := TempS;
end;

Target (variable "") is not a signal error in VHDL

I have this piece of code
function func (k1, k2 : in bit_vector) return bit_vector is
variable result : bit_vector(1 to 32);
begin
for i in 0 to 31 loop
result(i) <= k1(i);
end loop;
return result;
end func;
I get this error :
target (variable "result") is not a signal
I know I need to change the type of result but I don't know what it should be.
Thanks.
When assigning to a variable use := as:
result(i) := k1(i);
Assign with <= is for assign to signal.
The range of result (1 to 32) does not match the range in the loop (0 to 31), so first assign in the loop (result(0) := k1(0)) will cause in a range error. Fix this by changing either result or loop range.

Resources