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):
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;
}