PDA

View Full Version : Elapsed Timer Demo



Darrel Taylor
- 26th December 2003, 22:22
Elapsed Timer Demo

Attached to this post is a Zip file that contains a "Drop-In" elapsed timer that uses Timer1 and interrupts. It's waaay down there at the bottom of all this dribble. This may get rather long winded, so my first suggestion is to scroll down to the bottom of this post and check out the file then come back and read this post later.

--------------------- ----------------------- -----------------------------
The files contained in the Zip are:

Test_Elapsed_SER.pbp ' Demo of Elapsed Timer using serout2 command and HyperTerminal
Test_Elapsed_LCD.pbp ' Demo of Elapsed Timer using an LCD
Elapsed.bas ' Elapsed Timer include file
ASM_INTS.bas ' Assembly language Interrupt Stubs

Note: These files are intended to be used on 14-bit core PICS (12F, 16C and 16F) that have a TIMER1 module.
Note2: They are written for use as the only interrupt in the program. If you need to use other interrupts as well, the program will have to be modified before it will work.
Note3: It will NEVER work in conjunction with PBP's ON INTERRUPT statement.

In it's simplest form, this is all it takes to use the Elapsed Timer:

Include "Elapsed.pbp"
Gosub ResetTime ' Reset Time to 0d-00:00:00.00
Gosub StartTimer ' Start the Elapsed Timer

This will create a Clock counting at 1/100 seconds. It runs in the background of PBP without any other program intervention required.

The time is kept in the variables:

Ticks var byte ' 1/100th of a second
Seconds var byte ' 0-59
Minutes var byte ' 0-59
Hours var byte ' 0-23
Days var word ' 0-65535
The time can be easily displayed with a single line:

LCDout $FE,2, dec Days,"d-",dec2 Hours,":",dec2 Minutes,":",dec2 Seconds

For each of the variables (Seconds, Minutes, Hours and Days) there is a flag that indicates when the value of that variable has changed.
The Flags are:

SecondsChanged var bit
MinutesChanged var bit
HoursChanged var bit
DaysChanged var bit
So, if you wanted to display the time like a clock, you could wait until SecondsChanged = 1, display the time, then reset the flag.

Loop1:
if SecondsChanged = 1 then
LCDout $FE,2, dec Days,"d-",dec2 Hours,":",dec2 Minutes,":",dec2 Seconds
SecondsChanged = 0
endif
Goto Loop1

If you only wanted to display the time each minute instead of every second just do the same thing using the MinutesChanged flag.

Loop1:
if MinutesChanged = 1 then
LCDout $FE,2, dec Days,"d-",dec2 Hours,":",dec2 Minutes<br> MinutesChanged = 0
endif
Goto Loop1

The timer can be Stopped and Started, like a stopwatch.

Gosub StopTimer
Gosub StartTimer

--------------------- ----------------------- -----------------------------
The Elapsed.bas include file also Includes another file, ASM_INTS.bas This file can also be used in your other programs as well. It handles the "Context Saving" that is required for any Assembly language interrupt.

It contains 2 macros:
INT_START
Saves the W, STATUS, PCLATH and FSR registers. This can be used at the beginning of your Interrupt routine.

INT_RETURN
Restores the W, STATUS, PCLATH and FSR registers after the Interrupt routine is finished and then returns from the Interrupt (RETFIE).

Using it in a normal Assembly language interrupt might look something like this:


Define INTHAND INT_CODE ' Tell PBP Where the code starts on an interrupt
ASM
INT_CODE
INT_START ' Save Context
... Your Interrupt routine goes here ...
INT_RETURN ' Restore Context
EndAsm


Well, I guess that covers most of it. if I've missed anything, or you still have questions, please don't hesitate to ask.

Happy holidays,

Bob_W
- 3rd September 2005, 22:03
Cool piece of code. I noticed something trying to use it..... I'm using a 12F675 at 4 mhz. I noticed that the ticks were incrementing at 1/4 the rate they were supposed to. Upon fishing around I thought about the fact that there's a 1:1, 1:2, 1:4, and 1:8 prescaler on this part, as well as many others. Forcing the prescaler off makes it count correctly. Just curious.... what part did you use when you wrote this?


Update... Nevermind.... I had left piece of my code in place that messed with the prescaler.

Darrel Taylor
- 4th September 2005, 20:59
That's the closest thing to "Hey it works" that I've gotten after it being here almost 2 years. :)

Thanks Bob!

Bob_W
- 5th September 2005, 00:48
Ok.... "Hey it works" Really well, I might add! A couple questions..
In elapsed.bas you've got:
if Ticks = 100 then
Ticks = Ticks-100
Is there a reason you subtract 100 instead of zeroing Ticks?

The question arises from a little excursion I had because of of the results I got doing subtraction to detect and handle rollovers. I found out that the first section evaluates true because of the unsigned nature of the math. I wound up reversing the sections and verifying the result for the rollover test wasn't > 255. Is there a better way. Also, what's the instruction count offset between 0x10000 - ((clock_freq / 4) /100) It looks like it's 7.

tmr is 80 Ticks sample at beginning of 500 ms interval
Ticks is 20 Ticks value now....

(The forum removed all my indentation, so it looks wierd)


ms100 = ticks
IF ms100 - tmr >= 50 THEN
GOSUB lptoggle
ELSE
IF ms100 < tmr THEN
IF (ms100 + 100) - tmr >= 50 THEN
GOSUB lptoggle
END IF
END IF
END IF
RETURN

lptoggle:
Toggle 0
RETURN

Darrel Taylor
- 5th September 2005, 22:09
Is there a reason you subtract 100 instead of zeroing Ticks?
Not really. The original program used ON INTERRUPT, and there was a possibility that Ticks might be higher than 100 by the time it got around to calculating the time. But when I switched it over to ASM interrupts, it wasn't needed anymore. Just never changed it.

And for the ms100 routine, I can't figure out what you're trying to do there.

If tmr = 80 then (ms100 - tmr >= 50) will be True when ms100 is between 0-79. So the output will be toggled on every loop. Then for 80-99 the (ms100 < tmr) will never evaluate True since it's always >= 80.

Maybe if you explained what you're trying to do, we can come up with something that'll work.
<br>

Bob_W
- 6th September 2005, 03:46
Basically, I want to turn something on - like an LED (later it will actually be a 7 HP siren with a longer on and off time) for 500 ms. I go through all the decision process to decide it should be turned on. Then....

I save the current ticks value - tmr - # of ticks at start, which is 16 for this grab.

I turn on the port - for the test program, I'm just toggling an LED at 500 ms intervals.

I come out of the main program loop every pass to see if I have expired my timer. Get current ticks - ms100 - we'll say I get 65 for this grab.

Sooo... IF ms100 - tmr >= 50 THEN 65 - 16 = 49.... almost there..

Where you run into the problem is if you grab, say 80 for tmr.

For 500 ms to elapse, you'd have to roll over the 100 mark. So, if you try to do the test IF ms100 - tmr >= 50 THEN with tmr = 80, and ms100 = 10 it evaluates true because, under pic math 10 - 80 = 250, or 65391 if you look at all 16 bits. So I figured out that you have to test to see if ms100 (the current tick value) is less than tmr (the tick value when you started timing) and if so, add 100 to the ms100 before subtracting tmr to see if it's 50 or better elapsed.

Sure you do timing with pause, but you can't look at change of state while you wait for pause to expire. Your timing routine makes it possible watch inputs, and manipulate and time multiple outputs at the same time.

I used to run into this when doing timing in vbdos. Seconds was easy - if you wound up with a negative, just add 86400.

Darrel Taylor
- 6th September 2005, 23:27
Well, I was hoping for more of what the program needs to do, instead of why it doesn't work the way you have it. But if I make a few assumptions I guess we can go from there.

Assumption 1 - The only thing the elapsed timer will be used for, is timing the 500ms periods.

Assumption 2 - The on/off cycle times are the same. 500ms ON, 500ms OFF. And repeats continuously untill it's determined that it needs to stop.

I believe the key here is in Starting the Elapsed Timer at the beginning of the alarm cycle so that it always starts at 0. That way you don't have to subtract the starting Ticks count from the current Ticks count. Al you'll need to do is test if it's less than 50.

Something like this...
Clear

Include "Elapsed.pbp"

AlarmOUT VAR PORTB.0
AlarmON VAR BIT

;----------------------------
MainLoop:
; Somewhere in here determine when to turn on/off alarm
; and Gosub to either StartAlarm or StopAlarm

