/*!
 * \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 functionality. The implementation
 *    expects:
 *    1) An Open drain tx and a 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 <http://www.gnu.org/licenses/>.
 *
 */
#ifndef __utl_com_1wire_uart_h__
#define __utl_com_1wire_uart_h__

#include <utl/core/impl.h>
#include <utl/core/crtp.h>
#include <utl/com/_1wire.h>

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 <typename Impl_t>
  class _1wire_uart_i : public _1wire_i<_1wire_uart_i<Impl_t>> {
     _CRTP_IMPL(Impl_t);   //! \brief  Syntactic sugar to CRTP casting
     friend _1wire_i <_1wire_uart_i<Impl_t>>;
  public:
     using type    = _1wire_uart_i<Impl_t>;          //!< Export type as identity meta-function
     using Speed   = typename _1wire_i<type>::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
   */
  //!@{
  private:
     /*!
      * \brief
      *    Implementers's (driver) read-write function. We expect 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 () const { return _speed; } //!< Get the 1-wire bus speed
     void speed (Speed s);                   //!< Set the 1-wire bus speed

     /*!
      * \brief
      *    Send a 1-Wire write bit and read the response
      *
      * Write 1   ---       --------------------------------------
      * Read 0/1     \     /
      *               ----
      *  RS:          |   |   |   |   |   |   |   |   |   |   |
      *  bit:          ST   0   1   2   3   4   5   6   7   SP
      *  TX: 0xFF      RX: 0xFF->1, less->0
      *
      * Write 0   ---                                       ------
      * Read 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        The response
      */
     bool bit (bool b) {
        if (b)
           return (UART_RW (0xFF) < 0xFF) ? 0 : 1;
        else {
           UART_RW (0x00);
           return 0;
        }
     }
     bool _reset (Speed s);
  //!@}
  };

  /*!
   * 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 <typename Impl_t>
  void _1wire_uart_i<Impl_t>::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 ------------->
   *
   *  TX: (STD)0xF0, (OVDR)0xF8    RX: less if present
   *
   * \param   t  Timing
   * \return  The status of the operation
   *    \arg  0  Fail
   *    \arg  1  Success
   */
  template <typename Impl_t>
  bool _1wire_uart_i<Impl_t>::_reset (Speed s) {
     // Select frame to send
     uint8_t   w = ((_speed = s) == 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<virtual_tag>
    * \param   impl_t = virtual_tag
    */
   template <>
   class _1wire_uart_i<virtual_tag> : public _1wire_i<virtual_tag> {
   public:
      using type    = _1wire_uart_i<virtual_tag>;              //!< Export type as identity meta-function
      using Speed   = typename _1wire_i<virtual_tag>::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 () const { return _speed; }   //!< Get the 1-wire bus speed
      void speed (Speed s);                     //!< Set the 1-wire bus speed

      /*!
       * \brief
       *    Send a 1-Wire write bit and read the response
       *
       * Write 1   ---       --------------------------------------
       * Read 0/1     \     /
       *               ----
       *  RS:          |   |   |   |   |   |   |   |   |   |   |
       *  bit:          ST   0   1   2   3   4   5   6   7   SP
       *  TX: 0xFF      RX: 0xFF->1, less->0
       *
       * Write 0   ---                                       ------
       * Read 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        The response
       */
      bool bit (bool b) {
         if (b)
            return (UART_RW (0xFF) < 0xFF) ? 0 : 1;
         else {
            UART_RW (0x00);
            return 0;
         }
      }
      bool _reset (Speed s);
   //!@}
   };

   /*!
    * 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<virtual_tag>::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 ------------->
    *
    *  TX: (STD)0xF0, (OVDR)0xF8    RX: less if present
    *
    * \param   t  Timing
    * \return  The status of the operation
    *    \arg  0  Fail
    *    \arg  1  Success
    */
   bool _1wire_uart_i<virtual_tag>::_reset (Speed s) {
      // Select frame to send
      uint8_t   w = ((_speed = s) == 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__ */
