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.

Can CCP do this on 16F876A? :(

Status
Not open for further replies.
bjox1 said:
Sorry Nigel, I am finding very hard to analyse ur solution :(. Please enlighten me.



I may have explained it wrongly. That’s the whole task:
1) User will choose any frequency from 500 Hz to 10kHz.
2) Then same frequency signal will be tap down to TWO signals. One will be the reference and the other will go through this unknown delay circuit.
3) My aim will be to find the value of the phase difference hence determining the unknown circuit [phase value].

I understood you before, but as you will be using different frequencies the absolute time of the high pulse doesn't provide you enough information, you need to know the time of the low pulse as well, so you can calculate the lag in degrees. Knowing the frequency would do the same thing, and by measuring both high and low, you are in effect measuring the frequency.
 
Guys I need ur valuable guidance in this case. I was wondering if it is at all possible to compare the XOR output to the constant PIC clock??

What I am trying to do is input the XOR waveform into PIC and run 4MHz clock with it. Thus a 4MHz clock will be constant irrespective of the input waveform. If I compare both waveforms and get the time difference [pulse width], I might achieve the task.

For instance, if the two signals are of 10kHz freq i.e. 277.7 nsec will represent 1 deg of difference. That is, the XOR pulse width representing 1 deg [ON time of pulse], which in freq is 3.6 MHz. Now if I compare it to the constant running 4MHz clock, I can get the time difference which is the phase.

For the other case if the signals are now at 1kHz freq, i.e. 2.77 us will represent the smallest 1 deg difference. So the XOR pulse width representing 1 deg in freq will be 360 kHz. Again comparing it to the same constant 4MHz PIC clock can give us the difference.

I honestly have no idea and I may be wrong in assuming all this. The advantage will be that the comparison will always be with a 4MHz crystal, independent of the signal frequency.

I will greatly appreciate if you could help me implementing this solution [if you think it's at all possible to achieve].

I will appreciate any suggestions on this.
 
Sounds like a job for a PLL (Phase Locked Loop) using the reference signal the PLL will output a signal proportional to the phase error. PLL are used in things like instrument tuners, indicating whether a played note is higher or lower than the reference.
 
Nigel Goodwin said:
Check the spec on the CCP module, you certainly can't get 277nS resolution with a 4MHz clock.

Nigel,
Mate if I use a 20MHz XT?? Is it possible to do it in this way?? if so then how can I implement in a code?

I will really really appreciate that mate.

Thanks again for ur time.

PS:
Nigel Goodwin said:
If you're using different frquencies you need to measure both the HIGH period and the LOW period, the phase difference is the ratio between them.

Nigel,

Could you explain this once again ?or probably how I can write it in code.

Cheers :)


Sounds like a job for a PLL (Phase Locked Loop) using the reference signal the PLL will output a signal proportional to the phase error. PLL are used in things like instrument tuners, indicating whether a played note is higher or lower than the reference.

Hi,
PLL outputs a current signal. I am not sure how to input it to my PIC for finding a phase.
 
Last edited:
bjox1 said:
Nigel,
Mate if I use a 20MHz XT?? Is it possible to do it in this way?? if so then how can I implement in a code?

Running the PIC at 20MHz, the CCP module should have 200n resolution, use that to measure the width of the pulse from an XOR gate.
 
The 16F876 has two CCP modules. Why not use one module to Capture the leading edge of one of the signals and the other module to Capture the leading edge of the other. Then you don't need any external XOR function; you can compute the frequency and the phase directly with the 200 ns resolution.

Mike
 
mike50 said:
The 16F876 has two CCP modules. Why not use one module to Capture the leading edge of one of the signals and the other module to Capture the leading edge of the other. Then you don't need any external XOR function; you can compute the frequency and the phase directly with the 200 ns resolution.

Mike

Mike,

Mate it sounds perfect but I haven't got that much experience in writing this sort of code. I have done a few LED blinking codes and can attempt this CCP if I get a little bit of help.

