Timer issue in PIC

Thread Starter

RG23

Joined Dec 6, 2010
304
If anyone has an idea please guide me on this

I defined three timers TMR0(10 ms), TMR1(0.5 sec) AND TMR2(50 ms) in the main part of the assembly program.

Now in the Interrupt service routine I used the following code

ISR:
movwf W_TEMP ; copy W to TEMP register
swapf STATUS,W ; swap status to be saved into W
movwf STATUS_TEMP ; save status

btfsc INTCON,2 ; TMR0 interrupt (10ms)
goto TMR0_int

btfsc PIR1,0 ; TMR1 interrupt (.5sec)
goto TMR1_int

btfsc PIR1,1 ; TMR2 interrupt (50 ms)
goto TMR2_int
goto ISR_End

ISR_End:
swapf STATUS_TEMP,W ; swap STATUS_TEMP register into W
movwf STATUS ; restore STATUS
swapf W_TEMP,F ; swap W_TEMP
swapf W_TEMP,W ; swap W_TEMP into W
retfie

TMR0_int:
bcf INTCON,2 ; clear TMR0L int flag
movlw b'00100000'
xorwf PORTC
movlw 0xD9
movwf TMR0L
goto ISR_End

TMR1_int:
bcf PIR1,0
goto ISR_End

TMR2_int:
bcf PIR1,1
goto ISR_End

The issue is that PORTC, pin 5 toggles every 10 ms that is I get 50Hz frequency but not all the time
Sometimes the frequency changes to 33 Hz
I want to get a stable frequency all the time from that timer
I couldn't figure out the problem

Thanks
 

spinnaker

Joined Oct 29, 2009
7,830
With 3 interrupt timers it is not always going to be 100% accurate. Try making your most important interrupt a high priority interrupt and see if that helps.
 

MMcLaren

Joined Feb 14, 2010
861
Why not use a single 10 msec interrupt and a couple counters? Perform your 50 msec interval task(s) once every five interrupts and perform your 500 msec interval task(s) once every fifty interrupts...
 
Last edited:

thatoneguy

Joined Feb 19, 2009
6,359
I agree with one timer and testing it in the interrupt.

If it matches one of the timers you need, set a flag bit/variable so it can be handled in the main loop.

All the GOTOs are slowing down your ISR, I prefer to get in, find source, update a flag, and get out. Plenty of time in the main loop to test those variables(flags) with time to spare.

One thing that may be making it not 100% accurate is if you are setting a variable that the 50Hz event happened, and the ISR was called in the middle of a delay loop, it won't be checked in main code until that delay loop has finished.

Try to minimize delays, use them only for short times, such as between LCD commands, or when bit-banging. Using long delays is better spent checking a timer in a loop, and take action when that condition is met. No reason to do a delay loop when the main loop can sit around and check flags set by an interrupt at a high speed. This also gives you a bit of a way to prioritize them, such as when the 50Hz 0.5 second occur at the same time.
 

Thread Starter

RG23

Joined Dec 6, 2010
304
@atferrari

PIC18f87J90


i removed one call to a big subroutine from the timer interrupt subroutine
and now i get 50Hz correctly
but problem is I have to call that big subroutine at specific time interval
As soon as i call it in any one of the timer interrupts i stop getting constant 50Hz signal
 

Thread Starter

RG23

Joined Dec 6, 2010
304
Is there any way the three timers can run independent of each other in the pic assembly?

I think the way the timer interrupts are executed in a sequence in the ISR, any subroutines called within the timer interrupts are fluctuating the output frequency corresponding to each timer period

Please let me know if anyone has an idea
 

CraigHB

Joined Aug 12, 2011
127
I usually handle that situation with interrupt priorities, as already mentioned. But then you only have one ISR that is independant. The next is dependant on the first and so on.

There's no way to make multiple ISRs completely independant of concurrent calls. You'd have to use multiple MCUs. You might consider using a seperate MCU for each task and assign one as a master controller.

