Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

C programming question for Arduino

32 views
Skip to first unread message

RogerN

unread,
Mar 31, 2012, 10:50:16 AM3/31/12
to
I'm wanting to make timers that work like PLC timers, a condition comes
true, a preset period of time later code is executed.

It would be something like:

void mytimer(bool enable, long preset)

void mytimer( (input 1 is on), 1000) // If a condition is true (input 1 ==
1) for 1000ms then code is executed.
{
// after preset time delay, as long as enable stays true, code here
would execute
}

This timer would be multi - instance, I'm thinking about a struct
mytimerdata[] that would hold the data for one instance of the timer.

The timer data structure would have a long for time started and preset, plus
some boolean for timer enabled, timer timing, timer done, last_state. So in
this timer function if the last state was disabled and the current state is
enabled (condition became true) you record time_started=millis();. If the
timer is enabled then you compare millis(); with time_started + preset. If
enable condition is not true then clear bits for enable, timing, and done.

The structure array would be how I would use multiple instances, in this
case multiple timers.

Now the question How do you control the execution of the code after the
function? Does returning a value control this?

In case I'm not clear:

if(condition)
{
// how does if function control the execution of this code?
}

Any better ideas? I'm not wanting to wait for a condition to come true,
just control the execution of code after a preset time delay, that way other
things get processed in the loop.

RogerN


Spehro Pefhany

unread,
Mar 31, 2012, 11:26:43 AM3/31/12
to
PLC firmware uses 'hidden' looping (and interrupts) to give the
illusion of simultaneous execution of multiple ladder logic
instructions. Almost like an interpreted HDL.

Your if (..) {}; example just continues to the next statement if the
condition is false, and unless you check it again the code between the
curly brackets will never be executed.

In C you can pass pointers to functions as parameters, so you could
set up a timer routine which would be called periodically that
executed a function upon timeout, but you need to write code to do it.

General rule is to do only what is absolutely necessary within
interrupt service routines and get the heck out of there, setting
flags or stuffing queues to have more complex things done later. IOW,
I would not suggest calling the functions from within an ISR.

Best regards,


Best regards,
Spehro Pefhany
--
"it's the network..." "The Journey is the reward"
sp...@interlog.com Info for manufacturers: http://www.trexon.com
Embedded software/hardware/analog Info for designers: http://www.speff.com

Tim Wescott

unread,
Mar 31, 2012, 12:21:45 PM3/31/12
to
I'm not sure exactly what you want. I feel like I should, but I haven't
had breakfast yet!

But I can see the core of what you want: event A happens, then at some
time T later you want code B to execute. Usually the way that I do this
in a non-interrupt-driven system is to set up one of the processor clocks
to free run, then on the event set

extern int time_now; // set by hardware
int timeout;

timeout = time_now + T;
enabled = true;

Then in my task loop I'll have a check

if (enabled == true && time_now - timeout > 0)
{
// put your code here
}

Note that the order of operations in the test is important: time_now
_will_ roll over; if you test on time_now > timeout you will get
different, and quite possibly undesired, behavior from your code. Note
also that whatever size your 'int' is (probably 16 bits), this method
will only support a timeout of MAX_INT (32767, if 16 bits) ticks.

Alternately, you can have a routine that just runs at 1ms intervals, and
keep track of all your timers there.

--
Tim Wescott
Control system and signal processing consulting
www.wescottdesign.com

Don Y

unread,
Mar 31, 2012, 2:41:26 PM3/31/12
to
Hi Roger,

On 3/31/2012 7:50 AM, RogerN wrote:
> I'm wanting to make timers that work like PLC timers, a condition comes
> true, a preset period of time later code is executed.
>
> It would be something like:
>
> void mytimer(bool enable, long preset)
>
> void mytimer( (input 1 is on), 1000) // If a condition is true (input 1 ==
> 1) for 1000ms then code is executed.
> {
> // after preset time delay, as long as enable stays true, code here
> would execute
> }

I.e., in ladder logic, you have a single rung with the conditional
enabling a timer. The completion of the timer enabling the "code
here would execute" portion of that *other* rung.

> This timer would be multi - instance, I'm thinking about a struct
> mytimerdata[] that would hold the data for one instance of the timer.
>
> The timer data structure would have a long for time started and preset, plus
> some boolean for timer enabled, timer timing, timer done, last_state. So in
> this timer function if the last state was disabled and the current state is
> enabled (condition became true) you record time_started=millis();. If the
> timer is enabled then you compare millis(); with time_started + preset. If
> enable condition is not true then clear bits for enable, timing, and done.
>
> The structure array would be how I would use multiple instances, in this
> case multiple timers.
>
> Now the question How do you control the execution of the code after the
> function? Does returning a value control this?

In a ladder logic analogy, you would do:

if (timer_expired) {
do_something();
}
try_again();

If you further want to qualify this execution with the initial
"arming" condition remaining true:

if ((input_1_is_on) && (timer_expired)) {
do_something();
}
try_again();

Here, "try_again()" is some mechanism that causes other "rungs"
to be evaluated.

And these "rung-equivalents" would be continuously re-evaluated.
A more intuitive control structure would then be:

while ((input_1_is_on) && (timer_expired)) {
do_something();
try_again();
}

This ensures everything in the while-loop is done before the input
is re-examined and/or timer is re-verified as having expired (ask
yourself how can an expired timer NOT remain expired?)

But, there is nothing in this model that precludes something like:

while ((input_1_is_on) && (timer_expired)) {
while (FOREVER) {
twiddle_thumbs();
}
/* NOT REACHED */
try_again();
}

In this case, this one "rung" effectively monopolizes the processor
(since try_again() is never invoked!)

Similarly, even if you don't (carelessly) twiddle_thumbs FOREVER,
if do_something() takes a REALLY LONG TIME, then the other rungs
will see their execution deferred. If do_something() takes a
varying amount of time, then this deferral becomes unpredictable
(the order in which the rungs are evaluated determines your
actual system response; changes to one rung affect the performance
of all others. This is usually not A Good Thing)

PLC's give the illusion of parallel operation (parallel rung
evaluation) by running a multitasking interpreter behind the
scenes. In essence, the interpreter evaluates part of each rung
before advancing to the next rung (to resume evaluation from
wherever it left off "last time" it looked at this rung).

[you can also evaluate an entire rung before advancing to the
next but then the complexity of each rung affects its neighbors]

PLC's optimize this "rung switching" ability by carefully
considering the complexity of each operation that can be
invoked within a rung as well as each condition that can
be examined. They publish a metric that effectively indicates
how many of these evaluations can be performed per second.

But, you have a very limited set of conditions/operators
available to you with ladder logic. Each is well defined
in terms of the effort required to implement it. This
doesn't translate well to a conventional programming environment.

I've already shown how you wouldn't want to treat the entire
if/while block as a single operation (because it can take
FOREVER to execute). Perhaps treat individual C statements
as schedulable operations? What happens when someone writes
the single line of code:
count_to(1000000);
while someone else has:
count++;
count++;
count++;
...
The former has gimmicked the system to get more resources
(his *one* line of code gets executed once for each time
one of the other guy's "count++" gets executed).

Instead, you want to build a mechanism that sits *under*
the code (like the interpreter that sits under the ladder
rungs) and parcels out resources dynamically for each of
the various "programs" co-existing on the machine. A
preemptive multitasking OS (MTOS) gives you this capability
(which need not be an RTOS).

Then, each rung becomes:

while(FOREVER) {
if (enabling_conditions)
perform_actions();
}

Noting that perform_actions can affect enabling conditions
for other "rungs".

You can then build a task that implements any number of
timers which can be enabled/disabled individually, set to
expire at different times, etc. If the number of timers
is large, you can use delta queues to reduce the cost of
updating them to something closer to constant time. Or,
you can cheat and run:

timer_t timers[NUMBER_TIMERS];

update_timers() {
now = get_system_time();
elapsed_time = now - last_time;
last_time = now;
if (elapsed_time > 0) {
for (index=0; index < NUMBER_TIMERS; index++)
if (timer[index] > elapsed_time) {
timer[index] -= elapsed_time;
} else {
timer[index] = 0;
}
}
}
}

each time you process the "rungs".

Here, you can set a timer to expire at some time in the
future by simply loading it with a value:
timer[MY_TIMER] = 37;
and then wait for the timer to "expire" by:
while (timer[MY_TIMER] > 0)
/* SIT IDLY */ ;
or, test the state of the timer:
if (timer[MY_TIMER] > 0) {
do_something_while_timer_is_still_running()
}
or, *retrigger* an unexpired timer:
if (timer[MY_TIMER] > 0) {
timer[MY_TIMER] = 37;
} else {
timer_expired_before_I_managed_to_retrigger_it();
}
or, abort a running (or non-running!) timer:
timer[MY_TIMER] = 0;
etc.

Personally, I frown on doing any "timer arithmetic" to
alter a timer's "remaining time" to anything other than
a full reload (retrigger) or "forced expiration".

No idea what's inside an arduino but surprised if it doesn't
have *some* hooks to make this possible.

> In case I'm not clear:
>
> if(condition)
> {
> // how does if function control the execution of this code?
> }
>
> Any better ideas? I'm not wanting to wait for a condition to come true,
> just control the execution of code after a preset time delay, that way other
> things get processed in the loop.

If you only have a *single* thread of execution (i.e., one big
"loop"), then you have to take on the task of updating the timers
at some point in that loop so that their values are available
at other places in the loop.

E.g.,

big_loop() {
update_timers();
do_other_stuff();
}

You also have to ensure that the all of the "other stuff's"
are "fair" (using whatever definition of "fair" is appropriate
for you) in how they share the set of resources in that *single*
thread.

For example:

do_other_stuff() {
do_one_thing();
do_other_thing();
}

do_one_thing() {
while (timer[MY_TIMER] > 0)
/* SIT IDLY */ ;
// do what you REALLY wanted to do
}

will block do_other_thing() from executing until timer[MY_TIMER]
reaches '0'. But, it will also block update_timers() from
executing -- so, timer[MY_TIMER] never gets updated! Furthermore,
this sort of thing might not be obvious during testing:

do_one_thing() {
...
if (today == "April 15 2012") {
while (timer[MY_TIMER] > 0)
/* SIT IDLY */ ;
// do what you REALLY wanted to do
} else {
// do whatever you normally do
}
}

Will hide the bug until April 15th...

Bill Martin

unread,
Mar 31, 2012, 6:36:58 PM3/31/12
to
If you had something like a ThreadX OS, it would be simple.

TimerISR()
{..}


Main()
{
...
StartTimer( timeout, &TimerISR);

...go about business

}
You can have a bunch of these system timers...

regards,
Bill
ps: no syntax checking!!!

Martin Riddle

unread,
Mar 31, 2012, 7:41:56 PM3/31/12
to

"RogerN" <re...@midwest.net> wrote in message
news:hNudnUTwmLExhOrS...@earthlink.com...
I usually use an interrupt that runs at 1ms and place timer code in
there, like this:

unsigned int delay, delay1; // 65535 ms

TimerISR(){

if(delay) --delay;
if(delay1) --delay1;

}

Then use them like so:

delay=1000; // 1 sec

while(delay){

// your code here

}; // blocking function


