CCP2 Capture - where am I going wrong?

Coding and general discussion relating to user created compiler modules

Moderators: David Barker, Jerry Messina

Post Reply
SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

CCP2 Capture - where am I going wrong?

Post by SHughes_Fusion » Thu Apr 30, 2015 9:53 am

I'm trying to use the capture function of the CCP2 module on the PIC18F26K22 and I'm struggling! I've used CCP1 on the 14K22 before without problem but I can't seem to get any interrupts generated with this new setup.

Has anyone used this before and noticed anything to keep an eye out for?

I've done the following:
In Config, set CCP2MX = PORTB3.
Defined CCP2IE = PIE2.0 and CCP2IF = PIR2.0 and initialised these to 1/0 respectively.
Set all inputs as analogue and done Input(PORTB.3).
Set CCP2CON = %00000100, T3CON = 0 and T1CON = %00110010.
And finally done Enable(OnInt)

I've stripped the ISR down to just setting a flag when it sees a CCP2IF interrupt but that flag never gets set! I've confirmed there is a pulse train coming in - I have an LED available which I set to be the inverse of PORTB.3 and that flashes as expected.

Any ideas at all why I'm not getting this interrupt? From what I can see (and this works on other code) I don't need to set PEIE as Swordfish works will all interrupts high priority so GEIE enables everything. Not that this has stopped me trying this and numerous other things!

This really is one of those things that should take 2 minutes but has now taken over 3 hours...

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: CCP2 Capture - where am I going wrong?

Post by Jerry Messina » Thu Apr 30, 2015 10:35 am

Just off the top of my head from the description...

>>Set all inputs as analogue and done Input(PORTB.3)

You need RB3 (CCP input) to be digital


>> T1CON = %00110010

You didn't mention that you're turning on the timer anywhere (TMR1ON, T1CON.bits(0))


>>Defined CCP2IE = PIE2.0 and CCP2IF = PIR2.0 and initialised these to 1/0 respectively.

Move this step to after: Set CCP2CON = %00000100, T3CON = 0 and T1CON = %00110010.
You should always clear the IF after changing the CCP mode/setup

I didn't actually try it, so there might be something else going on too. Might help to have the real code.

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: CCP2 Capture - where am I going wrong?

Post by SHughes_Fusion » Thu Apr 30, 2015 10:39 am

Sorry, typo there... All ports set to digital, not analogue.

I'm not turning on the timer but my understanding is that this isn't a pre-requisite for a capture interrupt to be generated?

The plan is that when I see the first capture interrupt I turn the timer on so the subsequent capture in effect starts from zero.

Hard to post code as there is a lot in there not relevant, I'll see if I can cut it down and post something.

One thing, I've read elsewhere that CCP2CON isn't in access RAM on some PICs, not sure if this applies to the 26K22 but presumably Swordfish (full version, not SE) should deal with this anyway? The ASM generated to set CCP2CON is:

Code: Select all

    MOVLW 4
    MOVWF CCP2CON,0

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: CCP2 Capture - where am I going wrong?

Post by SHughes_Fusion » Thu Apr 30, 2015 10:43 am

Actually, I will post all the code as it may be relevant:

This has the capture interrupt enabling commented out as I'm trying just polling the flag and toggling an LED to show captures are being detected - still nothing!

The code works with a bootloader to explain the offset and uses your SetAllDigital routine.

Code: Select all

Device = 18F26K22
Clock = 64

Config
    DEBUG = OFF,
    FCMEN = OFF,
    IESO = OFF,
    PWRTEN = ON,
    BOREN = OFF,
    WDTEN = OFF,
    WDTPS = 4096,
    MCLRE = INTMCLR,
    STVREN = OFF,
    XINST = OFF,
    LVP = OFF,
    CCP2MX = PORTB3 
   
#option org_reset = 2048
#option WDT = true                          // Enable Watchdog, include code in SF libraries to clear watchdog
#option SWORDFISH_SE = false
     
Include "IntOsc_K22.bas"
Include "SetDigitalIO.bas" 
Include "System.bas"

Const Fosc = _clock * 250000                // Instruction clock frequency.

