On delay Timer routine

A

Thread Starter

Anand

On Delay Timer for PLC's.

The following is the algorithm/program for timers in PLC's specifically
for linux PLC's as it uses the jiffies variable which is available in
linux OS. the benefit of this algorithm is that it gives AB like
programming flexibility for the PLC front end design. The same logic can
be used in STL, FBD, LAD etc and user needs no special programming
skills (once final product is ready).

Please note that at this stage this is a preliminary document. The
author's knowledge of C dates to 1992, basic used intermittently and
database used in 1998. Thus at this moment the usage has several
combinations and many shortcomings in syntaxt and semantics. variable
declarations are required, and the program may have to leave the use of
database for time of executions sake. But algorithm is solid!

the database tables can be changed to Files but further "refreshment" of
the C section of authors Brain needs to be done. Also further refinement
of the logic is in progress as the timer enable and disable is
frequently set now (frequent execution of the functions) and better
means to enable it once when logic becomes true and disable once when
the conditions become untrue is being worked on.

Similar logic can also be applied for retentive timers, except that
Disable is invoked only when the reset bit for the particular timer is
set. in other words, the disable logic moves out of the else condition
to another set of if conditions.

Notes:
1.the timers are of three types, based on their time base of 0.01
seconds, 0.1 seconds and 1 second.
2.The timers are stored in a database. Each time base has its own
database. The three tables used are t001t for 0.01 second time base
timers, t010t for 0.1 second timebase timer and t100t for 1 second time
base timers.
3.The fields of all these tables are
Num the timer number (integer).
En Enable bit (boolean)
T Timing bit (boolean)
D Done bit (boolean)
Pre The preset value (integer)
Acc The Accumulated value (integer)
4.The logic uses the jiffies value defined in <linux/sched.h> and the
default Hz setting of 100Hz is assumed. The main program of the Linux
PLC can also set the frequency to 100Hz, but this may require
recompiling of all modules.
5.Several integers that are used are
t001b the intermediate time base for 0.01 second timers.
t010b the intermediate time base for 0.1 second timers.
t100b the intermediate time base for 1.0 second timers..
tempj A long unsigned value used for jiffy changeover check

Algorithm flow:
1.. Initially the intermediate values are set to zero.
2.. At each 100Hz signal (jiffies increments), the 0.01 second timer
base is incremented.
3.. Then the database is used and the table t001t which has data
regarding the 0.01 second timers is updated. All the timers with enable
bit set are incremented provided they arenot done (i.e. acc value = preset values).
4.. If any timer has acc value=preset value on updation then the
done bit is set and timing bit is reset.
5.. When 10 such cycles have occured then the t001b/10 = |t001b/10|.
This is basically a convention i used in a old version of basic and
looking for an alternative value in C. what we do here is see that
truncated value of t001b/10 and t001b/10 are same or reminder=0 will
also do.
6.. The database cycle for 0.1 second timer as described in step 3
follows.
7.. the step 5 and 6 are then done for the 1 second timers.
8.. Functions tent001b, tent010b and tent100b are the three functions
defined for doing the enabling part of the timer with time bases 0.01,
0.1 and 1 seconds respectively. This function needs to be executed once
when the logic conditions become true for a particular timer.
9.. Functions tdbt001b, tdbt010b and tdbt100b are the three functions
defined for doing the disbling part of the timer with time bases 0.01,
0.1 and 1 seconds respectively. This function needs to be executed once
when the logic conditions become false for a particular timer.

In the main PLC program when the PLC is initialized or started the
values of t001b, t010b, t100b is set to zero and a tempj value set equal
to jiffies.

Initialization done once by main program when the PLC starts up.
t001b=0;
t010b=0;
t100b=0;
tempj=jiffies;

Once the initialization is done then the timer routine is started in the
main PLC program.

if tempj<>jiffies then
t001b=t001b+1; /*increment 0.01 second timer*/
update too1t set acc=acc+1 where en=1 and pre>acc
update t001t set d=1 where pre=acc
update t001t set t=0 where pre=acc
if t001b/10=|t001b/10| then /*check if ten 0.01 seconds have
elapsed*/
update t010t set acc=acc+1 where en=1 and pre>acc
update t010t set d=1 where pre=acc
update t010t set t=0 where pre=acc
t010b=t010b+1;
endif;
if t010b/10=|t010b/10| then /*check if ten 0.1 seconds have
elapsed*/
t100b=t100b+1;
update t100t set acc=acc+1 where en=1 and pre>acc
update t100t set d=1 where pre=acc
update t100t set t=0 where pre=acc
endif;
tempb=jiffies;
endif;

