644 and SPI library

Hi all, I wrote this sketch http://arduino.cc/forum/index.php/topic,67188.msg566901.html#msg566901 for that project http://arduino.cc/forum/index.php/topic,68925.msg555639.html#msg555639. In brief: a sensor on the SPI bus measures a value which is to be stored on a µSD card which sits also on the SPI. The board is a self made Sanguino clone and I installed the files from avr-developers and am sure the board itself is ok. It works perfectly well with the SdFat based sketch for the µSD card (example AnalogLogger, modified). Also, all the SD related sketches in the ArduinoTestSuite are running. I see spikes on my scope if I connect it to MOSI. But the above mentioned code for the sensor (which worked with my Duemilanove) is not running at all. I do not see spikes on the MOSI, so I would think it is a matter of the SPI library, right? I already modified the pins_arduino.h to include the SPI Pins. Nothing. I am trying to get through the SdFat library to find a reference to the pin assignement, but I found nothing so far. Since I am not a real programmer I rely on libraries and cannot start programing from scratch. The hardware is also more or less designed, so bitbanging is not an option. Besides: I know the sketch works and I know the SPI bus works at least for the SD card, so there MUST be a fix for that, isn't it? Thanks very much in advance, any help is apprechiated. Have a nice weekend and keep up the good work, greetings from Patagonia Sebastian

godo: I already modified the pins_arduino.h to include the SPI Pins. Nothing.

Can you show your modifications please?

Sure thing, here it is. It used to be:

#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
const static uint8_t SS   = 53;
const static uint8_t MOSI = 51;
const static uint8_t MISO = 50;
const static uint8_t SCK  = 52;
#else
const static uint8_t SS   = 10;
const static uint8_t MOSI = 11;
const static uint8_t MISO = 12;
const static uint8_t SCK  = 13;
#endif

Now it is:

#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
const static uint8_t SS   = 53;
const static uint8_t MOSI = 51;
const static uint8_t MISO = 50;
const static uint8_t SCK  = 52;

#elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__)
const static uint8_t SS   = 4;
const static uint8_t MOSI = 5;
const static uint8_t MISO = 6;
const static uint8_t SCK  = 7;

#else
const static uint8_t SS   = 10;
const static uint8_t MOSI = 11;
const static uint8_t MISO = 12;
const static uint8_t SCK  = 13;
#endif

But can anyone tell me anyway why the SPI bus is quite exclusively redefined for the mega in this file? I guess if I select a megaboard the compiler would get the info about the pin layout from another file anyway, am I right?

No, I think most boards go through the same file, hence the #if defined stuff.

However some boards (Sanguino, Attiny spring to mind) need separate files in the "hardware" section of your sketches folder. Or maybe "need" is the wrong word, but that is how my setup is.

Can you clarify from your first post, you said you had a sensor, and an SD card, both using SPI. Do neither work on the Sanguino?

Can you confirm that those redefinitions "took"? Eg.

Serial.print ("SS = ");
Serial.println (SS, DEC);
// ditto for the others

Hi Nick,
if you refer to the cxx file, I got it in the right place.
The SD card work perfectly using a SdFat library example (AnalogLogger). I see spikes on the MOSI pin. And well, it simply works storing 4 analog sensor values and a temp from a DS18B20 and time and date from the DS1307.
I cannot make something useful out of your codesnippet, sorry. But can we not assume that at least the SdFat library manages to redefine the SPI pins correctly? So shouldn’t it be working. [Thinking hard] Oh I see, you suggest putting the snippet in the not working code to see what it says? When I do that the compiler complaines that “SS was not declared in that scope”. And if I define them in the header it is like that: I get all the values displayed, but they are either only 1s or 0s, and in the end the right numbers are displayed, but that ain’t a surprise after defining em right?

You say you did this:

const static uint8_t SS   = 4;
const static uint8_t MOSI = 5;
const static uint8_t MISO = 6;
const static uint8_t SCK  = 7;

And then:

"SS was not declared in that scope"

Well something is very wrong then.

But can we not assume that at least the SdFat library manages to redefine the SPI pins correctly?

I'm not going to assume anything, particularly when you say it isn't working.

I get all the values displayed, but they are either only 1s or 0s, and in the end the right numbers are displayed, but that ain't a surprise after defining em right?

I don't really understand this. Perhaps if you posted your code, and what the output was.

Hi Nick,
yes, I did this in the pins_arduino.h file.
And yes, we probably should not assume anything for obvious reasons.
I did not really know what to do with your proposed code lines (again, I have no deep understanding of the coding, I just improvise code until it works, which seems to be not the worst idea).
This is my code:

