Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

PIC CCP module - capturing low frequency periods

Status
Not open for further replies.

Blueteeth

Well-Known Member
Hi,

Well, I rarely post about code problems, because its almost always a dumb mistake on my part, and I'm sure this one is no different.

Background:
Like many projects on the web, I'm using the 'capture' mode of the CCP module on a PIC16F684 (newer pics don't have extra functionality so I used this one to test). Frequency input ranges from roughly 2Hz to ~350Hz, and I'm having trouble picking up overflow of timer1 (when input is less than ~2Hz).

With a 4MHz clock, /8 prescaler, max period is 65535 * 8us = ~524ms.

I have some other things going on, like a timer pinging every 100ms that sets a flag to tell the main routine to send something via SPI, so, the capture mode of the CCP module, along with interrupts is handy.

The basic idea is, whenever the capture fires (every rising edge) its interrupt saves the capture results, along with a copy of how many times timer1 has overflowed. Sets a flag to indicate a capture, then use the following pseudo code:

Code:
If (New_capture < Old_Capture)
   Overflow_count--

Measured period = (Overflow_count * 65536) + New_capture - Old_capture;
Old_capture = New_Capture

So my main routine just looks for the 'capture found' flag, and just checks if Measured period is less than 65536 before doing anything.

For debugging, when it overflows, I send a byte out a software UART (tested and working fine). Every so often, almost randomly - maybe once every.. 30 seconds - it sends out an overflow byte, despite capturing a frequency well within its range.

I have used the above equation in the interrupt, in the main routine (when checking for the flag) and both give the same results. That is, its very accurate the majority of the time, but occasionally, it measures a period over 16-bits. I am fairly sure there is a problem with the overflow_count, as this would cause erroneous results from the above equation.

