MLX90609 SPI

Hi!

am relatively new to arduino, but already had some achievements :slight_smile:

anyway, at the moment i am struggeling with interfacing the Melexis MLX90609 gyroscope with SPI.

I think I understand the basics of SPI commuication, but I have problems understanding the datasheet (see next post).

SPI init:
SPCR = (1<<SPE)|(1<<MSTR);

to set the sensor online:

void setActive()
{
digitalWrite(SLAVESELECT, LOW); // activates device
//create the ADCC command
byte highByte=B10010110;
byte lowByte=B00000000;
spi_transfer (highByte);
spi_transfer (lowByte);
digitalWrite(SLAVESELECT,HIGH);//deactivates device

Serial.print("Active: ");
Serial.println(SPDR, BIN);
}

but I do not get the desired respopnses, not even the "Refusal Answer" ... indeed i can send what I want and never get a "refusal answer".

has anybody have any experience with this sensor?

any help, tips and hints are very appreciated!

thank you :slight_smile:
Daniel

PS: this is my first post and i am not allowed to post the link the datasheet in my first post.

here is the datasheet: Error - Melexis

Daniel

nobody any clues? If you need more information, let me know!

OK this code doesn't appear to be doing anything so can you post all the code so we can see if you are missing anything else. According to the data sheet you are just activating the ADC and not doing anything about receiving an answer before you de activate the device.

If you post code please surround it in the square bracket code as given by the hash icon on the top row.

I have not posted more code, because the part I posted already does not work, more code would confuse I think and according to the datasheet I should get an answer to that as well (set the ADC online). even if I would send nonsense data to the sensor, it should return me a refusal answer, but it doesnt.

here is my complete code, its a bit messy because I tried several things to get it work.

thx for looking at it!

#define DATAOUT 11//MOSI
#define DATAIN 12//MISO - not used, but part of builtin SPI
#define SPICLOCK  13//sck
#define SLAVESELECT 10//ss


void setup()
{
  Serial.begin(9600);
  
  byte clr;
  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(SLAVESELECT,OUTPUT);
  digitalWrite(SLAVESELECT,HIGH); //disable device
  
  SPCR = (1<<SPE)|(1<<MSTR);
  clr=SPSR;
  clr=SPDR;
  
  //delay(3000);
  
  //write_SPI(0b10011100, false);
  //write_SPI(0b00000000, false);
  //write_SPI(0b11110000, true);
  
  delay(10);
  setActive();
  delay(10);
}

char spi_transfer(volatile char data)
{
  SPDR = data;                    // Start the transmission
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };
  return SPDR;                    // return the received byte
}



byte write_SPI(byte data, boolean end)
{
  digitalWrite(SLAVESELECT,LOW);
  byte rr = spi_transfer(data);
  
  digitalWrite(SLAVESELECT,HIGH); //release chip, signal end transfer
  delay(10);
  return rr;
}

void setActive()
{
  digitalWrite(SLAVESELECT, LOW); // activates device
  //create the ADCC command
  byte highByte=B10010110;
  byte lowByte=B00000000;
  spi_transfer (highByte);
  spi_transfer (lowByte);
  digitalWrite(SLAVESELECT,HIGH);//deactivates device
  
  Serial.print("Active: ");
  Serial.println(SPDR, BIN);
}

void conversionStart()
{
  //MOSI):1 0 0 1 CHAN 1 0 0 x x x x x x x x x x x x x x x x
  write_SPI(0b10011100, false);
  write_SPI(0b00000000, true);
  //write_SPI(0b00000000);
  
  Serial.print("Start: ");
  Serial.println(SPDR, BIN);
}

void poll()
{
  //1 0 0 0 0 0 0 0 x x x x x x x x x x x x x x x x
  write_SPI(B10000000, false);
  Serial.print("Poll: ");
  
  byte data1 = write_SPI(B00000000, false);  
  byte data2 = write_SPI(B00000000, true);
  
  
  Serial.print(data1, BIN);
  Serial.print(".");
  Serial.println(data2, BIN);
}