if AlarmON and (Ticks < 50) then
HIGH AlarmOUT ' Alarm ON
else
LOW ALARMOUT ' Alarm OFF
endif
Goto Mainloop

;----------------------------
StartAlarm:
Gosub ResetTime ' Reset Time to 0d-00:00:00.00
Gosub StartTimer ' Start the Elapsed Timer
AlarmON = 1
Return

;----------------------------
StopAlarm:
Gosub StopTimer
AlarmON = 0
ReturnHTH
<br>

Bob_W
- 7th September 2005, 00:31
The 500 ms on, 500 ms off is just an LED for testing your code. The beauty of your timer routine is that you can time multiple things at the same time......

Example...
Siren is running in first of four blasts - timing the on and off interval... WHILE...

Timing to see it someone is holding down the stop button more than 2 seconds.... WHILE timing to see how long it's been since activation, so we refuse to activate again if it's been less than, say , 5 minutes, so if the tones are sent over the air again, we don't drive the neighbors nuts.... ETC..


And, that's just this project.


Thanks.....

jessey
- 4th November 2005, 08:50
Hi Darrel

I am trying to use your Elapsed Timer in a thermostat program using a DS1820 chip. My thermostat code compares a user set value to the temperature reading which turns a relay on/off accordingly. I'd like to be able to use your timer as a stop watch to record the total time that the heater is on for. I was really surprised and also delighted to see that your elapsed timer code only uses 139 words! My code by itself seems to work flawlessly but when I include your elapsed code then the temperature isn't consistent, as in, every so often it toggles the heater relay off then back on again ever so briefly.

I'm thinking that the interrupt is giving me a false temperature reading by corrupting the read. Could you suggest anything I could try to alleviate this? Also, I noticed that you use DEC2 in your LCDOUT command for your timer code, what does the DEC2 command relate to? I've seen that being used in some code before but I don't understand what it's used for. Could you or someone else here explain why and when you'd use the DEC2 or 3 or 4 commands? I've included my thermostat code so you can see how I have your timer set up.

Thanks
jessey

Darrel Taylor
- 4th November 2005, 20:03
Hiya Jessey,

That's the only bad part about interrupts. They Interrupt things.

And when you have something with very strict timing requirement such as 1-Wire devices, they just don't go together very good.

Probably the easiest way to remedy the situation is to just turn-off the interrupts when reading the sensor. This may cause a small amount of error to accumulate in the Timer routine, but if you keep the Temp readings to a minimum, it shouldn't even be noticible, maybe add a second a month or so.

Other than that, using a syncronous device like I2C or SPI will eliminate the problem all together.

HTH,
&nbsp;&nbsp;Darrel

jessey
- 6th November 2005, 05:52
Hi Darrel,

Well I figured out what the DEC2 command is for, it adds an extra 0 to the display which disappears when the variable is > 9, so I'm guessing it's just added to give the Lcd that digital clock look, which is cool. Now if I could just figure out how to turn off/on your interrupts? I thought it would be as simple as adding PIE1.0 = 0:INTCON.6 = 0:INTCON.7 = 0, for overflow, peripheral and global interrupts just before reading the DS1820 then after the read setting them back to 1 but with no success. When I do that it takes about 10 seconds for the clock to increment?

Then I implemented a counter in the mainloop and added an IF...THEN to only read the temperature about once every second or so as you suggested and that just wrecks havoc with the temperature reading, making it fluctuate wildly. I tried to make sense of the asm interrupts by reading the manual and searching the archives but to no avail. This experience really has me scratching my head. One thing that's for sure, is that using interrupts can be a scary thing and are not for the faint of heart! How would I go about turning the interrupts off and then back on again? Any further help would be greatly appreciated.

Thanks
jessey

Darrel Taylor
- 6th November 2005, 08:49
Oops, sorry, forgot to answer that part. (DEC2)

You should only have to reset GIE before reading the sensor.

INTCON.7 = 0
OWIN ....
INTCON.7 = 1

Not sure what you did with the counter so I can't imagine what happened there.
<br>

jessey
- 6th November 2005, 09:52
Oops, sorry, forgot to answer that part. (DEC2)

You should only have to reset GIE before reading the sensor.

INTCON.7 = 0
OWIN ....
INTCON.7 = 1

Not sure what you did with the counter so I can't imagine what happened there.
<br>


Hi Darrel,

That's great, I sure appreciate it, I'll give it a try tomorrow for sure.

Thanks Again
jessey

jessey
- 9th November 2005, 11:32
Oops, sorry, forgot to answer that part. (DEC2)

You should only have to reset GIE before reading the sensor.

INTCON.7 = 0
OWIN ....
INTCON.7 = 1

Not sure what you did with the counter so I can't imagine what happened there.
<br>


Hi Darrel,

I tried as you suggested and it's still giving me major problems. The counter does increment but it takes about 50 seconds to increment each second. The temperature seems to function ok, as when I touch the DS1820 it does increase the temp reading without any delays and all the buttons are working ok to enter the other subroutines and I can reset your clock ok as well.

The only function that doesn't work, is that the heater doesn't shut off when the temperature is equal to or greater that the set reference that's suppose to shut it off.

I have a Tocks counter that Gosubs the counter in the mainloop that's keeping time as well and it's functioning ok and keeping the proper time so it's a real puzzler. I tried "INTCON.7 = 0" before checking the temp and then instead of "INTCON.7 = 1" to turn it back on I used "Gosub StartTimer" figuring that something wasn't set proper, but still no go.

It has to be something in the mainloop that's causing the havoc. I noticed that when I enter another subroutine from the mainloop that your clock counts the time ok because if I time the seconds that I'm out of the mainloop and when I return, then your counter has counted the time correctly and I can see the time has advanced on the Lcd.

I've spent a couple of late nights going through my code thinking that I've got something in it that might be interfering with your counter but came up empty.

I went through the data sheet again for the 16f877 and still can't figure it out. Would you have any ideas on what's happening to cause this? I've included my code this time and if you get a chance to look at it sometime I'd surely appreciate it.

Thanks
jessey

Darrel Taylor
- 10th November 2005, 06:22
The DS1820 takes 750 ms to do a conversion.

In this section here, you have the interrupts turned off the whole time while waiting 3/4 of a second for it to finish. Missing a few (75) interrupts.

Update_The_Temperature:

INTCON.7 = 0 ' Disables all interrupts
OWOut DQ, 1, [$CC, $44] ' Start temperature conversion
waitloop:OWIn DQ, 4, [count_remain] ' Check for still busy converting
IF count_remain = 0 THEN waitloop
OWOut DQ, 1, [$CC, $BE] ' Read the temperature
OWIn DQ, 0, [temperature.LOWBYTE, temperature.HIGHBYTE, Skip 4, _
count_remain,count_per_c]
'Gosub StartTimer
INTCON.7 = 1 ' Enables all unmasked interrupts
RETURN


Since you have the Elapsed timer going, maybe you could send the convert command, then go back 1 second later and read the results. "Quickly"
<br>

CocaColaKid
- 6th December 2005, 19:50
Darrel,

I'm trying this code on an 877 and it work great, nice job. I would however like to run it on a 452 or a 4620. What would I have to change for this? I was looking thought the files and came across the lines:



IF OSC == 4 ; Constants for 100hz interrupt from Timer1
TimerConst = 0D8F7h ; Executed at compile time only
EndIF
If OSC == 8
TimerConst = 0B1E7h
EndIF
If OSC == 10
TimerConst = 09E5Fh
EndIF
If OSC == 20
TimerConst = 03CB7h
EndIF


What would these be if I wanted to say run a PLL at 4 MHz or a PLL at 10 MHz?

Darrel Taylor
- 6th December 2005, 22:54
The easiest way to figure out the constant at any OSC is to use the Timer Calc form at ...
http://www.picbasic.co.uk/forum/showthread.php?t=2031

For instance, in the form, enter 4 in the OSC field, then put 100 in the bottom Freq field. Press Calculate, and it shows that it takes 10,000 "Ticks" of the timer. And the number to reload into the Timer on each interrupt is 55543, or 0D8F7h This is the same number shown in the ASM section you listed, for 4Mhz.

That's 65536 - 10000 + 7

7 is the number of instructions used to reload the timer. (Elapsed.bas : ADD2_TIMER)

So, just plug in the new OSC value (Any crystal or PLL Freq) and enter 100 in the FREQ field. The new constant can be added to the ASM section of Elapsed.bas.