Here's some proper code for the interrupt:
Code:
void interrupt()
     {
     if (PIR1.TMR1IF)
        {
        flags.TMR1_OF = 1;
        TMR1_OV_cnt1++;
        PIR1.TMR1IF  = 0;
        }
     if (PIR1.CCP1IF)
        {
        TMR1_OV_temp = TMR1_OV_cnt1;             // get a copy of the overflow counter
        TMR1_OV_cnt1 = 0;                        //
        Old_capture = New_capture;
        Hi(New_capture) = CCPR1H;
        Lo(New_capture) = CCPR1L;
       
        // check if timer1 overflowed whilst we are servicing this:
        if (PIR1.TMR1IF)
           {
           if (Hi(New_capture) == 0)
              {
              TMR1_OV_temp++;
              TMR1_OV_cnt1++;
              PIR1.TMR1IF = 0;
              }
           }
        // get difference
        Period_val_long = (New_capture - Old_capture);
        if (Old_capture > New_capture)
           {
           TMR1_OV_temp--;         // if the new is less than the old , allow one overflow
           }
        Higher(Period_val_long) = TMR1_OV_temp;     // add 16*overflow to our 24-bit counter.
        flags.Cap_found = 1;
        PIR1.CCP1IF = 0;
        }

And in the main routine...

Code:
do {
        if (flags.TMR1_OF)
          {
          flags.TMR1_OF = 0;
          if (TMR1_OV_cnt1 > 1)
             {
             TMR1_OV_cnt1 = 3;         // cap our overflow
             SoftUARTsend('O');
             continue;
             }
          }
        if (flags.Cap_found)       // if we have a capture
          {
          flags.Cap_found = 0;
          // disable interrupts to copy ISR value)
          INTCON.GIE = 0;
          Period_val_long_a = Period_val_long;
          INTCON.GIE = 1;

          if (Higher(Period_val_long_a))   // just checks the second MSByte of a long, anything other than 0, means its > 65535 = overflow.
             {
              LED1 = 0;
             SoftUARTsend('P');
             continue;
             }
         
          Period_val = Period_val_long_a;   // truncate to 16-bits, as upper 16-bits are 0

So, Period_val is a uint - 16-bit variable, Period_val_long_a is a unsigned long, 32-bits. The above code will occasionally spit out a 'P' from the UART despite a continuous input of around 100Hz. The other part of the main code computes an RPM value from the period, using a division routine (fully tested) and most of the time, the result is bang on.

If anyone has some working reliable interrupt driven CCP capture code, to continuously measure an input period *and* detect overflow, I would be grateful. So far the only one I have found that does this is here:

https://www.ccsinfo.com/forum/viewtopic.php?t=53557&highlight=ccp
https://www.ccsinfo.com/forum/viewtopic.php?t=53557&highlight=ccp
I can follow it, but there are subtle differences between CSS and mikroC. For example, it has separate routines for each interrupt source, where-as I have a single ISR, that checks each flag every time. It could be that I suppose, I have so many versions to check :/

Sorry for posting yet another 'I'm stuck!' question. But I usually over complicated things to the point I can't see the simple mistakes.

Cheers,

BT.

Ps. Roman, big fan of your website, and timing code, would appreciate your input here!
 
Does the unwanted overflow occur when the CCP value is close to 65535? If so, it would appear that the overflow interrupt is occurring after the CCP interrupt, but is being processed before it. You could spit out a different character on the serial port if there's an overflow and the CCP value > 65500, for example.
 
If the overflow occurs while the interrupt is being serviced then you increment the count after it's been cleared thus making it invalid in the next period.
Code:
     if (PIR1.CCP1IF)
        {
        TMR1_OV_temp = TMR1_OV_cnt1;
        TMR1_OV_cnt1 = 0;                                      // Cleared here
        Old_capture = New_capture;
        Hi(New_capture) = CCPR1H;
        Lo(New_capture) = CCPR1L;
     
        // check if timer1 overflowed whilst we are servicing this:
        if (PIR1.TMR1IF)
           {
           if (Hi(New_capture) == 0)
              {
              TMR1_OV_temp++;
              TMR1_OV_cnt1++;                                  //Set here.
              PIR1.TMR1IF = 0;
              }
           }
I suspect you only meant to set the temporary variable.

Mike.
 
Ps. Roman, big fan of your website, and timing code, would appreciate your input here!
Unfortunately he doesn't frequent here anymore... If you post a similar post at AAC he will probably see it.

However! The reason the linked code works isn't because its CCS, its because the pic18 have two interrupts... The timer gets high priority... Have you tried servicing the ccp outside the interrupt?? If you just flag that the CCP interrupt has occurred, then clear the flag in your main routine when it has been dealt with...
 
Hi Blueteeth,
I had a similar problem a few years ago with a tachometer that sound similar. This is part of the software description that describes how I solved it.

There is a situation that could occur if an input pulse is received from the sensor at almost the same time as TMR1 overflows. We do not know if the value of TMR1 was captured just before or just after it incremented. The code which deals with CCP1 interrupts tests if a TMR1 overflow interrupt has also occurred. If so it tests the top bit of the captured value of TMR1. If it is set then the capture occurred just before TMR1 overflowed. If it is clear then the captured value of the top 16 bits of the 32 bit count of the number of 0.4 uS pulses is incremented..

The full description is on my web page ( https://lesjhobbies.weebly.com/) under the section of a simple tachometer. The project is written in assembler so the source code will probably be now help to you.

Les.
 
Does the unwanted overflow occur when the CCP value is close to 65535? If so, it would appear that the overflow interrupt is occurring after the CCP interrupt, but is being processed before it. You could spit out a different character on the serial port if there's an overflow and the CCP value > 65500, for example.

Excellent suggestion! I didn't think of that.. could also spit out a copy of the timer overflow value, I think I am probably incrementing it twice at some point. I did have an idea of checking if the capture value was < 256, so I could work out if the last timeroverflow was before, or after the capture interrupt ping . I'll add this to the list of 'debug things to add'.

Pommie. I'm sure I had a valid reason for doing that. but now you mention it.. I can' think why >.< I believe it was... when the capture event fires, *ideally* I should also grab a copy of the overflow timer - effectively getting a snapshot of the 16-bit timer (capture mode), but also the timer extension (timer1 overflow counter). Giving me my 24-bit counter. But of course, the capture is in hardware, and the copying of the timer overflow is in software, so the 16-bits are stored within 1 instruction cycle, but the upper 8 bits take.. however long it takes to get to that part of the interrupt. That difference requires a way to check *when* the last overflow occurred, before, or after the capture.

Ok, so that's my reasoning.. and looking at it you're right.. if the timer overflows whilst servicing the interrupt.. it doesn't matter, as it should re-enter the ISR once that routine has finished, and see the timer1 overflow.

Les Jones. Thank you! I cut my teeth with PIC's using assembler, so I can follow it just fine. And yes, it seems we all agree it is about how I find out what the timer1 overflow *was* when the Capture fired. This would explain the occasional blips, as it would only happen if the capture was almost when timer1 overflowed. I shall go over that code with a fine tooth comb :)

Unfortunately he doesn't frequent here anymore... If you post a similar post at AAC he will probably see it.

However! The reason the linked code works isn't because its CCS, its because the pic18 have two interrupts... The timer gets high priority... Have you tried servicing the ccp outside the interrupt?? If you just flag that the CCP interrupt has occurred, then clear the flag in your main routine when it has been dealt with...

Ah of course.. I have waaay too many PIC's but haven't used an 18F in a while (past few jobs/projects have all been pretty small ones). I have tried servicing the CCP outside of the ISR. My original interrupt, just set flags to indicate to the main code what to do, and was very short. I decided to push calculating the 'capture difference' part into the ISR because I was worried I would run out of time doing other things like 32-bit unsigned division, software-UART sends etc.. but it turns out it made no difference whether that was in the ISR, or in the main code that checks the flag. I was going to just use a higher Fosc of 8, or 16MHz, but that was before I decided to use the timer1 overflow/extension which meant I was limited by the max period of timer1.

Hold the phone!!!!

How can you use timer1 as a 100mS cycle counter!!! CCP needs timer1 to operate!! Its where it gets its timing from!
Ahh, thats timer2 :D I always have a 'heart beat' timer going somewhere for slow, or low priority things, that just sets flags in the ISR. Timer1 is indeed for the CCP, timer2 for a heartbeat. I generally leave timer0 for things like bit-banging, as it has a versatile prescaler.

Cheers lads! This morning (before I read these posts) I came to the conclusion to start from scratch, not continuously measuring periods between rising edges, but just measuring the difference between two edges - then calculating - adding to window average - then re-enabling the CCP, and timer1, ready to grab another two edges. At low input frequency, that should all be done by the time another edge comes along (120ms), but at higher input frequency, it'll miss out on a few edges, only capturing periods every... 4-5 input pulses. This is actually a good thing, as although I do need fast response, the output only gets updated every 20-40ms anyway. The output isn't a display, it is a voltage from a DAC, so response time can be (and has to be) much faster. This is why I didn't count pulses, but rather measure periods.

I'll post any modified code if it seems to behave, so anyone else who has issues can go through this topic.

BT
 
Ahh, thats timer2 :D I always have a 'heart beat' timer going somewhere for slow, or low priority things, that just sets flags in the ISR. Timer1 is indeed for the CCP, timer2 for a heartbeat. I generally leave timer0 for things like bit-banging, as it has a versatile prescaler.

But you're testing the timer 1 interrupt flag ??
 
Seriously, I identify the problem and nobody else sees that I'm right?

Mike.
 
I did see the problem you pointed out Pommie.

Problem 1: As I was checking TMR1 flag first.. if there was a capture, then TMR1 overflowed, it would first read the overflow, increment the counter, and clear the flag. Then check the CCP routine, reading in the capture, along with the extra (erroneous) timer overflow that occurred *after* the capture.... and than check the timer flag again (even though it was previously cleared).

Problem 2: Incrementing the timer overflow counter, shortly after it has been cleared, is indeed a no-no.

I have moved the TMRIF checking to after the CCP1. TMR1IF is only cleared in the checking code, not the CCP1 routine - but still checked in the CCP1 routine, and.. providing the CCP1 capture value is low (was captured just after the overflow) it increments the 3rd byte of the capture value (+OF x 65526).

I am still having issues, and I now have two example codes to work through (both assembly). It is proving tough to separate out the bits I don't need, whilst seeing whats happening.

I haev rewritten this at least 6 times so far, and in my latest test, I made minor mods to the code I posted, and instead of spitting out a 'P' when it the result is over 16-bits, I send out a copy of the latest capture value. I have left it running for 20 minutes so far, and have 12 overflows with a 200Hz input. All the values it kicks out are very low 0x0005, 0x0002, 0x0006 etc.. EDIT: Also seeing some very high values: 0xFFF4, 0xFFF2.

So... this says, the capture value when it flags up an overflow occurs just before the capture. As my prescaler is 2 (apologies, I said it was 8 earlier, but the calcuations afterwards take this into account), that means if the capture is say '5', that is 10us.

I am still trying to get my head around it - usually I can get the basic principles down, but its giving me a headache trying to work out exactly the order in which to do things..

Ok, time for some code. I will only post my interrupt routine, and what happens in the main.

Code:
void interrupt()
     {
     // checks for interrupt sources, namely the USART recieve flag.
     if (PIR1.CCP1IF)
        {
        TMR1_OV_temp = TMR1_OV_cnt1;             // get a copy of the overflow counter
        TMR1_OV_cnt1 = 0;                        //
        Old_capture = New_capture;
        Hi(New_capture) = CCPR1H;
        Lo(New_capture) = CCPR1L;
     
        // check if timer1 overflowed whilst we are servicing this:
        if (PIR1.TMR1IF)
           {
           if (Hi(New_capture) == 0)
              {
              TMR1_OV_temp++;
              }
           }
        // get difference
        Period_val_long = (New_capture - Old_capture);
        if (Old_capture > New_capture)
           {
           TMR1_OV_temp--;         // if the new is less than the old , allow one overflow
           }
        Higher(Period_val_long) = TMR1_OV_temp;     // add 16*overflow to our 24-bit counter.
        flags.Cap_found = 1;
        PIR1.CCP1IF = 0;
        }
     if (PIR1.TMR1IF)
        {
        flags.TMR1_OF = 1;
        TMR1_OV_cnt1++;
        PIR1.TMR1IF  = 0;
        }
     if (PIR1.TMR2IF)
        {
        flags.Timer_update = 1;
        PIR1.TMR2IF = 0;
        }
     if (INTCON.INTF)
        {
        flags.char_fnd = 1;
        INTCON.INTE = 0;   // disable interrupt
        INTCON.INTF = 0;
        }
     }

The main that checks the flag:
Code:
do {
        if (flags.TMR1_OF)
          {
          flags.TMR1_OF = 0;
          New_capture_temp = New_capture; // get a copy of our latest
          if (TMR1_OV_cnt1 > 1)
             {
             TMR1_OV_cnt1 = 3;         // cap our overflow timer - can only be reset by a capture
             RPM_val = 23;             // minimum
             RPM_val_DAC = RPM_val;
             LED1 = 0;
             SoftUARTsend('O');
             continue;
             }
          }
        if (flags.Cap_found)       // if we have a capture
          {
          flags.Cap_found = 0;
          // disable interrupts to copy ISR value)
          INTCON.GIE = 0;
          Period_val_long_a = Period_val_long;
          INTCON.GIE = 1;

          if (Higher(Period_val_long_a))
             {
             RPM_val = 23;             // minimum
             RPM_val_DAC = RPM_val;
             LED1 = 0;
             SoftUARTsend(Hi(New_capture_temp));
             SoftUARTsend(Lo(New_capture_temp));
             continue;
             }
       
          Period_val = Period_val_long_a;   // truncate to 16-bits, as upper 16-bits are 0
          ......

The 'flags.TMR1_OF' isn't actually needed. I originally added that to just check for a timeout when there isn't an input.

As I mentioend before, this is continuous period measurement, getting delta from each input pulse. I may end up using a simple state machine that gets the first capture, then the second, then stops. This way, I can grab the first timer value, clear any timer overflows, leaving the second capture to have a timer value + overflows. This way I shouldn't have to fiddle about incrementing, or decrementing the overflow counter based on which capture is larger.
 
Last edited:
But you're testing the timer 1 interrupt flag ??

Oops, forgot to reply to this.
I'm testing timer1 interrupt flag to check for overflows, so those can be added to the capture value extending the 16-bit timer to 24-bits (or 32-bits if the overflow counter is 16 bits). It also sets a flag so the main routine can keep track of it to see if there is a timeout (many overflows, no input) but that part is somewhat redundant and I'll add a separate timeout counter, in fact, that may be part of the problem, as if the overflow counter is >1, it caps it forces it to 3. I'll check the main routine so it doesn't touch this counter at all, and uses a separate one.
 
If using the CCP module and require low resolution have you though of using a 32Khz clock crystal on the TIM1 input you will have T1 roll over of 1 sec or 2 sec etc. pre load T1 for anything in between.
Max.
 
Alas I would like quite good resolution, I'm outputting the result as a 12-bit voltage, but 10-bit resolution is acceptable (use the 2LSB's to adjust for minor offsets). With a prescale of 2, and Fosc of 4Mhz, it easily covers the low end of the range I want, whilst still getting fairly good resolution at the higher end (shorter periods). In fact the count is around 1500 for my maximum input frequency, giving a theoretical precision of 1 in 1500, increasing with lower frequency.
 
Do you have another timebase with a period less than your 524ms overflow rate (e.g. a counter variable incremented every 10ms)? If so, you can use the timebase to get a rough estimate of the total capture period, and the CCP register to get the accurate estimate of the capture period MOD 65536. This shouldn't have any trouble with overflows as you are seeing at present.
 
I have two other timers going, timer2 is every 10ms, so I could use that. With a prescale of 8, 4Mhz Fosc, timer1 overflows every ~131ms. That is only a count of 13 for a simple counter on timer 2.
Even if I did rewrite a routine that just waits for one edge, then a second, then disables everything whilst I compute, this is bugging me now :/

As dougy suggested, I got a value for 'new capture' every time it fired and the subtraction indicated an overflow (New capture is a 32-bit long, I simply check if the 2nd MSB is non-zero, ie > 65535). The results I have are either very low (< 7 counts) or very high (0xFFFB) but some are lower such as 0xFC07. So there is definitely something fishy in my code when using this overflow. Often I have issues with syntax and best practices with code, but occasionally, its the actual algorithm itself.

Just to prove the rest of the system works, I did the dirty (and non-precise) of reloading timer1 every time CCP1 fired, using a 'guess' of the instruction latency of 32. That is reload TMR1L with 16, and clear TMR1H. This of course works, as every capture is referenced from *roughly* a start of 0. The overflow counter can then either be used as a extension, or just a flag, as anything other than 0 indicates an overflow. That'll be back-up code which may well be precise enough for the application, but I can't let this go until I understand whats happening. Tomorrow I might even draw some timing diagrams to work out the order of things :/

Les, I have every confidence your tach project is solid, but I am not sure I follow the averaging side of things - it is wonderfully complicated, incorporating counters for display refresh, averaging, and some quite awesome assembly maths routines. Right now I'm just after a routine that can spit out a difference between two captures, that is 16-bits + (ie 16-bit capture value, and an overflow flag). I'll try and convert yours to pseudocode.
 
Hi Bluetooth,
It only does averaging if there is more than one pulse within 0.5 seconds. Basically we start counting input pulses and counting timing pulses. When the next input pulse occurs after 0.5 seconds have elapsed we do the calculation by dividing the total time (Since the last calculation.) by the number of input pulses. This gives the average time for one revolution. If the time between pulses is more than 0.5 seconds then there is no averaging done. So that the last valid reading is not displayed for ever when the machine stops there is another test to see if the time since the last pulse is greater than some chosen time. (I can't remember the value I have set it to but it has to be long enough to allow for the slowest speed to be measured.
I did not write the maths routines. they came from an application note on the Microchip website. I think I have created a slightly modified version for use with the PIC18 instruction set. (I think I did this when designing a PID motor speed controller for the table feed on my milling machine.)

If you can find a way to view the assembler code created by the "C" compiler it might be worth looking at that in case the way it is compiled is causing the problem. (It took me a long time to find out how to do this with compiled "C" code on an Arduino.)

Les.
 
Les,

Yes, I generally have the generated assembly in view, and check what it does for various C comparison routines.

I'm glad you mentioned that because that leads me to .. a long post about detective work and bugs. Warning.. long post.. One of those bugs I think is a compiler bug (I know many blame the compiler for their own silly mistakes, but it really is...). Yesterday I stripped it all down and when the main routine detected an overflow (32-bit period output > 65535) I sent the 'new capture' and 'old capture' out the UART. After leaving it for an hour I picked up quite a few outputs and noticed a pattern:

Code:
    should be 135.7Hz = 3684 count = 0E64


     <newcap>       <oldcap>    difference (only 16-bits)   
RX:  01 0E 72        00 01         0E71
RX:  01 0E 72        00 04         0E6E
RX:  01 FF F8        F1 96         0E62
RX:  01 FF F7        F1 9B         0E5C
RX:  01 FF FA        F1 9B         0E5F
RX:  01 0E 6F        00 05         0E6A
RX:  01 FF F4        F1 91         0E63
RX:  01 FF FD        F1 98         0E65
RX:  01 FF F2        F1 93         0E5F
RX:  01 FF F5        F1 8C         0E69
RX:  01 0E 6A        00 07         0E63
RX:  01 FF F8        F1 99         0E5F
RX:  01 0E 65        00 03         0E62
RX:  01 0E 61        00 03         0E5E

As you can see, if we just look at the values as 16-bit, the differences are all very close to the ideal - 0E64. The variation is probably down to my simple 555 timer generator not being overly stable. If it was my frequency generator dropping pulses, then the results wouldn't all be roughly the same.

So, it only flags up an overflow when within range when :

1. The 'New_capture' value is very high - close to overflow, FFF2. OR..
2. When the 'Old_capture' is very low, just *after* an overflow has occurred.

I suspected that the extra bit of code used to check if the timer1 had overflowed during the CCP1 routine was always adding an overflow count if timer1 overflowed during - regardless of the value of the captures high byte.

The original code:
Code:
// check if timer1 overflowed whilst we are servicing this:
        if (PIR1.TMR1IF)
           {
           if (Hi(New_capture) == 0);
              {
              TMR1_OV_temp++;
              }
           }

Checking the assembly output, I found this:

mikroC_bug2.png


Anyone who has done PIC assembly can see that, _New_capture+1 is moved to W, XOR'd with 0 so the zero flag is set if the W reg is 0. If set, it should replace the goto statement with NOP, and execute the INCF statement. It does. But... if the result isn't zero, the goto statement is executed.. and goes to.. L_interrupt23, which is also the INCF statement. So it always executes, regardless of the value of _New_capture+1. It should goto L_interrupt22.

So I replaced that routine with one that checks both the TMR1IF, and that Hi(New_capture) is 0, in the same line. Seems like it only does it if an 'IF' statement executes only another IF statement and nothing else. As soon as you add an extra line in there, the above bug disappears.

Now, that got rid of the overflows where the New_capture had a large value. Another test run ...

Code:
     <newcap>        <oldcap>
RX:  01 07 9E        00 05
RX:  01 07 97        00 00
RX:  01 07 9A        00 04
RX:  01 07 99        00 05
RX:  01 07 96        00 03
RX:  01 07 A1        00 02
RX:  01 07 95        00 00
RX:  01 07 97        00 01
RX:  01 07 97        00 02
RX:  01 07 99        00 07
RX:  01 07 99        00 03
RX:  01 07 9B        00 06
RX:  01 07 97        00 00

Ok, so now the second problem. Timer1 overflows, then a few us later (each count is 2us, so 0007 = 14us) it captures an edge. The overflow causes the ISR to execute, and the first thing it does is check the CCP1IF for a capture event.. if this event occurred as it was entering the ISR, then it services the capture module first. That routine also checks TMR1IF, it is set, so it adds 65535 to the 0007 capture = 0x00010007. But it doesn't clear the TMR1IF, so after the CCP1 routine, it checks the overflow flag again, and as it is set, increments the timer overflow counter.

Next time it captures an event (at timer1 = 0799) it stores this overflow, which was incremented to '1', and again, adds this to the result. This gives 0x00010799. Before that it also copies the old New capture to 'Old_capture', which... I stupidly made only a 16-bit variable, so it isn't 0x00010007, but just 0x0007. The subtraction gives 0x00010799 - 0x00000007 = 0x00010792 => overflow. At some point, the overflow counter needs to be cleared, after the first capture, but before the second. It is in the first part of the routine, but then incremented in the TMRIF check.

Code:
void interrupt()
     {
     // checks for interrupt sources, namely the USART recieve flag.
     if (PIR1.CCP1IF)
        {
        TMR1_OV_temp = TMR1_OV_cnt1;             // get a copy of the overflow counter
        TMR1_OV_cnt1 = 0;                        // overflow counter cleared.  TMR1IF still pending
        Old_capture = New_capture;
        Hi(New_capture) = CCPR1H;
        Lo(New_capture) = CCPR1L;
       
        // check if timer1 overflowed whilst we are servicing this:
        if (PIR1.TMR1IF)  // TMRIF still active
           {
           if (Hi(New_capture) == 0);
              {
              TMR1_OV_temp++;
              }
           }
        // get difference
        Period_val_long = (New_capture - Old_capture);
        if (Old_capture > New_capture)
           {
           TMR1_OV_temp--;         // if the new is less than the old , allow one overflow
           }
        Higher(Period_val_long) = TMR1_OV_temp;     // add 16*overflow to our 24-bit counter.
        flags.Cap_found = 1;
        PIR1.CCP1IF = 0;
        }
     if (PIR1.TMR1IF)
        {
        flags.TMR1_OF = 1;
        TMR1_OV_cnt1++;  // TMR1IF was still active, so an overflow was added.
        PIR1.TMR1IF  = 0;
        }

If I make the 'Old_capture' a 32-bit variable, it is stored as 0x00010007, but seems to overflow all the time. So I am kind of stuck here. Perhaps the part that checks TMR1IF in the CCP routine, should also check if the captured value was low (<0009), and if so, clear the TMRIF flag to prevent the overflow counter from incrementing.

Again, sorry for the epic long post. I tend to over complicated things, but I also hate it when I don't fully understand something, and lord knows it can take me a while :(

As always, thoughts suggestions as to what to do are welcome!!

BT
 
Hi BT,
First I will point out that I am useless at programming in "C" (I consider myself average at assembler.) I could see the first problem as soon as a looked at the assembler listing. (Even before reading your comments about it.) I'm not sure if I understand the second problem fully. I think the way you check if the timer overflowed is a good one (After seeing it it seems the obvious way to do it rather than looking to see if it contained a low value or high value.) I can't see why it should overflow all of the time as you cleared the flag last time in the interrupt routine. I don't follow exactly how the overflow counter works. I would have thought that the 16 bit value captured was the lower 16 bits of a 32 bit word and bits 16 to 31 was the value of the overflow counter. I will have another look at it later to see if I can fully understand how your code is working.

Les.
 
I'm just as confused mate,

A 32-bit value, with an overflow, will always be bigger than any 16-bit value. But perhaps it didn't always add the overflow? I have left the old value as a 16-bit variable - not ideal if I wanted to capture longer periods, as it negates the use of the overflow counter as a timer extension.

C is awesome. I used to write everything in assembly, as it allows full control, and makes precise timing easy. But for those projects where you just want to knock something up quickly, it became just too tedious. With C, and a compiler with built in routines, I can have a little micro reading SPI, UART, I2C, performing all manner of mind boggling maths, measuring periods (one shot.. not like this one..) up in less than an hour.

Well, I have it sitting here, and it hasn't complained once with a frequency well within range. Output is stable, and indicates out of range nicely (this time only one character out the UART depending on too high or too low). All I did was clear the TMR1IF, when it is checked in the CCP1 routine.. IF... the captured result was <10. If the timer overflows just before the capture, it still adds 65526 to the capture, but.. as it clears the flag, it prevents the ISR part that checks TMR1IF from incrementing the overflow counter (which, wold be added to the next capture).

I just deleted a massive explanation as I confused myself, plus.. I'm not sure anyone really wants to read it, but here's what *appears* to be working code:

Code:
void interrupt()
     {
     // checks for interrupt sources, namely the USART recieve flag.
     if (PIR1.CCP1IF)
        {
        Higher(New_capture)  = TMR1_OV_cnt1;             // get a copy of the overflow counter
        TMR1_OV_cnt1 = 0;                        //
        Old_capture = New_capture;
        Hi(New_capture) = CCPR1H;
        Lo(New_capture) = CCPR1L;
      
        // check if timer1 overflowed whilst we are servicing this:

        if ((PIR1.TMR1IF) && (Hi(New_capture) == 0))
           {
           Higher(New_capture)++;
           if (Lo(New_capture) < 10)
              {
              PIR1.TMR1IF = 0;
              }
           }
        // get difference
        Period_val_long = (New_capture - Old_capture);
        flags.Cap_found = 1;
        PIR1.CCP1IF = 0;
        }
     if (PIR1.TMR1IF)
        {
        flags.TMR1_OF = 1;
        TMR1_OV_cnt1++;
        PIR1.TMR1IF  = 0;
        }
     if (PIR1.TMR2IF)
        {
        flags.Timer_update = 1;
        PIR1.TMR2IF = 0;
        }
     if (INTCON.INTF)
        {
        flags.char_fnd = 1;
        INTCON.INTE = 0;   // disable interrupt
        INTCON.INTF = 0;
        }
     }

It really does look like a fudge, and in a way, it is - trying to cope with the possibility of CCP1IF or TMR1IF firing at any point in the ISR.

I did think 'whats the big deal? as long as your input period is within range, and you can detect when its way off, just subtract the two in 16-bit'. But if it is just out of range, only a few counts over 16-bit, the subtraction yields a small number (indicating high frequency input) and the timer only overflowed once. I cannot guarantee that a user will only subject this to inputs within range. I guess this is why microchips application note about measuring periods with the CCP1 is somewhat mute on the subject over overruns - can become a minefield to detect it every time. I hope microchip take a leaf out of Atmels use of capture in the XMEGA series - give the option of resetting the timer at the same time as a capture :D

I could always choose a lower clock, or a higher pre-scaler. I can't really use a higher pre-scaler, because I have the option of capturing every 4 events, rather than single ones. This is 4x the period, so my pre-scaler of 2, must be multiplied by 4 = 8. And 8 is the highest it goes.

So yeah, not sure if it is case closed or what.
 
Hi BT,
I'm pleased you've sorted the problem out. I realised overnight that just testing the interrupt bits in the registers was not enough to be sure if an overflow occurred just before or just after after the capture. I keep having attempts to learn "C" but the syntax does not seem to stick in my brain. The thing I like most about the Atmel micro is the vectored interrupts. I also like the Texas MSP430 but find the development environment very cumbersome compared with the PIC and Atmel IDEs

Les.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top