Improved Analog Input API (reqest for comments)

PaulStoffregen

Well-known member
Posted today to the Arduino developers mail list, and reposting so we can discuss it here, if anyone's interested?

------------------------------------

Sometime next year I'm planning to implement an improved analog input API for Teensy. If possible, I'd like to build consensus on the API, to (hopefully) someday bring these improvements to all Arduino users. I also want to maximize compatibility across all Arduino compatible boards, if a well defined API can be agreed upon?

At this early stage, I really only have a rough wish-list, not a detailed proposal. Here are some of the things I want to accomplish:

1: Non-blocking functions: Functions similar to Serial.available() or serialEvent() can receive data without waiting. Or perhaps new functions are better than following the familiar Stream API? Or maybe the Stream base class ought to be extended slightly? Or perhaps a new base class for multi-byte streams or very specifically focused on analog input should be added? If the core library provides a capable base class, maybe it could be useful for libraries to support external ADC chips?

2: Low-jitter sampling: Both single-shot triggering and automatic sampling are needed. For many applications, the timing accuracy of sampling is as important as the analog measurement accuracy. With analogRead(), achieving low jitter timing is nearly impossible, even for experts. The API should make setting up automatic repetitive measurements simple and easy for novices.

3: Input sequencing: For sampling, often users need to read more than one input. The setup function for sampling should provide a way to specify a list of inputs to read. Repeating an input in the list should be legal, so certain inputs can be sampled more regularly than others. For receiving the data with the usual Stream model, perhaps a 3 function could work, Analog.available(), Analog.channel(), Analog.read()? Or maybe the available() function could report which channel is the next that read() will return, but somehow also work with the zero-based numbering scheme? For event-based reception, passing the channel number to the event function seems pretty simple. Another minor question involves whether to use the A0-A15 numbers (which actually correspond to the digital pin numbers), or to use zero-based numbers in the API?

4: Multi-ADC capable: Today, most microcontrollers have a single ADC with an analog mux. But multi-ADC chips are already entering the market and are likely to become common over the next 10 years. Ideally, the API should "just work" with these more capable chips. I honestly haven't thought much about how this ought to work.

5: Handling different resolutions: I know this is a thorny issue. Arduino already has analogReadResolution, which defaults to 10 bits. Perhaps it's best to just stick with that? Or maybe always left-justifying data to some high res is best for long-term use? Even left-justified, should the return type be able to accommodate more than 16 bits? Some microcontrollers available today from Analog Devices have > 16 bit delta-sigma ADCs. Certainly the return should be signed, since many chips, even some AVRs, support differential inputs. Maybe the return should be "int" that defaults to 16 bits on AVR and 32 bits on ARM? (locking AVR out of higher than 16 res, but also making the code more efficient...)

Did I miss important features?

I know this is a large and complex topics. Even relatively simple API stuff tends to result in huge conversations on this mail list, so please try to keep replies on-topic?

Ultimately, I want to bring more analog input capabilities to as many users as possible with the sort of easy-to-use API everyone expects from Arduino. My hope is to build consensus, so long-term all users can benefit from a more capable API that's highly compatible across many boards.
 
Paul:

I think resolution handling is pretty simple and effective now, using the analogReadResolution() function. I really don't care if it's signed or integer, I can work with either if I know what it is, although some DSP commands might expect signed. Triggering: The ADC needs to have ability for lots of trigger modes, external DIO, internal time based, maybe even trigger when chanX is finished, for fast sequential sampling.
It would be good for the ADC to have the ability to sample a lot of inputs in some order, based on maybe some state machine, and then save results to memory for later processing.
It would be nice also for routines for external ADC's, especially multiple channels, so that simultaneous sampling, like what is necessary for power measurement, can be done. Totally agree with number 2, for RMS voltage and current measurements, it's crucial to have high repeatability, and very good timing accuracy.

I'm fine with A0-A15 pin assignments, it works and it's documented, so why change?

Just My 2 rubles...
Tim
 
Your efforts are to be commended, even if they are likely meant to be limited to internal ADCs found inside the Teensy series. Never mind the mental hooliganism I propose further below...

Regular sampling is a great improvement goal. Besides tying the ADC trigger to something like the PIT to sample regularly, it would be great if the ADC could be configured to dump samples automatically into a ring buffer and then to move on to the next sample with the kind of auto-channel sequencing you envisioned. Allowing the user to designate easy differential channel measurement on the Teensy 3 would be awesome too.

A different designation re: pins may make sense, however, just to make it easier to integrate pin functions into logical loops. I would be happier with a channel designation like ADC[0], ADC[1], etc. for channel selections rather than A0, A1, and so on. Makes integration into logic functions as simple as they are on the Digital side. (also makes more sense to me to treat the ADC input as an array).

However, a even bigger prize could be a flexible framework to address the plethora of external ADCs, i.e. a framework that allows external ADCs to be declared the way Atmel chips are in the pins config file, allowing for much greater abstraction re: settings, communication, etc. with an external ADC. For example, the framework should handle all SPI and I2C communications, be able to handle interrupts, clock triggering on the ADC, and so on.

The end-user should simply declare which Teensy pins are in use by what pins on the external chip and the framework takes over. That makes for a very flexible framework requirement (think PGA settings, offset and gain corrections, sampling speeds, channels, and so on)!!! Hopefully, more advanced users could then contribute to your library settings for various external ADCs. Ideally, the library framework would be smart enough to handle ADCs from the primitive (i.e. ADC122s625 to sophisticated (multi-channel chips like the MCP3909). But it would be a huge prize to come up with a framework that allows all these chips to be addressed in a more coherent manner.
 
My hope is this API could be used for most external chips. Whether it's a base class with pure virtual functions that force inherited classes to conform, or if libraries for external chips simply use the same function names and args by convention is one of many API questions to resolve.

Very little of the coding for the internal ADC will apply to external chips, so it remains to be seen if library authors for those chip will use this API. Often those libraries are a bottom-up design, with a variety of functions inspired by the functionality and sometimes even the names mentioned in the chip's datasheet. My hope in this work is to take a top-down approach, starting with an API that is designed to be both easy to use but provides a lot of capability. Then the implementation can map the actual ADC to that API.
 
Back
Top