Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 4 of 4

Thread: MAX31865 and Adafruit RTD library on Teensy 4.1 problem

  1. #1
    Junior Member
    Join Date
    Oct 2020
    Location
    Berks UK
    Posts
    5

    MAX31865 and Adafruit RTD library on Teensy 4.1 problem

    Hi all, new here, I'm very much an amateur though have some past PIC C programming experience, I wanted to try out the Teensy as it seems ideal for my car project. I'm replacing the 80s retro looking dash thermometer in a Porsche 968 with something that looks the part but the hope is to develop the project over time to control dash display of other CAN sourced data, I plan to add a CAN bus to the car and gather a lot more data in time... Anyway I started out with the thermometer part of the project as an intro to the Arduino/Teensy world.

    I attached a TM 1637 4x 7 segment board to the Teensy on pins 2-3 via a level shifter, all went well, I coded for temperature display from a float value, all worked fine. Perhaps being overconfident I then soldered an Adafruit clone MAX31865 board into the prototype. I cannot get any reading from the 31865 sadly. Ive extracted the code relating to the Max read and run it standalone, its pasted below, so no interaction with the TM 1367 library. The Adafruit library rtd() ADC read function returns zero as does fault(), cant even get an error code. Ive looked at much of the relevant library code though I admit I get lost in it a bit, I checked the pin config parameters seem to be being passed correctly though.

    I'm sure many will say I should have breadboarded this, oh well. I went for software SPI on pins 29-32, the connections are shown in my photo. I've listed them in the attached test code. Connections seem good, no shorts, continuity from breakout to the Teensy pins is as expected and documented here. 3.3v breakout supply and good ground as expected. The 3 wire PT100 RTD is connected as shown, approx 108R accross the centre 2 screw terminals and 0.6 R across the yellow/blue wires as expected. The RTD resistance increases as I warm it so it seems good. The breakout has the correct RREF, 430R. The code below loops fine, counting/flashing the LED but outputting 0 for rtd and fault values (there is an invalid calculated temp) via the serial terminal. I've run the code with/without #includes, no different, guess the Adafruit code sorts those. Ive tried setting the CS pin 29 as output/low in my code, again no different.

    Just wonder if anyone has had success with the Adafruit library and software SPI on the Teensy 4.1? I guess I could rewire so that Im using hardware SPI, I was intending to save that channel for other devices perhaps. I should really be able to make the current configuration work? Next step might be to play with a cheap logic analyser on the SPI bus I think, though I wonder if dead communications due to non-working code is more likely the problem. Im surprised none of the library functions returns an error really. I cannot 100 pc rule out a dead breakout board I suppose, but Ive done nothing bad to it that I know about. Any advice appreciated!

    Steve


    Click image for larger version. 

