/*! * \file /utl/com/i2c.h * \brief An Abstract base class interface for the i2c bus * * Copyright (C) 2018 Christos Choutouridis * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . * */ #ifndef _utl_com_i2c_h__ #define _utl_com_i2c_h__ #include #include #include namespace utl { /*! * \ingroup Communication * \brief Abstract base class for i2c bus */ //!@{ /*! * \brief * this class force a common interface for I2C communication * protocol implementations using CRTP * \param impl_t The CRTP type (the derived/implementation class typename). */ template class i2c_i { _CRTP_IMPL(impl_t); public: using type = i2c_i; //!< Export type as identity meta-function //! I2C transmit/receive sequence enum class Sequence { BYTE =0, //!< Only read/write byte [8 clocks] ACK, //!< Only send/receive ack [1 clock] BYTEnACK //!< Read/Write byte and ack [9 clocks] }; /*! * \name Object lifetime */ //!@{ protected: i2c_i () = default; //!< Allow constructor from derived only ~i2c_i () = default; //!< Allow destructor from derived only i2c_i (const type&) = delete; //!< No copies type& operator= (const type&) = delete; //!@} /*! * \name Implementation requirements * \note * In order for the implementation to have the following as private members * it also need to declare this class as friend */ //!@{ private: uint32_t _clock () const { return impl()._clock (); } //!< clock frequency of the bus [Hz] void _clock (uint32_t c) { impl()._clock (c); } //!< set clock frequency of the bus [Hz] void _start() { impl()._start (); } //!< Send start functionality void _stop() { impl()._stop (); } //!< Send stop functionality byte_t _rx_data (bool ack, Sequence seq) { return impl()._rx_data (ack, seq); } bool _tx_data (byte_t byte, Sequence seq) { return impl()._tx_data (byte, seq); } //!@} /*! * \name Get/Set functions */ //!@{ public: uint32_t clock () const { return _clock (); } //!< \return clock frequency of the bus void clock (uint32_t f) { _clock (f); } //!< \brief set clock frequency of the bus //!@} /*! * \name User functions */ //!@{ public: void start() { _start (); } //!< Send start functionality void stop () { _stop (); } //!< Send stop functionality /*! * \brief * Receive a byte from the i2c bus. * \param ack Optional ack bit. * \arg 1 ACK the reception * \arg 0 Don't ACK the reception. * \param seq The operation sequence to execute * \arg Sequence::BYTE Receive only the byte, do not send ack clock * \arg Sequence::ACK Send only the ack bit * \arg Sequence::BYTEnACK Receive the byte and send the ack bit * \return The byte received. */ byte_t rx_data (bool ack, Sequence seq =Sequence::BYTEnACK) { return _rx_data (ack, seq); } /*! * \brief * Transmit a byte to the i2c bus. * \param byte The byte to send. * \param seq The operation sequence to execute * \arg Sequence::BYTE Transmit only the byte, do not read ack bit * \arg Sequence::ACK Read only the ack bit * \arg Sequence::BYTEnACK Transmit the byte and read the ack bit * \return Slave's ACK bit * \arg false Slave didn't ACK * \arg true Slave did ACK */ bool tx_data (byte_t byte, Sequence seq =Sequence::BYTEnACK) { return _tx_data (byte, seq); } //!@} }; /*! * \brief * A virtual base class specialization * \param impl_t = virtual_tag */ template<> class i2c_i { public: using type = i2c_i ; //!< Export type as identity meta-function //! I2C transmit/receive sequence enum class Sequence { BYTE=0, //!< Only read/write byte [8 clocks] ACK, //!< Only send/receive ack [1 clock] BYTEnACK //!< Read/Write byte and ack [9 clocks] }; /*! * \name Object lifetime */ //!@{ protected: i2c_i () = default; //!< Allow constructor from derived only i2c_i (const type&) = delete; //!< No copies type& operator= (const type&) = delete; public: virtual ~i2c_i () = default; //!< Virtual default destructor //!@} /*! * \name Implementation requirements */ //!@{ private: virtual uint32_t _clock () const = 0; //!< \return clock frequency of the bus [Hz] virtual void _clock (uint32_t) = 0; //!< \brief set clock frequency of the bus [Hz] virtual void _start() = 0; //!< Send start functionality virtual void _stop() = 0; //!< Send stop functionality virtual byte_t _rx_data (bool ack, Sequence seq) = 0; //!< Receive a byte from the i2c bus. virtual bool _tx_data (byte_t byte, Sequence seq) = 0; //!< Transmit a byte to the i2c bus. //!@} /*! * \name Get/Set functions */ //!@{ public: uint32_t clock () const { return _clock(); } //!< \return clock frequency of the bus [Hz] void clock (uint32_t c) { _clock(c); } //!< \brief set clock frequency of the bus [Hz] //!@} /*! * \name User functions */ //!@{ public: void start () { _start(); } void stop () { _stop(); } /*! * \brief * Receive a byte from the i2c bus. * \param ack Optional ack bit. * \arg 1 ACK the reception * \arg 0 Don't ACK the reception. * \param seq The operation sequence to execute * \arg Sequence::BYTE Receive only the byte, do not send ack clock * \arg Sequence::ACK Send only the ack bit * \arg Sequence::BYTEnACK Receive the byte and send the ack bit * \return The byte received. */ byte_t rx_data (bool ack, Sequence seq =Sequence::BYTEnACK) { return _rx_data (ack, seq); } /*! * \brief * Transmit a byte to the i2c bus. * \param byte The byte to send. * \param seq The operation sequence to execute * \arg Sequence::BYTE Transmit only the byte, do not read ack bit * \arg Sequence::ACK Read only the ack bit * \arg Sequence::BYTEnACK Transmit the byte and read the ack bit * \return Slave's ACK bit * \arg false Slave didn't ACK * \arg true Slave did ACK */ bool tx_data (byte_t byte, Sequence seq =Sequence::BYTEnACK) { return _tx_data (byte, seq); } //!@} }; #if defined _utl_have_concepts /*! * i2c interface concept */ template concept bool I2c_i = requires (T t, const T ct, typename T::Sequence s) { // Object type requires not_::value>::value; requires not_::value>::value; // Methods {ct.clock()} -> uint32_t; {t.clock(0)} -> void; {t.start()} -> void; {t.stop()} -> void; {t.rx_data (1, s)} -> byte_t; {t.tx_data (0, s)} -> bool; }; #else namespace i2c_i_details { using std::declval; template using try_cclk_t = decltype (declval().clock()); template using try_clk_t = decltype (declval<_Tp>().clock(declval())); template using try_start_t = decltype (declval<_Tp>().start()); template using try_stop_t = decltype (declval<_Tp>().stop()); template using try_rx_data_t = decltype (declval<_Tp>().rx_data (declval(), declval())); template using try_tx_data_t = decltype (declval<_Tp>().tx_data (declval(), declval())); //! Primary template to catch any non I2C interface types template struct is_i2c_ : meta::false_ { }; //! template to catch a proper I2C interface type template struct is_i2c_ <_Tp, meta::void_t < typename _Tp::Sequence, meta::use_if_same_t >, meta::use_if_same_t >, meta::use_if_same_t >, meta::use_if_same_t >, meta::use_if_same_t >, meta::use_if_same_t > > > : meta::true_ { }; } /*! * Value meta-programming function alias for I2C interface checking * \param _Tp Type to check * \return True if _Tp is a i2c interface */ template constexpr bool I2c_i = i2c_i_details::is_i2c_<_Tp>::value; #endif //!@} } // namespace utl #endif // _utl_com_i2c_h__