Const BTCBaudGen As Word = ( Fosc / 115200 ) - 1 + 0.5            // Adding 0.5 'rounds up' to the nearest
Const BTCBaudH As Byte = BTCBaudGen / 256
Const BTCBaudL As Byte = BTCBaudGen Mod 256 

    // Define pulse limits. Running at 64Mhz with a divider of 8 on the timer input we have 2MHz in. This gives a pulse time of 0.5usec. We only use the MSB so divide pulse count by 256 to get
    // these values. i.e. Lead Pulse is 9msec = 9000 * 2 / 256 = 70.3. Set a range of 67 to 73 to allow for variation.
    // Other pulses: Leading space is 4.5msec, pulse for either data bit is 562us, space for 0 is also 562us, space for 1 is 1687us, space for repeated data is 2250usec.
Const LeadPulseMin As Byte = 67
Const LeadPulseMax As Byte = 73
Const LeadSpaceMin As Byte = 33
Const LeadSpaceMax As Byte = 37
Const RepeatSpaceMin As Byte = 16
Const RepeatSpaceMax As Byte = 19
Const BitShortMin As Byte = 3
Const BitShortMax As Byte = 6
Const BitLongMin As Byte = 11
Const BitLongMax As Byte = 15

// Timer 1
Dim Timer1H As TMR1H
Dim Timer1L As TMR1L
Dim Timer1IF As PIR1.booleans(0)
Dim Timer1IE As PIE1.booleans(0)
Dim Timer1On As T1CON.booleans(0)

Dim CCP2IE As PIE2.booleans(0)
Dim CCP2IF As PIR2.booleans(0)

Dim IRReceive As LongWord
Dim IRAddress As IRReceive.Word1
Dim IRData As IRReceive.Word0
Dim IRBitCount As Byte

Structure FlagsIR
  Flags As Byte
  Edge As Flags.Booleans(0)
  First As Flags.Booleans(1)
  Invalid As Flags.Booleans(2)
  Timeout As Flags.Booleans(3)
  LeadPulse As Flags.Booleans(4)
  LeadSpace As Flags.Booleans(5)
  Data As Flags.Booleans(6)
  Decoded As Flags.Booleans(7)
End Structure

Dim DecodeIR As Boolean

Dim IRFlags As FlagsIR                              

Dim LED As PORTB.0


    // ISR. Processes timer interrupts
Interrupt OnInt()
   Save(0,FSR0,FSR1)                                        // Save FSR and variables. May need streamlining.
   
   If CCP2IF Then
     Timer1H = 0
     Timer1L = 0
     DecodeIR = true
     If IRFlags.Edge Then
       CCP2CON = 0
       CCP2CON = %00000100
       IRFlags.Edge = false
     Else
       CCP2CON = 0
       CCP2CON = %00000101
       IRFlags.Edge = true
     EndIf
     
     If IRFlags.First Then
       IRFlags.First = false
       IRFlags.LeadPulse = true
       Timer1On = true
     Else
       DecodeIR = true
     EndIf
     CCP2IF = false
   EndIf
   
   If Timer1IF Then
     Timer1IF = false
     IRFlags.Timeout = true
   EndIf
     
   Restore                                                      // Restore saved registers
End Interrupt
  
    // Set which port pins are inputs and which are outputs. Also configures pull-ups. 
Sub SetIO()
  PORTA = 0                   // Set all outputs low before configuring pins
  PORTB = 0
  PORTC = 0
  TRISA = %00000000           // All Port A output
  TRISB = %00001100           // All Port B output apart from button input (RB2) and CCP input (RB3)
  TRISC = %11111101           // RN42 CTS input set as output, Rest inputs. (TX/RX controlled by UART module)
  
  Input(PORTB.3)
  Input(PORTB.2)
  Output(LED)

  SLRCON = %11111011          // Remove slew rate limiting on port C - this is used for the serial port and I2C
  WPUB = %00000100            // Enable pull-up for button
  INTCON2.7 = 0               // Global pull-up enable
End Sub    

Sub PutChar(sdata As Char)
  Repeat
    ClrWDT
  Until PIR1.booleans(4)
  
  TXREG = sdata
End Sub