Also, I have a version of "Elapsed" for the 18F's. I'll send it, later tonight, when I get off work.

L8R,
Darrel

Darrel Taylor
- 7th December 2005, 06:33
Here's the version for 18F's.

It should be a drop-in replacement for the old version. Everything works the same, except for the registers saved on entry to the INT routine.

So, you can still use the Test_Elapsed_LCD/SER.bas demo's from the original version for testing. Just add the "-18" to the INCLUDE line.
<br>

CocaColaKid
- 7th December 2005, 13:01
It works like a charm, thank you vary much. Now I can finish making my waterbed temperature controller.

crematory
- 2nd January 2006, 05:23
Hi Darrel

I was wondering if elapsed timer code is accurate enough to make a digital clock and calender, showing time and date.

Thanks

Darrel Taylor
- 2nd January 2006, 13:26
Hi crematory,

That depends on the accuracy of the crystal being used, and the ambient temperature.
And, you'd probably need to have some kind of backup battery, to keep it going during any power outages.

I think I calculated it at around +/- 4 minutes per year with a standard 20mhz crystal. It was around 20 seconds/year with one of those temperature compensated TTL Oscillators.
<br>

crematory
- 3rd January 2006, 00:42
Hi crematory,

That depends on the accuracy of the crystal being used, and the ambient temperature.
And, you'd probably need to have some kind of backup battery, to keep it going during any power outages.

I think I calculated it at around +/- 4 minutes per year with a standard 20mhz crystal. It was around 20 seconds/year with one of those temperature compensated TTL Oscillators.
<br>


4 Minutes a year, that is more than acceptable for me, I will be happy with that in deed.

I was wondering if you can through a hint for using the rest of interrupts in the PIC when using the elapsed timer code, shall I use your way to handle other interrupts, or what should I exactly do.

Thanks Darrel

Darrel Taylor
- 3rd January 2006, 22:41
I've been working on something that'll answer that question, but it's still in bits and pieces right now. I guess it's time to finish it.

Give me a couple days to put it together. Hopefully it'll be worth the wait.
<br>

crematory
- 4th January 2006, 00:14
Darrel,

I will be waiting for the answer.



Good luck

Darrel Taylor
- 8th January 2006, 10:58
Almost there. &nbsp; I keep getting sidetracked.

I hope you're using MPASM. I've been unable to make it work with PM.
<br>

crematory
- 9th January 2006, 16:01
Hi Darrel

MPASM would never be a problem, I always keep tracking the new versions of microchip compiler (MPASM), as being almost there, I hope you finish it as soon as possible :)

Thanks alot

mister_e
- 9th January 2006, 22:10
In fact, i don't know why PM still exist since it doesn't work anyway with any 18Fs. I always use MPASM.

mazlan
- 20th March 2006, 08:43
Hi Darrel,

I got this message after compiling my program. I'm using Pic18F452 & PicBasic Pro Ver 2.46a

Error[113] C:\MPLAB\PICBASIC\PBPPIC18.LIB 1154 : Symbol not previously defined (_ClockCount)


Thank You.

Ioannis
- 20th March 2006, 09:01
In fact, i don't know why PM still exist since it doesn't work anyway with any 18Fs. I always use MPASM.

Hmm, what are the changes one have to make so the program be compatible with MPASM (other than the DEFINES) ?

Thanks
Ioannis

Darrel Taylor
- 20th March 2006, 09:44
mazlan,

Since the ClockCount subroutine is in the same file as the Define that assigns it to the interrupt handler, it's unlikely that it can't find the label.

Unless, you've edited the files or tried to paste the stuff into you're main program instead of using the Include files. In which case I have no way to know what you've done.

DT

mazlan
- 21st March 2006, 01:25
I'm using the original include files and didn't change anything. Now it's ok. After deleting the space between INTHAND and _ClockCount, everything's OK. Actually, i'm new in microchip assembly.

Thank You. :-)

Eng4444
- 3rd June 2006, 08:44
How to make the first version of the program works for PIC16F876A with a 4 line LCD?

Could anyone help me??

Darrel Taylor
- 3rd June 2006, 16:28
1. Set the DEFINEs to match your hardware.

2. Add CMCON = 7 if using PORTA

3. compile and run.
<br>

mister_e
- 3rd June 2006, 17:32
And ===> Adcon1=7

Darrel Taylor
- 4th June 2006, 02:30
Hi Steve,

ADCON1 = 7 was already in the "Test_Elapsed_LCD.pbp" program.

Just need to add the CMCON for the 87x"A" varieties.

DT
<br>

Eng4444
- 4th June 2006, 06:15
This might sound strange for some of you and stupid for others... but i'll say it again, i do not have any previous knowledge in all this...

i already asked how to make it work for PIC16876A with a 4 lines LCD.

i got : 1. Set the DEFINEs to match your hardware.

2. Add CMCON = 7 if using PORTA

3. compile and run.

i'm thankful for this.. but at that time, i didn't pay attention to what was written in the first post! :

" Note2: They are written for use as the only interrupt in the program. If you need to use other interrupts as well, the program will have to be modified before it will work. "

then, i opened the .zip file and found...4 files!

ASM_INTS.BAS
ELAPSED.BAS
TEST_ELAPSED_LCD.pbp
Tets_elapsed_ser.pbp

So what to do with all these......? where to download each of them?....

Darrel Taylor
- 4th June 2006, 06:31
Naaa. Don't bother.

Try Melanie's Olympic timer instead (cheers Darrel, you keally know how to make friends - Melanie). It doesn't have the ASM stuff to worry about.

Or Paul's clock that doesn't even need interrupts.

DT
<br>

MSapper
- 9th June 2006, 05:19
Just wanted to say thanks, works great on a 16F690.

I was going to modify your program a bit, I have an application which uses count down timers, but I thought I should check with you, I noticed a copyright on the code.

Thanks,
Mike

Darrel Taylor
- 9th June 2006, 08:21
Hi Mike,

Glad it's working.

No problem with the copyright.
You're free to use it any way you want. Well, except posting it somewhere saying you wrote it :)

shahidali55
- 11th June 2006, 18:20
Hi Darrel,
I tried making a LCD clock with calender with the elapsed timer code, but the clock runs nearly 2 seconds slow per day.
I've made some modifications to the Elapsed_INT.bas.txt file so attached that also with my code ...
I dont know how to fix this error...
What should i do?... try tweeking the TimerConst or something???

shahidali55
- 11th June 2006, 18:28
Sorry for posting that same message again...
I was getting an error message...
The attachment wasn't uploading correctly...

Darrel Taylor
- 12th June 2006, 15:06
Hi shahidali55,

"tweeking the TimerConst" won't help. 1 count either direction will change the timing by more than you are off. @ 4mhz, 1 count will change the time by about 4 seconds per day.

At 2 seconds per day, it works out to around 23 PPM (parts per million), which is within the normal 50 PPM tolerance of the crystal. Not to mention the +/- 100 PPM variance due to temperature.

To get closer than that, you'll need to calibrate the crystal frequency. You may be able to use a small Trimmer Capacitor in place of one of the capacitors on the crystal. The size needed depends on your crystal, but 0-20pf will probably work.

Other than that, you could simply add 1 second every 12 hours. But if you build more units, that could be problamatic since they won't all be the same.

Here's a couple links to some more crystal info:
http://www.circuitcellar.com/library/print/0298/bujanos91/3.htm
http://ww1.microchip.com/downloads/en/AppNotes/00588b.pdf
http://ww1.microchip.com/downloads/en/AppNotes/00949a.pdf

shahidali55
- 17th June 2006, 16:31
I dont think the problem is with the oscillator.
I noticed that the time loss takes place every time the hours increase...
Could it be some flaw in the code execution?

Darrel Taylor
- 18th June 2006, 00:15
If its losing time on every change of the hour, and the total loss is 2 seconds per day, then each change of the hour would lose 1/12th or 0.083 seconds.

@ 4mhz, that's 83,000 instruction cycles. No, I don't think it's accidently losing that many instructions to increment an hour. And, looking at the ClockCount interrupt routine, if that were the case, it would also happen on every second, minute, and day as well. Which it doesn't.

Why don't you think it's the crystal?

Have you measured the frequency?

DT

shahidali55
- 18th June 2006, 16:11
when i run Josepino's code, on the same board with the same crystal, i do not get any error.
This is why I think it is a fault in the software...

Darrel Taylor
- 19th June 2006, 01:33
Too bad the guy only gives out HEX files.

