ADC platform interface proposal

classic Classic list List threaded Threaded
4 messages Options
BogdanM BogdanM
Reply | Threaded
Open this post in threaded view
|

ADC platform interface proposal

Hi,

Following is my proposal for a platform interface for the ADC peripheral. I
have only very limited experience with ADCs/DACs, in fact the only two
"serious" applications that I wrote and used ADCs were based on external
components and were quite basic on the data acquisition part, so you might
find this specification incomplete or just plain strange. If so, please let
me know so we can refine it to a better form.
I tried to extract a common set of features of ADCs from all the CPUs that
eLua currently supports, and this is what I came up with:

- most (or maybe all) of them support both "single shot" and "continous"
modes
- some of them let the user select ADC timings (like startup time and
sample&hold time). This seems to be quite rare though, so I won't include it
in the platform interface.
- some of them also support a special mode in which they are "paired" with a
hardware timer and use that timer as a conversion clock. This is useful, and
it can be emulated even on platforms that don't support this feature.

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
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
void platform_adc_set_mode( unsigned id, int mode ); // sets the mode on the
specified ADC channel to either "single shot" or "continuous"
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".

That's about it for now. What do you think?

Best,
Bogdan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: https://lists.berlios.de/pipermail/elua-dev/attachments/20090108/5205151c/attachment.html 

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

ADC platform interface proposal

Overall, those make a good core for the ADC functions.  I think a few  
additions might be nice as well.

On Jan 8, 2009, at 3:07 PM, Bogdan Marinescu wrote:

> Hi,
>
> Following is my proposal for a platform interface for the ADC  
> peripheral. I have only very limited experience with ADCs/DACs, in  
> fact the only two "serious" applications that I wrote and used ADCs  
> were based on external components and were quite basic on the data  
> acquisition part, so you might find this specification incomplete or  
> just plain strange. If so, please let me know so we can refine it to  
> a better form.
> I tried to extract a common set of features of ADCs from all the  
> CPUs that eLua currently supports, and this is what I came up with:
>
> - most (or maybe all) of them support both "single shot" and  
> "continous" modes
> - some of them let the user select ADC timings (like startup time  
> and sample&hold time). This seems to be quite rare though, so I  
> won't include it in the platform interface.
> - some of them also support a special mode in which they are  
> "paired" with a hardware timer and use that timer as a conversion  
> clock. This is useful, and it can be emulated even on platforms that  
> don't support this feature.

I agree with the use of the timer as a conversion clock.

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.

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.

> 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?

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


> 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?  Certainly, one is more likely to need this if  
one wants to know if a burst has completed.

> 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?

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


I realize some of these things might extend beyond what could be  
appropriate for a simple module, but  I think they could be useful  
just the same.  Also, I think the original functions make sense to get  
working as an initial implementation before making any of the  
extensions I'm proposing.

Thoughts?


--
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/20090109/1e66edac/attachment-0001.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/20090109/1e66edac/attachment-0001.pgp 

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

ADC platform interface proposal

Bogdan, any thoughts on these points?

I'm a little busy at the moment (work pileup, plus a not quite cheap  
linear actuator broke on a project of mine.. AC servos can pack quite  
a punch), but I'm going to resume work on this in a day or two.

My reasoning on including the filtering/smoothing is that doing  
averaging and data handling in C might be lighter weight than having  
them converted to tables and then iterating in Lua, but I could be  
wrong :-)

One other thought is that, I suppose triggering could be handled with  
an ISR in Lua I suppose?  I guess I might worry about how  
deterministic something like this might be, as well as how many extra  
clock cycles might go by with such a trigger made in Lua vs some sort  
of direct hardware configuration or C ISR.  I guess one way to know  
would be to give it a go and see how many samples one might drop  
between trigger and sampling onset, with different configurations.

On Jan 9, 2009, at 2:16 PM, James Snyder wrote:

> Overall, those make a good core for the ADC functions.  I think a  
> few additions might be nice as well.
>
> On Jan 8, 2009, at 3:07 PM, Bogdan Marinescu wrote:
>
>> Hi,
>>
>> Following is my proposal for a platform interface for the ADC  
>> peripheral. I have only very limited experience with ADCs/DACs, in  
>> fact the only two "serious" applications that I wrote and used ADCs  
>> were based on external components and were quite basic on the data  
>> acquisition part, so you might find this specification incomplete  
>> or just plain strange. If so, please let me know so we can refine  
>> it to a better form.
>> I tried to extract a common set of features of ADCs from all the  
>> CPUs that eLua currently supports, and this is what I came up with:
>>
>> - most (or maybe all) of them support both "single shot" and  
>> "continous" modes
>> - some of them let the user select ADC timings (like startup time  
>> and sample&hold time). This seems to be quite rare though, so I  
>> won't include it in the platform interface.
>> - some of them also support a special mode in which they are  
>> "paired" with a hardware timer and use that timer as a conversion  
>> clock. This is useful, and it can be emulated even on platforms  
>> that don't support this feature.
>
> I agree with the use of the timer as a conversion clock.
>
> 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.
>
> 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.
>
>> 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?
>
> 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).
>
>
>> 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?  Certainly, one is more likely to need  
> this if one wants to know if a burst has completed.
>
>> 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?
>
>>
>> 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.
>
>
> I realize some of these things might extend beyond what could be  
> appropriate for a simple module, but  I think they could be useful  
> just the same.  Also, I think the original functions make sense to  
> get working as an initial implementation before making any of the  
> extensions I'm proposing.
>
> Thoughts?
>
>
> --
> James Snyder
> Biomedical Engineering
> Northwestern University
> jbsnyder at fanplastic.org
> http://fanplastic.org/key.txt
> ph: (847) 644-2322
>
> _______________________________________________
> Elua-dev mailing list
> Elua-dev at lists.berlios.de
> https://lists.berlios.de/mailman/listinfo/elua-dev

--
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/20090114/e79a816b/attachment-0001.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/20090114/e79a816b/attachment-0001.pgp 

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

ADC platform interface proposal

In reply to this post by jbsnyder
> 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.

> 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?

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

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

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

> 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 :)

Best,
Bogdan

> Biomedical Engineering
> Northwestern University
> jbsnyder at fanplastic.org
> http://fanplastic.org/key.txt
> ph: (847) 644-2322
>
> _______________________________________________
> Elua-dev mailing list
> Elua-dev at lists.berlios.de
> https://lists.berlios.de/mailman/listinfo/elua-dev
>
>