The built-in analogRead() function blocks until the conversion is ready; for most scenarios this behavior is ok. However, for a high-responsive systems such behavior would be unacceptable.
Another reason I wrote this small non-blocking class is because I bought a lcd-keypad shield, and then I built some improved clones:
The keypad is implemented through an analog ladder, where each push-button is a rung. Tipically any keypad is decoded through a state machine driven inside the system-tick ISR for debounce purposes. That’s way I cannot use the analogRead() function. My class allows me to handle the conversion inside the system-tick ISR without blocking it. Weeee! You might discover other useful applications for a non-blocking conversions. I will glad to hear them!
Source code
My class is totally based upon the analogRead() source code. Moreover, I’ve left the original comments. What I did was to split it out in 3 stages: start, observe and read. Although my goal is to use a templetized class, this version works well for now for most purposes.
// Analog.hpp /*Copyright (C) * 2018 - fjrg76 at hotmail dot com * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #ifndef ANALOG_INC #define ANALOG_INC #include <Arduino.h> #include "wiring_private.h" #include "pins_arduino.h" extern uint8_t analog_reference; class Analog { public: explicit Analog( uint8_t _pin ); void Start(); bool IsReady() const; int Read() const; private: uint8_t pin; }; #endif /* ----- #ifndef ANALOG_INC ----- */
// Analog.cpp #include "Analog.hpp" Analog::Analog( uint8_t _pin ) : pin{ _pin } { pinMode( this->pin, INPUT ); digitalWrite( this->pin, LOW ); #if defined(analogPinToChannel) #if defined(__AVR_ATmega32U4__) if (pin >= 18) pin -= 18; // allow for channel or pin numbers #endif pin = analogPinToChannel(pin); #elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) if (pin >= 54) pin -= 54; // allow for channel or pin numbers #elif defined(__AVR_ATmega32U4__) if (pin >= 18) pin -= 18; // allow for channel or pin numbers #elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) if (pin >= 24) pin -= 24; // allow for channel or pin numbers #else if (pin >= 14) pin -= 14; // allow for channel or pin numbers #endif #if defined(ADCSRB) && defined(MUX5) // the MUX5 bit of ADCSRB selects whether we're reading from channels // 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high). ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5); #endif // set the analog reference (high two bits of ADMUX) and select the // channel (low 4 bits). this also sets ADLAR (left-adjust result) // to 0 (the default). #if defined(ADMUX) #if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) ADMUX = (analog_reference << 4) | (pin & 0x07); #else ADMUX = (analog_reference << 6) | (pin & 0x07); #endif #endif } void Analog::Start() { // without a delay, we seem to read from the wrong channel //delay(1); #if defined(ADCSRA) && defined(ADCL) // start the conversion sbi(ADCSRA, ADSC); #endif } bool Analog::IsReady() const { #if defined(ADCSRA) && defined(ADCL) // ADSC is cleared when the conversion finishes return !bit_is_set( ADCSRA, ADSC ); #else // we don't want to get stuck return true; #endif } int Analog::Read() const { uint8_t low, high; #if defined(ADCSRA) && defined(ADCL) // we have to read ADCL first; doing so locks both ADCL // and ADCH until ADCH is read. reading ADCL second would // cause the results of each conversion to be discarded, // as ADCL and ADCH would be locked when it completed. low = ADCL; high = ADCH; #else // we dont have an ADC, return 0 low = 0; high = 0; #endif // combine the two bytes return (high << 8) | low; }
Example
Just a little example:
#include <Arduino.h> #include "Analog.h" int main(void) { init(); Analog a0( A0 ); while( 1 ) { a0.Start(); while( not a0.IsReady() ); auto read = a0.Read(); // do something with the reading } }
Greetings!
[…] the Arduino’s code in order to break the ADC code into 3 parts. You might want to read that entry in my alternative blog. But this is not a general purpose solution […]
Me gustaMe gusta