Otherwise, we could see what special magic he has, to cancel out the tolerance of crystals.

DT

shahidali55
- 19th June 2006, 18:12
Hi Darrel,
Some where Josepino had mentioned on his site that he had used Roman Black's one_sec.asm routine in his clocks (maybe he mentioned this by accident).
I tried making a similar program in PBP but had no success, till now.
Finally i got the progam running. its the exactly same one_sec.asm routine running with instant interrupts, and guess what,... it worked, and really accurately...
Its even more accurate than the Elapsed Timer...
I'll post the code so that it may be useful for someone...
(I'm not as mean and selfish as Josepino...
and thanks a lot for the instant interrupts program...
Chiao...

Leonardo
- 19th June 2006, 20:55
Hello shahidali55,

It can publish or to send to my mail [email protected] the code and complete circuit since is very interesting its project, congratulations for its idea!.

Leonard

Regards





Hi Darrel,
Some where Josepino had mentioned on his site that he had used Roman Black's one_sec.asm routine in his clocks (maybe he mentioned this by accident).
I tried making a similar program in PBP but had no success, till now.
Finally i got the progam running. its the exactly same one_sec.asm routine running with instant interrupts, and guess what,... it worked, and really accurately...
Its even more accurate than the Elapsed Timer...
I'll post the code so that it may be useful for someone...
(I'm not as mean and selfish as Josepino...
and thanks a lot for the instant interrupts program...
Chiao...

Darrel Taylor
- 20th June 2006, 08:23
shahidali55,

Pardon my stubborness!
Sometimes I get so defensive, I don't recognize when someone knows better.

I've tried your code, and there's definately a big difference.

But now, I must know how much, and more importantly why.

Fortunately, with Instant Interrupts, it was easy to combine both timers into one program, and I'm running it on a telnet server, in case you (or anyone else) wants to see too. &nbsp; I started the test at 12:15 am PST, and have set the time as close as possible relative to GMT. This screen capture was 30 seconds later.

http://www.darreltaylor.com/files/TimerStart.JPG


If you have Hyperterminal installed you can see the results by using the TimerTest.ht file in the .zip below. Hopefully in a few days the error will be more evident. T1 is the Elapsed timer, and T0 is your new timer.

password: guest
then type data and press enter
<br>
Here's a reference time http://nist.time.gov/timezone.cgi?Pacific/d/-8/java
Although i started about 1/2 second late.
<br>

Test completed. .zip file removed.

Leonardo
- 21st June 2006, 13:42
Hello, I have not managed to compile I cosay give errors me, can help me please.

Thanks

Leonard




shahidali55,

Pardon my stubborness!
Sometimes I get so defensive, I don't recognize when someone knows better.

I've tried your code, and there's definately a big difference.

But now, I must know how much, and more importantly why.

Fortunately, with Instant Interrupts, it was easy to combine both timers into one program, and I'm running it on a telnet server, in case you (or anyone else) wants to see too. &nbsp; I started the test at 12:15 am PST, and have set the time as close as possible relative to GMT. This screen capture was 30 seconds later.



If you have Hyperterminal installed you can see the results by using the TimerTest.ht file in the .zip below. Hopefully in a few days the error will be more evident. T1 is the Elapsed timer, and T0 is your new timer.

password: guest
then type data and press enter
<br>
Here's a reference time http://nist.time.gov/timezone.cgi?Pacific/d/-8/java
Although i started about 1/2 second late.
<br>

shahidali55
- 21st June 2006, 16:43
Hi Leonard,
To compile this code you need to have Darrel's Instant interrupt file (DT_INTS-14.bas) in the folder along with the source code...
You also need to have enabled the "Use MPASM" option in the compile and options menu...

Leonardo
- 22nd June 2006, 00:57
Hello,

Don't I find the "DT_INTS-14.bas" in none of the folders, can send it please to my mail?. [email protected]

Thank you



Hi Leonard,
To compile this code you need to have Darrel's Instant interrupt file (DT_INTS-14.bas) in the folder along with the source code...
You also need to have enabled the "Use MPASM" option in the compile and options menu...

Darrel Taylor
- 22nd June 2006, 09:11
Hmmm,

I fully expected the test to show that the new timer would keep accurate time and give a good indication how far off the Elapsed Timer was, but instead after 24hrs, the Elapsed Timer (T1) was still within 1 second of actual time, and the new timer (T0) had gained almost 8 seconds. Also, my PC's clock was 3 seconds slow. (interesting, but not relevant)<br>
http://www.darreltaylor.com/files/END001500.jpg<br>
Since then, I've turned Roman's routine into a subroutine and ran it in loops just to verify it's counting, and can say that it does accuratly count 1 million instructions per second. &nbsp; Well, actually, it counts 3 seconds at 999,936 instructions and every 4th second at 1,000,192. Which over the 4 seconds averages out to exactly 4,000,000 instructions. So in a perfect world, with a 4.000000mhz crystal, it should count perfect time. But it doesn't. (in my case)

This leads me to beilieve that My crystal is off.
So, continuing with with the perfect world theory...

There's 86,400 seconds per day
or 86,400,000,000 instructions per day

an additional 8 seconds is 8,000,000 instructions
for a total of 86,408,000,000

that divided by the 86,400 seconds per day gives
1,000,092.593 instructions per day or a freq. of
4,000,370.37 hz.

That's 93PPM off center frequency. So I've either got a crystal that's almost twice the tolerance. OR I'm still missing something big time.
<br>

SteveB
- 22nd June 2006, 18:01
Darrel,
This may be stating the obvious, but either:
1) Both timer routines are accurate (Definitely not true)
2) T0 is accurate, T1 is not
3) T1 is accurate, T0 is not
4) Neither timer routine is accurate (if your Xtal is within specs and running fast, T0 runs fast and T1 runs slow, this could explain the results you have shown)

The Xtal error needs to be eliminated to see what’s happening with the last three. One suggestion may work. If you have a RTC handy that will output the 32768Hz, use this as an input to the timers. Then, you can compare the PIC clocks against RTC, all of which would be running at the same exact freq. Some tweeking of the code will be required for this. Also, This may lead to changes in the code which would mask the errors occurring with the 4Mhz Xtal, but it may be worth a try.
I have actually done this with a timer routine similar to the Elapsed timer, and it works fine. I will add it to this post when I get back to my home computer (Found it! 931). It has some significant differences though. It never stops the timer, only adds to the TMRxH byte, and counts 128 "ticks" per second. (edit: I also count DOWN. I think this added some efficiency with the ASM, but can't remember off the top of my head. It's run on an 18F4620)

The other is to get a measure of the actually freq of your crystal, then factor this into the timer errors. And I am not sure how to do this to the accuracy needed to find the problem.

Intriguing problem,
Steve

shahidali55
- 26th June 2006, 16:57
Hello again Darrel,

I've landed to one conclusion...
The only way to get accuracy is to tweak the counter variable (bres_lo)...
I'm using 33pf oscillator caps...
(4Mhz osc)...
I found that my oscillator is clocking at 4.000176 Mhz (4000176 Hz)
and not 4Mhz...
After tweaking the low byte of the 24bit variable, i was able to get nearly 100 percent accuracy...

Darrel Taylor
- 27th June 2006, 08:08
Yup, It appears that the capacitor value is crucial to long term timing.

In my test above (8 sec. fast). I was using a PROTO-40 board from the now defunct projectx.com It came with 15pf capacitors on the crystal. After making a program to be able to read the crystal frequency, I found that it was very close to my estimate above. (4,000,358 instead of 4,000,370). It's amazing that a 7pf difference can throw the crystal that far out of tolerance.

After changing the capacitors to 22pf, the frequency changed to 3,999,978

And, after two days of running your Romanesque code, it's showing a loss of 0.5 seconds per day. So it's at least gratifying to know that the frequency measurement was correct, and the overall timing is predictable.

However, now you've thrown me for another loop by saying that your crystal was oscillating fast with 33pf caps. Which doesn't fit in with my theory of more pF = lower Freq. So, yet another thing to figure out. Oy vey.

I guess it won't really matter too much though, since there are only 3 standard capacitor values in the range we need, 15, 22 and 33 pf, there's no way to compensate for inaccuracies by changing the values. So, it seems to boil down to a few possibilities.


Measure the actual frequency and use a variable capacitor on the crystal to adjust to the correct frequency.
Use normal fixed capacitors. Measure the actual frequency, and calculate a "Fudge Factor" for the Timer counts.
Spend several days watching a clock count and compare it to Real time, then calculate the Fudge Factor from the time difference. (I got really tired of this one, takes too long)


