PDA

View Full Version : Darrel Taylor Elapsed Timer



rwskinner
- 11th March 2008, 22:39
I've been through this before but I must be doing something wrong.
My DT Elapsed Timer is incrementing the seconds every 15 seconds.

I'm running a 18F4520 with an External clock of 20 MHZ

I set T1CON = 0 'Main Clock FOSC/4, 1:1 Prescaler .....

In the Elapsed_Int-18 code I verified the constant is 15543... for 100 interrupts per second.

If OSC == 20
TimerConst = 03CB7h
EndIF

What did I miss? Why am I running so slow.

Darrel Taylor
- 11th March 2008, 23:09
Hmmm ~15 seconds.

I would guess that you are actually running on the internal oscillators default of 1mhz.

If FCMEN (Fail-safe Clock Monitor) is enabled, and the external oscillator fails, it automatically switches over to the internal oscillator.
<br>

rwskinner
- 11th March 2008, 23:29
I'll double check that, but if that were the case, wouldn't my baud rates and stuff be off as well on my serial port stuff?

Darrel Taylor
- 11th March 2008, 23:45
Yes it would.
Didn't realize you had serial working along with the problem.
Thought maybe it was just an LCD, which wouldn't be affected.

Do you have other interrupts in the program too?
Something that might be keeping the interrupts locked up for too long?
<br>

rwskinner
- 11th March 2008, 23:54
Yes, I have one other that's for Rx but it grabs a short 8 byte serial string once every 2 seconds.

INCLUDE "DT_INTS-18.bas" ; Base Interrupt System
INCLUDE "ReEnterPBP-18.bas" ; Include if using PBP interrupts
INCLUDE "Elapsed_INT-18.bas" ; Elapsed Timer Routines

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler RX_INT, _Receive, PBP, yes
INT_Handler TMR1_INT, _ClockCount, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

@ INT_ENABLE RX_INT ; enable external (INT) interrupts
@ INT_ENABLE TMR1_INT ; enable external (INT) interrupts


15 seconds is a long time and is pretty consistient.

rwskinner
- 11th March 2008, 23:58
I did check, and I do have the following fuses set in my programmer.

Osc = HS, FSCM = on, Brown Out = Enabled,
Low Power Timer1 Osc = Low Power ??? Was default

Darrel Taylor
- 12th March 2008, 00:56
Try it like this, and see if the Low Priority RX_INT helps.
DEFINE USE_LOWPRIORITY 1
INCLUDE "DT_INTS-18.bas" ; Base Interrupt System
INCLUDE "ReEnterPBP-18.bas" ; Include if using PBP interrupts
INCLUDE "ReEnterPBP-18LP.bas" ; Include if using Low Pr. PBP INTS
INCLUDE "Elapsed_INT-18.bas" ; Elapsed Timer Routines

;----[High Priority Interrupts]-----------------------------------------------
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler TMR1_INT, _ClockCount, PBP, yes
endm
INT_CREATE ; Creates the High Priority interrupt processor

;----[Low Priority Interrupts]------------------------------------------------
INT_LIST_L macro ; IntSource, Label, Type, ResetFlag?
INT_Handler RX_INT, _Receive, PBP, no
endm
INT_CREATE_L ; Creates the Low Priority interrupt processor
ENDASM

@ INT_ENABLE TMR1_INT ; Enable Timer 1 Interrupts
@ INT_ENABLE RX_INT ; Enable USART Receive interrupts

rwskinner
- 12th March 2008, 02:45
That works better.


Is this causing my problem since they are PBP Interrupts, it's not exiting like it should...

AdcMaxReads is at 64, Each time I loop I increment the channel, that way I only do 64 reads per loop.


GetADC:
ADCVAL = 0
ADCAVG = 0

For AdcCnt = 1 to AdcMaxReads
ADCIN NextChan,ADCVAL
ADCAVG = ADCAVG + ADCVal
Next AdcCnt
Reg[NextChan] = ADCAVG / AdcMaxReads

NextChan = NextChan + 1
If NextChan = 10 then 'Cycled thru all 9 analogs
If (LowBatSP > 0) Then
BatVolts = Reg[8]
GoSub CheckBat
EndIf
NextChan = 0
EndIf
Return





Main:
Gosub GetADC
Goto Main

Darrel Taylor
- 12th March 2008, 03:30
Those A/D statements are in the Main Loop, they will not have any affect on the interrupts.
However, you haven't initialized the NextChan variable before starting. It may take several thousand readings from invalid channels before falling back into line when it rolls over.
But much like the RX handler, you may not have included that part of the program.


That works better.
Does that mean it's counting at 1 second intervals now?

If so, then the problem is in your RX handler. If you have a Timeout value in the HSERIN statement, it sits in the interrupt until it either receives ALL the data, or it times out. Or if there's no timeout, it simply waits until it's received enough data before returning.

If that handler is a High Priority interrupt, then no other interrupts can occur until it's returned from that handler. Timer1 would then probably miss many interrupts.

By making the RX handler a Low Priority interrupt, it allows the (high priority) TMR1_INT to continue on without conflict.
<br>

rwskinner
- 12th March 2008, 03:35
Oh, I have lots of initilized variables in my Init: routine before it drops into the Main. But thanks.

Yes, it's counting properly now. You 100% correct, I moved my prewritten HSerIn call from the previous main loop and copied it up there. I did not think about the time out but the timeout is only set to 5.

Holy Cow, Also on timeout, I have it going back to the Receive label as well, I think that is the issue.
Better make that the Interrupt Exit...
WAS


'Receive Interrupt
Receive:
'Clear the buffers before anything
GOSUB ClearBuffers

