#ifndef SPIDEVICE_H
#define SPIDEVICE_H

#include <QObject>
#include "errors.h"
#include "ftd2xx/ftd2xx.h"
#include "libmpsse/include/libmpsse_spi.h"

/*!
 * \brief The SpiDevice class
 */
class SpiDevice : public QObject
{
    Q_OBJECT

public:
    explicit SpiDevice(quint32 index, QObject *parent = nullptr);
    ~SpiDevice();
    error_t open();
    error_t close();
    error_t init(quint32 spiClock, quint8 spiLatency, quint8 spiMode, quint8 csPin,
                 quint8 csActiveLevel);
    error_t read(quint8 *rxBuffer, quint32 sizeToRead, quint32 *sizeRead,
                 quint32 xferOptions);
    error_t write(quint8 *txBuffer, quint32 sizeToWrite, quint32 *sizeWritten,
                  quint32 xferOptions);
    error_t transfer(quint8 *rxBuffer, quint8 *txBuffer, quint32 sizeToTransfer,
                     quint32 *sizeTransferred, quint32 xferOptions);

    // Getter and setter
    error_t setChipSelect(quint8 csPin);
    error_t isBusy(bool &state);

public:
    static error_t getIndexByDescription(const QString &description, quint32 &index);
    static error_t getIndexBySerialNumber(const QString &serialNumber, quint32 &index);

private:
    static error_t errCodeFromFtStatus(FT_STATUS status);

private:
    quint32                  m_index;
    FT_DEVICE_LIST_INFO_NODE m_channelInfo;
    FT_HANDLE                m_handle;
    ChannelConfig            m_channelConfig;
    quint32                  m_configOptions;

private:
    static quint32 m_libRefCtr;
};

#endif // SPIDEVICE_H


----------


#include "spidevice.h"

#include <QtDebug>

// ===============================
//  Useful definitions and macros
// ===============================

#define ftCheckErr(fCall)       if (FT_OK != (ftStatus = (fCall))) goto Error;

// ==================
//  Static variables
// ==================

quint32 SpiDevice::m_libRefCtr = 0;

// =========================
//  Public member functions
// =========================

/*******************************************************************************************
 * \brief     Creates an instance of SpiDevice (constructor).
 * \param[in] index the device index
 * \param[in] parent the object's parent object
 * \return    the newly created instance of the object
 *******************************************************************************************/
SpiDevice::SpiDevice(quint32 index, QObject *parent)
    : QObject{parent}
    , m_index{index}
    , m_channelInfo{}
    , m_handle{0}
    , m_channelConfig{}
    , m_configOptions{}
{
    qDebug() << "---> SpiDevice::SpiDevice(quint32, QObject*)";

    FT_STATUS ftStatus = FT_OK;

    if (m_libRefCtr == 0) {
        Init_libMPSSE();
    }
    ++ m_libRefCtr;

Error:

    qDebug() << "<--- SpiDevice::SpiDevice(quint32, QObject*)";
}

/*******************************************************************************************
 * \brief  Destroys the object instance (destructor).
 * \return n/a
 *******************************************************************************************/
SpiDevice::~SpiDevice()
{
    qDebug() << "---> SpiDevice::~SpiDevice()";

    close();

    -- m_libRefCtr;
    if (m_libRefCtr == 0) {
        Cleanup_libMPSSE();
    }

    qDebug() << "<--- SpiDevice::~SpiDevice()";
}

/*******************************************************************************************
 * \brief  Opens the SPI channel.
 * \return ERR_OK or any of the ERR_* error codes according to the error encountered
 *******************************************************************************************/
error_t SpiDevice::open()
{
    qDebug() << "---> SpiDevice::open()";

    error_t errCode = ERR_OK;
    FT_STATUS ftStatus = FT_OK;

    ftCheckErr(SPI_GetChannelInfo(m_index, &m_channelInfo));
    ftCheckErr(SPI_OpenChannel(m_index, &m_handle));

Error:
    if (ftStatus != FT_OK) {
        errCode = errCodeFromFtStatus(ftStatus);
    }

    qDebug() << "<--- SpiDevice::open()";

    return errCode;
}

/*******************************************************************************************
 * \brief  Closes the SPI channel.
 * \return ERR_OK or any of the ERR_* error codes according to the error encountered
 *******************************************************************************************/
