// Copyright 2009 Olivier Gillet. // // Author: Olivier Gillet (ol.gillet@gmail.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 3 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, see . // // ----------------------------------------------------------------------------- // // Interface to the onboard ADC converter, and analog multiplexer. #ifndef AVRLIB_ADC_H_ #define AVRLIB_ADC_H_ #include #include "avrlib/avrlib.h" namespace avrlib { enum AdcReference { ADC_EXTERNAL = 0, ADC_DEFAULT = 1, ADC_INTERNAL = 3 }; enum AdcAlignment { ADC_RIGHT_ALIGNED = 0, ADC_LEFT_ALIGNED = 1 }; IORegister(ADCSRA); typedef BitInRegister AdcConvert; typedef BitInRegister AdcEnabled; class Adc { public: Adc() { } static inline void Init() { set_prescaler(7); // 128 -> 156kHz ADC clock. Enable(); } static inline void set_prescaler(uint8_t factor) { ADCSRA = (ADCSRA & ~0x07) | (factor & 0x07); } static inline void set_reference(AdcReference reference) { admux_value_ = (admux_value_ & 0x3f) | (reference << 6); } static inline void set_alignment(AdcAlignment alignment) { admux_value_ &= 0xc0; if (alignment == ADC_LEFT_ALIGNED) { admux_value_ |= 0x20; } } static inline void Enable() { AdcEnabled::set(); } static inline void Disable() { AdcEnabled::clear(); } static inline int16_t Read(uint8_t pin) { StartConversion(pin); Wait(); return ReadOut(); } static inline void StartConversion(uint8_t pin) { ADMUX = admux_value_ | (pin & 0x07); AdcConvert::set(); } static inline void Wait() { while (AdcConvert::value()); } static bool ready() { return !AdcConvert::value(); } static inline int16_t ReadOut() { uint8_t low = ADCL; uint8_t high = ADCH; return (high << 8) | low; } // Only works when the ADC is left aligned static inline uint8_t ReadOut8() { return ADCH; } private: static uint8_t admux_value_; DISALLOW_COPY_AND_ASSIGN(Adc); }; // Class that cycles through all the analog pins and read their value. Compared // to Adc::Read(), this class is wait-free as it doesn't block on the // ADSC bit value. class AdcInputScanner { public: enum { buffer_size = 0, data_size = 16, }; AdcInputScanner() { } static void Init() { Adc::Init(); current_pin_ = 0; Adc::StartConversion(0); } static inline void set_num_inputs(uint8_t num_inputs) { num_inputs_ = num_inputs; } static inline int16_t Read(uint8_t pin) { return state_[pin]; } static inline uint8_t Read8(uint8_t pin) { return static_cast(state_[pin]) >> 8; } static uint8_t current_pin() { return current_pin_; } static int16_t Sync(uint8_t pin) { Adc::Wait(); int16_t value = Adc::Read(pin); Adc::StartConversion(current_pin_); return value; } static void Scan() { Adc::Wait(); state_[current_pin_] = Adc::ReadOut(); ++current_pin_; if (current_pin_ >= num_inputs_) { current_pin_ = 0; } Adc::StartConversion(current_pin_); } private: static uint8_t current_pin_; static uint8_t num_inputs_; static int16_t state_[8]; DISALLOW_COPY_AND_ASSIGN(AdcInputScanner); }; template struct AnalogInput { enum { buffer_size = 0, data_size = 16, }; static void Init() { } static int16_t Read() { return Adc::Read(pin); } }; template class MuxedAnalogInput { public: enum { buffer_size = 0, data_size = 16, }; static void Init() { } static int16_t Read() { return Adc::Read(current_pin_); } static void set_pin(uint8_t current_pin) { current_pin_ = current_pin; } private: static uint8_t current_pin_; }; /* static */ template uint8_t MuxedAnalogInput::current_pin_ = 0; } // namespace avrlib #endif // AVRLIB_ADC_H_