Function tent001b(int fno) \* This function is performed when a timer
with base 0.01 seconds is enabled by the logic*/

Update t001t set en=1,t=1 where no=fno;
return;

Function tent010b(int fno) \* This function is performed when a timer
with base 0.1 seconds is enabled by the logic*/

Update t010t set en=1,t=1 where no=fno;
return;

Function tent100b(int fno) \* This function is performed when a timer
with base 1 second is enabled by the logic*/

Update t100t set en=1,t=1 where no=fno;
return;

Function tdbt001b (int fno) \* This function is performed when a timer
with base 0.01 second is disabled by the logic*/
Update t001t set en=0,t=0, d=0, acc=0, where no=fno;
return;

Function tdbt010b (int fno) \* This function is performed when a timer
with base 0.1 second is disabled by the logic*/
Update t010t set en=0,t=0, d=0, acc=0, where no=fno;
return;

Function tdbt100b (int fno) \* This function is performed when a timer
with base 1 second is disabled by the logic*/
Update t100t set en=0,t=0, d=0, acc=0, where no=3Dfno;
return;

In the user program the timer is either enabled by the logic or
disabled. In this case the user program performs the routine:
the following shows the 0.01 base timer number 125 being enabled or
disabled .

If ......... then /* if conditions that enable the timer*/

tent001b(125); /* executed if the conditions are true and enables is
set*/
else
tdbt001b(125) /*executed if the enable is not set*/
endif;



/* Please be patient as more time is required to make this into a final
product*/

All Comments and criticisms are welcome.

Anand Krishnan Iyer.

_______________________________________________
LinuxPLC mailing list
[email protected]
http://linuxplc.org/mailman/listinfo/linuxplc
 
Anand:
> On Delay Timer for PLC's.

Are you aware of the existing timer routines in lib/logic ? They don't do
nearly as much as yours, but they should make a reasonable starting
point...

> Please note that at this stage this is a preliminary document. The
> author's knowledge of C dates to 1992, basic used intermittently and
> database used in 1998. Thus at this moment the usage has several
> combinations and many shortcomings in syntaxt and semantics.

As a way of writing algorithms, it's actually quite neat!

> Also further refinement of the logic is in progress as the timer enable
> and disable is frequently set now (frequent execution of the functions)
> and better means to enable it once when logic becomes true and disable
> once when the conditions become untrue is being woked on.

I expect that the functions will just have to be called on the rising edge.
(The lib/logic/timer.c functions check the enable bit first thing and
return quickly, so it might not be worth it unless you have the rising edge
anyway.)

> Notes:
> 1.the timers are of three types, based on their time base of 0.01
> seconds, 0.1 seconds and 1 second.

It's probably not worth the trouble to have three different timer types -
the only reason to do it is to save memory, but these days memory is a lot
cheaper than programmer time.

Also, Linux already has a data type for storing time down to the
microsecond, so it makes sense to use that. (See gettimeofday(2).)

> 2.The timers are stored in a database.

lib/logic/timer.h simply declares a timer type - it's up to whoever's using
them to declare an array (or to declare individual ones as they need).

> 3.The fields of all these tables are
> Num the timer number (integer).
> En Enable bit (boolean)
> T Timing bit (boolean)
> D Done bit (boolean)
> Pre The preset value (integer)
> Acc The Accumulated value (integer)

The lib/logic/timer.h only handles En and Acc.

The others can be built into a wrapper around it. (For instance, currently
the preset value must be given to the function checking whether the timer
is done or not; this could be built into a wrapper type.)

Alternately, some of them can be added to lib/logic/timer.h - there's
currently 31 bits padding in the struct, or we can just make it bigger.

Another thing the wrapper should offer is to sort the timers by remaining
time and only check the first one. (Using a priority queue, operations can
be complexity O(log n) in the number of running timers, so it's not as bad
as it sounds.)