Name:	Boardpic.jpg 
Views:	19 
Size:	99.7 KB 
ID:	22221

    Code:
    /*   Hardware:
     *   
     *   Teensyduino 4.1 ARM processor board, note 3.3V I/O 
     *
     *   MAX 31865 PTD100 interface (3.3V Vin, 430R ref), 3 wire PTD, connected pins 29 CS, 30 SDI, 31 SDO, 32 CLK 
     *   
     *   
     */
    
    
    //#include <Arduino.h>
    //#include <Wire.h>
    //#include <SPI.h>
    #include <Adafruit_MAX31865.h>
    
    // MAX 31867 setup:
    
    #define RREF 430                                                    //RTD reference resistor value used for the PT100 sensor
    #define RNOMINAL 100                                                // Nominal 0C resistance for PT 100 RTD
    
    Adafruit_MAX31865 thermo = Adafruit_MAX31865(29,30,31,32);        //Set up thermometer object, defining interface pins for MAX RTD interface
    
    
    
    // The amount of time (in milliseconds) for default delays
    #define DELAY   2000
    
    
    int led = 13;                     // Pin 13 has the LED on Teensy
    float temp = 0;
    int ctr = 0;
    int rtd = 0;
    
    
    void setup()
    {
     
      pinMode(led, OUTPUT); 
      //pinMode(29, OUTPUT);           //Slave select pin MAX 31865 is 29 
      //digitalWrite(29, LOW);
      flashLED();
      thermo.begin(MAX31865_3WIRE);           // Set up MAX interface for the 3 wire RTD in use
    }
    
      
    void loop()
    {
      ctr ++;
      
      int fault = thermo.readFault();
    
      temp = thermo.temperature(RNOMINAL, RREF);                 // Read temp, returns float value
      
      rtd = thermo.readRTD();                                    // Read the raw ADC data, returns int value
    
      Serial.print("Reading no: "); Serial.println (ctr);
      Serial.print("Fault code: "); Serial.println(fault);
      Serial.print("RTD value: "); Serial.println(rtd);
      Serial.print("Temp: "); Serial.println(temp);
      
      flashLED();
    }
    
    void flashLED (void)
    {
      digitalWrite(led, HIGH);         // turn the LED on 
      delay(100);                      // one LED flash
      digitalWrite(led, LOW);          // turn the LED off
      delay (DELAY);
    }

  2. #2
    Member
    Join Date
    Feb 2020
    Location
    Dublin, Ireland
    Posts
    48
    Hi Steve,
    I'm using those boards but with hardware SPI. Are you sure that board will work with a 3.3V supply? I'm using 5V. Have you checked what the voltage is at the 3.3V output on the breakout board?
    All the best,
    Alan

  3. #3
    Junior Member
    Join Date
    Oct 2020
    Location
    Berks UK
    Posts
    5
    Quote Originally Posted by AlanK View Post
    Hi Steve,
    I'm using those boards but with hardware SPI. Are you sure that board will work with a 3.3V supply? I'm using 5V. Have you checked what the voltage is at the 3.3V output on the breakout board?
    All the best,
    Alan
    Thanks for the input Alan. Maybe I should have gone for hardware SPI, it is a prototype after all... I thought that the Adafruit style breakout could be fed with 3.3 to 5v optional at Vin. I checked the breakouts 3.3v out pin and itís reading 3.3v so the on board regulator seems to be outputting correctly. If I donít get anywhere I might get another teensy and play with hardware spi on a breadboard. Just frustrating that what perhaps should work does not,

    Steve

  4. #4
    Junior Member
    Join Date
    Oct 2020
    Location
    Berks UK
    Posts
    5
    OK so inspired by both Alan's comment and the not-fun nature of debugging code using the Adafruit MAX31865 library I decided to switch my design to hardware SPI and to dump the library, reducing code complexity. I coded my own Max routines so dependency is now only upon the SPI library. I wanted to use port SPI1 for geographical convenience and to avoid a clash with the on board LED. For a noob to the Teensy 4.1 the use of port SPI1 vs default SPI is a little thinly documented online, so I wondered if my working test code might help others finding this thread. I commented the code to try to make it helpful, there's a lot of MAX18365 info in there, not all of which was obvious to me from the datasheet. The code goes as far as obtaining a float temperature calculated via the Callender Van Dusen method - thanks for that bit Adafruit. I intend to develop it onwards and probably use lookup table temp conversion in final use. Obviously it can be made a lot more elegant and tighter, with removal of the delay()s (polling, interrupts for timing). I just wanted to get a basic working version first! Anyway, hope some Teensy 4.1/SPI1/Max31865 code is useful to someone:

    Code:
    /*   Teensy 4.1 MAX31865 SPI RTD interface test code
     *   Code demonstrates the use of the Teensy SPI1 serial interface
     *   
     *   S Williams, 7/11/20
     *   
     *   Disclaimer:
     *   
     *   This is demo code with no warranty and is not intended for final use as is, rather as a basis for modification. Any user is responsible for assessment of suitability for any 
     *   purpose and for modifying the code to ensure compatibilty and function.  Beware that use with inappropriate hardware configurations, such as when incorrect hardware port pins are 
     *   grounded, can cause permanent hardware damage.
     *    
     *   Hardware:
     *   
     *   Teensyduino 4.1 ARM processor board, note 3.3V I/O 
     *   MAX 31865 PTD100 interface (3.3V Vin, 430R ref) with 3 wire PTD ("max1")
     *   Connections on Teensy 4.1 are to SPI1 pins MOSI1 26, MISO1 39, SCK1 27. Data pins also used: CS 32, RDY 28 (these two can be any data pin)
     *   The default Teensy SPI port was not used during testing, note that the on board LED is connected to the SPI SCK output if modifying for this port
     *   
     *   The Adafruit Callender Van Dusen conversion code in function tempconversionCVD() was downloaded from: https://github.com/adafruit/Adafruit_MAX31865
     *   and was written by Limor Fried/Ladyada for Adafruit Industries (suppliers of MAX31865 interface boards!)
     *   
     */
    
    
    #include <Arduino.h>
    #include <SPI.h>
    
    #define DEBUG 1
    #define DELAY   2000                                        // The amount of time (in milliseconds) for default delays
    #define LED 13                                              // LED is on pin 13 Teensy board
    
    // SPI setup:
    #define MOSI1 26                                            // Define the hardware SPI pins in use (must be available on hardware)
    #define MISO1 39
    #define SCK1 27
    #define CSMAX1 32                                           // Not using a hardware CS pin, this can be any digital IO capable pin
    #define RDYMAX1 28                                          // Pin can be polled for MAX conversion data ready
    
    // MAX 31867 setup:
    #define RREF 430                                            // RTD reference resistor value used for the PT100 sensor
    #define RNOMINAL 100                                        // Nominal 0C resistance for PT 100 RTD
    #define RTD_A 3.9083e-3                                     // Callender V D coefficiants A/B for temp conversion
    #define RTD_B -5.775e-7
    
    
    #define HIGH_THRESH_MSB 0x87                                // High fault threshold is 0x87B6, low fault 0x196C, configuration values are ADC RTD output in 16 bit format
    #define HIGH_THRESH_LSB 0xB6                                // Fault will be flagged if RTD resistance > 212.05R, equivalent to approx 300C or if < 39.72R, -150C
    #define LOW_THRESH_MSB  0x19
    #define LOW_THRESH_LSB 0x6C    
                                
    
    #define CONFIG_WRITE_ADDR 0x80
    #define CONFIG_READ_ADDR 0x00    
    #define RTDLSB 0x02                                         // RTD read data address LSB
    #define RTDMSB 0x01                                         // RTD read data address MSB
    #define FAULT_ADDR 0x07                                     // Fault register
    #define RTDFAULTBIT 0x01                                    // Fault bit is bit 0 of rtd LSB 
    #define CONFIG3WIRE 0b00110001                              // Config register for MAX31865 set up with bias current off(D7) initially, 3 wire(D4),1 shot conversion 
    #define CONFIG24WIRE 0b00100001                             // Config setup for 2/4 wire sensors.Base configs do not clear faults, random initial data on read fault reg.
    #define FAULTCLEAR 0b00000010                               // Bit D1 of config clears faults when set, autoclear
    #define BIAS_ON 0b10000000                                  // Mask to set D7 of config register, turns on RTD bias current     
    #define CONVERSION_ON 0b00100000                            // Mask to set config D5, starts a one shot conversion
    
    
    SPISettings max1(5000000,MSBFIRST,SPI_MODE3);               // Specifies SPI interface params for the MAX31865 RTD interface chip max1, maximum chip clock rate is 5 MHz
    
    
    // Global variables:
    
    float temp = 0;
    uint8_t maxconfigdata = CONFIG3WIRE;
    uint16_t ctr = 0;
    uint16_t rtd = 0;
    uint8_t fault = 0;
    uint8_t data = 0;
    uint8_t configread = 0;
    uint8_t configerrorflag = 0;
    uint8_t faultregister =0;
    uint8_t faultflag = 0;
    uint8_t readbuffer[8];                            // Buffer used for multibyte register reads, could be up to 8 registers
    
    
    void setup()
    { 
      // Setup LED and SPI: 
      
      pinMode(LED, OUTPUT); 
      pinMode(CSMAX1, OUTPUT);                        // Slave select pin for MAX 31865 set as output
      digitalWrite(CSMAX1, HIGH);                     // CS pin high = MAX1 inactive
      SPI1.setMOSI(MOSI1);                            // Alternate hardware SPI pins set here, need to do if use SPI1 rather than default SPI port (testing was on Teensy 4.1)
      SPI1.setMISO(MISO1);      
      SPI1.setSCK(SCK1);
      SPI1.begin();                                   // Enable SPI port, SPI1, second of the hardware SPI ports being used here 
      delay(10);                                      // Delay after setup in case any timing issue with Max chip response
    
    
      //Initialise the MAX31865 registers:
      
      SPI1.beginTransaction(max1);                                  // SPI communication is transactional using max1 settings
      SPI1WriteByte(CONFIG_WRITE_ADDR,maxconfigdata);               // Write initial config data to the Max 13865 config register via port SPI1
      configread=SPI1ReadByte(CONFIG_READ_ADDR);                    // Now read the same register to confirm we have SPI communication
      SPI1.endTransaction();                                        // Don't set config fault clear bit pre read test here, autoclear causes error
      configerrorflag = (maxconfigdata == configread) ? 0 : 1;            // 0 means the configuration register write succeeded
      
      if (configerrorflag)
        {
          // Handle error and generate a system error code, could indicate errors with onboard LED flashes?
        }
      else
        {
          SPI1.beginTransaction(max1); 
          SPI1WriteByte(0x83,HIGH_THRESH_MSB);                // Write configuration data to the 4 fault threshold registers 0x83-86
          SPI1WriteByte(0x84,HIGH_THRESH_LSB);                // Could code more elegantly using data buffer and multibyte write if preferred
          SPI1WriteByte(0x85,LOW_THRESH_MSB); 
          SPI1WriteByte(0x86,LOW_THRESH_LSB);
          SPI1.endTransaction();
        }
        
      flashLED();                        // Board LED indicates that setup() code ran       
    }                //end setup
    
      
    void loop()
    {
      ctr ++;
      
      //int fault = thermo.readFault();
    
      
      // Read the raw ADC data, returns int value:
      
      setbias(1);                                                          // Sets RTD bias current on ready for 1 shot conversion
      delay(10);                                                           // Allow for bias circuit time constant, ideally replace delay to allow processing, use timer instead
      clearfault();                                                        // Clear the fault register, note initialisation with random data
      startconversion();                                                   // Start one shot conversion  ADD FAULT REGISTER result handling. Dont read value in if fault, reset rtd to zero!
      delay(70);                                                           // Delay should be replaced to allow ongoing loop processing, use timer or Max RDY flag to allow read completion
      readrtd();                                                           // Readrtd() will return rtd data via global readbuffer[], element [0]=lsb, [1]=msb, [3]=fault register contents 
      setbias(0);                                                          // Bias off
      faultflag = readbuffer[0] & RTDFAULTBIT;                             // Get the fault bit, D0 of LSB, 1 is fault, 0 is ok
      rtd = 0;
      if (faultflag==1)
      {
        faultregister=readbuffer[2];                                       // *** ADD FAULT HANDLING TO CODE ***                                                               
      }
      else
      {
        rtd = readbuffer[1];                                               // High byte of rtd value bitshifted into D16-9 of the 16 bit rtd integer
        rtd <<= 8;
        rtd |= readbuffer[0];
        rtd >>= 1;                                                         // Shift 1 right, gets rid of fault bit leaving the 15 bit ADC value (RTD resistance = ADC/32767*RREF)
      }
    
      temp = tempconversionCVD(rtd, RNOMINAL, RREF);                       // Call Adafruit code for CVD temp conversion of 15 bit ADC value
    
      if (DEBUG)
        {
          debugoutput();                                                   // Outputs debug data including registers to serial monitor, nb delays loop, repeated output
        }
        
      delay (DELAY);                                                       // *** Test code delay, replace with timing code for reads  ***
    }                  //end loop
    
    
    
    // Functions:
    
    void flashLED (void)
    {
      digitalWrite(LED, HIGH);         // turn the LED on 
      delay(100);                      // one LED flash
      digitalWrite(LED, LOW);          // turn the LED off
      return;
    }
    
    
    void debugoutput (void)
    {
        Serial.print("Loop counter: "); Serial.println (ctr);                                            // Serial output of debug data, nb delays loop, DEBUG=TRUE to enable
        Serial.print("Max 31867 Config data: 0x"); Serial.println(configread,HEX);
        Serial.print("Max 31867 Config error flag: "); Serial.println(configerrorflag);
        Serial.print("Max 31867 RTD Fault flag: "); Serial.println(faultflag);
        Serial.print("Max 31867 Fault Register: 0x"); Serial.println(fault,HEX);   
        Serial.print("Fault threshold hi MSB: 0x"); Serial.println (SPI1ReadByte(0x03),HEX);             // Non-transactional reads used during debug, beware interrupt effects        
        Serial.print("Fault threshold hi LSB: 0x"); Serial.println (SPI1ReadByte(0x04),HEX);
        Serial.print("Fault threshold low MSB: 0x"); Serial.println (SPI1ReadByte(0x05),HEX);
        Serial.print("Fault threshold low LSB: 0x"); Serial.println (SPI1ReadByte(0x06),HEX);   
        Serial.print("RTD value: "); Serial.println(rtd);
        Serial.print("Temp degrees C: "); Serial.println(temp);
        
        /*   
        Serial.print("Fault code: "); Serial.println(fault);
        */  
        flashLED();
    }  
    
    void SPI1WriteByte (uint8_t address, uint8_t writebyte)
    {
      digitalWrite(CSMAX1, LOW);          // CS pin low = Activates MAX1 interface on SPI1
      SPI1.transfer(address);             // Send the address of register to be written
      SPI1.transfer(writebyte);           // Send the data to be written
      digitalWrite(CSMAX1, HIGH);         // Must toggle the CS pin high to terminate write before sending a further address
      return;
    }
    
    uint8_t SPI1ReadByte(uint8_t address)
    {
      uint8_t returnbyte = 0;
      digitalWrite(CSMAX1, LOW);            // Take CS pin low to activate MAX1 interface
      SPI1.transfer(address);               // Send the address of register to be read
      returnbyte = SPI1.transfer(0xFF);     // Dummy FF write to clock out data
      digitalWrite(CSMAX1, HIGH);           // CS pin high = MAX1 inactive, further address write may follow if CS taken is active low again
      return (returnbyte);
    }
    
    void clearfault (void)
    {
      SPI1.beginTransaction(max1); 
      uint8_t data=SPI1ReadByte(CONFIG_READ_ADDR);                          // First read the config register for the read modify write operation
      SPI1WriteByte(CONFIG_WRITE_ADDR,(data |= FAULTCLEAR));                // Set bit 2 config register to clear fault register, bit autoclears
      SPI1.endTransaction();
      return;
    }
    
    void startconversion (void)
    {
      SPI1.beginTransaction(max1);                                           // Starts one shot conversion, note can take approx 65 ms
      uint8_t data=SPI1ReadByte(CONFIG_READ_ADDR);                           // Read modify write of config register
      SPI1WriteByte(CONFIG_WRITE_ADDR,(data |= CONVERSION_ON));              // Set bit 5 of config to start one shot conversion, bit will clear automatically
      SPI1.endTransaction();
      return;
    }
    
    void readrtd (void)
    {
      SPI1.beginTransaction(max1);                                           // Read both ADC RTD registers, must have allowed enough time for a conversion or read data RDY flag
      readbuffer[0]=SPI1ReadByte(RTDLSB);                                    // Return is via the global buffer, 3 bytes total
      readbuffer[1]=SPI1ReadByte(RTDMSB);                                           
      readbuffer[2]=SPI1ReadByte(FAULT_ADDR);                                // Also return the contents of fault register
      SPI1WriteByte(CONFIG_WRITE_ADDR,maxconfigdata);                        // Restore max config as initialised, turns off bias current
      SPI1.endTransaction();
      return;
    }
    
    void setbias(uint8_t bias)
    {
      SPI1.beginTransaction(max1); 
      uint8_t data=SPI1ReadByte(CONFIG_READ_ADDR);                            // First read the config register for the read modify write operation
      if (bias)
        {
          SPI1WriteByte(CONFIG_WRITE_ADDR,(data |= BIAS_ON));                 // Set bit 7 config register to to turn on RTD bias current
        }
      else
        {
          SPI1WriteByte(CONFIG_WRITE_ADDR,(data &= (~BIAS_ON)));              // Unset bit 7 for bias current off
        }
      SPI1.endTransaction();
      return;
    }
    
    float tempconversionCVD(uint16_t rtdval, float RTDnominal, float refResistor) 
    
    {
      /*
       * Callender Van Dusen temperature conversion of 15 bit ADC resistance ratio data 
       * The Adafruit conversion code in this function was downloaded from: https://github.com/adafruit/Adafruit_MAX31865
       * Written by Limor Fried/Ladyada for Adafruit Industries (suppliers of MAX31865 interface boards!)
       */
      
      float Z1, Z2, Z3, Z4, Rt, temp;
    
      Rt = rtdval;
      Rt /= 32768;
      Rt *= refResistor;
    
      // Serial.print("\nResistance: "); Serial.println(Rt, 8);
    
      Z1 = -RTD_A;
      Z2 = RTD_A * RTD_A - (4 * RTD_B);
      Z3 = (4 * RTD_B) / RTDnominal;
      Z4 = 2 * RTD_B;
    
      temp = Z2 + (Z3 * Rt);
      temp = (sqrt(temp) + Z1) / Z4;
    
      if (temp >= 0)
        return temp;
    
      // ugh.
      Rt /= RTDnominal;
      Rt *= 100; // normalize to 100 ohm
    
      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;
    
      return temp;
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •