News:

Let's find out together what makes a PIC Tick!

Main Menu

Capture, am I doing something wrong

Started by RGV250, Jan 29, 2022, 08:50 PM

Previous topic - Next topic

RGV250

Hi,
I am simulating a capture and the values do not look right, it could be the VSM but it is a very simple program.
I have a 10hz signal so in the following code I expected to see 100 000 for PERIOD where it is only showing around 62 000. This equates to 960 RPM where it should be 600.
I have set it for every rising edge and the timer is 1:1 to keep things simple.

Any ideas,

Bob

Device 18F452
Config_Start
   OSC = HS ; HS
   OSCS = OFF ; Disabled
   PWRT = On ; Enabled
   BOR = On ; Enabled
   BORV = 45 ; 4.5V
   WDT = OFF ; Disabled
   WDTPS = 1 ; 1:1
   CCP2MUX = OFF ; Disable (RB3)
   STVR = On ; Enabled
   LVP = OFF ; Disabled
   Debug = OFF ; Disabled
   Cp0 = OFF ; Disabled
   CP1 = OFF ; Disabled
   CP2 = OFF ; Disabled
   CP3 = OFF ; Disabled
   CPB = OFF ; Disabled
   CPD = OFF ; Disabled
   WRT0 = OFF ; Disabled
   WRT1 = OFF ; Disabled
   WRT2 = OFF ; Disabled
   WRT3 = OFF ; Disabled
   WRTB = OFF ; Disabled
   WRTC = OFF ; Disabled
   WRTD = OFF ; Disabled
   EBTR0 = OFF ; Disabled
   EBTR1 = OFF ; Disabled
   EBTR2 = OFF ; Disabled
   EBTR3 = OFF ; Disabled
   EBTRB = OFF ; Disabled
Config_End
       
        Xtal = 40                    'Define clock
        All_Digital = True          'Set all I/O to digital
       
'****************************************************************         
'* DISPLAY                                                      *
'****************************************************************
        Declare LCD_DTPort = PORTD
Declare LCD_RSPin = PORTC.1
Declare LCD_ENPin = PORTE.0
Declare LCD_RWPin = PORTC.0
        Declare LCD_CS1Pin = PORTE.1
Declare LCD_CS2Pin = PORTE.2

Declare LCD_Type = GRAPHIC
Declare Internal_Font = On       

        Clear                       'Clear all RAM before we start
   
' Variables for Capture   
    Dim PERIOD As Dword         'Time interval between pulses
   
' Variables for RPM calculations
        Dim FREQUENCY As Float
        Dim RPM As Dword 

'PORT setup
        TRISC.2 = 1                 'SET CCP1 AS INPUT

'Capture settings - used for RPM                       
    Symbol CCPR1_Word = CCPR1L.Word 'Create a 16-bit variable out of CCPRL1
    CCP1CON = %00000101         'Enable the CCP1 capture, every rising edge
'0100 = Capture mode, every falling edge
'0101 = Capture mode, every rising edge
'0110 = Capture mode, every 4th rising edge
'0111 = Capture mode, every 16th rising edge   
   
        Symbol CAPTURE = PIR1.2    'CCP1 capture flag

'Timer 1 is used for capture       
        T3CON.6 = 0
        T3CON.3 = 0

'Timer 1 settings - used for Capture     
        Symbol TIMER1 = TMR1L.Word  'Create a 16-bit variable out of TMR1L
        T1CON = %00000001     'TMR1 prescale=1:1, and turn it on (1uS per count)
'bits 5-4
'00 = 1:1
'01 = 1:2
'10 = 1:4
'11 = 1:8   
     
    Symbol OVERFLOW = PIR1.0 'Timer1 overflow flag 
        Symbol TIMER1_RUN = T1CON.0
 
'INTERRUPT setup 
'Used for RPM 
'   PIE1.2 = 1                      'CCP1 Interrupt enable
'   PIE1.0 = 1                      'Timer 1 overflow Interrupt Enable

       PIE1 = %00000101
   
'****************************************************************
'* Interrupt service vector initialization                      *
'****************************************************************

On_Hardware_Interrupt GoTo INTERRUPT_ROUTINE 
                                                         
        GoTo OVER_INT 
'****************************************************************       
'* Interrupt Service Routine                                    *
'****************************************************************

INTERRUPT_ROUTINE:
        Context Save   
        T1CON.0 = 0                 'Stop the timer
 
RPM_VAL:
    PERIOD = CCPR1_Word        'Store the captured value
 
        FREQUENCY = 1000.0 / (PERIOD)
       
        RPM = FREQUENCY * 6000      'Maths is done this way so RPM only
        RPM = RPM * 10              'increments in 10 RPM intervals   
                                             
      Clear TIMER1     'Clear Timer1
    CAPTURE = 0     'Clear CCP1 capture flag
    OVERFLOW = 0     'Clear Timer1 overflow flag                    
                                   
        T1CON.0 = 1   
        Context Restore             'Exit ISR and re-enable interrupts 

'****************************************************************
'* End of Interrupt routines                                    *
'****************************************************************       

OVER_INT:     

'***********************************************
'* Start of Main Code                          *
'***********************************************
START:
        DelayMS 100                 'Wait for the PICmicro to stabilise
     
        Cls
       
        INTCON = %11000000          'Turn interrupts on   
           
'************************************************
'* Main program LOOP                            *
'************************************************
MAIN_LOOP:

        Print At 1,4, "PERIOD ",Dec PERIOD, "     "
        Print At 2,1, "FREQUENCY ",Dec FREQUENCY, "     "
        Print At 3,7, "RPM ", Dec RPM, "     "

        GoTo MAIN_LOOP       