> 4.The logic uses the jiffies value defined in <linux/sched.h> and the
> default Hz setting of 100Hz is assumed.

Not sure what the resolution of gettimeofday(2) is - would have to look it
up somewhere - but it's probably better to use that interface even if we do
have to fix it.

> 2.. At each 100Hz signal (jiffies increments), the 0.01 second timer
> base is incremented.
> 3.. Then the database is used and the table t001t which has data
> regarding the 0.01 second timers is updated. All the timers with enable
> bit set are incremented provided they arenot done (i.e. acc value =
> preset values).

Sounds unnecessarily CPU-intensive. Easier to just store the time when the
timer should be finished (or when the timer was zeroed) and wait quietly
until it happens.

Disabled timers do store elapsed time, but running timers store start time.
That makes the enable/disable functions more complicated, but they won't be
called all that often.

> 4.. If any timer has acc value=preset value on updation then the done
> bit is set and timing bit is reset.

Sorting them by remaining time should speed that up a lot.

> 5.. When 10 such cycles have occured then the t001b/10 = |t001b/10|.
> This is basically a convention i used in a old version of basic and
> looking for an alternative value in C. what we do here is see that
> truncated value of t001b/10 and t001b/10 are same or reminder=0 will
> also do.

To check remainder is zero: (t001b % 10 == 0)

C has shortcuts for powers of 2, but not for other numbers. (It'll use them
automatically anyway if it can.)


Basically, my philosophy is that as little as possible should be done every
scan; and the kernel already updates its own time counter, so we can just
ask it what time it is when we need to know.

Hope that makes sense - and sorry about all the criticism, be sure to
criticise back please!

Jiri
--
Jiri Baum <[email protected]>
http://www.csse.monash.edu.au/~jiribvisit the MAT LinuxPLC project at http://mat.sourceforge.net

_______________________________________________
LinuxPLC mailing list
[email protected]
http://linuxplc.org/mailman/listinfo/linuxplc
 
<< Subj: LinuxPLC: On delay Timer routine
Date: 7/12/01 2:41:04 PM Eastern Daylight Time
From: [email protected] (Anand)
On Delay Timer for PLC's.

The following is the algorithm/program for timers in PLC's specifically for
linux PLC's as it uses the jiffies variable which is available in linux OS.
the benefit of this algorithm is that it gives AB like programming
flexibility for the PLC front end design.

<algorithms and pseudo-code snipped>

All Comments and criticisms are welcome.

Anand Krishnan Iyer.
>>

Anand et al,

I have several issues with the "Update timers in the Timers table" approach to timer management. ISTR this issue before on this list, but it may have been somewhere else.

Sorry if this is already in the code, as I have only been following postings and haven't had a chance to actually download and examine the code base.

And perhaps Anand only meant the timers he describes for use only for people who wish to emulate PLCs as closely as possible, kinks and all. I, on the other hand, am of the "can be like a PLC, only better" camp.

I believe it is far preferable to simply record the "end of interval" clock tick and have one "universal clock tick" (I guess this is a jiffy?).

The reasons are:

1) Only one timer comparison is needed at a time, being the time closest to arrival. If the time hasn't reached that end of interval, it has certainly not reached anyone elses. (After all, the only timer whose status changes is the one whose time has elapsed).

2) Timer accuracy is independent of time base. Whether you choose to use the ,01, .1, or 1 second timers, the timeout will remain accurate to the master tick (jiffy?). e.g. in the multiple clock tick scheme (.01 tick, .1 tick, and 1 second tick) if you set the 1 second timer halfway between 1 second pulses you lose or gain .5 second on the "true interval" time.

3) End of interval often indicates a critical change of state somewhere. In the multiple timebase scenario, you may well have a pile of events coming true at the exact same time. If I have 100 events that are using a 1 second timebase, they have a far lesser chance at "hitting" at the same time if their end times were relative to their own individual "system ticks" rather than the quantized 1 second tick.

4) (low level stuff) Timer processing overhead in the table version is in a "system overhead" part of the cycle. In the sorted arrival time scheme, the queuing overhead is part of the calling routine's time. (In fact, if it is not one of the next few events to arrive, you can "take your time" queuing it up. So even in a simple linked list of timer completions, you needn't scan all the way to your insertion point during the call. Just scan a few positions closer each iteration. But now we're getting to low level implementation :))

