Discussion about the ZBasic language including the System Library. If you're not sure where to post your message, do it here. However, do not make test posts here; that's the purpose of the Sandbox.
'Function to read an adc voltage using the internal reference
Function readADC(ByVal chan as Byte) as Single
' Wait for the ADC to be available to use.
Do While CBool(Register.ADCSRA And &H80)
Sleep(1)
Loop
Call LockTask()
' Select the desired channel and the internal 2.56V reference.
Register.ADMUX = (chan And &H07) Or &Hc0
' Start a conversion using the 128 prescaler (the resulting
' conversion clock must be between 50KHz and 200KHz).
Register.ADCSRA = &Hd7
' Wait for conversion completion. Using the 128 prescaler,
' the conversion should only take about 0.2mS.
Do While Not CBool(Register.ADCSRA And &H10)
Sleep(1)
Loop
' Read the conversion result and turn off the converter.
Dim adcVal as UnsignedInteger
adcVal = Register.ADC
Register.ADCSRA = 0
Call UnlockTask()
' Convert the ADC value to a voltage (2.56V is full scale
' voltage, 1024 is the number of quantization steps).
readADC = CSng(adcVal) * (2.56 / 1024.0)
End Function
FFMan wrote:why is this code required and can't a simple call to getadc work ?
If the capabilities of GetADC() meet your requirements you can use it directly. For the xmega, the GetADC() function uses a reference voltage of Vcc/1.6 and performs a single-ended conversion. If this is suitable for your application then you're all set.
The forum contains at least two posts with a readADC() function: one for differential conversion and one for using an internal reference voltage. The same general ideas can be implemented on the xmega but the specifics are different as described in the xmega datasheet.
FFMan wrote:I see you note about the reference being aref/1.6, does this mean this is the max voltage that can be read.
The referenced used by GetADC() is Vcc/1.6 rather than aref/1.6. If you want to use an external reference voltage you'll have to implement a function something like the one below (untested). Except for the reference selected, this is essentially equivalent to the xmega code for GetADC().
' Function to read an adc voltage using an external reference.
Function readADC(ByVal chan as Byte) as Single
' Define the external reference voltage value.
Const ADC_REF_VOLTS as Single = 2.56
' Wait for the ADC to be available to use.
Do While CBool(Register.ADCA_CTRLA And &H01)
Sleep(1)
Loop
Call LockTask()
' Set the prescaler to ensure the ADC clock is < 800KHz.
Register.ADCA_PRESCALER = &H04
' Select the REFA external reference.
Register.ADCA_REFCTRL = &H20
Register.ADCA_CTRLB = 0
' Select the desired channel.
Register.ADCA_CH0_MUXCTRL = Shl(chan, 3) And &H38
Register.ADCA_CH0_CTRL = 1
' Ensure the "done" flag is clear, enable the ADC and start a conversion.
Register.ADCA_INTFLAGS = 1
Register.ADCA_CTRLA = &H05
' Wait for conversion completion.
Do While Not CBool(Register.ADCA_INTFLAGS And &H01)
Sleep(1)
Loop
' Read the conversion result.
Dim adcVal as UnsignedInteger
adcVal = Register.ADCA_CH0RES
' Disable the ADC and Clear the completion flag.
Register.ADCA_CTRLA = 0
Register.ADCA_INTFLAGS = 1
Call UnlockTask()
' Convert the ADC value to a voltage.
readADC = CSng(adcVal) * (ADC_REF_VOLTS / 4096.0)
End Function
Last edited by dkinzer on 19 January 2012, 9:57 AM, edited 1 time in total.
I made a change to the code setting the CH0_MUXCTRL register. The code is still untested, however.
Note, also, that if an internal reference is used, the ADC must be enabled and then a certain amount of time (called the settling time) must pass before the first conversion is started. The code above doesn't include this detail because it uses an external reference, assumed to be on always.
i think this may be getting more involved than i need. I simply need to read the battery voltage which i can scale as required.
I have wired in the reference voltage using the choke and the capacitors exist already on the break out board but if i don't need to use this then that is fine.
So some advice, best way to read voltage on 128a1. i'm using pin b.5, ideally using getadc to keep it simple and non-atomic (as there are up to 4 serial streams running concurrently).
For clarity what is the maximum voltage that can be read ?
FFMan wrote:For clarity what is the maximum voltage that can be read ?
The maximum voltage that can be converted is one quantization level below the reference voltage (due to the fact that the digital representation ranges from 0 to 2^N - 1). For an xmega, which has a 12-bit ADC, running at 3.3V and using the internal Vcc/1.6 reference (as does the GetADC() function) the maximum voltage that can be converted is (3.3V/1.6 * 4095/4096)=2.062V.
Note that if you use the internal Vcc/1.6 reference, the external reference voltage (Aref or Bref) is not needed. If you do use an external reference, the value is limited to the range 1.0V <= Vref <= (Vcc-0.6V).
FFMan wrote:I am using pin b.5 on a 128a1 and i have set it to inputtristate before use.
It could be that there is an issue in the ZBasic xmega code for GetADC() for the pins of PortB. If you get expected results on a pin of PortA that's probably the issue. We'll look into it here.
ok - I think I can easily switch to a port A pin but won't be able to try this until saturday evening probably now, but i shall and let you know, unless of course in the mean time you find a problem.
the documentations lists port B as usable in the getadc section.
FFMan wrote:I am using pin b.5 on a 128a1 and i have set it to inputtristate before use.
We confirmed that the ADC configuration was not correct for PortB inputs. You can download a new ZBasic Library (v3.3.4) for the xmegaA1 using the link below. Since the change only affects the xmegaA1, there is no update for other devices at this time. This update must be used with a ZBasic compiler in the v3.3 series.