you can compute the frequency and the phase directly with the 200 ns resolution.
Mike, Sorry for being so slow but could you check my following steps:

1) Configure CCP [capture] for the reference signal.
2)Configure CCP for the delay signal.

But what do I have to write in my code to get the phase value which will be independent of the signal frequency??

I would appreciate any help on this.

Thanks in advance.
 
Last edited:
Anyone up for the kind help?? :eek:


Ps:

Heyy guys,

I found this nice document[attached with this post] for measuring the width of a signal [in my case, XOR output]. I will be grateful if you could help me in writing the steps described in the document.


Many many thanks guys.

<edit>
Please please help me out on this...:(
 

Attachments

  • CCP.jpg
    CCP.jpg
    44.9 KB · Views: 219
Last edited:
bjox1 said:
Mike,

Mate it sounds perfect but I haven't got that much experience in writing this sort of code. I have done a few LED blinking codes and can attempt this CCP if I get a little bit of help.


Mike, Sorry for being so slow but could you check my following steps:

1) Configure CCP [capture] for the reference signal.
2)Configure CCP for the delay signal.

But what do I have to write in my code to get the phase value which will be independent of the signal frequency??

I would appreciate any help on this.

Thanks in advance.

  1. Run the PIC at 20 Mhz.
  2. You need to configure Timer1 to count instruction cycles (200 ns).
  3. You need to setup CCP1 to capture the leading edge of signal 1.
  4. You need to setup CCP2 to capture the leading edge of signal 2.
  5. Each time a capture occurs save the captured time from CCP1 and/or CCP2 (whichever caused the interrupt)
  6. Each time you get an interrupt for a capture of CCP1, subtract the previous value that you captured from CCP1. This will give you the time between leading edges of the signal (in units of 200 ns)
  7. 5,000,000 divided by this time will give the frequency in Hz.
  8. You could do the same with the times from CCP2 to verify that the frequencies are the same.
  9. Subtracting the most recent CCP2 time from the most recent CCP1 time will give you the time between the edges of the two signals. Take this time interval and multiply it by 360 and then divide by the period (the time you measured in step 5 above) and you will have the phase angle in degrees.

This technique will work for signals down to about 77 Hz. Below that, Timer1 will overflow between pulses and you will get incorrect values. You don't ever have to stop Timer1, just let it run free. It is okay for Timer1 to overflow as long as it doesn't overflow more than once between captures on either timer....and that won't happen for frequencies above 77 Hz.

Mike
 
mike50 said:
  1. Run the PIC at 20 Mhz.
  2. You need to configure Timer1 to count instruction cycles (200 ns).
  3. You need to setup CCP1 to capture the leading edge of signal 1.
  4. You need to setup CCP2 to capture the leading edge of signal 2.
  5. Each time a capture occurs save the captured time from CCP1 and/or CCP2 (whichever caused the interrupt)
  6. Each time you get an interrupt for a capture of CCP1, subtract the previous value that you captured from CCP1. This will give you the time between leading edges of the signal (in units of 200 ns)
  7. 5,000,000 divided by this time will give the frequency in Hz.
  8. You could do the same with the times from CCP2 to verify that the frequencies are the same.
  9. Subtracting the most recent CCP2 time from the most recent CCP1 time will give you the time between the edges of the two signals. Take this time interval and multiply it by 360 and then divide by the period (the time you measured in step 5 above) and you will have the phase angle in degrees.

This technique will work for signals down to about 77 Hz. Below that, Timer1 will overflow between pulses and you will get incorrect values. You don't ever have to stop Timer1, just let it run free. It is okay for Timer1 to overflow as long as it doesn't overflow more than once between captures on either timer....and that won't happen for frequencies above 77 Hz.

Mike

Mike,

I have no words to thank you for being so down-to-earth and for being so helpful.

