|
|
View previous topic :: View next topic |
Author |
Message |
trojanman
Joined: 09 Apr 2006 Posts: 7
|
problem with comparator |
Posted: Thu Apr 27, 2006 6:41 pm |
|
|
Hi
Im using a 16f873a's comparator to turn a 40khz sinusoidal signal with a 50 % duty cycle into a 40khz square wave signal. The problem is that when i check the comparator's output with an oscilloscope I get a 20kHz square wave signal with a 33% duty cycle. Here is the code I'm using to test the comparator
Code: | #include <16F873A.h>
#fuses HS,NOWDT,NOPROTECT,PUT,BROWNOUT,NOLVP
#use delay(clock=20000000)
#use rs232(baud=2400, xmit=PIN_C6, rcv=PIN_C7)
#BYTE CMCON = 0x9C
#int_COMP
COMP_isr() {
byte b;
b = CMCON;
if(bit_test (b, 6))
{
output_high(PIN_B2);
}
if(!bit_test (b, 6))
{
output_low(PIN_B2);
}
}
void main() {
setup_comparator(A0_VR_A1_VR);
setup_vref(VREF_HIGH|1);
enable_interrupts(INT_COMP);
enable_interrupts(GLOBAL);
while(TRUE){
}
} |
Any suggestions or ideas?
Thanks |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Apr 27, 2006 8:48 pm |
|
|
The problem is that it takes about 70 instruction cycles to handle the
interrupt. At 20 MHz, this is 14 us. Your input sinewave is 40 KHz,
which has a period of 25 us. Each half-cycle is 12.5 us. So you're
not able to handle an interrupt fast enough. You can't get out of the
first interrupt before the next transition has already passed by.
Besides looking at the .LST file, one way to test this is to poll the
comparator interrupt flag in main(), and then execute your isr, without
the PIC having to go through the CCS interrupt dispatcher. This will
save about 50 instructions, which is 10 us at 20 MHz. It should work
now. See the code below.
Also, your Vref level is only 1.40v for the comparator. Is that correct ?
(Assuming the PIC is running at 5v).
Code: |
#include <16F873A.h>
#fuses HS,NOWDT,NOPROTECT,PUT,BROWNOUT,NOLVP
#use delay(clock=20000000)
#use rs232(baud=2400, xmit=PIN_C6, rcv=PIN_C7)
#BYTE CMCON = 0x9C
#byte PIR2 = 0x0D
#bit CMIF = PIR2.6
// #int_COMP // Commented out this line.
COMP_isr() {
byte b;
b = CMCON;
if(bit_test (b, 6))
{
output_high(PIN_B2);
}
if(!bit_test (b, 6))
{
output_low(PIN_B2);
}
}
void main() {
char c;
setup_comparator(A0_VR_A1_VR);
setup_vref(VREF_HIGH|1);
// enable_interrupts(INT_COMP); // Commented out
// enable_interrupts(GLOBAL); // Commented out
c = CMCON; // Clear "change" condition
CMIF = 0; // Clear Comp. int. flag
while(1)
{
while(!CMIF); // Wait for Comp. interrupt
COMP_isr(); // Handle it
CMIF = 0; // Clear Comp. interrupt flag
}
} |
|
|
|
treitmey
Joined: 23 Jan 2004 Posts: 1094 Location: Appleton,WI USA
|
|
Posted: Fri Apr 28, 2006 8:00 am |
|
|
If your input freq. is the same as what your producing, (40kHz) I would try an opamp and a cliper circuit. That would make the sin wave look like a square wave. |
|
|
trojanman
Joined: 09 Apr 2006 Posts: 7
|
|
Posted: Fri Apr 28, 2006 5:53 pm |
|
|
Thanks for your answers.
The reason why I'm trying to turn the sine wave into a square wave is because I want to make the PIC count the number of 40 kHz pulses it received, so I tried using the comparator to achieve this, but i think that that is no the best aproach. Is it possible to do this with out adding any hardware? |
|
|
cmdrdan
Joined: 08 Apr 2005 Posts: 25 Location: Washington
|
|
Posted: Fri Apr 28, 2006 6:48 pm |
|
|
With the code described by PCM, you don't need external hardware. All he said was that by using interrupts, you can't count fast enough for a 40 kHz signal. By polling, as he suggested, you may be able to do that -- just add code to increment a variable in your former comparator ISR routine, and you're all set. The only drawback to this is that it ties up your PIC for the time you're counting pulses, and perhaps that is okay; we don't know your requirements.
Another possibility is to use an external comparator as Treitmey suggested to square-up your sine wave. Perhaps then you could feed it into the timer0 input, with timer0 setup as a counter. I've never worked with the A-series parts, but with a cursory glance of the data sheet it doesn't seem capable to route the internal comparator's output to the timer0 input. Using timer0 as a counter should free up some processor time on your PIC, but at the cost of an external comparator....
Dan |
|
|
Leef_me
Joined: 14 Mar 2006 Posts: 45
|
|
Posted: Fri Apr 28, 2006 6:56 pm |
|
|
>>I'm trying to turn the sine wave into a square wave
>>I want to make the PIC count the number of 40 kHz pulses it received
>>Is it possible to do this with out adding any hardware?
trojanman,
Does a trace, or piece of wire count as 'hardware' ?
But seriously, how about the following idea?
If you can use Timer1, why not enable the comparator output to an external pin, wire that pin to the Timer1 external clock input, and use Timer1 as a synchronous counter. There is a capture register that can
hold the Timer1 value. Timer1 has on overflow interrupt available.
Some useful sections of the datasheet are:
6.3 Timer1 Operation in synchronized counter mode
8.1 Capture mode (and timer mode selection) this also talks about a
prescaler that allows for an interrupt based on 1, 4 or 16 rising edges,
so you could reduce the number of times you go through the ISR
The capture register value could be read when it is convenient to get an
exact count.
12.5 Comparator outputs
May I ask, what is the source of you 40khz?
Leef_me |
|
|
trojanman
Joined: 09 Apr 2006 Posts: 7
|
|
Posted: Fri Apr 28, 2006 9:20 pm |
|
|
Leef_me wrote: | >>I'm trying to turn the sine wave into a square wave
>>I want to make the PIC count the number of 40 kHz pulses it received
>>Is it possible to do this with out adding any hardware?
trojanman,
Does a trace, or piece of wire count as 'hardware' ?
But seriously, how about the following idea?
If you can use Timer1, why not enable the comparator output to an external pin, wire that pin to the Timer1 external clock input, and use Timer1 as a synchronous counter. There is a capture register that can
hold the Timer1 value. Timer1 has on overflow interrupt available.
Some useful sections of the datasheet are:
6.3 Timer1 Operation in synchronized counter mode
8.1 Capture mode (and timer mode selection) this also talks about a
prescaler that allows for an interrupt based on 1, 4 or 16 rising edges,
so you could reduce the number of times you go through the ISR
The capture register value could be read when it is convenient to get an
exact count.
12.5 Comparator outputs
May I ask, what is the source of you 40khz?
Leef_me |
I can't enable the comparator's output to an external pin since I'm using the internal vref. What would be the easiest way to implement an external vref if the pic's operating voltage is 5v?
Going to try PCM programmer's code to count pulses and see if it doesn't tie up the pic |
|
|
Leef_me
Joined: 14 Mar 2006 Posts: 45
|
|
Posted: Sun Apr 30, 2006 9:27 pm |
|
|
>>I can't enable the comparator's output to an external pin since I'm >>using the internal vref. What would be the easiest way to implement >>an external vref if the pic's operating voltage is 5v?
Sorry,
I missed that idiosyncrasy (from the latin word for STUPID)
I have NOT tried this, I expect it to work based on the sections of the datasheet I have read.
http://ww1.microchip.com/downloads/en/DeviceDoc/39582b.pdf
13.0 Comparator voltage reference module
" the output of the refernce generator may be connectoed to the RA2/AN2/VREF-/CREF pin. This can be used as a simple D/A function by the user if a very high-impedance load is used. The primary purpose of this function is to provide a test path for testing the reference generator function. "
Also take a look at AN611
http://ww1.microchip.com/downloads/en/AppNotes/00611b.pdf
At the section "Using the Voltage Reference" on the page marked
"DS00611B-page 7" which has some details and cautions.
What is the source of your 40khz?
Can you tell I found the emoticons?
Leef_me |
|
|
trojanman
Joined: 09 Apr 2006 Posts: 7
|
|
Posted: Mon May 01, 2006 12:07 am |
|
|
I used a voltage divider to get a voltage reference of 2.1V and connected it to pin A3 with mode 5 and that worked fine when checking pin A5 with an oscilloscope, although I couldn't get anything from pin A4, probably because of this:
"RA4 is an open collector I/O pin. When
used as an output, a pull-up resistor is
required."
How do I connect the reference generator to pin RA2/AN2/VREF-/CREF, I tried using the code below and checked pin A5 but it didn't work:
Code: | #include <16F873A.h>
#fuses HS,NOWDT,NOPROTECT,PUT,BROWNOUT,NOLVP
#use delay(clock=20000000)
void main() {
setup_comparator(A0_A3_A1_A2_OUT_ON_A4_A5);
setup_vref(VREF_HIGH|10);
while(1){
}
}
|
the source of the 40 kHz is a transducer |
|
|
trojanman
Joined: 09 Apr 2006 Posts: 7
|
|
Posted: Mon May 01, 2006 12:31 am |
|
|
If I'm going to use the external output of the comparator to connect it to Timer1 external clock input, and use Timer1 as a synchronous counter, do I have to poll the comparator's interrupt flag or can I use the comparator interrupt service routine? |
|
|
SherpaDoug
Joined: 07 Sep 2003 Posts: 1640 Location: Cape Cod Mass USA
|
|
Posted: Mon May 01, 2006 7:16 am |
|
|
What is the source of the sine wave? Do you need to use a comparator at all? Microchip application note AN521 is a classic piece on how to connect nasty real world signals to a digital PIC pin with almost no hardware. _________________ The search for better is endless. Instead simply find very good and get the job done. |
|
|
trojanman
Joined: 09 Apr 2006 Posts: 7
|
|
Posted: Mon May 01, 2006 9:47 am |
|
|
Going to take a look at the application note, though I've decided to go with the external comparator.
If I'm using Timer1's external input (pin C0?), how can I make timer1 give me its contents if after a specified amount of time it has stopped counting? Does any one have an example?
Is it by resetting timer1 using a ccp trigger output? |
|
|
Leef_me
Joined: 14 Mar 2006 Posts: 45
|
|
Posted: Tue May 02, 2006 12:45 pm |
|
|
Uhh, what? Sorry, I got distracted looking at your code line:
setup_comparator(A0_A3_A1_A2_OUT_ON_A4_A5);
I have compiler version 3.180 and the project wizard thinks it's
"...OUT_ON_A3_A4" looks like I might need a compiler update.
>>how can I make timer1 give me its contents if after a specified amount of time it has stopped counting?
You can't using just Timer1 and the CCP registers.
A 'watchdog timer' idea comes to mind. One method is to use Timer0
and its overflow interrupt to signal when some time has elapsed after the last edge was detected.
Here is my attempt, this has not been compiled or tested
Code: |
// Inserted into .c file before main():
int16 t_value; // this is the count of 40khz pulses received.
int8 t_value_bad; // a flag indicating status of measurement
#int_TIMER1
TIMER1_isr()
{
// timer overflowed, count of 40khz pulses is invalid
t_value_bad=0xff;
t_value=0;
}
#int_CCP1
CCP1_isr() // 16 edges occurred, 40khz is still coming in
{
set_timer0(0); // reset the 'watchdog' timer (this timer does not reset the uC)
}
#int_TIMER0 // overflow in 819 us, which is greater than the time for 16 edges of 40khz
TIMER0_isr()
{
// time has elapsed without more 40khz pulses; report contents of Timer1
t_value=get_timer1();
t_value_bad=0x00; // good result
}
// Inserted into .c file in main():
setup_counters(RTCC_INTERNAL,RTCC_DIV_16);
setup_timer_1(T1_EXTERNAL|T1_DIV_BY_1|T1_EXTERNAL_SYNC);
setup_timer_2(T2_DISABLED,0,1);
setup_ccp1(CCP_CAPTURE_RE|CCP_CAPTURE_DIV_16); // so the interrupt is called 1/16 of the time
disable_interrupts(INT_TIMER1);
disable_interrupts(INT_CCP1);
disable_interrupts(INT_TIMER0);
enable_interrupts(global);
*
*
*
//*******************************************************//
// Get ready to start a measurement
//*******************************************************//
disable_interrupts(global);
set_timer0(0); // reset the 'watchdog' timer (this timer does not reset the uC)
set_timer1(0); // reset the 40khz pulse counter
// you need to make sure the interrupt flags associated with the interrupts below
// are cleared before starting a measurement
enable_interrupts(INT_TIMER1);
enable_interrupts(INT_CCP1);
enable_interrupts(INT_TIMER0);
t_value_bad=0x01; // measurement in progress
enable_interrupts(global);
While(t_value_bad==0x01) // wait for a result
;
//*******************************************************//
// Measurement ended, restore normal operation
//*******************************************************//
disable_interrupts(global);
disable_interrupts(INT_TIMER1);
disable_interrupts(INT_CCP1);
disable_interrupts(INT_TIMER0);
enable_interrupts(global);
If (t_value_bad==0xff)
; // bad result
else
; //good result
|
I have also used Timer0 to set up a 'clock' with an interrupt that keeps several processes running. I use it to create a pseudo-realtime clock.
I can add a variable that is decremented at a desired rate, say 1ms
and then monitor that value from the code in 'main'. When the variable
hits zero, the main loop continues to the next step -- whatever that is.
It is similar to the delay_us() function, but other things can be happening; instead of the uC being stuck in a counter loop, doing nothing.
>>Is it by resetting timer1 using a ccp trigger output?
This will not give you any value. I have found the CCP trigger output
useful in starting a A/D conversion or aiding in generating a single or repetitive square-edged pulse with varing duty cycle. In the above code, I used the edge detection part of the CCP to delay having to go through the Timer1 interrupt for each edge of the 40khz pulse, but the CCP register values are not used.
Leef_me |
|
|
trojanman
Joined: 09 Apr 2006 Posts: 7
|
|
Posted: Fri May 05, 2006 1:59 pm |
|
|
Thanks for the suggestion, going to try that code |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|