So it looks like the only way to go is actually measuring the frequency and adjusting from there, either hardware or software. But how to measure it? Simply putting a frequency counter probe on the crystal will change the frequency significantly due to the extra capacitance. So it needs to be done some other way.

<hr>My first attempt:

I'm using a DS1307 as the time base, and for the moment am using a +/-20ppm 32,768 crystal on it. I've been trying to find a DS32KHZ +/-2ppm oscillator, but can't seem to find anywhere that has one in stock. But I'm not too worried about it now because, after 3 days, the DS1307 is still "Exactly" in sync with real time, so I think it's pretty close to 32,768

Timer1 is set up as a Free-running counter, that never gets stopped or reloaded, so it's just sitting there counting instruction cycles forever. The overflow interrupt simply adds 1 to a 32-bit variable each time, extending timer1's count to 48 bits, which let's it count instruction cycles for a very long time.

The 1hz square wave output from the 1307 is fed to CCP1 input. CCP1 is in Capture mode, triggering on every rising edge. The Capture catches the TMR1 value, and the interrupt handler copies the upper 32 bits to temp vars so they don't change while sending out RS.232

The Timer is started on the first rising edge of the Square Wave input, and the number of cycles received from the 1307 are counted in another 32-bit variable. But since PBP doesn't work with 48 and 32-bit variables, I have to send the data out as hex numbers. Looks like this...<pre>Count 001D 1855 A0E0<br>Seconds 0001 E823</pre>
From there I can cut&paste the numbers into the windows calculator and figure out that after 124,963 seconds (0001 E823) about 34 hours, the pic had executed 124,962,316,512 instructions (001D 1855 A0E0)

Dividing the total by the seconds passed says that 999,994.53 instructions are being executed each second from a crystal frequency of 3,999,978.12 hz.

However, after only 10 minutes, it had it nailed down to 999,994.42 inst/sec. and as time goes on, it just works out the decimal places.

So now, I'm running the Roman code with a constant of 999,994 (0F 42 3A), instead of 1,000,000 (0F 42 40). So far so good.

While this procedure seems to work, it still takes external hardware (DS1307). &nbsp; And frankly, it's a pain in the butt converting all those hex numbers. So I think the next step is to create a program that runs on a PC to do all those conversions, and to handle the timing. instead of the 1307. Hopefully that will make it so you don't need any external hardware on the pic, except for a serial port. Then once the frequency is known it can save the Fudge Factor in EEPROM.

I also thought about using Steve's nice little timer as the time base, but I needed Timer1 for the CCP capture. May want to use that one later though.
<hr>
After the frequency is determined and the constant calculated, It's still not going to keep perfect time. With a +/- 100 ppm variance over the temperature range, it just messes everything up. As seen earlier, 93ppm made a difference of 8 seconds per day.

Granted, I never plan on seeing -10 deg C in my home, or outside for that matter (California). But even 10 or 15 degrees away from 25 deg C could have a big impact.

So I'm thinking that monitoring the temperature and adjusting the constant dynamically as the temperature changes, will yield the best results.

Geez, this is turning into one of those never ending projects. And I just have to find the answers.

BTW shahidali, if you "tweaked" the constant for 4,000,176 hz, if my math is correct should have been (0F 42 6C)? Anywhere close?
<br>

shahidali55
- 28th June 2006, 17:22
Ya 0F 42 6C is right...
I agree with you ...
I think keeping track of the temp. is the only way...
I'll try to get a DS32 Khz oscillator...

SteveB
- 28th June 2006, 18:03
I'll try to get a DS32 Khz oscillator...

This is the chip I used as my timer input (see my last post). It works as advertised...
which is to say very well

Pic_User
- 28th June 2006, 18:13
Hi,
Here is a start:

Circuit Cellar Magazine
http://www.circuitcellar.com/

Choosing the Right Crystal For Your Oscillator
http://www.circuitcellar.com/library/print/0298/bujanos91/index.htm


Circuit Cellar>Selecting quartz crystals for oscillators can be confusing and mysterious.

Four crystal parameters play a key role in system accuracy. The contribution from each must be added to obtain the total system accuracy....

Frequency tolerance....
Frequency stability is a function of temperature and is related to the crystal cut type.
Aging...
Load capacitance....

Darrel Taylor
- 28th June 2006, 18:24
Post #42 up there had the same link.

And while it's good information, that documents target was only +/- 2 min per month. Which is 4 seconds per day.
This whole thing started because, 2 seconds per day with the elapsed timer wasn't good enough.

<hr>Steve,

Do you remember where you got the DS32KHZ. Can't seem to find one. Can get a thousand of them for around $3,500, but can't find just one for $11.
<br>

SteveB
- 28th June 2006, 21:03
Do you remember where you got the DS32KHZ. Can't seem to find one. Can get a thousand of them for around $3,500, but can't find just one for $11.
<br>

Yea, I ran into the same issue when I learned about them. So, after a lot of looking I bought 1k.....

No really, I figured "what the heck," registered with Maxim and requested a sample. What did I have to lose? It required some sort of approval so I guessed it probably wasn't goint to work out. But, after a bit of a wait I was very plesently suprised to receive 2 in the mail! I suppose they consider it good business. I sure tend to look at Maxim poducts first in my search for parts after this.

Steve

Darrel Taylor
- 30th June 2006, 06:32
Apparently, that's the only way you can get them. &nbsp; Geez, what a way to sell a product.

Fortunately for me, Bruce over at Rentron.com had done the same thing, and had an extra 1 left over.

Sweet! can't wait to get it. And .. all the other parts I ordered at the same time. Crystal Mania! :eek:
<br>

SelfishJosePino
- 8th July 2006, 21:54
No, I didn't mention the Roman Black's web page by accident.

I good to know there is people not mean and selfish like JosePino. Anyway, I found quite interesting this forum.

Regards,
Jose Pino
www.josepino.com


Hi Darrel,
Some where Josepino had mentioned on his site that he had used Roman Black's one_sec.asm routine in his clocks (maybe he mentioned this by accident).
I tried making a similar program in PBP but had no success, till now.
Finally i got the progam running. its the exactly same one_sec.asm routine running with instant interrupts, and guess what,... it worked, and really accurately...
Its even more accurate than the Elapsed Timer...
I'll post the code so that it may be useful for someone...
(I'm not as mean and selfish as Josepino...
and thanks a lot for the instant interrupts program...
Chiao...

shahidali55
- 9th July 2006, 16:31
......................

shahidali55
- 9th July 2006, 16:32
Hello Jose Pino,
I found your LED and LCD clock projects very interesting...
Why dont you put your source codes online??? Its not like everyone is looking to make money by selling your codes or something...
Most of them, like me, just want to learn more in this wonderful world of microcontrollers!!!

SelfishJosePino
- 9th July 2006, 21:16
About one year and half ago, I was so mad and upset when I found some of my projects were used for commercial applications on Europe, specifically on Germany. After that, I decided not to post source codes and protect it with copyrights and patents.

Later, I started to receive some hate mail (Specially from Spanish-speaking readers) accusing me that my projects are stealed from other web pages, so I started to post algorithms of my projects. (only some of them)

Now, I receive many e-mails from hungy-of-knowledge people over the world asking me for source codes. At this point, I'm thinking seriously to make the source code available to the public.

As you can see, Is really hard to make everybody happy. So, I guess the best thing to do is to make the source code available for everybody. It may take a while until I have some time available to find the source code and put it online.

Regards,

Jose Pino
www.josepino.com


Hello Jose Pino,
I found your LED and LCD clock projects very interesting...
Why dont you put your source codes online??? Its not like everyone is looking to make money by selling your codes or something...
Most of them, like me, just want to learn more in this wonderful world of microcontrollers!!!

shahidali55
- 10th July 2006, 13:39
Hello Josepino,

It'll be great if you put your souce codes online...
Thanks for considering my posts...

thirsty
- 20th July 2006, 08:01
Hi Guys.

I'm writing a program to control a drag racing xmas tree and time the 1/4 mile but I need .0000 (ie thousandths) second accuracy.

Looking a the ASM routine I'm wondering if I can just change the 100Hz freq to 1KHz by triggering the interupt more often.

Will this work?

Thanks and Regards

Thirsty

Darrel Taylor
- 20th July 2006, 13:22
That should work.

But .0000 would be Ten Thousandths of a sec. That'll be harder.
<br>

jessey
- 27th November 2006, 16:15
Here's the version for 18F's.

It should be a drop-in replacement for the old version. Everything works the same, except for the registers saved on entry to the INT routine.
<br>


Hello Darrel,

I tried using your Elapsed-18 for use with my 18f452 and when I compile I get these errors:

Error(113)c\pbp\10tim~2asm 503:Symbol not previously defined (TimerConst)
Error(113)c\pbp\10tim~2asm 507:Symbol not previously defined (TimerConst)

Would you have any ideas why I'm getting these errors or what I could try to get it to compile? I got my new version of PBP 2.47 installed now.

Thanks
jessey

Darrel Taylor
- 27th November 2006, 21:54
Hi Jessey,

What OSC are you using?

The Elapsed-18.bas only has constants for 4,8,10,20 and 40 mhz.

If it's something different, it needs a new constant.
See Post #17
<br>

jessey
- 28th November 2006, 09:58
What OSC are you using?
<br>
Hi Darrel,

I was running my pic at 16 mhz, I changed it to 4 and its working great now.

Thanks A Lot
jessey

geckogrotto
- 27th February 2007, 07:49
Using a 12F675 I am getting wsave position request beyond ram_end errors how do I fix this?

Darrel Taylor
- 27th February 2007, 11:41
Using a 12F675 I am getting wsave position request beyond ram_end errors how do I fix this?

Open the ASM_INTS.bas file and comment out the offending lines.
The 12F675 only has 2 banks of RAM.
<br>

geckogrotto
- 28th February 2007, 04:31
Thanks got it :)

