Reading analogue values

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.
Post Reply
FFMan
Posts: 502
Joined: 09 January 2010, 12:52 PM

Reading analogue values

Post by FFMan »

a while back i implemented a battery monitor routine in one of my projects and used the code below (cadged from an example) to do this reliably.

Question - why is this code required and can't a simple call to getadc work ?

What amendments are required to the code to work on a 128a1 cpu ?

Code: Select all

'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
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Re: Reading analogue values

Post by dkinzer »

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.
- Don Kinzer
FFMan
Posts: 502
Joined: 09 January 2010, 12:52 PM

Post by FFMan »

I think i will be fine using getadc.

I am trying to read a voltage of 2.23, with an aref of 3.3v

However i get the same value whether reading 2.23v or 2.45v of approx 1135

I see you note about the reference being aref/1.6, does this mean this is the max voltage that can be read. I can scale it some more if required.

thanks
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

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().

Code: Select all

' 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&#40;chan, 3&#41; 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&#40;Register.ADCA_INTFLAGS And &H01&#41;
      Sleep&#40;1&#41;
   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&#40;&#41;

   ' Convert the ADC value to a voltage.
   readADC = CSng&#40;adcVal&#41; * &#40;ADC_REF_VOLTS / 4096.0&#41;
End Function
Last edited by dkinzer on 19 January 2012, 9:57 AM, edited 1 time in total.
- Don Kinzer
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

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.
- Don Kinzer
FFMan
Posts: 502
Joined: 09 January 2010, 12:52 PM

Post by FFMan »

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 ?

thanks
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

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).
- Don Kinzer
FFMan
Posts: 502
Joined: 09 January 2010, 12:52 PM

Post by FFMan »

something is not going quite right. i get the same values returned from getadc for a voltage of 1.2v of approx 1107 and the same for voltage 1.87v

I am using pin b.5 on a 128a1 and i have set it to inputtristate before use.

I have a voltage of 3.28v on avcc but am not using it in this instance.

is this behaving properly or do i have a problem somewhere ?

thanks
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

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.
- Don Kinzer
FFMan
Posts: 502
Joined: 09 January 2010, 12:52 PM

Post by FFMan »

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.
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

FFMan wrote:the documentations lists port B as usable in the getadc section.
True enough. My point was that it should work but there may be a flaw in the ZBasic library code that prevents it from working.
- Don Kinzer
FFMan
Posts: 502
Joined: 09 January 2010, 12:52 PM

Post by FFMan »

dkinzer wrote:My point was that it should work but there may be a flaw in the ZBasic library code that prevents it from working.
i understand, i was just pointing it out for compelteness because my experience is that your documentation is usually very accurate.
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

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.

http://www.zbasic.net/download/zlib/3.3 ... megaA1.zip
- Don Kinzer
FFMan
Posts: 502
Joined: 09 January 2010, 12:52 PM

Post by FFMan »

thanks - that seems to be returning more expected values

I'll calibrate it later.

thanks
Post Reply