/*! * \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 */ //!@{ //! uint32_t _frequency () const; < clock frequency of the bus [Hz] //! void _frequency (uint32_t); < set clock frequency of the bus [Hz] //! void _start(); < Send start functionality //! void _stop(); < Send stop functionality //! byte_t _read (bool ack, Sequence seq); < Receive a byte from the i2c bus. //! bool _write (byte_t byte, Sequence seq);< Transmit a byte to the i2c bus. //!@} /*! * \name Get/Set functions */ //!@{ public: uint32_t frequency () const { return impl()._frequency (); } //!< \return clock frequency of the bus void frequency (uint32_t f) { impl()._frequency (f); } //!< \brief set clock frequency of the bus //!@} /*! * \name User functions */ //!@{ public: void start() { impl()._start (); } //!< Send start functionality void stop () { impl()._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 read (bool ack, Sequence seq =Sequence::BYTEnACK) { return impl()._read (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 write (byte_t byte, Sequence seq =Sequence::BYTEnACK) { return impl()._write (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 _frequency () const = 0; //!< \return clock frequency of the bus [Hz] virtual void _frequency (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 _read (bool ack, Sequence seq) = 0; //!< Receive a byte from the i2c bus. virtual bool _write (byte_t byte, Sequence seq) = 0; //!< Transmit a byte to the i2c bus. //!@} /*! * \name Get/Set functions */ //!@{ public: uint32_t frequency () const { return _frequency(); } //!< \return clock frequency of the bus [Hz] void frequency (uint32_t f) { _frequency(f); } //!< \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 read (bool ack, Sequence seq =Sequence::BYTEnACK) { return _read (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 write (byte_t byte, Sequence seq =Sequence::BYTEnACK) { return _write (byte, seq); } //!@} }; #if defined _utl_have_concepts /*! * i2c interface concept */ template concept bool i2c_c = requires (T t, const T ct, typename T::Sequence s) { // Object type requires not_::value>::value; requires not_::value>::value; // Methods {ct.frequency()} -> uint32_t; {t.frequency(0)} -> void; {t.start()} -> void; {t.stop()} -> void; {t.read (1, s)} -> byte_t; {t.write(0, s)} -> bool; }; #else namespace i2c_i_cnpt { using std::declval; template using try_cfreq_t = decltype (declval().frequency()); template using try_freq_t = decltype (declval<_Tp>().frequency(declval())); template using try_start_t = decltype (declval<_Tp>().start()); template using try_stop_t = decltype (declval<_Tp>().stop()); template using try_read_t = decltype (declval<_Tp>().read (declval(), declval())); template using try_write_t = decltype (declval<_Tp>().write (declval(), declval())); //! Primary template to catch any non I2C interface types template struct is_i2c_ : false_ { }; //! template to catch a proper I2C interface type template struct is_i2c_ <_Tp, void_t < typename _Tp::Sequence, use_if_same_t >, use_if_same_t >, use_if_same_t >, use_if_same_t >, use_if_same_t >, use_if_same_t > > //!^ SFINAE may apply inside if_same_t<> also > : 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_c = i2c_i_cnpt::is_i2c_<_Tp>::value; #endif //!@} } // namespace utl #endif // _utl_com_i2c_h__