atMETEO
An ATmega based weather station
adc.h
Go to the documentation of this file.
1 /*
2  * atMETEO - An ATmega based weather station
3  * Copyright (C) 2014-2015 Christian Fetzer
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #pragma once
21 
36 #include <inttypes.h> // AVR toolchain doesn't offer cinttypes header
37 
38 #include <avr/io.h>
39 
40 #ifndef F_CPU
41 #error "F_CPU not defined for adc.h"
42 #endif
43 
44 #ifndef VCC
45 #error "VCC not defined for adc.h"
46 #endif
47 
48 namespace Avr
49 {
50 
65 class Adc
66 {
67 public:
73  static Adc& instance()
74  {
75  return s_instance;
76  }
77 
85  uint16_t read(uint8_t channel, uint8_t nsamples = 1)
86  {
87  uint32_t sum = 0;
88  for (uint8_t n = 0; n < nsamples; n++) {
89  sum += readAdc(channel);
90  }
91  return (uint16_t)(sum / nsamples);
92  }
93 
101  uint16_t readMilliVolts(uint8_t channel, uint8_t nsamples = 1)
102  {
103  return toMillivolts(read(channel, nsamples));
104  }
105 
113  float readVolts(uint8_t channel, uint8_t nsamples = 1)
114  {
115  return toVolts(read(channel, nsamples));
116  }
117 
118 private:
119  static Adc s_instance;
120 
121  Adc()
122  {
123  // Use Vcc as reference voltage
124  ADMUX = (1 << REFS0);
125 
126  // Set ADC prescaler
127  ADCSRA |= prescaler();
128 
129  // Enable ADC (single conversion)
130  ADCSRA |= (1 << ADEN);
131 
132  // Dummy readout
133  read(0);
134  }
135 
136  uint16_t readAdc(uint8_t channel)
137  {
138  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
139  ADCSRA |= (1 << ADSC);
140  while (ADCSRA & (1 << ADSC));
141  return ADCW;
142  }
143 
144  static uint16_t toMillivolts(uint16_t adc)
145  {
146  uint16_t result = (static_cast<uint32_t>(VCC) * adc) / 1024;
147  return Sensors::min(result, VCC);
148  }
149 
150  static float toVolts(uint16_t adc)
151  {
152  float result = (static_cast<uint32_t>(VCC) * adc) / 1024000.0F;
153  return Sensors::min(result, static_cast<float>(VCC));
154  }
155 
156  template <uint16_t prescaler>
157  static constexpr uint8_t adcPrescaler()
158  {
159  static_assert(prescaler == 2 || prescaler == 4 || prescaler == 8 ||
160  prescaler == 16 || prescaler == 32 || prescaler == 64 ||
161  prescaler == 128,
162  "Invalid prescaler (2, 4, 8, 16, 32, 64, 128)");
163  return prescaler == 2 ? (1 << ADPS0) :
164  prescaler == 4 ? (1 << ADPS1) :
165  prescaler == 8 ? (1 << ADPS1) | (1 << ADPS0) :
166  prescaler == 16 ? (1 << ADPS2) :
167  prescaler == 32 ? (1 << ADPS2) | (1 << ADPS0) :
168  prescaler == 64 ? (1 << ADPS2) | (1 << ADPS1) :
169  prescaler == 128 ? (1 << ADPS2) | (1 << ADPS1) |
170  (1 << ADPS0) : 0;
171  }
172 
173  template <uint32_t min, uint32_t max, uint8_t prescaler>
174  static constexpr bool checkPrescaler()
175  {
176  return (F_CPU / prescaler) <= max &&
177  (F_CPU / prescaler) >= min ? true : false;
178  }
179 
180  template <uint32_t min, uint32_t max>
181  static constexpr uint8_t calcPrescaler()
182  {
183  return checkPrescaler<min, max, 2>() ? 2 :
184  checkPrescaler<min, max, 4>() ? 4 :
185  checkPrescaler<min, max, 8>() ? 8 :
186  checkPrescaler<min, max, 16>() ? 16 :
187  checkPrescaler<min, max, 32>() ? 32 :
188  checkPrescaler<min, max, 64>() ? 64 :
189  checkPrescaler<min, max, 128>() ? 128 : 0;
190  }
191 
192  static constexpr uint8_t prescaler()
193  {
194  return adcPrescaler<calcPrescaler<50000, 200000>()>();
195  }
196 };
197 
198 Adc Adc::s_instance = Adc();
199  // \addtogroup libtarget_adc
201 
202 } // namespace Avr
uint16_t read(uint8_t channel, uint8_t nsamples=1)
Reads the (average) raw ADC value (in Single Conversion mode) for the given channel.
Definition: adc.h:85
static Adc & instance()
Returns the Avr::Adc instance.
Definition: adc.h:73
Namespace containing all symbols of the AVR C++ utilities library.
Definition: adc.h:48
T min(T a, T b)
Returns the minimum of the two values a and b.
Definition: utils.h:188
float readVolts(uint8_t channel, uint8_t nsamples=1)
Reads the (average) ADC voltage (in Single Conversion mode) for the given channel.
Definition: adc.h:113
T max(T a, T b)
Returns the maximum of the two values a and b.
Definition: utils.h:201
uint16_t readMilliVolts(uint8_t channel, uint8_t nsamples=1)
Reads the (average) ADC voltage (in Single Conversion mode) for the given channel.
Definition: adc.h:101
A C++ wrapper for accessing the built-in 10 bit Analog to Digital Conversion (ADC) facilities...
Definition: adc.h:65