Could be possible to use the ADC in free running mode?
Could be possible to use the ADC in free running mode?
Could be possible to use the ADC in free running mode?
I need to increase the speed of my ADC routine... Could be possible to have the ADC working "forever" while I read only the "last" result with my ADC routine?
Do you have an example to do that with ZBasic Micro?
Thank you,
Pier Andrea.
I need to increase the speed of my ADC routine... Could be possible to have the ADC working "forever" while I read only the "last" result with my ADC routine?
Do you have an example to do that with ZBasic Micro?
Thank you,
Pier Andrea.
There is no method for ZBasic to do that for you as far as I know.
But, ZBasic might not interfere with the normal ADC functioning of the AVR hardware if you access the hardware directly.
I have not tried this myself, but other direct hardware accesses do work, so this probably would too.
"Just" set up the hardware registers to start the ADC running autonomously. Then check the ADC hardware register for the result. I believe it will hold the last complete ACD reading.
I will look again at the AVR docs to remind myself how to do this.
-Tony
But, ZBasic might not interfere with the normal ADC functioning of the AVR hardware if you access the hardware directly.
I have not tried this myself, but other direct hardware accesses do work, so this probably would too.
"Just" set up the hardware registers to start the ADC running autonomously. Then check the ADC hardware register for the result. I believe it will hold the last complete ACD reading.
I will look again at the AVR docs to remind myself how to do this.
-Tony
Read last ADC value
Thank you Tony.
My (theorical) idea is to setup one ADC channel writing the rights values on ADMUX, ADSCRA and ADSCRB registers to operate in free running mode.
After that, I hope that I can obtain the last ADC value just (and only) reading ADCL and ADCH registers (my ZBasic code has to be a little more "slow" than ADCL/ADCH update)...
Could be I obtain an improvement in the ADC speed for embedded DSP (oversampling and decimation in particular)...
Regards, Pier Andrea.
My (theorical) idea is to setup one ADC channel writing the rights values on ADMUX, ADSCRA and ADSCRB registers to operate in free running mode.
After that, I hope that I can obtain the last ADC value just (and only) reading ADCL and ADCH registers (my ZBasic code has to be a little more "slow" than ADCL/ADCH update)...
Could be I obtain an improvement in the ADC speed for embedded DSP (oversampling and decimation in particular)...
Regards, Pier Andrea.
I just checked the AVR hardware docs and the ZBasic language reference. It looks as if you CAN access all the necessary registers to set-up the free running conversions.
You can access the ADCL, ADCH registers atomically in Zbasic with the via the 16 bit ZBasic register ADC.
I just wonder if ZBasic will have a problem with the ADC interrupt firing at 114KHz. Probably it will not be a problem at all.
As for speed enhancement, I would assume there IS going to be a speed improvement vs the usual ADC command.
The usual ZBasic ADC command probably will set up all the registers every time, then trigger a sample, then wait for the sample to be done, then get the sample.
With the free-running technique, all you need to do is get the value of the last sample. BUT you only get the sample from one channel. If you need to change channels, then you have as much overhead as the built-in command, AND by coding it in ZBasic, it will be slower than the built-in function.
I wonder if this is an opening for a new/enhanced ZBasic function(s). To set up and access continuous free-running conversions.
It would be interesting to see how much faster it is to do use free-running conversions, compared to the built-in single ADC conversion
-Tony
You can access the ADCL, ADCH registers atomically in Zbasic with the via the 16 bit ZBasic register ADC.
I just wonder if ZBasic will have a problem with the ADC interrupt firing at 114KHz. Probably it will not be a problem at all.
As for speed enhancement, I would assume there IS going to be a speed improvement vs the usual ADC command.
The usual ZBasic ADC command probably will set up all the registers every time, then trigger a sample, then wait for the sample to be done, then get the sample.
With the free-running technique, all you need to do is get the value of the last sample. BUT you only get the sample from one channel. If you need to change channels, then you have as much overhead as the built-in command, AND by coding it in ZBasic, it will be slower than the built-in function.
I wonder if this is an opening for a new/enhanced ZBasic function(s). To set up and access continuous free-running conversions.
It would be interesting to see how much faster it is to do use free-running conversions, compared to the built-in single ADC conversion
-Tony
Re: Read last ADC value
I've written some sample code (see below) to show how to do this for a mega644-based ZX. It may need to be modified slightly for other ZX devices.paserra wrote:My (theorical) idea is to setup one ADC channel writing the rights values on ADMUX, ADSCRA and ADSCRB registers to operate in free running mode. After that, I hope that I can obtain the last ADC value just (and only) reading ADCL and ADCH registers [...]
The simple driver sits in a loop calling the readADC() function and displaying the result. Note that the wait loop in readADC() is only necessary on the first conversion. After the first conversion has been completed there will always be a conversion value available. If desired, this wait loop could be moved to the initADC() subroutine so that it will not return until the first conversion is completed.
Code: Select all
'
' This sample code illustrates how to set up the ADC for
' continuous conversions. The code is written specifically
' for the mega644-based ZX devices. It may need to be
' modified for other ZX devices.
'
Sub Main()
Dim val as UnsignedInteger
Call initADC(0)
Do
val = readADC()
Debug.Print CStr(val)
Call Sleep(1.0)
Loop
End Sub
'
'' initADC
'
' Initialize the ADC to operate in free-running mode, sampling
' the specified channel.
'
Private Sub initADC(ByVal chan as Byte)
' bit values for the ADCSRA register
Const ADEN as Byte = &H80
Const ADSC as Byte = &H40
Const ADATE as Byte = &H20
Const ADIF as Byte = &H10
Const ADPS2 as Byte = &H04
Const ADPS1 as Byte = &H02
Const ADPS0 as Byte = &H01
' bit values for the ADCSRB register
Const ADTS2 as Byte = &H04
Const ADTS1 as Byte = &H02
Const ADTS0 as Byte = &H01
' bit values for the ADMUX register
Const REFS1 as Byte = &H80
Const REFS0 as Byte = &H40
Const CHAN_MASK as Byte = &H07
' disable the ADC during configuration
Call SetBits(Register.ADCSRA, ADEN, 0)
' select the free-running trigger mode
Call SetBits(Register.ADCSRB, ADTS2 Or ADTS1 Or ADTS0, 0)
' select the AVcc reference and the desired channel
Call SetBits(Register.ADMUX, REFS1 Or REFS0 Or CHAN_MASK, REFS0 Or (chan And CHAN_MASK))
' enable auto-trigger mode, select the 128 prescaler and clear the completion flag
Call SetBits(Register.ADCSRA, ADATE Or ADIF Or ADPS2 Or ADPS1 Or ADPS0, _
ADATE Or ADIF Or ADPS2 Or ADPS1 Or ADPS0)
' enable the ADC and start the first conversion
Call SetBits(Register.ADCSRA, ADEN Or ADSC, ADEN Or ADSC)
End Sub
'
'' readADC
'
' Get the lastest ADC conversion result.
'
Private Function readADC() as UnsignedInteger
' bit values for the ADCSRA register
Const ADIF as Byte = &H10
' wait for a conversion to be completed
Do Until (CBool(Register.ADCSRA And ADIF))
Loop
' get the conversion value
readADC = Register.ADC
End Function
Last edited by dkinzer on 05 May 2008, 14:45 PM, edited 1 time in total.
- Don Kinzer
Great ADC solution!!!
Don,
fantastic work!!! I'll try the code immediately (I'm so curious about the performance improvement!!!!)
Thank you very much!.
Regards, Pier Andrea.
p.s. Thanks to Tony too for the suggestions!!!!
fantastic work!!! I'll try the code immediately (I'm so curious about the performance improvement!!!!)
Thank you very much!.
Regards, Pier Andrea.
p.s. Thanks to Tony too for the suggestions!!!!
Don't forget the venerable ADCtoCOM1 function that already performs free-running conversions and sends the output to COM1.spamiam wrote:I wonder if this is an opening for a new/enhanced ZBasic function(s). To set up and access continuous free-running conversions.
See also the note in this function (and in the Atmel datasheet). Switching any of the digital I/Os on the same port will cause noise and possibly inaccurate results.
Mike Perks
Don, this is EXACTLY what I had in mind. I would have put the wait for the first conversion to be complete in the initialization code. That way the reading of the ADC is as lean as possible because it is most likely that the reason this technique is being used is to get the FASTEST ADC reading possible.
I am not sure, but maybe the first ADC is so fast (even though it is half the speed of later conversions) that data will be ready before the next ZBasic instruction is ready to execute. The first conversion takes 25 clock cycles. I presume that reading of the ADC result would take longer than that even if it is the very next instruction (I.E. fetching, and processing the next instruction causes more than a 25 clock lag?)
Mike, good point re: noise on the ADC. Apparently lots of other processes can cause noise, but I would guess that other activity on the same port would be the worst.
-Tony
I am not sure, but maybe the first ADC is so fast (even though it is half the speed of later conversions) that data will be ready before the next ZBasic instruction is ready to execute. The first conversion takes 25 clock cycles. I presume that reading of the ADC result would take longer than that even if it is the very next instruction (I.E. fetching, and processing the next instruction causes more than a 25 clock lag?)
Mike, good point re: noise on the ADC. Apparently lots of other processes can cause noise, but I would guess that other activity on the same port would be the worst.
-Tony
Fantastic
Have look at this working code to read a differential signal ADC0/ADC1 with a gain of 1x or 10x and internal VRef = 2.56 volts
I have to optimize the DiffADC() routine but I can read an oversampled value (plus 4/5 bits of resolution) in a short period of time:
... IN MAIN ROUTINE...
Regards,
Pier Andrea.
[Edit: wrapped the code with code tags for better readability]
I have to optimize the DiffADC() routine but I can read an oversampled value (plus 4/5 bits of resolution) in a short period of time:
Code: Select all
const GAIN_1 as byte = &HD0
const GAIN_10 as byte = &HC9
dim sign as single
Private Sub InitADC(ByVal gain as Byte)
Call SetBits(Register.ADCSRA, 128, 0)
Call SetBits(Register.ADCSRB, 7, 0)
if gain = 1 then
sign = 1.0
Register.ADMUX = GAIN_1
end if
if gain = 10 then
sign = -1.0
Register.ADMUX = GAIN_10
end if
Call SetBits(Register.ADCSRA, 63,63)
Call SetBits(Register.ADCSRA, 192, 192)
Do Until (CBool(Register.ADCSRA And 16))
Loop
End Sub
Private Function DiffADC() As single
Dim lowByte as Byte, highByte as Byte
dim xvalue as single
dim i as integer
dim value as integer
for i = 0 to 511 'OVERSAMPLING
lowByte = Register.ADCL
highByte = Register.ADCH
If (CBool(highByte And &H02)) Then 'SIGN
highByte = highByte Or &HFC
End If
value = CInt(MakeWord(lowByte, highByte))
xvalue = xvalue + CSng(value) 'ACCUMULATOR
next i
xvalue = (xvalue/512.0) * sign 'DECIMATION
xvalue = (xvalue*2560.0)/512.0 'TO VOLTAGE
DiffADC = xvalue
End Function
Code: Select all
dim value as single
Call InitADC(1) 'GAIN = 1X
or
Call InitADC(10) 'GAIN = 1X
then
value = DiffADC()
Pier Andrea.
[Edit: wrapped the code with code tags for better readability]
Re: Fantastic
The execution time can be reduced by reading all 10 bits of the ADC result at once. Then, to handle the sign extension the value is manipulated byte-wise.paserra wrote:I have to optimize the DiffADC() routine [...]
Also, note that the initialization of the accumulator was missing in your original code.
Code: Select all
Private Function DiffADC() As Single
Dim adcRes as Integer
Dim adcResBytes() as Byte Alias adcRes
Dim i as Integer
' initialize the accumulator
DiffADC = 0.0
'OVERSAMPLING loop
For i = 0 to 511
' read the conversion result
adcRes = CInt(Register.ADC)
' perform sign extension of the value, operating on the high byte only
If (CBool(adcResBytes(2) And &H02)) Then
adcResBytes(2) = adcResBytes(2) Or &HFC
End If
' add the reading to the accumulator
DiffADC = DiffADC + CSng(adcRes)
Next i
' average the reading and convert to voltage value
DiffADC = (((DiffADC / 512.0) * sign) * 2560.0) / 512.0
End Function
In the final conversion, the multiplication by 5 replaces the division by 512 and subsequent multiplication by 2560. If the 'sign' variable were integral as well that would eliminate one more floating point multiplication. I'm not quite sure what the purpose of that is so I left it as it was.
Code: Select all
Private Function DiffADC() As Single
Dim acc as Long
Dim adcRes as Integer
Dim adcResBytes() as Byte Alias adcRes
Dim i as Integer
' initialize the accumulator
acc = 0
'OVERSAMPLING loop
For i = 0 to 511
' read the conversion result
adcRes = CInt(Register.ADC)
' perform sign extension of the value, operating on the high byte only
If (CBool(adcResBytes(2) And &H02)) Then
adcResBytes(2) = adcResBytes(2) Or &HFC
End If
' add the reading to the accumulator
acc = acc + CLng(adcRes)
Next i
' average the reading and convert to voltage value
DiffADC = (CSng(acc * 5) * sign) / 512.0
End Function
Last edited by dkinzer on 18 October 2007, 18:52 PM, edited 1 time in total.
- Don Kinzer
Oh, I did not know that! For some reason I had always thought that it was CPU clock cycles. Fortunately, I never needed to watch every stray clock cycle when using ADC. Plus, I always set it up as free-running, so I did not have to worry about it.dkinzer wrote:Of course, that's 25 ADC clock cycles. Since the 128 prescaler is being used (in order to get the ADC clock below 200KHz), that's 3200 CPU clock cycles.spamiam wrote:The first conversion takes 25 clock cycles.
At 3200 cpu clock cycles, I would imagine you DO have to watch out for the need to wait for the first conversion to be done....
-Tony