Weird SPI MISO readings after writing to slaves

Status
Not open for further replies.

ChrisHell

New member
Hi!

I am slightly confused and and hope you can help me. I am working on a temperature controller with the Teensy 3.1. I have a LTC2410 as ADC and an AD5689 as DAC connected to the SPI0 bus. Additionaly I connected a 1.44" TFT LCD from Adafruit which uses the ST7735 controller to the SPI bus. The Teensy is of course the master; ADC, DAC, LCD are slaves.

At this point there is no controlling in the software because I'm still struggling with this SPI problem. In the software I basically take 50 samples from the ADC, (calculate mean value and standard deviation, not so important here), send data to the pc, write the temperature on the LCD and update the DAC (just a slow ramp at the moment).

If I use SPI only to read the ADC, everything works fine. If I write values to the LCD (DAC still inactive), the second value of the 50 samples taken together is always wrong, the first byte of it is missing. I could fix this with setting the ROOE bit (Receive Overflow Overwrite Enable) to the SPI0_MCR register. I am aware that this is probably no good solution, but as the signals on the bus were fine, I tried to find an answer in the datasheet of the microcontroller. However this helped me with using the ADC and the LCD, but when I then tried to use all three components (ADC, LCD, DAC) on the SPI bus, always the first value of the 50 was wrong, the signals on the bus were still fine. To fix this, I flush the SPI receive FIFO with setting the CLR_RXF bit to the SPI0_MCR register between the write and the read part of the software (which works also without enabling the overflow overwrite in the setup()).

Maybe someone else has had similar problems which I didn't find yet or I am doing something obviously wrong - anyway I appreciate your help.

Here's the code (quite long, sorry, but quite simply, too. Forget about the statistics and temperature calculation at the bottom, that's not important right now):
Code:
// this is config.h
#define CS_DAC          10
#define CS_ADC          6
#define CS_LCD          21
#define SPI_ROOE        0x01000000      // SPI: Receive FIFO Overflow Overwrite Enable
#define SPI_CLR_RXF     0x00000400      // SPI: Flush Receive FIFO

#define CLK_DAC         24000000
#define CLK_ADC         12000000
//#define CLK_LCD         16000000

#define DAC_UPDATE_CH_A 0x31            // first byte to send to DAC to select channel and other stuff
#define EN_OUTPUT_AMP   16              // Pin: Output Peltier Power OpAmp: 0: disable, 1: enable (?)
#define BUSYPIN         12              // Pin: ADC SDO on falling edge of CS_ADC: 0: done, 1: still busy
#define VREF_ADC        (1.0)           // reference voltage of ADC
#define RST_LCD         20              // Pin: Reset pin for LCD 
#define DC_LCD          23              // Pin: Data/Command pin for LCD
#define BL_LCD          22              // Pin: Output PWM to dim backlight of LCD. Keep low for full BL

#define B_NTC           3977            // material constant B of NTC (http://www.vishay.com/docs/29049/ntcle100.pdf)
#define T_R_NTC         298.15          // rated temperature of NTC 25°C = 273.15K + 25K
#define R_R_NTC         10000           // rated resistance of NTC @ T=25°C
#define R_BRIDGE        10000           // Bridge resistors
#define VREF_BRIDGE     5.0             // Reference voltage of wheatstone bridge
#define KELVIN          273.15          // Offset between Celsius and Kelvin scale

#define MAX_VOLT_OUT    5.0             // maximum voltage output of DAC plus op-amp-circuit
#define MIN_VOLT_OUT    -5.0            // minimum voltage output of DAC plus op-amp-circuit
#define MAX_DAC         65535           // maximum numerical value valid for the DAC









// some ideas and parts of the code are taken from 
// http://dangerousprototypes.com/forum/viewtopic.php?t=4247&p=42053

// include the library code:
#include "config.h"             // definitions for this code
#include <SPI.h>                // include the SPI library
#include <Adafruit_ST7735.h>    // display driver ST7735
#include <Adafruit_GFX.h>       // core graphics library

// make display object 
Adafruit_ST7735 tft = Adafruit_ST7735(CS_LCD, DC_LCD, RST_LCD);

// global variables
// SPI settings for different peripherie
SPISettings SPISettingsDAC(CLK_DAC, MSBFIRST, SPI_MODE0);
SPISettings SPISettingsADC(CLK_ADC, MSBFIRST, SPI_MODE0);