error_t SpiDevice::close()
{
    qDebug() << "---> SpiDevice::close()";

    error_t errCode = ERR_OK;
    FT_STATUS ftStatus = FT_OK;

    ftCheckErr(SPI_CloseChannel(m_handle));

Error:
    if (ftStatus != FT_OK) {
        errCode = errCodeFromFtStatus(ftStatus);
    }

    qDebug() << "<--- SpiDevice::close()";

    return errCode;
}

/*******************************************************************************************
 * \brief     Initializes the SPI channel.
 * \param[in] spiClock the SPI clock frequency
 * \param[in] spiLatency the SPI latency
 * \param[in] spiMode the SPI mode
 * \param[in] csPin the pin that is used as chip select
 * \param[in] csActiveLevel indicates if CS active low or high is used
 * \return    ERR_OK or any of the ERR_* error codes according to the error encountered
 *******************************************************************************************/
error_t SpiDevice::init(quint32 spiClock, quint8 spiLatency, quint8 spiMode, quint8 csPin,
                        quint8 csActiveLevel)
{
    qDebug() << "---> SpiDevice::init()";

    error_t errCode = ERR_OK;
    FT_STATUS ftStatus = FT_OK;

    m_channelConfig.ClockRate     = spiClock;
    m_channelConfig.LatencyTimer  = spiLatency;
    m_channelConfig.configOptions = spiMode | csPin | csActiveLevel;
    m_channelConfig.Pin           = 0x00000000;
    // m_channelConfig.Pin           = 0x000b000b;
    // m_channelConfig.currentPinState = 0x00000000;
    ftCheckErr(SPI_InitChannel(m_handle, &m_channelConfig));

Error:
    if (ftStatus != FT_OK) {
        errCode = errCodeFromFtStatus(ftStatus);
    }

    qDebug() << "<--- SpiDevice::init()";

    return errCode;
}

/*******************************************************************************************
 * \brief      Reads data from an SPI device.
 * \param[out] rxBuffer the address of the buffer that takes the data
 * \param[in]  sizeToRead the number of bits or bytes to read
 * \param[out] *sizeRead the number of bits or bytes that have been read
 * \param[in]  xferOptions the transfer options
 * \return     ERR_OK or any of the ERR_* error codes according to the error encountered
 *******************************************************************************************/
error_t SpiDevice::read(quint8 *rxBuffer, quint32 sizeToRead, quint32 *sizeRead,
                        quint32 xferOptions)
{
    qDebug() << "---> SpiDevice::read()";

    error_t errCode = ERR_OK;
    FT_STATUS ftStatus = FT_OK;
    quint32 numRead = 0;

    ftCheckErr(SPI_Read(m_handle, rxBuffer, sizeToRead,
                        reinterpret_cast<LPDWORD>(&numRead), xferOptions));
    if (sizeRead) {
        *sizeRead = numRead;
    }

Error:
    if (ftStatus != FT_OK) {
        errCode = errCodeFromFtStatus(ftStatus);
    }

    qDebug() << "<--- SpiDevice::read()";

    return errCode;
}

/*******************************************************************************************
 * \brief      Writes data to an SPI device.
 * \param[in]  txBuffer the data to write
 * \param[in]  sizeToWrite the number of bits or bytes to write
 * \param[out] *sizeWritten the number of bits or bytes that have been written
 * \param[in]  xferOptions the transfer options
 * \return     ERR_OK or any of the ERR_* error codes according to the error encountered
 *******************************************************************************************/
error_t SpiDevice::write(quint8 *txBuffer, quint32 sizeToWrite, quint32 *sizeWritten,
                         quint32 xferOptions)
{
    qDebug() << "---> SpiDevice::write()";

    error_t errCode = ERR_OK;
    FT_STATUS ftStatus = FT_OK;
    quint32 numWritten = 0;

    ftCheckErr(SPI_Write(m_handle, txBuffer, sizeToWrite,
                         reinterpret_cast<LPDWORD>(&numWritten), xferOptions));
    if (sizeWritten) {
        *sizeWritten = numWritten;
    }

Error:
    if (ftStatus != FT_OK) {
        errCode = errCodeFromFtStatus(ftStatus);
    }

    qDebug() << "<--- SpiDevice::write()";

    return errCode;
}

