Added automatic clock calculation with compile-time checks

This commit is contained in:
BlackMark 2020-02-21 16:32:11 +01:00
parent 629be83f8a
commit 5c3fb99c19

View File

@ -8,20 +8,16 @@ namespace i2c {
#if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega328P__) #if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega328P__)
enum class Prescaler { template <uint32_t Clock>
PRESCALER_1 = 0b00,
PRESCALER_4 = 0b01,
PRESCALER_16 = 0b10,
PRESCALER_64 = 0b11,
};
template <Prescaler Pres, uint32_t Clock>
class Hardware { class Hardware {
public: public:
static void init() static void init()
{ {
TWSR = static_cast<uint8_t>(Pres); constexpr auto clock = calcClock();
TWBR = calcClock(); static_assert(checkClock(clock), "Unable to set requested I2C frequency");
TWSR = clock.prescaler;
TWBR = clock.bitrate;
} }
template <uint8_t Addr> template <uint8_t Addr>
@ -82,9 +78,46 @@ class Hardware {
} }
private: private:
static constexpr uint8_t calcClock() struct ClockCfg {
uint8_t prescaler;
uint8_t bitrate;
};
static constexpr auto calcScl(ClockCfg clk)
{ {
return ((F_CPU / Clock) - 16) / 2; return F_CPU / (16 + 2 * clk.bitrate * clk.prescaler);
}
static constexpr auto checkClock(ClockCfg clk)
{
constexpr auto constexprAbs = [](auto val) {
if (val < 0)
return -val;
return val;
};
constexpr auto CLOCK_EPSILON = 10;
auto scl = calcScl(clk);
if (constexprAbs(static_cast<int>(scl) - static_cast<int>(Clock)) < CLOCK_EPSILON)
return true;
return false;
}
static constexpr auto calcClock()
{
constexpr auto calcBitRate = [](uint8_t prescaler) { return (F_CPU / Clock - 16) / (2 * prescaler); };
auto clk = ClockCfg{1, 0};
for (uint8_t prescaler = 1; prescaler <= 64; prescaler *= 4) {
clk.prescaler = prescaler;
clk.bitrate = calcBitRate(prescaler);
if (checkClock(clk))
break;
}
return clk;
} }
}; };