I don't know exactly what you're trying to do, but you might be able to use of the MCU's built-in modules instead. For example, if you just need to ouptut a digital signal at some frequency and duty cycle, you could use the MCU's PWM module.
 

thatoneguy

Joined Feb 19, 2009
6,359
@atferrari

PIC18f87J90


i removed one call to a big subroutine from the timer interrupt subroutine
and now i get 50Hz correctly
but problem is I have to call that big subroutine at specific time interval
As soon as i call it in any one of the timer interrupts i stop getting constant 50Hz signal

How far does your signal drift from 50Hz?

Are you calling delay routines in the other routines that are set by the timer? If so, find away to get rid of them, usually delays over 100uS can be worked around in code by running other code in the meantime.

State how critical each of the 3 timers are, the allowed error of the timer action, and how many lines of actual code you have (or compiled program size), and we might be able to find a way to make it work.

Prioritizing is a huge part of programming when you have only one processor, and until the last decade, quite a lot of "realtime code" was done one single processor machines running below 1Ghz.
 

Thread Starter

RG23

Joined Dec 6, 2010
304
The signal is 50 Hz mostly but drifts to 33 Hz

I do call some other subroutines in the timer interrupts ISR that perform specific functions at regular time intervals.

I tried to remove one of the subroutines which was too big
Then I got 50Hz exactly

I know now that subroutine is creating the drift but the problem is I have to call that subroutine in at least one of the timer interrupts

I am not calling any delay routines in the other routines that are set by the timer

@State how critical each of the 3 timers are, the allowed error of the timer action, and how many lines of actual code you have (or compiled program size), and we might be able to find a way to make it work

I would like to have precise time intervals.

Actually the code is really big around 9000 lines as I see in the program memory section of MPLAB
 

thatoneguy

Joined Feb 19, 2009
6,359
Ok, Do not call functions from the interrupt, AT ALL. Put the code inside the interrupt if you must, but keep it very short.

If it is code that drops your timer by 17Hz, that's a big loop doing something.

What you do is when that big loop needs to happen, set a bit to "TRUE", either define a variable at the top of the program, or use an unused and disabled peripheral register bit. Alias that bit to BIGHUGEFUNCTION or something.

Then, DO NOT call BIGHUGEFUNCTION from the interrupt, just set that bit flag so the Program knows BIGHUGEFUNCTION needs to run, and get out of the interrupt. Do the same with bit flags for the other functions that need to run regularly.

In the main loop, Check to see if BIGHUGEFUNCTION bit is set, if so, THEN call The Big Huge Function, which may get interrupted by your timer, but it will return to where it left off. Do the same with bit flags for the other functions that need to run regularly.

That way, all of your timers will be pretty close to accurate, especially the 50Hz one, I can't imagine what is taking over 50mS to run with a 1Mhz code clock (assuming 4Mhz XTAL). In addition to taking calls to external functions OUT of the interrupt function, find a way to optimize the one that is taking over 50mS to run. If you are waiting for ADC conversions to complete, those are delays, if you are waiting for a UART bit to arrive, that's a delay. Anything that is not active processing should be cut out and "flagged" to be handled by the interrupt, such as UART, SPI, Etc.
 

Thread Starter

RG23

Joined Dec 6, 2010
304
actually i tried that logic before

when i called that big subroutine outside the timer interrupt my subroutines for the switches(up and down buttons) and many other subrotuines did not execute correctly
 

thatoneguy

Joined Feb 19, 2009
6,359
Ok, for BIG Huge FUnction, Can you share with us what it does?

Can it be calculated in parts? e.g. When the 50Hz delay goes off, the wave is changed, and then the uC is sitting there waiting for th enext interrupt. Is there a way you can work the code for BIGHUGEFUNCTION into those delay times, so that when it is called, a lot less processor time is used, since many of the functions were done in "idle time"

It would require a rewrite, but if you have a 50+mS function, it needs to be re-written. Is it doing floating point math or something?
 

Thread Starter

RG23

Joined Dec 6, 2010
304
i am interfacing pic with graphic display(122x32)