/*
 MS5541 Pressure Sensor calwords readout
 This program will read your MS5441 or compatible pressure sensor every 5 seconds and show you the calibration words, the calibration factors, 
 the raw values and the compensated values of temperature and pressure.
 Once you read out the calibration factors you can define them in the header of any sketch you write for the sensor.
 
Pins:
 MS5541 sensor attached to the following pins:
 MOSI: pin 5
 MISO: pin 6
 SCK: pin 7
 MCLK: pin 13
 CS: pin 3
 
 created August 2011
 by SMStrauch based on application note AN510 from www.intersema.ch, 
 and with help of robtillaart and ulrichard. Thanks!
 */

// include library:
#include <SPI.h>

// generate a MCKL signal pin
const int clock = 13; //works

void resetsensor() //this function keeps the sketch a little shorter
{
  digitalWrite(3, LOW);
  SPI.setDataMode(SPI_MODE0); 
  SPI.transfer(0x15);
  SPI.transfer(0x55);
  SPI.transfer(0x40);
  digitalWrite(3, HIGH);
}

void setup() {
  Serial.begin(9600);
  SPI.begin(); //see SPI library details on arduino.cc for details
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV32); //divide 16 MHz to communicate on 500 kHz
  pinMode(clock, OUTPUT);
  pinMode(4, OUTPUT); //that is the standard CS pin in use for the SDcard
  pinMode(3, OUTPUT); 
  digitalWrite(4, HIGH);
  digitalWrite(3, HIGH); 
  delay(100);
  
}

void loop() 
{
  TCCR1B = (TCCR1B & 0xF8) | 1 ; //generates the MCKL signal
  analogWrite (clock, 128) ; 

  void resetsensor();//resets the sensor - caution: afterwards mode = SPI_MODE0!

  //Calibration word 1
  unsigned int result1 = 0;
  unsigned int inbyte1 = 0;
  digitalWrite(3, LOW);
  SPI.transfer(0x1D); //send first byte of command to get calibration word 1
  SPI.transfer(0x50); //send second byte of command to get calibration word 1
  SPI.setDataMode(SPI_MODE1); //change mode in order to listen
  result1 = SPI.transfer(0x00); //send dummy byte to read first byte of word
  result1 = result1 << 8; //shift returned byte 
  inbyte1 = SPI.transfer(0x00); //send dummy byte to read second byte of word
  digitalWrite(3, HIGH);
  result1 = result1 | inbyte1; //combine first and second byte of word
  Serial.print("Calibration word 1 =");
  Serial.println(result1);

  void resetsensor();//resets the sensor

  //Calibration word 2; see comments on calibration word 1
  unsigned int result2 = 0;
  byte inbyte2 = 0; 
  digitalWrite(3, LOW);
  SPI.transfer(0x1D);
  SPI.transfer(0x60);
  SPI.setDataMode(SPI_MODE1); 
  result2 = SPI.transfer(0x00);
  result2 = result2 <<8;
  inbyte2 = SPI.transfer(0x00);
  digitalWrite(3, HIGH);
  result2 = result2 | inbyte2;
  Serial.print("Calibration word 2 =");
  Serial.println(result2);  

  void resetsensor();//resets the sensor

  //Calibration word 3; see comments on calibration word 1
  unsigned int result3 = 0;
  byte inbyte3 = 0;
  digitalWrite(3, LOW);
  SPI.transfer(0x1D);
  SPI.transfer(0x90); 
  SPI.setDataMode(SPI_MODE1); 
  result3 = SPI.transfer(0x00);
  result3 = result3 <<8;
  inbyte3 = SPI.transfer(0x00);
  digitalWrite(3, HIGH);
  result3 = result3 | inbyte3;
  Serial.print("Calibration word 3 =");
  Serial.println(result3);  

  void resetsensor();//resets the sensor

  //Calibration word 4; see comments on calibration word 1
  unsigned int result4 = 0;
  byte inbyte4 = 0;
  digitalWrite(3, LOW);
  SPI.transfer(0x1D);
  SPI.transfer(0xA0);
  SPI.setDataMode(SPI_MODE1); 
  result4 = SPI.transfer(0x00);
  result4 = result4 <<8;
  inbyte4 = SPI.transfer(0x00);
  result4 = result4 | inbyte4;
  digitalWrite(3, HIGH);
  Serial.print("Calibration word 4 =");
  Serial.println(result4);

  //now we do some bitshifting to extract the calibration factors 
  //out of the calibration words; read datasheet AN510 for better understanding
  long c1 = result1 >> 3 & 0x1FFF;
  long c2 = ((result1 & 0x07) << 10) | ((result2 >> 6) & 0x03FF);
  long c3 = (result3 >> 6) & 0x03FF;
  long c4 = (result4 >> 7) & 0x07FF;
  long c5 = ((result2 & 0x003F) << 6) | (result3 & 0x003F);
  long c6 = result4 & 0x007F;

  Serial.println(c1);
  Serial.println(c2);
  Serial.println(c3);
  Serial.println(c4);
  Serial.println(c5);
  Serial.println(c6);

  void resetsensor();//resets the sensor

  //Temperature:
  unsigned int tempMSB = 0; //first byte of value
  unsigned int tempLSB = 0; //last byte of value
  unsigned int D2 = 0;
  digitalWrite(3, LOW);
  SPI.transfer(0x0F); //send first byte of command to get temperature value
  SPI.transfer(0x20); //send second byte of command to get temperature value
  delay(35); //wait for conversion end
  SPI.setDataMode(SPI_MODE1); //change mode in order to listen
  tempMSB = SPI.transfer(0x00); //send dummy byte to read first byte of value
  tempMSB = tempMSB << 8; //shift first byte
  tempLSB = SPI.transfer(0x00); //send dummy byte to read second byte of value
  digitalWrite(3, HIGH);
  D2 = tempMSB | tempLSB; //combine first and second byte of value
  Serial.print("Temperature raw =");
  Serial.println(D2); //voilá!

  void resetsensor();//resets the sensor

  //Pressure:
  unsigned int presMSB = 0; //first byte of value
  unsigned int presLSB =0; //last byte of value
  unsigned int D1 = 0;
  digitalWrite(3, LOW);
  SPI.transfer(0x0F); //send first byte of command to get pressure value
  SPI.transfer(0x40); //send second byte of command to get pressure value
  delay(35); //wait for conversion end
  SPI.setDataMode(SPI_MODE1); //change mode in order to listen
  presMSB = SPI.transfer(0x00); //send dummy byte to read first byte of value
  presMSB = presMSB << 8; //shift first byte
  presLSB = SPI.transfer(0x00); //send dummy byte to read second byte of value
  digitalWrite(3, HIGH);
  D1 = presMSB | presLSB; //combine first and second byte of value
  Serial.print("Pressure raw =");
  Serial.println(D1);

  //calculation of the real values by means of the calibration factors and the maths
  //in the datasheet. const MUST be long
  const long UT1 = (c5 << 3) + 10000;
  const long dT = D2 - UT1;
  const long TEMP = 200 + ((dT * (c6 + 100)) >> 11);
  const long OFF  = c2 + (((c4 - 250) * dT) >> 12) + 10000;
  const long SENS = (c1/2) + (((c3 + 200) * dT) >> 13) + 3000;
  long PCOMP = (SENS * (D1 - OFF) >> 12) + 1000;
  float TEMPREAL = TEMP/10;

  Serial.print("Real Temperature in °C=");
  Serial.println(TEMPREAL);

  Serial.print("Compensated pressure in mbar =");
  Serial.println(PCOMP);

  //2nd order compensation only for T > 0°C
  const long dT2 = dT - ((dT >> 7 * dT >> 7) >> 3);
  const float TEMPCOMP = (200 + (dT2*(c6+100) >>11))/10;
  Serial.print("2nd order compensated temperature in °C =");
  Serial.println(TEMPCOMP);  

  delay(5000);
}

