PDA

View Full Version : Software ADC Comparator?



Ryan7777
- 13th February 2008, 02:44
I don't know if comparator is the proper term, but I need some direction here!
I need to measure a voltage on a ADC input and then set an output high when the input voltage goes higher than the original reading, no matter what the original reading was (not exceeding 5 volts or full scale of the ADC of course). sort of like an autogain of some type. I'd like to be able to adjust the gain (or level of difference) of the readings with another ADC input and pot. so:

if the original reading on AN0 was 2.33 volts
and then right after that the next reading on AN0 exceeds 2.33 volts by 1 volt (3.33)
High myOutput once, for one second.

so then if the next reading is 3.33 it does nothing, because there was no positive change

but, if the next reading is 4.33 then the output goes high again because there was a positive change.

and so on

the difference being 1 volt, which i would also like to have control of with another ADC..

the voltage on my ADC input is going to vary between 1 - 4 (thereabouts!) slowly, with only sudden bursts of a higher voltage for a short pulse (a few mS?) but i think my PIC (F684, 20 MHz) will be able to capture it.

I already know how to set up ADCs, and do my Defines and all of that, I just need some workable code here. Thanks in advance for any help!

mackrackit
- 13th February 2008, 02:52
IF ADCVAR <= to x THEN
DO SOMETHING
ELSE
IF ADCVAR >= to x THEN
DO SOMETING ELSE
ELSE
ENDIF
EDNIF
????????????????????????

Is that what you are looking for ?
......................

skimask
- 13th February 2008, 02:54
Might help to know the end application...

Ryan7777
- 13th February 2008, 03:15
the first suggestion by mackrackit doesn't take into account the level of "difference"
i.e. setting the output high based on the amount of change of a varible number, not just by the fact that it changed for one.
and its classified skimask! but, heres an example:

say my input is a light dependent resistor, or solar cell, whatever, and its sitting in a dark room, doing nothing, no output. then someone turns on a dim light, the pic would detect the change in light level in the span of one loop, and toggle an output on and off one time because it saw a positve change in voltage on the ADC.
Then it readjusts for the new light level in the room and sits there, doing nothing, but someone makes the light a bit brighter, and again the output toggles because there is a positve change in light. so therefore, it only toggles the output because there is a positve change in light, it would ignore someone turning the light off, or dimming it. I also need control of how much of a positive change it responds to, like the amount that the voltage level is increased, so if its a very slight change, nothing happens, but if there is a large change, it will respond..
this is really messing with my poor ol' brain! but i seem to end up asking more questions on here than answering anyway!

Also, when i say "readjusts" i mean the circuit automatically readjusts its self, like autogain, or something!

thanks again for your help!

skimask
- 13th February 2008, 03:35
and its classified skimask! but, heres an example:
I've seen classified (or have I?)...this isn't classified (or is it?)... :D


say my input is a light dependent resistor, or solar cell, whatever, and its sitting in a dark room, doing nothing, no output. then someone turns on a dim light, the pic would detect the change in light level in the span of one loop, and toggle an output on and off one time because it saw a positve change in voltage on the ADC. Then it readjusts for the new light level in the room and sits there, doing nothing, but someone makes the light a bit brighter, and again the output toggles because there is a positve change in light. so therefore, it only toggles the output because there is a positve change in light, it would ignore someone turning the light off, or dimming it. I also need control of how much of a positive change it responds to, like the amount that the voltage level is increased, so if its a very slight change, nothing happens, but if there is a large change, it will respond..
this is really messing with my poor ol' brain! but i seem to end up asking more questions on here than answering anyway!
Also, when i say "readjusts" i mean the circuit automatically readjusts its self, like autogain, or something!

If you read thru what you wrote carefully enough...you've just wrote the program that you want....no, really. I was writing it as I was reading it.
Now my question is, the input can only go so high. Once it gets up to a maximum value, what's going to bring it back down to a nominal nothing?

Jerson
- 13th February 2008, 03:43
Ryan

What you're looking for is a derivative function. What you can do is something like this



LastADC var word ' previous error
CurrADC var word ' current error
Threshold con 1 ' can be more if you want

loop:
gosub ReadADC
Error = CurrADC-LastADC
if Error < 0 then Error = -Error ' make absolute error
if Error > Threshold then
High Output
pause 1000 ' wait 1 second
Low Output
endif
LastAdc = CurrAdc
goto loop


This will more or less achieve what you want to. However, this is pure untested code. Use at your own risk.

Jerson