int nSamples = 50;              // number of samples to average
double voltADC;                 // return value of ADC-function
float uV, stdev, pp;            // results from statistic calculations over sampled ADC values
double measTemp;                // measured temperature
int rect_l, rect_u, 
    rect_w, rect_h;             // rectangle values for display purposes

int i;                          // loop counter variable for for-loops
int cnt;                        // counts loops of loop function

void setup() 
{ 
    // configure serial communication with pc
    Serial.begin(9600);
    
    // initialize SPI:
    pinMode(CS_DAC, OUTPUT);
    digitalWriteFast(CS_DAC, HIGH);
    pinMode(CS_ADC, OUTPUT);
    digitalWriteFast(CS_ADC, HIGH);
    pinMode(CS_LCD, OUTPUT);
    digitalWriteFast(CS_LCD, HIGH);
    SPI.begin();

    // initialize control of Power Output Amp
    pinMode(EN_OUTPUT_AMP, OUTPUT);
    digitalWriteFast(EN_OUTPUT_AMP, LOW);
    digitalWriteFast(EN_OUTPUT_AMP, HIGH);

    // initialize busy pin of ADC 
//    pinMode(BUSYPIN, INPUT);          // Busypin = MISO. This command fucks up SPI MISO actions

    // configure IO pins

    // first line of serial output
    Serial.println("sample no; uV; peak-peak; stddev; temp(C)");

    // initialization for LCD (backlight)
    pinMode(BL_LCD, OUTPUT);
    digitalWriteFast(BL_LCD, LOW);
    tft.initR(0x01);                    // init sequence of display
    tft.fillScreen(ST7735_BLACK);       // fill screen black in the beginning
    
    // reconfigure SPI Module Configuration Register (allow ovorflow overwrite)
    // must be AFTER initialisation of LCD / TFT!
//    SPI0_MCR |= SPI_ROOE;
}

//////////////////////////////////////////////////////////////////////////////////////////////
// main loop                                                                                //
//////////////////////////////////////////////////////////////////////////////////////////////
void loop() 
{
    // Flush the receive FIFO of SPI module
    // otherwise the SPI read operation will return random numbers on the first of nSamples reads
    SPI0_MCR |= SPI_CLR_RXF;
    
    // sample the ADC nSamples times
    for(i=0; i<nSamples; i++)
    {        
        voltADC = LTC2410_read(SPISettingsADC);
        if(i==0)
            calcStat(voltADC, &uV, &pp, &stdev, 1);
        else
            calcStat(voltADC, &uV, &pp, &stdev, 0);
        delay(200);
    }

    measTemp = calcTemperatur(uV);

    Serial.print(cnt);
    Serial.print("; ");
    Serial.print(uV,3);
    Serial.print("; ");
    Serial.print(pp,3);
    Serial.print("; ");
    Serial.print(stdev,3);
    Serial.print("; ");
    Serial.println(measTemp,3);
    
    // print temperature on display
    tft.setTextWrap(false);
    tft.setCursor(10, 10);
    tft.setTextColor(ST7735_RED);
    tft.setTextSize(2);
    rect_l = rect_u = 10;
    rect_w = 10*9;
    rect_h = 14;
    tft.fillRect(rect_l, rect_u, rect_w, rect_h, ST7735_BLACK);
    tft.print(measTemp,3);
    delay(500);

    // test DAC write to check if it messes up the ADC SPI, too
    AD5689_write(cnt*0.1);  
    delay(100);

    cnt++;
}



//////////////////////////////////////////////////////////////////////////////////////////////
// Read analog value from ADC (LTC2410)                                                     //
//////////////////////////////////////////////////////////////////////////////////////////////
double LTC2410_read(SPISettings mode)
{
    // local variables
    int value = 0; 
    byte byteRead = 0;
    double voltMeasured;

    // read ADC value
    SPI.beginTransaction(mode);
    digitalWriteFast(CS_ADC, LOW);
    delayMicroseconds(1);

    // make sure the ADC is ready
    while(digitalReadFast(BUSYPIN)==HIGH) {}  // wait until ADC result is ready

    // read 4 bytes which contain analog value
    byteRead = SPI.transfer(0);
    value |= (byteRead & 0x1F)<<24;     // first three bits are control signals
    byteRead = SPI.transfer(0);
    value |= (byteRead & 0xFF)<<16;
    byteRead = SPI.transfer(0);
    value |= (byteRead & 0xFF)<<8;
    byteRead = SPI.transfer(0);
    value |= (byteRead & 0xFF)<<0;      // last 5 bits can or cannot be used (e.g. averaging)
    digitalWriteFast(CS_ADC, HIGH);
    SPI.endTransaction();

    // calculate the measured voltage from the 24 bit ADC value
    value = value >> 5;         // last 5 bits are "sub-LSB"

    if(value>=8388608)          // if bit 24 is high
        value |= 0xFF000000;    // -> 2-complement
    voltMeasured = (double)value * VREF_ADC / 16777216;

    
    // return
    return voltMeasured;
}


