/*
 * drv8350.hpp
 *
 *  Created on: 7 мар. 2019 г.
 *      Author: Lityagin Aleksandr
 *              alexrayne <alexraynepe196@gmail.com>
  ------------------------------------------------------------------------
    Copyright (c) alexrayne

   All rights reserved.
   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are met:
   - Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
   - Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.
   - Neither the name of ARM nor the names of its contributors may be used
     to endorse or promote products derived from this software without
     specific prior written permission.
   *
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   POSSIBILITY OF SUCH DAMAGE. *
 */

#ifndef HAL_DEV_DRV8350DEVICE_HPP_
#define HAL_DEV_DRV8350DEVICE_HPP_

#include <cslr.h>
#include <ssp_hal.hpp>
#include "drv8350.hpp"
#include <gpio.h>




class DRV8350_Device
    : public SSP_Client
    , public Power_Manager
{
    public:
        typedef SSP_Client inherited;

        DRV8350_Device();

        enum {
            mode_spi = ssp_t::msCLKLO | ssp_t::msCLKFALL
                      | ssp_t::msNB16 | ssp_t::msCS_KEEP
        };
        void connect(ssp_t*    _port
                , unsigned mode = mode_spi
                , unsigned freq = ssp_t::speedKEEP)
        {
            inherited::connect(_port, mode, freq);
        };

        struct INIT {
            ssp_t*      port;
            PIN_INIT    enable_pin;
            PIN_INIT    fault_pin;
        };
        DevResult init( const INIT* x, int csid );

    public:

        typedef DRV8350::REG_ADDRid REG_ADDRid;
        typedef DRV8350::word_t     reg_t;

        // \return >= 0 - read value
        // \return < 0  - DevResult error code
        IOResult write_reg(REG_ADDRid rid, unsigned x);
        IOResult read_reg(REG_ADDRid rid);

        //Power_Manager
        virtual int setPower(POWER_STATE state);
        void enable( bool onoff = true);
        bool enabled() const {return  Power_Manager::state <= WORK;};

        // pulse ENABLE for reset device
        void reset();

        bool is_enabled() const { return gpio_pins(enable_pin) != 0;};
        bool is_fault() const { return gpio_pins(fault_pin) != 0;};

        // \return true - if gateh.locked field in LOCK state
        bool is_locked();
        void lock_registers(bool onoff);

        // вкл/выкл режимеа торможения - все нижние вентили вкл и коротят обмотки
        bool brake(bool onoff);
        bool is_brake();

    public:
        enum ModeID {
            // общее срабатывание всех полумостов на защиту певышения тока
              modeOCP_COMMON    = DRV8350::regDRIVE_CTRL::OCP_ACT
            , modePWM3          = DRV8350::regDRIVE_CTRL::PwmMode_3
            , modePWM6          = DRV8350::regDRIVE_CTRL::PwmMode_6
            // замыкание всех обмоток == торможение
            , modeBREAK         = DRV8350::regDRIVE_CTRL::BRAKE
            // обрыв всех обмоток == свободное кручение
            , modeCOAST         = DRV8350::regDRIVE_CTRL::COAST

            , modeDT_50ns        = DRV8350::regOCP_CTRL::DeadTime_50_ns <<16
            , modeDT_100ns       = DRV8350::regOCP_CTRL::DeadTime_100_ns<<16
            , modeDT_200ns       = DRV8350::regOCP_CTRL::DeadTime_200_ns<<16
            , modeDT_400ns       = DRV8350::regOCP_CTRL::DeadTime_400_ns<<16
        };
        typedef unsigned mode_t;

        void mode(mode_t m);
        mode_t mode();

        //OverCurrentProtection mode
        enum OCPModeID{
              ocpLatch          = DRV8350::regOCP_CTRL::Latched_Shutdown
            , ocpRetry          = DRV8350::regOCP_CTRL::Automatic_Retry
            , ocpReport         = DRV8350::regOCP_CTRL::Report_Only
            , ocpOFF            = DRV8350::regOCP_CTRL::Disable_OCP
            , ocpRETRY_50US     = DRV8350::regOCP_CTRL::TRETRY_50us
            , ocpRETRY_8US      = DRV8350::regOCP_CTRL::TRETRY_8us
            // ocp ClearByCycle - clear OCP fault on every PWM
            , ocpCBC            = 0x10000
            , ocpMode_Msk       = ocpCBC
                                | ocpRETRY_50US
                                | DRV8350::regOCP_CTRL::OCPMode_Msk
        };
        typedef unsigned mode_ocp_t;
        void mode_ocp(mode_ocp_t m);

        typedef DRV8350::ISink_ID ISink_ID;
        typedef DRV8350::ISour_ID ISour_ID;
        void mode_gate_strong(ISink_ID isink, ISour_ID isour);

        typedef DRV8350::regOCP_CTRL::VDSLVLid OCP_VDSLevel;
        enum OCP_SenceLevelID{
              ocpSEN_0v25 = DRV8350::regCSA_CTRL::SEN_Lvl_Ocp_0p25
            , ocpSEN_0v50 = DRV8350::regCSA_CTRL::SEN_Lvl_Ocp_0p50
            , ocpSEN_0v75 = DRV8350::regCSA_CTRL::SEN_Lvl_Ocp_0p75
            , ocpSEN_1v   = DRV8350::regCSA_CTRL::SEN_Lvl_Ocp_1p00
            , ocpSEN_DISABLE = DRV8350::regCSA_CTRL::DIS_SEN
        };
        typedef unsigned mode_ocp_sence_level_t;
        void mode_ocp_level(OCP_VDSLevel vds, mode_ocp_sence_level_t sen);

        // настройка усиления сенсора тока
        enum GainID{
              gain5  = DRV8350::regCSA_CTRL::Gain_5VpV
            , gain10 = DRV8350::regCSA_CTRL::Gain_10VpV
            , gain20 = DRV8350::regCSA_CTRL::Gain_20VpV
            , gain40 = DRV8350::regCSA_CTRL::Gain_40VpV
        };
        void mode_isen_gain(GainID x);

        // в статусе содержится 2 регистра regFAULT_STAT и regVGS_STAT
        typedef DRV8350::regFAULT_STAT  StatFaults;
        typedef DRV8350::regVGS_STAT    StatVGS;
        union status_t{
            struct {
                StatFaults  faults;
                StatVGS     vgs;
            };
            // raw < 0 - spi transfer failure
            long            raw;
            bool is_valid() const {return raw != ~0ul;};
        };

        // \return .war < 0 - spi transfer failure
        status_t status();

        // содержит последний результат опроса
        status_t last_status;

        // сбросить флаги сбоев в драйвере
        void fault_clear();

    public:
        DRV8350::AllRegs    regs_setup;
        // write all config registers from regs_setup
        void reinit();

    protected:
        const PIN_INIT* enable_pin;
        const PIN_INIT* fault_pin;

        // ENABLE pin control
        void enable_gates( bool onoff = true){
            if (onoff)
                gpio_on(enable_pin);
            else
                gpio_off(enable_pin);
        };
        bool enabled_gates() const {
            return gpio_pins(enable_pin) != 0;
        }

        enum {
            // поля regOCP управляемые mode_cache:16+
            modeOCP_Msk = DRV8350::regOCP_CTRL::DeadTime_Msk
        };
        mode_t  mode_cache;
        void    mode_cache_refresh();

        // последовательное чтение регистров требует паузы между словам
        unsigned    last_access_tick;
        unsigned    access_timeout;

        void    reg_timeout();

};



#include <lib/cli.hpp>

class DRV8350_CLI
        : public virtual cli::CLI_OnOffCommand
{
    public:
        typedef  cli::CLI_OnOffCommand     inherited;
        DRV8350_CLI(const_cmd name, DRV8350_Device& target );

    public:
        //CLICommand
        virtual int cli_process(CLI_shell* shell, const_line line);
        //* печатает хелп команды в шелл
        virtual void cli_help(CLI_shell* shell);

        //CLI_onoff
        virtual void cli_onoff(bool onoff);

    public:
        typedef  DRV8350_Device     io_t;
        typedef  io_t::REG_ADDRid   addr_id;

        int cli_process_show(CLI_shell* shell, const_line line);
        int cli_process_set(CLI_shell* shell, const_line line);

        void cli_show_stat(CLI_shell* shell);
        void cli_brake_onoff(CLI_shell* shell, bool onoff);

    public:
        DRV8350_Device* io;
};



#endif /* HAL_DEV_DRV8350_HPP_ */