void loop()
{
  
  //setActive();
  delayMicroseconds(250);
  conversionStart();
  delayMicroseconds(250);
  poll();
  
  Serial.println(SPCR, BIN);
  delay(2000); 
}

@grumpy_mike:
just seen u are a manc, lived there a couple of months, am on the wirral since couple of months... originaly from germany.... damn small planet :slight_smile:

got it working, maybe it helps others when i post how to do it:

in general: RTFM :wink: there is an SPI configuration register called SPCR, each bit is one configuration flag. The sensor/chip datasheets tell you how to configure SPI.

in case of the Melexis MLX90609 Gyro, i just copied this from the datasheet:

void configureSPI_GYRO()
{
  SPCR = /* Configure SPI mode: */
    (1<<SPE) |  /* to enable SPI */
    (1<<MSTR) | /* to set Master SPI mode */
    (1<<CPOL) | /* SCK is high when idle */
    (1<<CPHA) | /* data is sampled on the trailing edge of the SCK */
    (1<<SPR0);  /* It sets SCK freq. in 8 times less than a system clock */    
}

ok, SPI config sorted, if you want to know more about the SPCR register, look in the arduino playground.

next we write a generic function to interact with SPI devices. There was one example in the datasheet, but that was identical to the one I already found in the web, here it is:

char spi_transfer(unsigned char data)
{
  SPDR = data;                    // Start the transmission
  while (!(SPSR & (1<<SPIF))){};  // Wait the end of the transmission
  return SPDR;                    // return the received byte
}

what was confusing for me as a newbie, was that SPI has an 8bit packet size, but the datasheet showed 24 and 16 bit packets, and was even talking about 8 bits when actually 24 were needed :o
I found out that the first byte is the command to the melexis, but somehow i can only receive as many bits as i sent to the chip, so to get the 16 bit answer, i have to send 16 dummy bits of any value to the chip to get the answer. but because SPI has only an 8bit register, we have to send 2 dummy bytes to get to bytes response and then combine those two bytes to one 16 bit integer and strip out the parts we need, there re many ways to do thats, mine need some simplification i think, any ideas?

so, here is my complete code, I have tried to comment it a bit...

#define DATAOUT 11//MOSI
#define DATAIN 12//MISO - not used, but part of builtin SPI
#define SPICLOCK  13//sck
#define CS_GYRO 10//ss



void setup()
{
  Serial.begin(115200);
  
  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(CS_GYRO,OUTPUT);
  
  digitalWrite(CS_GYRO,HIGH); //disable device
    
  //set SPI speed, not needed!?
  //SPSR = (1<<SPI2X);
  
  configureSPI_GYRO();
}

/*
Writes to the SPI configuration register for device specific communication configuration

RTFM -> read the datasheet very carefully!

*/
void configureSPI_GYRO()
{
  SPCR = /* Configure SPI mode: */
    // (1<<SPIE) | /* should be activated to enable interruption from the SPI */
    (1<<SPE) | /* to enable SPI */
    (1<<MSTR) | /* to set Master SPI mode */
    (1<<CPOL) | /* SCK is high when idle */
    (1<<CPHA) | /* data is sampled on the trailing edge of the SCK */
    // (1<<SPR1) | /* In this example SPI0=1, SPR1=0 (commented) and SPI2X=1 */
    (1<<SPR0); /* It sets SCK freq. in 8 times less than a system clock */    
}


/*
generic SPI read/write function, can be used with any device
*/
char spi_transfer(unsigned char data)
{
  SPDR = data;                    // Start the transmission
  while (!(SPSR & (1<<SPIF))){};  // Wait the end of the transmission
  return SPDR;                    // return the received byte
}