Include "FONT.INC"


trastikata

I see some FP math in the ISR while the timer is off but you are not compensating for the time the clock is off?

m.kaviani

Where is your PLL config?
you are using xtal = 40 but without PLL activation. it seems you connected 40Mhz crystal to PIC.
may use this one.
Config_Start
  OSC = HSPLL ;HS oscillator, PLL enabled (Clock Frequency = 4 x FOSC1)
Config_End

RGV250

Hi,
Thanks for the replies.
QuoteWhere is your PLL config?
I am using a 40mhz osc module, also if it was the PLL surely the value I got in the capture would be a quarter of what I am expecting.

QuoteI see some FP math in the ISR while the timer is off but you are not compensating for the time the clock is off?
This should not matter as the capture is the count between 2 rising edges, then it does the calculation and resets and restarts for the next capture.
I did move the calculation outside the interrupt to try and I was surprised I got a totally different (but not correct) result.

Regards,
Bob


tumbleweed

#4
A 10Hz period is 100ms.

If your clock is 40MHz then the instruction clock is Fosc/4 = 10MHz (100ns).
Without a prescaler (prescaler=1:1), the max a 16-bit timer can count is 65535 x 100ns = 6553500ns = 6.5535ms, so you can't measure 100ms.
The largest prescaler for TMR1 with an 18F452 is 1:8, making the max time 8 x 6.5535ms = 52.428ms.

So, if you want to measure a 10Hz period (100ms) you'll need to slow down the clock.
If you want a 1:1 prescaler the max count rate would need to be about 0.655MHz (2.62MHz system clock)
Using a 2MHz clock would give a count rate of 500KHz (2us), giving a max time of 65535 x 2us = 131ms

Either that, or use a 1:8 prescaler and drop the clock to 20MHz.


Now, when you use the CCP Capture mode to measure period, one way is to get the difference in time between two rising edges. Capture mode doesn't automatically measure the time between two edges, you have to do that yourself. If you want the time to be cycle-accurate, don't stop TMR1, and don't reset it.

The easiest example is without using interrupts (this isn't complete... just the basics)
' 18F452 hdw reg bits
Symbol CCP1IE = PIE1.2
Symbol CCP1IF = PIR1.2
Symbol TMR1ON = T1CON.0

' Combine CCPR1L and CCPR1H into unsigned Word variable
Dim wCCPR1 As CCPR1L.Word

' 16-bit period (number of TMR1 counts between two edges)
' this can count 65535 instruction cycles before it wraps/overflows
Dim wPeriod As Word

' setup TMR1
'T1CON = xx
' set CCP1 to use TMR1
'CCP1CON = xx

CCP1IE = 0 ' disable CCP1 interrupts

TMR1ON = 1 ' turn on TMR1

MAIN_LOOP:
    CCP1IF = 0          ' clear capture flag to begin...
    While CCP1IF = 0    ' wait for 1st rising edge
    Wend
    CCP1IF = 0          ' clear CCPIF flag and
    wPeriod = wCCPR1    ' save the first edge time
   
    While CCP1IF = 0    ' wait for 2nd rising edge
    Wend
    CCP1IF = 0          ' clear CCPIF flag,
    wPeriod = wCCPR1 - wPeriod    ' get 2nd edge time, and calc difference
   
    ' do all your freq and RPM math here
   
    ' display results
    'Print At 1,4, "PERIOD ",Dec PERIOD, "     "
    'Print At 2,1, "FREQUENCY ",Dec FREQUENCY, "     "
    'Print At 3,7, "RPM ", Dec RPM, "     "

    GoTo MAIN_LOOP       

You can change this around to use interrupts, but if you do then remove all of the math from the ISR and do that in the main loop.

EDIT: I changed the routine to clear the capture flag at the start of the loop, before looking for the first edge

trastikata

#5
Quote from: RGV250 on Jan 30, 2022, 01:01 PMThis should not matter as the capture is the count between 2 rising edges, then it does the calculation and resets and restarts for the next capture.
I did move the calculation outside the interrupt to try and I was surprised I got a totally different (but not correct) result.

Hi Bob,

I think it matters, this is the event sequence as per your code:

- Spinning
- Rising edge detected
- Stop timer (continue spinning)
- Do calculations (continue spinning)
- Reset timer (continue spinning)
- Start timer (continue spinning)
- Rising edge detected
...

Thus, the time between two rising edges is:  ISR cycles + Timer count  because while the ISR is running, the device keep spinning - which means that the sensor (or whatever detection mechanism is used) still travels some distance while the timer is not counting.


Quote from: tumbleweed on Jan 30, 2022, 02:03 PMThe largest prescaler for TMR1 with an 18F452 is 1:8, making the max time 8 x 6.5535ms = 52.428ms.
So, if you want to measure a 10Hz period (100ms) you'll need to slow down the clock.

tumbleweed, my understanding is that he's measuring around 1kHz but the data is displayed in increments of 10 Hz.

tumbleweed

#6
Quote from: RGV250 on Jan 29, 2022, 08:50 PMI have a 10hz signal...

If it's not a 10Hz signal, then what is it?

As I mentioned, capture mode doesn't automatically measure the time between two edges... you have to do two captures and look at the difference.

And even if you change it around to use interrupts, trastikata's right in that you don't want all that math in the ISR.

I changed the example code in post#4 to clear the capture flag at the start of the loop.
That'll make the timing of the calculations and displays irrelevant.