ISM RF transmitter library for MAX4146X family devices

Library for MAX41460, MAX41461, MAX41462, MAX41463, MAX41464 RF Transmitter ICs.

Max4146x.cpp

Committer:
Ibrahim Tilki
Date:
2022-01-13
Revision:
2:80969d8f6d2b
Parent:
1:ccf0e1d28860

File content as of revision 2:80969d8f6d2b:

/*******************************************************************************
* Copyright (C) 2019 Maxim Integrated Products, Inc., All rights Reserved.
*
* This software is protected by copyright laws of the United States and
* of foreign countries. This material may also be protected by patent laws
* and technology transfer regulations of the United States and of foreign
* countries. This software is furnished under a license agreement and/or a
* nondisclosure agreement and may only be used or reproduced in accordance
* with the terms of those agreements. Dissemination of this information to
* any party or parties not specified in the license agreement and/or
* nondisclosure agreement is expressly prohibited.
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*******************************************************************************
*/

#include "Max4146x.h"
#include <iostream>

using namespace std;

const uint8_t default_register_value_0[17] = {0x90, 0x81, 0x03, 0x00, 0x00, 0x04, 0x80, 0x80, 0x60, 0x00,
                                              0x00, 0xC4, 0xDE, 0x98, 0x28, 0x04, 0x02};
const uint8_t default_register_value_1[20] = {0x90, 0x81, 0x03, 0x00, 0x00, 0x04, 0x80, 0x80, 0x60, 0x00,
                                              0x00, 0xC4, 0xDE, 0x98, 0x28, 0x04, 0x04, 0x00, 0xFF, 0x00};

template <class REG>
MAX4146X<REG>::MAX4146X(REG *reg, SPI *spi, DigitalOut *cs)
{
    operation_mode = UNINITIALIZED;

    if (reg == NULL) {
        return;
    }

    if (cs == NULL) {
        return;
    }

    this->reg = reg;
    ssel = cs;

    if (spi == NULL) {
        return;
    }

    spi_handler = spi;
    i2c_handler = NULL;
    preset_mode = 0;

    if (initial_programming() < 0) {
        return;
    }

    this->crystal_frequency = 16.0;
    this->center_frequency = 315.0;
    this->baud_rate = 5000.0;  //5 kHz

    operation_mode = INITIALIZED;
}

template <class REG>
MAX4146X<REG>::MAX4146X(REG *reg, SPI *spi)
{
    operation_mode = UNINITIALIZED;

    if (reg == NULL) {
        return;
    }

    this->reg = reg;

    if (spi == NULL) {
        return;
    }

    spi_handler = spi;
    i2c_handler = NULL;
    ssel = NULL;
    preset_mode = 0;

    if (initial_programming() < 0) {
        return;
    }

    this->crystal_frequency = 16.0;
    this->center_frequency = 315.0;
    this->baud_rate = 5000.0; //5 kHz

    operation_mode = INITIALIZED;
}

template <class REG>
MAX4146X<REG>::MAX4146X(REG *reg, I2C *i2c)
{
    operation_mode = UNINITIALIZED;

    if (reg == NULL) {
        return;
    }

    this->reg = reg;

    if (i2c == NULL) {
        return;
    }

    i2c_handler = i2c;
    spi_handler = NULL;
    ssel = NULL;
    preset_mode = 0;

    if (initial_programming() < 0) {
        return;
    }

    this->crystal_frequency = 16.0;
    this->center_frequency = 315.0;
    this->baud_rate = 5000.0;  //5 kHz

    operation_mode = INITIALIZED;
}

template <class REG>
MAX4146X<REG>::MAX4146X(DigitalOut *cs)
{
    operation_mode = UNINITIALIZED;

    if (cs == NULL) {
        return;
    }

    data_sent = cs;

    data_rate = 5;

    this->reg = NULL;
    this->ssel = NULL;
    spi_handler = NULL;
    i2c_handler = NULL;
    preset_mode = 1;

    operation_mode = INITIALIZED;
}

