ADC platform interface proposal

classic Classic list List threaded Threaded
3 messages Options
jbsnyder jbsnyder
Reply | Threaded
Open this post in threaded view
|

ADC platform interface proposal

I've got an initial attempt at the ADC module sketched out, and I'll check it in as soon as I make a few more modifications to clean things up and make sure I haven't broken builds for platforms that don't have ADC implementations written yet.  So far it will happily do single shot readings, and the voltages I'm putting in seem to correlate with what I'd expect.

Currently I'm testing it by outputting text on the OLED, and I suppose I could turn it into a little oscilloscope demo.

Some thoughts aside from the responses below:
As things stand, I'm returning the raw integer value provided off of the ADC.  Should the ADC module know what range of voltage inputs is and attempt to convert to say, millivolts, when a sample is returned?  For lower bit depths, like 10 bits, having the result returned as u16 in millivolts wouldn't decimate the resolution of the result, however at higher resolutions converting a raw integer result to mV might result in loss of precision without floating point.  This is one of those "level of abstraction" questions that might be valuable to discuss.  I suppose, one way to handle things would be to have this functionality be optional, but which should be the default?

Another item is:  If I do a single shot conversion, and then subsequently call the function to get a sample, should default behavior be to keep returning the last result?  I've found that at least with the Stellaris platform the results are stored in a FIFO, so the first time I get my result, and the second time I get a default value (presumably telling me that the FIFO is empty).  Any thoughts?


----- "Bogdan Marinescu" <bogdan.marinescu at gmail.com> wrote:

> > In a related vein, one thing that I've not used on micros that much,
> but
> > have on dedicated ADC boards is the use of triggers for sampling.
> This
> > would be particularly useful for burst sampling.  Sometimes this is
> done
> > based on the signal itself, but often with a digital trigger.  The
> digital
> > trigger should be pretty easy to do by having the kickoff be
> interrupt
> > driven.  If a board doesn't support external interrupts, you could
> always
> > emulate it by polling, although it's not as good.
>
> True, but I don't know much about the possible triggering sources on
> different platforms, so I don't know how to formulate this into a
> proper API.

Yeah, I don't know either.  It is present in the stellaris platform, I'd have to check on other platforms.  I can

One somewhat generic way to configure them would be to use hardware when available, and set up ISRs tied to whatever would initiate the triggering.  So, in some cases you'd get the hardware benefit, and in other cases it would still work, but you'd lose more clock cycles.  I'll leave this on the back burner for the moment, and if I find that I need it for a project I could evaluate at that time what the other platforms do for this sort of thing and at that time think further about adding such features.  I think it may be a good idea to think about the API accommodating such additions gracefully.

Previously I've used it for things like making sure that when I sample an oscillating signal (like output from a function generator), that I start my sampling at the same point in the phase of the signal.  This isn't such a big deal when you're something like an order of magnitude or more higher than the Nyquist rate, but it can be useful when you aren't, but have a TTL signal synced with whatever source you're using.

>
> > I guess this brings up the question:  do we allow arbitrary pin
> selection
> > regardless of whether it will generate interrupts?  I have less
> knowledge of
> > how this is supported on various 32-bit platforms that eLua is
> running on.
> >  IIRC, the LM3S boards can do interrupts on any pin.
>
> Yes, but are any of them possible triggers for the ADC?

I'm not sure about arbitrary pins generating the right sort of interrupt, but certainly on Stellaris you can trigger ADC to start on GPIO, a comparator, timer, and also PWM.

>
> > With this in mind, this is what I came up with:
> >
> > int platform_adc_exists( unsigned id ); // generic, it will be part
> of
> > src/common.c
> > u16 platform_adc_sample( unsigned id ); // sample the specified ADC
> channel
> >
> > OK.  So this will return the latest sample collected?  Does it block
> waiting
> > for completion if the latest one hasn't finished yet?
>
> Yes on both questions.
>
> > Also, should this behavior be different in continuous vs single
> shot? i.e.:
> > Single Shot: wait for sample if not ready
> > Continuous: take whatever the last completed sample was?  wait for
> next
> > sample? (I think the former would make more sense as a reason to
> use
> > continuous sampling).
>
> Not sure yet. I think the difference should be in the
> "platform_adc_sample" function, which should know how to behave in
> both 'single' and 'continous' modes.

Right, I agree.  I think what I'll do here is take a look at at least one or two other hardware implementations and check on some of the assumptions made about data stored in the ADC result register.  If one can pretty easily read the register that contains the conversion result without worrying about weird states in between conversions, then it could be rather lightweight to read the last result.  If it's more complicated, we'll have to see.