sayzer
- 24th December 2008, 09:23
Hi crematory,

That depends on the accuracy of the crystal being used, and the ambient temperature.
And, you'd probably need to have some kind of backup battery, to keep it going during any power outages.

I think I calculated it at around +/- 4 minutes per year with a standard 20mhz crystal. It was around 20 seconds/year with one of those temperature compensated TTL Oscillators.
<br>


Hi Darrel,

Could you suggest a good "one of those temperature compensated TTL Oscillators" please ?

Also, if aging for an oscillator is stated, say, +/-5ppm a year or +/- 100ppm a year, what would it mean in your elapsed timer application?

Last question, if I modify your routine to work for 100years, forget about power failures etc., and use a good osc as you may suggest, what would be a possible problem as years pass by?

Thanks.


--------------

Darrel Taylor
- 26th December 2008, 00:04
The way I understand it is that the Total PPM error stated for crystals includes 10 years of aging.

Say you have a crystal with +/-50ppm tolerance. When you first install the crystal it will be very close to the specified frequency (+/-5ppm), and it's anticipated that in 10 years, the frequency will still be within the stated 50ppm @25°C.

Most crystals have an aging of less than 5-10 ppm per year. If you've come across a crystal that has +/-100ppm per year, throw it in the garbage.

For a recommendation, I'd still have to say the DS32KZ is the best for the price at less than $10.
http://pdfserv.maxim-ic.com/en/ds/DS32kHz-DS32KHZS.pdf
With an indoor application it will hold to +/- 2 ppm, which is 1 minute per year.
For outdoor apps, it's 7.5 ppm, or 4 minutes per year.

TCXO's for the CPU's osc at 10-20Mhz are more expensive, but some are really accurate. Like this one that holds to 0.28 ppm, or about 9 seconds/year. So after your 100 years it would only be off by 15 minutes. ($30US, 3.3V)
http://www.conwin.com/datasheets/tx/tx236.pdf

There are so many possible combinations of tolerance, compensation and time, it's difficult to give a direct answer. But for any of the possibilities, if you can come up with the anticipated PPM Error, this calculator will help to determine how much it would affect the long term timing of a clock.

Press the Recalculate button after changing the Freq or Error.
<table cellspacing="10"><tr><td valign="top"><OBJECT CLASSID="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" CODEBASE="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" WIDTH="400" HEIGHT="250" ><PARAM NAME="MOVIE" VALUE="http://www.pbpgroup.com/CrystalErr/CrystalPPM.swf"><PARAM NAME="PLAY" VALUE="true"><PARAM NAME="LOOP" VALUE="true"><PARAM NAME="QUALITY" VALUE="high"><EMBED SRC="http://www.pbpgroup.com/CrystalErr/CrystalPPM.swf" WIDTH="400" HEIGHT="250" PLAY="true" LOOP="true" WMODE="opaque" QUALITY="high" TYPE="application/x-shockwave-flash" PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer"></EMBED></OBJECT></td><td valign="top">For normal crystals, the PPM tolerance is specified at 25°C. Temperature extremes in either direction can cause a 50ppm crystal to change as much as 100-300 ppm. Granted, those are extremes that would kill a human being, but somewhere in-between are the changes from summer to winter in an outdoor application.

The TCXO (Temperature Compensated Crystal Oscillator) will actively adjust the frequency according to the temperature, so that the frequency is always within the stated PPM tolerance. It usually does this by changing the voltage to a Varactor Diode, which changes the capacitance on the crystal. While it does compensate for temperature, the relative aging of the device remains the same as an uncompensated crystal.

</td></tr></table>

Happy Holidays!

lil_mark
- 1st February 2009, 12:26
Hi Darrel,
first of all compliments for this excellent job.
I would use "DEFINE OSC 48", because I need to compile for PIC18F4550 using USB.
And using USB in PBP we must set "DEFINE OSC 48" due to PLL...
When I try, I always receive the error "Symbol not previously defined (TimerConst)".
So I tried setting the right TimerConst in the "Elapsed_INT-18.bas" file...
I read all in this thread, and tried using the TimerCalc form, but it seem is impossible getting: 10,000 "Ticks", 100 Freq and 48 OSC all together.
So it means it's impossible using USB and Elapsed Timer at the same time ?
Is there a TimerConst that work with osc @ 48 MHZ ?
Mark

Darrel Taylor
- 1st February 2009, 13:21
Hi Mark, and thanks!

Is there a TimerConst that work with osc @ 48 MHZ ?

Yes there is.
In 2003 I hadn't heard of a 48mhz pic :)

Hmmm, let's see here,
pulling up mister_e's PicMultiCalc ... (Timer Helper page)
http://www.picbasic.co.uk/forum/attachment.php?attachmentid=2957

and ... uht oh, with the existing timer load routine, the best we can do is 99.999hz. Not so good for a long-term timer.

But not a problem, increasing the timer reload time to 10 instructions (add 3 nop's), with a Timer1 prescaler of 2, the reload value is 5541.

I'm sure that will elicit more questions, but I'll let you give it a go first. :D
<br>

hackableFM
- 25th February 2009, 22:55
I have given this a good reading over but some of it has gone straight over my head.
I intend to built a control unit based around a 16F877 which will include menu's to control output pins on the PIC, Obviously within the menu there will likely be instructions which will keep the processor busy for a few Ms at a time. My project ideas originally were to include an external clock device such as the DS1307 or the DS1603 to keep track of time whilst the rest of the program (Menu's etc etc) is running and keeping the processor busy.
I was advised on this forum to look at this thread and if this is what I think it is then it will be truly fantastic for my project.

My problem is that when the PIC returns from a menu I want it to display the time on the LCD. Am I right in what I have read in thinking (Besides setting LCD pins & oscillator etc) I can simply include all of the attached files in post number 1 of this thread but modify the 'loop' subroutine within "Test_Elapsed_LCD.pbp" file, to include my menu software?

I understand that this thread creates a time elasped counter and not a clock, but I do have in mind ideas to include buttons with which I can set the hours, minutes & seconds. I will also miss out the displaying of 'Days' when it comes to print on the LCD.

Apologies for what might seem to be a simple question to you guys but I have only been playing for a couple of months with PIC's and I am trying to learn from other people's examples.

Thanks.

hackableFM.

Darrel Taylor
- 26th February 2009, 00:45
I intend to built a control unit based around a 16F877 which will include menu's to control output pins on the PIC, Obviously within the menu there will likely be instructions which will keep the processor busy for a few Ms at a time. My project ideas originally were to include an external clock device such as the DS1307 or the DS1603 to keep track of time whilst the rest of the program (Menu's etc etc) is running and keeping the processor busy.
I was advised on this forum to look at this thread and if this is what I think it is then it will be truly fantastic for my project. ...It can do the things you are suggesting.

But it's not a replacement for a battery backed-up RTC chip.
Any RESET will lose the accumulated time.