Ryan7777
- 13th February 2008, 04:58
I tired your code, Jerson, and it didn't seem to work right, say the ADC had 2.5 volts applied, and then i turned the voltage up to 3 volts, the output would turn on and off, over and over, rather than toggle once, to show a change happened, and then stop. i changed the threshold value to account for any instabilty in my input voltage, and still no dice. and like a i said before, i need the circuit to "readjust automatically" so that it only cares that there is a positive increase in voltage by a certain value and reacts once and then resets, it shouldn't care what the original ADC value was, only that the value increased. so it would have to "dynamically" adjust itself. you would figure that you would take a reading, and then some miliseconds later, take another reading, subtract the first value from the second and that number would show a difference (if there is any), which would mean a positive change. if that value was over that of the threshold, then react. you would have to clamp that value to a number between 0 and 1023, so there wouldn't be a negative value and a roll over (or under, in this case) which i think is what Jerson's code showed, and i already tried the same thing, in a slightly different way, and neither work... but i could be, and probably am missing something! i really don't understand absolute values (only their vodka!) and how it applies here, all your saying is that the number can only have a certain maximum value right? so couldn't i use MAX?

mackrackit
- 13th February 2008, 06:07
Like skimask said, you have the code in you explanation of the project.
Jerson gave an "untested" example to get you started.

Think about this:


Two VAR : read ADC first : write that value to VAR1 : start LOOP : read ADC :_
write ADC value to VAR2 : IF VAR2 > VAR1 THEN ? : Now make VAR1 = VAR2_
GOTO LOOP

Ryan7777
- 13th February 2008, 07:24
This is what i got so far, and im not seeing why it doesn't work.
please excuse the formatting and what not!

@ DEVICE pic16F684, HS_OSC, WDT_OFF, PWRT_OFF, MCLR_ON, BOD_OFF, IESO_OFF, FCMEN_OFF

DEFINE OSC 20
DEFINE ADC_BITS 10
DEFINE ADC_CLOCK 2
DEFINE ADC_SAMPLEUS 11

CMCON0 = %00000111
VRCON = %00000000

ANSEL = %00110100

ADCON0.6 = 0
ADCON0.7 = 0

voltLevBf VAR WORD
voltLevAf VAR WORD
voltLevCk VAR WORD
threshHold VAR WORD
timOut VAR WORD

prgSel VAR PORTC.2
swtOut VAR PORTC.3

CLEAR

PAUSE 1000


autoGainPrg:

ADCIN 2, voltLevBf

PAUSEUS 12

ADCIN 4, threshHold

PAUSEUS 12

ADCIN 2, voltLevAf

PAUSEUS 12

voltLevCk = voltLevAf - voltLevBf

IF voltLevCk > threshHold THEN

HIGH swtOut

PAUSE 60

LOW swtOut

ADCIN 5, timOut

PAUSEUS 12

PAUSE timOut

ENDIF

GOTO autoGainPrg:

the ADC acts as if there are two seperate voltage values present at the pin even if the voltage is the same and doesnt change. like on the first reading it should read 3 volts, and the second reading should be 3 volts or close enough! it shouldn't flash the LED sitting on the output, but its does, over and over like a blinky circuit, like the two readings are never close to equal, no matter what threshold is set at! so that would make me think that the difference between the two readings is somehow always larger than threshold.. what i'd like to know is, what did Jerson mean by "if Error < 0 then Error = -Error ' make absolute error" ?

IF Error < 0 THEN
Error = Error - Error
ENDIF
??? if so, i tried that version of it, and it didn't work either..

Jerson
- 13th February 2008, 08:57
Ryan

You seem to be reading 3 diff adc ports. First of all, you have to tell us what is each one for any of the code I gave to make sense.

The code I gave wants only 1 adc port to sense the light intensity. So, the other 2 are redundant. And the code I gave should do exactly what you asked for.

Check your code. You seem to have got it diff from what I have given you.

in Pseudo code what I have sent you does this

if CurrentADC != PreviousADC
Output = high
Wait 1 second
Output = Low
PreviousADC = CurrentADC
endif

Jerson

Ryan7777
- 13th February 2008, 18:08
here is where i am now, and it still doesn't work, no matter.. also, if I were to set my current reading and previous readings equal, the next loop through would set them to what ever the ADC says at sample time anyway.. so it would be pointless wouldn't it?
PreviousADC = CurrentADC ?

@ DEVICE pic16F684, HS_OSC, WDT_OFF, PWRT_OFF, MCLR_ON, BOD_OFF, IESO_OFF, FCMEN_OFF