5) If you still want to do the .01, .1, 1 second tick scenario as Anand described in the section of his post (clipped from my quote) "Algoritm Flow" , that can still be
accomodated, by creating the .01 second timer that requeues itself automatically on the master list and doing the table update (and other timebase updates) on that.

Oops, it's getting late around here, better wrap it up...

Regards to all,

Rufus

_______________________________________________
LinuxPLC mailing list
[email protected]
http://linuxplc.org/mailman/listinfo/linuxplc
 
RufusVS:
> I have several issues with the "Update timers in the Timers table"
> approach to timer management. ISTR this issue before on this list, but it
> may have been somewhere else.

Probably was.

> I believe it is far preferable to simply record the "end of interval"
> clock tick and have one "universal clock tick" (I guess this is a
> jiffy?).

Yes, that's the approach the lib/logic/timer.c code takes - it records when the would be zero. If you use the timers in countdown mode (and you can), this is exactly what you're proposing.

> 1) Only one timer comparison is needed at a time, being the time closest
> to arrival. If the time hasn't reached that end of interval, it has
> certainly not reached anyone elses. (After all, the only timer whose
> status changes is the one whose time has elapsed).

This is not yet done, for the simple reason that the timers are individual variables. However, if someone implements a `bunch of timers' data type,
this should definitely be done.

> 4) (low level stuff) Timer processing overhead in the table version is in
> a "system overhead" part of the cycle. In the sorted arrival time
> scheme, the queuing overhead is part of the calling routine's time. (In
> fact, if it is not one of the next few events to arrive, you can "take
> your time" queuing it up. So even in a simple linked list of timer
> completions, you needn't scan all the way to your insertion point during
> the call. Just scan a few positions closer each iteration. But now we're
> getting to low level implementation :))

Use a priority queue - insertion is O(log n) and so is removing the first timer. (Removing other timers is a bit trickier, but it can be done. With
some bookkeeping, it can even still be O(log n).)

BTW, are you on the mat-devel list?

Jiri
--
Jiri Baum <[email protected]>
http://www.csse.monash.edu.au/~jiribvisit the MAT LinuxPLC project at http://mat.sourceforge.net

_______________________________________________
LinuxPLC mailing list
[email protected]
http://linuxplc.org/mailman/listinfo/linuxplc
 
> BTW, are you on the mat-devel list?

What is the mat-devel list?
I have been on the list for some time, but my hard disk crashed some time back and a lot of mails were lost. Currently due to some problems with my ISP, I have to do STD to connect which is costlier. So am not surfing that much. Anyway a visit to the site and downloading is necessary. When will the CVS be available. My mailbox is 1 MB so I will have to download it from some site.

Timer clarifications:
a.. First of all, I agree that my first aim was to provide at least the minimum functionality as per PLC's made available to the user. In case we are to replace a unit with an existing PLC, then the reprogramming effors will be reduced.
b.. I believed that in case there are hundreds of timers, then waiting etc. would lead to more program time. Multiple uses of the acc value could also complicate the issue.
c.. Secondly, having a database or a binary file will make multiple types of programming like STL, LAD, FBD etc. easier. Whereby we can plan to make the Linux PLC IEC-61131-3 compliant later on. This i strongly believe ties to the success of the project.
d.. I suppose we can remove the base 0.1 seconds and 1 second timers and have 1 timer with 0.01 second time base and all preset values can be manipulated as per user requirements.
e.. All process controls and machine Automation use the timer timing, timer done and the timer enabled bit several times in their logic. So these bits were provided.
f.. I picked up the use of jiffies from Alessandro Runbini's book on Linux Device Drivers published by O'Reilly (chapter 6 on flow of time).
g.. I suppose I will have to set the too1b bits to zero after certain time in order to avoid overflow. Lot of work is still pending. And I have to brush up my C. thanks for the help that has been provided. Also planning to make use of binary files instead of database if time permits during this weekend. have a couple of interviewsin the next week. So am a bit tied up.
Anand

Next algorithm is the totalizer block.

ip is the value to be totalized. Samples are taken every 10 milliseconds. flu indicates weather the input flow is in per hour units (lph, kg/hr etc.) or per minute units (lpm, kg/min etc.), or per second units (lps, Kg/s etc.) flu is 1,2 and 3 respectively for these conditions. the Val stores the totalized value.

function total(long ip, val, integer flu,)
if ip < 0 then ip=3D0; /* there is no negative flow */
begin
if flu=3D1
val=3Dip/360000 +val;
endif
if flu=3D2
val=3Dip/6000 + val;
endif;
if flu=3D3
val=3Dip/100 + val;
endif;
end;

Again here having a file to store the ip references, flu values and the =
totalized values can lead to a more flexible code.



_______________________________________________
LinuxPLC mailing list
[email protected]
http://linuxplc.org/mailman/listinfo/linuxplc
 
Anand:
> Timer clarifications:

> a.. First of all, I agree that my first aim was to provide at least the
> minimum functionality as per PLC's made available to the user. In case we
> are to replace a unit with an existing PLC, then the reprogramming effors
> will be reduced.

Makes sense.

> b.. I believed that in case there are hundreds of timers, then waiting
> etc. would lead to more program time. Multiple uses of the acc value
> could also complicate the issue.

I'm not sure what you mean here?

Certainly in the case of hundreds of timers, it makes sense to only check one each time through (the one that will expire first) rather than all of them.

On the other hand, if you have ladder logic that has to be scanned all the way through each time anyway, there's not much point...

> c.. Secondly, having a database or a binary file will make multiple types
> of programming like STL, LAD, FBD etc. easier. Whereby we can plan to
> make the Linux PLC IEC-61131-3 compliant later on. This i strongly
> believe ties to the success of the project.

In the current architecture, the global map fulfills this purpose. The LPLC is modular, and many different modules can run simultaneously.

All the modules work with data from the global map.

The main restriction on the global map is that for each point or variable, one module writes it and the others can only read. For inputs that's
obvious (the I/O module writes their status into the global map), but it's used throughout the map. That way it's much easier to trace where a problem is coming from. (It's also a lot easier to implement.)

> d.. I suppose we can remove the base 0.1 seconds and 1 second timers and
> have 1 timer with 0.01 second time base and all preset values can be
> manipulated as per user requirements.

Yup.

> e.. All process controls and machine Automation use the timer timing,
> timer done and the timer enabled bit several times in their logic. So
> these bits were provided.

OK. Like I said, easy enough to provide with the ones we already have (something will be translating from the STL/LAD/FBD/etc anyway).

> f.. I picked up the use of jiffies from Alessandro Runbini's book on
> Linux Device Drivers published by O'Reilly (chapter 6 on flow of time).

OK - be careful with that, though, device drivers can do some things that user programs have to do differently. I think jiffies is one of them.

> g.. I suppose I will have to set the too1b bits to zero after certain
> time in order to avoid overflow.

That's another advantage of using struct timeval - when that stops working,
all hell will break loose throughout the OS anyway (like the Y2K bug).

> Also planning to make use of binary files instead of database if time
> permits during this weekend.

Be sure to check the global memory - most things should go through there if possible. (Most likely some things just won't fit the model, but those that do should be there.)

Have a look at
http://mat.sourceforge.net/structure.txt
particularly the "data map" section, and then
http://mat.sourceforge.net/smm/description.txt
http://mat.sourceforge.net/smm/tutorial.txt
for the details.

These describe the C interface to the global data map (it used to be called the "shared memory map"), but any other language is going to have much the same access - the mnemonic "LD X001" corresponds to "plc_get(X001)" etc.

> Next algorithm is the totalizer block.

This probably belongs in Mario's DSP module, so I'll leave it up to him to respond in detail, but...

> Samples are taken every 10 milliseconds.

It might be better to have the algorithm calculate the time since the last sample and let the user specify the period using Mario's "period" setting. To do it really properly, keep the previous value and add the average of the two instead of the current value - that way it'll work better if the periods are uneven. It makes no difference if they're regular.


Jiri
--
Jiri Baum <[email protected]>
http://www.csse.monash.edu.au/~jiribvisit the MAT LinuxPLC project at http://mat.sourceforge.net

_______________________________________________
LinuxPLC mailing list
[email protected]
http://linuxplc.org/mailman/listinfo/linuxplc
 
Top