I have used it as a clock running on-chip, but on power-up it reads the time from an RTC. That way it doesn't have to continuously read the time via I2C and it's always available from the internal time clock.
<br>

hackableFM
- 26th February 2009, 09:10
Thank you, This sounds good so I'll have a bit more of a read up on it, I'll try to understand more about how it works before I go building anything.

Battery backed is not an issue, this will be connected to a permanent 12v system within a caravan and therefore never likely to loose the accumulated time.

hackableFM...

jmgelba
- 24th February 2012, 14:20
Hi Darrel,

I'm trying to create a timer - a run time timer that will turn off an output after 30 minutes. Would this code work for such a task?

Thanks

Darrel Taylor
- 24th February 2012, 18:35
Sure, it'll do that really easy.



IF MinutesChanged THEN
MinutesChanged = 0
IF Minutes = 30 THEN
; turn off whatever it is here
GOSUB ResetTime
ENDIF
ENDIF

jmgelba
- 25th February 2012, 03:55
Excellent!

Can it do incremental too? As in can it be started and stopped during the 30 minute period?

Also I need it to run when an output is on, but if the output is only on say 20 minutes then off for say an hour, I dont want it to only run for 10 minutes if the output is turned on again. What this timer is actually doing is stopping high power LED's getting hot due to excessive run time.

Darrel Taylor
- 25th February 2012, 04:50
The Elapsed Timer can be Stopped and Started, like a stopwatch.


GOSUB StopTimer
GOSUB ResetTime
GOSUB StartTimer

You can time things any which way you need to.

jmgelba
- 25th February 2012, 19:01
Last question!! I swear!! Can I have 2 instances running at once? 1 counting the on time, and the other counting the off time?

Darrel Taylor
- 25th February 2012, 20:29
No, the Elapsed Timer Demo can only have one timer at a time.
The same concept can be used to create multiple timers, but this demo doesn't do that.

Since the ON and OFF periods can't happen at the same time...
When one period is finished, Reset the timer and measure the next period.
You only need one timer.

You don't have to stop and start the timer, just GOSUB ResetTime, and the time will revert to 0d-00:00:00.0 and keep counting.
Use it like a Stopwatch.

The interactive LCD on this page shows how it works. (Click on the "GOSUBs")
http://www.darreltaylor.com/DT_INTS-14/elapsed.html
But that page is for the DT_INTS version of the Elapsed Timer.

jmgelba
- 26th April 2012, 20:26
Well I've had an implementation of this working great for months now. Its counts down 20 minutes, turns off the output, flashes an LED using the pause command, then sits there waiting for the input to start again.
I need to add a feature now though. As the time reaches 15 minutes, I need it to flash an LED at 2Hz while still counting down and turning off at 20 minutes.

Currently the code is like this:





IF MinutesChanged THEN
MinutesChanged = 0
IF Minutes = 20 THEN
HPWM 1, 0 , 30000
POWER = 0
LEDS = 0
CURRENT = 0


If I change it to this will it do what I need it to do? Second thing is if it will, how do I flash an LED while still running the main program? I am using HPWM already.



IF MinutesChanged THEN
MinutesChanged = 0
IF Minutes = 15 THEN
GOSUB Flashyflashy
ENDIF


IF MinutesChanged THEN
MinutesChanged = 0
IF Minutes = 20 THEN
HPWM 1, 0 , 30000
POWER = 0
LEDS = 0
CURRENT = 0
ENDIF

HenrikOlsson
- 26th April 2012, 21:16
Hi,
I'm not that familiar with the code but the way you suggest will only call FlashyFlashy when minutes is 15 - not more, not less. If you want FlashyFlashy to keep executing all the way to 0 you need IF MINUTES <= 15 THEN You could also streamline it a bit, there's no real need to check and reset MinutesChanged twice, perhaps something like

IF MinutesChanged THEN
MinutesChanged = 0

IF Minutes <= 15 THEN
GOSUB Flashyflashy

IF Minutes = 20 THEN
HPWM 1, 0 , 30000
POWER = 0
LEDS = 0
CURRENT = 0
ENDIF

Darrel Taylor
- 26th April 2012, 21:59
Jim,

I think that'll just flash it once at the 15 minute point.

This should work.

Flashing VAR BIT

IF MinutesChanged THEN
MinutesChanged = 0
IF Minutes = 15 THEN Flashing = 1
IF Minutes = 20 THEN
Flashing = 0
HPWM 1, 0 , 30000
POWER = 0
LEDS = 0
CURRENT = 0
ENDIF
ENDIF

IF Flashing THEN
SELECT CASE Ticks
CASE IS <25 : HIGH LED
CASE IS <50 : LOW LED
CASE IS <75 : HIGH LED
CASE ELSE : LOW LED
END SELECT
ENDIF

jmgelba
- 8th October 2012, 17:15
Darrel, can this be used as a countdown timer with the time left updated each second and displayed on a LCD?

Thanks.

Darrel Taylor
- 8th October 2012, 19:47
It Can ...

But you would need to create the count down routine.
The demo only counts up.

jmgelba
- 11th October 2012, 02:10
OK thanks. I will play around with it and see what mess I can create.

jmgelba
- 23rd October 2012, 05:23
Ticks = Ticks + 1
if Ticks = 100 then
Ticks = Ticks-100
Seconds = Seconds - 1
SecondsChanged = 1
if Seconds = 0 then
Minutes = Minutes - 1
MinutesChanged = 1 Seconds = 59
endif

if Minutes = 0 then
Hours = Hours - 1
HoursChanged = 1 Minutes = 59
endif

if Hours = 0 then
Days = Days - 1
DaysChanged = 1 Hours = 23
endif
endif

To my untrained eyes, this would count down the time at every 100 ticks. I guess the issue, if this works, is how to preloaded the timer with the required start time and how to get it to stop at 0.

jmgelba
- 24th October 2012, 18:40
Seem to be having an issue I cant resolve. I've only use the timer software on a 18F2550 and it worked perfectly. I'm now using a 18F1320 and I've ran into an issue when compiling.

ERROR Line 23: Redefinition of VAR. (DT_INTS-18.bas)
ERROR Line 24: Redefinition of VAR. (DT_INTS-18.bas)
ERROR Line 25: Redefinition of VAR. (DT_INTS-18.bas)

Which are these lines in the code:


wsave var byte BANKA SYSTEM ' location for WREG
ssave var byte BANK0 SYSTEM ' location for STATUS register
bsave var byte BANK0 SYSTEM ' location for BSR register

I am unsure as to what the problem is and where to start.

I'm using PBP 2.60A and MCS 3.0.0.5

jmgelba
- 24th October 2012, 19:11
Fixed the above issue.

jmgelba
- 25th October 2012, 01:07
Getting frustrated! :-) I've tried a bunch of things to get the counter to count down correctly.

There is a problem at rollover. Let's say the timer is set to 1 hour 1 minute zero seconds, it should start there and one second later change to 1:00:59. It doesn't, it stays at 1:01:00 for 1 second and then counts down from 1:01:59. Then when it gets to 1:00:00 it rolls over to 0:00:00 for 1 minute then the next second its at 0:59:59.
Not really sure what to try. What about a "first run through the interrupt" flag that automatically makes minutes = minutes - 1 and starts the seconds at 59?

HenrikOlsson
- 25th October 2012, 06:13
Hi,
Without thinking too much, how about this:

Hours VAR BYTE
Minutes VAR BYTE
Seconds VAR BYTE

Hours = 1
Minutes = 1
Seconds = 0
Ticks = 100

ISR:
Ticks = Ticks - 1
If Ticks = 0 THEN ' Ticks rolled over, one second has passed
Ticks = 100 ' Reset ticks (10ms ticks)

Seconds = Seconds - 1 ' Count down a second.

If Seconds = 255 THEN ' Seconds rolled over
Seconds = 59 ' Reset seconds
Minutes = Minutes - 1 ' Count down a minute

If Minutes = 255 THEN ' Minutes has rolled over
Minutes = 59 ' Reset minutes
Hour = Hour - 1 ' Count down an hour
ENDIF
ENDIF
ENDIF

/Henrik.

jmgelba
- 25th October 2012, 14:58
Henrik, thank you for your reply. It is very logical.

I came up with this before you posted your reply last night:


if seconds => 59 then
minutes = minutes - 1
minuteschanged = 1
endif
if seconds > 59 then
seconds = 59
endif


if Minutes => 59 and seconds => 59 then
hours = hours - 1
HoursChanged = 1
endif
IF Minutes > 59 then
Minutes = 59
endif