i have to update the signal strength on an analog channel continuously with the help of scrolling bar

If the signal is max i display 122 vertical bars on the entire first line of display

If the signal is half strength 61 vertical bars and so on

Since it is graphic display i have to make the bars pixel by pixel hence that subroutine has become large

@Is there a way you can work the code for BIGHUGEFUNCTION into those delay times, so that when it is called, a lot less processor time is used, since many of the functions were done in "idle time"

I am not using any delay subroutines, only the timer interrupts
 
Last edited:

ErnieM

Joined Apr 24, 2011
8,377
i am interfacing pic with graphic display(122x32)

i have to update the signal strength on an analog channel continuously with the help of scrolling bar

If the signal is max i display 122 vertical bars on the entire first line of display

If the signal is half strength 61 vertical bars and so on

Since it is graphic display i have to make the bars pixel by pixel hence that subroutine has become large
I doubt the display can react to accurately display a 50Hz change. First thing I would change is to pull the display update OUT of the BIG FUNCTION, and run it less often.

Depending on the display you can also just update the change in the bar; ie, turn off a few segments or turn of a few based on the change in readings. More computation but less updating.
 

MMcLaren

Joined Feb 14, 2010
861
The issue is that PORTC, pin 5 toggles every 10 ms that is I get 50Hz frequency but not all the time. Sometimes the frequency changes to 33 Hz. I want to get a stable frequency all the time from that timer I couldn't figure out the problem
Rich (BB code):
TMR0_int:
        bcf     INTCON,2        ; clear TMR0L int flag
        movlw   b'00100000'
        xorwf   PORTC
        movlw   0xD9
        movwf   TMR0L 
        goto    ISR_End
I wonder if this code could be part of your original problem? It appears you're trying to reload the free-running TMR0 for your next interval. There are a few problems with this approach. First, if you're using any prescaler value other than 1:1 for TMR0, you should be aware that any write to TMR0 will clear the prescaler and you'll lose those counts. Second, if you're not using a prescaler, then you would need to add your constant to the free-running TMR0 register to avoid losing counts. This is important because there may be a delay servicing TMR0 (from servicing one of the other timers). Finally, counting resumes two cycles after a writing TMR0 so you need to take that into account when you reload TMR0.

May I provide an example? Let's say you're using a 4-MHz clock so you've got a Tcy (instruction cycle time) of 1 usec. If you wanted a 250-usec periodic interrupt, here's one way you might reload TMR0 in your ISR;

Rich (BB code):
        bcf     INTCON,T0IF     ; clear interrupt flag and        |B0
        movlw   -(250-1)+2      ; value for 250 cycle interval    |B0
        addwf   TMR0,F          ; tmr0 += -(250-1)+2              |B0
If you need a precise TMR0 interrupt for some value other than 256 TMR0 counts, you can't use the prescaler. And if you can't use the prescaler, it becomes a little more difficult trying to use TMR0 for a 10 msec interrupt interval...
 
Last edited:

thatoneguy

Joined Feb 19, 2009
6,359
I doubt the display can react to accurately display a 50Hz change. First thing I would change is to pull the display update OUT of the BIG FUNCTION, and run it less often.

Depending on the display you can also just update the change in the bar; ie, turn off a few segments or turn of a few based on the change in readings. More computation but less updating.
I agree.

Make a function called "Addbar" that takes coordinate x for position and y for height.

Have the rest of your code monitor for changes which would require an update on the LCD.

When something changes, call addbar (x,y) to have the LCD cursor move to that spot and draw the new bar.

you might also use clearbar (x) before addbar if the bar needs to be shorter. This is just using the LCD cursor and having it move around setting or clearing bits. More efficient than re-drawing the entire screen, which should happen when you change display modes, or go to a menu, or something similar.

Why refresh the entire display many times per second? People can only notice 'flicker' below about 30 frames/sec if it is smooth, which it won't be at the speed you are running at, it will look continually "re-painted" which is less readable than anything.
 
Top