2020-02-21 20:38:32 +01:00
|
|
|
#ifndef ADC_HPP
|
|
|
|
#define ADC_HPP
|
2020-02-01 23:41:18 +01:00
|
|
|
|
2020-02-21 19:08:47 +01:00
|
|
|
#include "config.hpp"
|
2020-02-01 23:41:18 +01:00
|
|
|
#include "hardware.hpp"
|
|
|
|
|
2020-02-21 19:08:47 +01:00
|
|
|
#include <stdint.h>
|
|
|
|
|
2020-02-01 23:41:18 +01:00
|
|
|
#include "../io/io.hpp"
|
|
|
|
|
|
|
|
namespace adc {
|
|
|
|
|
2020-02-21 20:38:32 +01:00
|
|
|
namespace detail {
|
2020-02-01 23:41:18 +01:00
|
|
|
|
2020-02-21 20:38:32 +01:00
|
|
|
extern void (*fnAdcIntHandler)(uint16_t);
|
|
|
|
|
2020-02-21 21:24:10 +01:00
|
|
|
using reg_ptr_t = volatile uint8_t *;
|
2020-02-21 19:08:47 +01:00
|
|
|
|
2020-02-21 21:24:10 +01:00
|
|
|
template <uintptr_t Address>
|
|
|
|
static inline reg_ptr_t getRegPtr()
|
|
|
|
{
|
|
|
|
return reinterpret_cast<reg_ptr_t>(Address);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Cfg>
|
|
|
|
class AdcImpl {
|
2020-02-01 23:41:18 +01:00
|
|
|
public:
|
2020-02-21 21:24:10 +01:00
|
|
|
static uint16_t read()
|
2020-02-21 20:38:32 +01:00
|
|
|
{
|
2020-02-21 21:24:10 +01:00
|
|
|
return 0;
|
2020-02-21 20:38:32 +01:00
|
|
|
}
|
2020-02-21 19:08:47 +01:00
|
|
|
|
2020-02-21 21:24:10 +01:00
|
|
|
protected:
|
|
|
|
static void init(uint8_t muxVal)
|
|
|
|
{
|
|
|
|
*getRegPtr<Registers::MUX_SEL_ADDR>() = muxVal | calcRef();
|
|
|
|
}
|
2020-02-21 19:08:47 +01:00
|
|
|
|
2020-02-21 21:24:10 +01:00
|
|
|
static constexpr auto calcRef()
|
2020-02-21 19:08:47 +01:00
|
|
|
{
|
2020-02-21 21:24:10 +01:00
|
|
|
uint8_t muxVal = 0;
|
|
|
|
|
|
|
|
if constexpr (Cfg::VREF == VoltageRef::AVCC) {
|
|
|
|
muxVal |= 1 << ControlFlagsMUX::REF_SEL_0;
|
|
|
|
} else if constexpr (Cfg::VREF == VoltageRef::INTERNAL) {
|
|
|
|
muxVal |= (1 << ControlFlagsMUX::REF_SEL_0) | (1 << ControlFlagsMUX::REF_SEL_1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return muxVal;
|
2020-02-21 19:08:47 +01:00
|
|
|
}
|
2020-02-21 20:38:32 +01:00
|
|
|
};
|
2020-02-21 19:08:47 +01:00
|
|
|
|
2020-02-21 20:38:32 +01:00
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
template <typename Cfg, typename Input, Input src>
|
|
|
|
class Adc {
|
|
|
|
public:
|
|
|
|
static_assert(sizeof(Input) == -1, "Invalid input source selected");
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename Cfg, io::P pin>
|
2020-02-21 21:24:10 +01:00
|
|
|
class Adc<Cfg, io::P, pin> : public detail::AdcImpl<Cfg> {
|
|
|
|
using callback_t = void (*)(uint16_t);
|
|
|
|
|
2020-02-21 20:38:32 +01:00
|
|
|
public:
|
|
|
|
static_assert(detail::supports_adc_v<pin>, "Pin does not support ADC");
|
2020-02-21 21:24:10 +01:00
|
|
|
|
|
|
|
static void init(callback_t callback)
|
|
|
|
{
|
|
|
|
detail::fnAdcIntHandler = callback;
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void init()
|
|
|
|
{
|
|
|
|
constexpr auto muxVal = calcChannel();
|
|
|
|
detail::AdcImpl<Cfg>::init(muxVal);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
static constexpr auto calcChannel()
|
|
|
|
{
|
|
|
|
return static_cast<uint8_t>(pin) - static_cast<uint8_t>(io::P::C0);
|
|
|
|
}
|
2020-02-21 19:08:47 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
template <typename Cfg, InputSource src>
|
2020-02-21 21:24:10 +01:00
|
|
|
class Adc<Cfg, InputSource, src> : public detail::AdcImpl<Cfg> {
|
|
|
|
using callback_t = void (*)(uint16_t);
|
|
|
|
|
|
|
|
public:
|
|
|
|
static void init(callback_t callback)
|
|
|
|
{
|
|
|
|
detail::fnAdcIntHandler = callback;
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void init()
|
|
|
|
{
|
|
|
|
constexpr auto muxVal = calcChannel();
|
|
|
|
detail::AdcImpl<Cfg>::init(muxVal);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
static constexpr auto calcChannel()
|
|
|
|
{
|
|
|
|
using mx = detail::ControlFlagsMUX;
|
|
|
|
// clang-format off
|
|
|
|
switch (src) {
|
|
|
|
case InputSource::TEMP: return 1 << mx::CHANNEL_SEL_3;
|
|
|
|
case InputSource::VBG: return (1 << mx::CHANNEL_SEL_3) | (1 << mx::CHANNEL_SEL_2) | (1 << mx::CHANNEL_SEL_1);
|
|
|
|
case InputSource::GND: return (1 << mx::CHANNEL_SEL_3) | (1 << mx::CHANNEL_SEL_2) | (1 << mx::CHANNEL_SEL_1) | (1 << mx::CHANNEL_SEL_0);
|
|
|
|
}
|
|
|
|
// clang-format on
|
|
|
|
}
|
2020-02-21 20:38:32 +01:00
|
|
|
};
|
2020-02-21 19:08:47 +01:00
|
|
|
|
2020-02-21 20:38:32 +01:00
|
|
|
} // namespace adc
|
2020-02-21 19:08:47 +01:00
|
|
|
|
2020-02-21 20:38:32 +01:00
|
|
|
#endif
|
2020-02-21 19:08:47 +01:00
|
|
|
|
2020-02-21 20:38:32 +01:00
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#ifdef ADC_INT_VECTOR
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
|
|
#include <avr/interrupt.h>
|
|
|
|
|
|
|
|
namespace adc {
|
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
#if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega328P__)
|
|
|
|
|
|
|
|
void (*fnAdcIntHandler)(uint16_t) = nullptr;
|
|
|
|
|
|
|
|
ISR(ADC_vect)
|
|
|
|
{
|
|
|
|
if (fnAdcIntHandler) {
|
|
|
|
const auto adcSample = *getRegPtr<Registers::DATA_L_ADDR>() | (*getRegPtr<Registers::DATA_H_ADDR>() << 8);
|
|
|
|
fnAdcIntHandler(adcSample);
|
2020-02-21 19:08:47 +01:00
|
|
|
}
|
2020-02-21 20:38:32 +01:00
|
|
|
}
|
2020-02-01 23:41:18 +01:00
|
|
|
|
2020-02-21 20:38:32 +01:00
|
|
|
#else
|
|
|
|
#error "This chip is not supported"
|
|
|
|
#endif
|
2020-02-01 23:41:18 +01:00
|
|
|
|
2020-02-21 20:38:32 +01:00
|
|
|
} // namespace detail
|
2020-02-01 23:41:18 +01:00
|
|
|
} // namespace adc
|
2020-02-21 20:38:32 +01:00
|
|
|
|
|
|
|
#undef ADC_INT_VECTORS
|
|
|
|
|
|
|
|
#endif
|