template <>
int MAX4146X<max41460_reg_map_t>::read_register(uint8_t reg, uint8_t *value, uint8_t len)
{
    int rtn_val = -1;

    if (value == NULL) {
        return -1;
    }

    if (this->reg == NULL) {
        return -1;
    }

    if (ssel != NULL) {
        *ssel = 0;
    }
    spi_handler->write((uint8_t)0x80 | reg);
    for (uint8_t i = 0; i < len; i++) {
        *(value++) = spi_handler->write(0x00);     // read back  data bytes
    }
    if (ssel != NULL) {
        *ssel = 1;
    }
    return 0;
}

template <class REG>
int MAX4146X<REG>::read_register(uint8_t reg, uint8_t *value, uint8_t len)
{
    int rtn_val = -1;

    if (value == NULL) {
        return -1;
    }

    if (this->reg == NULL) {
        return -1;
    }

    rtn_val = i2c_handler->write(I2C_ADDRESS, (const char *)&reg, 1, true);
    if (rtn_val != 0) {
        return -1;
    }

    rtn_val = i2c_handler->read(I2C_ADDRESS, (char *) value, len, false);
    if (rtn_val < 0) {
        return rtn_val;
    }

    return 0;
}

template <>
int MAX4146X<max41460_reg_map_t>::write_register(uint8_t reg, const uint8_t *value, uint8_t len)
{
    int rtn_val = -1;
    uint8_t local_data[1 + len];

    if (value == NULL) {
        return -1;
    }

    memcpy(&local_data[0], value, len);

    rtn_val = spi_handler->write(0x7F & reg); // write mode and adress send
    for (int i = 0; i < len; i++) {
        rtn_val = spi_handler->write(local_data[i]); // write adress
    }
    if (rtn_val != 0) {
        return rtn_val;
    }

    return 0;
}

template <class REG>
int MAX4146X<REG>::write_register(uint8_t reg, const uint8_t *value, uint8_t len)
{
    int rtn_val = -1;
    uint8_t local_data[1 + len];

    if (value == NULL) {
        return -1;
    }

    local_data[0] = reg;

    memcpy(&local_data[1], value, len);

    rtn_val = i2c_handler->write(I2C_ADDRESS, (const char *)local_data,
            sizeof(local_data));
    if (rtn_val != 0) {
        return -1;
    }

    return 0;
}

#define SET_BIT_FIELD(address, reg_name, bit_field_name, value)                             \
                int ret;                                                                    \
                ret = read_register(address, (uint8_t *)&(reg_name), 1);                    \
                if (ret) {                                                                  \
                    return ret;                                                             \
                }                                                                           \
                bit_field_name = value;                                                     \
                ret = write_register(address, (uint8_t *)&(reg_name), 1);                   \
                if (ret) {                                                                  \
                    return ret;                                                             \
                }

template <class REG>
int MAX4146X<REG>::set_crystal_frequency(float freq)
{
    if (freq < 12.7 || freq > 19.3) {
        return -1;
    }
    this->crystal_frequency = freq;

    return 0;
}

template <class REG>
float MAX4146X<REG>::get_crystal_frequency()
{
    return this->crystal_frequency;
}

template <class REG>
int MAX4146X<REG>::set_center_frequency(float freq)
{
    if (freq < 250 || freq > 950) {
        return -1;
    }

    this->center_frequency = freq;

    uint32_t value = (uint32_t)((65536 * freq) / this->crystal_frequency); //65536 is constant defined in the datasheet

    return this->set_frequency(value);
}

template <class REG>
float MAX4146X<REG>::get_center_frequency()
{
    return this->center_frequency;
}

template <class REG>
int MAX4146X<REG>::adjust_baudrate(float rate)
{
    if (rate < 195.3 || rate > 200000.0) {
        return -1;
    }

    if (this->preset_mode == 1) {
        this->baud_rate = rate;
    }

    int error = 0;
    uint8_t prediv = 3;

    if (rate < 12500.0) {
        error = this->set_bclk_postdiv(this->BCLK_POSTDIV_BY_5);
        prediv = (uint8_t)((50000.0 / rate) - 1);
    } else if (rate < 25000.0) {
        error = this->set_bclk_postdiv(this->BCLK_POSTDIV_BY_4);
        prediv = (uint8_t)((100000.0 / rate) - 1);
    } else if (rate < 50000.0) {
        error = this->set_bclk_postdiv(this->BCLK_POSTDIV_BY_3);
        prediv = (uint8_t)((200000.0 / rate) - 1);
    } else if (rate < 100000.0) {
        error = this->set_bclk_postdiv(this->BCLK_POSTDIV_BY_2);
        prediv = (uint8_t)((400000.0 / rate) - 1);
    } else {
        error = this->set_bclk_postdiv(this->BCLK_POSTDIV_BY_1);
        prediv = (uint8_t)((800000.0 / rate) - 1);
    }

    if (error < 0) {
        return -1;
    }

    return this->set_bclk_prediv(prediv);
}

