One of the things we're looking at is detecting large bodies of liquid (ok, pint glasses filled with beer) for a fun experiment. It might turn into something, or it might just remain one on the ideas board. The first thing that springs to mind with detecting any kind of body of liquid is capacitive sensing.
We've done this in the past, using a "charge and count" digital input method (our touch-sensitive miniature guitars used this approach before we finally settled on the darlington multiplex idea instead, and very early prototypes of electronic Blood Bowl used the same, simple approach). The basic idea is to set a pin as an output, drive it high, turn it into an input and count how long it takes to drain; any addition capacitance added to the pin/pad will make it discharge more slowly and you can use this to detect the presence (or absence) of a finger.
In recent years Microchip have been promoting their mTouch technology but it's basically a variation on using two pins/ADC channels to create a voltage divider and using an analogue input to detect the presence (or absence) of an object. A great explanation of how it works can be found here.
The basic idea is:
- The design uses at least two i/o pins (even if only one sensor is required, although the second pin can also be used as a sensor, if queried in sequence, so three sensors would require three pins, not four - it is only for one sensor you require more pins than you're testing)
- Pin A is made a digital output and set high
- Point the ADC to PinA (this causes the ADC to charge up)
- Make Pin B (the sensor pin) a digital output and force low (set to ground)
- Turn Pin B (the sensor pin) into input (TRISx = 1)
- Point the ADC to PinB (the sensor line). While the ADC charge-and-hold capacitor retains charge, we've effectively created a voltage divider between Pin A and Pin B.
- Begin ADC conversion
- Read the values of ADRESH: ADRESL (10-bit analogue value)
This is why we need two pins for a single sensor (but there's no reason we can't swap them over and use PinA as the sensor and PinB as the ADC charge circuit if we need two sensors). When the ADC is pointing to PinA, it's held high and the internal charge-and-hold capacitor that makes up part of the analogue-to-digital circuitry is charged up to 5V (or whatever the supply voltage is).
The input pin is grounded (to put it into a known state) then made an input. When the ADC is pointed to the sensor pin, PinB, we now have the VC(hold) capacitor in parallel to the capacitance of the sensor. We also have a voltage divider - the VC(hold) capacitor is fully charged, and the sensor pad is at zero volts. The input pin reads an analogue value, somewhere between the two.
Any increase in capacitance at the "sensor side" of the voltage divider results in a lower analogue value on the input pin.
We take multiple samples and look for a relatively low or high value on the analogue input to determine whether or not there is extra capacitance (and therefore a larger body of liquid) on the sensor. Relative means we keep a rolling average and look for values that are noticeably larger or smaller than this average, to indicate an object being placed or removed from the sensor.
The overall code looks something like this.
Define CONFIG1 = 0x0804
Define CONFIG2 = 0x1dff
Define CLOCK_FREQUENCY = 32
declarations:
Symbol tx = PORTA.0
Symbol rx = PORTA.1
ConfigPin tx = Output
ConfigPin rx = Input
Symbol pin_a = PORTA.4
Symbol pin_b = PORTA.2
Dim adc_value As Word
Symbol pin_led = PORTB.6
ConfigPin pin_led = Output
Dim i As Byte
Dim last_val As Word
Dim val_diff As Word
Dim trigger_threshold As Word
initialise_chip:
OSCCON = 11110000b 'oscillator = 32mhz
WaitMs 10
APFCON0 = 11000100b 'APFCON
initialise:
trigger_threshold = 30
loop:
Gosub readsensor_1
'Serout tx, 9600, #adc_value, CrLf
If adc_value > last_val Then
val_diff = adc_value - last_val
Else
val_diff = last_val - adc_value
Endif
If val_diff > trigger_threshold Then
If adc_value > last_val Then
'this is a rising edge (remove glass)
Low pin_led
Else
'this is a rising edge (glass added)
If last_val > 0 Then
High pin_led
Endif
Endif
last_val = adc_value
Endif
Goto loop
End
readsensor_1:
'make pinA a digital output and set high
ANSELA.4 = 0 'set to one for analogue, clear to zero for digital
TRISA.4 = 0 'set to one for input, clear to zero for output
High pin_a
'point the ADC to pinA to allow the charge-and-hold capacitor
'to start to charge up (RA4 is analogue channel 3)
ADCON0 = 00001101b
'make pinB a digital output and set low
ANSELA.2 = 0 'set to one for analogue, clear to zero for digital
TRISA.2 = 0 'set to one for input, clear to zero for output
Low pin_b
'wait for a bit
WaitMs 1
'turn pinB into an analogue input
ANSELA.2 = 1 'set to one for analogue, clear to zero for digital
TRISA.2 = 1 'set to one for input, clear to zero for output
'point the ADC to pinB to complete the voltage divider effect
'(RA2 is analogue channel 2)
ADCON0 = 00001001b
'read the ADC values
Adcin 2, adc_value
Return
Define CONFIG2 = 0x1dff
Define CLOCK_FREQUENCY = 32
declarations:
Symbol tx = PORTA.0
Symbol rx = PORTA.1
ConfigPin tx = Output
ConfigPin rx = Input
Symbol pin_a = PORTA.4
Symbol pin_b = PORTA.2
Dim adc_value As Word
Symbol pin_led = PORTB.6
ConfigPin pin_led = Output
Dim i As Byte
Dim last_val As Word
Dim val_diff As Word
Dim trigger_threshold As Word
initialise_chip:
OSCCON = 11110000b 'oscillator = 32mhz
WaitMs 10
APFCON0 = 11000100b 'APFCON
initialise:
trigger_threshold = 30
loop:
Gosub readsensor_1
'Serout tx, 9600, #adc_value, CrLf
If adc_value > last_val Then
val_diff = adc_value - last_val
Else
val_diff = last_val - adc_value
Endif
If val_diff > trigger_threshold Then
If adc_value > last_val Then
'this is a rising edge (remove glass)
Low pin_led
Else
'this is a rising edge (glass added)
If last_val > 0 Then
High pin_led
Endif
Endif
last_val = adc_value
Endif
Goto loop
End
readsensor_1:
'make pinA a digital output and set high
ANSELA.4 = 0 'set to one for analogue, clear to zero for digital
TRISA.4 = 0 'set to one for input, clear to zero for output
High pin_a
'point the ADC to pinA to allow the charge-and-hold capacitor
'to start to charge up (RA4 is analogue channel 3)
ADCON0 = 00001101b
'make pinB a digital output and set low
ANSELA.2 = 0 'set to one for analogue, clear to zero for digital
TRISA.2 = 0 'set to one for input, clear to zero for output
Low pin_b
'wait for a bit
WaitMs 1
'turn pinB into an analogue input
ANSELA.2 = 1 'set to one for analogue, clear to zero for digital
TRISA.2 = 1 'set to one for input, clear to zero for output
'point the ADC to pinB to complete the voltage divider effect
'(RA2 is analogue channel 2)
ADCON0 = 00001001b
'read the ADC values
Adcin 2, adc_value
Return
And the result works like this
Do J20 and beer have the same trigger points? Would using copper instead of carbon-power-and-gum make it more or less sensistive? We'll have to find out....
But so far, it's all looking quite positive. It's only a simple test, but it's encouraging to see that the trigger stays set even after you release the glass - but it's not so sensitive that it triggers if you just wave your hand over the sensor.
Will it work with an 8 bit ADC?
ReplyDelete