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.
 
 
 
 

307 lines
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__