/*
reads the temperature value of the 11 bit ADC of the melexis, the MLX uses the temp sensor to compensate temperature drift!

- send the command to sample the sensor, the value is stored in an internal register, this is done by the first bit send to the chip
- the answer is 16 bits, to get it, we need to send 2 dummy bytes (=16bits), SPI is synchron, with every bit i send, i got one back from the chip
- the tricky bit is the combination of the two response bytes into one value... ok it is for me because i never really used bit operations

*/
unsigned int getTemperature()
{
  byte data1, data2;
  
  digitalWrite(CS_GYRO,LOW);
  spi_transfer(0b10011100);// ADCC --> ADC Conversion
  digitalWrite(CS_GYRO,HIGH);
  
  delayMicroseconds(250);
  
  digitalWrite(CS_GYRO,LOW);
  spi_transfer(0b10000000);  // Send SPI ADCR Instruction
  data1 = spi_transfer(0x00);
  data2 = spi_transfer(0x00);  
  digitalWrite(CS_GYRO,HIGH);
  
  /*
  tricky... for me
  - the last/left 4 bits of the first bit are not part of the ADC value, so we eliminate them with the mask & B00001111
  - the value has 11 bits, so we need to put the two bytes into one int which is capable of holding 16 bits
  - then we have to move the whole first byte, where only the first 4 bits are left, 7 bits to the left
  - the first bit of the second byte is always 0, so we have to shift one to right  
  - once that is done, we can just add the both values, or we could bitwise OR them to fuse them together, what is better/faster???
  */  
  unsigned int result = ((data1 & B00001111) << 7) + (data2>>1);
  
  return result;
}

/*
reads the angular rate value of the 11 bit ADC of the melexis.

- send the command to sample the sensor, the value is stored in an internal register, this is done by the first bit send to the chip
- the answer is 16 bits, to get it, we need to send 2 dummy bytes (=16bits), SPI is synchron, with every bit i send, i got one back from the chip
- the tricky bit is the combination of the two response bytes into one value... ok it is for me because i never really used bit operations

*/
unsigned int getAngularRate()
{
  byte data1, data2;
  
  digitalWrite(CS_GYRO,LOW);
  spi_transfer(0b10010100);
  digitalWrite(CS_GYRO,HIGH);
  
  delayMicroseconds(250);
  
  digitalWrite(CS_GYRO,LOW);
  spi_transfer(0b10000000);  // Send SPI ADCR Instruction
  data1 = spi_transfer(0x00);
  data2 = spi_transfer(0x00);  
  digitalWrite(CS_GYRO,HIGH);
  
  /*
  tricky... for me
  - the last/left 4 bits of the first bit are not part of the ADC value, so we eliminate them with the mask & B00001111
  - the value has 11 bits, so we need to put the two bytes into one int which is capable of holding 16 bits
  - then we have to move the whole first byte, where only the first 4 bits are left, 7 bits to the left
  - the first bit of the second byte is always 0, so we have to shift one to right  
  - once that is done, we can just add the both values, or we could bitwise OR them to fuse them together, what is better/faster???
  */    
  unsigned int result = ((data1 & B00001111) << 7) + (data2>>1);
  return result;
}

/*
if we really want to have values in a human common way...
here: degrees per second
*/
int toAngularRate(unsigned int adcValue)
{
  int conversion = (adcValue * 25/12)+400;
  signed int offset = 2500;
  return (conversion - offset)/6.67;
}

/*
if we really want to have values in a human common way...
here: degree celsius
*/
int toTemp(unsigned int adcValue)
{
  int conversion = (adcValue * 25/16)+300;
  signed int offset = 2500;
  return 25 + ((conversion - offset)/10);
}


/*
main loop
*/
void loop()
{  
  //sample MLX
  int rate = getAngularRate();
  int temp = getTemperature();
  
  //print results
  Serial.print("rate: ");
  Serial.println(toAngularRate(rate), DEC);
  
  Serial.print("temp: ");
  Serial.println(toTemp(temp), DEC);
  
  delay(200);
}