/*! * \file utl/com/spi_bb.h * \brief A bit banking implementation of spi bus inherited from * spi_i base class. * * 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_spi_bb_h__ #define __utl_com_spi_bb_h__ #include #include #include #include namespace utl { /*! * \ingroup Communication * \brief A bit banking implementation of spi bus * inherited from \ref spi_i base class. * \sa spi_i */ //!@{ /*! * \brief * SPI bit banking interface template class using CRTP. * Using the private interface we provide the interface from * spi_i * * \param impl_t The CRTP type (the derived/implementation class typename). * \param CPOL Clock polarity * \param CPHA Clock phase * \param BitOrder Data transfer bit order */ template class spi_bb_i : public spi_i> { _CRTP_IMPL(impl_t); //! \brief Syntactic sugar to CRTP casting friend spi_i>; public: using type = spi_bb_i; //!< Export type as identity meta-function /*! * \name Object lifetime */ //!@{ protected: ~spi_bb_i () = default; //!< A default destructor, allow destructor from derived only //! \brief A default constructor spi_bb_i (uint32_t clk) noexcept : nsec_ {1000000000/(2*clk)} { } //!@} private: //! \name SPI implementation specific functions //!@{ template constexpr use_if_t <(B == spi::bitOrder::LSB_First), void> shift (byte_t& b) { b <<=1; } template constexpr use_if_t <(B == spi::bitOrder::MSB_First), void> shift (byte_t& b) { b >>=1; } template static constexpr bool clkHigh () { return !static_cast(C); } template static constexpr bool clkLow () { return static_cast(C); } static constexpr bool clkH_ {clkHigh()}; static constexpr bool clkL_ {clkLow()}; //!} /*! * \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 */ //!@{ private: void MOSI (bool st) { impl().MOSI(st); } //!< Implementers's MOSI pin function bool MISO () { return impl().MISO(); } //!< Implementers's MISO pin function void SCLK (bool st) { impl().SCLK (st); } //!< Implementers's SCLK pin function void delay (uint32_t nsec) { impl().delay (nsec); } //!< Implementers's half period delay function //!@} /*! * \name Implementation of base requirements */ //!@{ private: uint32_t _clock () const { return 1000000000/(2*nsec_); } void _clock (uint32_t c) { nsec_ = 1000000000/(2*c); } byte_t _tx_data (byte_t out) { return _tx_data_impl (out); } template use_if_t <(C == spi::cpha::LOW), byte_t> _tx_data_impl (byte_t out); template use_if_t <(C == spi::cpha::HIGH), byte_t> _tx_data_impl (byte_t out); //!@} //! Data members //! @{ private: uint32_t nsec_; //!< half period of SPI bus //! @} }; /*! * \brief * _tx_data implementation for CPHA == LOW * Out at preceding clock trailing edge, In at leading clock edge * \param out The byte to send to SPI bus * \return The byte read from the bus */ template template use_if_t <(C == spi::cpha::LOW), byte_t> spi_bb_i::_tx_data_impl (byte_t out) { byte_t in {}; SCLK (clkL_); for (uint8_t bit {static_cast(BitOrder)} ; bit ; shift (bit)) { MOSI (out & bit); // Out at preceding clock trailing edge delay (nsec_); // Half cycle delay SCLK (clkH_); // Leading edge in |= (MISO ()) ? bit : 0; // In at leading clock edge delay (nsec_); // Half cycle delay SCLK (clkL_); // Trailing edge } return in; } /*! * \brief * _tx_data implementation CPHA == HIGH * Out at leading clock edge, In at trailing clock edge * \param out The byte to send to SPI bus * \return The byte read from the bus */ template template use_if_t <(C == spi::cpha::HIGH), byte_t> spi_bb_i::_tx_data_impl (byte_t out) { byte_t in {}; SCLK (clkL_); for (uint8_t bit {static_cast(BitOrder)} ; bit ; shift (bit)) { delay (nsec_); // Half cycle delay SCLK (clkH_); // Leading edge MOSI (out & bit); // Out at leading clock edge delay (nsec_); // Half cycle delay SCLK (clkL_); // Trailing edge in |= (MISO ()) ? bit : 0; // In at trailing clock edge } return in; } /*! * \brief * A virtual base class interface specialization. * Using the private virtual interface we provide the interface from * spi_i * \param impl_t = virtual_tag * \param CPOL Clock polarity * \param CPHA Clock phase * \param BitOrder Data transfer bit order */ template class spi_bb_i : public spi_i { public: using type = spi_bb_i; //!< Export type as identity meta-function /*! * \name Object lifetime */ //!@{ protected: ~spi_bb_i () = default; //!< A default destructor, allow destructor from derived only //! \brief A default constructor spi_bb_i (uint32_t clk) noexcept : nsec_ {1000000000/(2*clk)} { } //!@} private: //! \name SPI implementation specific functions //!@{ template constexpr use_if_t <(B == spi::bitOrder::LSB_First), void> shift (byte_t& b) { b <<=1; } template constexpr use_if_t <(B == spi::bitOrder::MSB_First), void> shift (byte_t& b) { b >>=1; } template static constexpr bool clkHigh () { return !static_cast(C); } template static constexpr bool clkLow () { return static_cast(C); } static constexpr bool clkH_ {clkHigh()}; static constexpr bool clkL_ {clkLow()}; //!} //! \name Implementation requirements //!@{ private: virtual void MOSI (bool) =0; //!< Implementers's MOSI pin function virtual bool MISO () =0; //!< Implementers's MISO pin function virtual void SCLK (bool) =0; //!< Implementers's SCLK pin function virtual void delay (uint32_t) =0; //!< Implementers's half period delay function //!@} /*! * \name Implementation of base requirements */ //!@{ private: uint32_t _clock () const final { return 1000000000/(2*nsec_); } void _clock (uint32_t c) final { nsec_ = 1000000000/(2*c); } byte_t _tx_data (byte_t out) final { return _tx_data_impl (out); } template use_if_t <(C == spi::cpha::LOW), byte_t> _tx_data_impl (byte_t out); template use_if_t <(C == spi::cpha::HIGH), byte_t> _tx_data_impl (byte_t out); //!@} //! Data members //! @{ private: uint32_t nsec_; //!< half period of SPI bus //! @} }; /*! * \brief * _tx_data implementation CPHA == LOW * Out at preceding clock trailing edge, In at leading clock edge * \param out The byte to send to SPI bus * \return The byte read from the bus */ template template use_if_t <(C == spi::cpha::LOW), byte_t> spi_bb_i::_tx_data_impl (byte_t out) { byte_t in {}; SCLK (clkL_); for (uint8_t bit {static_cast(BitOrder)} ; bit ; shift (bit)) { MOSI (out & bit); // Out at preceding clock trailing edge delay (nsec_); // Half cycle delay SCLK (clkH_); // Leading edge in |= (MISO ()) ? bit : 0; // In at leading clock edge delay (nsec_); // Half cycle delay SCLK (clkL_); // Trailing edge } return in; } /*! * \brief * _tx_data implementation CPHA == HIGH * Out at leading clock edge, In at trailing clock edge * \param out The byte to send to SPI bus * \return The byte read from the bus */ template template use_if_t <(C == spi::cpha::HIGH), byte_t> spi_bb_i::_tx_data_impl (byte_t out) { byte_t in {}; SCLK (clkL_); for (uint8_t bit {static_cast(BitOrder)} ; bit ; shift (bit)) { delay (nsec_); // Half cycle delay SCLK (clkH_); // Leading edge MOSI (out & bit); // Out at leading clock edge delay (nsec_); // Half cycle delay SCLK (clkL_); // Trailing edge in |= (MISO ()) ? bit : 0; // In at trailing clock edge } return in; } } #endif //#ifndef __spi_bb_h__