3D Low Frequency Wakeup Receiver

AS3933.cpp

Committer:
mcm
Date:
2018-03-13
Revision:
4:10d482ca4eb1
Parent:
3:2de552c4ffbc

File content as of revision 4:10d482ca4eb1:

/**
 * @brief       AS3933.cpp
 * @details     3D Low Frequency Wakeup Receiver.
 *              Function file.
 *
 *
 * @return      N/A
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018    The ORIGIN
 * @pre         N/A.
 * @warning     N/A
 * @pre         This code belongs to Nimbus Centre ( http://www.nimbus.cit.ie ).
 */

#include "AS3933.h"


AS3933::AS3933 ( PinName mosi, PinName miso, PinName sclk, PinName cs, uint32_t freq )
    : _spi ( mosi, miso, sclk )
    , _cs  ( cs )
{
    _spi.frequency( freq );
    _spi.format   ( 8, 1 );                                                     // 8-bits, mode1: CPOL = 0 | CPHA = 1
}


AS3933::~AS3933()
{
}



/**
 * @brief       AS3933_SetLowPowerMode   ( AS3933_channels_enable_t , AS3933_scanning_mode_t , AS3933_r4_t_off_value_t  )
 *
 * @details     It configures the low power mode.
 *
 * @param[in]    myEnabledChannels: Number of channels enabled.
 * @param[in]    myLowPowerMode:    Low power mode.
 * @param[in]    myT_Off:           Period only if ON/OFF mode is selected ( in myLowPowerMode ), it is ignored otherwise.
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_SetLowPowerMode.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A
 * @warning     Data-sheet p.14 8.1.1 Listening Mode. It is NOT clear which channels have to be enabled according to the
 *              low power mode: '...If the three dimensional detection is not required, then it is possible to deactivate one
 *              or more channels. In case only two channels are required, then the deactivated channel must be the number
 *              three; while in case only one channel is needed, then the active channel must be the number one'.
 *
 *              Data-sheet p.37 8.10 Channel Selection in Scanning Mode and ON/OFF Mode: 'In case only 2 channels are active and
 *              one of the Low Power modes is enabled, then the channels 1 and 3 have to be active. If the chip works in On-Off
 *              mode and only one channel is active then the active channel has to be the channel 1.'.
 *
 *              This function follows the methodology in: Low Power Modes/Channels enabled: Data-sheet p.37 8.10 Channel
 *              Selection in Scanning Mode and ON/OFF Mode.
 */