>
> >
> > void platform_adc_start( unsigned id ); // starts a conversion on
> the
> > specified ADC channel and returns immediately
> > int platform_adc_is_done( unsigned id ); // returns 1 if the
> conversion on
> > the specified channel ended, 0 otherwise
> >
> > I wonder if in the case of using an interpreted language like Lua
> that one
> > might be converting values fast enough that having some sort of loop
> waiting
> > for the data to be done might not be fruitful, at least in single
> shot mode?
>
> It might be, but as this is highly dependent on the platform we won't
> make this assumption, going to the more generic route instead.

I see your point, especially if the driver might handle something like an external ADC where results need to be shipped back over some sort of serial communication.  Or if one is using interrupt driven sampling, if that interrupt hasn't fired, you might not have a result either.

>
> > void platform_adc_set_mode( unsigned id, int mode ); // sets the
> mode on the
> > specified ADC channel to either "single shot" or "continuous"
> >
> > Hmm.. what about adding a filtered/smoothed mode? By this I don't
> mean
> > anything particularly intense, perhaps just a ring buffer of a set
> length
> > that gets averaged whenever platform_adc_sample is called.  I find
> myself
> > frequently doing things like this when sampling things that are
> noisier than
> > the best resolution the ADC can deliver.  My only thought here would
> be that
> > the C code might do this faster than doing the averaging in Lua.
> > One way to handle this somewhat elegantly would be to not even add
> anything
> > to the mode function, just have the default buffer be of length 1,
> and under
> > this condition, no smoothing is done.  If you want it, use the
> following
> > function to enable the rolling average:
> > void platform_adc_set_smoothing( unsigned id, int length ); // sets
> window
> > length/buffer size for moving average filter
> > Does this seem too much to have in the library?
>
> No, actually this is a very good idea. In all the apps I used so far
> I
> needed to implement a moving average filter in order to actually use
> the data I read from the ADC. Even more, some platforms (like LM3S)
> have hardware filters, which is even cooler. So yes, let's add this
> too.

OK, will do.  This is not yet implemented, but I'll look into doing a generic filter first, and then try and use the hardware support on the hardware averaging.

>
> > void platform_adc_burst( unsigned id, u16* buf, unsigned count, u32
> > frequency ); // burst conversion: read "count" samples from the ADC
> channel
> > "id", storing the results in "buf". The samples are read at
> periodic
> > intervals, the period is given by "frequency".
> >
> > I like this as well.
> > One thing that might be interesting to provide as well, would be
> some method
> > to do this type of recording directly to a binary file in the mini
> > filesystem or onto an SD card or something similar.  This could be
> useful if
> > one wants to capture more data than can be held in SRAM at once, but
> the
> > sampling rate would have to drop down to much to record and transfer
> data a
> > machine connected to the micro.
>
> This is a logging application by itself :), and since it could be
> implemented directly from Lua, let's not do this in the ADC platform
> interface, it's beyond its purpose. One thing that we could do for
> this kind of applications is to make the platform_adc_burst function
> non-blocking, so one can use double buffering (or even better give it
> another argument to specifiy if it's blocking or not). Something like
> this:
>
> local buf1, buf2
> local active = buf1
> adc.start_burst( active, ... , adc.NONBLOCKING )
> while true do
>   local newactive = active == buf1 and buf2 or buf1
>   while adc.burst_in_progress( active, ...) do end
>   adc.start_burst( newactive, ... adc.NONBLOCKING )
>   io.write( active )
>   active = newactive
> end
>
> I have other ideas about this. Another mode I'm considering is having
> the ADC acquisition run continously "in the background" (using
> interrupt handlers) at a given frequency (with averaging if required)
> and running over a circular buffering mechanism that would provide
> functions like "get_num_samples", or (when interrupts handlers in Lua
> are implemented) interrupt triggering at a specified buffer fill rate
> (in much the same way most UARTs with FIFOs can trigger an interrupt
> when the FIFO is filled up to a given ratio). It would be much easier
> to implement double buffering with something like this. But we're not
> there just yet :)

Right.  This is somewhat similar to what I'm thinking as well.  Some of this should be fairly easy with hardware support provided on LM3S, but the generic fallback that works on all platforms should be implemented first.

BogdanM BogdanM
Reply | Threaded
Open this post in threaded view
|

ADC platform interface proposal

On Mon, Jan 19, 2009 at 10:06 PM, James Snyder <jbsnyder at fanplastic.org> wrote:
> I've got an initial attempt at the ADC module sketched out, and I'll check it in as soon as I make a few more modifications to clean things up and make sure I haven't broken builds for platforms that don't have ADC implementations written yet.  So far it will happily do single shot readings, and the voltages I'm putting in seem to correlate with what I'd expect.

Good news, thanks! :)