/*******************************************************************************************
 * \brief      Transfers data to and from an SPI device.
 * \param[out] rxBuffer the address of the buffer that takes the data
 * \param[in]  txBuffer the data to write
 * \param[in]  sizeToTransfer the number of bits or bytes to transfer
 * \param[out] *sizeTransferred the number of bits or bytes that have been transferred
 * \param[in]  xferOptions the transfer options
 * \return     ERR_OK or any of the ERR_* error codes according to the error encountered
 *******************************************************************************************/
error_t SpiDevice::transfer(quint8 *rxBuffer, quint8 *txBuffer, quint32 sizeToTransfer,
                 quint32 *sizeTransferred, quint32 xferOptions)
{
    qDebug() << "---> SpiDevice::transfer()";

    error_t errCode = ERR_OK;
    FT_STATUS ftStatus = FT_OK;
    quint32 numTransferred = 0;

    ftCheckErr(SPI_ReadWrite(m_handle, rxBuffer, txBuffer, sizeToTransfer,
                             reinterpret_cast<LPDWORD>(&numTransferred), xferOptions));
    if (sizeTransferred) {
        *sizeTransferred = numTransferred;
    }

Error:
    if (ftStatus != FT_OK) {
        errCode = errCodeFromFtStatus(ftStatus);
    }

    qDebug() << "<--- SpiDevice::transfer()";

    return errCode;
}

/*******************************************************************************************
 * \brief     Sets or changes the pin that is used as the chip celect signal.
 * \param[in] csPin the pin that is used as the chip select signal
 * \return    ERR_OK or any of the ERR_* error codes according to the error encountered
 *******************************************************************************************/
error_t SpiDevice::setChipSelect(quint8 csPin)
{
    qDebug() << "---> SpiDevice::setChipSelect()";

    error_t errCode = ERR_OK;
    FT_STATUS ftStatus = FT_OK;

    quint32 configOptions = m_channelConfig.configOptions;
    configOptions &= ~SPI_CONFIG_OPTION_CS_MASK;
    configOptions |= csPin;
    ftCheckErr(SPI_ChangeCS(m_handle, configOptions));
    m_channelConfig.configOptions = configOptions;

Error:
    if (ftStatus != FT_OK) {
        errCode = errCodeFromFtStatus(ftStatus);
    }

    qDebug() << "<--- SpiDevice::setChipSelect()";

    return errCode;
}

/*******************************************************************************************
 * \brief      Checks the MISO line without clocking the SPI bus.
 * \param[out] state the busy state
 * \return     ERR_OK or any of the ERR_* error codes according to the error encountered
 *******************************************************************************************/
error_t SpiDevice::isBusy(bool &state)
{
    qDebug() << "---> SpiDevice::setChipSelect()";

    error_t errCode = ERR_OK;
    FT_STATUS ftStatus = FT_OK;

    ftCheckErr(SPI_IsBusy(m_handle, reinterpret_cast<BOOL*>(&state)));

Error:
    if (ftStatus != FT_OK) {
        errCode = errCodeFromFtStatus(ftStatus);
    }

    qDebug() << "<--- SpiDevice::setChipSelect()";

    return errCode;
}

// ==================================
//  Public member functions (static)
// ==================================

/*******************************************************************************************
 * \brief      Searches for an SPI device with a given description.
 * \param[in]  description the device description as in FT_DEVICE_LIST_INFO_NODE
 * \param[out] index the device index
 * \return     ERR_OK or any of the ERR_* error codes according to the error encountered
 * \note       The first device that corresponds to the criterion is taken into account.
 *******************************************************************************************/
error_t SpiDevice::getIndexByDescription(const QString &description, quint32 &index)
{
    qDebug() << "---> SpiDevice::getIndexByDescription(const QString &, quint32&)";

    error_t errCode = ERR_OK;
    FT_STATUS ftStatus = FT_OK;
    quint32 numChannels;
    bool found = false;

    ftCheckErr(SPI_GetNumChannels(reinterpret_cast<LPDWORD>(&numChannels)));

    for (int i = 0; i < numChannels; ++ i) {
        FT_DEVICE_LIST_INFO_NODE deviceInfo;
        ftCheckErr(SPI_GetChannelInfo(i, &deviceInfo));
        if (description.compare(deviceInfo.Description, Qt::CaseInsensitive) == 0) {
            index = i;
            found = true;
            break;
        }
    }

Error:
    if (ftStatus != FT_OK) {
        errCode = errCodeFromFtStatus(ftStatus);
    }
    else if (!found) {
        errCode = ERR_DEVICE_NOT_FOUND;
    }
    qDebug() << "<--- SpiDevice::getIndexByDescription(const QString &, quint32&)";

    return errCode;
}