//////////////////////////////////////////////////////////////////////////////////////////////
// Write value to DAC (AD5689)                                                              //
//////////////////////////////////////////////////////////////////////////////////////////////
void AD5689_write(double voltage) 
{   
    // local variable
    unsigned int value;
    
    // calculate numerical from voltage
    if((voltage > MAX_VOLT_OUT) || (voltage < MIN_VOLT_OUT))
        return;
    value = (int)((voltage+MAX_VOLT_OUT)*MAX_DAC/(MAX_VOLT_OUT-MIN_VOLT_OUT));

    // SPI transaction
    SPI.beginTransaction(SPISettingsDAC);               
    digitalWriteFast(CS_DAC, LOW);
    SPI.transfer(DAC_UPDATE_CH_A);
    SPI.transfer(highByte(value));
    SPI.transfer(lowByte(value));
    digitalWriteFast(CS_DAC, HIGH);
    SPI.endTransaction();
    
    return;
}

//////////////////////////////////////////////////////////////////////////////////////////////
// Statistics calculation                                                                   //
//////////////////////////////////////////////////////////////////////////////////////////////
int calcStat(float x, float *uV, float *pp, float *stdev, int rst)
{
    static float datSum, n, sMax, sMin, mean, m2; 
    float delta, variance;

    // if rst, reset all static variables to zero for new measurement
    if(rst)
    {
        datSum  = 0;
        sMax    = -VREF_ADC/2;
        sMin    = VREF_ADC/2;
        n       = 0;
        datSum  = 0;
        mean    = 0;
        m2      = 0;     
    }

    // statistics...
    datSum += x;
    if (x > sMax) sMax = x;
    if (x < sMin) sMin = x;
                      // from http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
    n++;
    delta   = x - mean;
    mean    += delta/n;
    m2      += (delta * (x - mean));

    variance    = m2/(n-1);             // (n-1):Sample Variance  (n): Population Variance
    *stdev      = 1E6*sqrt(variance);   // Calculate standard deviation in microvolts
    *pp         = 1E6 * (sMax - sMin);  // peak-to-peak difference of readings, in uV
    *uV         = datSum * (1E6) / n;   // average voltage in microVolts
    
    return 0;    
}


//////////////////////////////////////////////////////////////////////////////////////////////
// Temperature calculation                                                                  //
//////////////////////////////////////////////////////////////////////////////////////////////
double calcTemperatur(double v_meas)
{
    double temperature, r_temperature;

    r_temperature = (-2*v_meas/(VREF_BRIDGE*1e6/2+v_meas)+1)*R_BRIDGE;

    temperature = B_NTC*T_R_NTC/(T_R_NTC*log(r_temperature/R_R_NTC)+B_NTC);
    
    return temperature-KELVIN;  
}
 
Hi everyone,

maybe my first post was too bloated. What I found in the meantime is that everything works fine, if I delete the "Adafruit_ST7735.h" and "Adafruit_ST7735.cpp" files from the Arduino\hardware\teensy\avr\libraries directories. I kept the files of the same name (probably the ones directly from Adafruit) in the Arduino\libraries\TFT\src\utility directory, which the compiler seems to use now.

Now I get the following warning but the program is working as I expected it in the beginning (warning translated from German).

Invalid library C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Adafruit_ST7735 in C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Adafruit_ST7735 found
Invalid library C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Adafruit_ST7735 in C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Adafruit_ST7735 found

Since I use the 1.44" TFT with the ST7735 controller and on https://www.pjrc.com/teensy/td_libs_ST7735.html there is only support for the 1.8" displays mentioned, maybe that is the source of the problem. Maybe someone can confirm this?
 
Status
Not open for further replies.
Back
Top