DEFINE OSC 20
DEFINE ADC_BITS 10
DEFINE ADC_CLOCK 2
DEFINE ADC_SAMPLEUS 11

CMCON0 = %00000111
VRCON = %00000000

ANSEL = %00110100

ADCON0.6 = 0
ADCON0.7 = 0

voltLevBf VAR WORD ' variable for first reading or "previous"
voltLevAf VAR WORD ' variable for second reading "current"
voltLevCk VAR WORD ' variable for holding the difference
threshHold VAR WORD ' variable for the threshold
timOut VAR WORD ' variable for holding the timeout

prgSel VAR PORTC.2 ' not in use yet
swtOut VAR PORTC.3 ' output to LED

CLEAR

PAUSE 1000


autoGainPrg:

ADCIN 2, voltLevBf ' sample voltage on ADC 2

PAUSEUS 12

ADCIN 4, threshHold ' get threshold from pot set up as volt. divider on ADC 4

PAUSEUS 12

ADCIN 2, voltLevAf ' sample voltage on ADC 2 again, to check for difference

PAUSEUS 12

IF lghtlevAf > lghtlevbf THEN ' IF the second reading is higher than the first,

voltLevCk = voltLevAf - voltLevBf ' subtract the first reading from the second to get the Dif.

IF voltLevCk > threshHold THEN ' IF there is a difference, and it is greater then threshold

HIGH swtOut ' set the LED high

PAUSE 60 ' pause for 60 mS

LOW swtOut

ADCIN 5, timOut ' check volt. div pot on ADC 5, use number as time out - 0 to 1023

PAUSEUS 12

PAUSE timOut ' Pause for the time out setting, determined by ADC 5

ENDIF

ENDIF

GOTO autoGainPrg:

Ryan7777
- 13th February 2008, 23:04
just to make sure the problem i need to solve is clear, i will explain what i want yet again.
I will not be monitoring a sine wave, the signal will not vary that often actually and will never go negative in relation to ground. but for the sake of an example, I will use sinewave.

Say that there is a sine wave, the frequency of which does not matter, that is 2 Vp-p.
This Sine wave is "floating" with its zero point at 2 volts above ground, making the lowest peak 1 volt, and the highest peak 3 volts. then we'll say my threshold is set at 1.5V.
When the sine wave reaches 1.5V above the 1 volt line (not ground!), it triggers a response from my circuit, or flashes the LED once, and then waits.
Where the sine wave is "floating" (again, floating, not peaking) doesn't matter, it will be anywhere between 0 and 5 volts and is out of my control, it is an independent circuit.
The PIC circuit only needs to trigger when there is a positive going change in voltage that exceeds a threshold that i will need to have control of. so therefore, the threshold has to follow the 1 volt ( or 2V or whatever!) line automatically, while i can vary the threshold level, it will always be in reference to the 1 volt line! not ground. please see my example.bmp
(and yes i know that if my threshold is 1.5 volts, and the signal zero reference exceeds 3.5, the threshold will be out of range, and that is fine because 1.5V is only an example.)

Jerson
- 14th February 2008, 02:34
Ryan

Now, I understand what you're trying to do. However, reading the adc like this hardly gives you anything worthwhile.



ADCIN 2, voltLevBf ' sample voltage on ADC 2
PAUSEUS 12
ADCIN 4, threshHold ' get threshold from pot set up as volt. divider on ADC 4
PAUSEUS 12
ADCIN 2, voltLevAf ' sample voltage on ADC 2 again, to check for difference
PAUSEUS 12


Why? The difference between the 2 samples on ADC 2 is just around 25uS. Is this what you really want??

On reading closely, I think you need to do this


'Find the minimum value
MinAdc var word 'I'm assuming the ADC to be 10bit or more
CurrAdc var word
Threshold var word
DelayVal var word

while 1 ' loop forever
ADCIN 2, CurrAdc ' current ADC reading
ADCIN 4, Threshold ' threshold setting
ADCIN 5, DelayVal ' delay setting
if CurrAdc > MinAdc + Threshold then
High Output
pause DelayVal
Low Output
if CurrAdc <> MinAdc then ' check if the current ADC value < Min ADC
MinAdc = CurrAdc ' and correct the MinADC value
endif
endif
wend



Does this do what you want?
Jerson

Ryan7777
- 14th February 2008, 03:27
I don't know yet, i'll have to give that code a shot, one thing i did figure out tho is that my justification was wrong, i should have had it set to right justify, and I should have saw that earlier! I used serout to spit out the ADC values and now everything looks like it should, but i'll have to do further testing.

Thanks again!