Just before I try this, is there any example code anywhere similar to this?? :(
I mean it would really help me in learning [I really don’t know how to use an interrupt to capture the time].

As u have suggested that there will be two capture times for signal1 i.e. CCP1a for the first rising edge and CCP1b for the second rising edge. And then CCP2 will be used to get the time for the first rising edge on signal2.

My question is, do I need to use two different Timers for this??

i.e. -Timer1 [T1] will be clear at the first rising edge of signal1 and it will count till the next rising edge arrives. This gives the value T1 [or period of the signal1].

- Now as soon as we move to Pin2 for CCP2, the second counter [T2] will start counting till the next rising edge on signal2.

Lastly - T2 / T1 will be my phase value in time.

Please please suggest if I am on the right track??:confused:

Cheers Mike.
 
bjox1 said:
Mike,

I have no words to thank you for being so down-to-earth and for being so helpful.

Just before I try this, is there any example code anywhere similar to this?? :(
I mean it would really help me in learning [I really don’t know how to use an interrupt to capture the time].
You wouldn't have to use an interrupt. Your main application could just stay in a loop watching the CCP1IF bit in PIR1 and the CCP2IF bit in PIR2. When CCP1IF changes to 1, then read out the CCPR1H and CCPR1L to get the captured value. Then turn off the CCP1IF bit in PIR1. Likewise, when CCP2IF changes to 1, then read out the CCPR1H and CCPR1L to get that captured value. Don't forget to turn off the CCP2IF bit in PIR2.
bjox1 said:
As u have suggested that there will be two capture times for signal1 i.e. CCP1a for the first rising edge and CCP1b for the second rising edge. And then CCP2 will be used to get the time for the first rising edge on signal2.

My question is, do I need to use two different Timers for this??
Well, you can't use two timers. Both CCP1 and CCP2 capture modes use Timer1. But that works fine for you.
bjox1 said:
i.e. -Timer1 [T1] will be clear at the first rising edge of signal1 and it will count till the next rising edge arrives. This gives the value T1 [or period of the signal1].
Timer1 just runs all the time. Whatever value it has when you capture the rising edge of signal1 is the start time. Later when you capture signal1's rising edge again you will get some other value. The difference between these is the time for one cycle. It doesn't matter where Timer1 starts nor whether it overflows (as long as the total time is less than 65,536 * 200ns - which is why 77 Hz is the lowest frequency you could capture).
bjox1 said:
- Now as soon as we move to Pin2 for CCP2, the second counter [T2] will start counting till the next rising edge on signal2.
You don't "move" to CCP2, both CCP modules are capturing all the time. At any time, the most recent CCP1 and CCP2 captured values will represent the time between the leading edges of the two signals.
bjox1 said:
Lastly - T2 / T1 will be my phase value in time.

Please please suggest if I am on the right track??:confused:

Cheers Mike.
Imagine that you keep the last two times captured from CCP1 in SIG1x and SIG1y (with SIG1y being the most recent) and that you keep the last time captured from CCP2 in SIG2. Then right after capturing CCP2 in SIG2 you can compute

frequency = 5,000,000 / (SIG1y - SIG1x)
phase = 360 * (SIG2 - SIG1y) / (SIG1y - SIG1x)

Of course, to work with numbers this big you will need to use 32-bit variables.


Mike
 
Mike,

Mate you don't realise how you have helped. Thanks again pal.

After reading the datasheet [Timer1 section] I am a little bit confused on the following things:

-I found out that the max Timer1 value is FFFFh. But I don't know if I need to set the Timer1 as a timer or as a counter?? :confused:

- Also, do I need an additional external XT for this??
- what will be the prescaler value to run it free.
- Do I need an interrupt service routine in my code??

Could you please check my settings below:
For PIC-16F876A
T1CON
b'00000001'

Am I correct? Please enlighten me Mike.

Thanks again Mike.
 
Last edited:
You need to set Timer1 as a timer. The T1CON value you came up with is correct. This will cause Timer1 to increment on every instruction cycle. If you are running the PIC at 20 MHz, then Timer1 will increment every 200 ns.

You don't need an external crystal specifically for Timer1 because you are running it in Internal Clock mode. You do need a 20 MHz crystal to connect to the OSC1 and OSC2 pins to clock the chip. (and don't forget that you need appropriate capacitors too) - see Figure 14-1 and Table 14-2 in section 14.2 of the PIC16F87XA datasheet.

The prescaler is set to 1:1 by the T1CON register value you selected. That is what you want, as you want Timer1 to increment on every instruction clock.

As I said in my previous note, you don't need an interrupt service routine. You can just "poll" the CCP1IF and CCP2IF bits in a mainline loop. When you see either of those bits come on, you copy out the capture value from the corresponding registers. Whether you use interrupts or not, you need to turn the CCP1IF and CCP2IF bits off to wait for the next capture.

The arithmetic to compute the difference between two capture values needs to be done modulo 2^16. Which means you want to do that using 16-bit unsigned integers. That is required to make sure you get the right results if a Timer1 overflow occurs between the two captures.

Imagine that you capture the value F120h from CCP1 and then later capture 0240h from CCP1. Notice that a Timer1 overflow occurred between those two captures. You will compute the time between these two events as:

time = 0240h - F120h

doing that in 16-bit variables will give you 1120h which is in fact the number of 200ns clock ticks between Timer1 values of F120h and 0240h. If you did this arithmetic in 32-bit variables you would get the result FFFF1120h which wouldn't be what you want. As long as no more than one Timer1 overflow occurs between captures you will get the right answer.

Of course when you do further calculations such as:

frequency = 5000000 / time

you want to use 32-bit variables because 5000000 doesn't fit in a 16-bit variable. It is just when you are subtracting two captured time values that 16-bit arithmetic is required. Or alternatively you could just do:

time = ( SIG1y - SIG1x ) & FFFFh

this works with 32-bit variables too. BTW, you haven't said what language you will be writing the program in. It is easier to talk about this knowing the language you will be using.

Mike
 
Last edited:
Hey Mike,

I have no words to thank you again. You are just like an angel sent. :)

