PDA

View Full Version : Using hardware capture



Bruce
- 27th January 2010, 01:58
Got a PIC with hardware capture, and looking for a way to record high & low pulse widths
with better resolution than PULSIN?

Here's how .. with an example from the Microchip CCP & ECCP Tips & Tricks modified to
work with PBP.


' Measuring signal pulse widths with capture module

' Procedure for high-going pulse:
' 1. Configure CCP to capture on rising edge
' 2. Setup Timer1 so it will not overflow during max pulse width time
' 3. Enable ccp capture
' 4. Once capture flag bit is set, save captured value as T1
' 5. Reconfigure CCP to capture on falling edge
' 6. On 2nd capture, save 2nd value as PW
' 7. Subtract T1 from PW for the pulse width value

DEFINE OSC 4

Symbol Capture = PIR1.2 ' CCP1 capture flag
T1 VAR WORD ' 1st capture value
PW VAR WORD ' 2nd capture value & ultimately final pulse width

TRISC.2 = 1 ' CCP1 input pin (Capture input on 18F242)
INTCON = 0 ' Interrupts off

ReLoad:
CCP1CON = %00000101 ' Capture mode, capture on rising edge
T1CON = 0 ' TMR1 prescale=1, clock=Fosc/4, TMR1=off
TMR1H = 0 ' Clear high byte of TMR1 counter
TMR1L = 0 ' Clear low byte
T1CON.0 = 1 ' Turn TMR1 on here

Capture = 0 ' Clear capture int flag bit
While !Capture ' Wait here until capture on rising edge
Wend

' Rising edge detected / stuff 'captured' Timer1 value in T1
T1.HighByte = CCPR1H
T1.LowByte = CCPR1L

CCP1CON.0 = 0 ' Configure capture for falling edge now
Capture = 0 ' Clear capture interrupt flag bit

While !Capture ' Wait here until capture on falling edge
Wend

' Falling edge detected / stuff 'captured' Timer1 value in PW
PW.HighByte = CCPR1H
PW.LowByte = CCPR1L

PW = PW-T1 ' High pulse width = PW-T1

HSEROUT [DEC PW,"uS High",13,10] ' Output to RS232 display
GOTO ReLoad

END

If you need to measure the width of a low-going pulse , just change it to capture
the falling edge first with CCP1CON = %00000100, and rising edge next with
CCP1CON.0 = 1.

Real simple ... real effective ... and spot-on timing. Even works on tiny little PICs
like the 12F683 12F615, etc...;o)

mackrackit
- 27th January 2010, 02:24
Bruce,

Does posting all the examples of late mean that you are getting closer with your book?

Thanks for all of the examples!!!

Bruce
- 27th January 2010, 11:43
Dave,

No. It's just a few things people have asked me for. I prefer to post if here for everyone
rather than sending it to just a few by email. Still working on that book too. It's just slow
with the economy in the tank, and working 12-16 hours a day to stay afloat...;o)

rmteo
- 27th January 2010, 15:35
The newer PIC's such as the PIC16F690 and the 28/40-pin PIC18FxxJ11 families have a Timer1 module with Gate Control which is ideal for doing this sort of thing.

6.6 Timer1 Gate

Timer1 gate source is software configurable to be the
T1G pin or the output of Comparator C2. This allows the
device to directly time external events using T1G or
analog events using Comparator C2. See the
CM2CON1 register (Register 8-3) for selecting the
Timer1 gate source. This feature can simplify the
software for a Delta-Sigma A/D converter and many
other applications.

Timer1 gate can be inverted using the T1GINV bit of
the T1CON register, whether it originates from the T1G
pin or Comparator C2 output. This configures Timer1 to
measure either the active-high or active-low time
between events.

Bruce
- 27th January 2010, 19:55
That'll work too ... if you have a PIC with the TMR1 gate option.


' PIC12F609 using Timer1 gate input to record pulse widths

@ __config _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_OFF & _IOSCFS_4MHZ & _CP_OFF

CMCON0 = 0 ' comparator disabled
CMCON1 = %00000010 ' TMR1 gate source is T1G pin
GPIO = 0 ' outputs all low at POR
TRISIO = %00010000 ' GPIO.4 = TMR1 gate input

PVAL VAR WORD ' holds pulse time

TMR1H = 0
TMR1L = 0
T1CON = %11000001 ' TMR1 gate active-high, Fosc/4 clock, TMR1 on

Main: ' apply a pulse < 65536uS to TMR1 gate input GPIO.4 to use
WHILE !GPIO.4 ' wait for high signal to trigger TMR1 gate pin
WEND
WHILE GPIO.4 ' wait for signal to return low (stops TMR1)
WEND
PVAL.LowByte = TMR1L ' get TMR1 counts into PVAL
PVAL.HighByte = TMR1H ' from TMR1
TMR1H = 0 ' reset TMR1
TMR1L = 0
GOTO Main

END

rmteo
- 30th January 2010, 04:03
Would it be possible for you to do a non-blocking version of Measuring signal pulse widths with capture module.