and when I just appended your proposed

Serial.print ("SS = ");
Serial.println (SS, DEC);
// ditto for the others

I got the “not declared” message. So “scope” refers more to sketch? Do I explicitely have to include the pins_arduino.h here, which is included in the SPI.cpp? Nope, I checked that, does not change a thing.
Then I defined the pins in the header: the serial monitor gave me the output and showed me probably only what I defined for the pin numbers. And it showed me all the values either as 0 or as the highest possible number depending on the bits of each calibration word (1023, 511, 4095 etc). Here is an example for the first case:

Calibration word 1 =0
Calibration word 2 =0
Calibration word 3 =0
Calibration word 4 =0
0
0
0
0
0
0
Temperature raw =0
Pressure raw =0
Real Temperature in °C=-28.00
Compensated pressure in mbar =-6137
2nd order compensated temperature in °C =-28.00

Thanks very much, Nick, I do appreciate your help very much and I hope I am halfway clear with my answers.

This isn’t doing what you think it is:

void loop() 

{

void resetsensor();//resets the sensor - caution: afterwards mode = SPI_MODE0!

That is a function prototype, not a function call. You want:

void loop() 
{
  ...

  resetsensor();  //resets the sensor - caution: afterwards mode = SPI_MODE0!

And this?

  SPI.setDataMode(SPI_MODE1); //change mode in order to listen

Why are you chopping and changing modes? That looks wrong on the face of it.


I see what you mean about the SS thing. Try adding another include:

// include library:
#include <SPI.h>
#include <pins_arduino.h>

Then make setup show what you get:

void setup() {

// blah blah, other stuff you had

  Serial.print ("SS = ");
  Serial.println (SS, DEC);
  Serial.print ("MOSI = ");
  Serial.println (MOSI, DEC);
  Serial.print ("MISO = ");
  Serial.println (MISO, DEC);
  Serial.print ("SCK = ");
  Serial.println (SCK, DEC);
  
}

Hmm, ok, I un-voided the resetsensor calls. The SPI.setDataMode is changeing because the sensor needs it, I guess. The datasheet says:

The SCLK (Serial clock) signal initiates the communication and synchronizes thedata transfer with each bit being sampled by the MS5541C on the rising edge of SCLK and each bit being sent by the MS5541C on the rising edge of SCLK. The data should thus be sampled by the microcontroller on the falling edge of SCLK and sent to the MS5541C with the falling edge of SCLK.

and

Transmission must occur on rising edge of the serial clock when the microcontroller wants to send data received by the sensor. On the other side, when the microcontroller wants to receive data sent by the sensor transmission must occur on the falling edge of the clock.

. I am pretty sure I tried it once without the changes and it probably did not work, so I included them. And now the big surprise: I included pins_arduino.h and I got this:

MOSI = 11 MISO = 12 SCK = 13 Calibration word 1 =0 Calibration word 2 =0 Calibration word 3 =0 Calibration word 4 =0 0 0 0 0 0 0 Temperature raw =0 Pressure raw =0 Real Temperature in °C=-28.00 Compensated pressure in mbar =-6137 2nd order compensated temperature in °C =-28.00

Hu! Why? When I add "static const uint_8 MOSI = 5" and ditto for the others the compiler complains about a redefinition (but only of the MOSI) and suddenly does not get the "clock" declaration any more. Even though it is there in plain sight.

The good news is that we seem to be closer to solving the problem. It would appear there are more than one pins_arduino.h file, and it would appear you have amended the wrong one. That was the purpose of the debugging displays, to establish that.

I'm not sure what platform you are on (eg. Windows) but a brute-force search of both the Arduino IDE directory tree, and your sketches "hardware" folder should find any extra copies.

Oh no.... I don't believe it... I found two very old folders in my c:/downloads folder (definitions for ATtiny) and deleted them. Afterwards I tested the sketch just compiling it (board is at home) and it claimed again "SS was not declared in that scope", but with a reference to the SPI.cpp file. Then I undeleted the old folders and the compiler did not complain any more. So it looks like the SPI library is for some reason refering to those files in my download folder. I will check that as soon as I get home to my board, but I am pretty positive we are on the right way here, thanks, Nick! PS: I use Windows 7.

[Hours later] Nope, didn't help. Still indicating the wrong pins. At least I deleted the old folders and they are not missed any more. So back to start...

Oh God, no.... I got it, and yet, I don't belief it... First of all: if you modify a whatever.h-file and rename it like "whatever_orig.h" the compiler will still look at that file and probably complain about redefinitions. I did not know that. You have to remove modified files from the folder (probably outside your arduino folder). Weird, hu? And here is the solution: I copied code with copy + paste, and even though it looks identical, it is not, it rather seems invisible to the compiler. So the compiler never read the #elif defined(AVR_ATmega644) || defined(AVR_ATmega644P) line in my modified pins_arduino.h file because it did not see it but jumped right to the #else. So I typed the copied lines manually into the code again, deleted the copied ones, saved it and: joy!

Now it gives me:

SS = 4 MOSI = 5 MISO = 6 SCK = 7 Calibration word 1 =22693 Calibration word 2 =19745 Calibration word 3 =25097 Calibration word 4 =28597 2836 5428 392 223 2121 53 Temperature raw =27596 Pressure raw =15440 Real Temperature in °C=24.00 Compensated pressure in mbar =1018 2nd order compensated temperature in °C =24.00

I already outcommented everything necessary for displaying the pin numbers and it still gives me good values. Perfect. So one last question: should I have known? Is it common knowledge not to use Ctrl-C/Ctrl-V? Thanks again, Nick, your suggestions kept me optimistic and I will keep the diagnostic code snippet for future challenges.

Well I'm glad it works. As to why ... well the problem is that the development environment does a few things "under the hood" that perhaps are a bit obscure (to me included). For one thing, it copies various .h files to a temporary directory for the actual compile. Its rules for what to copy are not well documented that I am aware of. Second, the define you mention (#if defined(AVR_ATmega644) ) I'm not sure even where it gets those from. Perhaps boards.txt? In my copy of boards.txt is this:

ADABoot644P.build.mcu=atmega644p

But that isn't exactly "AVR_ATmega644". So I just don't know. Perhaps one of the greater experts can cast light on how your choice of board morphs into the correct board type for compilation purposes.

You have to remove modified files from the folder (probably outside your arduino folder).

Or rename the file such that the extension is not a recognized extension. Rename whatever.h to whatever.hold, and it can stay in the directory it was in.