AS3933::AS3933_status_t  AS3933::AS3933_SetLowPowerMode ( AS3933_channels_enable_t myEnabledChannels, AS3933_scanning_mode_t myLowPowerMode, AS3933_r4_t_off_value_t myT_Off )
{
    char   cmd[]   =    { 0, 0 };
    int    mySPI_status;


    // Read R0 register
    cmd[0]           =   ( AS3933_READ | AS3933_R0 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask Channels 1:3
    cmd[1] &=   ~( EN_A1_MASK | EN_A2_MASK | EN_A3_MASK );

    // Mask Scanning Mode
    cmd[1] &=   ~( MUX_123_MASK | ON_OFF_MASK );



    // Set the channels enabled
    switch ( myEnabledChannels ) {
        case AS3933_CH1_OFF_CH2_OFF_CH3_OFF:
            cmd[1] &=   ( EN_A1_DISABLED | EN_A2_DISABLED | EN_A3_DISABLED );
            break;

        case AS3933_CH1_ON_CH2_OFF_CH3_OFF:
            cmd[1] |=   ( EN_A1_ENABLED );
            break;

        case AS3933_CH1_OFF_CH2_ON_CH3_OFF:
            cmd[1] |=   ( EN_A2_ENABLED );
            break;

        case AS3933_CH1_ON_CH2_ON_CH3_OFF:
            cmd[1] |=   ( EN_A1_ENABLED | EN_A2_ENABLED );
            break;

        case AS3933_CH1_OFF_CH2_OFF_CH3_ON:
            cmd[1] |=   ( EN_A3_ENABLED );
            break;

        case AS3933_CH1_ON_CH2_OFF_CH3_ON:
            cmd[1] |=   ( EN_A1_ENABLED | EN_A3_ENABLED );
            break;

        case AS3933_CH1_OFF_CH2_ON_CH3_ON:
            cmd[1] |=   ( EN_A2_ENABLED | EN_A3_ENABLED );
            break;

        default:
        case AS3933_CH1_ON_CH2_ON_CH3_ON:
            cmd[1] |=   ( EN_A1_ENABLED | EN_A2_ENABLED | EN_A3_ENABLED );
            break;
    }


    // Set Scanning mode
    switch ( myLowPowerMode ) {
        default:
        case AS3933_STANDARD_LISTENING_MODE:
            break;

        case AS3933_SCANNING_MODE:
            if ( ( myEnabledChannels == AS3933_CH1_ON_CH2_OFF_CH3_ON )  || ( myEnabledChannels == AS3933_CH1_ON_CH2_ON_CH3_ON )  || ( myEnabledChannels == AS3933_CH1_ON_CH2_OFF_CH3_OFF ) ||
                    ( myEnabledChannels == AS3933_CH1_OFF_CH2_ON_CH3_OFF ) || ( myEnabledChannels == AS3933_CH1_OFF_CH2_OFF_CH3_ON ) )
                cmd[1] |=   ( MUX_123_ENABLED );
            else
                return   AS3933_FAILURE;

            break;

        case AS3933_ON_OFF_MODE:
            if ( ( myEnabledChannels == AS3933_CH1_ON_CH2_OFF_CH3_ON ) || ( myEnabledChannels == AS3933_CH1_ON_CH2_ON_CH3_ON ) || ( myEnabledChannels == AS3933_CH1_ON_CH2_OFF_CH3_OFF ) )
                cmd[1] |=   ( ON_OFF_ENABLED );
            else
                return   AS3933_FAILURE;
            break;
    }

    // Update power mode and active channels
    cmd[0]           =   ( AS3933_WRITE | AS3933_R0 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;


    if ( myLowPowerMode == AS3933_ON_OFF_MODE ) {
        // Read R4 register
        cmd[0]           =    ( AS3933_READ | AS3933_R4 );
        _cs              =   1;
        mySPI_status     =    _spi.write ( &cmd[0], 1, &cmd[0], 2 );
        _cs              =   0;

        // Mask T_OFF
        cmd[1]          &=   ~( T_OFF_MASK );

        // Update Off time in ON/OFF operation mode
        cmd[1]          |=    ( myT_Off );

        cmd[0]           =    ( AS3933_WRITE | AS3933_R4 );
        _cs              =   1;
        mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
        _cs              =   0;
    }



    if ( ( mySPI_status / ( sizeof( cmd )/sizeof( cmd[0] ) ) ) == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_SetArtificialWakeUp   ( AS3933_r8_t_auto_value_t )
 *
 * @details     It configures the artificial wakeup.
 *
 * @param[in]    myArtificialWakeUp:    Period only if ON/OFF mode is selected ( in myLowPowerMode ), it is ignored otherwise.
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_SetArtificialWakeUp.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_SetArtificialWakeUp ( AS3933_r8_t_auto_value_t myArtificialWakeUp )
{
    char    cmd[]   =    { 0, 0 };
    int     mySPI_status;


    // Read R8 register
    cmd[0]           =   ( AS3933_READ | AS3933_R8 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask Artificial wakeup
    cmd[1] &=   ~( T_AUTO_MASK );

    // Update the value
    cmd[1] |=    myArtificialWakeUp;


    // Update artificial wakeup
    cmd[0]           =   ( AS3933_WRITE | AS3933_R8 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;




    if ( ( mySPI_status / ( sizeof( cmd )/sizeof( cmd[0] ) ) ) == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_ReadFalseWakeUpRegister   ( AS3933_data_t* )
 *
 * @details     It gets feedback on the surrounding environment reading the false wakeup register.
 *
 * @param[in]    N/A
 *
 * @param[out]   myF_WAKE:              F_WAKE register.
 *
 *
 * @return       Status of AS3933_ReadFalseWakeUpRegister.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_ReadFalseWakeUpRegister ( AS3933_data_t* myF_WAKE )
{
    char    cmd[]   =    { ( AS3933_READ | AS3933_R13 ), 0 };
    int     mySPI_status;


    // Read R13 register
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;


    myF_WAKE->f_wake     =   cmd[1];



    if ( mySPI_status == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_SetClockGenerator   ( AS3933_r1_en_xtal_value_t, AS3933_r16_clock_gen_dis_value_t )
 *
 * @details     It configures the clock generator.
 *
 * @param[in]    myClockGenerator:              Enable/Disable external crystal oscillator.
 * @param[in]    myClockGeneratorOutputMode:    Enable/Disable the clock generator output signal displayed on CL_DAT pin.
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_SetClockGenerator.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_SetClockGenerator ( AS3933_r1_en_xtal_value_t myClockGenerator, AS3933_r16_clock_gen_dis_value_t myClockGeneratorOutputMode )
{
    char    cmd[]   =    { 0, 0 };
    int     mySPI_status;


    // Configure the Crystal oscillator
    // Read R1 register
    cmd[0]           =   ( AS3933_READ | AS3933_R1 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask Crystal oscillator
    cmd[1] &=   ~( EN_XTAL_MASK );

    // Update the value
    cmd[1] |=    myClockGenerator;


    // Update Crystal oscillator
    cmd[0]           =   ( AS3933_WRITE | AS3933_R1 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;


    // Configure the Clock Generator Output
    // Read R16 register
    cmd[0]           =   ( AS3933_READ | AS3933_R16 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask Clock Generator output signal
    cmd[1] &=   ~( CLOCK_GEN_DIS_MASK );

    // Update the value
    cmd[1] |=    myClockGeneratorOutputMode;


    // Update Clock Generator output signal
    cmd[0]           =   ( AS3933_WRITE | AS3933_R16 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;




    if ( ( mySPI_status / ( sizeof( cmd )/sizeof( cmd[0] ) ) ) == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_CalibrateRC_Oscillator   ( AS3933_r1_en_xtal_value_t, AS3933_r16_clock_gen_dis_value_t )
 *
 * @details     It calibrates RC oscillator.
 *
 * @param[in]    N/A
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_CalibrateRC_Oscillator.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     12/March/2018       The timeout was removed, the bits for calibration are checked instead.
 *              7/March/2018        The ORIGIN
 * @pre         RC-Oscillator: Self Calibration is ONLY available.
 * @pre         There is NO need to calibrate the RC oscillator if the crystal oscillator is enabled.
 * @warning     In case the pattern detection and the Manchester decoder are not enabled ( R1<1>=0 and R1<3>=1 ) the calibration on the RC-oscillator
 *              is not needed. Should this not be the case, the RC-oscillator has to be calibrated.
 */
AS3933::AS3933_status_t  AS3933::AS3933_CalibrateRC_Oscillator ( void )
{
    char        cmd[]       =    { 0, 0 };
    int         mySPI_status;


    // Start the calibration
    cmd[0]           =   ( AS3933_DIRECT_COMMAND | CALIB_RCO_LC );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;


    // RC oscillator will be calibrated when RC_CAL_OK = '1' ( R14<6> )
    do {
        cmd[0]           =   ( AS3933_READ | AS3933_R14 );
        _cs              =   1;
        mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
        _cs              =   0;
    } while ( ( ( cmd[1] & RC_CAL_OK_MASK ) != RC_CAL_OK_HIGH ) && ( ( cmd[1] & RC_CAL_KO_MASK ) != RC_CAL_KO_HIGH ) );



    if ( ( mySPI_status == SPI_SUCCESS ) && ( ( cmd[1] & RC_CAL_KO_MASK ) != RC_CAL_KO_HIGH ) )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_SetAntennaDamper   ( AS3933_r1_att_on_value_t, AS3933_r4_d_res_value_t )
 *
 * @details     It configures the antenna damper and its shunt resistor.
 *
 * @param[in]    myAntennaDamperMode:   Enable/Disable antenna dumper.
 * @param[in]    myShuntResistor:       Configure antenna dumping resistor.
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_SetAntennaDamper.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_SetAntennaDamper ( AS3933_r1_att_on_value_t myAntennaDamperMode, AS3933_r4_d_res_value_t myShuntResistor )
{
    char    cmd[]       =    { 0, 0 };
    int     mySPI_status;


    // Configure Antenna dumper
    // Read R1 register
    cmd[0]           =   ( AS3933_READ | AS3933_R1 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask Antenna dumper
    cmd[1]          &=  ~( ATT_ON_MASK );

    // Update Antenna dumper
    cmd[0]           =   ( AS3933_WRITE | AS3933_R1 );
    cmd[1]          |=   ( myAntennaDamperMode );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;


    // Configure Antenna dumper resistor
    // Read R4 register
    cmd[0]           =   ( AS3933_READ | AS3933_R4 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask Antenna dumper resistor
    cmd[1]          &=  ~( D_RES_MASK );

    // Update Antenna dumper resistor
    cmd[0]           =   ( AS3933_WRITE | AS3933_R4 );
    cmd[1]          |=   ( myShuntResistor );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;




    if ( ( mySPI_status / ( sizeof( cmd )/sizeof( cmd[0] ) ) ) == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_SetEnvelopDetector   ( AS3933_r3_fs_env_value_t )
 *
 * @details     It configures the envelop detector for different symbol rates.
 *
 * @param[in]    mySymbolRates:     Envelop detector time constant.
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_SetEnvelopDetector.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_SetEnvelopDetector ( AS3933_r3_fs_env_value_t mySymbolRates )
{
    char    cmd[]       =    { 0, 0 };
    int     mySPI_status;


    // Read R3 register
    cmd[0]           =   ( AS3933_READ | AS3933_R3 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask Symbol rate
    cmd[1]          &=  ~( FS_ENV_MASK );

    // Update symbol rate
    cmd[0]           =   ( AS3933_WRITE | AS3933_R3 );
    cmd[1]          |=   ( mySymbolRates );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;





    if ( ( mySPI_status / ( sizeof( cmd )/sizeof( cmd[0] ) ) ) == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_SetDataSlicer   ( AS3933_r1_abs_hy_value_t , AS3933_r3_fs_scl_value_t )
 *
 * @details     It configures the data slicer for different preamble length.
 *
 * @param[in]    myAbsoluteThresholdMode:   Enable Data slicer absolute reference.
 * @param[in]    myMinimumPreambleLength:   Data slices time constant.
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_SetDataSlicer.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_SetDataSlicer ( AS3933_r1_abs_hy_value_t myAbsoluteThresholdMode, AS3933_r3_fs_scl_value_t myMinimumPreambleLength )
{
    char    cmd[]       =    { 0, 0 };
    int     mySPI_status;


    // Configure Data slicer absolute reference
    // Read R1 register
    cmd[0]           =   ( AS3933_READ | AS3933_R1 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask Data slicer absolute reference
    cmd[1]          &=  ~( ABS_HY_MASK );

    // Update symbol rate
    cmd[0]           =   ( AS3933_WRITE | AS3933_R1 );
    cmd[1]          |=   ( myAbsoluteThresholdMode );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;


    // Configure Data slices time constant
    // Read R3 register
    cmd[0]           =   ( AS3933_READ | AS3933_R3 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask Data slices time constant
    cmd[1]          &=  ~( FS_SCL_MASK );

    // Update Data slices time constant
    cmd[0]           =   ( AS3933_WRITE | AS3933_R3 );
    cmd[1]          |=   ( myMinimumPreambleLength );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;




    if ( ( mySPI_status / ( sizeof( cmd )/sizeof( cmd[0] ) ) ) == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_SetComparatorHysteresis   ( AS3933_r3_hy_pos_value_t , AS3933_r3_hy_20m_value_t )
 *
 * @details     It configures the hysteresis on the data slicer comparator.
 *
 * @param[in]    myHysteresisMode:  Data slicer hysteresis, edge.
 * @param[in]    myHysteresisRange: Data slicer hysteresis, comparator.
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_SetComparatorHysteresis.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_SetComparatorHysteresis ( AS3933_r3_hy_pos_value_t myHysteresisMode, AS3933_r3_hy_20m_value_t myHysteresisRange )
{
    char    cmd[]       =    { 0, 0 };
    int     mySPI_status;


    // Read R3 register
    cmd[0]           =   ( AS3933_READ | AS3933_R3 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask both hysteresis edge and comparator
    cmd[1]          &=  ~( HY_POS_MASK | HY_20M_MASK );

    // Update hysteresis on the data slicer comparator
    cmd[0]           =   ( AS3933_WRITE | AS3933_R3 );
    cmd[1]          |=   ( myHysteresisMode | myHysteresisRange );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;




    if ( ( mySPI_status / ( sizeof( cmd )/sizeof( cmd[0] ) ) ) == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_SetGainReduction   ( AS3933_r4_gr_value_t )
 *
 * @details     It configures the gain reduction.
 *
 * @param[in]    myGainReductionValue:  Gain reduction.
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_SetGainReduction.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_SetGainReduction ( AS3933_r4_gr_value_t myGainReductionValue )
{
    char    cmd[]       =    { 0, 0 };
    int     mySPI_status;


    // Read R4 register
    cmd[0]           =   ( AS3933_READ | AS3933_R4 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask Gain reduction
    cmd[1]          &=  ~( GR_MASK );

    // Update Gain reduction
    cmd[0]           =   ( AS3933_WRITE | AS3933_R4 );
    cmd[1]          |=   ( myGainReductionValue );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;




    if ( ( mySPI_status / ( sizeof( cmd )/sizeof( cmd[0] ) ) ) == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_SetOperatingFrequencyRange   ( AS3933_r8_band_sel_value_t )
 *
 * @details     It configures the operating frequency range.
 *
 * @param[in]    myOperatingFrequencyRange: Band selection.
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_SetOperatingFrequencyRange.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_SetOperatingFrequencyRange ( AS3933_r8_band_sel_value_t myOperatingFrequencyRange )
{
    char    cmd[]       =    { 0, 0 };
    int     mySPI_status;


    // Read R8 register
    cmd[0]           =   ( AS3933_READ | AS3933_R8 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask Band selection
    cmd[1]          &=  ~( BAND_SEL_MASK );

    // Update Band selection
    cmd[0]           =   ( AS3933_WRITE | AS3933_R8 );
    cmd[1]          |=   ( myOperatingFrequencyRange );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;




    if ( ( mySPI_status / ( sizeof( cmd )/sizeof( cmd[0] ) ) ) == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_SetFrequencyDetectionTolerance   ( AS3933_tolerance_settings_t )
 *
 * @details     It configures the frequency detection tolerance.
 *
 * @param[in]    myTolerance:       Tolerance band.
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_SetFrequencyDetectionTolerance.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_SetFrequencyDetectionTolerance ( AS3933_tolerance_settings_t myTolerance )
{
    char    cmd[]       =    { 0, 0 };
    int     mySPI_status;


    // Read R2 register
    cmd[0]           =   ( AS3933_READ | AS3933_R2 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask Tolerance band
    cmd[1]          &=  ~( AS3933_TOLERANCE_MASK );

    // Update Tolerance band
    cmd[0]           =   ( AS3933_WRITE | AS3933_R2 );
    cmd[1]          |=   ( myTolerance );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;




    if ( ( mySPI_status / ( sizeof( cmd )/sizeof( cmd[0] ) ) ) == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_SetGainBoost   ( AS3933_r2_g_boost_value_t )
 *
 * @details     It configures the +3dB gain boost.
 *
 * @param[in]    myGainBoostMode:   Enable/Disable +3dB Amplifier Gain Boost.
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_SetGainBoost.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_SetGainBoost ( AS3933_r2_g_boost_value_t myGainBoostMode )
{
    char    cmd[]       =    { 0, 0 };
    int     mySPI_status;


    // Read R2 register
    cmd[0]           =   ( AS3933_READ | AS3933_R2 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask Gain boost
    cmd[1]          &=  ~( G_BOOST_MASK );

    // Update Gain boost
    cmd[0]           =   ( AS3933_WRITE | AS3933_R2 );
    cmd[1]          |=   ( myGainBoostMode );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;




    if ( ( mySPI_status / ( sizeof( cmd )/sizeof( cmd[0] ) ) ) == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_SetAGC   ( AS3933_r1_agc_tlim_value_t , AS3933_r1_agc_ud_value_t )
 *
 * @details     It configures the Automatic Gain Control ( AGC ).
 *
 * @param[in]    myAGC_CarrierBurstMode:    Enable/Disable AGC acting only on the first carrier burst.
 * @param[in]    myAGC_OperatingDirection:  Configure AGC direction operating.
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_SetAGC.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_SetAGC ( AS3933_r1_agc_tlim_value_t myAGC_CarrierBurstMode, AS3933_r1_agc_ud_value_t myAGC_OperatingDirection )
{
    char    cmd[]       =    { 0, 0 };
    int     mySPI_status;


    // Read R1 register
    cmd[0]           =   ( AS3933_READ | AS3933_R1 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask both AGC acting only on the first carrier burst and AGC direction operating
    cmd[1]          &=  ~( AGC_TLIM_MASK | AGC_UD_MASK );

    // Update both AGC acting only on the first carrier burst and AGC direction operating
    cmd[0]           =   ( AS3933_WRITE | AS3933_R1 );
    cmd[1]          |=   ( myAGC_CarrierBurstMode | myAGC_OperatingDirection );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;




    if ( ( mySPI_status / ( sizeof( cmd )/sizeof( cmd[0] ) ) ) == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_SetDataMask   ( AS3933_r0_dat_mask_value_t )
 *
 * @details     It configures the mask data before wakeup.
 *
 * @param[in]    myDataMaskMode:    Mask data on DAT pin before wakeup happens.
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_SetDataMask.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_SetDataMask ( AS3933_r0_dat_mask_value_t myDataMaskMode )
{
    char    cmd[]       =    { 0, 0 };
    int     mySPI_status;


    // Read R0 register
    cmd[0]           =   ( AS3933_READ | AS3933_R0 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask Mask data on DAT pin before wakeup happens
    cmd[1]          &=  ~( DAT_MASK_MASK );

    // Update Mask data on DAT pin before wakeup happens
    cmd[0]           =   ( AS3933_WRITE | AS3933_R0 );
    cmd[1]          |=   ( myDataMaskMode );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;




    if ( ( mySPI_status / ( sizeof( cmd )/sizeof( cmd[0] ) ) ) == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_SetCorrelator   ( AS3933_r1_en_wpat_value_t , AS3933_r0_patt32_value_t , AS3933_r7_t_hbit_value_t , AS3933_r1_en_manch_value_t )
 *
 * @details     It configures the correlator and the Manchester Decoder.
 *
 * @param[in]    myCorrelatorMode:          Enable/Disable Correlator.
 * @param[in]    mySymbolPattern:           Pattern extended.
 * @param[in]    myRate:                    Bit rate definition.
 * @param[in]    myManchesterDecoderMode:   Enable/Disable Manchester decoder.
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_SetCorrelator.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_SetCorrelator ( AS3933_r1_en_wpat_value_t myCorrelatorMode, AS3933_r0_patt32_value_t mySymbolPattern, AS3933_r7_t_hbit_value_t myRate,
        AS3933_r1_en_manch_value_t myManchesterDecoderMode )
{
    char    cmd[]       =    { 0, 0 };
    int     mySPI_status;


    // Configure Correlator mode and Manchester decoder mode
    // Read R1 register
    cmd[0]           =   ( AS3933_READ | AS3933_R1 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask both Correlator mode and Manchester decoder mode
    cmd[1]          &=  ~( EN_WPAT_MASK | EN_MANCH_MASK );

    // Update both Correlator mode and Manchester decoder mode
    cmd[0]           =   ( AS3933_WRITE | AS3933_R1 );
    cmd[1]          |=   ( myCorrelatorMode | myManchesterDecoderMode );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;


    // Configure Pattern extended
    // Read R0 register
    cmd[0]           =   ( AS3933_READ | AS3933_R0 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask Pattern extended
    cmd[1]          &=  ~( PATT32_MASK );

    // Update Pattern extended
    cmd[0]           =   ( AS3933_WRITE | AS3933_R0 );
    cmd[1]          |=   ( mySymbolPattern );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;


    // Configure Bit rate definition
    // Read R7 register
    cmd[0]           =   ( AS3933_READ | AS3933_R7 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask Bit rate definition
    cmd[1]          &=  ~( T_HBIT_MASK );

    // Update Bit rate definition
    cmd[0]           =   ( AS3933_WRITE | AS3933_R7 );
    cmd[1]          |=   ( myRate );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;





    if ( ( mySPI_status / ( sizeof( cmd )/sizeof( cmd[0] ) ) ) == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_SetWakeUpPattern   ( AS3933_data_t )
 *
 * @details     It sets the wakeup pattern ( Manchester ).
 *
 * @param[in]    myWakeUpPattern:   PATT1B and PATT2B ( Manchester ).
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_SetWakeUpPattern.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_SetWakeUpPattern ( AS3933_data_t myWakeUpPattern )
{
    char    cmd[]       =    { 0, 0 };
    int     mySPI_status;


    // Update PATT2B
    cmd[0]           =   ( AS3933_WRITE | AS3933_R5 );
    cmd[1]           =   ( myWakeUpPattern.patt2b );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;

    // Update PATT1B
    cmd[0]           =   ( AS3933_WRITE | AS3933_R6 );
    cmd[1]           =   ( myWakeUpPattern.patt1b );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;




    if ( ( mySPI_status / ( sizeof( cmd )/sizeof( cmd[0] ) ) ) == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_GetWakeUpPattern   ( AS3933_data_t* )
 *
 * @details     It gets the wakeup pattern ( Manchester ).
 *
 * @param[in]    N/A
 *
 * @param[out]   myWakeUpPattern:   PATT1B and PATT2B ( Manchester ).
 *
 *
 * @return       Status of AS3933_GetWakeUpPattern.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_GetWakeUpPattern ( AS3933_data_t* myWakeUpPattern )
{
    char    cmd[]       =    { 0, 0 };
    int     mySPI_status;


    // Read R5 register
    cmd[0]           =   ( AS3933_READ | AS3933_R5 );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    myWakeUpPattern->patt2b  =   cmd[1];

    // Read R6 register
    cmd[0]           =   ( AS3933_READ | AS3933_R6 );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    myWakeUpPattern->patt1b  =   cmd[1];


    if ( mySPI_status == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_SetAutomaticTimeOut   ( AS3933_r7_t_out_value_t )
 *
 * @details     It sets the automatic time-out setup.
 *
 * @param[in]    myAutomaticTimeOut:    Automatic time-out.
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_SetAutomaticTimeOut.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_SetAutomaticTimeOut ( AS3933_r7_t_out_value_t myAutomaticTimeOut )
{
    char    cmd[]       =    { 0, 0 };
    int     mySPI_status;


    // Read R7 register
    cmd[0]           =   ( AS3933_READ | AS3933_R7 );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask Automatic time-out
    cmd[1]          &=  ~( T_OUT_MASK );

    // Update Automatic time-out
    cmd[0]           =   ( AS3933_WRITE | AS3933_R7 );
    cmd[1]          |=   ( myAutomaticTimeOut );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;




    if ( ( mySPI_status / ( sizeof( cmd )/sizeof( cmd[0] ) ) ) == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_SetParallelTuningCapacitance   ( AS3933_parallel_tuning_channels_t , AS3933_parallel_tuning_capacitance_t )
 *
 * @details     It sets the parallel tuning capacitance on the chosen channel.
 *
 * @param[in]    myChannel:             Channel for Parallel Tuning Capacitance.
 * @param[in]    myAddedCapacitance:    Parallel Tuning Capacitance.
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_SetParallelTuningCapacitance.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_SetParallelTuningCapacitance ( AS3933_parallel_tuning_channels_t myChannel, AS3933_parallel_tuning_capacitance_t myAddedCapacitance )
{
    char    cmd[]       =    { 0, 0 };
    int     mySPI_status;

    AS3933_spi_command_structure_registers_t    myAuxRegister;


    // Select the channel
    switch ( myChannel ) {
        default:
        case AS3933_CHANNEL_LF1P:
            myAuxRegister    =   AS3933_R17;
            break;

        case AS3933_CHANNEL_LF2P:
            myAuxRegister    =   AS3933_R18;
            break;

        case AS3933_CHANNEL_LF3P:
            myAuxRegister    =   AS3933_R19;
            break;
    }


    // Read register
    cmd[0]           =   ( AS3933_READ | myAuxRegister );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Mask Parallel Tuning Capacitance
    cmd[1]          &=  ~( AS3933_CAPACITANCE_MASK );

    // Update Parallel Tuning Capacitance
    cmd[0]           =   ( AS3933_WRITE | myAuxRegister );
    cmd[1]          |=   ( myAddedCapacitance );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), &cmd[0], 2 );
    _cs              =   0;





    if ( ( mySPI_status / ( sizeof( cmd )/sizeof( cmd[0] ) ) ) == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}


/**
 * @brief       AS3933_GetRSSI   ( AS3933_data_t* )
 *
 * @details     It gets the RSSI for all channels.
 *
 * @param[in]    mySPI_parameters:  N/A
 *
 * @param[out]   myChannelRSSI:     RSSI.
 *
 *
 * @return       Status of AS3933_GetRSSI.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     12/March/2018  Auto-increment does NOT work when the registers are read
 *              7/March/2018   The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_GetRSSI ( AS3933_data_t* myChannelRSSI )
{
    char    cmd[]       =    { 0, 0, 0 };
    int     mySPI_status;


    // Get RSSI1
    // Read R10 register
    cmd[0]           =   ( AS3933_READ | AS3933_R10 );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Parse the data
    myChannelRSSI->rssi1     =   ( cmd[1] & RSSI1_MASK );                       // Channel1: RSSI1


    // Get RSSI3
    // Read R11 register
    cmd[0]           =   ( AS3933_READ | AS3933_R11 );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Parse the data
    myChannelRSSI->rssi3     =   ( cmd[1] & RSSI3_MASK );                       // Channel1: RSSI1


    // Get RSSI2
    // Read R12 register
    cmd[0]           =   ( AS3933_READ | AS3933_R12 );
    _cs              =   1;
    mySPI_status     =    _spi.write ( &cmd[0], 1, &cmd[0], 2 );
    _cs              =   0;

    // Parse the data
    myChannelRSSI->rssi2     =   ( cmd[1] & RSSI2_MASK );                       // Channel1: RSSI1




    if ( ( mySPI_status / ( sizeof( cmd )/sizeof( cmd[0] ) ) ) == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}



/**
 * @brief       AS3933_Send_DirectCommand   ( AS3933_spi_direct_commands_t )
 *
 * @details     It sends a direct command.
 *
 * @param[in]    myDirectCommand:   Direct command to be sent.
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of AS3933_Send_DirectCommand.
 *
 *
 * @author      Manuel Caballero
 * @date        7/March/2018
 * @version     7/March/2018   The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
AS3933::AS3933_status_t  AS3933::AS3933_Send_DirectCommand ( AS3933_spi_direct_commands_t myDirectCommand )
{
    char    cmd       =    0;
    int     mySPI_status;


    // Send a direct command
    cmd              =   ( AS3933_DIRECT_COMMAND | myDirectCommand );
    _cs              =   1;
    mySPI_status     =   _spi.write ( &cmd, 1, &cmd, 1 );
    _cs              =   0;




    if ( mySPI_status == SPI_SUCCESS )
        return   AS3933_SUCCESS;
    else
        return   AS3933_FAILURE;
}