Bruce
- 30th January 2010, 15:22
Definitely. Just let CCP1 generate an interrupt, and handle flipping CCP1CON.0 + grabbing
results in T1/PW in the interrupt handler.

rmteo
- 30th January 2010, 18:07
OK, thanks.

Bruce
- 30th January 2010, 18:18
This should work;


' Measuring signal pulse widths with capture module & interrupt

' Procedure for high-going pulse:
' 1. Configure CCP to capture on rising edge
' 2. Setup Timer1 so it will not overflow during max pulse width time
' 3. Enable ccp capture
' 4. Once capture flag bit is set, save captured value as T1
' 5. Reconfigure CCP to capture on falling edge
' 6. On 2nd capture, save 2nd value as PW
' 7. Subtract T1 from PW for the pulse width value

DEFINE OSC 4 ' 4MHz for 1uS resolution TMR1 counts
DEFINE INTHAND CCP_INT ' declare high-pri interrupt handler

Symbol Capture = PIR1.2 ' CCP1 capture flag
SYMBOL CapIE = PIE1.2 ' CCP1 interrupt enable bit
SYMBOL CapPriEn = IPR1.2 ' priority enable bit for CCP1 interrupt
SYMBOL PriEnable = RCON.7 ' set to enable priority levels on interrupts

T1 VAR WORD BANKA SYSTEM ' 1st capture value
PW VAR WORD BANKA SYSTEM ' 2nd capture value & ultimately final pulse width
CF VAR BYTE BANKA SYSTEM ' indicates when last capture is ready

CLEAR ' clear RAM on POR
TRISC.2 = 1 ' CCP1 input pin (Capture input on 18F242)
INTCON = 0 ' Interrupts off for now

GOTO Init ' jump over interrupt handler

ASM
CCP_INT
BTFSS CCP1CON,0 ; capture from rising edge?
BRA Fall ; no .. goto falling edge
MOVFF CCPR1L, T1 ; get low capture byte into T1
MOVFF CCPR1H, T1+1 ; get high capture byte into T1
BRA IntExit ; outta here
Fall
MOVFF CCPR1L, PW ; get low capture byte into PW
MOVFF CCPR1H, PW+1 ; get high capture byte into PW
BSF CF,0 ; indicate last capture
IntExit
BTG CCP1CON,0 ; toggle between rising/falling edge captures
BCF PIR1,2 ; clear capture interrupt flag bit
RETFIE FAST ; return/restore W, STATUS and BSR
ENDASM

Init: ' initialize a few things first
CCP1CON = %00000101 ' Capture mode, capture on rising edge
T1CON = 0 ' TMR1 prescale=1, clock=Fosc/4, TMR1=off
TMR1H = 0 ' Clear high byte of TMR1 counter
TMR1L = 0 ' Clear low byte
PriEnable = 1 ' enable priority levels on interrupts
Capture = 0 ' clear capture flag bit
CapPriEn = 1 ' set CCP1 int to high priority
CapIE = 1 ' enable the CCP1 capture interrupt
INTCON = %11000000 ' global + peripheral ints enabled
T1CON.0 = 1 ' Turn TMR1 on here

Main:
' do other stuff here as required
IF CF.0 THEN ' figure out & print result only after last capture
PW = PW-T1 ' High pulse width = PW-T1
CF.0 = 0 ' clear flag bit
HSEROUT [DEC PW,"uS High",13,10]
ENDIF

GOTO Main

END

rmteo
- 30th January 2010, 19:19
Thanks. I converted it to SF.

Device = 18F4620
Clock = 4

Dim Capture As PIR1.2 ' CCP1 capture flag
Dim CapIE As PIE1.2 ' CCP1 interrupt enable bit
Dim CapPriEn As IPR1.2 ' priority enable bit for CCP1 interrupt
Dim PriEnable As RCON.7 ' set to enable priority levels on interrupts

Dim T1 As Word ' 1st capture value
Dim PW As Word ' 2nd capture value & ultimately final pulse width
Dim CF As Boolean ' indicates when last capture is ready
Dim CCP1 As Word Absolute $fbe ' Location of CCPR1H:CCPR1L

Interrupt IntHigh()
if CCP1CON.0=1 then ' Capture from rising edge?
t1 = ccp1 ' Move word to T1
else
pw = ccp1 ' Move word to PW
cf = true ' Indicate last capture
end if
CCP1CON.0 = not CCP1CON.0 ' toggle between rising/falling edge captures
capture = 0 ' Clear capture interrupt flag bit
End Interrupt

Sub Init()
CCP1CON = %00000101 ' Capture mode, capture on rising edge
T1CON = 0 ' TMR1 prescale=1, clock=Fosc/4, TMR1=off
TMR1H = 0 ' Clear high byte of TMR1 counter
TMR1L = 0 ' Clear low byte
PriEnable = 1 ' enable priority levels on interrupts
Capture = 0 ' clear capture flag bit
CapPriEn = 1 ' set CCP1 int to high priority
CapIE = 1 ' enable the CCP1 capture interrupt
INTCON = %11000000 ' global + peripheral ints enabled
T1CON.0 = 1 ' Turn TMR1 on here
End Sub