template <class REG>
float MAX4146X<REG>::get_baudrate()
{
    return this->baud_rate;
}

template <class REG>
int MAX4146X<REG>::adjust_frequency_deviation(float deviation)
{
    uint8_t dev = 0;

    if (this->read_register(CFG1_ADDR, (uint8_t *) & (this->reg->reg_cfg1), 1) < 0) {
        return -1;
    }

    if (this->reg->reg_cfg1.bits.fskshape == 0) {
        dev = (uint8_t)(deviation * 8.192 / crystal_frequency);
        if (dev < 127) {
            return this->set_deltaf(dev);
        }
    } else {
        dev = (uint8_t)(deviation * 81.92 / crystal_frequency); // crystal_frequency in MHz form
        if (dev < 15) {
            return this->set_deltaf_shape(dev);
        }
    }

    return -1;
}

template <class REG>
int MAX4146X<REG>::adjust_manchester_bitrate(char rate)
{
    this->data_rate = rate;

    return 0;
}

template <class REG>
char MAX4146X<REG>::get_manchester_bitrate()
{
    return this->data_rate;
}

template <>
int MAX4146X<max41460_reg_map_t>::send_data(uint8_t *data, uint32_t length)
{
    if (this->preset_mode == 0) {

        if (ssel != NULL) {
            *ssel = 0;
        }

        spi_handler->write(0x7F & 0x0A); /*write mode and adress send*/

        spi_handler->write(0x01); /*write data SPI_EN1 clear*/


        if (ssel != NULL) {
            *ssel = 1;
        }

        wait_us(300); /* for waiting another SPI operation*/

        if (ssel != NULL) {
            *ssel = 0;
        }

        spi_handler->write(0x7F & 0x10); /*write mode and adress send*/

        spi_handler->write(0x03); /*write data SPI_EN2 set*/

        if (ssel != NULL) {
            *ssel = 0;
        }

        wait_us(300); /* for waiting another SPI operation*/

    }

    return this->io_write(data, length);
}

template <class REG>
int MAX4146X<REG>::send_data(uint8_t *data, uint32_t length)
{
    if (this->preset_mode == 0) {
        if (length > 32767) {
            return -100;
        }

        this->adjust_baudrate(this->baud_rate);

//        this->set_i2c_txen1(I2C_TXEN1_DISABLE);

        uint8_t local_data[4+length];

        local_data[0] = CFG7_ADDR;
        local_data[1] = 0x04;
        local_data[2] = (uint8_t)((length >> 8) | 0x80);
        local_data[3] = (uint8_t)((length) & 0x0FF);

        memcpy(&local_data[4], data, length);

        i2c_handler->write(I2C_ADDRESS, (const char *)local_data, sizeof(local_data), false);

    } else {
        this->io_write(data, length);
    }

    return 0;
}