Mate, I am using ASM language. I spent last night figuring out how to do a 16 bit subtraction in assembly. I found a few help links on the internet to do that.I have done that successfully. :D

I am all puzzled about the division now.:confused:

Mike I will be grateful if you could double check the following steps:

- Setup CCP1CON for the rising edge. [Set input pins as well]
- Setup T1CON for free running.
- Check for CCP1IF flag to set.
- Save the capture time T1H and T1L in Memory reg.[This is CCP1a]
- NOW setup CCP1CON for the falling edge.
- Check again for CCP1IF flag to set.
- Save the capture values again. [This is CCP1b]

- Do 16 bit subtraction and save the result in 16 bit memory reg.

Mike, I am all stuck after this :confused: . Please advise on this:

- Now at this time Timer1 will still be running so setup CCP2CON for the rising edge on CCP2 pin.
- Save CCP2 capture value.
- Thus do CCP2 - CCP1b.

Am I on the right track mike??

I really appreciate all ur suggestions.

Awaiting ur reply.

Thanks again.

<edit> Please please help me on this!! :(
 
Last edited:
bjox1 said:
Hey Mike,

I have no words to thank you again. You are just like an angel sent. :)

Mate, I am using ASM language. I spent last night figuring out how to do a 16 bit subtraction in assembly. I found a few help links on the internet to do that.I have done that successfully. :D

I am all puzzled about the division now.:confused:
You might take a look at which does a 16-bit by 16-bit multiply with a 32-bit result.
bjox1 said:
Mike I will be grateful if you could double check the following steps:

- Setup CCP1CON for the rising edge. [Set input pins as well]
- Setup T1CON for free running.
- Check for CCP1IF flag to set.
- Save the capture time T1H and T1L in Memory reg.[This is CCP1a]
- NOW setup CCP1CON for the falling edge.
- Check again for CCP1IF flag to set.
- Save the capture values again. [This is CCP1b]

