I believe I've settled on a high-rate ADC solution - actually two.
Since the input in the project can include zero volts, this code corrects the zero-volt input (often offset by several tens of mV) via one of two methods. One method converts a pin connected to the ground plane and subtracts that value from the input conversion; the other uses differential ADC mode to effectively achieve the same result, but at half the resolution - but also at a faster rate since no time is required to convert the grounded pin.
I've placed the ADC setup code in InitADC(), run once; function readADC(pin) can then be called as required. Differential mode can select A.0 through A.3 as the negative input or, with a different CONVMODE setting in Register.ADCA_CTRLB, A.4 through A.7. I chose and grounded A.1 since A.0 can be used as an external vRef - although I continue to have some difficulty with that mode when trying to use Vcc as vRef (
see edit below). I've reverted to Vcc/1.6 vRef.
The single-ended method converts at ~57kHz; the differential method converts at ~83kHz.
Code: Select all
' ZX-24x
#define Differential
const pinInput as Byte = A.7 ' ZX-24x pin 14
const pinVcc as byte = A.0 ' 20 ' Vcc reference
const pinZero as Byte = A.1 ' 19 ' 0v reference
const Vcc as single = 3.25
const vref as Single = Vcc / 1.6
#ifdef Differential
const fs as single = 4096.0 / 2.0 ' +/-2048
#else
const fs as single = 4096.0
#endif
dim SpinCount as long, ivalp1 as integer
dim ReadSpinTaskStack(1 to 100) as byte
Sub Main()
dim StartCount as long, StopCount as long, ivalp2 as integer
CallTask ReadSpinTask, ReadSpinTaskStack
do
Atomic
Startcount = SpinCount
End Atomic
Call Sleep(1.0)
Atomic
ivalp2 = ivalp1 ' copy ival from module level
StopCount = SpinCount
End Atomic
Debug.Print "Loops/Sec= "; CStr(StopCount - StartCount); " ival= "; cstr(ivalp2); " volts= "; fmt(vref * csng(ivalp2) / fs, 2)
loop
End Sub
Sub ReadSpinTask()
Dim ival as Integer
Call InitADC()
Do
#ifdef Differential
ival = readADC(pinInput)
#else
ival = readADC(pinInput) - readADC(pinZero) ' 0v offset correction
#endif
Atomic
ivalp1 = ival ' copy ival to module level
SpinCount = SpinCount + 1
End Atomic
Call Sleep(0)
Loop
End Sub
Sub InitADC()
Register.ADCA_PRESCALER = 2 ' divide-by-16
#ifdef Differential
Register.ADCA_CTRLB = &H10 ' differential signed
Register.ADCA_CH0_CTRL = &H02
#else
Register.ADCA_CTRLB = &H00 ' single-ended unsigned
Register.ADCA_CH0_CTRL = &H01
#endif
Register.ADCA_REFCTRL = &H10 ' vref= Vcc/1.6 = 2.06v
Register.ADCA_CH0_INTFLAGS = &H01 ' clear flag
Register.ADCA_CTRLA = &H01 ' enable ADC
'Call Sleep(1) ' settle
End Sub
Function readADC(byval pin as byte) as integer
' select pin(s)
#ifdef Differential
Register.ADCA_CH0_MUXCTRL = (Shl(PortBit(pin), 3) And &H38) Or (PortBit(pinZero) And &H03)
#else
Register.ADCA_CH0_MUXCTRL = Shl(PortBit(pin), 3) And &H38
#endif
Register.ADCA_CTRLA = &H05 ' start channel 0
Do While Not CBool(Register.ADCA_CH0_INTFLAGS And &H01) ' wait for conversion
Loop
readADC = cint(Register.ADCA_CH0_RES) ' result
Register.ADCA_CH0_INTFLAGS = &H01 ' clear flag
End Function
Result:
Code: Select all
Differential:
Loops/Sec= 83305 ival= 1685 volts= 1.67
Single-ended:
Loops/Sec= 57241 ival= 3335 volts= 1.65
Later edit:
Vcc as vRef via AREFA (-24x,u pin 20) won't work; the maximum AREFA voltage is Vcc-0.6v on these parts.