> As things stand, I'm returning the raw integer value provided off of the ADC.  Should the ADC module know what range of voltage inputs is and attempt to convert to say, millivolts, when a sample is returned?  For lower bit depths, like 10 bits, having the result returned as u16 in millivolts wouldn't decimate the resolution of the result, however at higher resolutions converting a raw integer result to mV might result in loss of precision without floating point.  This is one of those "level of abstraction" questions that might be valuable to discuss.  I suppose, one way to handle things would be to have this functionality be optional, but which should be the default?

Let's return the ADC readings for now. I want this to work on the
integer-only Lua as well (maybe with a fixed point library), so I'm
trying to avoid floating point numbers in the platform interface as
much as possible.

> Another item is:  If I do a single shot conversion, and then subsequently call the function to get a sample, should default behavior be to keep returning the last result?  I've found that at least with the Stellaris platform the results are stored in a FIFO, so the first time I get my result, and the second time I get a default value (presumably telling me that the FIFO is empty).  Any thoughts?

I'm not sure I understand this. If you call a function to get a
sample, it should read a new sample and return it. What would be the
point of returning the previous sample?

Best,
Bogdan

jbsnyder jbsnyder
Reply | Threaded
Open this post in threaded view
|

ADC platform interface proposal

On Jan 21, 2009, at 1:38 AM, Bogdan Marinescu wrote:

> On Mon, Jan 19, 2009 at 10:06 PM, James Snyder <jbsnyder at fanplastic.org
> > wrote:
>> I've got an initial attempt at the ADC module sketched out, and  
>> I'll check it in as soon as I make a few more modifications to  
>> clean things up and make sure I haven't broken builds for platforms  
>> that don't have ADC implementations written yet.  So far it will  
>> happily do single shot readings, and the voltages I'm putting in  
>> seem to correlate with what I'd expect.
>
> Good news, thanks! :)
>
>> As things stand, I'm returning the raw integer value provided off  
>> of the ADC.  Should the ADC module know what range of voltage  
>> inputs is and attempt to convert to say, millivolts, when a sample  
>> is returned?  For lower bit depths, like 10 bits, having the result  
>> returned as u16 in millivolts wouldn't decimate the resolution of  
>> the result, however at higher resolutions converting a raw integer  
>> result to mV might result in loss of precision without floating  
>> point.  This is one of those "level of abstraction" questions that  
>> might be valuable to discuss.  I suppose, one way to handle things  
>> would be to have this functionality be optional, but which should  
>> be the default?
>
> Let's return the ADC readings for now. I want this to work on the
> integer-only Lua as well (maybe with a fixed point library), so I'm
> trying to avoid floating point numbers in the platform interface as
> much as possible.

Yeah, the way to do this without losing detail would be to use  
floating point.  I'm not adverse to the idea that just returning raw  
results is appropriate, since in a lot of cases one is going to do  
some additional conversion anyways.  At least on the LM3S board I have  
the ADC is 0-3V, so, in most cases, I'd likely have some sort of op-
amp arrangement to take whatever signal I had as a source and remap it  
onto that range.  In such a situation, pre-conversion isn't all that  
useful.  I'm not sure, overall, how many applications end up doing  
additional conversion or not.  One thing that may make sense is to be  
able to let the programmer know what the bit depth is, or what the max  
and min readable values are, similar to how one can set timers?

I think all you would need would be maxval, because I don't know of  
any ADCs that will return negative values.  Does that seem reasonable?

>
>
>> Another item is:  If I do a single shot conversion, and then  
>> subsequently call the function to get a sample, should default  
>> behavior be to keep returning the last result?  I've found that at  
>> least with the Stellaris platform the results are stored in a FIFO,  
>> so the first time I get my result, and the second time I get a  
>> default value (presumably telling me that the FIFO is empty).  Any  
>> thoughts?
>
> I'm not sure I understand this. If you call a function to get a
> sample, it should read a new sample and return it. What would be the
> point of returning the previous sample?

Well, I guess what I was asking was, what to do in order to make  
behavior consistent across platforms.  I suppose what I should do is  
throw an error based on however a particular platform indicates the  
sample buffer is empty.


--
James Snyder
Biomedical Engineering
Northwestern University
jbsnyder at fanplastic.org
http://fanplastic.org/key.txt
ph: (847) 644-2322

-------------- next part --------------
An HTML attachment was scrubbed...
URL: https://lists.berlios.de/pipermail/elua-dev/attachments/20090121/616ab22d/attachment.html 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: PGP.sig
Type: application/pgp-signature
Size: 194 bytes
Desc: This is a digitally signed message part
Url : https://lists.berlios.de/pipermail/elua-dev/attachments/20090121/616ab22d/attachment.pgp