Timer issue in PIC

Discussion in 'Embedded Systems and Microcontrollers' started by RG23, Jan 13, 2012.

  1. RG23

    Thread Starter Active Member

    Dec 6, 2010
    301
    2
    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
     
  2. spinnaker

    AAC Fanatic!

    Oct 29, 2009
    4,866
    988
    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.
     
  3. atferrari

    AAC Fanatic!

    Jan 6, 2004
    2,644
    759
    RG, what family is that PIC?

    What timer is the one you want to be "exact"? Sorry I do not get it.
     
  4. MMcLaren

    Well-Known Member

    Feb 14, 2010
    759
    116
    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: Jan 16, 2012
  5. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    6,357
    718
    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.
     
  6. RG23

    Thread Starter Active Member

    Dec 6, 2010
    301
    2
    @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
     
  7. RG23

    Thread Starter Active Member

    Dec 6, 2010
    301
    2
    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
     
  8. CraigHB

    Member

    Aug 12, 2011
    127
    15
    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.
     
  9. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    6,357
    718

    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.
     
  10. RG23

    Thread Starter Active Member

    Dec 6, 2010
    301
    2
    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
     
  11. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    6,357
    718
    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.
     
  12. RG23

    Thread Starter Active Member

    Dec 6, 2010
    301
    2
    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
     
  13. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    6,357
    718
    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?
     
  14. RG23

    Thread Starter Active Member

    Dec 6, 2010
    301
    2
    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: Jan 18, 2012
  15. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,386
    1,605
    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.
     
  16. MMcLaren

    Well-Known Member

    Feb 14, 2010
    759
    116
    Code ( (Unknown Language)):
    1. TMR0_int:
    2.         bcf     INTCON,2        ; clear TMR0L int flag
    3.         movlw   b'00100000'
    4.         xorwf   PORTC
    5.         movlw   0xD9
    6.         movwf   TMR0L
    7.         goto    ISR_End
    8.  
    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;

    Code ( (Unknown Language)):
    1.  
    2.         bcf     INTCON,T0IF     ; clear interrupt flag and        |B0
    3.         movlw   -(250-1)+2      ; value for 250 cycle interval    |B0
    4.         addwf   TMR0,F          ; tmr0 += -(250-1)+2              |B0
    5.  
    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: Jan 19, 2012
  17. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    6,357
    718
    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.
     
  18. MMcLaren

    Well-Known Member

    Feb 14, 2010
    759
    116
    May we ask what you're doing with the 10 ms interrupt, please? Is it simply toggling a port pin?
     
Loading...