You could also just run a counter in a ISR and create a function
GetTickCount (aka Microsoft)
And use safe subtraction (handles the rollover)
see <http://msdn.microsoft.com/en-us/library/aa915056.aspx>

Either one will work, just watch for getting hung up in a loop (Use a
timer to get out!)

Cheers


RogerN

unread,
Mar 31, 2012, 9:15:54 PM3/31/12
to
"Tim Wescott" wrote in message
news:xqSdnfzZWKmEsurS...@web-ster.com...


>I'm not sure exactly what you want. I feel like I should, but I haven't
>had breakfast yet!
>
>But I can see the core of what you want: event A happens, then at some
>time T later you want code B to execute. Usually the way that I do this
>in a non-interrupt-driven system is to set up one of the processor clocks
>to free run, then on the event set
>
>extern int time_now; // set by hardware
>int timeout;
>
>timeout = time_now + T;
>enabled = true;
>
>Then in my task loop I'll have a check
>
>if (enabled == true && time_now - timeout > 0)
>{
> // put your code here
>}
>
>Note that the order of operations in the test is important: time_now
>_will_ roll over; if you test on time_now > timeout you will get
>different, and quite possibly undesired, behavior from your code. Note
>also that whatever size your 'int' is (probably 16 bits), this method
>will only support a timeout of MAX_INT (32767, if 16 bits) ticks.
>
>Alternately, you can have a routine that just runs at 1ms intervals, and
>keep track of all your timers there.
>
>--
>Tim Wescott
>Control system and signal processing consulting
>www.wescottdesign.com

Oops, something I didn't make clear.. millis() is part of the Arduino
functions.
From Arduio Reference:

millis()
Description
Returns the number of milliseconds since the Arduino board began running the
current program. This number will overflow (go back to zero), after
approximately 50 days.

So what I want to do is make my loop run as fast as it can and control by
setting flags and conditions, sort of parallel processing like in a PLC, not
sequential style programming.

A little background. My first computer was a sinclare zx 80 (IIRC) for
$199, later I bought a Commodore 64, did some Basic and 6502 assembly
language. Later I played with PIC microcontrollers, 16C84, 16f84 came out
later. So about everything I learned was pretty much sequential style
programming. When I got a 286/12 I bought Turbo C++ and a "Teach yourself
C" book, got where I could write programs but not enough practice to get
good at it. After some time I ended up with an Engineering company that did
industrial automation type work. It was difficult at first for me to get
PLC's to work like I wanted, I was used to writing sequential type of
programs and the PLC was parallel, all rungs execute at the same time, well,
not really but I learned that I should write programs as if they did.

Here's a project I designed, built, and programmed the controls for.

http://www.youtube.com/watch?v=9xFwpbpusTo

You can see there are a lot of things happening at once.

Anyway, over the years I have pretty much figured out how I can write the
same type of programming in other languages beside ladder logic. The
straight runs just AND and OR conditions to control outputs when they should
be according to the logic. I think I could write the bulb machine program
in 'C' very much like it was written in ladder logic.

