got it working, maybe it helps others when i post how to do it:
in general: RTFM
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);
}