Sub InitIR()
  CCP2CON = %00000100               // Capture on falling edge
  T3CON = 0                         // Ensure timer 1 selected for capture
  T1CON = %00110010                 // 1:8 Prescale using Fosc/4, 16 bit RW mode, timer off
  Timer1H = 0
  Timer1L = 0
  Timer1IF = false
  Timer1IE = true
  IRFlags.Flags = 0
  DecodeIR = false
  IRBitCount = 0
  IRFlags.Edge = false
  IRFlags.First = true
  IRReceive = 0
//  CCP2IF = false
//  CCP2IE = true
//  Enable(OnInt)                     // Enable interrupts
End Sub
 
Sub ProcessIR()
  If IRFlags.Invalid = false Then
    If IRFlags.LeadPulse Then
      If CCPR2H < LeadPulseMin Or CCPR2H > LeadPulseMax Then
        IRFlags.Invalid = true
      Else
        IRFlags.LeadPulse = false
        IRFlags.LeadSpace = true
      EndIf
    ElseIf IRFlags.LeadSpace Then
      If CCPR2H < LeadSpaceMin Or CCPR2H > LeadSpaceMax Then
        IRFlags.Invalid = true
      Else
        IRFlags.LeadSpace = false
        IRFlags.Data = false
      EndIf    
    Else
      If CCPR2H < BitShortMin Then
        IRFlags.Invalid = true
      ElseIf CCPR2H > BitLongMax Then
        IRFlags.Invalid = true
      Else
        If IRFlags.Data Then            // Decoding space
          IRFlags.Data = false
          IRReceive = IRReceive >> 1
          Inc(IRBitCount)
          If IRBitCount = 32 Then IRFlags.Decoded = true EndIf
          If CCPR2H > BitShortMax Then  // Long space = 1
            If CCPR2H < BitLongMin Then
              IRFlags.Invalid = true
            EndIf
            IRReceive.31 = 1
          Else                          // Short space = 0
            IRReceive.31 = 0
          EndIf  
        Else                            // Decoding pulse
          If CCPR2H > BitShortMax Then
            IRFlags.Invalid = true      // Pulse is always short
          EndIf
          IRFlags.Data = true
        EndIf        
      EndIf
    EndIf 
  EndIf
End Sub                                   


// program start...

ClearRAM    
SetAllDigital                       // All port pins digital (disable analogue peripherals) 
SetIO                               // Configure which pins are inputs and which outputs, and set default levels for outputs.

        // Configure UART   
SLRCON = %11111011                  // Remove slew rate limiting on port C.
BAUDCON1 = %00001000                // 16 bit BRG, no autodetect
SPBRGH1 = BTCBaudH                  // Set BRG to give 115200 baud.    
SPBRG1 = BTCBaudL                
RCSTA1 = %10010000                  // Set 8 bit TX and RX modes and ascyn mode, enable TX and RX
TXSTA1 = %00100110

InitIR

While true
  ClrWDT
  If PORTB.2 = 0 Then
    Asm
      Reset
    End Asm
  EndIf  

  if CCP2IF then
    CCP2IF = false
    toggle(LED)
  endif
  
//  LED = Not PORTB.3
  
  If DecodeIR Then
    DecodeIR = false
    ProcessIR
    If IRFlags.Timeout Then PutChar("T") EndIf
    If IRFlags.Invalid Then PutChar("I") EndIf
    If IRFlags.Decoded Then 
      PutChar(IRAddress.byte0) 
      PutChar(IRAddress.byte1)
      PutChar(IRData.byte0)
      PutChar(IRData.byte1)
      InitIR
    EndIf
  EndIf
Wend                                 

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: CCP2 Capture - where am I going wrong?

Post by Jerry Messina » Thu Apr 30, 2015 11:23 am

I'm not turning on the timer but my understanding is that this isn't a pre-requisite for a capture interrupt to be generated?
The plan is that when I see the first capture interrupt I turn the timer on so the subsequent capture in effect starts from zero.
I've never tried to use it that way, so you might be right about not needing the timer to be on.

Normally, I'd let the timer run. On the first CCPIF, save the captured value. On the next CCPIF, read the new capture and subtract the two to get the time difference.
If you wait until the 1st CCPIF to turn on the timer you'll be off by the interrupt latency + the time it takes to get everything rolling.