' Main Program Entry
Init ' Initialization
TRISC.2 = 1 ' CCP1 input pin (Capture input on 18F4620)
Enable(IntHigh) ' Enable Interrupts

While true
' do other stuff here as required
If CF Then ' figure out & print result only after last capture
PW = PW-T1 ' High pulse width = PW-T1
CF = false ' Clear flag bit
' Send Out Data
EndIf
Wend

picone
- 30th August 2011, 00:13
That'll work too ... if you have a PIC with the TMR1 gate option.


' PIC12F609 using Timer1 gate input to record pulse widths

@ __config _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_OFF & _IOSCFS_4MHZ & _CP_OFF

CMCON0 = 0 ' comparator disabled
CMCON1 = %00000010 ' TMR1 gate source is T1G pin
GPIO = 0 ' outputs all low at POR
TRISIO = %00010000 ' GPIO.4 = TMR1 gate input

PVAL VAR WORD ' holds pulse time

TMR1H = 0
TMR1L = 0
T1CON = %11000001 ' TMR1 gate active-high, Fosc/4 clock, TMR1 on

Main: ' apply a pulse < 65536uS to TMR1 gate input GPIO.4 to use
WHILE !GPIO.4 ' wait for high signal to trigger TMR1 gate pin
WEND
WHILE GPIO.4 ' wait for signal to return low (stops TMR1)
WEND
PVAL.LowByte = TMR1L ' get TMR1 counts into PVAL
PVAL.HighByte = TMR1H ' from TMR1
TMR1H = 0 ' reset TMR1
TMR1L = 0
GOTO Main

END

For all the NOOBS (like me) don't forget the
ANSEL=0 to make your T1G pin a digital input. I spent a couple hours scratching my head trying to figure out why I couldn't get this code to run on my 12f683......that was all I needed.

Thank you Bruce for the code snippets...can't wait for the book. I actually got started with micros about 10 years ago with the help of your website. I'm only coming back to working with them in the last few months, and your code is helping me again!

Bruce
- 30th August 2011, 16:13
Hi Picone,

Glad I could help, and welcome back.

FYI: Darrel has a nifty include file you can use to disable all analog features by just including it in your code. See this thread http://www.picbasic.co.uk/forum/showthread.php?t=11100

It's pretty handy when you forget to disable A/D it takes care of it for you.

isaac
- 23rd March 2012, 16:44
Hi Bruce
i am trying to use your code on 18f452 but it seems to stop at start i must be missing something

DEFINE LOADER_USED 1 ' uses a bootloader
Include "Modedefs.Bas"

Define OSC 20
clear

' Setup Hardware for uart
DEFINE HSER_BAUD 9600
DEFINE HSER_RCSTA 90h
DEFINE HSER_TXSTA 24h
DEFINE HSER_CLROERR 1
Symbol Capture = PIR1.2 ' CCP1 capture flag
T1 VAR WORD ' 1st capture value
PW VAR WORD ' 2nd capture value & ultimately final pulse width
TRISC.2 = 1 ' CCP1 input pin (Capture input)
INTCON.7 = 0 ' Interrupts off

hserout [" Start Measure ",10,13 ]
ReLoad:
CCP1CON = 000101 ' Capture mode, capture on rising edge
T1CON = 0 ' TMR1 prescale=1, clock=Fosc/4, TMR1=off (200nS per count @20MHz)
TMR1H = 0 ' Clear high byte of TMR1 counter
TMR1L = 0 ' Clear low byte
T1CON.0 = 1 ' Turn TMR1 on here
Capture = 0 ' Clear capture int flag bit
While !Capture ' Wait here until capture on rising edge
Wend

' Rising edge detected / stuff Timer1 value in T1
T1.HighByte = CCPR1H
T1.LowByte = CCPR1L

CCP1CON.0 = 0 ' Configure capture for falling edge now
Capture = 0 ' Clear capture interrupt flag bit

While !Capture ' While here until capture on falling edge
Wend

' Falling edge detected / stuff Timer1 value in PW
PW.HighByte = CCPR1H
PW.LowByte = CCPR1L

PW = PW-T1 ' High pulse width = PW-T1
' Convert to uS for 20MHz osc with 200nS Timer1 ticks
PW = (PW * 2)/10
hserout ["uS High = ",dec PW,10,13 ]
'DEBUG dec PW,"uS High",13,10 ' Output to RS232 display
GOTO ReLoad

END

Bruce
- 24th March 2012, 15:31
Not sure what you mean by it seems to stop at start but timer1 is running a lot faster at 20MHz, and you don't mention how far it gets or what it shows as the result, so it's tough to help.

Have you tried at 4MHz?

isaac
- 25th March 2012, 05:52
Sorry Bruce
But got it going now
i meant it always displayed: start Measure on pc and no measurements but its ok now
thanks for you support

Isaac