0

I am looking for some guidance on integrating a MAX31865 board into my Raspberry Pi project with a Qt front end. The board contains 4 MAX31865 chips with separate CS pins. I have confirmed the chips / RTDs are working correctly with both Arduino and STM32 boards, but for some reason I can't get the same output from Raspberry Pi. I'm wondering if the incorrect output has to do with the pigpio spiWrite / spiRead / spiXfer functions using (char *) for the rx / tx buffers rather than uint8_t like most other libraries. For example, I'll get 92.5 (roughly 92.406) on chips with RTDs attached, and -403.636 on chips without RTDs attached. Regardless, I'm not getting resistance changes at all on the sensor.

I'm trying to adapt some code I have used for the STM32 chipset into a C++ class and it looks OK, but would love a second (or more) pair of eyes if possible.

Here is my max31865.cpp file (For simplicity, i included the #defines here instead of the .h file)

#include "math.h"
#include "rpigpio.h"


#define MAX31865_CONFIG_REG             0x00
#define MAX31865_CONFIG_BIAS            0x80
#define MAX31865_CONFIG_MODEAUTO        0x40
#define MAX31865_CONFIG_MODEOFF         0x00
#define MAX31865_CONFIG_1SHOT           0x20
#define MAX31865_CONFIG_3WIRE           0x10
#define MAX31865_CONFIG_24WIRE          0x00
#define MAX31865_CONFIG_FAULTSTAT       0x02
#define MAX31865_CONFIG_FILT50HZ        0x01
#define MAX31865_CONFIG_FILT60HZ        0x00

#define MAX31865_RTDMSB_REG             0x01
#define MAX31865_RTDLSB_REG             0x02
#define MAX31865_HFAULTMSB_REG          0x03
#define MAX31865_HFAULTLSB_REG          0x04
#define MAX31865_LFAULTMSB_REG          0x05
#define MAX31865_LFAULTLSB_REG          0x06
#define MAX31865_FAULTSTAT_REG          0x07

#define MAX31865_FAULT_HIGHTHRESH       0x80
#define MAX31865_FAULT_LOWTHRESH        0x40
#define MAX31865_FAULT_REFINLOW         0x20
#define MAX31865_FAULT_REFINHIGH        0x10
#define MAX31865_FAULT_RTDINLOW         0x08
#define MAX31865_FAULT_OVUV             0x04
#define MAX31865_FAULT_NONE             0x00

#define RTD_A                           3.9083e-3
#define RTD_B                           -5.775e-7
#define _MAX31865_RREF                  402.0f
#define _MAX31865_RNOMINAL              100.0f

#define MAX31865_WIRES                  3
#define MAX31865_FILTERHZ               50


MAX31865::MAX31865(uint8_t spiHandle, uint8_t cs_pin)
{
    this->_spiHandle = spiHandle;
    this->_cs_pin = cs_pin;

    gpioSetMode(this->_cs_pin, PI_OUTPUT);
    gpioWrite(this->_cs_pin, PI_HIGH);

    MAX31865_setWires(MAX31865_WIRES);
    MAX31865_enableBias(false);
    MAX31865_autoConvert(false);
    MAX31865_clearFault();
    MAX31865_setFilter(MAX31865_FILTERHZ);
}

void MAX31865::MAX31865_readRegisterN(uint8_t address, uint8_t *buffer, uint8_t n) {

    uint8_t tmp = 0xFF;
    address &= 0x7F;

    gpioWrite(this->_cs_pin, PI_LOW);

    spiWrite(this->_spiHandle, (char *) &address, 1);
    while (n--) {
        spiXfer(this->_spiHandle, (char*) &tmp, (char *) buffer, 1);
        buffer++;
    }

    gpioWrite(this->_cs_pin, PI_HIGH);
}

uint8_t MAX31865::MAX31865_readRegister8(uint8_t address) {

        uint8_t ret = 0;
        MAX31865_readRegisterN(address, &ret, 1);
        return ret;
}

uint16_t MAX31865::MAX31865_readRegister16(uint8_t address) {
    uint8_t buffer[2] = {0, 0};

    MAX31865_readRegisterN(address, buffer, 2);
    uint16_t ret = buffer[0];
    ret <<= 8;
    ret |= buffer[1];

    return ret;
}

void MAX31865::MAX31865_writeRegister8(uint8_t address, uint8_t data) {

    gpioWrite(this->_cs_pin, PI_LOW);
    address |= 0x80;

    spiWrite(this->_spiHandle, (char*) &address, 1);
    spiWrite(this->_spiHandle, (char*) &data, 1);

    gpioWrite(this->_cs_pin, PI_HIGH);
}

void MAX31865::MAX31865_readFault(){
    this->_fault = MAX31865_readRegister8(MAX31865_FAULTSTAT_REG);
}

void MAX31865::MAX31865_clearFault() {

        uint8_t t = MAX31865_readRegister8(MAX31865_CONFIG_REG);
        t &= ~0x2C;
        t |= MAX31865_CONFIG_FAULTSTAT;
        MAX31865_writeRegister8(MAX31865_CONFIG_REG, t);
}

void MAX31865::MAX31865_enableBias(bool enable) {

    uint8_t t = MAX31865_readRegister8(MAX31865_CONFIG_REG);
    if (enable) {
        t |= MAX31865_CONFIG_BIAS;
    }
    else {
        t &= ~MAX31865_CONFIG_BIAS;
    }
    MAX31865_writeRegister8(MAX31865_CONFIG_REG, t);
}

void MAX31865::MAX31865_autoConvert(bool enable) {

        uint8_t t = MAX31865_readRegister8(MAX31865_CONFIG_REG);
        if (enable) {
            t |= MAX31865_CONFIG_MODEAUTO;
        }
        else {
            t &= ~MAX31865_CONFIG_MODEAUTO;
        }
        MAX31865_writeRegister8(MAX31865_CONFIG_REG, t);
}

void MAX31865::MAX31865_setWires(uint8_t numWires) {

    uint8_t t = MAX31865_readRegister8(MAX31865_CONFIG_REG);
    if (numWires == 3) {
        t |= MAX31865_CONFIG_3WIRE;
    }
    else {
        t &= ~MAX31865_CONFIG_3WIRE;
    }
    MAX31865_writeRegister8(MAX31865_CONFIG_REG, t);
}

void MAX31865::MAX31865_setFilter(uint8_t filterHz) {
    uint8_t t = MAX31865_readRegister8(MAX31865_CONFIG_REG);
    if (filterHz == 50) {
        t |= MAX31865_CONFIG_FILT50HZ;
    }
    else {
        t &= ~MAX31865_CONFIG_FILT50HZ;
    }
    MAX31865_writeRegister8(MAX31865_CONFIG_REG, t);
}

uint16_t MAX31865::MAX31865_readRTD() {

    MAX31865_clearFault();
    MAX31865_enableBias(true);

    std::this_thread::sleep_for (std::chrono::milliseconds(10));

    uint8_t t = MAX31865_readRegister8(MAX31865_CONFIG_REG);

    t |= MAX31865_CONFIG_1SHOT;
    MAX31865_writeRegister8(MAX31865_CONFIG_REG, t);

    std::this_thread::sleep_for (std::chrono::milliseconds(10));

    uint16_t rtd = MAX31865_readRegister16(MAX31865_RTDMSB_REG);
    MAX31865_enableBias(false);
    rtd >>= 1;

    return rtd;
}

void MAX31865::MAX31865_readTemp() {

    float Z1, Z2, Z3, Z4, Rt, temp;

    Rt = MAX31865_readRTD();
    Rt /= 32768;
    Rt *= _MAX31865_RREF;
    Z1 = -RTD_A;
    Z2 = RTD_A * RTD_A - (4 * RTD_B);
    Z3 = (4 * RTD_B) / _MAX31865_RNOMINAL;
    Z4 = 2 * RTD_B;
    temp = Z2 + (Z3 * Rt);
    temp = (sqrtf(temp) + Z1) / Z4;

    if (temp >= 0) {
        MAX31865_readFault();
        MAX31865_compareFault();
        // Assume the temperature is correct
        this->_MAX31865_tempC = temp;
        this->_MAX31865_tempF = (this->_MAX31865_tempC * 9.0f / 5.0f) + 32.0f;
    }
    else {
        Rt /= _MAX31865_RNOMINAL;
        Rt *= 100;

        float rpoly = Rt;

        temp = -242.02;
        temp += 2.2228 * rpoly;
        rpoly *= Rt;  // square
        temp += 2.5859e-3 * rpoly;
        rpoly *= Rt;  // ^3
        temp -= 4.8260e-6 * rpoly;
        rpoly *= Rt;  // ^4
        temp -= 2.8183e-8 * rpoly;
        rpoly *= Rt;  // ^5
        temp += 1.5243e-10 * rpoly;

        MAX31865_readFault();
        MAX31865_compareFault();
        this->_MAX31865_tempC = temp;
        this->_MAX31865_tempF = (this->_MAX31865_tempC * 9.0f / 5.0f) + 32.0f;
    }
}

void MAX31865::MAX31865_compareFault() {

    this->_faultText = "Unknown error has occured. Refer to the MAX31865 datasheet.";

    if (this->_fault == MAX31865_FAULT_NONE)
        this->_faultText = "No errors detected";

    if (this->_fault == MAX31865_FAULT_HIGHTHRESH)
        this->_faultText = "Measured resistance greater than High Fault Threshold value.";

    if (this->_fault == MAX31865_FAULT_LOWTHRESH)
       this->_faultText = "Measured resistance less than Low Fault Threshold value.";

    if (this->_fault == MAX31865_FAULT_REFINLOW)
        this->_faultText = "vREFIN > 0.85 x vBIAS.";

    if (this->_fault == MAX31865_FAULT_REFINHIGH)
        this->_faultText = "vRERFIN < 0.85 X vBIAS (FORCE - open).";

    if (this->_fault == MAX31865_FAULT_RTDINLOW)
        this->_faultText = "vRTRIN- < 0.85 X vBIAS (FORCE - open).";

    if (this->_fault == MAX31865_FAULT_OVUV)
        this->_faultText = "Any protected input voltage > vDD or < GND1.";

}

Here is the initialization code and temperature reading functions

int spiHandle = 0;

spiHandle = spiOpen(SPI_CHANNEL, SPI_SPEED, 0);
qDebug() << "[DEBUG] SPI Handle Open. Handle: " << spiHandle;

MAX31865 MAX31865_1(spiHandle, MAX31865_1_GPIO);
qDebug() << "[DEBUG] MAX31865 Initialized.";

volatile float _temp1_F = 0.0f;

MAX31865_1.MAX31865_readTemp();
qDebug() << "[DEBUG] 1 Temp F: " << MAX31865_1.read_MAX31865TempF();

_temp1_F = MAX31865_1.read_MAX31865TempF();

qDebug() << "[DEBUG] 1 Fault: " << MAX31865_1.read_MAX31865FaultText();

RPIDataStructure->set_tempMAX_1(_temp1_F);

Really appreciate any and all help on this project. Thank you.

Gerhardh
  • 11,688
  • 4
  • 17
  • 39
TyrantUT
  • 29
  • 1
  • 5
  • Do you have a logic analyzer? It might come in handy. To be completely frank, there's a lot underway that can go wrong, so before hopping to QT and the actual application I'd check the SPI transmission runs at all. And from there, progress it in baby-steps. Thus, I'd try to make it talk to some Arduino or whatever else you have (just by sending dummy, hardcoded bytes), then add the sensor, then configure it, and so on and so forth. – alagner Oct 21 '21 at 06:54
  • Now, provided it does not "talk" (lack of sensor's reaction suggests that to me): do you have spi kernel module loaded? Or maybe first: what is your platform? Raspbian, Arch, what versions of the OS, kernel etc.,? Now, you might not like my advice, but I'd start with system-level checks, e.g. by checking the wiring first. If that's fine, check the driver's loaded, consult the manual https://www.kernel.org/doc/html/latest/spi/spidev.html and try to connect using that API. I really suggests starting from there, as it should give you a deep understanding on how it works under the bonnet. – alagner Oct 21 '21 at 07:02
  • So, I did a basic loopback with miso/mosi bound together. I verified the module is loaded and what is sent, is what is received. I am testing the code outside of Qt for just concept first and I’m getting the same results as I do within Qt. The return of the spiWrite Read and aXfer all return no errors. Also not getting any SPI or GPIO initialization errors. Any recommendations as to whether pigpio is the best in this case or is something better? I’m open to suggestions. – TyrantUT Oct 21 '21 at 14:48
  • For me it was easier to wrap I2C and GPIO ioctl C API into Qt IO classes (a bit in the same manner as UART is implemented) but that's your call really. – alagner Oct 21 '21 at 15:03
  • Since it’s a Raspberry Pi, is there one particular GPIO library that works better within Qt than others? I need to run a few more tests but an analyzer would be awesome, unfortunately I just don’t have one. Typically things work as long as the data sheets are followed. In this case I know the hardware is good since it’s been used in other applications already. – TyrantUT Oct 22 '21 at 06:48
  • I used the [generic LInux one](https://blog.lxsang.me/post/id/33). What I can discourage is the usage of sysfs as it tends to lag behind the actual state quite often. As you know the SPI itself is fine via loopback connection I wonder: have you checked if the CS pin really goes down? And maybe it's worth to try a single sensor first, with fully hardware SPI, i.e. kernel-driver controlled CE pin? Also, doesnt the CE1 or CE0 override your manual CS setting causing the problem? – alagner Oct 22 '21 at 07:21
  • I have checked the CS pin yes. I control that with a manual setMode and gpioWrite prior to each spiWrite, Read or Xfer call. Ill run some sample code with the generic Linux one to see if that helps. – TyrantUT Oct 22 '21 at 17:36
  • I ran some extremely basic tests and so far, running the whole SPI routine to bring in data from the sensor works correctly within Python. From C/Cpp using pigpio I get nothing back from the read or transfer commands, which is telling me the pigpio library is garbage and isn’t working correctly. I’ll implement the Linux GPIO method and see if that works. – TyrantUT Oct 27 '21 at 16:43
  • Still no go. Even using the Linux interface for GPIO and banging at SPI, same as my Python implementation - still don’t get squat in C. No matter what, the same values are read over and over. Even though the exact same code in Python is bringing back the correct information. It’s just not possible so something has to be missing. any ideas? – TyrantUT Oct 28 '21 at 01:16

0 Answers0