"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