template <class REG>
int MAX4146X<REG>::io_write(uint8_t *data, uint32_t length)
{
    //manchester array
    manchester_bit_array = new  unsigned char[length * 2 * 8];

    //bit array
    bits_array = new unsigned char[length * 8];

    //byte to bit conversion
    for (int i = 0; i < length; i++) {
        for (int j = 0; j < 8; j++) {
            // Mask each bit in the byte and store it
            if (data[i] & (mask << j)) {
                bits_array[i * 8 + j] = 1;
            } else {
                bits_array[i * 8 + j] = 0;
            }
        }
    }

    //manchester encode
    for (int i = 0; i < length * 8; i++) {
        if (bits_array[i] == 0) {
            //falling edge
            manchester_bit_array[2 * i] = 1;
            manchester_bit_array[2 * i + 1] = 0;
        } else {
            //rising edge
            manchester_bit_array[2 * i] = 0;
            manchester_bit_array[2 * i + 1] = 1;
        }
    }

    delete[] bits_array;  //delete bit array anymore not used

    float result = (500.0 / data_rate);

    bool rxFinished = false;
    Timer t;
    core_util_critical_section_enter();
    *this->data_sent = 0;
    wait_us(100);
    *this->data_sent = 1;
    wait_us(350);
    *this->data_sent = 0;
    wait_us(10);
    t.start();
    int manch_bit_counter = 0;
    do {
        if (t.read_us() >= (result * manch_bit_counter)) {
            if (manchester_bit_array[manch_bit_counter] == 0) {
                *this->data_sent = 0;
            } else {
                *this->data_sent = 1;
            }

            manch_bit_counter++;

            if (manch_bit_counter >= (length * 2 * 8)) {
                rxFinished = true;
                t.stop();
                if (this->ssel != NULL) {
                    *this->ssel = 1;
                }
            }

        }
    } while (!rxFinished);
    *this->data_sent = 0;
    core_util_critical_section_exit();

    delete[]  manchester_bit_array;  //manchester array clean

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_softreset(softreset_t softreset)
{
    SET_BIT_FIELD(CFG8_ADDR, this->reg->reg_cfg8, this->reg->reg_cfg8.bits.softreset, softreset);

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_xoclkdelay(xoclkdelay_t delay)
{
    SET_BIT_FIELD(CFG1_ADDR, this->reg->reg_cfg1, this->reg->reg_cfg1.bits.xoclkdelay, delay);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_xoclkdelay(xoclkdelay_t *delay)
{
    int ret;

    ret = read_register(CFG1_ADDR, (uint8_t *) & (this->reg->reg_cfg1), 1);
    if (ret < 0) {
        return ret;
    }

    *delay = (xoclkdelay_t)this->reg->reg_cfg1.bits.xoclkdelay;

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_fifo_flags(uint8_t *fifo_flags)
{
    return read_register(I2C6_ADDR, fifo_flags, 1);
}

template <class REG>
int MAX4146X<REG>::get_pktcomplete(uint8_t *pktcomplete)
{
    int ret;

    ret = read_register(I2C4_ADDR, (uint8_t *) & (this->reg->reg_i2c4), 1);
    if (ret < 0) {
        return ret;
    }

    *pktcomplete = (uint8_t)this->reg->reg_i2c4.bits.pktcomplete;

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_tx_pktlen(uint16_t *pktlen)
{
    int ret;

    ret = read_register(I2C4_ADDR, (uint8_t *) & (this->reg->reg_i2c4), 2);
    if (ret < 0) {
        return ret;
    }

    *pktlen = (uint16_t)(((this->reg->reg_i2c4.bits.tx_pktlen_14_to_8) << 8) + (this->reg->reg_i2c5.raw)) ;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_xoclkdiv(xoclkdiv_t div)
{
    SET_BIT_FIELD(CFG1_ADDR, this->reg->reg_cfg1, this->reg->reg_cfg1.bits.xoclkdiv, div);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_xoclkdiv(xoclkdiv_t* div)
{
    int ret;

    ret = read_register(CFG1_ADDR, (uint8_t *) & (this->reg->reg_cfg1), 1);
    if (ret < 0) {
        return ret;
    }

    *div = (xoclkdiv_t)this->reg->reg_cfg1.bits.xoclkdiv;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_fskshape(fskshape_t shape)
{
    SET_BIT_FIELD(CFG1_ADDR, this->reg->reg_cfg1, this->reg->reg_cfg1.bits.fskshape, shape);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_fskshape(fskshape_t* shape)
{
    int ret;

    ret = read_register(CFG1_ADDR, (uint8_t *) & (this->reg->reg_cfg1), 1);
    if (ret < 0) {
        return ret;
    }

    *shape = (fskshape_t)this->reg->reg_cfg1.bits.fskshape;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_sync(sync_t state)
{
    SET_BIT_FIELD(CFG1_ADDR, this->reg->reg_cfg1, this->reg->reg_cfg1.bits.sync, state);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_sync(sync_t* state)
{
    int ret;

    ret = read_register(CFG1_ADDR, (uint8_t *) & (this->reg->reg_cfg1), 1);
    if (ret < 0) {
        return ret;
    }

    *state = (sync_t)this->reg->reg_cfg1.bits.sync;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_modmode(modmode_t mode)
{
    SET_BIT_FIELD(CFG1_ADDR, this->reg->reg_cfg1, this->reg->reg_cfg1.bits.modmode, mode);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_modmode(modmode_t* mode)
{
    int ret;

    ret = read_register(CFG1_ADDR, (uint8_t *) & (this->reg->reg_cfg1), 1);
    if (ret < 0) {
        return ret;
    }

    *mode = (modmode_t)this->reg->reg_cfg1.bits.modmode;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_clkout_delay(clkout_delay_t delay)
{
    SET_BIT_FIELD(CFG2_ADDR, this->reg->reg_cfg2, this->reg->reg_cfg2.bits.clkout_delay, delay);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_clkout_delay(clkout_delay_t* delay)
{
    int ret;

    ret = read_register(CFG2_ADDR, (uint8_t *) & (this->reg->reg_cfg2), 1);
    if (ret < 0) {
        return ret;
    }

    *delay = (clkout_delay_t)this->reg->reg_cfg2.bits.clkout_delay;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_bclk_postdiv(bclk_postdiv_t div)
{
    SET_BIT_FIELD(CFG2_ADDR, this->reg->reg_cfg2, this->reg->reg_cfg2.bits.bclk_postdiv, div);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_bclk_postdiv(bclk_postdiv_t* div)
{
    int ret;

    ret = read_register(CFG2_ADDR, (uint8_t *) & (this->reg->reg_cfg2), 1);
    if (ret < 0) {
        return ret;
    }

    *div = (bclk_postdiv_t)this->reg->reg_cfg2.bits.bclk_postdiv;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_bclk_prediv(uint8_t prediv)
{
    if (prediv < 3) {
        return -1;
    }

    SET_BIT_FIELD(CFG3_ADDR, this->reg->reg_cfg3, this->reg->reg_cfg3.bits.bclk_prediv, prediv);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_bclk_prediv(uint8_t* prediv)
{
    int ret;

    ret = read_register(CFG3_ADDR, (uint8_t *) & (this->reg->reg_cfg3), 1);
    if (ret < 0) {
        return ret;
    }

    *prediv = (uint8_t)this->reg->reg_cfg3.bits.bclk_prediv;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_pwdn_mode(pwdn_mode_t pwdn_mode)
{
    SET_BIT_FIELD(CFG4_ADDR, this->reg->reg_cfg4, this->reg->reg_cfg4.bits.pwdn_mode, pwdn_mode);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_pwdn_mode(pwdn_mode_t* pwdn_mode)
{
    int ret;

    ret = read_register(CFG4_ADDR, (uint8_t *) & (this->reg->reg_cfg4), 1);
    if (ret < 0) {
        return ret;
    }

    *pwdn_mode = (pwdn_mode_t)this->reg->reg_cfg4.bits.pwdn_mode;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_tstep(uint8_t tstep)
{
    SET_BIT_FIELD(CFG5_ADDR, this->reg->reg_cfg5, this->reg->reg_cfg5.bits.tstep, tstep);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_tstep(uint8_t* tstep)
{
    int ret;

    ret = read_register(CFG5_ADDR, (uint8_t *) & (this->reg->reg_cfg5), 1);
    if (ret < 0) {
        return ret;
    }

    *tstep = (uint8_t)this->reg->reg_cfg5.bits.tstep;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_pa_boost(pa_boost_t pa_boost)
{
    SET_BIT_FIELD(SHDN_ADDR, this->reg->reg_shdn, this->reg->reg_shdn.bits.pa_boost, pa_boost);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_pa_boost(pa_boost_t* pa_boost)
{
    int ret;

    ret = read_register(CFG5_ADDR, (uint8_t *) & (this->reg->reg_shdn), 1);
    if (ret < 0) {
        return ret;
    }

    *pa_boost = (pa_boost_t)this->reg->reg_shdn.bits.pa_boost;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_papwr(papwr_t papwr)
{
    SET_BIT_FIELD(PA1_ADDR, this->reg->reg_pa1, this->reg->reg_pa1.bits.papwr, papwr);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_papwr(papwr_t* papwr)
{
    int ret;

    ret = read_register(PA1_ADDR, (uint8_t *) & (this->reg->reg_pa1), 1);
    if (ret < 0) {
        return ret;
    }

    *papwr = (papwr_t)this->reg->reg_pa1.bits.papwr;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_pacap(pacap_t pacap)
{
    SET_BIT_FIELD(PA2_ADDR, this->reg->reg_pa2, this->reg->reg_pa2.bits.pacap, pacap);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_pacap(pacap_t* pacap)
{
    int ret;

    ret = read_register(CFG5_ADDR, (uint8_t *) & (this->reg->reg_pa2), 1);
    if (ret < 0) {
        return ret;
    }

    *pacap = (pacap_t)this->reg->reg_pa2.bits.pacap;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_cplin(cplin_t cplin)
{
    SET_BIT_FIELD(PLL1_ADDR, this->reg->reg_pll1, this->reg->reg_pll1.bits.cplin, cplin);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_cplin(cplin_t* cplin)
{
    int ret;

    ret = read_register(PLL1_ADDR, (uint8_t *) & (this->reg->reg_pll1), 1);
    if (ret < 0) {
        return ret;
    }

    *cplin = (cplin_t)this->reg->reg_pll1.bits.cplin;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_fracmode(fracmode_t fracmode)
{
    SET_BIT_FIELD(PLL1_ADDR, this->reg->reg_pll1, this->reg->reg_pll1.bits.fracmode, fracmode);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_fracmode(fracmode_t* fracmode)
{
    int ret;

    ret = read_register(PLL1_ADDR, (uint8_t *) & (this->reg->reg_pll1), 1);
    if (ret < 0) {
        return ret;
    }

    *fracmode = (fracmode_t)this->reg->reg_pll1.bits.fracmode;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_lodiv(lodiv_t lodiv)
{
    SET_BIT_FIELD(PLL1_ADDR, this->reg->reg_pll1, this->reg->reg_pll1.bits.lodiv, lodiv);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_lodiv(lodiv_t* lodiv)
{
    int ret;

    ret = read_register(PLL1_ADDR, (uint8_t *) & (this->reg->reg_pll1), 1);
    if (ret < 0) {
        return ret;
    }

    *lodiv = (lodiv_t)this->reg->reg_pll1.bits.lodiv;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_lomode(lomode_t lomode)
{
    SET_BIT_FIELD(PLL1_ADDR, this->reg->reg_pll1, this->reg->reg_pll1.bits.lomode, lomode);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_lomode(lomode_t* lomode)
{
    int ret;

    ret = read_register(PLL1_ADDR, (uint8_t *) & (this->reg->reg_pll1), 1);
    if (ret < 0) {
        return ret;
    }

    *lomode = (lomode_t)this->reg->reg_pll1.bits.lomode;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_cpval(cpval_t cpval)
{

    SET_BIT_FIELD(PLL2_ADDR, this->reg->reg_pll2, this->reg->reg_pll2.bits.cpval, cpval);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_cpval(cpval_t* cpval)
{
    int ret;

    ret = read_register(PLL2_ADDR, (uint8_t *) & (this->reg->reg_pll2), 1);
    if (ret < 0) {
        return ret;
    }

    *cpval = (cpval_t)this->reg->reg_pll2.bits.cpval;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_frequency(uint32_t freq)
{
    uint8_t value[3] = {(uint8_t)(freq >> 16), (uint8_t)(freq >> 8), (uint8_t)freq};

    return write_register(PLL3_ADDR, (uint8_t *)&value, 3);

}

template <class REG>
int MAX4146X<REG>::get_frequency(uint32_t* freq)
{
    int ret;

    uint8_t value[3];

    ret =  read_register(PLL3_ADDR, (uint8_t *)&value, 3);
    if (ret < 0) {
        return ret;
    }

    *freq = (uint32_t)((value[0] << 16) + (value[1] << 8) + value[2]);

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_deltaf(uint8_t deltaf)
{
    if (deltaf > 127) {
        return -1;
    }

    SET_BIT_FIELD(PLL6_ADDR, this->reg->reg_pll6, this->reg->reg_pll6.bits.deltaf, deltaf);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_deltaf(uint8_t* deltaf)
{
    int ret;

    ret = read_register(PLL6_ADDR, (uint8_t *) & (this->reg->reg_pll6), 1);
    if (ret < 0) {
        return ret;
    }

    *deltaf = (uint8_t)this->reg->reg_pll6.bits.deltaf;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_deltaf_shape(uint8_t deltaf_shape)
{
    if (deltaf_shape > 15) {
        return -1;
    }

    SET_BIT_FIELD(PLL7_ADDR, this->reg->reg_pll7, this->reg->reg_pll7.bits.deltaf_shape, deltaf_shape);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_deltaf_shape(uint8_t* deltaf_shape)
{
    int ret;

    ret = read_register(PLL7_ADDR, (uint8_t *) & (this->reg->reg_pll7), 1);
    if (ret < 0) {
        return ret;
    }

    *deltaf_shape = (uint8_t)this->reg->reg_pll7.bits.deltaf_shape;

    return 0;
}

template <class REG>
int MAX4146X<REG>::set_pktlen_mode(pktlen_mode_t pktlen_mode)
{
    SET_BIT_FIELD(I2C1_ADDR, this->reg->reg_i2c1, this->reg->reg_i2c1.bits.pktlen_mode, pktlen_mode);

    return 0;
}

template <class REG>
int MAX4146X<REG>::get_pktlen_mode(pktlen_mode_t* pktlen_mode)
{
    int ret;

    ret = read_register(I2C1_ADDR, (uint8_t *) & (this->reg->reg_i2c1), 1);
    if (ret < 0) {
        return ret;
    }

    *pktlen_mode = (pktlen_mode_t)this->reg->reg_i2c1.bits.pktlen_mode;

    return 0;
}


template <class REG>
int MAX4146X<REG>::set_i2c_pktlen(uint16_t pktlen)
{
    if (pktlen > 0x7FF) {
        return -1;
    }

    SET_BIT_FIELD(I2C1_ADDR, this->reg->reg_i2c1, this->reg->reg_i2c1.bits.pktlen_14_to_8, (uint8_t)((pktlen >> 8) & 0x07));

    uint8_t value = (uint8_t)(pktlen & 0xFF);

    return write_register(I2C2_ADDR, (uint8_t *)&value, 1);
}

template <class REG>
int MAX4146X<REG>::get_i2c_pktlen(uint16_t* pktlen)
{
    int ret;

    ret = read_register(I2C1_ADDR, (uint8_t *) & (this->reg->reg_i2c1), 1);
    if (ret < 0) {
        return ret;
    }

    ret = read_register(I2C2_ADDR, (uint8_t *) & (this->reg->reg_i2c2), 1);
    if (ret < 0) {
        return ret;
    }

    *pktlen = (uint16_t)(((this->reg->reg_i2c1.raw & 0x7F)<<8) + (this->reg->reg_i2c2.raw &0x7F));

    return 0;
}

template <class REG>
int MAX4146X<REG>::initial_programming(void)
{
    uint8_t value = 0x80;
    write_register(ADDL2_ADDR, (uint8_t *)&value, 1);

    return write_register(CFG1_ADDR, default_register_value_1, 20);
}

template <>
int MAX4146X<max41460_reg_map_t>::initial_programming(void)
{
    if (this->ssel != NULL){
        *this->ssel = 0;
        wait_us(100);
    }

    int rtn = write_register(CFG1_ADDR, default_register_value_0, 17);

    if (this->ssel != NULL){
        wait_us(90);
        *this->ssel = 1;
    }

    return rtn;
}


template class MAX4146X<max41460_reg_map_t>;
template class MAX4146X<max41461_2_reg_map_t>;
template class MAX4146X<max41463_4_reg_map_t>;