From 44f7dff3dda986bd4d5e97127ba775fecb2167f1 Mon Sep 17 00:00:00 2001 From: Christos Houtouridis Date: Wed, 24 Oct 2018 22:35:34 +0300 Subject: [PATCH] com: 1wire interface base class[es] added. Added: * A base class fot 1wire interface (both CRTP and virtual) * An UARD based implementation derived added (both CRTP and virtual) --- include/utl/com/_1wire.h | 708 ++++++++++++++++++++++++++++++++++ include/utl/com/_1wire_uart.h | 412 ++++++++++++++++++++ 2 files changed, 1120 insertions(+) create mode 100644 include/utl/com/_1wire.h create mode 100644 include/utl/com/_1wire_uart.h diff --git a/include/utl/com/_1wire.h b/include/utl/com/_1wire.h new file mode 100644 index 0000000..0fa34c9 --- /dev/null +++ b/include/utl/com/_1wire.h @@ -0,0 +1,708 @@ +/*! + * \file /utl/com/_1wire.h + * \brief An 1-wire interface implementation + * + * 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_1wire_h__ +#define __utl_com_1wire_h__ + +#include +#include +#include +#include + +#include +using gsl::span; + +namespace utl { + + + +/*! + * \ingroup Communication + * \brief Abstract base class interface for 1-wire bus + */ +//!@{ + + /*! + * \brief + * Template base class for 1-wire communication interface using CRTP + * \param impl_t The CRTP type (the derived/implementation class typename). + */ + template + class _1wire_i { + _CRTP_IMPL(impl_t); + + public: + using type = _1wire_i; //!< Export type as identity meta-function + + //! 1-wire bus speed + enum class Speed { STD =0, OVDR }; + + /*! + * \name Object lifetime + */ + //!@{ + protected: + _1wire_i () = default; //!< Allow constructor from derived only + ~_1wire_i () = default; //!< Allow destructor from derived only + _1wire_i (const _1wire_i&) = delete; //!< No copies + _1wire_i& operator= (const _1wire_i&) = delete; + //!@} + + /*! + * \name Implementation requirements + * \note + * In order for the implementation to have the following as private members too + * it need to declare this class as friend + */ + //!@{ + private: + Speed speed () { return impl().speed(); } //!< Get the 1-wire bus speed + void speed (Speed s) { return impl().speed(s); } //!< Set the 1-wire bus speed + bool bit () { return impl().bit(); } //!< Read a bit from the 1-Wire bus, return it and provide the recovery time. + bool bit (bool b) { return impl().bit(b); } //!< Write a bit to the 1-Wire bus, return write status and provide the recovery time. + bool _reset () { return impl()._reset(); } //!< Generate a 1-wire reset and return the operation status + //!@} + + /*! + * \name Friends api to provide functionality to all class specializations + */ + //!@{ + private: + enum Command { + CMD_READ_ROM = 0x33, + CMD_OVDR_SKIP = 0x3C, + CMD_MATCH = 0x55, + CMD_OVDR_MATCH = 0x69, + CMD_SKIP = 0xCC, + CMD_ALARM_SEARCH = 0xEC, + CMD_SEARCH_ROM = 0xF0 + }; + template friend bool _1wire_i_reset (_T&, typename _T::Speed); + template friend byte_t _1wire_i_rw (_T&, byte_t, typename _T::Speed); + template friend byte_t _1wire_i_rx (_T&, typename _T::Speed); + template friend size_t _1wire_i_rx (_T&, span, typename _T::Speed); + template friend size_t _1wire_i_tx (_T&, byte_t, typename _T::Speed); + template friend size_t _1wire_i_tx (_T&, const span, typename _T::Speed); + template friend void _1wire_i_match (_T& obj, _1wire_id_t& id, typename _T::Speed s); + template friend void _1wire_i_match_n_ovdr (_T& obj, _1wire_id_t& id); + template friend void _1wire_i_skip (_T& obj, typename _T::Speed s); + template friend void _1wire_i_skip_n_ovdr (_T& obj); + template friend _1wire_id_t _1wire_i_first (_T&, typename _T::Speed, bool); + template friend _1wire_id_t _1wire_i_next (_T&, typename _T::Speed, bool); + //!@} + + /*! + * \name User functionality provided by the interface + */ + //!@{ + public: + /*! + * \brief + * Generate a 1-wire reset + * \param s Bus speed + * \return The status of the operation + * \arg 0 Fail + * \arg 1 Success + */ + bool reset (Speed s) { return _1wire_i_reset (*this, s); }; + //! Read a byte from 1-Wire bus + //! @param s The bus speed + //! @return The received byte + byte_t read (Speed s) { return _1wire_i_rx (*this, s); } + //! Read a span of bytes from 1-wire bus + //! \param data Ref to data span + //! \param s The speed to use + //! \return The number of received bytes + size_t read (span data, Speed s) { return _1wire_i_rx (*this, data, s); } + //! \brief Write a byte to 1-Wire bus + //! \param b The byte to write + //! \param s Bus speed to use + //! \return The number of transmitted bytes + size_t write (byte_t byte, Speed s) { return _1wire_i_tx (*this, byte, s); } + //! \brief Write a span of bytes to 1-wire bus + //! \param data Ref to data span + //! \param s Bus speed to use + //! \return The number of transmitted bytes + size_t write (const span data, Speed s) { return _1wire_i_tx (*this, data, s); } + /*! + * \brief + * Write a byte to 1-Wire bus and read the response + * \param b The byte to write + * \param s Bus speed to use + * \return The byte received. + */ + byte_t rw (byte_t b, Speed s) { return _1wire_i_rw (*this, b, s); } + + void match (_1wire_id_t& id, Speed s) { _1wire_i_match (*this, id, s); } + void match_n_ovdr (_1wire_id_t& id) { _1wire_i_match_n_ovdr (*this, id); } + void skip (Speed s) { _1wire_i_skip (*this, s); } + void skip_n_ovdr () { _1wire_i_skip_n_ovdr (*this); } + + /*! + * \brief + * 'first' operation, to search on the 1-Wire for the first device. + * This is performed by setting dec_, pos_ and cur_ to zero and then doing the search. + * \param s The bus speed + * \param alarm If set, search for alarm devices + * \return ID The romID + * \arg nullDev Indicate no [more] device[s] + */ + _1wire_id_t first (Speed s, bool alarm =false) { return _1wire_i_first (*this, s, alarm); } + /*! + * \brief + * 'next' operation, to search on the 1-Wire for the next device. + * This search is usually performed after a 'first' operation or another 'next' operation. + * Based on maxim-ic application note 187. + * \param s The bus speed + * \param alarm If set, search for alarm devices + * \return ID The romID + * \arg nullDev Indicate no [more] device[s] + */ + _1wire_id_t next (Speed s, bool alarm =false) { return _1wire_i_next (*this, s, alarm); } + //!@} + + + private: + _1wire_id_t dec_ {_1wire_id_t::nullDev()}; /*!< + * Hold the algorithm's select bit when a discrepancy + * is detected. We use this variable to navigate to the + * ROM tree as we store the path we take each time (0-1). + * Each bit represent a bit position in the ROM ID. + */ + _1wire_id_t pos_ {_1wire_id_t::nullDev()}; /*!< + * Hold the discrepancy position. We use this variable to + * navigate to the ROM tree as we store the crossroads(1) we encounter. + * Each bit represent a bit position in the ROM ID. + */ + _1wire_id_t cur_ {_1wire_id_t::nullDev()}; //! Current rom discrepancy state + }; + + + + /*! + * \brief + * A virtual base class implementation + * \param impl_t = virtual_tag + */ + template <> + class _1wire_i { + public: + using type = _1wire_i; //!< Export type as identity meta-function + + //! 1-wire bus speed + enum class Speed { STD =0, OVDR }; + + /*! + * \name Object lifetime + */ + //!@{ + protected: + _1wire_i () = default; //!< Allow constructor from derived only + _1wire_i (const type&) = delete; //!< No copies + type& operator= (const type&) = delete; + public: + virtual ~_1wire_i () = default; //!< Virtual default destructor + //!@} + + /*! + * \name Implementation requirements + */ + //!@{ + private: + virtual Speed speed () =0; //!< Get the 1-wire bus speed + virtual void speed (Speed) =0; //!< Set the 1-wire bus speed + virtual bool bit () =0; //!< Read a bit from the 1-Wire bus, return it and provide the recovery time. + virtual bool bit (bool) =0; //!< Write a bit to the 1-Wire bus, return write status and provide the recovery time. + virtual bool _reset () =0; //!< Generate a 1-wire reset and return the operation status + //!@} + + /*! + * \name Friends api to provide functionality to all class specializations + */ + //!@{ + private: + enum Command { + CMD_READ_ROM = 0x33, + CMD_OVDR_SKIP = 0x3C, + CMD_MATCH = 0x55, + CMD_OVDR_MATCH = 0x69, + CMD_SKIP = 0xCC, + CMD_ALARM_SEARCH = 0xEC, + CMD_SEARCH_ROM = 0xF0 + }; + template friend bool _1wire_i_reset (_T&, typename _T::Speed); + template friend byte_t _1wire_i_rw (_T&, byte_t, typename _T::Speed); + template friend byte_t _1wire_i_rx (_T&, typename _T::Speed); + template friend size_t _1wire_i_rx (_T&, span, typename _T::Speed); + template friend size_t _1wire_i_tx (_T&, byte_t, typename _T::Speed); + template friend size_t _1wire_i_tx (_T&, const span, typename _T::Speed); + template friend void _1wire_i_match (_T& obj, _1wire_id_t& id, typename _T::Speed s); + template friend void _1wire_i_match_n_ovdr (_T& obj, _1wire_id_t& id); + template friend void _1wire_i_skip (_T& obj, typename _T::Speed s); + template friend void _1wire_i_skip_n_ovdr (_T& obj); + template friend _1wire_id_t _1wire_i_first (_T&, typename _T::Speed, bool); + template friend _1wire_id_t _1wire_i_next (_T&, typename _T::Speed, bool); + //!@} + + /*! + * \name User functionality provided by the interface + */ + //!@{ + public: + /*! + * \brief + * Generate a 1-wire reset + * \param s Bus speed + * \return The status of the operation + * \arg 0 Fail + * \arg 1 Success + */ + bool reset (Speed s) { return _1wire_i_reset (*this, s); }; + //! Read a byte from 1-Wire bus + //! @param s The bus speed + //! @return The received byte + byte_t read (Speed s) { return _1wire_i_rx (*this, s); } + //! Read a span of bytes from 1-wire bus + //! \param data Ref to data span + //! \param s The speed to use + //! \return The number of received bytes + size_t read (span data, Speed s) { return _1wire_i_rx (*this, data, s); } + //! \brief Write a byte to 1-Wire bus + //! \param b The byte to write + //! \param s Bus speed to use + //! \return The number of transmitted bytes + size_t write (byte_t byte, Speed s) { return _1wire_i_tx (*this, byte, s); } + //! \brief Write a span of bytes to 1-wire bus + //! \param data Ref to data span + //! \param s Bus speed to use + //! \return The number of transmitted bytes + size_t write (const span data, Speed s) { return _1wire_i_tx (*this, data, s); } + /*! + * \brief + * Write a byte to 1-Wire bus and read the response + * \param b The byte to write + * \param s Bus speed to use + * \return The byte received. + */ + byte_t rw (byte_t b, Speed s) { return _1wire_i_rw (*this, b, s); } + + void match (_1wire_id_t& id, Speed s) { _1wire_i_match (*this, id, s); } + void match_n_ovdr (_1wire_id_t& id) { _1wire_i_match_n_ovdr (*this, id); } + void skip (Speed s) { _1wire_i_skip (*this, s); } + void skip_n_ovdr () { _1wire_i_skip_n_ovdr (*this); } + + /*! + * \brief + * 'first' operation, to search on the 1-Wire for the first device. + * This is performed by setting dec_, pos_ and cur_ to zero and then doing the search. + * \param s The bus speed + * \param alarm If set, search for alarm devices + * \return ID The romID + * \arg nullDev Indicate no [more] device[s] + */ + _1wire_id_t first (Speed s, bool alarm =false) { return _1wire_i_first (*this, s, alarm); } + /*! + * \brief + * 'next' operation, to search on the 1-Wire for the next device. + * This search is usually performed after a 'first' operation or another 'next' operation. + * Based on maxim-ic application note 187. + * \param s The bus speed + * \param alarm If set, search for alarm devices + * \return ID The romID + * \arg nullDev Indicate no [more] device[s] + */ + _1wire_id_t next (Speed s, bool alarm =false) { return _1wire_i_next (*this, s, alarm); } + //!@} + + + private: + _1wire_id_t dec_ {_1wire_id_t::nullDev()}; /*!< + * Hold the algorithm's select bit when a discrepancy + * is detected. We use this variable to navigate to the + * ROM tree as we store the path we take each time (0-1). + * Each bit represent a bit position in the ROM ID. + */ + _1wire_id_t pos_ {_1wire_id_t::nullDev()}; /*!< + * Hold the discrepancy position. We use this variable to + * navigate to the ROM tree as we store the crossroads(1) we encounter. + * Each bit represent a bit position in the ROM ID. + */ + _1wire_id_t cur_ {_1wire_id_t::nullDev()}; //! Current rom discrepancy state + }; + + + /*! + * \name Friend API to provide common functionality to all specializations + */ + //!@{ + + /*! + * \brief + * Generate a 1-wire reset + * \param obj The object from which we call private members + * \param s Bus speed + * \return The status of the operation + * \arg 0 Fail + * \arg 1 Success + */ + template + bool _1wire_i_reset (_T& obj, typename _T::Speed s) { + if (obj.speed () != s) + obj.speed (s); + return obj._reset (); + } + + /*! + * \brief + * Write a byte to 1-Wire bus and read the response + * \param obj The object from which we call private members + * \param b The byte to write + * \param s Bus speed to use + * \return The byte received. + */ + template + byte_t _1wire_i_rw (_T& obj, byte_t b, typename _T::Speed s) { + byte_t ret {0}; + + // Select speed once + if (obj.speed () != s) + obj.speed (s); + for (uint8_t i =8; i>0 ; --i) { + ret >>= 1; + if ((b & 0x01) != 0) ret |= (obj.bit ()) ? 0x80 : 0x00; + else obj.bit (0); + b >>= 1; + /*^ + * If the bit is 1 we use the read sequence, as it has the same + * waveform with write-1 and we get the slave response + * If the bit is 0, we can not read the slave response so we just write-0 + */ + } + return ret; + } + /*! + * \brief Read a byte from 1-Wire bus + * \param obj The object from which we call private members + * \param s Bus speed to use + * \return The byte received. + */ + template + byte_t _1wire_i_rx (_T& obj, typename _T::Speed s) { + byte_t byte {0}; + // Select speed once + if (obj.speed () != s) + obj.speed (s); + for (uint8_t i =8; i>0 ; --i) { + // shift bits to right as LSB comes first and mask it to MSB + byte >>= 1; + byte |= (obj.bit ()) ? 0x80 : 0x00; + } + return byte; + } + + /*! + * \brief Read a span of bytes from 1-Wire bus + * \param obj The object from which we call private members + * \param data Reference to byte span + * \param s Bus speed to use + * \return The number of received bytes. Actual return data.size() + */ + template + size_t _1wire_i_rx (_T& obj, span data, typename _T::Speed s) { + // Select speed once + if (obj.speed () != s) + obj.speed (s); + for (byte_t& byte : data) { + byte = 0; + for (uint8_t i =8; i>0 ; --i) { + // shift bits to right as LSB comes first and mask it to MSB + byte >>= 1; + byte |= (obj.bit ()) ? 0x80 : 0x00; + } + } + return data.size(); + } + /*! + * \brief Write a byte to 1-Wire bus + * \param obj The object from which we call private members + * \param byte The byte to write + * \param s Bus speed to use + * \return The number of transmitted bytes. So "1" of "0" + */ + template + size_t _1wire_i_tx (_T& obj, byte_t byte, typename _T::Speed s) { + return (_1wire_i_rw (obj, byte, s) == byte) ? 1 : 0; + } + /*! + * \brief Write a byte to 1-Wire bus + * \param obj The object from which we call private members + * \param data Reference to byte span + * \param s Bus speed to use + * \return The number of transmitted bytes. + * \note + * The procedure breaks on first transmission error + */ + template + size_t _1wire_i_tx (_T& obj, const span data, typename _T::Speed s) { + size_t ret {0}; + for (byte_t byte : data) { + if (_1wire_i_rw (obj, byte, s) == byte) + ++ret; + else + break; + } + return ret; + } + + /*! + * Send match rom command + * \param obj The object from which we call private members + * \param id The ID to select on the bus + * \param s The speed to use for the command + */ + template + void _1wire_i_match (_T& obj, _1wire_id_t& id, typename _T::Speed s) { + _1wire_i_tx (obj, (s == _T::Speed::STD) ? _T::CMD_MATCH : _T::CMD_OVDR_MATCH, s); + for (uint8_t& b : id) + _1wire_i_tx (obj, b, s); + } + + /*! + * Match and overdrive sequence + * \param obj The object from which we call private members + * \param id The ID to select on the bus + */ + template + void _1wire_i_match_n_ovdr (_T& obj, _1wire_id_t& id) { + _1wire_i_tx (obj, _T::CMD_MATCH, _T::Speed::STD); + for (uint8_t& b : id) + _1wire_i_tx (obj, b, _T::Speed::OVDR); + } + + /*! + * Send skip command to the bus + * \param obj The object from which we call private members + * \param id The ID to select on the bus + */ + template + void _1wire_i_skip (_T& obj, typename _T::Speed s) { + _1wire_i_tx (obj, (s == _T::Speed::STD) ? _T::CMD_SKIP : _T::CMD_SKIP, s); + } + + /*! + * Send the Skip and Overdrive sequence + * \param obj The object from which we call private members + */ + template + void _1wire_i_skip_n_ovdr (_T& obj) { + _1wire_i_tx (obj, _T::CMD_OVDR_SKIP, _T::Speed::STD); + } + + /*! + * \brief + * 'first' operation, to search on the 1-Wire for the first device. + * This is performed by setting dec_, pos_ and cur_ to zero and then doing the search. + * \param obj The object from which we call private members + * \param s The bus speed + * \return ID The romID + * \arg nullDev Indicate no [more] device[s] + */ + template + _1wire_id_t _1wire_i_first (_T& obj, typename _T::Speed s, bool alarm) { + obj.dec_ = obj.pos_ = obj.cur_ = _1wire_id_t::nullDev(); + return _1wire_i_next (obj, s, alarm); + } + + /*! + * \brief + * 'next' operation, to search on the 1-Wire for the next device. + * This search is usually performed after a 'first' operation or another 'next' operation. + * Based on maxim-ic application note 187. + * \param obj The object from which we call private members + * \param s The bus speed + * \return The romID + * \arg nullDev Indicate no [more] device[s] + */ + template + _1wire_id_t _1wire_i_next (_T& obj, typename _T::Speed s, bool alarm) { + uint8_t b, bxx; // bit helper vars + uint8_t i; + _1wire_id_t ID; + + do { + if ((obj.pos_ != _1wire_id_t::nullDev()) && (obj.dec_ >= obj.pos_)) { + // dec_ == pos_: We have found all the leafs, already + // dec_ > pos_ : Error + ID = _1wire_id_t::nullDev(); + break; + } + if (!obj.reset (s)) { + ID = _1wire_id_t::nullDev(); + break; + } + // Issue search command + if (alarm) obj.write (_T::CMD_ALARM_SEARCH, s); + else obj.write (_T::CMD_SEARCH_ROM, s); + + // traverse entire RomID from LSB to MSB + for (i =0 ; i<64 ; ++i) { + // Get response pair bits + bxx = obj.bit (); // bit + bxx <<= 1; + bxx |= obj.bit (); // complementary bit + + if (bxx == 0x00) { + // 00 - We have discrepancy + obj.cur_.bit (i, 1); + switch (_1wire_id_t::compare (obj.pos_, obj.cur_)) { + default: + case -1: + // pos_ < cur_: This discrepancy is the most far for now. Mark position and select 0. + b = 0; + //dec_.bit (i, (b = 0)); //<-- Its already 0 + obj.pos_.bit (i, 1); + break; + case 0: + // pos_ == cur_: This was the last discrepancy in the last pass. + // Select the other branch this time (1) + obj.dec_.bit (i, (b = 1)); + break; + case 1: + // pos_ > cur_: We had a discrepancy in a MSB than that, in a previous pass. + // Continue with the last pass decision. + b = obj.dec_.bit (i); + break; + } + // Send selection and update romid + obj.bit (b); + ID.bit (i, b); + } + else if (bxx == 0x01) { + // 01 - All bits of all ROMs are 0s + obj.bit (0); + ID.bit (i, 0); + + } + else if (bxx == 0x02) { + // 10 - All bits of all ROMs are 1s + obj.bit (1); + ID.bit (i, 1); + } + else { + // 11 - No device on the bus + ID = _1wire_id_t::nullDev(); + break; + } + } // for + + if (i == 64 && obj.pos_ == _1wire_id_t::nullDev()) { + // Mark done with only one device + obj.pos_.bit (0, 1); + obj.dec_.bit (0, 1); + } + } while (0); + + return ID; + } + //!@} + + +#if defined _utl_have_concepts + /*! + * \name 1-wire type interface concept + */ + template + concept bool _1wire_c = requires (T t, typename T::Speed s, _1wire_id_t id) { + // Object type + requires not_::value>::value; + requires not_::value>::value; + // Members +// typename T::Speed; +// typename T::Command; + // Methods + {t.reset(s)} -> bool; + {t.read(s)} -> byte_t; + {t.write(0, s)} -> size_t; + {t.rw(0, s)} -> byte_t; + t.match(id, s); + t.match_n_ovdr(id); + t.skip(s); + t.skip_n_ovdr(); + {t.first(s)} -> _1wire_id_t; + {t.next(s)} -> _1wire_id_t; + }; +#else + namespace _1wire_i_cnpt { + using std::declval; + + template using try_reset_t = decltype (declval<_Tp>().reset (declval())); + template using try_rw_t = decltype (declval<_Tp>().rw (declval(), declval())); + template using try_rx1_t = decltype (declval<_Tp>().read (declval())); + template using try_rx2_t = decltype (declval<_Tp>().read (declval>(), declval())); + template using try_tx1_t = decltype (declval<_Tp>().write (declval(), declval())); + template using try_tx2_t = decltype (declval<_Tp>().write (declval>(), declval())); + + + template using try_match_t = decltype (declval<_Tp>().match (declval<_1wire_id_t&>(), declval())); + template using try_match_n_ovdr_t = decltype (declval<_Tp>().match_n_ovdr (declval<_1wire_id_t&>())); + template using try_skip_t = decltype (declval<_Tp>().skip (declval())); + template using try_skip_n_ovdr_t = decltype (declval<_Tp>().skip_n_ovdr ()); + template using try_first_t = decltype (declval<_Tp>().first (declval())); + template using try_next_t = decltype (declval<_Tp>().next (declval())); + + //! Primary template to catch any non 1-wire interface types + template + struct is_1wire_ : false_ {}; + + //! template to catch a proper 1-wire interface type + template + struct is_1wire_ <_Tp, + void_t < +// typename _Tp::Speed, +// typename _Tp::Command, + use_if_same_t , bool>, + use_if_same_t , byte_t>, + use_if_same_t , byte_t>, + use_if_same_t , size_t>, + use_if_same_t , size_t>, + use_if_same_t , size_t>, + use_if_same_t , void>, + use_if_same_t , void>, + use_if_same_t , void>, + use_if_same_t , void>, + use_if_same_t , _1wire_id_t>, + use_if_same_t , _1wire_id_t> + > //!^ SFINAE may apply + > : true_ {}; + } + + /*! + * Value meta-programming function for 1-wire interface checking + * \param _Tp Type to check + * \return True if _Tp is a 1-wire interface + */ + template + constexpr bool _1wire_c = _1wire_i_cnpt::is_1wire_<_Tp>::value; +#endif + +//!@} +} //namespace utl + +#endif /* __utl_com_1wire_h__ */ diff --git a/include/utl/com/_1wire_uart.h b/include/utl/com/_1wire_uart.h new file mode 100644 index 0000000..546d19b --- /dev/null +++ b/include/utl/com/_1wire_uart.h @@ -0,0 +1,412 @@ +/*! + * \file utl/com/_1wire_uart.h + * \brief + * A 1-wire implementation using a microprocessor's uart for bit timing + * \note + * This 1-wire implementation is based on MCU UART io functionality. In order + * to work it needs: + * 1) A Open drain tx and floating(or pull-up) rx UART pin configuration with both pins + * connected to the 1-wire bus wire + * 2) A Transmit/receive function even in blocking/polling mode + * 3) A baudrate set function + * + * 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_1wire_uart_h__ +#define __utl_com_1wire_uart_h__ + +#include +#include +#include + +namespace utl { +/*! + * \ingroup Communication + * A 1-wire implementation using a microprocessor's uart for bit timing + * inherited from \ref _1wire_i base class. + * \sa _1wire_i + */ +//!@{ + + /*! + * \brief + * 1-wire UART interface template class using CRTP + * Using the private virtual interface we provide the interface from + * _1wire_i<> + */ + template + class _1wire_uart_i : public _1wire_i<_1wire_uart_i> { + _CRTP_IMPL(Impl_t); //! \brief Syntactic sugar to CRTP casting + friend _1wire_i <_1wire_uart_i>; + public: + using type = _1wire_uart_i; //!< Export type as identity meta-function + using Speed = typename _1wire_i::Speed; //!< Bring bus speed + + /*! + * \name Object lifetime + */ + //!@{ + protected: + _1wire_uart_i () = default; //!< Allow constructor from derived only + ~_1wire_uart_i () = default; //!< Allow destructor from derived only + //!@} + + /*! + * \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 + * for ex: + * class Foo : public _1wire_uart { + * friend _1wire_uart; + * byte_t UART_RW (byte_t byte); + * void UART_BR (uint32_t br); + * // ... + * }; + */ + //!@{ + private: + /*! + * \brief + * Implementers's (driver) read-write function. We use the following USART configuration. + * - Word Length = 8 Bits + * - Stop Bit = One Stop bit + * - Parity = No parity + * \param The byte to send + * \return The byte received. + * \note + * Due to the nature of the technique, the received byte is the actual bus + * condition during the communication frame (time slot) + */ + byte_t UART_RW (byte_t byte) { return impl().UART_RW (byte); } + + /*! + * \brief + * Implementers's (driver) baudrate function. + * \param The desired Baudrate + */ + void UART_BR (uint32_t br) { impl().UART_BR (br); } + //!@} + + //! \name Implementation of base requirements + //!@{ + private: + //! 1-wire UART baudrates + enum BR { + BR_STD_RST =9600, + BR_OVR_RST =57600, + BR_STD =115200, + BR_OVR =921600 + }; + Speed _speed {Speed::STD}; + + Speed speed () { return _speed; } //!< Get the 1-wire bus speed + void speed (Speed s); //!< Set the 1-wire bus speed + + /*! + * \brief + * Read a bit from the 1-Wire bus, return it and provide + * the recovery time. + * + * --- - - - - - - - - - - - - - - - - ------ + * Read \ / X X X X X X X X X X X X X X X / + * ---- - - - - - - - - - - - - - - - - + * RS: | | | | | | | | | | | + * bit: ST 0 1 2 3 4 5 6 7 SP + * < ------------- 87/11 usec -------------> + * ^ + * | + * Master sample + * + * 8 bits, no parity, 1 stop + * standard: BR: 115200 + * Overdrive: BR: 921600 + * TX: 0xFF + * RX: {1 - 0xFF, 0 - [0x00 - 0xFE] } + * + * \return The answer + * \arg 0 Read 0 + * \arg 1 Read 1 (This is also returned on transition error). + */ + bool bit () { + return (impl().UART_RW (0xFF) < 0xFF); + } + + + /*! + * \brief + * Send a 1-Wire write bit + * 8 bits, no parity, 1 stop + * standard: BR: 115200 + * Overdrive: BR: 921600 + * + * --- -------------------------------------- + * Write 1 \ / + * ---- + * RS: | | | | | | | | | | | + * bit: ST 0 1 2 3 4 5 6 7 SP + * TX: 0xFF RX: 0xFF + * + * --- ------ + * Write 0 \ / + * ------------------------------------- + * RS: | | | | | | | | | | | + * bit: ST 0 1 2 3 4 5 6 7 SP + * < ------------- 87/11 usec -------------> + * TX: 0x00 RX: 0x00 + * + * \param b The bit to send + * \return Status of the operation + * \arg 0 Fail + * \arg 1 Success + */ + bool bit (bool b) { + uint8_t w = (b) ? 0xFF : 0x00; // Select write frame to send + return (w == impl().UART_RW (w)); // Return status + } + bool _reset (); + //!@} + }; + + /*! + * Set the 1-wire bus speed for normal operation only + * \note + * We have moved the BR set functionality here to reduce the code inside bit(). + * This is OK as long as the _1wire_i<> always check speed. + * \param s The desired speed + */ + template + void _1wire_uart_i::speed (Speed s) { + switch (_speed = s) { + case Speed::STD: impl().UART_BR (BR_STD); break; + case Speed::OVDR: impl().UART_BR (BR_OVR); break; + } + } + + /*! + * \brief + * Generate a 1-wire reset + * + * --- ---- - - - ------- + * Reset \ / \ X X X / + * -------------------- - - - - + * RS: | | | | | | | | | | | + * bit: ST 0 1 2 3 4 5 6 7 SP + * < ---------- 1024/174 usec -------------> + * + * 8 bits, no parity, 1 stop + * Standard: Overdrive : + * BR: 9600, BR: 57600 + * TX: 0xF0, TX: 0xF8 + * RX: 0xF0 not present RX: 0xF8 not present + * less if present less if present + * + * \param t Timing + * \return The status of the operation + * \arg 0 Fail + * \arg 1 Success + */ + template + bool _1wire_uart_i::_reset () { + // Select frame to send + uint8_t w = (_speed == Speed::STD) ? 0xF0 : 0xF8; + // Select baudrate + impl().UART_BR ((_speed == Speed::STD) ? BR_STD_RST : BR_OVR_RST); + // Send frame and check the result + return (impl().UART_RW (w) < w); + } + + /*! + * \brief + * A virtual base class interface implementation. + * Using the private virtual interface we provide the interface from + * _1wire_i + * \param impl_t = virtual_tag + */ + template <> + class _1wire_uart_i : public _1wire_i { + public: + using type = _1wire_uart_i; //!< Export type as identity meta-function + using Speed = typename _1wire_i::Speed; //!< Bring bus speed + + /*! + * \name Object lifetime + */ + //!@{ + protected: + _1wire_uart_i () = default; //!< Allow constructor from derived only + ~_1wire_uart_i () = default; //!< Allow destructor from derived only + //!@} + + /*! + * \name Implementation requirements + */ + //!@{ + private: + /*! + * \brief + * Implementers's (driver) read-write function. We use the following USART configuration. + * - Word Length = 8 Bits + * - Stop Bit = One Stop bit + * - Parity = No parity + * \param The byte to send + * \return The byte received. + * \note + * Due to the nature of the technique, the received byte is the actual bus + * condition during the communication frame (time slot) + */ + virtual byte_t UART_RW (byte_t byte) =0; + + /*! + * \brief + * Implementers's (driver) baudrate function. + * \param The desired Baudrate + */ + virtual void UART_BR (uint32_t br) =0; + //!@} + + //! \name Implementation of base requirements + //!@{ + private: + //! 1-wire UART baudrates + enum BR { + BR_STD_RST =9600, + BR_OVR_RST =57600, + BR_STD =115200, + BR_OVR =921600 + }; + Speed _speed {Speed::STD}; + + Speed speed () { return _speed; } //!< Get the 1-wire bus speed + void speed (Speed s); //!< Set the 1-wire bus speed + + /*! + * \brief + * Read a bit from the 1-Wire bus, return it and provide + * the recovery time. + * + * --- - - - - - - - - - - - - - - - - ------ + * Read \ / X X X X X X X X X X X X X X X / + * ---- - - - - - - - - - - - - - - - - + * RS: | | | | | | | | | | | + * bit: ST 0 1 2 3 4 5 6 7 SP + * < ------------- 87/11 usec -------------> + * ^ + * | + * Master sample + * + * 8 bits, no parity, 1 stop + * standard: BR: 115200 + * Overdrive: BR: 921600 + * TX: 0xFF + * RX: {1 - 0xFF, 0 - [0x00 - 0xFE] } + * + * \return The answer + * \arg 0 Read 0 + * \arg 1 Read 1 (This is also returned on transition error). + */ + bool bit () { + return (UART_RW (0xFF) < 0xFF); + } + + + /*! + * \brief + * Send a 1-Wire write bit + * 8 bits, no parity, 1 stop + * standard: BR: 115200 + * Overdrive: BR: 921600 + * + * --- -------------------------------------- + * Write 1 \ / + * ---- + * RS: | | | | | | | | | | | + * bit: ST 0 1 2 3 4 5 6 7 SP + * TX: 0xFF RX: 0xFF + * + * --- ------ + * Write 0 \ / + * ------------------------------------- + * RS: | | | | | | | | | | | + * bit: ST 0 1 2 3 4 5 6 7 SP + * < ------------- 87/11 usec -------------> + * TX: 0x00 RX: 0x00 + * + * \param b The bit to send + * \return Status of the operation + * \arg 0 Fail + * \arg 1 Success + */ + bool bit (bool b) { + uint8_t w = (b) ? 0xFF : 0x00; // Select write frame to send + return (w == UART_RW (w)); // Return status + } + bool _reset (); + //!@} + }; + + /*! + * Set the 1-wire bus speed for normal operation only + * \note + * We have moved the BR set functionality here to reduce the code inside bit(). + * This is OK as long as the _1wire_i<> always check speed. + * \param s The desired speed + */ + void _1wire_uart_i::speed (Speed s) { + switch (_speed = s) { + case Speed::STD: UART_BR (BR_STD); break; + case Speed::OVDR: UART_BR (BR_OVR); break; + } + } + + /*! + * \brief + * Generate a 1-wire reset + * + * --- ---- - - - ------- + * Reset \ / \ X X X / + * -------------------- - - - - + * RS: | | | | | | | | | | | + * bit: ST 0 1 2 3 4 5 6 7 SP + * < ---------- 1024/174 usec -------------> + * + * 8 bits, no parity, 1 stop + * Standard: Overdrive : + * BR: 9600, BR: 57600 + * TX: 0xF0, TX: 0xF8 + * RX: 0xF0 not present RX: 0xF8 not present + * less if present less if present + * + * \param t Timing + * \return The status of the operation + * \arg 0 Fail + * \arg 1 Success + */ + bool _1wire_uart_i::_reset () { + // Select frame to send + uint8_t w = (_speed == Speed::STD) ? 0xF0 : 0xF8; + // Select baudrate + UART_BR ((_speed == Speed::STD) ? BR_STD_RST : BR_OVR_RST); + // Send frame and check the result + return (UART_RW (w) < w); + } + +//!@} + +} // namespace utl + +#endif /* __utl_com_1wire_uart_h__ */