I'll take a look over the code to see if I spot anything funny...

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: CCP2 Capture - where am I going wrong?

Post by SHughes_Fusion » Thu Apr 30, 2015 11:28 am

I've just tried enabling the timer and no change.

Interrupt latency isn't a major issue here - I'm decoding pulses from an IR transmitter so I'm only looking for the pulse width to be within a fairly wide range. I'm basing this on some code I found online which does the same and they claim works - seems easier when your timing isn't critical to just reset the timer rather than have to look out for roll-overs.

Also, I (and they) use the timer roll-over interrupt to signal a timeout.

I've just tried changing to keeping the timer running and toggling the LED on Timer1IF and this works as expected.

I'm suspecting the capture isn't being set up properly and so interrupts aren't being generated...

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: CCP2 Capture - where am I going wrong?

Post by SHughes_Fusion » Thu Apr 30, 2015 2:19 pm

Totally stumped by this one!

I've cut the code back to the very basics and the CCP2IF flag is just not getting set. I'm even creating pulses on the CCP2 line and verifying the logic state with a multimeter but the flag is not getting set.

Unfortunately I'm limited on this hardware and can't change to another CCP input to see if it is an issue with the module itself...

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: CCP2 Capture - where am I going wrong?

Post by Jerry Messina » Thu Apr 30, 2015 2:45 pm

I uncommented the lines

Code: Select all

  CCP2IF = false
  CCP2IE = true
  Enable(OnInt)                     // Enable interrupts
in InitIR()

and commented out the

Code: Select all

  if CCP2IF then
    CCP2IF = false
    toggle(LED)
  endif
in the main loop and I get both TMR1 and CCP2 interrupts as I wiggle a line connected to RB3

I have some other minor mods (running on a 25K22 w/out bootloader) but I don't think they'd have much effect. Sure your bootloader is redirecting the ISR ok?

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: CCP2 Capture - where am I going wrong?

Post by SHughes_Fusion » Thu Apr 30, 2015 2:49 pm

Hi Jerry,

I'm sure it isn't to do with the ISR as even just testing the flag I wasn't getting anything.

I've not found the cause but I have got somewhere. After a bit of track hacking I managed to connect the input to CCP1. Changed the code to suit and bingo, I'm getting capture interrupts.

This must be to do with configuring the CCP module....

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: CCP2 Capture - where am I going wrong?

Post by Jerry Messina » Thu Apr 30, 2015 3:04 pm

That's odd. I'm using your setup code... the only thing I've changed is

Code: Select all

Sub InitIR()
  CCP2CON = %00000100               // Capture on falling edge
'  T3CON = 0                         // Ensure timer 1 selected for capture
  CCPTMRS0.bits(3) = 0              // CCP2 uses Timer1 for capture/compare
  CCPTMRS0.bits(4) = 0
....
But that should be the default setting.

I connected the "LED" output (RB0) up to RB3, shut off the interrupts and toggled RB0 high/low and I see CCP2IF get set.

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: CCP2 Capture - where am I going wrong?

Post by SHughes_Fusion » Thu Apr 30, 2015 3:08 pm

You know what, Jerry? I've just realised what a numpty I am...

One down-side of my bootloader is that it doesn't write the config bits - I thought it safest not to change them.

So, while my code sets the fuse to alter which pin CCP2 is on, the bootloader doesn't alter this from the default so the reason I'm not getting CCP2 interrupts is that I'm using the wrong pin...

:oops:

Thanks for your help, I'm sure it pushed me towards the solution faster than I'd have got there myself!

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: CCP2 Capture - where am I going wrong?

Post by Jerry Messina » Thu Apr 30, 2015 3:24 pm

>>One down-side of my bootloader is that it doesn't write the config bits - I thought it safest not to change them.

That's a good thing (until it isn't!). It can be a real disaster if you screw it up. What I've done w/mine is to not allow the bootloader to do config changes by default, but there's an override command to allow the config to be changed. I still cringe and cross my fingers whenever I use that feature...

Of course, I'd probably have forgotten about having to update them and ended up in the same boat as you!

Post Reply