You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

200 lines
4.4KB

  1. // Copyright 2009 Olivier Gillet.
  2. //
  3. // Author: Olivier Gillet (ol.gillet@gmail.com)
  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 3 of the License, or
  8. // (at your option) any later version.
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. //
  16. // -----------------------------------------------------------------------------
  17. //
  18. // Interface to the onboard ADC converter, and analog multiplexer.
  19. #ifndef AVRLIB_ADC_H_
  20. #define AVRLIB_ADC_H_
  21. #include <avr/io.h>
  22. #include "avrlib/avrlib.h"
  23. namespace avrlib {
  24. enum AdcReference {
  25. ADC_EXTERNAL = 0,
  26. ADC_DEFAULT = 1,
  27. ADC_INTERNAL = 3
  28. };
  29. enum AdcAlignment {
  30. ADC_RIGHT_ALIGNED = 0,
  31. ADC_LEFT_ALIGNED = 1
  32. };
  33. IORegister(ADCSRA);
  34. typedef BitInRegister<ADCSRARegister, ADSC> AdcConvert;
  35. typedef BitInRegister<ADCSRARegister, ADEN> AdcEnabled;
  36. class Adc {
  37. public:
  38. Adc() { }
  39. static inline void Init() {
  40. set_prescaler(7); // 128 -> 156kHz ADC clock.
  41. Enable();
  42. }
  43. static inline void set_prescaler(uint8_t factor) {
  44. ADCSRA = (ADCSRA & ~0x07) | (factor & 0x07);
  45. }
  46. static inline void set_reference(AdcReference reference) {
  47. admux_value_ = (admux_value_ & 0x3f) | (reference << 6);
  48. }
  49. static inline void set_alignment(AdcAlignment alignment) {
  50. admux_value_ &= 0xc0;
  51. if (alignment == ADC_LEFT_ALIGNED) {
  52. admux_value_ |= 0x20;
  53. }
  54. }
  55. static inline void Enable() {
  56. AdcEnabled::set();
  57. }
  58. static inline void Disable() {
  59. AdcEnabled::clear();
  60. }
  61. static inline int16_t Read(uint8_t pin) {
  62. StartConversion(pin);
  63. Wait();
  64. return ReadOut();
  65. }
  66. static inline void StartConversion(uint8_t pin) {
  67. ADMUX = admux_value_ | (pin & 0x07);
  68. AdcConvert::set();
  69. }
  70. static inline void Wait() {
  71. while (AdcConvert::value());
  72. }
  73. static bool ready() {
  74. return !AdcConvert::value();
  75. }
  76. static inline int16_t ReadOut() {
  77. uint8_t low = ADCL;
  78. uint8_t high = ADCH;
  79. return (high << 8) | low;
  80. }
  81. // Only works when the ADC is left aligned
  82. static inline uint8_t ReadOut8() {
  83. return ADCH;
  84. }
  85. private:
  86. static uint8_t admux_value_;
  87. DISALLOW_COPY_AND_ASSIGN(Adc);
  88. };
  89. // Class that cycles through all the analog pins and read their value. Compared
  90. // to Adc::Read(), this class is wait-free as it doesn't block on the
  91. // ADSC bit value.
  92. class AdcInputScanner {
  93. public:
  94. enum {
  95. buffer_size = 0,
  96. data_size = 16,
  97. };
  98. AdcInputScanner() { }
  99. static void Init() {
  100. Adc::Init();
  101. current_pin_ = 0;
  102. Adc::StartConversion(0);
  103. }
  104. static inline void set_num_inputs(uint8_t num_inputs) {
  105. num_inputs_ = num_inputs;
  106. }
  107. static inline int16_t Read(uint8_t pin) {
  108. return state_[pin];
  109. }
  110. static inline uint8_t Read8(uint8_t pin) {
  111. return static_cast<uint16_t>(state_[pin]) >> 8;
  112. }
  113. static uint8_t current_pin() {
  114. return current_pin_;
  115. }
  116. static int16_t Sync(uint8_t pin) {
  117. Adc::Wait();
  118. int16_t value = Adc::Read(pin);
  119. Adc::StartConversion(current_pin_);
  120. return value;
  121. }
  122. static void Scan() {
  123. Adc::Wait();
  124. state_[current_pin_] = Adc::ReadOut();
  125. ++current_pin_;
  126. if (current_pin_ >= num_inputs_) {
  127. current_pin_ = 0;
  128. }
  129. Adc::StartConversion(current_pin_);
  130. }
  131. private:
  132. static uint8_t current_pin_;
  133. static uint8_t num_inputs_;
  134. static int16_t state_[8];
  135. DISALLOW_COPY_AND_ASSIGN(AdcInputScanner);
  136. };
  137. template<int pin>
  138. struct AnalogInput {
  139. enum {
  140. buffer_size = 0,
  141. data_size = 16,
  142. };
  143. static void Init() { }
  144. static int16_t Read() {
  145. return Adc::Read(pin);
  146. }
  147. };
  148. template<int id>
  149. class MuxedAnalogInput {
  150. public:
  151. enum {
  152. buffer_size = 0,
  153. data_size = 16,
  154. };
  155. static void Init() { }
  156. static int16_t Read() {
  157. return Adc::Read(current_pin_);
  158. }
  159. static void set_pin(uint8_t current_pin) {
  160. current_pin_ = current_pin;
  161. }
  162. private:
  163. static uint8_t current_pin_;
  164. };
  165. /* static */
  166. template<int id>
  167. uint8_t MuxedAnalogInput<id>::current_pin_ = 0;
  168. } // namespace avrlib
  169. #endif // AVRLIB_ADC_H_