/*******************************************************************************************
 * \brief      Searches for an SPI device with a given serial number.
 * \param[in]  serialNumber the serial number of the device as in FT_DEVICE_LIST_INFO_NODE
 * \param[out] index the device index
 * \return     ERR_OK or any of the ERR_* error codes according to the error encountered
 * \note       The first device that corresponds to the criterion is taken into account.
 *******************************************************************************************/
error_t SpiDevice::getIndexBySerialNumber(const QString &serialNumber, quint32 &index)
{
    qDebug() << "---> SpiDevice::getIndexBySerialNumber(const QString &, quint32&)";

    error_t errCode = ERR_OK;
    FT_STATUS ftStatus = FT_OK;
    quint32 numChannels;
    bool found = false;

    ftCheckErr(SPI_GetNumChannels(reinterpret_cast<LPDWORD>(&numChannels)));

    for (int i = 0; i < numChannels; ++ i) {
        FT_DEVICE_LIST_INFO_NODE deviceInfo;
        ftCheckErr(SPI_GetChannelInfo(i, &deviceInfo));
        if (serialNumber.compare(deviceInfo.SerialNumber, Qt::CaseInsensitive) == 0) {
            index = i;
            found = true;
            break;
        }
    }

Error:
    if (ftStatus != FT_OK) {
        errCode = errCodeFromFtStatus(ftStatus);
    }
    else if (!found) {
        errCode = ERR_DEVICE_NOT_FOUND;
    }
    qDebug() << "<--- SpiDevice::getIndexBySerialNumber(const QString &, quint32&)";

    return errCode;
}

// ===================================
//  Private member functions (static)
// ===================================

/*******************************************************************************************
 * \brief     Converts an FT_* status code to an ERR_* status code of the application.
 * \param[in] status the FT_* status code
 * \return    ERR_OK or any of the ERR_* error codes according to the FT_* status code
 *******************************************************************************************/
error_t SpiDevice::errCodeFromFtStatus(FT_STATUS status)
{
    qDebug() << "---> SpiDevice::errCodeFromFtStatus(FT_STATUS)";

    error_t errCode = ERR_OK;

    switch (status) {
    case FT_OK:
        errCode = ERR_OK;
        break;
    case FT_INVALID_HANDLE:
        errCode = ERR_OTHER;
        break;
    case FT_DEVICE_NOT_FOUND:
        errCode = ERR_DEVICE_NOT_FOUND;
        break;
    case FT_DEVICE_NOT_OPENED:
        errCode = ERR_OTHER;
        break;
    case FT_IO_ERROR:
        errCode = ERR_IO_ERROR;
        break;
    case FT_INSUFFICIENT_RESOURCES:
        errCode = ERR_OTHER;
        break;
    case FT_INVALID_PARAMETER:
        errCode = ERR_INVALID_PARAM;
        break;
    case FT_INVALID_BAUD_RATE:
        errCode = ERR_INVALID_PARAM;
        break;
    case FT_DEVICE_NOT_OPENED_FOR_ERASE:
        errCode = ERR_OTHER;
        break;
    case FT_DEVICE_NOT_OPENED_FOR_WRITE:
        errCode = ERR_OTHER;
        break;
    case FT_FAILED_TO_WRITE_DEVICE:
        errCode = ERR_IO_ERROR;
        break;
    case FT_EEPROM_READ_FAILED:
        errCode = ERR_IO_ERROR;
        break;
    case FT_EEPROM_WRITE_FAILED:
        errCode = ERR_IO_ERROR;
        break;
    case FT_EEPROM_ERASE_FAILED:
        errCode = ERR_IO_ERROR;
        break;
    case FT_EEPROM_NOT_PRESENT:
        errCode = ERR_OTHER;
        break;
    case FT_EEPROM_NOT_PROGRAMMED:
        errCode = ERR_OTHER;
        break;
    case FT_INVALID_ARGS:
        errCode = ERR_INVALID_PARAM;
        break;
    case FT_NOT_SUPPORTED:
        errCode = ERR_NOT_SUPPORTED;
        break;
    case FT_OTHER_ERROR:
        errCode = ERR_OTHER;
        break;
    case FT_DEVICE_LIST_NOT_READY:
        errCode = ERR_OTHER;
        break;
    default:
        errCode = ERR_UNKNOWN;
    }

    qDebug() << "<--- SpiDevice::errCodeFromFtStatus(FT_STATUS)";

    return errCode;
}


----------


#ifndef MCP300X_H
#define MCP300X_H

#include <QObject>
#include "spidevice.h"

/*!
 * \brief The MCP300X class
 */
class MCP300X : public QObject
{
    Q_OBJECT

public:
    bool init();
    virtual bool readChannel(int channel, int &value, bool single = true);

public:
    static MCP300X *create(SpiDevice &device, int channels, QObject *parent = nullptr);

private:
    explicit MCP300X(SpiDevice &device, int channels, QObject *parent = nullptr);

protected:
    SpiDevice &m_device;
    int        m_numChannels;
    quint16    m_value;
    quint8     m_csPin;

private:
    int     m_channel;
    quint8  m_txBuffer[3];
    quint32 m_txCount;
    quint8  m_rxBuffer[3];
    quint32 m_rxCount;
};

/*!
 * \brief The MCP3004 class
 */
class MCP3004 : public MCP300X
{
    Q_OBJECT

public:
    static MCP3004 *create(SpiDevice &device, quint8 csPin, QObject *parent = nullptr);
};

/*!
 * \brief The MCP3008 class
 */
class MCP3008 : public MCP300X
{
    Q_OBJECT

public:
    static MCP3008 *create(SpiDevice &device, quint8 csPin, QObject *parent = nullptr);
};

#endif // MCP300X_H


----------


#include "mcp300x.h"

#include <QtDebug>

constexpr const quint32 SPI_CLOCK       (3600000);
constexpr const quint8  SPI_LATENCY     (1);
constexpr const quint8  SPI_MODE        (SPI_CONFIG_OPTION_MODE0);
constexpr const quint8  SPI_CS0         (SPI_CONFIG_OPTION_CS_DBUS3);
constexpr const quint8  SPI_CS1         (SPI_CONFIG_OPTION_CS_DBUS4);
constexpr const quint8  SPI_CS_LEVEL    (SPI_CONFIG_OPTION_CS_ACTIVELOW);


// ===============
//  Class MCP300X
// ===============

// =========================
//  Public member functions
// =========================

/*******************************************************************************************
 * \brief     Creates an instance of MCP300X.
 * \param[in] device the SpiDevice instance that will be used by this object
 * \param[in] channels the number of channels of the ADC
 * \param[in] parent the object's parent object
 * \return    the newly created instance of the object or nullptr if an error occurred
 *******************************************************************************************/
MCP300X *MCP300X::create(SpiDevice &device, int channels, QObject *parent)
{
    qDebug() << "---> MCP300X::create(SpiDevice &, int, QObject*)";

    MCP300X *adc = nullptr;

    if (channels) {
        adc = new MCP300X(device, channels, parent);
    }

    qDebug() << "<--- MCP300X::create(SpiDevice &, int, QObject*)";

    return adc;
}

bool MCP300X::init()
{
    qDebug() << "---> MCP3004::init()";

    bool rc = true;
    error_t errCode = ERR_OK;

    checkErr(m_device.open());
    // checkErr(m_device.init(SPI_CLOCK, SPI_LATENCY, SPI_MODE, m_csPin, SPI_CS_LEVEL));
    checkErr(m_device.init(100000, 100, SPI_MODE, SPI_CS0, SPI_CS_LEVEL));

Error:
    if (errCode != ERR_OK) {
        rc = false;
    }

    qDebug() << "<--- MCP3004::init()";

    return rc;
}

/*******************************************************************************************
 * \brief      This member function starts an A/D conversion on a given channel and returns
 *             the value.
 * \param[in]  channel the channel number
 * \param[out] *value the value
 * \param[in]  single indicates single-ended or differential input mode
 * \return     true if successfull, otherwise false
 *******************************************************************************************/
