Micro template library A library for building device drivers
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 

287 lignes
10 KiB

  1. /*!
  2. * \file utl/com/spi_bb.h
  3. * \brief A bit banking implementation of spi bus inherited from
  4. * spi_i base class.
  5. *
  6. * Copyright (C) 2018 Christos Choutouridis
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU Lesser General Public License as
  10. * published by the Free Software Foundation, either version 3
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. #ifndef __utl_com_spi_bb_h__
  23. #define __utl_com_spi_bb_h__
  24. #include <utl/impl/impl.h>
  25. #include <utl/helper/crtp.h>
  26. #include <utl/meta/sfinae.h>
  27. #include <utl/com/spi.h>
  28. namespace utl {
  29. /*!
  30. * \ingroup Communication
  31. * \brief A bit banking implementation of spi bus
  32. * inherited from \ref spi_i base class.
  33. * \sa spi_i
  34. */
  35. //!@{
  36. /*!
  37. * \brief
  38. * SPI bit banking interface template class using CRTP.
  39. * Using the private interface we provide the interface from
  40. * spi_i<impl_t>
  41. *
  42. * \param impl_t The CRTP type (the derived/implementation class typename).
  43. * \param CPOL Clock polarity
  44. * \param CPHA Clock phase
  45. * \param BitOrder Data transfer bit order
  46. */
  47. template <typename impl_t,
  48. spi::cpol CPOL,
  49. spi::cpha CPHA,
  50. spi::bitOrder BitOrder =spi::bitOrder::MSB_First>
  51. class spi_bb_i : public spi_i<spi_bb_i<impl_t, CPOL, CPHA, BitOrder>> {
  52. _CRTP_IMPL(impl_t); //! \brief Syntactic sugar to CRTP casting
  53. friend spi_i<spi_bb_i<impl_t, CPOL, CPHA, BitOrder>>;
  54. public:
  55. using type = spi_bb_i<impl_t, CPOL, CPHA, BitOrder>; //!< Export type as identity meta-function
  56. /*!
  57. * \name Object lifetime
  58. */
  59. //!@{
  60. protected:
  61. ~spi_bb_i () = default; //!< A default destructor, allow destructor from derived only
  62. //! \brief A default constructor
  63. spi_bb_i (uint32_t clk =1000000) noexcept
  64. : nsec_ {1000000000/(2*clk)} {
  65. }
  66. //!@}
  67. private:
  68. //! \name SPI implementation specific functions
  69. //!@{
  70. template <spi::cpol C =CPOL> constexpr bool clkHigh () { return !static_cast<bool>(C); }
  71. template <spi::cpol C =CPOL> constexpr bool clkLow () { return static_cast<bool>(C); }
  72. template <spi::bitOrder B =BitOrder> constexpr
  73. use_if_t <(B == spi::bitOrder::LSB_First), void> shift (byte_t& b) { b <<=1; }
  74. template <spi::bitOrder B =BitOrder> constexpr
  75. use_if_t <(B == spi::bitOrder::MSB_First), void> shift (byte_t& b) { b >>=1; }
  76. //!}
  77. /*!
  78. * \name Implementation requirements
  79. * \note
  80. * In order for the implementation to have the following as private members
  81. * it also need to declare this class as friend
  82. */
  83. //!@{
  84. private:
  85. void MOSI (bool st) { impl().MOSI(st); } //!< Implementers's MOSI pin function
  86. bool MISO () { return impl().MISO(); } //!< Implementers's MISO pin function
  87. void SCLK (bool st) { impl().SCLK (st); } //!< Implementers's SCLK pin function
  88. void delay (uint32_t nsec) { impl().delay (nsec); } //!< Implementers's half period delay function
  89. //!@}
  90. /*!
  91. * \name Implementation of base requirements
  92. */
  93. //!@{
  94. private:
  95. uint32_t _clock () const { return 1000000000/(2*nsec_); }
  96. void _clock (uint32_t c) { nsec_ = 1000000000/(2*c); }
  97. byte_t _tx_data (byte_t out) { return _tx_data_impl (out); }
  98. template <spi::cpha C =CPHA>
  99. use_if_t <(C == spi::cpha::LOW), byte_t> _tx_data_impl (byte_t out);
  100. template <spi::cpha C =CPHA>
  101. use_if_t <(C == spi::cpha::HIGH), byte_t> _tx_data_impl (byte_t out);
  102. uint32_t nsec_; //!< half period of SPI bus
  103. //!@}
  104. };
  105. /*!
  106. * \brief
  107. * _tx_data implementation for CPHA == LOW
  108. * Out at preceding clock trailing edge, In at leading clock edge
  109. * \param out The byte to send to SPI bus
  110. * \return The byte read from the bus
  111. */
  112. template <typename impl_t, spi::cpol CPOL, spi::cpha CPHA, spi::bitOrder BitOrder>
  113. template <spi::cpha C>
  114. use_if_t <(C == spi::cpha::LOW), byte_t>
  115. spi_bb_i<impl_t, CPOL, CPHA, BitOrder>::_tx_data_impl (byte_t out) {
  116. byte_t in {};
  117. SCLK (clkLow());
  118. for (uint8_t bit {static_cast<uint8_t>(BitOrder)} ; bit ; shift (bit)) {
  119. MOSI (out & bit); // Out at preceding clock trailing edge
  120. delay (nsec_); // Half cycle delay
  121. SCLK (clkHigh()); // Leading edge
  122. in |= (MISO ()) ? bit : 0; // In at leading clock edge
  123. delay (nsec_); // Half cycle delay
  124. SCLK (clkLow()); // Trailing edge
  125. }
  126. return in;
  127. }
  128. /*!
  129. * \brief
  130. * _tx_data implementation CPHA == HIGH
  131. * Out at leading clock edge, In at trailing clock edge
  132. * \param out The byte to send to SPI bus
  133. * \return The byte read from the bus
  134. */
  135. template <typename impl_t, spi::cpol CPOL, spi::cpha CPHA, spi::bitOrder BitOrder>
  136. template <spi::cpha C>
  137. use_if_t <(C == spi::cpha::HIGH), byte_t>
  138. spi_bb_i<impl_t, CPOL, CPHA, BitOrder>::_tx_data_impl (byte_t out) {
  139. byte_t in {};
  140. SCLK (clkLow());
  141. for (uint8_t bit {static_cast<uint8_t>(BitOrder)} ; bit ; shift (bit)) {
  142. delay (nsec_); // Half cycle delay
  143. SCLK (clkHigh()); // Leading edge
  144. MOSI (out & bit); // Out at leading clock edge
  145. delay (nsec_); // Half cycle delay
  146. SCLK (clkLow()); // Trailing edge
  147. in |= (MISO ()) ? bit : 0; // In at trailing clock edge
  148. }
  149. return in;
  150. }
  151. /*!
  152. * \brief
  153. * A virtual base class interface specialization.
  154. * Using the private virtual interface we provide the interface from
  155. * spi_i<virtual_tag>
  156. * \param impl_t = virtual_tag
  157. * \param CPOL Clock polarity
  158. * \param CPHA Clock phase
  159. * \param BitOrder Data transfer bit order
  160. */
  161. template <spi::cpol CPOL,
  162. spi::cpha CPHA,
  163. spi::bitOrder BitOrder>
  164. class spi_bb_i <virtual_tag, CPOL, CPHA, BitOrder> : public spi_i<virtual_tag> {
  165. public:
  166. using type = spi_bb_i<virtual_tag, CPOL, CPHA, BitOrder>; //!< Export type as identity meta-function
  167. /*!
  168. * \name Object lifetime
  169. */
  170. //!@{
  171. protected:
  172. ~spi_bb_i () = default; //!< A default destructor, allow destructor from derived only
  173. //! \brief A default constructor
  174. spi_bb_i (uint32_t clk =1000000) noexcept
  175. : nsec_ {1000000000/(2*clk)} {
  176. }
  177. //!@}
  178. private:
  179. //! \name SPI implementation specific functions
  180. //!@{
  181. template <spi::cpol C =CPOL> constexpr bool clkHigh () { return !static_cast<bool>(C); }
  182. template <spi::cpol C =CPOL> constexpr bool clkLow () { return static_cast<bool>(C); }
  183. template <spi::bitOrder B =BitOrder> constexpr
  184. use_if_t <(B == spi::bitOrder::LSB_First), void> shift (byte_t& b) { b <<=1; }
  185. template <spi::bitOrder B =BitOrder> constexpr
  186. use_if_t <(B == spi::bitOrder::MSB_First), void> shift (byte_t& b) { b >>=1; }
  187. //!}
  188. //! \name Implementation requirements
  189. //!@{
  190. private:
  191. virtual void MOSI (bool) =0; //!< Implementers's MOSI pin function
  192. virtual bool MISO () =0; //!< Implementers's MISO pin function
  193. virtual void SCLK (bool) =0; //!< Implementers's SCLK pin function
  194. virtual void delay (uint32_t) =0; //!< Implementers's half period delay function
  195. //!@}
  196. /*!
  197. * \name Implementation of base requirements
  198. */
  199. //!@{
  200. private:
  201. uint32_t _clock () const final { return 1000000000/(2*nsec_); }
  202. void _clock (uint32_t c) final { nsec_ = 1000000000/(2*c); }
  203. byte_t _tx_data (byte_t out) final { return _tx_data_impl (out); }
  204. template <spi::cpha C =CPHA>
  205. use_if_t <(C == spi::cpha::LOW), byte_t> _tx_data_impl (byte_t out);
  206. template <spi::cpha C =CPHA>
  207. use_if_t <(C == spi::cpha::HIGH), byte_t> _tx_data_impl (byte_t out);
  208. uint32_t nsec_; //!< half period of SPI bus
  209. //!@}
  210. };
  211. /*!
  212. * \brief
  213. * _tx_data implementation CPHA == LOW
  214. * Out at preceding clock trailing edge, In at leading clock edge
  215. * \param out The byte to send to SPI bus
  216. * \return The byte read from the bus
  217. */
  218. template <spi::cpol CPOL, spi::cpha CPHA, spi::bitOrder BitOrder>
  219. template <spi::cpha C>
  220. use_if_t <(C == spi::cpha::LOW), byte_t>
  221. spi_bb_i<virtual_tag, CPOL, CPHA, BitOrder>::_tx_data_impl (byte_t out) {
  222. byte_t in {};
  223. SCLK (clkLow());
  224. for (uint8_t bit {static_cast<uint8_t>(BitOrder)} ; bit ; shift (bit)) {
  225. MOSI (out & bit); // Out at preceding clock trailing edge
  226. delay (nsec_); // Half cycle delay
  227. SCLK (clkHigh()); // Leading edge
  228. in |= (MISO ()) ? bit : 0; // In at leading clock edge
  229. delay (nsec_); // Half cycle delay
  230. SCLK (clkLow()); // Trailing edge
  231. }
  232. return in;
  233. }
  234. /*!
  235. * \brief
  236. * _tx_data implementation CPHA == HIGH
  237. * Out at leading clock edge, In at trailing clock edge
  238. * \param out The byte to send to SPI bus
  239. * \return The byte read from the bus
  240. */
  241. template <spi::cpol CPOL, spi::cpha CPHA, spi::bitOrder BitOrder>
  242. template <spi::cpha C>
  243. use_if_t <(C == spi::cpha::HIGH), byte_t>
  244. spi_bb_i<virtual_tag, CPOL, CPHA, BitOrder>::_tx_data_impl (byte_t out) {
  245. byte_t in {};
  246. SCLK (clkLow());
  247. for (uint8_t bit {static_cast<uint8_t>(BitOrder)} ; bit ; shift (bit)) {
  248. delay (nsec_); // Half cycle delay
  249. SCLK (clkHigh()); // Leading edge
  250. MOSI (out & bit); // Out at leading clock edge
  251. delay (nsec_); // Half cycle delay
  252. SCLK (clkLow()); // Trailing edge
  253. in |= (MISO ()) ? bit : 0; // In at trailing clock edge
  254. }
  255. return in;
  256. }
  257. }
  258. #endif //#ifndef __spi_bb_h__