Hi there,
I am currently working on a project that has two SPI devices. One is a CAN controller and the other is an A/D. My idea is to read two thermocouples from a ADS1118 and send the data out on CAN via a MCP2515. Separately, they both work fine (as in – separate test programs). However, I am struggling to get them to work in the same block of code due to the SPI modes being different for each of the devices. So:
A/D: ADS1118 – SPI mode 1 (from the data sheet).
CAN: MCP2515 – SPI mode 0 or 3 (from the data sheet).
I am setting up the ADS1118 as a normal SPI device – as in:
SPISettings thermocouple(500000, MSBFIRST, SPI_MODE1);
However, I’m using a library to drive the CAN (from Seeed Technology Inc):
#include <mcp_can.h>
Is there a way I can keep changing the SPI mode before I talk to each device? A bit like having two RS232 circuits? As in calling the ‘SPISettings’ line of code? I’ve looked through the #include <mcp_can.h> file and its associated files but I can’t see anything in there that appears to set the SPI up so I’ve not been able to try it (perhaps it uses ‘bit banging?). I’m only calling the CAN device once to send out the data:
CAN0.sendMsgBuf(0x610, 0, 8, stmp);
Any help would be much appreciated – I’m sure that someone else must have come across this issue before. Ultimately, I have no issue with the actual code – it’s calling the SPI functions at different intervals to alter the SPI mode that I’m struggling with.
Here's the code:
// software calculates internal and external temperatures
// further deatils are listed withing the code
#include <mcp_can.h> // used for sending the data out on the can bus
#include <SPI.h> // used for the communication between the can module and the ads1118
const int csThermocouple = 7; // set cs to pin 7 for the ads1118 board
MCP_CAN CAN0(6); // Set cs to pin 6 for the can board
// mcp2515 has a spi mode of 0 (cpol = 0, cpha = 0) or mode 3 (cpol = 1, cpha = 1)
// this is different from the ads1118 - which is purely mode 1
// can frame data configuration - note - the dyno uses 'little endian':
// 16 bits 16 bits 16 bits
// lsb msb lsb msb lsb msb
// 0 1 : 2 3 : 4 5 : 6 7
// ch0 ch0 : ch1 ch1 : int int : n/a n/a
// note - in the can software display as a decimal so we can read it easier (well for positive values anyway)
// mapping locations:
// location / input 1 2 3 4 5 6 7 8
// array value 0 1 2 3 4 5 6 7
// unsigned char stmp[8] = {0, 0, 0, 0, 0, 0, 0, 0};
// set up the speed, data order and data mode
// SPISettings mySettting(speedMaximum, dataOrder, dataMode)
// speedMaximum: up to 5000000
// dataOrder: MSBFIRST or LSBFIRST
// dataMode : SPI_MODE0, SPI_MODE1, SPI_MODE2, or SPI_MODE3:
// Mode Clock Polarity Clock Phase Output Edge Data Capture
// SPI_MODE0 0 0 Falling Rising
// SPI_MODE1 0 1 Rising Falling // correct for the ads1118 - see datasheet page 28
// SPI_MODE2 1 0 Rising Falling
// SPI_MODE3 1 1 Falling Rising
SPISettings thermocouple(500000, MSBFIRST, SPI_MODE1); // spi configuration for the ads1118
// config register set ups - only need three main ones - two for external and one for internal temperature
// channel 0 internal temperature
unsigned int configInt = 0x8fdb; // 1 000 111 1 110 1 1 01 1 0.256v, power-down and single-shot mode, pull ups enabled, port 0, 8sps, internal temperature
//unsigned int configInt = 0x8fdb; // 1 000 111 1 110 1 1 01 1 0.256v, power-down and single-shot mode, pull ups enabled, port 0, 8sps, internal temperature
// channel 0 external temperature
//unsigned int configCh0 = 0x8fc3; // 1 000 111 1 110 0 1 01 1 0.256v, power-down and single-shot mode, pull ups enabled, port 0, 8sps, external temperature
unsigned int configCh0 = 0x8fcb; // 1 000 111 1 110 0 1 01 1 0.256v, power-down and single-shot mode, pull ups enabled, port 0, 8sps, external temperature
// channel 1 external temperature
//unsigned int configCh1 = 0xbfc3; // 1 011 111 0 110 0 1 01 1 0.256v, power-down and single-shot mode, pull ups enabled, port 1, 8sps, external temperature
unsigned int configCh1 = 0xbfcb; // 1 011 111 0 110 0 1 01 1 0.256v, power-down and single-shot mode, pull ups enabled, port 1, 8sps, external temperature
boolean entryFlag = false; // entry flat set by timer interrupt
int intTemperature = 0; // internal temperature
int ch0Temperature = 0; // ch0 temperature
int ch1Temperature = 0; // ch1 temperature
float intTemperatureFloat = 0; // internal temperature
float ch0TemperatureFloat = 0; // ch0 temperature
float ch1TemperatureFloat = 0; // ch1 temperature
float intVoltage = 0; // internal temperature voltage
float ch0Voltage = 0; // ch0 voltage
float ch0TotalVoltage = 0; // internal and ch0 voltage
float ch1Voltage = 0; // ch1 voltage
float ch1TotalVoltage = 0; // internal and ch1 voltage
unsigned char stmp[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // used for the can frame data
void setup()
{
Serial.begin(9600); // set up the serial monitor for debugging
SPI.begin(); // kick start the spi
// baudrate 500k and crystal 8mhz (see header file)
if(CAN0.begin(CAN_500KBPS, MCP_8MHz) == CAN_OK) Serial.print("can init ok!!\r\n");
else Serial.print("Can init fail!!\r\n");
// chip select as an output
pinMode (csThermocouple, OUTPUT);
// 16 bit register - hence the large tcnt1 value (approx half of 65536)
TCCR1A = 0; // clear timer / counter control register a
TCCR1B = 0; // clear timer / counter control register b
// transpose the formaula to give us hz to calculate the preload - several options available:
//TCNT1 = 3036; // preload timer = 65536-16MHz/256/1Hz - must load that value int a and b
//TCNT1 = 34286; // preload timer = 65536-16MHz/256/2Hz - must load that value int a and b
//TCNT1 = 49911; // preload timer = 65536-16MHz/256/4Hz - must load that value int a and b
TCNT1 = 57724; // preload timer = 65536-16MHz/256/8Hz - must load that value int a and b
TCCR1B |= (1 << CS12); // 256 prescaler - see above calculation
TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt
/*
// send internal temperature config to get things started
SPI.beginTransaction(thermocouple);
//SPI.beginTransaction();
digitalWrite (csThermocouple, LOW); // activate
SPI.transfer16(configInt);
digitalWrite (csThermocouple, HIGH); // deactivate
SPI.endTransaction(); // all done
*/
}
ISR(TIMER1_OVF_vect)
{
// check out tcnt in the data sheet
TCNT1 = 57724; // 8hz / 125ms
entryFlag = 1; // set the entry flag
}
void loop()
{
// wait here until the overflow has been triggered
if(entryFlag == 1)
{
entryFlag = 0; // clear the entry flag
// get int data and send ch0 config
int intSpiData = dataTransmission(configCh0);
// * * * shifting the bits bt 2 here * * *
intSpiData = intSpiData >> 2; // shift accross by 2 - we are only usng 14 bits
// function to convert spi data into a int data
//int intData = convertData(intSpiData);
float intData = convertData(intSpiData);
delay(15); // small delay between next read
// next get the ch0 data and send out ch1 config
int ch0SpiData = dataTransmission(configCh1);
// function to convert spi data into a ext data
int ch0Data = convertData(ch0SpiData);
delay(15); // small delay between next read
// next get the ch1 data and send out int config
int ch1SpiData = dataTransmission(configInt); // correct - reads back the internal temperature
// function to convert spi data into a ext data
int ch1Data = convertData(ch1SpiData);
delay(15); // small delay between next read
// * * * calculations and displaying results - perhaps use a function(s) for part of this * * *
// Convert internal data into a tempertature - >>2 has been done after spi function - see above
//intSpiData = intSpiData >> 2; // shift accross by 2 - we are only usng 14 bits
intData = intData * 0.03125; // one 14 bit lsb = 0.03125 degrees so multiply intData for temperature
intTemperatureFloat = intData; // load value into intTemperature for display )keep intData for conversion below)
// function to calculate the external temperature from the internal and external thermocouple voltages
ch0TemperatureFloat = calculateExtTemperature(intData, ch0Data);
delay(15); // small delay between next read
// function to calculate the external temperature from the internal and external thermocouple voltages
ch1TemperatureFloat = calculateExtTemperature(intData, ch1Data);
delay(15); // small delay between next read
// print the three calculated temperatures (dp)
Serial.print(" Int: ");
Serial.print(intTemperatureFloat);
Serial.print(" Ch0: ");
Serial.print(ch0TemperatureFloat);
Serial.print(" Ch1: ");
Serial.print(ch1TemperatureFloat);
intTemperature = intTemperatureFloat;
ch0Temperature = ch0TemperatureFloat;
ch1Temperature = ch1TemperatureFloat;
/*
// print the three calculated temperatures (dp)
Serial.print("Int Temperature: ");
Serial.println(intTemperatureFloat);
Serial.print("Ch0 Temperature: ");
Serial.println(ch0TemperatureFloat);
Serial.print("Ch1 Temperature: ");
Serial.println(ch1TemperatureFloat);
intTemperature = intTemperatureFloat;
ch0Temperature = ch0TemperatureFloat;
ch1Temperature = ch1TemperatureFloat;
// can data ready (no dp)
Serial.print("Int Temperature: ");
Serial.println(intTemperature);
Serial.print("Ch0 Temperature: ");
Serial.println(ch0Temperature);
Serial.print("Ch1 Temperature: ");
Serial.println(ch1Temperature);
*/
Serial.println(); // apply a space between each readout
//stmp[7] = analogRead(input5); // read the voltage on the potentiometer
// lnow send out the data on can - note - the dyno uses 'little endian':
stmp[0] = intTemperature;
stmp[1] = intTemperature >> 8;
stmp[2] = ch0Temperature;
stmp[3] = ch0Temperature >> 8;
stmp[4] = ch1Temperature;
stmp[5] = ch1Temperature >> 8;
// send data: id = 0x610, standrad flame, data len = 8, stmp: data buf
CAN0.sendMsgBuf(0x610, 0, 8, stmp);
} // end of entry loop
} // end void loop
// * * functions * * *
int dataTransmission(int thermocoupleConfig)
{
int returnData = 0;
SPI.beginTransaction(thermocouple);
//SPI.beginTransaction();
digitalWrite (csThermocouple, LOW); // activate
returnData = SPI.transfer16(thermocoupleConfig); // send out new config / get previous config temperature data
digitalWrite (csThermocouple, HIGH); // deactivate
SPI.endTransaction(); // all done
return returnData; // sent the data back for calculation
}
int convertData(int thermocoupleData)
{
//float returnConvertedData = 0;
int returnConvertedData = 0;
//float positiveThermocoupleData = 0;
int positiveThermocoupleData = 0;
// Bit-14 is the sign - copy the 13 bits accross from 'thermocoupleData' and delete the 14th bit
positiveThermocoupleData = (thermocoupleData & 0x00001FFF);
// Check for negative temperature using the original thermocoupleData that still holds the 14th bit
if (thermocoupleData & 0x00002000) // 10 0000 0000 0000
{
// 2's complement operation -> invert
thermocoupleData = ~thermocoupleData;
// Ensure operation involves lower 13-bit only
positiveThermocoupleData = thermocoupleData & 0x00001FFF;
// Add 1 to obtain the positive number
//thermocoupleTemperature += 1;
positiveThermocoupleData = positiveThermocoupleData + 1;
// Make temperature negative
//thermocoupleTemperature *= -1;
positiveThermocoupleData = positiveThermocoupleData * -1;
}
returnConvertedData = positiveThermocoupleData; // load value for return
return returnConvertedData; // to load into the can frame (no decimal place - see int)
}
float calculateExtTemperature(int intData, int extData)
{
float intVoltage = 0;
float extVoltage = 0;
float totalVoltage = 0;
float extTemperature = 0;
intVoltage = intData * 41e-6; // 41xe-6 is he best conversion figure available for the non linear scale
// eg: 20 degrees * 41e-6 = 0.82mv (0.798mv on the k type conversion table)
//extVoltage = float(7.82e-6 * extData); // not sure if the float makes a difference?
extVoltage = (7.82e-6 * extData); // convert adc reading to a voltage - fsr = 256mv, 15 bits = 32767/6
// 256mv / 32767 = 7.82e-6
totalVoltage = intVoltage + extVoltage; // add the hot and cold junction voltages together
extTemperature = totalVoltage / 41e-6; // now convert to a temperature
return extTemperature; // return calculated temperature for displa / can frame
}