bool MCP300X::readChannel(int channel, int &value, bool single)
{
    qDebug() << "---> MCP300X::readChannel(int, int&, bool)";

    bool rc = true;
    error_t errCode = ERR_OK;

    if (channel < 0 || channel >= m_numChannels) {
        rc = false;
    }
    else {
        m_channel     = channel;                        // Channel number
        m_txBuffer[0] = 0x01;                           // Start bit
        m_txBuffer[1] = m_channel << 4 | single << 7;   // Input config. + channel number
        m_txBuffer[2] = 0x00;
        // Test
        m_txBuffer[0] = 0x00;
        m_txBuffer[1] = 0x00;
        m_txBuffer[2] = 0x00;
        m_rxBuffer[0] = 0x00;
        m_rxBuffer[1] = 0x00;
        m_rxBuffer[2] = 0x00;
        // checkErr(m_device.setChipSelect(m_csPin));
        checkErr(m_device.transfer(m_rxBuffer, m_txBuffer, m_txCount, &m_rxCount,
                                   SPI_TRANSFER_OPTIONS_SIZE_IN_BYTES |
                                   SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE |
                                   SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE));
        reinterpret_cast<quint8*>(&m_value)[0] = m_rxBuffer[2];
        reinterpret_cast<quint8*>(&m_value)[1] = m_rxBuffer[1] & 0x07;
        // reinterpret_cast<quint8*>(&m_value)[1] = m_rxBuffer[1] & 0x03;
        value = m_value;
    }

Error:
    qDebug() << "<--- MCP300X::readChannel(int, int&, bool)";

    return rc;
}

// ==========================
//  Private member functions
// ==========================

/*******************************************************************************************
 * \brief     Creates an instance of MCP300X (constructor).
 * \param[in] device the SpiDevice instance that will be used by this object
 * \param[in] channels the number of channels of the ADC
 * \param[in] parent the object's parent object
 * \return    the newly created instance of the object or nullptr if an error occurred
 *******************************************************************************************/
MCP300X::MCP300X(SpiDevice &device, int channels, QObject *parent)
    : QObject{parent}
    , m_device{device}
    , m_numChannels{channels}
    , m_value{0}
    , m_csPin{SPI_CS0}
    , m_txBuffer{0x00, 0x00, 0x00}
    , m_txCount{3}
    , m_rxBuffer{0x00, 0x00, 0x00}
    , m_rxCount{3}
{
    qDebug() << "---> MCP300X::MCP300X(SpiDevice &, int, QObject*)";
    qDebug() << "<--- MCP300X::MCP300X(SpiDevice &, int, QObject*)";
}

// ===============
//  Class MCP3004
// ===============

/*******************************************************************************************
 * \brief     Creates an instance of MCP3004.
 * \param[in] device the SpiDevice instance that will be used by this object
 * \param[in] csPin the pin that is used as chip select
 * \param[in] parent the object's parent object
 * \return    the newly created instance of the object or nullptr if an error occurred
 *******************************************************************************************/
MCP3004 *MCP3004::create(SpiDevice &device, quint8 csPin, QObject *parent)
{
    qDebug() << "---> MCP3004::create(SpiDevice &, quint8, QObject*)";

    MCP3004 *adc = reinterpret_cast<MCP3004*>(MCP300X::create(device, 4, parent));

    // if (adc) {
    //     m_csPin = csPin;
    // }

    qDebug() << "<--- MCP3004::create(SpiDevice &, quint8, QObject*)";

    return adc;
}

// ===============
//  Class MCP3008
// ===============

/*******************************************************************************************
 * \brief     Creates an instance of MCP3004.
 * \param[in] device the SpiDevice instance that will be used by this object
 * \param[in] csPin the pin that is used as chip select
 * \param[in] parent the object's parent object
 * \return    the newly created instance of the object or nullptr if an error occurred
 *******************************************************************************************/
MCP3008 *MCP3008::create(SpiDevice &device, quint8 csPin, QObject *parent)
{
    qDebug() << "---> MCP3008::create(SpiDevice &, quint8, QObject*)";

    MCP3008 *adc = reinterpret_cast<MCP3008*>(MCP300X::create(device, 8, parent));

    // if (adc) {
    //     m_csPin = csPin;
    // }

    qDebug() << "<--- MCP3008::create(SpiDevice &, quint8, QObject*)";

    return adc;
}