if hours = 0 then
Days = Days - 1
DaysChanged = 1
Hours = 0
endif

IF HOURS > 23 then
HOURS = 23
ENDIF

endif
ENDIF


It works and has been running all night. Yours is cleaner though! My issue was I was trying to change numbers at 0 not 0 - 1. Once I realized this is took 2 minutes to fix it.

jmgelba
- 25th October 2012, 17:56
This interrupt based timer runs in the background. Does that mean a pause in the main program will stop it or will it continue to run in the background? I am trying to de-bounce a switch.

HenrikOlsson
- 25th October 2012, 18:46
Hi,
A Pause (or any other command) will not interfere with the interrupt - it'll keep running in the background.
However, if you're interrupting at a high rate the context saving, interrupt code and context restore will take cycles from the main PBP program without it knowing so your pause may be a bit longer than what you specify. For non critical timings it's not a problem.

/Henrik.

EDIT: Obviosuly interrupting at ANY rate will take cycles away from the main program. What I mean is that the faster you interrupt the more cycles will "vanish" and any software timed routines like Pause, SERIN, Pulsin that you have in the main program will be affected.

jmgelba
- 25th October 2012, 19:10
No the pause length isnt critical. I'm trying to get one switch to start and stop the elapsed timer. So it needs a debounce and a wait period so that it doesnt immediately stop the timer again.

Here's what I have but it doesnt work to well:


loop1:

For A = 0 to 1000
IF PORTB.7 = 1 and TimerRunning = 0 then
gosub StartTimer
endif
next A

if SecondsChanged = 1 then
LCDout $FE,2, dec2 Hours,":",dec2 Minutes,":",dec2 Seconds
SecondsChanged = 0
endif

For B = 0 to 1000
If PORTB.7 = 1 and timerrunning = 1 then
gosub StopTimer
endif
next B
goto loop1

This means the user has to press and hold the button for 1 second before the timer will start. However, if they press it for 1.5 seconds, then they only need to press it again for half a second and it stops. If they press it for 2.5 seconds, then timer has started, stopped and is half way to starting again.

I was wondering id something like this would work?





'If PORTB.7 = 1 then
'gosub StartTimer
'TimerRunning = 1
'pause 150
'endif

If SecondsChanged = 1 then
LCDout $FE,2, dec2 Hours,":",dec2 Minutes,":",dec2 Seconds
SecondsChanged = 0
endif

If PORTB.7 = 1 AND TimerRunning = 1 then
'gosub StopTimer
'TimerRunning = 0
'pause 150
'endif



I think the problem with both of them is that there is no time between the on if then and the off if then, and there needs to be some sort of logic that says that if the timer is running and the button is still pressed, do not stop the timer until the button has been released and pressed again.

Darrel Taylor
- 25th October 2012, 19:15
It keeps running in the background.
The interrupts will not wait for PBP statements to finish.

Ok fine, ... for many years I've resisted making a count down elapsed timer for humanitarian reasons.
But if you guys are going to do it anyhow, I might as well make a new version of the Elapsed Timer.
I can only hope that if somebody uses it for nefarious purposes, they end up blowing themselves up.

Here's the test circuit.

http://support.melabs.com/DT/Elapsed_DN.gif

Here's the test program ...
' Define LCD connections
DEFINE LCD_DREG PORTC ' Set LCD Data port
DEFINE LCD_DBIT 4 ' Set starting Data bit (0 or 4) if 4-bit bus
DEFINE LCD_RSREG PORTC ' Set LCD Register Select port
DEFINE LCD_RSBIT 2 ' Set LCD Register Select bit
DEFINE LCD_EREG PORTC ' Set LCD Enable port
DEFINE LCD_EBIT 3 ' Set LCD Enable bit
DEFINE LCD_BITS 4 ' Set LCD bus size (4 or 8 bits)
DEFINE LCD_LINES 2 ' Set number of lines on LCD
DEFINE LCD_COMMANDUS 2000 'Command delay time in us
DEFINE LCD_DATAUS 50 'Data delay time in us

DEFINE OSC 4

INCLUDE "Elapsed_DN.bas" ; Elapsed Timer Routines

ZERO_LED VAR PORTB.3

ANSEL = 0 ' All Digital
ANSELH = 0
LOW ZERO_LED ' start with LED OFF
OPTION_REG.7 = 0 ' enable PORTB pull-ups
PAUSE 250
LCDOUT $FE,1 ' Initialize LCD
PAUSE 250

Days = 1 ' set initial time
Hours = 1
Minutes = 3
Seconds = 10

GOSUB StartTimer ' Start the Elapsed Timer

Main:
CountDown = !PORTB.0
IF SecondsChanged = 1 THEN
SecondsChanged = 0
LCDOUT $FE,2, DEC Days,"d-",DEC2 Hours,":",DEC2 Minutes,":",DEC2 Seconds
ENDIF
IF ZERO_LED != ZERO THEN ZERO_LED = ZERO
GOTO Main

If the button on PORTB.0 is pressed it counts down.
If it is not pressed, it counts up.
The LED comes on when it reaches 0.

If you are using the countdown for a movie set, it has to stop at 1 second. :)
Put this in the main loop.
IF (CountDown=1) AND (Days=0) AND (Hours=0) AND (Minutes=0) and (Seconds=1) _
THEN GOSUB StopTimer


You'll need the ASM_INTS include from the original Elapsed Demo.

jmgelba
- 25th October 2012, 19:58
Question then. Can I place the LCDOUT routine in the interrupt so that the screen always updates, even if the main program needed to pause for a total length over 1 second? The reason is to keep the display updating correctly on the second, every second.

Darrel Taylor
- 25th October 2012, 20:23
NO!

You could in the Elapsed_INT version, but not in this standalone version.

SteveB
- 25th October 2012, 23:56
Darrell,
You should have put the stop at 1 sec in the code, and make people pull it out otherwise. Just a little step towards keeping the knuckle-heads from causing trouble. ;)

Darrel Taylor
- 26th October 2012, 03:00
LOL Steve, I like that idea. :D

But as it is, they have to be holding the button down for it to count down.
If they're still holding it when it reaches 0 ... well ...

I know it can be bypassed, but don't tell them how :tongue:

jmgelba
- 28th October 2012, 23:19
IF PORTA.2 = 1 OR PORTA.3 = 1 AND DIM = 1 AND timerrunning = 1 THEN
PORTA.0 = 1
ENDIF
IF PORTA.2 = 1 OR PORTA.3 = 1 AND DIM = 2 AND timerrunning = 1 THEN
PORTA.1 = 1
ENDIF
IF PORTA.2 = 1 OR PORTA.3 = 1 AND DIM = 3 AND timerrunning = 1 THEN
PORTA.0 = 1
PORTA.1 = 1
ENDIF


Looking for a more efficient way of doing this. I need to monitor 2 inputs and turn on the correct outputs only when the timer is running. This does compile but I havent tested it to see if it actually works. This snippet will go in the main loop of the program that runs while the timer counts down.

HenrikOlsson
- 29th October 2012, 08:46
Here's one idea

Temp VAR BYTE

If TimerRunning = 1 THEN
If (PortA.2 = 1) OR (PortA.3 = 1) THEN
Temp = PortA & %11111100
PortA = Temp + DIM
ENDIF
ENDIF

/Henrik.

jmgelba
- 29th October 2012, 16:45
Henrik I see what you are doing but wouldnt this create an issue with the higher bits? For instance if I am understanding correctly, the higher bits are always at 1, which would indicate a high, which in the program would stop and reset the timer. If I were to make them 0 in the loop, they would never change to 1 when the switch was closed. Switches are on bits 4, 5, 6 and 7.

HenrikOlsson
- 29th October 2012, 17:39
Hi,
No, the high bits are not always 'at 1'.
When doing a bitwise AND both "inputs" must be true for the "output" to be true. Thus Temp = PortA & %11111100 will do a 'copy' of PortA except the two LSB's will be forced to zero. All the other bits will be left as they are when PortA is read.

Besides, it doesn't really matter what you write to the port/pins which are configured as inputs. If, for example, PortA.5 is an input and you write '1' to it it will still read the actual state that whatever is connected to it drives it to - which isn't neccesarily '1'. If you look at the schematic for the I/O pins you'll see that the output driver for the pin is disabled when TRIS is set (ie. pin is made an input). If it wasn't it would short out whatever drives the pin to either Vdd or Vss.

/Henrik.