One of the problem areas is getting timers to work like PLC timers, they can
have dozens timing multiple events at once, none interfering with the
others. So I'm trying to come up with a good function that will compare the
preset time with the system time and execute additional code 1 pass per pass
through the loop (won't hang up the loop).

Now I know I can write something to hog the processor with a loop and halt
the main loop, but I will have to be careful not to, if it happens I will
have to correct the error.

The system timer will overflow once every 50 days so I need to set an
"will_overflow_bit" when the destination time is less than the current time
( a positive number plus a positive number is greater unless there is an
overflow). So once the timer condition is true, and the timer preset is
timed out, the code will be executed once per loop, like an if() function,
not cycled inside continuously like a while() function.

RogerN


Tim Wescott

unread,
Mar 31, 2012, 9:59:09 PM3/31/12
to
In that case, then Arduino's millis() is my time_now, your ints should be
32-bits, and I'm pretty darned sure that millis() just rolls over from
0xffffffff to 0. That would be 49 days, 17 hours, 2 minutes 47.296
seconds, which is "about 50 days" for anyone but Spock.

You avoid the rollover by doing what I showed you. For example, if the
time is 0xffffff00 and you want an event to pop off 1 second later, you
would calculate a timeout of 0x000002e8. _If_ you did a test on

millis() > timeout

then it would be popping off all the time; but if you test on

timeout - millis() > 0

then you would find that timeout - millis() would have the value 1000,
then 999, then 998, etc., down to 0 and then to -1 (if you are using
signed arithmetic), or UINT32_MAX (if you are using unsigned
arithmetic). Either one of these conditions can be tested for, and your
test will be unambiguous as long as your test often enough and don't have
really long times -- basically, as long as your desired timeout plus the
amount of time you wait to do the check is shorter than 25 days, you
should be OK.

--
My liberal friends think I'm a conservative kook.
My conservative friends think I'm a liberal kook.
Why am I not happy that they have found common ground?

Tim Wescott, Communications, Control, Circuits & Software
http://www.wescottdesign.com

RogerN

unread,
Mar 31, 2012, 11:18:11 PM3/31/12
to
"Tim Wescott" wrote in message
news:sIydnRKFdafwK-rS...@web-ster.com...

>In that case, then Arduino's millis() is my time_now, your ints should be
>32-bits, and I'm pretty darned sure that millis() just rolls over from
>0xffffffff to 0. That would be 49 days, 17 hours, 2 minutes 47.296
>seconds, which is "about 50 days" for anyone but Spock.
>
>You avoid the rollover by doing what I showed you. For example, if the
>time is 0xffffff00 and you want an event to pop off 1 second later, you
>would calculate a timeout of 0x000002e8. _If_ you did a test on
>
>millis() > timeout
>
>then it would be popping off all the time; but if you test on
>
>timeout - millis() > 0

I was aware of the rollover problem and was going to handle it something
like:

boolean mytimer(boolean currentState, unsigned long Preset)
{
unsigned long currentTime = millis();
if(previousState = 0 && currentState=1 // first pass that timer condition
became true
{
TimeUp = currentTime + Preset;
if{currentTime>TimeUp) overflow = 1;
}

if(overflow==1 && currentTime<TimeUp) overflow =0;
if(overflow==0 && currentTime>=TimeUp) return(1);
return(0);
}

Then the above might be used in a program something like:

if(mytimer(a>b(some condition to test for), 100) // If Evaluates my timer,
returns zero until after 100 milliseconds of true condition (a>b example
here)
{
// Code to be executed after timed condition is true for preset time
(turn light on/off, read sensor, position servo, alarm, or whatever
}

Your method works without using the overflow bit so that simplifies it some.

>then you would find that timeout - millis() would have the value 1000,
>then 999, then 998, etc., down to 0 and then to -1 (if you are using
>signed arithmetic), or UINT32_MAX (if you are using unsigned
>arithmetic). Either one of these conditions can be tested for, and your
>test will be unambiguous as long as your test often enough and don't have
>really long times -- basically, as long as your desired timeout plus the
>amount of time you wait to do the check is shorter than 25 days, you
>should be OK.
>
>--
>My liberal friends think I'm a conservative kook.
>My conservative friends think I'm a liberal kook.
>Why am I not happy that they have found common ground?
>
>Tim Wescott, Communications, Control, Circuits & Software
>http://www.wescottdesign.com

Now what I would like to do is define a data structure that I can use so I
can have multiple instances of the timer.

These are new to me but maybe get the idea;
struct mytimerdata
{
unsigned long Preset, currentTime;
boolean previousState, currentState;
}
struct mytimerdata analogUpdate, faultDelay, UpdateDisplay;


Now I'm wondering how I can point my different timers to their data
structure.

RogerN


Tim Wescott

unread,
Mar 31, 2012, 11:35:43 PM3/31/12
to
Just make your "mytimer" function take a pointer to a "mytimerdata"
struct, and do the obvious inside.

DA

unread,
Mar 31, 2012, 11:43:01 PM3/31/12
to
responding to
http://www.electrondepot.com/electrodesign/c-programming-question-for-arduino-631809-.htm
DA wrote:
RogerN wrote:

> The system timer will overflow once every 50 days so I need to set an
> "will_overflow_bit" when the destination time is less than
> the current time
> ( a positive number plus a positive number is greater unless there is
> an
> overflow). So once the timer condition is true, and the timer preset
> is
> timed out, the code will be executed once per loop, like an if()
> function,
> not cycled inside continuously like a while() function.

Can the application tolerate the startup time from a reset every so often
(much sooner than 50 days)? I think ATMega328P starts in less than 10ms
and so it seems like pulling the reset pin to high from time to time would
alleviate the need for the extra variable. Atmel does not recommend using
another pin for resetting AVR but it does work. You can also setup a
watchdog timer to reset it in the software (and free an I/O pin).

Besides, I don't know if I would trust an Arduino board to run for 50 days
without a reset anyhow. I've had it hang on me for various reasons after
several days of continuous operation and I think it might just make the
whole system a bit more reliable if you reset it at a predictable time.


-------------------------------------
/\_/\
((@v@))
():::()
VV-VV



RogerN

unread,
Apr 1, 2012, 12:18:23 AM4/1/12
to

"Don Y" wrote in message news:jl7j4j$mrl$1...@speranza.aioe.org...

>Hi Roger,
<snip>
>If you only have a *single* thread of execution (i.e., one big
>"loop"), then you have to take on the task of updating the timers
>at some point in that loop so that their values are available
>at other places in the loop.
>
>E.g.,
>
>big_loop() {
> update_timers();
> do_other_stuff();
>}

That's what I want to do
big_loop()
{
read_inputs();
read_operator_interface();
process_add_ons();
control_logic();
check_faults();
update_display();
update_outputs();
}

>You also have to ensure that the all of the "other stuff's"
>are "fair" (using whatever definition of "fair" is appropriate
>for you) in how they share the set of resources in that *single*
>thread.

Everything will need to pass right through.
if(condition) turn on output or set logic bit;
else turn off output or reset logic bit;

The only allowed exceptions would be if I need to read or write precision
timed pulses where a loop scan time would cause too large of an error. For
example if I were going to read a pulse from a radio control receiver
ranging between 1 to 2 milliseconds every 20 milliseconds, the precision
time would be more important to be accurate than general scan time varying a
millisecond or so.

>For example:
>
>do_other_stuff() {
> do_one_thing();
> do_other_thing();
>}
>
>do_one_thing() {
> while (timer[MY_TIMER] > 0)
> /* SIT IDLY */ ;
> // do what you REALLY wanted to do
>}
>
>will block do_other_thing() from executing until timer[MY_TIMER]
>reaches '0'. But, it will also block update_timers() from
>executing -- so, timer[MY_TIMER] never gets updated! Furthermore,
>this sort of thing might not be obvious during testing:

This sort of thing won't be allowed, it would destroy scan time. The
control of execution won't be by holding up scan time but rather by setting
up the condition.
example:
will not use
while(not ready); // waste time until ready;

instead use:
if(ready)execute code it's ready to execute;

>do_one_thing() {
> ...
> if (today == "April 15 2012") {
> while (timer[MY_TIMER] > 0)
> /* SIT IDLY */ ;
> // do what you REALLY wanted to do
> } else {
> // do whatever you normally do
> }
>}
>
>Will hide the bug until April 15th...

That's the thing I'm trying to avoid, never sit idly except for short time
for precision timing. If this doesn't work well enough then I'll set up
interrupts for time critical functions.
The timer I'm describing checks to see if it's time to execute the code, if
not it doesn't wait, just goes on to the next instruction.

RogerN


Don Y

unread,
Apr 1, 2012, 1:36:44 AM4/1/12
to
Hi Roger,

On 3/31/2012 9:18 PM, RogerN wrote:
> "Don Y" wrote in message news:jl7j4j$mrl$1...@speranza.aioe.org...
>
>> Hi Roger,
> <snip>
>> If you only have a *single* thread of execution (i.e., one big
>> "loop"), then you have to take on the task of updating the timers
>> at some point in that loop so that their values are available
>> at other places in the loop.
>>
>> E.g.,
>>
>> big_loop() {
>> update_timers();
>> do_other_stuff();
>> }
>
> That's what I want to do
> big_loop()
> {
> read_inputs();
> read_operator_interface();
> process_add_ons();
> control_logic();
> check_faults();
> update_display();
> update_outputs();
> }

So, throw the "update_timers()" in there, somewhere.

If the only places you examine/alter timer values are
in update_timers() and, thereAFTER, in the various
other functions in your loop, you don't have to worry
about atomic operations (i.e., no one will alter
timer[foo] while <someone> is reading it -- regardless
of the implementation data type).

You also have to decide if you are implementing
*timers* or timeOUTs. A timer can be used to
*measure* time; a timeout is used to delimit a
time *period* (i.e., 20 seconds from now).

If you want to blink a light, you would typically
do:

light_on(foo);
delay(ON_INTERVAL);
light_off(foo);
delay(OFF_INTERVAL);

Note that this only guarantees that the light stays on for
AT LEAST the ON_INTERVAL (and off for at least the OFF_INTERVAL).
I.e., if your "scan time" has too much variation, then the
lamp might be on for ON_INTERVAL+epsilon1 and, similarly, off for
OFF_INTERVAL+epsilon2.

This is important if, for example, you want to maintain synchronism
between periodic actions (imagine having two different lights
blinking from two different INSTANCES of this bit of code. It
is possible (likely!) that one will creep wrt the other (and
this creep can vary).

OTOH, if you are aware of this, you can define a "task"
that does:

raise_flag(foo);
delay(ON_INTERVAL);
lower_flag(foo);
delay(OFF_INTERVAL);

and have *all* tasks that need to be synchronized do:

if (test_flag(foo)) {
// this is the on portion of the synchronized cycle
} else {
// this is the off portion
}

One of the big advantages of using timers for delays/timeouts
(instead of to measure elapsed time) is that you already
implicitly have acknowledged that some things might take
a bit longer than planned (because you don't know what the other
activities in the big_loop() might be doing, right now, to
defer some specific response).

It also means your code doesn't need to know what the units
of time actually are! milliseconds, quarter seconds, etc.
(which they would otherwise need to be aware of if they
have to convert "elapsed time units" into "elapsed *time*")
If you have a good macro processor, (or, want to manually maintain
your code), you can implement pseudo multitasking *within* a
function. This allows an expensive "task" (sorry, hard to
avoid that term) to deliberately be staged over multiple passes
"around the loop".

function_within_big_loop()
{
...

do_a_little_work();

LET_SOMEONE_ELSE_GET_STUFF_DONE();

do_a_little_more_work();

LET_SOMEONE_ELSE_GET_STUFF_DONE();

do_still_more_work();

LET_SOMEONE_ELSE_GET_STUFF_DONE();

do_whatever_is_left();

/* all done! */
}

I.e., you *manually* simulate the task switch that an MTOS
would provide for you. But, you do it *where* it is
convenient for your algorithm to "take a break". (There
are also constraints on where you *can* do this!)

>> do_one_thing() {
>> ...
>> if (today == "April 15 2012") {
>> while (timer[MY_TIMER]> 0)
>> /* SIT IDLY */ ;
>> // do what you REALLY wanted to do
>> } else {
>> // do whatever you normally do
>> }
>> }
>>
>> Will hide the bug until April 15th...
>
> That's the thing I'm trying to avoid, never sit idly except for short time
> for precision timing. If this doesn't work well enough then I'll set up
> interrupts for time critical functions.
> The timer I'm describing checks to see if it's time to execute the code, if
> not it doesn't wait, just goes on to the next instruction.

Instead of thinking about "if it is time", consider thinking about
"have I waited long enough" (subtle difference -- the first
checks the actual time, the second checks to see if an interval
has expired. IME, the second is a lot easier to "get right"
within a multitasking-ish framework.

Jamie

unread,
Apr 1, 2012, 10:51:47 AM4/1/12
to
PLC's capture all of the inputs and output states just prior to a
scan of your program. When OS of the PLC scans ladder rungs, it is
simply viewing and modifying the captured values, not the actual values
sitting at the hardware outputs/inputs. This allows for a ladder program
to see the same values captured through out, however, changing the state of
any outputs with the ladder code, only changes the captured values but
does not actually change the hardware output, yet. So this means that
any remaining ladder code there after will now see this change. Care
must be taken when doing this because it can cause debugging problems
when more then one point in the ladder code is setting the value of an
output.

At the end of all scans, these values that were captured to reflect
output states, which also could have been altered ofcourse, get written
back out to the outputs.

You can in effect do the same exact process and that is, to simply
capture all of the needed values and hardware states at the start of the
loop and store them in variables. You then address those values instead
of directly accessing the IO on the fly in your program. This would
solve problems where you may have an unpredictable event due to noise or
what ever changing the state of an input while you're scanning your code
and thus if your code is referencing this input several times, would end
up causing your code to fail on making a proper determination with
external events.
Also, you should do this with the outputs, too. That would prevent an
unwanted pulse to appear if your code is changing the output hardware
value before it actually decides where it will stay.

P.S.

I could be wrong? But, that PLC shown in the video looks like a PLC5
family from AB ?

Jamie

Don Y

unread,
Apr 1, 2012, 10:32:01 AM4/1/12
to
Hi Jamie,

On 4/1/2012 7:51 AM, Jamie wrote:

> PLC's capture all of the inputs and output states just prior to a scan
> of your program. When OS of the PLC scans ladder rungs, it is simply
> viewing and modifying the captured values, not the actual values sitting
> at the hardware outputs/inputs. This allows for a ladder program to see
> the same values captured through out, however, changing the state of
> any outputs with the ladder code, only changes the captured values but
> does not actually change the hardware output, yet. So this means that
> any remaining ladder code there after will now see this change. Care
> must be taken when doing this because it can cause debugging problems
> when more then one point in the ladder code is setting the value of an
> output.

I don't think you can easily transition from ladder logic "style"
to treating each rung as if a "task". You (probably) don't want to
constrain your (C) implementation to "run each task to completion"
before advancing to the next task (function). It's just too easy
to "do too much" in a function and effectively slow down all
*other* functions/tasks.

Alternatively, to *naively* try to slice a function into little
pieces and still maintain a coherent model of the task being
implemented ("I pointed the ship towards the other vessel -- but
it had moved before I got there!")

Instead (*I* think), you want to be able to proceed through a "job"
(resorting to an older lexicon) more methodically -- distributing
it's computational load over multiple "scans" (iterations) of
the big_loop(). Otherwise, the effort to slice the activities
into the "multiple, sequentially-enabled rung analogy starts
to interfere with the goal of the "job" at hand.

But, this has consequences to the designer since it means *this*
task (rung) didn't "start" at the same *virtual* time (i.e.,
in the same big_loop() iteration) as each of the other tasks.
I.e., the inputs *can* have changed -- because this might be
iteration #6 and the task (rung) still hasn't been completely
processed.

> At the end of all scans, these values that were captured to reflect
> output states, which also could have been altered ofcourse, get written
> back out to the outputs.
>
> You can in effect do the same exact process and that is, to simply
> capture all of the needed values and hardware states at the start of the
> loop and store them in variables. You then address those values instead
> of directly accessing the IO on the fly in your program. This would
> solve problems where you may have an unpredictable event due to noise or
> what ever changing the state of an input while you're scanning your code
> and thus if your code is referencing this input several times, would end
> up causing your code to fail on making a proper determination with
> external events.

A task (rung) should capture the state of its inputs when *it*
starts -- if this is important to the task's integrity. Otherwise,
you risk races.

Imagine:
if (switch == ON) {
do_something();
}
...
if (switch == OFF) {
do_something_completely_different();
}
If "switch" can change between the evaluations of these two
conditionals, then you get some bizarre (probably unintended)
behavior!

If "switch" reflects the instantaneous value of a hardware
input, then a switch that is "in transition" (ON->OFF or OFF->ON)
between these two statements screws you.

if "switch" reflects a *variable* into which the hardware state of
the physical switch was *copied* "at the start of a scan" (loop),
then you are similarly vulnerable if that variable can be
updated between those two conditionals. I.e., if the conditionals
can be evaluated in DIFFERENT SCANS!

This is true of *every* shared variable/resource! I.e., for
timers, you only want to look at "current_time()" in one place
and share that value among all "time consumers". Otherwise:

if (current_time() == X) {
do_X_stuff();
}
...
if (current_time() == Y) {
do_Y_stuff();
}

could result in X and Y "stuff" happening in a *single* "scan"
of big_loop().

> Also, you should do this with the outputs, too. That would prevent an
> unwanted pulse to appear if your code is changing the output hardware
> value before it actually decides where it will stay.

Imagine if do_X_stuff() turns on a light. And, do_Y_stuff()
turns it off. Furthermore, imagine you only copy the actual
"light" variable to physical outputs at the end of the scan
(or, at some explicit point in this "task"). Then, the
light may NEVER turn on -- since the Y stuff turns it off
before it is ever "manifested" to the outside world.

Ladder logic is designed to support a different sort of
programming and application environment. Trying to come
up with direct parallels to coding in a sequential programming
language... in a single-threaded, sequential environment...
means you can easily come up with solutions that don't work
well.

(rungs tend to be *short*, predictable; C functions/tasks not
quite so!)

Tim Wescott

unread,
Apr 1, 2012, 5:07:36 PM4/1/12
to
That's not ladder logic that you're arguing against -- it's non-
preemptive multitasking.

Note that non-preemtime multitasking can work really well, as long as you
observe the "don't do too much at once" rule. But if it were so
thoroughly hot, there'd be a lot fewer RTOSs in the world.

Jamie

unread,
Apr 1, 2012, 5:28:05 PM4/1/12
to
Also, I don't think he has a clear understanding on how ladder works. At
least it does not appear so. I get the impression he believes each rung
is a separate task on its own that can hang there until something is
satisfied.

And if the processor unit isn't able to process a complete ladder scan
with out the danger of missing some critical input triggers, then it is
not suited for the job and the job requires a faster unit.

Jamie


Don Y

unread,
Apr 1, 2012, 7:25:19 PM4/1/12
to
Hi Tim,
The ladder logic model *is* nonpreemtive multitasking,
though some controllers impose a strict, predictable
order on the evaluation of *rungs* (I've never seen any
that imposed any order on the evaluation of terms *within*
a rung).

Rungs tend to be inherently short. The equivalent of
*expressions* in most traditional programming languages.
"Programs" (tasks) written in procedural languages tend to
be *long*.

Some controllers give a true parallel illusion to the
evaluation of ladder rungs -- as if implementing "relay
logic". Others expose the sequential/"interpretive"
nature of the implementation which makes rungs look like
"(assignment) statements":
<actuator> <-- <expression>

Whether you draw the analogy to "tasks" executing in parallel
(rungs APPARENTLY being evaluated simultaneously -- i.e., at
the granularity of the scan cycle) or "statements" executing
in sequence, the "atoms" that you deal with in ladder logic
tend to be considerably simpler than the types of operators
you would deploy in a HLL (procedural).

I.e., it is too easy to do too much in a function-equivalent
of a rung. You either don't bundle things in functions (since
they are not "divisible") or build functions as "sets of rungs"
sharing a common enabling/activating condition ("contact closure").

So, you have to exercise SELF-discipline -- whereas the PLC's
limitations *impose* limits on how much you can do in a rung.

If you want to make "heavy" (C) rungs, you want to be able to
split the execution of that (C) "rung" over multiple scans of
the big loop. So, any "rung" (task) that doesn't finish its
operation in one iteration of the loop runs the risk of
getting out of sync with the I/O's that other rungs are
seeing "now".

If your PLC-like implementation doesn't start the evaluation
of all rungs concurrently (or, sequentially, depending on
the model implemented), then the user exposes himself to the
sorts of races I mentioned. Unless he takes deliberate actions
to protect himself (from himself) -- the switch = ON/OFF example
I mentioned.

[Races are just an inherent aspect of multitasking -- even if you
neglect the issues that non-atomic reads/writes represent]

> Note that non-preemtime multitasking can work really well, as long as you
> observe the "don't do too much at once" rule. But if it were so
> thoroughly hot, there'd be a lot fewer RTOSs in the world.

Yes. PLC's inherently satisfy the "don't do too much at once"
rule on a per-rung basis. Evaluate some number of booleans
with some combination of AND and OR and use them to affect
one (or more) "actuators". The PLC's design, knowing this,
can exploit it to make locating a particular "value" very
inexpensive -- and the operations performed with those are
(typically) pretty inexpensive.

By contrast, in a C analogy, a single printf() (indivisible!)
invocation would grind the rung scanning to a noticeable halt
(esp if the printf has to deal with slow I/O).

You *can* manually share the processor in a C context. But,
without the support of an underlying MTOS (preemptive or not),
it just trades one set of (self-)disciplines for another.

E.g., consider how you would "manually" pause a recursive
algorithm and then *resume* it -- at that same point -- some
time later AFTER HAVING EXECUTED OTHER (non-interrupt) CODE
IN THE INTERIM. (exercise left for student :> )

Don Y

unread,
Apr 1, 2012, 7:44:07 PM4/1/12
to
Hi Jamie,

On 4/1/2012 2:28 PM, Jamie wrote:

> Also, I don't think he has a clear understanding on how ladder works. At
> least it does not appear so. I get the impression he believes each rung
> is a separate task on its own that can hang there until something is
> satisfied.

It's an *analogy*. How would you try to relate ladder logic and
rungs to "programming in C"? C has variables, operators, expressions,
statements, functions, etc. What are you going to map the "rung"
concept onto?

You can model rungs as "assignment statements". Or, as "execution
units" -- *tasks*.

If you choose "assignment statements", then this begs the question:
"what order are they executed in?" (this varies between
implementations).

OTOH, if you treat them as independent execution units, they can
operate in "true" parallel or in any sequential order you care
to define.

Look at the market that PLC's were created to address. They were
NOT designed to do scientific computations. Nor business
applications (accounting, etc.). Nor graphical user interfaces.
Nor...

They were designed to replace *relays* and switches (and other
functional black boxes -- time delay relays with elastic stop
nuts, electromechanical counters, etc.).

When you look at a "wiring diagram" for a control system,
there is no "line 1" that says, "This start button is pushed
which pulls in this relay." "Line 1" (if there were such
a thing) could just as easily say, "This relay closure
causes the start+run windings of the motor to be energized."
"Line 934" might say "The opening of the centrifugal clutch
causes the start winding to drop out of the circuit."
"Line 28" might say "The start button pulls in this contactor".

This sort of cognitive model is not intended for implicitly
representing sequences -- like procedural languages. Electricity
doesn't observe "line numbers" on a wiring diagram. AS SOON AS
a contact closes, power (signal) is available to everything
that it feeds (assuming signal is already present upstream of
the contact).

The multi*TASK*ing model stresses the illusion of parallelism.
I.e., if you want tasks to execute in SERIES, you have to
explicitly add mechanisms to ensure that happens. Otherwise,
the model implies that everything *could* operate concurrently
(if you assume otherwise, you end up with a race/bug).

So, IMHO, treating rungs as *tasks* is a more sensible model
than as "statements" (or any other C construct). I.e.,

big_loop() {
update_timers();
...
update_controllers();

read_inputs();

rung1();
rung2();
...
rungN();

write_outputs();
}

The differences between PLC models can then be seen as:
- are inputs and/or outputs buffered?
- are rung()'s executed in a predictable manner?

> And if the processor unit isn't able to process a complete ladder scan
> with out the danger of missing some critical input triggers, then it is
> not suited for the job and the job requires a faster unit.

Or, a cleverer implementation! You can write ladder logic to offload
a "heavy job" to a lower priority so that it doesn't impact the
scan time of the rest of the system. But, this requires more effort
(self-discipline) and knowledge of the PLC's actual implementation
to best exploit this.

(Or, the approach favored by PLC vendors: selling you another
"processor-in-a-module" to offload some can-able functionality)

RogerN

unread,
Apr 1, 2012, 9:23:14 PM4/1/12
to
"Jamie" wrote in message news:gZYdr.13230$dq4....@newsfe23.iad...

<snip>
> PLC's capture all of the inputs and output states just prior to a scan
> of your program. When OS of the PLC scans ladder rungs, it is simply
> viewing and modifying the captured values, not the actual values sitting
> at the hardware outputs/inputs. This allows for a ladder program to see
> the same values captured through out, however, changing the state of
>any outputs with the ladder code, only changes the captured values but does
>not actually change the hardware output, yet. So this means that any
>remaining ladder code there after will now see this change. Care
>must be taken when doing this because it can cause debugging problems when
>more then one point in the ladder code is setting the value of an
>output.

That sounds similar to what I have heard, that the inputs are read and
written to an input image table. The logic is processes, outputs written to
output image table, then the output image is written to the actual outputs.


> At the end of all scans, these values that were captured to reflect
>output states, which also could have been altered ofcourse, get written
>back out to the outputs.
>
> You can in effect do the same exact process and that is, to simply
> capture all of the needed values and hardware states at the start of the
> loop and store them in variables. You then address those values instead of
> directly accessing the IO on the fly in your program. This would solve
> problems where you may have an unpredictable event due to noise or what
> ever changing the state of an input while you're scanning your code and
> thus if your code is referencing this input several times, would end up
> causing your code to fail on making a proper determination with external
> events.
> Also, you should do this with the outputs, too. That would prevent an
> unwanted pulse to appear if your code is changing the output hardware
> value before it actually decides where it will stay.
>
> P.S.
>
> I could be wrong? But, that PLC shown in the video looks like a PLC5
> family from AB ?
>
>Jamie

The one in the video was a Mitsubishi A series IIRC. It's about the same
size as the PLC 5. One of the things I would have preferred on the PLC5 is
that I could have written each station in a separate ladder file. With the
Mitsubishi I had to write everything in a single ladder file.

I made sequence bits that I called things like STN1 STP1 Home(station 1 step
1), STN1 STP2 Open Gripper, STN1 STP3 Gripper Down, STN1 STP4 Gripper Close,
STN1 STP5 Gripper Up, STN1 STP6 Gripper to Table .... Gripper Open, ...
Gripper Up ... go back to step 1 to rehome gripper for next socket. This
way, the first -||- in the rungs told me the Station Number, Step Number,
and a brief description of what it was trying to do. So even though
everything was in one file, it was still easy to see what was going on. The
bits for the steps were all in order so you could just look online at the
memory and see what station was hung up and at what step.

Most of the processors I have worked with have an option to look at the
listing that looks kind of like an assembly language listing. I know you
can make FOR loops inside the ladder programs I have used, but I've never
needed to use them.

So I'm thinking I could take a rung like this:
A C
|--+--||---+--||----( )--|
| B |
+--|/|_+

and write it something like this:

if((conditionA==1 || conditionB!=1) && ConditionC==1) turn on output;
else turn off output;

Ladder Logic one shot rising (OSR) would be if (oldState==0 &&
currentState==1)...

One thing missing is the way PLC's do timers, they don't halt execution
while the timer is timing, such as delay(1000), the timer runs when enabled,
when the time is elapsed the Done bit is turned on. So the accuracy of the
timer could be a scan time longer. I use timers often for dwell, extend a
cylinder, wait a half second go to next step. This is because often
cylinders have prox switches to detect their position, sometimes they are
not fully stroked out when the input first makes.

If you notice in the video when the bulb is being seated, the bulb gripper
opens a little sooner than desired, I put a short dwell timer in it to make
sure the bulbs were fully seated. The video was taken when I barely got the
machine running. I made manual controls for each station so the people
assembling the machine could operate it to get it all aligned. I also made
a manually started automatic sequence for each station, so they could hit a
single button and that station would run through all the steps like it would
in automatic mode. So the morning before I shot the video, I made an auto
mode that triggered the stations to do their thing just like the button on
the touch screen did.

RogerN


Don Y

unread,
Apr 1, 2012, 10:32:05 PM4/1/12
to
Hi Roger,

On 4/1/2012 6:23 PM, RogerN wrote:

> That sounds similar to what I have heard, that the inputs are read and
> written to an input image table. The logic is processes, outputs written to
> output image table, then the output image is written to the actual outputs.

This varies with manufacturer/implementation. Some implementations
make the "output image" available to "subsequent rungs" *before*
the hardware is updated. This allows implementations that have
predictable rung scan orders to allow rungs to act like
"sequential statements" in a procedural language.

E.g. (fixed width font):

Start Stop Run
+---[ ]--+---[/]--------( )---+
| | |
| | |
| Run | |
+---[ ]--+ |
| |
| Run Motor |
+---[ ]-----------------( )---+

causes "Motor" to be active "shortly after" Start is pressed
(actuating "Run") *without* requiring another pass around the
big_loop() to transfer the "output image table" to the
physical outputs.

This sort of thing can lead to races as the order in which
you arrange the rungs determines how the logic works.

> Most of the processors I have worked with have an option to look at the
> listing that looks kind of like an assembly language listing. I know you
> can make FOR loops inside the ladder programs I have used, but I've never
> needed to use them.
>
> So I'm thinking I could take a rung like this:
> A C
> |--+--||---+--||----( )--|
> | B |
> +--|/|_+
>
> and write it something like this:
>
> if((conditionA==1 || conditionB!=1)&& ConditionC==1) turn on output;
> else turn off output;

A more intuitive form might be:

if ((conditionA || !conditionB) && ConditionC) {
turn on output;
} else {
turn off output;
}

And, for something this straightforward:

output = (conditionA || !conditionB) && ConditionC;

I would wrap this in a function invocation:

void
rung28(void)
{
output = (conditionA || !conditionB) && ConditionC;
}

so that big_loop just consists of a bunch of rungX()'s

> Ladder Logic one shot rising (OSR) would be if (oldState==0&&
> currentState==1)...
>
> One thing missing is the way PLC's do timers, they don't halt execution
> while the timer is timing, such as delay(1000), the timer runs when enabled,
> when the time is elapsed the Done bit is turned on. So the accuracy of the
> timer could be a scan time longer. I use timers often for dwell, extend a
> cylinder, wait a half second go to next step. This is because often
> cylinders have prox switches to detect their position, sometimes they are
> not fully stroked out when the input first makes.

The equivalent is:

void
rung28(void)
{
if (!timer_expired(TIMER_ID))
return;

output = (conditionA || !conditionB) && ConditionC;
}

So, timer_expired() just checks to see if there is any time
remaining on timer #TIMER_ID. If so (timer NOT expired), then
you just take an early exit from the function ("rung28()").

If you try to actually *watch* the current time, then you
need to store an "expiration time" for each timer. Or, track
it yourself, manually, and pass it as a parameter to
timer_expired():

I.e., timer_expired(expiration) then becomes:

return (now > expiration);

Or, timer_expired(TIMER_ID) becomes:

return (now > timer[TIMER_ID]);

The problem with tracking it yourself is that you may want to
set/specify an expiration time in one place and "wait on it"
somewhere else. You have to propagate that *time* to the
other place instead of just saying "wait for this TIMER"

Imagine setting up a rung that blinks a light at a certain
rate. And, wanting to be able to blink the light at two
*different* rates to indicate two different "conditions".
Using a TIMER_ID in the blink "rung()" lets that code
ignore the details of the actual blink rate.

(this depends on how you implement your timers)

Jamie

unread,
Apr 1, 2012, 10:42:06 PM4/1/12
to
Don Y wrote:

> Hi Jamie,
>
> On 4/1/2012 2:28 PM, Jamie wrote:
>
>> Also, I don't think he has a clear understanding on how ladder works. At
>> least it does not appear so. I get the impression he believes each rung
>> is a separate task on its own that can hang there until something is
>> satisfied.
>
>
> It's an *analogy*. How would you try to relate ladder logic and
> rungs to "programming in C"? C has variables, operators, expressions,
> statements, functions, etc. What are you going to map the "rung"
> concept onto?

It's very simple...

each rung has a destination results with no expected delay of
information.
All information required per rung is available. The logic with
in each rung does not allow for any such process of loops that would
other wise hold a program in place.

The whole idea of capturing external values and maybe some internal
values to be fixed through the scan is the best solution, because there
would never be any thing that can change with in the middle of a rung or
multiple rungs or complete scan of all the page files.

The only items that change are those items that were captured at the
start of a scan,which the OS of the PLC does that for you, you may
change a value to one of these external events that were captured so
that the remaining rungs that have not yet been scanned will see this
change, But nothing is actually changed on the out side.

No loops there by waiting for an event that only takes place in some
hardware timer or out side IO.

Talking about timers, All timers that have been started have their
current values frozen at the start of the scan. No timers change while
scanning down through the rungs. However, you can change a value of some
timer that has been froze so that when you reach the end of all rungs,
the timer will simply pick up and start from the change or from what
ever it was before. These are ladder timers I refer to, not back ground
real time timers, which you normally only have a couple of but with
those you can make many software one's which is what the Ladder code
uses for the most part. There are special timers/counters you can
get to.

The only data that changes in real time while the rungs are being
scanned are things like background communications, handling the hardware
stuff. But any data you expect to read from this hardware is cached to
be view only on the next scan cycle of the ladder program.

In other words, the Ladder never stops on a resource waiting for
something specific to happen there by, holding up the rest of the
system, that would be dangerous in many cases.

PLC controllers do not execute native code, that code you down load to
the controller is like a form of P code.

There is very little a PCL does that could actually place a stall on
your ladder code and that would be out of your hands. If things like
that happens, there is something wrong and a watch dog timer kicks in
and most likely will force a fault on the PLC and stop the ladder
program. When this happens, all outputs on the PLC are switched off. SO
you're suppose to tie in a interlock with one of the outputs.

You can have things like function blocks that contain FOR Loops to do
a restricted amount of code. You can also have what looks like an INT
service block and that still shares between the standard scan and INT's

When it comes to INT"s, what the system does is instead of doing a
schedule ladder scan, it will call a INT block if a defined event like
an external trigger took place on a input. The PCL will not wait for the
schedule event. Doing that, the system still does the initial load of
all the current inputs and outputs in cache before it goes to the ISR block.

Many PLC's also have a command that can force a early scan at the end
of the ISR so to get things moving sooner.

BUt, You'll never see something like

While !(I:100.01) do; // where I:100.01 would be an INPUT for example.

That could really screw things up if the input does not come on in
short time, that is, even if that command was doing real time hardware
reads.

You simply do test and set a results on the test, you never loop to
wait for the value to change to what you want.

All this can be done in a uC with C language with no problem. You just
need to code with no wait loops.

Jamie


Jamie

unread,
Apr 1, 2012, 10:52:24 PM4/1/12
to
You can get high speed high accuracy timer modules. They normally come
with IO also attached to the module and what you can do is, you can
program that module via talking to the address space of some specific
indexes to set the timers count scale, alarm trigger value, have it
stop/start on external events attached to its module and also activate
an output on its module. This has been with operating servo's in many cases.

Mean while, the main CPU/processor can go and get cached information
on the module while it remains operating..

Years ago, Seimens did this with their S5 line, They may still be
doing this now? But you could have one of the modules be a hardware
timer that can be read and controlled via the main CPU STL (step
language) code, but it can run on its own. Some of them even have
hardware trimmer knobs you can set on them to adjust values instead of
changing the code in the processor.

Jamie


Grant

unread,
Apr 2, 2012, 12:04:34 AM4/2/12
to
On Sun, 01 Apr 2012 19:32:05 -0700, Don Y <th...@isnotme.com> wrote:

>Hi Roger,
>
>On 4/1/2012 6:23 PM, RogerN wrote:
>
>> That sounds similar to what I have heard, that the inputs are read and
>> written to an input image table. The logic is processes, outputs written to
>> output image table, then the output image is written to the actual outputs.
>
>This varies with manufacturer/implementation. Some implementations
>make the "output image" available to "subsequent rungs" *before*
>the hardware is updated. This allows implementations that have
>predictable rung scan orders to allow rungs to act like
>"sequential statements" in a procedural language.
>
>E.g. (fixed width font):
>
> Start Stop Run
>+---[ ]--+---[/]--------( )---+
>| | |
>| | |
>| Run | |
>+---[ ]--+ |
>| |
>| Run Motor |
>+---[ ]-----------------( )---+
>
>causes "Motor" to be active "shortly after" Start is pressed
>(actuating "Run") *without* requiring another pass around the
>big_loop() to transfer the "output image table" to the
>physical outputs.

Way back when I was writing cooperative multitasking OS for 8-bit
microcontrollers I had several IRQ run decrement towards but not
below zero counters. This let me specify timeouts without regards
to the overall control loop, as each 'process' had its own reference
to the notion of passing 'real time'.

Of course the down side to writing cooperative multitasking was
that each 'process' had to run a state machine in order to yield
when there was nothing to do.

The main loop was simply a list of call statements.
>
>This sort of thing can lead to races as the order in which
>you arrange the rungs determines how the logic works.

Implemented as above, races could not occur. One reentrant task
locked the 'gate' into accepting further calls; that's a far as I
wanted to go while writing multitasking code in assembler.
>
>> Most of the processors I have worked with have an option to look at the
>> listing that looks kind of like an assembly language listing. I know you
>> can make FOR loops inside the ladder programs I have used, but I've never
>> needed to use them.
>>
>> So I'm thinking I could take a rung like this:

Sorry, it is decades since I was warped into programming an industrial
controller :)
...
OT, very OT for this group :)

Grant.

Don Y

unread,
Apr 2, 2012, 12:27:59 AM4/2/12
to
Hi Grant,
I inevitably have a "system time" that is updated by the jiffy.
So, there are only two pieces of code looking at/modifying
this variable: the IRQ and the *one* "task" that examines
it and conveys that value to the timing subsystem. (This
cuts down on the number of mutexes that have to be invoked
to transact with this datum)

The timing subsystem periodically (e.g., if a "big_loop"
implementation, then it is invoked "once per iteration")
and updates the "armed" timers to reflect the time that
has passed (present_system_time - previous_system_time).
This can be done via delta queues or simple brute force
(subtract elapsed_time from all timers and clamp result
at 0).

[if a preemptive implementation, I make ready any tasks
waiting on timers, etc.]

This allows tasks to examine timers (in a big_loop) without
having to worry about the timer being updated *while* it is
being examined (since the timer task has run "elsewhere"
in the loop).

A timer then becomes just another entry in an array of timers
(for example). Very low overhead. And, piggy backs on *one*
ISR (only to update the "system_time" variable -- also an
inexpensive operation)

The problem with this approach is that you can't measure *elapsed*
time with these timers because they use saturated arithmetic.

OTOH, they are really easy to set, abort, test, etc.

> Of course the down side to writing cooperative multitasking was
> that each 'process' had to run a state machine in order to yield
> when there was nothing to do.
>
> The main loop was simply a list of call statements.

Yes. Hence my analogy, here, to "a list of function invocations".

>> This sort of thing can lead to races as the order in which
>> you arrange the rungs determines how the logic works.
>
> Implemented as above, races could not occur. One reentrant task
> locked the 'gate' into accepting further calls; that's a far as I
> wanted to go while writing multitasking code in assembler.
>
>>> Most of the processors I have worked with have an option to look at the
>>> listing that looks kind of like an assembly language listing. I know you
>>> can make FOR loops inside the ladder programs I have used, but I've never
>>> needed to use them.
>>>
>>> So I'm thinking I could take a rung like this:
>
> Sorry, it is decades since I was warped into programming an industrial
> controller :)

Ditto. For me, a 1771 (or maybe it was a 1772? The CPU had
one number while the field modules had the other number) in the
mid 80's. The cost was ridiculous and the capabilities so
limited that it was (IMO) a silly way to do things!

Don Y

unread,
Apr 2, 2012, 9:30:08 AM4/2/12
to
a搖al搗搽y, noun: a similarity between like features of two things,
ON WHICH A COMPARISON MAY BE BASED: e.g., the analogy between the
heart and a pump.

I didn't find *your* ANALOGY in the above.

It would be like a cardiologist describing the role of calcium
and potassium ions in how the sinoatrial node's action potential
is conducted, first, to the atrioventricular node as the atria
contract and, then, through the ventricular endocardium to the
myocardium to bring about the contraction of the ventricles
and the ultimate repolarization of the heart at the start of the
next cycle (um, "beat").

Wanna bet "the heart is like a pump" conveys far more useful
information than all of the above?

RogerN

unread,
Apr 2, 2012, 9:19:55 PM4/2/12
to
"Don Y" wrote in message news:jlb336$i00$1...@speranza.aioe.org...

Hi Roger,

>This varies with manufacturer/implementation. Some implementations
>make the "output image" available to "subsequent rungs" *before*
>the hardware is updated. This allows implementations that have
>predictable rung scan orders to allow rungs to act like
>"sequential statements" in a procedural language.
>
>E.g. (fixed width font):
>
> Start Stop Run
>+---[ ]--+---[/]--------( )---+
>| | |
>| | |
>| Run | |
>+---[ ]--+ |
>| |
>| Run Motor |
>+---[ ]-----------------( )---+
>
>causes "Motor" to be active "shortly after" Start is pressed
>(actuating "Run") *without* requiring another pass around the
>big_loop() to transfer the "output image table" to the
>physical outputs.
>
>This sort of thing can lead to races as the order in which
>you arrange the rungs determines how the logic works.

<snip>
>> So I'm thinking I could take a rung like this:
>> A C
>> |--+--||---+--||----( )--|
>> | B |
>> +--|/|_+
>>
>> and write it something like this:
>>
>> if((conditionA==1 || conditionB!=1)&& ConditionC==1) turn on output;
>> else turn off output;
>
>A more intuitive form might be:
>
> if ((conditionA || !conditionB) && ConditionC) {
> turn on output;
> } else {
> turn off output;
> }
>
>And, for something this straightforward:
>
> output = (conditionA || !conditionB) && ConditionC;

Yes, this is what I'm talking (err typing) about, the loop is scanned as
fast as possible and the sequence is controlled by when conditions come
true, not when the processor happens to get there. I figure the above would
execute in a few microseconds.

Here's an example:
I have a 24 bit A/D converter connected to my Arduino. My function to read
the A/D consists of sending a conversion command, waiting on ready, reading
the value into bytes, calculating long analogIn = byte3 * 65536 +byte2 * 256
+ byte1;

I want to change this to something like:

If(adtimer.dn) startConversion(); // after whatever timer is done, start the
A/D conversion process.

if(conversionready) analogin=readAD();

This way I'm not wasting time waiting on the conversion to be ready.

I have it working OK as my inefficient example above, I'm wanting to add a
display and buttons.
I think I'll use a structure for the timer memory type and have a done bit
like we use in PLC's. This way I can set up the condition to enable the
timer, example a fault timer that ignores faults less than 1 second.

if(faultCondition) OnDelayTimer(faultTimer);

If (faultTimer.done) //trigger appropriate alarm. Some faults would be
immediate and wouldn't use a timer.

In your blink example I could use fastblink.done and slowblink.done as the
bits for flashing. Or the blink rate could be adjusted by changing
blink.preset

Another example

GripperOpenOutput = (auto_mode && open_auto)||(manual_mode &&
gripper_open_button);

RogerN


Don Y

unread,
Apr 3, 2012, 12:32:35 AM4/3/12
to
Hi Roger,

On 4/2/2012 6:19 PM, RogerN wrote:
> "Don Y" wrote in message news:jlb336$i00$1...@speranza.aioe.org...

>>> So I'm thinking I could take a rung like this:
>>> A C
>>> |--+--||---+--||----( )--|
>>> | B |
>>> +--|/|_+
>>>
>>> and write it something like this:
>>>
>>> if((conditionA==1 || conditionB!=1)&& ConditionC==1) turn on output;
>>> else turn off output;
>>
>> A more intuitive form might be:
>>
>> if ((conditionA || !conditionB)&& ConditionC) {
>> turn on output;
>> } else {
>> turn off output;
>> }
>>
>> And, for something this straightforward:
>>
>> output = (conditionA || !conditionB)&& ConditionC;
>
> Yes, this is what I'm talking (err typing) about, the loop is scanned as
> fast as possible and the sequence is controlled by when conditions come
> true, not when the processor happens to get there. I figure the above would
> execute in a few microseconds.

The problem comes when condition is actually an expression -- and
possibly including function invocations. Then, it gets harder
to control the time required (especially if the functions are opaque).

I suggested wrapping "rungs" in void functions so that you could
conceptually package everything associated with a rung in that
function. E.g., if you want to maintain some "state" that the
function examines and updates (without it becoming a formal
"output"), you could embed the manipulation of that state in
the function instead of exposing it in big_loop().

> Here's an example:
> I have a 24 bit A/D converter connected to my Arduino. My function to read
> the A/D consists of sending a conversion command, waiting on ready, reading
> the value into bytes, calculating long analogIn = byte3 * 65536 +byte2 * 256
> + byte1;
>
> I want to change this to something like:
>
> If(adtimer.dn) startConversion(); // after whatever timer is done, start the
> A/D conversion process.
>
> if(conversionready) analogin=readAD();
>
> This way I'm not wasting time waiting on the conversion to be ready.

This is a perfect example of how wrapping this ALL in rung_ReadADC()
can group the details of this "task" together (so you can conceptualize
it as a single "job") without exposing the innards needlessly to
unrelated bits of code.

For example (untested pseudocode):

void
rungReadADC(void)
{
static state = UNINITIALIZED:

switch (state) {
case UNINITIALIZED:
// do whatever you need to do to set up the hardware
state = IDLE;
break;
case IDLE:
// hardware is ready, start the conversion
startConversion();
load_timer(ADC_TIMER, ADC_INTERVAL);
state = CONVERTING;
break;
case CONVERTING:
if (!expired(ADC_TIMER))
break; // stay in the same state!
state = READY;
break;
case READY:
ADCvalue = readADC();
state = IDLE;
break;
default:
/* NOT REACHED */
}

return;
}

I.e., you have built a little "program" (task) that knows how to
deal with an ADC. It doesn't care how fast the scan time is. It
ensures the ADC's output is not read until ADC_TIMER has expired
(if the scan time starts to get sluggish, it might end up reading
the ADC far less often than you had hoped!)

It never does anything costly in any invocation (look at how little
is in each state's "case").

And, if you need to modify it to, for example, support a front-end
multiplexor, you can add states to switch the mux to the desired
input (perhaps it routinely examines all N inputs and makes them
available AT THE SAME TIME to the rest of the application -- instead
of "staggered"), delay for some amount of time while the mux settles,
then, later, advance to the CONVERTING state when you know the
inputs are nice and stable.

The switch statement gives you finer control over how much/little
gets done in any given invocation (i.e., "scan instance"). You can
also *cheat* and have big_loop() look like:

big_loop()
{
...
rungReadADC();
rung27();
rung35();
rung78();
...
rungReadADC(); // !!!!!
rung19();
rung44();
...
}

I.e., rungReadADC now gets twice as much CPU time as it would otherwise
have had if it only appeared *once* in big_loop(). Yet, the above
code continues to work properly regardless!

RogerN

unread,
Apr 4, 2012, 8:22:43 PM4/4/12
to

I got a timer working, also used the same data structure to make a counter.

Instead of longs I used ints and a time base. The time base simply uses the
milliseconds divided by the time base. In other words, if you want to time
in milliseconds, you use a time base of 1 and maximum time is 65535
milliseconds. If you want to time in seconds, the time base would be 1000
and maximum time should be 65535 seconds. So I guess this should also work
for minutes with a time base of 60,000. Need more than that you can trigger
a counter for hours, days, or whatever you want. I made the switch from
longs to ints to save memory and because I don't need timers that can time a
month with millisecond resolution.

I'll paste my Arduino program at the bottom, it blinks a LED, prints
millisecond time, and counts 20 blinks before resetting the counter. My
weird timer and counter names come from Allen Bradley PLC addressing (T4,
C5).

Most of the loop time comes from the Serial.print statements, at 9600 baud I
get ~ 12 millisecond loop time, at 38,400 baud I get 40 lines of print in
0.1 seconds, at 57,600 baud I get 1-2 millisecond loop time.

In my example I use a preset of 50 and a base of 10 for 1/2 second ON, 1/2
second OFF.

My guess is that others here will have some ideas for improvement. I'm
pretty green at C programming but I think this kind of idea, timing without
waiting & wasting processor clocks, can help to get a processor to do more
tasks at once. In PLC programming I use mostly instructions that are the
equivalent of ANDs, ORs, NOTs, plus timers and the occasional counters,
shift registers...

// Timers & Counters, Testing Not Complete

struct timerdata
{
unsigned int accum, starttime;
boolean TT,DN,EN;
}
T4_0, T4_1,C5;


void setup()
{
Serial.begin(9600);
pinMode(13,OUTPUT);
}

void loop()
{
Serial.print(millis());
Serial.print(" ");
TON(&T4_0,!T4_1.DN,50,10);
TON(&T4_1,T4_0.DN,50,10);
digitalWrite(13, T4_0.DN);
CTU(&C5,T4_0.DN,1000);
Serial.println(C5.accum);
if(C5.accum>=20)RES(&C5);
}

int TON(struct timerdata *sp, int EN, int preset, byte base) // Time On
Timer
{
if(!base)base=1; // no divide by zero if base unspecified
//sp->preset = preset;
if(!sp->EN && EN)// Enable has transitioned from falset to true
{
sp->starttime = millis()/base;
sp->EN = EN;
sp->accum = 0;
}
if(EN && !sp->DN)
{
sp->accum = millis()/base-sp->starttime;
sp->DN=sp->accum >= preset;
sp->TT = 1;
}
if(sp->DN)sp->TT=0;
if(!EN)
{
sp->EN=0;
sp->DN=0;
sp->TT=0;
}

}

int CTU(struct timerdata *sp, int EN, int preset)// Count Up Counter
{
//sp->preset = preset;
if(!sp->EN && EN)// Enable has transitioned from falset to true
{
sp->accum++;
sp->EN = EN;
if(sp->accum>=preset)sp->DN=1;
}
if(!EN)sp->EN=0;
if(sp->DN)sp->TT=0;
}

void RES(struct timerdata *sp)
{
sp->accum=0;
sp->EN=0;
sp->TT=0;
sp->DN=0;
}

RogerN


josephkk

unread,
Apr 5, 2012, 7:31:30 AM4/5/12
to
I think you are looking for a timer manager, a common component of a
microkernel. Your data structure is more than sufficient. Add a linked
list of times and keep track of the next one to expire and keep flags to
the semaphore for that tasklet to execute. As they expire, you can
"reset" them with a new call to set_timeout(). Yes, you are pretty close
to writing a microRTOS.

?-)

Don Y

unread,
Apr 5, 2012, 11:09:36 AM4/5/12
to
Hi Roger,

On 4/4/2012 5:22 PM, RogerN wrote:
> I got a timer working, also used the same data structure to make a counter.
>
> Instead of longs I used ints and a time base. The time base simply uses the
> milliseconds divided by the time base. In other words, if you want to time
> in milliseconds, you use a time base of 1 and maximum time is 65535
> milliseconds. If you want to time in seconds, the time base would be 1000
> and maximum time should be 65535 seconds. So I guess this should also work
> for minutes with a time base of 60,000. Need more than that you can trigger
> a counter for hours, days, or whatever you want. I made the switch from
> longs to ints to save memory and because I don't need timers that can time a
> month with millisecond resolution.

I don't see the savings. You've just moved the "data" to
a different place (i.e., the function invocation) -- which may
be an acceptable trade (if the data can be const, there!).

However, your "base" is declared as a byte. Awful hard
to represent "1000" -- to use units of "seconds" -- in a
byte! :-/ Even harder to represent "60000" for units of
minutes!

> I'll paste my Arduino program at the bottom, it blinks a LED, prints
> millisecond time, and counts 20 blinks before resetting the counter. My
> weird timer and counter names come from Allen Bradley PLC addressing (T4,
> C5).

Unless you are specifically intending the code to be read and
maintained by folks with that background, I suggest you adopt
"friendlier" names. Code gets read more often than written
so the cost of typing a few extra characters is peanuts if
it improves comprehension.

> Most of the loop time comes from the Serial.print statements, at 9600 baud I
> get ~ 12 millisecond loop time, at 38,400 baud I get 40 lines of print in
> 0.1 seconds, at 57,600 baud I get 1-2 millisecond loop time.

These, presumably, are present in the Arduino library?

> In my example I use a preset of 50 and a base of 10 for 1/2 second ON, 1/2
> second OFF.
>
> My guess is that others here will have some ideas for improvement. I'm
> pretty green at C programming but I think this kind of idea, timing without
> waiting& wasting processor clocks, can help to get a processor to do more
> tasks at once. In PLC programming I use mostly instructions that are the
> equivalent of ANDs, ORs, NOTs, plus timers and the occasional counters,
> shift registers...
>
> // Timers& Counters, Testing Not Complete
>
> struct timerdata
> {
> unsigned int accum, starttime;
> boolean TT,DN,EN;
> }
> T4_0, T4_1,C5;

Point of style:
typedef struct {
unsigned int accum;
unsigned int starttime;
boolean TT;
boolean DN;
boolean EN;
} timer_t;

Then, you can use:
timer_t T4_0, T4_1, C5;
as a nice shorthand.

Another point of style:
timer_t T4_0; // timer used for ... ?
timer_t T4_1; // timer used for ... ?
timer_t C5; // counts number of blinks

It's arguable whether you want to use a different type for
counters -- even if just in terms of *name* (to draw the
distinction between the two conceptual types)

> void setup()
> {
> Serial.begin(9600);
> pinMode(13,OUTPUT);
> }
>
> void loop()
> {
> Serial.print(millis());
> Serial.print(" ");

These will effectively determine the speed at which your loop
can operate. Change the baudrate and you'll see that the loop
speed will change.

If you *want* the timestamp of each loop invocation (iteration),
then you have to do this (or something equivalent).

OTOH, if you only want to emit data when it won't otherwise
impact your loop speed, then you have to resort to other
mechanisms.

> TON(&T4_0,!T4_1.DN,50,10);
> TON(&T4_1,T4_0.DN,50,10);
> digitalWrite(13, T4_0.DN);
> CTU(&C5,T4_0.DN,1000);
> Serial.println(C5.accum);
> if(C5.accum>=20)RES(&C5);

Gack! Take advantage of the obvious improvements in capabilities
that this sort of programming environment offers and:
- get rid of the hard constants
- add some commentary

E.g.,

#define NUMBER_OF_BLINKS (1000)
...
count(&blink_counter, blink_timer.done, NUMBER_OF_BLINKS);

is arguably more obvious in it's intent.

> }
>
> int TON(struct timerdata *sp, int EN, int preset, byte base) // Time On
> Timer

why is TON declared to return an int?
why *doesn't* it?
why hasn't your compiler warned you about this?

typedef unsigned int time_t; // represents a time

(N.B. this conflicts with other "standard" uses of time_t!)

typedef enum {MS = 1, SEC, MIN, HR, DAY, WK, MON, YR, CEN, MIL...) base_t;

void
time(
timer_t *theTimer, // points to timer's representation
boolean enable, // false->true transition starts timer
time_t duration, // duration of timer after enabled
base_t units // units of measure used for this time_t
)

> {
> if(!base)base=1; // no divide by zero if base unspecified

switch (units) {
case MIN:
prescaler = 60 * 1000;
break;
case SEC:
prescaler = 1000;
break;
default:
// warn user he forgot to specify units?
case MS:
prescaler = 1;
break;
}

> //sp->preset = preset;
> if(!sp->EN&& EN)// Enable has transitioned from falset to true
> {
> sp->starttime = millis()/base;

Note that your implementation has the consequence of forcing all
starttime's to be truncated to the start of the *preceding*
"time unit" (base). I.e., if you count in seconds and the *first*
time this is invoked the millis() value happens to be "999"
(i.e., the current time is 0.999 seconds), then your timer
will record an entire second as having passed EXACTLY ONE MS LATER!
(when millis()/1000 is now "1" -- previously "0")

This has consequences when viewed with respect to other co-operating
timers. E.g., if one is using seconds and another minutes, then
the relative errors in each will vary depending on when each is
started.

Also, two timers started in two different TON invocations may
or may not be in sync. E.g., trying to flash a light in
one and beep in another could (or could NOT!) result in a
phase difference between them if their respective "starttime"
notions are different.

I mentioned this previously -- you may want to capture
millis() in a pseudo-global variable at the start of
the loop and have all of your timing routines refer to
that, instead. This ensures that everyone in a single
loop iteration has the same sense of "what time is it?"
regardless of where in the loop their code executed!

> sp->EN = EN;
> sp->accum = 0;
> }
> if(EN&& !sp->DN)
> {
> sp->accum = millis()/base-sp->starttime;
> sp->DN=sp->accum>= preset;
> sp->TT = 1;
> }
> if(sp->DN)sp->TT=0;
> if(!EN)
> {
> sp->EN=0;
> sp->DN=0;
> sp->TT=0;
> }
>
> }

For personal preference, I would also rewrite the code to
make the state machine within it more obvious: moving
from IDLE to ENABLED to DONE, etc. (what's the role of TT?)

HTH...

RogerN

unread,
Apr 6, 2012, 6:28:36 PM4/6/12
to
"Don Y" wrote in message news:jlkcjf$hce$1...@speranza.aioe.org...

Hi Roger,

>On 4/4/2012 5:22 PM, RogerN wrote:
>> I got a timer working, also used the same data structure to make a
>> counter.
>>
>> Instead of longs I used ints and a time base. The time base simply uses
>> the
>> milliseconds divided by the time base. In other words, if you want to
>> time
>> in milliseconds, you use a time base of 1 and maximum time is 65535
>> milliseconds. If you want to time in seconds, the time base would be
>> 1000
>> and maximum time should be 65535 seconds. So I guess this should also
>> work
>> for minutes with a time base of 60,000. Need more than that you can
>> trigger
>> a counter for hours, days, or whatever you want. I made the switch from
>> longs to ints to save memory and because I don't need timers that can
>> time a
>> month with millisecond resolution.
>
>I don't see the savings. You've just moved the "data" to
>a different place (i.e., the function invocation) -- which may
>be an acceptable trade (if the data can be const, there!).

Is it possible to enter a number as a function parameter and assign it to
the structure? There are 3 possibilities that I know of, assign the data
before the function is called, use data as a parameter then move to
structure inside of function, or if not needed in both places then just use
it as a parameter for the function.

>However, your "base" is declared as a byte. Awful hard
>to represent "1000" -- to use units of "seconds" -- in a
>byte! :-/ Even harder to represent "60000" for units of
>minutes!

Originally I was planning to use the "base" as time base similar to the way
it's used in PLC's, much like your "switch ... case" example below. In the
Allen Bradley PLC5 they use a time base of 0.01 Sec, 0.1 Sec or 1.0 Sec time
base, they also use signed integer for a preset but since I can't time to a
negative time I went with unsigned. I had a "base" in the struct with the
other unsigned int's, removed from the struct but forgot to change it in the
function parameter.

>> I'll paste my Arduino program at the bottom, it blinks a LED, prints
>> millisecond time, and counts 20 blinks before resetting the counter. My
>> weird timer and counter names come from Allen Bradley PLC addressing (T4,
>> C5).
>
>Unless you are specifically intending the code to be read and
>maintained by folks with that background, I suggest you adopt
>"friendlier" names. Code gets read more often than written
>so the cost of typing a few extra characters is peanuts if
>it improves comprehension.

In use I would use meaningful names but just for testing I used short names
similar to what I work with, actually in the PLC it's the first timer is
T4:0, it's done bit is T4:0.DN or T4:0/DN. If T4:0 was to be used for a
global flashing bit for pushbutton lights, tower lights, to blink stuff on
the operator panel, I'd assign it a symbol like "Flash".

>> Most of the loop time comes from the Serial.print statements, at 9600
>> baud I
>> get ~ 12 millisecond loop time, at 38,400 baud I get 40 lines of print in
>> 0.1 seconds, at 57,600 baud I get 1-2 millisecond loop time.
>
>These, presumably, are present in the Arduino library?

Yes, it goes up to 115,200 but it doesn't run as well as 57,600 on my PC
with all the line feeds. I saw some C source that includes Arduino.h, I've
always used the Arduino environment and haven't tried programming the AVR in
C by including Arduino's library.

>> // Timers& Counters, Testing Not Complete
>>
>> struct timerdata
>> {
>> unsigned int accum, starttime;
>> boolean TT,DN,EN;
>> }
>> T4_0, T4_1,C5;
>
>Point of style:
> typedef struct {
> unsigned int accum;
> unsigned int starttime;
> boolean TT;
> boolean DN;
> boolean EN;
> } timer_t;
>
>Then, you can use:
> timer_t T4_0, T4_1, C5;
>as a nice shorthand.
>
>Another point of style:
> timer_t T4_0; // timer used for ... ?
> timer_t T4_1; // timer used for ... ?
> timer_t C5; // counts number of blinks
>
>It's arguable whether you want to use a different type for
>counters -- even if just in terms of *name* (to draw the
>distinction between the two conceptual types)

Would probably be more memory efficient if I created a different struct for
counters since I don't need "starttime".

>> void setup()
>> {
>> Serial.begin(9600);
>> pinMode(13,OUTPUT);
>> }
>>
>> void loop()
>> {
>> Serial.print(millis());
>> Serial.print(" ");
>
>These will effectively determine the speed at which your loop
>can operate. Change the baudrate and you'll see that the loop
>speed will change.

Right, I went from ~12 millisecond loops to ~1 millisecond loops going from
9600 baud to 57,600 baud. Normally I wouldn't write to a display that
often.

>If you *want* the timestamp of each loop invocation (iteration),
>then you have to do this (or something equivalent).
>
>OTOH, if you only want to emit data when it won't otherwise
>impact your loop speed, then you have to resort to other
>mechanisms.

Yes, I added another timer I called printtime that I only print when the
timer is done, so I can update the display at the time I preset.

>> TON(&T4_0,!T4_1.DN,50,10);
>> TON(&T4_1,T4_0.DN,50,10);
>> digitalWrite(13, T4_0.DN);
>> CTU(&C5,T4_0.DN,1000);
>> Serial.println(C5.accum);
>> if(C5.accum>=20)RES(&C5);
>
>Gack! Take advantage of the obvious improvements in capabilities
>that this sort of programming environment offers and:
>- get rid of the hard constants
>- add some commentary

Once I got the functions to compile, I wrote a quick and dirty loop to test
it out. The counter was a quick and dirty afterthought I tried out. It
does need some commentary though to help explain how it (is supposed to)
work.

>E.g.,
>
>#define NUMBER_OF_BLINKS (1000)
> ...
> count(&blink_counter, blink_timer.done, NUMBER_OF_BLINKS);
>
>is arguably more obvious in it's intent.
>
>> }
>>
>> int TON(struct timerdata *sp, int EN, int preset, byte base) // Time On
>> Timer
>
>why is TON declared to return an int?

In future I would like it to be able to be used in an if() function,
returning a 1 when done so the block after if() will execute after the timer
is done.

>why *doesn't* it?

I haven't put it in there yet, plan to have something like: return(*sp.DN);

>why hasn't your compiler warned you about this?

It might have, the Arduino environment uses the GCC AVR compiler AFAIK.
It would be better if I could do something like used in Siemens programming.
Their timer values are like S5T#1h30m25s for 1 hour 30 minutes and 25
seconds. They automatically select the time base that is required for that
time. If you enter a time of 500 milliseconds, the time base will be
milliseconds, etc. I don't remember all their time bases but they are more
limited to 9,990 seconds with a 10 second time base. Out of a 16 bit word
they use 3 digits of BCD and 2 bits are used for time base, other 2 bits, I
don't know if they are used.

>This has consequences when viewed with respect to other co-operating
>timers. E.g., if one is using seconds and another minutes, then
>the relative errors in each will vary depending on when each is
>started.

Would be nice to automatically use the highest resolution that will do the
job. For up to 65535 milliseconds, use milliseconds, up to 655.535
seconds, use 10 millisecond, up to 6553.5 seconds, use 0.1 sec, etc. If
someone needs better than that they can modify the function for long data
types.

>Also, two timers started in two different TON invocations may
>or may not be in sync. E.g., trying to flash a light in
>one and beep in another could (or could NOT!) result in a
>phase difference between them if their respective "starttime"
>notions are different.
>
>I mentioned this previously -- you may want to capture
>millis() in a pseudo-global variable at the start of
>the loop and have all of your timing routines refer to
>that, instead. This ensures that everyone in a single
>loop iteration has the same sense of "what time is it?"
>regardless of where in the loop their code executed!

I thought about having a "housekeeping" loop at the beginning or end of the
loop to update timers, etc. I'm guessing Allen Bradley does something like
that since their timer data has only PRESET, ACCUM, and the bits, no start
time. Is there a way for the program to know of every instance of the timer
data type and update every timer ACCUM every loop? Maybe a for() going from
first timer to last timer and setting accumulated value properly for each
timer according to it's time base?

>> sp->EN = EN;
>> sp->accum = 0;
>> }
>> if(EN&& !sp->DN)
>> {
>> sp->accum = millis()/base-sp->starttime;
>> sp->DN=sp->accum>= preset;
>> sp->TT = 1;
>> }
>> if(sp->DN)sp->TT=0;
>> if(!EN)
>> {
>> sp->EN=0;
>> sp->DN=0;
>> sp->TT=0;
>> }
>>
>> }
>
>For personal preference, I would also rewrite the code to
>make the state machine within it more obvious: moving
>from IDLE to ENABLED to DONE, etc. (what's the role of TT?)

TT indicates that the timer is actively timing. It's enabled but not done
yet. In my example of flashing the LED, I could alternate 2 LED's having 1
come on when T4_0.DN and the other come on when T4_0.TT.

>HTH...

Yes, helps very much, what I pasted was quick, dirty, and barely functional.
It needs some refining, fixing, ... One goal is to get handy functional
timers for my own use but I also want to encourage others to learn to
program without tying up the processor waiting for one thing to happen.

Like instead of:
Flash = 1;
delay(500);
Flash = 0;
delay(500);

use the timers that don't hold up the processor so it can do other things
until it is time for flash to turn on or off.

thanks!

RogerN


Don Y

unread,
Apr 7, 2012, 5:21:17 PM4/7/12
to
Hi Roger,

[much elided]
You can create types that have a hierarchical relationship.
E.g., if a timer is a *counter* that has the additional
characteristic of having a "starttime", then you can:

typedef struct {
unsigned int accum;
// boolean timing; redundant? timing = enabled && !done?
boolean done;
boolean enabled;
} counter_t;

typedef struct {
counter_t offset;
unsigned int starttime;
} timer_t;

So, much of the same "counter" code can be used to update a
"timer_t" by passing the "offset" counter_t contained within the
timer_t to that counter_t code.

[There are many issues with this simplification... :< ]

>>> void setup()
>>> {
>>> Serial.begin(9600);
>>> pinMode(13,OUTPUT);
>>> }
>>>
>>> void loop()
>>> {
>>> Serial.print(millis());
>>> Serial.print(" ");
>>
>> These will effectively determine the speed at which your loop
>> can operate. Change the baudrate and you'll see that the loop
>> speed will change.
>
> Right, I went from ~12 millisecond loops to ~1 millisecond loops going from
> 9600 baud to 57,600 baud. Normally I wouldn't write to a display that
> often.

Since you (probably) don't want the "display" to dictate performance
yet *probably* want to emit some sort of periodic "status" to the
user, you can, instead, have a "task" (sorry to keep falling back
on this term) that checks to see if the PREVIOUS "status update" has
been conveyed to the user. And, if so, it can create a *new*
status update message using sprintf() in lieu of printf() to
fabricate the message in a *buffer*. At this point, you mark the
"status update" activity as "in progress" (i.e., "not yet conveyed
to the user"). Hereafter, that causes the output device to be
examined to determine if it can accept more data (characters).
If so, the next character(s) from the buffer are passed to the
output device NEVER WAITING FOR THEM TO BE TRANSMITTED so that
the "OS"/interrupt system can push them out while your code is
doing other things.

Each time around the loop, you check to see if you can move another
character(s) from the buffer to the I/O device. Eventually, all
of the characters will have been moved from the buffer to the
physical I/O device and you will declare the status update as
having been "conveyed to the user" -- so, you can now prepare a
*new* update.

This approach throttles the update frequency to match the capabilities
of the I/O device -- instead of throttling the "loop()" to match the
capabilities of the I/O device.

[Of course, sprintf() is still a bloated pig but at least it
doesn't also carry the overhead of the physical I/O with it!]

>> If you *want* the timestamp of each loop invocation (iteration),
>> then you have to do this (or something equivalent).
>>
>> OTOH, if you only want to emit data when it won't otherwise
>> impact your loop speed, then you have to resort to other
>> mechanisms.


>>> int TON(struct timerdata *sp, int EN, int preset, byte base) // Time On
>>> Timer
>>
>> why is TON declared to return an int?
>
> In future I would like it to be able to be used in an if() function,
> returning a 1 when done so the block after if() will execute after the timer
> is done.

OK.
#define INTERVAL(hr, min, sec) (1000*(60*hr + (60*min + sec)))

> They automatically select the time base that is required for that
> time. If you enter a time of 500 milliseconds, the time base will be
> milliseconds, etc. I don't remember all their time bases but they are more
> limited to 9,990 seconds with a 10 second time base. Out of a 16 bit word
> they use 3 digits of BCD and 2 bits are used for time base, other 2 bits, I
> don't know if they are used.

#define TIMEBASE(ms) ( ( ms < 65535) ? MS : \
( (ms/1000) < 65535) ? SEC : \
( ((ms/1000)/60) < 65535) ? MIN : \
((((ms/1000)/60)/60) < 65535) ? HR : \
UNDEFINED );

[typos left as an exercise for the reader]

Not the most efficient way of doing this but fairly obvious,
syntactically.

(Easier to have a genuine function that returns a (time, base) tuple)

You could also parse the character string at runtime (ick!) to
adopt a syntax closer to the one you described above.

>> This has consequences when viewed with respect to other co-operating
>> timers. E.g., if one is using seconds and another minutes, then
>> the relative errors in each will vary depending on when each is
>> started.
>
> Would be nice to automatically use the highest resolution that will do the
> job. For up to 65535 milliseconds, use milliseconds, up to 655.535
> seconds, use 10 millisecond, up to 6553.5 seconds, use 0.1 sec, etc. If
> someone needs better than that they can modify the function for long data
> types.

See above.

>> Also, two timers started in two different TON invocations may
>> or may not be in sync. E.g., trying to flash a light in
>> one and beep in another could (or could NOT!) result in a
>> phase difference between them if their respective "starttime"
>> notions are different.
>>
>> I mentioned this previously -- you may want to capture
>> millis() in a pseudo-global variable at the start of
>> the loop and have all of your timing routines refer to
>> that, instead. This ensures that everyone in a single
>> loop iteration has the same sense of "what time is it?"
>> regardless of where in the loop their code executed!
>
> I thought about having a "housekeeping" loop at the beginning or end of the
> loop to update timers, etc.

Pull the "loop()" out of the *real* loop so you have a "real loop"
that contains the overhead that the "framework" provides (timers,
printing, etc.) and have *it* invoke "yourloop()" that does the
stuff that is "application specific":

realloop()
{
updatetimers();
scaninputs();
yourloop();
updateoutputs();
}

yourloop()
{
...
}

> I'm guessing Allen Bradley does something like
> that since their timer data has only PRESET, ACCUM, and the bits, no start
> time. Is there a way for the program to know of every instance of the timer
> data type and update every timer ACCUM every loop? Maybe a for() going from
> first timer to last timer and setting accumulated value properly for each
> timer according to it's time base?
>
>>> sp->EN = EN;
>>> sp->accum = 0;
>>> }
>>> if(EN&& !sp->DN)
>>> {
>>> sp->accum = millis()/base-sp->starttime;
>>> sp->DN=sp->accum>= preset;
>>> sp->TT = 1;
>>> }
>>> if(sp->DN)sp->TT=0;
>>> if(!EN)
>>> {
>>> sp->EN=0;
>>> sp->DN=0;
>>> sp->TT=0;
>>> }
>>>
>>> }
>>
>> For personal preference, I would also rewrite the code to
>> make the state machine within it more obvious: moving
>>from IDLE to ENABLED to DONE, etc. (what's the role of TT?)
>
> TT indicates that the timer is actively timing. It's enabled but not done
> yet. In my example of flashing the LED, I could alternate 2 LED's having 1
> come on when T4_0.DN and the other come on when T4_0.TT.

TT = EN && !DN?

>> HTH...
>
> Yes, helps very much, what I pasted was quick, dirty, and barely functional.
> It needs some refining, fixing, ... One goal is to get handy functional
> timers for my own use but I also want to encourage others to learn to
> program without tying up the processor waiting for one thing to happen.
>
> Like instead of:
> Flash = 1;
> delay(500);
> Flash = 0;
> delay(500);
>
> use the timers that don't hold up the processor so it can do other things
> until it is time for flash to turn on or off.

The above, IMO, is more intuitive -- but, requires extra support
so that "delay" can yield the processor to other tasks when it
decides that the timer has not yet expired.

[since that is done so often, it is the sort of service I would
move into an OS]

Good luck!
0 new messages