Micro template library A library for building device drivers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

288 lines
10 KiB

  1. /*!
  2. * \file /utl/com/i2c.h
  3. * \brief An Abstract base class interface for the i2c bus
  4. *
  5. * Copyright (C) 2018 Christos Choutouridis
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation, either version 3
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. #ifndef _utl_com_i2c_h__
  22. #define _utl_com_i2c_h__
  23. #include <utl/core/impl.h>
  24. #include <utl/core/crtp.h>
  25. #include <utl/meta/meta.h>
  26. namespace utl {
  27. /*!
  28. * \ingroup Communication
  29. * \brief Abstract base class for i2c bus
  30. */
  31. //!@{
  32. /*!
  33. * \brief
  34. * this class force a common interface for I2C communication
  35. * protocol implementations using CRTP
  36. * \param impl_t The CRTP type (the derived/implementation class typename).
  37. */
  38. template <typename impl_t>
  39. class i2c_i {
  40. _CRTP_IMPL(impl_t);
  41. public:
  42. using type = i2c_i<impl_t>; //!< Export type as identity meta-function
  43. //! I2C transmit/receive sequence
  44. enum class Sequence {
  45. BYTE =0, //!< Only read/write byte [8 clocks]
  46. ACK, //!< Only send/receive ack [1 clock]
  47. BYTEnACK //!< Read/Write byte and ack [9 clocks]
  48. };
  49. /*!
  50. * \name Object lifetime
  51. */
  52. //!@{
  53. protected:
  54. i2c_i () = default; //!< Allow constructor from derived only
  55. ~i2c_i () = default; //!< Allow destructor from derived only
  56. i2c_i (const type&) = delete; //!< No copies
  57. type& operator= (const type&) = delete;
  58. //!@}
  59. /*!
  60. * \name Implementation requirements
  61. * \note
  62. * In order for the implementation to have the following as private members
  63. * it also need to declare this class as friend
  64. */
  65. //!@{
  66. private:
  67. uint32_t _clock () const { return impl()._clock (); } //!< clock frequency of the bus [Hz]
  68. void _clock (uint32_t c) { impl()._clock (c); } //!< set clock frequency of the bus [Hz]
  69. void _start() { impl()._start (); } //!< Send start functionality
  70. void _stop() { impl()._stop (); } //!< Send stop functionality
  71. byte_t _rx_data (bool ack, Sequence seq) { return impl()._rx_data (ack, seq); }
  72. bool _tx_data (byte_t byte, Sequence seq) { return impl()._tx_data (byte, seq); }
  73. //!@}
  74. /*!
  75. * \name Get/Set functions
  76. */
  77. //!@{
  78. public:
  79. uint32_t clock () const { return _clock (); } //!< \return clock frequency of the bus
  80. void clock (uint32_t f) { _clock (f); } //!< \brief set clock frequency of the bus
  81. //!@}
  82. /*!
  83. * \name User functions
  84. */
  85. //!@{
  86. public:
  87. void start() { _start (); } //!< Send start functionality
  88. void stop () { _stop (); } //!< Send stop functionality
  89. /*!
  90. * \brief
  91. * Receive a byte from the i2c bus.
  92. * \param ack Optional ack bit.
  93. * \arg 1 ACK the reception
  94. * \arg 0 Don't ACK the reception.
  95. * \param seq The operation sequence to execute
  96. * \arg Sequence::BYTE Receive only the byte, do not send ack clock
  97. * \arg Sequence::ACK Send only the ack bit
  98. * \arg Sequence::BYTEnACK Receive the byte and send the ack bit
  99. * \return The byte received.
  100. */
  101. byte_t rx_data (bool ack, Sequence seq =Sequence::BYTEnACK) {
  102. return _rx_data (ack, seq);
  103. }
  104. /*!
  105. * \brief
  106. * Transmit a byte to the i2c bus.
  107. * \param byte The byte to send.
  108. * \param seq The operation sequence to execute
  109. * \arg Sequence::BYTE Transmit only the byte, do not read ack bit
  110. * \arg Sequence::ACK Read only the ack bit
  111. * \arg Sequence::BYTEnACK Transmit the byte and read the ack bit
  112. * \return Slave's ACK bit
  113. * \arg false Slave didn't ACK
  114. * \arg true Slave did ACK
  115. */
  116. bool tx_data (byte_t byte, Sequence seq =Sequence::BYTEnACK) {
  117. return _tx_data (byte, seq);
  118. }
  119. //!@}
  120. };
  121. /*!
  122. * \brief
  123. * A virtual base class specialization
  124. * \param impl_t = virtual_tag
  125. */
  126. template<>
  127. class i2c_i <virtual_tag> {
  128. public:
  129. using type = i2c_i <virtual_tag>; //!< Export type as identity meta-function
  130. //! I2C transmit/receive sequence
  131. enum class Sequence {
  132. BYTE=0, //!< Only read/write byte [8 clocks]
  133. ACK, //!< Only send/receive ack [1 clock]
  134. BYTEnACK //!< Read/Write byte and ack [9 clocks]
  135. };
  136. /*!
  137. * \name Object lifetime
  138. */
  139. //!@{
  140. protected:
  141. i2c_i () = default; //!< Allow constructor from derived only
  142. i2c_i (const type&) = delete; //!< No copies
  143. type& operator= (const type&) = delete;
  144. public:
  145. virtual ~i2c_i () = default; //!< Virtual default destructor
  146. //!@}
  147. /*!
  148. * \name Implementation requirements
  149. */
  150. //!@{
  151. private:
  152. virtual uint32_t _clock () const = 0; //!< \return clock frequency of the bus [Hz]
  153. virtual void _clock (uint32_t) = 0; //!< \brief set clock frequency of the bus [Hz]
  154. virtual void _start() = 0; //!< Send start functionality
  155. virtual void _stop() = 0; //!< Send stop functionality
  156. virtual byte_t _rx_data (bool ack, Sequence seq) = 0; //!< Receive a byte from the i2c bus.
  157. virtual bool _tx_data (byte_t byte, Sequence seq) = 0; //!< Transmit a byte to the i2c bus.
  158. //!@}
  159. /*!
  160. * \name Get/Set functions
  161. */
  162. //!@{
  163. public:
  164. uint32_t clock () const { return _clock(); } //!< \return clock frequency of the bus [Hz]
  165. void clock (uint32_t c) { _clock(c); } //!< \brief set clock frequency of the bus [Hz]
  166. //!@}
  167. /*!
  168. * \name User functions
  169. */
  170. //!@{
  171. public:
  172. void start () { _start(); }
  173. void stop () { _stop(); }
  174. /*!
  175. * \brief
  176. * Receive a byte from the i2c bus.
  177. * \param ack Optional ack bit.
  178. * \arg 1 ACK the reception
  179. * \arg 0 Don't ACK the reception.
  180. * \param seq The operation sequence to execute
  181. * \arg Sequence::BYTE Receive only the byte, do not send ack clock
  182. * \arg Sequence::ACK Send only the ack bit
  183. * \arg Sequence::BYTEnACK Receive the byte and send the ack bit
  184. * \return The byte received.
  185. */
  186. byte_t rx_data (bool ack, Sequence seq =Sequence::BYTEnACK) {
  187. return _rx_data (ack, seq);
  188. }
  189. /*!
  190. * \brief
  191. * Transmit a byte to the i2c bus.
  192. * \param byte The byte to send.
  193. * \param seq The operation sequence to execute
  194. * \arg Sequence::BYTE Transmit only the byte, do not read ack bit
  195. * \arg Sequence::ACK Read only the ack bit
  196. * \arg Sequence::BYTEnACK Transmit the byte and read the ack bit
  197. * \return Slave's ACK bit
  198. * \arg false Slave didn't ACK
  199. * \arg true Slave did ACK
  200. */
  201. bool tx_data (byte_t byte, Sequence seq =Sequence::BYTEnACK) {
  202. return _tx_data (byte, seq);
  203. }
  204. //!@}
  205. };
  206. #if defined _utl_have_concepts
  207. /*!
  208. * i2c interface concept
  209. */
  210. template <typename T>
  211. concept bool I2c_i = requires (T t, const T ct, typename T::Sequence s) {
  212. // Object type
  213. requires not_<std::is_copy_constructible<T>::value>::value;
  214. requires not_<std::is_copy_assignable<T>::value>::value;
  215. // Methods
  216. {ct.clock()} -> uint32_t;
  217. {t.clock(0)} -> void;
  218. {t.start()} -> void;
  219. {t.stop()} -> void;
  220. {t.rx_data (1, s)} -> byte_t;
  221. {t.tx_data (0, s)} -> bool;
  222. };
  223. #else
  224. namespace i2c_i_details {
  225. using std::declval;
  226. template <class _Tp> using try_cclk_t = decltype (declval<const _Tp>().clock());
  227. template <class _Tp> using try_clk_t = decltype (declval<_Tp>().clock(declval<uint32_t>()));
  228. template <class _Tp> using try_start_t = decltype (declval<_Tp>().start());
  229. template <class _Tp> using try_stop_t = decltype (declval<_Tp>().stop());
  230. template <class _Tp> using try_rx_data_t
  231. = decltype (declval<_Tp>().rx_data (declval<bool>(), declval<typename _Tp::Sequence>()));
  232. template <class _Tp> using try_tx_data_t
  233. = decltype (declval<_Tp>().tx_data (declval<byte_t>(), declval<typename _Tp::Sequence>()));
  234. //! Primary template to catch any non I2C interface types
  235. template <typename _Tp, typename =void>
  236. struct is_i2c_ : meta::false_ { };
  237. //! template to catch a proper I2C interface type
  238. template <typename _Tp>
  239. struct is_i2c_ <_Tp,
  240. meta::void_t <
  241. typename _Tp::Sequence,
  242. // meta::use_if_same_t <uint32_t,try_cclk_t <_Tp>>,
  243. // meta::use_if_same_t <void, try_clk_t <_Tp>>,
  244. // meta::use_if_same_t <void, try_start_t <_Tp>>,
  245. // meta::use_if_same_t <void, try_stop_t <_Tp>>,
  246. // meta::use_if_same_t <byte_t, try_rx_data_t <_Tp>>,
  247. // meta::use_if_same_t <bool, try_tx_data_t <_Tp>>
  248. void
  249. >
  250. > : meta::true_ { };
  251. }
  252. /*!
  253. * Value meta-programming function alias for I2C interface checking
  254. * \param _Tp Type to check
  255. * \return True if _Tp is a i2c interface
  256. */
  257. // template <typename _Tp>
  258. // constexpr bool I2c_i = i2c_i_details::is_i2c_<_Tp>::value;
  259. #endif
  260. //!@}
  261. } // namespace utl
  262. #endif // _utl_com_i2c_h__