Compare commits

...

5 Commits

3 changed files with 291 additions and 6 deletions

208
adc.hpp
View File

@@ -1,21 +1,219 @@
#pragma once
#ifndef ADC_HPP
#define ADC_HPP
#include "config.hpp"
#include "hardware.hpp"
#include <stdint.h>
#include "../io/io.hpp"
namespace adc {
template <typename... T>
struct Config {
namespace detail {
extern void (*fnAdcIntHandler)(uint16_t);
using reg_ptr_t = volatile uint8_t *;
template <uintptr_t Address>
static inline reg_ptr_t getRegPtr()
{
return reinterpret_cast<reg_ptr_t>(Address);
}
template <typename Cfg>
class AdcImpl {
public:
static uint16_t read()
{
*getRegPtr<Registers::CTRL_STAT_A_ADDR>() |= 1 << ControlFlagsA::START_CONV;
while (*getRegPtr<Registers::CTRL_STAT_A_ADDR>() & (1 << ControlFlagsA::START_CONV))
;
uint16_t adcSample = *getRegPtr<Registers::DATA_L_ADDR>();
adcSample |= *getRegPtr<Registers::DATA_H_ADDR>() << 8;
return adcSample;
}
protected:
static void init(uint8_t muxVal)
{
*getRegPtr<Registers::MUX_SEL_ADDR>() = muxVal | calcRef();
auto ctrlStatA = calcCtrlStatA(detail::fnAdcIntHandler != nullptr);
*getRegPtr<Registers::CTRL_STAT_A_ADDR>() = ctrlStatA;
constexpr auto ctrlStatB = calcCtrlStatB();
*getRegPtr<Registers::CTRL_STAT_B_ADDR>() = ctrlStatB;
}
static constexpr auto calcRef()
{
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;
}
static constexpr uint8_t calcPrescaler()
{
constexpr auto validPrescaler = Cfg::PRESCALER == 2 || Cfg::PRESCALER == 4 || Cfg::PRESCALER == 8 ||
Cfg::PRESCALER == 16 || Cfg::PRESCALER == 32 || Cfg::PRESCALER == 64 ||
Cfg::PRESCALER == 128;
static_assert(validPrescaler, "Invalid prescaler");
// clang-format off
switch (Cfg::PRESCALER) {
case 2: return 1;
case 4: return 2;
case 8: return 3;
case 16: return 4;
case 32: return 5;
case 64: return 6;
case 128: return 7;
}
// clang-format on
}
static auto calcCtrlStatA(bool interruptEnable)
{
uint8_t ctrlStatA = 1 << ControlFlagsA::ENABLE;
if constexpr (Cfg::MODE == Mode::AUTO) {
ctrlStatA |= 1 << ControlFlagsA::AUTO_TRIGGER;
} else if (interruptEnable) {
ctrlStatA |= 1 << ControlFlagsA::CONV_COMPLETE_INT_ENABLE;
}
return ctrlStatA | calcPrescaler();
}
static constexpr uint8_t calcCtrlStatB()
{
if constexpr (Cfg::MODE == Mode::AUTO) {
// clang-format off
switch (Cfg::TRIGGER_SRC) {
case TriggerSource::FREE_RUNNING: return 0;
case TriggerSource::ANALOG_COMP: return 1;
case TriggerSource::EXTERNAL_INT_0: return 2;
case TriggerSource::TIMER0_COMP_A: return 3;
case TriggerSource::TIMER0_OVERFLOW: return 4;
case TriggerSource::TIMER1_COMP_B: return 5;
case TriggerSource::TIMER1_OVERFLOW: return 6;
case TriggerSource::TIMER1_CAPTURE: return 7;
}
// clang-format on
}
return 0;
}
};
} // 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>
class Adc {
class Adc<Cfg, io::P, pin> : public detail::AdcImpl<Cfg> {
using callback_t = void (*)(uint16_t);
public:
void read() {}
static_assert(detail::supports_adc_v<pin>, "Pin does not support ADC");
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);
}
};
template <typename Cfg, InputSource src>
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
}
};
} // namespace adc
#endif
//////////////////////////////////////////////////////////////////////////
#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);
}
}
#else
#error "This chip is not supported"
#endif
} // namespace detail
} // namespace adc
#undef ADC_INT_VECTORS
#endif

73
config.hpp Normal file
View File

@@ -0,0 +1,73 @@
#pragma once
#include <stdint.h>
namespace adc {
namespace detail {
enum class Mode {
SINGLE,
AUTO,
FREE_RUNNING,
};
} // namespace detail
enum class TriggerSource {
FREE_RUNNING,
ANALOG_COMP,
EXTERNAL_INT_0,
TIMER0_COMP_A,
TIMER0_OVERFLOW,
TIMER1_COMP_B,
TIMER1_OVERFLOW,
TIMER1_CAPTURE,
};
template <TriggerSource src = TriggerSource::FREE_RUNNING>
struct AutoMode {
static constexpr auto SRC = src;
};
struct FreeRunningMode {
};
struct SingleMode {
};
enum class VoltageRef {
EXTERNAL,
AVCC,
INTERNAL,
};
enum class InputSource {
TEMP,
VBG,
GND,
};
template <class Mode, VoltageRef vref = VoltageRef::AVCC, uint8_t prescaler = 128>
struct Config {
static constexpr auto MODE = detail::Mode::AUTO;
static constexpr auto TRIGGER_SRC = Mode::SRC;
static constexpr auto VREF = vref;
static constexpr auto PRESCALER = prescaler;
};
template <VoltageRef vref, uint8_t prescaler>
struct Config<FreeRunningMode, vref, prescaler> {
static constexpr auto MODE = detail::Mode::FREE_RUNNING;
static constexpr auto VREF = vref;
static constexpr auto PRESCALER = prescaler;
};
template <VoltageRef vref, uint8_t prescaler>
struct Config<SingleMode, vref, prescaler> {
static constexpr auto MODE = detail::Mode::SINGLE;
static constexpr auto VREF = vref;
static constexpr auto PRESCALER = prescaler;
};
} // namespace adc

View File

@@ -2,7 +2,7 @@
#include <stdint.h>
#include <avr/io.h>
#include "../io/io.hpp"
namespace adc {
@@ -70,6 +70,20 @@ enum class ControlFlagsDigInDis {
DIGITAL_INPUT_DISABLE_5 = ADC5D,
};
// clang-format off
constexpr int operator<<(const int &lhs, const ControlFlagsMUX &rhs) { return lhs << static_cast<int>(rhs); }
constexpr int operator<<(const int &lhs, const ControlFlagsA &rhs) { return lhs << static_cast<int>(rhs); }
constexpr int operator<<(const int &lhs, const ControlFlagsB &rhs) { return lhs << static_cast<int>(rhs); }
// clang-format on
template <io::P pin>
struct supports_adc {
static constexpr auto value = (io::detail::getBus(pin) == io::Bus::C) ? true : false;
};
template <io::P pin>
constexpr auto supports_adc_v = supports_adc<pin>::value;
#pragma pop_macro("_MMIO_BYTE")
#else