|
- /*!
- * \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 <http://www.gnu.org/licenses/>.
- *
- */
- #ifndef __utl_com_spi_bb_h__
- #define __utl_com_spi_bb_h__
-
- #include <utl/core/impl.h>
- #include <utl/core/crtp.h>
- #include <utl/com/spi.h>
- #include <utl/meta/meta.h>
-
-
- 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<impl_t>
- *
- * \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 <typename impl_t,
- spi::cpol CPOL,
- spi::cpha CPHA,
- spi::bitOrder BitOrder =spi::bitOrder::MSB_First>
- class spi_bb_i : public spi_i<spi_bb_i<impl_t, CPOL, CPHA, BitOrder>> {
- _CRTP_IMPL(impl_t); //! \brief Syntactic sugar to CRTP casting
- friend spi_i<spi_bb_i<impl_t, CPOL, CPHA, BitOrder>>;
- public:
- using type = spi_bb_i<impl_t, CPOL, CPHA, BitOrder>; //!< 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 <spi::bitOrder B =BitOrder> constexpr
- meta::enable_if_t <(B == spi::bitOrder::LSB_First), void> shift (byte_t& b) { b <<=1; }
- template <spi::bitOrder B =BitOrder> constexpr
- meta::enable_if_t <(B == spi::bitOrder::MSB_First), void> shift (byte_t& b) { b >>=1; }
- template <spi::cpol C =CPOL> static constexpr bool clkHigh () {
- return !static_cast<bool>(C);
- }
- template <spi::cpol C =CPOL> static constexpr bool clkLow () {
- return static_cast<bool>(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 <spi::cpha C =CPHA>
- meta::enable_if_t <(C == spi::cpha::LOW), byte_t> _tx_data_impl (byte_t out);
- template <spi::cpha C =CPHA>
- meta::enable_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 <typename impl_t, spi::cpol CPOL, spi::cpha CPHA, spi::bitOrder BitOrder>
- template <spi::cpha C>
- meta::enable_if_t <(C == spi::cpha::LOW), byte_t>
- spi_bb_i<impl_t, CPOL, CPHA, BitOrder>::_tx_data_impl (byte_t out) {
- byte_t in {};
- SCLK (clkL_);
- for (uint8_t bit {static_cast<uint8_t>(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 <typename impl_t, spi::cpol CPOL, spi::cpha CPHA, spi::bitOrder BitOrder>
- template <spi::cpha C>
- meta::enable_if_t <(C == spi::cpha::HIGH), byte_t>
- spi_bb_i<impl_t, CPOL, CPHA, BitOrder>::_tx_data_impl (byte_t out) {
- byte_t in {};
- SCLK (clkL_);
- for (uint8_t bit {static_cast<uint8_t>(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<virtual_tag>
- * \param impl_t = virtual_tag
- * \param CPOL Clock polarity
- * \param CPHA Clock phase
- * \param BitOrder Data transfer bit order
- */
- template <spi::cpol CPOL,
- spi::cpha CPHA,
- spi::bitOrder BitOrder>
- class spi_bb_i <virtual_tag, CPOL, CPHA, BitOrder> : public spi_i<virtual_tag> {
- public:
- using type = spi_bb_i<virtual_tag, CPOL, CPHA, BitOrder>; //!< 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 <spi::bitOrder B =BitOrder> constexpr
- meta::enable_if_t <(B == spi::bitOrder::LSB_First), void> shift (byte_t& b) { b <<=1; }
- template <spi::bitOrder B =BitOrder> constexpr
- meta::enable_if_t <(B == spi::bitOrder::MSB_First), void> shift (byte_t& b) { b >>=1; }
- template <spi::cpol C =CPOL> static constexpr bool clkHigh () {
- return !static_cast<bool>(C);
- }
- template <spi::cpol C =CPOL> static constexpr bool clkLow () {
- return static_cast<bool>(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 <spi::cpha C =CPHA>
- meta::enable_if_t <(C == spi::cpha::LOW), byte_t> _tx_data_impl (byte_t out);
- template <spi::cpha C =CPHA>
- meta::enable_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 <spi::cpol CPOL, spi::cpha CPHA, spi::bitOrder BitOrder>
- template <spi::cpha C>
- meta::enable_if_t <(C == spi::cpha::LOW), byte_t>
- spi_bb_i<virtual_tag, CPOL, CPHA, BitOrder>::_tx_data_impl (byte_t out) {
- byte_t in {};
- SCLK (clkL_);
- for (uint8_t bit {static_cast<uint8_t>(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 <spi::cpol CPOL, spi::cpha CPHA, spi::bitOrder BitOrder>
- template <spi::cpha C>
- meta::enable_if_t <(C == spi::cpha::HIGH), byte_t>
- spi_bb_i<virtual_tag, CPOL, CPHA, BitOrder>::_tx_data_impl (byte_t out) {
- byte_t in {};
- SCLK (clkL_);
- for (uint8_t bit {static_cast<uint8_t>(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__
|