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.
 
 
 
 

307 lignes
11 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) noexcept
  64. : nsec_ {1000000000/(2*clk)} {
  65. }
  66. //!@}
  67. private:
  68. //! \name SPI implementation specific functions
  69. //!@{
  70. template <spi::bitOrder B =BitOrder> constexpr
  71. use_if_t <(B == spi::bitOrder::LSB_First), void> shift (byte_t& b) { b <<=1; }
  72. template <spi::bitOrder B =BitOrder> constexpr
  73. use_if_t <(B == spi::bitOrder::MSB_First), void> shift (byte_t& b) { b >>=1; }
  74. template <spi::cpol C =CPOL> static constexpr bool clkHigh () {
  75. return !static_cast<bool>(C);
  76. }
  77. template <spi::cpol C =CPOL> static constexpr bool clkLow () {
  78. return static_cast<bool>(C);
  79. }
  80. static constexpr bool clkH_ {clkHigh()};
  81. static constexpr bool clkL_ {clkLow()};
  82. //!}
  83. /*!
  84. * \name Implementation requirements
  85. * \note
  86. * In order for the implementation to have the following as private members
  87. * it also need to declare this class as friend
  88. */
  89. //!@{
  90. private:
  91. void MOSI (bool st) { impl().MOSI(st); } //!< Implementers's MOSI pin function
  92. bool MISO () { return impl().MISO(); } //!< Implementers's MISO pin function
  93. void SCLK (bool st) { impl().SCLK (st); } //!< Implementers's SCLK pin function
  94. void delay (uint32_t nsec) { impl().delay (nsec); } //!< Implementers's half period delay function
  95. //!@}
  96. /*!
  97. * \name Implementation of base requirements
  98. */
  99. //!@{
  100. private:
  101. uint32_t _clock () const { return 1000000000/(2*nsec_); }
  102. void _clock (uint32_t c) { nsec_ = 1000000000/(2*c); }
  103. byte_t _tx_data (byte_t out) { return _tx_data_impl (out); }
  104. template <spi::cpha C =CPHA>
  105. use_if_t <(C == spi::cpha::LOW), byte_t> _tx_data_impl (byte_t out);
  106. template <spi::cpha C =CPHA>
  107. use_if_t <(C == spi::cpha::HIGH), byte_t> _tx_data_impl (byte_t out);
  108. //!@}
  109. //! Data members
  110. //! @{
  111. private:
  112. uint32_t nsec_; //!< half period of SPI bus
  113. //! @}
  114. };
  115. /*!
  116. * \brief
  117. * _tx_data implementation for CPHA == LOW
  118. * Out at preceding clock trailing edge, In at leading clock edge
  119. * \param out The byte to send to SPI bus
  120. * \return The byte read from the bus
  121. */
  122. template <typename impl_t, spi::cpol CPOL, spi::cpha CPHA, spi::bitOrder BitOrder>
  123. template <spi::cpha C>
  124. use_if_t <(C == spi::cpha::LOW), byte_t>
  125. spi_bb_i<impl_t, CPOL, CPHA, BitOrder>::_tx_data_impl (byte_t out) {
  126. byte_t in {};
  127. SCLK (clkL_);
  128. for (uint8_t bit {static_cast<uint8_t>(BitOrder)} ; bit ; shift (bit)) {
  129. MOSI (out & bit); // Out at preceding clock trailing edge
  130. delay (nsec_); // Half cycle delay
  131. SCLK (clkH_); // Leading edge
  132. in |= (MISO ()) ? bit : 0; // In at leading clock edge
  133. delay (nsec_); // Half cycle delay
  134. SCLK (clkL_); // Trailing edge
  135. }
  136. return in;
  137. }
  138. /*!
  139. * \brief
  140. * _tx_data implementation CPHA == HIGH
  141. * Out at leading clock edge, In at trailing clock edge
  142. * \param out The byte to send to SPI bus
  143. * \return The byte read from the bus
  144. */
  145. template <typename impl_t, spi::cpol CPOL, spi::cpha CPHA, spi::bitOrder BitOrder>
  146. template <spi::cpha C>
  147. use_if_t <(C == spi::cpha::HIGH), byte_t>
  148. spi_bb_i<impl_t, CPOL, CPHA, BitOrder>::_tx_data_impl (byte_t out) {
  149. byte_t in {};
  150. SCLK (clkL_);
  151. for (uint8_t bit {static_cast<uint8_t>(BitOrder)} ; bit ; shift (bit)) {
  152. delay (nsec_); // Half cycle delay
  153. SCLK (clkH_); // Leading edge
  154. MOSI (out & bit); // Out at leading clock edge
  155. delay (nsec_); // Half cycle delay
  156. SCLK (clkL_); // Trailing edge
  157. in |= (MISO ()) ? bit : 0; // In at trailing clock edge
  158. }
  159. return in;
  160. }
  161. /*!
  162. * \brief
  163. * A virtual base class interface specialization.
  164. * Using the private virtual interface we provide the interface from
  165. * spi_i<virtual_tag>
  166. * \param impl_t = virtual_tag
  167. * \param CPOL Clock polarity
  168. * \param CPHA Clock phase
  169. * \param BitOrder Data transfer bit order
  170. */
  171. template <spi::cpol CPOL,
  172. spi::cpha CPHA,
  173. spi::bitOrder BitOrder>
  174. class spi_bb_i <virtual_tag, CPOL, CPHA, BitOrder> : public spi_i<virtual_tag> {
  175. public:
  176. using type = spi_bb_i<virtual_tag, CPOL, CPHA, BitOrder>; //!< Export type as identity meta-function
  177. /*!
  178. * \name Object lifetime
  179. */
  180. //!@{
  181. protected:
  182. ~spi_bb_i () = default; //!< A default destructor, allow destructor from derived only
  183. //! \brief A default constructor
  184. spi_bb_i (uint32_t clk) noexcept
  185. : nsec_ {1000000000/(2*clk)} {
  186. }
  187. //!@}
  188. private:
  189. //! \name SPI implementation specific functions
  190. //!@{
  191. template <spi::bitOrder B =BitOrder> constexpr
  192. use_if_t <(B == spi::bitOrder::LSB_First), void> shift (byte_t& b) { b <<=1; }
  193. template <spi::bitOrder B =BitOrder> constexpr
  194. use_if_t <(B == spi::bitOrder::MSB_First), void> shift (byte_t& b) { b >>=1; }
  195. template <spi::cpol C =CPOL> static constexpr bool clkHigh () {
  196. return !static_cast<bool>(C);
  197. }
  198. template <spi::cpol C =CPOL> static constexpr bool clkLow () {
  199. return static_cast<bool>(C);
  200. }
  201. static constexpr bool clkH_ {clkHigh()};
  202. static constexpr bool clkL_ {clkLow()};
  203. //!}
  204. //! \name Implementation requirements
  205. //!@{
  206. private:
  207. virtual void MOSI (bool) =0; //!< Implementers's MOSI pin function
  208. virtual bool MISO () =0; //!< Implementers's MISO pin function
  209. virtual void SCLK (bool) =0; //!< Implementers's SCLK pin function
  210. virtual void delay (uint32_t) =0; //!< Implementers's half period delay function
  211. //!@}
  212. /*!
  213. * \name Implementation of base requirements
  214. */
  215. //!@{
  216. private:
  217. uint32_t _clock () const final { return 1000000000/(2*nsec_); }
  218. void _clock (uint32_t c) final { nsec_ = 1000000000/(2*c); }
  219. byte_t _tx_data (byte_t out) final { return _tx_data_impl (out); }
  220. template <spi::cpha C =CPHA>
  221. use_if_t <(C == spi::cpha::LOW), byte_t> _tx_data_impl (byte_t out);
  222. template <spi::cpha C =CPHA>
  223. use_if_t <(C == spi::cpha::HIGH), byte_t> _tx_data_impl (byte_t out);
  224. //!@}
  225. //! Data members
  226. //! @{
  227. private:
  228. uint32_t nsec_; //!< half period of SPI bus
  229. //! @}
  230. };
  231. /*!
  232. * \brief
  233. * _tx_data implementation CPHA == LOW
  234. * Out at preceding clock trailing edge, In at leading clock edge
  235. * \param out The byte to send to SPI bus
  236. * \return The byte read from the bus
  237. */
  238. template <spi::cpol CPOL, spi::cpha CPHA, spi::bitOrder BitOrder>
  239. template <spi::cpha C>
  240. use_if_t <(C == spi::cpha::LOW), byte_t>
  241. spi_bb_i<virtual_tag, CPOL, CPHA, BitOrder>::_tx_data_impl (byte_t out) {
  242. byte_t in {};
  243. SCLK (clkL_);
  244. for (uint8_t bit {static_cast<uint8_t>(BitOrder)} ; bit ; shift (bit)) {
  245. MOSI (out & bit); // Out at preceding clock trailing edge
  246. delay (nsec_); // Half cycle delay
  247. SCLK (clkH_); // Leading edge
  248. in |= (MISO ()) ? bit : 0; // In at leading clock edge
  249. delay (nsec_); // Half cycle delay
  250. SCLK (clkL_); // Trailing edge
  251. }
  252. return in;
  253. }
  254. /*!
  255. * \brief
  256. * _tx_data implementation CPHA == HIGH
  257. * Out at leading clock edge, In at trailing clock edge
  258. * \param out The byte to send to SPI bus
  259. * \return The byte read from the bus
  260. */
  261. template <spi::cpol CPOL, spi::cpha CPHA, spi::bitOrder BitOrder>
  262. template <spi::cpha C>
  263. use_if_t <(C == spi::cpha::HIGH), byte_t>
  264. spi_bb_i<virtual_tag, CPOL, CPHA, BitOrder>::_tx_data_impl (byte_t out) {
  265. byte_t in {};
  266. SCLK (clkL_);
  267. for (uint8_t bit {static_cast<uint8_t>(BitOrder)} ; bit ; shift (bit)) {
  268. delay (nsec_); // Half cycle delay
  269. SCLK (clkH_); // Leading edge
  270. MOSI (out & bit); // Out at leading clock edge
  271. delay (nsec_); // Half cycle delay
  272. SCLK (clkL_); // Trailing edge
  273. in |= (MISO ()) ? bit : 0; // In at trailing clock edge
  274. }
  275. return in;
  276. }
  277. }
  278. #endif //#ifndef __spi_bb_h__