// 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_