- Do 16 bit subtraction and save the result in 16 bit memory reg.
You do not want to change CCP1CON to look for the falling edge. Once you set it up to look for leading edges, leave it alone. The time you want is the time between two successive leading edges.

You want something like this:
Code:
- Configure the pins - disable analog inputs and set TRIS regs 
- Setup T1CON for free running.
- Setup CCP1CON for the rising edge. 
- Setup CCP2CON for the rising edge. 
- Set CCP1IH to 0
- Set CCP2IH to 0

- outer loop

   - Inner loop 1
   - If CCP1IH = 0 then goto Inner loop 1

   - Copy CCPR1H to CCP1aH
   - Copy CCPR1L to CCP1aL
   - Set CCP1IH = 0

   - Inner loop 2
   - If CCP1IH = 0 then goto Inner loop 2

   - Set CCP2IH = 0  ; make sure to capture CCP2 after second CCP1 capture
   - Copy CCPR1H to CCP1bH
   - Copy CCPR1L to CCP1bL
   - Set CCP1IH = 0

   - Inner loop 3
   - If CCP2IH = 0 then goto Inner loop 3

   - Copy CCPR2H to CCP2aH
   - Copy CCPR2L to CCP2aL
   - Set CCP2IH = 0

   - time = CCP1b - CCP1a  (use 16-bit subtract)
   - frequency = 5000000 / time  (use 32 by 16 divide - since 5000000 won't fit in 16 bits)
   - phase = 360 * ( CCP2a - CCP1b ) / time  (use 16 bit subtract, then 16-bit by 16-bit multiply, then 32-bit by 16-bit divide)
   - output frequency and phase as necessary
   - goto outer loop
bjox1 said:
Mike, I am all stuck after this :confused: . Please advise on this:

- Now at this time Timer1 will still be running so setup CCP2CON for the rising edge on CCP2 pin.
- Save CCP2 capture value.
- Thus do CCP2 - CCP1b.

Am I on the right track mike??
As I showed above, do all the setup at the start.
bjox1 said:
I really appreciate all ur suggestions.

Awaiting ur reply.

Thanks again.

<edit> Please please help me on this!! :(
 
Mike,

I am really indebted to you for all ur help and guidance!

I am just going to try the code with all ur suggestions.:D

Mike, Just to get some handy experience with the capture module, I have written my code which measures the width of a TTL signal. It does the following things:

{My setup-: PIC 16F877A [I am using this PIC], with 4MHz XT and the CCP1 input is RC2 pin}

-Set CCP1 pin.
-Setup Timer1.
-Setup CCP1CON for the rising edge.
-As soon as CCP1IF flag is set, Save the value to H2 and L2 reg [these values are smaller than the next capture values]
-clear CCP1IF and setup CCP1CON for falling edge.
- do the same, get H1 and L1 values.
- Subtract L2 from L1, Propagate borrow and sub H2 from H1.
-Light LEDs [8 on each port] on PORTB and PORTD.

But somehow it doesn't show me anything on the output. I have already checked the SUBTRACT routine and it works fine.

Please help me on this code.

Thanks again Mike. I really appreciate it.

Code:
list p=16f877a

       include "p16f877a.inc"
		

	__config _LVP_OFF & _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF & _BODEN_OFF & _DEBUG_OFF

	cblock	0x020			;start of general purpose registers
		H1
		H2
		L1
		L2
		AnsH
		AnsL	
		endc
; Start at the reset vector
        org     0x000
        goto    Start

Start	org		0x010
		clrf	PORTB
		clrf	PORTD
		clrf	PORTC
		clrf	H1
		clrf	H2
		clrf	L1
		clrf	L2
		clrf	AnsH
		clrf	AnsL
		clrf	TMR1H
		clrf	TMR1L
	;	clrf 	CCP1CON 
		clrf	CCPR1H
		clrf	CCPR1L
       		bsf     STATUS,RP0      ;bank 1
        	bcf     STATUS,RP1
		movlw	b'00000100'
		movwf	TRISC
     		movlw   b'00000000'
     		movwf   TRISB           ;portb [7-0] outputs
		movlw   b'00000000'
     		movwf   TRISD			;portd output
		bcf		STATUS,RP0		;bank0
	;	bcf		PIR1,CCP1IF
;**********************************************

Main:		movlw 	b'00000001' 	 ;timer 1 using to capture, prescaler 1:1 
		movwf 	T1CON 
		;bsf	T1CON,TMR1ON	

		movlw 	b'00000101'
		movwf 	CCP1CON    		 ;start with rising CAPTURE 
Wait:
		btfss 	PIR1,CCP1IF 
		goto 	Wait
		movf 	CCPR1H,H2 		;save the value in H2 and L2 {lower value} 
		movf 	CCPR1L,L2
		bcf	PIR1,CCP1IF
;
		movlw 	b'00000100'           ;config CCP1 to faling 
		movwf 	CCP1CON    
Wait1:
		btfss 	PIR1,CCP1IF 
		goto 	Wait1

		movf    CCPR1H,H1 		;save now the value in H1,L1
		movf    CCPR1L,L1  
		bcf 	PIR1,CCP1IF     ;clr flag CCP1                     

;SUB lower value H2,L2 from current value H1,L1

SUB:
		movf	L2,W
		subwf	L1,W
		movwf	AnsL
		movwf	PORTB

		btfss  STATUS, C
		goto   BORROW
		goto   SUB_1
BORROW:
		decf   H1, F
	
SUB_1:	movf	H2,W
		subwf	H1,W
		movwf	AnsH
		movwf	PORTD
	;	goto	Main
	
;************************************************
		
		end
 
Last edited:
You cannot do move file to file. MPASM is really lax in this area and should give an error but unfortunalely doesn't.

These instructions won't work,
Code:
		movf    CCPR1H,H1 		;save now the value in H1,L1
		movf    CCPR1L,L1
You have to do,
Code:
		movf    CCPR1H,W 		;save now the value in H1,L1
		movwf	H1
		movf    CCPR1L,W  
		movwf	H1

Mike.
 
You really need to pay attention to the errors and warnings you get when you assemble (build) the program. The first three you get are okay...they are just warning you that you are accessing memory outside of bank 0...but you have done the banking correctly.

The next four errors you got "Argument out of range" are real errors in your program. These really should have been "errors" not "warnings".

You have misinterpreted what the MOVF instruction does. At the first of these errors you coded:

Code:
movf 	CCPR1H,H2 		;save the value in H2 and L2 {lower value}

thinking that this will move data from CCPR1H to H2. But these PICs are not that clever. They can only move data to and from the W register. So you need two instructions at each of these four places. At this first place you would code:

Code:
movf    CCPR1H,w                ; copy the value into the W reg
movwf   H2                      ; and then into H2
Alternatively you could code:
Code:
movfw   CCPR1H                  ; copy the value into the W reg
movwf   H2                      ; and then into H2
You need similar code at the other three similar warnings.

You should always reset CCP1IH after setting CCP1CON. See section 8.1.3 of the datasheet.

What do you suppose the chip will do when your program reaches your last instruction? That "end" directive isn't actually an instruction, it just tells the assembler that there is no more code. The CPU will go on executing instructions - whatever it finds there. Most likely it will end up running your whole program over again - but it is hard to say. If you just want it to end there, then you should code something like:

Code:
loop    goto loop
It is really useful to learn to use the MPLAB SIM debugger. That lets you step through your code and even simulate inputs on the pins. You could set up a simulation of your whole frequency/phase prloblem and step through the code to see that it is working.

Mike
 
Status
Not open for further replies.

Latest threads

Back
Top