HSerIn Timeout,Receive,[WAIT(MyID)] <---- Opps
For Cnt = 1 to 7
HSerIn Timeout,Receive,[BufRX(Cnt)] <---- Opps
Next Cnt
Gosub ProcessData
ExitRx:
@ INT_RETURN




Changed to

'Receive Interrupt
Receive:
'Clear the buffers before anything
GOSUB ClearBuffers

HSerIn Timeout,ExitRx,[WAIT(MyID)]
For Cnt = 1 to 7
HSerIn Timeout,ExitRx,[BufRX(Cnt)]
Next Cnt
Gosub ProcessData
ExitRx:
@ INT_RETURN

rwskinner
- 12th March 2008, 04:14
Would it be better to do something like this and get rid of the WAIT?
Just Grab the first byte and see if it's for me, if so process, else Exit Interrupt....

'Receive Interrupt
Receive:
'Clear the buffers before anything
GOSUB ClearBuffers

'Instead of waiting, grab the first byte and see if it's for me or Exit
HSerIn Timeout,ExitRx,[BufRX(0)]
If (BufRx <> MyID)
Goto ExitRx
End If

For Cnt = 1 to 7
HSerIn Timeout,ExitRx,[BufRX(Cnt)]
Next Cnt

Gosub ProcessData
ExitRx:
@ INT_RETURN

Darrel Taylor
- 12th March 2008, 05:16
It might be a little better, but there's a much better way.

Receive 1 byte at a time in the handler

If you get an interrupt from the USART, you are guaranteed that there is at least 1 byte in the USART's buffer.

So when you get to the handler, just get the byte that you know is there. No Timeouts, no sitting around waiting for the next byte to finally come in. Serial data is Really slow compared to the speed of the processor. It doesn't make sense to waist millions of instructions sitting around waiting for serial data.

After getting the byte, exit the Handler. If there is another byte still in the buffer, then DT_INT's will automatically loop back around to grab the next one, without ever leaving the interrupt processor.

Then all you need to do is watch for the synchronizing character, and count how many bytes have been received. If you get a sync byte, reset the count. If you count the correct number of bytes received, set a flag to tell the main loop that the receive succeeded.

Something like this off the top of my head...
TempByte VAR BYTE
RXdone VAR BIT

'Receive Interrupt
Receive:
HSERIN [TempByte] ; Get the byte
IF (TempByte = MyID) THEN ; is it the sync byte?
Cnt = 0 ; Yes, reset the counter
RXdone = 0 ; indicate, RX not finished
@ INT_RETURN ; Return, and discard the sync byte
ENDIF

BufRX(Cnt) = TempByte ; Not a sync byte. save it.
Cnt = Cnt + 1 ; increment buffer pointer

IF (Cnt = 7) THEN ; If all bytes are received?
RXdone = 1 ; Tell Main loop to process it.

; If the main loop cannot process
; FOR Cnt = 0 to 6 ; the data before the next sync byte?
; RX_Data(Cnt) = BufRX(Cnt) ; Copy it to another "Working" buffer
; NEXT Cnt ; Reception will continue while the
ENDIF ; last packet is processed

@ INT_RETURN

;---------------------------------------
Main:
; ...
IF RXdone then Gosub ProcessData
; ....
GOTO Main

;---------------------------------------
ProcessData:
RXdone = 0
; ....
; ....
RETURN

This will use the least amount of time required to receive serial data. And the main loop has much more time to do whatever it needs to. And allows many more interrupts to happen too, if needed.

Just make sure that the Sync byte can NEVER be found in the data bytes. This goes for the way you had it too.

If you are sending data in the form of Bytes, it's likely that a data byte will have the same value as the sync byte. If that's possible, you'll need to develop a multi-byte synchronization, or transmit the data in plain text (HEX is the easiest).

HTH,
<br>

rwskinner
- 12th March 2008, 11:57
Thanks for your help Darrel

Darrel Taylor
- 12th March 2008, 20:37
That sounded like a ... "Thanks, but No Thanks".

OK, but it is the Best way to receive serial from the USART.
<br>

rwskinner
- 13th March 2008, 01:22
No, not at all, was just thanking you for the help you've given. Too many times I look the time to search and look up links and info for folks and not even a reply or thank you.

I see it happen to folks here all the time. (like all the modbus questions a few days ago. not a thank you or reply one)

The buffer is a fine idea, I usually do this in low level serial work as well on pc's. For your question, the sync byte is simply a single byte that is the ID of the system it's meant for. When you see it, you either collect 7 more bytes or timeout. After you collect the total of 8 bytes (including the ID) you then first check to see if the last two bytes calculates to the CRC of the message you received. If not, toss it. There are numerous other checks as well.

So, No Start Byte besides the ID,
No terminator, just a silent time to time out on. Usually collect 8 bytes for a valid message depending on mode.

In total it goes something like this.

1)Grab a byte, is it my ID? No, then ignore it. Yes then go to next step.
2) Collect a total of 8 bytes or timeout and clear the buffer.
3) e collected 8 bytes, now get the last two bytes and calc the 16 bit CRC and see if it matches the message. if so next, or ignore message.
4) now check to see if the Function Code is valid or supported, if not, return an error message FC Invalid, else process the request.
5) Return the required Data

The thing is, since it's only 8 bytes of data, it doesn't really take that long and other than gathering a few adc readings there isn't much else going on, so I took the simple route.

I still have to figure out how to use 7,N,1 with the usart by shifting bits for valid data or something. Hadn't played with it, took the software serial routines for that portion but I think with a little logic I could shift 1 bit before storing the 7 bit value in the array and do the same when sending it and making the last bit a 1 for a stop bit.

Something to play with I guess.


Richard